From 2a0e46b3d61de97abc0d5c425e1a24fab54be959 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Jos=C3=A9=20Pedro=20Sousa?= Date: Tue, 23 Jan 2024 12:58:15 +0000 Subject: [PATCH 01/16] fix: fixing boxes wip --- boxes/blank-react/.gitignore | 4 +- boxes/blank-react/README.md | 93 +- boxes/blank-react/package.json | 16 +- .../app/components/contract_function_form.tsx | 13 +- .../src/app/components/wallet_dropdown.tsx | 2 +- boxes/blank-react/src/app/home.tsx | 4 +- boxes/blank-react/src/config.ts | 30 +- boxes/blank-react/src/environment/index.ts | 11 - .../{src => }/tests/blank.contract.test.ts | 15 +- boxes/blank-react/tsconfig.json | 3 +- boxes/blank/README.md | 89 +- boxes/blank/artifacts/Blank.ts | 91 ++ boxes/blank/package.json | 27 +- boxes/blank/src/config.ts | 29 + boxes/blank/src/environment/index.ts | 11 - boxes/blank/src/index.ts | 2 +- .../{src => }/tests/blank.contract.test.ts | 14 +- boxes/blank/tsconfig.json | 3 +- boxes/yarn.lock | 1053 ++++++++--------- 19 files changed, 759 insertions(+), 751 deletions(-) delete mode 100644 boxes/blank-react/src/environment/index.ts rename boxes/blank-react/{src => }/tests/blank.contract.test.ts (81%) create mode 100644 boxes/blank/artifacts/Blank.ts create mode 100644 boxes/blank/src/config.ts delete mode 100644 boxes/blank/src/environment/index.ts rename boxes/blank/{src => }/tests/blank.contract.test.ts (81%) diff --git a/boxes/blank-react/.gitignore b/boxes/blank-react/.gitignore index 1270997806b..d37f6988611 100644 --- a/boxes/blank-react/.gitignore +++ b/boxes/blank-react/.gitignore @@ -3,5 +3,5 @@ node_modules dest -src/artifacts -src/contracts/target \ No newline at end of file +artifacts +src/contracts/target diff --git a/boxes/blank-react/README.md b/boxes/blank-react/README.md index 3a1035d8a79..36286156a6e 100644 --- a/boxes/blank-react/README.md +++ b/boxes/blank-react/README.md @@ -1,80 +1,67 @@ -This is a minimal [Aztec](https://aztec.network/) Noir smart contract and frontend bootstrapped with [`aztec-cli unbox`](https://github.com/AztecProtocol/aztec-packages/tree/master/yarn-project/cli). It is recommended you use the `aztec-cli unbox blank-react` command so that the repository is copied with needed modifications from the monorepo subpackage. +# Aztec Box -## Setup +This box is a one-stop-shop for Aztec that will deploy a blank React Page. You can use it as a boilerplate to start developing your own Aztec app in seconds! -Dependencies can be installed from the root of the package: +## Prerequisites + +- You should have Docker installed. If you don't, follow [this guide](https://docs.aztec.network/dev_docs/getting_started/quickstart#install-docker). + +## Installation + +To start, run the Aztec install script: ```bash -yarn -yarn install:noir -yarn install:sandbox +bash -i <(curl -s install.aztec.network)` ``` -This sandbox requires [Docker](https://www.docker.com/) to be installed _and running_ locally. In the event the image needs updating, you can run `yarn install:sandbox` (see [sandbox docs](https://aztec-docs-dev.netlify.app/dev_docs/getting_started/sandbox) for more information.) +After a few minutes, you should have all the Aztec CLI commands ready to run. -In addition to the usual javascript dependencies, this project requires `nargo` (package manager) and `noir` (Aztec ZK smart contract language) in addition to `@aztec/aztec-cli`. The former two are installed by `yarn install:noir`. +### 1. Launching the sandbox -## Getting started +Run: -After `yarn` has run,`yarn start:sandbox` in one terminal will launch a local instance of the Aztec sandbox via Docker Compose and `yarn start:dev` will launch a frontend app for deploying and interacting with an empty Aztec smart contract. +```bash +aztec-sandbox +``` -At this point, [http://localhost:5173](http://localhost:5173) should provide a minimal smart contract frontend. +This will install all the dependencies and run the sandbox on port 8080 together with a anvil node. -This folder should have the following directory structure: +### 2. Unboxing the box -``` -|— README.md -|— package.json -|— src - |-config.ts - Blank Contract specific configuration for the frontend. - | You may need to update this if you modify the contract functions. - |— app - |— [frontend React .tsx code files] - |- scripts - |- [helpers for frontend to interact with contract on the sandbox] - |— contracts - |— src - | The Noir smart contract source files are here. - |— main.nr - the cloned noir contract, your starting point - |- interface.nr - autogenerated from main.nr when you compile - |— Nargo.toml [Noir build file, includes Aztec smart contract dependencies] - |— artifacts - | These are both generated from `contracts/` by the compile command - |— blank_contract.json - |— blank.ts - |— tests - | A simple end2end test deploying and testing the Blank contract deploys on a local sandbox - | The test requires the sandbox and anvil to be running (`yarn start:sandbox`). - | You can run the tests with `yarn test:integration` - |- blank.contract.test.ts -``` +Unbox the box with: -Most relevant to you is likely `src/contracts/main.nr` (and the build config `src/contracts/Nargo.toml`). This contains the example blank contract logic that the frontend interacts with and is a good place to start writing Noir. +```bash +aztec-cli unbox blank-react +``` -The `src/artifacts` folder can be re-generated from the command line +and install dependencies: ```bash -yarn compile +yarn ``` -This will generate a [Contract ABI](src/artifacts/test_contract.json) and TypeScript class for the [Aztec smart contract](src/contracts/main.nr), which the frontend uses to generate the UI. +## Start developing -Note: the `compile` command seems to generate a Typescript file which needs a single change - +Time to build. Run: +```bash +yarn start ``` -import TestContractArtifactJson from 'text_contract.json' assert { type: 'json' }; -// need to update the relative import to -import TestContractArtifactJson from './test_contract.json' assert { type: 'json' }; -``` -After compiling, you can re-deploy the updated noir smart contract from the web UI. The function interaction forms are generated from parsing the contract artifacts, so they should update automatically after you recompile. +Your React app is waiting for you on port 5176. Time to make it your own! + +In the `src/contracts` folder, you'll find the default contract being deployed. Don't forget to recompile with `aztec-nargo compile`! Read the [aztec.nr documentation](https://docs.aztec.network/dev_docs/contracts/main) to get started with the `aztec.nr` framework. -## Learn More +[Read the full Sandbox reference](https://docs.aztec.network/dev_docs/cli/sandbox-reference) for more info on what exactly is happening on your machine! -To learn more about Noir Smart Contract development, take a look at the following resources: +## More info -- [Awesome Noir](https://github.com/noir-lang/awesome-noir) - learn about the Noir programming language. +There are five folders in your `src` folder: -## Deploy on Aztec3 +- `app` - This is your actual React app +- `scripts` - These are the scripts the frontend is using to talk with the sandbox +- `contracts` - The Aztec Contracts you just deployed! +- `artifacts` - Auto-generated when you compile +- `test` - A boilerplate with a simple test -Coming Soon :) +Visit the [Aztec Docs](https://docs.aztec.network) for more information on how Aztec works, and the [Awesome Aztec Repository](https://github.com/AztecProtocol/awesome-aztec) for more cool projects, boilerplates and tooling. diff --git a/boxes/blank-react/package.json b/boxes/blank-react/package.json index b9a9ff00dfa..bc074a55f52 100644 --- a/boxes/blank-react/package.json +++ b/boxes/blank-react/package.json @@ -5,14 +5,15 @@ "type": "module", "main": "./dest/index.js", "scripts": { + "compile": "cd src/contracts && ${AZTEC_NARGO:-aztec-nargo} compile", + "codegen": "${AZTEC_CLI:-aztec-cli} codegen src/contracts/target -o artifacts --ts", "build": "yarn clean && yarn compile && yarn codegen && tsc -b && webpack", - "clean": "rm -rf ./dest .tsbuildinfo ./src/artifacts ./src/contracts/target", - "start": "serve -p 3000 ./dest", - "start:dev": "webpack serve --mode=development", + "clean": "rm -rf ./dest .tsbuildinfo ./artifacts ./src/contracts/target", + "prep": "yarn clean && yarn compile && yarn codegen", + "dev": "yarn prep && webpack serve --mode development", + "serve": "serve -p 3000 ./dest", "formatting": "prettier --check ./src && eslint ./src", "formatting:fix": "prettier -w ./src", - "compile": "cd src/contracts && ${AZTEC_NARGO:-aztec-nargo} compile", - "codegen": "${AZTEC_CLI:-aztec-cli} codegen src/contracts/target -o src/artifacts --ts", "test": "NODE_NO_WARNINGS=1 node --experimental-vm-modules $(yarn bin jest) --runInBand" }, "jest": { @@ -28,8 +29,8 @@ "moduleNameMapper": { "^(\\.{1,2}/.*)\\.js$": "$1" }, - "testRegex": "./src/.*\\.test\\.ts$", - "rootDir": "./src" + "testRegex": "tests/.*\\.test\\.ts$", + "rootDir": "./" }, "dependencies": { "@aztec/accounts": "^0.16.9", @@ -46,7 +47,6 @@ }, "devDependencies": { "@types/jest": "^29.5.0", - "@types/mocha": "^10.0.3", "@types/node": "^20.5.9", "@types/react": "^18.2.15", "@types/react-dom": "^18.2.7", diff --git a/boxes/blank-react/src/app/components/contract_function_form.tsx b/boxes/blank-react/src/app/components/contract_function_form.tsx index 66609bd2ab9..99c4bb08337 100644 --- a/boxes/blank-react/src/app/components/contract_function_form.tsx +++ b/boxes/blank-react/src/app/components/contract_function_form.tsx @@ -79,18 +79,25 @@ async function handleFunctionCall( // for now, dont let user change the salt. requires some change to the form generation if we want to let user choose one // since everything is currently based on parsing the contractABI, and the salt parameter is not present there const salt = Fr.random(); - return await deployContract(wallet, contractArtifact, typedArgs, salt, pxe); + return await deployContract(wallet, contractArtifact, typedArgs, salt, pxe.getPxe()); } if (functionAbi.functionType === 'unconstrained') { - return await viewContractFunction(contractAddress!, contractArtifact, functionName, typedArgs, pxe, wallet); + return await viewContractFunction( + contractAddress!, + contractArtifact, + functionName, + typedArgs, + pxe.getPxe(), + wallet, + ); } else { const txnReceipt = await callContractFunction( contractAddress!, contractArtifact, functionName, typedArgs, - pxe, + pxe.getPxe(), wallet, ); return `Transaction ${txnReceipt.status} on block number ${txnReceipt.blockNumber}`; diff --git a/boxes/blank-react/src/app/components/wallet_dropdown.tsx b/boxes/blank-react/src/app/components/wallet_dropdown.tsx index 28cba3e570a..456bac8c45b 100644 --- a/boxes/blank-react/src/app/components/wallet_dropdown.tsx +++ b/boxes/blank-react/src/app/components/wallet_dropdown.tsx @@ -20,7 +20,7 @@ export function WalletDropdown({ selected, onSelectChange, onError }: Props) { return; } const loadOptions = async () => { - const fetchedOptions = await pxe.getRegisteredAccounts(); + const fetchedOptions = await pxe.getPxe().getRegisteredAccounts(); setOptions(fetchedOptions); onSelectChange(fetchedOptions[0]); }; diff --git a/boxes/blank-react/src/app/home.tsx b/boxes/blank-react/src/app/home.tsx index 8475b2afe55..10c3a77d4ff 100644 --- a/boxes/blank-react/src/app/home.tsx +++ b/boxes/blank-react/src/app/home.tsx @@ -1,4 +1,4 @@ -import { PXE_URL } from '../config.js'; +import { pxe } from '../config.js'; import { WalletDropdown } from './components/wallet_dropdown.js'; import { Contract } from './contract.js'; import styles from './home.module.scss'; @@ -73,7 +73,7 @@ export function Home() { <> {`Failed to load accounts. Error: ${selectWalletError}`}
- {`Make sure PXE from Aztec Sandbox is running at: ${PXE_URL}`} + {`Make sure PXE from Aztec Sandbox is running at: ${pxe.getPxeUrl()}`} )} {!selectWalletError && !selectedWallet && `No accounts.`} diff --git a/boxes/blank-react/src/config.ts b/boxes/blank-react/src/config.ts index f7130110e5f..d393763ff1e 100644 --- a/boxes/blank-react/src/config.ts +++ b/boxes/blank-react/src/config.ts @@ -1,11 +1,29 @@ -import { ContractArtifact, PXE, createPXEClient } from '@aztec/aztec.js'; -import { BlankContractArtifact } from './artifacts/Blank.js'; +import { createPXEClient, waitForPXE } from '@aztec/aztec.js'; +import { BlankContractArtifact } from '../artifacts/Blank.js'; -// update this if using a different contract +class PXE { + pxeUrl; + pxe; -export const contractArtifact: ContractArtifact = BlankContractArtifact; + constructor() { + this.pxeUrl = process.env.PXE_URL || 'http://localhost:8080'; + this.pxe = createPXEClient(this.pxeUrl); + } -export const PXE_URL: string = process.env.PXE_URL || 'http://localhost:8080'; -export const pxe: PXE = createPXEClient(PXE_URL); + async setupPxe() { + await waitForPXE(this.pxe); + return this.pxe; + } + getPxeUrl() { + return this.pxeUrl; + } + + getPxe() { + return this.pxe; + } +} + +export const pxe = new PXE(); +export const contractArtifact = BlankContractArtifact; export const CONTRACT_ADDRESS_PARAM_NAMES = ['address']; diff --git a/boxes/blank-react/src/environment/index.ts b/boxes/blank-react/src/environment/index.ts deleted file mode 100644 index c64d7f6a2a3..00000000000 --- a/boxes/blank-react/src/environment/index.ts +++ /dev/null @@ -1,11 +0,0 @@ -import { createPXEClient, waitForPXE } from '@aztec/aztec.js'; - -const { PXE_URL = 'http://localhost:8080' } = process.env; - -// assumes environment is running locally, which this script does not trigger -// as well as anvil. anvil can be started with yarn test:integration -export const setupEnvironment = async () => { - const pxe = createPXEClient(PXE_URL); - await waitForPXE(pxe); - return pxe; - }; \ No newline at end of file diff --git a/boxes/blank-react/src/tests/blank.contract.test.ts b/boxes/blank-react/tests/blank.contract.test.ts similarity index 81% rename from boxes/blank-react/src/tests/blank.contract.test.ts rename to boxes/blank-react/tests/blank.contract.test.ts index 8adba3984e5..09f62128f39 100644 --- a/boxes/blank-react/src/tests/blank.contract.test.ts +++ b/boxes/blank-react/tests/blank.contract.test.ts @@ -1,5 +1,5 @@ import { BlankContract } from '../artifacts/Blank.js'; -import { callContractFunction, deployContract, getWallet } from '../scripts/index.js'; +import { callContractFunction, deployContract, getWallet } from '../src/scripts/index.js'; import { AccountWallet, AztecAddress, @@ -11,11 +11,10 @@ import { Wallet, createDebugLogger, } from '@aztec/aztec.js'; -import { setupEnvironment } from '../environment/index.js'; +import { pxe } from '../src/config.js'; const logger = createDebugLogger('aztec:http-pxe-client'); - async function deployZKContract(owner: CompleteAddress, wallet: Wallet, pxe: PXE) { logger('Deploying Blank contract...'); const contractAddress = await deployContract(owner, BlankContract.artifact, [], Fr.random(), pxe); @@ -31,16 +30,14 @@ describe('ZK Contract Tests', () => { let _account3: CompleteAddress; let contract: Contract; let contractAddress: AztecAddress; - let pxe: PXE; beforeAll(async () => { - pxe = await setupEnvironment(); - const accounts = await pxe.getRegisteredAccounts(); + const accounts = await pxe.getPxe().getRegisteredAccounts(); [owner, _account2, _account3] = accounts; - wallet = await getWallet(owner, pxe); + wallet = await getWallet(owner, pxe.getPxe()); - contract = await deployZKContract(owner, wallet, pxe); + contract = await deployZKContract(owner, wallet, pxe.getPxe()); contractAddress = contract.address; }, 60000); @@ -50,7 +47,7 @@ describe('ZK Contract Tests', () => { contract.artifact, 'getPublicKey', [owner.address.toField()], - pxe, + pxe.getPxe(), owner, ); diff --git a/boxes/blank-react/tsconfig.json b/boxes/blank-react/tsconfig.json index acc585a3b0e..3b4c57677db 100644 --- a/boxes/blank-react/tsconfig.json +++ b/boxes/blank-react/tsconfig.json @@ -1,6 +1,5 @@ { "compilerOptions": { - "rootDir": "src", "outDir": "dest", "tsBuildInfoFile": ".tsbuildinfo", "target": "es2020", @@ -21,5 +20,5 @@ "skipLibCheck": true, "jsx": "react-jsx" }, - "include": ["src", "src/contracts/target/*.json"] + "include": ["src", "tests", "src/contracts/target/*.json", "artifacts/*"] } diff --git a/boxes/blank/README.md b/boxes/blank/README.md index 36504fefc83..36286156a6e 100644 --- a/boxes/blank/README.md +++ b/boxes/blank/README.md @@ -1,76 +1,67 @@ -This is a minimal [Aztec](https://aztec.network/) Noir smart contract and frontend bootstrapped with [`aztec-cli unbox`](https://github.com/AztecProtocol/aztec-packages/tree/master/yarn-project/cli). It is recommended you use the `aztec-cli unbox blank` command so that the repository is copied with needed modifications from the monorepo subpackage. +# Aztec Box -## Setup +This box is a one-stop-shop for Aztec that will deploy a blank React Page. You can use it as a boilerplate to start developing your own Aztec app in seconds! -Dependencies can be installed from the root of the package: +## Prerequisites + +- You should have Docker installed. If you don't, follow [this guide](https://docs.aztec.network/dev_docs/getting_started/quickstart#install-docker). + +## Installation + +To start, run the Aztec install script: ```bash -yarn -yarn install:noir -yarn install:sandbox +bash -i <(curl -s install.aztec.network)` ``` -This sandbox requires [Docker](https://www.docker.com/) to be installed _and running_ locally. In the event the image needs updating, you can run `yarn install:sandbox` (see [sandbox docs](https://aztec-docs-dev.netlify.app/dev_docs/getting_started/sandbox) for more information.) +After a few minutes, you should have all the Aztec CLI commands ready to run. -In addition to the usual javascript dependencies, this project requires `nargo` (package manager) and `noir` (a Domain Specific Language for SNARK proving systems) in addition to `@aztec/aztec-cli`. The former are installed within `yarn install:noir`. +### 1. Launching the sandbox -## Getting started +Run: -After `yarn` has run,`yarn start:sandbox` in one terminal will launch a local instance of the Aztec sandbox via Docker Compose and `yarn start:dev` will launch a frontend app for deploying and interacting with an empty Aztec smart contract. +```bash +aztec-sandbox +``` -At this point, [http://localhost:5173](http://localhost:5173) should provide a minimal smart contract frontend. +This will install all the dependencies and run the sandbox on port 8080 together with a anvil node. -This folder should have the following directory structure: +### 2. Unboxing the box -``` -|— README.md -|— package.json -|— src - index.html - index.ts - |— contracts - |— src - | The Noir smart contract source files are here. - |— main.nr - the cloned noir contract, your starting point - |- interface.nr - autogenerated from main.nr when you compile - |— Nargo.toml [Noir build file, includes Aztec smart contract dependencies] - |— artifacts - | These are both generated from `contracts/` by the compile command - |— blank_contract.json - |— blank.ts - |— tests - | A simple end2end test deploying and testing the minimal contract on a local sandbox - | using the front end helper methods in index.ts - | The test requires the sandbox and anvil to be running (yarn start:sandbox). - |- blank.contract.test.ts -``` +Unbox the box with: -Most relevant to you is likely `src/contracts/main.nr` (and the build config `src/contracts/Nargo.toml`). This contains the example blank contract logic that the frontend interacts with and is a good place to start writing Noir. +```bash +aztec-cli unbox blank-react +``` -The `src/artifacts` folder can be re-generated from the command line +and install dependencies: ```bash -yarn compile +yarn ``` -This will generate a [contract artifact](src/artifacts/test_contract.json) and TypeScript class for the [Aztec smart contract](src/contracts/main.nr), which the frontend uses to generate the UI. +## Start developing -Note: the `compile` command seems to generate a Typescript file which needs a single change - +Time to build. Run: +```bash +yarn start ``` -import TestContractArtifactJson from 'text_contract.json' assert { type: 'json' }; -// need to update the relative import to -import TestContractArtifactJson from './test_contract.json' assert { type: 'json' }; -``` -After compiling, you can re-deploy the updated noir smart contract from the web UI. The function interaction forms are generated from parsing the contract artifact, so they should update automatically after you recompile. +Your React app is waiting for you on port 5176. Time to make it your own! + +In the `src/contracts` folder, you'll find the default contract being deployed. Don't forget to recompile with `aztec-nargo compile`! Read the [aztec.nr documentation](https://docs.aztec.network/dev_docs/contracts/main) to get started with the `aztec.nr` framework. -## Learn More +[Read the full Sandbox reference](https://docs.aztec.network/dev_docs/cli/sandbox-reference) for more info on what exactly is happening on your machine! -To learn more about Noir Smart Contract development, take a look at the following resources: +## More info -- [Awesome Noir](https://github.com/noir-lang/awesome-noir) - learn about the Noir programming language. +There are five folders in your `src` folder: -## Deploy on Aztec3 +- `app` - This is your actual React app +- `scripts` - These are the scripts the frontend is using to talk with the sandbox +- `contracts` - The Aztec Contracts you just deployed! +- `artifacts` - Auto-generated when you compile +- `test` - A boilerplate with a simple test -Coming Soon :) +Visit the [Aztec Docs](https://docs.aztec.network) for more information on how Aztec works, and the [Awesome Aztec Repository](https://github.com/AztecProtocol/awesome-aztec) for more cool projects, boilerplates and tooling. diff --git a/boxes/blank/artifacts/Blank.ts b/boxes/blank/artifacts/Blank.ts new file mode 100644 index 00000000000..4b1bad68380 --- /dev/null +++ b/boxes/blank/artifacts/Blank.ts @@ -0,0 +1,91 @@ + +/* Autogenerated file, do not edit! */ + +/* eslint-disable */ +import { + AztecAddress, + AztecAddressLike, + CompleteAddress, + Contract, + ContractArtifact, + ContractBase, + ContractFunctionInteraction, + ContractMethod, + DeployMethod, + EthAddress, + EthAddressLike, + FieldLike, + Fr, + FunctionSelectorLike, + loadContractArtifact, + NoirCompiledContract, + Point, + PublicKey, + Wallet, +} from '@aztec/aztec.js'; +import BlankContractArtifactJson from '../src/contracts/target/blank-Blank.json' assert { type: 'json' }; +export const BlankContractArtifact = loadContractArtifact(BlankContractArtifactJson as NoirCompiledContract); + +/** + * Type-safe interface for contract Blank; + */ +export class BlankContract extends ContractBase { + + private constructor( + completeAddress: CompleteAddress, + wallet: Wallet, + portalContract = EthAddress.ZERO + ) { + super(completeAddress, BlankContractArtifact, wallet, portalContract); + } + + + + /** + * Creates a contract instance. + * @param address - The deployed contract's address. + * @param wallet - The wallet to use when interacting with the contract. + * @returns A promise that resolves to a new Contract instance. + */ + public static async at( + address: AztecAddress, + wallet: Wallet, + ) { + return Contract.at(address, BlankContract.artifact, wallet) as Promise; + } + + + /** + * Creates a tx to deploy a new instance of this contract. + */ + public static deploy(wallet: Wallet, ) { + return new DeployMethod(Point.ZERO, wallet, BlankContractArtifact, BlankContract.at, Array.from(arguments).slice(1)); + } + + /** + * Creates a tx to deploy a new instance of this contract using the specified public key to derive the address. + */ + public static deployWithPublicKey(publicKey: PublicKey, wallet: Wallet, ) { + return new DeployMethod(publicKey, wallet, BlankContractArtifact, BlankContract.at, Array.from(arguments).slice(2)); + } + + + + /** + * Returns this contract's artifact. + */ + public static get artifact(): ContractArtifact { + return BlankContractArtifact; + } + + + /** Type-safe wrappers for the public methods exposed by the contract. */ + public methods!: { + + /** compute_note_hash_and_nullifier(contract_address: struct, nonce: field, storage_slot: field, serialized_note: array) */ + compute_note_hash_and_nullifier: ((contract_address: AztecAddressLike, nonce: FieldLike, storage_slot: FieldLike, serialized_note: FieldLike[]) => ContractFunctionInteraction) & Pick; + + /** getPublicKey(address: struct) */ + getPublicKey: ((address: AztecAddressLike) => ContractFunctionInteraction) & Pick; + }; +} diff --git a/boxes/blank/package.json b/boxes/blank/package.json index f8033cdea46..1af3cbb4c4a 100644 --- a/boxes/blank/package.json +++ b/boxes/blank/package.json @@ -5,14 +5,15 @@ "type": "module", "main": "./dest/index.js", "scripts": { + "compile": "cd src/contracts && ${AZTEC_NARGO:-aztec-nargo} compile", + "codegen": "${AZTEC_CLI:-aztec-cli} codegen src/contracts/target -o artifacts --ts", "build": "yarn clean && yarn compile && yarn codegen && tsc -b && webpack", - "clean": "rm -rf ./dest .tsbuildinfo ./src/artifacts ./src/contracts/target", - "start": "serve -p 3000 ./dest", - "start:dev": "webpack serve --mode=development", + "clean": "rm -rf ./dest .tsbuildinfo ./artifacts ./src/contracts/target", + "prep": "yarn clean && yarn compile && yarn codegen && tsc -b", + "dev": "yarn prep && webpack serve --mode development", + "serve": "serve -p 3000 ./dest", "formatting": "prettier --check ./src && eslint ./src", "formatting:fix": "prettier -w ./src", - "compile": "cd src/contracts && ${AZTEC_NARGO:-aztec-nargo} compile", - "codegen": "${AZTEC_CLI:-aztec-cli} codegen src/contracts/target -o src/artifacts --ts", "test": "NODE_NO_WARNINGS=1 node --experimental-vm-modules $(yarn bin jest) --runInBand" }, "jest": { @@ -28,8 +29,8 @@ "moduleNameMapper": { "^(\\.{1,2}/.*)\\.js$": "$1" }, - "testRegex": "./src/.*\\.test\\.ts$", - "rootDir": "./src" + "testRegex": "tests/.*\\.test\\.ts$", + "rootDir": "./" }, "dependencies": { "@aztec/accounts": "^0.16.9", @@ -62,18 +63,6 @@ "webpack-cli": "^5.1.4", "webpack-dev-server": "^4.15.1" }, - "browserslist": { - "production": [ - ">0.5%", - "not dead", - "not op_mini all" - ], - "development": [ - "last 1 chrome version", - "last 1 firefox version", - "last 1 safari version" - ] - }, "files": [ "dest", "src", diff --git a/boxes/blank/src/config.ts b/boxes/blank/src/config.ts new file mode 100644 index 00000000000..d393763ff1e --- /dev/null +++ b/boxes/blank/src/config.ts @@ -0,0 +1,29 @@ +import { createPXEClient, waitForPXE } from '@aztec/aztec.js'; +import { BlankContractArtifact } from '../artifacts/Blank.js'; + +class PXE { + pxeUrl; + pxe; + + constructor() { + this.pxeUrl = process.env.PXE_URL || 'http://localhost:8080'; + this.pxe = createPXEClient(this.pxeUrl); + } + + async setupPxe() { + await waitForPXE(this.pxe); + return this.pxe; + } + + getPxeUrl() { + return this.pxeUrl; + } + + getPxe() { + return this.pxe; + } +} + +export const pxe = new PXE(); +export const contractArtifact = BlankContractArtifact; +export const CONTRACT_ADDRESS_PARAM_NAMES = ['address']; diff --git a/boxes/blank/src/environment/index.ts b/boxes/blank/src/environment/index.ts deleted file mode 100644 index c64d7f6a2a3..00000000000 --- a/boxes/blank/src/environment/index.ts +++ /dev/null @@ -1,11 +0,0 @@ -import { createPXEClient, waitForPXE } from '@aztec/aztec.js'; - -const { PXE_URL = 'http://localhost:8080' } = process.env; - -// assumes environment is running locally, which this script does not trigger -// as well as anvil. anvil can be started with yarn test:integration -export const setupEnvironment = async () => { - const pxe = createPXEClient(PXE_URL); - await waitForPXE(pxe); - return pxe; - }; \ No newline at end of file diff --git a/boxes/blank/src/index.ts b/boxes/blank/src/index.ts index 6b7cafeac9f..1d0bf66f82d 100644 --- a/boxes/blank/src/index.ts +++ b/boxes/blank/src/index.ts @@ -1,5 +1,5 @@ // docs:start:imports -import { BlankContractArtifact } from './artifacts/Blank.js'; +import { BlankContractArtifact } from '../artifacts/Blank.js'; import { AccountWallet, AztecAddress, diff --git a/boxes/blank/src/tests/blank.contract.test.ts b/boxes/blank/tests/blank.contract.test.ts similarity index 81% rename from boxes/blank/src/tests/blank.contract.test.ts rename to boxes/blank/tests/blank.contract.test.ts index c4e2cb5cdba..34515dce208 100644 --- a/boxes/blank/src/tests/blank.contract.test.ts +++ b/boxes/blank/tests/blank.contract.test.ts @@ -1,5 +1,5 @@ import { BlankContract } from '../artifacts/Blank.js'; -import { callContractFunction, deployContract, getWallet } from '../index.js'; +import { callContractFunction, deployContract, getWallet } from '../src/index.js'; import { AccountWallet, AztecAddress, @@ -11,7 +11,7 @@ import { Wallet, createDebugLogger, } from '@aztec/aztec.js'; -import { setupEnvironment } from '../environment/index.js'; +import { pxe } from '../src/config.js'; const logger = createDebugLogger('aztec:blank-box-test'); @@ -30,16 +30,14 @@ describe('ZK Contract Tests', () => { let _account3: CompleteAddress; let contract: Contract; let contractAddress: AztecAddress; - let pxe: PXE; beforeAll(async () => { - pxe = await setupEnvironment(); - const accounts = await pxe.getRegisteredAccounts(); + const accounts = await pxe.getPxe().getRegisteredAccounts(); [owner, _account2, _account3] = accounts; - wallet = await getWallet(owner, pxe); + wallet = await getWallet(owner, pxe.getPxe()); - contract = await deployZKContract(owner, wallet, pxe); + contract = await deployZKContract(owner, wallet, pxe.getPxe()); contractAddress = contract.address; }, 60000); @@ -49,7 +47,7 @@ describe('ZK Contract Tests', () => { contract.artifact, 'getPublicKey', [owner.address.toField()], - pxe, + pxe.getPxe(), owner, ); expect(callTxReceipt.status).toBe(TxStatus.MINED); diff --git a/boxes/blank/tsconfig.json b/boxes/blank/tsconfig.json index acc585a3b0e..3b4c57677db 100644 --- a/boxes/blank/tsconfig.json +++ b/boxes/blank/tsconfig.json @@ -1,6 +1,5 @@ { "compilerOptions": { - "rootDir": "src", "outDir": "dest", "tsBuildInfoFile": ".tsbuildinfo", "target": "es2020", @@ -21,5 +20,5 @@ "skipLibCheck": true, "jsx": "react-jsx" }, - "include": ["src", "src/contracts/target/*.json"] + "include": ["src", "tests", "src/contracts/target/*.json", "artifacts/*"] } diff --git a/boxes/yarn.lock b/boxes/yarn.lock index 5719e430a8d..fd941bea4da 100644 --- a/boxes/yarn.lock +++ b/boxes/yarn.lock @@ -96,7 +96,6 @@ __metadata: "@aztec/aztec-ui": "npm:^0.1.14" "@aztec/aztec.js": "npm:^0.16.9" "@types/jest": "npm:^29.5.0" - "@types/mocha": "npm:^10.0.3" "@types/node": "npm:^20.5.9" "@types/react": "npm:^18.2.15" "@types/react-dom": "npm:^18.2.7" @@ -255,7 +254,6 @@ __metadata: "@aztec/foundation": "workspace:^" eslint: "npm:^8.35.0" lodash.chunk: "npm:^4.2.0" - lodash.times: "npm:^4.3.2" tslib: "npm:^2.4.0" languageName: node linkType: soft @@ -302,6 +300,7 @@ __metadata: resolution: "@aztec/types@portal:../yarn-project/types::locator=%40aztec%2Fboxes%40workspace%3A." dependencies: "@aztec/ethereum": "workspace:^" + "@aztec/foundation": "workspace:^" languageName: node linkType: soft @@ -323,25 +322,25 @@ __metadata: linkType: hard "@babel/core@npm:^7.11.6, @babel/core@npm:^7.12.3": - version: 7.23.6 - resolution: "@babel/core@npm:7.23.6" + version: 7.23.7 + resolution: "@babel/core@npm:7.23.7" dependencies: "@ampproject/remapping": "npm:^2.2.0" "@babel/code-frame": "npm:^7.23.5" "@babel/generator": "npm:^7.23.6" "@babel/helper-compilation-targets": "npm:^7.23.6" "@babel/helper-module-transforms": "npm:^7.23.3" - "@babel/helpers": "npm:^7.23.6" + "@babel/helpers": "npm:^7.23.7" "@babel/parser": "npm:^7.23.6" "@babel/template": "npm:^7.22.15" - "@babel/traverse": "npm:^7.23.6" + "@babel/traverse": "npm:^7.23.7" "@babel/types": "npm:^7.23.6" convert-source-map: "npm:^2.0.0" debug: "npm:^4.1.0" gensync: "npm:^1.0.0-beta.2" json5: "npm:^2.2.3" semver: "npm:^6.3.1" - checksum: a02bae7d916029b70706dc301535e1b31e5d216f55d4ee6f64a15825c6b69ee2c14c52a213d1497ec414e925ed4e9d897d41fb0d75df9fea28ed2c0008790e31 + checksum: 38c9934973d384ed83369712978453eac91dc3f22167404dbdb272b64f602e74728a6f37012c53ee57e521b8ae2da60097f050497d9b6a212d28b59cdfb2cd1d languageName: node linkType: hard @@ -466,14 +465,14 @@ __metadata: languageName: node linkType: hard -"@babel/helpers@npm:^7.23.6": - version: 7.23.6 - resolution: "@babel/helpers@npm:7.23.6" +"@babel/helpers@npm:^7.23.7": + version: 7.23.8 + resolution: "@babel/helpers@npm:7.23.8" dependencies: "@babel/template": "npm:^7.22.15" - "@babel/traverse": "npm:^7.23.6" + "@babel/traverse": "npm:^7.23.7" "@babel/types": "npm:^7.23.6" - checksum: df1cf6607676ad36f52f652ec03536f2732d70aef5e76dba5c964e34d49f3c2d3dcf9fb3740db359f53071d74b64606a833d5ba156f79f437f71bfe06e2e7e19 + checksum: d9fce49278a31aaa017a40c1fcdaa450999c49e33582cce8138058c58b1acbe3a2d2488f010f28e91dedf0d35795ea32f0ee18745bbb6c7f54052ae0fd7e6a3f languageName: node linkType: hard @@ -652,11 +651,11 @@ __metadata: linkType: hard "@babel/runtime@npm:^7.12.5": - version: 7.23.6 - resolution: "@babel/runtime@npm:7.23.6" + version: 7.23.8 + resolution: "@babel/runtime@npm:7.23.8" dependencies: regenerator-runtime: "npm:^0.14.0" - checksum: d886954e985ef8e421222f7a2848884d96a752e0020d3078b920dd104e672fdf23bcc6f51a44313a048796319f1ac9d09c2c88ec8cbb4e1f09174bcd3335b9ff + checksum: ba5e8fbb32ef04f6cab5e89c54a0497c2fde7b730595cc1af93496270314f13ff2c6a9360fdb2f0bdd4d6b376752ce3cf85642bd6b876969a6a62954934c2df8 languageName: node linkType: hard @@ -671,9 +670,9 @@ __metadata: languageName: node linkType: hard -"@babel/traverse@npm:^7.23.6": - version: 7.23.6 - resolution: "@babel/traverse@npm:7.23.6" +"@babel/traverse@npm:^7.23.7": + version: 7.23.7 + resolution: "@babel/traverse@npm:7.23.7" dependencies: "@babel/code-frame": "npm:^7.23.5" "@babel/generator": "npm:^7.23.6" @@ -685,7 +684,7 @@ __metadata: "@babel/types": "npm:^7.23.6" debug: "npm:^4.3.1" globals: "npm:^11.1.0" - checksum: 5b4ebb94a00a7e1daf111e4b0b45a7998d5b7598637a14e75e855e88cc1b702789e09a958726b5d599a003be1e9032dbdfde4b88ea6061332228738950d5582d + checksum: e32fceb4249beec2bde83968ddffe17444221c1ee5cd18c543a2feaf94e3ca83f2a4dfbc2dcca87cf226e0105973e0fe3717063a21e982a9de9945615ab3f3f5 languageName: node linkType: hard @@ -1241,13 +1240,13 @@ __metadata: linkType: hard "@humanwhocodes/config-array@npm:^0.11.13": - version: 0.11.13 - resolution: "@humanwhocodes/config-array@npm:0.11.13" + version: 0.11.14 + resolution: "@humanwhocodes/config-array@npm:0.11.14" dependencies: - "@humanwhocodes/object-schema": "npm:^2.0.1" - debug: "npm:^4.1.1" + "@humanwhocodes/object-schema": "npm:^2.0.2" + debug: "npm:^4.3.1" minimatch: "npm:^3.0.5" - checksum: d76ca802d853366094d0e98ff0d0994117fc8eff96649cd357b15e469e428228f597cd2e929d54ab089051684949955f16ee905bb19f7b2f0446fb377157be7a + checksum: 66f725b4ee5fdd8322c737cb5013e19fac72d4d69c8bf4b7feb192fcb83442b035b92186f8e9497c220e58b2d51a080f28a73f7899bc1ab288c3be172c467541 languageName: node linkType: hard @@ -1258,10 +1257,10 @@ __metadata: languageName: node linkType: hard -"@humanwhocodes/object-schema@npm:^2.0.1": - version: 2.0.1 - resolution: "@humanwhocodes/object-schema@npm:2.0.1" - checksum: 9dba24e59fdb4041829d92b693aacb778add3b6f612aaa9c0774f3b650c11a378cc64f042a59da85c11dae33df456580a3c36837b953541aed6ff94294f97fac +"@humanwhocodes/object-schema@npm:^2.0.2": + version: 2.0.2 + resolution: "@humanwhocodes/object-schema@npm:2.0.2" + checksum: 6fd83dc320231d71c4541d0244051df61f301817e9f9da9fd4cb7e44ec8aacbde5958c1665b0c419401ab935114fdf532a6ad5d4e7294b1af2f347dd91a6983f languageName: node linkType: hard @@ -1588,13 +1587,13 @@ __metadata: languageName: node linkType: hard -"@jridgewell/trace-mapping@npm:^0.3.12, @jridgewell/trace-mapping@npm:^0.3.17, @jridgewell/trace-mapping@npm:^0.3.18, @jridgewell/trace-mapping@npm:^0.3.9": - version: 0.3.20 - resolution: "@jridgewell/trace-mapping@npm:0.3.20" +"@jridgewell/trace-mapping@npm:^0.3.12, @jridgewell/trace-mapping@npm:^0.3.17, @jridgewell/trace-mapping@npm:^0.3.18, @jridgewell/trace-mapping@npm:^0.3.20, @jridgewell/trace-mapping@npm:^0.3.9": + version: 0.3.22 + resolution: "@jridgewell/trace-mapping@npm:0.3.22" dependencies: "@jridgewell/resolve-uri": "npm:^3.1.0" "@jridgewell/sourcemap-codec": "npm:^1.4.14" - checksum: 0ea0b2675cf513ec44dc25605616a3c9b808b9832e74b5b63c44260d66b58558bba65764f81928fc1033ead911f8718dca1134049c3e7a93937faf436671df31 + checksum: 18cf19f88e2792c1c91515f2b629aae05f3cdbb2e60c3886e16e80725234ce26dd10144c4981c05d9366e7094498c0b4fe5c1a89f4a730d7376a4ba4af448149 languageName: node linkType: hard @@ -1680,13 +1679,13 @@ __metadata: linkType: hard "@metamask/json-rpc-engine@npm:^7.0.0": - version: 7.3.0 - resolution: "@metamask/json-rpc-engine@npm:7.3.0" + version: 7.3.1 + resolution: "@metamask/json-rpc-engine@npm:7.3.1" dependencies: "@metamask/rpc-errors": "npm:^6.1.0" "@metamask/safe-event-emitter": "npm:^3.0.0" "@metamask/utils": "npm:^8.2.0" - checksum: fcc70d15854dda72d5b353e8157b3f1d749fbd7c09d23bdd022c92fceb1101a2f83623cd9032c36d41fdfaa400131574c587bef2c7d7ca95bb29976b0f12faf6 + checksum: a34d9ef7a2277ce03bbaf537e08d00ae3b85928e67aff53e54ba116ac6bd1e12969808a224ccbd8d77ea482656a15ae37eb9741b34bcb69322a93c2a306cb89f languageName: node linkType: hard @@ -1728,8 +1727,8 @@ __metadata: linkType: hard "@metamask/utils@npm:^8.1.0, @metamask/utils@npm:^8.2.0": - version: 8.2.1 - resolution: "@metamask/utils@npm:8.2.1" + version: 8.3.0 + resolution: "@metamask/utils@npm:8.3.0" dependencies: "@ethereumjs/tx": "npm:^4.2.0" "@noble/hashes": "npm:^1.3.1" @@ -1739,54 +1738,54 @@ __metadata: pony-cause: "npm:^2.1.10" semver: "npm:^7.5.4" superstruct: "npm:^1.0.3" - checksum: 7f6f02138f69f544dc7e27b52af995a630622c7e884bdf94f8c8ee78232a659a128c77088659f7ff9b030839fb52b14cc1655bdac85688ca435b46b5ecdbb844 + checksum: 1f33ad3d61528be8e39a269ad9082e36827675c9df4fa84cf57e7468be0da405690a062df3a960da39e6175c9e4aa87a98ede40ef3cc701f8773c143b1121c6e languageName: node linkType: hard -"@motionone/animation@npm:^10.15.1, @motionone/animation@npm:^10.16.3": - version: 10.16.3 - resolution: "@motionone/animation@npm:10.16.3" +"@motionone/animation@npm:^10.15.1, @motionone/animation@npm:^10.17.0": + version: 10.17.0 + resolution: "@motionone/animation@npm:10.17.0" dependencies: - "@motionone/easing": "npm:^10.16.3" - "@motionone/types": "npm:^10.16.3" - "@motionone/utils": "npm:^10.16.3" + "@motionone/easing": "npm:^10.17.0" + "@motionone/types": "npm:^10.17.0" + "@motionone/utils": "npm:^10.17.0" tslib: "npm:^2.3.1" - checksum: c1bb7a03acc9c09647321a4653bf53878ea05ce91305507cb4000d75641dcad85faa8696ef12d0c28fa52d4b3708bc7ae34334c95ef532567a26082f0176ea4a + checksum: 51873c9532ccb9f2b8475e871ba3eeebff2171bb2bd88e76bcf1fdb5bc1a7150f319c148063d17c16597038a8993c68033d918cc73a9fec40bb1f78ee8a52764 languageName: node linkType: hard "@motionone/dom@npm:^10.16.2, @motionone/dom@npm:^10.16.4": - version: 10.16.4 - resolution: "@motionone/dom@npm:10.16.4" + version: 10.17.0 + resolution: "@motionone/dom@npm:10.17.0" dependencies: - "@motionone/animation": "npm:^10.16.3" - "@motionone/generators": "npm:^10.16.4" - "@motionone/types": "npm:^10.16.3" - "@motionone/utils": "npm:^10.16.3" + "@motionone/animation": "npm:^10.17.0" + "@motionone/generators": "npm:^10.17.0" + "@motionone/types": "npm:^10.17.0" + "@motionone/utils": "npm:^10.17.0" hey-listen: "npm:^1.0.8" tslib: "npm:^2.3.1" - checksum: 1efaa29a18471c18dbe7f849a7c83b12c27edf85209cb366856720e051870302c27567f5eab2a1aef3aa7ae1438c6fbc3a7e686077f5ed4e173e4cca8d22e0d5 + checksum: bca972f6d60aa1462993ea1b36f0ba702c8c4644b602e460834d3a4ce88f3c7c1dcfec8810c6598e9cf502ad6d3af718a889ab1661c97ec162979fe9a0ff36ab languageName: node linkType: hard -"@motionone/easing@npm:^10.16.3": - version: 10.16.3 - resolution: "@motionone/easing@npm:10.16.3" +"@motionone/easing@npm:^10.17.0": + version: 10.17.0 + resolution: "@motionone/easing@npm:10.17.0" dependencies: - "@motionone/utils": "npm:^10.16.3" + "@motionone/utils": "npm:^10.17.0" tslib: "npm:^2.3.1" - checksum: df98a643f0b2955afd16b78063899d050b22cfcf3db1bb86ecdbde831614f24c41143d5d887bc287f6de979baa20a00e8e1dca39ef7b2dfb67c0ec1b1ca0bcaa + checksum: 9e82cf970cb754c44bc8226fd660c4a546aa06bb6eabb0b8be3a1466fc07920da13195e76d09d81704d059411584ba66de3bfc0192acc585a6fe352bf3e3fe22 languageName: node linkType: hard -"@motionone/generators@npm:^10.16.4": - version: 10.16.4 - resolution: "@motionone/generators@npm:10.16.4" +"@motionone/generators@npm:^10.17.0": + version: 10.17.0 + resolution: "@motionone/generators@npm:10.17.0" dependencies: - "@motionone/types": "npm:^10.16.3" - "@motionone/utils": "npm:^10.16.3" + "@motionone/types": "npm:^10.17.0" + "@motionone/utils": "npm:^10.17.0" tslib: "npm:^2.3.1" - checksum: cef71d1236a625b3579791d480ebd1875bec2a62e249771eb2af883981074016cc6f2ef112c2bf27f93d05d19830893f3f486944cd68d2fbf35a990c41729152 + checksum: b1a951d7c20474b34d31cb199907a3ee4ae5074ff2ab49e18e54a63f5eacba6662e179a76f9b64ed7eaac5922ae934eaeca567f2a48c5a1a3ebf59cc5a43fc9f languageName: node linkType: hard @@ -1800,21 +1799,21 @@ __metadata: languageName: node linkType: hard -"@motionone/types@npm:^10.15.1, @motionone/types@npm:^10.16.3": - version: 10.16.3 - resolution: "@motionone/types@npm:10.16.3" - checksum: a792acd8bacd7949c29fd47fda1d3d7919b86ab209499a374a1f3c85f57a92d16f7a05f94edc6d46831c55180da2ff5e1193fa538bcb76e0ff38a24e25da2e87 +"@motionone/types@npm:^10.15.1, @motionone/types@npm:^10.17.0": + version: 10.17.0 + resolution: "@motionone/types@npm:10.17.0" + checksum: 9c91d887b368c93e860c1ff4b245d60d33966ec5bd2525ce91c4e2904c223f79333013fe06140feeab23f27ad9e8546a151e8357c5dc5218c5b658486bac3f82 languageName: node linkType: hard -"@motionone/utils@npm:^10.15.1, @motionone/utils@npm:^10.16.3": - version: 10.16.3 - resolution: "@motionone/utils@npm:10.16.3" +"@motionone/utils@npm:^10.15.1, @motionone/utils@npm:^10.17.0": + version: 10.17.0 + resolution: "@motionone/utils@npm:10.17.0" dependencies: - "@motionone/types": "npm:^10.16.3" + "@motionone/types": "npm:^10.17.0" hey-listen: "npm:^1.0.8" tslib: "npm:^2.3.1" - checksum: c5a1cce9bf5d1e8c5051a4636bd6a7030bf67f5662a94a8ec1524a72de3baca3f4c59e46cee9a41b111806fdd2956256c65c7e99b7de260803f2e44840bbae11 + checksum: a90dc772245fa379d522d752dcbe80b02b1fcb17da6a3f3ebc725ac0e99b7847d39f1f4a29f10cbf5e8b6157766191ba03e96c75b0fa8378e3a1c4cc8cad728a languageName: node linkType: hard @@ -1965,65 +1964,65 @@ __metadata: languageName: node linkType: hard -"@parcel/watcher-android-arm64@npm:2.3.0": - version: 2.3.0 - resolution: "@parcel/watcher-android-arm64@npm:2.3.0" +"@parcel/watcher-android-arm64@npm:2.4.0": + version: 2.4.0 + resolution: "@parcel/watcher-android-arm64@npm:2.4.0" conditions: os=android & cpu=arm64 languageName: node linkType: hard -"@parcel/watcher-darwin-arm64@npm:2.3.0": - version: 2.3.0 - resolution: "@parcel/watcher-darwin-arm64@npm:2.3.0" +"@parcel/watcher-darwin-arm64@npm:2.4.0": + version: 2.4.0 + resolution: "@parcel/watcher-darwin-arm64@npm:2.4.0" conditions: os=darwin & cpu=arm64 languageName: node linkType: hard -"@parcel/watcher-darwin-x64@npm:2.3.0": - version: 2.3.0 - resolution: "@parcel/watcher-darwin-x64@npm:2.3.0" +"@parcel/watcher-darwin-x64@npm:2.4.0": + version: 2.4.0 + resolution: "@parcel/watcher-darwin-x64@npm:2.4.0" conditions: os=darwin & cpu=x64 languageName: node linkType: hard -"@parcel/watcher-freebsd-x64@npm:2.3.0": - version: 2.3.0 - resolution: "@parcel/watcher-freebsd-x64@npm:2.3.0" +"@parcel/watcher-freebsd-x64@npm:2.4.0": + version: 2.4.0 + resolution: "@parcel/watcher-freebsd-x64@npm:2.4.0" conditions: os=freebsd & cpu=x64 languageName: node linkType: hard -"@parcel/watcher-linux-arm-glibc@npm:2.3.0": - version: 2.3.0 - resolution: "@parcel/watcher-linux-arm-glibc@npm:2.3.0" +"@parcel/watcher-linux-arm-glibc@npm:2.4.0": + version: 2.4.0 + resolution: "@parcel/watcher-linux-arm-glibc@npm:2.4.0" conditions: os=linux & cpu=arm & libc=glibc languageName: node linkType: hard -"@parcel/watcher-linux-arm64-glibc@npm:2.3.0": - version: 2.3.0 - resolution: "@parcel/watcher-linux-arm64-glibc@npm:2.3.0" +"@parcel/watcher-linux-arm64-glibc@npm:2.4.0": + version: 2.4.0 + resolution: "@parcel/watcher-linux-arm64-glibc@npm:2.4.0" conditions: os=linux & cpu=arm64 & libc=glibc languageName: node linkType: hard -"@parcel/watcher-linux-arm64-musl@npm:2.3.0": - version: 2.3.0 - resolution: "@parcel/watcher-linux-arm64-musl@npm:2.3.0" +"@parcel/watcher-linux-arm64-musl@npm:2.4.0": + version: 2.4.0 + resolution: "@parcel/watcher-linux-arm64-musl@npm:2.4.0" conditions: os=linux & cpu=arm64 & libc=musl languageName: node linkType: hard -"@parcel/watcher-linux-x64-glibc@npm:2.3.0": - version: 2.3.0 - resolution: "@parcel/watcher-linux-x64-glibc@npm:2.3.0" +"@parcel/watcher-linux-x64-glibc@npm:2.4.0": + version: 2.4.0 + resolution: "@parcel/watcher-linux-x64-glibc@npm:2.4.0" conditions: os=linux & cpu=x64 & libc=glibc languageName: node linkType: hard -"@parcel/watcher-linux-x64-musl@npm:2.3.0": - version: 2.3.0 - resolution: "@parcel/watcher-linux-x64-musl@npm:2.3.0" +"@parcel/watcher-linux-x64-musl@npm:2.4.0": + version: 2.4.0 + resolution: "@parcel/watcher-linux-x64-musl@npm:2.4.0" conditions: os=linux & cpu=x64 & libc=musl languageName: node linkType: hard @@ -2039,43 +2038,43 @@ __metadata: languageName: node linkType: hard -"@parcel/watcher-win32-arm64@npm:2.3.0": - version: 2.3.0 - resolution: "@parcel/watcher-win32-arm64@npm:2.3.0" +"@parcel/watcher-win32-arm64@npm:2.4.0": + version: 2.4.0 + resolution: "@parcel/watcher-win32-arm64@npm:2.4.0" conditions: os=win32 & cpu=arm64 languageName: node linkType: hard -"@parcel/watcher-win32-ia32@npm:2.3.0": - version: 2.3.0 - resolution: "@parcel/watcher-win32-ia32@npm:2.3.0" +"@parcel/watcher-win32-ia32@npm:2.4.0": + version: 2.4.0 + resolution: "@parcel/watcher-win32-ia32@npm:2.4.0" conditions: os=win32 & cpu=ia32 languageName: node linkType: hard -"@parcel/watcher-win32-x64@npm:2.3.0": - version: 2.3.0 - resolution: "@parcel/watcher-win32-x64@npm:2.3.0" +"@parcel/watcher-win32-x64@npm:2.4.0": + version: 2.4.0 + resolution: "@parcel/watcher-win32-x64@npm:2.4.0" conditions: os=win32 & cpu=x64 languageName: node linkType: hard "@parcel/watcher@npm:^2.3.0": - version: 2.3.0 - resolution: "@parcel/watcher@npm:2.3.0" - dependencies: - "@parcel/watcher-android-arm64": "npm:2.3.0" - "@parcel/watcher-darwin-arm64": "npm:2.3.0" - "@parcel/watcher-darwin-x64": "npm:2.3.0" - "@parcel/watcher-freebsd-x64": "npm:2.3.0" - "@parcel/watcher-linux-arm-glibc": "npm:2.3.0" - "@parcel/watcher-linux-arm64-glibc": "npm:2.3.0" - "@parcel/watcher-linux-arm64-musl": "npm:2.3.0" - "@parcel/watcher-linux-x64-glibc": "npm:2.3.0" - "@parcel/watcher-linux-x64-musl": "npm:2.3.0" - "@parcel/watcher-win32-arm64": "npm:2.3.0" - "@parcel/watcher-win32-ia32": "npm:2.3.0" - "@parcel/watcher-win32-x64": "npm:2.3.0" + version: 2.4.0 + resolution: "@parcel/watcher@npm:2.4.0" + dependencies: + "@parcel/watcher-android-arm64": "npm:2.4.0" + "@parcel/watcher-darwin-arm64": "npm:2.4.0" + "@parcel/watcher-darwin-x64": "npm:2.4.0" + "@parcel/watcher-freebsd-x64": "npm:2.4.0" + "@parcel/watcher-linux-arm-glibc": "npm:2.4.0" + "@parcel/watcher-linux-arm64-glibc": "npm:2.4.0" + "@parcel/watcher-linux-arm64-musl": "npm:2.4.0" + "@parcel/watcher-linux-x64-glibc": "npm:2.4.0" + "@parcel/watcher-linux-x64-musl": "npm:2.4.0" + "@parcel/watcher-win32-arm64": "npm:2.4.0" + "@parcel/watcher-win32-ia32": "npm:2.4.0" + "@parcel/watcher-win32-x64": "npm:2.4.0" detect-libc: "npm:^1.0.3" is-glob: "npm:^4.0.3" micromatch: "npm:^4.0.5" @@ -2106,7 +2105,7 @@ __metadata: optional: true "@parcel/watcher-win32-x64": optional: true - checksum: f223a6d5c56071c5f466725b93a83d0066ef01837fdae12ce86c9127586ad8138fe52f18de18c2752e3d8ca350b582ea4b55d16a51bd0584428d20698ace17a0 + checksum: f8a7103d8402dceaeed6e7ceef5592ceed6c3ceed7bd747590dbf7b51ca56fd4cb26a6322d1952b4bca52acb41e9d4a13468035b371ef5d264230c4286bf4d0a languageName: node linkType: hard @@ -2124,17 +2123,10 @@ __metadata: languageName: node linkType: hard -"@pkgr/utils@npm:^2.4.2": - version: 2.4.2 - resolution: "@pkgr/utils@npm:2.4.2" - dependencies: - cross-spawn: "npm:^7.0.3" - fast-glob: "npm:^3.3.0" - is-glob: "npm:^4.0.3" - open: "npm:^9.1.0" - picocolors: "npm:^1.0.0" - tslib: "npm:^2.6.0" - checksum: 7c3e68f6405a1d4c51f418d8d580e71d7bade2683d5db07e8413d8e57f7e389047eda44a2341f77a1b3085895fca7676a9d45e8812a58312524f8c4c65d501be +"@pkgr/core@npm:^0.1.0": + version: 0.1.1 + resolution: "@pkgr/core@npm:0.1.1" + checksum: 3f7536bc7f57320ab2cf96f8973664bef624710c403357429fbf680a5c3b4843c1dbd389bb43daa6b1f6f1f007bb082f5abcb76bb2b5dc9f421647743b71d3d8 languageName: node linkType: hard @@ -2157,10 +2149,10 @@ __metadata: languageName: node linkType: hard -"@remix-run/router@npm:1.14.1": - version: 1.14.1 - resolution: "@remix-run/router@npm:1.14.1" - checksum: aa179e96fa7da5bdb86e6887219613cbe47b67d6595564b4f8fe7e80f8980f9c76e120524f0864aa8af46ac0f1a06bcdb4d20058d770c9199e5af599f772c0a9 +"@remix-run/router@npm:1.14.2": + version: 1.14.2 + resolution: "@remix-run/router@npm:1.14.2" + checksum: 163d4a8ea3e25a7a7e3015f274e54b8043eddaaa92da220cad2893eb0fcbb649f617152c6d74680a4b55c0f319944ff1b362e87f814bb73be54f8d687ee730d6 languageName: node linkType: hard @@ -2195,9 +2187,9 @@ __metadata: linkType: hard "@safe-global/safe-gateway-typescript-sdk@npm:^3.5.3": - version: 3.13.3 - resolution: "@safe-global/safe-gateway-typescript-sdk@npm:3.13.3" - checksum: 379e0deb6b938151434b451c627c61658567a4adbfbf8b6323b203fbe5be7082bcaabd31b3e269c6b6a6f65662635341c79ef2a20a828fd7d2e0daac83e81b17 + version: 3.14.0 + resolution: "@safe-global/safe-gateway-typescript-sdk@npm:3.14.0" + checksum: 9c4d8d79ee44aec2a635f612f433ab1ad3e3571b8e270bf61054319e03e82aeb83036ef6754e356bb99eab5a21bf2f9fb53c74ff8d3fa74e1a90ec808c69991b languageName: node linkType: hard @@ -2248,11 +2240,11 @@ __metadata: linkType: hard "@sinonjs/commons@npm:^3.0.0": - version: 3.0.0 - resolution: "@sinonjs/commons@npm:3.0.0" + version: 3.0.1 + resolution: "@sinonjs/commons@npm:3.0.1" dependencies: type-detect: "npm:4.0.8" - checksum: 1df9cd257942f4e4960dfb9fd339d9e97b6a3da135f3d5b8646562918e863809cb8e00268535f4f4723535d2097881c8fc03d545c414d8555183376cfc54ee84 + checksum: 1227a7b5bd6c6f9584274db996d7f8cee2c8c350534b9d0141fc662eaf1f292ea0ae3ed19e5e5271c8fd390d27e492ca2803acd31a1978be2cdc6be0da711403 languageName: node linkType: hard @@ -2565,11 +2557,11 @@ __metadata: linkType: hard "@types/babel__traverse@npm:*, @types/babel__traverse@npm:^7.0.6": - version: 7.20.4 - resolution: "@types/babel__traverse@npm:7.20.4" + version: 7.20.5 + resolution: "@types/babel__traverse@npm:7.20.5" dependencies: "@babel/types": "npm:^7.20.7" - checksum: e76cb4974c7740fd61311152dc497e7b05c1c46ba554aab875544ab0a7457f343cafcad34ba8fb2ff543ab0e012ef2d3fa0c13f1a4e9a4cd9c4c703c7a2a8d62 + checksum: 033abcb2f4c084ad33e30c3efaad82161240f351e3c71b6154ed289946b33b363696c0fbd42502b68e4582a87413c418321f40eb1ea863e34fe525641345e05b languageName: node linkType: hard @@ -2631,12 +2623,12 @@ __metadata: linkType: hard "@types/eslint@npm:*": - version: 8.44.9 - resolution: "@types/eslint@npm:8.44.9" + version: 8.56.2 + resolution: "@types/eslint@npm:8.56.2" dependencies: "@types/estree": "npm:*" "@types/json-schema": "npm:*" - checksum: e9da4e4c7b7c9014b17d40007e36f02f3b5dd55c43bb05928b52dd9c19f2a8fb7971a851a4e7a11625c3c69da286c5baf55de2f8bb900b1a4cfb5145a4491b37 + checksum: e33ca87a30a9454ba9943e1270ac759996f5fe598a1c1afbaec1d1e7346a339e20bf2a9d81f177067116bbaa6cfa4f748993cb338f57978ae862ad38ffae56fe languageName: node linkType: hard @@ -2791,20 +2783,20 @@ __metadata: linkType: hard "@types/node-forge@npm:^1.3.0": - version: 1.3.10 - resolution: "@types/node-forge@npm:1.3.10" + version: 1.3.11 + resolution: "@types/node-forge@npm:1.3.11" dependencies: "@types/node": "npm:*" - checksum: b190e93e36e3bf5881e099df930645bbeb963c1cabb110948f90e11f5f59a2514d5632e6bd1101dfb839725eab25a8e2eba4a2b1b7551f12bc43302863e050ae + checksum: 3d7d23ca0ba38ac0cf74028393bd70f31169ab9aba43f21deb787840170d307d662644bac07287495effe2812ddd7ac8a14dbd43f16c2936bbb06312e96fc3b9 languageName: node linkType: hard "@types/node@npm:*, @types/node@npm:^20.10.4, @types/node@npm:^20.5.9": - version: 20.10.5 - resolution: "@types/node@npm:20.10.5" + version: 20.11.5 + resolution: "@types/node@npm:20.11.5" dependencies: undici-types: "npm:~5.26.4" - checksum: be30609aae0bfe492097815f166ccc07f465220cb604647fa4e5ec05a1d16c012a41b82b5f11ecfe2485cbb479d4d20384b95b809ca0bcff6d94d5bbafa645bb + checksum: 6d18cec852f5cfbed3ec42b5c01c026e7a3f9da540d6e3d6738d4cee9979fb308cf27b6df7ba40a6553e7bc82e678f0ef53ba6e6ad52e5b86bd97b7783c2a42c languageName: node linkType: hard @@ -2823,9 +2815,9 @@ __metadata: linkType: hard "@types/qs@npm:*": - version: 6.9.10 - resolution: "@types/qs@npm:6.9.10" - checksum: 6be12e5f062d1b41eb037d59bf9cb65bc9410cedd5e6da832dfd7c8e2b3f4c91e81c9b90b51811140770e5052c6c4e8361181bd9437ddcd4515dc128b7c00353 + version: 6.9.11 + resolution: "@types/qs@npm:6.9.11" + checksum: 657a50f05b694d6fd3916d24177cfa0f3b8b87d9deff4ffa4dddcb0b03583ebf7c47b424b8de400270fb9a5cc1e9cf790dd82c833c6935305851e7da8ede3ff5 languageName: node linkType: hard @@ -2846,13 +2838,13 @@ __metadata: linkType: hard "@types/react@npm:*, @types/react@npm:^18.2.15": - version: 18.2.45 - resolution: "@types/react@npm:18.2.45" + version: 18.2.48 + resolution: "@types/react@npm:18.2.48" dependencies: "@types/prop-types": "npm:*" "@types/scheduler": "npm:*" csstype: "npm:^3.0.2" - checksum: 4cc650c47ffb88baac29fb7a74e842e4af4a55f437086ef70250fdc75f0a5f2fcf8adc272d05ab2e00b1de6e14613296881271caee037dadf9130fdeb498c59e + checksum: 7e89f18ea2928b1638f564b156d692894dcb9352a7e0a807873c97e858abe1f23dbd165a25dd088a991344e973fdeef88ba5724bfb64504b74072cbc9c220c3a languageName: node linkType: hard @@ -2956,14 +2948,14 @@ __metadata: linkType: hard "@typescript-eslint/eslint-plugin@npm:^6.0.0": - version: 6.15.0 - resolution: "@typescript-eslint/eslint-plugin@npm:6.15.0" + version: 6.19.1 + resolution: "@typescript-eslint/eslint-plugin@npm:6.19.1" dependencies: "@eslint-community/regexpp": "npm:^4.5.1" - "@typescript-eslint/scope-manager": "npm:6.15.0" - "@typescript-eslint/type-utils": "npm:6.15.0" - "@typescript-eslint/utils": "npm:6.15.0" - "@typescript-eslint/visitor-keys": "npm:6.15.0" + "@typescript-eslint/scope-manager": "npm:6.19.1" + "@typescript-eslint/type-utils": "npm:6.19.1" + "@typescript-eslint/utils": "npm:6.19.1" + "@typescript-eslint/visitor-keys": "npm:6.19.1" debug: "npm:^4.3.4" graphemer: "npm:^1.4.0" ignore: "npm:^5.2.4" @@ -2976,44 +2968,44 @@ __metadata: peerDependenciesMeta: typescript: optional: true - checksum: 78054afb0d4ab12d82db7a9cb005dfa2be42962341728abf4a81802e1f4c0f5b23de4870287f4b7e32aa4a4bc900bbc218f2d4d0c02aa77452e8e8e0b71fe3de + checksum: 01f1d643219b51bfad76734c6eb19a480309f0e66877ddc00bca89368e5aee3eb907e366977a8d11094c49e807773f5cfba306c861cd690d70044a7925173823 languageName: node linkType: hard "@typescript-eslint/parser@npm:^6.0.0": - version: 6.15.0 - resolution: "@typescript-eslint/parser@npm:6.15.0" + version: 6.19.1 + resolution: "@typescript-eslint/parser@npm:6.19.1" dependencies: - "@typescript-eslint/scope-manager": "npm:6.15.0" - "@typescript-eslint/types": "npm:6.15.0" - "@typescript-eslint/typescript-estree": "npm:6.15.0" - "@typescript-eslint/visitor-keys": "npm:6.15.0" + "@typescript-eslint/scope-manager": "npm:6.19.1" + "@typescript-eslint/types": "npm:6.19.1" + "@typescript-eslint/typescript-estree": "npm:6.19.1" + "@typescript-eslint/visitor-keys": "npm:6.19.1" debug: "npm:^4.3.4" peerDependencies: eslint: ^7.0.0 || ^8.0.0 peerDependenciesMeta: typescript: optional: true - checksum: e7f265fd4abd3bc49fa5b304cd4b9c22801ac5a9da4ee342bbab0c117d629ac4aad6998555b61a8c5a0b279c443a44ae99f16669e24e3ef17ccec20c8b7019e7 + checksum: 442e860fbc4786fe999205528cc74b31d933008e170a707ddaec0c9e2c374f62c36c8d05d3dd446c9ceb802f2b403806d72c78ffd97867cf1672028b754b6262 languageName: node linkType: hard -"@typescript-eslint/scope-manager@npm:6.15.0": - version: 6.15.0 - resolution: "@typescript-eslint/scope-manager@npm:6.15.0" +"@typescript-eslint/scope-manager@npm:6.19.1": + version: 6.19.1 + resolution: "@typescript-eslint/scope-manager@npm:6.19.1" dependencies: - "@typescript-eslint/types": "npm:6.15.0" - "@typescript-eslint/visitor-keys": "npm:6.15.0" - checksum: 3428d99de440f227cbc2afb44cdcb25e44c4b49c5f490392f83e21d2048210a6ec2f2f68133376c842034f5b5ba4ec9721da7caa18e631e23b57e20927b5b6f0 + "@typescript-eslint/types": "npm:6.19.1" + "@typescript-eslint/visitor-keys": "npm:6.19.1" + checksum: a81315b4a2888343d3be781fe8d6b4c229c656d7bf1bd74bc44a89bba96bb6a10a0319d301f24ca91adb898374eaadbd38979e6567ac9085b5d7076163794281 languageName: node linkType: hard -"@typescript-eslint/type-utils@npm:6.15.0": - version: 6.15.0 - resolution: "@typescript-eslint/type-utils@npm:6.15.0" +"@typescript-eslint/type-utils@npm:6.19.1": + version: 6.19.1 + resolution: "@typescript-eslint/type-utils@npm:6.19.1" dependencies: - "@typescript-eslint/typescript-estree": "npm:6.15.0" - "@typescript-eslint/utils": "npm:6.15.0" + "@typescript-eslint/typescript-estree": "npm:6.19.1" + "@typescript-eslint/utils": "npm:6.19.1" debug: "npm:^4.3.4" ts-api-utils: "npm:^1.0.1" peerDependencies: @@ -3021,59 +3013,60 @@ __metadata: peerDependenciesMeta: typescript: optional: true - checksum: 32cb531a4b5e0ccd431cba553ec73b87d4453b48af288a33e359ba4f5278126390d82799b61d3f0fbf135cfde1ac6c2275c2cf37a676e8a2a2811e774e660f16 + checksum: 78c185c64a8c92d7b5f2132ef4880b974a2e07e9ae7913ad53e327972af540a8a8bf75bc319c8aaa82445615e2680f3c85736ee67aa174a5ba91798fe5068f95 languageName: node linkType: hard -"@typescript-eslint/types@npm:6.15.0": - version: 6.15.0 - resolution: "@typescript-eslint/types@npm:6.15.0" - checksum: 6e33529ea301c8c4b8c1f589dadd5d2a66c1b24ec87a577524fbc996d4c7b65d4f4fdfa4a3937b691efee6a10a6b16f7bfcabe98a15e0fc0c0c57aa0d80dcc25 +"@typescript-eslint/types@npm:6.19.1": + version: 6.19.1 + resolution: "@typescript-eslint/types@npm:6.19.1" + checksum: b8f75df157ca383e5bd6c07276fbeed6ff775e1354260a1653777749c0d71626fb29be5d36c9570e2c5cfaa5db62deaae20aa4be8a2d7d753782ab66d88e007f languageName: node linkType: hard -"@typescript-eslint/typescript-estree@npm:6.15.0": - version: 6.15.0 - resolution: "@typescript-eslint/typescript-estree@npm:6.15.0" +"@typescript-eslint/typescript-estree@npm:6.19.1": + version: 6.19.1 + resolution: "@typescript-eslint/typescript-estree@npm:6.19.1" dependencies: - "@typescript-eslint/types": "npm:6.15.0" - "@typescript-eslint/visitor-keys": "npm:6.15.0" + "@typescript-eslint/types": "npm:6.19.1" + "@typescript-eslint/visitor-keys": "npm:6.19.1" debug: "npm:^4.3.4" globby: "npm:^11.1.0" is-glob: "npm:^4.0.3" + minimatch: "npm:9.0.3" semver: "npm:^7.5.4" ts-api-utils: "npm:^1.0.1" peerDependenciesMeta: typescript: optional: true - checksum: 08955f6e84b8edb855a6769671e85889e52b15b82e00a64f595da867b21ad060e5342787c436d77702b2a1f39d411ac79b81a8d2e2006e9b1886eadb08b626df + checksum: dec16f873084e9eeb1a696dff82c42164e75908221f7868d900ad7b7fcec6fc62a9a7dddb8bc17c78c19bf35f07acee81b3778b20b9735ffdaeee732ecb643d3 languageName: node linkType: hard -"@typescript-eslint/utils@npm:6.15.0": - version: 6.15.0 - resolution: "@typescript-eslint/utils@npm:6.15.0" +"@typescript-eslint/utils@npm:6.19.1": + version: 6.19.1 + resolution: "@typescript-eslint/utils@npm:6.19.1" dependencies: "@eslint-community/eslint-utils": "npm:^4.4.0" "@types/json-schema": "npm:^7.0.12" "@types/semver": "npm:^7.5.0" - "@typescript-eslint/scope-manager": "npm:6.15.0" - "@typescript-eslint/types": "npm:6.15.0" - "@typescript-eslint/typescript-estree": "npm:6.15.0" + "@typescript-eslint/scope-manager": "npm:6.19.1" + "@typescript-eslint/types": "npm:6.19.1" + "@typescript-eslint/typescript-estree": "npm:6.19.1" semver: "npm:^7.5.4" peerDependencies: eslint: ^7.0.0 || ^8.0.0 - checksum: 53519a2027681bdc8f028f9421c65f193f91b5bb1659465fedb8043376c693c2391211f1c01d8ba25bfaa7f7b3a102263d7123f9dfade12032159f4b4490f0fb + checksum: 5fa58a32722e9915bfe8433fda2f46be894352549e8406acc4e29a04a8ddb0ea5988fddda2a3145f8952129a267cb51b666206b30489d2ff36b7911f540f1d57 languageName: node linkType: hard -"@typescript-eslint/visitor-keys@npm:6.15.0": - version: 6.15.0 - resolution: "@typescript-eslint/visitor-keys@npm:6.15.0" +"@typescript-eslint/visitor-keys@npm:6.19.1": + version: 6.19.1 + resolution: "@typescript-eslint/visitor-keys@npm:6.19.1" dependencies: - "@typescript-eslint/types": "npm:6.15.0" + "@typescript-eslint/types": "npm:6.19.1" eslint-visitor-keys: "npm:^3.4.1" - checksum: bf9f71af60bd63d1073900e75c5a0aa6eddd672f6c3ac6092c765d67deb7a0c32d2a5f6f3aee9e95f93a93d58563a76da209bd8487aadafd4d013100ffe38520 + checksum: b0370a9bc6fd8d243aa8b7ccd1657ec2fbd25ceb7b067aac64322f03aa0f64b97444b13b0946f52a53d6bc5edd43e0b447f72160be4a5b72e073c1d3679b6b4c languageName: node linkType: hard @@ -3973,18 +3966,18 @@ __metadata: linkType: hard "acorn-walk@npm:^8.1.1": - version: 8.3.1 - resolution: "acorn-walk@npm:8.3.1" - checksum: a23d2f7c6b6cad617f4c77f14dfeb062a239208d61753e9ba808d916c550add92b39535467d2e6028280761ac4f5a904cc9df21530b84d3f834e3edef74ddde5 + version: 8.3.2 + resolution: "acorn-walk@npm:8.3.2" + checksum: 7e2a8dad5480df7f872569b9dccff2f3da7e65f5353686b1d6032ab9f4ddf6e3a2cb83a9b52cf50b1497fd522154dda92f0abf7153290cc79cd14721ff121e52 languageName: node linkType: hard -"acorn@npm:^8.10.0, acorn@npm:^8.4.1, acorn@npm:^8.7.1, acorn@npm:^8.8.2, acorn@npm:^8.9.0": - version: 8.11.2 - resolution: "acorn@npm:8.11.2" +"acorn@npm:^8.11.3, acorn@npm:^8.4.1, acorn@npm:^8.7.1, acorn@npm:^8.8.2, acorn@npm:^8.9.0": + version: 8.11.3 + resolution: "acorn@npm:8.11.3" bin: acorn: bin/acorn - checksum: a3ed76c761b75ec54b1ec3068fb7f113a182e95aea7f322f65098c2958d232e3d211cb6dac35ff9c647024b63714bc528a26d54a925d1fef2c25585b4c8e4017 + checksum: 3ff155f8812e4a746fee8ecff1f227d527c4c45655bb1fad6347c3cb58e46190598217551b1500f18542d2bbe5c87120cb6927f5a074a59166fbdd9468f0a299 languageName: node linkType: hard @@ -4284,13 +4277,6 @@ __metadata: languageName: node linkType: hard -"array-flatten@npm:^2.1.2": - version: 2.1.2 - resolution: "array-flatten@npm:2.1.2" - checksum: bdc1cee68e41bec9cfc1161408734e2269428ef371445606bce4e6241001e138a94b9a617cc9a5b4b7fe6a3a51e3d5a942646975ce82a2e202ccf3e9b478c82f - languageName: node - linkType: hard - "array-includes@npm:^3.1.7": version: 3.1.7 resolution: "array-includes@npm:3.1.7" @@ -4401,12 +4387,12 @@ __metadata: linkType: hard "autoprefixer@npm:^10.4.15": - version: 10.4.16 - resolution: "autoprefixer@npm:10.4.16" + version: 10.4.17 + resolution: "autoprefixer@npm:10.4.17" dependencies: - browserslist: "npm:^4.21.10" - caniuse-lite: "npm:^1.0.30001538" - fraction.js: "npm:^4.3.6" + browserslist: "npm:^4.22.2" + caniuse-lite: "npm:^1.0.30001578" + fraction.js: "npm:^4.3.7" normalize-range: "npm:^0.1.2" picocolors: "npm:^1.0.0" postcss-value-parser: "npm:^4.2.0" @@ -4414,7 +4400,7 @@ __metadata: postcss: ^8.1.0 bin: autoprefixer: bin/autoprefixer - checksum: e00256e754d481a026d928bca729b25954074dd142dbec022f0a7db0d3bbc0dc2e2dc7542e94fec22eff81e21fe140e6856448e2d9a002660cb1e2ad434daee0 + checksum: 1d21cc8edb7bf993682094ceed03a32c18f5293f071182a64c2c6defb44bbe91d576ad775d2347469a81997b80cea0bbc4ad3eeb5b12710f9feacf2e6c04bb51 languageName: node linkType: hard @@ -4538,13 +4524,6 @@ __metadata: languageName: node linkType: hard -"big-integer@npm:^1.6.44": - version: 1.6.52 - resolution: "big-integer@npm:1.6.52" - checksum: 9604224b4c2ab3c43c075d92da15863077a9f59e5d4205f4e7e76acd0cd47e8d469ec5e5dba8d9b32aa233951893b29329ca56ac80c20ce094b4a647a66abae0 - languageName: node - linkType: hard - "binary-extensions@npm:^2.0.0": version: 2.2.0 resolution: "binary-extensions@npm:2.2.0" @@ -4594,14 +4573,12 @@ __metadata: linkType: hard "bonjour-service@npm:^1.0.11": - version: 1.1.1 - resolution: "bonjour-service@npm:1.1.1" + version: 1.2.1 + resolution: "bonjour-service@npm:1.2.1" dependencies: - array-flatten: "npm:^2.1.2" - dns-equal: "npm:^1.0.0" fast-deep-equal: "npm:^3.1.3" multicast-dns: "npm:^7.2.5" - checksum: 8dd3fef3ff8a11678d8f586be03c85004a45bae4353c55d7dbffe288cad73ddb38dee08b57425b9945c9a3a840d50bd40ae5aeda0066186dabe4b84a315b4e05 + checksum: 953cbfc27fc9e36e6f988012993ab2244817d82426603e0390d4715639031396c932b6657b1aa4ec30dbb5fa903d6b2c7f1be3af7a8ba24165c93e987c849730 languageName: node linkType: hard @@ -4621,15 +4598,6 @@ __metadata: languageName: node linkType: hard -"bplist-parser@npm:^0.2.0": - version: 0.2.0 - resolution: "bplist-parser@npm:0.2.0" - dependencies: - big-integer: "npm:^1.6.44" - checksum: ce79c69e0f6efe506281e7c84e3712f7d12978991675b6e3a58a295b16f13ca81aa9b845c335614a545e0af728c8311b6aa3142af76ba1cb616af9bbac5c4a9f - languageName: node - linkType: hard - "brace-expansion@npm:^1.1.7": version: 1.1.11 resolution: "brace-expansion@npm:1.1.11" @@ -4709,7 +4677,7 @@ __metadata: languageName: node linkType: hard -"browserslist@npm:^4.14.5, browserslist@npm:^4.21.10, browserslist@npm:^4.22.2": +"browserslist@npm:^4.14.5, browserslist@npm:^4.22.2": version: 4.22.2 resolution: "browserslist@npm:4.22.2" dependencies: @@ -4799,15 +4767,6 @@ __metadata: languageName: node linkType: hard -"bundle-name@npm:^3.0.0": - version: 3.0.0 - resolution: "bundle-name@npm:3.0.0" - dependencies: - run-applescript: "npm:^5.0.0" - checksum: 57bc7f8b025d83961b04db2f1eff6a87f2363c2891f3542a4b82471ff8ebb5d484af48e9784fcdb28ef1d48bb01f03d891966dc3ef58758e46ea32d750ce40f8 - languageName: node - linkType: hard - "bytes@npm:3.0.0": version: 3.0.0 resolution: "bytes@npm:3.0.0" @@ -4875,8 +4834,8 @@ __metadata: linkType: hard "cacache@npm:^18.0.0": - version: 18.0.1 - resolution: "cacache@npm:18.0.1" + version: 18.0.2 + resolution: "cacache@npm:18.0.2" dependencies: "@npmcli/fs": "npm:^3.1.0" fs-minipass: "npm:^3.0.0" @@ -4890,7 +4849,7 @@ __metadata: ssri: "npm:^10.0.0" tar: "npm:^6.1.11" unique-filename: "npm:^3.0.0" - checksum: a31666805a80a8b16ad3f85faf66750275a9175a3480896f4f6d31b5d53ef190484fabd71bdb6d2ea5603c717fbef09f4af03d6a65b525c8ef0afaa44c361866 + checksum: 7992665305cc251a984f4fdbab1449d50e88c635bc43bf2785530c61d239c61b349e5734461baa461caaee65f040ab14e2d58e694f479c0810cffd181ba5eabc languageName: node linkType: hard @@ -4954,17 +4913,10 @@ __metadata: languageName: node linkType: hard -"caniuse-lite@npm:^1.0.30001538": - version: 1.0.30001571 - resolution: "caniuse-lite@npm:1.0.30001571" - checksum: 632f476e39febbfb5dc91c236981f3d518dc0cf55c42cc2bba431a6b6f4cceae3f9cd74d26312f30e9de65a3cc92ccf80d964ba8de061e25f37b7f0518303dad - languageName: node - linkType: hard - -"caniuse-lite@npm:^1.0.30001565": - version: 1.0.30001570 - resolution: "caniuse-lite@npm:1.0.30001570" - checksum: e47230d2016edea56e002fa462a5289f697b48dcfbf703fb01aecc6c98ad4ecaf945ab23c253cb7af056c2d05f266e4e4cbebf45132100e2c9367439cb95b95b +"caniuse-lite@npm:^1.0.30001565, caniuse-lite@npm:^1.0.30001578": + version: 1.0.30001579 + resolution: "caniuse-lite@npm:1.0.30001579" + checksum: 4003970f8d01a5fa314e39f4a21751dc750a530f3d19aed225e18e8e02892b590b8b0debfa0961eae9bc0e49b77bfb17cf30d2469540e428a8305e3cc9164fb8 languageName: node linkType: hard @@ -4976,8 +4928,8 @@ __metadata: linkType: hard "chai@npm:^4.3.7": - version: 4.3.10 - resolution: "chai@npm:4.3.10" + version: 4.4.1 + resolution: "chai@npm:4.4.1" dependencies: assertion-error: "npm:^1.1.0" check-error: "npm:^1.0.3" @@ -4986,7 +4938,7 @@ __metadata: loupe: "npm:^2.3.6" pathval: "npm:^1.1.1" type-detect: "npm:^4.0.8" - checksum: c887d24f67be6fb554c7ebbde3bb0568697a8833d475e4768296916891ba143f25fc079f6eb34146f3dd5a3279d34c1f387c32c9a6ab288e579f948d9ccf53fe + checksum: 91590a8fe18bd6235dece04ccb2d5b4ecec49984b50924499bdcd7a95c02cb1fd2a689407c19bb854497bde534ef57525cfad6c7fdd2507100fd802fbc2aefbd languageName: node linkType: hard @@ -5100,7 +5052,7 @@ __metadata: languageName: node linkType: hard -"citty@npm:^0.1.4, citty@npm:^0.1.5": +"citty@npm:^0.1.5": version: 0.1.5 resolution: "citty@npm:0.1.5" dependencies: @@ -5117,9 +5069,9 @@ __metadata: linkType: hard "classnames@npm:^2.3.2": - version: 2.3.2 - resolution: "classnames@npm:2.3.2" - checksum: cd50ead57b4f97436aaa9f9885c6926323efc7c2bea8e3d4eb10e4e972aa6a1cfca1c7a0e06f8a199ca7498d4339e30bb6002e589e61c9f21248cbf3e8b0b18d + version: 2.5.1 + resolution: "classnames@npm:2.5.1" + checksum: afff4f77e62cea2d79c39962980bf316bacb0d7c49e13a21adaadb9221e1c6b9d3cdb829d8bb1b23c406f4e740507f37e1dcf506f7e3b7113d17c5bab787aa69 languageName: node linkType: hard @@ -5137,7 +5089,7 @@ __metadata: languageName: node linkType: hard -"clipboardy@npm:3.0.0, clipboardy@npm:^3.0.0": +"clipboardy@npm:3.0.0": version: 3.0.0 resolution: "clipboardy@npm:3.0.0" dependencies: @@ -5148,6 +5100,17 @@ __metadata: languageName: node linkType: hard +"clipboardy@npm:^4.0.0": + version: 4.0.0 + resolution: "clipboardy@npm:4.0.0" + dependencies: + execa: "npm:^8.0.1" + is-wsl: "npm:^3.1.0" + is64bit: "npm:^2.0.0" + checksum: 02bb5f3d0a772bd84ec26a3566c72c2319a9f3b4cb8338370c3bffcf0073c80b834abe1a6945bea4f2cbea28e1627a975aaac577e3f61a868d924ce79138b041 + languageName: node + linkType: hard + "cliui@npm:^5.0.0": version: 5.0.0 resolution: "cliui@npm:5.0.0" @@ -5422,13 +5385,13 @@ __metadata: languageName: node linkType: hard -"cookies@npm:~0.8.0": - version: 0.8.0 - resolution: "cookies@npm:0.8.0" +"cookies@npm:~0.9.0": + version: 0.9.1 + resolution: "cookies@npm:0.9.1" dependencies: depd: "npm:~2.0.0" keygrip: "npm:~1.1.0" - checksum: 0af32f30d1ece0596efc05782c66b9d61659e20c6cc5b695452abf5ceb51883ef43c5c73d86badd7d028a0da7d39f864c95f33640aef04f97fad70f35986bea3 + checksum: 3ffa1c0e992b62ee119adae4dd2ddd4a89166fa5434cd9bd9ff84ec4d2f14dfe2318a601280abfe32a4f64f884ec9345fb1912e488b002d188d2efa0d3919ba3 languageName: node linkType: hard @@ -5471,7 +5434,7 @@ __metadata: languageName: node linkType: hard -"cosmiconfig@npm:^8.2.0": +"cosmiconfig@npm:^8.3.5": version: 8.3.6 resolution: "cosmiconfig@npm:8.3.6" dependencies: @@ -5555,20 +5518,20 @@ __metadata: linkType: hard "css-loader@npm:^6.8.1": - version: 6.8.1 - resolution: "css-loader@npm:6.8.1" + version: 6.9.1 + resolution: "css-loader@npm:6.9.1" dependencies: icss-utils: "npm:^5.1.0" - postcss: "npm:^8.4.21" + postcss: "npm:^8.4.33" postcss-modules-extract-imports: "npm:^3.0.0" - postcss-modules-local-by-default: "npm:^4.0.3" - postcss-modules-scope: "npm:^3.0.0" + postcss-modules-local-by-default: "npm:^4.0.4" + postcss-modules-scope: "npm:^3.1.1" postcss-modules-values: "npm:^4.0.0" postcss-value-parser: "npm:^4.2.0" - semver: "npm:^7.3.8" + semver: "npm:^7.5.4" peerDependencies: webpack: ^5.0.0 - checksum: a6e23de4ec1d2832f10b8ca3cfec6b6097a97ca3c73f64338ae5cd110ac270f1b218ff0273d39f677a7a561f1a9d9b0d332274664d0991bcfafaae162c2669c4 + checksum: 3dfff7d4372a1d8f2b1606a8376f2f5e484e59d7ffdc373ea3b04aa2b54ecd052aaf72c090a97148b0eb8a164cdc1f9dcb7a68686b2d5d190d69367c30f76132 languageName: node linkType: hard @@ -5745,28 +5708,6 @@ __metadata: languageName: node linkType: hard -"default-browser-id@npm:^3.0.0": - version: 3.0.0 - resolution: "default-browser-id@npm:3.0.0" - dependencies: - bplist-parser: "npm:^0.2.0" - untildify: "npm:^4.0.0" - checksum: 8db3ab882eb3e1e8b59d84c8641320e6c66d8eeb17eb4bb848b7dd549b1e6fd313988e4a13542e95fbaeff03f6e9dedc5ad191ad4df7996187753eb0d45c00b7 - languageName: node - linkType: hard - -"default-browser@npm:^4.0.0": - version: 4.0.0 - resolution: "default-browser@npm:4.0.0" - dependencies: - bundle-name: "npm:^3.0.0" - default-browser-id: "npm:^3.0.0" - execa: "npm:^7.1.1" - titleize: "npm:^3.0.0" - checksum: 7c8848badc139ecf9d878e562bc4e7ab4301e51ba120b24d8dcb14739c30152115cc612065ac3ab73c02aace4afa29db5a044257b2f0cf234f16e3a58f6c925e - languageName: node - linkType: hard - "default-gateway@npm:^6.0.3": version: 6.0.3 resolution: "default-gateway@npm:6.0.3" @@ -5804,13 +5745,6 @@ __metadata: languageName: node linkType: hard -"define-lazy-prop@npm:^3.0.0": - version: 3.0.0 - resolution: "define-lazy-prop@npm:3.0.0" - checksum: 5ab0b2bf3fa58b3a443140bbd4cd3db1f91b985cc8a246d330b9ac3fc0b6a325a6d82bddc0b055123d745b3f9931afeea74a5ec545439a1630b9c8512b0eeb49 - languageName: node - linkType: hard - "define-properties@npm:^1.1.3, define-properties@npm:^1.2.0, define-properties@npm:^1.2.1": version: 1.2.1 resolution: "define-properties@npm:1.2.1" @@ -5822,10 +5756,10 @@ __metadata: languageName: node linkType: hard -"defu@npm:^6.1.2, defu@npm:^6.1.3": - version: 6.1.3 - resolution: "defu@npm:6.1.3" - checksum: 60d0d9a6e328148d5313fe0239ba3777701291f35570b52562454653d953fec5281b084514540f8d3b60d61bad9e39b52e95b3c0451631ded220ad8fdc893455 +"defu@npm:^6.1.3, defu@npm:^6.1.4": + version: 6.1.4 + resolution: "defu@npm:6.1.4" + checksum: 2d6cc366262dc0cb8096e429368e44052fdf43ed48e53ad84cc7c9407f890301aa5fcb80d0995abaaf842b3949f154d060be4160f7a46cb2bc2f7726c81526f5 languageName: node linkType: hard @@ -5962,13 +5896,6 @@ __metadata: languageName: node linkType: hard -"dns-equal@npm:^1.0.0": - version: 1.0.0 - resolution: "dns-equal@npm:1.0.0" - checksum: da966e5275ac50546e108af6bc29aaae2164d2ae96d60601b333c4a3aff91f50b6ca14929cf91f20a9cad1587b356323e300cea3ff6588a6a816988485f445f1 - languageName: node - linkType: hard - "dns-packet@npm:^5.2.2": version: 5.6.1 resolution: "dns-packet@npm:5.6.1" @@ -5997,9 +5924,9 @@ __metadata: linkType: hard "dotenv@npm:^16.0.3": - version: 16.3.1 - resolution: "dotenv@npm:16.3.1" - checksum: b95ff1bbe624ead85a3cd70dbd827e8e06d5f05f716f2d0cbc476532d54c7c9469c3bc4dd93ea519f6ad711cb522c00ac9a62b6eb340d5affae8008facc3fbd7 + version: 16.3.2 + resolution: "dotenv@npm:16.3.2" + checksum: a87d62cef0810b670cb477db1a24a42a093b6b428c9e65c185ce1d6368ad7175234b13547718ba08da18df43faae4f814180cc0366e11be1ded2277abc4dd22e languageName: node linkType: hard @@ -6039,9 +5966,9 @@ __metadata: linkType: hard "electron-to-chromium@npm:^1.4.601": - version: 1.4.615 - resolution: "electron-to-chromium@npm:1.4.615" - checksum: 6602172761e44ca1a6c010a010efd0c42710e1e08911e76dd2d3df72ae2a563fb75b0853387273d1e45a4befd314162b2b1debcf9055513f62c6d6a8df4de73a + version: 1.4.642 + resolution: "electron-to-chromium@npm:1.4.642" + checksum: 1a45c745e4cfa6dd0a756ca7df8295dd227b80a6d3552ccedd9665525aedab53d5335e8b97d64c02a6b10b0c773f8b9c20877875b3a3eb2428330f14d3892f27 languageName: node linkType: hard @@ -6379,11 +6306,11 @@ __metadata: linkType: hard "eslint-plugin-prettier@npm:^5.0.1": - version: 5.1.0 - resolution: "eslint-plugin-prettier@npm:5.1.0" + version: 5.1.3 + resolution: "eslint-plugin-prettier@npm:5.1.3" dependencies: prettier-linter-helpers: "npm:^1.0.0" - synckit: "npm:^0.8.5" + synckit: "npm:^0.8.6" peerDependencies: "@types/eslint": ">=8.0.0" eslint: ">=8.0.0" @@ -6394,7 +6321,7 @@ __metadata: optional: true eslint-config-prettier: optional: true - checksum: 76b9a6cc5fa8dcc0d5d8aac5e7b717c08c736d2f158ba63709d94526d3152761d36963f930f9c64a29270a107519eca24b41e82b67cc43f0291d43db4a975fd1 + checksum: f45d5fc1fcfec6b0cf038a7a65ddd10a25df4fe3f9e1f6b7f0d5100e66f046a26a2492e69ee765dddf461b93c114cf2e1eb18d4970aafa6f385448985c136e09 languageName: node linkType: hard @@ -6702,20 +6629,20 @@ __metadata: languageName: node linkType: hard -"execa@npm:^7.1.1": - version: 7.2.0 - resolution: "execa@npm:7.2.0" +"execa@npm:^8.0.1": + version: 8.0.1 + resolution: "execa@npm:8.0.1" dependencies: cross-spawn: "npm:^7.0.3" - get-stream: "npm:^6.0.1" - human-signals: "npm:^4.3.0" + get-stream: "npm:^8.0.1" + human-signals: "npm:^5.0.0" is-stream: "npm:^3.0.0" merge-stream: "npm:^2.0.0" npm-run-path: "npm:^5.1.0" onetime: "npm:^6.0.0" - signal-exit: "npm:^3.0.7" + signal-exit: "npm:^4.1.0" strip-final-newline: "npm:^3.0.0" - checksum: 098cd6a1bc26d509e5402c43f4971736450b84d058391820c6f237aeec6436963e006fd8423c9722f148c53da86aa50045929c7278b5522197dff802d10f9885 + checksum: 2c52d8775f5bf103ce8eec9c7ab3059909ba350a5164744e9947ed14a53f51687c040a250bda833f906d1283aa8803975b84e6c8f7a7c42f99dc8ef80250d1af languageName: node linkType: hard @@ -6980,12 +6907,12 @@ __metadata: linkType: hard "follow-redirects@npm:^1.0.0, follow-redirects@npm:^1.14.0": - version: 1.15.3 - resolution: "follow-redirects@npm:1.15.3" + version: 1.15.5 + resolution: "follow-redirects@npm:1.15.5" peerDependenciesMeta: debug: optional: true - checksum: 915a2cf22e667bdf47b1a43cc6b7dce14d95039e9bbf9a24d0e739abfbdfa00077dd43c86d4a7a19efefcc7a99af144920a175eedc3888d268af5df67c272ee5 + checksum: 418d71688ceaf109dfd6f85f747a0c75de30afe43a294caa211def77f02ef19865b547dfb73fde82b751e1cc507c06c754120b848fe5a7400b0a669766df7615 languageName: node linkType: hard @@ -7033,7 +6960,7 @@ __metadata: languageName: node linkType: hard -"fraction.js@npm:^4.3.6": +"fraction.js@npm:^4.3.7": version: 4.3.7 resolution: "fraction.js@npm:4.3.7" checksum: df291391beea9ab4c263487ffd9d17fed162dbb736982dee1379b2a8cc94e4e24e46ed508c6d278aded9080ba51872f1bc5f3a5fd8d7c74e5f105b508ac28711 @@ -7203,10 +7130,10 @@ __metadata: languageName: node linkType: hard -"get-port-please@npm:^3.1.1": - version: 3.1.1 - resolution: "get-port-please@npm:3.1.1" - checksum: d9229fd671cf43ab846bf187aad917e10688f154db467e0dbc423d0ab9f47363f9612bfb9094a89de196873a3966d33c907475a76bbfd7b68d81caf610035958 +"get-port-please@npm:^3.1.2": + version: 3.1.2 + resolution: "get-port-please@npm:3.1.2" + checksum: 61237342fe035967e5ad1b67a2dee347a64de093bf1222b7cd50072568d73c48dad5cc5cd4fa44635b7cfdcd14d6c47554edb9891c2ec70ab33ecb831683e257 languageName: node linkType: hard @@ -7217,13 +7144,20 @@ __metadata: languageName: node linkType: hard -"get-stream@npm:^6.0.0, get-stream@npm:^6.0.1": +"get-stream@npm:^6.0.0": version: 6.0.1 resolution: "get-stream@npm:6.0.1" checksum: 49825d57d3fd6964228e6200a58169464b8e8970489b3acdc24906c782fb7f01f9f56f8e6653c4a50713771d6658f7cfe051e5eb8c12e334138c9c918b296341 languageName: node linkType: hard +"get-stream@npm:^8.0.1": + version: 8.0.1 + resolution: "get-stream@npm:8.0.1" + checksum: 5c2181e98202b9dae0bb4a849979291043e5892eb40312b47f0c22b9414fc9b28a3b6063d2375705eb24abc41ecf97894d9a51f64ff021511b504477b27b4290 + languageName: node + linkType: hard + "get-symbol-description@npm:^1.0.0": version: 1.0.0 resolution: "get-symbol-description@npm:1.0.0" @@ -7424,9 +7358,9 @@ __metadata: languageName: node linkType: hard -"h3@npm:^1.8.1, h3@npm:^1.8.2": - version: 1.9.0 - resolution: "h3@npm:1.9.0" +"h3@npm:^1.10.0, h3@npm:^1.8.2": + version: 1.10.0 + resolution: "h3@npm:1.10.0" dependencies: cookie-es: "npm:^1.0.0" defu: "npm:^6.1.3" @@ -7435,8 +7369,8 @@ __metadata: radix3: "npm:^1.1.0" ufo: "npm:^1.3.2" uncrypto: "npm:^0.1.3" - unenv: "npm:^1.7.4" - checksum: 90e80c34c9d0b7bdb24b13865ac27a88ca7724f0d1ce005295ae16408d4527020328a077d6c5df02de9f7ce7a15ab8a110978e1394a31717b07a34f09be91c06 + unenv: "npm:^1.8.0" + checksum: 23b474c9472ffd14e0e0e2f97b03f311c9b53d996a5d468f93f223e111320f71cb637a25731d1f803224cff16f942c7cc03aff35a76dedcd88cce6e7c9122d87 languageName: node linkType: hard @@ -7475,7 +7409,7 @@ __metadata: languageName: node linkType: hard -"has-property-descriptors@npm:^1.0.0": +"has-property-descriptors@npm:^1.0.0, has-property-descriptors@npm:^1.0.1": version: 1.0.1 resolution: "has-property-descriptors@npm:1.0.1" dependencies: @@ -7786,10 +7720,10 @@ __metadata: languageName: node linkType: hard -"human-signals@npm:^4.3.0": - version: 4.3.1 - resolution: "human-signals@npm:4.3.1" - checksum: 40498b33fe139f5cc4ef5d2f95eb1803d6318ac1b1c63eaf14eeed5484d26332c828de4a5a05676b6c83d7b9e57727c59addb4b1dea19cb8d71e83689e5b336c +"human-signals@npm:^5.0.0": + version: 5.0.0 + resolution: "human-signals@npm:5.0.0" + checksum: 5a9359073fe17a8b58e5a085e9a39a950366d9f00217c4ff5878bd312e09d80f460536ea6a3f260b5943a01fe55c158d1cea3fc7bee3d0520aeef04f6d915c82 languageName: node linkType: hard @@ -8370,6 +8304,24 @@ __metadata: languageName: node linkType: hard +"is-wsl@npm:^3.1.0": + version: 3.1.0 + resolution: "is-wsl@npm:3.1.0" + dependencies: + is-inside-container: "npm:^1.0.0" + checksum: d3317c11995690a32c362100225e22ba793678fe8732660c6de511ae71a0ff05b06980cf21f98a6bf40d7be0e9e9506f859abe00a1118287d63e53d0a3d06947 + languageName: node + linkType: hard + +"is64bit@npm:^2.0.0": + version: 2.0.0 + resolution: "is64bit@npm:2.0.0" + dependencies: + system-architecture: "npm:^0.1.0" + checksum: 9f3741d4b7560e2a30b9ce0c79bb30c7bdcc5df77c897bd59bb68f0fd882ae698015e8da81d48331def66c778d430c1ae3cb8c1fcc34e96c576b66198395faa7 + languageName: node + linkType: hard + "isarray@npm:^2.0.1, isarray@npm:^2.0.5": version: 2.0.5 resolution: "isarray@npm:2.0.5" @@ -8942,7 +8894,7 @@ __metadata: languageName: node linkType: hard -"jiti@npm:^1.18.2, jiti@npm:^1.20.0": +"jiti@npm:^1.20.0, jiti@npm:^1.21.0": version: 1.21.0 resolution: "jiti@npm:1.21.0" bin: @@ -9077,9 +9029,9 @@ __metadata: linkType: hard "jsonc-parser@npm:^3.2.0": - version: 3.2.0 - resolution: "jsonc-parser@npm:3.2.0" - checksum: 5a12d4d04dad381852476872a29dcee03a57439574e4181d91dca71904fcdcc5e8e4706c0a68a2c61ad9810e1e1c5806b5100d52d3e727b78f5cdc595401045b + version: 3.2.1 + resolution: "jsonc-parser@npm:3.2.1" + checksum: ada66dec143d7f9cb0e2d0d29c69e9ce40d20f3a4cb96b0c6efb745025ac7f9ba647d7ac0990d0adfc37a2d2ae084a12009a9c833dbdbeadf648879a99b9df89 languageName: node linkType: hard @@ -9195,14 +9147,14 @@ __metadata: linkType: hard "koa@npm:^2.14.2": - version: 2.14.2 - resolution: "koa@npm:2.14.2" + version: 2.15.0 + resolution: "koa@npm:2.15.0" dependencies: accepts: "npm:^1.3.5" cache-content-type: "npm:^1.0.0" content-disposition: "npm:~0.5.2" content-type: "npm:^1.0.4" - cookies: "npm:~0.8.0" + cookies: "npm:~0.9.0" debug: "npm:^4.3.2" delegates: "npm:^1.0.0" depd: "npm:^2.0.0" @@ -9221,7 +9173,7 @@ __metadata: statuses: "npm:^1.5.0" type-is: "npm:^1.6.16" vary: "npm:^1.1.2" - checksum: f60ae84974d7cb834a5937592e010d97134278527a55a3a38973935db9ea409fecbd824e5ff0fa767077d9a9dc1f1a32881b626ddaad8b5978756c345f520cdd + checksum: 018daa5d3521621699e4228de9191849083c0356e1e4abda6d96aa44fa3ee1f6a67849040c2a0b681697d1431a8232cca1e532a7246fc785257bfdf1e6ccf43a languageName: node linkType: hard @@ -9319,30 +9271,30 @@ __metadata: linkType: hard "listhen@npm:^1.5.5": - version: 1.5.5 - resolution: "listhen@npm:1.5.5" + version: 1.5.6 + resolution: "listhen@npm:1.5.6" dependencies: "@parcel/watcher": "npm:^2.3.0" "@parcel/watcher-wasm": "npm:2.3.0" - citty: "npm:^0.1.4" - clipboardy: "npm:^3.0.0" + citty: "npm:^0.1.5" + clipboardy: "npm:^4.0.0" consola: "npm:^3.2.3" - defu: "npm:^6.1.2" - get-port-please: "npm:^3.1.1" - h3: "npm:^1.8.1" + defu: "npm:^6.1.4" + get-port-please: "npm:^3.1.2" + h3: "npm:^1.10.0" http-shutdown: "npm:^1.2.2" - jiti: "npm:^1.20.0" + jiti: "npm:^1.21.0" mlly: "npm:^1.4.2" node-forge: "npm:^1.3.1" pathe: "npm:^1.1.1" - std-env: "npm:^3.4.3" - ufo: "npm:^1.3.0" - untun: "npm:^0.1.2" + std-env: "npm:^3.7.0" + ufo: "npm:^1.3.2" + untun: "npm:^0.1.3" uqr: "npm:^0.1.2" bin: listen: bin/listhen.mjs listhen: bin/listhen.mjs - checksum: 84a8a6c0e0d347db3110af3f77aa86fba428fcec1e2cd53e17d0d8daf36edd8833c75a647b718e6cea723d452b0b2a78b2290d03c79315c52eda1f1984384bb2 + checksum: 7c004a994eb77b0034fdecf9b8b146427182c970a5bc98296a745ee4ef9a51c4e0ce13413603409742e495027c8dd9245f674d8ac064f4df901bd35bddff8eee languageName: node linkType: hard @@ -9881,21 +9833,21 @@ __metadata: languageName: node linkType: hard -"minimatch@npm:^5.0.1": - version: 5.1.6 - resolution: "minimatch@npm:5.1.6" +"minimatch@npm:9.0.3, minimatch@npm:^9.0.1": + version: 9.0.3 + resolution: "minimatch@npm:9.0.3" dependencies: brace-expansion: "npm:^2.0.1" - checksum: 3defdfd230914f22a8da203747c42ee3c405c39d4d37ffda284dac5e45b7e1f6c49aa8be606509002898e73091ff2a3bbfc59c2c6c71d4660609f63aa92f98e3 + checksum: 85f407dcd38ac3e180f425e86553911d101455ca3ad5544d6a7cec16286657e4f8a9aa6695803025c55e31e35a91a2252b5dc8e7d527211278b8b65b4dbd5eac languageName: node linkType: hard -"minimatch@npm:^9.0.1": - version: 9.0.3 - resolution: "minimatch@npm:9.0.3" +"minimatch@npm:^5.0.1": + version: 5.1.6 + resolution: "minimatch@npm:5.1.6" dependencies: brace-expansion: "npm:^2.0.1" - checksum: 85f407dcd38ac3e180f425e86553911d101455ca3ad5544d6a7cec16286657e4f8a9aa6695803025c55e31e35a91a2252b5dc8e7d527211278b8b65b4dbd5eac + checksum: 3defdfd230914f22a8da203747c42ee3c405c39d4d37ffda284dac5e45b7e1f6c49aa8be606509002898e73091ff2a3bbfc59c2c6c71d4660609f63aa92f98e3 languageName: node linkType: hard @@ -10059,14 +10011,14 @@ __metadata: linkType: hard "mlly@npm:^1.2.0, mlly@npm:^1.4.2": - version: 1.4.2 - resolution: "mlly@npm:1.4.2" + version: 1.5.0 + resolution: "mlly@npm:1.5.0" dependencies: - acorn: "npm:^8.10.0" - pathe: "npm:^1.1.1" + acorn: "npm:^8.11.3" + pathe: "npm:^1.1.2" pkg-types: "npm:^1.0.3" - ufo: "npm:^1.3.0" - checksum: 905e3a704c7d3bcaad55f31d6efe9f680eab5be053ab7f8b299b8dbc027041f741fa6a93db9a3c461be2552632f3831b6c43c50af530f5fb2e9cd6273bc9d642 + ufo: "npm:^1.3.2" + checksum: 0861d64f13e8e6f99e4897b652b553ded4d4b9e7b011d6afd7141e013b77ed9b9be0cd76e60c46c60c56cc9b8e27061165e5696179ba9f4161c24d162db7b621 languageName: node linkType: hard @@ -10235,18 +10187,18 @@ __metadata: linkType: hard "node-addon-api@npm:^7.0.0": - version: 7.0.0 - resolution: "node-addon-api@npm:7.0.0" + version: 7.1.0 + resolution: "node-addon-api@npm:7.1.0" dependencies: node-gyp: "npm:latest" - checksum: 3d5a15ee434e122b345e614db122a63f30194c298104c3d8a0fa9f68707abb278af27b45222602456a131890a59b4a92291ff5b4b7938ff282168e9ad1bf7103 + checksum: 2e096ab079e3c46d33b0e252386e9c239c352f7cc6d75363d9a3c00bdff34c1a5da170da861917512843f213c32d024ced9dc9552b968029786480d18727ec66 languageName: node linkType: hard -"node-fetch-native@npm:^1.4.0, node-fetch-native@npm:^1.4.1": - version: 1.4.1 - resolution: "node-fetch-native@npm:1.4.1" - checksum: ab298a42ebf3b1b6c6a8cbc53d8ba703895f55171ed743b0828c2a87d461642d8053143864915a69d41cc01013db86406da105fff6c0a05a00d8caf5c279549c +"node-fetch-native@npm:^1.4.0, node-fetch-native@npm:^1.4.1, node-fetch-native@npm:^1.6.1": + version: 1.6.1 + resolution: "node-fetch-native@npm:1.6.1" + checksum: 5df52cd7fb18a51b7e3ec65420b04cd5c01ce6a15ca853b6112a3ae17eb071970a15e7099f3bd258006ab8a0cecac3c7c212800a680466c5bb1a679eab14338f languageName: node linkType: hard @@ -10272,13 +10224,13 @@ __metadata: linkType: hard "node-gyp-build@npm:^4.2.0, node-gyp-build@npm:^4.3.0": - version: 4.7.1 - resolution: "node-gyp-build@npm:4.7.1" + version: 4.8.0 + resolution: "node-gyp-build@npm:4.8.0" bin: node-gyp-build: bin.js node-gyp-build-optional: optional.js node-gyp-build-test: build-test.js - checksum: b8e4a3f889237cd08edde3775e2b4e1e39a0571580584e33e29979f0c532a254ce3c5ec9435bd526254ad0b3f0b4a7e7fe14e53bd400f6ea9445f3bfd88a6b1e + checksum: 85324be16f81f0235cbbc42e3eceaeb1b5ab94c8d8f5236755e1435b4908338c65a4e75f66ee343cbcb44ddf9b52a428755bec16dcd983295be4458d95c8e1ad languageName: node linkType: hard @@ -10430,11 +10382,11 @@ __metadata: linkType: hard "npm-run-path@npm:^5.1.0": - version: 5.1.0 - resolution: "npm-run-path@npm:5.1.0" + version: 5.2.0 + resolution: "npm-run-path@npm:5.2.0" dependencies: path-key: "npm:^4.0.0" - checksum: ff6d77514489f47fa1c3b1311d09cd4b6d09a874cc1866260f9dea12cbaabda0436ed7f8c2ee44d147bf99a3af29307c6f63b0f83d242b0b6b0ab25dff2629e3 + checksum: 7963c1f98e42afebe9524a08b0881477ec145aab34f6018842a315422b25ad40e015bdee709b697571e5efda2ecfa2640ee917d92674e4de1166fa3532a211b1 languageName: node linkType: hard @@ -10613,18 +10565,6 @@ __metadata: languageName: node linkType: hard -"open@npm:^9.1.0": - version: 9.1.0 - resolution: "open@npm:9.1.0" - dependencies: - default-browser: "npm:^4.0.0" - define-lazy-prop: "npm:^3.0.0" - is-inside-container: "npm:^1.0.0" - is-wsl: "npm:^2.2.0" - checksum: 8073ec0dd8994a7a7d9bac208bd17d093993a65ce10f2eb9b62b6d3a91c9366ae903938a237c275493c130171d339f6dcbdd2a2de7e32953452c0867b97825af - languageName: node - linkType: hard - "optionator@npm:^0.9.3": version: 0.9.3 resolution: "optionator@npm:0.9.3" @@ -10839,10 +10779,10 @@ __metadata: languageName: node linkType: hard -"pathe@npm:^1.1.0, pathe@npm:^1.1.1": - version: 1.1.1 - resolution: "pathe@npm:1.1.1" - checksum: 3ae5a0529c3415d91c3ac9133f52cffea54a0dd46892fe059f4b80faf36fd207957d4594bdc87043b65d0761b1e5728f81f46bafff3b5302da4e2e48889b8c0e +"pathe@npm:^1.1.0, pathe@npm:^1.1.1, pathe@npm:^1.1.2": + version: 1.1.2 + resolution: "pathe@npm:1.1.2" + checksum: 64ee0a4e587fb0f208d9777a6c56e4f9050039268faaaaecd50e959ef01bf847b7872785c36483fa5cdcdbdfdb31fef2ff222684d4fc21c330ab60395c681897 languageName: node linkType: hard @@ -10968,16 +10908,16 @@ __metadata: linkType: hard "postcss-loader@npm:^7.3.3": - version: 7.3.3 - resolution: "postcss-loader@npm:7.3.3" + version: 7.3.4 + resolution: "postcss-loader@npm:7.3.4" dependencies: - cosmiconfig: "npm:^8.2.0" - jiti: "npm:^1.18.2" - semver: "npm:^7.3.8" + cosmiconfig: "npm:^8.3.5" + jiti: "npm:^1.20.0" + semver: "npm:^7.5.4" peerDependencies: postcss: ^7.0.0 || ^8.0.1 webpack: ^5.0.0 - checksum: d039654273f858be1f75dfdf8b550869d88905b73a7684b3e48a2937a6087619e84fd1a3551cdef78685a965a2573e985b29a532c3878d834071ecd2da0eb304 + checksum: 1bf7614aeea9ad1f8ee6be3a5451576c059391688ea67f825aedc2674056369597faeae4e4a81fe10843884c9904a71403d9a54197e1f560e8fbb9e61f2a2680 languageName: node linkType: hard @@ -10990,27 +10930,27 @@ __metadata: languageName: node linkType: hard -"postcss-modules-local-by-default@npm:^4.0.3": - version: 4.0.3 - resolution: "postcss-modules-local-by-default@npm:4.0.3" +"postcss-modules-local-by-default@npm:^4.0.4": + version: 4.0.4 + resolution: "postcss-modules-local-by-default@npm:4.0.4" dependencies: icss-utils: "npm:^5.0.0" postcss-selector-parser: "npm:^6.0.2" postcss-value-parser: "npm:^4.1.0" peerDependencies: postcss: ^8.1.0 - checksum: be49b86efbfb921f42287e227584aac91af9826fc1083db04958ae283dfe215ca539421bfba71f9da0f0b10651f28e95a64b5faca7166f578a1933b8646051f7 + checksum: 9ebf464867eb10b29b73501b1466dcac8352ed852ef68ec23571f515daa74401d7ace9a6c72f354542081fdbb47d098c9bc6b05373b553a6e35779d072f967bb languageName: node linkType: hard -"postcss-modules-scope@npm:^3.0.0": - version: 3.1.0 - resolution: "postcss-modules-scope@npm:3.1.0" +"postcss-modules-scope@npm:^3.1.1": + version: 3.1.1 + resolution: "postcss-modules-scope@npm:3.1.1" dependencies: postcss-selector-parser: "npm:^6.0.4" peerDependencies: postcss: ^8.1.0 - checksum: bc8e12e9312d7070f34ccef2929f65154102e2b2984a385eaf2ef25b6d4e22234de71116c240a05b541a79946b717d6fa8c5d314f6697bf05f295261693050fe + checksum: 3ef6ac14fcda1581bc43e37622256bd87b99ea49c59b2aae648d057d57f5ecc634648cce9910166220a797567af674bc09246ccc010f1dd58d2863b805719109 languageName: node linkType: hard @@ -11026,12 +10966,12 @@ __metadata: linkType: hard "postcss-selector-parser@npm:^6.0.2, postcss-selector-parser@npm:^6.0.4": - version: 6.0.13 - resolution: "postcss-selector-parser@npm:6.0.13" + version: 6.0.15 + resolution: "postcss-selector-parser@npm:6.0.15" dependencies: cssesc: "npm:^3.0.0" util-deprecate: "npm:^1.0.2" - checksum: 51f099b27f7c7198ea1826470ef0adfa58b3bd3f59b390fda123baa0134880a5fa9720137b6009c4c1373357b144f700b0edac73335d0067422063129371444e + checksum: 48b425d6cef497bcf6b7d136f6fd95cfca43026955e07ec9290d3c15457de3a862dbf251dd36f42c07a0d5b5ab6f31e41acefeff02528995a989b955505e440b languageName: node linkType: hard @@ -11042,14 +10982,14 @@ __metadata: languageName: node linkType: hard -"postcss@npm:^8.4.21, postcss@npm:^8.4.29": - version: 8.4.32 - resolution: "postcss@npm:8.4.32" +"postcss@npm:^8.4.29, postcss@npm:^8.4.33": + version: 8.4.33 + resolution: "postcss@npm:8.4.33" dependencies: nanoid: "npm:^3.3.7" picocolors: "npm:^1.0.0" source-map-js: "npm:^1.0.2" - checksum: 39308a9195fa34d4dbdd7b58a896cff0c7809f84f7a4ac1b95b68ca86c9138a395addff33075668ed3983d41b90aac05754c445237a9365eb1c3a5602ebd03ad + checksum: 16eda83458fcd8a91bece287b5920c7f57164c3ea293e6c80d0ea71ce7843007bcd8592260a5160b9a7f02693e6ac93e2495b02d8c7596d3f3f72c1447e3ba79 languageName: node linkType: hard @@ -11084,11 +11024,11 @@ __metadata: linkType: hard "prettier@npm:^3.1.1": - version: 3.1.1 - resolution: "prettier@npm:3.1.1" + version: 3.2.4 + resolution: "prettier@npm:3.2.4" bin: prettier: bin/prettier.cjs - checksum: facc944ba20e194ff4db765e830ffbcb642803381f0d2033ed397e79904fa4ccc877dc25ad68f42d36985c01d051c990ca1b905fb83d2d7d65fe69e4386fa1a3 + checksum: 88dfeb78ac6096522c9a5b81f1413d875f568420d9bb6a5e5103527912519b993f2bcdcac311fcff5718d5869671d44e4f85827d3626f3a6ce32b9abc65d88e0 languageName: node linkType: hard @@ -11453,26 +11393,26 @@ __metadata: linkType: hard "react-router-dom@npm:^6.8.0": - version: 6.21.1 - resolution: "react-router-dom@npm:6.21.1" + version: 6.21.3 + resolution: "react-router-dom@npm:6.21.3" dependencies: - "@remix-run/router": "npm:1.14.1" - react-router: "npm:6.21.1" + "@remix-run/router": "npm:1.14.2" + react-router: "npm:6.21.3" peerDependencies: react: ">=16.8" react-dom: ">=16.8" - checksum: 2c2b653de8cc90397e5b057c0d32dd780fe894c4bd36397005da169a7422b31988ebc14ef0da3ac3f2b7286b244f97a141974acaa86c731715816e64c8cd2912 + checksum: 1bea7bf17eb148461a7a99c9314e5ccc662ae00f563e29c16038e0b1b40aefb7381685f21289df07ad2295087a783c897b4e841ed12b07cd9a0829274a224231 languageName: node linkType: hard -"react-router@npm:6.21.1, react-router@npm:^6.8.0": - version: 6.21.1 - resolution: "react-router@npm:6.21.1" +"react-router@npm:6.21.3, react-router@npm:^6.8.0": + version: 6.21.3 + resolution: "react-router@npm:6.21.3" dependencies: - "@remix-run/router": "npm:1.14.1" + "@remix-run/router": "npm:1.14.2" peerDependencies: react: ">=16.8" - checksum: 81278cf25a999d7c93a31d540102e3b74b696eb4349779e22bba77b633e4e5f8f91b8c6f7946c9572a1e92f64363724150216f5643a6f9817f8bc1643cb8bdbc + checksum: 66a0377a74ef2d298b9d617c02ba7a10e7e8b8be34b303068b5b5986f05e965037c51c0d45ea3a7967438f2dfde76c2c0f638cf19d3417fb408608ee9aee1668 languageName: node linkType: hard @@ -11793,15 +11733,6 @@ __metadata: languageName: node linkType: hard -"run-applescript@npm:^5.0.0": - version: 5.0.0 - resolution: "run-applescript@npm:5.0.0" - dependencies: - execa: "npm:^5.0.0" - checksum: f9977db5770929f3f0db434b8e6aa266498c70dec913c84320c0a06add510cf44e3a048c44da088abee312006f9cbf572fd065cdc8f15d7682afda8755f4114c - languageName: node - linkType: hard - "run-parallel@npm:^1.1.9": version: 1.2.0 resolution: "run-parallel@npm:1.2.0" @@ -11812,14 +11743,14 @@ __metadata: linkType: hard "safe-array-concat@npm:^1.0.1": - version: 1.0.1 - resolution: "safe-array-concat@npm:1.0.1" + version: 1.1.0 + resolution: "safe-array-concat@npm:1.1.0" dependencies: - call-bind: "npm:^1.0.2" - get-intrinsic: "npm:^1.2.1" + call-bind: "npm:^1.0.5" + get-intrinsic: "npm:^1.2.2" has-symbols: "npm:^1.0.3" isarray: "npm:^2.0.5" - checksum: 4b15ce5fce5ce4d7e744a63592cded88d2f27806ed229eadb2e42629cbcd40e770f7478608e75f455e7fe341acd8c0a01bdcd7146b10645ea7411c5e3c1d1dd8 + checksum: 833d3d950fc7507a60075f9bfaf41ec6dac7c50c7a9d62b1e6b071ecc162185881f92e594ff95c1a18301c881352dd6fd236d56999d5819559db7b92da9c28af languageName: node linkType: hard @@ -11845,13 +11776,13 @@ __metadata: linkType: hard "safe-regex-test@npm:^1.0.0": - version: 1.0.0 - resolution: "safe-regex-test@npm:1.0.0" + version: 1.0.2 + resolution: "safe-regex-test@npm:1.0.2" dependencies: - call-bind: "npm:^1.0.2" - get-intrinsic: "npm:^1.1.3" + call-bind: "npm:^1.0.5" + get-intrinsic: "npm:^1.2.2" is-regex: "npm:^1.1.4" - checksum: 14a81a7e683f97b2d6e9c8be61fddcf8ed7a02f4e64a825515f96bb1738eb007145359313741d2704d28b55b703a0f6300c749dde7c1dbc13952a2b85048ede2 + checksum: c24df9c3cbd9e6a6800f02411a12ce2bd642be22ce6ad03f796e7b3f3851d9eb1fb8d1fab48278b04fabe75dd279c10bc07a45e39543aa72407fbd8a31174958 languageName: node linkType: hard @@ -11884,8 +11815,8 @@ __metadata: linkType: hard "sass-loader@npm:^13.3.2": - version: 13.3.2 - resolution: "sass-loader@npm:13.3.2" + version: 13.3.3 + resolution: "sass-loader@npm:13.3.3" dependencies: neo-async: "npm:^2.6.2" peerDependencies: @@ -11903,20 +11834,20 @@ __metadata: optional: true sass-embedded: optional: true - checksum: 7db8132101ed663f3cf936ce765b9b960a48b14f13f17d367a4e0c2ae259e91b6c401e33ab0f27ee88c98c8b5893c778848fc8366f1f387ac788ebef244e000a + checksum: 5e955a4ffce35ee0a46fce677ce51eaa69587fb5371978588c83af00f49e7edc36dcf3bb559cbae27681c5e24a71284463ebe03a1fb65e6ecafa1db0620e3fc8 languageName: node linkType: hard "sass@npm:^1.58.0": - version: 1.69.5 - resolution: "sass@npm:1.69.5" + version: 1.70.0 + resolution: "sass@npm:1.70.0" dependencies: chokidar: "npm:>=3.0.0 <4.0.0" immutable: "npm:^4.0.0" source-map-js: "npm:>=0.6.2 <2.0.0" bin: sass: sass.js - checksum: a9003a9482f2e467fc412cfe58ba4fa14fb78bef7e1283ce5d64a065f8a31114ec3bbf5d4e724f94eb8512c32c768a6f91f228c7f16a26a300bbf4db293b5608 + checksum: 7c309ee1c096d591746d122da9f1ebd65b4c4b3a60c2cc0ec720fd98fe1205fa8b44c9f563d113b9fdfeb25af1e32ec9b3e048bd4b8e05d267f020953bd7baf0 languageName: node linkType: hard @@ -12056,11 +11987,11 @@ __metadata: linkType: hard "serialize-javascript@npm:^6.0.0, serialize-javascript@npm:^6.0.1": - version: 6.0.1 - resolution: "serialize-javascript@npm:6.0.1" + version: 6.0.2 + resolution: "serialize-javascript@npm:6.0.2" dependencies: randombytes: "npm:^2.1.0" - checksum: 1af427f4fee3fee051f54ffe15f77068cff78a3c96d20f5c1178d20630d3ab122d8350e639d5e13cde8111ef9db9439b871305ffb185e24be0a2149cec230988 + checksum: 2dd09ef4b65a1289ba24a788b1423a035581bef60817bea1f01eda8e3bda623f86357665fe7ac1b50f6d4f583f97db9615b3f07b2a2e8cbcb75033965f771dd2 languageName: node linkType: hard @@ -12136,14 +12067,15 @@ __metadata: linkType: hard "set-function-length@npm:^1.1.1": - version: 1.1.1 - resolution: "set-function-length@npm:1.1.1" + version: 1.2.0 + resolution: "set-function-length@npm:1.2.0" dependencies: define-data-property: "npm:^1.1.1" - get-intrinsic: "npm:^1.2.1" + function-bind: "npm:^1.1.2" + get-intrinsic: "npm:^1.2.2" gopd: "npm:^1.0.1" - has-property-descriptors: "npm:^1.0.0" - checksum: a29e255c116c29e3323b851c4f46c58c91be9bb8b065f191e2ea1807cb2c839df56e3175732a498e0c6d54626ba6b6fef896bf699feb7ab70c42dc47eb247c95 + has-property-descriptors: "npm:^1.0.1" + checksum: b4fdf68bbfa9944284a9469c04e0d9cdb7924942fab75cd11fb61e8a7518f0d40bbbbc1b46871f648a93b97d170d8047fe3492cdadff066a8a8ae4ce68d0564a languageName: node linkType: hard @@ -12243,7 +12175,7 @@ __metadata: languageName: node linkType: hard -"signal-exit@npm:^4.0.1": +"signal-exit@npm:^4.0.1, signal-exit@npm:^4.1.0": version: 4.1.0 resolution: "signal-exit@npm:4.1.0" checksum: 41602dce540e46d599edba9d9860193398d135f7ff72cab629db5171516cfae628d21e7bfccde1bbfdf11c48726bc2a6d1a8fb8701125852fbfda7cf19c6aa83 @@ -12521,10 +12453,10 @@ __metadata: languageName: node linkType: hard -"std-env@npm:^3.4.3": - version: 3.6.0 - resolution: "std-env@npm:3.6.0" - checksum: a540b8cb011bef4bf5905e1e28f24ce37124f9d001c69224ee0025d3600144e6847bac62cd38fbd98148ab4d26ab0682b9b4d42bc863cd1cca0b9807f18aadba +"std-env@npm:^3.7.0": + version: 3.7.0 + resolution: "std-env@npm:3.7.0" + checksum: 60edf2d130a4feb7002974af3d5a5f3343558d1ccf8d9b9934d225c638606884db4a20d2fe6440a09605bca282af6b042ae8070a10490c0800d69e82e478f41e languageName: node linkType: hard @@ -12557,9 +12489,9 @@ __metadata: linkType: hard "stream-shift@npm:^1.0.0": - version: 1.0.1 - resolution: "stream-shift@npm:1.0.1" - checksum: b63a0d178cde34b920ad93e2c0c9395b840f408d36803b07c61416edac80ef9e480a51910e0ceea0d679cec90921bcd2cccab020d3a9fa6c73a98b0fbec132fd + version: 1.0.3 + resolution: "stream-shift@npm:1.0.3" + checksum: 939cd1051ca750d240a0625b106a2b988c45fb5a3be0cebe9a9858cb01bc1955e8c7b9fac17a9462976bea4a7b704e317c5c2200c70f0ca715a3363b9aa4fd3b languageName: node linkType: hard @@ -12743,11 +12675,11 @@ __metadata: linkType: hard "style-loader@npm:^3.3.1, style-loader@npm:^3.3.3": - version: 3.3.3 - resolution: "style-loader@npm:3.3.3" + version: 3.3.4 + resolution: "style-loader@npm:3.3.4" peerDependencies: webpack: ^5.0.0 - checksum: 104bae8abd0627579dc14f3917cf65f1117e8098e3529872f09c26b5eee07933567b7be5c8ebf94d16e322b6e726dc569c5787111bf3786915850db4e351ef33 + checksum: 8f8027fc5c6e91400cbb60066e7db3315810f8eaa0d19b2a254936eb0bec399ba8a7043b1789da9d05ab7c3ba50faf9267765ae0bf3571e48aa34ecdc774be37 languageName: node linkType: hard @@ -12792,13 +12724,20 @@ __metadata: languageName: node linkType: hard -"synckit@npm:^0.8.5": - version: 0.8.6 - resolution: "synckit@npm:0.8.6" +"synckit@npm:^0.8.6": + version: 0.8.8 + resolution: "synckit@npm:0.8.8" dependencies: - "@pkgr/utils": "npm:^2.4.2" + "@pkgr/core": "npm:^0.1.0" tslib: "npm:^2.6.2" - checksum: 200528062e3915a0190a4c6b1e01436fcfdf812e2e8d977746746f3998bb4182d758af760e51b06a64f8323e705735aff7b4b3efc4a0ab5f75eaccc044a8cfcc + checksum: c3d3aa8e284f3f84f2f868b960c9f49239b364e35f6d20825a448449a3e9c8f49fe36cdd5196b30615682f007830d46f2ea354003954c7336723cb821e4b6519 + languageName: node + linkType: hard + +"system-architecture@npm:^0.1.0": + version: 0.1.0 + resolution: "system-architecture@npm:0.1.0" + checksum: 1969974ea5d31a9ac7c38f2657cfe8255b36f9e1d5ba3c58cb84c24fbeedf562778b8511f18a0abe6d70ae90148cfcaf145ecf26e37c0a53a3829076f3238cbb languageName: node linkType: hard @@ -12824,14 +12763,14 @@ __metadata: linkType: hard "terser-webpack-plugin@npm:^5.3.7": - version: 5.3.9 - resolution: "terser-webpack-plugin@npm:5.3.9" + version: 5.3.10 + resolution: "terser-webpack-plugin@npm:5.3.10" dependencies: - "@jridgewell/trace-mapping": "npm:^0.3.17" + "@jridgewell/trace-mapping": "npm:^0.3.20" jest-worker: "npm:^27.4.5" schema-utils: "npm:^3.1.1" serialize-javascript: "npm:^6.0.1" - terser: "npm:^5.16.8" + terser: "npm:^5.26.0" peerDependencies: webpack: ^5.1.0 peerDependenciesMeta: @@ -12841,13 +12780,13 @@ __metadata: optional: true uglify-js: optional: true - checksum: 8a757106101ea1504e5dc549c722506506e7d3f0d38e72d6c8108ad814c994ca0d67ac5d0825ba59704a4b2b04548201b2137f198bfce897b09fe9e36727a1e9 + checksum: 66d1ed3174542560911cf96f4716aeea8d60e7caab212291705d50072b6ba844c7391442541b13c848684044042bea9ec87512b8506528c12854943da05faf91 languageName: node linkType: hard -"terser@npm:^5.16.8": - version: 5.26.0 - resolution: "terser@npm:5.26.0" +"terser@npm:^5.26.0": + version: 5.27.0 + resolution: "terser@npm:5.27.0" dependencies: "@jridgewell/source-map": "npm:^0.3.3" acorn: "npm:^8.8.2" @@ -12855,7 +12794,7 @@ __metadata: source-map-support: "npm:~0.5.20" bin: terser: bin/terser - checksum: 3906289c6bacd75804a47a583cdafefbd76c5edb39435369755c7b1592e57586fb2f4bddf6eb37a807d6e782171dbf0aa7bbdc80fd5b77b2f2b62196cac49b62 + checksum: bed0d39d9a7f2b82c87173e48081c46426a8820ba1dcb864bbfccd2df2b7fb8498a7ea4c8ef045ccce5713b23a6b4c3a784967f1b9f3115adaa7f51712f6e6ae languageName: node linkType: hard @@ -12907,13 +12846,6 @@ __metadata: languageName: node linkType: hard -"titleize@npm:^3.0.0": - version: 3.0.0 - resolution: "titleize@npm:3.0.0" - checksum: 5ae6084ba299b5782f95e3fe85ea9f0fa4d74b8ae722b6b3208157e975589fbb27733aeba4e5080fa9314a856044ef52caa61b87caea4b1baade951a55c06336 - languageName: node - linkType: hard - "tmpl@npm:1.0.5": version: 1.0.5 resolution: "tmpl@npm:1.0.5" @@ -12989,8 +12921,8 @@ __metadata: linkType: hard "ts-jest@npm:^29.1.0, ts-jest@npm:^29.1.1": - version: 29.1.1 - resolution: "ts-jest@npm:29.1.1" + version: 29.1.2 + resolution: "ts-jest@npm:29.1.2" dependencies: bs-logger: "npm:0.x" fast-json-stable-stringify: "npm:2.x" @@ -13017,7 +12949,7 @@ __metadata: optional: true bin: ts-jest: cli.js - checksum: 6c45e0aeeff9cc54a64f931c43e1b99f4a1f0ddf44786cc128e7e55603ab7473c8c8f62fd83bd7e51bfe83e3c0c683132152efaeb844516bf7c923f4e92d157d + checksum: c2f51f0241f89d127d41392decbcb83b5dfd5e57ab9d50220aa7b7e2f9b3f3b07ccdbba33311284df1c41941879e4ddfad44b15a9d0da4b74bd1b98702b729df languageName: node linkType: hard @@ -13101,7 +13033,7 @@ __metadata: languageName: node linkType: hard -"tslib@npm:^2.0.0, tslib@npm:^2.1.0, tslib@npm:^2.3.1, tslib@npm:^2.4.0, tslib@npm:^2.5.0, tslib@npm:^2.6.0, tslib@npm:^2.6.2": +"tslib@npm:^2.0.0, tslib@npm:^2.1.0, tslib@npm:^2.3.1, tslib@npm:^2.4.0, tslib@npm:^2.5.0, tslib@npm:^2.6.2": version: 2.6.2 resolution: "tslib@npm:2.6.2" checksum: e03a8a4271152c8b26604ed45535954c0a45296e32445b4b87f8a5abdb2421f40b59b4ca437c4346af0f28179780d604094eb64546bee2019d903d01c6c19bdb @@ -13317,16 +13249,16 @@ __metadata: languageName: node linkType: hard -"unenv@npm:^1.7.4": - version: 1.8.0 - resolution: "unenv@npm:1.8.0" +"unenv@npm:^1.8.0": + version: 1.9.0 + resolution: "unenv@npm:1.9.0" dependencies: consola: "npm:^3.2.3" defu: "npm:^6.1.3" mime: "npm:^3.0.0" - node-fetch-native: "npm:^1.4.1" + node-fetch-native: "npm:^1.6.1" pathe: "npm:^1.1.1" - checksum: f5ad66425ef5b1848d2daab4bdb18e3f2576a4a8df48f3e994ef373290489a6251969b78b965963a905b90dc01db6e838e2deb826e384ec637df2345a146b0bb + checksum: d00012badc83731c07f08d5129c702c49c0212375eb3732b27aae89ace3c67162dbaea4496965676f18fc06b0ec445d91385e283f5fd3e4540dda8b0b5424f81 languageName: node linkType: hard @@ -13448,14 +13380,7 @@ __metadata: languageName: node linkType: hard -"untildify@npm:^4.0.0": - version: 4.0.0 - resolution: "untildify@npm:4.0.0" - checksum: d758e624c707d49f76f7511d75d09a8eda7f2020d231ec52b67ff4896bcf7013be3f9522d8375f57e586e9a2e827f5641c7e06ee46ab9c435fc2b2b2e9de517a - languageName: node - linkType: hard - -"untun@npm:^0.1.2": +"untun@npm:^0.1.3": version: 0.1.3 resolution: "untun@npm:0.1.3" dependencies: @@ -13509,8 +13434,8 @@ __metadata: linkType: hard "use-callback-ref@npm:^1.3.0": - version: 1.3.0 - resolution: "use-callback-ref@npm:1.3.0" + version: 1.3.1 + resolution: "use-callback-ref@npm:1.3.1" dependencies: tslib: "npm:^2.0.0" peerDependencies: @@ -13519,7 +13444,7 @@ __metadata: peerDependenciesMeta: "@types/react": optional: true - checksum: 8a0867ffd441f358c66d79567970a745cc78ac2f98840a81c1fa749a525e8716116c645497d886a815e1dcf40ad81a107ebd6a7d15fd9ab5925c44a994a1d89a + checksum: 6666cd62e13053d03e453b5199037cb8f6475a8f55afd664ff488bd8f2ee2ede4da3b220dd7e60f5ecd4926133364fbf4b1aed463eeb8203e7c5be3b1533b59b languageName: node linkType: hard @@ -13635,8 +13560,8 @@ __metadata: linkType: hard "viem@npm:^1.2.5": - version: 1.20.3 - resolution: "viem@npm:1.20.3" + version: 1.21.4 + resolution: "viem@npm:1.21.4" dependencies: "@adraffy/ens-normalize": "npm:1.10.0" "@noble/curves": "npm:1.2.0" @@ -13651,7 +13576,7 @@ __metadata: peerDependenciesMeta: typescript: optional: true - checksum: 5bc3b85b18f19e5c366a9031512d3995e7d55394ed6e66c9c1db8a7d8d95cac5a95b76c5e2358e2808e383a730491775bf0457cae3c3c3d47d8490c9273e38de + checksum: 8b29c790181e44c4c95b9ffed1a8c1b6c2396eb949b95697cc390ca8c49d88ef9e2cd56bd4800b90a9bbc93681ae8d63045fc6fa06e00d84f532bef77967e751 languageName: node linkType: hard @@ -14109,8 +14034,8 @@ __metadata: linkType: hard "ws@npm:^8.13.0": - version: 8.15.1 - resolution: "ws@npm:8.15.1" + version: 8.16.0 + resolution: "ws@npm:8.16.0" peerDependencies: bufferutil: ^4.0.1 utf-8-validate: ">=5.0.2" @@ -14119,7 +14044,7 @@ __metadata: optional: true utf-8-validate: optional: true - checksum: 9964360dd5ab35c7376bd7c4295a3c8bd44ea0838c9413742548a6fb3ec371fc6c18552d5b8e76bdc21536db1909765612815bae072674b5ec69971605395a96 + checksum: a7783bb421c648b1e622b423409cb2a58ac5839521d2f689e84bc9dc41d59379c692dd405b15a997ea1d4c0c2e5314ad707332d0c558f15232d2bc07c0b4618a languageName: node linkType: hard @@ -14319,13 +14244,13 @@ __metadata: linkType: hard "zustand@npm:^4.3.1": - version: 4.4.7 - resolution: "zustand@npm:4.4.7" + version: 4.5.0 + resolution: "zustand@npm:4.5.0" dependencies: use-sync-external-store: "npm:1.2.0" peerDependencies: "@types/react": ">=16.8" - immer: ">=9.0" + immer: ">=9.0.6" react: ">=16.8" peerDependenciesMeta: "@types/react": @@ -14334,6 +14259,6 @@ __metadata: optional: true react: optional: true - checksum: de507f09eb79039d74d282df6ffac6c7fb6b840ca3620b0392bcbe3f9049902802db5448b4002e6fcb32f903f7ec1aea14602049eb2a3a8410bfea7186d72fb7 + checksum: 4802c4e26f2c1ec0d8e0ce8e596413cafb8b3d383e664de4eb30af63cedd23686f17b96a8a4d4583fc4470b187de0bbeafcfb94894efc50f6e62813e02a70b64 languageName: node linkType: hard From 0df888fff1686c36dd5ad79557f145f794dabe8d Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Jos=C3=A9=20Pedro=20Sousa?= Date: Tue, 23 Jan 2024 16:06:58 +0000 Subject: [PATCH 02/16] fix: fixing boxes wip --- boxes/blank/.gitignore | 4 +- boxes/blank/README.md | 14 +- boxes/token/README.md | 90 +- boxes/token/artifacts/Token.ts | 145 + boxes/token/package.json | 15 +- .../app/components/contract_function_form.tsx | 13 +- .../src/app/components/wallet_dropdown.tsx | 2 +- boxes/token/src/app/home.tsx | 4 +- boxes/token/src/config.ts | 30 +- boxes/token/src/environment/index.ts | 11 - .../{src => }/tests/token.contract.test.ts | 19 +- .../token/{src => }/tests/token_simulator.ts | 0 boxes/token/tsconfig.json | 3 +- boxes/yarn.lock | 68 +- yarn-project/yarn.lock | 4853 ++++++++--------- 15 files changed, 2438 insertions(+), 2833 deletions(-) create mode 100644 boxes/token/artifacts/Token.ts delete mode 100644 boxes/token/src/environment/index.ts rename boxes/token/{src => }/tests/token.contract.test.ts (99%) rename boxes/token/{src => }/tests/token_simulator.ts (100%) diff --git a/boxes/blank/.gitignore b/boxes/blank/.gitignore index 1270997806b..d37f6988611 100644 --- a/boxes/blank/.gitignore +++ b/boxes/blank/.gitignore @@ -3,5 +3,5 @@ node_modules dest -src/artifacts -src/contracts/target \ No newline at end of file +artifacts +src/contracts/target diff --git a/boxes/blank/README.md b/boxes/blank/README.md index 36286156a6e..2273e7871ed 100644 --- a/boxes/blank/README.md +++ b/boxes/blank/README.md @@ -1,6 +1,6 @@ # Aztec Box -This box is a one-stop-shop for Aztec that will deploy a blank React Page. You can use it as a boilerplate to start developing your own Aztec app in seconds! +This box is a one-stop-shop for Aztec that will deploy a minimal barebones HTML+JS page. You can use it as a boilerplate to start developing your own Aztec app in seconds! ## Prerequisites @@ -31,7 +31,7 @@ This will install all the dependencies and run the sandbox on port 8080 together Unbox the box with: ```bash -aztec-cli unbox blank-react +aztec-cli unbox blank ``` and install dependencies: @@ -56,12 +56,12 @@ In the `src/contracts` folder, you'll find the default contract being deployed. ## More info -There are five folders in your `src` folder: +Here's what is inside your `src` folder: -- `app` - This is your actual React app -- `scripts` - These are the scripts the frontend is using to talk with the sandbox - `contracts` - The Aztec Contracts you just deployed! -- `artifacts` - Auto-generated when you compile -- `test` - A boilerplate with a simple test +- `config.ts` - A file exporting environment, and other configurations you need +- `index.html` and `.ts` - The actual website you're deploying + +There's also a `test` folder with minimal testing you can expand on, and an `artifacts` folder should pop up once you run your app, these are the artifacts from your contract's compilation. Visit the [Aztec Docs](https://docs.aztec.network) for more information on how Aztec works, and the [Awesome Aztec Repository](https://github.com/AztecProtocol/awesome-aztec) for more cool projects, boilerplates and tooling. diff --git a/boxes/token/README.md b/boxes/token/README.md index 8c9189ca67e..06bfa7da227 100644 --- a/boxes/token/README.md +++ b/boxes/token/README.md @@ -1,75 +1,67 @@ -# Aztec Boxes +# Aztec Box -This is a minimal [Aztec](https://aztec.network/) Noir smart contract and frontend bootstrapped with [`aztec-cli unbox`](https://github.com/AztecProtocol/aztec-packages/tree/master/yarn-project/cli). It is recommended you use the `aztec-cli unbox PrivateToken` command so that the repository is copied with needed modifications from the monorepo subpackage. +This box is a one-stop-shop for Aztec that will deploy a full React web page featuring a wallet. You can use it as a boilerplate to start developing your own Aztec app in seconds! -Some contract specific settings for `PrivateToken` are in a [config](src/config.ts) will require manual updates depending on your changes to the source code. +## Prerequisites -## Setup +- You should have Docker installed. If you don't, follow [this guide](https://docs.aztec.network/dev_docs/getting_started/quickstart#install-docker). -Dependencies can be installed from the root of the package: +## Installation + +To start, run the Aztec install script: ```bash -yarn -yarn install:noir -yarn install:sandbox +bash -i <(curl -s install.aztec.network)` ``` -In addition to the usual javascript dependencies, this project requires `nargo` (package manager) and `noir` (a Domain Specific Language for SNARK proving systems) in addition to `@aztec/aztec-cli`. +After a few minutes, you should have all the Aztec CLI commands ready to run. -The former are installed within `yarn install:noir` which executes +### 1. Launching the sandbox -This sandbox requires [Docker](https://www.docker.com/) to be installed _and running_ locally. In the event the image needs updating, you can run `yarn install:sandbox`. +Run: -## Getting started +```bash +aztec-sandbox +``` -After `yarn` has run,`yarn start:sandbox` in one terminal will launch a local instance of the Aztec sandbox via Docker Compose and `yarn start:dev` will launch a frontend app for deploying and interacting with the PrivateToken contract. +This will install all the dependencies and run the sandbox on port 8080 together with a anvil node. -At this point, [http://localhost:5173](http://localhost:5173) should provide a minimal smart contract frontend. +### 2. Unboxing the box -This folder should have the following directory structure: +Unbox the box with: +```bash +aztec-cli unbox blank-react ``` -|— README.md -|— package.json -|— src - |-config.ts - PrivateToken specific configuration for the frontend. - | You may need to update this if you modify the contract functions. - |— app - |— [frontend React .tsx code files] - |- scripts - |- [helpers for frontend to interact with contract on the sandbox] - |— contracts - |— src - | The Noir smart contract source files are here. - |— main.nr - the cloned noir contract, your starting point - |- interface.nr - autogenerated from main.nr when you compile - |— Nargo.toml [Noir build file, includes Aztec smart contract dependencies] - |— artifacts - | These are both generated from `contracts/` by the compile command - |— private_token_contract.json - |— private_token.ts - |— tests - | A simple end2end test deploying and testing the Token on a local sandbox - | using the front end helper methods in app/scripts/ - | The test requires the sandbox and anvil to be running (yarn start:sandbox). - | You can run it via `yarn test:integration`. - |- token.test.ts + +and install dependencies: + +```bash +yarn ``` -Most relevant to you is likely `src/contracts/main.nr` (and the build config `src/contracts/Nargo.toml`). This contains the example PrivateToken logic that the frontend interacts with and is a good place to start writing Noir. +## Start developing -The `src/artifacts` folder can be re-generated from the command line with `yarn compile`. +Time to build. Run: + +```bash +yarn start +``` -This will generate a [contract artifact](src/artifacts/test_contract.json) and TypeScript class for the Aztec smart contract in `src/contracts/main.nr`, which the frontend uses to generate the UI. +Your React app is waiting for you on port 5176. Time to make it your own! -After compiling, you can re-deploy the updated noir smart contract from the web UI. The function interaction forms are generated from parsing the contract artifact, so they should update automatically after you recompile. +In the `src/contracts` folder, you'll find the default contract being deployed. Don't forget to recompile with `aztec-nargo compile`! Read the [aztec.nr documentation](https://docs.aztec.network/dev_docs/contracts/main) to get started with the `aztec.nr` framework. -## Learn More +[Read the full Sandbox reference](https://docs.aztec.network/dev_docs/cli/sandbox-reference) for more info on what exactly is happening on your machine! -To learn more about Noir Smart Contract development, take a look at the following resources: +## More info -- [Awesome Noir](https://github.com/noir-lang/awesome-noir) - learn about the Noir programming language. +There are five folders in your `src` folder: -## Deploy on Aztec3 +- `app` - This is your actual React app +- `scripts` - These are the scripts the frontend is using to talk with the sandbox +- `contracts` - The Aztec Contracts you just deployed! +- `artifacts` - Auto-generated when you compile +- `test` - A boilerplate with a simple test -Coming Soon :) +Visit the [Aztec Docs](https://docs.aztec.network) for more information on how Aztec works, and the [Awesome Aztec Repository](https://github.com/AztecProtocol/awesome-aztec) for more cool projects, boilerplates and tooling. diff --git a/boxes/token/artifacts/Token.ts b/boxes/token/artifacts/Token.ts new file mode 100644 index 00000000000..bfc61debb06 --- /dev/null +++ b/boxes/token/artifacts/Token.ts @@ -0,0 +1,145 @@ + +/* Autogenerated file, do not edit! */ + +/* eslint-disable */ +import { + AztecAddress, + AztecAddressLike, + CompleteAddress, + Contract, + ContractArtifact, + ContractBase, + ContractFunctionInteraction, + ContractMethod, + DeployMethod, + EthAddress, + EthAddressLike, + FieldLike, + Fr, + FunctionSelectorLike, + loadContractArtifact, + NoirCompiledContract, + Point, + PublicKey, + Wallet, +} from '@aztec/aztec.js'; +import TokenContractArtifactJson from '../src/contracts/target/token_contract-Token.json' assert { type: 'json' }; +export const TokenContractArtifact = loadContractArtifact(TokenContractArtifactJson as NoirCompiledContract); + +/** + * Type-safe interface for contract Token; + */ +export class TokenContract extends ContractBase { + + private constructor( + completeAddress: CompleteAddress, + wallet: Wallet, + portalContract = EthAddress.ZERO + ) { + super(completeAddress, TokenContractArtifact, wallet, portalContract); + } + + + + /** + * Creates a contract instance. + * @param address - The deployed contract's address. + * @param wallet - The wallet to use when interacting with the contract. + * @returns A promise that resolves to a new Contract instance. + */ + public static async at( + address: AztecAddress, + wallet: Wallet, + ) { + return Contract.at(address, TokenContract.artifact, wallet) as Promise; + } + + + /** + * Creates a tx to deploy a new instance of this contract. + */ + public static deploy(wallet: Wallet, admin: AztecAddressLike) { + return new DeployMethod(Point.ZERO, wallet, TokenContractArtifact, TokenContract.at, Array.from(arguments).slice(1)); + } + + /** + * Creates a tx to deploy a new instance of this contract using the specified public key to derive the address. + */ + public static deployWithPublicKey(publicKey: PublicKey, wallet: Wallet, admin: AztecAddressLike) { + return new DeployMethod(publicKey, wallet, TokenContractArtifact, TokenContract.at, Array.from(arguments).slice(2)); + } + + + + /** + * Returns this contract's artifact. + */ + public static get artifact(): ContractArtifact { + return TokenContractArtifact; + } + + + /** Type-safe wrappers for the public methods exposed by the contract. */ + public methods!: { + + /** mint_private(amount: field, secret_hash: field) */ + mint_private: ((amount: FieldLike, secret_hash: FieldLike) => ContractFunctionInteraction) & Pick; + + /** burn_public(from: struct, amount: field, nonce: field) */ + burn_public: ((from: AztecAddressLike, amount: FieldLike, nonce: FieldLike) => ContractFunctionInteraction) & Pick; + + /** is_minter(minter: struct) */ + is_minter: ((minter: AztecAddressLike) => ContractFunctionInteraction) & Pick; + + /** unshield(from: struct, to: struct, amount: field, nonce: field) */ + unshield: ((from: AztecAddressLike, to: AztecAddressLike, amount: FieldLike, nonce: FieldLike) => ContractFunctionInteraction) & Pick; + + /** total_supply() */ + total_supply: (() => ContractFunctionInteraction) & Pick; + + /** shield(from: struct, amount: field, secret_hash: field, nonce: field) */ + shield: ((from: AztecAddressLike, amount: FieldLike, secret_hash: FieldLike, nonce: FieldLike) => ContractFunctionInteraction) & Pick; + + /** compute_note_hash_and_nullifier(contract_address: struct, nonce: field, storage_slot: field, serialized_note: array) */ + compute_note_hash_and_nullifier: ((contract_address: AztecAddressLike, nonce: FieldLike, storage_slot: FieldLike, serialized_note: FieldLike[]) => ContractFunctionInteraction) & Pick; + + /** set_minter(minter: struct, approve: boolean) */ + set_minter: ((minter: AztecAddressLike, approve: boolean) => ContractFunctionInteraction) & Pick; + + /** burn(from: struct, amount: field, nonce: field) */ + burn: ((from: AztecAddressLike, amount: FieldLike, nonce: FieldLike) => ContractFunctionInteraction) & Pick; + + /** transfer_public(from: struct, to: struct, amount: field, nonce: field) */ + transfer_public: ((from: AztecAddressLike, to: AztecAddressLike, amount: FieldLike, nonce: FieldLike) => ContractFunctionInteraction) & Pick; + + /** mint_public(to: struct, amount: field) */ + mint_public: ((to: AztecAddressLike, amount: FieldLike) => ContractFunctionInteraction) & Pick; + + /** transfer(from: struct, to: struct, amount: field, nonce: field) */ + transfer: ((from: AztecAddressLike, to: AztecAddressLike, amount: FieldLike, nonce: FieldLike) => ContractFunctionInteraction) & Pick; + + /** _initialize(new_admin: struct) */ + _initialize: ((new_admin: AztecAddressLike) => ContractFunctionInteraction) & Pick; + + /** _reduce_total_supply(amount: field) */ + _reduce_total_supply: ((amount: FieldLike) => ContractFunctionInteraction) & Pick; + + /** redeem_shield(to: struct, amount: field, secret: field) */ + redeem_shield: ((to: AztecAddressLike, amount: FieldLike, secret: FieldLike) => ContractFunctionInteraction) & Pick; + + /** balance_of_public(owner: struct) */ + balance_of_public: ((owner: AztecAddressLike) => ContractFunctionInteraction) & Pick; + + /** set_admin(new_admin: struct) */ + set_admin: ((new_admin: AztecAddressLike) => ContractFunctionInteraction) & Pick; + + /** _increase_public_balance(to: struct, amount: field) */ + _increase_public_balance: ((to: AztecAddressLike, amount: FieldLike) => ContractFunctionInteraction) & Pick; + + /** balance_of_private(owner: struct) */ + balance_of_private: ((owner: AztecAddressLike) => ContractFunctionInteraction) & Pick; + + /** admin() */ + admin: (() => ContractFunctionInteraction) & Pick; + }; +} diff --git a/boxes/token/package.json b/boxes/token/package.json index 133a6bb0144..70985754c89 100644 --- a/boxes/token/package.json +++ b/boxes/token/package.json @@ -5,14 +5,15 @@ "type": "module", "main": "./dest/index.js", "scripts": { + "compile": "cd src/contracts && ${AZTEC_NARGO:-aztec-nargo} compile", + "codegen": "${AZTEC_CLI:-aztec-cli} codegen src/contracts/target -o artifacts --ts", "build": "yarn clean && yarn compile && yarn codegen && tsc -b && webpack", - "clean": "rm -rf ./dest .tsbuildinfo ./src/artifacts ./src/contracts/target", - "start": "serve -p 3000 ./dest", - "start:dev": "webpack serve --mode=development", + "clean": "rm -rf ./dest .tsbuildinfo ./artifacts ./src/contracts/target", + "prep": "yarn clean && yarn compile && yarn codegen", + "dev": "yarn prep && webpack serve --mode development", + "serve": "serve -p 3000 ./dest", "formatting": "prettier --check ./src && eslint ./src", "formatting:fix": "prettier -w ./src", - "compile": "cd src/contracts && ${AZTEC_NARGO:-aztec-nargo} compile", - "codegen": "${AZTEC_CLI:-aztec-cli} codegen src/contracts/target -o src/artifacts --ts", "test": "NODE_NO_WARNINGS=1 node --experimental-vm-modules $(yarn bin jest) --runInBand" }, "jest": { @@ -28,8 +29,8 @@ "moduleNameMapper": { "^(\\.{1,2}/.*)\\.js$": "$1" }, - "testRegex": "./src/.*\\.test\\.ts$", - "rootDir": "./src" + "testRegex": "tests/.*\\.test\\.ts$", + "rootDir": "./" }, "dependencies": { "@aztec/accounts": "^0.16.9", diff --git a/boxes/token/src/app/components/contract_function_form.tsx b/boxes/token/src/app/components/contract_function_form.tsx index 3f736a4e667..d92e046b3ed 100644 --- a/boxes/token/src/app/components/contract_function_form.tsx +++ b/boxes/token/src/app/components/contract_function_form.tsx @@ -113,13 +113,20 @@ async function handleFunctionCall( // for now, dont let user change the salt. requires some change to the form generation if we want to let user choose one // since everything is currently based on parsing the contractABI, and the salt parameter is not present there const salt = Fr.random(); - return await deployContract(wallet, artifact, typedArgs, salt, pxe); + return await deployContract(wallet, artifact, typedArgs, salt, pxe.getPxe()); } if (functionAbi.functionType === 'unconstrained') { - return await viewContractFunction(contractAddress!, artifact, functionName, typedArgs, pxe, wallet); + return await viewContractFunction(contractAddress!, artifact, functionName, typedArgs, pxe.getPxe(), wallet); } else { - const txnReceipt = await callContractFunction(contractAddress!, artifact, functionName, typedArgs, pxe, wallet); + const txnReceipt = await callContractFunction( + contractAddress!, + artifact, + functionName, + typedArgs, + pxe.getPxe(), + wallet, + ); return `Transaction ${txnReceipt.status} on block number ${txnReceipt.blockNumber}`; } } diff --git a/boxes/token/src/app/components/wallet_dropdown.tsx b/boxes/token/src/app/components/wallet_dropdown.tsx index 28cba3e570a..456bac8c45b 100644 --- a/boxes/token/src/app/components/wallet_dropdown.tsx +++ b/boxes/token/src/app/components/wallet_dropdown.tsx @@ -20,7 +20,7 @@ export function WalletDropdown({ selected, onSelectChange, onError }: Props) { return; } const loadOptions = async () => { - const fetchedOptions = await pxe.getRegisteredAccounts(); + const fetchedOptions = await pxe.getPxe().getRegisteredAccounts(); setOptions(fetchedOptions); onSelectChange(fetchedOptions[0]); }; diff --git a/boxes/token/src/app/home.tsx b/boxes/token/src/app/home.tsx index 8475b2afe55..10c3a77d4ff 100644 --- a/boxes/token/src/app/home.tsx +++ b/boxes/token/src/app/home.tsx @@ -1,4 +1,4 @@ -import { PXE_URL } from '../config.js'; +import { pxe } from '../config.js'; import { WalletDropdown } from './components/wallet_dropdown.js'; import { Contract } from './contract.js'; import styles from './home.module.scss'; @@ -73,7 +73,7 @@ export function Home() { <> {`Failed to load accounts. Error: ${selectWalletError}`}
- {`Make sure PXE from Aztec Sandbox is running at: ${PXE_URL}`} + {`Make sure PXE from Aztec Sandbox is running at: ${pxe.getPxeUrl()}`} )} {!selectWalletError && !selectedWallet && `No accounts.`} diff --git a/boxes/token/src/config.ts b/boxes/token/src/config.ts index 86d549894a3..9bcf667b1e8 100644 --- a/boxes/token/src/config.ts +++ b/boxes/token/src/config.ts @@ -1,12 +1,30 @@ -import { TokenContractArtifact } from './artifacts/Token.js'; -import { ContractArtifact, PXE, createPXEClient } from '@aztec/aztec.js'; +import { ContractArtifact, createPXEClient, waitForPXE } from '@aztec/aztec.js'; +import { TokenContractArtifact } from '../artifacts/Token.js'; -// update this if using a different contract +class PXE { + pxeUrl; + pxe; -export const contractArtifact: ContractArtifact = TokenContractArtifact; + constructor() { + this.pxeUrl = process.env.PXE_URL || 'http://localhost:8080'; + this.pxe = createPXEClient(this.pxeUrl); + } + + async setupPxe() { + await waitForPXE(this.pxe); + return this.pxe; + } -export const PXE_URL: string = process.env.PXE_URL || 'http://localhost:8080'; -export const pxe: PXE = createPXEClient(PXE_URL); + getPxeUrl() { + return this.pxeUrl; + } + getPxe() { + return this.pxe; + } +} + +export const pxe = new PXE(); +export const contractArtifact: ContractArtifact = TokenContractArtifact; export const CONTRACT_ADDRESS_PARAM_NAMES = ['owner', 'address', 'recipient']; export const FILTERED_FUNCTION_NAMES = ['compute_note_hash_and_nullifier']; diff --git a/boxes/token/src/environment/index.ts b/boxes/token/src/environment/index.ts deleted file mode 100644 index c64d7f6a2a3..00000000000 --- a/boxes/token/src/environment/index.ts +++ /dev/null @@ -1,11 +0,0 @@ -import { createPXEClient, waitForPXE } from '@aztec/aztec.js'; - -const { PXE_URL = 'http://localhost:8080' } = process.env; - -// assumes environment is running locally, which this script does not trigger -// as well as anvil. anvil can be started with yarn test:integration -export const setupEnvironment = async () => { - const pxe = createPXEClient(PXE_URL); - await waitForPXE(pxe); - return pxe; - }; \ No newline at end of file diff --git a/boxes/token/src/tests/token.contract.test.ts b/boxes/token/tests/token.contract.test.ts similarity index 99% rename from boxes/token/src/tests/token.contract.test.ts rename to boxes/token/tests/token.contract.test.ts index 5fbcfcd4179..6634e3834af 100644 --- a/boxes/token/src/tests/token.contract.test.ts +++ b/boxes/token/tests/token.contract.test.ts @@ -7,7 +7,6 @@ import { ExtendedNote, Fr, Note, - PXE, TxHash, TxStatus, computeAuthWitMessageHash, @@ -18,7 +17,7 @@ import { import { getInitialTestAccountsWallets } from '@aztec/accounts/testing'; import { afterEach, beforeAll, expect, jest } from '@jest/globals'; -import { setupEnvironment } from '../environment/index.js'; +import { pxe } from '../src/config.js'; const TIMEOUT = 60_000; @@ -32,7 +31,6 @@ describe('e2e_token_contract', () => { let asset: TokenContract; let tokenSim: TokenSimulator; - let pxe: PXE; const addPendingShieldNoteToPXE = async (accountIndex: number, amount: bigint, secretHash: Fr, txHash: TxHash) => { const storageSlot = new Fr(5); // The storage slot of `pending_shields` is 5. @@ -43,10 +41,9 @@ describe('e2e_token_contract', () => { beforeAll(async () => { logger = createDebugLogger('box:token_contract_test'); - pxe = await setupEnvironment(); // wallets = await createAccounts(pxe, 3); - accounts = await pxe.getRegisteredAccounts(); - wallets = await getInitialTestAccountsWallets(pxe); + accounts = await pxe.getPxe().getRegisteredAccounts(); + wallets = await getInitialTestAccountsWallets(pxe.getPxe()); logger(`Accounts: ${accounts.map(a => a.toReadableString())}`); logger(`Wallets: ${wallets.map(w => w.getAddress().toString())}`); @@ -217,7 +214,7 @@ describe('e2e_token_contract', () => { }); }); - describe('Transfer', () => { + describe.skip('Transfer', () => { describe('public', () => { it('transfer less than balance', async () => { const balance0 = await asset.methods.balance_of_public(accounts[0].address).view(); @@ -369,7 +366,7 @@ describe('e2e_token_contract', () => { }); }); - describe('private', () => { + describe.skip('private', () => { it('transfer less than balance', async () => { const balance0 = await asset.methods.balance_of_private(accounts[0].address).view(); const amount = balance0 / 2n; @@ -509,7 +506,7 @@ describe('e2e_token_contract', () => { }); }); - describe('Shielding (shield + redeem_shield)', () => { + describe.skip('Shielding (shield + redeem_shield)', () => { const secret = Fr.random(); let secretHash: Fr; @@ -634,7 +631,7 @@ describe('e2e_token_contract', () => { }); }); - describe('Unshielding', () => { + describe.skip('Unshielding', () => { it('on behalf of self', async () => { const balancePriv = await asset.methods.balance_of_private(accounts[0].address).view(); const amount = balancePriv / 2n; @@ -746,7 +743,7 @@ describe('e2e_token_contract', () => { }); }); - describe('Burn', () => { + describe.skip('Burn', () => { describe('public', () => { it('burn less than balance', async () => { const balance0 = await asset.methods.balance_of_public(accounts[0].address).view(); diff --git a/boxes/token/src/tests/token_simulator.ts b/boxes/token/tests/token_simulator.ts similarity index 100% rename from boxes/token/src/tests/token_simulator.ts rename to boxes/token/tests/token_simulator.ts diff --git a/boxes/token/tsconfig.json b/boxes/token/tsconfig.json index acc585a3b0e..748007e620e 100644 --- a/boxes/token/tsconfig.json +++ b/boxes/token/tsconfig.json @@ -1,6 +1,5 @@ { "compilerOptions": { - "rootDir": "src", "outDir": "dest", "tsBuildInfoFile": ".tsbuildinfo", "target": "es2020", @@ -21,5 +20,5 @@ "skipLibCheck": true, "jsx": "react-jsx" }, - "include": ["src", "src/contracts/target/*.json"] + "include": ["src", "src/*", "tests/*", "tests", "src/contracts/target/*.json", "artifacts", "artifacts/*"] } diff --git a/boxes/yarn.lock b/boxes/yarn.lock index fd941bea4da..3970be8d047 100644 --- a/boxes/yarn.lock +++ b/boxes/yarn.lock @@ -1827,15 +1827,6 @@ __metadata: languageName: node linkType: hard -"@noble/curves@npm:1.1.0, @noble/curves@npm:~1.1.0": - version: 1.1.0 - resolution: "@noble/curves@npm:1.1.0" - dependencies: - "@noble/hashes": "npm:1.3.1" - checksum: 81115c3ebfa7e7da2d7e18d44d686f98dc6d35dbde3964412c05707c92d0994a01545bc265d5c0bc05c8c49333f75b99c9acef6750f5a79b3abcc8e0546acf88 - languageName: node - linkType: hard - "@noble/curves@npm:1.2.0, @noble/curves@npm:~1.2.0": version: 1.2.0 resolution: "@noble/curves@npm:1.2.0" @@ -1845,7 +1836,7 @@ __metadata: languageName: node linkType: hard -"@noble/curves@npm:^1.2.0": +"@noble/curves@npm:1.3.0, @noble/curves@npm:^1.2.0, @noble/curves@npm:~1.3.0": version: 1.3.0 resolution: "@noble/curves@npm:1.3.0" dependencies: @@ -1854,13 +1845,6 @@ __metadata: languageName: node linkType: hard -"@noble/hashes@npm:1.3.1": - version: 1.3.1 - resolution: "@noble/hashes@npm:1.3.1" - checksum: 86512713aaf338bced594bc2046ab249fea4e1ba1e7f2ecd02151ef1b8536315e788c11608fafe1b56f04fad1aa3c602da7e5f8e5fcd5f8b0aa94435fe65278e - languageName: node - linkType: hard - "@noble/hashes@npm:1.3.2": version: 1.3.2 resolution: "@noble/hashes@npm:1.3.2" @@ -1868,7 +1852,7 @@ __metadata: languageName: node linkType: hard -"@noble/hashes@npm:1.3.3, @noble/hashes@npm:^1.3.1, @noble/hashes@npm:~1.3.0, @noble/hashes@npm:~1.3.1, @noble/hashes@npm:~1.3.2": +"@noble/hashes@npm:1.3.3, @noble/hashes@npm:^1.3.1, @noble/hashes@npm:~1.3.0, @noble/hashes@npm:~1.3.2": version: 1.3.3 resolution: "@noble/hashes@npm:1.3.3" checksum: 23c020b33da4172c988e44100e33cd9f8f6250b68b43c467d3551f82070ebd9716e0d9d2347427aa3774c85934a35fa9ee6f026fca2117e3fa12db7bedae7668 @@ -2193,24 +2177,13 @@ __metadata: languageName: node linkType: hard -"@scure/base@npm:^1.1.3, @scure/base@npm:~1.1.0, @scure/base@npm:~1.1.2": +"@scure/base@npm:^1.1.3, @scure/base@npm:~1.1.0, @scure/base@npm:~1.1.2, @scure/base@npm:~1.1.4": version: 1.1.5 resolution: "@scure/base@npm:1.1.5" checksum: 6eb07be0202fac74a57c79d0d00a45f6f7e57447010c1e3d90a4275d197829727b7abc54b248fc6f9bef9ae374f7be5ee9154dde5b5b73da773560bf17aa8504 languageName: node linkType: hard -"@scure/bip32@npm:1.3.1": - version: 1.3.1 - resolution: "@scure/bip32@npm:1.3.1" - dependencies: - "@noble/curves": "npm:~1.1.0" - "@noble/hashes": "npm:~1.3.1" - "@scure/base": "npm:~1.1.0" - checksum: 9ff0ad56f512794aed1ed62e582bf855db829e688235420a116b210169dc31e3e2a8cc4a908126aaa07b6dcbcc4cd085eb12f9d0a8b507a88946d6171a437195 - languageName: node - linkType: hard - "@scure/bip32@npm:1.3.2": version: 1.3.2 resolution: "@scure/bip32@npm:1.3.2" @@ -2222,6 +2195,17 @@ __metadata: languageName: node linkType: hard +"@scure/bip32@npm:1.3.3": + version: 1.3.3 + resolution: "@scure/bip32@npm:1.3.3" + dependencies: + "@noble/curves": "npm:~1.3.0" + "@noble/hashes": "npm:~1.3.2" + "@scure/base": "npm:~1.1.4" + checksum: 48fa04ebf0e3b56e3d086f029ae207ea753d8d8a1b3564f3c80fafea63dc3ee4edbd21e44eadb79bd4de4afffb075cbbbcb258fd5030a9680065cb524424eb83 + languageName: node + linkType: hard + "@scure/bip39@npm:1.2.1": version: 1.2.1 resolution: "@scure/bip39@npm:1.2.1" @@ -2232,6 +2216,16 @@ __metadata: languageName: node linkType: hard +"@scure/bip39@npm:1.2.2": + version: 1.2.2 + resolution: "@scure/bip39@npm:1.2.2" + dependencies: + "@noble/hashes": "npm:~1.3.2" + "@scure/base": "npm:~1.1.4" + checksum: be38bc1dc10b9a763d8b02d91dc651a4f565c822486df6cb1d3cc84896c1aab3ef6acbf7b3dc7e4a981bc9366086a4d72020aa21e11a692734a750de049c887c + languageName: node + linkType: hard + "@sinclair/typebox@npm:^0.27.8": version: 0.27.8 resolution: "@sinclair/typebox@npm:0.27.8" @@ -6531,14 +6525,14 @@ __metadata: linkType: hard "ethereum-cryptography@npm:^2.0.0": - version: 2.1.2 - resolution: "ethereum-cryptography@npm:2.1.2" + version: 2.1.3 + resolution: "ethereum-cryptography@npm:2.1.3" dependencies: - "@noble/curves": "npm:1.1.0" - "@noble/hashes": "npm:1.3.1" - "@scure/bip32": "npm:1.3.1" - "@scure/bip39": "npm:1.2.1" - checksum: 784552709e3afd4ae9c606f3cf04ced49ab69f3864df58aca64f15317641470afd44573cbda821b9cf6781dac6dd3a95559fcc062299e23394094a3370387ec6 + "@noble/curves": "npm:1.3.0" + "@noble/hashes": "npm:1.3.3" + "@scure/bip32": "npm:1.3.3" + "@scure/bip39": "npm:1.2.2" + checksum: a2f25ad5ffa44b4364b1540a57969ee6f1dd820aa08a446f40f31203fef54a09442a6c099e70e7c1485922f6391c4c45b90f2c401e04d88ac9cc4611b05e606f languageName: node linkType: hard diff --git a/yarn-project/yarn.lock b/yarn-project/yarn.lock index 5e7bbb29474..e22e0a806fc 100644 --- a/yarn-project/yarn.lock +++ b/yarn-project/yarn.lock @@ -13,30 +13,37 @@ __metadata: linkType: hard "@achingbrain/nat-port-mapper@npm:^1.0.9": - version: 1.0.9 - resolution: "@achingbrain/nat-port-mapper@npm:1.0.9" + version: 1.0.13 + resolution: "@achingbrain/nat-port-mapper@npm:1.0.13" dependencies: "@achingbrain/ssdp": ^4.0.1 - "@libp2p/logger": ^2.0.0 - default-gateway: ^6.0.2 + "@libp2p/logger": ^4.0.1 + default-gateway: ^7.2.2 err-code: ^3.0.1 it-first: ^3.0.1 p-defer: ^4.0.0 p-timeout: ^6.1.1 xml2js: ^0.6.0 - checksum: 4d8b97ff6eceab530a51682f4f2d5b91101de029b429693bdcff73f19617770fe4e3271b353c3d932c419ed9fd1c0d6ba5bd98b716aab48c64c1d836b2d8020a + checksum: 0ece5a52be65fcb26e75918c39a5dad76a7752103bb32686e564e4c8f10e658e80e0e0d82acca6653cff4bac938faae06d71ceaef7a38916b9928b68a78c2c2b languageName: node linkType: hard "@achingbrain/ssdp@npm:^4.0.1": - version: 4.0.4 - resolution: "@achingbrain/ssdp@npm:4.0.4" + version: 4.0.6 + resolution: "@achingbrain/ssdp@npm:4.0.6" dependencies: event-iterator: ^2.0.0 freeport-promise: ^2.0.0 merge-options: ^3.0.4 - xml2js: ^0.5.0 - checksum: ebd5b25f64a2cd8fd14e760f92824cc308784d833eba887bfabc68a678de692eab83ac6c6859e56a187b7ea78e4fbdc9403899ed2be9cb245e0591a925431565 + xml2js: ^0.6.2 + checksum: 18af3ebf0ddd331531730b3c998729cb3db8f915b7f8296e503babf813a42af284e62b271148ac3ac85aa2420bb1c813c2d3b661c90e75f2e6d8700f9302f50b + languageName: node + linkType: hard + +"@adraffy/ens-normalize@npm:1.10.0": + version: 1.10.0 + resolution: "@adraffy/ens-normalize@npm:1.10.0" + checksum: af0540f963a2632da2bbc37e36ea6593dcfc607b937857133791781e246d47f870d5e3d21fa70d5cfe94e772c284588c81ea3f5b7f4ea8fbb824369444e4dbcb languageName: node linkType: hard @@ -899,42 +906,43 @@ __metadata: languageName: unknown linkType: soft -"@babel/code-frame@npm:^7.0.0, @babel/code-frame@npm:^7.12.13, @babel/code-frame@npm:^7.16.7, @babel/code-frame@npm:^7.22.5": - version: 7.22.5 - resolution: "@babel/code-frame@npm:7.22.5" +"@babel/code-frame@npm:^7.0.0, @babel/code-frame@npm:^7.12.13, @babel/code-frame@npm:^7.22.13, @babel/code-frame@npm:^7.23.5": + version: 7.23.5 + resolution: "@babel/code-frame@npm:7.23.5" dependencies: - "@babel/highlight": ^7.22.5 - checksum: cfe804f518f53faaf9a1d3e0f9f74127ab9a004912c3a16fda07fb6a633393ecb9918a053cb71804204c1b7ec3d49e1699604715e2cfb0c9f7bc4933d324ebb6 + "@babel/highlight": ^7.23.4 + chalk: ^2.4.2 + checksum: d90981fdf56a2824a9b14d19a4c0e8db93633fd488c772624b4e83e0ceac6039a27cd298a247c3214faa952bf803ba23696172ae7e7235f3b97f43ba278c569a languageName: node linkType: hard -"@babel/compat-data@npm:^7.22.9": - version: 7.22.9 - resolution: "@babel/compat-data@npm:7.22.9" - checksum: bed77d9044ce948b4327b30dd0de0779fa9f3a7ed1f2d31638714ed00229fa71fc4d1617ae0eb1fad419338d3658d0e9a5a083297451e09e73e078d0347ff808 +"@babel/compat-data@npm:^7.23.5": + version: 7.23.5 + resolution: "@babel/compat-data@npm:7.23.5" + checksum: 06ce244cda5763295a0ea924728c09bae57d35713b675175227278896946f922a63edf803c322f855a3878323d48d0255a2a3023409d2a123483c8a69ebb4744 languageName: node linkType: hard "@babel/core@npm:^7.11.6, @babel/core@npm:^7.12.3": - version: 7.22.9 - resolution: "@babel/core@npm:7.22.9" + version: 7.23.7 + resolution: "@babel/core@npm:7.23.7" dependencies: "@ampproject/remapping": ^2.2.0 - "@babel/code-frame": ^7.22.5 - "@babel/generator": ^7.22.9 - "@babel/helper-compilation-targets": ^7.22.9 - "@babel/helper-module-transforms": ^7.22.9 - "@babel/helpers": ^7.22.6 - "@babel/parser": ^7.22.7 - "@babel/template": ^7.22.5 - "@babel/traverse": ^7.22.8 - "@babel/types": ^7.22.5 - convert-source-map: ^1.7.0 + "@babel/code-frame": ^7.23.5 + "@babel/generator": ^7.23.6 + "@babel/helper-compilation-targets": ^7.23.6 + "@babel/helper-module-transforms": ^7.23.3 + "@babel/helpers": ^7.23.7 + "@babel/parser": ^7.23.6 + "@babel/template": ^7.22.15 + "@babel/traverse": ^7.23.7 + "@babel/types": ^7.23.6 + convert-source-map: ^2.0.0 debug: ^4.1.0 gensync: ^1.0.0-beta.2 - json5: ^2.2.2 + json5: ^2.2.3 semver: ^6.3.1 - checksum: 7bf069aeceb417902c4efdaefab1f7b94adb7dea694a9aed1bda2edf4135348a080820529b1a300c6f8605740a00ca00c19b2d5e74b5dd489d99d8c11d5e56d1 + checksum: 32d5bf73372a47429afaae9adb0af39e47bcea6a831c4b5dcbb4791380cda6949cb8cb1a2fea8b60bb1ebe189209c80e333903df1fa8e9dcb04798c0ce5bf59e languageName: node linkType: hard @@ -949,51 +957,49 @@ __metadata: languageName: node linkType: hard -"@babel/generator@npm:^7.17.3, @babel/generator@npm:^7.22.7, @babel/generator@npm:^7.22.9, @babel/generator@npm:^7.7.2": - version: 7.22.9 - resolution: "@babel/generator@npm:7.22.9" +"@babel/generator@npm:^7.23.0, @babel/generator@npm:^7.23.6, @babel/generator@npm:^7.7.2": + version: 7.23.6 + resolution: "@babel/generator@npm:7.23.6" dependencies: - "@babel/types": ^7.22.5 + "@babel/types": ^7.23.6 "@jridgewell/gen-mapping": ^0.3.2 "@jridgewell/trace-mapping": ^0.3.17 jsesc: ^2.5.1 - checksum: 7c9d2c58b8d5ac5e047421a6ab03ec2ff5d9a5ff2c2212130a0055e063ac349e0b19d435537d6886c999771aef394832e4f54cd9fc810100a7f23d982f6af06b + checksum: 1a1a1c4eac210f174cd108d479464d053930a812798e09fee069377de39a893422df5b5b146199ead7239ae6d3a04697b45fc9ac6e38e0f6b76374390f91fc6c languageName: node linkType: hard -"@babel/helper-compilation-targets@npm:^7.22.9": - version: 7.22.9 - resolution: "@babel/helper-compilation-targets@npm:7.22.9" +"@babel/helper-compilation-targets@npm:^7.23.6": + version: 7.23.6 + resolution: "@babel/helper-compilation-targets@npm:7.23.6" dependencies: - "@babel/compat-data": ^7.22.9 - "@babel/helper-validator-option": ^7.22.5 - browserslist: ^4.21.9 + "@babel/compat-data": ^7.23.5 + "@babel/helper-validator-option": ^7.23.5 + browserslist: ^4.22.2 lru-cache: ^5.1.1 semver: ^6.3.1 - peerDependencies: - "@babel/core": ^7.0.0 - checksum: ea0006c6a93759025f4a35a25228ae260538c9f15023e8aac2a6d45ca68aef4cf86cfc429b19af9a402cbdd54d5de74ad3fbcf6baa7e48184dc079f1a791e178 + checksum: c630b98d4527ac8fe2c58d9a06e785dfb2b73ec71b7c4f2ddf90f814b5f75b547f3c015f110a010fd31f76e3864daaf09f3adcd2f6acdbfb18a8de3a48717590 languageName: node linkType: hard -"@babel/helper-environment-visitor@npm:^7.16.7, @babel/helper-environment-visitor@npm:^7.22.5": - version: 7.22.5 - resolution: "@babel/helper-environment-visitor@npm:7.22.5" - checksum: 248532077d732a34cd0844eb7b078ff917c3a8ec81a7f133593f71a860a582f05b60f818dc5049c2212e5baa12289c27889a4b81d56ef409b4863db49646c4b1 +"@babel/helper-environment-visitor@npm:^7.22.20": + version: 7.22.20 + resolution: "@babel/helper-environment-visitor@npm:7.22.20" + checksum: d80ee98ff66f41e233f36ca1921774c37e88a803b2f7dca3db7c057a5fea0473804db9fb6729e5dbfd07f4bed722d60f7852035c2c739382e84c335661590b69 languageName: node linkType: hard -"@babel/helper-function-name@npm:^7.16.7, @babel/helper-function-name@npm:^7.22.5": - version: 7.22.5 - resolution: "@babel/helper-function-name@npm:7.22.5" +"@babel/helper-function-name@npm:^7.23.0": + version: 7.23.0 + resolution: "@babel/helper-function-name@npm:7.23.0" dependencies: - "@babel/template": ^7.22.5 - "@babel/types": ^7.22.5 - checksum: 6b1f6ce1b1f4e513bf2c8385a557ea0dd7fa37971b9002ad19268ca4384bbe90c09681fe4c076013f33deabc63a53b341ed91e792de741b4b35e01c00238177a + "@babel/template": ^7.22.15 + "@babel/types": ^7.23.0 + checksum: e44542257b2d4634a1f979244eb2a4ad8e6d75eb6761b4cfceb56b562f7db150d134bc538c8e6adca3783e3bc31be949071527aa8e3aab7867d1ad2d84a26e10 languageName: node linkType: hard -"@babel/helper-hoist-variables@npm:^7.16.7, @babel/helper-hoist-variables@npm:^7.22.5": +"@babel/helper-hoist-variables@npm:^7.22.5": version: 7.22.5 resolution: "@babel/helper-hoist-variables@npm:7.22.5" dependencies: @@ -1002,27 +1008,27 @@ __metadata: languageName: node linkType: hard -"@babel/helper-module-imports@npm:^7.22.5": - version: 7.22.5 - resolution: "@babel/helper-module-imports@npm:7.22.5" +"@babel/helper-module-imports@npm:^7.22.15": + version: 7.22.15 + resolution: "@babel/helper-module-imports@npm:7.22.15" dependencies: - "@babel/types": ^7.22.5 - checksum: 9ac2b0404fa38b80bdf2653fbeaf8e8a43ccb41bd505f9741d820ed95d3c4e037c62a1bcdcb6c9527d7798d2e595924c4d025daed73283badc180ada2c9c49ad + "@babel/types": ^7.22.15 + checksum: ecd7e457df0a46f889228f943ef9b4a47d485d82e030676767e6a2fdcbdaa63594d8124d4b55fd160b41c201025aec01fc27580352b1c87a37c9c6f33d116702 languageName: node linkType: hard -"@babel/helper-module-transforms@npm:^7.22.9": - version: 7.22.9 - resolution: "@babel/helper-module-transforms@npm:7.22.9" +"@babel/helper-module-transforms@npm:^7.23.3": + version: 7.23.3 + resolution: "@babel/helper-module-transforms@npm:7.23.3" dependencies: - "@babel/helper-environment-visitor": ^7.22.5 - "@babel/helper-module-imports": ^7.22.5 + "@babel/helper-environment-visitor": ^7.22.20 + "@babel/helper-module-imports": ^7.22.15 "@babel/helper-simple-access": ^7.22.5 "@babel/helper-split-export-declaration": ^7.22.6 - "@babel/helper-validator-identifier": ^7.22.5 + "@babel/helper-validator-identifier": ^7.22.20 peerDependencies: "@babel/core": ^7.0.0 - checksum: 2751f77660518cf4ff027514d6f4794f04598c6393be7b04b8e46c6e21606e11c19f3f57ab6129a9c21bacdf8b3ffe3af87bb401d972f34af2d0ffde02ac3001 + checksum: 5d0895cfba0e16ae16f3aa92fee108517023ad89a855289c4eb1d46f7aef4519adf8e6f971e1d55ac20c5461610e17213f1144097a8f932e768a9132e2278d71 languageName: node linkType: hard @@ -1042,7 +1048,7 @@ __metadata: languageName: node linkType: hard -"@babel/helper-split-export-declaration@npm:^7.16.7, @babel/helper-split-export-declaration@npm:^7.22.6": +"@babel/helper-split-export-declaration@npm:^7.22.6": version: 7.22.6 resolution: "@babel/helper-split-export-declaration@npm:7.22.6" dependencies: @@ -1051,64 +1057,55 @@ __metadata: languageName: node linkType: hard -"@babel/helper-string-parser@npm:^7.22.5": - version: 7.22.5 - resolution: "@babel/helper-string-parser@npm:7.22.5" - checksum: 836851ca5ec813077bbb303acc992d75a360267aa3b5de7134d220411c852a6f17de7c0d0b8c8dcc0f567f67874c00f4528672b2a4f1bc978a3ada64c8c78467 +"@babel/helper-string-parser@npm:^7.23.4": + version: 7.23.4 + resolution: "@babel/helper-string-parser@npm:7.23.4" + checksum: c0641144cf1a7e7dc93f3d5f16d5327465b6cf5d036b48be61ecba41e1eece161b48f46b7f960951b67f8c3533ce506b16dece576baef4d8b3b49f8c65410f90 languageName: node linkType: hard -"@babel/helper-validator-identifier@npm:^7.16.7, @babel/helper-validator-identifier@npm:^7.22.5": - version: 7.22.5 - resolution: "@babel/helper-validator-identifier@npm:7.22.5" - checksum: 7f0f30113474a28298c12161763b49de5018732290ca4de13cdaefd4fd0d635a6fe3f6686c37a02905fb1e64f21a5ee2b55140cf7b070e729f1bd66866506aea +"@babel/helper-validator-identifier@npm:^7.16.7, @babel/helper-validator-identifier@npm:^7.22.20": + version: 7.22.20 + resolution: "@babel/helper-validator-identifier@npm:7.22.20" + checksum: 136412784d9428266bcdd4d91c32bcf9ff0e8d25534a9d94b044f77fe76bc50f941a90319b05aafd1ec04f7d127cd57a179a3716009ff7f3412ef835ada95bdc languageName: node linkType: hard -"@babel/helper-validator-option@npm:^7.22.5": - version: 7.22.5 - resolution: "@babel/helper-validator-option@npm:7.22.5" - checksum: bbeca8a85ee86990215c0424997438b388b8d642d69b9f86c375a174d3cdeb270efafd1ff128bc7a1d370923d13b6e45829ba8581c027620e83e3a80c5c414b3 +"@babel/helper-validator-option@npm:^7.23.5": + version: 7.23.5 + resolution: "@babel/helper-validator-option@npm:7.23.5" + checksum: 537cde2330a8aede223552510e8a13e9c1c8798afee3757995a7d4acae564124fe2bf7e7c3d90d62d3657434a74340a274b3b3b1c6f17e9a2be1f48af29cb09e languageName: node linkType: hard -"@babel/helpers@npm:^7.22.6": - version: 7.22.6 - resolution: "@babel/helpers@npm:7.22.6" +"@babel/helpers@npm:^7.23.7": + version: 7.23.8 + resolution: "@babel/helpers@npm:7.23.8" dependencies: - "@babel/template": ^7.22.5 - "@babel/traverse": ^7.22.6 - "@babel/types": ^7.22.5 - checksum: 5c1f33241fe7bf7709868c2105134a0a86dca26a0fbd508af10a89312b1f77ca38ebae43e50be3b208613c5eacca1559618af4ca236f0abc55d294800faeff30 + "@babel/template": ^7.22.15 + "@babel/traverse": ^7.23.7 + "@babel/types": ^7.23.6 + checksum: 8b522d527921f8df45a983dc7b8e790c021250addf81ba7900ba016e165442a527348f6f877aa55e1debb3eef9e860a334b4e8d834e6c9b438ed61a63d9a7ad4 languageName: node linkType: hard -"@babel/highlight@npm:^7.22.5": - version: 7.22.5 - resolution: "@babel/highlight@npm:7.22.5" +"@babel/highlight@npm:^7.23.4": + version: 7.23.4 + resolution: "@babel/highlight@npm:7.23.4" dependencies: - "@babel/helper-validator-identifier": ^7.22.5 - chalk: ^2.0.0 + "@babel/helper-validator-identifier": ^7.22.20 + chalk: ^2.4.2 js-tokens: ^4.0.0 - checksum: f61ae6de6ee0ea8d9b5bcf2a532faec5ab0a1dc0f7c640e5047fc61630a0edb88b18d8c92eb06566d30da7a27db841aca11820ecd3ebe9ce514c9350fbed39c4 + checksum: 643acecdc235f87d925979a979b539a5d7d1f31ae7db8d89047269082694122d11aa85351304c9c978ceeb6d250591ccadb06c366f358ccee08bb9c122476b89 languageName: node linkType: hard -"@babel/parser@npm:^7.0.0, @babel/parser@npm:^7.21.4": - version: 7.23.0 - resolution: "@babel/parser@npm:7.23.0" +"@babel/parser@npm:^7.0.0, @babel/parser@npm:^7.1.0, @babel/parser@npm:^7.14.7, @babel/parser@npm:^7.20.5, @babel/parser@npm:^7.20.7, @babel/parser@npm:^7.21.4, @babel/parser@npm:^7.22.15, @babel/parser@npm:^7.23.0, @babel/parser@npm:^7.23.6": + version: 7.23.6 + resolution: "@babel/parser@npm:7.23.6" bin: parser: ./bin/babel-parser.js - checksum: 453fdf8b9e2c2b7d7b02139e0ce003d1af21947bbc03eb350fb248ee335c9b85e4ab41697ddbdd97079698de825a265e45a0846bb2ed47a2c7c1df833f42a354 - languageName: node - linkType: hard - -"@babel/parser@npm:^7.1.0, @babel/parser@npm:^7.14.7, @babel/parser@npm:^7.17.3, @babel/parser@npm:^7.20.5, @babel/parser@npm:^7.20.7, @babel/parser@npm:^7.22.5, @babel/parser@npm:^7.22.7": - version: 7.22.7 - resolution: "@babel/parser@npm:7.22.7" - bin: - parser: ./bin/babel-parser.js - checksum: 02209ddbd445831ee8bf966fdf7c29d189ed4b14343a68eb2479d940e7e3846340d7cc6bd654a5f3d87d19dc84f49f50a58cf9363bee249dc5409ff3ba3dab54 + checksum: 140801c43731a6c41fd193f5c02bc71fd647a0360ca616b23d2db8be4b9739b9f951a03fc7c2db4f9b9214f4b27c1074db0f18bc3fa653783082d5af7c8860d5 languageName: node linkType: hard @@ -1168,13 +1165,13 @@ __metadata: linkType: hard "@babel/plugin-syntax-jsx@npm:^7.7.2": - version: 7.22.5 - resolution: "@babel/plugin-syntax-jsx@npm:7.22.5" + version: 7.23.3 + resolution: "@babel/plugin-syntax-jsx@npm:7.23.3" dependencies: "@babel/helper-plugin-utils": ^7.22.5 peerDependencies: "@babel/core": ^7.0.0-0 - checksum: 8829d30c2617ab31393d99cec2978e41f014f4ac6f01a1cecf4c4dd8320c3ec12fdc3ce121126b2d8d32f6887e99ca1a0bad53dedb1e6ad165640b92b24980ce + checksum: 89037694314a74e7f0e7a9c8d3793af5bf6b23d80950c29b360db1c66859d67f60711ea437e70ad6b5b4b29affe17eababda841b6c01107c2b638e0493bafb4e languageName: node linkType: hard @@ -1256,69 +1253,69 @@ __metadata: linkType: hard "@babel/plugin-syntax-typescript@npm:^7.7.2": - version: 7.22.5 - resolution: "@babel/plugin-syntax-typescript@npm:7.22.5" + version: 7.23.3 + resolution: "@babel/plugin-syntax-typescript@npm:7.23.3" dependencies: "@babel/helper-plugin-utils": ^7.22.5 peerDependencies: "@babel/core": ^7.0.0-0 - checksum: 8ab7718fbb026d64da93681a57797d60326097fd7cb930380c8bffd9eb101689e90142c760a14b51e8e69c88a73ba3da956cb4520a3b0c65743aee5c71ef360a + checksum: abfad3a19290d258b028e285a1f34c9b8a0cbe46ef79eafed4ed7ffce11b5d0720b5e536c82f91cbd8442cde35a3dd8e861fa70366d87ff06fdc0d4756e30876 languageName: node linkType: hard "@babel/runtime@npm:^7.21.0": - version: 7.22.6 - resolution: "@babel/runtime@npm:7.22.6" + version: 7.23.8 + resolution: "@babel/runtime@npm:7.23.8" dependencies: - regenerator-runtime: ^0.13.11 - checksum: e585338287c4514a713babf4fdb8fc2a67adcebab3e7723a739fc62c79cfda875b314c90fd25f827afb150d781af97bc16c85bfdbfa2889f06053879a1ddb597 + regenerator-runtime: ^0.14.0 + checksum: 0bd5543c26811153822a9f382fd39886f66825ff2a397a19008011376533747cd05c33a91f6248c0b8b0edf0448d7c167ebfba34786088f1b7eb11c65be7dfc3 languageName: node linkType: hard -"@babel/template@npm:^7.22.5, @babel/template@npm:^7.3.3": - version: 7.22.5 - resolution: "@babel/template@npm:7.22.5" +"@babel/template@npm:^7.22.15, @babel/template@npm:^7.3.3": + version: 7.22.15 + resolution: "@babel/template@npm:7.22.15" dependencies: - "@babel/code-frame": ^7.22.5 - "@babel/parser": ^7.22.5 - "@babel/types": ^7.22.5 - checksum: c5746410164039aca61829cdb42e9a55410f43cace6f51ca443313f3d0bdfa9a5a330d0b0df73dc17ef885c72104234ae05efede37c1cc8a72dc9f93425977a3 + "@babel/code-frame": ^7.22.13 + "@babel/parser": ^7.22.15 + "@babel/types": ^7.22.15 + checksum: 1f3e7dcd6c44f5904c184b3f7fe280394b191f2fed819919ffa1e529c259d5b197da8981b6ca491c235aee8dbad4a50b7e31304aa531271cb823a4a24a0dd8fd languageName: node linkType: hard -"@babel/traverse@npm:7.17.3": - version: 7.17.3 - resolution: "@babel/traverse@npm:7.17.3" +"@babel/traverse@npm:7.23.2": + version: 7.23.2 + resolution: "@babel/traverse@npm:7.23.2" dependencies: - "@babel/code-frame": ^7.16.7 - "@babel/generator": ^7.17.3 - "@babel/helper-environment-visitor": ^7.16.7 - "@babel/helper-function-name": ^7.16.7 - "@babel/helper-hoist-variables": ^7.16.7 - "@babel/helper-split-export-declaration": ^7.16.7 - "@babel/parser": ^7.17.3 - "@babel/types": ^7.17.0 + "@babel/code-frame": ^7.22.13 + "@babel/generator": ^7.23.0 + "@babel/helper-environment-visitor": ^7.22.20 + "@babel/helper-function-name": ^7.23.0 + "@babel/helper-hoist-variables": ^7.22.5 + "@babel/helper-split-export-declaration": ^7.22.6 + "@babel/parser": ^7.23.0 + "@babel/types": ^7.23.0 debug: ^4.1.0 globals: ^11.1.0 - checksum: 780d7ecf711758174989794891af08d378f81febdb8932056c0d9979524bf0298e28f8e7708a872d7781151506c28f56c85c63ea3f1f654662c2fcb8a3eb9fdc + checksum: 26a1eea0dde41ab99dde8b9773a013a0dc50324e5110a049f5d634e721ff08afffd54940b3974a20308d7952085ac769689369e9127dea655f868c0f6e1ab35d languageName: node linkType: hard -"@babel/traverse@npm:^7.22.6, @babel/traverse@npm:^7.22.8": - version: 7.22.8 - resolution: "@babel/traverse@npm:7.22.8" +"@babel/traverse@npm:^7.23.7": + version: 7.23.7 + resolution: "@babel/traverse@npm:7.23.7" dependencies: - "@babel/code-frame": ^7.22.5 - "@babel/generator": ^7.22.7 - "@babel/helper-environment-visitor": ^7.22.5 - "@babel/helper-function-name": ^7.22.5 + "@babel/code-frame": ^7.23.5 + "@babel/generator": ^7.23.6 + "@babel/helper-environment-visitor": ^7.22.20 + "@babel/helper-function-name": ^7.23.0 "@babel/helper-hoist-variables": ^7.22.5 "@babel/helper-split-export-declaration": ^7.22.6 - "@babel/parser": ^7.22.7 - "@babel/types": ^7.22.5 - debug: ^4.1.0 + "@babel/parser": ^7.23.6 + "@babel/types": ^7.23.6 + debug: ^4.3.1 globals: ^11.1.0 - checksum: a381369bc3eedfd13ed5fef7b884657f1c29024ea7388198149f0edc34bd69ce3966e9f40188d15f56490a5e12ba250ccc485f2882b53d41b054fccefb233e33 + checksum: d4a7afb922361f710efc97b1e25ec343fab8b2a4ddc81ca84f9a153f22d4482112cba8f263774be8d297918b6c4767c7a98988ab4e53ac73686c986711dd002e languageName: node linkType: hard @@ -1332,14 +1329,14 @@ __metadata: languageName: node linkType: hard -"@babel/types@npm:^7.0.0, @babel/types@npm:^7.17.0, @babel/types@npm:^7.20.7, @babel/types@npm:^7.22.5, @babel/types@npm:^7.3.3, @babel/types@npm:^7.8.3": - version: 7.22.5 - resolution: "@babel/types@npm:7.22.5" +"@babel/types@npm:^7.0.0, @babel/types@npm:^7.17.0, @babel/types@npm:^7.20.7, @babel/types@npm:^7.22.15, @babel/types@npm:^7.22.5, @babel/types@npm:^7.23.0, @babel/types@npm:^7.23.6, @babel/types@npm:^7.3.3, @babel/types@npm:^7.8.3": + version: 7.23.6 + resolution: "@babel/types@npm:7.23.6" dependencies: - "@babel/helper-string-parser": ^7.22.5 - "@babel/helper-validator-identifier": ^7.22.5 + "@babel/helper-string-parser": ^7.23.4 + "@babel/helper-validator-identifier": ^7.22.20 to-fast-properties: ^2.0.0 - checksum: c13a9c1dc7d2d1a241a2f8363540cb9af1d66e978e8984b400a20c4f38ba38ca29f06e26a0f2d49a70bad9e57615dac09c35accfddf1bb90d23cd3e0a0bab892 + checksum: 68187dbec0d637f79bc96263ac95ec8b06d424396678e7e225492be866414ce28ebc918a75354d4c28659be6efe30020b4f0f6df81cc418a2d30645b690a8de0 languageName: node linkType: hard @@ -1350,14 +1347,21 @@ __metadata: languageName: node linkType: hard -"@chainsafe/is-ip@npm:^2.0.1": - version: 2.0.1 - resolution: "@chainsafe/is-ip@npm:2.0.1" - checksum: c5bbebe58eadc700d12112713fd75e41ae26020eedcac0e8b593fdda16a180239ede1d68d55b500fd8b30189c9a82b5e10769ed86a816e879c83248069152b79 +"@chainsafe/as-chacha20poly1305@npm:^0.1.0": + version: 0.1.0 + resolution: "@chainsafe/as-chacha20poly1305@npm:0.1.0" + checksum: 2bf38f0595bb379489388174e2049e57ae1512815a80fddfe5b9055865739a970651847892233e53cde6a3e34554ede97d1abd8a0f02b4a0005a1a6cf0146a6c languageName: node linkType: hard -"@chainsafe/is-ip@npm:^2.0.2": +"@chainsafe/as-sha256@npm:^0.4.1": + version: 0.4.1 + resolution: "@chainsafe/as-sha256@npm:0.4.1" + checksum: 6d86975e648ecdafd366802278ac15b392b252e967f3681412ec48b5a3518b936cc5e977517499882b084991446d25787d98f8f585891943688cc81549a44e9a + languageName: node + linkType: hard + +"@chainsafe/is-ip@npm:^2.0.1, @chainsafe/is-ip@npm:^2.0.2": version: 2.0.2 resolution: "@chainsafe/is-ip@npm:2.0.2" checksum: 2600350ba1c8fbad5d1ebee71317beeb29fbaebf43780d89e30f8c6c2d27b95ebdab0284dfbab7336b5eb6d8ffcc7081e3e4c5b221889dc366463f83bbe38adb @@ -1365,14 +1369,16 @@ __metadata: linkType: hard "@chainsafe/libp2p-noise@npm:^13.0.0": - version: 13.0.0 - resolution: "@chainsafe/libp2p-noise@npm:13.0.0" + version: 13.0.5 + resolution: "@chainsafe/libp2p-noise@npm:13.0.5" dependencies: + "@chainsafe/as-chacha20poly1305": ^0.1.0 + "@chainsafe/as-sha256": ^0.4.1 "@libp2p/crypto": ^2.0.0 "@libp2p/interface": ^0.1.0 "@libp2p/logger": ^3.0.0 "@libp2p/peer-id": ^3.0.0 - "@noble/ciphers": ^0.1.4 + "@noble/ciphers": ^0.4.0 "@noble/curves": ^1.1.0 "@noble/hashes": ^1.3.1 it-byte-stream: ^1.0.0 @@ -1384,22 +1390,23 @@ __metadata: protons-runtime: ^5.0.0 uint8arraylist: ^2.4.3 uint8arrays: ^4.0.4 - checksum: 177c44003cb550b3ce1f867db2f5a3bb8f97bf368095a1a5bd4ccddd64bc2fc4167119a61fa6ed045abe8ef2dbbea9ddac9b8e1d6381d9056749b900029b8de5 + wherearewe: ^2.0.1 + checksum: 98dd78bfd547501280c7a318acfa81624016351f18e0a28debe451340418fd263f90ce8d7358d5b83f61429522de6631321606b98eda35609114041853057af1 languageName: node linkType: hard "@chainsafe/libp2p-yamux@npm:^5.0.0": - version: 5.0.0 - resolution: "@chainsafe/libp2p-yamux@npm:5.0.0" + version: 5.0.4 + resolution: "@chainsafe/libp2p-yamux@npm:5.0.4" dependencies: "@libp2p/interface": ^0.1.0 "@libp2p/logger": ^3.0.0 - abortable-iterator: ^5.0.1 + get-iterator: ^2.0.1 it-foreach: ^2.0.3 it-pipe: ^3.0.1 it-pushable: ^3.2.0 uint8arraylist: ^2.4.3 - checksum: 8de6f9e16097ed983ab3d0da2dffe752f09c2c09473783d39dcb2d54b788fe4353bbed417a3870fb4de151de906a734a73cd53e2b2b4ccec4392d2195828137c + checksum: 58c33b28d8da2b8c6813127de2cc4005f2f09d845cf535c56a3db0495774a2feb935d2813d2141bb6a747f3c1181dfde415945821d489ac7f987e36df744721f languageName: node linkType: hard @@ -1412,10 +1419,10 @@ __metadata: languageName: node linkType: hard -"@colors/colors@npm:1.5.0": - version: 1.5.0 - resolution: "@colors/colors@npm:1.5.0" - checksum: d64d5260bed1d5012ae3fc617d38d1afc0329fec05342f4e6b838f46998855ba56e0a73833f4a80fa8378c84810da254f76a8a19c39d038260dc06dc4e007425 +"@colors/colors@npm:1.6.0, @colors/colors@npm:^1.6.0": + version: 1.6.0 + resolution: "@colors/colors@npm:1.6.0" + checksum: aa209963e0c3218e80a4a20553ba8c0fbb6fa13140540b4e5f97923790be06801fc90172c1114fc8b7e888b3d012b67298cde6b9e81521361becfaee400c662f languageName: node linkType: hard @@ -1467,156 +1474,156 @@ __metadata: languageName: node linkType: hard -"@esbuild/android-arm64@npm:0.18.17": - version: 0.18.17 - resolution: "@esbuild/android-arm64@npm:0.18.17" +"@esbuild/android-arm64@npm:0.18.20": + version: 0.18.20 + resolution: "@esbuild/android-arm64@npm:0.18.20" conditions: os=android & cpu=arm64 languageName: node linkType: hard -"@esbuild/android-arm@npm:0.18.17": - version: 0.18.17 - resolution: "@esbuild/android-arm@npm:0.18.17" +"@esbuild/android-arm@npm:0.18.20": + version: 0.18.20 + resolution: "@esbuild/android-arm@npm:0.18.20" conditions: os=android & cpu=arm languageName: node linkType: hard -"@esbuild/android-x64@npm:0.18.17": - version: 0.18.17 - resolution: "@esbuild/android-x64@npm:0.18.17" +"@esbuild/android-x64@npm:0.18.20": + version: 0.18.20 + resolution: "@esbuild/android-x64@npm:0.18.20" conditions: os=android & cpu=x64 languageName: node linkType: hard -"@esbuild/darwin-arm64@npm:0.18.17": - version: 0.18.17 - resolution: "@esbuild/darwin-arm64@npm:0.18.17" +"@esbuild/darwin-arm64@npm:0.18.20": + version: 0.18.20 + resolution: "@esbuild/darwin-arm64@npm:0.18.20" conditions: os=darwin & cpu=arm64 languageName: node linkType: hard -"@esbuild/darwin-x64@npm:0.18.17": - version: 0.18.17 - resolution: "@esbuild/darwin-x64@npm:0.18.17" +"@esbuild/darwin-x64@npm:0.18.20": + version: 0.18.20 + resolution: "@esbuild/darwin-x64@npm:0.18.20" conditions: os=darwin & cpu=x64 languageName: node linkType: hard -"@esbuild/freebsd-arm64@npm:0.18.17": - version: 0.18.17 - resolution: "@esbuild/freebsd-arm64@npm:0.18.17" +"@esbuild/freebsd-arm64@npm:0.18.20": + version: 0.18.20 + resolution: "@esbuild/freebsd-arm64@npm:0.18.20" conditions: os=freebsd & cpu=arm64 languageName: node linkType: hard -"@esbuild/freebsd-x64@npm:0.18.17": - version: 0.18.17 - resolution: "@esbuild/freebsd-x64@npm:0.18.17" +"@esbuild/freebsd-x64@npm:0.18.20": + version: 0.18.20 + resolution: "@esbuild/freebsd-x64@npm:0.18.20" conditions: os=freebsd & cpu=x64 languageName: node linkType: hard -"@esbuild/linux-arm64@npm:0.18.17": - version: 0.18.17 - resolution: "@esbuild/linux-arm64@npm:0.18.17" +"@esbuild/linux-arm64@npm:0.18.20": + version: 0.18.20 + resolution: "@esbuild/linux-arm64@npm:0.18.20" conditions: os=linux & cpu=arm64 languageName: node linkType: hard -"@esbuild/linux-arm@npm:0.18.17": - version: 0.18.17 - resolution: "@esbuild/linux-arm@npm:0.18.17" +"@esbuild/linux-arm@npm:0.18.20": + version: 0.18.20 + resolution: "@esbuild/linux-arm@npm:0.18.20" conditions: os=linux & cpu=arm languageName: node linkType: hard -"@esbuild/linux-ia32@npm:0.18.17": - version: 0.18.17 - resolution: "@esbuild/linux-ia32@npm:0.18.17" +"@esbuild/linux-ia32@npm:0.18.20": + version: 0.18.20 + resolution: "@esbuild/linux-ia32@npm:0.18.20" conditions: os=linux & cpu=ia32 languageName: node linkType: hard -"@esbuild/linux-loong64@npm:0.18.17": - version: 0.18.17 - resolution: "@esbuild/linux-loong64@npm:0.18.17" +"@esbuild/linux-loong64@npm:0.18.20": + version: 0.18.20 + resolution: "@esbuild/linux-loong64@npm:0.18.20" conditions: os=linux & cpu=loong64 languageName: node linkType: hard -"@esbuild/linux-mips64el@npm:0.18.17": - version: 0.18.17 - resolution: "@esbuild/linux-mips64el@npm:0.18.17" +"@esbuild/linux-mips64el@npm:0.18.20": + version: 0.18.20 + resolution: "@esbuild/linux-mips64el@npm:0.18.20" conditions: os=linux & cpu=mips64el languageName: node linkType: hard -"@esbuild/linux-ppc64@npm:0.18.17": - version: 0.18.17 - resolution: "@esbuild/linux-ppc64@npm:0.18.17" +"@esbuild/linux-ppc64@npm:0.18.20": + version: 0.18.20 + resolution: "@esbuild/linux-ppc64@npm:0.18.20" conditions: os=linux & cpu=ppc64 languageName: node linkType: hard -"@esbuild/linux-riscv64@npm:0.18.17": - version: 0.18.17 - resolution: "@esbuild/linux-riscv64@npm:0.18.17" +"@esbuild/linux-riscv64@npm:0.18.20": + version: 0.18.20 + resolution: "@esbuild/linux-riscv64@npm:0.18.20" conditions: os=linux & cpu=riscv64 languageName: node linkType: hard -"@esbuild/linux-s390x@npm:0.18.17": - version: 0.18.17 - resolution: "@esbuild/linux-s390x@npm:0.18.17" +"@esbuild/linux-s390x@npm:0.18.20": + version: 0.18.20 + resolution: "@esbuild/linux-s390x@npm:0.18.20" conditions: os=linux & cpu=s390x languageName: node linkType: hard -"@esbuild/linux-x64@npm:0.18.17": - version: 0.18.17 - resolution: "@esbuild/linux-x64@npm:0.18.17" +"@esbuild/linux-x64@npm:0.18.20": + version: 0.18.20 + resolution: "@esbuild/linux-x64@npm:0.18.20" conditions: os=linux & cpu=x64 languageName: node linkType: hard -"@esbuild/netbsd-x64@npm:0.18.17": - version: 0.18.17 - resolution: "@esbuild/netbsd-x64@npm:0.18.17" +"@esbuild/netbsd-x64@npm:0.18.20": + version: 0.18.20 + resolution: "@esbuild/netbsd-x64@npm:0.18.20" conditions: os=netbsd & cpu=x64 languageName: node linkType: hard -"@esbuild/openbsd-x64@npm:0.18.17": - version: 0.18.17 - resolution: "@esbuild/openbsd-x64@npm:0.18.17" +"@esbuild/openbsd-x64@npm:0.18.20": + version: 0.18.20 + resolution: "@esbuild/openbsd-x64@npm:0.18.20" conditions: os=openbsd & cpu=x64 languageName: node linkType: hard -"@esbuild/sunos-x64@npm:0.18.17": - version: 0.18.17 - resolution: "@esbuild/sunos-x64@npm:0.18.17" +"@esbuild/sunos-x64@npm:0.18.20": + version: 0.18.20 + resolution: "@esbuild/sunos-x64@npm:0.18.20" conditions: os=sunos & cpu=x64 languageName: node linkType: hard -"@esbuild/win32-arm64@npm:0.18.17": - version: 0.18.17 - resolution: "@esbuild/win32-arm64@npm:0.18.17" +"@esbuild/win32-arm64@npm:0.18.20": + version: 0.18.20 + resolution: "@esbuild/win32-arm64@npm:0.18.20" conditions: os=win32 & cpu=arm64 languageName: node linkType: hard -"@esbuild/win32-ia32@npm:0.18.17": - version: 0.18.17 - resolution: "@esbuild/win32-ia32@npm:0.18.17" +"@esbuild/win32-ia32@npm:0.18.20": + version: 0.18.20 + resolution: "@esbuild/win32-ia32@npm:0.18.20" conditions: os=win32 & cpu=ia32 languageName: node linkType: hard -"@esbuild/win32-x64@npm:0.18.17": - version: 0.18.17 - resolution: "@esbuild/win32-x64@npm:0.18.17" +"@esbuild/win32-x64@npm:0.18.20": + version: 0.18.20 + resolution: "@esbuild/win32-x64@npm:0.18.20" conditions: os=win32 & cpu=x64 languageName: node linkType: hard @@ -1633,15 +1640,15 @@ __metadata: linkType: hard "@eslint-community/regexpp@npm:^4.5.1, @eslint-community/regexpp@npm:^4.6.1": - version: 4.6.2 - resolution: "@eslint-community/regexpp@npm:4.6.2" - checksum: a3c341377b46b54fa228f455771b901d1a2717f95d47dcdf40199df30abc000ba020f747f114f08560d119e979d882a94cf46cfc51744544d54b00319c0f2724 + version: 4.10.0 + resolution: "@eslint-community/regexpp@npm:4.10.0" + checksum: 2a6e345429ea8382aaaf3a61f865cae16ed44d31ca917910033c02dc00d505d939f10b81e079fa14d43b51499c640138e153b7e40743c4c094d9df97d4e56f7b languageName: node linkType: hard -"@eslint/eslintrc@npm:^2.1.1": - version: 2.1.1 - resolution: "@eslint/eslintrc@npm:2.1.1" +"@eslint/eslintrc@npm:^2.1.4": + version: 2.1.4 + resolution: "@eslint/eslintrc@npm:2.1.4" dependencies: ajv: ^6.12.4 debug: ^4.3.2 @@ -1652,25 +1659,25 @@ __metadata: js-yaml: ^4.1.0 minimatch: ^3.1.2 strip-json-comments: ^3.1.1 - checksum: bf909ea183d27238c257a82d4ffdec38ca94b906b4b8dfae02ecbe7ecc9e5a8182ef5e469c808bb8cb4fea4750f43ac4ca7c4b4a167b6cd7e3aaacd386b2bd25 + checksum: 10957c7592b20ca0089262d8c2a8accbad14b4f6507e35416c32ee6b4dbf9cad67dfb77096bbd405405e9ada2b107f3797fe94362e1c55e0b09d6e90dd149127 languageName: node linkType: hard -"@eslint/js@npm:^8.46.0": - version: 8.46.0 - resolution: "@eslint/js@npm:8.46.0" - checksum: 7aed479832302882faf5bec37e9d068f270f84c19b3fb529646a7c1b031e73a312f730569c78806492bc09cfce3d7651dfab4ce09a56cbb06bc6469449e56377 +"@eslint/js@npm:8.56.0": + version: 8.56.0 + resolution: "@eslint/js@npm:8.56.0" + checksum: 5804130574ef810207bdf321c265437814e7a26f4e6fac9b496de3206afd52f533e09ec002a3be06cd9adcc9da63e727f1883938e663c4e4751c007d5b58e539 languageName: node linkType: hard -"@humanwhocodes/config-array@npm:^0.11.10": - version: 0.11.10 - resolution: "@humanwhocodes/config-array@npm:0.11.10" +"@humanwhocodes/config-array@npm:^0.11.13": + version: 0.11.14 + resolution: "@humanwhocodes/config-array@npm:0.11.14" dependencies: - "@humanwhocodes/object-schema": ^1.2.1 - debug: ^4.1.1 + "@humanwhocodes/object-schema": ^2.0.2 + debug: ^4.3.1 minimatch: ^3.0.5 - checksum: 1b1302e2403d0e35bc43e66d67a2b36b0ad1119efc704b5faff68c41f791a052355b010fb2d27ef022670f550de24cd6d08d5ecf0821c16326b7dcd0ee5d5d8a + checksum: 861ccce9eaea5de19546653bccf75bf09fe878bc39c3aab00aeee2d2a0e654516adad38dd1098aab5e3af0145bbcbf3f309bdf4d964f8dab9dcd5834ae4c02f2 languageName: node linkType: hard @@ -1681,10 +1688,10 @@ __metadata: languageName: node linkType: hard -"@humanwhocodes/object-schema@npm:^1.2.1": - version: 1.2.1 - resolution: "@humanwhocodes/object-schema@npm:1.2.1" - checksum: a824a1ec31591231e4bad5787641f59e9633827d0a2eaae131a288d33c9ef0290bd16fda8da6f7c0fcb014147865d12118df10db57f27f41e20da92369fcb3f1 +"@humanwhocodes/object-schema@npm:^2.0.2": + version: 2.0.2 + resolution: "@humanwhocodes/object-schema@npm:2.0.2" + checksum: 2fc11503361b5fb4f14714c700c02a3f4c7c93e9acd6b87a29f62c522d90470f364d6161b03d1cc618b979f2ae02aed1106fd29d302695d8927e2fc8165ba8ee languageName: node linkType: hard @@ -1729,50 +1736,50 @@ __metadata: languageName: node linkType: hard -"@jest/console@npm:^29.6.2": - version: 29.6.2 - resolution: "@jest/console@npm:29.6.2" +"@jest/console@npm:^29.7.0": + version: 29.7.0 + resolution: "@jest/console@npm:29.7.0" dependencies: - "@jest/types": ^29.6.1 + "@jest/types": ^29.6.3 "@types/node": "*" chalk: ^4.0.0 - jest-message-util: ^29.6.2 - jest-util: ^29.6.2 + jest-message-util: ^29.7.0 + jest-util: ^29.7.0 slash: ^3.0.0 - checksum: 1198667bda0430770c3e9b92681c0ee9f8346394574071c633f306192ac5f08e12972d6a5fdf03eb0d441051c8439bce0f6f9f355dc60d98777a35328331ba2e + checksum: 0e3624e32c5a8e7361e889db70b170876401b7d70f509a2538c31d5cd50deb0c1ae4b92dc63fe18a0902e0a48c590c21d53787a0df41a52b34fa7cab96c384d6 languageName: node linkType: hard -"@jest/core@npm:^29.6.2": - version: 29.6.2 - resolution: "@jest/core@npm:29.6.2" +"@jest/core@npm:^29.7.0": + version: 29.7.0 + resolution: "@jest/core@npm:29.7.0" dependencies: - "@jest/console": ^29.6.2 - "@jest/reporters": ^29.6.2 - "@jest/test-result": ^29.6.2 - "@jest/transform": ^29.6.2 - "@jest/types": ^29.6.1 + "@jest/console": ^29.7.0 + "@jest/reporters": ^29.7.0 + "@jest/test-result": ^29.7.0 + "@jest/transform": ^29.7.0 + "@jest/types": ^29.6.3 "@types/node": "*" ansi-escapes: ^4.2.1 chalk: ^4.0.0 ci-info: ^3.2.0 exit: ^0.1.2 graceful-fs: ^4.2.9 - jest-changed-files: ^29.5.0 - jest-config: ^29.6.2 - jest-haste-map: ^29.6.2 - jest-message-util: ^29.6.2 - jest-regex-util: ^29.4.3 - jest-resolve: ^29.6.2 - jest-resolve-dependencies: ^29.6.2 - jest-runner: ^29.6.2 - jest-runtime: ^29.6.2 - jest-snapshot: ^29.6.2 - jest-util: ^29.6.2 - jest-validate: ^29.6.2 - jest-watcher: ^29.6.2 + jest-changed-files: ^29.7.0 + jest-config: ^29.7.0 + jest-haste-map: ^29.7.0 + jest-message-util: ^29.7.0 + jest-regex-util: ^29.6.3 + jest-resolve: ^29.7.0 + jest-resolve-dependencies: ^29.7.0 + jest-runner: ^29.7.0 + jest-runtime: ^29.7.0 + jest-snapshot: ^29.7.0 + jest-util: ^29.7.0 + jest-validate: ^29.7.0 + jest-watcher: ^29.7.0 micromatch: ^4.0.4 - pretty-format: ^29.6.2 + pretty-format: ^29.7.0 slash: ^3.0.0 strip-ansi: ^6.0.0 peerDependencies: @@ -1780,76 +1787,76 @@ __metadata: peerDependenciesMeta: node-notifier: optional: true - checksum: 6bbb3886430248c0092f275b1b946a701406732f7442c04e63e4ee2297c2ec02d8ceeec508a202e08128197699b2bcddbae2c2f74adb2cf30f2f0d7d94a7c2dc + checksum: af759c9781cfc914553320446ce4e47775ae42779e73621c438feb1e4231a5d4862f84b1d8565926f2d1aab29b3ec3dcfdc84db28608bdf5f29867124ebcfc0d languageName: node linkType: hard -"@jest/environment@npm:^29.6.2": - version: 29.6.2 - resolution: "@jest/environment@npm:29.6.2" +"@jest/environment@npm:^29.7.0": + version: 29.7.0 + resolution: "@jest/environment@npm:29.7.0" dependencies: - "@jest/fake-timers": ^29.6.2 - "@jest/types": ^29.6.1 + "@jest/fake-timers": ^29.7.0 + "@jest/types": ^29.6.3 "@types/node": "*" - jest-mock: ^29.6.2 - checksum: c7de0e4c0d9166e02d0eb166574e05ec460e1db3b69d6476e63244edd52d7c917e6876af55fe723ff3086f52c0b1869dec60654054735a7a48c9d4ac43af2a25 + jest-mock: ^29.7.0 + checksum: 6fb398143b2543d4b9b8d1c6dbce83fa5247f84f550330604be744e24c2bd2178bb893657d62d1b97cf2f24baf85c450223f8237cccb71192c36a38ea2272934 languageName: node linkType: hard -"@jest/expect-utils@npm:^29.6.2": - version: 29.6.2 - resolution: "@jest/expect-utils@npm:29.6.2" +"@jest/expect-utils@npm:^29.7.0": + version: 29.7.0 + resolution: "@jest/expect-utils@npm:29.7.0" dependencies: - jest-get-type: ^29.4.3 - checksum: 0decf2009aa3735f9df469e78ce1721c2815e4278439887e0cf0321ca8979541a22515d114a59b2445a6cd70a074b09dc9c00b5e7b3b3feac5174b9c4a78b2e1 + jest-get-type: ^29.6.3 + checksum: 75eb177f3d00b6331bcaa057e07c0ccb0733a1d0a1943e1d8db346779039cb7f103789f16e502f888a3096fb58c2300c38d1f3748b36a7fa762eb6f6d1b160ed languageName: node linkType: hard -"@jest/expect@npm:^29.6.2": - version: 29.6.2 - resolution: "@jest/expect@npm:29.6.2" +"@jest/expect@npm:^29.7.0": + version: 29.7.0 + resolution: "@jest/expect@npm:29.7.0" dependencies: - expect: ^29.6.2 - jest-snapshot: ^29.6.2 - checksum: bd2d88a4e7c5420079c239afef341ec53dc7e353816cd13acbb42631a31fd321fe58677bb43a4dba851028f4c7e31da7980314e9094cd5b348896cb6cd3d42b2 + expect: ^29.7.0 + jest-snapshot: ^29.7.0 + checksum: a01cb85fd9401bab3370618f4b9013b90c93536562222d920e702a0b575d239d74cecfe98010aaec7ad464f67cf534a353d92d181646a4b792acaa7e912ae55e languageName: node linkType: hard -"@jest/fake-timers@npm:^29.6.2": - version: 29.6.2 - resolution: "@jest/fake-timers@npm:29.6.2" +"@jest/fake-timers@npm:^29.7.0": + version: 29.7.0 + resolution: "@jest/fake-timers@npm:29.7.0" dependencies: - "@jest/types": ^29.6.1 + "@jest/types": ^29.6.3 "@sinonjs/fake-timers": ^10.0.2 "@types/node": "*" - jest-message-util: ^29.6.2 - jest-mock: ^29.6.2 - jest-util: ^29.6.2 - checksum: 1abcda02f22d2ba32e178b7ab80a9180235a6c75ec9faef33324627b19a70dad64889a9ea49b8f07230e14a6e683b9120542c6d1d6b2ecaf937f4efde32dad88 + jest-message-util: ^29.7.0 + jest-mock: ^29.7.0 + jest-util: ^29.7.0 + checksum: caf2bbd11f71c9241b458d1b5a66cbe95debc5a15d96442444b5d5c7ba774f523c76627c6931cca5e10e76f0d08761f6f1f01a608898f4751a0eee54fc3d8d00 languageName: node linkType: hard -"@jest/globals@npm:^29.5.0, @jest/globals@npm:^29.6.2": - version: 29.6.2 - resolution: "@jest/globals@npm:29.6.2" +"@jest/globals@npm:^29.5.0, @jest/globals@npm:^29.7.0": + version: 29.7.0 + resolution: "@jest/globals@npm:29.7.0" dependencies: - "@jest/environment": ^29.6.2 - "@jest/expect": ^29.6.2 - "@jest/types": ^29.6.1 - jest-mock: ^29.6.2 - checksum: aa4a54f19cc025205bc696546940e1fe9c752c2d4d825852088aa76d44677ebba1ec66fabb78e615480cff23a06a70b5a3f893ab5163d901cdfa0d2267870b10 + "@jest/environment": ^29.7.0 + "@jest/expect": ^29.7.0 + "@jest/types": ^29.6.3 + jest-mock: ^29.7.0 + checksum: 97dbb9459135693ad3a422e65ca1c250f03d82b2a77f6207e7fa0edd2c9d2015fbe4346f3dc9ebff1678b9d8da74754d4d440b7837497f8927059c0642a22123 languageName: node linkType: hard -"@jest/reporters@npm:^29.6.2": - version: 29.6.2 - resolution: "@jest/reporters@npm:29.6.2" +"@jest/reporters@npm:^29.7.0": + version: 29.7.0 + resolution: "@jest/reporters@npm:29.7.0" dependencies: "@bcoe/v8-coverage": ^0.2.3 - "@jest/console": ^29.6.2 - "@jest/test-result": ^29.6.2 - "@jest/transform": ^29.6.2 - "@jest/types": ^29.6.1 + "@jest/console": ^29.7.0 + "@jest/test-result": ^29.7.0 + "@jest/transform": ^29.7.0 + "@jest/types": ^29.6.3 "@jridgewell/trace-mapping": ^0.3.18 "@types/node": "*" chalk: ^4.0.0 @@ -1858,13 +1865,13 @@ __metadata: glob: ^7.1.3 graceful-fs: ^4.2.9 istanbul-lib-coverage: ^3.0.0 - istanbul-lib-instrument: ^5.1.0 + istanbul-lib-instrument: ^6.0.0 istanbul-lib-report: ^3.0.0 istanbul-lib-source-maps: ^4.0.0 istanbul-reports: ^3.1.3 - jest-message-util: ^29.6.2 - jest-util: ^29.6.2 - jest-worker: ^29.6.2 + jest-message-util: ^29.7.0 + jest-util: ^29.7.0 + jest-worker: ^29.7.0 slash: ^3.0.0 string-length: ^4.0.1 strip-ansi: ^6.0.0 @@ -1874,88 +1881,88 @@ __metadata: peerDependenciesMeta: node-notifier: optional: true - checksum: 7cf880d0730cee7d24ee96928003ef6946bf93423b0ae9a2edb53cae2c231b8ac50ec264f48a73744e3f11ca319cd414edacf99b2e7bf37cd72fe0b362090dd1 + checksum: 7eadabd62cc344f629024b8a268ecc8367dba756152b761bdcb7b7e570a3864fc51b2a9810cd310d85e0a0173ef002ba4528d5ea0329fbf66ee2a3ada9c40455 languageName: node linkType: hard -"@jest/schemas@npm:^29.6.0": - version: 29.6.0 - resolution: "@jest/schemas@npm:29.6.0" +"@jest/schemas@npm:^29.6.3": + version: 29.6.3 + resolution: "@jest/schemas@npm:29.6.3" dependencies: "@sinclair/typebox": ^0.27.8 - checksum: c00511c69cf89138a7d974404d3a5060af375b5a52b9c87215d91873129b382ca11c1ff25bd6d605951404bb381ddce5f8091004a61e76457da35db1f5c51365 + checksum: 910040425f0fc93cd13e68c750b7885590b8839066dfa0cd78e7def07bbb708ad869381f725945d66f2284de5663bbecf63e8fdd856e2ae6e261ba30b1687e93 languageName: node linkType: hard -"@jest/source-map@npm:^29.6.0": - version: 29.6.0 - resolution: "@jest/source-map@npm:29.6.0" +"@jest/source-map@npm:^29.6.3": + version: 29.6.3 + resolution: "@jest/source-map@npm:29.6.3" dependencies: "@jridgewell/trace-mapping": ^0.3.18 callsites: ^3.0.0 graceful-fs: ^4.2.9 - checksum: 9c6c40387410bb70b2fae8124287fc28f6bdd1b2d7f24348e8611e1bb638b404518228a4ce64a582365b589c536ae8e7ebab0126cef59a87874b71061d19783b + checksum: bcc5a8697d471396c0003b0bfa09722c3cd879ad697eb9c431e6164e2ea7008238a01a07193dfe3cbb48b1d258eb7251f6efcea36f64e1ebc464ea3c03ae2deb languageName: node linkType: hard -"@jest/test-result@npm:^29.6.2": - version: 29.6.2 - resolution: "@jest/test-result@npm:29.6.2" +"@jest/test-result@npm:^29.7.0": + version: 29.7.0 + resolution: "@jest/test-result@npm:29.7.0" dependencies: - "@jest/console": ^29.6.2 - "@jest/types": ^29.6.1 + "@jest/console": ^29.7.0 + "@jest/types": ^29.6.3 "@types/istanbul-lib-coverage": ^2.0.0 collect-v8-coverage: ^1.0.0 - checksum: 8aff37f18c8d2df4d9f453d57ec018a6479eb697fabcf74b1ca06e34553da1d7a2b85580a290408ba0b02e58543263244a2cb065c7c7180c8d8180cc78444fbd + checksum: 67b6317d526e335212e5da0e768e3b8ab8a53df110361b80761353ad23b6aea4432b7c5665bdeb87658ea373b90fb1afe02ed3611ef6c858c7fba377505057fa languageName: node linkType: hard -"@jest/test-sequencer@npm:^29.6.2": - version: 29.6.2 - resolution: "@jest/test-sequencer@npm:29.6.2" +"@jest/test-sequencer@npm:^29.7.0": + version: 29.7.0 + resolution: "@jest/test-sequencer@npm:29.7.0" dependencies: - "@jest/test-result": ^29.6.2 + "@jest/test-result": ^29.7.0 graceful-fs: ^4.2.9 - jest-haste-map: ^29.6.2 + jest-haste-map: ^29.7.0 slash: ^3.0.0 - checksum: 12dc2577e45eeb98b85d1769846b7d6effa536907986ad3c4cbd014df9e24431a564cc8cd94603332e4b1f9bfb421371883efc6a5085b361a52425ffc2a52dc6 + checksum: 73f43599017946be85c0b6357993b038f875b796e2f0950487a82f4ebcb115fa12131932dd9904026b4ad8be131fe6e28bd8d0aa93b1563705185f9804bff8bd languageName: node linkType: hard -"@jest/transform@npm:^29.6.2": - version: 29.6.2 - resolution: "@jest/transform@npm:29.6.2" +"@jest/transform@npm:^29.7.0": + version: 29.7.0 + resolution: "@jest/transform@npm:29.7.0" dependencies: "@babel/core": ^7.11.6 - "@jest/types": ^29.6.1 + "@jest/types": ^29.6.3 "@jridgewell/trace-mapping": ^0.3.18 babel-plugin-istanbul: ^6.1.1 chalk: ^4.0.0 convert-source-map: ^2.0.0 fast-json-stable-stringify: ^2.1.0 graceful-fs: ^4.2.9 - jest-haste-map: ^29.6.2 - jest-regex-util: ^29.4.3 - jest-util: ^29.6.2 + jest-haste-map: ^29.7.0 + jest-regex-util: ^29.6.3 + jest-util: ^29.7.0 micromatch: ^4.0.4 pirates: ^4.0.4 slash: ^3.0.0 write-file-atomic: ^4.0.2 - checksum: ffb8c3c344cd48bedadec295d9c436737eccc39c1f0868aa9753b76397b33b2e5b121058af6f287ba6f2036181137e37df1212334bfa9d9a712986a4518cdc18 + checksum: 0f8ac9f413903b3cb6d240102db848f2a354f63971ab885833799a9964999dd51c388162106a807f810071f864302cdd8e3f0c241c29ce02d85a36f18f3f40ab languageName: node linkType: hard -"@jest/types@npm:^29.6.1": - version: 29.6.1 - resolution: "@jest/types@npm:29.6.1" +"@jest/types@npm:^29.6.3": + version: 29.6.3 + resolution: "@jest/types@npm:29.6.3" dependencies: - "@jest/schemas": ^29.6.0 + "@jest/schemas": ^29.6.3 "@types/istanbul-lib-coverage": ^2.0.0 "@types/istanbul-reports": ^3.0.0 "@types/node": "*" "@types/yargs": ^17.0.8 chalk: ^4.0.0 - checksum: 89fc1ccf71a84fe0da643e0675b1cfe6a6f19ea72e935b2ab1dbdb56ec547e94433fb59b3536d3832a6e156c077865b7176fe9dae707dab9c3d2f9405ba6233c + checksum: a0bcf15dbb0eca6bdd8ce61a3fb055349d40268622a7670a3b2eb3c3dbafe9eb26af59938366d520b86907b9505b0f9b29b85cec11579a9e580694b87cd90fcc languageName: node linkType: hard @@ -1970,14 +1977,7 @@ __metadata: languageName: node linkType: hard -"@jridgewell/resolve-uri@npm:3.1.0": - version: 3.1.0 - resolution: "@jridgewell/resolve-uri@npm:3.1.0" - checksum: b5ceaaf9a110fcb2780d1d8f8d4a0bfd216702f31c988d8042e5f8fbe353c55d9b0f55a1733afdc64806f8e79c485d2464680ac48a0d9fcadb9548ee6b81d267 - languageName: node - linkType: hard - -"@jridgewell/resolve-uri@npm:^3.0.3": +"@jridgewell/resolve-uri@npm:^3.0.3, @jridgewell/resolve-uri@npm:^3.1.0": version: 3.1.1 resolution: "@jridgewell/resolve-uri@npm:3.1.1" checksum: f5b441fe7900eab4f9155b3b93f9800a916257f4e8563afbcd3b5a5337b55e52bd8ae6735453b1b745457d9f6cdb16d74cd6220bbdd98cf153239e13f6cbb653 @@ -2001,14 +2001,7 @@ __metadata: languageName: node linkType: hard -"@jridgewell/sourcemap-codec@npm:1.4.14": - version: 1.4.14 - resolution: "@jridgewell/sourcemap-codec@npm:1.4.14" - checksum: 61100637b6d173d3ba786a5dff019e1a74b1f394f323c1fee337ff390239f053b87266c7a948777f4b1ee68c01a8ad0ab61e5ff4abb5a012a0b091bec391ab97 - languageName: node - linkType: hard - -"@jridgewell/sourcemap-codec@npm:^1.4.10": +"@jridgewell/sourcemap-codec@npm:^1.4.10, @jridgewell/sourcemap-codec@npm:^1.4.14": version: 1.4.15 resolution: "@jridgewell/sourcemap-codec@npm:1.4.15" checksum: b881c7e503db3fc7f3c1f35a1dd2655a188cc51a3612d76efc8a6eb74728bef5606e6758ee77423e564092b4a518aba569bbb21c9bac5ab7a35b0c6ae7e344c8 @@ -2025,13 +2018,13 @@ __metadata: languageName: node linkType: hard -"@jridgewell/trace-mapping@npm:^0.3.12, @jridgewell/trace-mapping@npm:^0.3.17, @jridgewell/trace-mapping@npm:^0.3.18, @jridgewell/trace-mapping@npm:^0.3.9": - version: 0.3.18 - resolution: "@jridgewell/trace-mapping@npm:0.3.18" +"@jridgewell/trace-mapping@npm:^0.3.12, @jridgewell/trace-mapping@npm:^0.3.17, @jridgewell/trace-mapping@npm:^0.3.18, @jridgewell/trace-mapping@npm:^0.3.20, @jridgewell/trace-mapping@npm:^0.3.9": + version: 0.3.22 + resolution: "@jridgewell/trace-mapping@npm:0.3.22" dependencies: - "@jridgewell/resolve-uri": 3.1.0 - "@jridgewell/sourcemap-codec": 1.4.14 - checksum: 0572669f855260808c16fe8f78f5f1b4356463b11d3f2c7c0b5580c8ba1cbf4ae53efe9f627595830856e57dbac2325ac17eb0c3dd0ec42102e6f227cc289c02 + "@jridgewell/resolve-uri": ^3.1.0 + "@jridgewell/sourcemap-codec": ^1.4.14 + checksum: ac7dd2cfe0b479aa1b81776d40d789243131cc792dc8b6b6a028c70fcd6171958ae1a71bf67b618ffe3c0c3feead9870c095ee46a5e30319410d92976b28f498 languageName: node linkType: hard @@ -2045,23 +2038,23 @@ __metadata: linkType: hard "@libp2p/bootstrap@npm:^9.0.4": - version: 9.0.4 - resolution: "@libp2p/bootstrap@npm:9.0.4" + version: 9.0.12 + resolution: "@libp2p/bootstrap@npm:9.0.12" dependencies: - "@libp2p/interface": ^0.1.2 - "@libp2p/logger": ^3.0.2 - "@libp2p/peer-id": ^3.0.2 + "@libp2p/interface": ^0.1.6 + "@libp2p/logger": ^3.1.0 + "@libp2p/peer-id": ^3.0.6 "@multiformats/mafmt": ^12.1.2 "@multiformats/multiaddr": ^12.1.5 - checksum: 10e66b4b20645606319ed9192c9672e424aa89cd3e16b3202ac23b36bdf188873882bf71535b6b9adcef4720c69af7cf3f29a39a8139334b11dcdefbed8dcd4a + checksum: 249198129b806bf5525d527074e9151c96a411c61474543f8e2679664733af0873c5267b4c579fa29ac4f64f7fe3dae32e70dba66acafd321a3368adc579bccf languageName: node linkType: hard -"@libp2p/crypto@npm:^2.0.0, @libp2p/crypto@npm:^2.0.3": - version: 2.0.3 - resolution: "@libp2p/crypto@npm:2.0.3" +"@libp2p/crypto@npm:^2.0.0, @libp2p/crypto@npm:^2.0.8": + version: 2.0.8 + resolution: "@libp2p/crypto@npm:2.0.8" dependencies: - "@libp2p/interface": ^0.1.2 + "@libp2p/interface": ^0.1.6 "@noble/curves": ^1.1.0 "@noble/hashes": ^1.3.1 multiformats: ^12.0.1 @@ -2069,23 +2062,7 @@ __metadata: protons-runtime: ^5.0.0 uint8arraylist: ^2.4.3 uint8arrays: ^4.0.6 - checksum: b1806cea33fe5f16ff6d7d920f8cf77a33d06f418d9488f0b40c0251568e01a12014ee84481048764bc1d2a4372ba536842f2fb4f9879c529c6a2a0755b77e95 - languageName: node - linkType: hard - -"@libp2p/crypto@npm:^2.0.4": - version: 2.0.4 - resolution: "@libp2p/crypto@npm:2.0.4" - dependencies: - "@libp2p/interface": ^0.1.2 - "@noble/curves": ^1.1.0 - "@noble/hashes": ^1.3.1 - multiformats: ^12.0.1 - node-forge: ^1.1.0 - protons-runtime: ^5.0.0 - uint8arraylist: ^2.4.3 - uint8arrays: ^4.0.6 - checksum: 1e0a0e8006e9ca7caaec16ce94d193063749fc7f62a7b3d0b4469915e2837c380987baa400a9f35ee13c23783f8317f8bc58b8ed647a5b4bf55635a2a909ae97 + checksum: 4047dc54e33670c7aa4717b9f5e7980717e00ef6a2cefaae336cdcd88f9ca38d7f29a1c642d8cbc7cd85bd2957eb79b869791b4f48fb2e91652b2d87224a136d languageName: node linkType: hard @@ -2113,15 +2090,15 @@ __metadata: languageName: node linkType: hard -"@libp2p/interface-internal@npm:^0.1.4": - version: 0.1.4 - resolution: "@libp2p/interface-internal@npm:0.1.4" +"@libp2p/interface-internal@npm:^0.1.9": + version: 0.1.12 + resolution: "@libp2p/interface-internal@npm:0.1.12" dependencies: - "@libp2p/interface": ^0.1.2 - "@libp2p/peer-collections": ^4.0.3 + "@libp2p/interface": ^0.1.6 + "@libp2p/peer-collections": ^4.0.8 "@multiformats/multiaddr": ^12.1.5 uint8arraylist: ^2.4.3 - checksum: 26bbd1a2811baa089eb3ec9031f64d8b7c5e183e4f4a1f4b6c37b1de22985e61e63f9ba06fa7cbc91d60217d66773407361c8ea7abba93cf900e633547d5da97 + checksum: 3ffa5843fd7fd046f5ab4cb70a96da6c392ba2f24f832a3b99a0b20653fa6478c924caacd8c1a6aa4de2cb2a39669778ed804bfcb5c0c232a775cc1e29f3dd17 languageName: node linkType: hard @@ -2243,9 +2220,9 @@ __metadata: languageName: node linkType: hard -"@libp2p/interface@npm:^0.1.0, @libp2p/interface@npm:^0.1.1, @libp2p/interface@npm:^0.1.2": - version: 0.1.2 - resolution: "@libp2p/interface@npm:0.1.2" +"@libp2p/interface@npm:^0.1.0, @libp2p/interface@npm:^0.1.2, @libp2p/interface@npm:^0.1.6": + version: 0.1.6 + resolution: "@libp2p/interface@npm:0.1.6" dependencies: "@multiformats/multiaddr": ^12.1.5 abortable-iterator: ^5.0.1 @@ -2253,12 +2230,27 @@ __metadata: it-stream-types: ^2.0.1 multiformats: ^12.0.1 p-defer: ^4.0.0 + race-signal: ^1.0.0 uint8arraylist: ^2.4.3 - checksum: d33748ba16473c622802ee95e57445f2ac79b1ddffbaba7499157a769c57f9c1374ac10de72517e7899f88808e6fee09d77aef928d8eeaaf2bb8951fa93653a7 + checksum: dbf0c4544bbb2a299d54615e8ef553324657e7cb06a2fdc3f1a99d785276c69882841b0e9cf73b934923318d04fe6c6f7ef59194d438a126d0d8d3a4b05cc22b languageName: node linkType: hard -"@libp2p/interfaces@npm:^3.0.0, @libp2p/interfaces@npm:^3.3.1": +"@libp2p/interface@npm:^1.0.0, @libp2p/interface@npm:^1.1.2": + version: 1.1.2 + resolution: "@libp2p/interface@npm:1.1.2" + dependencies: + "@multiformats/multiaddr": ^12.1.10 + it-pushable: ^3.2.3 + it-stream-types: ^2.0.1 + multiformats: ^13.0.0 + progress-events: ^1.0.0 + uint8arraylist: ^2.4.7 + checksum: 99e257281fde4a226124344f24eb246b2a1f0639be3a73aae4478e97267f4fa5d9f86754291b16d5da01179d0fa11066477a7e1b6bb4ddfa025442b0fdc90809 + languageName: node + linkType: hard + +"@libp2p/interfaces@npm:^3.0.0": version: 3.3.2 resolution: "@libp2p/interfaces@npm:3.3.2" checksum: 3071fa49dcbb81a4b218248a1f648fba1061fb9c51e4b5edab9b8a7b9425c25afec96fdf3351ea7a469e7039269e59d95265682a934aa9c21630226dfcb67313 @@ -2266,21 +2258,20 @@ __metadata: linkType: hard "@libp2p/kad-dht@npm:^10.0.4": - version: 10.0.4 - resolution: "@libp2p/kad-dht@npm:10.0.4" - dependencies: - "@libp2p/crypto": ^2.0.3 - "@libp2p/interface": ^0.1.2 - "@libp2p/interface-internal": ^0.1.4 - "@libp2p/logger": ^3.0.2 - "@libp2p/peer-collections": ^4.0.3 - "@libp2p/peer-id": ^3.0.2 + version: 10.0.15 + resolution: "@libp2p/kad-dht@npm:10.0.15" + dependencies: + "@libp2p/crypto": ^2.0.8 + "@libp2p/interface": ^0.1.6 + "@libp2p/interface-internal": ^0.1.9 + "@libp2p/logger": ^3.1.0 + "@libp2p/peer-collections": ^4.0.8 + "@libp2p/peer-id": ^3.0.6 "@multiformats/multiaddr": ^12.1.5 - "@types/sinon": ^10.0.15 + "@types/sinon": ^17.0.0 abortable-iterator: ^5.0.1 any-signal: ^4.1.1 datastore-core: ^9.0.1 - events: ^3.3.0 hashlru: ^2.3.0 interface-datastore: ^8.2.0 it-all: ^3.0.2 @@ -2292,6 +2283,7 @@ __metadata: it-merge: ^3.0.0 it-parallel: ^3.0.0 it-pipe: ^3.0.1 + it-pushable: ^3.2.1 it-stream-types: ^2.0.1 it-take: ^3.0.1 multiformats: ^12.0.1 @@ -2304,27 +2296,27 @@ __metadata: uint8-varint: ^2.0.0 uint8arraylist: ^2.4.3 uint8arrays: ^4.0.6 - checksum: 8fbc6b2e12eeb98825b7dfa9e09a1c26f22a679167bde6305e8c524ee5514f509639db70915c432e1749272348f3eb8bb37ea7978a1a6f4133053e6b37ae3e3f + checksum: 566c62d45ff8ba92ea15332c8b62395a8e4f794ee46c038b04e4c144f032ddceae080e2a6de0e0948370620d3b708f61052783b788ba40d53d11044910f9becf languageName: node linkType: hard -"@libp2p/keychain@npm:^3.0.3": - version: 3.0.3 - resolution: "@libp2p/keychain@npm:3.0.3" +"@libp2p/keychain@npm:^3.0.8": + version: 3.0.8 + resolution: "@libp2p/keychain@npm:3.0.8" dependencies: - "@libp2p/crypto": ^2.0.3 - "@libp2p/interface": ^0.1.2 - "@libp2p/logger": ^3.0.2 - "@libp2p/peer-id": ^3.0.2 + "@libp2p/crypto": ^2.0.8 + "@libp2p/interface": ^0.1.6 + "@libp2p/logger": ^3.1.0 + "@libp2p/peer-id": ^3.0.6 interface-datastore: ^8.2.0 merge-options: ^3.0.4 sanitize-filename: ^1.6.3 uint8arrays: ^4.0.6 - checksum: cf02dea0290d891c903fb9be2ad4955ae1a5dcb5a8f842873667231d8a3ce9225cadee57ca88d23d887be0be932fe5aece16412965da267f01b1a2e4c478f38f + checksum: 765971d2ef29cdc781ff2447f28501b9f58c8a9d0e4339c17b01de5a57d50344f58b40928c3b0ad85534a416ac1055a0c1ca59852ca26329118d423d7e305e66 languageName: node linkType: hard -"@libp2p/logger@npm:^2.0.0, @libp2p/logger@npm:^2.0.7": +"@libp2p/logger@npm:^2.0.7": version: 2.1.1 resolution: "@libp2p/logger@npm:2.1.1" dependencies: @@ -2337,44 +2329,57 @@ __metadata: languageName: node linkType: hard -"@libp2p/logger@npm:^3.0.0, @libp2p/logger@npm:^3.0.2": - version: 3.0.2 - resolution: "@libp2p/logger@npm:3.0.2" +"@libp2p/logger@npm:^3.0.0, @libp2p/logger@npm:^3.1.0": + version: 3.1.0 + resolution: "@libp2p/logger@npm:3.1.0" dependencies: - "@libp2p/interface": ^0.1.2 + "@libp2p/interface": ^0.1.6 "@multiformats/multiaddr": ^12.1.5 debug: ^4.3.4 interface-datastore: ^8.2.0 multiformats: ^12.0.1 - checksum: 55734af96bdcf1572796e26d859c4a8f6ee9b4f0ca98bf0112e46cc90ce690a807e533095db79887919d7d33d5a90d2365e1354cdd3787b08ec396388a32f7a6 + checksum: ff80803afe40a1078dbaf3119fd312bce283660e6e3d0705c91395c5e1f09520217ef29d8426880db23837bcfa444579cae6b4fc6e9ae4c869ecf4e5b9a0a59c + languageName: node + linkType: hard + +"@libp2p/logger@npm:^4.0.1": + version: 4.0.5 + resolution: "@libp2p/logger@npm:4.0.5" + dependencies: + "@libp2p/interface": ^1.1.2 + "@multiformats/multiaddr": ^12.1.10 + debug: ^4.3.4 + interface-datastore: ^8.2.0 + multiformats: ^13.0.0 + checksum: e0bc60e3ac6f3ab017eb16b26eb3badc3c728291fdbea28cf9ef2f9fbfc1161d562acfa4c4fbd148d6ad0de8a4417f02d1ffada611af5255fff8495df2d65707 languageName: node linkType: hard "@libp2p/mplex@npm:^9.0.4": - version: 9.0.4 - resolution: "@libp2p/mplex@npm:9.0.4" + version: 9.0.12 + resolution: "@libp2p/mplex@npm:9.0.12" dependencies: - "@libp2p/interface": ^0.1.2 - "@libp2p/logger": ^3.0.2 + "@libp2p/interface": ^0.1.6 + "@libp2p/logger": ^3.1.0 abortable-iterator: ^5.0.1 benchmark: ^2.1.4 it-batched-bytes: ^2.0.2 it-pushable: ^3.2.0 it-stream-types: ^2.0.1 - rate-limiter-flexible: ^2.3.11 + rate-limiter-flexible: ^3.0.0 uint8-varint: ^2.0.0 uint8arraylist: ^2.4.3 uint8arrays: ^4.0.6 - checksum: ab3bbda7a8fd4c4fd5c0a3d6865cfcd2c2659eff8dbb81c20b8dceeaa7a804039bada63faa7e9d04cdf48c215cc772cef0d38be0f61046b55f0c888b879ce9c3 + checksum: 13c9c8a1826d81fc0497f84bfff97d75563602ea7bf07ac64f29bb3f6c13129610e825bca1bda8d63de62945009a0dc91b607c4979d2e91ad6c2e10f8008ec10 languageName: node linkType: hard -"@libp2p/multistream-select@npm:^4.0.2": - version: 4.0.2 - resolution: "@libp2p/multistream-select@npm:4.0.2" +"@libp2p/multistream-select@npm:^4.0.6": + version: 4.0.10 + resolution: "@libp2p/multistream-select@npm:4.0.10" dependencies: - "@libp2p/interface": ^0.1.2 - "@libp2p/logger": ^3.0.2 + "@libp2p/interface": ^0.1.6 + "@libp2p/logger": ^3.1.0 abortable-iterator: ^5.0.1 it-first: ^3.0.1 it-handshake: ^4.1.3 @@ -2384,90 +2389,76 @@ __metadata: it-pushable: ^3.2.0 it-reader: ^6.0.1 it-stream-types: ^2.0.1 + uint8-varint: ^2.0.0 uint8arraylist: ^2.4.3 uint8arrays: ^4.0.6 - checksum: 2909e297eabcb7b3da0391f6a323bb414d509e4fbe2feaa3a6a287f95bb0d46c27ec3d952755353142fe910b1fc613114e3efa17bc0f2f50109897d1a7dc85e5 - languageName: node - linkType: hard - -"@libp2p/peer-collections@npm:^4.0.3": - version: 4.0.3 - resolution: "@libp2p/peer-collections@npm:4.0.3" - dependencies: - "@libp2p/interface": ^0.1.2 - "@libp2p/peer-id": ^3.0.2 - checksum: 3a8784a17d1cbd3098b43d71b888ff15e4d9b2638090c44cb02b5d02c0590ef589788cc0f61708050b74f8d52b38ed4101796016e64478a2e3c1ae0f321e8451 + checksum: 3a3d32cc3605f73cef4039712b394c1bf18d5ca624f9b8b43c880b4d65b8bbc0491273a970183febd52c3f30dfbdbd2f6ac4aa102a117661e057749e67910bb6 languageName: node linkType: hard -"@libp2p/peer-id-factory@npm:^3.0.3": - version: 3.0.3 - resolution: "@libp2p/peer-id-factory@npm:3.0.3" +"@libp2p/peer-collections@npm:^4.0.8": + version: 4.0.11 + resolution: "@libp2p/peer-collections@npm:4.0.11" dependencies: - "@libp2p/crypto": ^2.0.3 - "@libp2p/interface": ^0.1.2 - "@libp2p/peer-id": ^3.0.2 - multiformats: ^12.0.1 - protons-runtime: ^5.0.0 - uint8arraylist: ^2.4.3 - uint8arrays: ^4.0.6 - checksum: 093530837f0d73fc6cb40c168c10c4bc2f196783b089c1006b9958b5ad86b93963de9fccf18159ce60afad01896c549bee5e368283fba288d9abf2598c7a6ea5 + "@libp2p/interface": ^0.1.6 + "@libp2p/peer-id": ^3.0.6 + checksum: b15651d905007f51cb9867f08aa3af21b8a36fef67a2f3a6309483df13d99d7611e6a9aed7dbaf1cf2adbe4ccbca0d366be92dc4e66afc705bbd9ffe43dc8b80 languageName: node linkType: hard -"@libp2p/peer-id-factory@npm:^3.0.4": - version: 3.0.4 - resolution: "@libp2p/peer-id-factory@npm:3.0.4" +"@libp2p/peer-id-factory@npm:^3.0.3, @libp2p/peer-id-factory@npm:^3.0.4, @libp2p/peer-id-factory@npm:^3.0.8": + version: 3.0.11 + resolution: "@libp2p/peer-id-factory@npm:3.0.11" dependencies: - "@libp2p/crypto": ^2.0.4 - "@libp2p/interface": ^0.1.2 - "@libp2p/peer-id": ^3.0.2 + "@libp2p/crypto": ^2.0.8 + "@libp2p/interface": ^0.1.6 + "@libp2p/peer-id": ^3.0.6 multiformats: ^12.0.1 protons-runtime: ^5.0.0 uint8arraylist: ^2.4.3 uint8arrays: ^4.0.6 - checksum: 40c534029bfa8d9b98119ee2c0ce3c0c91bcf7280028e7ba8bfe4da2a90de53014791d75c8d1400877919f748918704276b6e3337c5128975842c0593464fda9 + checksum: bdcee4fef7f8aace6a8316e523e8c82753986a42c58b51f59d04534a1095c9c1eec8193e859614aa2589a7f5e43e64e529bb0b475e7bad7150b2034b2ebc0aa2 languageName: node linkType: hard -"@libp2p/peer-id@npm:^3.0.0, @libp2p/peer-id@npm:^3.0.2": - version: 3.0.2 - resolution: "@libp2p/peer-id@npm:3.0.2" +"@libp2p/peer-id@npm:^3.0.0, @libp2p/peer-id@npm:^3.0.2, @libp2p/peer-id@npm:^3.0.6": + version: 3.0.6 + resolution: "@libp2p/peer-id@npm:3.0.6" dependencies: - "@libp2p/interface": ^0.1.2 + "@libp2p/interface": ^0.1.6 multiformats: ^12.0.1 uint8arrays: ^4.0.6 - checksum: 021a5854dd2b8afc0d83e1541531d9710be237d5a6883aa0965d85cba629fbaaa27f68f774f5fa9c331df6a8e6b1187031d270905a7c33f103177cf0e190e2bb + checksum: d573948b9b9fc64d80a2175ff27c9878d15a43aec5c71d9486c9b6d5e4a0510c9d935c71e35420cd09d55f32c3a87307112627becc5d9f709b4b19a7100f7d30 languageName: node linkType: hard -"@libp2p/peer-record@npm:^6.0.3": - version: 6.0.3 - resolution: "@libp2p/peer-record@npm:6.0.3" +"@libp2p/peer-record@npm:^6.0.9": + version: 6.0.12 + resolution: "@libp2p/peer-record@npm:6.0.12" dependencies: - "@libp2p/crypto": ^2.0.3 - "@libp2p/interface": ^0.1.2 - "@libp2p/peer-id": ^3.0.2 - "@libp2p/utils": ^4.0.2 + "@libp2p/crypto": ^2.0.8 + "@libp2p/interface": ^0.1.6 + "@libp2p/peer-id": ^3.0.6 + "@libp2p/utils": ^4.0.7 "@multiformats/multiaddr": ^12.1.5 protons-runtime: ^5.0.0 - uint8-varint: ^1.0.2 + uint8-varint: ^2.0.0 uint8arraylist: ^2.4.3 uint8arrays: ^4.0.6 - checksum: 63e7a6190a608d649761303b8e2783fcdb8f2024d3cafc137afeb67eab553469fb1f052cbe88fdb489385cd621aaa4cf52d9d93effb99111704158d98408e1cb + checksum: d252cafa7c63fc05c8715cb4de8e340bbd76e5438b3eaf309f6e2de5a41d550f399f4ddba110ef2edcb4e667659baa20d4da2cc4d9f19611ae4402f72eb87006 languageName: node linkType: hard -"@libp2p/peer-store@npm:^9.0.3": - version: 9.0.3 - resolution: "@libp2p/peer-store@npm:9.0.3" +"@libp2p/peer-store@npm:^9.0.9": + version: 9.0.12 + resolution: "@libp2p/peer-store@npm:9.0.12" dependencies: - "@libp2p/interface": ^0.1.2 - "@libp2p/logger": ^3.0.2 - "@libp2p/peer-collections": ^4.0.3 - "@libp2p/peer-id": ^3.0.2 - "@libp2p/peer-id-factory": ^3.0.3 - "@libp2p/peer-record": ^6.0.3 + "@libp2p/interface": ^0.1.6 + "@libp2p/logger": ^3.1.0 + "@libp2p/peer-collections": ^4.0.8 + "@libp2p/peer-id": ^3.0.6 + "@libp2p/peer-id-factory": ^3.0.8 + "@libp2p/peer-record": ^6.0.9 "@multiformats/multiaddr": ^12.1.5 interface-datastore: ^8.2.0 it-all: ^3.0.2 @@ -2476,79 +2467,80 @@ __metadata: protons-runtime: ^5.0.0 uint8arraylist: ^2.4.3 uint8arrays: ^4.0.6 - checksum: 25b489c067ad910dcb8290afcbd5669ca87ddba9e1bed19c3b0279ab328d147b0f7fd0bc736707da61f897e1864c525733b2f9daae7791d7a67b76e0e7e1826b + checksum: b4d3ee98781742a7f46d50afc43f6b530058c9d8f2db04fd66c391d59427eaa4b49eb9d4df26e6bd4e6ae8d9a60e90686ae522f13e9ad517da4860c3f868a778 languageName: node linkType: hard "@libp2p/tcp@npm:^8.0.4": - version: 8.0.4 - resolution: "@libp2p/tcp@npm:8.0.4" + version: 8.0.13 + resolution: "@libp2p/tcp@npm:8.0.13" dependencies: - "@libp2p/interface": ^0.1.2 - "@libp2p/logger": ^3.0.2 - "@libp2p/utils": ^4.0.2 + "@libp2p/interface": ^0.1.6 + "@libp2p/logger": ^3.1.0 + "@libp2p/utils": ^4.0.7 "@multiformats/mafmt": ^12.1.2 "@multiformats/multiaddr": ^12.1.5 - "@types/sinon": ^10.0.15 + "@types/sinon": ^17.0.0 stream-to-it: ^0.2.2 - checksum: ba7fc1da50181c6e118d362f85baf6a16cdab609be9eb2b7c182326cabef87676df03f9b5f816bac8cb2d597b1462075ebe7ca4d84243b60ba45359322aa4185 + checksum: cceff8633265c3bee7b0a246808fe26a61cb5876ca3363ae3278ebd9d2bd4775e3d3057c5ef8f18b8dd78368a0fd6359c94e7f8cd3f747ad4ebc89fce80db274 languageName: node linkType: hard -"@libp2p/utils@npm:^4.0.2": - version: 4.0.2 - resolution: "@libp2p/utils@npm:4.0.2" +"@libp2p/utils@npm:^4.0.7": + version: 4.0.7 + resolution: "@libp2p/utils@npm:4.0.7" dependencies: "@chainsafe/is-ip": ^2.0.2 - "@libp2p/interface": ^0.1.2 - "@libp2p/logger": ^3.0.2 + "@libp2p/interface": ^0.1.6 + "@libp2p/logger": ^3.1.0 "@multiformats/multiaddr": ^12.1.5 + "@multiformats/multiaddr-matcher": ^1.0.1 is-loopback-addr: ^2.0.1 it-stream-types: ^2.0.1 private-ip: ^3.0.0 uint8arraylist: ^2.4.3 - checksum: 1fd40278c58fe75d587f2d209bec039787b9997ba3ad9e2f8a47b716247efb73be98093d08fa5872395cd74b6f5d1b575fd5319d545c31155313a865c9135aee + checksum: df883f04b9efda532009ae0b2f03d918567277479e6f115a02673394e4239605bbaa0718e7cea7bd4c70307b44cd782492b6fa4ad9f03c7c4621f12cbb1f7d4c languageName: node linkType: hard -"@lmdb/lmdb-darwin-arm64@npm:2.9.1": - version: 2.9.1 - resolution: "@lmdb/lmdb-darwin-arm64@npm:2.9.1" +"@lmdb/lmdb-darwin-arm64@npm:2.9.2": + version: 2.9.2 + resolution: "@lmdb/lmdb-darwin-arm64@npm:2.9.2" conditions: os=darwin & cpu=arm64 languageName: node linkType: hard -"@lmdb/lmdb-darwin-x64@npm:2.9.1": - version: 2.9.1 - resolution: "@lmdb/lmdb-darwin-x64@npm:2.9.1" +"@lmdb/lmdb-darwin-x64@npm:2.9.2": + version: 2.9.2 + resolution: "@lmdb/lmdb-darwin-x64@npm:2.9.2" conditions: os=darwin & cpu=x64 languageName: node linkType: hard -"@lmdb/lmdb-linux-arm64@npm:2.9.1": - version: 2.9.1 - resolution: "@lmdb/lmdb-linux-arm64@npm:2.9.1" +"@lmdb/lmdb-linux-arm64@npm:2.9.2": + version: 2.9.2 + resolution: "@lmdb/lmdb-linux-arm64@npm:2.9.2" conditions: os=linux & cpu=arm64 languageName: node linkType: hard -"@lmdb/lmdb-linux-arm@npm:2.9.1": - version: 2.9.1 - resolution: "@lmdb/lmdb-linux-arm@npm:2.9.1" +"@lmdb/lmdb-linux-arm@npm:2.9.2": + version: 2.9.2 + resolution: "@lmdb/lmdb-linux-arm@npm:2.9.2" conditions: os=linux & cpu=arm languageName: node linkType: hard -"@lmdb/lmdb-linux-x64@npm:2.9.1": - version: 2.9.1 - resolution: "@lmdb/lmdb-linux-x64@npm:2.9.1" +"@lmdb/lmdb-linux-x64@npm:2.9.2": + version: 2.9.2 + resolution: "@lmdb/lmdb-linux-x64@npm:2.9.2" conditions: os=linux & cpu=x64 languageName: node linkType: hard -"@lmdb/lmdb-win32-x64@npm:2.9.1": - version: 2.9.1 - resolution: "@lmdb/lmdb-win32-x64@npm:2.9.1" +"@lmdb/lmdb-win32-x64@npm:2.9.2": + version: 2.9.2 + resolution: "@lmdb/lmdb-win32-x64@npm:2.9.2" conditions: os=win32 & cpu=x64 languageName: node linkType: hard @@ -2572,30 +2564,30 @@ __metadata: languageName: node linkType: hard -"@monorepo-utils/package-utils@npm:^2.10.2": - version: 2.10.2 - resolution: "@monorepo-utils/package-utils@npm:2.10.2" +"@monorepo-utils/package-utils@npm:^2.10.4": + version: 2.10.4 + resolution: "@monorepo-utils/package-utils@npm:2.10.4" dependencies: globby: ^11.0.1 load-json-file: ^6.2.0 upath: ^2.0.1 yaml: ^2.1.3 - checksum: fa8ff74b63a5760bb5088c1897f2e82c77d8c5e60858141d354b434fcefdf19a8d590721ddec328821fecfe8797018bdb79ad20be02a19d168859d19f03823ad + checksum: aa55887ff790693e2db7158aa28fbf178c7041607e65f857f516306e6ff6a77d596151ee6d61519fce8369db8cffffd07ee6fb66b99f12619f3845985dc7543f languageName: node linkType: hard "@monorepo-utils/workspaces-to-typescript-project-references@npm:^2.9.0": - version: 2.10.2 - resolution: "@monorepo-utils/workspaces-to-typescript-project-references@npm:2.10.2" + version: 2.10.4 + resolution: "@monorepo-utils/workspaces-to-typescript-project-references@npm:2.10.4" dependencies: - "@monorepo-utils/package-utils": ^2.10.2 + "@monorepo-utils/package-utils": ^2.10.4 comment-json: ^3.0.3 meow: ^7.1.1 semver-match: 0.1.1 upath: ^2.0.1 bin: workspaces-to-typescript-project-references: bin/cmd.js - checksum: 076575a837a6a928e029eeac284278e2953638a60632264d60e2efcdb216fba1d8de4fc2a74ab598ad71663a87cc595cbe3b8732da7d4845b44f1fb321a4da74 + checksum: 34d539247bdcaff9f0182ec8654a5f63d81bd8b06e0947fb6ab3f8c72127684e45906510576fa1a15dd31d8b7d7ad99f29918e5a4ff9b21a74f45227f8b6ccef languageName: node linkType: hard @@ -2650,51 +2642,36 @@ __metadata: languageName: node linkType: hard -"@multiformats/multiaddr-matcher@npm:^1.0.0": - version: 1.0.1 - resolution: "@multiformats/multiaddr-matcher@npm:1.0.1" +"@multiformats/multiaddr-matcher@npm:^1.0.0, @multiformats/multiaddr-matcher@npm:^1.0.1": + version: 1.1.2 + resolution: "@multiformats/multiaddr-matcher@npm:1.1.2" dependencies: "@chainsafe/is-ip": ^2.0.1 "@multiformats/multiaddr": ^12.0.0 - multiformats: ^12.0.1 - checksum: 71579db42aa0e22297e542946d7ad2ab5a3427d619de6838cce46cce3bb168615577b6e94b58fca1b4af94520dae366810ecc309081810e15ff61781591908d5 + multiformats: ^13.0.0 + checksum: ae0619211ad1a4f1021993c1372f6498cbaec07897559b0b8644e0c8e53a3fc209136d3faf4f6cef5b1533f952b55b232fd6eb089d578a3594fa92d01802d4c3 languageName: node linkType: hard -"@multiformats/multiaddr@npm:^12.0.0, @multiformats/multiaddr@npm:^12.1.3": - version: 12.1.5 - resolution: "@multiformats/multiaddr@npm:12.1.5" +"@multiformats/multiaddr@npm:^12.0.0, @multiformats/multiaddr@npm:^12.1.10, @multiformats/multiaddr@npm:^12.1.3, @multiformats/multiaddr@npm:^12.1.5": + version: 12.1.12 + resolution: "@multiformats/multiaddr@npm:12.1.12" dependencies: "@chainsafe/is-ip": ^2.0.1 "@chainsafe/netmask": ^2.0.0 - "@libp2p/interfaces": ^3.3.1 - dns-over-http-resolver: ^2.1.0 - multiformats: ^12.0.1 - uint8arrays: ^4.0.2 - varint: ^6.0.0 - checksum: 01181807070382fb96019aec68df6276c90801185eedeb82c69dfef0ff6898eacc87a13e639c364dc0da976c5be623b56198e7107f2da40e4ef3a2d3523e8c49 - languageName: node - linkType: hard - -"@multiformats/multiaddr@npm:^12.1.5": - version: 12.1.7 - resolution: "@multiformats/multiaddr@npm:12.1.7" - dependencies: - "@chainsafe/is-ip": ^2.0.1 - "@chainsafe/netmask": ^2.0.0 - "@libp2p/interface": ^0.1.1 - dns-over-http-resolver: ^2.1.0 - multiformats: ^12.0.1 + "@libp2p/interface": ^1.0.0 + dns-over-http-resolver: 3.0.0 + multiformats: ^13.0.0 uint8-varint: ^2.0.1 - uint8arrays: ^4.0.2 - checksum: 96b83208b7bd3e9387f2fdac20fc554d962395c02661e9c1da819646d2f3129e1a76e5abc6a0c8d386c7126a7678e58d05b08dc812260b7cad2488533cbe44b0 + uint8arrays: ^5.0.0 + checksum: a50d62aeb330ef9df0d5b9a36980eba28dc619fce9adba9a5d5da4344091c42457501969cbc15e1d705dbf90513fb4730f91eabe1b68ceada56e3e721ebb3249 languageName: node linkType: hard -"@noble/ciphers@npm:^0.1.4": - version: 0.1.4 - resolution: "@noble/ciphers@npm:0.1.4" - checksum: a846f91dc876ea8cf01c20f04df2816926ad4e4d90169e6334de39b477ce13bf5e720f4df9f9898dd2a87643660ccc8a04aa466baf885c43860c270bcc7deced +"@noble/ciphers@npm:^0.4.0": + version: 0.4.1 + resolution: "@noble/ciphers@npm:0.4.1" + checksum: 8301334d6281c1cd6200716be6d01e30b8fd07b6ff7a537587187a649e625a347f24d52eba4812fc3535a077cd53e33a7abb77aeee19ff6662b7f048148f9e21 languageName: node linkType: hard @@ -2707,16 +2684,7 @@ __metadata: languageName: node linkType: hard -"@noble/curves@npm:^1.0.0, @noble/curves@npm:^1.1.0": - version: 1.1.0 - resolution: "@noble/curves@npm:1.1.0" - dependencies: - "@noble/hashes": 1.3.1 - checksum: 2658cdd3f84f71079b4e3516c47559d22cf4b55c23ac8ee9d2b1f8e5b72916d9689e59820e0f9d9cb4a46a8423af5b56dc6bb7782405c88be06a015180508db5 - languageName: node - linkType: hard - -"@noble/curves@npm:^1.2.0": +"@noble/curves@npm:1.2.0, @noble/curves@npm:~1.2.0": version: 1.2.0 resolution: "@noble/curves@npm:1.2.0" dependencies: @@ -2725,17 +2693,19 @@ __metadata: languageName: node linkType: hard -"@noble/hashes@npm:1.3.0": +"@noble/curves@npm:^1.0.0, @noble/curves@npm:^1.1.0, @noble/curves@npm:^1.2.0": version: 1.3.0 - resolution: "@noble/hashes@npm:1.3.0" - checksum: d7ddb6d7c60f1ce1f87facbbef5b724cdea536fc9e7f59ae96e0fc9de96c8f1a2ae2bdedbce10f7dcc621338dfef8533daa73c873f2b5c87fa1a4e05a95c2e2e + resolution: "@noble/curves@npm:1.3.0" + dependencies: + "@noble/hashes": 1.3.3 + checksum: b65342ee66c4a440eee2978524412eabba9a9efdd16d6370e15218c6a7d80bddf35e66bb57ed52c0dfd32cb9a717b439ab3a72db618f1a0066dfebe3fd12a421 languageName: node linkType: hard -"@noble/hashes@npm:1.3.1, @noble/hashes@npm:^1.3.1, @noble/hashes@npm:~1.3.0": - version: 1.3.1 - resolution: "@noble/hashes@npm:1.3.1" - checksum: 7fdefc0f7a0c1ec27acc6ff88841793e3f93ec4ce6b8a6a12bfc0dd70ae6b7c4c82fe305fdfeda1735d5ad4a9eebe761e6693b3d355689c559e91242f4bc95b1 +"@noble/hashes@npm:1.3.0": + version: 1.3.0 + resolution: "@noble/hashes@npm:1.3.0" + checksum: d7ddb6d7c60f1ce1f87facbbef5b724cdea536fc9e7f59ae96e0fc9de96c8f1a2ae2bdedbce10f7dcc621338dfef8533daa73c873f2b5c87fa1a4e05a95c2e2e languageName: node linkType: hard @@ -2746,6 +2716,13 @@ __metadata: languageName: node linkType: hard +"@noble/hashes@npm:1.3.3, @noble/hashes@npm:^1.3.1, @noble/hashes@npm:~1.3.0, @noble/hashes@npm:~1.3.2": + version: 1.3.3 + resolution: "@noble/hashes@npm:1.3.3" + checksum: 8a6496d1c0c64797339bc694ad06cdfaa0f9e56cd0c3f68ae3666cfb153a791a55deb0af9c653c7ed2db64d537aa3e3054629740d2f2338bb1dcb7ab60cd205b + languageName: node + linkType: hard + "@nodelib/fs.scandir@npm:2.1.5": version: 2.1.5 resolution: "@nodelib/fs.scandir@npm:2.1.5" @@ -2813,6 +2790,19 @@ __metadata: languageName: node linkType: soft +"@npmcli/agent@npm:^2.0.0": + version: 2.2.0 + resolution: "@npmcli/agent@npm:2.2.0" + dependencies: + agent-base: ^7.1.0 + http-proxy-agent: ^7.0.0 + https-proxy-agent: ^7.0.1 + lru-cache: ^10.0.1 + socks-proxy-agent: ^8.0.1 + checksum: 3b25312edbdfaa4089af28e2d423b6f19838b945e47765b0c8174c1395c79d43c3ad6d23cb364b43f59fd3acb02c93e3b493f72ddbe3dfea04c86843a7311fc4 + languageName: node + linkType: hard + "@npmcli/fs@npm:^3.1.0": version: 3.1.0 resolution: "@npmcli/fs@npm:3.1.0" @@ -2829,96 +2819,9 @@ __metadata: languageName: node linkType: hard -"@pkgr/utils@npm:^2.3.1": - version: 2.4.2 - resolution: "@pkgr/utils@npm:2.4.2" - dependencies: - cross-spawn: ^7.0.3 - fast-glob: ^3.3.0 - is-glob: ^4.0.3 - open: ^9.1.0 - picocolors: ^1.0.0 - tslib: ^2.6.0 - checksum: 24e04c121269317d259614cd32beea3af38277151c4002df5883c4be920b8e3490bb897748e844f9d46bf68230f86dabd4e8f093773130e7e60529a769a132fc - languageName: node - linkType: hard - -"@protobufjs/aspromise@npm:^1.1.1, @protobufjs/aspromise@npm:^1.1.2": - version: 1.1.2 - resolution: "@protobufjs/aspromise@npm:1.1.2" - checksum: 011fe7ef0826b0fd1a95935a033a3c0fd08483903e1aa8f8b4e0704e3233406abb9ee25350ec0c20bbecb2aad8da0dcea58b392bbd77d6690736f02c143865d2 - languageName: node - linkType: hard - -"@protobufjs/base64@npm:^1.1.2": - version: 1.1.2 - resolution: "@protobufjs/base64@npm:1.1.2" - checksum: 67173ac34de1e242c55da52c2f5bdc65505d82453893f9b51dc74af9fe4c065cf4a657a4538e91b0d4a1a1e0a0642215e31894c31650ff6e3831471061e1ee9e - languageName: node - linkType: hard - -"@protobufjs/codegen@npm:^2.0.4": - version: 2.0.4 - resolution: "@protobufjs/codegen@npm:2.0.4" - checksum: 59240c850b1d3d0b56d8f8098dd04787dcaec5c5bd8de186fa548de86b86076e1c50e80144b90335e705a044edf5bc8b0998548474c2a10a98c7e004a1547e4b - languageName: node - linkType: hard - -"@protobufjs/eventemitter@npm:^1.1.0": - version: 1.1.0 - resolution: "@protobufjs/eventemitter@npm:1.1.0" - checksum: 0369163a3d226851682f855f81413cbf166cd98f131edb94a0f67f79e75342d86e89df9d7a1df08ac28be2bc77e0a7f0200526bb6c2a407abbfee1f0262d5fd7 - languageName: node - linkType: hard - -"@protobufjs/fetch@npm:^1.1.0": - version: 1.1.0 - resolution: "@protobufjs/fetch@npm:1.1.0" - dependencies: - "@protobufjs/aspromise": ^1.1.1 - "@protobufjs/inquire": ^1.1.0 - checksum: 3fce7e09eb3f1171dd55a192066450f65324fd5f7cc01a431df01bb00d0a895e6bfb5b0c5561ce157ee1d886349c90703d10a4e11a1a256418ff591b969b3477 - languageName: node - linkType: hard - -"@protobufjs/float@npm:^1.0.2": - version: 1.0.2 - resolution: "@protobufjs/float@npm:1.0.2" - checksum: 5781e1241270b8bd1591d324ca9e3a3128d2f768077a446187a049e36505e91bc4156ed5ac3159c3ce3d2ba3743dbc757b051b2d723eea9cd367bfd54ab29b2f - languageName: node - linkType: hard - -"@protobufjs/inquire@npm:^1.1.0": - version: 1.1.0 - resolution: "@protobufjs/inquire@npm:1.1.0" - checksum: ca06f02eaf65ca36fb7498fc3492b7fc087bfcc85c702bac5b86fad34b692bdce4990e0ef444c1e2aea8c034227bd1f0484be02810d5d7e931c55445555646f4 - languageName: node - linkType: hard - -"@protobufjs/path@npm:^1.1.2": - version: 1.1.2 - resolution: "@protobufjs/path@npm:1.1.2" - checksum: 856eeb532b16a7aac071cacde5c5620df800db4c80cee6dbc56380524736205aae21e5ae47739114bf669ab5e8ba0e767a282ad894f3b5e124197cb9224445ee - languageName: node - linkType: hard - -"@protobufjs/pool@npm:^1.1.0": - version: 1.1.0 - resolution: "@protobufjs/pool@npm:1.1.0" - checksum: d6a34fbbd24f729e2a10ee915b74e1d77d52214de626b921b2d77288bd8f2386808da2315080f2905761527cceffe7ec34c7647bd21a5ae41a25e8212ff79451 - languageName: node - linkType: hard - -"@protobufjs/utf8@npm:^1.1.0": - version: 1.1.0 - resolution: "@protobufjs/utf8@npm:1.1.0" - checksum: f9bf3163d13aaa3b6f5e6fbf37a116e094ea021c0e1f2a7ccd0e12a29e2ce08dafba4e8b36e13f8ed7397e1591610ce880ed1289af4d66cf4ace8a36a9557278 - languageName: node - linkType: hard - -"@puppeteer/browsers@npm:1.7.1": - version: 1.7.1 - resolution: "@puppeteer/browsers@npm:1.7.1" +"@puppeteer/browsers@npm:1.9.1": + version: 1.9.1 + resolution: "@puppeteer/browsers@npm:1.9.1" dependencies: debug: 4.3.4 extract-zip: 2.0.1 @@ -2926,17 +2829,17 @@ __metadata: proxy-agent: 6.3.1 tar-fs: 3.0.4 unbzip2-stream: 1.4.3 - yargs: 17.7.1 + yargs: 17.7.2 bin: browsers: lib/cjs/main-cli.js - checksum: fb7cf7773a1aed4e34ce0952dbf9609a164e624d4f8e1f342b816fe3e983888d7a7b2fbafc963559e96cb5bca0d75fb9c81f2097f9b1f5478a0f1cc7cbc12dff + checksum: 1ea82e34af882dc6d7e8392a88ec4196e206a7f65743be39c196c7068d66b9bdfa370e28c6ab09946bd2baa2182adbcbf445e79cc9bcc5242f05878ae7045b27 languageName: node linkType: hard -"@scure/base@npm:~1.1.0": - version: 1.1.1 - resolution: "@scure/base@npm:1.1.1" - checksum: b4fc810b492693e7e8d0107313ac74c3646970c198bbe26d7332820886fa4f09441991023ec9aa3a2a51246b74409ab5ebae2e8ef148bbc253da79ac49130309 +"@scure/base@npm:~1.1.0, @scure/base@npm:~1.1.2": + version: 1.1.5 + resolution: "@scure/base@npm:1.1.5" + checksum: 9e9ee6088cb3aa0fb91f5a48497d26682c7829df3019b1251d088d166d7a8c0f941c68aaa8e7b96bbad20c71eb210397cb1099062cde3e29d4bad6b975c18519 languageName: node linkType: hard @@ -2951,6 +2854,17 @@ __metadata: languageName: node linkType: hard +"@scure/bip32@npm:1.3.2": + version: 1.3.2 + resolution: "@scure/bip32@npm:1.3.2" + dependencies: + "@noble/curves": ~1.2.0 + "@noble/hashes": ~1.3.2 + "@scure/base": ~1.1.2 + checksum: c5ae84fae43490853693b481531132b89e056d45c945fc8b92b9d032577f753dfd79c5a7bbcbf0a7f035951006ff0311b6cf7a389e26c9ec6335e42b20c53157 + languageName: node + linkType: hard + "@scure/bip39@npm:1.2.0": version: 1.2.0 resolution: "@scure/bip39@npm:1.2.0" @@ -2961,6 +2875,16 @@ __metadata: languageName: node linkType: hard +"@scure/bip39@npm:1.2.1": + version: 1.2.1 + resolution: "@scure/bip39@npm:1.2.1" + dependencies: + "@noble/hashes": ~1.3.0 + "@scure/base": ~1.1.0 + checksum: c5bd6f1328fdbeae2dcdd891825b1610225310e5e62a4942714db51066866e4f7bef242c7b06a1b9dcc8043a4a13412cf5c5df76d3b10aa9e36b82e9b6e3eeaa + languageName: node + linkType: hard + "@sinclair/typebox@npm:^0.27.8": version: 0.27.8 resolution: "@sinclair/typebox@npm:0.27.8" @@ -2969,11 +2893,11 @@ __metadata: linkType: hard "@sinonjs/commons@npm:^3.0.0": - version: 3.0.0 - resolution: "@sinonjs/commons@npm:3.0.0" + version: 3.0.1 + resolution: "@sinonjs/commons@npm:3.0.1" dependencies: type-detect: 4.0.8 - checksum: b4b5b73d4df4560fb8c0c7b38c7ad4aeabedd362f3373859d804c988c725889cde33550e4bcc7cd316a30f5152a2d1d43db71b6d0c38f5feef71fd8d016763f8 + checksum: a7c3e7cc612352f4004873747d9d8b2d4d90b13a6d483f685598c945a70e734e255f1ca5dc49702515533c403b32725defff148177453b3f3915bcb60e9d4601 languageName: node linkType: hard @@ -2986,13 +2910,6 @@ __metadata: languageName: node linkType: hard -"@tootallnate/once@npm:2": - version: 2.0.0 - resolution: "@tootallnate/once@npm:2.0.0" - checksum: ad87447820dd3f24825d2d947ebc03072b20a42bfc96cbafec16bff8bbda6c1a81fcb0be56d5b21968560c5359a0af4038a68ba150c3e1694fe4c109a063bed8 - languageName: node - linkType: hard - "@tootallnate/quickjs-emscripten@npm:^0.23.0": version: 0.23.0 resolution: "@tootallnate/quickjs-emscripten@npm:0.23.0" @@ -3001,12 +2918,12 @@ __metadata: linkType: hard "@trivago/prettier-plugin-sort-imports@npm:^4.1.1": - version: 4.2.0 - resolution: "@trivago/prettier-plugin-sort-imports@npm:4.2.0" + version: 4.3.0 + resolution: "@trivago/prettier-plugin-sort-imports@npm:4.3.0" dependencies: "@babel/generator": 7.17.7 "@babel/parser": ^7.20.5 - "@babel/traverse": 7.17.3 + "@babel/traverse": 7.23.2 "@babel/types": 7.17.0 javascript-natural-sort: 0.7.1 lodash: ^4.17.21 @@ -3016,7 +2933,7 @@ __metadata: peerDependenciesMeta: "@vue/compiler-sfc": optional: true - checksum: 2081ba9f1a2d33b9a3eeadeb3e713d404ee3d1a5cff3b20a23d94d6d915f0a8ff549616c1e77cd728f1b33733e0d7ab8e4c2512f344a612d81ece40025351160 + checksum: 22bb311ca24f09eef25915a66727e7be113b703f196f6ea0589dc9730b11a6f1e5e4bcc468213101d138b570d310792c83abb8d9487c53f9e597942fea052b6e languageName: node linkType: hard @@ -3049,274 +2966,264 @@ __metadata: linkType: hard "@types/abstract-leveldown@npm:*": - version: 7.2.1 - resolution: "@types/abstract-leveldown@npm:7.2.1" - checksum: 20689e7d144ce26d2384e2e151eed59046c95d573a6988da5e77e3076808eb4f435f474a0387af9ac786bfbfc7089e277dcfd9572ae902553d5c018e9b527a30 + version: 7.2.5 + resolution: "@types/abstract-leveldown@npm:7.2.5" + checksum: 3a99b13c81a53a62b42bea9cff326880de3146b4eeff528b039be69a268515b3120a6c12142e96646fcb0a03c463f298998581e86d9ddb29fbea3612f40edb2b languageName: node linkType: hard "@types/accepts@npm:*": - version: 1.3.5 - resolution: "@types/accepts@npm:1.3.5" + version: 1.3.7 + resolution: "@types/accepts@npm:1.3.7" dependencies: "@types/node": "*" - checksum: 590b7580570534a640510c071e09074cf63b5958b237a728f94322567350aea4d239f8a9d897a12b15c856b992ee4d7907e9812bb079886af2c00714e7fb3f60 + checksum: 7678cf74976e16093aff6e6f9755826faf069ac1e30179276158ce46ea246348ff22ca6bdd46cef08428881337d9ceefbf00bab08a7731646eb9fc9449d6a1e7 languageName: node linkType: hard "@types/babel__core@npm:^7.1.14": - version: 7.20.1 - resolution: "@types/babel__core@npm:7.20.1" + version: 7.20.5 + resolution: "@types/babel__core@npm:7.20.5" dependencies: "@babel/parser": ^7.20.7 "@babel/types": ^7.20.7 "@types/babel__generator": "*" "@types/babel__template": "*" "@types/babel__traverse": "*" - checksum: 9fcd9691a33074802d9057ff70b0e3ff3778f52470475b68698a0f6714fbe2ccb36c16b43dc924eb978cd8a81c1f845e5ff4699e7a47606043b539eb8c6331a8 + checksum: a3226f7930b635ee7a5e72c8d51a357e799d19cbf9d445710fa39ab13804f79ab1a54b72ea7d8e504659c7dfc50675db974b526142c754398d7413aa4bc30845 languageName: node linkType: hard "@types/babel__generator@npm:*": - version: 7.6.4 - resolution: "@types/babel__generator@npm:7.6.4" + version: 7.6.8 + resolution: "@types/babel__generator@npm:7.6.8" dependencies: "@babel/types": ^7.0.0 - checksum: 20effbbb5f8a3a0211e95959d06ae70c097fb6191011b73b38fe86deebefad8e09ee014605e0fd3cdaedc73d158be555866810e9166e1f09e4cfd880b874dcb0 + checksum: 5b332ea336a2efffbdeedb92b6781949b73498606ddd4205462f7d96dafd45ff3618770b41de04c4881e333dd84388bfb8afbdf6f2764cbd98be550d85c6bb48 languageName: node linkType: hard "@types/babel__template@npm:*": - version: 7.4.1 - resolution: "@types/babel__template@npm:7.4.1" + version: 7.4.4 + resolution: "@types/babel__template@npm:7.4.4" dependencies: "@babel/parser": ^7.1.0 "@babel/types": ^7.0.0 - checksum: 649fe8b42c2876be1fd28c6ed9b276f78152d5904ec290b6c861d9ef324206e0a5c242e8305c421ac52ecf6358fa7e32ab7a692f55370484825c1df29b1596ee + checksum: d7a02d2a9b67e822694d8e6a7ddb8f2b71a1d6962dfd266554d2513eefbb205b33ca71a0d163b1caea3981ccf849211f9964d8bd0727124d18ace45aa6c9ae29 languageName: node linkType: hard "@types/babel__traverse@npm:*, @types/babel__traverse@npm:^7.0.6": - version: 7.20.1 - resolution: "@types/babel__traverse@npm:7.20.1" + version: 7.20.5 + resolution: "@types/babel__traverse@npm:7.20.5" dependencies: "@babel/types": ^7.20.7 - checksum: 58341e23c649c0eba134a1682d4f20d027fad290d92e5740faa1279978f6ed476fc467ae51ce17a877e2566d805aeac64eae541168994367761ec883a4150221 + checksum: 608e0ab4fc31cd47011d98942e6241b34d461608c0c0e153377c5fd822c436c475f1ded76a56bfa76a1adf8d9266b727bbf9bfac90c4cb152c97f30dadc5b7e8 languageName: node linkType: hard "@types/bn.js@npm:*, @types/bn.js@npm:^5.1.3": - version: 5.1.3 - resolution: "@types/bn.js@npm:5.1.3" + version: 5.1.5 + resolution: "@types/bn.js@npm:5.1.5" dependencies: "@types/node": "*" - checksum: 6cd144b8192b6655a009021a4f838a725ea3eb4c5e6425ffc5b144788f7612fb09018c2359954edef32ab7db15f7070b77d05499318b6d9824a55cb7e6776620 + checksum: c87b28c4af74545624f8a3dae5294b16aa190c222626e8d4b2e327b33b1a3f1eeb43e7a24d914a9774bca43d8cd6e1cb0325c1f4b3a244af6693a024e1d918e6 languageName: node linkType: hard "@types/body-parser@npm:*": - version: 1.19.2 - resolution: "@types/body-parser@npm:1.19.2" + version: 1.19.5 + resolution: "@types/body-parser@npm:1.19.5" dependencies: "@types/connect": "*" "@types/node": "*" - checksum: e17840c7d747a549f00aebe72c89313d09fbc4b632b949b2470c5cb3b1cb73863901ae84d9335b567a79ec5efcfb8a28ff8e3f36bc8748a9686756b6d5681f40 + checksum: 1e251118c4b2f61029cc43b0dc028495f2d1957fe8ee49a707fb940f86a9bd2f9754230805598278fe99958b49e9b7e66eec8ef6a50ab5c1f6b93e1ba2aaba82 languageName: node linkType: hard "@types/connect@npm:*": - version: 3.4.35 - resolution: "@types/connect@npm:3.4.35" + version: 3.4.38 + resolution: "@types/connect@npm:3.4.38" dependencies: "@types/node": "*" - checksum: fe81351470f2d3165e8b12ce33542eef89ea893e36dd62e8f7d72566dfb7e448376ae962f9f3ea888547ce8b55a40020ca0e01d637fab5d99567673084542641 + checksum: 7eb1bc5342a9604facd57598a6c62621e244822442976c443efb84ff745246b10d06e8b309b6e80130026a396f19bf6793b7cecd7380169f369dac3bfc46fb99 languageName: node linkType: hard "@types/content-disposition@npm:*": - version: 0.5.5 - resolution: "@types/content-disposition@npm:0.5.5" - checksum: fdf7379db1d509990bcf9a21d85f05aad878596f28b1418f9179f6436cb22513262c670ce88c6055054a7f5804a9303eeacb70aa59a5e11ffdc1434559db9692 + version: 0.5.8 + resolution: "@types/content-disposition@npm:0.5.8" + checksum: eeea868fb510ae7a32aa2d7de680fba79d59001f3e758a334621e10bc0a6496d3a42bb79243a5e53b9c63cb524522853ccc144fe1ab160c4247d37cdb81146c4 languageName: node linkType: hard -"@types/cookiejar@npm:*": - version: 2.1.2 - resolution: "@types/cookiejar@npm:2.1.2" - checksum: f6e1903454007f86edd6c3520cbb4d553e1d4e17eaf1f77f6f75e3270f48cc828d74397a113a36942f5fe52f9fa71067bcfa738f53ad468fcca0bc52cb1cbd28 +"@types/cookiejar@npm:^2.1.5": + version: 2.1.5 + resolution: "@types/cookiejar@npm:2.1.5" + checksum: 04d5990e87b6387532d15a87d9ec9b2eb783039291193863751dcfd7fc723a3b3aa30ce4c06b03975cba58632e933772f1ff031af23eaa3ac7f94e71afa6e073 languageName: node linkType: hard "@types/cookies@npm:*": - version: 0.7.7 - resolution: "@types/cookies@npm:0.7.7" + version: 0.7.10 + resolution: "@types/cookies@npm:0.7.10" dependencies: "@types/connect": "*" "@types/express": "*" "@types/keygrip": "*" "@types/node": "*" - checksum: d3759efc1182cb0651808570ae13638677b67b0ea724eef7b174e58ffe6ea044b62c7c2715e532f76f88fce4dd8101ed32ac6fbb73226db654017924e8a2a1e6 + checksum: 99cd44a193398932ff7926cfaac1eb4441d3dc47c3f64fdfb28861acbeb290b6db6a20376f993defc9d302db92bb1d36189b89ba447a633f960535f3f0d34e2d languageName: node linkType: hard "@types/debug@npm:^4.1.7": - version: 4.1.8 - resolution: "@types/debug@npm:4.1.8" + version: 4.1.12 + resolution: "@types/debug@npm:4.1.12" dependencies: "@types/ms": "*" - checksum: a9a9bb40a199e9724aa944e139a7659173a9b274798ea7efbc277cb084bc37d32fc4c00877c3496fac4fed70a23243d284adb75c00b5fdabb38a22154d18e5df + checksum: 47876a852de8240bfdaf7481357af2b88cb660d30c72e73789abf00c499d6bc7cd5e52f41c915d1b9cd8ec9fef5b05688d7b7aef17f7f272c2d04679508d1053 languageName: node linkType: hard "@types/detect-node@npm:^2.0.0": - version: 2.0.0 - resolution: "@types/detect-node@npm:2.0.0" - checksum: f0f5c8ec948f5d4a40944773c8f81460ca7fa08fddf53330166feff1f8e28719bba9a01984872c5823c5de00c8223984381640a41b3c541bc57f3b2d529a0024 + version: 2.0.2 + resolution: "@types/detect-node@npm:2.0.2" + checksum: 064af29e09c5e336174d69b7709510457b1c6704d195a0d1dde9d26091c6cc8aaed39f8e7d329eedbc765655296b5a46db12b50841265a721f5bd4d0b48cbe6f languageName: node linkType: hard "@types/elliptic@npm:^6.4.16": - version: 6.4.16 - resolution: "@types/elliptic@npm:6.4.16" + version: 6.4.18 + resolution: "@types/elliptic@npm:6.4.18" dependencies: "@types/bn.js": "*" - checksum: fedecadbab1a469a22bc9f8e44ce730bd945faed82230174c9df4748f29948d34d9d6f7c79122049cd37f048522e28019a470df7a55c86765a82fb0d05f3f415 + checksum: c27613c530fb95441e5e6b456c8c9bc26568ca14c546aae6d7c1d8d46869f79a2272feaef266ac00bdb68b2671e6351ed01b91b82266eac30ca9092720825d16 languageName: node linkType: hard "@types/eslint-scope@npm:^3.7.3": - version: 3.7.4 - resolution: "@types/eslint-scope@npm:3.7.4" + version: 3.7.7 + resolution: "@types/eslint-scope@npm:3.7.7" dependencies: "@types/eslint": "*" "@types/estree": "*" - checksum: ea6a9363e92f301cd3888194469f9ec9d0021fe0a397a97a6dd689e7545c75de0bd2153dfb13d3ab532853a278b6572c6f678ce846980669e41029d205653460 + checksum: e2889a124aaab0b89af1bab5959847c5bec09809209255de0e63b9f54c629a94781daa04adb66bffcdd742f5e25a17614fb933965093c0eea64aacda4309380e languageName: node linkType: hard "@types/eslint@npm:*": - version: 8.44.1 - resolution: "@types/eslint@npm:8.44.1" + version: 8.56.2 + resolution: "@types/eslint@npm:8.56.2" dependencies: "@types/estree": "*" "@types/json-schema": "*" - checksum: 8b45be72d3c22a1ee0b1cc7e7fb0e34e32bbf959e6b7e0e46d160c17894aedf159c1db5c85750f10068884c741eebc37a1cc7ea659de23a8df0c9a3203e2ff9d + checksum: 38e054971596f5c0413f66a62dc26b10e0a21ac46ceacb06fbf8cfb838d20820787209b17218b3916e4c23d990ff77cfdb482d655cac0e0d2b837d430fcc5db8 languageName: node linkType: hard "@types/estree@npm:*, @types/estree@npm:^1.0.0": - version: 1.0.1 - resolution: "@types/estree@npm:1.0.1" - checksum: e9aa175eacb797216fafce4d41e8202c7a75555bc55232dee0f9903d7171f8f19f0ae7d5191bb1a88cb90e65468be508c0df850a9fb81b4433b293a5a749899d + version: 1.0.5 + resolution: "@types/estree@npm:1.0.5" + checksum: dd8b5bed28e6213b7acd0fb665a84e693554d850b0df423ac8076cc3ad5823a6bc26b0251d080bdc545af83179ede51dd3f6fa78cad2c46ed1f29624ddf3e41a languageName: node linkType: hard "@types/express-serve-static-core@npm:^4.17.33": - version: 4.17.35 - resolution: "@types/express-serve-static-core@npm:4.17.35" + version: 4.17.41 + resolution: "@types/express-serve-static-core@npm:4.17.41" dependencies: "@types/node": "*" "@types/qs": "*" "@types/range-parser": "*" "@types/send": "*" - checksum: cc8995d10c6feda475ec1b3a0e69eb0f35f21ab6b49129ad5c6f279e0bc5de8175bc04ec51304cb79a43eec3ed2f5a1e01472eb6d5f827b8c35c6ca8ad24eb6e + checksum: 12750f6511dd870bbaccfb8208ad1e79361cf197b147f62a3bedc19ec642f3a0f9926ace96705f4bc88ec2ae56f61f7ca8c2438e6b22f5540842b5569c28a121 languageName: node linkType: hard "@types/express@npm:*": - version: 4.17.17 - resolution: "@types/express@npm:4.17.17" + version: 4.17.21 + resolution: "@types/express@npm:4.17.21" dependencies: "@types/body-parser": "*" "@types/express-serve-static-core": ^4.17.33 "@types/qs": "*" "@types/serve-static": "*" - checksum: 0196dacc275ac3ce89d7364885cb08e7fb61f53ca101f65886dbf1daf9b7eb05c0943e2e4bbd01b0cc5e50f37e0eea7e4cbe97d0304094411ac73e1b7998f4da - languageName: node - linkType: hard - -"@types/fs-extra@npm:^11.0.1": - version: 11.0.1 - resolution: "@types/fs-extra@npm:11.0.1" - dependencies: - "@types/jsonfile": "*" - "@types/node": "*" - checksum: 3e930346e5d84f419deb8ced1c582beef8cb20d0bd8a0eb145a37d75bab0572a1895f0e48a0d681d386b3a58b9a992b2d2acecc464bcaec2548f53ea00718651 + checksum: fb238298630370a7392c7abdc80f495ae6c716723e114705d7e3fb67e3850b3859bbfd29391463a3fb8c0b32051847935933d99e719c0478710f8098ee7091c5 languageName: node linkType: hard -"@types/fs-extra@npm:^11.0.2": - version: 11.0.2 - resolution: "@types/fs-extra@npm:11.0.2" +"@types/fs-extra@npm:^11.0.1, @types/fs-extra@npm:^11.0.2": + version: 11.0.4 + resolution: "@types/fs-extra@npm:11.0.4" dependencies: "@types/jsonfile": "*" "@types/node": "*" - checksum: 5b3e30343ee62d2e393e1029355f13f64bab6f3416226e22492483f99da840e2e53ca22cbfa4ac3749f2f83f7086d19c009005c8fa175da01df0fae59c2d73e1 + checksum: 242cb84157631f057f76495c8220707541882c00a00195b603d937fb55e471afecebcb089bab50233ed3a59c69fd68bf65c1f69dd7fafe2347e139cc15b9b0e5 languageName: node linkType: hard "@types/graceful-fs@npm:^4.1.3": - version: 4.1.6 - resolution: "@types/graceful-fs@npm:4.1.6" + version: 4.1.9 + resolution: "@types/graceful-fs@npm:4.1.9" dependencies: "@types/node": "*" - checksum: c3070ccdc9ca0f40df747bced1c96c71a61992d6f7c767e8fd24bb6a3c2de26e8b84135ede000b7e79db530a23e7e88dcd9db60eee6395d0f4ce1dae91369dd4 + checksum: 79d746a8f053954bba36bd3d94a90c78de995d126289d656fb3271dd9f1229d33f678da04d10bce6be440494a5a73438e2e363e92802d16b8315b051036c5256 languageName: node linkType: hard "@types/http-assert@npm:*": - version: 1.5.3 - resolution: "@types/http-assert@npm:1.5.3" - checksum: 9553e5a0b8bcfdac4b51d3fa3b89a91b5450171861a667a5b4c47204e0f4a1ca865d97396e6ceaf220e87b64d06b7a8bad7bfba15ef97acb41a87507c9940dbc + version: 1.5.5 + resolution: "@types/http-assert@npm:1.5.5" + checksum: cd6bb7fd42cc6e2a702cb55370b8b25231954ad74c04bcd185b943a74ded3d4c28099c30f77b26951df2426441baff41718816c60b5af80efe2b8888d900bf93 languageName: node linkType: hard "@types/http-errors@npm:*": - version: 2.0.1 - resolution: "@types/http-errors@npm:2.0.1" - checksum: 3bb0c50b0a652e679a84c30cd0340d696c32ef6558518268c238840346c077f899315daaf1c26c09c57ddd5dc80510f2a7f46acd52bf949e339e35ed3ee9654f + version: 2.0.4 + resolution: "@types/http-errors@npm:2.0.4" + checksum: 1f3d7c3b32c7524811a45690881736b3ef741bf9849ae03d32ad1ab7062608454b150a4e7f1351f83d26a418b2d65af9bdc06198f1c079d75578282884c4e8e3 languageName: node linkType: hard "@types/istanbul-lib-coverage@npm:*, @types/istanbul-lib-coverage@npm:^2.0.0, @types/istanbul-lib-coverage@npm:^2.0.1": - version: 2.0.4 - resolution: "@types/istanbul-lib-coverage@npm:2.0.4" - checksum: a25d7589ee65c94d31464c16b72a9dc81dfa0bea9d3e105ae03882d616e2a0712a9c101a599ec482d297c3591e16336962878cb3eb1a0a62d5b76d277a890ce7 + version: 2.0.6 + resolution: "@types/istanbul-lib-coverage@npm:2.0.6" + checksum: 3feac423fd3e5449485afac999dcfcb3d44a37c830af898b689fadc65d26526460bedb889db278e0d4d815a670331796494d073a10ee6e3a6526301fe7415778 languageName: node linkType: hard "@types/istanbul-lib-report@npm:*": - version: 3.0.0 - resolution: "@types/istanbul-lib-report@npm:3.0.0" + version: 3.0.3 + resolution: "@types/istanbul-lib-report@npm:3.0.3" dependencies: "@types/istanbul-lib-coverage": "*" - checksum: 656398b62dc288e1b5226f8880af98087233cdb90100655c989a09f3052b5775bf98ba58a16c5ae642fb66c61aba402e07a9f2bff1d1569e3b306026c59f3f36 + checksum: b91e9b60f865ff08cb35667a427b70f6c2c63e88105eadd29a112582942af47ed99c60610180aa8dcc22382fa405033f141c119c69b95db78c4c709fbadfeeb4 languageName: node linkType: hard "@types/istanbul-reports@npm:^3.0.0": - version: 3.0.1 - resolution: "@types/istanbul-reports@npm:3.0.1" + version: 3.0.4 + resolution: "@types/istanbul-reports@npm:3.0.4" dependencies: "@types/istanbul-lib-report": "*" - checksum: f1ad54bc68f37f60b30c7915886b92f86b847033e597f9b34f2415acdbe5ed742fa559a0a40050d74cdba3b6a63c342cac1f3a64dba5b68b66a6941f4abd7903 + checksum: 93eb18835770b3431f68ae9ac1ca91741ab85f7606f310a34b3586b5a34450ec038c3eed7ab19266635499594de52ff73723a54a72a75b9f7d6a956f01edee95 languageName: node linkType: hard "@types/jest@npm:^29.5.0": - version: 29.5.3 - resolution: "@types/jest@npm:29.5.3" + version: 29.5.11 + resolution: "@types/jest@npm:29.5.11" dependencies: expect: ^29.0.0 pretty-format: ^29.0.0 - checksum: e36bb92e0b9e5ea7d6f8832baa42f087fc1697f6cd30ec309a07ea4c268e06ec460f1f0cfd2581daf5eff5763475190ec1ad8ac6520c49ccfe4f5c0a48bfa676 + checksum: f892a06ec9f0afa9a61cd7fa316ec614e21d4df1ad301b5a837787e046fcb40dfdf7f264a55e813ac6b9b633cb9d366bd5b8d1cea725e84102477b366df23fdd languageName: node linkType: hard "@types/json-schema@npm:*, @types/json-schema@npm:^7.0.12, @types/json-schema@npm:^7.0.8": - version: 7.0.12 - resolution: "@types/json-schema@npm:7.0.12" - checksum: 00239e97234eeb5ceefb0c1875d98ade6e922bfec39dd365ec6bd360b5c2f825e612ac4f6e5f1d13601b8b30f378f15e6faa805a3a732f4a1bbe61915163d293 + version: 7.0.15 + resolution: "@types/json-schema@npm:7.0.15" + checksum: 97ed0cb44d4070aecea772b7b2e2ed971e10c81ec87dd4ecc160322ffa55ff330dace1793489540e3e318d90942064bb697cc0f8989391797792d919737b3b98 languageName: node linkType: hard @@ -3328,46 +3235,46 @@ __metadata: linkType: hard "@types/jsonfile@npm:*": - version: 6.1.1 - resolution: "@types/jsonfile@npm:6.1.1" + version: 6.1.4 + resolution: "@types/jsonfile@npm:6.1.4" dependencies: "@types/node": "*" - checksum: 0f8fe0a9221a00e8413cffba723dfe16553868724b830237256fb0052ecd5cac96498189d1235a001cfa815f352008261c9ceb373f0aa58227f891e0c7a12c4d + checksum: 309fda20eb5f1cf68f2df28931afdf189c5e7e6bec64ac783ce737bb98908d57f6f58757ad5da9be37b815645a6f914e2d4f3ac66c574b8fe1ba6616284d0e97 languageName: node linkType: hard "@types/keygrip@npm:*": - version: 1.0.2 - resolution: "@types/keygrip@npm:1.0.2" - checksum: 60bc2738a4f107070ee3d96f44709cb38f3a96c7ccabab09f56c1b2b4d85f869fd8fb9f1f2937e863d0e9e781f005c2223b823bf32b859185b4f52370c352669 + version: 1.0.6 + resolution: "@types/keygrip@npm:1.0.6" + checksum: d157f60bf920492347791d2b26d530d5069ce05796549fbacd4c24d66ffbebbcb0ab67b21e7a1b80a593b9fd4b67dc4843dec04c12bbc2e0fddfb8577a826c41 languageName: node linkType: hard "@types/koa-bodyparser@npm:^4.3.10": - version: 4.3.10 - resolution: "@types/koa-bodyparser@npm:4.3.10" + version: 4.3.12 + resolution: "@types/koa-bodyparser@npm:4.3.12" dependencies: "@types/koa": "*" - checksum: 4b4cd176815a6c1fb0d593bfea03de1285e606d3a96e56ad3691144e35061750ed95e4ecf2ff8e25599d360a93646e29dbb167fdfaaa73ccf87ca5b6141ff0db + checksum: 645cc253c6b9b2e98252b1cdc75a4812cd6d3c228e426f9893a755324b7a6936559ec659a0ff288cb2642340b3cc4e2110167f24b84efc8e3b89c04fe67ed883 languageName: node linkType: hard "@types/koa-compose@npm:*": - version: 3.2.5 - resolution: "@types/koa-compose@npm:3.2.5" + version: 3.2.8 + resolution: "@types/koa-compose@npm:3.2.8" dependencies: "@types/koa": "*" - checksum: 5d1147c4b057eb158195f442f0384f06503f3e69dba99fb517b30a05261a9f92928945c12bb1cfc17a5b7d60db003f38b455a3a9b125f12e4fc81fffa396b3cf + checksum: 95c32bdee738ac7c10439bbf6342ca3b9f0aafd7e8118739eac7fb0fa703a23cfe4c88f63e13a69a16fbde702e0bcdc62b272aa734325fc8efa7e5625479752e languageName: node linkType: hard "@types/koa-compress@npm:^4.0.3": - version: 4.0.3 - resolution: "@types/koa-compress@npm:4.0.3" + version: 4.0.6 + resolution: "@types/koa-compress@npm:4.0.6" dependencies: "@types/koa": "*" "@types/node": "*" - checksum: 6f09e4ad8160204fbee9d0a452b83ba62fec503a2eec60cf41fc67a032971027b6858e0b90c6e05bf1ad3b006f7c7a2d02922db4d159d223ab8d33eeeb108757 + checksum: 0ec8ffac1bf3c7dc36a9ee4588f83ade0a485b615aff7e9e08082319b1b3f7e2f5954ed5ce4303a7f9ba1b4144081e0700cbf3165d1aef54e5e12130c810b2e4 languageName: node linkType: hard @@ -3381,210 +3288,173 @@ __metadata: linkType: hard "@types/koa-router@npm:^7.4.4": - version: 7.4.4 - resolution: "@types/koa-router@npm:7.4.4" + version: 7.4.8 + resolution: "@types/koa-router@npm:7.4.8" dependencies: "@types/koa": "*" - checksum: 23ff5b725daa1427dc822602f5d4fdcecca5f990595af48879e41338a9c71819ae312326028eef4645beb6ea32ea852416e2f0761a2abd5bf80c2575a3301837 + checksum: 30b9735748f25ac338ec4197430a10f1cf58eeea0445f0b64733ed95df82b9245a31c01bbfdd3c9b71e90aa7a1ccf9546f4dd91bac87f6186152e67146ad9b6c languageName: node linkType: hard "@types/koa-send@npm:*": - version: 4.1.4 - resolution: "@types/koa-send@npm:4.1.4" + version: 4.1.6 + resolution: "@types/koa-send@npm:4.1.6" dependencies: "@types/koa": "*" - checksum: 27732c85e97465810bc7631153368daa8e8715bd356eab344d0b3deaec162de09b6cd9d61a524f4d3631493234da7104167e788d593ce27405b70bdfb12fc81b + checksum: d46d207f1d826ccd74bf3a02180d0475be8456eb3a2244244d19cb3f1737251e163d73958fdcd12111e03c7c0545cc89e7888a6ef2ba370ebf2b2e804efaaaf1 languageName: node linkType: hard "@types/koa-static@npm:^4.0.2": - version: 4.0.2 - resolution: "@types/koa-static@npm:4.0.2" + version: 4.0.4 + resolution: "@types/koa-static@npm:4.0.4" dependencies: "@types/koa": "*" "@types/koa-send": "*" - checksum: a9c557a37b25a677f3aae084b2afd267fa78a728cd69aec20821d8acca3ef4bda172d1fd16a23711266d97e77962d037ffd25ee76b24608413032226321f461f + checksum: 99087a9b6f4214679932008fbed2d4332fca06cd01f2d333439bd1cf0844c313584c8eb6b805360d1c3d6c6c8a475468a5f4f73ecad551c8cc369e290ad41331 languageName: node linkType: hard -"@types/koa@npm:*, @types/koa@npm:^2.13.5, @types/koa@npm:^2.13.6": - version: 2.13.8 - resolution: "@types/koa@npm:2.13.8" +"@types/koa@npm:*, @types/koa@npm:^2.13.5, @types/koa@npm:^2.13.6, @types/koa@npm:^2.13.9": + version: 2.14.0 + resolution: "@types/koa@npm:2.14.0" dependencies: "@types/accepts": "*" "@types/content-disposition": "*" - "@types/cookies": "*" - "@types/http-assert": "*" - "@types/http-errors": "*" - "@types/keygrip": "*" - "@types/koa-compose": "*" - "@types/node": "*" - checksum: 76a2a6d219c65f242a43efca42970d864701c58319c346a91dd8c3b4df2021786fd0d600a88dfb098358c9085f9f4a2dfe62563641441cf21e11e2bfe04f4fdf - languageName: node - linkType: hard - -"@types/koa@npm:^2.13.9": - version: 2.13.9 - resolution: "@types/koa@npm:2.13.9" - dependencies: - "@types/accepts": "*" - "@types/content-disposition": "*" - "@types/cookies": "*" - "@types/http-assert": "*" - "@types/http-errors": "*" - "@types/keygrip": "*" - "@types/koa-compose": "*" - "@types/node": "*" - checksum: af9cd599c8e17e2ae0f4168a61d964e343f713d002b65fd995658d7addc6551ccadecfd32b3405cf44e4d360178ee4f972d6881533548261ae1f636a655d24b1 - languageName: node - linkType: hard - -"@types/koa__cors@npm:^4.0.0": - version: 4.0.0 - resolution: "@types/koa__cors@npm:4.0.0" - dependencies: - "@types/koa": "*" - checksum: 0a7f8c2ab9b957befbbe31b9293af05a95282fb984b8468b0c0a0a0101beefe2663ce716ca56fbf4e5ec5ca2d89193ee7d635ee84bb8b5d718df01149286f4d2 - languageName: node - linkType: hard - -"@types/level-errors@npm:*": - version: 3.0.0 - resolution: "@types/level-errors@npm:3.0.0" - checksum: ad9392663439306677ac9cb704f8fa0b64c300dfea4f3494369eb78a2e09c194156cbab2b52c71a361a09b735d54a2de65195dcadba0ec7db1d14a320198133e + "@types/cookies": "*" + "@types/http-assert": "*" + "@types/http-errors": "*" + "@types/keygrip": "*" + "@types/koa-compose": "*" + "@types/node": "*" + checksum: 57d809e42350c9ddefa2150306355e40757877468bb027e0bd99f5aeb43cfaf8ba8b14761ea65e419d6fb4c2403a1f3ed0762872a9cf040dbd14357caca56548 languageName: node linkType: hard -"@types/leveldown@npm:^4.0.3": +"@types/koa__cors@npm:^4.0.0": version: 4.0.3 - resolution: "@types/leveldown@npm:4.0.3" + resolution: "@types/koa__cors@npm:4.0.3" dependencies: - "@types/abstract-leveldown": "*" - "@types/node": "*" - checksum: 0a476bd8d3c71266fdb323875d3312bf7828d6ab9f1bf9882a0ff93d04044660e45cd211ca8ec34c5665eaa773f98fe9fee14235792835bd06cb68192fe44669 + "@types/koa": "*" + checksum: ca7bfd1ffacf6c425393e2716a88d66dfe1b5e9a32cd92253cc846fa95bd7fd44c1dc848252f13b7febb5bccf23b8e88716b051cfa04d18fa31e0768432dccb7 languageName: node linkType: hard -"@types/leveldown@npm:^4.0.4": - version: 4.0.4 - resolution: "@types/leveldown@npm:4.0.4" - dependencies: - "@types/abstract-leveldown": "*" - "@types/node": "*" - checksum: 630b2d2d1c48f83d14ab0f6c03ad2af1c427675c3692873c4fd3d673bde4140eabc028ce5736ad3d76aeea20769cf53df6f83468a4f0cf28f6d04dbb435edf48 +"@types/level-errors@npm:*": + version: 3.0.2 + resolution: "@types/level-errors@npm:3.0.2" + checksum: 3d9b801f6499f795b60ac723c1b3f93ca105f20ed26966eeb606c804b10c65984c3233fb99914644d75a3223f80f220eca74fda316640a85a5b3d7572cd86925 languageName: node linkType: hard -"@types/levelup@npm:^5.1.2": - version: 5.1.2 - resolution: "@types/levelup@npm:5.1.2" +"@types/leveldown@npm:^4.0.3, @types/leveldown@npm:^4.0.4": + version: 4.0.6 + resolution: "@types/leveldown@npm:4.0.6" dependencies: "@types/abstract-leveldown": "*" - "@types/level-errors": "*" "@types/node": "*" - checksum: 6740284488b6806ba398bc38842fa789edd5667a342830c544a6b3611ebeed957a08d03dc8bde1e32fe03ac9c439341647c044c1ff0f73a26bcded9ca302a009 + checksum: 8b06cbc6858f3956fe8e10a8bb58edd75369587e72ce33ab7e35e21e1f1c8e89981a337c977ffd3a635d4441113e434362ba37e343d8a0ec69cd7c8988450977 languageName: node linkType: hard -"@types/levelup@npm:^5.1.3": - version: 5.1.3 - resolution: "@types/levelup@npm:5.1.3" +"@types/levelup@npm:^5.1.2, @types/levelup@npm:^5.1.3": + version: 5.1.5 + resolution: "@types/levelup@npm:5.1.5" dependencies: "@types/abstract-leveldown": "*" "@types/level-errors": "*" "@types/node": "*" - checksum: 948642ea481573eec323e5f8b5475c1ab32b5eac0982e5a2a9904ac74336d32fe579d51219410770c1cb88f69f0135fd9be3cbfa5ee6e9b84f81eeddf7ed8e0d + checksum: 844798bdc805e3c449e478e283eb1196892d3f4fb6b24158faa5f10b283fa785da736917de1ec030ce1b8be9a8c54881cee2354632cb540ef80a325a19d920d1 languageName: node linkType: hard "@types/lodash.camelcase@npm:^4.3.7": - version: 4.3.7 - resolution: "@types/lodash.camelcase@npm:4.3.7" + version: 4.3.9 + resolution: "@types/lodash.camelcase@npm:4.3.9" dependencies: "@types/lodash": "*" - checksum: ef068b921ab439f7b1a2c0ebbd890ba64ba437fc6aeb5e3ac18d34da30f9054fceaac5ccfdfa9e12b2fc403e10910439d3ebf92ba2f93de03c2f02261adfc000 + checksum: f54132d38ffa72b25bce2111e4d28f339599f6d4fcfc1248a89d1d96445512d7a431f0b0e74f6e6c8d6bc09fe53cf94d9426e188d8feacb3ffe04cd9c3a602e7 languageName: node linkType: hard "@types/lodash.capitalize@npm:^4.2.7": - version: 4.2.7 - resolution: "@types/lodash.capitalize@npm:4.2.7" + version: 4.2.9 + resolution: "@types/lodash.capitalize@npm:4.2.9" dependencies: "@types/lodash": "*" - checksum: dab8b781d7dcc56c18ba0c8286a6ccb61cc598d936a449265453a473e62b2b6d7c109c4447dfeb8ccacc4088769bc3bfd0d39bc8797f03e4e685d4f4b1bc7c01 + checksum: 54a9154b2084392986646335d5ed4902a89c24ce675cf8b8cfcd022f6a0eed71c30c2f8cc4b2682cfc5c55a5e1fdad2340609ba58db451dfdd41d82b14c88995 languageName: node linkType: hard "@types/lodash.chunk@npm:^4.2.7": - version: 4.2.7 - resolution: "@types/lodash.chunk@npm:4.2.7" + version: 4.2.9 + resolution: "@types/lodash.chunk@npm:4.2.9" dependencies: "@types/lodash": "*" - checksum: 09df5ca00d8866776038bf94658d30b4ea84bffe5f81c14c01568bcb6be692bf59f6ea093de2bb0afbb3f8e54ae96ebd97595f2838ab59b77865427ca77d391e + checksum: ccffe7273a0941655d5b988baeffa8f7d4d19a8b43ed728ff4e616013506efe85914ba99d4ec299e0106506e1bca3923b065eabb0aa5f1e4b18f68e790ae6b88 languageName: node linkType: hard "@types/lodash.clonedeep@npm:^4.5.7": - version: 4.5.7 - resolution: "@types/lodash.clonedeep@npm:4.5.7" + version: 4.5.9 + resolution: "@types/lodash.clonedeep@npm:4.5.9" dependencies: "@types/lodash": "*" - checksum: 20d6a20970b3b54b3c10cf17ace1cea49c4905d7f7cae2575a98108466e8d4c9bea3b3449d11ccaac4da1fc9bab225f477f4c2dbea8ba877cc47f629455efb69 + checksum: ef85512b7dce7a4f981a818ae44d11982907e1f26b5b26bedf0957c35e8591eb8e1d24fa31ca851d4b40e0a1ee88563853d762412691fe5f357e8335cead2325 languageName: node linkType: hard "@types/lodash.clonedeepwith@npm:^4.5.7": - version: 4.5.7 - resolution: "@types/lodash.clonedeepwith@npm:4.5.7" + version: 4.5.9 + resolution: "@types/lodash.clonedeepwith@npm:4.5.9" dependencies: "@types/lodash": "*" - checksum: 2cfc7c55533abf491a2b6897a233244b187bf55ac51545e4f0b937721fdfb7e82cb3743290aa4dcb41487d653d95fdddc172b39318a893c9a2ca6658b0866430 + checksum: c690fb28126f7248894f08abe13d6c7684dd0a4e9ac545a419a8687438b50d2e6fe32b31176c65a394d3ade4fd16a145ecbf77e7521992414bf657b8b1d936c8 languageName: node linkType: hard "@types/lodash.every@npm:^4.6.7": - version: 4.6.7 - resolution: "@types/lodash.every@npm:4.6.7" + version: 4.6.9 + resolution: "@types/lodash.every@npm:4.6.9" dependencies: "@types/lodash": "*" - checksum: 9bf1475332401948453019a6fc4d9ee1275acfe52dccd2b81746927ef4623f6fb846849a15dfe08ec5b5c50e6302875a19ac469cc820e7f05d7517407ba41dc7 + checksum: 9239e078c1aba47ead8d3232d46869a6b9e886387c92c8d9a0a1549b7f21864cfa8428b5725ebbfcd31e5c54761a6fa3d4bbe049a776fe3e973cb10d967fbd0c languageName: node linkType: hard "@types/lodash.isequal@npm:^4.5.6": - version: 4.5.6 - resolution: "@types/lodash.isequal@npm:4.5.6" + version: 4.5.8 + resolution: "@types/lodash.isequal@npm:4.5.8" dependencies: "@types/lodash": "*" - checksum: 0f065989408a9e0584e6c27495be2cd4602e62650f55266aa195812582444463c0c8570c674ae84f947c11748f49ab43fd5b482fa120e08eeee4c23b162edc38 + checksum: f3180c2d2925514fff1908a1303c11468c9f39b47fd7b053416aad3f1447f8e4a9894dd0460187ac9ac19387e25aec8dd8214d13a50a0967e0dc9cca8e4c5353 languageName: node linkType: hard "@types/lodash.omit@npm:^4.5.7": - version: 4.5.7 - resolution: "@types/lodash.omit@npm:4.5.7" + version: 4.5.9 + resolution: "@types/lodash.omit@npm:4.5.9" dependencies: "@types/lodash": "*" - checksum: e30600de518e648f4e9590d9238506ceab561726ec2fe56b4ff29b17aa1e47cbf9188be259b7d606dfb2040998c778533ccc0c58f065cca44ab5a6dbc8b6e518 + checksum: 5be43f3598d6b1fa481fe7046e9e15e2225f547e0e309746c2b87f4e4fa8705e96c564e2762a9312e73e2a223d701ab893b122c506500de49a4b9fb40fb6d17c languageName: node linkType: hard "@types/lodash.pick@npm:^4.4.7": - version: 4.4.7 - resolution: "@types/lodash.pick@npm:4.4.7" + version: 4.4.9 + resolution: "@types/lodash.pick@npm:4.4.9" dependencies: "@types/lodash": "*" - checksum: 78428a83b5d85e75bd13fb632030f9adb08dfc5bde3a9b6a302434fe7ac98e62919f87a7337e0ba674017d63bc57a8855ef863f5fcecd25e63b1d39eba2e4697 + checksum: 007c298133b5bc2157f13d6641b139d2782b4af7b6a942cc4b1a427c6ebc6020e46b32ee2e782607389b0c63a5211d51152606424fdfe25f3eeac8afea0bdf02 languageName: node linkType: hard "@types/lodash.startcase@npm:^4.4.7": - version: 4.4.7 - resolution: "@types/lodash.startcase@npm:4.4.7" + version: 4.4.9 + resolution: "@types/lodash.startcase@npm:4.4.9" dependencies: "@types/lodash": "*" - checksum: ee5b903e7cb99a4c747325c38167dea70c10f9929823033f7f2e02aad5e227c84f80c6f7d9d6923a7c0f30a429c5ea4a1b6505bd50a96655b6ab7ac43e8ebe27 + checksum: 448203f0b6d31c1af9fe8292d5417af670bee560bb0af0cac3a6047b90c2d60ba03197367c2defae21e3982c665763197343863ce7d97131efa8e13e6431fe9f languageName: node linkType: hard @@ -3598,180 +3468,159 @@ __metadata: linkType: hard "@types/lodash@npm:*": - version: 4.14.196 - resolution: "@types/lodash@npm:4.14.196" - checksum: 201d17c3e62ae02a93c99ec78e024b2be9bd75564dd8fd8c26f6ac51a985ab280d28ce2688c3bcdfe785b0991cd9814edff19ee000234c7b45d9a697f09feb6a - languageName: node - linkType: hard - -"@types/memdown@npm:^3.0.0, @types/memdown@npm:^3.0.1": - version: 3.0.1 - resolution: "@types/memdown@npm:3.0.1" - dependencies: - "@types/abstract-leveldown": "*" - checksum: 08085fff44f1868d352ec3be81890cfd0034ad1086f3dbc8bbfc412d55434bb6f5bbd512a22a92f2f9c416ccb0784815ecaa0a6fada4478c9a39db3f0f7a1a43 + version: 4.14.202 + resolution: "@types/lodash@npm:4.14.202" + checksum: a91acf3564a568c6f199912f3eb2c76c99c5a0d7e219394294213b3f2d54f672619f0fde4da22b29dc5d4c31457cd799acc2e5cb6bd90f9af04a1578483b6ff7 languageName: node linkType: hard -"@types/memdown@npm:^3.0.2": - version: 3.0.2 - resolution: "@types/memdown@npm:3.0.2" +"@types/memdown@npm:^3.0.0, @types/memdown@npm:^3.0.1, @types/memdown@npm:^3.0.2, @types/memdown@npm:^3.0.3": + version: 3.0.5 + resolution: "@types/memdown@npm:3.0.5" dependencies: "@types/abstract-leveldown": "*" - checksum: bfc36240e32ed6f82b2b858be88aa8814e8c1e0a8922aae8bf44ce07a2c9e0e7bf867e01cab8aad10e1cab2d0bcb0b800b4f63b89d9c791328892acc00683469 + checksum: a0c384858354da754933a83295642ac8710af08e1bf499ff3ae87d86ea34a0d9ebf2bdf75ec45d992cd152580c671c82ea4a0aea03f10eeb3aed33eceb21819e languageName: node linkType: hard -"@types/memdown@npm:^3.0.3": - version: 3.0.3 - resolution: "@types/memdown@npm:3.0.3" - dependencies: - "@types/abstract-leveldown": "*" - checksum: 9aa311838574b51e4334878102c80c6abc0e1ec14fe00f98c6ee812f3e6b24db53de632e0f763692730e0147e5c41add0ac4257c3deb3dc7abce176708ed73a4 +"@types/methods@npm:^1.1.4": + version: 1.1.4 + resolution: "@types/methods@npm:1.1.4" + checksum: ad2a7178486f2fd167750f3eb920ab032a947ff2e26f55c86670a6038632d790b46f52e5b6ead5823f1e53fc68028f1e9ddd15cfead7903e04517c88debd72b1 languageName: node linkType: hard "@types/mime@npm:*": - version: 3.0.1 - resolution: "@types/mime@npm:3.0.1" - checksum: 4040fac73fd0cea2460e29b348c1a6173da747f3a87da0dbce80dd7a9355a3d0e51d6d9a401654f3e5550620e3718b5a899b2ec1debf18424e298a2c605346e7 + version: 3.0.4 + resolution: "@types/mime@npm:3.0.4" + checksum: a6139c8e1f705ef2b064d072f6edc01f3c099023ad7c4fce2afc6c2bf0231888202adadbdb48643e8e20da0ce409481a49922e737eca52871b3dc08017455843 languageName: node linkType: hard "@types/mime@npm:^1": - version: 1.3.2 - resolution: "@types/mime@npm:1.3.2" - checksum: 0493368244cced1a69cb791b485a260a422e6fcc857782e1178d1e6f219f1b161793e9f87f5fae1b219af0f50bee24fcbe733a18b4be8fdd07a38a8fb91146fd + version: 1.3.5 + resolution: "@types/mime@npm:1.3.5" + checksum: e29a5f9c4776f5229d84e525b7cd7dd960b51c30a0fb9a028c0821790b82fca9f672dab56561e2acd9e8eed51d431bde52eafdfef30f643586c4162f1aecfc78 languageName: node linkType: hard "@types/minimist@npm:^1.2.0": - version: 1.2.2 - resolution: "@types/minimist@npm:1.2.2" - checksum: b8da83c66eb4aac0440e64674b19564d9d86c80ae273144db9681e5eeff66f238ade9515f5006ffbfa955ceff8b89ad2bd8ec577d7caee74ba101431fb07045d + version: 1.2.5 + resolution: "@types/minimist@npm:1.2.5" + checksum: 477047b606005058ab0263c4f58097136268007f320003c348794f74adedc3166ffc47c80ec3e94687787f2ab7f4e72c468223946e79892cf0fd9e25e9970a90 languageName: node linkType: hard "@types/ms@npm:*": - version: 0.7.31 - resolution: "@types/ms@npm:0.7.31" - checksum: daadd354aedde024cce6f5aa873fefe7b71b22cd0e28632a69e8b677aeb48ae8caa1c60e5919bb781df040d116b01cb4316335167a3fc0ef6a63fa3614c0f6da + version: 0.7.34 + resolution: "@types/ms@npm:0.7.34" + checksum: f38d36e7b6edecd9badc9cf50474159e9da5fa6965a75186cceaf883278611b9df6669dc3a3cc122b7938d317b68a9e3d573d316fcb35d1be47ec9e468c6bd8a languageName: node linkType: hard -"@types/node@npm:*, @types/node@npm:>=13.7.0": - version: 20.4.5 - resolution: "@types/node@npm:20.4.5" - checksum: 36a0304a8dc346a1b2d2edac4c4633eecf70875793d61a5274d0df052d7a7af7a8e34f29884eac4fbd094c4f0201477dcb39c0ecd3307ca141688806538d1138 +"@types/node@npm:*": + version: 20.11.5 + resolution: "@types/node@npm:20.11.5" + dependencies: + undici-types: ~5.26.4 + checksum: a542727de1334ae20a3ca034b0ecf4b464a57ca01efc4f9cf43bd9ab93896125ab3c2de060ecd8f6ae23b86c6bf3463f681b643e69c032c6a662d376c98a6092 languageName: node linkType: hard "@types/node@npm:^18.14.6, @types/node@npm:^18.15.11, @types/node@npm:^18.15.3, @types/node@npm:^18.7.23": - version: 18.17.1 - resolution: "@types/node@npm:18.17.1" - checksum: 56201bda9a2d05d68602df63b4e67b0545ac8c6d0280bd5fb31701350a978a577a027501fbf49db99bf177f2242ebd1244896bfd35e89042d5bd7dfebff28d4e + version: 18.19.8 + resolution: "@types/node@npm:18.19.8" + dependencies: + undici-types: ~5.26.4 + checksum: fa291495d6157a9d9393b4c3bdbf1ce12a8f661dc9da6a4fa19bcdb19af1c62bb8dbf7fb66ae135f29cd788b618e9845b83e9c47edcf39f0953a8561fdacd9a3 languageName: node linkType: hard "@types/normalize-package-data@npm:^2.4.0": - version: 2.4.1 - resolution: "@types/normalize-package-data@npm:2.4.1" - checksum: e87bccbf11f95035c89a132b52b79ce69a1e3652fe55962363063c9c0dae0fe2477ebc585e03a9652adc6f381d24ba5589cc5e51849df4ced3d3e004a7d40ed5 + version: 2.4.4 + resolution: "@types/normalize-package-data@npm:2.4.4" + checksum: 65dff72b543997b7be8b0265eca7ace0e34b75c3e5fee31de11179d08fa7124a7a5587265d53d0409532ecb7f7fba662c2012807963e1f9b059653ec2c83ee05 languageName: node linkType: hard "@types/pako@npm:^2.0.0": - version: 2.0.0 - resolution: "@types/pako@npm:2.0.0" - checksum: 50240a036b5e6acabbf36ac4dca93ec9e619241f0404da8d401cdb427bec3029833324b8a04c4b1ae2ecbc33422fdec31dbf9f43653d9d07cafb82ace78dfccd + version: 2.0.3 + resolution: "@types/pako@npm:2.0.3" + checksum: 0746dd5d29eccf5b2e6cceb3ccb093851219e78bd2e2e20d25757e247987139e061e5d4ba37cb5295493f06e3c683c74f8876011cd8a3f3748a09244fbc841d9 languageName: node linkType: hard "@types/qs@npm:*": - version: 6.9.7 - resolution: "@types/qs@npm:6.9.7" - checksum: 7fd6f9c25053e9b5bb6bc9f9f76c1d89e6c04f7707a7ba0e44cc01f17ef5284adb82f230f542c2d5557d69407c9a40f0f3515e8319afd14e1e16b5543ac6cdba + version: 6.9.11 + resolution: "@types/qs@npm:6.9.11" + checksum: 620ca1628bf3da65662c54ed6ebb120b18a3da477d0bfcc872b696685a9bb1893c3c92b53a1190a8f54d52eaddb6af8b2157755699ac83164604329935e8a7f2 languageName: node linkType: hard "@types/range-parser@npm:*": - version: 1.2.4 - resolution: "@types/range-parser@npm:1.2.4" - checksum: b7c0dfd5080a989d6c8bb0b6750fc0933d9acabeb476da6fe71d8bdf1ab65e37c136169d84148034802f48378ab94e3c37bb4ef7656b2bec2cb9c0f8d4146a95 - languageName: node - linkType: hard - -"@types/retry@npm:0.12.1": - version: 0.12.1 - resolution: "@types/retry@npm:0.12.1" - checksum: 5f46b2556053655f78262bb33040dc58417c900457cc63ff37d6c35349814471453ef511af0cec76a540c601296cd2b22f64bab1ab649c0dacc0223765ba876c - languageName: node - linkType: hard - -"@types/semver@npm:^7.5.0": - version: 7.5.0 - resolution: "@types/semver@npm:7.5.0" - checksum: 0a64b9b9c7424d9a467658b18dd70d1d781c2d6f033096a6e05762d20ebbad23c1b69b0083b0484722aabf35640b78ccc3de26368bcae1129c87e9df028a22e2 + version: 1.2.7 + resolution: "@types/range-parser@npm:1.2.7" + checksum: 95640233b689dfbd85b8c6ee268812a732cf36d5affead89e806fe30da9a430767af8ef2cd661024fd97e19d61f3dec75af2df5e80ec3bea000019ab7028629a languageName: node linkType: hard -"@types/semver@npm:^7.5.2": - version: 7.5.2 - resolution: "@types/semver@npm:7.5.2" - checksum: 743aa8a2b58e20b329c19bd2459152cb049d12fafab7279b90ac11e0f268c97efbcb606ea0c681cca03f79015381b40d9b1244349b354270bec3f939ed49f6e9 +"@types/retry@npm:0.12.2": + version: 0.12.2 + resolution: "@types/retry@npm:0.12.2" + checksum: e5675035717b39ce4f42f339657cae9637cf0c0051cf54314a6a2c44d38d91f6544be9ddc0280587789b6afd056be5d99dbe3e9f4df68c286c36321579b1bf4a languageName: node linkType: hard -"@types/semver@npm:^7.5.4": - version: 7.5.4 - resolution: "@types/semver@npm:7.5.4" - checksum: 120c0189f6fec5f2d12d0d71ac8a4cfa952dc17fa3d842e8afddb82bba8828a4052f8799c1653e2b47ae1977435f38e8985658fde971905ce5afb8e23ee97ecf +"@types/semver@npm:^7.5.0, @types/semver@npm:^7.5.2, @types/semver@npm:^7.5.4": + version: 7.5.6 + resolution: "@types/semver@npm:7.5.6" + checksum: 563a0120ec0efcc326567db2ed920d5d98346f3638b6324ea6b50222b96f02a8add3c51a916b6897b51523aad8ac227d21d3dcf8913559f1bfc6c15b14d23037 languageName: node linkType: hard "@types/send@npm:*": - version: 0.17.1 - resolution: "@types/send@npm:0.17.1" + version: 0.17.4 + resolution: "@types/send@npm:0.17.4" dependencies: "@types/mime": ^1 "@types/node": "*" - checksum: 10b620a5960058ef009afbc17686f680d6486277c62f640845381ec4baa0ea683fdd77c3afea4803daf5fcddd3fb2972c8aa32e078939f1d4e96f83195c89793 + checksum: cf4db48251bbb03cd6452b4de6e8e09e2d75390a92fd798eca4a803df06444adc94ed050246c94c7ed46fb97be1f63607f0e1f13c3ce83d71788b3e08640e5e0 languageName: node linkType: hard "@types/serve-static@npm:*": - version: 1.15.2 - resolution: "@types/serve-static@npm:1.15.2" + version: 1.15.5 + resolution: "@types/serve-static@npm:1.15.5" dependencies: "@types/http-errors": "*" "@types/mime": "*" "@types/node": "*" - checksum: 15c261dbfc57890f7cc17c04d5b22b418dfa0330c912b46c5d8ae2064da5d6f844ef7f41b63c7f4bbf07675e97ebe6ac804b032635ec742ae45d6f1274259b3e + checksum: 0ff4b3703cf20ba89c9f9e345bc38417860a88e85863c8d6fe274a543220ab7f5f647d307c60a71bb57dc9559f0890a661e8dc771a6ec5ef195d91c8afc4a893 languageName: node linkType: hard "@types/sha256@npm:^0.2.0": - version: 0.2.0 - resolution: "@types/sha256@npm:0.2.0" + version: 0.2.2 + resolution: "@types/sha256@npm:0.2.2" dependencies: "@types/node": "*" - checksum: f3c8e0dcaf11d833292b7dd19db567ef41b23036b2fb9ea7335cba100aac8c56e1346171fae6c63885f6c0e1048550506fd165628bc0001902fea010a16d3842 + checksum: 7701b9dc105e7b877090c9bb9b02e10953831737b599bfc7658635ae35d2b21927f77028f8090d50ea0281058ee975f190d664e5351c5aaf5535a1c26ba01f1f languageName: node linkType: hard -"@types/sinon@npm:^10.0.15": - version: 10.0.16 - resolution: "@types/sinon@npm:10.0.16" +"@types/sinon@npm:^17.0.0": + version: 17.0.3 + resolution: "@types/sinon@npm:17.0.3" dependencies: "@types/sinonjs__fake-timers": "*" - checksum: 1216aac584500d6bf845ca76f57e82f8459cf9de4ed80a55e50aa4438360fc418789a42181e211c5d279e97f86a3a994e3c81e43971d540737caca0193242bbf + checksum: c8e9956d9c90fe1ec1cc43085ae48897f93f9ea86e909ab47f255ea71f5229651faa070393950fb6923aef426c84e92b375503f9f8886ef44668b82a8ee49e9a languageName: node linkType: hard "@types/sinonjs__fake-timers@npm:*": - version: 8.1.2 - resolution: "@types/sinonjs__fake-timers@npm:8.1.2" - checksum: bbc73a5ab6c0ec974929392f3d6e1e8db4ebad97ec506d785301e1c3d8a4f98a35b1aa95b97035daef02886fd8efd7788a2fa3ced2ec7105988bfd8dce61eedd + version: 8.1.5 + resolution: "@types/sinonjs__fake-timers@npm:8.1.5" + checksum: 7e3c08f6c13df44f3ea7d9a5155ddf77e3f7314c156fa1c5a829a4f3763bafe2f75b1283b887f06e6b4296996a2f299b70f64ff82625f9af5885436e2524d10c languageName: node linkType: hard @@ -3785,86 +3634,86 @@ __metadata: linkType: hard "@types/stack-utils@npm:^2.0.0": - version: 2.0.1 - resolution: "@types/stack-utils@npm:2.0.1" - checksum: 205fdbe3326b7046d7eaf5e494d8084f2659086a266f3f9cf00bccc549c8e36e407f88168ad4383c8b07099957ad669f75f2532ed4bc70be2b037330f7bae019 + version: 2.0.3 + resolution: "@types/stack-utils@npm:2.0.3" + checksum: 72576cc1522090fe497337c2b99d9838e320659ac57fa5560fcbdcbafcf5d0216c6b3a0a8a4ee4fdb3b1f5e3420aa4f6223ab57b82fef3578bec3206425c6cf5 languageName: node linkType: hard "@types/superagent@npm:*": - version: 4.1.18 - resolution: "@types/superagent@npm:4.1.18" + version: 8.1.2 + resolution: "@types/superagent@npm:8.1.2" dependencies: - "@types/cookiejar": "*" + "@types/cookiejar": ^2.1.5 + "@types/methods": ^1.1.4 "@types/node": "*" - checksum: 4e50cb41e6f0ac55917dddae4665e5251ce0ec086f89172c8b53432c0c3ee026b9243ba4c994aa2702720d7c288fd7ae77f241f9fb9fb15d2d7c4b6bc2ee7079 + checksum: 45b4b2db943511938facadc0359877cda5ce62ea6b4b7e3a3e3f5cff8929594cd8eb3b9314885c2befdb6e052bc823f5dcf981d3fe04f556d6ae15fd92db61e9 languageName: node linkType: hard "@types/supertest@npm:^2.0.12": - version: 2.0.12 - resolution: "@types/supertest@npm:2.0.12" + version: 2.0.16 + resolution: "@types/supertest@npm:2.0.16" dependencies: "@types/superagent": "*" - checksum: f0e2b44f86bec2f708d6a3d0cb209055b487922040773049b0f8c6b557af52d4b5fa904e17dfaa4ce6e610172206bbec7b62420d158fa57b6ffc2de37b1730d3 + checksum: 2fc998ea698e0467cdbe3bea0ebce2027ea3a45a13e51a6cecb0435f44b486faecf99c34d8702d2d7fe033e6e09fdd2b374af52ecc8d0c69a1deec66b8c0dd52 languageName: node linkType: hard "@types/triple-beam@npm:^1.3.2": - version: 1.3.2 - resolution: "@types/triple-beam@npm:1.3.2" - checksum: dd7b4a563fb710abc992e5d59eac481bed9e303fada2e276e37b00be31c392e03300ee468e57761e616512872e77935f92472877d0704a19688d15a726cee17b + version: 1.3.5 + resolution: "@types/triple-beam@npm:1.3.5" + checksum: 519b6a1b30d4571965c9706ad5400a200b94e4050feca3e7856e3ea7ac00ec9903e32e9a10e2762d0f7e472d5d03e5f4b29c16c0bd8c1f77c8876c683b2231f1 languageName: node linkType: hard "@types/ws@npm:^8.5.4": - version: 8.5.5 - resolution: "@types/ws@npm:8.5.5" + version: 8.5.10 + resolution: "@types/ws@npm:8.5.10" dependencies: "@types/node": "*" - checksum: d00bf8070e6938e3ccf933010921c6ce78ac3606696ce37a393b27a9a603f7bd93ea64f3c5fa295a2f743575ba9c9a9fdb904af0f5fe2229bf2adf0630386e4a + checksum: 3ec416ea2be24042ebd677932a462cf16d2080393d8d7d0b1b3f5d6eaa4a7387aaf0eefb99193c0bfd29444857cf2e0c3ac89899e130550dc6c14ada8a46d25e languageName: node linkType: hard "@types/yargs-parser@npm:*": - version: 21.0.0 - resolution: "@types/yargs-parser@npm:21.0.0" - checksum: b2f4c8d12ac18a567440379909127cf2cec393daffb73f246d0a25df36ea983b93b7e9e824251f959e9f928cbc7c1aab6728d0a0ff15d6145f66cec2be67d9a2 + version: 21.0.3 + resolution: "@types/yargs-parser@npm:21.0.3" + checksum: ef236c27f9432983e91432d974243e6c4cdae227cb673740320eff32d04d853eed59c92ca6f1142a335cfdc0e17cccafa62e95886a8154ca8891cc2dec4ee6fc languageName: node linkType: hard "@types/yargs@npm:^17.0.8": - version: 17.0.24 - resolution: "@types/yargs@npm:17.0.24" + version: 17.0.32 + resolution: "@types/yargs@npm:17.0.32" dependencies: "@types/yargs-parser": "*" - checksum: 5f3ac4dc4f6e211c1627340160fbe2fd247ceba002190da6cf9155af1798450501d628c9165a183f30a224fc68fa5e700490d740ff4c73e2cdef95bc4e8ba7bf + checksum: 4505bdebe8716ff383640c6e928f855b5d337cb3c68c81f7249fc6b983d0aa48de3eee26062b84f37e0d75a5797bc745e0c6e76f42f81771252a758c638f36ba languageName: node linkType: hard "@types/yauzl@npm:^2.9.1": - version: 2.10.0 - resolution: "@types/yauzl@npm:2.10.0" + version: 2.10.3 + resolution: "@types/yauzl@npm:2.10.3" dependencies: "@types/node": "*" - checksum: 55d27ae5d346ea260e40121675c24e112ef0247649073848e5d4e03182713ae4ec8142b98f61a1c6cbe7d3b72fa99bbadb65d8b01873e5e605cdc30f1ff70ef2 + checksum: 5ee966ea7bd6b2802f31ad4281c92c4c0b6dfa593c378a2582c58541fa113bec3d70eb0696b34ad95e8e6861a884cba6c3e351285816693ed176222f840a8c08 languageName: node linkType: hard "@typescript-eslint/eslint-plugin@npm:^6.2.1": - version: 6.2.1 - resolution: "@typescript-eslint/eslint-plugin@npm:6.2.1" + version: 6.19.1 + resolution: "@typescript-eslint/eslint-plugin@npm:6.19.1" dependencies: "@eslint-community/regexpp": ^4.5.1 - "@typescript-eslint/scope-manager": 6.2.1 - "@typescript-eslint/type-utils": 6.2.1 - "@typescript-eslint/utils": 6.2.1 - "@typescript-eslint/visitor-keys": 6.2.1 + "@typescript-eslint/scope-manager": 6.19.1 + "@typescript-eslint/type-utils": 6.19.1 + "@typescript-eslint/utils": 6.19.1 + "@typescript-eslint/visitor-keys": 6.19.1 debug: ^4.3.4 graphemer: ^1.4.0 ignore: ^5.2.4 natural-compare: ^1.4.0 - natural-compare-lite: ^1.4.0 semver: ^7.5.4 ts-api-utils: ^1.0.1 peerDependencies: @@ -3873,44 +3722,44 @@ __metadata: peerDependenciesMeta: typescript: optional: true - checksum: e73f3fe36519d895037d223f3ddf200b97e17bcde9390984118c38733add1edf996357c809ec2db92cec61bc7c9e5a3d9a583e0d0f92fa9c3919b68716a27b37 + checksum: ad04000cd6c15d864ff92655baa3aec99bb0ccf4714fedd145fedde60a27590a5feafe480beb2f0f3864b416098bde1e9431bada7480eb7ca4efad891e1d2f6f languageName: node linkType: hard "@typescript-eslint/parser@npm:^6.2.1": - version: 6.2.1 - resolution: "@typescript-eslint/parser@npm:6.2.1" + version: 6.19.1 + resolution: "@typescript-eslint/parser@npm:6.19.1" dependencies: - "@typescript-eslint/scope-manager": 6.2.1 - "@typescript-eslint/types": 6.2.1 - "@typescript-eslint/typescript-estree": 6.2.1 - "@typescript-eslint/visitor-keys": 6.2.1 + "@typescript-eslint/scope-manager": 6.19.1 + "@typescript-eslint/types": 6.19.1 + "@typescript-eslint/typescript-estree": 6.19.1 + "@typescript-eslint/visitor-keys": 6.19.1 debug: ^4.3.4 peerDependencies: eslint: ^7.0.0 || ^8.0.0 peerDependenciesMeta: typescript: optional: true - checksum: cf4768cbfc696ce1d4b15ae55b3d2b52761e91a4a80e738cf3a75c501c2257d735cd6e462567965069d0d693a8cf5463ab9e8b97c36c6ed1fccd3c1c09855bdb + checksum: cd29619da08a2d9b7123ba4d8240989c747f8e0d5672179d8b147e413ee1334d1fa48570b0c37cf0ae4e26a275fd2d268cbe702c6fed639d3331abbb3292570a languageName: node linkType: hard -"@typescript-eslint/scope-manager@npm:6.2.1": - version: 6.2.1 - resolution: "@typescript-eslint/scope-manager@npm:6.2.1" +"@typescript-eslint/scope-manager@npm:6.19.1": + version: 6.19.1 + resolution: "@typescript-eslint/scope-manager@npm:6.19.1" dependencies: - "@typescript-eslint/types": 6.2.1 - "@typescript-eslint/visitor-keys": 6.2.1 - checksum: 3bb461678c7e729895c5ac16781ec7d66efc6ffa944bb49693ce8e9560f9a6cac70929157c0fc0875b2829ae19a5cdabb97973ddcfb7e81c16e22cdd5d39e3fd + "@typescript-eslint/types": 6.19.1 + "@typescript-eslint/visitor-keys": 6.19.1 + checksum: 848cdebc16a3803e8a6d6035a7067605309a652bb2425f475f755b5ace4d80d2c17c8c8901f0f4759556da8d0a5b71024d472b85c3f3c70d0e6dcfe2a972ef35 languageName: node linkType: hard -"@typescript-eslint/type-utils@npm:6.2.1": - version: 6.2.1 - resolution: "@typescript-eslint/type-utils@npm:6.2.1" +"@typescript-eslint/type-utils@npm:6.19.1": + version: 6.19.1 + resolution: "@typescript-eslint/type-utils@npm:6.19.1" dependencies: - "@typescript-eslint/typescript-estree": 6.2.1 - "@typescript-eslint/utils": 6.2.1 + "@typescript-eslint/typescript-estree": 6.19.1 + "@typescript-eslint/utils": 6.19.1 debug: ^4.3.4 ts-api-utils: ^1.0.1 peerDependencies: @@ -3918,7 +3767,7 @@ __metadata: peerDependenciesMeta: typescript: optional: true - checksum: 7f8d80f03e6ddc1838307a2a4df61dc4bd8400efb9dcc7316063ae293fce54afad238404a0c25cd2cdaceee73ae514f254b850bd7ff11e2def700d5d6b90af05 + checksum: eab1a30f8d85f7c6e2545de5963fbec2f3bb91913d59623069b4b0db372a671ab048c7018376fc853c3af06ea39417f3e7b27dd665027dd812347a5e64cecd77 languageName: node linkType: hard @@ -3936,28 +3785,29 @@ __metadata: languageName: node linkType: hard -"@typescript-eslint/types@npm:6.2.1": - version: 6.2.1 - resolution: "@typescript-eslint/types@npm:6.2.1" - checksum: 388d32f15a9db8ad5d80794caf9ab280d6e5a428efdf4f6a6dfc4069afe4d19da32d628acf638e4c5b92ee77a9a18eecf728a778a3b91cc8a24484af579fc9cf +"@typescript-eslint/types@npm:6.19.1": + version: 6.19.1 + resolution: "@typescript-eslint/types@npm:6.19.1" + checksum: 598ce222b59c20432d06f60703d0c2dd16d9b2151569c192852136c57b8188e3ef6ef9fddaa2c136c9a756fcc7d873c0e29ec41cfd340564842287ef7b4571cd languageName: node linkType: hard -"@typescript-eslint/typescript-estree@npm:6.2.1": - version: 6.2.1 - resolution: "@typescript-eslint/typescript-estree@npm:6.2.1" +"@typescript-eslint/typescript-estree@npm:6.19.1": + version: 6.19.1 + resolution: "@typescript-eslint/typescript-estree@npm:6.19.1" dependencies: - "@typescript-eslint/types": 6.2.1 - "@typescript-eslint/visitor-keys": 6.2.1 + "@typescript-eslint/types": 6.19.1 + "@typescript-eslint/visitor-keys": 6.19.1 debug: ^4.3.4 globby: ^11.1.0 is-glob: ^4.0.3 + minimatch: 9.0.3 semver: ^7.5.4 ts-api-utils: ^1.0.1 peerDependenciesMeta: typescript: optional: true - checksum: 3d9beeb5e36b8827de5c160ed8e5c111dd66ca00671b183409b051e242b291480679b900bb74aaf4895dcae49497037567d3fcbbe67fa9930786ddd01c685f04 + checksum: fb71a14aeee0468780219c5b8d39075f85d360b04ccd0ee88f4f0a615d2c232a6d3016e36d8c6eda2d9dfda86b4f4cc2c3d7582940fb29d33c7cf305e124d4e2 languageName: node linkType: hard @@ -3997,20 +3847,20 @@ __metadata: languageName: node linkType: hard -"@typescript-eslint/utils@npm:6.2.1": - version: 6.2.1 - resolution: "@typescript-eslint/utils@npm:6.2.1" +"@typescript-eslint/utils@npm:6.19.1": + version: 6.19.1 + resolution: "@typescript-eslint/utils@npm:6.19.1" dependencies: "@eslint-community/eslint-utils": ^4.4.0 "@types/json-schema": ^7.0.12 "@types/semver": ^7.5.0 - "@typescript-eslint/scope-manager": 6.2.1 - "@typescript-eslint/types": 6.2.1 - "@typescript-eslint/typescript-estree": 6.2.1 + "@typescript-eslint/scope-manager": 6.19.1 + "@typescript-eslint/types": 6.19.1 + "@typescript-eslint/typescript-estree": 6.19.1 semver: ^7.5.4 peerDependencies: eslint: ^7.0.0 || ^8.0.0 - checksum: d16356a633f39d988a9af159da15e28c6a28fa47abce372061c79cf186d193d148e1c32862c9702ff87e2a06f7a2f82773e4b56320a39f432f4b1a989f8005ad + checksum: fe72e75c3ea17a85772b83f148555ea94ff5d55d13586f3fc038833197a74f8071e14c2bbf1781c40eec20005f052f4be2513a725eea82a15da3cb9af3046c70 languageName: node linkType: hard @@ -4034,13 +3884,20 @@ __metadata: languageName: node linkType: hard -"@typescript-eslint/visitor-keys@npm:6.2.1": - version: 6.2.1 - resolution: "@typescript-eslint/visitor-keys@npm:6.2.1" +"@typescript-eslint/visitor-keys@npm:6.19.1": + version: 6.19.1 + resolution: "@typescript-eslint/visitor-keys@npm:6.19.1" dependencies: - "@typescript-eslint/types": 6.2.1 + "@typescript-eslint/types": 6.19.1 eslint-visitor-keys: ^3.4.1 - checksum: c05a1c45129f2cf9a8c49dadc3da10b675232e59b69dfe9fdc0bfb45d3be077ceff78097baf50e502dab3e71ce9fd799d2015e356a4be2787ee10c6c7a44ea8a + checksum: bdf057a42e776970a89cdd568e493e3ea7ec085544d8f318d33084da63c3395ad2c0fb9cef9f61ceeca41f5dab54ab064b7078fe596889005e412ec74d2d1ae4 + languageName: node + linkType: hard + +"@ungap/structured-clone@npm:^1.2.0": + version: 1.2.0 + resolution: "@ungap/structured-clone@npm:1.2.0" + checksum: 4f656b7b4672f2ce6e272f2427d8b0824ed11546a601d8d5412b9d7704e83db38a8d9f402ecdf2b9063fc164af842ad0ec4a55819f621ed7e7ea4d1efcc74524 languageName: node linkType: hard @@ -4254,10 +4111,10 @@ __metadata: languageName: node linkType: hard -"abbrev@npm:^1.0.0": - version: 1.1.1 - resolution: "abbrev@npm:1.1.1" - checksum: a4a97ec07d7ea112c517036882b2ac22f3109b7b19077dc656316d07d308438aac28e4d9746dc4d84bf6b1e75b4a7b0a5f3cb30592419f128ca9a8cee3bcfa17 +"abbrev@npm:^2.0.0": + version: 2.0.0 + resolution: "abbrev@npm:2.0.0" + checksum: 0e994ad2aa6575f94670d8a2149afe94465de9cedaaaac364e7fb43a40c3691c980ff74899f682f4ca58fa96b4cbd7421a015d3a6defe43a442117d7821a2f36 languageName: node linkType: hard @@ -4276,6 +4133,21 @@ __metadata: languageName: node linkType: hard +"abitype@npm:0.9.8": + version: 0.9.8 + resolution: "abitype@npm:0.9.8" + peerDependencies: + typescript: ">=5.0.4" + zod: ^3 >=3.19.1 + peerDependenciesMeta: + typescript: + optional: true + zod: + optional: true + checksum: d7d887f29d6821e3f7a400de9620511b80ead3f85c5c87308aaec97965d3493e6687ed816e88722b4f512249bd66dee9e69231b49af0e1db8f69400a62c87cf6 + languageName: node + linkType: hard + "abitype@npm:^0.8.11": version: 0.8.11 resolution: "abitype@npm:0.8.11" @@ -4342,27 +4214,18 @@ __metadata: linkType: hard "acorn-walk@npm:^8.1.1": - version: 8.2.0 - resolution: "acorn-walk@npm:8.2.0" - checksum: 1715e76c01dd7b2d4ca472f9c58968516a4899378a63ad5b6c2d668bba8da21a71976c14ec5f5b75f887b6317c4ae0b897ab141c831d741dc76024d8745f1ad1 + version: 8.3.2 + resolution: "acorn-walk@npm:8.3.2" + checksum: 3626b9d26a37b1b427796feaa5261faf712307a8920392c8dce9a5739fb31077667f4ad2ec71c7ac6aaf9f61f04a9d3d67ff56f459587206fc04aa31c27ef392 languageName: node linkType: hard "acorn@npm:^8.4.1, acorn@npm:^8.7.1, acorn@npm:^8.8.2, acorn@npm:^8.9.0": - version: 8.10.0 - resolution: "acorn@npm:8.10.0" + version: 8.11.3 + resolution: "acorn@npm:8.11.3" bin: acorn: bin/acorn - checksum: 538ba38af0cc9e5ef983aee196c4b8b4d87c0c94532334fa7e065b2c8a1f85863467bb774231aae91613fcda5e68740c15d97b1967ae3394d20faddddd8af61d - languageName: node - linkType: hard - -"agent-base@npm:6, agent-base@npm:^6.0.2": - version: 6.0.2 - resolution: "agent-base@npm:6.0.2" - dependencies: - debug: 4 - checksum: f52b6872cc96fd5f622071b71ef200e01c7c4c454ee68bc9accca90c98cfb39f2810e3e9aa330435835eedc8c23f4f8a15267f67c6e245d2b33757575bdac49d + checksum: 76d8e7d559512566b43ab4aadc374f11f563f0a9e21626dd59cb2888444e9445923ae9f3699972767f18af61df89cd89f5eaaf772d1327b055b45cb829b4a88c languageName: node linkType: hard @@ -4375,17 +4238,6 @@ __metadata: languageName: node linkType: hard -"agentkeepalive@npm:^4.2.1": - version: 4.3.0 - resolution: "agentkeepalive@npm:4.3.0" - dependencies: - debug: ^4.1.0 - depd: ^2.0.0 - humanize-ms: ^1.2.1 - checksum: 982453aa44c11a06826c836025e5162c846e1200adb56f2d075400da7d32d87021b3b0a58768d949d824811f5654223d5a8a3dad120921a2439625eb847c6260 - languageName: node - linkType: hard - "aggregate-error@npm:^3.0.0": version: 3.1.0 resolution: "aggregate-error@npm:3.1.0" @@ -4510,23 +4362,6 @@ __metadata: languageName: node linkType: hard -"aproba@npm:^1.0.3 || ^2.0.0": - version: 2.0.0 - resolution: "aproba@npm:2.0.0" - checksum: 5615cadcfb45289eea63f8afd064ab656006361020e1735112e346593856f87435e02d8dcc7ff0d11928bc7d425f27bc7c2a84f6c0b35ab0ff659c814c138a24 - languageName: node - linkType: hard - -"are-we-there-yet@npm:^3.0.0": - version: 3.0.1 - resolution: "are-we-there-yet@npm:3.0.1" - dependencies: - delegates: ^1.0.0 - readable-stream: ^3.6.0 - checksum: 52590c24860fa7173bedeb69a4c05fb573473e860197f618b9a28432ee4379049336727ae3a1f9c4cb083114601c1140cee578376164d0e651217a9843f9fe83 - languageName: node - linkType: hard - "arg@npm:^4.1.0": version: 4.1.3 resolution: "arg@npm:4.1.3" @@ -4567,16 +4402,16 @@ __metadata: languageName: node linkType: hard -"array-includes@npm:^3.1.6": - version: 3.1.6 - resolution: "array-includes@npm:3.1.6" +"array-includes@npm:^3.1.7": + version: 3.1.7 + resolution: "array-includes@npm:3.1.7" dependencies: call-bind: ^1.0.2 - define-properties: ^1.1.4 - es-abstract: ^1.20.4 - get-intrinsic: ^1.1.3 + define-properties: ^1.2.0 + es-abstract: ^1.22.1 + get-intrinsic: ^1.2.1 is-string: ^1.0.7 - checksum: f22f8cd8ba8a6448d91eebdc69f04e4e55085d09232b5216ee2d476dab3ef59984e8d1889e662c6a0ed939dcb1b57fd05b2c0209c3370942fc41b752c82a2ca5 + checksum: 06f9e4598fac12a919f7c59a3f04f010ea07f0b7f0585465ed12ef528a60e45f374e79d1bddbb34cdd4338357d00023ddbd0ac18b0be36964f5e726e8965d7fc languageName: node linkType: hard @@ -4587,54 +4422,55 @@ __metadata: languageName: node linkType: hard -"array.prototype.findlastindex@npm:^1.2.2": - version: 1.2.2 - resolution: "array.prototype.findlastindex@npm:1.2.2" +"array.prototype.findlastindex@npm:^1.2.3": + version: 1.2.3 + resolution: "array.prototype.findlastindex@npm:1.2.3" dependencies: call-bind: ^1.0.2 - define-properties: ^1.1.4 - es-abstract: ^1.20.4 + define-properties: ^1.2.0 + es-abstract: ^1.22.1 es-shim-unscopables: ^1.0.0 - get-intrinsic: ^1.1.3 - checksum: 8a166359f69a2a751c843f26b9c8cd03d0dc396a92cdcb85f4126b5f1cecdae5b2c0c616a71ea8aff026bde68165b44950b3664404bb73db0673e288495ba264 + get-intrinsic: ^1.2.1 + checksum: 31f35d7b370c84db56484618132041a9af401b338f51899c2e78ef7690fbba5909ee7ca3c59a7192085b328cc0c68c6fd1f6d1553db01a689a589ae510f3966e languageName: node linkType: hard -"array.prototype.flat@npm:^1.3.1": - version: 1.3.1 - resolution: "array.prototype.flat@npm:1.3.1" +"array.prototype.flat@npm:^1.3.2": + version: 1.3.2 + resolution: "array.prototype.flat@npm:1.3.2" dependencies: call-bind: ^1.0.2 - define-properties: ^1.1.4 - es-abstract: ^1.20.4 + define-properties: ^1.2.0 + es-abstract: ^1.22.1 es-shim-unscopables: ^1.0.0 - checksum: 5a8415949df79bf6e01afd7e8839bbde5a3581300e8ad5d8449dea52639e9e59b26a467665622783697917b43bf39940a6e621877c7dd9b3d1c1f97484b9b88b + checksum: 5d6b4bf102065fb3f43764bfff6feb3295d372ce89591e6005df3d0ce388527a9f03c909af6f2a973969a4d178ab232ffc9236654149173e0e187ec3a1a6b87b languageName: node linkType: hard -"array.prototype.flatmap@npm:^1.3.1": - version: 1.3.1 - resolution: "array.prototype.flatmap@npm:1.3.1" +"array.prototype.flatmap@npm:^1.3.2": + version: 1.3.2 + resolution: "array.prototype.flatmap@npm:1.3.2" dependencies: call-bind: ^1.0.2 - define-properties: ^1.1.4 - es-abstract: ^1.20.4 + define-properties: ^1.2.0 + es-abstract: ^1.22.1 es-shim-unscopables: ^1.0.0 - checksum: 8c1c43a4995f12cf12523436da28515184c753807b3f0bc2ca6c075f71c470b099e2090cc67dba8e5280958fea401c1d0c59e1db0143272aef6cd1103921a987 + checksum: ce09fe21dc0bcd4f30271f8144083aa8c13d4639074d6c8dc82054b847c7fc9a0c97f857491f4da19d4003e507172a78f4bcd12903098adac8b9cd374f734be3 languageName: node linkType: hard -"arraybuffer.prototype.slice@npm:^1.0.1": - version: 1.0.1 - resolution: "arraybuffer.prototype.slice@npm:1.0.1" +"arraybuffer.prototype.slice@npm:^1.0.2": + version: 1.0.2 + resolution: "arraybuffer.prototype.slice@npm:1.0.2" dependencies: array-buffer-byte-length: ^1.0.0 call-bind: ^1.0.2 define-properties: ^1.2.0 + es-abstract: ^1.22.1 get-intrinsic: ^1.2.1 is-array-buffer: ^3.0.2 is-shared-array-buffer: ^1.0.2 - checksum: e3e9b2a3e988ebfeddce4c7e8f69df730c9e48cb04b0d40ff0874ce3d86b3d1339dd520ffde5e39c02610bc172ecfbd4bc93324b1cabd9554c44a56b131ce0ce + checksum: c200faf437786f5b2c80d4564ff5481c886a16dee642ef02abdc7306c7edd523d1f01d1dd12b769c7eb42ac9bc53874510db19a92a2c035c0f6696172aafa5d3 languageName: node linkType: hard @@ -4695,9 +4531,9 @@ __metadata: linkType: hard "async@npm:^3.2.3": - version: 3.2.4 - resolution: "async@npm:3.2.4" - checksum: 43d07459a4e1d09b84a20772414aa684ff4de085cbcaec6eea3c7a8f8150e8c62aa6cd4e699fe8ee93c3a5b324e777d34642531875a0817a35697522c1b02e89 + version: 3.2.5 + resolution: "async@npm:3.2.5" + checksum: 5ec77f1312301dee02d62140a6b1f7ee0edd2a0f983b6fd2b0849b969f245225b990b47b8243e7b9ad16451a53e7f68e753700385b706198ced888beedba3af4 languageName: node linkType: hard @@ -4722,20 +4558,20 @@ __metadata: languageName: node linkType: hard -"babel-jest@npm:^29.6.2": - version: 29.6.2 - resolution: "babel-jest@npm:29.6.2" +"babel-jest@npm:^29.7.0": + version: 29.7.0 + resolution: "babel-jest@npm:29.7.0" dependencies: - "@jest/transform": ^29.6.2 + "@jest/transform": ^29.7.0 "@types/babel__core": ^7.1.14 babel-plugin-istanbul: ^6.1.1 - babel-preset-jest: ^29.5.0 + babel-preset-jest: ^29.6.3 chalk: ^4.0.0 graceful-fs: ^4.2.9 slash: ^3.0.0 peerDependencies: "@babel/core": ^7.8.0 - checksum: 3936b5d6ed6f08670c830ed919e38a4a593d0643b8e30fdeb16f4588b262ea5255fb96fd1849c02fba0b082ecfa4e788ce9a128ad1b9e654d46aac09c3a55504 + checksum: ee6f8e0495afee07cac5e4ee167be705c711a8cc8a737e05a587a131fdae2b3c8f9aa55dfd4d9c03009ac2d27f2de63d8ba96d3e8460da4d00e8af19ef9a83f7 languageName: node linkType: hard @@ -4752,15 +4588,15 @@ __metadata: languageName: node linkType: hard -"babel-plugin-jest-hoist@npm:^29.5.0": - version: 29.5.0 - resolution: "babel-plugin-jest-hoist@npm:29.5.0" +"babel-plugin-jest-hoist@npm:^29.6.3": + version: 29.6.3 + resolution: "babel-plugin-jest-hoist@npm:29.6.3" dependencies: "@babel/template": ^7.3.3 "@babel/types": ^7.3.3 "@types/babel__core": ^7.1.14 "@types/babel__traverse": ^7.0.6 - checksum: 099b5254073b6bc985b6d2d045ad26fb8ed30ff8ae6404c4fe8ee7cd0e98a820f69e3dfb871c7c65aae0f4b65af77046244c07bb92d49ef9005c90eedf681539 + checksum: 51250f22815a7318f17214a9d44650ba89551e6d4f47a2dc259128428324b52f5a73979d010cefd921fd5a720d8c1d55ad74ff601cd94c7bd44d5f6292fde2d1 languageName: node linkType: hard @@ -4786,15 +4622,15 @@ __metadata: languageName: node linkType: hard -"babel-preset-jest@npm:^29.5.0": - version: 29.5.0 - resolution: "babel-preset-jest@npm:29.5.0" +"babel-preset-jest@npm:^29.6.3": + version: 29.6.3 + resolution: "babel-preset-jest@npm:29.6.3" dependencies: - babel-plugin-jest-hoist: ^29.5.0 + babel-plugin-jest-hoist: ^29.6.3 babel-preset-current-node-syntax: ^1.0.0 peerDependencies: "@babel/core": ^7.0.0 - checksum: 5566ca2762766c9319b4973d018d2fa08c0fcf6415c72cc54f4c8e7199e851ea8f5e6c6730f03ed7ed44fc8beefa959dd15911f2647dee47c615ff4faeddb1ad + checksum: aa4ff2a8a728d9d698ed521e3461a109a1e66202b13d3494e41eea30729a5e7cc03b3a2d56c594423a135429c37bf63a9fa8b0b9ce275298be3095a88c69f6fb languageName: node linkType: hard @@ -4813,9 +4649,9 @@ __metadata: linkType: hard "basic-ftp@npm:^5.0.2": - version: 5.0.3 - resolution: "basic-ftp@npm:5.0.3" - checksum: 8b04e88eb85a64de9311721bb0707c9cd70453eefdd854cab85438e6f46fb6c597ddad57ed1acf0a9ede3c677b14e657f51051688a5f23d6f3ea7b5d9073b850 + version: 5.0.4 + resolution: "basic-ftp@npm:5.0.4" + checksum: 57725f24debd8c1b36f9bad1bfee39c5d9f5997f32a23e5c957389dcc64373a13b41711e5723b4a3b616a93530b345686119f480c27a115b2fde944c1652ceb1 languageName: node linkType: hard @@ -4829,13 +4665,6 @@ __metadata: languageName: node linkType: hard -"big-integer@npm:^1.6.44": - version: 1.6.51 - resolution: "big-integer@npm:1.6.51" - checksum: 3d444173d1b2e20747e2c175568bedeebd8315b0637ea95d75fd27830d3b8e8ba36c6af40374f36bdaea7b5de376dcada1b07587cb2a79a928fccdb6e6e3c518 - languageName: node - linkType: hard - "bl@npm:^4.1.0": version: 4.1.0 resolution: "bl@npm:4.1.0" @@ -4854,22 +4683,13 @@ __metadata: languageName: node linkType: hard -"bn.js@npm:^5.0.0, bn.js@npm:^5.1.1, bn.js@npm:^5.2.1": +"bn.js@npm:^5.0.0, bn.js@npm:^5.2.1": version: 5.2.1 resolution: "bn.js@npm:5.2.1" checksum: 3dd8c8d38055fedfa95c1d5fc3c99f8dd547b36287b37768db0abab3c239711f88ff58d18d155dd8ad902b0b0cee973747b7ae20ea12a09473272b0201c9edd3 languageName: node linkType: hard -"bplist-parser@npm:^0.2.0": - version: 0.2.0 - resolution: "bplist-parser@npm:0.2.0" - dependencies: - big-integer: ^1.6.44 - checksum: d5339dd16afc51de6c88f88f58a45b72ed6a06aa31f5557d09877575f220b7c1d3fbe375da0b62e6a10d4b8ed80523567e351f24014f5bc886ad523758142cdd - languageName: node - linkType: hard - "brace-expansion@npm:^1.1.7": version: 1.1.11 resolution: "brace-expansion@npm:1.1.11" @@ -4942,7 +4762,7 @@ __metadata: languageName: node linkType: hard -"browserify-rsa@npm:^4.0.0, browserify-rsa@npm:^4.0.1": +"browserify-rsa@npm:^4.0.0, browserify-rsa@npm:^4.1.0": version: 4.1.0 resolution: "browserify-rsa@npm:4.1.0" dependencies: @@ -4953,33 +4773,33 @@ __metadata: linkType: hard "browserify-sign@npm:^4.0.0": - version: 4.2.1 - resolution: "browserify-sign@npm:4.2.1" + version: 4.2.2 + resolution: "browserify-sign@npm:4.2.2" dependencies: - bn.js: ^5.1.1 - browserify-rsa: ^4.0.1 + bn.js: ^5.2.1 + browserify-rsa: ^4.1.0 create-hash: ^1.2.0 create-hmac: ^1.1.7 - elliptic: ^6.5.3 + elliptic: ^6.5.4 inherits: ^2.0.4 - parse-asn1: ^5.1.5 - readable-stream: ^3.6.0 - safe-buffer: ^5.2.0 - checksum: 0221f190e3f5b2d40183fa51621be7e838d9caa329fe1ba773406b7637855f37b30f5d83e52ff8f244ed12ffe6278dd9983638609ed88c841ce547e603855707 + parse-asn1: ^5.1.6 + readable-stream: ^3.6.2 + safe-buffer: ^5.2.1 + checksum: b622730c0fc183328c3a1c9fdaaaa5118821ed6822b266fa6b0375db7e20061ebec87301d61931d79b9da9a96ada1cab317fce3c68f233e5e93ed02dbb35544c languageName: node linkType: hard -"browserslist@npm:^4.14.5, browserslist@npm:^4.21.9": - version: 4.21.10 - resolution: "browserslist@npm:4.21.10" +"browserslist@npm:^4.14.5, browserslist@npm:^4.22.2": + version: 4.22.2 + resolution: "browserslist@npm:4.22.2" dependencies: - caniuse-lite: ^1.0.30001517 - electron-to-chromium: ^1.4.477 - node-releases: ^2.0.13 - update-browserslist-db: ^1.0.11 + caniuse-lite: ^1.0.30001565 + electron-to-chromium: ^1.4.601 + node-releases: ^2.0.14 + update-browserslist-db: ^1.0.13 bin: browserslist: cli.js - checksum: 1e27c0f111a35d1dd0e8fc2c61781b0daefabc2c9471b0b10537ce54843014bceb2a1ce4571af1a82b2bf1e6e6e05d38865916689a158f03bc2c7a4ec2577db8 + checksum: 33ddfcd9145220099a7a1ac533cecfe5b7548ffeb29b313e1b57be6459000a1f8fa67e781cf4abee97268ac594d44134fcc4a6b2b4750ceddc9796e3a22076d9 languageName: node linkType: hard @@ -5042,33 +4862,6 @@ __metadata: languageName: node linkType: hard -"bundle-name@npm:^3.0.0": - version: 3.0.0 - resolution: "bundle-name@npm:3.0.0" - dependencies: - run-applescript: ^5.0.0 - checksum: edf2b1fbe6096ed32e7566947ace2ea937ee427391744d7510a2880c4b9a5b3543d3f6c551236a29e5c87d3195f8e2912516290e638c15bcbede7b37cc375615 - languageName: node - linkType: hard - -"busboy@npm:^1.6.0": - version: 1.6.0 - resolution: "busboy@npm:1.6.0" - dependencies: - streamsearch: ^1.1.0 - checksum: 32801e2c0164e12106bf236291a00795c3c4e4b709ae02132883fe8478ba2ae23743b11c5735a0aae8afe65ac4b6ca4568b91f0d9fed1fdbc32ede824a73746e - languageName: node - linkType: hard - -"byte-access@npm:^1.0.0, byte-access@npm:^1.0.1": - version: 1.0.1 - resolution: "byte-access@npm:1.0.1" - dependencies: - uint8arraylist: ^2.0.0 - checksum: 12d5350d9fa6108da80844f5b8c6c80f54bc037cc90f914d81e3a27e374d6c5aac1ba875a8dc2da6b00c6d65c1726bde83a7d57914746cd6d52ff9939a5b21fa - languageName: node - linkType: hard - "bytes@npm:3.1.2, bytes@npm:^3.1.2": version: 3.1.2 resolution: "bytes@npm:3.1.2" @@ -5076,23 +4869,23 @@ __metadata: languageName: node linkType: hard -"cacache@npm:^17.0.0": - version: 17.1.3 - resolution: "cacache@npm:17.1.3" +"cacache@npm:^18.0.0": + version: 18.0.2 + resolution: "cacache@npm:18.0.2" dependencies: "@npmcli/fs": ^3.1.0 fs-minipass: ^3.0.0 glob: ^10.2.2 - lru-cache: ^7.7.1 - minipass: ^5.0.0 - minipass-collect: ^1.0.2 + lru-cache: ^10.0.1 + minipass: ^7.0.3 + minipass-collect: ^2.0.1 minipass-flush: ^1.0.5 minipass-pipeline: ^1.2.4 p-map: ^4.0.0 ssri: ^10.0.0 tar: ^6.1.11 unique-filename: ^3.0.0 - checksum: 385756781e1e21af089160d89d7462b7ed9883c978e848c7075b90b73cb823680e66092d61513050164588387d2ca87dd6d910e28d64bc13a9ac82cd8580c796 + checksum: 0250df80e1ad0c828c956744850c5f742c24244e9deb5b7dc81bca90f8c10e011e132ecc58b64497cc1cad9a98968676147fb6575f4f94722f7619757b17a11b languageName: node linkType: hard @@ -5106,13 +4899,14 @@ __metadata: languageName: node linkType: hard -"call-bind@npm:^1.0.0, call-bind@npm:^1.0.2": - version: 1.0.2 - resolution: "call-bind@npm:1.0.2" +"call-bind@npm:^1.0.0, call-bind@npm:^1.0.2, call-bind@npm:^1.0.4, call-bind@npm:^1.0.5": + version: 1.0.5 + resolution: "call-bind@npm:1.0.5" dependencies: - function-bind: ^1.1.1 - get-intrinsic: ^1.0.2 - checksum: f8e31de9d19988a4b80f3e704788c4a2d6b6f3d17cfec4f57dc29ced450c53a49270dc66bf0fbd693329ee948dd33e6c90a329519aef17474a4d961e8d6426b0 + function-bind: ^1.1.2 + get-intrinsic: ^1.2.1 + set-function-length: ^1.1.1 + checksum: 449e83ecbd4ba48e7eaac5af26fea3b50f8f6072202c2dd7c5a6e7a6308f2421abe5e13a3bbd55221087f76320c5e09f25a8fdad1bab2b77c68ae74d92234ea5 languageName: node linkType: hard @@ -5148,10 +4942,10 @@ __metadata: languageName: node linkType: hard -"caniuse-lite@npm:^1.0.30001517": - version: 1.0.30001518 - resolution: "caniuse-lite@npm:1.0.30001518" - checksum: 1b63272f6e3d628ac52e2547e0b75fc477004d4b19b63e34b2c045de7f2e48909f9ea513978fc5a46c4ab5ac6c9daf9cc5e6a78466e90684fb824c3f2105e8f5 +"caniuse-lite@npm:^1.0.30001565": + version: 1.0.30001579 + resolution: "caniuse-lite@npm:1.0.30001579" + checksum: 7539dcff74d2243a30c428393dc690c87fa34d7da36434731853e9bcfe783757763b2971f5cc878e25242a93e184e53f167d11bd74955af956579f7af71cc764 languageName: node linkType: hard @@ -5162,7 +4956,7 @@ __metadata: languageName: node linkType: hard -"chalk@npm:^2.0.0": +"chalk@npm:^2.4.2": version: 2.4.2 resolution: "chalk@npm:2.4.2" dependencies: @@ -5204,22 +4998,22 @@ __metadata: languageName: node linkType: hard -"chromium-bidi@npm:0.4.28": - version: 0.4.28 - resolution: "chromium-bidi@npm:0.4.28" +"chromium-bidi@npm:0.5.2": + version: 0.5.2 + resolution: "chromium-bidi@npm:0.5.2" dependencies: mitt: 3.0.1 urlpattern-polyfill: 9.0.0 peerDependencies: devtools-protocol: "*" - checksum: d8ac0aefcf11ebd744e0b97ecded9dac5c03ab55f46267570cdf1b780ad1e05e8cf6987b65178bda99a1ef1ea1bc59721bda85008283ca5f145912b9e1bf578d + checksum: baf9ff25065c73264d84bc5c28e81c27ef9c7fca0d38dfbb0f5d6a0446f3687e1988871e031a9c0f62b81f3173bde590a80301831beb60879500374bff0186b2 languageName: node linkType: hard "ci-info@npm:^3.2.0": - version: 3.8.0 - resolution: "ci-info@npm:3.8.0" - checksum: d0a4d3160497cae54294974a7246202244fff031b0a6ea20dd57b10ec510aa17399c41a1b0982142c105f3255aff2173e5c0dd7302ee1b2f28ba3debda375098 + version: 3.9.0 + resolution: "ci-info@npm:3.9.0" + checksum: 6b19dc9b2966d1f8c2041a838217299718f15d6c4b63ae36e4674edd2bee48f780e94761286a56aa59eb305a85fbea4ddffb7630ec063e7ec7e7e5ad42549a87 languageName: node linkType: hard @@ -5257,9 +5051,9 @@ __metadata: linkType: hard "cli-spinners@npm:^2.5.0": - version: 2.9.1 - resolution: "cli-spinners@npm:2.9.1" - checksum: 1780618be58309c469205bc315db697934bac68bce78cd5dfd46248e507a533172d623c7348ecfd904734f597ce0a4e5538684843d2cfb7af485d4466699940c + version: 2.9.2 + resolution: "cli-spinners@npm:2.9.2" + checksum: 1bd588289b28432e4676cb5d40505cfe3e53f2e4e10fbe05c8a710a154d6fe0ce7836844b00d6858f740f2ffe67cdc36e0fce9c7b6a8430e80e6388d5aa4956c languageName: node linkType: hard @@ -5360,15 +5154,6 @@ __metadata: languageName: node linkType: hard -"color-support@npm:^1.1.3": - version: 1.1.3 - resolution: "color-support@npm:1.1.3" - bin: - color-support: bin.js - checksum: 9b7356817670b9a13a26ca5af1c21615463b500783b739b7634a0c2047c16cef4b2865d7576875c31c3cddf9dd621fa19285e628f20198b233a5cfdda6d0793b - languageName: node - linkType: hard - "color@npm:^3.1.3": version: 3.2.1 resolution: "color@npm:3.2.1" @@ -5467,9 +5252,9 @@ __metadata: linkType: hard "component-emitter@npm:^1.3.0": - version: 1.3.0 - resolution: "component-emitter@npm:1.3.0" - checksum: b3c46de38ffd35c57d1c02488355be9f218e582aec72d72d1b8bbec95a3ac1b38c96cd6e03ff015577e68f550fbb361a3bfdbd9bb248be9390b7b3745691be6b + version: 1.3.1 + resolution: "component-emitter@npm:1.3.1" + checksum: 94550aa462c7bd5a61c1bc480e28554aa306066930152d1b1844a0dd3845d4e5db7e261ddec62ae184913b3e59b55a2ad84093b9d3596a8f17c341514d6c483d languageName: node linkType: hard @@ -5510,8 +5295,8 @@ __metadata: linkType: hard "concurrently@npm:^8.0.1": - version: 8.2.0 - resolution: "concurrently@npm:8.2.0" + version: 8.2.2 + resolution: "concurrently@npm:8.2.2" dependencies: chalk: ^4.1.2 date-fns: ^2.30.0 @@ -5525,14 +5310,7 @@ __metadata: bin: conc: dist/bin/concurrently.js concurrently: dist/bin/concurrently.js - checksum: eafe6a4d9b7fda87f55ea285cfc6acd937a5286ceec8991ab48e6cc27c45fce6a5c6f45e18d7555defa15dc7d7e8941bc5a9d1ceaf182e31441d420e00333434 - languageName: node - linkType: hard - -"console-control-strings@npm:^1.1.0": - version: 1.1.0 - resolution: "console-control-strings@npm:1.1.0" - checksum: 8755d76787f94e6cf79ce4666f0c5519906d7f5b02d4b884cf41e11dcd759ed69c57da0670afd9236d229a46e0f9cf519db0cd829c6dca820bb5a5c3def584ed + checksum: 8ac774df06869773438f1bf91025180c52d5b53139bc86cf47659136c0d97461d0579c515d848d1e945d4e3e0cafe646b2ea18af8d74259b46abddcfe39b2c6c languageName: node linkType: hard @@ -5559,13 +5337,6 @@ __metadata: languageName: node linkType: hard -"convert-source-map@npm:^1.6.0, convert-source-map@npm:^1.7.0": - version: 1.9.0 - resolution: "convert-source-map@npm:1.9.0" - checksum: dc55a1f28ddd0e9485ef13565f8f756b342f9a46c4ae18b843fe3c30c675d058d6a4823eff86d472f187b176f0adf51ea7b69ea38be34be4a63cbbf91b0593c8 - languageName: node - linkType: hard - "convert-source-map@npm:^2.0.0": version: 2.0.0 resolution: "convert-source-map@npm:2.0.0" @@ -5587,13 +5358,13 @@ __metadata: languageName: node linkType: hard -"cookies@npm:~0.8.0": - version: 0.8.0 - resolution: "cookies@npm:0.8.0" +"cookies@npm:~0.9.0": + version: 0.9.1 + resolution: "cookies@npm:0.9.1" dependencies: depd: ~2.0.0 keygrip: ~1.1.0 - checksum: 806055a44f128705265b1bc6a853058da18bf80dea3654ad99be20985b1fa1b14f86c1eef73644aab8071241f8a78acd57202b54c4c5c70769fc694fbb9c4edc + checksum: 213e4d14847b582fbd8a003203d3621a4b9fa792a315c37954e89332d38fac5bcc34ba92ef316ad6d5fe28f0187aaa115927fbbe2080744ad1707a93b4313247 languageName: node linkType: hard @@ -5665,6 +5436,23 @@ __metadata: languageName: node linkType: hard +"create-jest@npm:^29.7.0": + version: 29.7.0 + resolution: "create-jest@npm:29.7.0" + dependencies: + "@jest/types": ^29.6.3 + chalk: ^4.0.0 + exit: ^0.1.2 + graceful-fs: ^4.2.9 + jest-config: ^29.7.0 + jest-util: ^29.7.0 + prompts: ^2.0.1 + bin: + create-jest: bin/create-jest.js + checksum: 1427d49458adcd88547ef6fa39041e1fe9033a661293aa8d2c3aa1b4967cb5bf4f0c00436c7a61816558f28ba2ba81a94d5c962e8022ea9a883978fc8e1f2945 + languageName: node + linkType: hard + "create-require@npm:^1.1.0": version: 1.1.1 resolution: "create-require@npm:1.1.1" @@ -5718,31 +5506,31 @@ __metadata: languageName: node linkType: hard -"data-uri-to-buffer@npm:^5.0.1": - version: 5.0.1 - resolution: "data-uri-to-buffer@npm:5.0.1" - checksum: 10958f89c0047b84bd86d572b6b77c9bf238ebe7b55a9a9ab04c90fbf5ab1881783b72e31dc0febdffd30ec914930244f2f728e3629bb8911d922baba129426f +"data-uri-to-buffer@npm:^6.0.0": + version: 6.0.1 + resolution: "data-uri-to-buffer@npm:6.0.1" + checksum: 9140e68c585ae33d950f5943bd476751346c8b789ae80b01a578a33cb8f7f706d1ca7378aff2b1878b2a6d9a8c88c55cc286d88191c8b8ead8255c3c4d934530 languageName: node linkType: hard "datastore-core@npm:^9.0.1": - version: 9.2.0 - resolution: "datastore-core@npm:9.2.0" + version: 9.2.7 + resolution: "datastore-core@npm:9.2.7" dependencies: - "@libp2p/logger": ^2.0.0 + "@libp2p/logger": ^4.0.1 err-code: ^3.0.1 interface-store: ^5.0.0 it-all: ^3.0.1 it-drain: ^3.0.1 it-filter: ^3.0.0 it-map: ^3.0.1 - it-merge: ^3.0.0 + it-merge: ^3.0.1 it-pipe: ^3.0.0 it-pushable: ^3.0.0 it-sort: ^3.0.1 it-take: ^3.0.1 - uint8arrays: ^4.0.2 - checksum: f955cdea823e3e3b9ec0090e7dcfab54b4818156674555e7ca38b0047c98dd42cb14474495cbb3c5e6b721320269354fe0185ea2101f6542fb5149f47d7b2114 + uint8arrays: ^5.0.0 + checksum: 593f40d8e5ccbc80b073b4ec1553e70bc061d4656ca238c10eb47d799ff8a137f19698268b0639cc5a26cf5e036f72946dad0bd20cd37e57f713c9d7a1b32a67 languageName: node linkType: hard @@ -5833,34 +5621,12 @@ __metadata: languageName: node linkType: hard -"default-browser-id@npm:^3.0.0": - version: 3.0.0 - resolution: "default-browser-id@npm:3.0.0" - dependencies: - bplist-parser: ^0.2.0 - untildify: ^4.0.0 - checksum: 279c7ad492542e5556336b6c254a4eaf31b2c63a5433265655ae6e47301197b6cfb15c595a6fdc6463b2ff8e1a1a1ed3cba56038a60e1527ba4ab1628c6b9941 - languageName: node - linkType: hard - -"default-browser@npm:^4.0.0": - version: 4.0.0 - resolution: "default-browser@npm:4.0.0" +"default-gateway@npm:^7.2.2": + version: 7.2.2 + resolution: "default-gateway@npm:7.2.2" dependencies: - bundle-name: ^3.0.0 - default-browser-id: ^3.0.0 execa: ^7.1.1 - titleize: ^3.0.0 - checksum: 40c5af984799042b140300be5639c9742599bda76dc9eba5ac9ad5943c83dd36cebc4471eafcfddf8e0ec817166d5ba89d56f08e66a126c7c7908a179cead1a7 - languageName: node - linkType: hard - -"default-gateway@npm:^6.0.2": - version: 6.0.3 - resolution: "default-gateway@npm:6.0.3" - dependencies: - execa: ^5.0.0 - checksum: 126f8273ecac8ee9ff91ea778e8784f6cd732d77c3157e8c5bdd6ed03651b5291f71446d05bc02d04073b1e67583604db5394ea3cf992ede0088c70ea15b7378 + checksum: eec8a2a338677322bcdbf339bbdede61225f6145eedd0c3d4948deadfc929dc0e04b153b33674873d36efa403f76649aaacbfc728a438ee9f538a28723515478 languageName: node linkType: hard @@ -5883,20 +5649,25 @@ __metadata: languageName: node linkType: hard -"define-lazy-prop@npm:^3.0.0": - version: 3.0.0 - resolution: "define-lazy-prop@npm:3.0.0" - checksum: 54884f94caac0791bf6395a3ec530ce901cf71c47b0196b8754f3fd17edb6c0e80149c1214429d851873bb0d689dbe08dcedbb2306dc45c8534a5934723851b6 +"define-data-property@npm:^1.0.1, define-data-property@npm:^1.1.1": + version: 1.1.1 + resolution: "define-data-property@npm:1.1.1" + dependencies: + get-intrinsic: ^1.2.1 + gopd: ^1.0.1 + has-property-descriptors: ^1.0.0 + checksum: a29855ad3f0630ea82e3c5012c812efa6ca3078d5c2aa8df06b5f597c1cde6f7254692df41945851d903e05a1668607b6d34e778f402b9ff9ffb38111f1a3f0d languageName: node linkType: hard -"define-properties@npm:^1.1.3, define-properties@npm:^1.1.4, define-properties@npm:^1.2.0": - version: 1.2.0 - resolution: "define-properties@npm:1.2.0" +"define-properties@npm:^1.1.3, define-properties@npm:^1.2.0, define-properties@npm:^1.2.1": + version: 1.2.1 + resolution: "define-properties@npm:1.2.1" dependencies: + define-data-property: ^1.0.1 has-property-descriptors: ^1.0.0 object-keys: ^1.1.1 - checksum: e60aee6a19b102df4e2b1f301816804e81ab48bb91f00d0d935f269bf4b3f79c88b39e4f89eaa132890d23267335fd1140dfcd8d5ccd61031a0a2c41a54e33a6 + checksum: b4ccd00597dd46cb2d4a379398f5b19fca84a16f3374e2249201992f36b30f6835949a9429669ee6b41b6e837205a163eadd745e472069e70dfc10f03e5fcc12 languageName: node linkType: hard @@ -6184,10 +5955,10 @@ __metadata: languageName: node linkType: hard -"devtools-protocol@npm:0.0.1179426": - version: 0.0.1179426 - resolution: "devtools-protocol@npm:0.0.1179426" - checksum: 38a091bde42d7d0f8e5e6c7a445db6a56d7b80f21f51de47ed1316123f70b2256aa6fe0d87fbe37bcc4928c0b9245e28a17fb7fbf8d8622156ae36870b26c1ed +"devtools-protocol@npm:0.0.1203626": + version: 0.0.1203626 + resolution: "devtools-protocol@npm:0.0.1203626" + checksum: 7bbcb1e637cf9c957aeae11284d2c4cd5fb45c0324839c9074b046889982a53f5cfca190f404aa9468523113714909db126b70a04baf1ec59f9905945a4b5975 languageName: node linkType: hard @@ -6201,10 +5972,10 @@ __metadata: languageName: node linkType: hard -"diff-sequences@npm:^29.4.3": - version: 29.4.3 - resolution: "diff-sequences@npm:29.4.3" - checksum: 28b265e04fdddcf7f9f814effe102cc95a9dec0564a579b5aed140edb24fc345c611ca52d76d725a3cab55d3888b915b5e8a4702e0f6058968a90fa5f41fcde7 +"diff-sequences@npm:^29.6.3": + version: 29.6.3 + resolution: "diff-sequences@npm:29.6.3" + checksum: f4914158e1f2276343d98ff5b31fc004e7304f5470bf0f1adb2ac6955d85a531a6458d33e87667f98f6ae52ebd3891bb47d420bb48a5bd8b7a27ee25b20e33aa languageName: node linkType: hard @@ -6235,15 +6006,13 @@ __metadata: languageName: node linkType: hard -"dns-over-http-resolver@npm:^2.1.0": - version: 2.1.1 - resolution: "dns-over-http-resolver@npm:2.1.1" +"dns-over-http-resolver@npm:3.0.0": + version: 3.0.0 + resolution: "dns-over-http-resolver@npm:3.0.0" dependencies: - debug: ^4.3.1 - native-fetch: ^4.0.2 + debug: ^4.3.4 receptacle: ^1.3.2 - undici: ^5.12.0 - checksum: 153a0f4ef705cd08c9b0c163d654988dbb087eabe44c8ab243481c108af97b52ec6343b7127ed1a6a5705a497e8dc06cd1e6c33fbe5aae3a08e357c1e93fc93e + checksum: 8527d5cda8b01ef6eff356a983abca2e65c967b0d5f368bfd0d3865e9ae4c0d360c38cef5e8bcd8055b3c7d392a15094c98d6b6eefd9cd54734efcd42ac24068 languageName: node linkType: hard @@ -6266,9 +6035,9 @@ __metadata: linkType: hard "dotenv@npm:^16.0.3": - version: 16.3.1 - resolution: "dotenv@npm:16.3.1" - checksum: 15d75e7279018f4bafd0ee9706593dd14455ddb71b3bcba9c52574460b7ccaf67d5cf8b2c08a5af1a9da6db36c956a04a1192b101ee102a3e0cf8817bbcf3dfd + version: 16.3.2 + resolution: "dotenv@npm:16.3.2" + checksum: 917b27eeb654b95846484009326b1c52af7a7c25f7b09e2939ed49de8f98cb9895dcc04f13a39cdb078d247985e21147311ccb5bfbf2fd151afb20fa8f96de15 languageName: node linkType: hard @@ -6293,10 +6062,10 @@ __metadata: languageName: node linkType: hard -"electron-to-chromium@npm:^1.4.477": - version: 1.4.480 - resolution: "electron-to-chromium@npm:1.4.480" - checksum: 074b9d81dffa6ca182f604326c62a12d22139ae141d6ffabdb3b267f42fac88ba3e69d3614f9e939f65a0f03a343f0c81f702e24064bc54c4f138e02378b1e54 +"electron-to-chromium@npm:^1.4.601": + version: 1.4.642 + resolution: "electron-to-chromium@npm:1.4.642" + checksum: 18b729cf672e8663706998683034acc70fe3fccaff8efb70324a0b2e2a1c021fb670cf1061944d2ec7b5cf2dca8f443403942d9fecf53c333debee47e0cd1f90 languageName: node linkType: hard @@ -6386,11 +6155,11 @@ __metadata: linkType: hard "envinfo@npm:^7.7.3": - version: 7.10.0 - resolution: "envinfo@npm:7.10.0" + version: 7.11.0 + resolution: "envinfo@npm:7.11.0" bin: envinfo: dist/cli.js - checksum: 05e81a5768c42cbd5c580dc3f274db3401facadd53e9bd52e2aa49dfbb5d8b26f6181c25a6652d79618a6994185bd2b1c137673101690b147f758e4e71d42f7d + checksum: c45a7d20409d5f4cda72483b150d3816b15b434f2944d72c1495d8838bd7c4e7b2f32c12128ffb9b92b5f66f436237b8a525eb3a9a5da2d20013bc4effa28aef languageName: node linkType: hard @@ -6417,25 +6186,25 @@ __metadata: languageName: node linkType: hard -"es-abstract@npm:^1.19.0, es-abstract@npm:^1.20.4, es-abstract@npm:^1.21.2": - version: 1.22.1 - resolution: "es-abstract@npm:1.22.1" +"es-abstract@npm:^1.22.1": + version: 1.22.3 + resolution: "es-abstract@npm:1.22.3" dependencies: array-buffer-byte-length: ^1.0.0 - arraybuffer.prototype.slice: ^1.0.1 + arraybuffer.prototype.slice: ^1.0.2 available-typed-arrays: ^1.0.5 - call-bind: ^1.0.2 + call-bind: ^1.0.5 es-set-tostringtag: ^2.0.1 es-to-primitive: ^1.2.1 - function.prototype.name: ^1.1.5 - get-intrinsic: ^1.2.1 + function.prototype.name: ^1.1.6 + get-intrinsic: ^1.2.2 get-symbol-description: ^1.0.0 globalthis: ^1.0.3 gopd: ^1.0.1 - has: ^1.0.3 has-property-descriptors: ^1.0.0 has-proto: ^1.0.1 has-symbols: ^1.0.3 + hasown: ^2.0.0 internal-slot: ^1.0.5 is-array-buffer: ^3.0.2 is-callable: ^1.2.7 @@ -6443,51 +6212,51 @@ __metadata: is-regex: ^1.1.4 is-shared-array-buffer: ^1.0.2 is-string: ^1.0.7 - is-typed-array: ^1.1.10 + is-typed-array: ^1.1.12 is-weakref: ^1.0.2 - object-inspect: ^1.12.3 + object-inspect: ^1.13.1 object-keys: ^1.1.1 object.assign: ^4.1.4 - regexp.prototype.flags: ^1.5.0 - safe-array-concat: ^1.0.0 + regexp.prototype.flags: ^1.5.1 + safe-array-concat: ^1.0.1 safe-regex-test: ^1.0.0 - string.prototype.trim: ^1.2.7 - string.prototype.trimend: ^1.0.6 - string.prototype.trimstart: ^1.0.6 + string.prototype.trim: ^1.2.8 + string.prototype.trimend: ^1.0.7 + string.prototype.trimstart: ^1.0.7 typed-array-buffer: ^1.0.0 typed-array-byte-length: ^1.0.0 typed-array-byte-offset: ^1.0.0 typed-array-length: ^1.0.4 unbox-primitive: ^1.0.2 - which-typed-array: ^1.1.10 - checksum: 614e2c1c3717cb8d30b6128ef12ea110e06fd7d75ad77091ca1c5dbfb00da130e62e4bbbbbdda190eada098a22b27fe0f99ae5a1171dac2c8663b1e8be8a3a9b + which-typed-array: ^1.1.13 + checksum: b1bdc962856836f6e72be10b58dc128282bdf33771c7a38ae90419d920fc3b36cc5d2b70a222ad8016e3fc322c367bf4e9e89fc2bc79b7e933c05b218e83d79a languageName: node linkType: hard "es-module-lexer@npm:^1.2.1": - version: 1.3.0 - resolution: "es-module-lexer@npm:1.3.0" - checksum: 48fd9f504a9d2a894126f75c8b7ccc6273a289983e9b67255f165bfd9ae765d50100218251e94e702ca567826905ea2f7b3b4a0c4d74d3ce99cce3a2a606a238 + version: 1.4.1 + resolution: "es-module-lexer@npm:1.4.1" + checksum: a11b5a256d4e8e9c7d94c2fd87415ccd1591617b6edd847e064503f8eaece2d25e2e9078a02c5ce3ed5e83bb748f5b4820efbe78072c8beb07ac619c2edec35d languageName: node linkType: hard "es-set-tostringtag@npm:^2.0.1": - version: 2.0.1 - resolution: "es-set-tostringtag@npm:2.0.1" + version: 2.0.2 + resolution: "es-set-tostringtag@npm:2.0.2" dependencies: - get-intrinsic: ^1.1.3 - has: ^1.0.3 + get-intrinsic: ^1.2.2 has-tostringtag: ^1.0.0 - checksum: ec416a12948cefb4b2a5932e62093a7cf36ddc3efd58d6c58ca7ae7064475ace556434b869b0bbeb0c365f1032a8ccd577211101234b69837ad83ad204fff884 + hasown: ^2.0.0 + checksum: afcec3a4c9890ae14d7ec606204858441c801ff84f312538e1d1ccf1e5493c8b17bd672235df785f803756472cb4f2d49b87bde5237aef33411e74c22f194e07 languageName: node linkType: hard "es-shim-unscopables@npm:^1.0.0": - version: 1.0.0 - resolution: "es-shim-unscopables@npm:1.0.0" + version: 1.0.2 + resolution: "es-shim-unscopables@npm:1.0.2" dependencies: - has: ^1.0.3 - checksum: 83e95cadbb6ee44d3644dfad60dcad7929edbc42c85e66c3e99aefd68a3a5c5665f2686885cddb47dfeabfd77bd5ea5a7060f2092a955a729bbd8834f0d86fa1 + hasown: ^2.0.0 + checksum: 432bd527c62065da09ed1d37a3f8e623c423683285e6188108286f4a1e8e164a5bcbfbc0051557c7d14633cd2a41ce24c7048e6bbb66a985413fd32f1be72626 languageName: node linkType: hard @@ -6503,31 +6272,31 @@ __metadata: linkType: hard "esbuild@npm:^0.18.10": - version: 0.18.17 - resolution: "esbuild@npm:0.18.17" - dependencies: - "@esbuild/android-arm": 0.18.17 - "@esbuild/android-arm64": 0.18.17 - "@esbuild/android-x64": 0.18.17 - "@esbuild/darwin-arm64": 0.18.17 - "@esbuild/darwin-x64": 0.18.17 - "@esbuild/freebsd-arm64": 0.18.17 - "@esbuild/freebsd-x64": 0.18.17 - "@esbuild/linux-arm": 0.18.17 - "@esbuild/linux-arm64": 0.18.17 - "@esbuild/linux-ia32": 0.18.17 - "@esbuild/linux-loong64": 0.18.17 - "@esbuild/linux-mips64el": 0.18.17 - "@esbuild/linux-ppc64": 0.18.17 - "@esbuild/linux-riscv64": 0.18.17 - "@esbuild/linux-s390x": 0.18.17 - "@esbuild/linux-x64": 0.18.17 - "@esbuild/netbsd-x64": 0.18.17 - "@esbuild/openbsd-x64": 0.18.17 - "@esbuild/sunos-x64": 0.18.17 - "@esbuild/win32-arm64": 0.18.17 - "@esbuild/win32-ia32": 0.18.17 - "@esbuild/win32-x64": 0.18.17 + version: 0.18.20 + resolution: "esbuild@npm:0.18.20" + dependencies: + "@esbuild/android-arm": 0.18.20 + "@esbuild/android-arm64": 0.18.20 + "@esbuild/android-x64": 0.18.20 + "@esbuild/darwin-arm64": 0.18.20 + "@esbuild/darwin-x64": 0.18.20 + "@esbuild/freebsd-arm64": 0.18.20 + "@esbuild/freebsd-x64": 0.18.20 + "@esbuild/linux-arm": 0.18.20 + "@esbuild/linux-arm64": 0.18.20 + "@esbuild/linux-ia32": 0.18.20 + "@esbuild/linux-loong64": 0.18.20 + "@esbuild/linux-mips64el": 0.18.20 + "@esbuild/linux-ppc64": 0.18.20 + "@esbuild/linux-riscv64": 0.18.20 + "@esbuild/linux-s390x": 0.18.20 + "@esbuild/linux-x64": 0.18.20 + "@esbuild/netbsd-x64": 0.18.20 + "@esbuild/openbsd-x64": 0.18.20 + "@esbuild/sunos-x64": 0.18.20 + "@esbuild/win32-arm64": 0.18.20 + "@esbuild/win32-ia32": 0.18.20 + "@esbuild/win32-x64": 0.18.20 dependenciesMeta: "@esbuild/android-arm": optional: true @@ -6575,7 +6344,7 @@ __metadata: optional: true bin: esbuild: bin/esbuild - checksum: c6e1ffa776978a45697763a07ec9b16411db3d3b3997b2c4a0165a211727fce8b63b87165a28d8ef60d3a28b98197bbbc2833e51b89888a4437e0a483dffc8ff + checksum: 5d253614e50cdb6ec22095afd0c414f15688e7278a7eb4f3720a6dd1306b0909cf431e7b9437a90d065a31b1c57be60130f63fe3e8d0083b588571f31ee6ec7b languageName: node linkType: hard @@ -6633,43 +6402,42 @@ __metadata: linkType: hard "eslint-config-prettier@npm:^8.5.0": - version: 8.9.0 - resolution: "eslint-config-prettier@npm:8.9.0" + version: 8.10.0 + resolution: "eslint-config-prettier@npm:8.10.0" peerDependencies: eslint: ">=7.0.0" bin: eslint-config-prettier: bin/cli.js - checksum: a675d0dabd76b700ef2d062b5ec6a634e105a8e8c070f95281fd2ccb614527fac60b4c758132058c50f0521fd19313f1f5be45ce9ebf081f2e5f77ae6eb7d8db + checksum: 153266badd477e49b0759816246b2132f1dbdb6c7f313ca60a9af5822fd1071c2bc5684a3720d78b725452bbac04bb130878b2513aea5e72b1b792de5a69fec8 languageName: node linkType: hard -"eslint-import-resolver-node@npm:^0.3.7": - version: 0.3.7 - resolution: "eslint-import-resolver-node@npm:0.3.7" +"eslint-import-resolver-node@npm:^0.3.9": + version: 0.3.9 + resolution: "eslint-import-resolver-node@npm:0.3.9" dependencies: debug: ^3.2.7 - is-core-module: ^2.11.0 - resolve: ^1.22.1 - checksum: 3379aacf1d2c6952c1b9666c6fa5982c3023df695430b0d391c0029f6403a7775414873d90f397e98ba6245372b6c8960e16e74d9e4a3b0c0a4582f3bdbe3d6e + is-core-module: ^2.13.0 + resolve: ^1.22.4 + checksum: 439b91271236b452d478d0522a44482e8c8540bf9df9bd744062ebb89ab45727a3acd03366a6ba2bdbcde8f9f718bab7fe8db64688aca75acf37e04eafd25e22 languageName: node linkType: hard "eslint-import-resolver-typescript@npm:^3.5.5": - version: 3.5.5 - resolution: "eslint-import-resolver-typescript@npm:3.5.5" + version: 3.6.1 + resolution: "eslint-import-resolver-typescript@npm:3.6.1" dependencies: debug: ^4.3.4 enhanced-resolve: ^5.12.0 eslint-module-utils: ^2.7.4 + fast-glob: ^3.3.1 get-tsconfig: ^4.5.0 - globby: ^13.1.3 is-core-module: ^2.11.0 is-glob: ^4.0.3 - synckit: ^0.8.5 peerDependencies: eslint: "*" eslint-plugin-import: "*" - checksum: 27e6276fdff5d377c9036362ff736ac29852106e883ff589ea9092dc57d4bc2a67a82d75134221124f05045f9a7e2114a159b2c827d1f9f64d091f7afeab0f58 + checksum: 454fa0646533050fb57f13d27daf8c71f51b0bb9156d6a461290ccb8576d892209fcc6702a89553f3f5ea8e5b407395ca2e5de169a952c953685f1f7c46b4496 languageName: node linkType: hard @@ -6686,30 +6454,29 @@ __metadata: linkType: hard "eslint-plugin-import@npm:^2.27.5": - version: 2.28.0 - resolution: "eslint-plugin-import@npm:2.28.0" + version: 2.29.1 + resolution: "eslint-plugin-import@npm:2.29.1" dependencies: - array-includes: ^3.1.6 - array.prototype.findlastindex: ^1.2.2 - array.prototype.flat: ^1.3.1 - array.prototype.flatmap: ^1.3.1 + array-includes: ^3.1.7 + array.prototype.findlastindex: ^1.2.3 + array.prototype.flat: ^1.3.2 + array.prototype.flatmap: ^1.3.2 debug: ^3.2.7 doctrine: ^2.1.0 - eslint-import-resolver-node: ^0.3.7 + eslint-import-resolver-node: ^0.3.9 eslint-module-utils: ^2.8.0 - has: ^1.0.3 - is-core-module: ^2.12.1 + hasown: ^2.0.0 + is-core-module: ^2.13.1 is-glob: ^4.0.3 minimatch: ^3.1.2 - object.fromentries: ^2.0.6 - object.groupby: ^1.0.0 - object.values: ^1.1.6 - resolve: ^1.22.3 + object.fromentries: ^2.0.7 + object.groupby: ^1.0.1 + object.values: ^1.1.7 semver: ^6.3.1 - tsconfig-paths: ^3.14.2 + tsconfig-paths: ^3.15.0 peerDependencies: eslint: ^2 || ^3 || ^4 || ^5 || ^6 || ^7.2.0 || ^8 - checksum: f9eba311b93ca1bb89311856b1f7285bd79e0181d7eb70fe115053ff77e2235fea749b30f538b78927dc65769340b5be61f4c9581d1c82bcdcccb2061f440ad1 + checksum: e65159aef808136d26d029b71c8c6e4cb5c628e65e5de77f1eb4c13a379315ae55c9c3afa847f43f4ff9df7e54515c77ffc6489c6a6f81f7dd7359267577468c languageName: node linkType: hard @@ -6774,24 +6541,25 @@ __metadata: languageName: node linkType: hard -"eslint-visitor-keys@npm:^3.3.0, eslint-visitor-keys@npm:^3.4.1, eslint-visitor-keys@npm:^3.4.2": - version: 3.4.2 - resolution: "eslint-visitor-keys@npm:3.4.2" - checksum: 9e0e7e4aaea705c097ae37c97410e5f167d4d2193be2edcb1f0760762ede3df01545e4820ae314f42dcec687745f2c6dcaf6d83575c4a2a241eb0c8517d724f2 +"eslint-visitor-keys@npm:^3.3.0, eslint-visitor-keys@npm:^3.4.1, eslint-visitor-keys@npm:^3.4.3": + version: 3.4.3 + resolution: "eslint-visitor-keys@npm:3.4.3" + checksum: 36e9ef87fca698b6fd7ca5ca35d7b2b6eeaaf106572e2f7fd31c12d3bfdaccdb587bba6d3621067e5aece31c8c3a348b93922ab8f7b2cbc6aaab5e1d89040c60 languageName: node linkType: hard "eslint@npm:^8.21.0, eslint@npm:^8.35.0, eslint@npm:^8.37.0": - version: 8.46.0 - resolution: "eslint@npm:8.46.0" + version: 8.56.0 + resolution: "eslint@npm:8.56.0" dependencies: "@eslint-community/eslint-utils": ^4.2.0 "@eslint-community/regexpp": ^4.6.1 - "@eslint/eslintrc": ^2.1.1 - "@eslint/js": ^8.46.0 - "@humanwhocodes/config-array": ^0.11.10 + "@eslint/eslintrc": ^2.1.4 + "@eslint/js": 8.56.0 + "@humanwhocodes/config-array": ^0.11.13 "@humanwhocodes/module-importer": ^1.0.1 "@nodelib/fs.walk": ^1.2.8 + "@ungap/structured-clone": ^1.2.0 ajv: ^6.12.4 chalk: ^4.0.0 cross-spawn: ^7.0.2 @@ -6799,7 +6567,7 @@ __metadata: doctrine: ^3.0.0 escape-string-regexp: ^4.0.0 eslint-scope: ^7.2.2 - eslint-visitor-keys: ^3.4.2 + eslint-visitor-keys: ^3.4.3 espree: ^9.6.1 esquery: ^1.4.2 esutils: ^2.0.2 @@ -6824,7 +6592,7 @@ __metadata: text-table: ^0.2.0 bin: eslint: bin/eslint.js - checksum: 7a7d36b1a3bbc12e08fbb5bc36fd482a7a5a1797e62e762499dd45601b9e45aaa53a129f31ce0b4444551a9639b8b681ad535f379893dd1e3ae37b31dccd82aa + checksum: 883436d1e809b4a25d9eb03d42f584b84c408dbac28b0019f6ea07b5177940bf3cca86208f749a6a1e0039b63e085ee47aca1236c30721e91f0deef5cc5a5136 languageName: node linkType: hard @@ -6910,14 +6678,14 @@ __metadata: languageName: node linkType: hard -"eventemitter3@npm:^4.0.7": - version: 4.0.7 - resolution: "eventemitter3@npm:4.0.7" - checksum: 1875311c42fcfe9c707b2712c32664a245629b42bb0a5a84439762dd0fd637fc54d078155ea83c2af9e0323c9ac13687e03cfba79b03af9f40c89b4960099374 +"eventemitter3@npm:^5.0.1": + version: 5.0.1 + resolution: "eventemitter3@npm:5.0.1" + checksum: 543d6c858ab699303c3c32e0f0f47fc64d360bf73c3daf0ac0b5079710e340d6fe9f15487f94e66c629f5f82cd1a8678d692f3dbb6f6fcd1190e1b97fcad36f8 languageName: node linkType: hard -"events@npm:^3.2.0, events@npm:^3.3.0": +"events@npm:^3.2.0": version: 3.3.0 resolution: "events@npm:3.3.0" checksum: f6f487ad2198aa41d878fa31452f1a3c00958f46e9019286ff4787c84aac329332ab45c9cdc8c445928fc6d7ded294b9e005a7fce9426488518017831b272780 @@ -6976,17 +6744,16 @@ __metadata: languageName: node linkType: hard -"expect@npm:^29.0.0, expect@npm:^29.6.2": - version: 29.6.2 - resolution: "expect@npm:29.6.2" +"expect@npm:^29.0.0, expect@npm:^29.7.0": + version: 29.7.0 + resolution: "expect@npm:29.7.0" dependencies: - "@jest/expect-utils": ^29.6.2 - "@types/node": "*" - jest-get-type: ^29.4.3 - jest-matcher-utils: ^29.6.2 - jest-message-util: ^29.6.2 - jest-util: ^29.6.2 - checksum: 71f7b0c560e58bf6d27e0fded261d4bdb7ef81552a6bb4bd1ee09ce7a1f7dca67fbf83cf9b07a6645a88ef52e65085a0dcbe17f6c063b53ff7c2f0f3ea4ef69e + "@jest/expect-utils": ^29.7.0 + jest-get-type: ^29.6.3 + jest-matcher-utils: ^29.7.0 + jest-message-util: ^29.7.0 + jest-util: ^29.7.0 + checksum: 9257f10288e149b81254a0fda8ffe8d54a7061cd61d7515779998b012579d2b8c22354b0eb901daf0145f347403da582f75f359f4810c007182ad3fb318b5c0c languageName: node linkType: hard @@ -7028,16 +6795,16 @@ __metadata: languageName: node linkType: hard -"fast-glob@npm:^3.2.9, fast-glob@npm:^3.3.0": - version: 3.3.1 - resolution: "fast-glob@npm:3.3.1" +"fast-glob@npm:^3.2.9, fast-glob@npm:^3.3.1": + version: 3.3.2 + resolution: "fast-glob@npm:3.3.2" dependencies: "@nodelib/fs.stat": ^2.0.2 "@nodelib/fs.walk": ^1.2.3 glob-parent: ^5.1.2 merge2: ^1.3.0 micromatch: ^4.0.4 - checksum: b6f3add6403e02cf3a798bfbb1183d0f6da2afd368f27456010c0bc1f9640aea308243d4cb2c0ab142f618276e65ecb8be1661d7c62a7b4e5ba774b9ce5432e5 + checksum: 900e4979f4dbc3313840078419245621259f349950411ca2fa445a2f9a1a6d98c3b5e7e0660c5ccd563aa61abe133a21765c6c0dec8e57da1ba71d8000b05ec1 languageName: node linkType: hard @@ -7070,11 +6837,11 @@ __metadata: linkType: hard "fastq@npm:^1.6.0": - version: 1.15.0 - resolution: "fastq@npm:1.15.0" + version: 1.16.0 + resolution: "fastq@npm:1.16.0" dependencies: reusify: ^1.0.4 - checksum: 0170e6bfcd5d57a70412440b8ef600da6de3b2a6c5966aeaf0a852d542daff506a0ee92d6de7679d1de82e644bce69d7a574a6c93f0b03964b5337eed75ada1a + checksum: 1d40ed1f100ae625e5720484e8602b7ad07649370f1cbc3e34a6b9630a0bfed6946bab0322d8a368a1e3cde87bb9bbb8d3bc2ae01a0c1f022fac1d07c04e4feb languageName: node linkType: hard @@ -7191,19 +6958,29 @@ __metadata: linkType: hard "flat-cache@npm:^3.0.4": - version: 3.0.4 - resolution: "flat-cache@npm:3.0.4" + version: 3.2.0 + resolution: "flat-cache@npm:3.2.0" dependencies: - flatted: ^3.1.0 + flatted: ^3.2.9 + keyv: ^4.5.3 rimraf: ^3.0.2 - checksum: 4fdd10ecbcbf7d520f9040dd1340eb5dfe951e6f0ecf2252edeec03ee68d989ec8b9a20f4434270e71bcfd57800dc09b3344fca3966b2eb8f613072c7d9a2365 + checksum: e7e0f59801e288b54bee5cb9681e9ee21ee28ef309f886b312c9d08415b79fc0f24ac842f84356ce80f47d6a53de62197ce0e6e148dc42d5db005992e2a756ec languageName: node linkType: hard -"flatted@npm:^3.1.0": - version: 3.2.7 - resolution: "flatted@npm:3.2.7" - checksum: 427633049d55bdb80201c68f7eb1cbd533e03eac541f97d3aecab8c5526f12a20ccecaeede08b57503e772c769e7f8680b37e8d482d1e5f8d7e2194687f9ea35 +"flat@npm:^5.0.2": + version: 5.0.2 + resolution: "flat@npm:5.0.2" + bin: + flat: cli.js + checksum: 12a1536ac746db74881316a181499a78ef953632ddd28050b7a3a43c62ef5462e3357c8c29d76072bb635f147f7a9a1f0c02efef6b4be28f8db62ceb3d5c7f5d + languageName: node + linkType: hard + +"flatted@npm:^3.2.9": + version: 3.2.9 + resolution: "flatted@npm:3.2.9" + checksum: f14167fbe26a9d20f6fca8d998e8f1f41df72c8e81f9f2c9d61ed2bea058248f5e1cbd05e7f88c0e5087a6a0b822a1e5e2b446e879f3cfbe0b07ba2d7f80b026 languageName: node linkType: hard @@ -7294,13 +7071,13 @@ __metadata: linkType: hard "fs-extra@npm:^11.1.1": - version: 11.1.1 - resolution: "fs-extra@npm:11.1.1" + version: 11.2.0 + resolution: "fs-extra@npm:11.2.0" dependencies: graceful-fs: ^4.2.0 jsonfile: ^6.0.1 universalify: ^2.0.0 - checksum: fb883c68245b2d777fbc1f2082c9efb084eaa2bbf9fddaa366130d196c03608eebef7fb490541276429ee1ca99f317e2d73e96f5ca0999eefedf5a624ae1edfd + checksum: b12e42fa40ba47104202f57b8480dd098aa931c2724565e5e70779ab87605665594e76ee5fb00545f772ab9ace167fe06d2ab009c416dc8c842c5ae6df7aa7e8 languageName: node linkType: hard @@ -7325,11 +7102,11 @@ __metadata: linkType: hard "fs-minipass@npm:^3.0.0": - version: 3.0.2 - resolution: "fs-minipass@npm:3.0.2" + version: 3.0.3 + resolution: "fs-minipass@npm:3.0.3" dependencies: - minipass: ^5.0.0 - checksum: e9cc0e1f2d01c6f6f62f567aee59530aba65c6c7b2ae88c5027bc34c711ebcfcfaefd0caf254afa6adfe7d1fba16bc2537508a6235196bac7276747d078aef0a + minipass: ^7.0.3 + checksum: 8722a41109130851d979222d3ec88aabaceeaaf8f57b2a8f744ef8bd2d1ce95453b04a61daa0078822bc5cd21e008814f06fe6586f56fef511e71b8d2394d802 languageName: node linkType: hard @@ -7341,31 +7118,24 @@ __metadata: linkType: hard "fsevents@npm:^2.3.2, fsevents@npm:~2.3.2": - version: 2.3.2 - resolution: "fsevents@npm:2.3.2" + version: 2.3.3 + resolution: "fsevents@npm:2.3.3" dependencies: node-gyp: latest - checksum: 97ade64e75091afee5265e6956cb72ba34db7819b4c3e94c431d4be2b19b8bb7a2d4116da417950c3425f17c8fe693d25e20212cac583ac1521ad066b77ae31f + checksum: 11e6ea6fea15e42461fc55b4b0e4a0a3c654faa567f1877dbd353f39156f69def97a69936d1746619d656c4b93de2238bf731f6085a03a50cabf287c9d024317 conditions: os=darwin languageName: node linkType: hard "fsevents@patch:fsevents@^2.3.2#~builtin, fsevents@patch:fsevents@~2.3.2#~builtin": - version: 2.3.2 - resolution: "fsevents@patch:fsevents@npm%3A2.3.2#~builtin::version=2.3.2&hash=df0bf1" + version: 2.3.3 + resolution: "fsevents@patch:fsevents@npm%3A2.3.3#~builtin::version=2.3.3&hash=df0bf1" dependencies: node-gyp: latest conditions: os=darwin languageName: node linkType: hard -"function-bind@npm:^1.1.1": - version: 1.1.1 - resolution: "function-bind@npm:1.1.1" - checksum: b32fbaebb3f8ec4969f033073b43f5c8befbb58f1a79e12f1d7490358150359ebd92f49e72ff0144f65f2c48ea2a605bff2d07965f548f6474fd8efd95bf361a - languageName: node - linkType: hard - "function-bind@npm:^1.1.2": version: 1.1.2 resolution: "function-bind@npm:1.1.2" @@ -7373,15 +7143,15 @@ __metadata: languageName: node linkType: hard -"function.prototype.name@npm:^1.1.5": - version: 1.1.5 - resolution: "function.prototype.name@npm:1.1.5" +"function.prototype.name@npm:^1.1.6": + version: 1.1.6 + resolution: "function.prototype.name@npm:1.1.6" dependencies: call-bind: ^1.0.2 - define-properties: ^1.1.3 - es-abstract: ^1.19.0 - functions-have-names: ^1.2.2 - checksum: acd21d733a9b649c2c442f067567743214af5fa248dbeee69d8278ce7df3329ea5abac572be9f7470b4ec1cd4d8f1040e3c5caccf98ebf2bf861a0deab735c27 + define-properties: ^1.2.0 + es-abstract: ^1.22.1 + functions-have-names: ^1.2.3 + checksum: 7a3f9bd98adab09a07f6e1f03da03d3f7c26abbdeaeee15223f6c04a9fb5674792bdf5e689dac19b97ac71de6aad2027ba3048a9b883aa1b3173eed6ab07f479 languageName: node linkType: hard @@ -7392,29 +7162,13 @@ __metadata: languageName: node linkType: hard -"functions-have-names@npm:^1.2.2, functions-have-names@npm:^1.2.3": +"functions-have-names@npm:^1.2.3": version: 1.2.3 resolution: "functions-have-names@npm:1.2.3" checksum: c3f1f5ba20f4e962efb71344ce0a40722163e85bee2101ce25f88214e78182d2d2476aa85ef37950c579eb6cf6ee811c17b3101bb84004bb75655f3e33f3fdb5 languageName: node linkType: hard -"gauge@npm:^4.0.3": - version: 4.0.4 - resolution: "gauge@npm:4.0.4" - dependencies: - aproba: ^1.0.3 || ^2.0.0 - color-support: ^1.1.3 - console-control-strings: ^1.1.0 - has-unicode: ^2.0.1 - signal-exit: ^3.0.7 - string-width: ^4.2.3 - strip-ansi: ^6.0.1 - wide-align: ^1.1.5 - checksum: 788b6bfe52f1dd8e263cda800c26ac0ca2ff6de0b6eee2fe0d9e3abf15e149b651bd27bf5226be10e6e3edb5c4e5d5985a5a1a98137e7a892f75eff76467ad2d - languageName: node - linkType: hard - "gensync@npm:^1.0.0-beta.2": version: 1.0.0-beta.2 resolution: "gensync@npm:1.0.0-beta.2" @@ -7449,15 +7203,15 @@ __metadata: languageName: node linkType: hard -"get-intrinsic@npm:^1.0.2, get-intrinsic@npm:^1.1.1, get-intrinsic@npm:^1.1.3, get-intrinsic@npm:^1.2.0, get-intrinsic@npm:^1.2.1": - version: 1.2.1 - resolution: "get-intrinsic@npm:1.2.1" +"get-intrinsic@npm:^1.0.2, get-intrinsic@npm:^1.1.1, get-intrinsic@npm:^1.1.3, get-intrinsic@npm:^1.2.0, get-intrinsic@npm:^1.2.1, get-intrinsic@npm:^1.2.2": + version: 1.2.2 + resolution: "get-intrinsic@npm:1.2.2" dependencies: - function-bind: ^1.1.1 - has: ^1.0.3 + function-bind: ^1.1.2 has-proto: ^1.0.1 has-symbols: ^1.0.3 - checksum: 5b61d88552c24b0cf6fa2d1b3bc5459d7306f699de060d76442cce49a4721f52b8c560a33ab392cf5575b7810277d54ded9d4d39a1ea61855619ebc005aa7e5f + hasown: ^2.0.0 + checksum: 447ff0724df26829908dc033b62732359596fcf66027bc131ab37984afb33842d9cd458fd6cecadfe7eac22fd8a54b349799ed334cf2726025c921c7250e7417 languageName: node linkType: hard @@ -7468,10 +7222,10 @@ __metadata: languageName: node linkType: hard -"get-iterator@npm:^2.0.0": - version: 2.0.0 - resolution: "get-iterator@npm:2.0.0" - checksum: 4ed81e7d100dd0a8dab11bf7dc7f88d8895a03057fa4a56d24190bdfe490fa4da3005d5b88fc6336429b33b423586f40dbcc40ce723f5dea55593cae0e2937de +"get-iterator@npm:^2.0.0, get-iterator@npm:^2.0.1": + version: 2.0.1 + resolution: "get-iterator@npm:2.0.1" + checksum: 353baac51f5e335c19cb734cbf0401d7c47deeac9d375e2939fed646fe52db2912d61ed2a60112050cf4687080817d159ec938803e48e03cd602edd489a116f2 languageName: node linkType: hard @@ -7516,23 +7270,23 @@ __metadata: linkType: hard "get-tsconfig@npm:^4.5.0": - version: 4.6.2 - resolution: "get-tsconfig@npm:4.6.2" + version: 4.7.2 + resolution: "get-tsconfig@npm:4.7.2" dependencies: resolve-pkg-maps: ^1.0.0 - checksum: e791e671a9b55e91efea3ca819ecd7a25beae679e31c83234bf3dd62ddd93df070c1b95ae7e29d206358ebb6408f6f79ac6d83a32a3bbd6a6d217babe23de077 + checksum: 172358903250eff0103943f816e8a4e51d29b8e5449058bdf7266714a908a48239f6884308bd3a6ff28b09f692b9533dbebfd183ab63e4e14f073cda91f1bca9 languageName: node linkType: hard "get-uri@npm:^6.0.1": - version: 6.0.1 - resolution: "get-uri@npm:6.0.1" + version: 6.0.2 + resolution: "get-uri@npm:6.0.2" dependencies: basic-ftp: ^5.0.2 - data-uri-to-buffer: ^5.0.1 + data-uri-to-buffer: ^6.0.0 debug: ^4.3.4 fs-extra: ^8.1.0 - checksum: a8aec70e1c67386fbe67f66e344ecd671a19f4cfc8e0f0e14d070563af5123d540e77fbceb6e26566f29846fac864d2862699ab134d307f85c85e7d72ce23d14 + checksum: 762de3b0e3d4e7afc966e4ce93be587d70c270590da9b4c8fbff888362656c055838d926903d1774cbfeed4d392b4d6def4b2c06d48c050580070426a3a8629b languageName: node linkType: hard @@ -7561,22 +7315,7 @@ __metadata: languageName: node linkType: hard -"glob@npm:^10.2.2": - version: 10.3.3 - resolution: "glob@npm:10.3.3" - dependencies: - foreground-child: ^3.1.0 - jackspeak: ^2.0.3 - minimatch: ^9.0.1 - minipass: ^5.0.0 || ^6.0.2 || ^7.0.0 - path-scurry: ^1.10.1 - bin: - glob: dist/cjs/src/bin.js - checksum: 29190d3291f422da0cb40b77a72fc8d2c51a36524e99b8bf412548b7676a6627489528b57250429612b6eec2e6fe7826d328451d3e694a9d15e575389308ec53 - languageName: node - linkType: hard - -"glob@npm:^10.3.10": +"glob@npm:^10.2.2, glob@npm:^10.3.10": version: 10.3.10 resolution: "glob@npm:10.3.10" dependencies: @@ -7613,11 +7352,11 @@ __metadata: linkType: hard "globals@npm:^13.19.0": - version: 13.20.0 - resolution: "globals@npm:13.20.0" + version: 13.24.0 + resolution: "globals@npm:13.24.0" dependencies: type-fest: ^0.20.2 - checksum: ad1ecf914bd051325faad281d02ea2c0b1df5d01bd94d368dcc5513340eac41d14b3c61af325768e3c7f8d44576e72780ec0b6f2d366121f8eec6e03c3a3b97a + checksum: 56066ef058f6867c04ff203b8a44c15b038346a62efbc3060052a1016be9f56f4cf0b2cd45b74b22b81e521a889fc7786c73691b0549c2f3a6e825b3d394f43c languageName: node linkType: hard @@ -7644,19 +7383,6 @@ __metadata: languageName: node linkType: hard -"globby@npm:^13.1.3": - version: 13.2.2 - resolution: "globby@npm:13.2.2" - dependencies: - dir-glob: ^3.0.1 - fast-glob: ^3.3.0 - ignore: ^5.2.4 - merge2: ^1.4.1 - slash: ^4.0.0 - checksum: f3d84ced58a901b4fcc29c846983108c426631fe47e94872868b65565495f7bee7b3defd68923bd480582771fd4bbe819217803a164a618ad76f1d22f666f41e - languageName: node - linkType: hard - "gonzales-pe@npm:^4.2.3, gonzales-pe@npm:^4.3.0": version: 4.3.0 resolution: "gonzales-pe@npm:4.3.0" @@ -7726,12 +7452,12 @@ __metadata: languageName: node linkType: hard -"has-property-descriptors@npm:^1.0.0": - version: 1.0.0 - resolution: "has-property-descriptors@npm:1.0.0" +"has-property-descriptors@npm:^1.0.0, has-property-descriptors@npm:^1.0.1": + version: 1.0.1 + resolution: "has-property-descriptors@npm:1.0.1" dependencies: - get-intrinsic: ^1.1.1 - checksum: a6d3f0a266d0294d972e354782e872e2fe1b6495b321e6ef678c9b7a06a40408a6891817350c62e752adced73a94ac903c54734fee05bf65b1905ee1368194bb + get-intrinsic: ^1.2.2 + checksum: 2bcc6bf6ec6af375add4e4b4ef586e43674850a91ad4d46666d0b28ba8e1fd69e424c7677d24d60f69470ad0afaa2f3197f508b20b0bb7dd99a8ab77ffc4b7c4 languageName: node linkType: hard @@ -7758,22 +7484,6 @@ __metadata: languageName: node linkType: hard -"has-unicode@npm:^2.0.1": - version: 2.0.1 - resolution: "has-unicode@npm:2.0.1" - checksum: 1eab07a7436512db0be40a710b29b5dc21fa04880b7f63c9980b706683127e3c1b57cb80ea96d47991bdae2dfe479604f6a1ba410106ee1046a41d1bd0814400 - languageName: node - linkType: hard - -"has@npm:^1.0.3": - version: 1.0.3 - resolution: "has@npm:1.0.3" - dependencies: - function-bind: ^1.1.1 - checksum: b9ad53d53be4af90ce5d1c38331e712522417d017d5ef1ebd0507e07c2fbad8686fffb8e12ddecd4c39ca9b9b47431afbb975b8abf7f3c3b82c98e9aad052792 - languageName: node - linkType: hard - "hash-base@npm:^3.0.0": version: 3.1.0 resolution: "hash-base@npm:3.1.0" @@ -7898,17 +7608,6 @@ __metadata: languageName: node linkType: hard -"http-proxy-agent@npm:^5.0.0": - version: 5.0.0 - resolution: "http-proxy-agent@npm:5.0.0" - dependencies: - "@tootallnate/once": 2 - agent-base: 6 - debug: 4 - checksum: e2ee1ff1656a131953839b2a19cd1f3a52d97c25ba87bd2559af6ae87114abf60971e498021f9b73f9fd78aea8876d1fb0d4656aac8a03c6caa9fc175f22b786 - languageName: node - linkType: hard - "http-proxy-agent@npm:^7.0.0": version: 7.0.0 resolution: "http-proxy-agent@npm:7.0.0" @@ -7919,17 +7618,7 @@ __metadata: languageName: node linkType: hard -"https-proxy-agent@npm:^5.0.0": - version: 5.0.1 - resolution: "https-proxy-agent@npm:5.0.1" - dependencies: - agent-base: 6 - debug: 4 - checksum: 571fccdf38184f05943e12d37d6ce38197becdd69e58d03f43637f7fa1269cf303a7d228aa27e5b27bbd3af8f09fd938e1c91dcfefff2df7ba77c20ed8dfc765 - languageName: node - linkType: hard - -"https-proxy-agent@npm:^7.0.2": +"https-proxy-agent@npm:^7.0.1, https-proxy-agent@npm:^7.0.2": version: 7.0.2 resolution: "https-proxy-agent@npm:7.0.2" dependencies: @@ -7953,15 +7642,6 @@ __metadata: languageName: node linkType: hard -"humanize-ms@npm:^1.2.1": - version: 1.2.1 - resolution: "humanize-ms@npm:1.2.1" - dependencies: - ms: ^2.0.0 - checksum: 9c7a74a2827f9294c009266c82031030eae811ca87b0da3dceb8d6071b9bde22c9f3daef0469c3c533cc67a97d8a167cd9fc0389350e5f415f61a79b171ded16 - languageName: node - linkType: hard - "hyperdyperid@npm:^1.2.0": version: 1.2.0 resolution: "hyperdyperid@npm:1.2.0" @@ -7995,9 +7675,9 @@ __metadata: linkType: hard "ignore@npm:^5.2.0, ignore@npm:^5.2.4": - version: 5.2.4 - resolution: "ignore@npm:5.2.4" - checksum: 3d4c309c6006e2621659311783eaea7ebcd41fe4ca1d78c91c473157ad6666a57a2df790fe0d07a12300d9aac2888204d7be8d59f9aaf665b1c7fcdb432517ef + version: 5.3.0 + resolution: "ignore@npm:5.3.0" + checksum: 2736da6621f14ced652785cb05d86301a66d70248597537176612bd0c8630893564bd5f6421f8806b09e8472e75c591ef01672ab8059c07c6eb2c09cefe04bf9 languageName: node linkType: hard @@ -8052,9 +7732,9 @@ __metadata: linkType: hard "inflation@npm:^2.0.0": - version: 2.0.0 - resolution: "inflation@npm:2.0.0" - checksum: a0494871b12275afdef9e2710ee1af1e0fc642b04613a9be69c05ef8b5e9627f3bd7d358a937fa47aa20235ee7313a4f30255048533add0ad4918beb918a586e + version: 2.1.0 + resolution: "inflation@npm:2.1.0" + checksum: 80c1b5d9ec408105a85f0623c824d668ddf0cadafd8d9716c0737990e5a712ae5f7d6bb0ff216b6648eccb9c6ac69fe06c0d8c58456d168db5bf550c89dd74ed languageName: node linkType: hard @@ -8090,31 +7770,30 @@ __metadata: linkType: hard "interface-datastore@npm:^8.2.0": - version: 8.2.3 - resolution: "interface-datastore@npm:8.2.3" + version: 8.2.10 + resolution: "interface-datastore@npm:8.2.10" dependencies: interface-store: ^5.0.0 - nanoid: ^4.0.0 - uint8arrays: ^4.0.2 - checksum: 01520fb965281f1352b93244a449d254e2fa9f9e4fb7f7f911b459354efcaa000d6ac311efda632d366281dbad41b3a61f321ce6a6241ab0f31283a81e4084cd + uint8arrays: ^5.0.0 + checksum: 16e12820b8423e457a6255377f373ea20132d7080809756b350e8914fde9abb73c4dd226cb3a9dda31bff790c181a02382bdde0482f9385ea4d2168c35ae93d8 languageName: node linkType: hard "interface-store@npm:^5.0.0": - version: 5.1.2 - resolution: "interface-store@npm:5.1.2" - checksum: 1423c5ffb5e6f9e65a86661cda90fb35d8dda433255bce9f57417650c98df83c84c4dc65741635c3efb16965b0f409f979a524f12b22803db0ab53ecfdf7b7db + version: 5.1.7 + resolution: "interface-store@npm:5.1.7" + checksum: aeddcdbfe8601f127c485ede6c6323bf2b302c713a1661036403dfd01b6b91e19a314ecf002477f108cb9c94d67781f88d386d3688f1e9c7cd1464a756ac28fa languageName: node linkType: hard "internal-slot@npm:^1.0.5": - version: 1.0.5 - resolution: "internal-slot@npm:1.0.5" + version: 1.0.6 + resolution: "internal-slot@npm:1.0.6" dependencies: - get-intrinsic: ^1.2.0 - has: ^1.0.3 + get-intrinsic: ^1.2.2 + hasown: ^2.0.0 side-channel: ^1.0.4 - checksum: 97e84046bf9e7574d0956bd98d7162313ce7057883b6db6c5c7b5e5f05688864b0978ba07610c726d15d66544ffe4b1050107d93f8a39ebc59b15d8b429b497a + checksum: 7872454888047553ce97a3fa1da7cc054a28ec5400a9c2e9f4dbe4fe7c1d041cb8e8301467614b80d4246d50377aad2fb58860b294ed74d6700cc346b6f89549 languageName: node linkType: hard @@ -8221,16 +7900,7 @@ __metadata: languageName: node linkType: hard -"is-core-module@npm:^2.1.0, is-core-module@npm:^2.11.0, is-core-module@npm:^2.12.0, is-core-module@npm:^2.12.1": - version: 2.12.1 - resolution: "is-core-module@npm:2.12.1" - dependencies: - has: ^1.0.3 - checksum: f04ea30533b5e62764e7b2e049d3157dc0abd95ef44275b32489ea2081176ac9746ffb1cdb107445cf1ff0e0dfcad522726ca27c27ece64dadf3795428b8e468 - languageName: node - linkType: hard - -"is-core-module@npm:^2.13.0": +"is-core-module@npm:^2.1.0, is-core-module@npm:^2.11.0, is-core-module@npm:^2.13.0, is-core-module@npm:^2.13.1": version: 2.13.1 resolution: "is-core-module@npm:2.13.1" dependencies: @@ -8248,24 +7918,6 @@ __metadata: languageName: node linkType: hard -"is-docker@npm:^2.0.0": - version: 2.2.1 - resolution: "is-docker@npm:2.2.1" - bin: - is-docker: cli.js - checksum: 3fef7ddbf0be25958e8991ad941901bf5922ab2753c46980b60b05c1bf9c9c2402d35e6dc32e4380b980ef5e1970a5d9d5e5aa2e02d77727c3b6b5e918474c56 - languageName: node - linkType: hard - -"is-docker@npm:^3.0.0": - version: 3.0.0 - resolution: "is-docker@npm:3.0.0" - bin: - is-docker: cli.js - checksum: b698118f04feb7eaf3338922bd79cba064ea54a1c3db6ec8c0c8d8ee7613e7e5854d802d3ef646812a8a3ace81182a085dfa0a71cc68b06f3fa794b9783b3c90 - languageName: node - linkType: hard - "is-electron@npm:^2.2.0": version: 2.2.2 resolution: "is-electron@npm:2.2.2" @@ -8312,17 +7964,6 @@ __metadata: languageName: node linkType: hard -"is-inside-container@npm:^1.0.0": - version: 1.0.0 - resolution: "is-inside-container@npm:1.0.0" - dependencies: - is-docker: ^3.0.0 - bin: - is-inside-container: cli.js - checksum: c50b75a2ab66ab3e8b92b3bc534e1ea72ca25766832c0623ac22d134116a98bcf012197d1caabe1d1c4bd5f84363d4aa5c36bb4b585fbcaf57be172cd10a1a03 - languageName: node - linkType: hard - "is-interactive@npm:^1.0.0": version: 1.0.0 resolution: "is-interactive@npm:1.0.0" @@ -8338,9 +7979,9 @@ __metadata: linkType: hard "is-loopback-addr@npm:^2.0.1": - version: 2.0.1 - resolution: "is-loopback-addr@npm:2.0.1" - checksum: 77030f7a12875c124b0bb83f172c1fa29fcb14574d15a1d4ed13be71c441b9cdc393370967b2e4bf808c564072933467d48e9ea006a3fd2cb3c7437c6d913108 + version: 2.0.2 + resolution: "is-loopback-addr@npm:2.0.2" + checksum: c7818bd2815aa7aad1efd410d38f58647759e206b43fcf5b3a35db866ceff9af0b0145f1dcba472267c6e1b965883d1ebf321f3eb18c6a8c1c609c4f91092f2f languageName: node linkType: hard @@ -8351,6 +7992,13 @@ __metadata: languageName: node linkType: hard +"is-network-error@npm:^1.0.0": + version: 1.0.1 + resolution: "is-network-error@npm:1.0.1" + checksum: 165d61500c4186c62db5a3a693d6bfa14ca40fe9b471ef4cd4f27b20ef6760880faf5386dc01ca9867531631782941fedaa94521d09959edf71f046e393c7b91 + languageName: node + linkType: hard + "is-number-object@npm:^1.0.4": version: 1.0.7 resolution: "is-number-object@npm:1.0.7" @@ -8469,7 +8117,7 @@ __metadata: languageName: node linkType: hard -"is-typed-array@npm:^1.1.10, is-typed-array@npm:^1.1.3, is-typed-array@npm:^1.1.9": +"is-typed-array@npm:^1.1.10, is-typed-array@npm:^1.1.12, is-typed-array@npm:^1.1.3, is-typed-array@npm:^1.1.9": version: 1.1.12 resolution: "is-typed-array@npm:1.1.12" dependencies: @@ -8508,15 +8156,6 @@ __metadata: languageName: node linkType: hard -"is-wsl@npm:^2.2.0": - version: 2.2.0 - resolution: "is-wsl@npm:2.2.0" - dependencies: - is-docker: ^2.0.0 - checksum: 20849846ae414997d290b75e16868e5261e86ff5047f104027026fd61d8b5a9b0b3ade16239f35e1a067b3c7cc02f70183cb661010ed16f4b6c7c93dad1b19d8 - languageName: node - linkType: hard - "isarray@npm:^2.0.5": version: 2.0.5 resolution: "isarray@npm:2.0.5" @@ -8538,6 +8177,13 @@ __metadata: languageName: node linkType: hard +"isexe@npm:^3.1.1": + version: 3.1.1 + resolution: "isexe@npm:3.1.1" + checksum: 7fe1931ee4e88eb5aa524cd3ceb8c882537bc3a81b02e438b240e47012eef49c86904d0f0e593ea7c3a9996d18d0f1f3be8d3eaa92333977b0c3a9d353d5563e + languageName: node + linkType: hard + "isobject@npm:^3.0.1": version: 3.0.1 resolution: "isobject@npm:3.0.1" @@ -8554,14 +8200,23 @@ __metadata: languageName: node linkType: hard +"isows@npm:1.0.3": + version: 1.0.3 + resolution: "isows@npm:1.0.3" + peerDependencies: + ws: "*" + checksum: 9cacd5cf59f67deb51e825580cd445ab1725ecb05a67c704050383fb772856f3cd5e7da8ad08f5a3bd2823680d77d099459d0c6a7037972a74d6429af61af440 + languageName: node + linkType: hard + "istanbul-lib-coverage@npm:^3.0.0, istanbul-lib-coverage@npm:^3.2.0": - version: 3.2.0 - resolution: "istanbul-lib-coverage@npm:3.2.0" - checksum: a2a545033b9d56da04a8571ed05c8120bf10e9bce01cf8633a3a2b0d1d83dff4ac4fe78d6d5673c27fc29b7f21a41d75f83a36be09f82a61c367b56aa73c1ff9 + version: 3.2.2 + resolution: "istanbul-lib-coverage@npm:3.2.2" + checksum: 2367407a8d13982d8f7a859a35e7f8dd5d8f75aae4bb5484ede3a9ea1b426dc245aff28b976a2af48ee759fdd9be374ce2bd2669b644f31e76c5f46a2e29a831 languageName: node linkType: hard -"istanbul-lib-instrument@npm:^5.0.4, istanbul-lib-instrument@npm:^5.1.0": +"istanbul-lib-instrument@npm:^5.0.4": version: 5.2.1 resolution: "istanbul-lib-instrument@npm:5.2.1" dependencies: @@ -8574,6 +8229,19 @@ __metadata: languageName: node linkType: hard +"istanbul-lib-instrument@npm:^6.0.0": + version: 6.0.1 + resolution: "istanbul-lib-instrument@npm:6.0.1" + dependencies: + "@babel/core": ^7.12.3 + "@babel/parser": ^7.14.7 + "@istanbuljs/schema": ^0.1.2 + istanbul-lib-coverage: ^3.2.0 + semver: ^7.5.4 + checksum: fb23472e739cfc9b027cefcd7d551d5e7ca7ff2817ae5150fab99fe42786a7f7b56a29a2aa8309c37092e18297b8003f9c274f50ca4360949094d17fbac81472 + languageName: node + linkType: hard + "istanbul-lib-report@npm:^3.0.0": version: 3.0.1 resolution: "istanbul-lib-report@npm:3.0.1" @@ -8607,69 +8275,63 @@ __metadata: linkType: hard "it-all@npm:^3.0.0, it-all@npm:^3.0.1, it-all@npm:^3.0.2": - version: 3.0.2 - resolution: "it-all@npm:3.0.2" - checksum: 37ce90f8ee6a579aa5dd710899912f21d0b2083dad8686d8ca648b2d0c2ffdb4856770e3d004c9e9e4d0a17a9c57221ec501ada98571690ac508021e6227cdab + version: 3.0.4 + resolution: "it-all@npm:3.0.4" + checksum: fb7259660b6555ae268ffde6f0245026e9d4e8afccf9c43a088bb0ff0483aaca95954b6074c1c96d46a57b572bce35fa1bb8542934ce9aee477e1dba46293891 languageName: node linkType: hard "it-batched-bytes@npm:^2.0.2": - version: 2.0.3 - resolution: "it-batched-bytes@npm:2.0.3" + version: 2.0.5 + resolution: "it-batched-bytes@npm:2.0.5" dependencies: p-defer: ^4.0.0 uint8arraylist: ^2.4.1 - checksum: a5bd5c1f2969f1b93dbaa1fbcfe756dee270ffd262485ac1175066a235059b39c09dc9da14f93281bf6ac33381d81483d755ccfc386869ea37441555d98ba30c + checksum: c127881de01aae0d1625b2179ba66f7a084466adef0d5b20ab7129040651545e4aacbff01cf372e5118a2836685463732fa52714a16b16856657f8e05861b899 languageName: node linkType: hard "it-byte-stream@npm:^1.0.0": - version: 1.0.1 - resolution: "it-byte-stream@npm:1.0.1" + version: 1.0.7 + resolution: "it-byte-stream@npm:1.0.7" dependencies: - it-pushable: ^3.2.0 it-stream-types: ^2.0.1 + p-defer: ^4.0.0 + race-signal: ^1.0.1 uint8arraylist: ^2.4.1 - checksum: 5ebd79cc48a925937337ba8b9f2ced330f13692e4800c4053f099f190d1d62def8f3071f44fb5f78fd90113e58c9a9612d1e6dd3f2ce153d5c8a87e964bc9a5c - languageName: node - linkType: hard - -"it-drain@npm:^3.0.1": - version: 3.0.2 - resolution: "it-drain@npm:3.0.2" - checksum: 4ad8d8a829c2f35116f07c94bf80a82381b00ee2d431d3d0556c4848013d79fe16df0d71ebf39fc51bc5b75cef296d9a5d4c8101ef1828e1ef31b67087d2e414 + checksum: 9cd63aa3ab2e7ebe1a3cac545f546d9362577901922b7a75803e36caacb0ef46b0cf4bc5334a0acdc548ba976849065527b9467a3c048af64233e70891537aad languageName: node linkType: hard -"it-drain@npm:^3.0.2": - version: 3.0.3 - resolution: "it-drain@npm:3.0.3" - checksum: 5f918245b6b3de4c0371cad30bdaa2f85f6786305673e325c9c5fe0358f9ec93e1b42ab46790688b0b4cbda71e450f60f3f57fbb8f627a6e96b3ff0d9d35ec2c +"it-drain@npm:^3.0.1, it-drain@npm:^3.0.2": + version: 3.0.5 + resolution: "it-drain@npm:3.0.5" + checksum: 6ab86dc487737a0a87556fab52dadd00f376881b633bd00b8c461f1e8eace47c426e8065700946eb066072e33fc7df7f0e9fa12426bd1d8cac914d52c8f44f43 languageName: node linkType: hard "it-filter@npm:^3.0.0, it-filter@npm:^3.0.1": - version: 3.0.2 - resolution: "it-filter@npm:3.0.2" + version: 3.0.4 + resolution: "it-filter@npm:3.0.4" dependencies: it-peekable: ^3.0.0 - checksum: 6eea68bcd7ee75ac52dc477149a8e822da8d7626631256179601294412f22635929ee5c0b1566413c721d078ef4bfb8c94b0e2f0d093259909582c6d1d31f1f7 + checksum: 8d57903bd99fa1b18ff2c3d0fb7ba0d041a229a33b77ff5ff86ca591e5e0ed0a61b14e937c250754ff1085d8e1c4f88996a4feff76bfc3f73e5fe54726c74dd9 languageName: node linkType: hard "it-first@npm:^3.0.1": - version: 3.0.2 - resolution: "it-first@npm:3.0.2" - checksum: cb0131bed94e13e13cc02b67fb864c0995dc8deaf8e79a3b4685165f48b331f439caf21d6576e18027308ac03556e9108b517b18c34c0e11ede8a742806dccd8 + version: 3.0.4 + resolution: "it-first@npm:3.0.4" + checksum: 428cf4b7baaf04dcb0c157cbd6332c2bab9708eeae6df752533d8fd8e21f7c321bfa8a57d35982115f57760baf526a9bf210b7d982d793e8340e22db2aa68fc6 languageName: node linkType: hard "it-foreach@npm:^2.0.3": - version: 2.0.4 - resolution: "it-foreach@npm:2.0.4" + version: 2.0.6 + resolution: "it-foreach@npm:2.0.6" dependencies: it-peekable: ^3.0.0 - checksum: 7133068fb282ac74af2b7482466a72198e1c52393e09276c96e3c40ef5c8cb3f3b76f56a4a7b6dd2d40a7c20c1679bc75192abf6c90579aa803bb4ed5665930d + checksum: 95f66b141ced66ca4429711a5d4f36b605005e5607d5e17c2a0357f10ed1b6750e3d49683e029190c1d4ff7a89378fbf9d17b26ded31ddd55741b2a1ddc3d3f2 languageName: node linkType: hard @@ -8687,62 +8349,53 @@ __metadata: linkType: hard "it-length-prefixed-stream@npm:^1.0.0": - version: 1.0.2 - resolution: "it-length-prefixed-stream@npm:1.0.2" + version: 1.1.6 + resolution: "it-length-prefixed-stream@npm:1.1.6" dependencies: it-byte-stream: ^1.0.0 - it-length-prefixed: ^9.0.1 it-stream-types: ^2.0.1 uint8-varint: ^2.0.1 uint8arraylist: ^2.4.1 - checksum: 5fcb04352601bc43df96475d1d2c6b9bf40c87a68bc0dece26d8a5280df83f383aa468f154640a28112af9c1f85f24972837cfe538a3fe117da7dc68ac4b57db + checksum: 9bba9b781934eb85f68187f4c9128c158a856d0e7d3770e13201cee84829d9d482fb60bcf5eb9ca3ed85f3671a1a27df123e3869c8461cac6929a3a2f349b792 languageName: node linkType: hard "it-length-prefixed@npm:^9.0.1": - version: 9.0.1 - resolution: "it-length-prefixed@npm:9.0.1" + version: 9.0.4 + resolution: "it-length-prefixed@npm:9.0.4" dependencies: err-code: ^3.0.1 + it-reader: ^6.0.1 it-stream-types: ^2.0.1 - uint8-varint: ^1.0.1 + uint8-varint: ^2.0.1 uint8arraylist: ^2.0.0 - uint8arrays: ^4.0.2 - checksum: a431aedef3450287c9c93cbc4017aebfaf2443cae43abbbd65384a9981f58373ed6cbb505819e60e5e7ac414eefd9f3e9aa1b10cca3726d92c3f0d4671c25b40 + uint8arrays: ^5.0.1 + checksum: 18e7c4a96299c14ec654b2c41784d4e9889d9239ba858acd0a97b6ea0261b504b306c0850b1c28dd9e8678de03ff95f1e81ba03aed6ebf92f6d01b6e9cee3bab languageName: node linkType: hard "it-length@npm:^3.0.1": - version: 3.0.2 - resolution: "it-length@npm:3.0.2" - checksum: 688565353598557dc003324fc497896f5d24795f83011d03cc3625ced09207d9e66eda69e055080d1cd3c534673f65c76e5a5c48553b53e66b58a05995b49e81 - languageName: node - linkType: hard - -"it-map@npm:^3.0.1": - version: 3.0.3 - resolution: "it-map@npm:3.0.3" - dependencies: - it-peekable: ^3.0.0 - checksum: bf48828a40c19d98b49f42730fa3d2821b52056dd755dcdd9b4a1c1077fe8fcd79bf8ae810429e4a82d6cac5b2439e5f3aae8a3c7353c9b5c51ce14d038d9858 + version: 3.0.4 + resolution: "it-length@npm:3.0.4" + checksum: 881208cbcad1e3a396b27b35d73acbac9c27eb8b9fa43b1ed1bb4ca1aba489040981e0ea2b3db6fae90d2d9a1e4c610013abef4030ecd80eca64689f07df8dc9 languageName: node linkType: hard -"it-map@npm:^3.0.3": - version: 3.0.4 - resolution: "it-map@npm:3.0.4" +"it-map@npm:^3.0.1, it-map@npm:^3.0.3": + version: 3.0.5 + resolution: "it-map@npm:3.0.5" dependencies: it-peekable: ^3.0.0 - checksum: a5c0cc6ab36b2c2d2aa17189295891ec3b71244e251f99ff79fb5231891de97b79a9851f55f4e30b1cbc06ed1a13e25b1e4c4e4ea326199c86e382acc76de090 + checksum: bdaa2f1662325457a4eba487dfb04ca8aee0b1d91356b285bf6133aaeda67fba5b7d5c6644838ea8a025e4bd0e8a46910dd7b203f75940ed7ce0d8f3d159bbf3 languageName: node linkType: hard -"it-merge@npm:^3.0.0": - version: 3.0.1 - resolution: "it-merge@npm:3.0.1" +"it-merge@npm:^3.0.0, it-merge@npm:^3.0.1": + version: 3.0.3 + resolution: "it-merge@npm:3.0.3" dependencies: - it-pushable: ^3.1.0 - checksum: 8401b0394868cd02ccd988687a641b2a250668de1a818d7a867650621fb77e6dcd4b041972f738293ab65880b0a3a16b45d94c8b7b42d6b434430c817eee7565 + it-pushable: ^3.2.0 + checksum: 031c72302b35db8769c07646c561980c8d97097ce96aa869ebd0cf7b506ea075299b497a177a04bd5eb26398379b3e0b8f4c59a9a1ad0b1e7068d1a921cabf7b languageName: node linkType: hard @@ -8757,18 +8410,18 @@ __metadata: linkType: hard "it-parallel@npm:^3.0.0": - version: 3.0.3 - resolution: "it-parallel@npm:3.0.3" + version: 3.0.6 + resolution: "it-parallel@npm:3.0.6" dependencies: p-defer: ^4.0.0 - checksum: 2326fe2d15134d59dede916b8b72a83092f79b356537921ede24f0b0d95616f2916d8bf1ebd1af6db5326303af2ac654c64dc144df64b8f48b57a95d47c1be5f + checksum: ca9cc7faea9dee197dd5e683743542da21369c5a3d6991278b0221493d0e801abd7d750ed2860a97e6eeffae6b7c8af9fdd3e61285895317599d8608ccd7576d languageName: node linkType: hard "it-peekable@npm:^3.0.0": - version: 3.0.1 - resolution: "it-peekable@npm:3.0.1" - checksum: e327caf50bc205672b4133040e4c580cb39f6a8691565f420941ac5b7877ad1ca47c53ecf1ea62e4716abe36786b1cf07f2aea663eda6779fbf0d7a7cb0fbfdc + version: 3.0.3 + resolution: "it-peekable@npm:3.0.3" + checksum: 9603045130673b26a572cb2a9bfb7cbf9907fd759aa9dbfb1113b38c07c7b750b75a8dbec317b0cde6e47b6f3be2fddd9785fc7e38f1147ea3ded7eabd590c7a languageName: node linkType: hard @@ -8784,23 +8437,23 @@ __metadata: linkType: hard "it-protobuf-stream@npm:^1.0.0": - version: 1.0.2 - resolution: "it-protobuf-stream@npm:1.0.2" + version: 1.1.2 + resolution: "it-protobuf-stream@npm:1.1.2" dependencies: it-length-prefixed-stream: ^1.0.0 it-stream-types: ^2.0.1 protons-runtime: ^5.0.0 uint8arraylist: ^2.4.1 - checksum: a5d724c213f0e9e4f4f42145c43cf0bfe0036dc8f1cae094efd959710046aae399e77a2dd2e6c270a37103ae630386666676dddd6c8cf2138803402eba64dc1a + checksum: d10601aa530ee53da994377b4704e4f28a45ff26a4da1d64c1beccfcbdc1802da5cf480b692ff692a6557bd2dd0823c4e6992fc525122ab5da8d0ba67f003198 languageName: node linkType: hard -"it-pushable@npm:^3.0.0, it-pushable@npm:^3.1.0, it-pushable@npm:^3.1.2, it-pushable@npm:^3.1.3, it-pushable@npm:^3.2.0": - version: 3.2.1 - resolution: "it-pushable@npm:3.2.1" +"it-pushable@npm:^3.0.0, it-pushable@npm:^3.1.0, it-pushable@npm:^3.1.2, it-pushable@npm:^3.1.3, it-pushable@npm:^3.2.0, it-pushable@npm:^3.2.1, it-pushable@npm:^3.2.3": + version: 3.2.3 + resolution: "it-pushable@npm:3.2.3" dependencies: p-defer: ^4.0.0 - checksum: a23eaac8d1ec86785d0f3e71c67f1544cb1b99aff88c70d3043f3b87a9966c154ca387de76739f91dd744cd7815b40d20e9711a54079cc7ddaf36be54fd10c41 + checksum: 8b1d1ceb2a42b31b55119f9721b1f4568c498627470bac18479e6f8db3791fe1185653480cd1c319462bae3d64091bd9ca9e6e90e217e38a5ab7f078559ccca4 languageName: node linkType: hard @@ -8815,11 +8468,11 @@ __metadata: linkType: hard "it-sort@npm:^3.0.1": - version: 3.0.2 - resolution: "it-sort@npm:3.0.2" + version: 3.0.4 + resolution: "it-sort@npm:3.0.4" dependencies: it-all: ^3.0.0 - checksum: d45bb64413d949e1c7c459ad1b4418f14460959d5b73c830368ac18722277c8dcbc606740343ca87441c7c136838b7335a16f9174f0dd8ef03cdc80f8bcfdfbb + checksum: de4f1832c6d12914d51109ca3f8ccebba60fdb050d0af2b3d9b8bcd14cb3d320ba1a01e3ef59de2d3691886c0a903e1c4e46ad354796159d4b0d3d7013bc180c languageName: node linkType: hard @@ -8831,22 +8484,9 @@ __metadata: linkType: hard "it-take@npm:^3.0.1": - version: 3.0.2 - resolution: "it-take@npm:3.0.2" - checksum: 50a5449efa0832cd55878b7492f536b558861181786c7a95fb59d93c761646bc4bbc9dd46fd28a5b574b7349fa4caf028bbf66e3a86630473cc5907b811952a3 - languageName: node - linkType: hard - -"jackspeak@npm:^2.0.3": - version: 2.2.2 - resolution: "jackspeak@npm:2.2.2" - dependencies: - "@isaacs/cliui": ^8.0.2 - "@pkgjs/parseargs": ^0.11.0 - dependenciesMeta: - "@pkgjs/parseargs": - optional: true - checksum: 7b1468dd910afc00642db87448f24b062346570b8b47531409aa9012bcb95fdf7ec2b1c48edbb8b57a938c08391f8cc01b5034fc335aa3a2e74dbcc0ee5c555a + version: 3.0.4 + resolution: "it-take@npm:3.0.4" + checksum: 69dedde350817cba8de80e0432c9b81c35ff2b91f9c80582e657e382ec8c38af003f575353ae22605c963c28605a48cb994c7dba93fedac732db35ee86d7e516 languageName: node linkType: hard @@ -8870,59 +8510,59 @@ __metadata: languageName: node linkType: hard -"jest-changed-files@npm:^29.5.0": - version: 29.5.0 - resolution: "jest-changed-files@npm:29.5.0" +"jest-changed-files@npm:^29.7.0": + version: 29.7.0 + resolution: "jest-changed-files@npm:29.7.0" dependencies: execa: ^5.0.0 + jest-util: ^29.7.0 p-limit: ^3.1.0 - checksum: a67a7cb3c11f8f92bd1b7c79e84f724cbd11a9ad51f3cdadafe3ce7ee3c79ee50dbea128f920f5fddc807e9e4e83f5462143094391feedd959a77dd20ab96cf3 + checksum: 963e203893c396c5dfc75e00a49426688efea7361b0f0e040035809cecd2d46b3c01c02be2d9e8d38b1138357d2de7719ea5b5be21f66c10f2e9685a5a73bb99 languageName: node linkType: hard -"jest-circus@npm:^29.6.2": - version: 29.6.2 - resolution: "jest-circus@npm:29.6.2" +"jest-circus@npm:^29.7.0": + version: 29.7.0 + resolution: "jest-circus@npm:29.7.0" dependencies: - "@jest/environment": ^29.6.2 - "@jest/expect": ^29.6.2 - "@jest/test-result": ^29.6.2 - "@jest/types": ^29.6.1 + "@jest/environment": ^29.7.0 + "@jest/expect": ^29.7.0 + "@jest/test-result": ^29.7.0 + "@jest/types": ^29.6.3 "@types/node": "*" chalk: ^4.0.0 co: ^4.6.0 dedent: ^1.0.0 is-generator-fn: ^2.0.0 - jest-each: ^29.6.2 - jest-matcher-utils: ^29.6.2 - jest-message-util: ^29.6.2 - jest-runtime: ^29.6.2 - jest-snapshot: ^29.6.2 - jest-util: ^29.6.2 + jest-each: ^29.7.0 + jest-matcher-utils: ^29.7.0 + jest-message-util: ^29.7.0 + jest-runtime: ^29.7.0 + jest-snapshot: ^29.7.0 + jest-util: ^29.7.0 p-limit: ^3.1.0 - pretty-format: ^29.6.2 + pretty-format: ^29.7.0 pure-rand: ^6.0.0 slash: ^3.0.0 stack-utils: ^2.0.3 - checksum: 4f5a96a68c3c808c3d5a9279a2f39a2937386e2cebba5096971f267d79562ce2133a13bc05356a39f8f1ba68fcfe1eb39c4572b3fb0f91affbd932950e89c1e3 + checksum: 349437148924a5a109c9b8aad6d393a9591b4dac1918fc97d81b7fc515bc905af9918495055071404af1fab4e48e4b04ac3593477b1d5dcf48c4e71b527c70a7 languageName: node linkType: hard -"jest-cli@npm:^29.6.2": - version: 29.6.2 - resolution: "jest-cli@npm:29.6.2" +"jest-cli@npm:^29.7.0": + version: 29.7.0 + resolution: "jest-cli@npm:29.7.0" dependencies: - "@jest/core": ^29.6.2 - "@jest/test-result": ^29.6.2 - "@jest/types": ^29.6.1 + "@jest/core": ^29.7.0 + "@jest/test-result": ^29.7.0 + "@jest/types": ^29.6.3 chalk: ^4.0.0 + create-jest: ^29.7.0 exit: ^0.1.2 - graceful-fs: ^4.2.9 import-local: ^3.0.2 - jest-config: ^29.6.2 - jest-util: ^29.6.2 - jest-validate: ^29.6.2 - prompts: ^2.0.1 + jest-config: ^29.7.0 + jest-util: ^29.7.0 + jest-validate: ^29.7.0 yargs: ^17.3.1 peerDependencies: node-notifier: ^8.0.1 || ^9.0.0 || ^10.0.0 @@ -8931,34 +8571,34 @@ __metadata: optional: true bin: jest: bin/jest.js - checksum: 0b7b09ae4bd327caf1981eac5a14679ddda3c5c836c9f8ea0ecfe1e5e10e9a39a5ed783fa38d25383604c4d3405595e74b391d955e99aea7e51acb41a59ea108 + checksum: 664901277a3f5007ea4870632ed6e7889db9da35b2434e7cb488443e6bf5513889b344b7fddf15112135495b9875892b156faeb2d7391ddb9e2a849dcb7b6c36 languageName: node linkType: hard -"jest-config@npm:^29.6.2": - version: 29.6.2 - resolution: "jest-config@npm:29.6.2" +"jest-config@npm:^29.7.0": + version: 29.7.0 + resolution: "jest-config@npm:29.7.0" dependencies: "@babel/core": ^7.11.6 - "@jest/test-sequencer": ^29.6.2 - "@jest/types": ^29.6.1 - babel-jest: ^29.6.2 + "@jest/test-sequencer": ^29.7.0 + "@jest/types": ^29.6.3 + babel-jest: ^29.7.0 chalk: ^4.0.0 ci-info: ^3.2.0 deepmerge: ^4.2.2 glob: ^7.1.3 graceful-fs: ^4.2.9 - jest-circus: ^29.6.2 - jest-environment-node: ^29.6.2 - jest-get-type: ^29.4.3 - jest-regex-util: ^29.4.3 - jest-resolve: ^29.6.2 - jest-runner: ^29.6.2 - jest-util: ^29.6.2 - jest-validate: ^29.6.2 + jest-circus: ^29.7.0 + jest-environment-node: ^29.7.0 + jest-get-type: ^29.6.3 + jest-regex-util: ^29.6.3 + jest-resolve: ^29.7.0 + jest-runner: ^29.7.0 + jest-util: ^29.7.0 + jest-validate: ^29.7.0 micromatch: ^4.0.4 parse-json: ^5.2.0 - pretty-format: ^29.6.2 + pretty-format: ^29.7.0 slash: ^3.0.0 strip-json-comments: ^3.1.1 peerDependencies: @@ -8969,140 +8609,128 @@ __metadata: optional: true ts-node: optional: true - checksum: 3bd104a3ac2dd9d34986238142437606354169766dcf88359a7a12ac106d0dc17dcc6b627e4f20db97a58bac5b0502b5436c9cc4722b3629b2a114bba6da9128 + checksum: 4cabf8f894c180cac80b7df1038912a3fc88f96f2622de33832f4b3314f83e22b08fb751da570c0ab2b7988f21604bdabade95e3c0c041068ac578c085cf7dff languageName: node linkType: hard -"jest-diff@npm:^29.6.2": - version: 29.6.2 - resolution: "jest-diff@npm:29.6.2" +"jest-diff@npm:^29.7.0": + version: 29.7.0 + resolution: "jest-diff@npm:29.7.0" dependencies: chalk: ^4.0.0 - diff-sequences: ^29.4.3 - jest-get-type: ^29.4.3 - pretty-format: ^29.6.2 - checksum: 0effd66a0c23f8c139ebf7ca99ed30b479b86fff66f19ad4869f130aaf7ae6a24ca1533f697b7e4930cbe2ddffc85387723fcca673501c653fb77a38f538e959 + diff-sequences: ^29.6.3 + jest-get-type: ^29.6.3 + pretty-format: ^29.7.0 + checksum: 08e24a9dd43bfba1ef07a6374e5af138f53137b79ec3d5cc71a2303515335898888fa5409959172e1e05de966c9e714368d15e8994b0af7441f0721ee8e1bb77 languageName: node linkType: hard -"jest-docblock@npm:^29.4.3": - version: 29.4.3 - resolution: "jest-docblock@npm:29.4.3" +"jest-docblock@npm:^29.7.0": + version: 29.7.0 + resolution: "jest-docblock@npm:29.7.0" dependencies: detect-newline: ^3.0.0 - checksum: e0e9df1485bb8926e5b33478cdf84b3387d9caf3658e7dc1eaa6dc34cb93dea0d2d74797f6e940f0233a88f3dadd60957f2288eb8f95506361f85b84bf8661df + checksum: 66390c3e9451f8d96c5da62f577a1dad701180cfa9b071c5025acab2f94d7a3efc2515cfa1654ebe707213241541ce9c5530232cdc8017c91ed64eea1bd3b192 languageName: node linkType: hard -"jest-each@npm:^29.6.2": - version: 29.6.2 - resolution: "jest-each@npm:29.6.2" +"jest-each@npm:^29.7.0": + version: 29.7.0 + resolution: "jest-each@npm:29.7.0" dependencies: - "@jest/types": ^29.6.1 + "@jest/types": ^29.6.3 chalk: ^4.0.0 - jest-get-type: ^29.4.3 - jest-util: ^29.6.2 - pretty-format: ^29.6.2 - checksum: b64194f4ca27afc6070a42b7ecccbc68be0ded19a849f8cd8f91a2abb23fadae2d38d47559a315f4d1f576927761f3ea437a75ab6cf19206332abb8527d7c165 + jest-get-type: ^29.6.3 + jest-util: ^29.7.0 + pretty-format: ^29.7.0 + checksum: e88f99f0184000fc8813f2a0aa79e29deeb63700a3b9b7928b8a418d7d93cd24933608591dbbdea732b473eb2021c72991b5cc51a17966842841c6e28e6f691c languageName: node linkType: hard -"jest-environment-node@npm:^29.6.2": - version: 29.6.2 - resolution: "jest-environment-node@npm:29.6.2" +"jest-environment-node@npm:^29.7.0": + version: 29.7.0 + resolution: "jest-environment-node@npm:29.7.0" dependencies: - "@jest/environment": ^29.6.2 - "@jest/fake-timers": ^29.6.2 - "@jest/types": ^29.6.1 + "@jest/environment": ^29.7.0 + "@jest/fake-timers": ^29.7.0 + "@jest/types": ^29.6.3 "@types/node": "*" - jest-mock: ^29.6.2 - jest-util: ^29.6.2 - checksum: 0b754ac2d3bdb7ce5d6fc28595b9d1c64176f20506b6f773b18b0280ab0b396ed7d927c8519779d3c560fa2b13236ee7077092ccb19a13bea23d40dd30f06450 + jest-mock: ^29.7.0 + jest-util: ^29.7.0 + checksum: 501a9966292cbe0ca3f40057a37587cb6def25e1e0c5e39ac6c650fe78d3c70a2428304341d084ac0cced5041483acef41c477abac47e9a290d5545fd2f15646 languageName: node linkType: hard -"jest-get-type@npm:^29.4.3": - version: 29.4.3 - resolution: "jest-get-type@npm:29.4.3" - checksum: 6ac7f2dde1c65e292e4355b6c63b3a4897d7e92cb4c8afcf6d397f2682f8080e094c8b0b68205a74d269882ec06bf696a9de6cd3e1b7333531e5ed7b112605ce +"jest-get-type@npm:^29.6.3": + version: 29.6.3 + resolution: "jest-get-type@npm:29.6.3" + checksum: 88ac9102d4679d768accae29f1e75f592b760b44277df288ad76ce5bf038c3f5ce3719dea8aa0f035dac30e9eb034b848ce716b9183ad7cc222d029f03e92205 languageName: node linkType: hard -"jest-haste-map@npm:^29.6.2": - version: 29.6.2 - resolution: "jest-haste-map@npm:29.6.2" +"jest-haste-map@npm:^29.7.0": + version: 29.7.0 + resolution: "jest-haste-map@npm:29.7.0" dependencies: - "@jest/types": ^29.6.1 + "@jest/types": ^29.6.3 "@types/graceful-fs": ^4.1.3 "@types/node": "*" anymatch: ^3.0.3 fb-watchman: ^2.0.0 fsevents: ^2.3.2 graceful-fs: ^4.2.9 - jest-regex-util: ^29.4.3 - jest-util: ^29.6.2 - jest-worker: ^29.6.2 + jest-regex-util: ^29.6.3 + jest-util: ^29.7.0 + jest-worker: ^29.7.0 micromatch: ^4.0.4 walker: ^1.0.8 dependenciesMeta: fsevents: optional: true - checksum: 726233972030eb2e5bce6c9468e497310436b455c88b40e744bd053e20a6f3ff19aec340edcbd89537c629ed5cf8916506bc895d690cc39a0862c74dcd95b7b8 + checksum: c2c8f2d3e792a963940fbdfa563ce14ef9e14d4d86da645b96d3cd346b8d35c5ce0b992ee08593939b5f718cf0a1f5a90011a056548a1dbf58397d4356786f01 languageName: node linkType: hard -"jest-leak-detector@npm:^29.6.2": - version: 29.6.2 - resolution: "jest-leak-detector@npm:29.6.2" +"jest-leak-detector@npm:^29.7.0": + version: 29.7.0 + resolution: "jest-leak-detector@npm:29.7.0" dependencies: - jest-get-type: ^29.4.3 - pretty-format: ^29.6.2 - checksum: e00152acdba8aa8f9334775b77375947508051c34646fbeb702275da2b6ac6145f8cad6d5893112e76484d00fa8c0b4fd71b78ab0b4ef34950f5b6a84f37ae67 + jest-get-type: ^29.6.3 + pretty-format: ^29.7.0 + checksum: e3950e3ddd71e1d0c22924c51a300a1c2db6cf69ec1e51f95ccf424bcc070f78664813bef7aed4b16b96dfbdeea53fe358f8aeaaea84346ae15c3735758f1605 languageName: node linkType: hard -"jest-matcher-utils@npm:^29.6.2": - version: 29.6.2 - resolution: "jest-matcher-utils@npm:29.6.2" +"jest-matcher-utils@npm:^29.7.0": + version: 29.7.0 + resolution: "jest-matcher-utils@npm:29.7.0" dependencies: chalk: ^4.0.0 - jest-diff: ^29.6.2 - jest-get-type: ^29.4.3 - pretty-format: ^29.6.2 - checksum: 3e1b65dd30d05f75fe56dc45fbe4135aec2ff96a3d1e21afbf6a66f3a45a7e29cd0fd37cf80b9564e0381d6205833f77ccaf766c6f7e1aad6b7924d117be504e + jest-diff: ^29.7.0 + jest-get-type: ^29.6.3 + pretty-format: ^29.7.0 + checksum: d7259e5f995d915e8a37a8fd494cb7d6af24cd2a287b200f831717ba0d015190375f9f5dc35393b8ba2aae9b2ebd60984635269c7f8cff7d85b077543b7744cd languageName: node linkType: hard -"jest-message-util@npm:^29.6.2": - version: 29.6.2 - resolution: "jest-message-util@npm:29.6.2" +"jest-message-util@npm:^29.7.0": + version: 29.7.0 + resolution: "jest-message-util@npm:29.7.0" dependencies: "@babel/code-frame": ^7.12.13 - "@jest/types": ^29.6.1 + "@jest/types": ^29.6.3 "@types/stack-utils": ^2.0.0 chalk: ^4.0.0 graceful-fs: ^4.2.9 micromatch: ^4.0.4 - pretty-format: ^29.6.2 + pretty-format: ^29.7.0 slash: ^3.0.0 stack-utils: ^2.0.3 - checksum: e8e3c8d2301e2ca4038ed6df8cbba7fedc6949d1ede4c0e3f1f44f53afb56d77eb35983fa460140d0eadeab99a5f3ae04b703fe77cd7b316b40b361228b5aa1a - languageName: node - linkType: hard - -"jest-mock-extended@npm:^3.0.3, jest-mock-extended@npm:^3.0.4": - version: 3.0.4 - resolution: "jest-mock-extended@npm:3.0.4" - dependencies: - ts-essentials: ^7.0.3 - peerDependencies: - jest: ^24.0.0 || ^25.0.0 || ^26.0.0 || ^27.0.0 || ^28.0.0 || ^29.0.0 - typescript: ^3.0.0 || ^4.0.0 || ^5.0.0 - checksum: f861253c63508b30d971fbbbc1bf2911ff4406cd260d0e23483a1d4514898b18ba5efbd43fbdf6d94996dc09b20eb1aad1b46aeaea9c99244ba12dc99814fd3f + checksum: a9d025b1c6726a2ff17d54cc694de088b0489456c69106be6b615db7a51b7beb66788bea7a59991a019d924fbf20f67d085a445aedb9a4d6760363f4d7d09930 languageName: node linkType: hard -"jest-mock-extended@npm:^3.0.5": +"jest-mock-extended@npm:^3.0.3, jest-mock-extended@npm:^3.0.4, jest-mock-extended@npm:^3.0.5": version: 3.0.5 resolution: "jest-mock-extended@npm:3.0.5" dependencies: @@ -9114,14 +8742,14 @@ __metadata: languageName: node linkType: hard -"jest-mock@npm:^29.6.2": - version: 29.6.2 - resolution: "jest-mock@npm:29.6.2" +"jest-mock@npm:^29.7.0": + version: 29.7.0 + resolution: "jest-mock@npm:29.7.0" dependencies: - "@jest/types": ^29.6.1 + "@jest/types": ^29.6.3 "@types/node": "*" - jest-util: ^29.6.2 - checksum: 0bacb5d58441462c0e531ec4d2f7377eecbe21f664d8a460e72f94ba61d22635028931678e7a0f1c3e3f5894973db8e409432f7db4c01283456c8fdbd85f5b3b + jest-util: ^29.7.0 + checksum: 81ba9b68689a60be1482212878973700347cb72833c5e5af09895882b9eb5c4e02843a1bbdf23f94c52d42708bab53a30c45a3482952c9eec173d1eaac5b86c5 languageName: node linkType: hard @@ -9137,168 +8765,168 @@ __metadata: languageName: node linkType: hard -"jest-regex-util@npm:^29.4.3": - version: 29.4.3 - resolution: "jest-regex-util@npm:29.4.3" - checksum: 96fc7fc28cd4dd73a63c13a526202c4bd8b351d4e5b68b1a2a2c88da3308c2a16e26feaa593083eb0bac38cca1aa9dd05025412e7de013ba963fb8e66af22b8a +"jest-regex-util@npm:^29.6.3": + version: 29.6.3 + resolution: "jest-regex-util@npm:29.6.3" + checksum: 0518beeb9bf1228261695e54f0feaad3606df26a19764bc19541e0fc6e2a3737191904607fb72f3f2ce85d9c16b28df79b7b1ec9443aa08c3ef0e9efda6f8f2a languageName: node linkType: hard -"jest-resolve-dependencies@npm:^29.6.2": - version: 29.6.2 - resolution: "jest-resolve-dependencies@npm:29.6.2" +"jest-resolve-dependencies@npm:^29.7.0": + version: 29.7.0 + resolution: "jest-resolve-dependencies@npm:29.7.0" dependencies: - jest-regex-util: ^29.4.3 - jest-snapshot: ^29.6.2 - checksum: d40ee11af2c9d2ef0dbbcf9a5b7dda37c2b86cf4e5de1705795919fd8927907569115c502116ab56de0dca576d5faa31ec9b636240333b6830a568a63004da17 + jest-regex-util: ^29.6.3 + jest-snapshot: ^29.7.0 + checksum: aeb75d8150aaae60ca2bb345a0d198f23496494677cd6aefa26fc005faf354061f073982175daaf32b4b9d86b26ca928586344516e3e6969aa614cb13b883984 languageName: node linkType: hard -"jest-resolve@npm:^29.6.2": - version: 29.6.2 - resolution: "jest-resolve@npm:29.6.2" +"jest-resolve@npm:^29.7.0": + version: 29.7.0 + resolution: "jest-resolve@npm:29.7.0" dependencies: chalk: ^4.0.0 graceful-fs: ^4.2.9 - jest-haste-map: ^29.6.2 + jest-haste-map: ^29.7.0 jest-pnp-resolver: ^1.2.2 - jest-util: ^29.6.2 - jest-validate: ^29.6.2 + jest-util: ^29.7.0 + jest-validate: ^29.7.0 resolve: ^1.20.0 resolve.exports: ^2.0.0 slash: ^3.0.0 - checksum: 01721957e61821a576b2ded043eeab8b392166e0e6d8d680f75657737e2ea7481ff29c2716b866ccd12e743f3a8da465504b1028e78b6a3c68b9561303de7ec8 + checksum: 0ca218e10731aa17920526ec39deaec59ab9b966237905ffc4545444481112cd422f01581230eceb7e82d86f44a543d520a71391ec66e1b4ef1a578bd5c73487 languageName: node linkType: hard -"jest-runner@npm:^29.6.2": - version: 29.6.2 - resolution: "jest-runner@npm:29.6.2" +"jest-runner@npm:^29.7.0": + version: 29.7.0 + resolution: "jest-runner@npm:29.7.0" dependencies: - "@jest/console": ^29.6.2 - "@jest/environment": ^29.6.2 - "@jest/test-result": ^29.6.2 - "@jest/transform": ^29.6.2 - "@jest/types": ^29.6.1 + "@jest/console": ^29.7.0 + "@jest/environment": ^29.7.0 + "@jest/test-result": ^29.7.0 + "@jest/transform": ^29.7.0 + "@jest/types": ^29.6.3 "@types/node": "*" chalk: ^4.0.0 emittery: ^0.13.1 graceful-fs: ^4.2.9 - jest-docblock: ^29.4.3 - jest-environment-node: ^29.6.2 - jest-haste-map: ^29.6.2 - jest-leak-detector: ^29.6.2 - jest-message-util: ^29.6.2 - jest-resolve: ^29.6.2 - jest-runtime: ^29.6.2 - jest-util: ^29.6.2 - jest-watcher: ^29.6.2 - jest-worker: ^29.6.2 + jest-docblock: ^29.7.0 + jest-environment-node: ^29.7.0 + jest-haste-map: ^29.7.0 + jest-leak-detector: ^29.7.0 + jest-message-util: ^29.7.0 + jest-resolve: ^29.7.0 + jest-runtime: ^29.7.0 + jest-util: ^29.7.0 + jest-watcher: ^29.7.0 + jest-worker: ^29.7.0 p-limit: ^3.1.0 source-map-support: 0.5.13 - checksum: 46bd506a08ddf79628a509aed4105ab74c0b03727a3e24c90bbc2915531860b3da99f7ace2fd9603194440553cffac9cfb1a3b7d0ce03d5fc9c5f2d5ffbb3d3f + checksum: f0405778ea64812bf9b5c50b598850d94ccf95d7ba21f090c64827b41decd680ee19fcbb494007cdd7f5d0d8906bfc9eceddd8fa583e753e736ecd462d4682fb languageName: node linkType: hard -"jest-runtime@npm:^29.6.2": - version: 29.6.2 - resolution: "jest-runtime@npm:29.6.2" +"jest-runtime@npm:^29.7.0": + version: 29.7.0 + resolution: "jest-runtime@npm:29.7.0" dependencies: - "@jest/environment": ^29.6.2 - "@jest/fake-timers": ^29.6.2 - "@jest/globals": ^29.6.2 - "@jest/source-map": ^29.6.0 - "@jest/test-result": ^29.6.2 - "@jest/transform": ^29.6.2 - "@jest/types": ^29.6.1 + "@jest/environment": ^29.7.0 + "@jest/fake-timers": ^29.7.0 + "@jest/globals": ^29.7.0 + "@jest/source-map": ^29.6.3 + "@jest/test-result": ^29.7.0 + "@jest/transform": ^29.7.0 + "@jest/types": ^29.6.3 "@types/node": "*" chalk: ^4.0.0 cjs-module-lexer: ^1.0.0 collect-v8-coverage: ^1.0.0 glob: ^7.1.3 graceful-fs: ^4.2.9 - jest-haste-map: ^29.6.2 - jest-message-util: ^29.6.2 - jest-mock: ^29.6.2 - jest-regex-util: ^29.4.3 - jest-resolve: ^29.6.2 - jest-snapshot: ^29.6.2 - jest-util: ^29.6.2 + jest-haste-map: ^29.7.0 + jest-message-util: ^29.7.0 + jest-mock: ^29.7.0 + jest-regex-util: ^29.6.3 + jest-resolve: ^29.7.0 + jest-snapshot: ^29.7.0 + jest-util: ^29.7.0 slash: ^3.0.0 strip-bom: ^4.0.0 - checksum: 8e7e4486b23b01a9c407313681bed0def39680c2ae21cf01347f111983252ec3a024c56493c5411fed53633f02863eed0816099110cbe04b3889aa5babf1042d + checksum: d19f113d013e80691e07047f68e1e3448ef024ff2c6b586ce4f90cd7d4c62a2cd1d460110491019719f3c59bfebe16f0e201ed005ef9f80e2cf798c374eed54e languageName: node linkType: hard -"jest-snapshot@npm:^29.6.2": - version: 29.6.2 - resolution: "jest-snapshot@npm:29.6.2" +"jest-snapshot@npm:^29.7.0": + version: 29.7.0 + resolution: "jest-snapshot@npm:29.7.0" dependencies: "@babel/core": ^7.11.6 "@babel/generator": ^7.7.2 "@babel/plugin-syntax-jsx": ^7.7.2 "@babel/plugin-syntax-typescript": ^7.7.2 "@babel/types": ^7.3.3 - "@jest/expect-utils": ^29.6.2 - "@jest/transform": ^29.6.2 - "@jest/types": ^29.6.1 + "@jest/expect-utils": ^29.7.0 + "@jest/transform": ^29.7.0 + "@jest/types": ^29.6.3 babel-preset-current-node-syntax: ^1.0.0 chalk: ^4.0.0 - expect: ^29.6.2 + expect: ^29.7.0 graceful-fs: ^4.2.9 - jest-diff: ^29.6.2 - jest-get-type: ^29.4.3 - jest-matcher-utils: ^29.6.2 - jest-message-util: ^29.6.2 - jest-util: ^29.6.2 + jest-diff: ^29.7.0 + jest-get-type: ^29.6.3 + jest-matcher-utils: ^29.7.0 + jest-message-util: ^29.7.0 + jest-util: ^29.7.0 natural-compare: ^1.4.0 - pretty-format: ^29.6.2 + pretty-format: ^29.7.0 semver: ^7.5.3 - checksum: c1c70a9dbce7fca62ed73ac38234b4ee643e8b667acf71b4417ab67776c1188bb08b8ad450e56a2889ad182903ffd416386fa8082a477724ccf8d8c29a4c6906 + checksum: 86821c3ad0b6899521ce75ee1ae7b01b17e6dfeff9166f2cf17f012e0c5d8c798f30f9e4f8f7f5bed01ea7b55a6bc159f5eda778311162cbfa48785447c237ad languageName: node linkType: hard -"jest-util@npm:^29.0.0, jest-util@npm:^29.6.2": - version: 29.6.2 - resolution: "jest-util@npm:29.6.2" +"jest-util@npm:^29.0.0, jest-util@npm:^29.7.0": + version: 29.7.0 + resolution: "jest-util@npm:29.7.0" dependencies: - "@jest/types": ^29.6.1 + "@jest/types": ^29.6.3 "@types/node": "*" chalk: ^4.0.0 ci-info: ^3.2.0 graceful-fs: ^4.2.9 picomatch: ^2.2.3 - checksum: 8aedc0c80083d0cabd6c6c4f04dea1cbcac609fd7bc3b1fc05a3999291bd6e63dd52b0c806f9378d5cae28eff5a6191709a4987861001293f8d03e53984adca4 + checksum: 042ab4980f4ccd4d50226e01e5c7376a8556b472442ca6091a8f102488c0f22e6e8b89ea874111d2328a2080083bf3225c86f3788c52af0bd0345a00eb57a3ca languageName: node linkType: hard -"jest-validate@npm:^29.6.2": - version: 29.6.2 - resolution: "jest-validate@npm:29.6.2" +"jest-validate@npm:^29.7.0": + version: 29.7.0 + resolution: "jest-validate@npm:29.7.0" dependencies: - "@jest/types": ^29.6.1 + "@jest/types": ^29.6.3 camelcase: ^6.2.0 chalk: ^4.0.0 - jest-get-type: ^29.4.3 + jest-get-type: ^29.6.3 leven: ^3.1.0 - pretty-format: ^29.6.2 - checksum: 32648d002189c0ad8a958eace7c6b7d05ea1dc440a1b91e0f22dc1aef489899446ec80b2d527fd13713862d89dfb4606e24a3bf8a10c4ddac3c911e93b7f0374 + pretty-format: ^29.7.0 + checksum: 191fcdc980f8a0de4dbdd879fa276435d00eb157a48683af7b3b1b98b0f7d9de7ffe12689b617779097ff1ed77601b9f7126b0871bba4f776e222c40f62e9dae languageName: node linkType: hard -"jest-watcher@npm:^29.6.2": - version: 29.6.2 - resolution: "jest-watcher@npm:29.6.2" +"jest-watcher@npm:^29.7.0": + version: 29.7.0 + resolution: "jest-watcher@npm:29.7.0" dependencies: - "@jest/test-result": ^29.6.2 - "@jest/types": ^29.6.1 + "@jest/test-result": ^29.7.0 + "@jest/types": ^29.6.3 "@types/node": "*" ansi-escapes: ^4.2.1 chalk: ^4.0.0 emittery: ^0.13.1 - jest-util: ^29.6.2 + jest-util: ^29.7.0 string-length: ^4.0.1 - checksum: 14624190fc8b5fbae466a2ec81458a88c15716d99f042bb4674d53e9623d305cb2905bc1dffeda05fd1a10a05c2a83efe5ac41942477e2b15eaebb08d0aaab32 + checksum: 67e6e7fe695416deff96b93a14a561a6db69389a0667e9489f24485bb85e5b54e12f3b2ba511ec0b777eca1e727235b073e3ebcdd473d68888650489f88df92f languageName: node linkType: hard @@ -9313,26 +8941,26 @@ __metadata: languageName: node linkType: hard -"jest-worker@npm:^29.6.2": - version: 29.6.2 - resolution: "jest-worker@npm:29.6.2" +"jest-worker@npm:^29.7.0": + version: 29.7.0 + resolution: "jest-worker@npm:29.7.0" dependencies: "@types/node": "*" - jest-util: ^29.6.2 + jest-util: ^29.7.0 merge-stream: ^2.0.0 supports-color: ^8.0.0 - checksum: 11035564534bf181ead80b25be138c2d42372bd5626151a3e705200d47a74fd9da3ca79f8a7b15806cdc325ad73c3d21d23acceeed99d50941589ff02915ed38 + checksum: 30fff60af49675273644d408b650fc2eb4b5dcafc5a0a455f238322a8f9d8a98d847baca9d51ff197b6747f54c7901daa2287799230b856a0f48287d131f8c13 languageName: node linkType: hard "jest@npm:^29.5.0": - version: 29.6.2 - resolution: "jest@npm:29.6.2" + version: 29.7.0 + resolution: "jest@npm:29.7.0" dependencies: - "@jest/core": ^29.6.2 - "@jest/types": ^29.6.1 + "@jest/core": ^29.7.0 + "@jest/types": ^29.6.3 import-local: ^3.0.2 - jest-cli: ^29.6.2 + jest-cli: ^29.7.0 peerDependencies: node-notifier: ^8.0.1 || ^9.0.0 || ^10.0.0 peerDependenciesMeta: @@ -9340,7 +8968,7 @@ __metadata: optional: true bin: jest: bin/jest.js - checksum: dd63facd4e6aefc35d2c42acd7e4c9fb0d8fe4705df4b3ccedd953605424d7aa89c88af8cf4c9951752709cac081d29c35b264e1794643d5688ea724ccc9a485 + checksum: 17ca8d67504a7dbb1998cf3c3077ec9031ba3eb512da8d71cb91bcabb2b8995c4e4b292b740cb9bf1cbff5ce3e110b3f7c777b0cefb6f41ab05445f248d0ee0b languageName: node linkType: hard @@ -9397,9 +9025,16 @@ __metadata: languageName: node linkType: hard +"json-buffer@npm:3.0.1": + version: 3.0.1 + resolution: "json-buffer@npm:3.0.1" + checksum: 9026b03edc2847eefa2e37646c579300a1f3a4586cfb62bf857832b60c852042d0d6ae55d1afb8926163fa54c2b01d83ae24705f34990348bdac6273a29d4581 + languageName: node + linkType: hard + "json-joy@npm:^9.2.0": - version: 9.6.0 - resolution: "json-joy@npm:9.6.0" + version: 9.9.1 + resolution: "json-joy@npm:9.9.1" dependencies: arg: ^5.0.2 hyperdyperid: ^1.2.0 @@ -9416,7 +9051,7 @@ __metadata: json-pointer: bin/json-pointer.js json-pointer-test: bin/json-pointer-test.js json-unpack: bin/json-unpack.js - checksum: 517b1b466b1b477bd53ecf23693758e785cebba5bf32dbb04059164b067330246efd35f11df13e8a04fb85ec4c330f806e2752736189a12f1a4ff1a1e423ec27 + checksum: d165398682f00019796225faf365cd8d060f3e086af39bb5081c30907b7e52eaf13697d1c0f6ee2b010fe255ae1fd776e05ad7d6ee5fb549e98fe982f560884b languageName: node linkType: hard @@ -9452,7 +9087,7 @@ __metadata: languageName: node linkType: hard -"json5@npm:^2.2.2, json5@npm:^2.2.3": +"json5@npm:^2.2.3": version: 2.2.3 resolution: "json5@npm:2.2.3" bin: @@ -9462,9 +9097,9 @@ __metadata: linkType: hard "jsonc-parser@npm:^3.2.0": - version: 3.2.0 - resolution: "jsonc-parser@npm:3.2.0" - checksum: 946dd9a5f326b745aa326d48a7257e3f4a4b62c5e98ec8e49fa2bdd8d96cef7e6febf1399f5c7016114fd1f68a1c62c6138826d5d90bc650448e3cf0951c53c7 + version: 3.2.1 + resolution: "jsonc-parser@npm:3.2.1" + checksum: 656d9027b91de98d8ab91b3aa0d0a4cab7dc798a6830845ca664f3e76c82d46b973675bbe9b500fae1de37fd3e81aceacbaa2a57884bf2f8f29192150d2d1ef7 languageName: node linkType: hard @@ -9514,6 +9149,15 @@ __metadata: languageName: node linkType: hard +"keyv@npm:^4.5.3": + version: 4.5.4 + resolution: "keyv@npm:4.5.4" + dependencies: + json-buffer: 3.0.1 + checksum: 74a24395b1c34bd44ad5cb2b49140d087553e170625240b86755a6604cd65aa16efdbdeae5cdb17ba1284a0fbb25ad06263755dbc71b8d8b06f74232ce3cdd72 + languageName: node + linkType: hard + "kind-of@npm:^6.0.2, kind-of@npm:^6.0.3": version: 6.0.3 resolution: "kind-of@npm:6.0.3" @@ -9583,14 +9227,15 @@ __metadata: linkType: hard "koa-router@npm:^12.0.0": - version: 12.0.0 - resolution: "koa-router@npm:12.0.0" + version: 12.0.1 + resolution: "koa-router@npm:12.0.1" dependencies: + debug: ^4.3.4 http-errors: ^2.0.0 koa-compose: ^4.1.0 methods: ^1.1.2 path-to-regexp: ^6.2.1 - checksum: 29b02fd96972c037e805f6ce2626c971f4fd9cba04005bfedc080ab425d31b4b1cfe2ebc000b26e4a45e68215a3a3ed557f836ba486ea0d2f1e7e78fc95f8dca + checksum: 62852af6c47dba9327c993594367b8bcd995fd49972d2539eae9afa231571680f75ebe0f439bf886256cdfaf791900c2d11fe31b894d9ba8c60d1c727c03b9ed languageName: node linkType: hard @@ -9616,14 +9261,14 @@ __metadata: linkType: hard "koa@npm:^2.14.2": - version: 2.14.2 - resolution: "koa@npm:2.14.2" + version: 2.15.0 + resolution: "koa@npm:2.15.0" dependencies: accepts: ^1.3.5 cache-content-type: ^1.0.0 content-disposition: ~0.5.2 content-type: ^1.0.4 - cookies: ~0.8.0 + cookies: ~0.9.0 debug: ^4.3.2 delegates: ^1.0.0 depd: ^2.0.0 @@ -9642,7 +9287,7 @@ __metadata: statuses: ^1.5.0 type-is: ^1.6.16 vary: ^1.1.2 - checksum: 17fe3b8f5e0b4759004a942cc6ba2a9507299943a697dff9766b85f41f45caed4077ca2645ac9ad254d3359fffedfc4c9ebdd7a70493e5df8cdfac159a8ee835 + checksum: a97741f89f328f25ae94d82d0ee608377d89e086c73f2d868023e6050dea682ef93e0a5c80097f3aaad28121853aea50a7fb3c0c12ecc45798da2fd1255f580b languageName: node linkType: hard @@ -9730,26 +9375,25 @@ __metadata: linkType: hard "libp2p@npm:^0.46.6": - version: 0.46.6 - resolution: "libp2p@npm:0.46.6" + version: 0.46.21 + resolution: "libp2p@npm:0.46.21" dependencies: "@achingbrain/nat-port-mapper": ^1.0.9 - "@libp2p/crypto": ^2.0.3 - "@libp2p/interface": ^0.1.2 - "@libp2p/interface-internal": ^0.1.4 - "@libp2p/keychain": ^3.0.3 - "@libp2p/logger": ^3.0.2 - "@libp2p/multistream-select": ^4.0.2 - "@libp2p/peer-collections": ^4.0.3 - "@libp2p/peer-id": ^3.0.2 - "@libp2p/peer-id-factory": ^3.0.3 - "@libp2p/peer-record": ^6.0.3 - "@libp2p/peer-store": ^9.0.3 - "@libp2p/utils": ^4.0.2 + "@libp2p/crypto": ^2.0.8 + "@libp2p/interface": ^0.1.6 + "@libp2p/interface-internal": ^0.1.9 + "@libp2p/keychain": ^3.0.8 + "@libp2p/logger": ^3.1.0 + "@libp2p/multistream-select": ^4.0.6 + "@libp2p/peer-collections": ^4.0.8 + "@libp2p/peer-id": ^3.0.6 + "@libp2p/peer-id-factory": ^3.0.8 + "@libp2p/peer-record": ^6.0.9 + "@libp2p/peer-store": ^9.0.9 + "@libp2p/utils": ^4.0.7 "@multiformats/mafmt": ^12.1.2 "@multiformats/multiaddr": ^12.1.5 "@multiformats/multiaddr-matcher": ^1.0.0 - abortable-iterator: ^5.0.1 any-signal: ^4.1.1 datastore-core: ^9.0.1 delay: ^6.0.0 @@ -9771,15 +9415,15 @@ __metadata: multiformats: ^12.0.1 p-defer: ^4.0.0 p-queue: ^7.3.4 - p-retry: ^5.0.0 + p-retry: ^6.0.0 private-ip: ^3.0.0 protons-runtime: ^5.0.0 - rate-limiter-flexible: ^2.3.11 + rate-limiter-flexible: ^3.0.0 uint8arraylist: ^2.4.3 uint8arrays: ^4.0.6 wherearewe: ^2.0.1 xsalsa20: ^1.1.0 - checksum: c23d6621f1ed7d5c96de440fb08bfc807a8fc38c44e54ed6422f9414414dae0c7e87675f9416eacd5b143a2ff91fba26e6001bdde670f80696c4e73574fc228e + checksum: 6f69e89e8f4ac28ee1b365db878379d28865982bf6d4cec9524d9445975b5eab4fa5dca581e704ce1438b196258e88856cf6778048b59a75ae09ca1b9417fbb8 languageName: node linkType: hard @@ -9800,15 +9444,15 @@ __metadata: linkType: hard "lmdb@npm:^2.9.1": - version: 2.9.1 - resolution: "lmdb@npm:2.9.1" - dependencies: - "@lmdb/lmdb-darwin-arm64": 2.9.1 - "@lmdb/lmdb-darwin-x64": 2.9.1 - "@lmdb/lmdb-linux-arm": 2.9.1 - "@lmdb/lmdb-linux-arm64": 2.9.1 - "@lmdb/lmdb-linux-x64": 2.9.1 - "@lmdb/lmdb-win32-x64": 2.9.1 + version: 2.9.2 + resolution: "lmdb@npm:2.9.2" + dependencies: + "@lmdb/lmdb-darwin-arm64": 2.9.2 + "@lmdb/lmdb-darwin-x64": 2.9.2 + "@lmdb/lmdb-linux-arm": 2.9.2 + "@lmdb/lmdb-linux-arm64": 2.9.2 + "@lmdb/lmdb-linux-x64": 2.9.2 + "@lmdb/lmdb-win32-x64": 2.9.2 msgpackr: ^1.9.9 node-addon-api: ^6.1.0 node-gyp: latest @@ -9830,7 +9474,7 @@ __metadata: optional: true bin: download-lmdb-prebuilds: bin/download-prebuilds.js - checksum: 1f0a8754cc019586c8e34bd45e4ee1df99f6f5732e8dc04f951cf631895a179dfd913123773206935a580cfe80bce117800a3ccf0a2cc8187821badfdaa71cd4 + checksum: b2471c4d2c36f15a27233ae1eece86fcbb40613574ec54245cf697b16b1b35c70c72e4092dc739407df145f14b7c1b6b56c4281a439c314e79a5338df8b2b63b languageName: node linkType: hard @@ -9987,33 +9631,23 @@ __metadata: linkType: hard "logform@npm:^2.3.2, logform@npm:^2.4.0": - version: 2.5.1 - resolution: "logform@npm:2.5.1" + version: 2.6.0 + resolution: "logform@npm:2.6.0" dependencies: - "@colors/colors": 1.5.0 + "@colors/colors": 1.6.0 "@types/triple-beam": ^1.3.2 fecha: ^4.2.0 ms: ^2.1.1 safe-stable-stringify: ^2.3.1 triple-beam: ^1.3.0 - checksum: 08fdf03be5bb69af33bac214eb4f6a0c83ad3821a30de498925fccb61e993e5a4a87470aab356ca2110c11e4643685bed5597ca5f46dd1cd11437c44a0e0e3c2 - languageName: node - linkType: hard - -"long@npm:^5.0.0": - version: 5.2.3 - resolution: "long@npm:5.2.3" - checksum: 885ede7c3de4facccbd2cacc6168bae3a02c3e836159ea4252c87b6e34d40af819824b2d4edce330bfb5c4d6e8ce3ec5864bdcf9473fa1f53a4f8225860e5897 + checksum: b9ea74bb75e55379ad0eb3e4d65ae6e8d02bc45b431c218162878bf663997ab9258a73104c2b30e09dd2db288bb83c8bf8748e46689d75f5e7e34cf69378d6df languageName: node linkType: hard -"longbits@npm:^1.1.0": - version: 1.1.0 - resolution: "longbits@npm:1.1.0" - dependencies: - byte-access: ^1.0.1 - uint8arraylist: ^2.0.0 - checksum: 7f8ec8ddef64b160da22c31875fa549b40a4cff626c948c423dc2cb73c0d85a5c6a13ce3a611b5229fcf65dd6ffb4fcd1d5eaef61458570194172ad485112521 +"lru-cache@npm:^10.0.1, lru-cache@npm:^9.1.1 || ^10.0.0": + version: 10.1.0 + resolution: "lru-cache@npm:10.1.0" + checksum: 58056d33e2500fbedce92f8c542e7c11b50d7d086578f14b7074d8c241422004af0718e08a6eaae8705cee09c77e39a61c1c79e9370ba689b7010c152e6a76ab languageName: node linkType: hard @@ -10035,20 +9669,13 @@ __metadata: languageName: node linkType: hard -"lru-cache@npm:^7.14.1, lru-cache@npm:^7.7.1": +"lru-cache@npm:^7.14.1": version: 7.18.3 resolution: "lru-cache@npm:7.18.3" checksum: e550d772384709deea3f141af34b6d4fa392e2e418c1498c078de0ee63670f1f46f5eee746e8ef7e69e1c895af0d4224e62ee33e66a543a14763b0f2e74c1356 languageName: node linkType: hard -"lru-cache@npm:^9.1.1 || ^10.0.0": - version: 10.0.0 - resolution: "lru-cache@npm:10.0.0" - checksum: 18f101675fe283bc09cda0ef1e3cc83781aeb8373b439f086f758d1d91b28730950db785999cd060d3c825a8571c03073e8c14512b6655af2188d623031baf50 - languageName: node - linkType: hard - "ltgt@npm:^2.2.0": version: 2.2.1 resolution: "ltgt@npm:2.2.1" @@ -10116,26 +9743,22 @@ __metadata: languageName: node linkType: hard -"make-fetch-happen@npm:^11.0.3": - version: 11.1.1 - resolution: "make-fetch-happen@npm:11.1.1" +"make-fetch-happen@npm:^13.0.0": + version: 13.0.0 + resolution: "make-fetch-happen@npm:13.0.0" dependencies: - agentkeepalive: ^4.2.1 - cacache: ^17.0.0 + "@npmcli/agent": ^2.0.0 + cacache: ^18.0.0 http-cache-semantics: ^4.1.1 - http-proxy-agent: ^5.0.0 - https-proxy-agent: ^5.0.0 is-lambda: ^1.0.1 - lru-cache: ^7.7.1 - minipass: ^5.0.0 + minipass: ^7.0.2 minipass-fetch: ^3.0.0 minipass-flush: ^1.0.5 minipass-pipeline: ^1.2.4 negotiator: ^0.6.3 promise-retry: ^2.0.1 - socks-proxy-agent: ^7.0.0 ssri: ^10.0.0 - checksum: 7268bf274a0f6dcf0343829489a4506603ff34bd0649c12058753900b0eb29191dce5dba12680719a5d0a983d3e57810f594a12f3c18494e93a1fbc6348a4540 + checksum: 7c7a6d381ce919dd83af398b66459a10e2fe8f4504f340d1d090d3fa3d1b0c93750220e1d898114c64467223504bd258612ba83efbc16f31b075cd56de24b4af languageName: node linkType: hard @@ -10352,6 +9975,15 @@ __metadata: languageName: node linkType: hard +"minimatch@npm:9.0.3, minimatch@npm:^9.0.0, minimatch@npm:^9.0.1": + version: 9.0.3 + resolution: "minimatch@npm:9.0.3" + dependencies: + brace-expansion: ^2.0.1 + checksum: 253487976bf485b612f16bf57463520a14f512662e592e95c571afdab1442a6a6864b6c88f248ce6fc4ff0b6de04ac7aa6c8bb51e868e99d1d65eb0658a708b5 + languageName: node + linkType: hard + "minimatch@npm:^3.0.4, minimatch@npm:^3.0.5, minimatch@npm:^3.1.1, minimatch@npm:^3.1.2": version: 3.1.2 resolution: "minimatch@npm:3.1.2" @@ -10361,15 +9993,6 @@ __metadata: languageName: node linkType: hard -"minimatch@npm:^9.0.0, minimatch@npm:^9.0.1": - version: 9.0.3 - resolution: "minimatch@npm:9.0.3" - dependencies: - brace-expansion: ^2.0.1 - checksum: 253487976bf485b612f16bf57463520a14f512662e592e95c571afdab1442a6a6864b6c88f248ce6fc4ff0b6de04ac7aa6c8bb51e868e99d1d65eb0658a708b5 - languageName: node - linkType: hard - "minimist-options@npm:4.1.0": version: 4.1.0 resolution: "minimist-options@npm:4.1.0" @@ -10388,27 +10011,27 @@ __metadata: languageName: node linkType: hard -"minipass-collect@npm:^1.0.2": - version: 1.0.2 - resolution: "minipass-collect@npm:1.0.2" +"minipass-collect@npm:^2.0.1": + version: 2.0.1 + resolution: "minipass-collect@npm:2.0.1" dependencies: - minipass: ^3.0.0 - checksum: 14df761028f3e47293aee72888f2657695ec66bd7d09cae7ad558da30415fdc4752bbfee66287dcc6fd5e6a2fa3466d6c484dc1cbd986525d9393b9523d97f10 + minipass: ^7.0.3 + checksum: b251bceea62090f67a6cced7a446a36f4cd61ee2d5cea9aee7fff79ba8030e416327a1c5aa2908dc22629d06214b46d88fdab8c51ac76bacbf5703851b5ad342 languageName: node linkType: hard "minipass-fetch@npm:^3.0.0": - version: 3.0.3 - resolution: "minipass-fetch@npm:3.0.3" + version: 3.0.4 + resolution: "minipass-fetch@npm:3.0.4" dependencies: encoding: ^0.1.13 - minipass: ^5.0.0 + minipass: ^7.0.3 minipass-sized: ^1.0.3 minizlib: ^2.1.2 dependenciesMeta: encoding: optional: true - checksum: af5ab2552a16fcf505d35fd7ffb84b57f4a0eeb269e6e1d9a2a75824dda48b36e527083250b7cca4a4def21d9544e2ade441e4730e233c0bc2133f6abda31e18 + checksum: af7aad15d5c128ab1ebe52e043bdf7d62c3c6f0cecb9285b40d7b395e1375b45dcdfd40e63e93d26a0e8249c9efd5c325c65575aceee192883970ff8cb11364a languageName: node linkType: hard @@ -10455,10 +10078,10 @@ __metadata: languageName: node linkType: hard -"minipass@npm:^5.0.0 || ^6.0.2 || ^7.0.0": - version: 7.0.2 - resolution: "minipass@npm:7.0.2" - checksum: 46776de732eb7cef2c7404a15fb28c41f5c54a22be50d47b03c605bf21f5c18d61a173c0a20b49a97e7a65f78d887245066410642551e45fffe04e9ac9e325bc +"minipass@npm:^5.0.0 || ^6.0.2 || ^7.0.0, minipass@npm:^7.0.2, minipass@npm:^7.0.3": + version: 7.0.4 + resolution: "minipass@npm:7.0.4" + checksum: 87585e258b9488caf2e7acea242fd7856bbe9a2c84a7807643513a338d66f368c7d518200ad7b70a508664d408aa000517647b2930c259a8b1f9f0984f344a21 languageName: node linkType: hard @@ -10535,21 +10158,20 @@ __metadata: linkType: hard "moment@npm:^2.29.1": - version: 2.29.4 - resolution: "moment@npm:2.29.4" - checksum: 0ec3f9c2bcba38dc2451b1daed5daded747f17610b92427bebe1d08d48d8b7bdd8d9197500b072d14e326dd0ccf3e326b9e3d07c5895d3d49e39b6803b76e80e + version: 2.30.1 + resolution: "moment@npm:2.30.1" + checksum: 859236bab1e88c3e5802afcf797fc801acdbd0ee509d34ea3df6eea21eb6bcc2abd4ae4e4e64aa7c986aa6cba563c6e62806218e6412a765010712e5fa121ba6 languageName: node linkType: hard "mortice@npm:^3.0.1": - version: 3.0.1 - resolution: "mortice@npm:3.0.1" + version: 3.0.4 + resolution: "mortice@npm:3.0.4" dependencies: - nanoid: ^4.0.0 observable-webworkers: ^2.0.1 - p-queue: ^7.2.0 + p-queue: ^8.0.1 p-timeout: ^6.0.0 - checksum: a6b23c98bdea9a18f25d0431424f0c9df54e6af5f2ebe9d5752b50bccc1fa19e2559007d813f7fc14171a9253ebc60c8702f0cda51a57c8fd0ae420abde83761 + checksum: 64d63b6d724636e94f59a8f72208561d621f601707df17e8c1ea5e236bc3f208eae98609586898458851228733908028d61b1bf5a2b6db58bd2aa9c1f7e8166b languageName: node linkType: hard @@ -10560,7 +10182,7 @@ __metadata: languageName: node linkType: hard -"ms@npm:^2.0.0, ms@npm:^2.1.1": +"ms@npm:^2.1.1": version: 2.1.3 resolution: "ms@npm:2.1.3" checksum: aa92de608021b242401676e35cfa5aa42dd70cbdc082b916da7fb925c542173e36bce97ea3e804923fe92c0ad991434e4a38327e15a1b5b5f945d66df615ae6d @@ -10599,14 +10221,14 @@ __metadata: linkType: hard "msgpackr@npm:^1.9.9": - version: 1.9.9 - resolution: "msgpackr@npm:1.9.9" + version: 1.10.1 + resolution: "msgpackr@npm:1.10.1" dependencies: msgpackr-extract: ^3.0.2 dependenciesMeta: msgpackr-extract: optional: true - checksum: b63182d99f479d79f0d082fd2688ce7cf699b1aee71e20f28591c30b48743bb57868fdd72656759a892891072d186d864702c756434520709e8fe7e0d350a119 + checksum: e422d18b01051598b23701eebeb4b9e2c686b9c7826b20f564724837ba2b5cd4af74c91a549eaeaf8186645cc95e8196274a4a19442aa3286ac611b98069c194 languageName: node linkType: hard @@ -10618,27 +10240,25 @@ __metadata: linkType: hard "multiformats@npm:^12.0.1": - version: 12.0.1 - resolution: "multiformats@npm:12.0.1" - checksum: 227f5a0bb835e998d105028ffaa4cec944b6461c7b7f3ddc4f3aca22f6917ad59643302898c67af167a6f9d7abddcab094e501488664e2b9437f0b6a9b2ab68d + version: 12.1.3 + resolution: "multiformats@npm:12.1.3" + checksum: 1060488612f8e6729c600f47a8741b91aa6e7b807ce165eca3c8cf07ab7465d2d9b212415a9c18886938b9e35b30ea7b9ae19b5ab5122589c65063440643e6bb languageName: node linkType: hard -"nanoid@npm:^3.3.6": - version: 3.3.6 - resolution: "nanoid@npm:3.3.6" - bin: - nanoid: bin/nanoid.cjs - checksum: 7d0eda657002738aa5206107bd0580aead6c95c460ef1bdd0b1a87a9c7ae6277ac2e9b945306aaa5b32c6dcb7feaf462d0f552e7f8b5718abfc6ead5c94a71b3 +"multiformats@npm:^13.0.0": + version: 13.0.1 + resolution: "multiformats@npm:13.0.1" + checksum: 63e5d6ee2c2a1d1e8fbd4b8c76fa41cf3d8204ceed1d57bc44cb30ff3d06b880ad58c3de52ae6d4397a662a6f1e3285dae74ee5d445fd1597516e1baec96d22a languageName: node linkType: hard -"nanoid@npm:^4.0.0": - version: 4.0.2 - resolution: "nanoid@npm:4.0.2" +"nanoid@npm:^3.3.7": + version: 3.3.7 + resolution: "nanoid@npm:3.3.7" bin: - nanoid: bin/nanoid.js - checksum: 747c399cea4664dd0be1d0ec498ffd1ef8f1f5221676fc8b577e3f46f66d9afcddb9595d63d19a2e78d0bc6cc33984f65e66bf1682c850b9e26288883d96b53f + nanoid: bin/nanoid.cjs + checksum: d36c427e530713e4ac6567d488b489a36582ef89da1d6d4e3b87eded11eb10d7042a877958c6f104929809b2ab0bafa17652b076cdf84324aa75b30b722204f2 languageName: node linkType: hard @@ -10649,22 +10269,6 @@ __metadata: languageName: node linkType: hard -"native-fetch@npm:^4.0.2": - version: 4.0.2 - resolution: "native-fetch@npm:4.0.2" - peerDependencies: - undici: "*" - checksum: 11e6d075aa03d40665a5fc438c56b535622fb4ee98eb2b035277c5ba47733cb4c7bc3ddb45e5ab8154869b509fc18ca1c0188ab271139ae89db14f9f552fc064 - languageName: node - linkType: hard - -"natural-compare-lite@npm:^1.4.0": - version: 1.4.0 - resolution: "natural-compare-lite@npm:1.4.0" - checksum: 5222ac3986a2b78dd6069ac62cbb52a7bf8ffc90d972ab76dfe7b01892485d229530ed20d0c62e79a6b363a663b273db3bde195a1358ce9e5f779d4453887225 - languageName: node - linkType: hard - "natural-compare@npm:^1.4.0": version: 1.4.0 resolution: "natural-compare@npm:1.4.0" @@ -10717,8 +10321,8 @@ __metadata: linkType: hard "node-fetch@npm:^2.6.12": - version: 2.6.12 - resolution: "node-fetch@npm:2.6.12" + version: 2.7.0 + resolution: "node-fetch@npm:2.7.0" dependencies: whatwg-url: ^5.0.0 peerDependencies: @@ -10726,7 +10330,7 @@ __metadata: peerDependenciesMeta: encoding: optional: true - checksum: 3bc1655203d47ee8e313c0d96664b9673a3d4dd8002740318e9d27d14ef306693a4b2ef8d6525775056fd912a19e23f3ac0d7111ad8925877b7567b29a625592 + checksum: d76d2f5edb451a3f05b15115ec89fc6be39de37c6089f1b6368df03b91e1633fd379a7e01b7ab05089a25034b2023d959b47e59759cb38d88341b2459e89d6e5 languageName: node linkType: hard @@ -10773,34 +10377,33 @@ __metadata: linkType: hard "node-gyp-build@npm:^4.3.0": - version: 4.6.0 - resolution: "node-gyp-build@npm:4.6.0" + version: 4.8.0 + resolution: "node-gyp-build@npm:4.8.0" bin: node-gyp-build: bin.js node-gyp-build-optional: optional.js node-gyp-build-test: build-test.js - checksum: 25d78c5ef1f8c24291f4a370c47ba52fcea14f39272041a90a7894cd50d766f7c8cb8fb06c0f42bf6f69b204b49d9be3c8fc344aac09714d5bdb95965499eb15 + checksum: b82a56f866034b559dd3ed1ad04f55b04ae381b22ec2affe74b488d1582473ca6e7f85fccf52da085812d3de2b0bf23109e752a57709ac7b9963951c710fea40 languageName: node linkType: hard "node-gyp@npm:latest": - version: 9.4.0 - resolution: "node-gyp@npm:9.4.0" + version: 10.0.1 + resolution: "node-gyp@npm:10.0.1" dependencies: env-paths: ^2.2.0 exponential-backoff: ^3.1.1 - glob: ^7.1.4 + glob: ^10.3.10 graceful-fs: ^4.2.6 - make-fetch-happen: ^11.0.3 - nopt: ^6.0.0 - npmlog: ^6.0.0 - rimraf: ^3.0.2 + make-fetch-happen: ^13.0.0 + nopt: ^7.0.0 + proc-log: ^3.0.0 semver: ^7.3.5 tar: ^6.1.2 - which: ^2.0.2 + which: ^4.0.0 bin: node-gyp: bin/node-gyp.js - checksum: 78b404e2e0639d64e145845f7f5a3cb20c0520cdaf6dda2f6e025e9b644077202ea7de1232396ba5bde3fee84cdc79604feebe6ba3ec84d464c85d407bb5da99 + checksum: 60a74e66d364903ce02049966303a57f898521d139860ac82744a5fdd9f7b7b3b61f75f284f3bfe6e6add3b8f1871ce305a1d41f775c7482de837b50c792223f languageName: node linkType: hard @@ -10811,10 +10414,10 @@ __metadata: languageName: node linkType: hard -"node-releases@npm:^2.0.13": - version: 2.0.13 - resolution: "node-releases@npm:2.0.13" - checksum: 17ec8f315dba62710cae71a8dad3cd0288ba943d2ece43504b3b1aa8625bf138637798ab470b1d9035b0545996f63000a8a926e0f6d35d0996424f8b6d36dda3 +"node-releases@npm:^2.0.14": + version: 2.0.14 + resolution: "node-releases@npm:2.0.14" + checksum: 59443a2f77acac854c42d321bf1b43dea0aef55cd544c6a686e9816a697300458d4e82239e2d794ea05f7bbbc8a94500332e2d3ac3f11f52e4b16cbe638b3c41 languageName: node linkType: hard @@ -10836,14 +10439,14 @@ __metadata: languageName: node linkType: hard -"nopt@npm:^6.0.0": - version: 6.0.0 - resolution: "nopt@npm:6.0.0" +"nopt@npm:^7.0.0": + version: 7.2.0 + resolution: "nopt@npm:7.2.0" dependencies: - abbrev: ^1.0.0 + abbrev: ^2.0.0 bin: nopt: bin/nopt.js - checksum: 82149371f8be0c4b9ec2f863cc6509a7fd0fa729929c009f3a58e4eb0c9e4cae9920e8f1f8eb46e7d032fec8fb01bede7f0f41a67eb3553b7b8e14fa53de1dac + checksum: a9c0f57fb8cb9cc82ae47192ca2b7ef00e199b9480eed202482c962d61b59a7fbe7541920b2a5839a97b42ee39e288c0aed770e38057a608d7f579389dfde410 languageName: node linkType: hard @@ -10876,23 +10479,11 @@ __metadata: linkType: hard "npm-run-path@npm:^5.1.0": - version: 5.1.0 - resolution: "npm-run-path@npm:5.1.0" + version: 5.2.0 + resolution: "npm-run-path@npm:5.2.0" dependencies: path-key: ^4.0.0 - checksum: dc184eb5ec239d6a2b990b43236845332ef12f4e0beaa9701de724aa797fe40b6bbd0157fb7639d24d3ab13f5d5cf22d223a19c6300846b8126f335f788bee66 - languageName: node - linkType: hard - -"npmlog@npm:^6.0.0": - version: 6.0.2 - resolution: "npmlog@npm:6.0.2" - dependencies: - are-we-there-yet: ^3.0.0 - console-control-strings: ^1.1.0 - gauge: ^4.0.3 - set-blocking: ^2.0.0 - checksum: ae238cd264a1c3f22091cdd9e2b106f684297d3c184f1146984ecbe18aaa86343953f26b9520dedd1b1372bc0316905b736c1932d778dbeb1fcf5a1001390e2a + checksum: c5325e016014e715689c4014f7e0be16cc4cbf529f32a1723e511bc4689b5f823b704d2bca61ac152ce2bda65e0205dc8b3ba0ec0f5e4c3e162d302f6f5b9efb languageName: node linkType: hard @@ -10903,10 +10494,10 @@ __metadata: languageName: node linkType: hard -"object-inspect@npm:^1.12.3, object-inspect@npm:^1.9.0": - version: 1.12.3 - resolution: "object-inspect@npm:1.12.3" - checksum: dabfd824d97a5f407e6d5d24810d888859f6be394d8b733a77442b277e0808860555176719c5905e765e3743a7cada6b8b0a3b85e5331c530fd418cc8ae991db +"object-inspect@npm:^1.13.1, object-inspect@npm:^1.9.0": + version: 1.13.1 + resolution: "object-inspect@npm:1.13.1" + checksum: 7d9fa9221de3311dcb5c7c307ee5dc011cdd31dc43624b7c184b3840514e118e05ef0002be5388304c416c0eb592feb46e983db12577fc47e47d5752fbbfb61f languageName: node linkType: hard @@ -10918,48 +10509,48 @@ __metadata: linkType: hard "object.assign@npm:^4.1.4": - version: 4.1.4 - resolution: "object.assign@npm:4.1.4" + version: 4.1.5 + resolution: "object.assign@npm:4.1.5" dependencies: - call-bind: ^1.0.2 - define-properties: ^1.1.4 + call-bind: ^1.0.5 + define-properties: ^1.2.1 has-symbols: ^1.0.3 object-keys: ^1.1.1 - checksum: 76cab513a5999acbfe0ff355f15a6a125e71805fcf53de4e9d4e082e1989bdb81d1e329291e1e4e0ae7719f0e4ef80e88fb2d367ae60500d79d25a6224ac8864 + checksum: f9aeac0541661370a1fc86e6a8065eb1668d3e771f7dbb33ee54578201336c057b21ee61207a186dd42db0c62201d91aac703d20d12a79fc79c353eed44d4e25 languageName: node linkType: hard -"object.fromentries@npm:^2.0.6": - version: 2.0.6 - resolution: "object.fromentries@npm:2.0.6" +"object.fromentries@npm:^2.0.7": + version: 2.0.7 + resolution: "object.fromentries@npm:2.0.7" dependencies: call-bind: ^1.0.2 - define-properties: ^1.1.4 - es-abstract: ^1.20.4 - checksum: 453c6d694180c0c30df451b60eaf27a5b9bca3fb43c37908fd2b78af895803dc631242bcf05582173afa40d8d0e9c96e16e8874b39471aa53f3ac1f98a085d85 + define-properties: ^1.2.0 + es-abstract: ^1.22.1 + checksum: 7341ce246e248b39a431b87a9ddd331ff52a454deb79afebc95609f94b1f8238966cf21f52188f2a353f0fdf83294f32f1ebf1f7826aae915ebad21fd0678065 languageName: node linkType: hard -"object.groupby@npm:^1.0.0": - version: 1.0.0 - resolution: "object.groupby@npm:1.0.0" +"object.groupby@npm:^1.0.1": + version: 1.0.1 + resolution: "object.groupby@npm:1.0.1" dependencies: call-bind: ^1.0.2 define-properties: ^1.2.0 - es-abstract: ^1.21.2 + es-abstract: ^1.22.1 get-intrinsic: ^1.2.1 - checksum: 64b00b287d57580111c958e7ff375c9b61811fa356f2cf0d35372d43cab61965701f00fac66c19fd8f49c4dfa28744bee6822379c69a73648ad03e09fcdeae70 + checksum: d7959d6eaaba358b1608066fc67ac97f23ce6f573dc8fc661f68c52be165266fcb02937076aedb0e42722fdda0bdc0bbf74778196ac04868178888e9fd3b78b5 languageName: node linkType: hard -"object.values@npm:^1.1.6": - version: 1.1.6 - resolution: "object.values@npm:1.1.6" +"object.values@npm:^1.1.7": + version: 1.1.7 + resolution: "object.values@npm:1.1.7" dependencies: call-bind: ^1.0.2 - define-properties: ^1.1.4 - es-abstract: ^1.20.4 - checksum: f6fff9fd817c24cfd8107f50fb33061d81cd11bacc4e3dbb3852e9ff7692fde4dbce823d4333ea27cd9637ef1b6690df5fbb61f1ed314fa2959598dc3ae23d8e + define-properties: ^1.2.0 + es-abstract: ^1.22.1 + checksum: f3e4ae4f21eb1cc7cebb6ce036d4c67b36e1c750428d7b7623c56a0db90edced63d08af8a316d81dfb7c41a3a5fa81b05b7cc9426e98d7da986b1682460f0777 languageName: node linkType: hard @@ -11022,18 +10613,6 @@ __metadata: languageName: node linkType: hard -"open@npm:^9.1.0": - version: 9.1.0 - resolution: "open@npm:9.1.0" - dependencies: - default-browser: ^4.0.0 - define-lazy-prop: ^3.0.0 - is-inside-container: ^1.0.0 - is-wsl: ^2.2.0 - checksum: 3993c0f61d51fed8ac290e99c9c3cf45d3b6cfb3e2aa2b74cafd312c3486c22fd81df16ac8f3ab91dd8a4e3e729a16fc2480cfc406c4833416cf908acf1ae7c9 - languageName: node - linkType: hard - "optionator@npm:^0.9.3": version: 0.9.3 resolution: "optionator@npm:0.9.3" @@ -11066,9 +10645,9 @@ __metadata: linkType: hard "ordered-binary@npm:^1.4.1": - version: 1.4.1 - resolution: "ordered-binary@npm:1.4.1" - checksum: 274940b4ef983562e11371c84415c265432a4e1337ab85f8e7669eeab6afee8f655c6c12ecee1cd121aaf399c32f5c781b0d50e460bd42da004eba16dcc66574 + version: 1.5.1 + resolution: "ordered-binary@npm:1.5.1" + checksum: ec4d3a6bd7f8c84afec9def1e599e7d460a45d11f94d07b16fdf62db4d2bc16405d79ef0277c2fdf86332fd2539761278981787d2ecf52376ade8b678104a0e6 languageName: node linkType: hard @@ -11133,23 +10712,34 @@ __metadata: languageName: node linkType: hard -"p-queue@npm:^7.2.0, p-queue@npm:^7.3.4": - version: 7.3.4 - resolution: "p-queue@npm:7.3.4" +"p-queue@npm:^7.3.4": + version: 7.4.1 + resolution: "p-queue@npm:7.4.1" dependencies: - eventemitter3: ^4.0.7 + eventemitter3: ^5.0.1 p-timeout: ^5.0.2 - checksum: a21b8a4dd75f64a4988e4468cc344d1b45132506ddd2c771932d3de446d108ee68713b629e0d3f0809c227bc10eafc613edde6ae741d9f60db89b6031e40921c + checksum: 1c6888aa994d399262a9fbdd49c7066f8359732397f7a42ecf03f22875a1d65899797b46413f97e44acc18dddafbcc101eb135c284714c931dbbc83c3967f450 languageName: node linkType: hard -"p-retry@npm:^5.0.0": - version: 5.1.2 - resolution: "p-retry@npm:5.1.2" +"p-queue@npm:^8.0.1": + version: 8.0.1 + resolution: "p-queue@npm:8.0.1" + dependencies: + eventemitter3: ^5.0.1 + p-timeout: ^6.1.2 + checksum: 84a27a5b1faf2dcc96b8c0e423c34b5984b241acc07353d3cc6d8d3d1dadefb250b4ec84ce278cb1c946466999c6bf2a36ff718a75810bad8e11c7ca47ce80f5 + languageName: node + linkType: hard + +"p-retry@npm:^6.0.0": + version: 6.2.0 + resolution: "p-retry@npm:6.2.0" dependencies: - "@types/retry": 0.12.1 + "@types/retry": 0.12.2 + is-network-error: ^1.0.0 retry: ^0.13.1 - checksum: f063c08b1adc3cf7c01de01eb2dbda841970229f9f229c5167ebf4e2080d8a38b1f4e6eccefac74bca97cfaf4436d0a0eeb0b551175b26bc8b3116195f61bba8 + checksum: 6003573c559ee812329c9c3ede7ba12a783fdc8dd70602116646e850c920b4597dc502fe001c3f9526fca4e93275045db7a27341c458e51db179c1374a01ac44 languageName: node linkType: hard @@ -11224,7 +10814,7 @@ __metadata: languageName: node linkType: hard -"parse-asn1@npm:^5.0.0, parse-asn1@npm:^5.1.5": +"parse-asn1@npm:^5.0.0, parse-asn1@npm:^5.1.6": version: 5.1.6 resolution: "parse-asn1@npm:5.1.6" dependencies: @@ -11419,25 +11009,14 @@ __metadata: languageName: node linkType: hard -"postcss@npm:^8.1.7, postcss@npm:^8.4.23": - version: 8.4.31 - resolution: "postcss@npm:8.4.31" - dependencies: - nanoid: ^3.3.6 - picocolors: ^1.0.0 - source-map-js: ^1.0.2 - checksum: 1d8611341b073143ad90486fcdfeab49edd243377b1f51834dc4f6d028e82ce5190e4f11bb2633276864503654fb7cab28e67abdc0fbf9d1f88cad4a0ff0beea - languageName: node - linkType: hard - -"postcss@npm:^8.4.26": - version: 8.4.27 - resolution: "postcss@npm:8.4.27" +"postcss@npm:^8.1.7, postcss@npm:^8.4.23, postcss@npm:^8.4.27": + version: 8.4.33 + resolution: "postcss@npm:8.4.33" dependencies: - nanoid: ^3.3.6 + nanoid: ^3.3.7 picocolors: ^1.0.0 source-map-js: ^1.0.2 - checksum: 1cdd0c298849df6cd65f7e646a3ba36870a37b65f55fd59d1a165539c263e9b4872a402bf4ed1ca1bc31f58b68b2835545e33ea1a23b161a1f8aa6d5ded81e78 + checksum: 6f98b2af4b76632a3de20c4f47bf0e984a1ce1a531cf11adcb0b1d63a6cbda0aae4165e578b66c32ca4879038e3eaad386a6be725a8fb4429c78e3c1ab858fe9 languageName: node linkType: hard @@ -11502,14 +11081,14 @@ __metadata: languageName: node linkType: hard -"pretty-format@npm:^29.0.0, pretty-format@npm:^29.6.2": - version: 29.6.2 - resolution: "pretty-format@npm:29.6.2" +"pretty-format@npm:^29.0.0, pretty-format@npm:^29.7.0": + version: 29.7.0 + resolution: "pretty-format@npm:29.7.0" dependencies: - "@jest/schemas": ^29.6.0 + "@jest/schemas": ^29.6.3 ansi-styles: ^5.0.0 react-is: ^18.0.0 - checksum: a0f972a44f959023c0df9cdfe9eed7540264d7f7ddf74667db8a5294444d5aa153fd47d20327df10ae86964e2ceec10e46ea06b1a5c9c12e02348b78c952c9fc + checksum: 032c1602383e71e9c0c02a01bbd25d6759d60e9c7cf21937dde8357aa753da348fcec5def5d1002c9678a8524d5fe099ad98861286550ef44de8808cc61e43b6 languageName: node linkType: hard @@ -11534,6 +11113,13 @@ __metadata: languageName: node linkType: hard +"proc-log@npm:^3.0.0": + version: 3.0.0 + resolution: "proc-log@npm:3.0.0" + checksum: 02b64e1b3919e63df06f836b98d3af002b5cd92655cab18b5746e37374bfb73e03b84fe305454614b34c25b485cc687a9eebdccf0242cda8fda2475dd2c97e02 + languageName: node + linkType: hard + "process-nextick-args@npm:~2.0.0": version: 2.0.1 resolution: "process-nextick-args@npm:2.0.1" @@ -11582,35 +11168,13 @@ __metadata: languageName: node linkType: hard -"protobufjs@npm:^7.0.0": - version: 7.2.4 - resolution: "protobufjs@npm:7.2.4" - dependencies: - "@protobufjs/aspromise": ^1.1.2 - "@protobufjs/base64": ^1.1.2 - "@protobufjs/codegen": ^2.0.4 - "@protobufjs/eventemitter": ^1.1.0 - "@protobufjs/fetch": ^1.1.0 - "@protobufjs/float": ^1.0.2 - "@protobufjs/inquire": ^1.1.0 - "@protobufjs/path": ^1.1.2 - "@protobufjs/pool": ^1.1.0 - "@protobufjs/utf8": ^1.1.0 - "@types/node": ">=13.7.0" - long: ^5.0.0 - checksum: a952cdf2a5e5250c16ae651b570849b6f5b20a5475c3eef63ffb290ad239aa2916adfc1cc676f7fc93c69f48113df268761c0c246f7f023118c85bdd1a170044 - languageName: node - linkType: hard - "protons-runtime@npm:^5.0.0": - version: 5.0.1 - resolution: "protons-runtime@npm:5.0.1" + version: 5.2.2 + resolution: "protons-runtime@npm:5.2.2" dependencies: - protobufjs: ^7.0.0 uint8arraylist: ^2.4.3 - peerDependencies: - uint8arraylist: ^2.3.2 - checksum: 08d14fb7ea57e6329765873bb7933189925cfd2a488419735fb2547b8075f06a34638bc3b95c24c803a6ac179b7b5cff9b0894b6adfa46c38b5b430424a77605 + uint8arrays: ^5.0.1 + checksum: b2c0c3612406eb49539e3f2be7e51bbb3333969b6c1052c8e2cf5cb3eb3aed86eb825eeecd3b5c579e02a545b658b90f046b7e06cc624a843130782bc8e22f6b languageName: node linkType: hard @@ -11673,41 +11237,43 @@ __metadata: linkType: hard "punycode@npm:^2.1.0": - version: 2.3.0 - resolution: "punycode@npm:2.3.0" - checksum: 39f760e09a2a3bbfe8f5287cf733ecdad69d6af2fe6f97ca95f24b8921858b91e9ea3c9eeec6e08cede96181b3bb33f95c6ffd8c77e63986508aa2e8159fa200 + version: 2.3.1 + resolution: "punycode@npm:2.3.1" + checksum: bb0a0ceedca4c3c57a9b981b90601579058903c62be23c5e8e843d2c2d4148a3ecf029d5133486fb0e1822b098ba8bba09e89d6b21742d02fa26bda6441a6fb2 languageName: node linkType: hard -"puppeteer-core@npm:21.3.5": - version: 21.3.5 - resolution: "puppeteer-core@npm:21.3.5" +"puppeteer-core@npm:21.7.0": + version: 21.7.0 + resolution: "puppeteer-core@npm:21.7.0" dependencies: - "@puppeteer/browsers": 1.7.1 - chromium-bidi: 0.4.28 + "@puppeteer/browsers": 1.9.1 + chromium-bidi: 0.5.2 cross-fetch: 4.0.0 debug: 4.3.4 - devtools-protocol: 0.0.1179426 - ws: 8.14.2 - checksum: c2e904e59c9d58ada08016ff893c6b574ab36848db8e0f214b6c02fef446cfa142d346384cb2bb49c39df45622caf6ef813e7cc10f613024e595aa4406fb77bc + devtools-protocol: 0.0.1203626 + ws: 8.16.0 + checksum: 8166137f9cee88509ec60340435442915f82419ed10054403a3c43a9d638013f13e2a80409aa4bb6b14ad671d4c2cf117d9f4d26c5db8992b05bba6fb09a1380 languageName: node linkType: hard "puppeteer@npm:^21.3.4": - version: 21.3.5 - resolution: "puppeteer@npm:21.3.5" + version: 21.7.0 + resolution: "puppeteer@npm:21.7.0" dependencies: - "@puppeteer/browsers": 1.7.1 + "@puppeteer/browsers": 1.9.1 cosmiconfig: 8.3.6 - puppeteer-core: 21.3.5 - checksum: 0bb3f0f2dead30f4a18b94066d73e27a0ec418ba3e1b73fc882ba4b747405c91ee2592eef0137b984f26bca5e52007543ecd01d1ac843dd68b8d78489a296f69 + puppeteer-core: 21.7.0 + bin: + puppeteer: lib/esm/puppeteer/node/cli.js + checksum: bb5f0f76f16cf09ae9a52d68a6ed7c8c06256b33e89c77898cba9b0f1863b9e76014d01b86371265fc7d1ea6b5f7c7a12e164159744145ee3a4251777ef59bfa languageName: node linkType: hard "pure-rand@npm:^6.0.0": - version: 6.0.2 - resolution: "pure-rand@npm:6.0.2" - checksum: 79de33876a4f515d759c48e98d00756bbd916b4ea260cc572d7adfa4b62cace9952e89f0241d0410214554503d25061140fe325c66f845213d2b1728ba8d413e + version: 6.0.4 + resolution: "pure-rand@npm:6.0.4" + checksum: e1c4e69f8bf7303e5252756d67c3c7551385cd34d94a1f511fe099727ccbab74c898c03a06d4c4a24a89b51858781057b83ebbfe740d984240cdc04fead36068 languageName: node linkType: hard @@ -11748,6 +11314,13 @@ __metadata: languageName: node linkType: hard +"race-signal@npm:^1.0.0, race-signal@npm:^1.0.1": + version: 1.0.2 + resolution: "race-signal@npm:1.0.2" + checksum: 01ea1f70059673cd239acbe9523eaf1649f3b02ec786b5266770d9b045018aa96e316150447f0a12e7b0f8aa02522deb23e7d3a2c3a58d37135c505f595f2e49 + languageName: node + linkType: hard + "randombytes@npm:^2.0.0, randombytes@npm:^2.0.1, randombytes@npm:^2.0.5, randombytes@npm:^2.1.0": version: 2.1.0 resolution: "randombytes@npm:2.1.0" @@ -11767,10 +11340,10 @@ __metadata: languageName: node linkType: hard -"rate-limiter-flexible@npm:^2.3.11": - version: 2.4.2 - resolution: "rate-limiter-flexible@npm:2.4.2" - checksum: 039e58b664991963ba2668a83d0406a72e5822683103acbe416854deb92ed834b840ce6e0acfea35917d9b49685bd53946ae47435a9f5916c2e7550395dec9dc +"rate-limiter-flexible@npm:^3.0.0": + version: 3.0.6 + resolution: "rate-limiter-flexible@npm:3.0.6" + checksum: 65cf8edacde55b78255d5de0286b1297ec897fee128f4f18734f36a230297d75433fa599ea423f7726b61b8f8ebf108d66ee09e7155e37aa25f506015b5166a4 languageName: node linkType: hard @@ -11830,7 +11403,7 @@ __metadata: languageName: node linkType: hard -"readable-stream@npm:^3.4.0, readable-stream@npm:^3.5.0, readable-stream@npm:^3.6.0": +"readable-stream@npm:^3.4.0, readable-stream@npm:^3.5.0, readable-stream@npm:^3.6.0, readable-stream@npm:^3.6.2": version: 3.6.2 resolution: "readable-stream@npm:3.6.2" dependencies: @@ -11884,21 +11457,21 @@ __metadata: languageName: node linkType: hard -"regenerator-runtime@npm:^0.13.11": - version: 0.13.11 - resolution: "regenerator-runtime@npm:0.13.11" - checksum: 27481628d22a1c4e3ff551096a683b424242a216fee44685467307f14d58020af1e19660bf2e26064de946bad7eff28950eae9f8209d55723e2d9351e632bbb4 +"regenerator-runtime@npm:^0.14.0": + version: 0.14.1 + resolution: "regenerator-runtime@npm:0.14.1" + checksum: 9f57c93277b5585d3c83b0cf76be47b473ae8c6d9142a46ce8b0291a04bb2cf902059f0f8445dcabb3fb7378e5fe4bb4ea1e008876343d42e46d3b484534ce38 languageName: node linkType: hard -"regexp.prototype.flags@npm:^1.5.0": - version: 1.5.0 - resolution: "regexp.prototype.flags@npm:1.5.0" +"regexp.prototype.flags@npm:^1.5.1": + version: 1.5.1 + resolution: "regexp.prototype.flags@npm:1.5.1" dependencies: call-bind: ^1.0.2 define-properties: ^1.2.0 - functions-have-names: ^1.2.3 - checksum: c541687cdbdfff1b9a07f6e44879f82c66bbf07665f9a7544c5fd16acdb3ec8d1436caab01662d2fbcad403f3499d49ab0b77fbc7ef29ef961d98cc4bc9755b4 + set-function-name: ^2.0.0 + checksum: 869edff00288442f8d7fa4c9327f91d85f3b3acf8cbbef9ea7a220345cf23e9241b6def9263d2c1ebcf3a316b0aa52ad26a43a84aa02baca3381717b3e307f47 languageName: node linkType: hard @@ -12001,20 +11574,7 @@ __metadata: languageName: node linkType: hard -"resolve@npm:^1.10.0, resolve@npm:^1.20.0, resolve@npm:^1.22.1, resolve@npm:^1.22.3": - version: 1.22.3 - resolution: "resolve@npm:1.22.3" - dependencies: - is-core-module: ^2.12.0 - path-parse: ^1.0.7 - supports-preserve-symlinks-flag: ^1.0.0 - bin: - resolve: bin/resolve - checksum: fb834b81348428cb545ff1b828a72ea28feb5a97c026a1cf40aa1008352c72811ff4d4e71f2035273dc536dcfcae20c13604ba6283c612d70fa0b6e44519c374 - languageName: node - linkType: hard - -"resolve@npm:^1.21.0": +"resolve@npm:^1.10.0, resolve@npm:^1.20.0, resolve@npm:^1.21.0, resolve@npm:^1.22.4": version: 1.22.8 resolution: "resolve@npm:1.22.8" dependencies: @@ -12037,20 +11597,7 @@ __metadata: languageName: node linkType: hard -"resolve@patch:resolve@^1.10.0#~builtin, resolve@patch:resolve@^1.20.0#~builtin, resolve@patch:resolve@^1.22.1#~builtin, resolve@patch:resolve@^1.22.3#~builtin": - version: 1.22.3 - resolution: "resolve@patch:resolve@npm%3A1.22.3#~builtin::version=1.22.3&hash=c3c19d" - dependencies: - is-core-module: ^2.12.0 - path-parse: ^1.0.7 - supports-preserve-symlinks-flag: ^1.0.0 - bin: - resolve: bin/resolve - checksum: ad59734723b596d0891321c951592ed9015a77ce84907f89c9d9307dd0c06e11a67906a3e628c4cae143d3e44898603478af0ddeb2bba3f229a9373efe342665 - languageName: node - linkType: hard - -"resolve@patch:resolve@^1.21.0#~builtin": +"resolve@patch:resolve@^1.10.0#~builtin, resolve@patch:resolve@^1.20.0#~builtin, resolve@patch:resolve@^1.21.0#~builtin, resolve@patch:resolve@^1.22.4#~builtin": version: 1.22.8 resolution: "resolve@patch:resolve@npm%3A1.22.8#~builtin::version=1.22.8&hash=c3c19d" dependencies: @@ -12125,9 +11672,9 @@ __metadata: languageName: node linkType: hard -"rollup@npm:^3.25.2": - version: 3.27.0 - resolution: "rollup@npm:3.27.0" +"rollup@npm:^3.27.1": + version: 3.29.4 + resolution: "rollup@npm:3.29.4" dependencies: fsevents: ~2.3.2 dependenciesMeta: @@ -12135,16 +11682,7 @@ __metadata: optional: true bin: rollup: dist/bin/rollup - checksum: f60c2c288d039dc14e1f6e7fd673b7fcb11928b5a781675791b37a741f63b7af110fc5d040d60d603175b6e03ff978bed83db018dd2ac542ef809fe1a5b32dae - languageName: node - linkType: hard - -"run-applescript@npm:^5.0.0": - version: 5.0.0 - resolution: "run-applescript@npm:5.0.0" - dependencies: - execa: ^5.0.0 - checksum: d00c2dbfa5b2d774de7451194b8b125f40f65fc183de7d9dcae97f57f59433586d3c39b9001e111c38bfa24c3436c99df1bb4066a2a0c90d39a8c4cd6889af77 + checksum: 8bb20a39c8d91130825159c3823eccf4dc2295c9a0a5c4ed851a5bf2167dbf24d9a29f23461a54c955e5506395e6cc188eafc8ab0e20399d7489fb33793b184e languageName: node linkType: hard @@ -12166,19 +11704,19 @@ __metadata: languageName: node linkType: hard -"safe-array-concat@npm:^1.0.0": - version: 1.0.0 - resolution: "safe-array-concat@npm:1.0.0" +"safe-array-concat@npm:^1.0.1": + version: 1.1.0 + resolution: "safe-array-concat@npm:1.1.0" dependencies: - call-bind: ^1.0.2 - get-intrinsic: ^1.2.0 + call-bind: ^1.0.5 + get-intrinsic: ^1.2.2 has-symbols: ^1.0.3 isarray: ^2.0.5 - checksum: f43cb98fe3b566327d0c09284de2b15fb85ae964a89495c1b1a5d50c7c8ed484190f4e5e71aacc167e16231940079b326f2c0807aea633d47cc7322f40a6b57f + checksum: 5c71eaa999168ee7474929f1cd3aae80f486353a651a094d9968936692cf90aa065224929a6486dcda66334a27dce4250a83612f9e0fef6dced1a925d3ac7296 languageName: node linkType: hard -"safe-buffer@npm:5.2.1, safe-buffer@npm:^5.0.1, safe-buffer@npm:^5.1.0, safe-buffer@npm:^5.1.1, safe-buffer@npm:^5.1.2, safe-buffer@npm:^5.2.0, safe-buffer@npm:~5.2.0": +"safe-buffer@npm:5.2.1, safe-buffer@npm:^5.0.1, safe-buffer@npm:^5.1.0, safe-buffer@npm:^5.1.1, safe-buffer@npm:^5.1.2, safe-buffer@npm:^5.2.0, safe-buffer@npm:^5.2.1, safe-buffer@npm:~5.2.0": version: 5.2.1 resolution: "safe-buffer@npm:5.2.1" checksum: b99c4b41fdd67a6aaf280fcd05e9ffb0813654894223afb78a31f14a19ad220bba8aba1cb14eddce1fcfb037155fe6de4e861784eb434f7d11ed58d1e70dd491 @@ -12193,13 +11731,13 @@ __metadata: linkType: hard "safe-regex-test@npm:^1.0.0": - version: 1.0.0 - resolution: "safe-regex-test@npm:1.0.0" + version: 1.0.2 + resolution: "safe-regex-test@npm:1.0.2" dependencies: - call-bind: ^1.0.2 - get-intrinsic: ^1.1.3 + call-bind: ^1.0.5 + get-intrinsic: ^1.2.2 is-regex: ^1.1.4 - checksum: bc566d8beb8b43c01b94e67de3f070fd2781685e835959bbbaaec91cc53381145ca91f69bd837ce6ec244817afa0a5e974fc4e40a2957f0aca68ac3add1ddd34 + checksum: 4af5ce05a2daa4f6d4bfd5a3c64fc33d6b886f6592122e93c0efad52f7147b9b605e5ffc03c269a1e3d1f8db2a23bc636628a961c9fd65bafdc09503330673fd languageName: node linkType: hard @@ -12238,9 +11776,9 @@ __metadata: linkType: hard "sax@npm:>=0.6.0": - version: 1.2.4 - resolution: "sax@npm:1.2.4" - checksum: d3df7d32b897a2c2f28e941f732c71ba90e27c24f62ee918bd4d9a8cfb3553f2f81e5493c7f0be94a11c1911b643a9108f231dd6f60df3fa9586b5d2e3e9e1fe + version: 1.3.0 + resolution: "sax@npm:1.3.0" + checksum: 238ab3a9ba8c8f8aaf1c5ea9120386391f6ee0af52f1a6a40bbb6df78241dd05d782f2359d614ac6aae08c4c4125208b456548a6cf68625aa4fe178486e63ecd languageName: node linkType: hard @@ -12294,18 +11832,35 @@ __metadata: linkType: hard "serialize-javascript@npm:^6.0.1": - version: 6.0.1 - resolution: "serialize-javascript@npm:6.0.1" + version: 6.0.2 + resolution: "serialize-javascript@npm:6.0.2" dependencies: randombytes: ^2.1.0 - checksum: 3c4f4cb61d0893b988415bdb67243637333f3f574e9e9cc9a006a2ced0b390b0b3b44aef8d51c951272a9002ec50885eefdc0298891bc27eb2fe7510ea87dc4f + checksum: c4839c6206c1d143c0f80763997a361310305751171dd95e4b57efee69b8f6edd8960a0b7fbfc45042aadff98b206d55428aee0dc276efe54f100899c7fa8ab7 languageName: node linkType: hard -"set-blocking@npm:^2.0.0": - version: 2.0.0 - resolution: "set-blocking@npm:2.0.0" - checksum: 6e65a05f7cf7ebdf8b7c75b101e18c0b7e3dff4940d480efed8aad3a36a4005140b660fa1d804cb8bce911cac290441dc728084a30504d3516ac2ff7ad607b02 +"set-function-length@npm:^1.1.1": + version: 1.2.0 + resolution: "set-function-length@npm:1.2.0" + dependencies: + define-data-property: ^1.1.1 + function-bind: ^1.1.2 + get-intrinsic: ^1.2.2 + gopd: ^1.0.1 + has-property-descriptors: ^1.0.1 + checksum: 63e34b45a2ff9abb419f52583481bf8ba597d33c0c85e56999085eb6078a0f7fbb4222051981c287feceeb358aa7789e7803cea2c82ac94c0ab37059596aff79 + languageName: node + linkType: hard + +"set-function-name@npm:^2.0.0": + version: 2.0.1 + resolution: "set-function-name@npm:2.0.1" + dependencies: + define-data-property: ^1.0.1 + functions-have-names: ^1.2.3 + has-property-descriptors: ^1.0.0 + checksum: 4975d17d90c40168eee2c7c9c59d023429f0a1690a89d75656306481ece0c3c1fb1ebcc0150ea546d1913e35fbd037bace91372c69e543e51fc5d1f31a9fa126 languageName: node linkType: hard @@ -12394,14 +11949,14 @@ __metadata: linkType: hard "shiki@npm:^0.14.1": - version: 0.14.3 - resolution: "shiki@npm:0.14.3" + version: 0.14.7 + resolution: "shiki@npm:0.14.7" dependencies: ansi-sequence-parser: ^1.1.0 jsonc-parser: ^3.2.0 vscode-oniguruma: ^1.7.0 vscode-textmate: ^8.0.0 - checksum: a4dd98e3b2a5dd8be207448f111ffb9ad2ed6c530f215714d8b61cbf91ec3edbabb09109b8ec58a26678aacd24e8161d5a9bc0c1fa1b4f64b27ceb180cbd0c89 + checksum: 2aec3b3519df977c4391df9e1825cb496e9a4d7e11395f05a0da77e4fa2f7c3d9d6e6ee94029ac699533017f2b25637ee68f6d39f05f311535c2704d0329b520 languageName: node linkType: hard @@ -12453,13 +12008,6 @@ __metadata: languageName: node linkType: hard -"slash@npm:^4.0.0": - version: 4.0.0 - resolution: "slash@npm:4.0.0" - checksum: da8e4af73712253acd21b7853b7e0dbba776b786e82b010a5bfc8b5051a1db38ed8aba8e1e8f400dd2c9f373be91eb1c42b66e91abb407ff42b10feece5e1d2d - languageName: node - linkType: hard - "smart-buffer@npm:^4.2.0": version: 4.2.0 resolution: "smart-buffer@npm:4.2.0" @@ -12467,18 +12015,7 @@ __metadata: languageName: node linkType: hard -"socks-proxy-agent@npm:^7.0.0": - version: 7.0.0 - resolution: "socks-proxy-agent@npm:7.0.0" - dependencies: - agent-base: ^6.0.2 - debug: ^4.3.3 - socks: ^2.6.2 - checksum: 720554370154cbc979e2e9ce6a6ec6ced205d02757d8f5d93fe95adae454fc187a5cbfc6b022afab850a5ce9b4c7d73e0f98e381879cf45f66317a4895953846 - languageName: node - linkType: hard - -"socks-proxy-agent@npm:^8.0.2": +"socks-proxy-agent@npm:^8.0.1, socks-proxy-agent@npm:^8.0.2": version: 8.0.2 resolution: "socks-proxy-agent@npm:8.0.2" dependencies: @@ -12489,7 +12026,7 @@ __metadata: languageName: node linkType: hard -"socks@npm:^2.6.2, socks@npm:^2.7.1": +"socks@npm:^2.7.1": version: 2.7.1 resolution: "socks@npm:2.7.1" dependencies: @@ -12540,6 +12077,13 @@ __metadata: languageName: node linkType: hard +"source-map@npm:^0.7.4": + version: 0.7.4 + resolution: "source-map@npm:0.7.4" + checksum: 01cc5a74b1f0e1d626a58d36ad6898ea820567e87f18dfc9d24a9843a351aaa2ec09b87422589906d6ff1deed29693e176194dc88bcae7c9a852dc74b311dbf5 + languageName: node + linkType: hard + "spawn-command@npm:0.0.2, spawn-command@npm:^0.0.2-1": version: 0.0.2 resolution: "spawn-command@npm:0.0.2" @@ -12575,9 +12119,9 @@ __metadata: linkType: hard "spdx-license-ids@npm:^3.0.0": - version: 3.0.13 - resolution: "spdx-license-ids@npm:3.0.13" - checksum: 3469d85c65f3245a279fa11afc250c3dca96e9e847f2f79d57f466940c5bb8495da08a542646086d499b7f24a74b8d0b42f3fc0f95d50ff99af1f599f6360ad7 + version: 3.0.16 + resolution: "spdx-license-ids@npm:3.0.16" + checksum: 5cdaa85aaa24bd02f9353a2e357b4df0a4f205cb35655f3fd0a5674a4fb77081f28ffd425379214bc3be2c2b7593ce1215df6bcc75884aeee0a9811207feabe2 languageName: node linkType: hard @@ -12598,11 +12142,11 @@ __metadata: linkType: hard "ssri@npm:^10.0.0": - version: 10.0.4 - resolution: "ssri@npm:10.0.4" + version: 10.0.5 + resolution: "ssri@npm:10.0.5" dependencies: - minipass: ^5.0.0 - checksum: fb14da9f8a72b04eab163eb13a9dda11d5962cd2317f85457c4e0b575e9a6e0e3a6a87b5bf122c75cb36565830cd5f263fb457571bf6f1587eb5f95d095d6165 + minipass: ^7.0.3 + checksum: 0a31b65f21872dea1ed3f7c200d7bc1c1b91c15e419deca14f282508ba917cbb342c08a6814c7f68ca4ca4116dd1a85da2bbf39227480e50125a1ceffeecb750 languageName: node linkType: hard @@ -12673,20 +12217,13 @@ __metadata: languageName: node linkType: hard -"streamsearch@npm:^1.1.0": - version: 1.1.0 - resolution: "streamsearch@npm:1.1.0" - checksum: 1cce16cea8405d7a233d32ca5e00a00169cc0e19fbc02aa839959985f267335d435c07f96e5e0edd0eadc6d39c98d5435fb5bbbdefc62c41834eadc5622ad942 - languageName: node - linkType: hard - "streamx@npm:^2.15.0": - version: 2.15.1 - resolution: "streamx@npm:2.15.1" + version: 2.15.6 + resolution: "streamx@npm:2.15.6" dependencies: fast-fifo: ^1.1.0 queue-tick: ^1.0.1 - checksum: 6f2b4fed68caacd28efbd44d4264f5d3c2b81b0a5de14419333dac57f2075c49ae648df8d03db632a33587a6c8ab7cb9cdb4f9a2f8305be0c2cd79af35742b15 + checksum: 37a245f5cee4c33fcb8b018ccb935bad6eab423f05b0d14d018e63dbd2670bb109a69442e961a195b750c2c774f613c19476d11bd727d645eedb655d2dba234b languageName: node linkType: hard @@ -12707,7 +12244,7 @@ __metadata: languageName: node linkType: hard -"string-width-cjs@npm:string-width@^4.2.0, string-width@npm:^1.0.2 || 2 || 3 || 4, string-width@npm:^4.1.0, string-width@npm:^4.2.0, string-width@npm:^4.2.3": +"string-width-cjs@npm:string-width@^4.2.0, string-width@npm:^4.1.0, string-width@npm:^4.2.0, string-width@npm:^4.2.3": version: 4.2.3 resolution: "string-width@npm:4.2.3" dependencies: @@ -12729,36 +12266,36 @@ __metadata: languageName: node linkType: hard -"string.prototype.trim@npm:^1.2.7": - version: 1.2.7 - resolution: "string.prototype.trim@npm:1.2.7" +"string.prototype.trim@npm:^1.2.8": + version: 1.2.8 + resolution: "string.prototype.trim@npm:1.2.8" dependencies: call-bind: ^1.0.2 - define-properties: ^1.1.4 - es-abstract: ^1.20.4 - checksum: 05b7b2d6af63648e70e44c4a8d10d8cc457536df78b55b9d6230918bde75c5987f6b8604438c4c8652eb55e4fc9725d2912789eb4ec457d6995f3495af190c09 + define-properties: ^1.2.0 + es-abstract: ^1.22.1 + checksum: 49eb1a862a53aba73c3fb6c2a53f5463173cb1f4512374b623bcd6b43ad49dd559a06fb5789bdec771a40fc4d2a564411c0a75d35fb27e76bbe738c211ecff07 languageName: node linkType: hard -"string.prototype.trimend@npm:^1.0.6": - version: 1.0.6 - resolution: "string.prototype.trimend@npm:1.0.6" +"string.prototype.trimend@npm:^1.0.7": + version: 1.0.7 + resolution: "string.prototype.trimend@npm:1.0.7" dependencies: call-bind: ^1.0.2 - define-properties: ^1.1.4 - es-abstract: ^1.20.4 - checksum: 0fdc34645a639bd35179b5a08227a353b88dc089adf438f46be8a7c197fc3f22f8514c1c9be4629b3cd29c281582730a8cbbad6466c60f76b5f99cf2addb132e + define-properties: ^1.2.0 + es-abstract: ^1.22.1 + checksum: 2375516272fd1ba75992f4c4aa88a7b5f3c7a9ca308d963bcd5645adf689eba6f8a04ebab80c33e30ec0aefc6554181a3a8416015c38da0aa118e60ec896310c languageName: node linkType: hard -"string.prototype.trimstart@npm:^1.0.6": - version: 1.0.6 - resolution: "string.prototype.trimstart@npm:1.0.6" +"string.prototype.trimstart@npm:^1.0.7": + version: 1.0.7 + resolution: "string.prototype.trimstart@npm:1.0.7" dependencies: call-bind: ^1.0.2 - define-properties: ^1.1.4 - es-abstract: ^1.20.4 - checksum: 89080feef416621e6ef1279588994305477a7a91648d9436490d56010a1f7adc39167cddac7ce0b9884b8cdbef086987c4dcb2960209f2af8bac0d23ceff4f41 + define-properties: ^1.2.0 + es-abstract: ^1.22.1 + checksum: 13d0c2cb0d5ff9e926fa0bec559158b062eed2b68cd5be777ffba782c96b2b492944e47057274e064549b94dd27cf81f48b27a31fee8af5b574cff253e7eb613 languageName: node linkType: hard @@ -12872,9 +12409,9 @@ __metadata: languageName: node linkType: hard -"superagent@npm:^8.0.5": - version: 8.0.9 - resolution: "superagent@npm:8.0.9" +"superagent@npm:^8.1.2": + version: 8.1.2 + resolution: "superagent@npm:8.1.2" dependencies: component-emitter: ^1.3.0 cookiejar: ^2.1.4 @@ -12886,17 +12423,17 @@ __metadata: mime: 2.6.0 qs: ^6.11.0 semver: ^7.3.8 - checksum: 5d00cdc7ceb5570663da80604965750e6b1b8d7d7442b7791e285c62bcd8d578a8ead0242a2426432b59a255fb42eb3a196d636157538a1392e7b6c5f1624810 + checksum: f3601c5ccae34d5ba684a03703394b5d25931f4ae2e1e31a1de809f88a9400e997ece037f9accf148a21c408f950dc829db1e4e23576a7f9fe0efa79fd5c9d2f languageName: node linkType: hard "supertest@npm:^6.3.3": - version: 6.3.3 - resolution: "supertest@npm:6.3.3" + version: 6.3.4 + resolution: "supertest@npm:6.3.4" dependencies: methods: ^1.1.2 - superagent: ^8.0.5 - checksum: 38239e517f7ba62b7a139a79c5c48d55f8d67b5ff4b6e51d5b07732ca8bbc4a28ffa1b10916fbb403dd013a054dbf028edc5850057d9a43aecbff439d494673e + superagent: ^8.1.2 + checksum: 875c6fa7940f21e5be9bb646579cdb030d4057bf2da643e125e1f0480add1200395d2b17e10b8e54e1009efc63e047422501e9eb30e12828668498c0910f295f languageName: node linkType: hard @@ -12934,16 +12471,6 @@ __metadata: languageName: node linkType: hard -"synckit@npm:^0.8.5": - version: 0.8.5 - resolution: "synckit@npm:0.8.5" - dependencies: - "@pkgr/utils": ^2.3.1 - tslib: ^2.5.0 - checksum: 8a9560e5d8f3d94dc3cf5f7b9c83490ffa30d320093560a37b88f59483040771fd1750e76b9939abfbb1b5a23fd6dfbae77f6b338abffe7cae7329cd9b9bb86b - languageName: node - linkType: hard - "tapable@npm:^2.1.1, tapable@npm:^2.2.0": version: 2.2.1 resolution: "tapable@npm:2.2.1" @@ -12963,19 +12490,19 @@ __metadata: linkType: hard "tar-stream@npm:^3.1.5": - version: 3.1.6 - resolution: "tar-stream@npm:3.1.6" + version: 3.1.7 + resolution: "tar-stream@npm:3.1.7" dependencies: b4a: ^1.6.4 fast-fifo: ^1.2.0 streamx: ^2.15.0 - checksum: f3627f918581976e954ff03cb8d370551053796b82564f8c7ca8fac84c48e4d042026d0854fc222171a34ff9c682b72fae91be9c9b0a112d4c54f9e4f443e9c5 + checksum: 6393a6c19082b17b8dcc8e7fd349352bb29b4b8bfe1075912b91b01743ba6bb4298f5ff0b499a3bbaf82121830e96a1a59d4f21a43c0df339e54b01789cb8cc6 languageName: node linkType: hard "tar@npm:^6.1.11, tar@npm:^6.1.2": - version: 6.1.15 - resolution: "tar@npm:6.1.15" + version: 6.2.0 + resolution: "tar@npm:6.2.0" dependencies: chownr: ^2.0.0 fs-minipass: ^2.0.0 @@ -12983,19 +12510,19 @@ __metadata: minizlib: ^2.1.1 mkdirp: ^1.0.3 yallist: ^4.0.0 - checksum: f23832fceeba7578bf31907aac744ae21e74a66f4a17a9e94507acf460e48f6db598c7023882db33bab75b80e027c21f276d405e4a0322d58f51c7088d428268 + checksum: db4d9fe74a2082c3a5016630092c54c8375ff3b280186938cfd104f2e089c4fd9bad58688ef6be9cf186a889671bf355c7cda38f09bbf60604b281715ca57f5c languageName: node linkType: hard "terser-webpack-plugin@npm:^5.3.7": - version: 5.3.9 - resolution: "terser-webpack-plugin@npm:5.3.9" + version: 5.3.10 + resolution: "terser-webpack-plugin@npm:5.3.10" dependencies: - "@jridgewell/trace-mapping": ^0.3.17 + "@jridgewell/trace-mapping": ^0.3.20 jest-worker: ^27.4.5 schema-utils: ^3.1.1 serialize-javascript: ^6.0.1 - terser: ^5.16.8 + terser: ^5.26.0 peerDependencies: webpack: ^5.1.0 peerDependenciesMeta: @@ -13005,13 +12532,13 @@ __metadata: optional: true uglify-js: optional: true - checksum: 41705713d6f9cb83287936b21e27c658891c78c4392159f5148b5623f0e8c48559869779619b058382a4c9758e7820ea034695e57dc7c474b4962b79f553bc5f + checksum: bd6e7596cf815f3353e2a53e79cbdec959a1b0276f5e5d4e63e9d7c3c5bb5306df567729da287d1c7b39d79093e56863c569c42c6c24cc34c76aa313bd2cbcea languageName: node linkType: hard -"terser@npm:^5.16.8": - version: 5.19.2 - resolution: "terser@npm:5.19.2" +"terser@npm:^5.26.0": + version: 5.27.0 + resolution: "terser@npm:5.27.0" dependencies: "@jridgewell/source-map": ^0.3.3 acorn: ^8.8.2 @@ -13019,7 +12546,7 @@ __metadata: source-map-support: ~0.5.20 bin: terser: bin/terser - checksum: e059177775b4d4f4cff219ad89293175aefbd1b081252270444dc83e42a2c5f07824eb2a85eae6e22ef6eb7ef04b21af36dd7d1dd7cfb93912310e57d416a205 + checksum: c165052cfea061e8512e9b9ba42a098c2ff6382886ae122b040fd5b6153443070cc2dcb4862269f1669c09c716763e856125a355ff984aa72be525d6fffd8729 languageName: node linkType: hard @@ -13049,11 +12576,11 @@ __metadata: linkType: hard "thingies@npm:^1.11.1": - version: 1.12.0 - resolution: "thingies@npm:1.12.0" + version: 1.16.0 + resolution: "thingies@npm:1.16.0" peerDependencies: tslib: ^2 - checksum: 04b75d264e1880676fb9f400b2c0e069abdd00de1fa006d9daee527ad6d899828169fd46bb17e7cabf2802b7dbd64053106e29e7a7aa271659f5b41b3a11657f + checksum: 9afbe70a9777fc31ac2567d06c9f64511585437298948330c39c076c6bd54bae6a700dee4cf4074486ef26c83f51031241ef4f4309c386555fb016a93be8aa06 languageName: node linkType: hard @@ -13064,13 +12591,6 @@ __metadata: languageName: node linkType: hard -"titleize@npm:^3.0.0": - version: 3.0.0 - resolution: "titleize@npm:3.0.0" - checksum: 71fbbeabbfb36ccd840559f67f21e356e1d03da2915b32d2ae1a60ddcc13a124be2739f696d2feb884983441d159a18649e8d956648d591bdad35c430a6b6d28 - languageName: node - linkType: hard - "tmpl@npm:1.0.5": version: 1.0.5 resolution: "tmpl@npm:1.0.5" @@ -13141,11 +12661,11 @@ __metadata: linkType: hard "ts-api-utils@npm:^1.0.1": - version: 1.0.1 - resolution: "ts-api-utils@npm:1.0.1" + version: 1.0.3 + resolution: "ts-api-utils@npm:1.0.3" peerDependencies: typescript: ">=4.2.0" - checksum: 78794fc7270d295b36c1ac613465b5dc7e7226907a533125b30f177efef9dd630d4e503b00be31b44335eb2ebf9e136ebe97353f8fc5d383885d5fead9d54c09 + checksum: 441cc4489d65fd515ae6b0f4eb8690057add6f3b6a63a36073753547fb6ce0c9ea0e0530220a0b282b0eec535f52c4dfc315d35f8a4c9a91c0def0707a714ca6 languageName: node linkType: hard @@ -13232,23 +12752,24 @@ __metadata: linkType: hard "ts-loader@npm:^9.4.4": - version: 9.4.4 - resolution: "ts-loader@npm:9.4.4" + version: 9.5.1 + resolution: "ts-loader@npm:9.5.1" dependencies: chalk: ^4.1.0 enhanced-resolve: ^5.0.0 micromatch: ^4.0.0 semver: ^7.3.4 + source-map: ^0.7.4 peerDependencies: typescript: "*" webpack: ^5.0.0 - checksum: 8e5e6b839b0edfa40d2156c880d88ccab58226894ea5978221bc48c7db3215e2e856bfd0093f148e925a2befc42d6c94cafa9a994a7da274541efaa916012b63 + checksum: 7cf396e656d905388ea2a9b5e82f16d3c955fda8d3df2fbf219f4bee16ff50a3c995c44ae3e584634e9443f056cec70bb3151add3917ffb4588ecd7394bac0ec languageName: node linkType: hard "ts-node@npm:^10.9.1": - version: 10.9.1 - resolution: "ts-node@npm:10.9.1" + version: 10.9.2 + resolution: "ts-node@npm:10.9.2" dependencies: "@cspotcode/source-map-support": ^0.8.0 "@tsconfig/node10": ^1.0.7 @@ -13280,7 +12801,7 @@ __metadata: ts-node-script: dist/bin-script.js ts-node-transpile-only: dist/bin-transpile.js ts-script: dist/bin-script-deprecated.js - checksum: 090adff1302ab20bd3486e6b4799e90f97726ed39e02b39e566f8ab674fd5bd5f727f43615debbfc580d33c6d9d1c6b1b3ce7d8e3cca3e20530a145ffa232c35 + checksum: fde256c9073969e234526e2cfead42591b9a2aec5222bac154b0de2fa9e4ceb30efcd717ee8bc785a56f3a119bdd5aa27b333d9dbec94ed254bd26f8944c67ac languageName: node linkType: hard @@ -13300,15 +12821,15 @@ __metadata: languageName: node linkType: hard -"tsconfig-paths@npm:^3.10.1, tsconfig-paths@npm:^3.14.2": - version: 3.14.2 - resolution: "tsconfig-paths@npm:3.14.2" +"tsconfig-paths@npm:^3.10.1, tsconfig-paths@npm:^3.15.0": + version: 3.15.0 + resolution: "tsconfig-paths@npm:3.15.0" dependencies: "@types/json5": ^0.0.29 json5: ^1.0.2 minimist: ^1.2.6 strip-bom: ^3.0.0 - checksum: a6162eaa1aed680537f93621b82399c7856afd10ec299867b13a0675e981acac4e0ec00896860480efc59fc10fd0b16fdc928c0b885865b52be62cadac692447 + checksum: 59f35407a390d9482b320451f52a411a256a130ff0e7543d18c6f20afab29ac19fbe55c360a93d6476213cc335a4d76ce90f67df54c4e9037f7d240920832201 languageName: node linkType: hard @@ -13326,20 +12847,13 @@ __metadata: languageName: node linkType: hard -"tslib@npm:^2.0.1": +"tslib@npm:^2.0.1, tslib@npm:^2.1.0, tslib@npm:^2.4.0, tslib@npm:^2.5.0": version: 2.6.2 resolution: "tslib@npm:2.6.2" checksum: 329ea56123005922f39642318e3d1f0f8265d1e7fcb92c633e0809521da75eeaca28d2cf96d7248229deb40e5c19adf408259f4b9640afd20d13aecc1430f3ad languageName: node linkType: hard -"tslib@npm:^2.1.0, tslib@npm:^2.4.0, tslib@npm:^2.5.0, tslib@npm:^2.6.0": - version: 2.6.1 - resolution: "tslib@npm:2.6.1" - checksum: b0d176d176487905b66ae4d5856647df50e37beea7571c53b8d10ba9222c074b81f1410fb91da13debaf2cbc970663609068bdebafa844ea9d69b146527c38fe - languageName: node - linkType: hard - "tsscmp@npm:1.0.6": version: 1.0.6 resolution: "tsscmp@npm:1.0.6" @@ -13510,12 +13024,12 @@ __metadata: linkType: hard "typescript@npm:^5.0.4": - version: 5.1.6 - resolution: "typescript@npm:5.1.6" + version: 5.3.3 + resolution: "typescript@npm:5.3.3" bin: tsc: bin/tsc tsserver: bin/tsserver - checksum: b2f2c35096035fe1f5facd1e38922ccb8558996331405eb00a5111cc948b2e733163cc22fab5db46992aba7dd520fff637f2c1df4996ff0e134e77d3249a7350 + checksum: 2007ccb6e51bbbf6fde0a78099efe04dc1c3dfbdff04ca3b6a8bc717991862b39fd6126c0c3ebf2d2d98ac5e960bcaa873826bb2bb241f14277034148f41f6a2 languageName: node linkType: hard @@ -13540,61 +13054,49 @@ __metadata: linkType: hard "typescript@patch:typescript@^5.0.4#~builtin": - version: 5.1.6 - resolution: "typescript@patch:typescript@npm%3A5.1.6#~builtin::version=5.1.6&hash=5da071" + version: 5.3.3 + resolution: "typescript@patch:typescript@npm%3A5.3.3#~builtin::version=5.3.3&hash=f3b441" bin: tsc: bin/tsc tsserver: bin/tsserver - checksum: f53bfe97f7c8b2b6d23cf572750d4e7d1e0c5fff1c36d859d0ec84556a827b8785077bc27676bf7e71fae538e517c3ecc0f37e7f593be913d884805d931bc8be - languageName: node - linkType: hard - -"uint8-varint@npm:^1.0.1, uint8-varint@npm:^1.0.2": - version: 1.0.6 - resolution: "uint8-varint@npm:1.0.6" - dependencies: - byte-access: ^1.0.0 - longbits: ^1.1.0 - uint8arraylist: ^2.0.0 - uint8arrays: ^4.0.2 - checksum: 9d97312b9d032cc20976c3d9ea9e7548bc761a5f4483156e349d5dcd9ffed7c71f597dc564591cbe93816ee39d258751dee2efa6b43859d3662c83ad1cf6cd1a + checksum: f61375590b3162599f0f0d5b8737877ac0a7bc52761dbb585d67e7b8753a3a4c42d9a554c4cc929f591ffcf3a2b0602f65ae3ce74714fd5652623a816862b610 languageName: node linkType: hard "uint8-varint@npm:^2.0.0, uint8-varint@npm:^2.0.1": - version: 2.0.1 - resolution: "uint8-varint@npm:2.0.1" + version: 2.0.3 + resolution: "uint8-varint@npm:2.0.3" dependencies: uint8arraylist: ^2.0.0 - uint8arrays: ^4.0.2 - checksum: b03431e7fb87224726524b6dcc4ac0ff2c61c122134be0b16394c04bac4a3b59328800f302e3d3df7715bdb676b2e1fd0a17332cf6dbdb148cbb9f81332cedef + uint8arrays: ^5.0.0 + checksum: 8c20b02f803ade88d3aad0c01368e8bfebedca3cb43f2d74347f32b92a219ab734b8df4bb0dee5540261de984896ef4ffb3c58e83b200d4b1d01a7e3f7757dee languageName: node linkType: hard -"uint8arraylist@npm:^2.0.0, uint8arraylist@npm:^2.4.1, uint8arraylist@npm:^2.4.3": - version: 2.4.3 - resolution: "uint8arraylist@npm:2.4.3" +"uint8arraylist@npm:^2.0.0, uint8arraylist@npm:^2.4.1, uint8arraylist@npm:^2.4.3, uint8arraylist@npm:^2.4.7": + version: 2.4.8 + resolution: "uint8arraylist@npm:2.4.8" dependencies: - uint8arrays: ^4.0.2 - checksum: 95225fe2b8f6a4d8919b6c8e3dcff32ced35a08a53efaef757a50bc0082fb5b91f09e7e46f0d1c234243899930f7cb02ef9eb44b97ce03e2381d416de15e16c9 + uint8arrays: ^5.0.1 + checksum: 8259124cf5c7acd29edeed346489d898f3eb12f129dadedb1c263ad8d637e1a2f689968934a94c16804e39f6e8765178507be6d7b3c3c6b67147ad7546d34186 languageName: node linkType: hard -"uint8arrays@npm:^4.0.2": - version: 4.0.4 - resolution: "uint8arrays@npm:4.0.4" +"uint8arrays@npm:^4.0.4, uint8arrays@npm:^4.0.6": + version: 4.0.10 + resolution: "uint8arrays@npm:4.0.10" dependencies: - multiformats: ^11.0.0 - checksum: 49b2f53cf41aecd18b4623eea71d4e32fbcc572341cc687bb3e6e3372122b022079b2f0beca7c21648b1bd8a019ee596c71861043babc677e5c74773071e5d55 + multiformats: ^12.0.1 + checksum: 784677a00f67d18d3aaaf441422b4055576e1ab76dbf276e474b86c91ddb95945ac1cc95a97979ab1f3b3c9a0ebeea74dd803ec6056adbd1ee6ef2f231f00f97 languageName: node linkType: hard -"uint8arrays@npm:^4.0.4, uint8arrays@npm:^4.0.6": - version: 4.0.6 - resolution: "uint8arrays@npm:4.0.6" +"uint8arrays@npm:^5.0.0, uint8arrays@npm:^5.0.1": + version: 5.0.1 + resolution: "uint8arrays@npm:5.0.1" dependencies: - multiformats: ^12.0.1 - checksum: 0d55d74fe8d791ee24396bf6175ffe8ff73aae763cfaca5bf774e43315ee57bc69cc3af854de5e7b20bc7e6b7bde731f73a478bc43c295ea8115bff8a49621e0 + multiformats: ^13.0.0 + checksum: 29b27d41e1b5fe2b3de0ce502556e9ac7caabf53c0a40f27d3654c4cb08f06913b14b7722325a1a4287664d15938abf48dd987340d966ca61c2daa40030b5f82 languageName: node linkType: hard @@ -13620,12 +13122,10 @@ __metadata: languageName: node linkType: hard -"undici@npm:^5.12.0": - version: 5.22.1 - resolution: "undici@npm:5.22.1" - dependencies: - busboy: ^1.6.0 - checksum: 048a3365f622be44fb319316cedfaa241c59cf7f3368ae7667a12323447e1822e8cc3d00f6956c852d1478a6fde1cbbe753f49e05f2fdaed229693e716ebaf35 +"undici-types@npm:~5.26.4": + version: 5.26.5 + resolution: "undici-types@npm:5.26.5" + checksum: 3192ef6f3fd5df652f2dc1cd782b49d6ff14dc98e5dced492aa8a8c65425227da5da6aafe22523c67f035a272c599bb89cfe803c1db6311e44bed3042fc25487 languageName: node linkType: hard @@ -13662,9 +13162,9 @@ __metadata: linkType: hard "universalify@npm:^2.0.0": - version: 2.0.0 - resolution: "universalify@npm:2.0.0" - checksum: 2406a4edf4a8830aa6813278bab1f953a8e40f2f63a37873ffa9a3bc8f9745d06cc8e88f3572cb899b7e509013f7f6fcc3e37e8a6d914167a5381d8440518c44 + version: 2.0.1 + resolution: "universalify@npm:2.0.1" + checksum: ecd8469fe0db28e7de9e5289d32bd1b6ba8f7183db34f3bfc4ca53c49891c2d6aa05f3fb3936a81285a905cc509fb641a0c3fc131ec786167eff41236ae32e60 languageName: node linkType: hard @@ -13675,13 +13175,6 @@ __metadata: languageName: node linkType: hard -"untildify@npm:^4.0.0": - version: 4.0.0 - resolution: "untildify@npm:4.0.0" - checksum: 39ced9c418a74f73f0a56e1ba4634b4d959422dff61f4c72a8e39f60b99380c1b45ed776fbaa0a4101b157e4310d873ad7d114e8534ca02609b4916bb4187fb9 - languageName: node - linkType: hard - "unzipit@npm:^1.4.3": version: 1.4.3 resolution: "unzipit@npm:1.4.3" @@ -13698,9 +13191,9 @@ __metadata: languageName: node linkType: hard -"update-browserslist-db@npm:^1.0.11": - version: 1.0.11 - resolution: "update-browserslist-db@npm:1.0.11" +"update-browserslist-db@npm:^1.0.13": + version: 1.0.13 + resolution: "update-browserslist-db@npm:1.0.13" dependencies: escalade: ^3.1.1 picocolors: ^1.0.0 @@ -13708,7 +13201,7 @@ __metadata: browserslist: ">= 4.21.0" bin: update-browserslist-db: cli.js - checksum: b98327518f9a345c7cad5437afae4d2ae7d865f9779554baf2a200fdf4bac4969076b679b1115434bd6557376bdd37ca7583d0f9b8f8e302d7d4cc1e91b5f231 + checksum: 1e47d80182ab6e4ad35396ad8b61008ae2a1330221175d0abd37689658bdb61af9b705bfc41057fd16682474d79944fb2d86767c5ed5ae34b6276b9bed353322 languageName: node linkType: hard @@ -13770,13 +13263,13 @@ __metadata: linkType: hard "v8-to-istanbul@npm:^9.0.1": - version: 9.1.0 - resolution: "v8-to-istanbul@npm:9.1.0" + version: 9.2.0 + resolution: "v8-to-istanbul@npm:9.2.0" dependencies: "@jridgewell/trace-mapping": ^0.3.12 "@types/istanbul-lib-coverage": ^2.0.1 - convert-source-map: ^1.6.0 - checksum: 2069d59ee46cf8d83b4adfd8a5c1a90834caffa9f675e4360f1157ffc8578ef0f763c8f32d128334424159bb6b01f3876acd39cd13297b2769405a9da241f8d1 + convert-source-map: ^2.0.0 + checksum: 31ef98c6a31b1dab6be024cf914f235408cd4c0dc56a5c744a5eea1a9e019ba279e1b6f90d695b78c3186feed391ed492380ccf095009e2eb91f3d058f0b4491 languageName: node linkType: hard @@ -13790,13 +13283,6 @@ __metadata: languageName: node linkType: hard -"varint@npm:^6.0.0": - version: 6.0.0 - resolution: "varint@npm:6.0.0" - checksum: 7684113c9d497c01e40396e50169c502eb2176203219b96e1c5ac965a3e15b4892bd22b7e48d87148e10fffe638130516b6dbeedd0efde2b2d0395aa1772eea7 - languageName: node - linkType: hard - "vary@npm:^1.1.2": version: 1.1.2 resolution: "vary@npm:1.1.2" @@ -13827,35 +13313,34 @@ __metadata: linkType: hard "viem@npm:^1.2.5": - version: 1.5.0 - resolution: "viem@npm:1.5.0" + version: 1.21.4 + resolution: "viem@npm:1.21.4" dependencies: - "@adraffy/ens-normalize": 1.9.0 - "@noble/curves": 1.0.0 - "@noble/hashes": 1.3.0 - "@scure/bip32": 1.3.0 - "@scure/bip39": 1.2.0 - "@wagmi/chains": 1.6.0 - abitype: 0.9.3 - isomorphic-ws: 5.0.0 - ws: 8.12.0 + "@adraffy/ens-normalize": 1.10.0 + "@noble/curves": 1.2.0 + "@noble/hashes": 1.3.2 + "@scure/bip32": 1.3.2 + "@scure/bip39": 1.2.1 + abitype: 0.9.8 + isows: 1.0.3 + ws: 8.13.0 peerDependencies: typescript: ">=5.0.4" peerDependenciesMeta: typescript: optional: true - checksum: 28ff9e64a3076b339182a3bab5dc442222400da4c66d258bf35e961f9a2e555ba2b0f3b30bdb87f63142682530c25f0807113580fbf888f840930f39df4bd8e3 + checksum: c351fdea2d53d2d781ac73c964348b3b9fc5dd46f9eb53903e867705fc9e30a893cb9f2c8d7a00acdcdeca27d14eeebf976eed9f948c28c47018dc9211369117 languageName: node linkType: hard "vite@npm:^4.2.3": - version: 4.4.8 - resolution: "vite@npm:4.4.8" + version: 4.5.2 + resolution: "vite@npm:4.5.2" dependencies: esbuild: ^0.18.10 fsevents: ~2.3.2 - postcss: ^8.4.26 - rollup: ^3.25.2 + postcss: ^8.4.27 + rollup: ^3.27.1 peerDependencies: "@types/node": ">= 14" less: "*" @@ -13884,7 +13369,7 @@ __metadata: optional: true bin: vite: bin/vite.js - checksum: e8ffe688f8a7396b1357778f00cb06d1f3dadad200823c47a1955cf52774a0cbff5ac4d6a8f8d09e26c1d4e588e5815956f9eba02ae301e77a36c3d181a1bc86 + checksum: 9d1f84f703c2660aced34deee7f309278ed368880f66e9570ac115c793d91f7fffb80ab19c602b3c8bc1341fe23437d86a3fcca2a9ef82f7ef0cdac5a40d0c86 languageName: node linkType: hard @@ -13945,9 +13430,9 @@ __metadata: linkType: hard "web-streams-polyfill@npm:^3.0.3": - version: 3.2.1 - resolution: "web-streams-polyfill@npm:3.2.1" - checksum: b119c78574b6d65935e35098c2afdcd752b84268e18746606af149e3c424e15621b6f1ff0b42b2676dc012fc4f0d313f964b41a4b5031e525faa03997457da02 + version: 3.3.2 + resolution: "web-streams-polyfill@npm:3.3.2" + checksum: 0292f4113c1bda40d8e8ecebee39eb14cc2e2e560a65a6867980e394537a2645130e2c73f5ef6e641fd3697d2f71720ccf659aebaf69a9d5a773f653a0fdf39d languageName: node linkType: hard @@ -13991,12 +13476,13 @@ __metadata: linkType: hard "webpack-merge@npm:^5.7.3": - version: 5.9.0 - resolution: "webpack-merge@npm:5.9.0" + version: 5.10.0 + resolution: "webpack-merge@npm:5.10.0" dependencies: clone-deep: ^4.0.1 + flat: ^5.0.2 wildcard: ^2.0.0 - checksum: 64fe2c23aacc5f19684452a0e84ec02c46b990423aee6fcc5c18d7d471155bd14e9a6adb02bd3656eb3e0ac2532c8e97d69412ad14c97eeafe32fa6d10050872 + checksum: 1fe8bf5309add7298e1ac72fb3f2090e1dfa80c48c7e79fa48aa60b5961332c7d0d61efa8851acb805e6b91a4584537a347bc106e05e9aec87fa4f7088c62f2f languageName: node linkType: hard @@ -14008,8 +13494,8 @@ __metadata: linkType: hard "webpack@npm:^5.88.2": - version: 5.88.2 - resolution: "webpack@npm:5.88.2" + version: 5.89.0 + resolution: "webpack@npm:5.89.0" dependencies: "@types/eslint-scope": ^3.7.3 "@types/estree": ^1.0.0 @@ -14040,7 +13526,7 @@ __metadata: optional: true bin: webpack: bin/webpack.js - checksum: 79476a782da31a21f6dd38fbbd06b68da93baf6a62f0d08ca99222367f3b8668f5a1f2086b7bb78e23172e31fa6df6fa7ab09b25e827866c4fc4dc2b30443ce2 + checksum: 43fe0dbc30e168a685ef5a86759d5016a705f6563b39a240aa00826a80637d4a3deeb8062e709d6a4b05c63e796278244c84b04174704dc4a37bedb0f565c5ed languageName: node linkType: hard @@ -14076,20 +13562,20 @@ __metadata: languageName: node linkType: hard -"which-typed-array@npm:^1.1.10, which-typed-array@npm:^1.1.11, which-typed-array@npm:^1.1.2": - version: 1.1.11 - resolution: "which-typed-array@npm:1.1.11" +"which-typed-array@npm:^1.1.11, which-typed-array@npm:^1.1.13, which-typed-array@npm:^1.1.2": + version: 1.1.13 + resolution: "which-typed-array@npm:1.1.13" dependencies: available-typed-arrays: ^1.0.5 - call-bind: ^1.0.2 + call-bind: ^1.0.4 for-each: ^0.3.3 gopd: ^1.0.1 has-tostringtag: ^1.0.0 - checksum: 711ffc8ef891ca6597b19539075ec3e08bb9b4c2ca1f78887e3c07a977ab91ac1421940505a197758fb5939aa9524976d0a5bbcac34d07ed6faa75cedbb17206 + checksum: 3828a0d5d72c800e369d447e54c7620742a4cc0c9baf1b5e8c17e9b6ff90d8d861a3a6dd4800f1953dbf80e5e5cec954a289e5b4a223e3bee4aeb1f8c5f33309 languageName: node linkType: hard -"which@npm:^2.0.1, which@npm:^2.0.2": +"which@npm:^2.0.1": version: 2.0.2 resolution: "which@npm:2.0.2" dependencies: @@ -14100,12 +13586,14 @@ __metadata: languageName: node linkType: hard -"wide-align@npm:^1.1.5": - version: 1.1.5 - resolution: "wide-align@npm:1.1.5" +"which@npm:^4.0.0": + version: 4.0.0 + resolution: "which@npm:4.0.0" dependencies: - string-width: ^1.0.2 || 2 || 3 || 4 - checksum: d5fc37cd561f9daee3c80e03b92ed3e84d80dde3365a8767263d03dacfc8fa06b065ffe1df00d8c2a09f731482fcacae745abfbb478d4af36d0a891fad4834d3 + isexe: ^3.1.1 + bin: + node-which: bin/which.js + checksum: f17e84c042592c21e23c8195108cff18c64050b9efb8459589116999ea9da6dd1509e6a1bac3aeebefd137be00fabbb61b5c2bc0aa0f8526f32b58ee2f545651 languageName: node linkType: hard @@ -14131,21 +13619,21 @@ __metadata: linkType: hard "winston-transport@npm:^4.4.0, winston-transport@npm:^4.5.0": - version: 4.5.0 - resolution: "winston-transport@npm:4.5.0" + version: 4.6.0 + resolution: "winston-transport@npm:4.6.0" dependencies: logform: ^2.3.2 readable-stream: ^3.6.0 triple-beam: ^1.3.0 - checksum: a56e5678a80b88a73e77ed998fc6e19d0db19c989a356b137ec236782f2bf58ae4511b11c29163f99391fa4dc12102c7bc5738dcb6543f28877fa2819adc3ee9 + checksum: 19f06ebdbb57cb14cdd48a23145d418d3bbe538851053303f84f04a8a849bb530b78b1495a175059c1299f92945dc61d5421c4914fee32d9a41bc397d84f26d7 languageName: node linkType: hard "winston@npm:^3.10.0": - version: 3.10.0 - resolution: "winston@npm:3.10.0" + version: 3.11.0 + resolution: "winston@npm:3.11.0" dependencies: - "@colors/colors": 1.5.0 + "@colors/colors": ^1.6.0 "@dabh/diagnostics": ^2.0.2 async: ^3.2.3 is-stream: ^2.0.0 @@ -14156,7 +13644,7 @@ __metadata: stack-trace: 0.0.x triple-beam: ^1.3.0 winston-transport: ^4.5.0 - checksum: 47df0361220d12b46d1b3c98a1c380a3718321739d527a182ce7984fc20715e5b0b55db0bcd3fd076d1b1d3261903b890b053851cfd4bc028bda7951fa8ca2e0 + checksum: ca4454070f7a71b19f53c8c1765c59a013dab220edb49161b2e81917751d3e9edc3382430e4fb050feda04fb8463290ecab7cbc9240ec8d3d3b32a121849bbb0 languageName: node linkType: hard @@ -14214,9 +13702,9 @@ __metadata: languageName: node linkType: hard -"ws@npm:8.14.2": - version: 8.14.2 - resolution: "ws@npm:8.14.2" +"ws@npm:8.13.0": + version: 8.13.0 + resolution: "ws@npm:8.13.0" peerDependencies: bufferutil: ^4.0.1 utf-8-validate: ">=5.0.2" @@ -14225,13 +13713,13 @@ __metadata: optional: true utf-8-validate: optional: true - checksum: 3ca0dad26e8cc6515ff392b622a1467430814c463b3368b0258e33696b1d4bed7510bc7030f7b72838b9fdeb8dbd8839cbf808367d6aae2e1d668ce741d4308b + checksum: 53e991bbf928faf5dc6efac9b8eb9ab6497c69feeb94f963d648b7a3530a720b19ec2e0ec037344257e05a4f35bd9ad04d9de6f289615ffb133282031b18c61c languageName: node linkType: hard -"ws@npm:^8.13.0": - version: 8.13.0 - resolution: "ws@npm:8.13.0" +"ws@npm:8.16.0, ws@npm:^8.13.0": + version: 8.16.0 + resolution: "ws@npm:8.16.0" peerDependencies: bufferutil: ^4.0.1 utf-8-validate: ">=5.0.2" @@ -14240,21 +13728,11 @@ __metadata: optional: true utf-8-validate: optional: true - checksum: 53e991bbf928faf5dc6efac9b8eb9ab6497c69feeb94f963d648b7a3530a720b19ec2e0ec037344257e05a4f35bd9ad04d9de6f289615ffb133282031b18c61c - languageName: node - linkType: hard - -"xml2js@npm:^0.5.0": - version: 0.5.0 - resolution: "xml2js@npm:0.5.0" - dependencies: - sax: ">=0.6.0" - xmlbuilder: ~11.0.0 - checksum: 1aa71d62e5bc2d89138e3929b9ea46459157727759cbc62ef99484b778641c0cd21fb637696c052d901a22f82d092a3e740a16b4ce218e81ac59b933535124ea + checksum: feb3eecd2bae82fa8a8beef800290ce437d8b8063bdc69712725f21aef77c49cb2ff45c6e5e7fce622248f9c7abaee506bae0a9064067ffd6935460c7357321b languageName: node linkType: hard -"xml2js@npm:^0.6.0": +"xml2js@npm:^0.6.0, xml2js@npm:^0.6.2": version: 0.6.2 resolution: "xml2js@npm:0.6.2" dependencies: @@ -14300,9 +13778,9 @@ __metadata: linkType: hard "yaml@npm:^2.1.3": - version: 2.3.1 - resolution: "yaml@npm:2.3.1" - checksum: 2c7bc9a7cd4c9f40d3b0b0a98e370781b68b8b7c4515720869aced2b00d92f5da1762b4ffa947f9e795d6cd6b19f410bd4d15fdd38aca7bd96df59bd9486fb54 + version: 2.3.4 + resolution: "yaml@npm:2.3.4" + checksum: e6d1dae1c6383bcc8ba11796eef3b8c02d5082911c6723efeeb5ba50fc8e881df18d645e64de68e421b577296000bea9c75d6d9097c2f6699da3ae0406c030d8 languageName: node linkType: hard @@ -14323,22 +13801,7 @@ __metadata: languageName: node linkType: hard -"yargs@npm:17.7.1": - version: 17.7.1 - resolution: "yargs@npm:17.7.1" - dependencies: - cliui: ^8.0.1 - escalade: ^3.1.1 - get-caller-file: ^2.0.5 - require-directory: ^2.1.1 - string-width: ^4.2.3 - y18n: ^5.0.5 - yargs-parser: ^21.1.1 - checksum: 3d8a43c336a4942bc68080768664aca85c7bd406f018bad362fd255c41c8f4e650277f42fd65d543fce99e084124ddafee7bbfc1a5c6a8fda4cec78609dcf8d4 - languageName: node - linkType: hard - -"yargs@npm:^17.3.1, yargs@npm:^17.7.2": +"yargs@npm:17.7.2, yargs@npm:^17.3.1, yargs@npm:^17.7.2": version: 17.7.2 resolution: "yargs@npm:17.7.2" dependencies: From 8d12a82e6c700614ace2e6b8c5317529e967a8b3 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Jos=C3=A9=20Pedro=20Sousa?= Date: Tue, 23 Jan 2024 16:18:16 +0000 Subject: [PATCH 03/16] yarn --- boxes/yarn.lock | 1121 +++++----- yarn-project/yarn.lock | 4841 ++++++++++++++++++++++------------------ 2 files changed, 3290 insertions(+), 2672 deletions(-) diff --git a/boxes/yarn.lock b/boxes/yarn.lock index 3970be8d047..5719e430a8d 100644 --- a/boxes/yarn.lock +++ b/boxes/yarn.lock @@ -96,6 +96,7 @@ __metadata: "@aztec/aztec-ui": "npm:^0.1.14" "@aztec/aztec.js": "npm:^0.16.9" "@types/jest": "npm:^29.5.0" + "@types/mocha": "npm:^10.0.3" "@types/node": "npm:^20.5.9" "@types/react": "npm:^18.2.15" "@types/react-dom": "npm:^18.2.7" @@ -254,6 +255,7 @@ __metadata: "@aztec/foundation": "workspace:^" eslint: "npm:^8.35.0" lodash.chunk: "npm:^4.2.0" + lodash.times: "npm:^4.3.2" tslib: "npm:^2.4.0" languageName: node linkType: soft @@ -300,7 +302,6 @@ __metadata: resolution: "@aztec/types@portal:../yarn-project/types::locator=%40aztec%2Fboxes%40workspace%3A." dependencies: "@aztec/ethereum": "workspace:^" - "@aztec/foundation": "workspace:^" languageName: node linkType: soft @@ -322,25 +323,25 @@ __metadata: linkType: hard "@babel/core@npm:^7.11.6, @babel/core@npm:^7.12.3": - version: 7.23.7 - resolution: "@babel/core@npm:7.23.7" + version: 7.23.6 + resolution: "@babel/core@npm:7.23.6" dependencies: "@ampproject/remapping": "npm:^2.2.0" "@babel/code-frame": "npm:^7.23.5" "@babel/generator": "npm:^7.23.6" "@babel/helper-compilation-targets": "npm:^7.23.6" "@babel/helper-module-transforms": "npm:^7.23.3" - "@babel/helpers": "npm:^7.23.7" + "@babel/helpers": "npm:^7.23.6" "@babel/parser": "npm:^7.23.6" "@babel/template": "npm:^7.22.15" - "@babel/traverse": "npm:^7.23.7" + "@babel/traverse": "npm:^7.23.6" "@babel/types": "npm:^7.23.6" convert-source-map: "npm:^2.0.0" debug: "npm:^4.1.0" gensync: "npm:^1.0.0-beta.2" json5: "npm:^2.2.3" semver: "npm:^6.3.1" - checksum: 38c9934973d384ed83369712978453eac91dc3f22167404dbdb272b64f602e74728a6f37012c53ee57e521b8ae2da60097f050497d9b6a212d28b59cdfb2cd1d + checksum: a02bae7d916029b70706dc301535e1b31e5d216f55d4ee6f64a15825c6b69ee2c14c52a213d1497ec414e925ed4e9d897d41fb0d75df9fea28ed2c0008790e31 languageName: node linkType: hard @@ -465,14 +466,14 @@ __metadata: languageName: node linkType: hard -"@babel/helpers@npm:^7.23.7": - version: 7.23.8 - resolution: "@babel/helpers@npm:7.23.8" +"@babel/helpers@npm:^7.23.6": + version: 7.23.6 + resolution: "@babel/helpers@npm:7.23.6" dependencies: "@babel/template": "npm:^7.22.15" - "@babel/traverse": "npm:^7.23.7" + "@babel/traverse": "npm:^7.23.6" "@babel/types": "npm:^7.23.6" - checksum: d9fce49278a31aaa017a40c1fcdaa450999c49e33582cce8138058c58b1acbe3a2d2488f010f28e91dedf0d35795ea32f0ee18745bbb6c7f54052ae0fd7e6a3f + checksum: df1cf6607676ad36f52f652ec03536f2732d70aef5e76dba5c964e34d49f3c2d3dcf9fb3740db359f53071d74b64606a833d5ba156f79f437f71bfe06e2e7e19 languageName: node linkType: hard @@ -651,11 +652,11 @@ __metadata: linkType: hard "@babel/runtime@npm:^7.12.5": - version: 7.23.8 - resolution: "@babel/runtime@npm:7.23.8" + version: 7.23.6 + resolution: "@babel/runtime@npm:7.23.6" dependencies: regenerator-runtime: "npm:^0.14.0" - checksum: ba5e8fbb32ef04f6cab5e89c54a0497c2fde7b730595cc1af93496270314f13ff2c6a9360fdb2f0bdd4d6b376752ce3cf85642bd6b876969a6a62954934c2df8 + checksum: d886954e985ef8e421222f7a2848884d96a752e0020d3078b920dd104e672fdf23bcc6f51a44313a048796319f1ac9d09c2c88ec8cbb4e1f09174bcd3335b9ff languageName: node linkType: hard @@ -670,9 +671,9 @@ __metadata: languageName: node linkType: hard -"@babel/traverse@npm:^7.23.7": - version: 7.23.7 - resolution: "@babel/traverse@npm:7.23.7" +"@babel/traverse@npm:^7.23.6": + version: 7.23.6 + resolution: "@babel/traverse@npm:7.23.6" dependencies: "@babel/code-frame": "npm:^7.23.5" "@babel/generator": "npm:^7.23.6" @@ -684,7 +685,7 @@ __metadata: "@babel/types": "npm:^7.23.6" debug: "npm:^4.3.1" globals: "npm:^11.1.0" - checksum: e32fceb4249beec2bde83968ddffe17444221c1ee5cd18c543a2feaf94e3ca83f2a4dfbc2dcca87cf226e0105973e0fe3717063a21e982a9de9945615ab3f3f5 + checksum: 5b4ebb94a00a7e1daf111e4b0b45a7998d5b7598637a14e75e855e88cc1b702789e09a958726b5d599a003be1e9032dbdfde4b88ea6061332228738950d5582d languageName: node linkType: hard @@ -1240,13 +1241,13 @@ __metadata: linkType: hard "@humanwhocodes/config-array@npm:^0.11.13": - version: 0.11.14 - resolution: "@humanwhocodes/config-array@npm:0.11.14" + version: 0.11.13 + resolution: "@humanwhocodes/config-array@npm:0.11.13" dependencies: - "@humanwhocodes/object-schema": "npm:^2.0.2" - debug: "npm:^4.3.1" + "@humanwhocodes/object-schema": "npm:^2.0.1" + debug: "npm:^4.1.1" minimatch: "npm:^3.0.5" - checksum: 66f725b4ee5fdd8322c737cb5013e19fac72d4d69c8bf4b7feb192fcb83442b035b92186f8e9497c220e58b2d51a080f28a73f7899bc1ab288c3be172c467541 + checksum: d76ca802d853366094d0e98ff0d0994117fc8eff96649cd357b15e469e428228f597cd2e929d54ab089051684949955f16ee905bb19f7b2f0446fb377157be7a languageName: node linkType: hard @@ -1257,10 +1258,10 @@ __metadata: languageName: node linkType: hard -"@humanwhocodes/object-schema@npm:^2.0.2": - version: 2.0.2 - resolution: "@humanwhocodes/object-schema@npm:2.0.2" - checksum: 6fd83dc320231d71c4541d0244051df61f301817e9f9da9fd4cb7e44ec8aacbde5958c1665b0c419401ab935114fdf532a6ad5d4e7294b1af2f347dd91a6983f +"@humanwhocodes/object-schema@npm:^2.0.1": + version: 2.0.1 + resolution: "@humanwhocodes/object-schema@npm:2.0.1" + checksum: 9dba24e59fdb4041829d92b693aacb778add3b6f612aaa9c0774f3b650c11a378cc64f042a59da85c11dae33df456580a3c36837b953541aed6ff94294f97fac languageName: node linkType: hard @@ -1587,13 +1588,13 @@ __metadata: languageName: node linkType: hard -"@jridgewell/trace-mapping@npm:^0.3.12, @jridgewell/trace-mapping@npm:^0.3.17, @jridgewell/trace-mapping@npm:^0.3.18, @jridgewell/trace-mapping@npm:^0.3.20, @jridgewell/trace-mapping@npm:^0.3.9": - version: 0.3.22 - resolution: "@jridgewell/trace-mapping@npm:0.3.22" +"@jridgewell/trace-mapping@npm:^0.3.12, @jridgewell/trace-mapping@npm:^0.3.17, @jridgewell/trace-mapping@npm:^0.3.18, @jridgewell/trace-mapping@npm:^0.3.9": + version: 0.3.20 + resolution: "@jridgewell/trace-mapping@npm:0.3.20" dependencies: "@jridgewell/resolve-uri": "npm:^3.1.0" "@jridgewell/sourcemap-codec": "npm:^1.4.14" - checksum: 18cf19f88e2792c1c91515f2b629aae05f3cdbb2e60c3886e16e80725234ce26dd10144c4981c05d9366e7094498c0b4fe5c1a89f4a730d7376a4ba4af448149 + checksum: 0ea0b2675cf513ec44dc25605616a3c9b808b9832e74b5b63c44260d66b58558bba65764f81928fc1033ead911f8718dca1134049c3e7a93937faf436671df31 languageName: node linkType: hard @@ -1679,13 +1680,13 @@ __metadata: linkType: hard "@metamask/json-rpc-engine@npm:^7.0.0": - version: 7.3.1 - resolution: "@metamask/json-rpc-engine@npm:7.3.1" + version: 7.3.0 + resolution: "@metamask/json-rpc-engine@npm:7.3.0" dependencies: "@metamask/rpc-errors": "npm:^6.1.0" "@metamask/safe-event-emitter": "npm:^3.0.0" "@metamask/utils": "npm:^8.2.0" - checksum: a34d9ef7a2277ce03bbaf537e08d00ae3b85928e67aff53e54ba116ac6bd1e12969808a224ccbd8d77ea482656a15ae37eb9741b34bcb69322a93c2a306cb89f + checksum: fcc70d15854dda72d5b353e8157b3f1d749fbd7c09d23bdd022c92fceb1101a2f83623cd9032c36d41fdfaa400131574c587bef2c7d7ca95bb29976b0f12faf6 languageName: node linkType: hard @@ -1727,8 +1728,8 @@ __metadata: linkType: hard "@metamask/utils@npm:^8.1.0, @metamask/utils@npm:^8.2.0": - version: 8.3.0 - resolution: "@metamask/utils@npm:8.3.0" + version: 8.2.1 + resolution: "@metamask/utils@npm:8.2.1" dependencies: "@ethereumjs/tx": "npm:^4.2.0" "@noble/hashes": "npm:^1.3.1" @@ -1738,54 +1739,54 @@ __metadata: pony-cause: "npm:^2.1.10" semver: "npm:^7.5.4" superstruct: "npm:^1.0.3" - checksum: 1f33ad3d61528be8e39a269ad9082e36827675c9df4fa84cf57e7468be0da405690a062df3a960da39e6175c9e4aa87a98ede40ef3cc701f8773c143b1121c6e + checksum: 7f6f02138f69f544dc7e27b52af995a630622c7e884bdf94f8c8ee78232a659a128c77088659f7ff9b030839fb52b14cc1655bdac85688ca435b46b5ecdbb844 languageName: node linkType: hard -"@motionone/animation@npm:^10.15.1, @motionone/animation@npm:^10.17.0": - version: 10.17.0 - resolution: "@motionone/animation@npm:10.17.0" +"@motionone/animation@npm:^10.15.1, @motionone/animation@npm:^10.16.3": + version: 10.16.3 + resolution: "@motionone/animation@npm:10.16.3" dependencies: - "@motionone/easing": "npm:^10.17.0" - "@motionone/types": "npm:^10.17.0" - "@motionone/utils": "npm:^10.17.0" + "@motionone/easing": "npm:^10.16.3" + "@motionone/types": "npm:^10.16.3" + "@motionone/utils": "npm:^10.16.3" tslib: "npm:^2.3.1" - checksum: 51873c9532ccb9f2b8475e871ba3eeebff2171bb2bd88e76bcf1fdb5bc1a7150f319c148063d17c16597038a8993c68033d918cc73a9fec40bb1f78ee8a52764 + checksum: c1bb7a03acc9c09647321a4653bf53878ea05ce91305507cb4000d75641dcad85faa8696ef12d0c28fa52d4b3708bc7ae34334c95ef532567a26082f0176ea4a languageName: node linkType: hard "@motionone/dom@npm:^10.16.2, @motionone/dom@npm:^10.16.4": - version: 10.17.0 - resolution: "@motionone/dom@npm:10.17.0" + version: 10.16.4 + resolution: "@motionone/dom@npm:10.16.4" dependencies: - "@motionone/animation": "npm:^10.17.0" - "@motionone/generators": "npm:^10.17.0" - "@motionone/types": "npm:^10.17.0" - "@motionone/utils": "npm:^10.17.0" + "@motionone/animation": "npm:^10.16.3" + "@motionone/generators": "npm:^10.16.4" + "@motionone/types": "npm:^10.16.3" + "@motionone/utils": "npm:^10.16.3" hey-listen: "npm:^1.0.8" tslib: "npm:^2.3.1" - checksum: bca972f6d60aa1462993ea1b36f0ba702c8c4644b602e460834d3a4ce88f3c7c1dcfec8810c6598e9cf502ad6d3af718a889ab1661c97ec162979fe9a0ff36ab + checksum: 1efaa29a18471c18dbe7f849a7c83b12c27edf85209cb366856720e051870302c27567f5eab2a1aef3aa7ae1438c6fbc3a7e686077f5ed4e173e4cca8d22e0d5 languageName: node linkType: hard -"@motionone/easing@npm:^10.17.0": - version: 10.17.0 - resolution: "@motionone/easing@npm:10.17.0" +"@motionone/easing@npm:^10.16.3": + version: 10.16.3 + resolution: "@motionone/easing@npm:10.16.3" dependencies: - "@motionone/utils": "npm:^10.17.0" + "@motionone/utils": "npm:^10.16.3" tslib: "npm:^2.3.1" - checksum: 9e82cf970cb754c44bc8226fd660c4a546aa06bb6eabb0b8be3a1466fc07920da13195e76d09d81704d059411584ba66de3bfc0192acc585a6fe352bf3e3fe22 + checksum: df98a643f0b2955afd16b78063899d050b22cfcf3db1bb86ecdbde831614f24c41143d5d887bc287f6de979baa20a00e8e1dca39ef7b2dfb67c0ec1b1ca0bcaa languageName: node linkType: hard -"@motionone/generators@npm:^10.17.0": - version: 10.17.0 - resolution: "@motionone/generators@npm:10.17.0" +"@motionone/generators@npm:^10.16.4": + version: 10.16.4 + resolution: "@motionone/generators@npm:10.16.4" dependencies: - "@motionone/types": "npm:^10.17.0" - "@motionone/utils": "npm:^10.17.0" + "@motionone/types": "npm:^10.16.3" + "@motionone/utils": "npm:^10.16.3" tslib: "npm:^2.3.1" - checksum: b1a951d7c20474b34d31cb199907a3ee4ae5074ff2ab49e18e54a63f5eacba6662e179a76f9b64ed7eaac5922ae934eaeca567f2a48c5a1a3ebf59cc5a43fc9f + checksum: cef71d1236a625b3579791d480ebd1875bec2a62e249771eb2af883981074016cc6f2ef112c2bf27f93d05d19830893f3f486944cd68d2fbf35a990c41729152 languageName: node linkType: hard @@ -1799,21 +1800,21 @@ __metadata: languageName: node linkType: hard -"@motionone/types@npm:^10.15.1, @motionone/types@npm:^10.17.0": - version: 10.17.0 - resolution: "@motionone/types@npm:10.17.0" - checksum: 9c91d887b368c93e860c1ff4b245d60d33966ec5bd2525ce91c4e2904c223f79333013fe06140feeab23f27ad9e8546a151e8357c5dc5218c5b658486bac3f82 +"@motionone/types@npm:^10.15.1, @motionone/types@npm:^10.16.3": + version: 10.16.3 + resolution: "@motionone/types@npm:10.16.3" + checksum: a792acd8bacd7949c29fd47fda1d3d7919b86ab209499a374a1f3c85f57a92d16f7a05f94edc6d46831c55180da2ff5e1193fa538bcb76e0ff38a24e25da2e87 languageName: node linkType: hard -"@motionone/utils@npm:^10.15.1, @motionone/utils@npm:^10.17.0": - version: 10.17.0 - resolution: "@motionone/utils@npm:10.17.0" +"@motionone/utils@npm:^10.15.1, @motionone/utils@npm:^10.16.3": + version: 10.16.3 + resolution: "@motionone/utils@npm:10.16.3" dependencies: - "@motionone/types": "npm:^10.17.0" + "@motionone/types": "npm:^10.16.3" hey-listen: "npm:^1.0.8" tslib: "npm:^2.3.1" - checksum: a90dc772245fa379d522d752dcbe80b02b1fcb17da6a3f3ebc725ac0e99b7847d39f1f4a29f10cbf5e8b6157766191ba03e96c75b0fa8378e3a1c4cc8cad728a + checksum: c5a1cce9bf5d1e8c5051a4636bd6a7030bf67f5662a94a8ec1524a72de3baca3f4c59e46cee9a41b111806fdd2956256c65c7e99b7de260803f2e44840bbae11 languageName: node linkType: hard @@ -1827,6 +1828,15 @@ __metadata: languageName: node linkType: hard +"@noble/curves@npm:1.1.0, @noble/curves@npm:~1.1.0": + version: 1.1.0 + resolution: "@noble/curves@npm:1.1.0" + dependencies: + "@noble/hashes": "npm:1.3.1" + checksum: 81115c3ebfa7e7da2d7e18d44d686f98dc6d35dbde3964412c05707c92d0994a01545bc265d5c0bc05c8c49333f75b99c9acef6750f5a79b3abcc8e0546acf88 + languageName: node + linkType: hard + "@noble/curves@npm:1.2.0, @noble/curves@npm:~1.2.0": version: 1.2.0 resolution: "@noble/curves@npm:1.2.0" @@ -1836,7 +1846,7 @@ __metadata: languageName: node linkType: hard -"@noble/curves@npm:1.3.0, @noble/curves@npm:^1.2.0, @noble/curves@npm:~1.3.0": +"@noble/curves@npm:^1.2.0": version: 1.3.0 resolution: "@noble/curves@npm:1.3.0" dependencies: @@ -1845,6 +1855,13 @@ __metadata: languageName: node linkType: hard +"@noble/hashes@npm:1.3.1": + version: 1.3.1 + resolution: "@noble/hashes@npm:1.3.1" + checksum: 86512713aaf338bced594bc2046ab249fea4e1ba1e7f2ecd02151ef1b8536315e788c11608fafe1b56f04fad1aa3c602da7e5f8e5fcd5f8b0aa94435fe65278e + languageName: node + linkType: hard + "@noble/hashes@npm:1.3.2": version: 1.3.2 resolution: "@noble/hashes@npm:1.3.2" @@ -1852,7 +1869,7 @@ __metadata: languageName: node linkType: hard -"@noble/hashes@npm:1.3.3, @noble/hashes@npm:^1.3.1, @noble/hashes@npm:~1.3.0, @noble/hashes@npm:~1.3.2": +"@noble/hashes@npm:1.3.3, @noble/hashes@npm:^1.3.1, @noble/hashes@npm:~1.3.0, @noble/hashes@npm:~1.3.1, @noble/hashes@npm:~1.3.2": version: 1.3.3 resolution: "@noble/hashes@npm:1.3.3" checksum: 23c020b33da4172c988e44100e33cd9f8f6250b68b43c467d3551f82070ebd9716e0d9d2347427aa3774c85934a35fa9ee6f026fca2117e3fa12db7bedae7668 @@ -1948,65 +1965,65 @@ __metadata: languageName: node linkType: hard -"@parcel/watcher-android-arm64@npm:2.4.0": - version: 2.4.0 - resolution: "@parcel/watcher-android-arm64@npm:2.4.0" +"@parcel/watcher-android-arm64@npm:2.3.0": + version: 2.3.0 + resolution: "@parcel/watcher-android-arm64@npm:2.3.0" conditions: os=android & cpu=arm64 languageName: node linkType: hard -"@parcel/watcher-darwin-arm64@npm:2.4.0": - version: 2.4.0 - resolution: "@parcel/watcher-darwin-arm64@npm:2.4.0" +"@parcel/watcher-darwin-arm64@npm:2.3.0": + version: 2.3.0 + resolution: "@parcel/watcher-darwin-arm64@npm:2.3.0" conditions: os=darwin & cpu=arm64 languageName: node linkType: hard -"@parcel/watcher-darwin-x64@npm:2.4.0": - version: 2.4.0 - resolution: "@parcel/watcher-darwin-x64@npm:2.4.0" +"@parcel/watcher-darwin-x64@npm:2.3.0": + version: 2.3.0 + resolution: "@parcel/watcher-darwin-x64@npm:2.3.0" conditions: os=darwin & cpu=x64 languageName: node linkType: hard -"@parcel/watcher-freebsd-x64@npm:2.4.0": - version: 2.4.0 - resolution: "@parcel/watcher-freebsd-x64@npm:2.4.0" +"@parcel/watcher-freebsd-x64@npm:2.3.0": + version: 2.3.0 + resolution: "@parcel/watcher-freebsd-x64@npm:2.3.0" conditions: os=freebsd & cpu=x64 languageName: node linkType: hard -"@parcel/watcher-linux-arm-glibc@npm:2.4.0": - version: 2.4.0 - resolution: "@parcel/watcher-linux-arm-glibc@npm:2.4.0" +"@parcel/watcher-linux-arm-glibc@npm:2.3.0": + version: 2.3.0 + resolution: "@parcel/watcher-linux-arm-glibc@npm:2.3.0" conditions: os=linux & cpu=arm & libc=glibc languageName: node linkType: hard -"@parcel/watcher-linux-arm64-glibc@npm:2.4.0": - version: 2.4.0 - resolution: "@parcel/watcher-linux-arm64-glibc@npm:2.4.0" +"@parcel/watcher-linux-arm64-glibc@npm:2.3.0": + version: 2.3.0 + resolution: "@parcel/watcher-linux-arm64-glibc@npm:2.3.0" conditions: os=linux & cpu=arm64 & libc=glibc languageName: node linkType: hard -"@parcel/watcher-linux-arm64-musl@npm:2.4.0": - version: 2.4.0 - resolution: "@parcel/watcher-linux-arm64-musl@npm:2.4.0" +"@parcel/watcher-linux-arm64-musl@npm:2.3.0": + version: 2.3.0 + resolution: "@parcel/watcher-linux-arm64-musl@npm:2.3.0" conditions: os=linux & cpu=arm64 & libc=musl languageName: node linkType: hard -"@parcel/watcher-linux-x64-glibc@npm:2.4.0": - version: 2.4.0 - resolution: "@parcel/watcher-linux-x64-glibc@npm:2.4.0" +"@parcel/watcher-linux-x64-glibc@npm:2.3.0": + version: 2.3.0 + resolution: "@parcel/watcher-linux-x64-glibc@npm:2.3.0" conditions: os=linux & cpu=x64 & libc=glibc languageName: node linkType: hard -"@parcel/watcher-linux-x64-musl@npm:2.4.0": - version: 2.4.0 - resolution: "@parcel/watcher-linux-x64-musl@npm:2.4.0" +"@parcel/watcher-linux-x64-musl@npm:2.3.0": + version: 2.3.0 + resolution: "@parcel/watcher-linux-x64-musl@npm:2.3.0" conditions: os=linux & cpu=x64 & libc=musl languageName: node linkType: hard @@ -2022,43 +2039,43 @@ __metadata: languageName: node linkType: hard -"@parcel/watcher-win32-arm64@npm:2.4.0": - version: 2.4.0 - resolution: "@parcel/watcher-win32-arm64@npm:2.4.0" +"@parcel/watcher-win32-arm64@npm:2.3.0": + version: 2.3.0 + resolution: "@parcel/watcher-win32-arm64@npm:2.3.0" conditions: os=win32 & cpu=arm64 languageName: node linkType: hard -"@parcel/watcher-win32-ia32@npm:2.4.0": - version: 2.4.0 - resolution: "@parcel/watcher-win32-ia32@npm:2.4.0" +"@parcel/watcher-win32-ia32@npm:2.3.0": + version: 2.3.0 + resolution: "@parcel/watcher-win32-ia32@npm:2.3.0" conditions: os=win32 & cpu=ia32 languageName: node linkType: hard -"@parcel/watcher-win32-x64@npm:2.4.0": - version: 2.4.0 - resolution: "@parcel/watcher-win32-x64@npm:2.4.0" +"@parcel/watcher-win32-x64@npm:2.3.0": + version: 2.3.0 + resolution: "@parcel/watcher-win32-x64@npm:2.3.0" conditions: os=win32 & cpu=x64 languageName: node linkType: hard "@parcel/watcher@npm:^2.3.0": - version: 2.4.0 - resolution: "@parcel/watcher@npm:2.4.0" - dependencies: - "@parcel/watcher-android-arm64": "npm:2.4.0" - "@parcel/watcher-darwin-arm64": "npm:2.4.0" - "@parcel/watcher-darwin-x64": "npm:2.4.0" - "@parcel/watcher-freebsd-x64": "npm:2.4.0" - "@parcel/watcher-linux-arm-glibc": "npm:2.4.0" - "@parcel/watcher-linux-arm64-glibc": "npm:2.4.0" - "@parcel/watcher-linux-arm64-musl": "npm:2.4.0" - "@parcel/watcher-linux-x64-glibc": "npm:2.4.0" - "@parcel/watcher-linux-x64-musl": "npm:2.4.0" - "@parcel/watcher-win32-arm64": "npm:2.4.0" - "@parcel/watcher-win32-ia32": "npm:2.4.0" - "@parcel/watcher-win32-x64": "npm:2.4.0" + version: 2.3.0 + resolution: "@parcel/watcher@npm:2.3.0" + dependencies: + "@parcel/watcher-android-arm64": "npm:2.3.0" + "@parcel/watcher-darwin-arm64": "npm:2.3.0" + "@parcel/watcher-darwin-x64": "npm:2.3.0" + "@parcel/watcher-freebsd-x64": "npm:2.3.0" + "@parcel/watcher-linux-arm-glibc": "npm:2.3.0" + "@parcel/watcher-linux-arm64-glibc": "npm:2.3.0" + "@parcel/watcher-linux-arm64-musl": "npm:2.3.0" + "@parcel/watcher-linux-x64-glibc": "npm:2.3.0" + "@parcel/watcher-linux-x64-musl": "npm:2.3.0" + "@parcel/watcher-win32-arm64": "npm:2.3.0" + "@parcel/watcher-win32-ia32": "npm:2.3.0" + "@parcel/watcher-win32-x64": "npm:2.3.0" detect-libc: "npm:^1.0.3" is-glob: "npm:^4.0.3" micromatch: "npm:^4.0.5" @@ -2089,7 +2106,7 @@ __metadata: optional: true "@parcel/watcher-win32-x64": optional: true - checksum: f8a7103d8402dceaeed6e7ceef5592ceed6c3ceed7bd747590dbf7b51ca56fd4cb26a6322d1952b4bca52acb41e9d4a13468035b371ef5d264230c4286bf4d0a + checksum: f223a6d5c56071c5f466725b93a83d0066ef01837fdae12ce86c9127586ad8138fe52f18de18c2752e3d8ca350b582ea4b55d16a51bd0584428d20698ace17a0 languageName: node linkType: hard @@ -2107,10 +2124,17 @@ __metadata: languageName: node linkType: hard -"@pkgr/core@npm:^0.1.0": - version: 0.1.1 - resolution: "@pkgr/core@npm:0.1.1" - checksum: 3f7536bc7f57320ab2cf96f8973664bef624710c403357429fbf680a5c3b4843c1dbd389bb43daa6b1f6f1f007bb082f5abcb76bb2b5dc9f421647743b71d3d8 +"@pkgr/utils@npm:^2.4.2": + version: 2.4.2 + resolution: "@pkgr/utils@npm:2.4.2" + dependencies: + cross-spawn: "npm:^7.0.3" + fast-glob: "npm:^3.3.0" + is-glob: "npm:^4.0.3" + open: "npm:^9.1.0" + picocolors: "npm:^1.0.0" + tslib: "npm:^2.6.0" + checksum: 7c3e68f6405a1d4c51f418d8d580e71d7bade2683d5db07e8413d8e57f7e389047eda44a2341f77a1b3085895fca7676a9d45e8812a58312524f8c4c65d501be languageName: node linkType: hard @@ -2133,10 +2157,10 @@ __metadata: languageName: node linkType: hard -"@remix-run/router@npm:1.14.2": - version: 1.14.2 - resolution: "@remix-run/router@npm:1.14.2" - checksum: 163d4a8ea3e25a7a7e3015f274e54b8043eddaaa92da220cad2893eb0fcbb649f617152c6d74680a4b55c0f319944ff1b362e87f814bb73be54f8d687ee730d6 +"@remix-run/router@npm:1.14.1": + version: 1.14.1 + resolution: "@remix-run/router@npm:1.14.1" + checksum: aa179e96fa7da5bdb86e6887219613cbe47b67d6595564b4f8fe7e80f8980f9c76e120524f0864aa8af46ac0f1a06bcdb4d20058d770c9199e5af599f772c0a9 languageName: node linkType: hard @@ -2171,19 +2195,30 @@ __metadata: linkType: hard "@safe-global/safe-gateway-typescript-sdk@npm:^3.5.3": - version: 3.14.0 - resolution: "@safe-global/safe-gateway-typescript-sdk@npm:3.14.0" - checksum: 9c4d8d79ee44aec2a635f612f433ab1ad3e3571b8e270bf61054319e03e82aeb83036ef6754e356bb99eab5a21bf2f9fb53c74ff8d3fa74e1a90ec808c69991b + version: 3.13.3 + resolution: "@safe-global/safe-gateway-typescript-sdk@npm:3.13.3" + checksum: 379e0deb6b938151434b451c627c61658567a4adbfbf8b6323b203fbe5be7082bcaabd31b3e269c6b6a6f65662635341c79ef2a20a828fd7d2e0daac83e81b17 languageName: node linkType: hard -"@scure/base@npm:^1.1.3, @scure/base@npm:~1.1.0, @scure/base@npm:~1.1.2, @scure/base@npm:~1.1.4": +"@scure/base@npm:^1.1.3, @scure/base@npm:~1.1.0, @scure/base@npm:~1.1.2": version: 1.1.5 resolution: "@scure/base@npm:1.1.5" checksum: 6eb07be0202fac74a57c79d0d00a45f6f7e57447010c1e3d90a4275d197829727b7abc54b248fc6f9bef9ae374f7be5ee9154dde5b5b73da773560bf17aa8504 languageName: node linkType: hard +"@scure/bip32@npm:1.3.1": + version: 1.3.1 + resolution: "@scure/bip32@npm:1.3.1" + dependencies: + "@noble/curves": "npm:~1.1.0" + "@noble/hashes": "npm:~1.3.1" + "@scure/base": "npm:~1.1.0" + checksum: 9ff0ad56f512794aed1ed62e582bf855db829e688235420a116b210169dc31e3e2a8cc4a908126aaa07b6dcbcc4cd085eb12f9d0a8b507a88946d6171a437195 + languageName: node + linkType: hard + "@scure/bip32@npm:1.3.2": version: 1.3.2 resolution: "@scure/bip32@npm:1.3.2" @@ -2195,17 +2230,6 @@ __metadata: languageName: node linkType: hard -"@scure/bip32@npm:1.3.3": - version: 1.3.3 - resolution: "@scure/bip32@npm:1.3.3" - dependencies: - "@noble/curves": "npm:~1.3.0" - "@noble/hashes": "npm:~1.3.2" - "@scure/base": "npm:~1.1.4" - checksum: 48fa04ebf0e3b56e3d086f029ae207ea753d8d8a1b3564f3c80fafea63dc3ee4edbd21e44eadb79bd4de4afffb075cbbbcb258fd5030a9680065cb524424eb83 - languageName: node - linkType: hard - "@scure/bip39@npm:1.2.1": version: 1.2.1 resolution: "@scure/bip39@npm:1.2.1" @@ -2216,16 +2240,6 @@ __metadata: languageName: node linkType: hard -"@scure/bip39@npm:1.2.2": - version: 1.2.2 - resolution: "@scure/bip39@npm:1.2.2" - dependencies: - "@noble/hashes": "npm:~1.3.2" - "@scure/base": "npm:~1.1.4" - checksum: be38bc1dc10b9a763d8b02d91dc651a4f565c822486df6cb1d3cc84896c1aab3ef6acbf7b3dc7e4a981bc9366086a4d72020aa21e11a692734a750de049c887c - languageName: node - linkType: hard - "@sinclair/typebox@npm:^0.27.8": version: 0.27.8 resolution: "@sinclair/typebox@npm:0.27.8" @@ -2234,11 +2248,11 @@ __metadata: linkType: hard "@sinonjs/commons@npm:^3.0.0": - version: 3.0.1 - resolution: "@sinonjs/commons@npm:3.0.1" + version: 3.0.0 + resolution: "@sinonjs/commons@npm:3.0.0" dependencies: type-detect: "npm:4.0.8" - checksum: 1227a7b5bd6c6f9584274db996d7f8cee2c8c350534b9d0141fc662eaf1f292ea0ae3ed19e5e5271c8fd390d27e492ca2803acd31a1978be2cdc6be0da711403 + checksum: 1df9cd257942f4e4960dfb9fd339d9e97b6a3da135f3d5b8646562918e863809cb8e00268535f4f4723535d2097881c8fc03d545c414d8555183376cfc54ee84 languageName: node linkType: hard @@ -2551,11 +2565,11 @@ __metadata: linkType: hard "@types/babel__traverse@npm:*, @types/babel__traverse@npm:^7.0.6": - version: 7.20.5 - resolution: "@types/babel__traverse@npm:7.20.5" + version: 7.20.4 + resolution: "@types/babel__traverse@npm:7.20.4" dependencies: "@babel/types": "npm:^7.20.7" - checksum: 033abcb2f4c084ad33e30c3efaad82161240f351e3c71b6154ed289946b33b363696c0fbd42502b68e4582a87413c418321f40eb1ea863e34fe525641345e05b + checksum: e76cb4974c7740fd61311152dc497e7b05c1c46ba554aab875544ab0a7457f343cafcad34ba8fb2ff543ab0e012ef2d3fa0c13f1a4e9a4cd9c4c703c7a2a8d62 languageName: node linkType: hard @@ -2617,12 +2631,12 @@ __metadata: linkType: hard "@types/eslint@npm:*": - version: 8.56.2 - resolution: "@types/eslint@npm:8.56.2" + version: 8.44.9 + resolution: "@types/eslint@npm:8.44.9" dependencies: "@types/estree": "npm:*" "@types/json-schema": "npm:*" - checksum: e33ca87a30a9454ba9943e1270ac759996f5fe598a1c1afbaec1d1e7346a339e20bf2a9d81f177067116bbaa6cfa4f748993cb338f57978ae862ad38ffae56fe + checksum: e9da4e4c7b7c9014b17d40007e36f02f3b5dd55c43bb05928b52dd9c19f2a8fb7971a851a4e7a11625c3c69da286c5baf55de2f8bb900b1a4cfb5145a4491b37 languageName: node linkType: hard @@ -2777,20 +2791,20 @@ __metadata: linkType: hard "@types/node-forge@npm:^1.3.0": - version: 1.3.11 - resolution: "@types/node-forge@npm:1.3.11" + version: 1.3.10 + resolution: "@types/node-forge@npm:1.3.10" dependencies: "@types/node": "npm:*" - checksum: 3d7d23ca0ba38ac0cf74028393bd70f31169ab9aba43f21deb787840170d307d662644bac07287495effe2812ddd7ac8a14dbd43f16c2936bbb06312e96fc3b9 + checksum: b190e93e36e3bf5881e099df930645bbeb963c1cabb110948f90e11f5f59a2514d5632e6bd1101dfb839725eab25a8e2eba4a2b1b7551f12bc43302863e050ae languageName: node linkType: hard "@types/node@npm:*, @types/node@npm:^20.10.4, @types/node@npm:^20.5.9": - version: 20.11.5 - resolution: "@types/node@npm:20.11.5" + version: 20.10.5 + resolution: "@types/node@npm:20.10.5" dependencies: undici-types: "npm:~5.26.4" - checksum: 6d18cec852f5cfbed3ec42b5c01c026e7a3f9da540d6e3d6738d4cee9979fb308cf27b6df7ba40a6553e7bc82e678f0ef53ba6e6ad52e5b86bd97b7783c2a42c + checksum: be30609aae0bfe492097815f166ccc07f465220cb604647fa4e5ec05a1d16c012a41b82b5f11ecfe2485cbb479d4d20384b95b809ca0bcff6d94d5bbafa645bb languageName: node linkType: hard @@ -2809,9 +2823,9 @@ __metadata: linkType: hard "@types/qs@npm:*": - version: 6.9.11 - resolution: "@types/qs@npm:6.9.11" - checksum: 657a50f05b694d6fd3916d24177cfa0f3b8b87d9deff4ffa4dddcb0b03583ebf7c47b424b8de400270fb9a5cc1e9cf790dd82c833c6935305851e7da8ede3ff5 + version: 6.9.10 + resolution: "@types/qs@npm:6.9.10" + checksum: 6be12e5f062d1b41eb037d59bf9cb65bc9410cedd5e6da832dfd7c8e2b3f4c91e81c9b90b51811140770e5052c6c4e8361181bd9437ddcd4515dc128b7c00353 languageName: node linkType: hard @@ -2832,13 +2846,13 @@ __metadata: linkType: hard "@types/react@npm:*, @types/react@npm:^18.2.15": - version: 18.2.48 - resolution: "@types/react@npm:18.2.48" + version: 18.2.45 + resolution: "@types/react@npm:18.2.45" dependencies: "@types/prop-types": "npm:*" "@types/scheduler": "npm:*" csstype: "npm:^3.0.2" - checksum: 7e89f18ea2928b1638f564b156d692894dcb9352a7e0a807873c97e858abe1f23dbd165a25dd088a991344e973fdeef88ba5724bfb64504b74072cbc9c220c3a + checksum: 4cc650c47ffb88baac29fb7a74e842e4af4a55f437086ef70250fdc75f0a5f2fcf8adc272d05ab2e00b1de6e14613296881271caee037dadf9130fdeb498c59e languageName: node linkType: hard @@ -2942,14 +2956,14 @@ __metadata: linkType: hard "@typescript-eslint/eslint-plugin@npm:^6.0.0": - version: 6.19.1 - resolution: "@typescript-eslint/eslint-plugin@npm:6.19.1" + version: 6.15.0 + resolution: "@typescript-eslint/eslint-plugin@npm:6.15.0" dependencies: "@eslint-community/regexpp": "npm:^4.5.1" - "@typescript-eslint/scope-manager": "npm:6.19.1" - "@typescript-eslint/type-utils": "npm:6.19.1" - "@typescript-eslint/utils": "npm:6.19.1" - "@typescript-eslint/visitor-keys": "npm:6.19.1" + "@typescript-eslint/scope-manager": "npm:6.15.0" + "@typescript-eslint/type-utils": "npm:6.15.0" + "@typescript-eslint/utils": "npm:6.15.0" + "@typescript-eslint/visitor-keys": "npm:6.15.0" debug: "npm:^4.3.4" graphemer: "npm:^1.4.0" ignore: "npm:^5.2.4" @@ -2962,44 +2976,44 @@ __metadata: peerDependenciesMeta: typescript: optional: true - checksum: 01f1d643219b51bfad76734c6eb19a480309f0e66877ddc00bca89368e5aee3eb907e366977a8d11094c49e807773f5cfba306c861cd690d70044a7925173823 + checksum: 78054afb0d4ab12d82db7a9cb005dfa2be42962341728abf4a81802e1f4c0f5b23de4870287f4b7e32aa4a4bc900bbc218f2d4d0c02aa77452e8e8e0b71fe3de languageName: node linkType: hard "@typescript-eslint/parser@npm:^6.0.0": - version: 6.19.1 - resolution: "@typescript-eslint/parser@npm:6.19.1" + version: 6.15.0 + resolution: "@typescript-eslint/parser@npm:6.15.0" dependencies: - "@typescript-eslint/scope-manager": "npm:6.19.1" - "@typescript-eslint/types": "npm:6.19.1" - "@typescript-eslint/typescript-estree": "npm:6.19.1" - "@typescript-eslint/visitor-keys": "npm:6.19.1" + "@typescript-eslint/scope-manager": "npm:6.15.0" + "@typescript-eslint/types": "npm:6.15.0" + "@typescript-eslint/typescript-estree": "npm:6.15.0" + "@typescript-eslint/visitor-keys": "npm:6.15.0" debug: "npm:^4.3.4" peerDependencies: eslint: ^7.0.0 || ^8.0.0 peerDependenciesMeta: typescript: optional: true - checksum: 442e860fbc4786fe999205528cc74b31d933008e170a707ddaec0c9e2c374f62c36c8d05d3dd446c9ceb802f2b403806d72c78ffd97867cf1672028b754b6262 + checksum: e7f265fd4abd3bc49fa5b304cd4b9c22801ac5a9da4ee342bbab0c117d629ac4aad6998555b61a8c5a0b279c443a44ae99f16669e24e3ef17ccec20c8b7019e7 languageName: node linkType: hard -"@typescript-eslint/scope-manager@npm:6.19.1": - version: 6.19.1 - resolution: "@typescript-eslint/scope-manager@npm:6.19.1" +"@typescript-eslint/scope-manager@npm:6.15.0": + version: 6.15.0 + resolution: "@typescript-eslint/scope-manager@npm:6.15.0" dependencies: - "@typescript-eslint/types": "npm:6.19.1" - "@typescript-eslint/visitor-keys": "npm:6.19.1" - checksum: a81315b4a2888343d3be781fe8d6b4c229c656d7bf1bd74bc44a89bba96bb6a10a0319d301f24ca91adb898374eaadbd38979e6567ac9085b5d7076163794281 + "@typescript-eslint/types": "npm:6.15.0" + "@typescript-eslint/visitor-keys": "npm:6.15.0" + checksum: 3428d99de440f227cbc2afb44cdcb25e44c4b49c5f490392f83e21d2048210a6ec2f2f68133376c842034f5b5ba4ec9721da7caa18e631e23b57e20927b5b6f0 languageName: node linkType: hard -"@typescript-eslint/type-utils@npm:6.19.1": - version: 6.19.1 - resolution: "@typescript-eslint/type-utils@npm:6.19.1" +"@typescript-eslint/type-utils@npm:6.15.0": + version: 6.15.0 + resolution: "@typescript-eslint/type-utils@npm:6.15.0" dependencies: - "@typescript-eslint/typescript-estree": "npm:6.19.1" - "@typescript-eslint/utils": "npm:6.19.1" + "@typescript-eslint/typescript-estree": "npm:6.15.0" + "@typescript-eslint/utils": "npm:6.15.0" debug: "npm:^4.3.4" ts-api-utils: "npm:^1.0.1" peerDependencies: @@ -3007,60 +3021,59 @@ __metadata: peerDependenciesMeta: typescript: optional: true - checksum: 78c185c64a8c92d7b5f2132ef4880b974a2e07e9ae7913ad53e327972af540a8a8bf75bc319c8aaa82445615e2680f3c85736ee67aa174a5ba91798fe5068f95 + checksum: 32cb531a4b5e0ccd431cba553ec73b87d4453b48af288a33e359ba4f5278126390d82799b61d3f0fbf135cfde1ac6c2275c2cf37a676e8a2a2811e774e660f16 languageName: node linkType: hard -"@typescript-eslint/types@npm:6.19.1": - version: 6.19.1 - resolution: "@typescript-eslint/types@npm:6.19.1" - checksum: b8f75df157ca383e5bd6c07276fbeed6ff775e1354260a1653777749c0d71626fb29be5d36c9570e2c5cfaa5db62deaae20aa4be8a2d7d753782ab66d88e007f +"@typescript-eslint/types@npm:6.15.0": + version: 6.15.0 + resolution: "@typescript-eslint/types@npm:6.15.0" + checksum: 6e33529ea301c8c4b8c1f589dadd5d2a66c1b24ec87a577524fbc996d4c7b65d4f4fdfa4a3937b691efee6a10a6b16f7bfcabe98a15e0fc0c0c57aa0d80dcc25 languageName: node linkType: hard -"@typescript-eslint/typescript-estree@npm:6.19.1": - version: 6.19.1 - resolution: "@typescript-eslint/typescript-estree@npm:6.19.1" +"@typescript-eslint/typescript-estree@npm:6.15.0": + version: 6.15.0 + resolution: "@typescript-eslint/typescript-estree@npm:6.15.0" dependencies: - "@typescript-eslint/types": "npm:6.19.1" - "@typescript-eslint/visitor-keys": "npm:6.19.1" + "@typescript-eslint/types": "npm:6.15.0" + "@typescript-eslint/visitor-keys": "npm:6.15.0" debug: "npm:^4.3.4" globby: "npm:^11.1.0" is-glob: "npm:^4.0.3" - minimatch: "npm:9.0.3" semver: "npm:^7.5.4" ts-api-utils: "npm:^1.0.1" peerDependenciesMeta: typescript: optional: true - checksum: dec16f873084e9eeb1a696dff82c42164e75908221f7868d900ad7b7fcec6fc62a9a7dddb8bc17c78c19bf35f07acee81b3778b20b9735ffdaeee732ecb643d3 + checksum: 08955f6e84b8edb855a6769671e85889e52b15b82e00a64f595da867b21ad060e5342787c436d77702b2a1f39d411ac79b81a8d2e2006e9b1886eadb08b626df languageName: node linkType: hard -"@typescript-eslint/utils@npm:6.19.1": - version: 6.19.1 - resolution: "@typescript-eslint/utils@npm:6.19.1" +"@typescript-eslint/utils@npm:6.15.0": + version: 6.15.0 + resolution: "@typescript-eslint/utils@npm:6.15.0" dependencies: "@eslint-community/eslint-utils": "npm:^4.4.0" "@types/json-schema": "npm:^7.0.12" "@types/semver": "npm:^7.5.0" - "@typescript-eslint/scope-manager": "npm:6.19.1" - "@typescript-eslint/types": "npm:6.19.1" - "@typescript-eslint/typescript-estree": "npm:6.19.1" + "@typescript-eslint/scope-manager": "npm:6.15.0" + "@typescript-eslint/types": "npm:6.15.0" + "@typescript-eslint/typescript-estree": "npm:6.15.0" semver: "npm:^7.5.4" peerDependencies: eslint: ^7.0.0 || ^8.0.0 - checksum: 5fa58a32722e9915bfe8433fda2f46be894352549e8406acc4e29a04a8ddb0ea5988fddda2a3145f8952129a267cb51b666206b30489d2ff36b7911f540f1d57 + checksum: 53519a2027681bdc8f028f9421c65f193f91b5bb1659465fedb8043376c693c2391211f1c01d8ba25bfaa7f7b3a102263d7123f9dfade12032159f4b4490f0fb languageName: node linkType: hard -"@typescript-eslint/visitor-keys@npm:6.19.1": - version: 6.19.1 - resolution: "@typescript-eslint/visitor-keys@npm:6.19.1" +"@typescript-eslint/visitor-keys@npm:6.15.0": + version: 6.15.0 + resolution: "@typescript-eslint/visitor-keys@npm:6.15.0" dependencies: - "@typescript-eslint/types": "npm:6.19.1" + "@typescript-eslint/types": "npm:6.15.0" eslint-visitor-keys: "npm:^3.4.1" - checksum: b0370a9bc6fd8d243aa8b7ccd1657ec2fbd25ceb7b067aac64322f03aa0f64b97444b13b0946f52a53d6bc5edd43e0b447f72160be4a5b72e073c1d3679b6b4c + checksum: bf9f71af60bd63d1073900e75c5a0aa6eddd672f6c3ac6092c765d67deb7a0c32d2a5f6f3aee9e95f93a93d58563a76da209bd8487aadafd4d013100ffe38520 languageName: node linkType: hard @@ -3960,18 +3973,18 @@ __metadata: linkType: hard "acorn-walk@npm:^8.1.1": - version: 8.3.2 - resolution: "acorn-walk@npm:8.3.2" - checksum: 7e2a8dad5480df7f872569b9dccff2f3da7e65f5353686b1d6032ab9f4ddf6e3a2cb83a9b52cf50b1497fd522154dda92f0abf7153290cc79cd14721ff121e52 + version: 8.3.1 + resolution: "acorn-walk@npm:8.3.1" + checksum: a23d2f7c6b6cad617f4c77f14dfeb062a239208d61753e9ba808d916c550add92b39535467d2e6028280761ac4f5a904cc9df21530b84d3f834e3edef74ddde5 languageName: node linkType: hard -"acorn@npm:^8.11.3, acorn@npm:^8.4.1, acorn@npm:^8.7.1, acorn@npm:^8.8.2, acorn@npm:^8.9.0": - version: 8.11.3 - resolution: "acorn@npm:8.11.3" +"acorn@npm:^8.10.0, acorn@npm:^8.4.1, acorn@npm:^8.7.1, acorn@npm:^8.8.2, acorn@npm:^8.9.0": + version: 8.11.2 + resolution: "acorn@npm:8.11.2" bin: acorn: bin/acorn - checksum: 3ff155f8812e4a746fee8ecff1f227d527c4c45655bb1fad6347c3cb58e46190598217551b1500f18542d2bbe5c87120cb6927f5a074a59166fbdd9468f0a299 + checksum: a3ed76c761b75ec54b1ec3068fb7f113a182e95aea7f322f65098c2958d232e3d211cb6dac35ff9c647024b63714bc528a26d54a925d1fef2c25585b4c8e4017 languageName: node linkType: hard @@ -4271,6 +4284,13 @@ __metadata: languageName: node linkType: hard +"array-flatten@npm:^2.1.2": + version: 2.1.2 + resolution: "array-flatten@npm:2.1.2" + checksum: bdc1cee68e41bec9cfc1161408734e2269428ef371445606bce4e6241001e138a94b9a617cc9a5b4b7fe6a3a51e3d5a942646975ce82a2e202ccf3e9b478c82f + languageName: node + linkType: hard + "array-includes@npm:^3.1.7": version: 3.1.7 resolution: "array-includes@npm:3.1.7" @@ -4381,12 +4401,12 @@ __metadata: linkType: hard "autoprefixer@npm:^10.4.15": - version: 10.4.17 - resolution: "autoprefixer@npm:10.4.17" + version: 10.4.16 + resolution: "autoprefixer@npm:10.4.16" dependencies: - browserslist: "npm:^4.22.2" - caniuse-lite: "npm:^1.0.30001578" - fraction.js: "npm:^4.3.7" + browserslist: "npm:^4.21.10" + caniuse-lite: "npm:^1.0.30001538" + fraction.js: "npm:^4.3.6" normalize-range: "npm:^0.1.2" picocolors: "npm:^1.0.0" postcss-value-parser: "npm:^4.2.0" @@ -4394,7 +4414,7 @@ __metadata: postcss: ^8.1.0 bin: autoprefixer: bin/autoprefixer - checksum: 1d21cc8edb7bf993682094ceed03a32c18f5293f071182a64c2c6defb44bbe91d576ad775d2347469a81997b80cea0bbc4ad3eeb5b12710f9feacf2e6c04bb51 + checksum: e00256e754d481a026d928bca729b25954074dd142dbec022f0a7db0d3bbc0dc2e2dc7542e94fec22eff81e21fe140e6856448e2d9a002660cb1e2ad434daee0 languageName: node linkType: hard @@ -4518,6 +4538,13 @@ __metadata: languageName: node linkType: hard +"big-integer@npm:^1.6.44": + version: 1.6.52 + resolution: "big-integer@npm:1.6.52" + checksum: 9604224b4c2ab3c43c075d92da15863077a9f59e5d4205f4e7e76acd0cd47e8d469ec5e5dba8d9b32aa233951893b29329ca56ac80c20ce094b4a647a66abae0 + languageName: node + linkType: hard + "binary-extensions@npm:^2.0.0": version: 2.2.0 resolution: "binary-extensions@npm:2.2.0" @@ -4567,12 +4594,14 @@ __metadata: linkType: hard "bonjour-service@npm:^1.0.11": - version: 1.2.1 - resolution: "bonjour-service@npm:1.2.1" + version: 1.1.1 + resolution: "bonjour-service@npm:1.1.1" dependencies: + array-flatten: "npm:^2.1.2" + dns-equal: "npm:^1.0.0" fast-deep-equal: "npm:^3.1.3" multicast-dns: "npm:^7.2.5" - checksum: 953cbfc27fc9e36e6f988012993ab2244817d82426603e0390d4715639031396c932b6657b1aa4ec30dbb5fa903d6b2c7f1be3af7a8ba24165c93e987c849730 + checksum: 8dd3fef3ff8a11678d8f586be03c85004a45bae4353c55d7dbffe288cad73ddb38dee08b57425b9945c9a3a840d50bd40ae5aeda0066186dabe4b84a315b4e05 languageName: node linkType: hard @@ -4592,6 +4621,15 @@ __metadata: languageName: node linkType: hard +"bplist-parser@npm:^0.2.0": + version: 0.2.0 + resolution: "bplist-parser@npm:0.2.0" + dependencies: + big-integer: "npm:^1.6.44" + checksum: ce79c69e0f6efe506281e7c84e3712f7d12978991675b6e3a58a295b16f13ca81aa9b845c335614a545e0af728c8311b6aa3142af76ba1cb616af9bbac5c4a9f + languageName: node + linkType: hard + "brace-expansion@npm:^1.1.7": version: 1.1.11 resolution: "brace-expansion@npm:1.1.11" @@ -4671,7 +4709,7 @@ __metadata: languageName: node linkType: hard -"browserslist@npm:^4.14.5, browserslist@npm:^4.22.2": +"browserslist@npm:^4.14.5, browserslist@npm:^4.21.10, browserslist@npm:^4.22.2": version: 4.22.2 resolution: "browserslist@npm:4.22.2" dependencies: @@ -4761,6 +4799,15 @@ __metadata: languageName: node linkType: hard +"bundle-name@npm:^3.0.0": + version: 3.0.0 + resolution: "bundle-name@npm:3.0.0" + dependencies: + run-applescript: "npm:^5.0.0" + checksum: 57bc7f8b025d83961b04db2f1eff6a87f2363c2891f3542a4b82471ff8ebb5d484af48e9784fcdb28ef1d48bb01f03d891966dc3ef58758e46ea32d750ce40f8 + languageName: node + linkType: hard + "bytes@npm:3.0.0": version: 3.0.0 resolution: "bytes@npm:3.0.0" @@ -4828,8 +4875,8 @@ __metadata: linkType: hard "cacache@npm:^18.0.0": - version: 18.0.2 - resolution: "cacache@npm:18.0.2" + version: 18.0.1 + resolution: "cacache@npm:18.0.1" dependencies: "@npmcli/fs": "npm:^3.1.0" fs-minipass: "npm:^3.0.0" @@ -4843,7 +4890,7 @@ __metadata: ssri: "npm:^10.0.0" tar: "npm:^6.1.11" unique-filename: "npm:^3.0.0" - checksum: 7992665305cc251a984f4fdbab1449d50e88c635bc43bf2785530c61d239c61b349e5734461baa461caaee65f040ab14e2d58e694f479c0810cffd181ba5eabc + checksum: a31666805a80a8b16ad3f85faf66750275a9175a3480896f4f6d31b5d53ef190484fabd71bdb6d2ea5603c717fbef09f4af03d6a65b525c8ef0afaa44c361866 languageName: node linkType: hard @@ -4907,10 +4954,17 @@ __metadata: languageName: node linkType: hard -"caniuse-lite@npm:^1.0.30001565, caniuse-lite@npm:^1.0.30001578": - version: 1.0.30001579 - resolution: "caniuse-lite@npm:1.0.30001579" - checksum: 4003970f8d01a5fa314e39f4a21751dc750a530f3d19aed225e18e8e02892b590b8b0debfa0961eae9bc0e49b77bfb17cf30d2469540e428a8305e3cc9164fb8 +"caniuse-lite@npm:^1.0.30001538": + version: 1.0.30001571 + resolution: "caniuse-lite@npm:1.0.30001571" + checksum: 632f476e39febbfb5dc91c236981f3d518dc0cf55c42cc2bba431a6b6f4cceae3f9cd74d26312f30e9de65a3cc92ccf80d964ba8de061e25f37b7f0518303dad + languageName: node + linkType: hard + +"caniuse-lite@npm:^1.0.30001565": + version: 1.0.30001570 + resolution: "caniuse-lite@npm:1.0.30001570" + checksum: e47230d2016edea56e002fa462a5289f697b48dcfbf703fb01aecc6c98ad4ecaf945ab23c253cb7af056c2d05f266e4e4cbebf45132100e2c9367439cb95b95b languageName: node linkType: hard @@ -4922,8 +4976,8 @@ __metadata: linkType: hard "chai@npm:^4.3.7": - version: 4.4.1 - resolution: "chai@npm:4.4.1" + version: 4.3.10 + resolution: "chai@npm:4.3.10" dependencies: assertion-error: "npm:^1.1.0" check-error: "npm:^1.0.3" @@ -4932,7 +4986,7 @@ __metadata: loupe: "npm:^2.3.6" pathval: "npm:^1.1.1" type-detect: "npm:^4.0.8" - checksum: 91590a8fe18bd6235dece04ccb2d5b4ecec49984b50924499bdcd7a95c02cb1fd2a689407c19bb854497bde534ef57525cfad6c7fdd2507100fd802fbc2aefbd + checksum: c887d24f67be6fb554c7ebbde3bb0568697a8833d475e4768296916891ba143f25fc079f6eb34146f3dd5a3279d34c1f387c32c9a6ab288e579f948d9ccf53fe languageName: node linkType: hard @@ -5046,7 +5100,7 @@ __metadata: languageName: node linkType: hard -"citty@npm:^0.1.5": +"citty@npm:^0.1.4, citty@npm:^0.1.5": version: 0.1.5 resolution: "citty@npm:0.1.5" dependencies: @@ -5063,9 +5117,9 @@ __metadata: linkType: hard "classnames@npm:^2.3.2": - version: 2.5.1 - resolution: "classnames@npm:2.5.1" - checksum: afff4f77e62cea2d79c39962980bf316bacb0d7c49e13a21adaadb9221e1c6b9d3cdb829d8bb1b23c406f4e740507f37e1dcf506f7e3b7113d17c5bab787aa69 + version: 2.3.2 + resolution: "classnames@npm:2.3.2" + checksum: cd50ead57b4f97436aaa9f9885c6926323efc7c2bea8e3d4eb10e4e972aa6a1cfca1c7a0e06f8a199ca7498d4339e30bb6002e589e61c9f21248cbf3e8b0b18d languageName: node linkType: hard @@ -5083,7 +5137,7 @@ __metadata: languageName: node linkType: hard -"clipboardy@npm:3.0.0": +"clipboardy@npm:3.0.0, clipboardy@npm:^3.0.0": version: 3.0.0 resolution: "clipboardy@npm:3.0.0" dependencies: @@ -5094,17 +5148,6 @@ __metadata: languageName: node linkType: hard -"clipboardy@npm:^4.0.0": - version: 4.0.0 - resolution: "clipboardy@npm:4.0.0" - dependencies: - execa: "npm:^8.0.1" - is-wsl: "npm:^3.1.0" - is64bit: "npm:^2.0.0" - checksum: 02bb5f3d0a772bd84ec26a3566c72c2319a9f3b4cb8338370c3bffcf0073c80b834abe1a6945bea4f2cbea28e1627a975aaac577e3f61a868d924ce79138b041 - languageName: node - linkType: hard - "cliui@npm:^5.0.0": version: 5.0.0 resolution: "cliui@npm:5.0.0" @@ -5379,13 +5422,13 @@ __metadata: languageName: node linkType: hard -"cookies@npm:~0.9.0": - version: 0.9.1 - resolution: "cookies@npm:0.9.1" +"cookies@npm:~0.8.0": + version: 0.8.0 + resolution: "cookies@npm:0.8.0" dependencies: depd: "npm:~2.0.0" keygrip: "npm:~1.1.0" - checksum: 3ffa1c0e992b62ee119adae4dd2ddd4a89166fa5434cd9bd9ff84ec4d2f14dfe2318a601280abfe32a4f64f884ec9345fb1912e488b002d188d2efa0d3919ba3 + checksum: 0af32f30d1ece0596efc05782c66b9d61659e20c6cc5b695452abf5ceb51883ef43c5c73d86badd7d028a0da7d39f864c95f33640aef04f97fad70f35986bea3 languageName: node linkType: hard @@ -5428,7 +5471,7 @@ __metadata: languageName: node linkType: hard -"cosmiconfig@npm:^8.3.5": +"cosmiconfig@npm:^8.2.0": version: 8.3.6 resolution: "cosmiconfig@npm:8.3.6" dependencies: @@ -5512,20 +5555,20 @@ __metadata: linkType: hard "css-loader@npm:^6.8.1": - version: 6.9.1 - resolution: "css-loader@npm:6.9.1" + version: 6.8.1 + resolution: "css-loader@npm:6.8.1" dependencies: icss-utils: "npm:^5.1.0" - postcss: "npm:^8.4.33" + postcss: "npm:^8.4.21" postcss-modules-extract-imports: "npm:^3.0.0" - postcss-modules-local-by-default: "npm:^4.0.4" - postcss-modules-scope: "npm:^3.1.1" + postcss-modules-local-by-default: "npm:^4.0.3" + postcss-modules-scope: "npm:^3.0.0" postcss-modules-values: "npm:^4.0.0" postcss-value-parser: "npm:^4.2.0" - semver: "npm:^7.5.4" + semver: "npm:^7.3.8" peerDependencies: webpack: ^5.0.0 - checksum: 3dfff7d4372a1d8f2b1606a8376f2f5e484e59d7ffdc373ea3b04aa2b54ecd052aaf72c090a97148b0eb8a164cdc1f9dcb7a68686b2d5d190d69367c30f76132 + checksum: a6e23de4ec1d2832f10b8ca3cfec6b6097a97ca3c73f64338ae5cd110ac270f1b218ff0273d39f677a7a561f1a9d9b0d332274664d0991bcfafaae162c2669c4 languageName: node linkType: hard @@ -5702,6 +5745,28 @@ __metadata: languageName: node linkType: hard +"default-browser-id@npm:^3.0.0": + version: 3.0.0 + resolution: "default-browser-id@npm:3.0.0" + dependencies: + bplist-parser: "npm:^0.2.0" + untildify: "npm:^4.0.0" + checksum: 8db3ab882eb3e1e8b59d84c8641320e6c66d8eeb17eb4bb848b7dd549b1e6fd313988e4a13542e95fbaeff03f6e9dedc5ad191ad4df7996187753eb0d45c00b7 + languageName: node + linkType: hard + +"default-browser@npm:^4.0.0": + version: 4.0.0 + resolution: "default-browser@npm:4.0.0" + dependencies: + bundle-name: "npm:^3.0.0" + default-browser-id: "npm:^3.0.0" + execa: "npm:^7.1.1" + titleize: "npm:^3.0.0" + checksum: 7c8848badc139ecf9d878e562bc4e7ab4301e51ba120b24d8dcb14739c30152115cc612065ac3ab73c02aace4afa29db5a044257b2f0cf234f16e3a58f6c925e + languageName: node + linkType: hard + "default-gateway@npm:^6.0.3": version: 6.0.3 resolution: "default-gateway@npm:6.0.3" @@ -5739,6 +5804,13 @@ __metadata: languageName: node linkType: hard +"define-lazy-prop@npm:^3.0.0": + version: 3.0.0 + resolution: "define-lazy-prop@npm:3.0.0" + checksum: 5ab0b2bf3fa58b3a443140bbd4cd3db1f91b985cc8a246d330b9ac3fc0b6a325a6d82bddc0b055123d745b3f9931afeea74a5ec545439a1630b9c8512b0eeb49 + languageName: node + linkType: hard + "define-properties@npm:^1.1.3, define-properties@npm:^1.2.0, define-properties@npm:^1.2.1": version: 1.2.1 resolution: "define-properties@npm:1.2.1" @@ -5750,10 +5822,10 @@ __metadata: languageName: node linkType: hard -"defu@npm:^6.1.3, defu@npm:^6.1.4": - version: 6.1.4 - resolution: "defu@npm:6.1.4" - checksum: 2d6cc366262dc0cb8096e429368e44052fdf43ed48e53ad84cc7c9407f890301aa5fcb80d0995abaaf842b3949f154d060be4160f7a46cb2bc2f7726c81526f5 +"defu@npm:^6.1.2, defu@npm:^6.1.3": + version: 6.1.3 + resolution: "defu@npm:6.1.3" + checksum: 60d0d9a6e328148d5313fe0239ba3777701291f35570b52562454653d953fec5281b084514540f8d3b60d61bad9e39b52e95b3c0451631ded220ad8fdc893455 languageName: node linkType: hard @@ -5890,6 +5962,13 @@ __metadata: languageName: node linkType: hard +"dns-equal@npm:^1.0.0": + version: 1.0.0 + resolution: "dns-equal@npm:1.0.0" + checksum: da966e5275ac50546e108af6bc29aaae2164d2ae96d60601b333c4a3aff91f50b6ca14929cf91f20a9cad1587b356323e300cea3ff6588a6a816988485f445f1 + languageName: node + linkType: hard + "dns-packet@npm:^5.2.2": version: 5.6.1 resolution: "dns-packet@npm:5.6.1" @@ -5918,9 +5997,9 @@ __metadata: linkType: hard "dotenv@npm:^16.0.3": - version: 16.3.2 - resolution: "dotenv@npm:16.3.2" - checksum: a87d62cef0810b670cb477db1a24a42a093b6b428c9e65c185ce1d6368ad7175234b13547718ba08da18df43faae4f814180cc0366e11be1ded2277abc4dd22e + version: 16.3.1 + resolution: "dotenv@npm:16.3.1" + checksum: b95ff1bbe624ead85a3cd70dbd827e8e06d5f05f716f2d0cbc476532d54c7c9469c3bc4dd93ea519f6ad711cb522c00ac9a62b6eb340d5affae8008facc3fbd7 languageName: node linkType: hard @@ -5960,9 +6039,9 @@ __metadata: linkType: hard "electron-to-chromium@npm:^1.4.601": - version: 1.4.642 - resolution: "electron-to-chromium@npm:1.4.642" - checksum: 1a45c745e4cfa6dd0a756ca7df8295dd227b80a6d3552ccedd9665525aedab53d5335e8b97d64c02a6b10b0c773f8b9c20877875b3a3eb2428330f14d3892f27 + version: 1.4.615 + resolution: "electron-to-chromium@npm:1.4.615" + checksum: 6602172761e44ca1a6c010a010efd0c42710e1e08911e76dd2d3df72ae2a563fb75b0853387273d1e45a4befd314162b2b1debcf9055513f62c6d6a8df4de73a languageName: node linkType: hard @@ -6300,11 +6379,11 @@ __metadata: linkType: hard "eslint-plugin-prettier@npm:^5.0.1": - version: 5.1.3 - resolution: "eslint-plugin-prettier@npm:5.1.3" + version: 5.1.0 + resolution: "eslint-plugin-prettier@npm:5.1.0" dependencies: prettier-linter-helpers: "npm:^1.0.0" - synckit: "npm:^0.8.6" + synckit: "npm:^0.8.5" peerDependencies: "@types/eslint": ">=8.0.0" eslint: ">=8.0.0" @@ -6315,7 +6394,7 @@ __metadata: optional: true eslint-config-prettier: optional: true - checksum: f45d5fc1fcfec6b0cf038a7a65ddd10a25df4fe3f9e1f6b7f0d5100e66f046a26a2492e69ee765dddf461b93c114cf2e1eb18d4970aafa6f385448985c136e09 + checksum: 76b9a6cc5fa8dcc0d5d8aac5e7b717c08c736d2f158ba63709d94526d3152761d36963f930f9c64a29270a107519eca24b41e82b67cc43f0291d43db4a975fd1 languageName: node linkType: hard @@ -6525,14 +6604,14 @@ __metadata: linkType: hard "ethereum-cryptography@npm:^2.0.0": - version: 2.1.3 - resolution: "ethereum-cryptography@npm:2.1.3" + version: 2.1.2 + resolution: "ethereum-cryptography@npm:2.1.2" dependencies: - "@noble/curves": "npm:1.3.0" - "@noble/hashes": "npm:1.3.3" - "@scure/bip32": "npm:1.3.3" - "@scure/bip39": "npm:1.2.2" - checksum: a2f25ad5ffa44b4364b1540a57969ee6f1dd820aa08a446f40f31203fef54a09442a6c099e70e7c1485922f6391c4c45b90f2c401e04d88ac9cc4611b05e606f + "@noble/curves": "npm:1.1.0" + "@noble/hashes": "npm:1.3.1" + "@scure/bip32": "npm:1.3.1" + "@scure/bip39": "npm:1.2.1" + checksum: 784552709e3afd4ae9c606f3cf04ced49ab69f3864df58aca64f15317641470afd44573cbda821b9cf6781dac6dd3a95559fcc062299e23394094a3370387ec6 languageName: node linkType: hard @@ -6623,20 +6702,20 @@ __metadata: languageName: node linkType: hard -"execa@npm:^8.0.1": - version: 8.0.1 - resolution: "execa@npm:8.0.1" +"execa@npm:^7.1.1": + version: 7.2.0 + resolution: "execa@npm:7.2.0" dependencies: cross-spawn: "npm:^7.0.3" - get-stream: "npm:^8.0.1" - human-signals: "npm:^5.0.0" + get-stream: "npm:^6.0.1" + human-signals: "npm:^4.3.0" is-stream: "npm:^3.0.0" merge-stream: "npm:^2.0.0" npm-run-path: "npm:^5.1.0" onetime: "npm:^6.0.0" - signal-exit: "npm:^4.1.0" + signal-exit: "npm:^3.0.7" strip-final-newline: "npm:^3.0.0" - checksum: 2c52d8775f5bf103ce8eec9c7ab3059909ba350a5164744e9947ed14a53f51687c040a250bda833f906d1283aa8803975b84e6c8f7a7c42f99dc8ef80250d1af + checksum: 098cd6a1bc26d509e5402c43f4971736450b84d058391820c6f237aeec6436963e006fd8423c9722f148c53da86aa50045929c7278b5522197dff802d10f9885 languageName: node linkType: hard @@ -6901,12 +6980,12 @@ __metadata: linkType: hard "follow-redirects@npm:^1.0.0, follow-redirects@npm:^1.14.0": - version: 1.15.5 - resolution: "follow-redirects@npm:1.15.5" + version: 1.15.3 + resolution: "follow-redirects@npm:1.15.3" peerDependenciesMeta: debug: optional: true - checksum: 418d71688ceaf109dfd6f85f747a0c75de30afe43a294caa211def77f02ef19865b547dfb73fde82b751e1cc507c06c754120b848fe5a7400b0a669766df7615 + checksum: 915a2cf22e667bdf47b1a43cc6b7dce14d95039e9bbf9a24d0e739abfbdfa00077dd43c86d4a7a19efefcc7a99af144920a175eedc3888d268af5df67c272ee5 languageName: node linkType: hard @@ -6954,7 +7033,7 @@ __metadata: languageName: node linkType: hard -"fraction.js@npm:^4.3.7": +"fraction.js@npm:^4.3.6": version: 4.3.7 resolution: "fraction.js@npm:4.3.7" checksum: df291391beea9ab4c263487ffd9d17fed162dbb736982dee1379b2a8cc94e4e24e46ed508c6d278aded9080ba51872f1bc5f3a5fd8d7c74e5f105b508ac28711 @@ -7124,10 +7203,10 @@ __metadata: languageName: node linkType: hard -"get-port-please@npm:^3.1.2": - version: 3.1.2 - resolution: "get-port-please@npm:3.1.2" - checksum: 61237342fe035967e5ad1b67a2dee347a64de093bf1222b7cd50072568d73c48dad5cc5cd4fa44635b7cfdcd14d6c47554edb9891c2ec70ab33ecb831683e257 +"get-port-please@npm:^3.1.1": + version: 3.1.1 + resolution: "get-port-please@npm:3.1.1" + checksum: d9229fd671cf43ab846bf187aad917e10688f154db467e0dbc423d0ab9f47363f9612bfb9094a89de196873a3966d33c907475a76bbfd7b68d81caf610035958 languageName: node linkType: hard @@ -7138,20 +7217,13 @@ __metadata: languageName: node linkType: hard -"get-stream@npm:^6.0.0": +"get-stream@npm:^6.0.0, get-stream@npm:^6.0.1": version: 6.0.1 resolution: "get-stream@npm:6.0.1" checksum: 49825d57d3fd6964228e6200a58169464b8e8970489b3acdc24906c782fb7f01f9f56f8e6653c4a50713771d6658f7cfe051e5eb8c12e334138c9c918b296341 languageName: node linkType: hard -"get-stream@npm:^8.0.1": - version: 8.0.1 - resolution: "get-stream@npm:8.0.1" - checksum: 5c2181e98202b9dae0bb4a849979291043e5892eb40312b47f0c22b9414fc9b28a3b6063d2375705eb24abc41ecf97894d9a51f64ff021511b504477b27b4290 - languageName: node - linkType: hard - "get-symbol-description@npm:^1.0.0": version: 1.0.0 resolution: "get-symbol-description@npm:1.0.0" @@ -7352,9 +7424,9 @@ __metadata: languageName: node linkType: hard -"h3@npm:^1.10.0, h3@npm:^1.8.2": - version: 1.10.0 - resolution: "h3@npm:1.10.0" +"h3@npm:^1.8.1, h3@npm:^1.8.2": + version: 1.9.0 + resolution: "h3@npm:1.9.0" dependencies: cookie-es: "npm:^1.0.0" defu: "npm:^6.1.3" @@ -7363,8 +7435,8 @@ __metadata: radix3: "npm:^1.1.0" ufo: "npm:^1.3.2" uncrypto: "npm:^0.1.3" - unenv: "npm:^1.8.0" - checksum: 23b474c9472ffd14e0e0e2f97b03f311c9b53d996a5d468f93f223e111320f71cb637a25731d1f803224cff16f942c7cc03aff35a76dedcd88cce6e7c9122d87 + unenv: "npm:^1.7.4" + checksum: 90e80c34c9d0b7bdb24b13865ac27a88ca7724f0d1ce005295ae16408d4527020328a077d6c5df02de9f7ce7a15ab8a110978e1394a31717b07a34f09be91c06 languageName: node linkType: hard @@ -7403,7 +7475,7 @@ __metadata: languageName: node linkType: hard -"has-property-descriptors@npm:^1.0.0, has-property-descriptors@npm:^1.0.1": +"has-property-descriptors@npm:^1.0.0": version: 1.0.1 resolution: "has-property-descriptors@npm:1.0.1" dependencies: @@ -7714,10 +7786,10 @@ __metadata: languageName: node linkType: hard -"human-signals@npm:^5.0.0": - version: 5.0.0 - resolution: "human-signals@npm:5.0.0" - checksum: 5a9359073fe17a8b58e5a085e9a39a950366d9f00217c4ff5878bd312e09d80f460536ea6a3f260b5943a01fe55c158d1cea3fc7bee3d0520aeef04f6d915c82 +"human-signals@npm:^4.3.0": + version: 4.3.1 + resolution: "human-signals@npm:4.3.1" + checksum: 40498b33fe139f5cc4ef5d2f95eb1803d6318ac1b1c63eaf14eeed5484d26332c828de4a5a05676b6c83d7b9e57727c59addb4b1dea19cb8d71e83689e5b336c languageName: node linkType: hard @@ -8298,24 +8370,6 @@ __metadata: languageName: node linkType: hard -"is-wsl@npm:^3.1.0": - version: 3.1.0 - resolution: "is-wsl@npm:3.1.0" - dependencies: - is-inside-container: "npm:^1.0.0" - checksum: d3317c11995690a32c362100225e22ba793678fe8732660c6de511ae71a0ff05b06980cf21f98a6bf40d7be0e9e9506f859abe00a1118287d63e53d0a3d06947 - languageName: node - linkType: hard - -"is64bit@npm:^2.0.0": - version: 2.0.0 - resolution: "is64bit@npm:2.0.0" - dependencies: - system-architecture: "npm:^0.1.0" - checksum: 9f3741d4b7560e2a30b9ce0c79bb30c7bdcc5df77c897bd59bb68f0fd882ae698015e8da81d48331def66c778d430c1ae3cb8c1fcc34e96c576b66198395faa7 - languageName: node - linkType: hard - "isarray@npm:^2.0.1, isarray@npm:^2.0.5": version: 2.0.5 resolution: "isarray@npm:2.0.5" @@ -8888,7 +8942,7 @@ __metadata: languageName: node linkType: hard -"jiti@npm:^1.20.0, jiti@npm:^1.21.0": +"jiti@npm:^1.18.2, jiti@npm:^1.20.0": version: 1.21.0 resolution: "jiti@npm:1.21.0" bin: @@ -9023,9 +9077,9 @@ __metadata: linkType: hard "jsonc-parser@npm:^3.2.0": - version: 3.2.1 - resolution: "jsonc-parser@npm:3.2.1" - checksum: ada66dec143d7f9cb0e2d0d29c69e9ce40d20f3a4cb96b0c6efb745025ac7f9ba647d7ac0990d0adfc37a2d2ae084a12009a9c833dbdbeadf648879a99b9df89 + version: 3.2.0 + resolution: "jsonc-parser@npm:3.2.0" + checksum: 5a12d4d04dad381852476872a29dcee03a57439574e4181d91dca71904fcdcc5e8e4706c0a68a2c61ad9810e1e1c5806b5100d52d3e727b78f5cdc595401045b languageName: node linkType: hard @@ -9141,14 +9195,14 @@ __metadata: linkType: hard "koa@npm:^2.14.2": - version: 2.15.0 - resolution: "koa@npm:2.15.0" + version: 2.14.2 + resolution: "koa@npm:2.14.2" dependencies: accepts: "npm:^1.3.5" cache-content-type: "npm:^1.0.0" content-disposition: "npm:~0.5.2" content-type: "npm:^1.0.4" - cookies: "npm:~0.9.0" + cookies: "npm:~0.8.0" debug: "npm:^4.3.2" delegates: "npm:^1.0.0" depd: "npm:^2.0.0" @@ -9167,7 +9221,7 @@ __metadata: statuses: "npm:^1.5.0" type-is: "npm:^1.6.16" vary: "npm:^1.1.2" - checksum: 018daa5d3521621699e4228de9191849083c0356e1e4abda6d96aa44fa3ee1f6a67849040c2a0b681697d1431a8232cca1e532a7246fc785257bfdf1e6ccf43a + checksum: f60ae84974d7cb834a5937592e010d97134278527a55a3a38973935db9ea409fecbd824e5ff0fa767077d9a9dc1f1a32881b626ddaad8b5978756c345f520cdd languageName: node linkType: hard @@ -9265,30 +9319,30 @@ __metadata: linkType: hard "listhen@npm:^1.5.5": - version: 1.5.6 - resolution: "listhen@npm:1.5.6" + version: 1.5.5 + resolution: "listhen@npm:1.5.5" dependencies: "@parcel/watcher": "npm:^2.3.0" "@parcel/watcher-wasm": "npm:2.3.0" - citty: "npm:^0.1.5" - clipboardy: "npm:^4.0.0" + citty: "npm:^0.1.4" + clipboardy: "npm:^3.0.0" consola: "npm:^3.2.3" - defu: "npm:^6.1.4" - get-port-please: "npm:^3.1.2" - h3: "npm:^1.10.0" + defu: "npm:^6.1.2" + get-port-please: "npm:^3.1.1" + h3: "npm:^1.8.1" http-shutdown: "npm:^1.2.2" - jiti: "npm:^1.21.0" + jiti: "npm:^1.20.0" mlly: "npm:^1.4.2" node-forge: "npm:^1.3.1" pathe: "npm:^1.1.1" - std-env: "npm:^3.7.0" - ufo: "npm:^1.3.2" - untun: "npm:^0.1.3" + std-env: "npm:^3.4.3" + ufo: "npm:^1.3.0" + untun: "npm:^0.1.2" uqr: "npm:^0.1.2" bin: listen: bin/listhen.mjs listhen: bin/listhen.mjs - checksum: 7c004a994eb77b0034fdecf9b8b146427182c970a5bc98296a745ee4ef9a51c4e0ce13413603409742e495027c8dd9245f674d8ac064f4df901bd35bddff8eee + checksum: 84a8a6c0e0d347db3110af3f77aa86fba428fcec1e2cd53e17d0d8daf36edd8833c75a647b718e6cea723d452b0b2a78b2290d03c79315c52eda1f1984384bb2 languageName: node linkType: hard @@ -9827,21 +9881,21 @@ __metadata: languageName: node linkType: hard -"minimatch@npm:9.0.3, minimatch@npm:^9.0.1": - version: 9.0.3 - resolution: "minimatch@npm:9.0.3" +"minimatch@npm:^5.0.1": + version: 5.1.6 + resolution: "minimatch@npm:5.1.6" dependencies: brace-expansion: "npm:^2.0.1" - checksum: 85f407dcd38ac3e180f425e86553911d101455ca3ad5544d6a7cec16286657e4f8a9aa6695803025c55e31e35a91a2252b5dc8e7d527211278b8b65b4dbd5eac + checksum: 3defdfd230914f22a8da203747c42ee3c405c39d4d37ffda284dac5e45b7e1f6c49aa8be606509002898e73091ff2a3bbfc59c2c6c71d4660609f63aa92f98e3 languageName: node linkType: hard -"minimatch@npm:^5.0.1": - version: 5.1.6 - resolution: "minimatch@npm:5.1.6" +"minimatch@npm:^9.0.1": + version: 9.0.3 + resolution: "minimatch@npm:9.0.3" dependencies: brace-expansion: "npm:^2.0.1" - checksum: 3defdfd230914f22a8da203747c42ee3c405c39d4d37ffda284dac5e45b7e1f6c49aa8be606509002898e73091ff2a3bbfc59c2c6c71d4660609f63aa92f98e3 + checksum: 85f407dcd38ac3e180f425e86553911d101455ca3ad5544d6a7cec16286657e4f8a9aa6695803025c55e31e35a91a2252b5dc8e7d527211278b8b65b4dbd5eac languageName: node linkType: hard @@ -10005,14 +10059,14 @@ __metadata: linkType: hard "mlly@npm:^1.2.0, mlly@npm:^1.4.2": - version: 1.5.0 - resolution: "mlly@npm:1.5.0" + version: 1.4.2 + resolution: "mlly@npm:1.4.2" dependencies: - acorn: "npm:^8.11.3" - pathe: "npm:^1.1.2" + acorn: "npm:^8.10.0" + pathe: "npm:^1.1.1" pkg-types: "npm:^1.0.3" - ufo: "npm:^1.3.2" - checksum: 0861d64f13e8e6f99e4897b652b553ded4d4b9e7b011d6afd7141e013b77ed9b9be0cd76e60c46c60c56cc9b8e27061165e5696179ba9f4161c24d162db7b621 + ufo: "npm:^1.3.0" + checksum: 905e3a704c7d3bcaad55f31d6efe9f680eab5be053ab7f8b299b8dbc027041f741fa6a93db9a3c461be2552632f3831b6c43c50af530f5fb2e9cd6273bc9d642 languageName: node linkType: hard @@ -10181,18 +10235,18 @@ __metadata: linkType: hard "node-addon-api@npm:^7.0.0": - version: 7.1.0 - resolution: "node-addon-api@npm:7.1.0" + version: 7.0.0 + resolution: "node-addon-api@npm:7.0.0" dependencies: node-gyp: "npm:latest" - checksum: 2e096ab079e3c46d33b0e252386e9c239c352f7cc6d75363d9a3c00bdff34c1a5da170da861917512843f213c32d024ced9dc9552b968029786480d18727ec66 + checksum: 3d5a15ee434e122b345e614db122a63f30194c298104c3d8a0fa9f68707abb278af27b45222602456a131890a59b4a92291ff5b4b7938ff282168e9ad1bf7103 languageName: node linkType: hard -"node-fetch-native@npm:^1.4.0, node-fetch-native@npm:^1.4.1, node-fetch-native@npm:^1.6.1": - version: 1.6.1 - resolution: "node-fetch-native@npm:1.6.1" - checksum: 5df52cd7fb18a51b7e3ec65420b04cd5c01ce6a15ca853b6112a3ae17eb071970a15e7099f3bd258006ab8a0cecac3c7c212800a680466c5bb1a679eab14338f +"node-fetch-native@npm:^1.4.0, node-fetch-native@npm:^1.4.1": + version: 1.4.1 + resolution: "node-fetch-native@npm:1.4.1" + checksum: ab298a42ebf3b1b6c6a8cbc53d8ba703895f55171ed743b0828c2a87d461642d8053143864915a69d41cc01013db86406da105fff6c0a05a00d8caf5c279549c languageName: node linkType: hard @@ -10218,13 +10272,13 @@ __metadata: linkType: hard "node-gyp-build@npm:^4.2.0, node-gyp-build@npm:^4.3.0": - version: 4.8.0 - resolution: "node-gyp-build@npm:4.8.0" + version: 4.7.1 + resolution: "node-gyp-build@npm:4.7.1" bin: node-gyp-build: bin.js node-gyp-build-optional: optional.js node-gyp-build-test: build-test.js - checksum: 85324be16f81f0235cbbc42e3eceaeb1b5ab94c8d8f5236755e1435b4908338c65a4e75f66ee343cbcb44ddf9b52a428755bec16dcd983295be4458d95c8e1ad + checksum: b8e4a3f889237cd08edde3775e2b4e1e39a0571580584e33e29979f0c532a254ce3c5ec9435bd526254ad0b3f0b4a7e7fe14e53bd400f6ea9445f3bfd88a6b1e languageName: node linkType: hard @@ -10376,11 +10430,11 @@ __metadata: linkType: hard "npm-run-path@npm:^5.1.0": - version: 5.2.0 - resolution: "npm-run-path@npm:5.2.0" + version: 5.1.0 + resolution: "npm-run-path@npm:5.1.0" dependencies: path-key: "npm:^4.0.0" - checksum: 7963c1f98e42afebe9524a08b0881477ec145aab34f6018842a315422b25ad40e015bdee709b697571e5efda2ecfa2640ee917d92674e4de1166fa3532a211b1 + checksum: ff6d77514489f47fa1c3b1311d09cd4b6d09a874cc1866260f9dea12cbaabda0436ed7f8c2ee44d147bf99a3af29307c6f63b0f83d242b0b6b0ab25dff2629e3 languageName: node linkType: hard @@ -10559,6 +10613,18 @@ __metadata: languageName: node linkType: hard +"open@npm:^9.1.0": + version: 9.1.0 + resolution: "open@npm:9.1.0" + dependencies: + default-browser: "npm:^4.0.0" + define-lazy-prop: "npm:^3.0.0" + is-inside-container: "npm:^1.0.0" + is-wsl: "npm:^2.2.0" + checksum: 8073ec0dd8994a7a7d9bac208bd17d093993a65ce10f2eb9b62b6d3a91c9366ae903938a237c275493c130171d339f6dcbdd2a2de7e32953452c0867b97825af + languageName: node + linkType: hard + "optionator@npm:^0.9.3": version: 0.9.3 resolution: "optionator@npm:0.9.3" @@ -10773,10 +10839,10 @@ __metadata: languageName: node linkType: hard -"pathe@npm:^1.1.0, pathe@npm:^1.1.1, pathe@npm:^1.1.2": - version: 1.1.2 - resolution: "pathe@npm:1.1.2" - checksum: 64ee0a4e587fb0f208d9777a6c56e4f9050039268faaaaecd50e959ef01bf847b7872785c36483fa5cdcdbdfdb31fef2ff222684d4fc21c330ab60395c681897 +"pathe@npm:^1.1.0, pathe@npm:^1.1.1": + version: 1.1.1 + resolution: "pathe@npm:1.1.1" + checksum: 3ae5a0529c3415d91c3ac9133f52cffea54a0dd46892fe059f4b80faf36fd207957d4594bdc87043b65d0761b1e5728f81f46bafff3b5302da4e2e48889b8c0e languageName: node linkType: hard @@ -10902,16 +10968,16 @@ __metadata: linkType: hard "postcss-loader@npm:^7.3.3": - version: 7.3.4 - resolution: "postcss-loader@npm:7.3.4" + version: 7.3.3 + resolution: "postcss-loader@npm:7.3.3" dependencies: - cosmiconfig: "npm:^8.3.5" - jiti: "npm:^1.20.0" - semver: "npm:^7.5.4" + cosmiconfig: "npm:^8.2.0" + jiti: "npm:^1.18.2" + semver: "npm:^7.3.8" peerDependencies: postcss: ^7.0.0 || ^8.0.1 webpack: ^5.0.0 - checksum: 1bf7614aeea9ad1f8ee6be3a5451576c059391688ea67f825aedc2674056369597faeae4e4a81fe10843884c9904a71403d9a54197e1f560e8fbb9e61f2a2680 + checksum: d039654273f858be1f75dfdf8b550869d88905b73a7684b3e48a2937a6087619e84fd1a3551cdef78685a965a2573e985b29a532c3878d834071ecd2da0eb304 languageName: node linkType: hard @@ -10924,27 +10990,27 @@ __metadata: languageName: node linkType: hard -"postcss-modules-local-by-default@npm:^4.0.4": - version: 4.0.4 - resolution: "postcss-modules-local-by-default@npm:4.0.4" +"postcss-modules-local-by-default@npm:^4.0.3": + version: 4.0.3 + resolution: "postcss-modules-local-by-default@npm:4.0.3" dependencies: icss-utils: "npm:^5.0.0" postcss-selector-parser: "npm:^6.0.2" postcss-value-parser: "npm:^4.1.0" peerDependencies: postcss: ^8.1.0 - checksum: 9ebf464867eb10b29b73501b1466dcac8352ed852ef68ec23571f515daa74401d7ace9a6c72f354542081fdbb47d098c9bc6b05373b553a6e35779d072f967bb + checksum: be49b86efbfb921f42287e227584aac91af9826fc1083db04958ae283dfe215ca539421bfba71f9da0f0b10651f28e95a64b5faca7166f578a1933b8646051f7 languageName: node linkType: hard -"postcss-modules-scope@npm:^3.1.1": - version: 3.1.1 - resolution: "postcss-modules-scope@npm:3.1.1" +"postcss-modules-scope@npm:^3.0.0": + version: 3.1.0 + resolution: "postcss-modules-scope@npm:3.1.0" dependencies: postcss-selector-parser: "npm:^6.0.4" peerDependencies: postcss: ^8.1.0 - checksum: 3ef6ac14fcda1581bc43e37622256bd87b99ea49c59b2aae648d057d57f5ecc634648cce9910166220a797567af674bc09246ccc010f1dd58d2863b805719109 + checksum: bc8e12e9312d7070f34ccef2929f65154102e2b2984a385eaf2ef25b6d4e22234de71116c240a05b541a79946b717d6fa8c5d314f6697bf05f295261693050fe languageName: node linkType: hard @@ -10960,12 +11026,12 @@ __metadata: linkType: hard "postcss-selector-parser@npm:^6.0.2, postcss-selector-parser@npm:^6.0.4": - version: 6.0.15 - resolution: "postcss-selector-parser@npm:6.0.15" + version: 6.0.13 + resolution: "postcss-selector-parser@npm:6.0.13" dependencies: cssesc: "npm:^3.0.0" util-deprecate: "npm:^1.0.2" - checksum: 48b425d6cef497bcf6b7d136f6fd95cfca43026955e07ec9290d3c15457de3a862dbf251dd36f42c07a0d5b5ab6f31e41acefeff02528995a989b955505e440b + checksum: 51f099b27f7c7198ea1826470ef0adfa58b3bd3f59b390fda123baa0134880a5fa9720137b6009c4c1373357b144f700b0edac73335d0067422063129371444e languageName: node linkType: hard @@ -10976,14 +11042,14 @@ __metadata: languageName: node linkType: hard -"postcss@npm:^8.4.29, postcss@npm:^8.4.33": - version: 8.4.33 - resolution: "postcss@npm:8.4.33" +"postcss@npm:^8.4.21, postcss@npm:^8.4.29": + version: 8.4.32 + resolution: "postcss@npm:8.4.32" dependencies: nanoid: "npm:^3.3.7" picocolors: "npm:^1.0.0" source-map-js: "npm:^1.0.2" - checksum: 16eda83458fcd8a91bece287b5920c7f57164c3ea293e6c80d0ea71ce7843007bcd8592260a5160b9a7f02693e6ac93e2495b02d8c7596d3f3f72c1447e3ba79 + checksum: 39308a9195fa34d4dbdd7b58a896cff0c7809f84f7a4ac1b95b68ca86c9138a395addff33075668ed3983d41b90aac05754c445237a9365eb1c3a5602ebd03ad languageName: node linkType: hard @@ -11018,11 +11084,11 @@ __metadata: linkType: hard "prettier@npm:^3.1.1": - version: 3.2.4 - resolution: "prettier@npm:3.2.4" + version: 3.1.1 + resolution: "prettier@npm:3.1.1" bin: prettier: bin/prettier.cjs - checksum: 88dfeb78ac6096522c9a5b81f1413d875f568420d9bb6a5e5103527912519b993f2bcdcac311fcff5718d5869671d44e4f85827d3626f3a6ce32b9abc65d88e0 + checksum: facc944ba20e194ff4db765e830ffbcb642803381f0d2033ed397e79904fa4ccc877dc25ad68f42d36985c01d051c990ca1b905fb83d2d7d65fe69e4386fa1a3 languageName: node linkType: hard @@ -11387,26 +11453,26 @@ __metadata: linkType: hard "react-router-dom@npm:^6.8.0": - version: 6.21.3 - resolution: "react-router-dom@npm:6.21.3" + version: 6.21.1 + resolution: "react-router-dom@npm:6.21.1" dependencies: - "@remix-run/router": "npm:1.14.2" - react-router: "npm:6.21.3" + "@remix-run/router": "npm:1.14.1" + react-router: "npm:6.21.1" peerDependencies: react: ">=16.8" react-dom: ">=16.8" - checksum: 1bea7bf17eb148461a7a99c9314e5ccc662ae00f563e29c16038e0b1b40aefb7381685f21289df07ad2295087a783c897b4e841ed12b07cd9a0829274a224231 + checksum: 2c2b653de8cc90397e5b057c0d32dd780fe894c4bd36397005da169a7422b31988ebc14ef0da3ac3f2b7286b244f97a141974acaa86c731715816e64c8cd2912 languageName: node linkType: hard -"react-router@npm:6.21.3, react-router@npm:^6.8.0": - version: 6.21.3 - resolution: "react-router@npm:6.21.3" +"react-router@npm:6.21.1, react-router@npm:^6.8.0": + version: 6.21.1 + resolution: "react-router@npm:6.21.1" dependencies: - "@remix-run/router": "npm:1.14.2" + "@remix-run/router": "npm:1.14.1" peerDependencies: react: ">=16.8" - checksum: 66a0377a74ef2d298b9d617c02ba7a10e7e8b8be34b303068b5b5986f05e965037c51c0d45ea3a7967438f2dfde76c2c0f638cf19d3417fb408608ee9aee1668 + checksum: 81278cf25a999d7c93a31d540102e3b74b696eb4349779e22bba77b633e4e5f8f91b8c6f7946c9572a1e92f64363724150216f5643a6f9817f8bc1643cb8bdbc languageName: node linkType: hard @@ -11727,6 +11793,15 @@ __metadata: languageName: node linkType: hard +"run-applescript@npm:^5.0.0": + version: 5.0.0 + resolution: "run-applescript@npm:5.0.0" + dependencies: + execa: "npm:^5.0.0" + checksum: f9977db5770929f3f0db434b8e6aa266498c70dec913c84320c0a06add510cf44e3a048c44da088abee312006f9cbf572fd065cdc8f15d7682afda8755f4114c + languageName: node + linkType: hard + "run-parallel@npm:^1.1.9": version: 1.2.0 resolution: "run-parallel@npm:1.2.0" @@ -11737,14 +11812,14 @@ __metadata: linkType: hard "safe-array-concat@npm:^1.0.1": - version: 1.1.0 - resolution: "safe-array-concat@npm:1.1.0" + version: 1.0.1 + resolution: "safe-array-concat@npm:1.0.1" dependencies: - call-bind: "npm:^1.0.5" - get-intrinsic: "npm:^1.2.2" + call-bind: "npm:^1.0.2" + get-intrinsic: "npm:^1.2.1" has-symbols: "npm:^1.0.3" isarray: "npm:^2.0.5" - checksum: 833d3d950fc7507a60075f9bfaf41ec6dac7c50c7a9d62b1e6b071ecc162185881f92e594ff95c1a18301c881352dd6fd236d56999d5819559db7b92da9c28af + checksum: 4b15ce5fce5ce4d7e744a63592cded88d2f27806ed229eadb2e42629cbcd40e770f7478608e75f455e7fe341acd8c0a01bdcd7146b10645ea7411c5e3c1d1dd8 languageName: node linkType: hard @@ -11770,13 +11845,13 @@ __metadata: linkType: hard "safe-regex-test@npm:^1.0.0": - version: 1.0.2 - resolution: "safe-regex-test@npm:1.0.2" + version: 1.0.0 + resolution: "safe-regex-test@npm:1.0.0" dependencies: - call-bind: "npm:^1.0.5" - get-intrinsic: "npm:^1.2.2" + call-bind: "npm:^1.0.2" + get-intrinsic: "npm:^1.1.3" is-regex: "npm:^1.1.4" - checksum: c24df9c3cbd9e6a6800f02411a12ce2bd642be22ce6ad03f796e7b3f3851d9eb1fb8d1fab48278b04fabe75dd279c10bc07a45e39543aa72407fbd8a31174958 + checksum: 14a81a7e683f97b2d6e9c8be61fddcf8ed7a02f4e64a825515f96bb1738eb007145359313741d2704d28b55b703a0f6300c749dde7c1dbc13952a2b85048ede2 languageName: node linkType: hard @@ -11809,8 +11884,8 @@ __metadata: linkType: hard "sass-loader@npm:^13.3.2": - version: 13.3.3 - resolution: "sass-loader@npm:13.3.3" + version: 13.3.2 + resolution: "sass-loader@npm:13.3.2" dependencies: neo-async: "npm:^2.6.2" peerDependencies: @@ -11828,20 +11903,20 @@ __metadata: optional: true sass-embedded: optional: true - checksum: 5e955a4ffce35ee0a46fce677ce51eaa69587fb5371978588c83af00f49e7edc36dcf3bb559cbae27681c5e24a71284463ebe03a1fb65e6ecafa1db0620e3fc8 + checksum: 7db8132101ed663f3cf936ce765b9b960a48b14f13f17d367a4e0c2ae259e91b6c401e33ab0f27ee88c98c8b5893c778848fc8366f1f387ac788ebef244e000a languageName: node linkType: hard "sass@npm:^1.58.0": - version: 1.70.0 - resolution: "sass@npm:1.70.0" + version: 1.69.5 + resolution: "sass@npm:1.69.5" dependencies: chokidar: "npm:>=3.0.0 <4.0.0" immutable: "npm:^4.0.0" source-map-js: "npm:>=0.6.2 <2.0.0" bin: sass: sass.js - checksum: 7c309ee1c096d591746d122da9f1ebd65b4c4b3a60c2cc0ec720fd98fe1205fa8b44c9f563d113b9fdfeb25af1e32ec9b3e048bd4b8e05d267f020953bd7baf0 + checksum: a9003a9482f2e467fc412cfe58ba4fa14fb78bef7e1283ce5d64a065f8a31114ec3bbf5d4e724f94eb8512c32c768a6f91f228c7f16a26a300bbf4db293b5608 languageName: node linkType: hard @@ -11981,11 +12056,11 @@ __metadata: linkType: hard "serialize-javascript@npm:^6.0.0, serialize-javascript@npm:^6.0.1": - version: 6.0.2 - resolution: "serialize-javascript@npm:6.0.2" + version: 6.0.1 + resolution: "serialize-javascript@npm:6.0.1" dependencies: randombytes: "npm:^2.1.0" - checksum: 2dd09ef4b65a1289ba24a788b1423a035581bef60817bea1f01eda8e3bda623f86357665fe7ac1b50f6d4f583f97db9615b3f07b2a2e8cbcb75033965f771dd2 + checksum: 1af427f4fee3fee051f54ffe15f77068cff78a3c96d20f5c1178d20630d3ab122d8350e639d5e13cde8111ef9db9439b871305ffb185e24be0a2149cec230988 languageName: node linkType: hard @@ -12061,15 +12136,14 @@ __metadata: linkType: hard "set-function-length@npm:^1.1.1": - version: 1.2.0 - resolution: "set-function-length@npm:1.2.0" + version: 1.1.1 + resolution: "set-function-length@npm:1.1.1" dependencies: define-data-property: "npm:^1.1.1" - function-bind: "npm:^1.1.2" - get-intrinsic: "npm:^1.2.2" + get-intrinsic: "npm:^1.2.1" gopd: "npm:^1.0.1" - has-property-descriptors: "npm:^1.0.1" - checksum: b4fdf68bbfa9944284a9469c04e0d9cdb7924942fab75cd11fb61e8a7518f0d40bbbbc1b46871f648a93b97d170d8047fe3492cdadff066a8a8ae4ce68d0564a + has-property-descriptors: "npm:^1.0.0" + checksum: a29e255c116c29e3323b851c4f46c58c91be9bb8b065f191e2ea1807cb2c839df56e3175732a498e0c6d54626ba6b6fef896bf699feb7ab70c42dc47eb247c95 languageName: node linkType: hard @@ -12169,7 +12243,7 @@ __metadata: languageName: node linkType: hard -"signal-exit@npm:^4.0.1, signal-exit@npm:^4.1.0": +"signal-exit@npm:^4.0.1": version: 4.1.0 resolution: "signal-exit@npm:4.1.0" checksum: 41602dce540e46d599edba9d9860193398d135f7ff72cab629db5171516cfae628d21e7bfccde1bbfdf11c48726bc2a6d1a8fb8701125852fbfda7cf19c6aa83 @@ -12447,10 +12521,10 @@ __metadata: languageName: node linkType: hard -"std-env@npm:^3.7.0": - version: 3.7.0 - resolution: "std-env@npm:3.7.0" - checksum: 60edf2d130a4feb7002974af3d5a5f3343558d1ccf8d9b9934d225c638606884db4a20d2fe6440a09605bca282af6b042ae8070a10490c0800d69e82e478f41e +"std-env@npm:^3.4.3": + version: 3.6.0 + resolution: "std-env@npm:3.6.0" + checksum: a540b8cb011bef4bf5905e1e28f24ce37124f9d001c69224ee0025d3600144e6847bac62cd38fbd98148ab4d26ab0682b9b4d42bc863cd1cca0b9807f18aadba languageName: node linkType: hard @@ -12483,9 +12557,9 @@ __metadata: linkType: hard "stream-shift@npm:^1.0.0": - version: 1.0.3 - resolution: "stream-shift@npm:1.0.3" - checksum: 939cd1051ca750d240a0625b106a2b988c45fb5a3be0cebe9a9858cb01bc1955e8c7b9fac17a9462976bea4a7b704e317c5c2200c70f0ca715a3363b9aa4fd3b + version: 1.0.1 + resolution: "stream-shift@npm:1.0.1" + checksum: b63a0d178cde34b920ad93e2c0c9395b840f408d36803b07c61416edac80ef9e480a51910e0ceea0d679cec90921bcd2cccab020d3a9fa6c73a98b0fbec132fd languageName: node linkType: hard @@ -12669,11 +12743,11 @@ __metadata: linkType: hard "style-loader@npm:^3.3.1, style-loader@npm:^3.3.3": - version: 3.3.4 - resolution: "style-loader@npm:3.3.4" + version: 3.3.3 + resolution: "style-loader@npm:3.3.3" peerDependencies: webpack: ^5.0.0 - checksum: 8f8027fc5c6e91400cbb60066e7db3315810f8eaa0d19b2a254936eb0bec399ba8a7043b1789da9d05ab7c3ba50faf9267765ae0bf3571e48aa34ecdc774be37 + checksum: 104bae8abd0627579dc14f3917cf65f1117e8098e3529872f09c26b5eee07933567b7be5c8ebf94d16e322b6e726dc569c5787111bf3786915850db4e351ef33 languageName: node linkType: hard @@ -12718,20 +12792,13 @@ __metadata: languageName: node linkType: hard -"synckit@npm:^0.8.6": - version: 0.8.8 - resolution: "synckit@npm:0.8.8" +"synckit@npm:^0.8.5": + version: 0.8.6 + resolution: "synckit@npm:0.8.6" dependencies: - "@pkgr/core": "npm:^0.1.0" + "@pkgr/utils": "npm:^2.4.2" tslib: "npm:^2.6.2" - checksum: c3d3aa8e284f3f84f2f868b960c9f49239b364e35f6d20825a448449a3e9c8f49fe36cdd5196b30615682f007830d46f2ea354003954c7336723cb821e4b6519 - languageName: node - linkType: hard - -"system-architecture@npm:^0.1.0": - version: 0.1.0 - resolution: "system-architecture@npm:0.1.0" - checksum: 1969974ea5d31a9ac7c38f2657cfe8255b36f9e1d5ba3c58cb84c24fbeedf562778b8511f18a0abe6d70ae90148cfcaf145ecf26e37c0a53a3829076f3238cbb + checksum: 200528062e3915a0190a4c6b1e01436fcfdf812e2e8d977746746f3998bb4182d758af760e51b06a64f8323e705735aff7b4b3efc4a0ab5f75eaccc044a8cfcc languageName: node linkType: hard @@ -12757,14 +12824,14 @@ __metadata: linkType: hard "terser-webpack-plugin@npm:^5.3.7": - version: 5.3.10 - resolution: "terser-webpack-plugin@npm:5.3.10" + version: 5.3.9 + resolution: "terser-webpack-plugin@npm:5.3.9" dependencies: - "@jridgewell/trace-mapping": "npm:^0.3.20" + "@jridgewell/trace-mapping": "npm:^0.3.17" jest-worker: "npm:^27.4.5" schema-utils: "npm:^3.1.1" serialize-javascript: "npm:^6.0.1" - terser: "npm:^5.26.0" + terser: "npm:^5.16.8" peerDependencies: webpack: ^5.1.0 peerDependenciesMeta: @@ -12774,13 +12841,13 @@ __metadata: optional: true uglify-js: optional: true - checksum: 66d1ed3174542560911cf96f4716aeea8d60e7caab212291705d50072b6ba844c7391442541b13c848684044042bea9ec87512b8506528c12854943da05faf91 + checksum: 8a757106101ea1504e5dc549c722506506e7d3f0d38e72d6c8108ad814c994ca0d67ac5d0825ba59704a4b2b04548201b2137f198bfce897b09fe9e36727a1e9 languageName: node linkType: hard -"terser@npm:^5.26.0": - version: 5.27.0 - resolution: "terser@npm:5.27.0" +"terser@npm:^5.16.8": + version: 5.26.0 + resolution: "terser@npm:5.26.0" dependencies: "@jridgewell/source-map": "npm:^0.3.3" acorn: "npm:^8.8.2" @@ -12788,7 +12855,7 @@ __metadata: source-map-support: "npm:~0.5.20" bin: terser: bin/terser - checksum: bed0d39d9a7f2b82c87173e48081c46426a8820ba1dcb864bbfccd2df2b7fb8498a7ea4c8ef045ccce5713b23a6b4c3a784967f1b9f3115adaa7f51712f6e6ae + checksum: 3906289c6bacd75804a47a583cdafefbd76c5edb39435369755c7b1592e57586fb2f4bddf6eb37a807d6e782171dbf0aa7bbdc80fd5b77b2f2b62196cac49b62 languageName: node linkType: hard @@ -12840,6 +12907,13 @@ __metadata: languageName: node linkType: hard +"titleize@npm:^3.0.0": + version: 3.0.0 + resolution: "titleize@npm:3.0.0" + checksum: 5ae6084ba299b5782f95e3fe85ea9f0fa4d74b8ae722b6b3208157e975589fbb27733aeba4e5080fa9314a856044ef52caa61b87caea4b1baade951a55c06336 + languageName: node + linkType: hard + "tmpl@npm:1.0.5": version: 1.0.5 resolution: "tmpl@npm:1.0.5" @@ -12915,8 +12989,8 @@ __metadata: linkType: hard "ts-jest@npm:^29.1.0, ts-jest@npm:^29.1.1": - version: 29.1.2 - resolution: "ts-jest@npm:29.1.2" + version: 29.1.1 + resolution: "ts-jest@npm:29.1.1" dependencies: bs-logger: "npm:0.x" fast-json-stable-stringify: "npm:2.x" @@ -12943,7 +13017,7 @@ __metadata: optional: true bin: ts-jest: cli.js - checksum: c2f51f0241f89d127d41392decbcb83b5dfd5e57ab9d50220aa7b7e2f9b3f3b07ccdbba33311284df1c41941879e4ddfad44b15a9d0da4b74bd1b98702b729df + checksum: 6c45e0aeeff9cc54a64f931c43e1b99f4a1f0ddf44786cc128e7e55603ab7473c8c8f62fd83bd7e51bfe83e3c0c683132152efaeb844516bf7c923f4e92d157d languageName: node linkType: hard @@ -13027,7 +13101,7 @@ __metadata: languageName: node linkType: hard -"tslib@npm:^2.0.0, tslib@npm:^2.1.0, tslib@npm:^2.3.1, tslib@npm:^2.4.0, tslib@npm:^2.5.0, tslib@npm:^2.6.2": +"tslib@npm:^2.0.0, tslib@npm:^2.1.0, tslib@npm:^2.3.1, tslib@npm:^2.4.0, tslib@npm:^2.5.0, tslib@npm:^2.6.0, tslib@npm:^2.6.2": version: 2.6.2 resolution: "tslib@npm:2.6.2" checksum: e03a8a4271152c8b26604ed45535954c0a45296e32445b4b87f8a5abdb2421f40b59b4ca437c4346af0f28179780d604094eb64546bee2019d903d01c6c19bdb @@ -13243,16 +13317,16 @@ __metadata: languageName: node linkType: hard -"unenv@npm:^1.8.0": - version: 1.9.0 - resolution: "unenv@npm:1.9.0" +"unenv@npm:^1.7.4": + version: 1.8.0 + resolution: "unenv@npm:1.8.0" dependencies: consola: "npm:^3.2.3" defu: "npm:^6.1.3" mime: "npm:^3.0.0" - node-fetch-native: "npm:^1.6.1" + node-fetch-native: "npm:^1.4.1" pathe: "npm:^1.1.1" - checksum: d00012badc83731c07f08d5129c702c49c0212375eb3732b27aae89ace3c67162dbaea4496965676f18fc06b0ec445d91385e283f5fd3e4540dda8b0b5424f81 + checksum: f5ad66425ef5b1848d2daab4bdb18e3f2576a4a8df48f3e994ef373290489a6251969b78b965963a905b90dc01db6e838e2deb826e384ec637df2345a146b0bb languageName: node linkType: hard @@ -13374,7 +13448,14 @@ __metadata: languageName: node linkType: hard -"untun@npm:^0.1.3": +"untildify@npm:^4.0.0": + version: 4.0.0 + resolution: "untildify@npm:4.0.0" + checksum: d758e624c707d49f76f7511d75d09a8eda7f2020d231ec52b67ff4896bcf7013be3f9522d8375f57e586e9a2e827f5641c7e06ee46ab9c435fc2b2b2e9de517a + languageName: node + linkType: hard + +"untun@npm:^0.1.2": version: 0.1.3 resolution: "untun@npm:0.1.3" dependencies: @@ -13428,8 +13509,8 @@ __metadata: linkType: hard "use-callback-ref@npm:^1.3.0": - version: 1.3.1 - resolution: "use-callback-ref@npm:1.3.1" + version: 1.3.0 + resolution: "use-callback-ref@npm:1.3.0" dependencies: tslib: "npm:^2.0.0" peerDependencies: @@ -13438,7 +13519,7 @@ __metadata: peerDependenciesMeta: "@types/react": optional: true - checksum: 6666cd62e13053d03e453b5199037cb8f6475a8f55afd664ff488bd8f2ee2ede4da3b220dd7e60f5ecd4926133364fbf4b1aed463eeb8203e7c5be3b1533b59b + checksum: 8a0867ffd441f358c66d79567970a745cc78ac2f98840a81c1fa749a525e8716116c645497d886a815e1dcf40ad81a107ebd6a7d15fd9ab5925c44a994a1d89a languageName: node linkType: hard @@ -13554,8 +13635,8 @@ __metadata: linkType: hard "viem@npm:^1.2.5": - version: 1.21.4 - resolution: "viem@npm:1.21.4" + version: 1.20.3 + resolution: "viem@npm:1.20.3" dependencies: "@adraffy/ens-normalize": "npm:1.10.0" "@noble/curves": "npm:1.2.0" @@ -13570,7 +13651,7 @@ __metadata: peerDependenciesMeta: typescript: optional: true - checksum: 8b29c790181e44c4c95b9ffed1a8c1b6c2396eb949b95697cc390ca8c49d88ef9e2cd56bd4800b90a9bbc93681ae8d63045fc6fa06e00d84f532bef77967e751 + checksum: 5bc3b85b18f19e5c366a9031512d3995e7d55394ed6e66c9c1db8a7d8d95cac5a95b76c5e2358e2808e383a730491775bf0457cae3c3c3d47d8490c9273e38de languageName: node linkType: hard @@ -14028,8 +14109,8 @@ __metadata: linkType: hard "ws@npm:^8.13.0": - version: 8.16.0 - resolution: "ws@npm:8.16.0" + version: 8.15.1 + resolution: "ws@npm:8.15.1" peerDependencies: bufferutil: ^4.0.1 utf-8-validate: ">=5.0.2" @@ -14038,7 +14119,7 @@ __metadata: optional: true utf-8-validate: optional: true - checksum: a7783bb421c648b1e622b423409cb2a58ac5839521d2f689e84bc9dc41d59379c692dd405b15a997ea1d4c0c2e5314ad707332d0c558f15232d2bc07c0b4618a + checksum: 9964360dd5ab35c7376bd7c4295a3c8bd44ea0838c9413742548a6fb3ec371fc6c18552d5b8e76bdc21536db1909765612815bae072674b5ec69971605395a96 languageName: node linkType: hard @@ -14238,13 +14319,13 @@ __metadata: linkType: hard "zustand@npm:^4.3.1": - version: 4.5.0 - resolution: "zustand@npm:4.5.0" + version: 4.4.7 + resolution: "zustand@npm:4.4.7" dependencies: use-sync-external-store: "npm:1.2.0" peerDependencies: "@types/react": ">=16.8" - immer: ">=9.0.6" + immer: ">=9.0" react: ">=16.8" peerDependenciesMeta: "@types/react": @@ -14253,6 +14334,6 @@ __metadata: optional: true react: optional: true - checksum: 4802c4e26f2c1ec0d8e0ce8e596413cafb8b3d383e664de4eb30af63cedd23686f17b96a8a4d4583fc4470b187de0bbeafcfb94894efc50f6e62813e02a70b64 + checksum: de507f09eb79039d74d282df6ffac6c7fb6b840ca3620b0392bcbe3f9049902802db5448b4002e6fcb32f903f7ec1aea14602049eb2a3a8410bfea7186d72fb7 languageName: node linkType: hard diff --git a/yarn-project/yarn.lock b/yarn-project/yarn.lock index e22e0a806fc..5e7bbb29474 100644 --- a/yarn-project/yarn.lock +++ b/yarn-project/yarn.lock @@ -13,37 +13,30 @@ __metadata: linkType: hard "@achingbrain/nat-port-mapper@npm:^1.0.9": - version: 1.0.13 - resolution: "@achingbrain/nat-port-mapper@npm:1.0.13" + version: 1.0.9 + resolution: "@achingbrain/nat-port-mapper@npm:1.0.9" dependencies: "@achingbrain/ssdp": ^4.0.1 - "@libp2p/logger": ^4.0.1 - default-gateway: ^7.2.2 + "@libp2p/logger": ^2.0.0 + default-gateway: ^6.0.2 err-code: ^3.0.1 it-first: ^3.0.1 p-defer: ^4.0.0 p-timeout: ^6.1.1 xml2js: ^0.6.0 - checksum: 0ece5a52be65fcb26e75918c39a5dad76a7752103bb32686e564e4c8f10e658e80e0e0d82acca6653cff4bac938faae06d71ceaef7a38916b9928b68a78c2c2b + checksum: 4d8b97ff6eceab530a51682f4f2d5b91101de029b429693bdcff73f19617770fe4e3271b353c3d932c419ed9fd1c0d6ba5bd98b716aab48c64c1d836b2d8020a languageName: node linkType: hard "@achingbrain/ssdp@npm:^4.0.1": - version: 4.0.6 - resolution: "@achingbrain/ssdp@npm:4.0.6" + version: 4.0.4 + resolution: "@achingbrain/ssdp@npm:4.0.4" dependencies: event-iterator: ^2.0.0 freeport-promise: ^2.0.0 merge-options: ^3.0.4 - xml2js: ^0.6.2 - checksum: 18af3ebf0ddd331531730b3c998729cb3db8f915b7f8296e503babf813a42af284e62b271148ac3ac85aa2420bb1c813c2d3b661c90e75f2e6d8700f9302f50b - languageName: node - linkType: hard - -"@adraffy/ens-normalize@npm:1.10.0": - version: 1.10.0 - resolution: "@adraffy/ens-normalize@npm:1.10.0" - checksum: af0540f963a2632da2bbc37e36ea6593dcfc607b937857133791781e246d47f870d5e3d21fa70d5cfe94e772c284588c81ea3f5b7f4ea8fbb824369444e4dbcb + xml2js: ^0.5.0 + checksum: ebd5b25f64a2cd8fd14e760f92824cc308784d833eba887bfabc68a678de692eab83ac6c6859e56a187b7ea78e4fbdc9403899ed2be9cb245e0591a925431565 languageName: node linkType: hard @@ -906,43 +899,42 @@ __metadata: languageName: unknown linkType: soft -"@babel/code-frame@npm:^7.0.0, @babel/code-frame@npm:^7.12.13, @babel/code-frame@npm:^7.22.13, @babel/code-frame@npm:^7.23.5": - version: 7.23.5 - resolution: "@babel/code-frame@npm:7.23.5" +"@babel/code-frame@npm:^7.0.0, @babel/code-frame@npm:^7.12.13, @babel/code-frame@npm:^7.16.7, @babel/code-frame@npm:^7.22.5": + version: 7.22.5 + resolution: "@babel/code-frame@npm:7.22.5" dependencies: - "@babel/highlight": ^7.23.4 - chalk: ^2.4.2 - checksum: d90981fdf56a2824a9b14d19a4c0e8db93633fd488c772624b4e83e0ceac6039a27cd298a247c3214faa952bf803ba23696172ae7e7235f3b97f43ba278c569a + "@babel/highlight": ^7.22.5 + checksum: cfe804f518f53faaf9a1d3e0f9f74127ab9a004912c3a16fda07fb6a633393ecb9918a053cb71804204c1b7ec3d49e1699604715e2cfb0c9f7bc4933d324ebb6 languageName: node linkType: hard -"@babel/compat-data@npm:^7.23.5": - version: 7.23.5 - resolution: "@babel/compat-data@npm:7.23.5" - checksum: 06ce244cda5763295a0ea924728c09bae57d35713b675175227278896946f922a63edf803c322f855a3878323d48d0255a2a3023409d2a123483c8a69ebb4744 +"@babel/compat-data@npm:^7.22.9": + version: 7.22.9 + resolution: "@babel/compat-data@npm:7.22.9" + checksum: bed77d9044ce948b4327b30dd0de0779fa9f3a7ed1f2d31638714ed00229fa71fc4d1617ae0eb1fad419338d3658d0e9a5a083297451e09e73e078d0347ff808 languageName: node linkType: hard "@babel/core@npm:^7.11.6, @babel/core@npm:^7.12.3": - version: 7.23.7 - resolution: "@babel/core@npm:7.23.7" + version: 7.22.9 + resolution: "@babel/core@npm:7.22.9" dependencies: "@ampproject/remapping": ^2.2.0 - "@babel/code-frame": ^7.23.5 - "@babel/generator": ^7.23.6 - "@babel/helper-compilation-targets": ^7.23.6 - "@babel/helper-module-transforms": ^7.23.3 - "@babel/helpers": ^7.23.7 - "@babel/parser": ^7.23.6 - "@babel/template": ^7.22.15 - "@babel/traverse": ^7.23.7 - "@babel/types": ^7.23.6 - convert-source-map: ^2.0.0 + "@babel/code-frame": ^7.22.5 + "@babel/generator": ^7.22.9 + "@babel/helper-compilation-targets": ^7.22.9 + "@babel/helper-module-transforms": ^7.22.9 + "@babel/helpers": ^7.22.6 + "@babel/parser": ^7.22.7 + "@babel/template": ^7.22.5 + "@babel/traverse": ^7.22.8 + "@babel/types": ^7.22.5 + convert-source-map: ^1.7.0 debug: ^4.1.0 gensync: ^1.0.0-beta.2 - json5: ^2.2.3 + json5: ^2.2.2 semver: ^6.3.1 - checksum: 32d5bf73372a47429afaae9adb0af39e47bcea6a831c4b5dcbb4791380cda6949cb8cb1a2fea8b60bb1ebe189209c80e333903df1fa8e9dcb04798c0ce5bf59e + checksum: 7bf069aeceb417902c4efdaefab1f7b94adb7dea694a9aed1bda2edf4135348a080820529b1a300c6f8605740a00ca00c19b2d5e74b5dd489d99d8c11d5e56d1 languageName: node linkType: hard @@ -957,49 +949,51 @@ __metadata: languageName: node linkType: hard -"@babel/generator@npm:^7.23.0, @babel/generator@npm:^7.23.6, @babel/generator@npm:^7.7.2": - version: 7.23.6 - resolution: "@babel/generator@npm:7.23.6" +"@babel/generator@npm:^7.17.3, @babel/generator@npm:^7.22.7, @babel/generator@npm:^7.22.9, @babel/generator@npm:^7.7.2": + version: 7.22.9 + resolution: "@babel/generator@npm:7.22.9" dependencies: - "@babel/types": ^7.23.6 + "@babel/types": ^7.22.5 "@jridgewell/gen-mapping": ^0.3.2 "@jridgewell/trace-mapping": ^0.3.17 jsesc: ^2.5.1 - checksum: 1a1a1c4eac210f174cd108d479464d053930a812798e09fee069377de39a893422df5b5b146199ead7239ae6d3a04697b45fc9ac6e38e0f6b76374390f91fc6c + checksum: 7c9d2c58b8d5ac5e047421a6ab03ec2ff5d9a5ff2c2212130a0055e063ac349e0b19d435537d6886c999771aef394832e4f54cd9fc810100a7f23d982f6af06b languageName: node linkType: hard -"@babel/helper-compilation-targets@npm:^7.23.6": - version: 7.23.6 - resolution: "@babel/helper-compilation-targets@npm:7.23.6" +"@babel/helper-compilation-targets@npm:^7.22.9": + version: 7.22.9 + resolution: "@babel/helper-compilation-targets@npm:7.22.9" dependencies: - "@babel/compat-data": ^7.23.5 - "@babel/helper-validator-option": ^7.23.5 - browserslist: ^4.22.2 + "@babel/compat-data": ^7.22.9 + "@babel/helper-validator-option": ^7.22.5 + browserslist: ^4.21.9 lru-cache: ^5.1.1 semver: ^6.3.1 - checksum: c630b98d4527ac8fe2c58d9a06e785dfb2b73ec71b7c4f2ddf90f814b5f75b547f3c015f110a010fd31f76e3864daaf09f3adcd2f6acdbfb18a8de3a48717590 + peerDependencies: + "@babel/core": ^7.0.0 + checksum: ea0006c6a93759025f4a35a25228ae260538c9f15023e8aac2a6d45ca68aef4cf86cfc429b19af9a402cbdd54d5de74ad3fbcf6baa7e48184dc079f1a791e178 languageName: node linkType: hard -"@babel/helper-environment-visitor@npm:^7.22.20": - version: 7.22.20 - resolution: "@babel/helper-environment-visitor@npm:7.22.20" - checksum: d80ee98ff66f41e233f36ca1921774c37e88a803b2f7dca3db7c057a5fea0473804db9fb6729e5dbfd07f4bed722d60f7852035c2c739382e84c335661590b69 +"@babel/helper-environment-visitor@npm:^7.16.7, @babel/helper-environment-visitor@npm:^7.22.5": + version: 7.22.5 + resolution: "@babel/helper-environment-visitor@npm:7.22.5" + checksum: 248532077d732a34cd0844eb7b078ff917c3a8ec81a7f133593f71a860a582f05b60f818dc5049c2212e5baa12289c27889a4b81d56ef409b4863db49646c4b1 languageName: node linkType: hard -"@babel/helper-function-name@npm:^7.23.0": - version: 7.23.0 - resolution: "@babel/helper-function-name@npm:7.23.0" +"@babel/helper-function-name@npm:^7.16.7, @babel/helper-function-name@npm:^7.22.5": + version: 7.22.5 + resolution: "@babel/helper-function-name@npm:7.22.5" dependencies: - "@babel/template": ^7.22.15 - "@babel/types": ^7.23.0 - checksum: e44542257b2d4634a1f979244eb2a4ad8e6d75eb6761b4cfceb56b562f7db150d134bc538c8e6adca3783e3bc31be949071527aa8e3aab7867d1ad2d84a26e10 + "@babel/template": ^7.22.5 + "@babel/types": ^7.22.5 + checksum: 6b1f6ce1b1f4e513bf2c8385a557ea0dd7fa37971b9002ad19268ca4384bbe90c09681fe4c076013f33deabc63a53b341ed91e792de741b4b35e01c00238177a languageName: node linkType: hard -"@babel/helper-hoist-variables@npm:^7.22.5": +"@babel/helper-hoist-variables@npm:^7.16.7, @babel/helper-hoist-variables@npm:^7.22.5": version: 7.22.5 resolution: "@babel/helper-hoist-variables@npm:7.22.5" dependencies: @@ -1008,27 +1002,27 @@ __metadata: languageName: node linkType: hard -"@babel/helper-module-imports@npm:^7.22.15": - version: 7.22.15 - resolution: "@babel/helper-module-imports@npm:7.22.15" +"@babel/helper-module-imports@npm:^7.22.5": + version: 7.22.5 + resolution: "@babel/helper-module-imports@npm:7.22.5" dependencies: - "@babel/types": ^7.22.15 - checksum: ecd7e457df0a46f889228f943ef9b4a47d485d82e030676767e6a2fdcbdaa63594d8124d4b55fd160b41c201025aec01fc27580352b1c87a37c9c6f33d116702 + "@babel/types": ^7.22.5 + checksum: 9ac2b0404fa38b80bdf2653fbeaf8e8a43ccb41bd505f9741d820ed95d3c4e037c62a1bcdcb6c9527d7798d2e595924c4d025daed73283badc180ada2c9c49ad languageName: node linkType: hard -"@babel/helper-module-transforms@npm:^7.23.3": - version: 7.23.3 - resolution: "@babel/helper-module-transforms@npm:7.23.3" +"@babel/helper-module-transforms@npm:^7.22.9": + version: 7.22.9 + resolution: "@babel/helper-module-transforms@npm:7.22.9" dependencies: - "@babel/helper-environment-visitor": ^7.22.20 - "@babel/helper-module-imports": ^7.22.15 + "@babel/helper-environment-visitor": ^7.22.5 + "@babel/helper-module-imports": ^7.22.5 "@babel/helper-simple-access": ^7.22.5 "@babel/helper-split-export-declaration": ^7.22.6 - "@babel/helper-validator-identifier": ^7.22.20 + "@babel/helper-validator-identifier": ^7.22.5 peerDependencies: "@babel/core": ^7.0.0 - checksum: 5d0895cfba0e16ae16f3aa92fee108517023ad89a855289c4eb1d46f7aef4519adf8e6f971e1d55ac20c5461610e17213f1144097a8f932e768a9132e2278d71 + checksum: 2751f77660518cf4ff027514d6f4794f04598c6393be7b04b8e46c6e21606e11c19f3f57ab6129a9c21bacdf8b3ffe3af87bb401d972f34af2d0ffde02ac3001 languageName: node linkType: hard @@ -1048,7 +1042,7 @@ __metadata: languageName: node linkType: hard -"@babel/helper-split-export-declaration@npm:^7.22.6": +"@babel/helper-split-export-declaration@npm:^7.16.7, @babel/helper-split-export-declaration@npm:^7.22.6": version: 7.22.6 resolution: "@babel/helper-split-export-declaration@npm:7.22.6" dependencies: @@ -1057,55 +1051,64 @@ __metadata: languageName: node linkType: hard -"@babel/helper-string-parser@npm:^7.23.4": - version: 7.23.4 - resolution: "@babel/helper-string-parser@npm:7.23.4" - checksum: c0641144cf1a7e7dc93f3d5f16d5327465b6cf5d036b48be61ecba41e1eece161b48f46b7f960951b67f8c3533ce506b16dece576baef4d8b3b49f8c65410f90 +"@babel/helper-string-parser@npm:^7.22.5": + version: 7.22.5 + resolution: "@babel/helper-string-parser@npm:7.22.5" + checksum: 836851ca5ec813077bbb303acc992d75a360267aa3b5de7134d220411c852a6f17de7c0d0b8c8dcc0f567f67874c00f4528672b2a4f1bc978a3ada64c8c78467 languageName: node linkType: hard -"@babel/helper-validator-identifier@npm:^7.16.7, @babel/helper-validator-identifier@npm:^7.22.20": - version: 7.22.20 - resolution: "@babel/helper-validator-identifier@npm:7.22.20" - checksum: 136412784d9428266bcdd4d91c32bcf9ff0e8d25534a9d94b044f77fe76bc50f941a90319b05aafd1ec04f7d127cd57a179a3716009ff7f3412ef835ada95bdc +"@babel/helper-validator-identifier@npm:^7.16.7, @babel/helper-validator-identifier@npm:^7.22.5": + version: 7.22.5 + resolution: "@babel/helper-validator-identifier@npm:7.22.5" + checksum: 7f0f30113474a28298c12161763b49de5018732290ca4de13cdaefd4fd0d635a6fe3f6686c37a02905fb1e64f21a5ee2b55140cf7b070e729f1bd66866506aea languageName: node linkType: hard -"@babel/helper-validator-option@npm:^7.23.5": - version: 7.23.5 - resolution: "@babel/helper-validator-option@npm:7.23.5" - checksum: 537cde2330a8aede223552510e8a13e9c1c8798afee3757995a7d4acae564124fe2bf7e7c3d90d62d3657434a74340a274b3b3b1c6f17e9a2be1f48af29cb09e +"@babel/helper-validator-option@npm:^7.22.5": + version: 7.22.5 + resolution: "@babel/helper-validator-option@npm:7.22.5" + checksum: bbeca8a85ee86990215c0424997438b388b8d642d69b9f86c375a174d3cdeb270efafd1ff128bc7a1d370923d13b6e45829ba8581c027620e83e3a80c5c414b3 languageName: node linkType: hard -"@babel/helpers@npm:^7.23.7": - version: 7.23.8 - resolution: "@babel/helpers@npm:7.23.8" +"@babel/helpers@npm:^7.22.6": + version: 7.22.6 + resolution: "@babel/helpers@npm:7.22.6" dependencies: - "@babel/template": ^7.22.15 - "@babel/traverse": ^7.23.7 - "@babel/types": ^7.23.6 - checksum: 8b522d527921f8df45a983dc7b8e790c021250addf81ba7900ba016e165442a527348f6f877aa55e1debb3eef9e860a334b4e8d834e6c9b438ed61a63d9a7ad4 + "@babel/template": ^7.22.5 + "@babel/traverse": ^7.22.6 + "@babel/types": ^7.22.5 + checksum: 5c1f33241fe7bf7709868c2105134a0a86dca26a0fbd508af10a89312b1f77ca38ebae43e50be3b208613c5eacca1559618af4ca236f0abc55d294800faeff30 languageName: node linkType: hard -"@babel/highlight@npm:^7.23.4": - version: 7.23.4 - resolution: "@babel/highlight@npm:7.23.4" +"@babel/highlight@npm:^7.22.5": + version: 7.22.5 + resolution: "@babel/highlight@npm:7.22.5" dependencies: - "@babel/helper-validator-identifier": ^7.22.20 - chalk: ^2.4.2 + "@babel/helper-validator-identifier": ^7.22.5 + chalk: ^2.0.0 js-tokens: ^4.0.0 - checksum: 643acecdc235f87d925979a979b539a5d7d1f31ae7db8d89047269082694122d11aa85351304c9c978ceeb6d250591ccadb06c366f358ccee08bb9c122476b89 + checksum: f61ae6de6ee0ea8d9b5bcf2a532faec5ab0a1dc0f7c640e5047fc61630a0edb88b18d8c92eb06566d30da7a27db841aca11820ecd3ebe9ce514c9350fbed39c4 + languageName: node + linkType: hard + +"@babel/parser@npm:^7.0.0, @babel/parser@npm:^7.21.4": + version: 7.23.0 + resolution: "@babel/parser@npm:7.23.0" + bin: + parser: ./bin/babel-parser.js + checksum: 453fdf8b9e2c2b7d7b02139e0ce003d1af21947bbc03eb350fb248ee335c9b85e4ab41697ddbdd97079698de825a265e45a0846bb2ed47a2c7c1df833f42a354 languageName: node linkType: hard -"@babel/parser@npm:^7.0.0, @babel/parser@npm:^7.1.0, @babel/parser@npm:^7.14.7, @babel/parser@npm:^7.20.5, @babel/parser@npm:^7.20.7, @babel/parser@npm:^7.21.4, @babel/parser@npm:^7.22.15, @babel/parser@npm:^7.23.0, @babel/parser@npm:^7.23.6": - version: 7.23.6 - resolution: "@babel/parser@npm:7.23.6" +"@babel/parser@npm:^7.1.0, @babel/parser@npm:^7.14.7, @babel/parser@npm:^7.17.3, @babel/parser@npm:^7.20.5, @babel/parser@npm:^7.20.7, @babel/parser@npm:^7.22.5, @babel/parser@npm:^7.22.7": + version: 7.22.7 + resolution: "@babel/parser@npm:7.22.7" bin: parser: ./bin/babel-parser.js - checksum: 140801c43731a6c41fd193f5c02bc71fd647a0360ca616b23d2db8be4b9739b9f951a03fc7c2db4f9b9214f4b27c1074db0f18bc3fa653783082d5af7c8860d5 + checksum: 02209ddbd445831ee8bf966fdf7c29d189ed4b14343a68eb2479d940e7e3846340d7cc6bd654a5f3d87d19dc84f49f50a58cf9363bee249dc5409ff3ba3dab54 languageName: node linkType: hard @@ -1165,13 +1168,13 @@ __metadata: linkType: hard "@babel/plugin-syntax-jsx@npm:^7.7.2": - version: 7.23.3 - resolution: "@babel/plugin-syntax-jsx@npm:7.23.3" + version: 7.22.5 + resolution: "@babel/plugin-syntax-jsx@npm:7.22.5" dependencies: "@babel/helper-plugin-utils": ^7.22.5 peerDependencies: "@babel/core": ^7.0.0-0 - checksum: 89037694314a74e7f0e7a9c8d3793af5bf6b23d80950c29b360db1c66859d67f60711ea437e70ad6b5b4b29affe17eababda841b6c01107c2b638e0493bafb4e + checksum: 8829d30c2617ab31393d99cec2978e41f014f4ac6f01a1cecf4c4dd8320c3ec12fdc3ce121126b2d8d32f6887e99ca1a0bad53dedb1e6ad165640b92b24980ce languageName: node linkType: hard @@ -1253,69 +1256,69 @@ __metadata: linkType: hard "@babel/plugin-syntax-typescript@npm:^7.7.2": - version: 7.23.3 - resolution: "@babel/plugin-syntax-typescript@npm:7.23.3" + version: 7.22.5 + resolution: "@babel/plugin-syntax-typescript@npm:7.22.5" dependencies: "@babel/helper-plugin-utils": ^7.22.5 peerDependencies: "@babel/core": ^7.0.0-0 - checksum: abfad3a19290d258b028e285a1f34c9b8a0cbe46ef79eafed4ed7ffce11b5d0720b5e536c82f91cbd8442cde35a3dd8e861fa70366d87ff06fdc0d4756e30876 + checksum: 8ab7718fbb026d64da93681a57797d60326097fd7cb930380c8bffd9eb101689e90142c760a14b51e8e69c88a73ba3da956cb4520a3b0c65743aee5c71ef360a languageName: node linkType: hard "@babel/runtime@npm:^7.21.0": - version: 7.23.8 - resolution: "@babel/runtime@npm:7.23.8" + version: 7.22.6 + resolution: "@babel/runtime@npm:7.22.6" dependencies: - regenerator-runtime: ^0.14.0 - checksum: 0bd5543c26811153822a9f382fd39886f66825ff2a397a19008011376533747cd05c33a91f6248c0b8b0edf0448d7c167ebfba34786088f1b7eb11c65be7dfc3 + regenerator-runtime: ^0.13.11 + checksum: e585338287c4514a713babf4fdb8fc2a67adcebab3e7723a739fc62c79cfda875b314c90fd25f827afb150d781af97bc16c85bfdbfa2889f06053879a1ddb597 languageName: node linkType: hard -"@babel/template@npm:^7.22.15, @babel/template@npm:^7.3.3": - version: 7.22.15 - resolution: "@babel/template@npm:7.22.15" +"@babel/template@npm:^7.22.5, @babel/template@npm:^7.3.3": + version: 7.22.5 + resolution: "@babel/template@npm:7.22.5" dependencies: - "@babel/code-frame": ^7.22.13 - "@babel/parser": ^7.22.15 - "@babel/types": ^7.22.15 - checksum: 1f3e7dcd6c44f5904c184b3f7fe280394b191f2fed819919ffa1e529c259d5b197da8981b6ca491c235aee8dbad4a50b7e31304aa531271cb823a4a24a0dd8fd + "@babel/code-frame": ^7.22.5 + "@babel/parser": ^7.22.5 + "@babel/types": ^7.22.5 + checksum: c5746410164039aca61829cdb42e9a55410f43cace6f51ca443313f3d0bdfa9a5a330d0b0df73dc17ef885c72104234ae05efede37c1cc8a72dc9f93425977a3 languageName: node linkType: hard -"@babel/traverse@npm:7.23.2": - version: 7.23.2 - resolution: "@babel/traverse@npm:7.23.2" +"@babel/traverse@npm:7.17.3": + version: 7.17.3 + resolution: "@babel/traverse@npm:7.17.3" dependencies: - "@babel/code-frame": ^7.22.13 - "@babel/generator": ^7.23.0 - "@babel/helper-environment-visitor": ^7.22.20 - "@babel/helper-function-name": ^7.23.0 - "@babel/helper-hoist-variables": ^7.22.5 - "@babel/helper-split-export-declaration": ^7.22.6 - "@babel/parser": ^7.23.0 - "@babel/types": ^7.23.0 + "@babel/code-frame": ^7.16.7 + "@babel/generator": ^7.17.3 + "@babel/helper-environment-visitor": ^7.16.7 + "@babel/helper-function-name": ^7.16.7 + "@babel/helper-hoist-variables": ^7.16.7 + "@babel/helper-split-export-declaration": ^7.16.7 + "@babel/parser": ^7.17.3 + "@babel/types": ^7.17.0 debug: ^4.1.0 globals: ^11.1.0 - checksum: 26a1eea0dde41ab99dde8b9773a013a0dc50324e5110a049f5d634e721ff08afffd54940b3974a20308d7952085ac769689369e9127dea655f868c0f6e1ab35d + checksum: 780d7ecf711758174989794891af08d378f81febdb8932056c0d9979524bf0298e28f8e7708a872d7781151506c28f56c85c63ea3f1f654662c2fcb8a3eb9fdc languageName: node linkType: hard -"@babel/traverse@npm:^7.23.7": - version: 7.23.7 - resolution: "@babel/traverse@npm:7.23.7" +"@babel/traverse@npm:^7.22.6, @babel/traverse@npm:^7.22.8": + version: 7.22.8 + resolution: "@babel/traverse@npm:7.22.8" dependencies: - "@babel/code-frame": ^7.23.5 - "@babel/generator": ^7.23.6 - "@babel/helper-environment-visitor": ^7.22.20 - "@babel/helper-function-name": ^7.23.0 + "@babel/code-frame": ^7.22.5 + "@babel/generator": ^7.22.7 + "@babel/helper-environment-visitor": ^7.22.5 + "@babel/helper-function-name": ^7.22.5 "@babel/helper-hoist-variables": ^7.22.5 "@babel/helper-split-export-declaration": ^7.22.6 - "@babel/parser": ^7.23.6 - "@babel/types": ^7.23.6 - debug: ^4.3.1 + "@babel/parser": ^7.22.7 + "@babel/types": ^7.22.5 + debug: ^4.1.0 globals: ^11.1.0 - checksum: d4a7afb922361f710efc97b1e25ec343fab8b2a4ddc81ca84f9a153f22d4482112cba8f263774be8d297918b6c4767c7a98988ab4e53ac73686c986711dd002e + checksum: a381369bc3eedfd13ed5fef7b884657f1c29024ea7388198149f0edc34bd69ce3966e9f40188d15f56490a5e12ba250ccc485f2882b53d41b054fccefb233e33 languageName: node linkType: hard @@ -1329,14 +1332,14 @@ __metadata: languageName: node linkType: hard -"@babel/types@npm:^7.0.0, @babel/types@npm:^7.17.0, @babel/types@npm:^7.20.7, @babel/types@npm:^7.22.15, @babel/types@npm:^7.22.5, @babel/types@npm:^7.23.0, @babel/types@npm:^7.23.6, @babel/types@npm:^7.3.3, @babel/types@npm:^7.8.3": - version: 7.23.6 - resolution: "@babel/types@npm:7.23.6" +"@babel/types@npm:^7.0.0, @babel/types@npm:^7.17.0, @babel/types@npm:^7.20.7, @babel/types@npm:^7.22.5, @babel/types@npm:^7.3.3, @babel/types@npm:^7.8.3": + version: 7.22.5 + resolution: "@babel/types@npm:7.22.5" dependencies: - "@babel/helper-string-parser": ^7.23.4 - "@babel/helper-validator-identifier": ^7.22.20 + "@babel/helper-string-parser": ^7.22.5 + "@babel/helper-validator-identifier": ^7.22.5 to-fast-properties: ^2.0.0 - checksum: 68187dbec0d637f79bc96263ac95ec8b06d424396678e7e225492be866414ce28ebc918a75354d4c28659be6efe30020b4f0f6df81cc418a2d30645b690a8de0 + checksum: c13a9c1dc7d2d1a241a2f8363540cb9af1d66e978e8984b400a20c4f38ba38ca29f06e26a0f2d49a70bad9e57615dac09c35accfddf1bb90d23cd3e0a0bab892 languageName: node linkType: hard @@ -1347,21 +1350,14 @@ __metadata: languageName: node linkType: hard -"@chainsafe/as-chacha20poly1305@npm:^0.1.0": - version: 0.1.0 - resolution: "@chainsafe/as-chacha20poly1305@npm:0.1.0" - checksum: 2bf38f0595bb379489388174e2049e57ae1512815a80fddfe5b9055865739a970651847892233e53cde6a3e34554ede97d1abd8a0f02b4a0005a1a6cf0146a6c - languageName: node - linkType: hard - -"@chainsafe/as-sha256@npm:^0.4.1": - version: 0.4.1 - resolution: "@chainsafe/as-sha256@npm:0.4.1" - checksum: 6d86975e648ecdafd366802278ac15b392b252e967f3681412ec48b5a3518b936cc5e977517499882b084991446d25787d98f8f585891943688cc81549a44e9a +"@chainsafe/is-ip@npm:^2.0.1": + version: 2.0.1 + resolution: "@chainsafe/is-ip@npm:2.0.1" + checksum: c5bbebe58eadc700d12112713fd75e41ae26020eedcac0e8b593fdda16a180239ede1d68d55b500fd8b30189c9a82b5e10769ed86a816e879c83248069152b79 languageName: node linkType: hard -"@chainsafe/is-ip@npm:^2.0.1, @chainsafe/is-ip@npm:^2.0.2": +"@chainsafe/is-ip@npm:^2.0.2": version: 2.0.2 resolution: "@chainsafe/is-ip@npm:2.0.2" checksum: 2600350ba1c8fbad5d1ebee71317beeb29fbaebf43780d89e30f8c6c2d27b95ebdab0284dfbab7336b5eb6d8ffcc7081e3e4c5b221889dc366463f83bbe38adb @@ -1369,16 +1365,14 @@ __metadata: linkType: hard "@chainsafe/libp2p-noise@npm:^13.0.0": - version: 13.0.5 - resolution: "@chainsafe/libp2p-noise@npm:13.0.5" + version: 13.0.0 + resolution: "@chainsafe/libp2p-noise@npm:13.0.0" dependencies: - "@chainsafe/as-chacha20poly1305": ^0.1.0 - "@chainsafe/as-sha256": ^0.4.1 "@libp2p/crypto": ^2.0.0 "@libp2p/interface": ^0.1.0 "@libp2p/logger": ^3.0.0 "@libp2p/peer-id": ^3.0.0 - "@noble/ciphers": ^0.4.0 + "@noble/ciphers": ^0.1.4 "@noble/curves": ^1.1.0 "@noble/hashes": ^1.3.1 it-byte-stream: ^1.0.0 @@ -1390,23 +1384,22 @@ __metadata: protons-runtime: ^5.0.0 uint8arraylist: ^2.4.3 uint8arrays: ^4.0.4 - wherearewe: ^2.0.1 - checksum: 98dd78bfd547501280c7a318acfa81624016351f18e0a28debe451340418fd263f90ce8d7358d5b83f61429522de6631321606b98eda35609114041853057af1 + checksum: 177c44003cb550b3ce1f867db2f5a3bb8f97bf368095a1a5bd4ccddd64bc2fc4167119a61fa6ed045abe8ef2dbbea9ddac9b8e1d6381d9056749b900029b8de5 languageName: node linkType: hard "@chainsafe/libp2p-yamux@npm:^5.0.0": - version: 5.0.4 - resolution: "@chainsafe/libp2p-yamux@npm:5.0.4" + version: 5.0.0 + resolution: "@chainsafe/libp2p-yamux@npm:5.0.0" dependencies: "@libp2p/interface": ^0.1.0 "@libp2p/logger": ^3.0.0 - get-iterator: ^2.0.1 + abortable-iterator: ^5.0.1 it-foreach: ^2.0.3 it-pipe: ^3.0.1 it-pushable: ^3.2.0 uint8arraylist: ^2.4.3 - checksum: 58c33b28d8da2b8c6813127de2cc4005f2f09d845cf535c56a3db0495774a2feb935d2813d2141bb6a747f3c1181dfde415945821d489ac7f987e36df744721f + checksum: 8de6f9e16097ed983ab3d0da2dffe752f09c2c09473783d39dcb2d54b788fe4353bbed417a3870fb4de151de906a734a73cd53e2b2b4ccec4392d2195828137c languageName: node linkType: hard @@ -1419,10 +1412,10 @@ __metadata: languageName: node linkType: hard -"@colors/colors@npm:1.6.0, @colors/colors@npm:^1.6.0": - version: 1.6.0 - resolution: "@colors/colors@npm:1.6.0" - checksum: aa209963e0c3218e80a4a20553ba8c0fbb6fa13140540b4e5f97923790be06801fc90172c1114fc8b7e888b3d012b67298cde6b9e81521361becfaee400c662f +"@colors/colors@npm:1.5.0": + version: 1.5.0 + resolution: "@colors/colors@npm:1.5.0" + checksum: d64d5260bed1d5012ae3fc617d38d1afc0329fec05342f4e6b838f46998855ba56e0a73833f4a80fa8378c84810da254f76a8a19c39d038260dc06dc4e007425 languageName: node linkType: hard @@ -1474,156 +1467,156 @@ __metadata: languageName: node linkType: hard -"@esbuild/android-arm64@npm:0.18.20": - version: 0.18.20 - resolution: "@esbuild/android-arm64@npm:0.18.20" +"@esbuild/android-arm64@npm:0.18.17": + version: 0.18.17 + resolution: "@esbuild/android-arm64@npm:0.18.17" conditions: os=android & cpu=arm64 languageName: node linkType: hard -"@esbuild/android-arm@npm:0.18.20": - version: 0.18.20 - resolution: "@esbuild/android-arm@npm:0.18.20" +"@esbuild/android-arm@npm:0.18.17": + version: 0.18.17 + resolution: "@esbuild/android-arm@npm:0.18.17" conditions: os=android & cpu=arm languageName: node linkType: hard -"@esbuild/android-x64@npm:0.18.20": - version: 0.18.20 - resolution: "@esbuild/android-x64@npm:0.18.20" +"@esbuild/android-x64@npm:0.18.17": + version: 0.18.17 + resolution: "@esbuild/android-x64@npm:0.18.17" conditions: os=android & cpu=x64 languageName: node linkType: hard -"@esbuild/darwin-arm64@npm:0.18.20": - version: 0.18.20 - resolution: "@esbuild/darwin-arm64@npm:0.18.20" +"@esbuild/darwin-arm64@npm:0.18.17": + version: 0.18.17 + resolution: "@esbuild/darwin-arm64@npm:0.18.17" conditions: os=darwin & cpu=arm64 languageName: node linkType: hard -"@esbuild/darwin-x64@npm:0.18.20": - version: 0.18.20 - resolution: "@esbuild/darwin-x64@npm:0.18.20" +"@esbuild/darwin-x64@npm:0.18.17": + version: 0.18.17 + resolution: "@esbuild/darwin-x64@npm:0.18.17" conditions: os=darwin & cpu=x64 languageName: node linkType: hard -"@esbuild/freebsd-arm64@npm:0.18.20": - version: 0.18.20 - resolution: "@esbuild/freebsd-arm64@npm:0.18.20" +"@esbuild/freebsd-arm64@npm:0.18.17": + version: 0.18.17 + resolution: "@esbuild/freebsd-arm64@npm:0.18.17" conditions: os=freebsd & cpu=arm64 languageName: node linkType: hard -"@esbuild/freebsd-x64@npm:0.18.20": - version: 0.18.20 - resolution: "@esbuild/freebsd-x64@npm:0.18.20" +"@esbuild/freebsd-x64@npm:0.18.17": + version: 0.18.17 + resolution: "@esbuild/freebsd-x64@npm:0.18.17" conditions: os=freebsd & cpu=x64 languageName: node linkType: hard -"@esbuild/linux-arm64@npm:0.18.20": - version: 0.18.20 - resolution: "@esbuild/linux-arm64@npm:0.18.20" +"@esbuild/linux-arm64@npm:0.18.17": + version: 0.18.17 + resolution: "@esbuild/linux-arm64@npm:0.18.17" conditions: os=linux & cpu=arm64 languageName: node linkType: hard -"@esbuild/linux-arm@npm:0.18.20": - version: 0.18.20 - resolution: "@esbuild/linux-arm@npm:0.18.20" +"@esbuild/linux-arm@npm:0.18.17": + version: 0.18.17 + resolution: "@esbuild/linux-arm@npm:0.18.17" conditions: os=linux & cpu=arm languageName: node linkType: hard -"@esbuild/linux-ia32@npm:0.18.20": - version: 0.18.20 - resolution: "@esbuild/linux-ia32@npm:0.18.20" +"@esbuild/linux-ia32@npm:0.18.17": + version: 0.18.17 + resolution: "@esbuild/linux-ia32@npm:0.18.17" conditions: os=linux & cpu=ia32 languageName: node linkType: hard -"@esbuild/linux-loong64@npm:0.18.20": - version: 0.18.20 - resolution: "@esbuild/linux-loong64@npm:0.18.20" +"@esbuild/linux-loong64@npm:0.18.17": + version: 0.18.17 + resolution: "@esbuild/linux-loong64@npm:0.18.17" conditions: os=linux & cpu=loong64 languageName: node linkType: hard -"@esbuild/linux-mips64el@npm:0.18.20": - version: 0.18.20 - resolution: "@esbuild/linux-mips64el@npm:0.18.20" +"@esbuild/linux-mips64el@npm:0.18.17": + version: 0.18.17 + resolution: "@esbuild/linux-mips64el@npm:0.18.17" conditions: os=linux & cpu=mips64el languageName: node linkType: hard -"@esbuild/linux-ppc64@npm:0.18.20": - version: 0.18.20 - resolution: "@esbuild/linux-ppc64@npm:0.18.20" +"@esbuild/linux-ppc64@npm:0.18.17": + version: 0.18.17 + resolution: "@esbuild/linux-ppc64@npm:0.18.17" conditions: os=linux & cpu=ppc64 languageName: node linkType: hard -"@esbuild/linux-riscv64@npm:0.18.20": - version: 0.18.20 - resolution: "@esbuild/linux-riscv64@npm:0.18.20" +"@esbuild/linux-riscv64@npm:0.18.17": + version: 0.18.17 + resolution: "@esbuild/linux-riscv64@npm:0.18.17" conditions: os=linux & cpu=riscv64 languageName: node linkType: hard -"@esbuild/linux-s390x@npm:0.18.20": - version: 0.18.20 - resolution: "@esbuild/linux-s390x@npm:0.18.20" +"@esbuild/linux-s390x@npm:0.18.17": + version: 0.18.17 + resolution: "@esbuild/linux-s390x@npm:0.18.17" conditions: os=linux & cpu=s390x languageName: node linkType: hard -"@esbuild/linux-x64@npm:0.18.20": - version: 0.18.20 - resolution: "@esbuild/linux-x64@npm:0.18.20" +"@esbuild/linux-x64@npm:0.18.17": + version: 0.18.17 + resolution: "@esbuild/linux-x64@npm:0.18.17" conditions: os=linux & cpu=x64 languageName: node linkType: hard -"@esbuild/netbsd-x64@npm:0.18.20": - version: 0.18.20 - resolution: "@esbuild/netbsd-x64@npm:0.18.20" +"@esbuild/netbsd-x64@npm:0.18.17": + version: 0.18.17 + resolution: "@esbuild/netbsd-x64@npm:0.18.17" conditions: os=netbsd & cpu=x64 languageName: node linkType: hard -"@esbuild/openbsd-x64@npm:0.18.20": - version: 0.18.20 - resolution: "@esbuild/openbsd-x64@npm:0.18.20" +"@esbuild/openbsd-x64@npm:0.18.17": + version: 0.18.17 + resolution: "@esbuild/openbsd-x64@npm:0.18.17" conditions: os=openbsd & cpu=x64 languageName: node linkType: hard -"@esbuild/sunos-x64@npm:0.18.20": - version: 0.18.20 - resolution: "@esbuild/sunos-x64@npm:0.18.20" +"@esbuild/sunos-x64@npm:0.18.17": + version: 0.18.17 + resolution: "@esbuild/sunos-x64@npm:0.18.17" conditions: os=sunos & cpu=x64 languageName: node linkType: hard -"@esbuild/win32-arm64@npm:0.18.20": - version: 0.18.20 - resolution: "@esbuild/win32-arm64@npm:0.18.20" +"@esbuild/win32-arm64@npm:0.18.17": + version: 0.18.17 + resolution: "@esbuild/win32-arm64@npm:0.18.17" conditions: os=win32 & cpu=arm64 languageName: node linkType: hard -"@esbuild/win32-ia32@npm:0.18.20": - version: 0.18.20 - resolution: "@esbuild/win32-ia32@npm:0.18.20" +"@esbuild/win32-ia32@npm:0.18.17": + version: 0.18.17 + resolution: "@esbuild/win32-ia32@npm:0.18.17" conditions: os=win32 & cpu=ia32 languageName: node linkType: hard -"@esbuild/win32-x64@npm:0.18.20": - version: 0.18.20 - resolution: "@esbuild/win32-x64@npm:0.18.20" +"@esbuild/win32-x64@npm:0.18.17": + version: 0.18.17 + resolution: "@esbuild/win32-x64@npm:0.18.17" conditions: os=win32 & cpu=x64 languageName: node linkType: hard @@ -1640,15 +1633,15 @@ __metadata: linkType: hard "@eslint-community/regexpp@npm:^4.5.1, @eslint-community/regexpp@npm:^4.6.1": - version: 4.10.0 - resolution: "@eslint-community/regexpp@npm:4.10.0" - checksum: 2a6e345429ea8382aaaf3a61f865cae16ed44d31ca917910033c02dc00d505d939f10b81e079fa14d43b51499c640138e153b7e40743c4c094d9df97d4e56f7b + version: 4.6.2 + resolution: "@eslint-community/regexpp@npm:4.6.2" + checksum: a3c341377b46b54fa228f455771b901d1a2717f95d47dcdf40199df30abc000ba020f747f114f08560d119e979d882a94cf46cfc51744544d54b00319c0f2724 languageName: node linkType: hard -"@eslint/eslintrc@npm:^2.1.4": - version: 2.1.4 - resolution: "@eslint/eslintrc@npm:2.1.4" +"@eslint/eslintrc@npm:^2.1.1": + version: 2.1.1 + resolution: "@eslint/eslintrc@npm:2.1.1" dependencies: ajv: ^6.12.4 debug: ^4.3.2 @@ -1659,25 +1652,25 @@ __metadata: js-yaml: ^4.1.0 minimatch: ^3.1.2 strip-json-comments: ^3.1.1 - checksum: 10957c7592b20ca0089262d8c2a8accbad14b4f6507e35416c32ee6b4dbf9cad67dfb77096bbd405405e9ada2b107f3797fe94362e1c55e0b09d6e90dd149127 + checksum: bf909ea183d27238c257a82d4ffdec38ca94b906b4b8dfae02ecbe7ecc9e5a8182ef5e469c808bb8cb4fea4750f43ac4ca7c4b4a167b6cd7e3aaacd386b2bd25 languageName: node linkType: hard -"@eslint/js@npm:8.56.0": - version: 8.56.0 - resolution: "@eslint/js@npm:8.56.0" - checksum: 5804130574ef810207bdf321c265437814e7a26f4e6fac9b496de3206afd52f533e09ec002a3be06cd9adcc9da63e727f1883938e663c4e4751c007d5b58e539 +"@eslint/js@npm:^8.46.0": + version: 8.46.0 + resolution: "@eslint/js@npm:8.46.0" + checksum: 7aed479832302882faf5bec37e9d068f270f84c19b3fb529646a7c1b031e73a312f730569c78806492bc09cfce3d7651dfab4ce09a56cbb06bc6469449e56377 languageName: node linkType: hard -"@humanwhocodes/config-array@npm:^0.11.13": - version: 0.11.14 - resolution: "@humanwhocodes/config-array@npm:0.11.14" +"@humanwhocodes/config-array@npm:^0.11.10": + version: 0.11.10 + resolution: "@humanwhocodes/config-array@npm:0.11.10" dependencies: - "@humanwhocodes/object-schema": ^2.0.2 - debug: ^4.3.1 + "@humanwhocodes/object-schema": ^1.2.1 + debug: ^4.1.1 minimatch: ^3.0.5 - checksum: 861ccce9eaea5de19546653bccf75bf09fe878bc39c3aab00aeee2d2a0e654516adad38dd1098aab5e3af0145bbcbf3f309bdf4d964f8dab9dcd5834ae4c02f2 + checksum: 1b1302e2403d0e35bc43e66d67a2b36b0ad1119efc704b5faff68c41f791a052355b010fb2d27ef022670f550de24cd6d08d5ecf0821c16326b7dcd0ee5d5d8a languageName: node linkType: hard @@ -1688,10 +1681,10 @@ __metadata: languageName: node linkType: hard -"@humanwhocodes/object-schema@npm:^2.0.2": - version: 2.0.2 - resolution: "@humanwhocodes/object-schema@npm:2.0.2" - checksum: 2fc11503361b5fb4f14714c700c02a3f4c7c93e9acd6b87a29f62c522d90470f364d6161b03d1cc618b979f2ae02aed1106fd29d302695d8927e2fc8165ba8ee +"@humanwhocodes/object-schema@npm:^1.2.1": + version: 1.2.1 + resolution: "@humanwhocodes/object-schema@npm:1.2.1" + checksum: a824a1ec31591231e4bad5787641f59e9633827d0a2eaae131a288d33c9ef0290bd16fda8da6f7c0fcb014147865d12118df10db57f27f41e20da92369fcb3f1 languageName: node linkType: hard @@ -1736,50 +1729,50 @@ __metadata: languageName: node linkType: hard -"@jest/console@npm:^29.7.0": - version: 29.7.0 - resolution: "@jest/console@npm:29.7.0" +"@jest/console@npm:^29.6.2": + version: 29.6.2 + resolution: "@jest/console@npm:29.6.2" dependencies: - "@jest/types": ^29.6.3 + "@jest/types": ^29.6.1 "@types/node": "*" chalk: ^4.0.0 - jest-message-util: ^29.7.0 - jest-util: ^29.7.0 + jest-message-util: ^29.6.2 + jest-util: ^29.6.2 slash: ^3.0.0 - checksum: 0e3624e32c5a8e7361e889db70b170876401b7d70f509a2538c31d5cd50deb0c1ae4b92dc63fe18a0902e0a48c590c21d53787a0df41a52b34fa7cab96c384d6 + checksum: 1198667bda0430770c3e9b92681c0ee9f8346394574071c633f306192ac5f08e12972d6a5fdf03eb0d441051c8439bce0f6f9f355dc60d98777a35328331ba2e languageName: node linkType: hard -"@jest/core@npm:^29.7.0": - version: 29.7.0 - resolution: "@jest/core@npm:29.7.0" +"@jest/core@npm:^29.6.2": + version: 29.6.2 + resolution: "@jest/core@npm:29.6.2" dependencies: - "@jest/console": ^29.7.0 - "@jest/reporters": ^29.7.0 - "@jest/test-result": ^29.7.0 - "@jest/transform": ^29.7.0 - "@jest/types": ^29.6.3 + "@jest/console": ^29.6.2 + "@jest/reporters": ^29.6.2 + "@jest/test-result": ^29.6.2 + "@jest/transform": ^29.6.2 + "@jest/types": ^29.6.1 "@types/node": "*" ansi-escapes: ^4.2.1 chalk: ^4.0.0 ci-info: ^3.2.0 exit: ^0.1.2 graceful-fs: ^4.2.9 - jest-changed-files: ^29.7.0 - jest-config: ^29.7.0 - jest-haste-map: ^29.7.0 - jest-message-util: ^29.7.0 - jest-regex-util: ^29.6.3 - jest-resolve: ^29.7.0 - jest-resolve-dependencies: ^29.7.0 - jest-runner: ^29.7.0 - jest-runtime: ^29.7.0 - jest-snapshot: ^29.7.0 - jest-util: ^29.7.0 - jest-validate: ^29.7.0 - jest-watcher: ^29.7.0 + jest-changed-files: ^29.5.0 + jest-config: ^29.6.2 + jest-haste-map: ^29.6.2 + jest-message-util: ^29.6.2 + jest-regex-util: ^29.4.3 + jest-resolve: ^29.6.2 + jest-resolve-dependencies: ^29.6.2 + jest-runner: ^29.6.2 + jest-runtime: ^29.6.2 + jest-snapshot: ^29.6.2 + jest-util: ^29.6.2 + jest-validate: ^29.6.2 + jest-watcher: ^29.6.2 micromatch: ^4.0.4 - pretty-format: ^29.7.0 + pretty-format: ^29.6.2 slash: ^3.0.0 strip-ansi: ^6.0.0 peerDependencies: @@ -1787,76 +1780,76 @@ __metadata: peerDependenciesMeta: node-notifier: optional: true - checksum: af759c9781cfc914553320446ce4e47775ae42779e73621c438feb1e4231a5d4862f84b1d8565926f2d1aab29b3ec3dcfdc84db28608bdf5f29867124ebcfc0d + checksum: 6bbb3886430248c0092f275b1b946a701406732f7442c04e63e4ee2297c2ec02d8ceeec508a202e08128197699b2bcddbae2c2f74adb2cf30f2f0d7d94a7c2dc languageName: node linkType: hard -"@jest/environment@npm:^29.7.0": - version: 29.7.0 - resolution: "@jest/environment@npm:29.7.0" +"@jest/environment@npm:^29.6.2": + version: 29.6.2 + resolution: "@jest/environment@npm:29.6.2" dependencies: - "@jest/fake-timers": ^29.7.0 - "@jest/types": ^29.6.3 + "@jest/fake-timers": ^29.6.2 + "@jest/types": ^29.6.1 "@types/node": "*" - jest-mock: ^29.7.0 - checksum: 6fb398143b2543d4b9b8d1c6dbce83fa5247f84f550330604be744e24c2bd2178bb893657d62d1b97cf2f24baf85c450223f8237cccb71192c36a38ea2272934 + jest-mock: ^29.6.2 + checksum: c7de0e4c0d9166e02d0eb166574e05ec460e1db3b69d6476e63244edd52d7c917e6876af55fe723ff3086f52c0b1869dec60654054735a7a48c9d4ac43af2a25 languageName: node linkType: hard -"@jest/expect-utils@npm:^29.7.0": - version: 29.7.0 - resolution: "@jest/expect-utils@npm:29.7.0" +"@jest/expect-utils@npm:^29.6.2": + version: 29.6.2 + resolution: "@jest/expect-utils@npm:29.6.2" dependencies: - jest-get-type: ^29.6.3 - checksum: 75eb177f3d00b6331bcaa057e07c0ccb0733a1d0a1943e1d8db346779039cb7f103789f16e502f888a3096fb58c2300c38d1f3748b36a7fa762eb6f6d1b160ed + jest-get-type: ^29.4.3 + checksum: 0decf2009aa3735f9df469e78ce1721c2815e4278439887e0cf0321ca8979541a22515d114a59b2445a6cd70a074b09dc9c00b5e7b3b3feac5174b9c4a78b2e1 languageName: node linkType: hard -"@jest/expect@npm:^29.7.0": - version: 29.7.0 - resolution: "@jest/expect@npm:29.7.0" +"@jest/expect@npm:^29.6.2": + version: 29.6.2 + resolution: "@jest/expect@npm:29.6.2" dependencies: - expect: ^29.7.0 - jest-snapshot: ^29.7.0 - checksum: a01cb85fd9401bab3370618f4b9013b90c93536562222d920e702a0b575d239d74cecfe98010aaec7ad464f67cf534a353d92d181646a4b792acaa7e912ae55e + expect: ^29.6.2 + jest-snapshot: ^29.6.2 + checksum: bd2d88a4e7c5420079c239afef341ec53dc7e353816cd13acbb42631a31fd321fe58677bb43a4dba851028f4c7e31da7980314e9094cd5b348896cb6cd3d42b2 languageName: node linkType: hard -"@jest/fake-timers@npm:^29.7.0": - version: 29.7.0 - resolution: "@jest/fake-timers@npm:29.7.0" +"@jest/fake-timers@npm:^29.6.2": + version: 29.6.2 + resolution: "@jest/fake-timers@npm:29.6.2" dependencies: - "@jest/types": ^29.6.3 + "@jest/types": ^29.6.1 "@sinonjs/fake-timers": ^10.0.2 "@types/node": "*" - jest-message-util: ^29.7.0 - jest-mock: ^29.7.0 - jest-util: ^29.7.0 - checksum: caf2bbd11f71c9241b458d1b5a66cbe95debc5a15d96442444b5d5c7ba774f523c76627c6931cca5e10e76f0d08761f6f1f01a608898f4751a0eee54fc3d8d00 + jest-message-util: ^29.6.2 + jest-mock: ^29.6.2 + jest-util: ^29.6.2 + checksum: 1abcda02f22d2ba32e178b7ab80a9180235a6c75ec9faef33324627b19a70dad64889a9ea49b8f07230e14a6e683b9120542c6d1d6b2ecaf937f4efde32dad88 languageName: node linkType: hard -"@jest/globals@npm:^29.5.0, @jest/globals@npm:^29.7.0": - version: 29.7.0 - resolution: "@jest/globals@npm:29.7.0" +"@jest/globals@npm:^29.5.0, @jest/globals@npm:^29.6.2": + version: 29.6.2 + resolution: "@jest/globals@npm:29.6.2" dependencies: - "@jest/environment": ^29.7.0 - "@jest/expect": ^29.7.0 - "@jest/types": ^29.6.3 - jest-mock: ^29.7.0 - checksum: 97dbb9459135693ad3a422e65ca1c250f03d82b2a77f6207e7fa0edd2c9d2015fbe4346f3dc9ebff1678b9d8da74754d4d440b7837497f8927059c0642a22123 + "@jest/environment": ^29.6.2 + "@jest/expect": ^29.6.2 + "@jest/types": ^29.6.1 + jest-mock: ^29.6.2 + checksum: aa4a54f19cc025205bc696546940e1fe9c752c2d4d825852088aa76d44677ebba1ec66fabb78e615480cff23a06a70b5a3f893ab5163d901cdfa0d2267870b10 languageName: node linkType: hard -"@jest/reporters@npm:^29.7.0": - version: 29.7.0 - resolution: "@jest/reporters@npm:29.7.0" +"@jest/reporters@npm:^29.6.2": + version: 29.6.2 + resolution: "@jest/reporters@npm:29.6.2" dependencies: "@bcoe/v8-coverage": ^0.2.3 - "@jest/console": ^29.7.0 - "@jest/test-result": ^29.7.0 - "@jest/transform": ^29.7.0 - "@jest/types": ^29.6.3 + "@jest/console": ^29.6.2 + "@jest/test-result": ^29.6.2 + "@jest/transform": ^29.6.2 + "@jest/types": ^29.6.1 "@jridgewell/trace-mapping": ^0.3.18 "@types/node": "*" chalk: ^4.0.0 @@ -1865,13 +1858,13 @@ __metadata: glob: ^7.1.3 graceful-fs: ^4.2.9 istanbul-lib-coverage: ^3.0.0 - istanbul-lib-instrument: ^6.0.0 + istanbul-lib-instrument: ^5.1.0 istanbul-lib-report: ^3.0.0 istanbul-lib-source-maps: ^4.0.0 istanbul-reports: ^3.1.3 - jest-message-util: ^29.7.0 - jest-util: ^29.7.0 - jest-worker: ^29.7.0 + jest-message-util: ^29.6.2 + jest-util: ^29.6.2 + jest-worker: ^29.6.2 slash: ^3.0.0 string-length: ^4.0.1 strip-ansi: ^6.0.0 @@ -1881,88 +1874,88 @@ __metadata: peerDependenciesMeta: node-notifier: optional: true - checksum: 7eadabd62cc344f629024b8a268ecc8367dba756152b761bdcb7b7e570a3864fc51b2a9810cd310d85e0a0173ef002ba4528d5ea0329fbf66ee2a3ada9c40455 + checksum: 7cf880d0730cee7d24ee96928003ef6946bf93423b0ae9a2edb53cae2c231b8ac50ec264f48a73744e3f11ca319cd414edacf99b2e7bf37cd72fe0b362090dd1 languageName: node linkType: hard -"@jest/schemas@npm:^29.6.3": - version: 29.6.3 - resolution: "@jest/schemas@npm:29.6.3" +"@jest/schemas@npm:^29.6.0": + version: 29.6.0 + resolution: "@jest/schemas@npm:29.6.0" dependencies: "@sinclair/typebox": ^0.27.8 - checksum: 910040425f0fc93cd13e68c750b7885590b8839066dfa0cd78e7def07bbb708ad869381f725945d66f2284de5663bbecf63e8fdd856e2ae6e261ba30b1687e93 + checksum: c00511c69cf89138a7d974404d3a5060af375b5a52b9c87215d91873129b382ca11c1ff25bd6d605951404bb381ddce5f8091004a61e76457da35db1f5c51365 languageName: node linkType: hard -"@jest/source-map@npm:^29.6.3": - version: 29.6.3 - resolution: "@jest/source-map@npm:29.6.3" +"@jest/source-map@npm:^29.6.0": + version: 29.6.0 + resolution: "@jest/source-map@npm:29.6.0" dependencies: "@jridgewell/trace-mapping": ^0.3.18 callsites: ^3.0.0 graceful-fs: ^4.2.9 - checksum: bcc5a8697d471396c0003b0bfa09722c3cd879ad697eb9c431e6164e2ea7008238a01a07193dfe3cbb48b1d258eb7251f6efcea36f64e1ebc464ea3c03ae2deb + checksum: 9c6c40387410bb70b2fae8124287fc28f6bdd1b2d7f24348e8611e1bb638b404518228a4ce64a582365b589c536ae8e7ebab0126cef59a87874b71061d19783b languageName: node linkType: hard -"@jest/test-result@npm:^29.7.0": - version: 29.7.0 - resolution: "@jest/test-result@npm:29.7.0" +"@jest/test-result@npm:^29.6.2": + version: 29.6.2 + resolution: "@jest/test-result@npm:29.6.2" dependencies: - "@jest/console": ^29.7.0 - "@jest/types": ^29.6.3 + "@jest/console": ^29.6.2 + "@jest/types": ^29.6.1 "@types/istanbul-lib-coverage": ^2.0.0 collect-v8-coverage: ^1.0.0 - checksum: 67b6317d526e335212e5da0e768e3b8ab8a53df110361b80761353ad23b6aea4432b7c5665bdeb87658ea373b90fb1afe02ed3611ef6c858c7fba377505057fa + checksum: 8aff37f18c8d2df4d9f453d57ec018a6479eb697fabcf74b1ca06e34553da1d7a2b85580a290408ba0b02e58543263244a2cb065c7c7180c8d8180cc78444fbd languageName: node linkType: hard -"@jest/test-sequencer@npm:^29.7.0": - version: 29.7.0 - resolution: "@jest/test-sequencer@npm:29.7.0" +"@jest/test-sequencer@npm:^29.6.2": + version: 29.6.2 + resolution: "@jest/test-sequencer@npm:29.6.2" dependencies: - "@jest/test-result": ^29.7.0 + "@jest/test-result": ^29.6.2 graceful-fs: ^4.2.9 - jest-haste-map: ^29.7.0 + jest-haste-map: ^29.6.2 slash: ^3.0.0 - checksum: 73f43599017946be85c0b6357993b038f875b796e2f0950487a82f4ebcb115fa12131932dd9904026b4ad8be131fe6e28bd8d0aa93b1563705185f9804bff8bd + checksum: 12dc2577e45eeb98b85d1769846b7d6effa536907986ad3c4cbd014df9e24431a564cc8cd94603332e4b1f9bfb421371883efc6a5085b361a52425ffc2a52dc6 languageName: node linkType: hard -"@jest/transform@npm:^29.7.0": - version: 29.7.0 - resolution: "@jest/transform@npm:29.7.0" +"@jest/transform@npm:^29.6.2": + version: 29.6.2 + resolution: "@jest/transform@npm:29.6.2" dependencies: "@babel/core": ^7.11.6 - "@jest/types": ^29.6.3 + "@jest/types": ^29.6.1 "@jridgewell/trace-mapping": ^0.3.18 babel-plugin-istanbul: ^6.1.1 chalk: ^4.0.0 convert-source-map: ^2.0.0 fast-json-stable-stringify: ^2.1.0 graceful-fs: ^4.2.9 - jest-haste-map: ^29.7.0 - jest-regex-util: ^29.6.3 - jest-util: ^29.7.0 + jest-haste-map: ^29.6.2 + jest-regex-util: ^29.4.3 + jest-util: ^29.6.2 micromatch: ^4.0.4 pirates: ^4.0.4 slash: ^3.0.0 write-file-atomic: ^4.0.2 - checksum: 0f8ac9f413903b3cb6d240102db848f2a354f63971ab885833799a9964999dd51c388162106a807f810071f864302cdd8e3f0c241c29ce02d85a36f18f3f40ab + checksum: ffb8c3c344cd48bedadec295d9c436737eccc39c1f0868aa9753b76397b33b2e5b121058af6f287ba6f2036181137e37df1212334bfa9d9a712986a4518cdc18 languageName: node linkType: hard -"@jest/types@npm:^29.6.3": - version: 29.6.3 - resolution: "@jest/types@npm:29.6.3" +"@jest/types@npm:^29.6.1": + version: 29.6.1 + resolution: "@jest/types@npm:29.6.1" dependencies: - "@jest/schemas": ^29.6.3 + "@jest/schemas": ^29.6.0 "@types/istanbul-lib-coverage": ^2.0.0 "@types/istanbul-reports": ^3.0.0 "@types/node": "*" "@types/yargs": ^17.0.8 chalk: ^4.0.0 - checksum: a0bcf15dbb0eca6bdd8ce61a3fb055349d40268622a7670a3b2eb3c3dbafe9eb26af59938366d520b86907b9505b0f9b29b85cec11579a9e580694b87cd90fcc + checksum: 89fc1ccf71a84fe0da643e0675b1cfe6a6f19ea72e935b2ab1dbdb56ec547e94433fb59b3536d3832a6e156c077865b7176fe9dae707dab9c3d2f9405ba6233c languageName: node linkType: hard @@ -1977,7 +1970,14 @@ __metadata: languageName: node linkType: hard -"@jridgewell/resolve-uri@npm:^3.0.3, @jridgewell/resolve-uri@npm:^3.1.0": +"@jridgewell/resolve-uri@npm:3.1.0": + version: 3.1.0 + resolution: "@jridgewell/resolve-uri@npm:3.1.0" + checksum: b5ceaaf9a110fcb2780d1d8f8d4a0bfd216702f31c988d8042e5f8fbe353c55d9b0f55a1733afdc64806f8e79c485d2464680ac48a0d9fcadb9548ee6b81d267 + languageName: node + linkType: hard + +"@jridgewell/resolve-uri@npm:^3.0.3": version: 3.1.1 resolution: "@jridgewell/resolve-uri@npm:3.1.1" checksum: f5b441fe7900eab4f9155b3b93f9800a916257f4e8563afbcd3b5a5337b55e52bd8ae6735453b1b745457d9f6cdb16d74cd6220bbdd98cf153239e13f6cbb653 @@ -2001,7 +2001,14 @@ __metadata: languageName: node linkType: hard -"@jridgewell/sourcemap-codec@npm:^1.4.10, @jridgewell/sourcemap-codec@npm:^1.4.14": +"@jridgewell/sourcemap-codec@npm:1.4.14": + version: 1.4.14 + resolution: "@jridgewell/sourcemap-codec@npm:1.4.14" + checksum: 61100637b6d173d3ba786a5dff019e1a74b1f394f323c1fee337ff390239f053b87266c7a948777f4b1ee68c01a8ad0ab61e5ff4abb5a012a0b091bec391ab97 + languageName: node + linkType: hard + +"@jridgewell/sourcemap-codec@npm:^1.4.10": version: 1.4.15 resolution: "@jridgewell/sourcemap-codec@npm:1.4.15" checksum: b881c7e503db3fc7f3c1f35a1dd2655a188cc51a3612d76efc8a6eb74728bef5606e6758ee77423e564092b4a518aba569bbb21c9bac5ab7a35b0c6ae7e344c8 @@ -2018,13 +2025,13 @@ __metadata: languageName: node linkType: hard -"@jridgewell/trace-mapping@npm:^0.3.12, @jridgewell/trace-mapping@npm:^0.3.17, @jridgewell/trace-mapping@npm:^0.3.18, @jridgewell/trace-mapping@npm:^0.3.20, @jridgewell/trace-mapping@npm:^0.3.9": - version: 0.3.22 - resolution: "@jridgewell/trace-mapping@npm:0.3.22" +"@jridgewell/trace-mapping@npm:^0.3.12, @jridgewell/trace-mapping@npm:^0.3.17, @jridgewell/trace-mapping@npm:^0.3.18, @jridgewell/trace-mapping@npm:^0.3.9": + version: 0.3.18 + resolution: "@jridgewell/trace-mapping@npm:0.3.18" dependencies: - "@jridgewell/resolve-uri": ^3.1.0 - "@jridgewell/sourcemap-codec": ^1.4.14 - checksum: ac7dd2cfe0b479aa1b81776d40d789243131cc792dc8b6b6a028c70fcd6171958ae1a71bf67b618ffe3c0c3feead9870c095ee46a5e30319410d92976b28f498 + "@jridgewell/resolve-uri": 3.1.0 + "@jridgewell/sourcemap-codec": 1.4.14 + checksum: 0572669f855260808c16fe8f78f5f1b4356463b11d3f2c7c0b5580c8ba1cbf4ae53efe9f627595830856e57dbac2325ac17eb0c3dd0ec42102e6f227cc289c02 languageName: node linkType: hard @@ -2038,23 +2045,23 @@ __metadata: linkType: hard "@libp2p/bootstrap@npm:^9.0.4": - version: 9.0.12 - resolution: "@libp2p/bootstrap@npm:9.0.12" + version: 9.0.4 + resolution: "@libp2p/bootstrap@npm:9.0.4" dependencies: - "@libp2p/interface": ^0.1.6 - "@libp2p/logger": ^3.1.0 - "@libp2p/peer-id": ^3.0.6 + "@libp2p/interface": ^0.1.2 + "@libp2p/logger": ^3.0.2 + "@libp2p/peer-id": ^3.0.2 "@multiformats/mafmt": ^12.1.2 "@multiformats/multiaddr": ^12.1.5 - checksum: 249198129b806bf5525d527074e9151c96a411c61474543f8e2679664733af0873c5267b4c579fa29ac4f64f7fe3dae32e70dba66acafd321a3368adc579bccf + checksum: 10e66b4b20645606319ed9192c9672e424aa89cd3e16b3202ac23b36bdf188873882bf71535b6b9adcef4720c69af7cf3f29a39a8139334b11dcdefbed8dcd4a languageName: node linkType: hard -"@libp2p/crypto@npm:^2.0.0, @libp2p/crypto@npm:^2.0.8": - version: 2.0.8 - resolution: "@libp2p/crypto@npm:2.0.8" +"@libp2p/crypto@npm:^2.0.0, @libp2p/crypto@npm:^2.0.3": + version: 2.0.3 + resolution: "@libp2p/crypto@npm:2.0.3" dependencies: - "@libp2p/interface": ^0.1.6 + "@libp2p/interface": ^0.1.2 "@noble/curves": ^1.1.0 "@noble/hashes": ^1.3.1 multiformats: ^12.0.1 @@ -2062,7 +2069,23 @@ __metadata: protons-runtime: ^5.0.0 uint8arraylist: ^2.4.3 uint8arrays: ^4.0.6 - checksum: 4047dc54e33670c7aa4717b9f5e7980717e00ef6a2cefaae336cdcd88f9ca38d7f29a1c642d8cbc7cd85bd2957eb79b869791b4f48fb2e91652b2d87224a136d + checksum: b1806cea33fe5f16ff6d7d920f8cf77a33d06f418d9488f0b40c0251568e01a12014ee84481048764bc1d2a4372ba536842f2fb4f9879c529c6a2a0755b77e95 + languageName: node + linkType: hard + +"@libp2p/crypto@npm:^2.0.4": + version: 2.0.4 + resolution: "@libp2p/crypto@npm:2.0.4" + dependencies: + "@libp2p/interface": ^0.1.2 + "@noble/curves": ^1.1.0 + "@noble/hashes": ^1.3.1 + multiformats: ^12.0.1 + node-forge: ^1.1.0 + protons-runtime: ^5.0.0 + uint8arraylist: ^2.4.3 + uint8arrays: ^4.0.6 + checksum: 1e0a0e8006e9ca7caaec16ce94d193063749fc7f62a7b3d0b4469915e2837c380987baa400a9f35ee13c23783f8317f8bc58b8ed647a5b4bf55635a2a909ae97 languageName: node linkType: hard @@ -2090,15 +2113,15 @@ __metadata: languageName: node linkType: hard -"@libp2p/interface-internal@npm:^0.1.9": - version: 0.1.12 - resolution: "@libp2p/interface-internal@npm:0.1.12" +"@libp2p/interface-internal@npm:^0.1.4": + version: 0.1.4 + resolution: "@libp2p/interface-internal@npm:0.1.4" dependencies: - "@libp2p/interface": ^0.1.6 - "@libp2p/peer-collections": ^4.0.8 + "@libp2p/interface": ^0.1.2 + "@libp2p/peer-collections": ^4.0.3 "@multiformats/multiaddr": ^12.1.5 uint8arraylist: ^2.4.3 - checksum: 3ffa5843fd7fd046f5ab4cb70a96da6c392ba2f24f832a3b99a0b20653fa6478c924caacd8c1a6aa4de2cb2a39669778ed804bfcb5c0c232a775cc1e29f3dd17 + checksum: 26bbd1a2811baa089eb3ec9031f64d8b7c5e183e4f4a1f4b6c37b1de22985e61e63f9ba06fa7cbc91d60217d66773407361c8ea7abba93cf900e633547d5da97 languageName: node linkType: hard @@ -2220,9 +2243,9 @@ __metadata: languageName: node linkType: hard -"@libp2p/interface@npm:^0.1.0, @libp2p/interface@npm:^0.1.2, @libp2p/interface@npm:^0.1.6": - version: 0.1.6 - resolution: "@libp2p/interface@npm:0.1.6" +"@libp2p/interface@npm:^0.1.0, @libp2p/interface@npm:^0.1.1, @libp2p/interface@npm:^0.1.2": + version: 0.1.2 + resolution: "@libp2p/interface@npm:0.1.2" dependencies: "@multiformats/multiaddr": ^12.1.5 abortable-iterator: ^5.0.1 @@ -2230,27 +2253,12 @@ __metadata: it-stream-types: ^2.0.1 multiformats: ^12.0.1 p-defer: ^4.0.0 - race-signal: ^1.0.0 uint8arraylist: ^2.4.3 - checksum: dbf0c4544bbb2a299d54615e8ef553324657e7cb06a2fdc3f1a99d785276c69882841b0e9cf73b934923318d04fe6c6f7ef59194d438a126d0d8d3a4b05cc22b - languageName: node - linkType: hard - -"@libp2p/interface@npm:^1.0.0, @libp2p/interface@npm:^1.1.2": - version: 1.1.2 - resolution: "@libp2p/interface@npm:1.1.2" - dependencies: - "@multiformats/multiaddr": ^12.1.10 - it-pushable: ^3.2.3 - it-stream-types: ^2.0.1 - multiformats: ^13.0.0 - progress-events: ^1.0.0 - uint8arraylist: ^2.4.7 - checksum: 99e257281fde4a226124344f24eb246b2a1f0639be3a73aae4478e97267f4fa5d9f86754291b16d5da01179d0fa11066477a7e1b6bb4ddfa025442b0fdc90809 + checksum: d33748ba16473c622802ee95e57445f2ac79b1ddffbaba7499157a769c57f9c1374ac10de72517e7899f88808e6fee09d77aef928d8eeaaf2bb8951fa93653a7 languageName: node linkType: hard -"@libp2p/interfaces@npm:^3.0.0": +"@libp2p/interfaces@npm:^3.0.0, @libp2p/interfaces@npm:^3.3.1": version: 3.3.2 resolution: "@libp2p/interfaces@npm:3.3.2" checksum: 3071fa49dcbb81a4b218248a1f648fba1061fb9c51e4b5edab9b8a7b9425c25afec96fdf3351ea7a469e7039269e59d95265682a934aa9c21630226dfcb67313 @@ -2258,20 +2266,21 @@ __metadata: linkType: hard "@libp2p/kad-dht@npm:^10.0.4": - version: 10.0.15 - resolution: "@libp2p/kad-dht@npm:10.0.15" - dependencies: - "@libp2p/crypto": ^2.0.8 - "@libp2p/interface": ^0.1.6 - "@libp2p/interface-internal": ^0.1.9 - "@libp2p/logger": ^3.1.0 - "@libp2p/peer-collections": ^4.0.8 - "@libp2p/peer-id": ^3.0.6 + version: 10.0.4 + resolution: "@libp2p/kad-dht@npm:10.0.4" + dependencies: + "@libp2p/crypto": ^2.0.3 + "@libp2p/interface": ^0.1.2 + "@libp2p/interface-internal": ^0.1.4 + "@libp2p/logger": ^3.0.2 + "@libp2p/peer-collections": ^4.0.3 + "@libp2p/peer-id": ^3.0.2 "@multiformats/multiaddr": ^12.1.5 - "@types/sinon": ^17.0.0 + "@types/sinon": ^10.0.15 abortable-iterator: ^5.0.1 any-signal: ^4.1.1 datastore-core: ^9.0.1 + events: ^3.3.0 hashlru: ^2.3.0 interface-datastore: ^8.2.0 it-all: ^3.0.2 @@ -2283,7 +2292,6 @@ __metadata: it-merge: ^3.0.0 it-parallel: ^3.0.0 it-pipe: ^3.0.1 - it-pushable: ^3.2.1 it-stream-types: ^2.0.1 it-take: ^3.0.1 multiformats: ^12.0.1 @@ -2296,27 +2304,27 @@ __metadata: uint8-varint: ^2.0.0 uint8arraylist: ^2.4.3 uint8arrays: ^4.0.6 - checksum: 566c62d45ff8ba92ea15332c8b62395a8e4f794ee46c038b04e4c144f032ddceae080e2a6de0e0948370620d3b708f61052783b788ba40d53d11044910f9becf + checksum: 8fbc6b2e12eeb98825b7dfa9e09a1c26f22a679167bde6305e8c524ee5514f509639db70915c432e1749272348f3eb8bb37ea7978a1a6f4133053e6b37ae3e3f languageName: node linkType: hard -"@libp2p/keychain@npm:^3.0.8": - version: 3.0.8 - resolution: "@libp2p/keychain@npm:3.0.8" +"@libp2p/keychain@npm:^3.0.3": + version: 3.0.3 + resolution: "@libp2p/keychain@npm:3.0.3" dependencies: - "@libp2p/crypto": ^2.0.8 - "@libp2p/interface": ^0.1.6 - "@libp2p/logger": ^3.1.0 - "@libp2p/peer-id": ^3.0.6 + "@libp2p/crypto": ^2.0.3 + "@libp2p/interface": ^0.1.2 + "@libp2p/logger": ^3.0.2 + "@libp2p/peer-id": ^3.0.2 interface-datastore: ^8.2.0 merge-options: ^3.0.4 sanitize-filename: ^1.6.3 uint8arrays: ^4.0.6 - checksum: 765971d2ef29cdc781ff2447f28501b9f58c8a9d0e4339c17b01de5a57d50344f58b40928c3b0ad85534a416ac1055a0c1ca59852ca26329118d423d7e305e66 + checksum: cf02dea0290d891c903fb9be2ad4955ae1a5dcb5a8f842873667231d8a3ce9225cadee57ca88d23d887be0be932fe5aece16412965da267f01b1a2e4c478f38f languageName: node linkType: hard -"@libp2p/logger@npm:^2.0.7": +"@libp2p/logger@npm:^2.0.0, @libp2p/logger@npm:^2.0.7": version: 2.1.1 resolution: "@libp2p/logger@npm:2.1.1" dependencies: @@ -2329,57 +2337,44 @@ __metadata: languageName: node linkType: hard -"@libp2p/logger@npm:^3.0.0, @libp2p/logger@npm:^3.1.0": - version: 3.1.0 - resolution: "@libp2p/logger@npm:3.1.0" +"@libp2p/logger@npm:^3.0.0, @libp2p/logger@npm:^3.0.2": + version: 3.0.2 + resolution: "@libp2p/logger@npm:3.0.2" dependencies: - "@libp2p/interface": ^0.1.6 + "@libp2p/interface": ^0.1.2 "@multiformats/multiaddr": ^12.1.5 debug: ^4.3.4 interface-datastore: ^8.2.0 multiformats: ^12.0.1 - checksum: ff80803afe40a1078dbaf3119fd312bce283660e6e3d0705c91395c5e1f09520217ef29d8426880db23837bcfa444579cae6b4fc6e9ae4c869ecf4e5b9a0a59c - languageName: node - linkType: hard - -"@libp2p/logger@npm:^4.0.1": - version: 4.0.5 - resolution: "@libp2p/logger@npm:4.0.5" - dependencies: - "@libp2p/interface": ^1.1.2 - "@multiformats/multiaddr": ^12.1.10 - debug: ^4.3.4 - interface-datastore: ^8.2.0 - multiformats: ^13.0.0 - checksum: e0bc60e3ac6f3ab017eb16b26eb3badc3c728291fdbea28cf9ef2f9fbfc1161d562acfa4c4fbd148d6ad0de8a4417f02d1ffada611af5255fff8495df2d65707 + checksum: 55734af96bdcf1572796e26d859c4a8f6ee9b4f0ca98bf0112e46cc90ce690a807e533095db79887919d7d33d5a90d2365e1354cdd3787b08ec396388a32f7a6 languageName: node linkType: hard "@libp2p/mplex@npm:^9.0.4": - version: 9.0.12 - resolution: "@libp2p/mplex@npm:9.0.12" + version: 9.0.4 + resolution: "@libp2p/mplex@npm:9.0.4" dependencies: - "@libp2p/interface": ^0.1.6 - "@libp2p/logger": ^3.1.0 + "@libp2p/interface": ^0.1.2 + "@libp2p/logger": ^3.0.2 abortable-iterator: ^5.0.1 benchmark: ^2.1.4 it-batched-bytes: ^2.0.2 it-pushable: ^3.2.0 it-stream-types: ^2.0.1 - rate-limiter-flexible: ^3.0.0 + rate-limiter-flexible: ^2.3.11 uint8-varint: ^2.0.0 uint8arraylist: ^2.4.3 uint8arrays: ^4.0.6 - checksum: 13c9c8a1826d81fc0497f84bfff97d75563602ea7bf07ac64f29bb3f6c13129610e825bca1bda8d63de62945009a0dc91b607c4979d2e91ad6c2e10f8008ec10 + checksum: ab3bbda7a8fd4c4fd5c0a3d6865cfcd2c2659eff8dbb81c20b8dceeaa7a804039bada63faa7e9d04cdf48c215cc772cef0d38be0f61046b55f0c888b879ce9c3 languageName: node linkType: hard -"@libp2p/multistream-select@npm:^4.0.6": - version: 4.0.10 - resolution: "@libp2p/multistream-select@npm:4.0.10" +"@libp2p/multistream-select@npm:^4.0.2": + version: 4.0.2 + resolution: "@libp2p/multistream-select@npm:4.0.2" dependencies: - "@libp2p/interface": ^0.1.6 - "@libp2p/logger": ^3.1.0 + "@libp2p/interface": ^0.1.2 + "@libp2p/logger": ^3.0.2 abortable-iterator: ^5.0.1 it-first: ^3.0.1 it-handshake: ^4.1.3 @@ -2389,76 +2384,90 @@ __metadata: it-pushable: ^3.2.0 it-reader: ^6.0.1 it-stream-types: ^2.0.1 - uint8-varint: ^2.0.0 uint8arraylist: ^2.4.3 uint8arrays: ^4.0.6 - checksum: 3a3d32cc3605f73cef4039712b394c1bf18d5ca624f9b8b43c880b4d65b8bbc0491273a970183febd52c3f30dfbdbd2f6ac4aa102a117661e057749e67910bb6 + checksum: 2909e297eabcb7b3da0391f6a323bb414d509e4fbe2feaa3a6a287f95bb0d46c27ec3d952755353142fe910b1fc613114e3efa17bc0f2f50109897d1a7dc85e5 languageName: node linkType: hard -"@libp2p/peer-collections@npm:^4.0.8": - version: 4.0.11 - resolution: "@libp2p/peer-collections@npm:4.0.11" +"@libp2p/peer-collections@npm:^4.0.3": + version: 4.0.3 + resolution: "@libp2p/peer-collections@npm:4.0.3" dependencies: - "@libp2p/interface": ^0.1.6 - "@libp2p/peer-id": ^3.0.6 - checksum: b15651d905007f51cb9867f08aa3af21b8a36fef67a2f3a6309483df13d99d7611e6a9aed7dbaf1cf2adbe4ccbca0d366be92dc4e66afc705bbd9ffe43dc8b80 + "@libp2p/interface": ^0.1.2 + "@libp2p/peer-id": ^3.0.2 + checksum: 3a8784a17d1cbd3098b43d71b888ff15e4d9b2638090c44cb02b5d02c0590ef589788cc0f61708050b74f8d52b38ed4101796016e64478a2e3c1ae0f321e8451 languageName: node linkType: hard -"@libp2p/peer-id-factory@npm:^3.0.3, @libp2p/peer-id-factory@npm:^3.0.4, @libp2p/peer-id-factory@npm:^3.0.8": - version: 3.0.11 - resolution: "@libp2p/peer-id-factory@npm:3.0.11" +"@libp2p/peer-id-factory@npm:^3.0.3": + version: 3.0.3 + resolution: "@libp2p/peer-id-factory@npm:3.0.3" dependencies: - "@libp2p/crypto": ^2.0.8 - "@libp2p/interface": ^0.1.6 - "@libp2p/peer-id": ^3.0.6 + "@libp2p/crypto": ^2.0.3 + "@libp2p/interface": ^0.1.2 + "@libp2p/peer-id": ^3.0.2 multiformats: ^12.0.1 protons-runtime: ^5.0.0 uint8arraylist: ^2.4.3 uint8arrays: ^4.0.6 - checksum: bdcee4fef7f8aace6a8316e523e8c82753986a42c58b51f59d04534a1095c9c1eec8193e859614aa2589a7f5e43e64e529bb0b475e7bad7150b2034b2ebc0aa2 + checksum: 093530837f0d73fc6cb40c168c10c4bc2f196783b089c1006b9958b5ad86b93963de9fccf18159ce60afad01896c549bee5e368283fba288d9abf2598c7a6ea5 languageName: node linkType: hard -"@libp2p/peer-id@npm:^3.0.0, @libp2p/peer-id@npm:^3.0.2, @libp2p/peer-id@npm:^3.0.6": - version: 3.0.6 - resolution: "@libp2p/peer-id@npm:3.0.6" +"@libp2p/peer-id-factory@npm:^3.0.4": + version: 3.0.4 + resolution: "@libp2p/peer-id-factory@npm:3.0.4" dependencies: - "@libp2p/interface": ^0.1.6 + "@libp2p/crypto": ^2.0.4 + "@libp2p/interface": ^0.1.2 + "@libp2p/peer-id": ^3.0.2 multiformats: ^12.0.1 + protons-runtime: ^5.0.0 + uint8arraylist: ^2.4.3 uint8arrays: ^4.0.6 - checksum: d573948b9b9fc64d80a2175ff27c9878d15a43aec5c71d9486c9b6d5e4a0510c9d935c71e35420cd09d55f32c3a87307112627becc5d9f709b4b19a7100f7d30 + checksum: 40c534029bfa8d9b98119ee2c0ce3c0c91bcf7280028e7ba8bfe4da2a90de53014791d75c8d1400877919f748918704276b6e3337c5128975842c0593464fda9 languageName: node linkType: hard -"@libp2p/peer-record@npm:^6.0.9": - version: 6.0.12 - resolution: "@libp2p/peer-record@npm:6.0.12" +"@libp2p/peer-id@npm:^3.0.0, @libp2p/peer-id@npm:^3.0.2": + version: 3.0.2 + resolution: "@libp2p/peer-id@npm:3.0.2" dependencies: - "@libp2p/crypto": ^2.0.8 - "@libp2p/interface": ^0.1.6 - "@libp2p/peer-id": ^3.0.6 - "@libp2p/utils": ^4.0.7 + "@libp2p/interface": ^0.1.2 + multiformats: ^12.0.1 + uint8arrays: ^4.0.6 + checksum: 021a5854dd2b8afc0d83e1541531d9710be237d5a6883aa0965d85cba629fbaaa27f68f774f5fa9c331df6a8e6b1187031d270905a7c33f103177cf0e190e2bb + languageName: node + linkType: hard + +"@libp2p/peer-record@npm:^6.0.3": + version: 6.0.3 + resolution: "@libp2p/peer-record@npm:6.0.3" + dependencies: + "@libp2p/crypto": ^2.0.3 + "@libp2p/interface": ^0.1.2 + "@libp2p/peer-id": ^3.0.2 + "@libp2p/utils": ^4.0.2 "@multiformats/multiaddr": ^12.1.5 protons-runtime: ^5.0.0 - uint8-varint: ^2.0.0 + uint8-varint: ^1.0.2 uint8arraylist: ^2.4.3 uint8arrays: ^4.0.6 - checksum: d252cafa7c63fc05c8715cb4de8e340bbd76e5438b3eaf309f6e2de5a41d550f399f4ddba110ef2edcb4e667659baa20d4da2cc4d9f19611ae4402f72eb87006 + checksum: 63e7a6190a608d649761303b8e2783fcdb8f2024d3cafc137afeb67eab553469fb1f052cbe88fdb489385cd621aaa4cf52d9d93effb99111704158d98408e1cb languageName: node linkType: hard -"@libp2p/peer-store@npm:^9.0.9": - version: 9.0.12 - resolution: "@libp2p/peer-store@npm:9.0.12" +"@libp2p/peer-store@npm:^9.0.3": + version: 9.0.3 + resolution: "@libp2p/peer-store@npm:9.0.3" dependencies: - "@libp2p/interface": ^0.1.6 - "@libp2p/logger": ^3.1.0 - "@libp2p/peer-collections": ^4.0.8 - "@libp2p/peer-id": ^3.0.6 - "@libp2p/peer-id-factory": ^3.0.8 - "@libp2p/peer-record": ^6.0.9 + "@libp2p/interface": ^0.1.2 + "@libp2p/logger": ^3.0.2 + "@libp2p/peer-collections": ^4.0.3 + "@libp2p/peer-id": ^3.0.2 + "@libp2p/peer-id-factory": ^3.0.3 + "@libp2p/peer-record": ^6.0.3 "@multiformats/multiaddr": ^12.1.5 interface-datastore: ^8.2.0 it-all: ^3.0.2 @@ -2467,80 +2476,79 @@ __metadata: protons-runtime: ^5.0.0 uint8arraylist: ^2.4.3 uint8arrays: ^4.0.6 - checksum: b4d3ee98781742a7f46d50afc43f6b530058c9d8f2db04fd66c391d59427eaa4b49eb9d4df26e6bd4e6ae8d9a60e90686ae522f13e9ad517da4860c3f868a778 + checksum: 25b489c067ad910dcb8290afcbd5669ca87ddba9e1bed19c3b0279ab328d147b0f7fd0bc736707da61f897e1864c525733b2f9daae7791d7a67b76e0e7e1826b languageName: node linkType: hard "@libp2p/tcp@npm:^8.0.4": - version: 8.0.13 - resolution: "@libp2p/tcp@npm:8.0.13" + version: 8.0.4 + resolution: "@libp2p/tcp@npm:8.0.4" dependencies: - "@libp2p/interface": ^0.1.6 - "@libp2p/logger": ^3.1.0 - "@libp2p/utils": ^4.0.7 + "@libp2p/interface": ^0.1.2 + "@libp2p/logger": ^3.0.2 + "@libp2p/utils": ^4.0.2 "@multiformats/mafmt": ^12.1.2 "@multiformats/multiaddr": ^12.1.5 - "@types/sinon": ^17.0.0 + "@types/sinon": ^10.0.15 stream-to-it: ^0.2.2 - checksum: cceff8633265c3bee7b0a246808fe26a61cb5876ca3363ae3278ebd9d2bd4775e3d3057c5ef8f18b8dd78368a0fd6359c94e7f8cd3f747ad4ebc89fce80db274 + checksum: ba7fc1da50181c6e118d362f85baf6a16cdab609be9eb2b7c182326cabef87676df03f9b5f816bac8cb2d597b1462075ebe7ca4d84243b60ba45359322aa4185 languageName: node linkType: hard -"@libp2p/utils@npm:^4.0.7": - version: 4.0.7 - resolution: "@libp2p/utils@npm:4.0.7" +"@libp2p/utils@npm:^4.0.2": + version: 4.0.2 + resolution: "@libp2p/utils@npm:4.0.2" dependencies: "@chainsafe/is-ip": ^2.0.2 - "@libp2p/interface": ^0.1.6 - "@libp2p/logger": ^3.1.0 + "@libp2p/interface": ^0.1.2 + "@libp2p/logger": ^3.0.2 "@multiformats/multiaddr": ^12.1.5 - "@multiformats/multiaddr-matcher": ^1.0.1 is-loopback-addr: ^2.0.1 it-stream-types: ^2.0.1 private-ip: ^3.0.0 uint8arraylist: ^2.4.3 - checksum: df883f04b9efda532009ae0b2f03d918567277479e6f115a02673394e4239605bbaa0718e7cea7bd4c70307b44cd782492b6fa4ad9f03c7c4621f12cbb1f7d4c + checksum: 1fd40278c58fe75d587f2d209bec039787b9997ba3ad9e2f8a47b716247efb73be98093d08fa5872395cd74b6f5d1b575fd5319d545c31155313a865c9135aee languageName: node linkType: hard -"@lmdb/lmdb-darwin-arm64@npm:2.9.2": - version: 2.9.2 - resolution: "@lmdb/lmdb-darwin-arm64@npm:2.9.2" +"@lmdb/lmdb-darwin-arm64@npm:2.9.1": + version: 2.9.1 + resolution: "@lmdb/lmdb-darwin-arm64@npm:2.9.1" conditions: os=darwin & cpu=arm64 languageName: node linkType: hard -"@lmdb/lmdb-darwin-x64@npm:2.9.2": - version: 2.9.2 - resolution: "@lmdb/lmdb-darwin-x64@npm:2.9.2" +"@lmdb/lmdb-darwin-x64@npm:2.9.1": + version: 2.9.1 + resolution: "@lmdb/lmdb-darwin-x64@npm:2.9.1" conditions: os=darwin & cpu=x64 languageName: node linkType: hard -"@lmdb/lmdb-linux-arm64@npm:2.9.2": - version: 2.9.2 - resolution: "@lmdb/lmdb-linux-arm64@npm:2.9.2" +"@lmdb/lmdb-linux-arm64@npm:2.9.1": + version: 2.9.1 + resolution: "@lmdb/lmdb-linux-arm64@npm:2.9.1" conditions: os=linux & cpu=arm64 languageName: node linkType: hard -"@lmdb/lmdb-linux-arm@npm:2.9.2": - version: 2.9.2 - resolution: "@lmdb/lmdb-linux-arm@npm:2.9.2" +"@lmdb/lmdb-linux-arm@npm:2.9.1": + version: 2.9.1 + resolution: "@lmdb/lmdb-linux-arm@npm:2.9.1" conditions: os=linux & cpu=arm languageName: node linkType: hard -"@lmdb/lmdb-linux-x64@npm:2.9.2": - version: 2.9.2 - resolution: "@lmdb/lmdb-linux-x64@npm:2.9.2" +"@lmdb/lmdb-linux-x64@npm:2.9.1": + version: 2.9.1 + resolution: "@lmdb/lmdb-linux-x64@npm:2.9.1" conditions: os=linux & cpu=x64 languageName: node linkType: hard -"@lmdb/lmdb-win32-x64@npm:2.9.2": - version: 2.9.2 - resolution: "@lmdb/lmdb-win32-x64@npm:2.9.2" +"@lmdb/lmdb-win32-x64@npm:2.9.1": + version: 2.9.1 + resolution: "@lmdb/lmdb-win32-x64@npm:2.9.1" conditions: os=win32 & cpu=x64 languageName: node linkType: hard @@ -2564,30 +2572,30 @@ __metadata: languageName: node linkType: hard -"@monorepo-utils/package-utils@npm:^2.10.4": - version: 2.10.4 - resolution: "@monorepo-utils/package-utils@npm:2.10.4" +"@monorepo-utils/package-utils@npm:^2.10.2": + version: 2.10.2 + resolution: "@monorepo-utils/package-utils@npm:2.10.2" dependencies: globby: ^11.0.1 load-json-file: ^6.2.0 upath: ^2.0.1 yaml: ^2.1.3 - checksum: aa55887ff790693e2db7158aa28fbf178c7041607e65f857f516306e6ff6a77d596151ee6d61519fce8369db8cffffd07ee6fb66b99f12619f3845985dc7543f + checksum: fa8ff74b63a5760bb5088c1897f2e82c77d8c5e60858141d354b434fcefdf19a8d590721ddec328821fecfe8797018bdb79ad20be02a19d168859d19f03823ad languageName: node linkType: hard "@monorepo-utils/workspaces-to-typescript-project-references@npm:^2.9.0": - version: 2.10.4 - resolution: "@monorepo-utils/workspaces-to-typescript-project-references@npm:2.10.4" + version: 2.10.2 + resolution: "@monorepo-utils/workspaces-to-typescript-project-references@npm:2.10.2" dependencies: - "@monorepo-utils/package-utils": ^2.10.4 + "@monorepo-utils/package-utils": ^2.10.2 comment-json: ^3.0.3 meow: ^7.1.1 semver-match: 0.1.1 upath: ^2.0.1 bin: workspaces-to-typescript-project-references: bin/cmd.js - checksum: 34d539247bdcaff9f0182ec8654a5f63d81bd8b06e0947fb6ab3f8c72127684e45906510576fa1a15dd31d8b7d7ad99f29918e5a4ff9b21a74f45227f8b6ccef + checksum: 076575a837a6a928e029eeac284278e2953638a60632264d60e2efcdb216fba1d8de4fc2a74ab598ad71663a87cc595cbe3b8732da7d4845b44f1fb321a4da74 languageName: node linkType: hard @@ -2642,36 +2650,51 @@ __metadata: languageName: node linkType: hard -"@multiformats/multiaddr-matcher@npm:^1.0.0, @multiformats/multiaddr-matcher@npm:^1.0.1": - version: 1.1.2 - resolution: "@multiformats/multiaddr-matcher@npm:1.1.2" +"@multiformats/multiaddr-matcher@npm:^1.0.0": + version: 1.0.1 + resolution: "@multiformats/multiaddr-matcher@npm:1.0.1" dependencies: "@chainsafe/is-ip": ^2.0.1 "@multiformats/multiaddr": ^12.0.0 - multiformats: ^13.0.0 - checksum: ae0619211ad1a4f1021993c1372f6498cbaec07897559b0b8644e0c8e53a3fc209136d3faf4f6cef5b1533f952b55b232fd6eb089d578a3594fa92d01802d4c3 + multiformats: ^12.0.1 + checksum: 71579db42aa0e22297e542946d7ad2ab5a3427d619de6838cce46cce3bb168615577b6e94b58fca1b4af94520dae366810ecc309081810e15ff61781591908d5 languageName: node linkType: hard -"@multiformats/multiaddr@npm:^12.0.0, @multiformats/multiaddr@npm:^12.1.10, @multiformats/multiaddr@npm:^12.1.3, @multiformats/multiaddr@npm:^12.1.5": - version: 12.1.12 - resolution: "@multiformats/multiaddr@npm:12.1.12" +"@multiformats/multiaddr@npm:^12.0.0, @multiformats/multiaddr@npm:^12.1.3": + version: 12.1.5 + resolution: "@multiformats/multiaddr@npm:12.1.5" dependencies: "@chainsafe/is-ip": ^2.0.1 "@chainsafe/netmask": ^2.0.0 - "@libp2p/interface": ^1.0.0 - dns-over-http-resolver: 3.0.0 - multiformats: ^13.0.0 + "@libp2p/interfaces": ^3.3.1 + dns-over-http-resolver: ^2.1.0 + multiformats: ^12.0.1 + uint8arrays: ^4.0.2 + varint: ^6.0.0 + checksum: 01181807070382fb96019aec68df6276c90801185eedeb82c69dfef0ff6898eacc87a13e639c364dc0da976c5be623b56198e7107f2da40e4ef3a2d3523e8c49 + languageName: node + linkType: hard + +"@multiformats/multiaddr@npm:^12.1.5": + version: 12.1.7 + resolution: "@multiformats/multiaddr@npm:12.1.7" + dependencies: + "@chainsafe/is-ip": ^2.0.1 + "@chainsafe/netmask": ^2.0.0 + "@libp2p/interface": ^0.1.1 + dns-over-http-resolver: ^2.1.0 + multiformats: ^12.0.1 uint8-varint: ^2.0.1 - uint8arrays: ^5.0.0 - checksum: a50d62aeb330ef9df0d5b9a36980eba28dc619fce9adba9a5d5da4344091c42457501969cbc15e1d705dbf90513fb4730f91eabe1b68ceada56e3e721ebb3249 + uint8arrays: ^4.0.2 + checksum: 96b83208b7bd3e9387f2fdac20fc554d962395c02661e9c1da819646d2f3129e1a76e5abc6a0c8d386c7126a7678e58d05b08dc812260b7cad2488533cbe44b0 languageName: node linkType: hard -"@noble/ciphers@npm:^0.4.0": - version: 0.4.1 - resolution: "@noble/ciphers@npm:0.4.1" - checksum: 8301334d6281c1cd6200716be6d01e30b8fd07b6ff7a537587187a649e625a347f24d52eba4812fc3535a077cd53e33a7abb77aeee19ff6662b7f048148f9e21 +"@noble/ciphers@npm:^0.1.4": + version: 0.1.4 + resolution: "@noble/ciphers@npm:0.1.4" + checksum: a846f91dc876ea8cf01c20f04df2816926ad4e4d90169e6334de39b477ce13bf5e720f4df9f9898dd2a87643660ccc8a04aa466baf885c43860c270bcc7deced languageName: node linkType: hard @@ -2684,21 +2707,21 @@ __metadata: languageName: node linkType: hard -"@noble/curves@npm:1.2.0, @noble/curves@npm:~1.2.0": - version: 1.2.0 - resolution: "@noble/curves@npm:1.2.0" +"@noble/curves@npm:^1.0.0, @noble/curves@npm:^1.1.0": + version: 1.1.0 + resolution: "@noble/curves@npm:1.1.0" dependencies: - "@noble/hashes": 1.3.2 - checksum: bb798d7a66d8e43789e93bc3c2ddff91a1e19fdb79a99b86cd98f1e5eff0ee2024a2672902c2576ef3577b6f282f3b5c778bebd55761ddbb30e36bf275e83dd0 + "@noble/hashes": 1.3.1 + checksum: 2658cdd3f84f71079b4e3516c47559d22cf4b55c23ac8ee9d2b1f8e5b72916d9689e59820e0f9d9cb4a46a8423af5b56dc6bb7782405c88be06a015180508db5 languageName: node linkType: hard -"@noble/curves@npm:^1.0.0, @noble/curves@npm:^1.1.0, @noble/curves@npm:^1.2.0": - version: 1.3.0 - resolution: "@noble/curves@npm:1.3.0" +"@noble/curves@npm:^1.2.0": + version: 1.2.0 + resolution: "@noble/curves@npm:1.2.0" dependencies: - "@noble/hashes": 1.3.3 - checksum: b65342ee66c4a440eee2978524412eabba9a9efdd16d6370e15218c6a7d80bddf35e66bb57ed52c0dfd32cb9a717b439ab3a72db618f1a0066dfebe3fd12a421 + "@noble/hashes": 1.3.2 + checksum: bb798d7a66d8e43789e93bc3c2ddff91a1e19fdb79a99b86cd98f1e5eff0ee2024a2672902c2576ef3577b6f282f3b5c778bebd55761ddbb30e36bf275e83dd0 languageName: node linkType: hard @@ -2709,6 +2732,13 @@ __metadata: languageName: node linkType: hard +"@noble/hashes@npm:1.3.1, @noble/hashes@npm:^1.3.1, @noble/hashes@npm:~1.3.0": + version: 1.3.1 + resolution: "@noble/hashes@npm:1.3.1" + checksum: 7fdefc0f7a0c1ec27acc6ff88841793e3f93ec4ce6b8a6a12bfc0dd70ae6b7c4c82fe305fdfeda1735d5ad4a9eebe761e6693b3d355689c559e91242f4bc95b1 + languageName: node + linkType: hard + "@noble/hashes@npm:1.3.2": version: 1.3.2 resolution: "@noble/hashes@npm:1.3.2" @@ -2716,13 +2746,6 @@ __metadata: languageName: node linkType: hard -"@noble/hashes@npm:1.3.3, @noble/hashes@npm:^1.3.1, @noble/hashes@npm:~1.3.0, @noble/hashes@npm:~1.3.2": - version: 1.3.3 - resolution: "@noble/hashes@npm:1.3.3" - checksum: 8a6496d1c0c64797339bc694ad06cdfaa0f9e56cd0c3f68ae3666cfb153a791a55deb0af9c653c7ed2db64d537aa3e3054629740d2f2338bb1dcb7ab60cd205b - languageName: node - linkType: hard - "@nodelib/fs.scandir@npm:2.1.5": version: 2.1.5 resolution: "@nodelib/fs.scandir@npm:2.1.5" @@ -2790,19 +2813,6 @@ __metadata: languageName: node linkType: soft -"@npmcli/agent@npm:^2.0.0": - version: 2.2.0 - resolution: "@npmcli/agent@npm:2.2.0" - dependencies: - agent-base: ^7.1.0 - http-proxy-agent: ^7.0.0 - https-proxy-agent: ^7.0.1 - lru-cache: ^10.0.1 - socks-proxy-agent: ^8.0.1 - checksum: 3b25312edbdfaa4089af28e2d423b6f19838b945e47765b0c8174c1395c79d43c3ad6d23cb364b43f59fd3acb02c93e3b493f72ddbe3dfea04c86843a7311fc4 - languageName: node - linkType: hard - "@npmcli/fs@npm:^3.1.0": version: 3.1.0 resolution: "@npmcli/fs@npm:3.1.0" @@ -2819,9 +2829,96 @@ __metadata: languageName: node linkType: hard -"@puppeteer/browsers@npm:1.9.1": - version: 1.9.1 - resolution: "@puppeteer/browsers@npm:1.9.1" +"@pkgr/utils@npm:^2.3.1": + version: 2.4.2 + resolution: "@pkgr/utils@npm:2.4.2" + dependencies: + cross-spawn: ^7.0.3 + fast-glob: ^3.3.0 + is-glob: ^4.0.3 + open: ^9.1.0 + picocolors: ^1.0.0 + tslib: ^2.6.0 + checksum: 24e04c121269317d259614cd32beea3af38277151c4002df5883c4be920b8e3490bb897748e844f9d46bf68230f86dabd4e8f093773130e7e60529a769a132fc + languageName: node + linkType: hard + +"@protobufjs/aspromise@npm:^1.1.1, @protobufjs/aspromise@npm:^1.1.2": + version: 1.1.2 + resolution: "@protobufjs/aspromise@npm:1.1.2" + checksum: 011fe7ef0826b0fd1a95935a033a3c0fd08483903e1aa8f8b4e0704e3233406abb9ee25350ec0c20bbecb2aad8da0dcea58b392bbd77d6690736f02c143865d2 + languageName: node + linkType: hard + +"@protobufjs/base64@npm:^1.1.2": + version: 1.1.2 + resolution: "@protobufjs/base64@npm:1.1.2" + checksum: 67173ac34de1e242c55da52c2f5bdc65505d82453893f9b51dc74af9fe4c065cf4a657a4538e91b0d4a1a1e0a0642215e31894c31650ff6e3831471061e1ee9e + languageName: node + linkType: hard + +"@protobufjs/codegen@npm:^2.0.4": + version: 2.0.4 + resolution: "@protobufjs/codegen@npm:2.0.4" + checksum: 59240c850b1d3d0b56d8f8098dd04787dcaec5c5bd8de186fa548de86b86076e1c50e80144b90335e705a044edf5bc8b0998548474c2a10a98c7e004a1547e4b + languageName: node + linkType: hard + +"@protobufjs/eventemitter@npm:^1.1.0": + version: 1.1.0 + resolution: "@protobufjs/eventemitter@npm:1.1.0" + checksum: 0369163a3d226851682f855f81413cbf166cd98f131edb94a0f67f79e75342d86e89df9d7a1df08ac28be2bc77e0a7f0200526bb6c2a407abbfee1f0262d5fd7 + languageName: node + linkType: hard + +"@protobufjs/fetch@npm:^1.1.0": + version: 1.1.0 + resolution: "@protobufjs/fetch@npm:1.1.0" + dependencies: + "@protobufjs/aspromise": ^1.1.1 + "@protobufjs/inquire": ^1.1.0 + checksum: 3fce7e09eb3f1171dd55a192066450f65324fd5f7cc01a431df01bb00d0a895e6bfb5b0c5561ce157ee1d886349c90703d10a4e11a1a256418ff591b969b3477 + languageName: node + linkType: hard + +"@protobufjs/float@npm:^1.0.2": + version: 1.0.2 + resolution: "@protobufjs/float@npm:1.0.2" + checksum: 5781e1241270b8bd1591d324ca9e3a3128d2f768077a446187a049e36505e91bc4156ed5ac3159c3ce3d2ba3743dbc757b051b2d723eea9cd367bfd54ab29b2f + languageName: node + linkType: hard + +"@protobufjs/inquire@npm:^1.1.0": + version: 1.1.0 + resolution: "@protobufjs/inquire@npm:1.1.0" + checksum: ca06f02eaf65ca36fb7498fc3492b7fc087bfcc85c702bac5b86fad34b692bdce4990e0ef444c1e2aea8c034227bd1f0484be02810d5d7e931c55445555646f4 + languageName: node + linkType: hard + +"@protobufjs/path@npm:^1.1.2": + version: 1.1.2 + resolution: "@protobufjs/path@npm:1.1.2" + checksum: 856eeb532b16a7aac071cacde5c5620df800db4c80cee6dbc56380524736205aae21e5ae47739114bf669ab5e8ba0e767a282ad894f3b5e124197cb9224445ee + languageName: node + linkType: hard + +"@protobufjs/pool@npm:^1.1.0": + version: 1.1.0 + resolution: "@protobufjs/pool@npm:1.1.0" + checksum: d6a34fbbd24f729e2a10ee915b74e1d77d52214de626b921b2d77288bd8f2386808da2315080f2905761527cceffe7ec34c7647bd21a5ae41a25e8212ff79451 + languageName: node + linkType: hard + +"@protobufjs/utf8@npm:^1.1.0": + version: 1.1.0 + resolution: "@protobufjs/utf8@npm:1.1.0" + checksum: f9bf3163d13aaa3b6f5e6fbf37a116e094ea021c0e1f2a7ccd0e12a29e2ce08dafba4e8b36e13f8ed7397e1591610ce880ed1289af4d66cf4ace8a36a9557278 + languageName: node + linkType: hard + +"@puppeteer/browsers@npm:1.7.1": + version: 1.7.1 + resolution: "@puppeteer/browsers@npm:1.7.1" dependencies: debug: 4.3.4 extract-zip: 2.0.1 @@ -2829,17 +2926,17 @@ __metadata: proxy-agent: 6.3.1 tar-fs: 3.0.4 unbzip2-stream: 1.4.3 - yargs: 17.7.2 + yargs: 17.7.1 bin: browsers: lib/cjs/main-cli.js - checksum: 1ea82e34af882dc6d7e8392a88ec4196e206a7f65743be39c196c7068d66b9bdfa370e28c6ab09946bd2baa2182adbcbf445e79cc9bcc5242f05878ae7045b27 + checksum: fb7cf7773a1aed4e34ce0952dbf9609a164e624d4f8e1f342b816fe3e983888d7a7b2fbafc963559e96cb5bca0d75fb9c81f2097f9b1f5478a0f1cc7cbc12dff languageName: node linkType: hard -"@scure/base@npm:~1.1.0, @scure/base@npm:~1.1.2": - version: 1.1.5 - resolution: "@scure/base@npm:1.1.5" - checksum: 9e9ee6088cb3aa0fb91f5a48497d26682c7829df3019b1251d088d166d7a8c0f941c68aaa8e7b96bbad20c71eb210397cb1099062cde3e29d4bad6b975c18519 +"@scure/base@npm:~1.1.0": + version: 1.1.1 + resolution: "@scure/base@npm:1.1.1" + checksum: b4fc810b492693e7e8d0107313ac74c3646970c198bbe26d7332820886fa4f09441991023ec9aa3a2a51246b74409ab5ebae2e8ef148bbc253da79ac49130309 languageName: node linkType: hard @@ -2854,17 +2951,6 @@ __metadata: languageName: node linkType: hard -"@scure/bip32@npm:1.3.2": - version: 1.3.2 - resolution: "@scure/bip32@npm:1.3.2" - dependencies: - "@noble/curves": ~1.2.0 - "@noble/hashes": ~1.3.2 - "@scure/base": ~1.1.2 - checksum: c5ae84fae43490853693b481531132b89e056d45c945fc8b92b9d032577f753dfd79c5a7bbcbf0a7f035951006ff0311b6cf7a389e26c9ec6335e42b20c53157 - languageName: node - linkType: hard - "@scure/bip39@npm:1.2.0": version: 1.2.0 resolution: "@scure/bip39@npm:1.2.0" @@ -2875,16 +2961,6 @@ __metadata: languageName: node linkType: hard -"@scure/bip39@npm:1.2.1": - version: 1.2.1 - resolution: "@scure/bip39@npm:1.2.1" - dependencies: - "@noble/hashes": ~1.3.0 - "@scure/base": ~1.1.0 - checksum: c5bd6f1328fdbeae2dcdd891825b1610225310e5e62a4942714db51066866e4f7bef242c7b06a1b9dcc8043a4a13412cf5c5df76d3b10aa9e36b82e9b6e3eeaa - languageName: node - linkType: hard - "@sinclair/typebox@npm:^0.27.8": version: 0.27.8 resolution: "@sinclair/typebox@npm:0.27.8" @@ -2893,11 +2969,11 @@ __metadata: linkType: hard "@sinonjs/commons@npm:^3.0.0": - version: 3.0.1 - resolution: "@sinonjs/commons@npm:3.0.1" + version: 3.0.0 + resolution: "@sinonjs/commons@npm:3.0.0" dependencies: type-detect: 4.0.8 - checksum: a7c3e7cc612352f4004873747d9d8b2d4d90b13a6d483f685598c945a70e734e255f1ca5dc49702515533c403b32725defff148177453b3f3915bcb60e9d4601 + checksum: b4b5b73d4df4560fb8c0c7b38c7ad4aeabedd362f3373859d804c988c725889cde33550e4bcc7cd316a30f5152a2d1d43db71b6d0c38f5feef71fd8d016763f8 languageName: node linkType: hard @@ -2910,6 +2986,13 @@ __metadata: languageName: node linkType: hard +"@tootallnate/once@npm:2": + version: 2.0.0 + resolution: "@tootallnate/once@npm:2.0.0" + checksum: ad87447820dd3f24825d2d947ebc03072b20a42bfc96cbafec16bff8bbda6c1a81fcb0be56d5b21968560c5359a0af4038a68ba150c3e1694fe4c109a063bed8 + languageName: node + linkType: hard + "@tootallnate/quickjs-emscripten@npm:^0.23.0": version: 0.23.0 resolution: "@tootallnate/quickjs-emscripten@npm:0.23.0" @@ -2918,12 +3001,12 @@ __metadata: linkType: hard "@trivago/prettier-plugin-sort-imports@npm:^4.1.1": - version: 4.3.0 - resolution: "@trivago/prettier-plugin-sort-imports@npm:4.3.0" + version: 4.2.0 + resolution: "@trivago/prettier-plugin-sort-imports@npm:4.2.0" dependencies: "@babel/generator": 7.17.7 "@babel/parser": ^7.20.5 - "@babel/traverse": 7.23.2 + "@babel/traverse": 7.17.3 "@babel/types": 7.17.0 javascript-natural-sort: 0.7.1 lodash: ^4.17.21 @@ -2933,7 +3016,7 @@ __metadata: peerDependenciesMeta: "@vue/compiler-sfc": optional: true - checksum: 22bb311ca24f09eef25915a66727e7be113b703f196f6ea0589dc9730b11a6f1e5e4bcc468213101d138b570d310792c83abb8d9487c53f9e597942fea052b6e + checksum: 2081ba9f1a2d33b9a3eeadeb3e713d404ee3d1a5cff3b20a23d94d6d915f0a8ff549616c1e77cd728f1b33733e0d7ab8e4c2512f344a612d81ece40025351160 languageName: node linkType: hard @@ -2966,264 +3049,274 @@ __metadata: linkType: hard "@types/abstract-leveldown@npm:*": - version: 7.2.5 - resolution: "@types/abstract-leveldown@npm:7.2.5" - checksum: 3a99b13c81a53a62b42bea9cff326880de3146b4eeff528b039be69a268515b3120a6c12142e96646fcb0a03c463f298998581e86d9ddb29fbea3612f40edb2b + version: 7.2.1 + resolution: "@types/abstract-leveldown@npm:7.2.1" + checksum: 20689e7d144ce26d2384e2e151eed59046c95d573a6988da5e77e3076808eb4f435f474a0387af9ac786bfbfc7089e277dcfd9572ae902553d5c018e9b527a30 languageName: node linkType: hard "@types/accepts@npm:*": - version: 1.3.7 - resolution: "@types/accepts@npm:1.3.7" + version: 1.3.5 + resolution: "@types/accepts@npm:1.3.5" dependencies: "@types/node": "*" - checksum: 7678cf74976e16093aff6e6f9755826faf069ac1e30179276158ce46ea246348ff22ca6bdd46cef08428881337d9ceefbf00bab08a7731646eb9fc9449d6a1e7 + checksum: 590b7580570534a640510c071e09074cf63b5958b237a728f94322567350aea4d239f8a9d897a12b15c856b992ee4d7907e9812bb079886af2c00714e7fb3f60 languageName: node linkType: hard "@types/babel__core@npm:^7.1.14": - version: 7.20.5 - resolution: "@types/babel__core@npm:7.20.5" + version: 7.20.1 + resolution: "@types/babel__core@npm:7.20.1" dependencies: "@babel/parser": ^7.20.7 "@babel/types": ^7.20.7 "@types/babel__generator": "*" "@types/babel__template": "*" "@types/babel__traverse": "*" - checksum: a3226f7930b635ee7a5e72c8d51a357e799d19cbf9d445710fa39ab13804f79ab1a54b72ea7d8e504659c7dfc50675db974b526142c754398d7413aa4bc30845 + checksum: 9fcd9691a33074802d9057ff70b0e3ff3778f52470475b68698a0f6714fbe2ccb36c16b43dc924eb978cd8a81c1f845e5ff4699e7a47606043b539eb8c6331a8 languageName: node linkType: hard "@types/babel__generator@npm:*": - version: 7.6.8 - resolution: "@types/babel__generator@npm:7.6.8" + version: 7.6.4 + resolution: "@types/babel__generator@npm:7.6.4" dependencies: "@babel/types": ^7.0.0 - checksum: 5b332ea336a2efffbdeedb92b6781949b73498606ddd4205462f7d96dafd45ff3618770b41de04c4881e333dd84388bfb8afbdf6f2764cbd98be550d85c6bb48 + checksum: 20effbbb5f8a3a0211e95959d06ae70c097fb6191011b73b38fe86deebefad8e09ee014605e0fd3cdaedc73d158be555866810e9166e1f09e4cfd880b874dcb0 languageName: node linkType: hard "@types/babel__template@npm:*": - version: 7.4.4 - resolution: "@types/babel__template@npm:7.4.4" + version: 7.4.1 + resolution: "@types/babel__template@npm:7.4.1" dependencies: "@babel/parser": ^7.1.0 "@babel/types": ^7.0.0 - checksum: d7a02d2a9b67e822694d8e6a7ddb8f2b71a1d6962dfd266554d2513eefbb205b33ca71a0d163b1caea3981ccf849211f9964d8bd0727124d18ace45aa6c9ae29 + checksum: 649fe8b42c2876be1fd28c6ed9b276f78152d5904ec290b6c861d9ef324206e0a5c242e8305c421ac52ecf6358fa7e32ab7a692f55370484825c1df29b1596ee languageName: node linkType: hard "@types/babel__traverse@npm:*, @types/babel__traverse@npm:^7.0.6": - version: 7.20.5 - resolution: "@types/babel__traverse@npm:7.20.5" + version: 7.20.1 + resolution: "@types/babel__traverse@npm:7.20.1" dependencies: "@babel/types": ^7.20.7 - checksum: 608e0ab4fc31cd47011d98942e6241b34d461608c0c0e153377c5fd822c436c475f1ded76a56bfa76a1adf8d9266b727bbf9bfac90c4cb152c97f30dadc5b7e8 + checksum: 58341e23c649c0eba134a1682d4f20d027fad290d92e5740faa1279978f6ed476fc467ae51ce17a877e2566d805aeac64eae541168994367761ec883a4150221 languageName: node linkType: hard "@types/bn.js@npm:*, @types/bn.js@npm:^5.1.3": - version: 5.1.5 - resolution: "@types/bn.js@npm:5.1.5" + version: 5.1.3 + resolution: "@types/bn.js@npm:5.1.3" dependencies: "@types/node": "*" - checksum: c87b28c4af74545624f8a3dae5294b16aa190c222626e8d4b2e327b33b1a3f1eeb43e7a24d914a9774bca43d8cd6e1cb0325c1f4b3a244af6693a024e1d918e6 + checksum: 6cd144b8192b6655a009021a4f838a725ea3eb4c5e6425ffc5b144788f7612fb09018c2359954edef32ab7db15f7070b77d05499318b6d9824a55cb7e6776620 languageName: node linkType: hard "@types/body-parser@npm:*": - version: 1.19.5 - resolution: "@types/body-parser@npm:1.19.5" + version: 1.19.2 + resolution: "@types/body-parser@npm:1.19.2" dependencies: "@types/connect": "*" "@types/node": "*" - checksum: 1e251118c4b2f61029cc43b0dc028495f2d1957fe8ee49a707fb940f86a9bd2f9754230805598278fe99958b49e9b7e66eec8ef6a50ab5c1f6b93e1ba2aaba82 + checksum: e17840c7d747a549f00aebe72c89313d09fbc4b632b949b2470c5cb3b1cb73863901ae84d9335b567a79ec5efcfb8a28ff8e3f36bc8748a9686756b6d5681f40 languageName: node linkType: hard "@types/connect@npm:*": - version: 3.4.38 - resolution: "@types/connect@npm:3.4.38" + version: 3.4.35 + resolution: "@types/connect@npm:3.4.35" dependencies: "@types/node": "*" - checksum: 7eb1bc5342a9604facd57598a6c62621e244822442976c443efb84ff745246b10d06e8b309b6e80130026a396f19bf6793b7cecd7380169f369dac3bfc46fb99 + checksum: fe81351470f2d3165e8b12ce33542eef89ea893e36dd62e8f7d72566dfb7e448376ae962f9f3ea888547ce8b55a40020ca0e01d637fab5d99567673084542641 languageName: node linkType: hard "@types/content-disposition@npm:*": - version: 0.5.8 - resolution: "@types/content-disposition@npm:0.5.8" - checksum: eeea868fb510ae7a32aa2d7de680fba79d59001f3e758a334621e10bc0a6496d3a42bb79243a5e53b9c63cb524522853ccc144fe1ab160c4247d37cdb81146c4 + version: 0.5.5 + resolution: "@types/content-disposition@npm:0.5.5" + checksum: fdf7379db1d509990bcf9a21d85f05aad878596f28b1418f9179f6436cb22513262c670ce88c6055054a7f5804a9303eeacb70aa59a5e11ffdc1434559db9692 languageName: node linkType: hard -"@types/cookiejar@npm:^2.1.5": - version: 2.1.5 - resolution: "@types/cookiejar@npm:2.1.5" - checksum: 04d5990e87b6387532d15a87d9ec9b2eb783039291193863751dcfd7fc723a3b3aa30ce4c06b03975cba58632e933772f1ff031af23eaa3ac7f94e71afa6e073 +"@types/cookiejar@npm:*": + version: 2.1.2 + resolution: "@types/cookiejar@npm:2.1.2" + checksum: f6e1903454007f86edd6c3520cbb4d553e1d4e17eaf1f77f6f75e3270f48cc828d74397a113a36942f5fe52f9fa71067bcfa738f53ad468fcca0bc52cb1cbd28 languageName: node linkType: hard "@types/cookies@npm:*": - version: 0.7.10 - resolution: "@types/cookies@npm:0.7.10" + version: 0.7.7 + resolution: "@types/cookies@npm:0.7.7" dependencies: "@types/connect": "*" "@types/express": "*" "@types/keygrip": "*" "@types/node": "*" - checksum: 99cd44a193398932ff7926cfaac1eb4441d3dc47c3f64fdfb28861acbeb290b6db6a20376f993defc9d302db92bb1d36189b89ba447a633f960535f3f0d34e2d + checksum: d3759efc1182cb0651808570ae13638677b67b0ea724eef7b174e58ffe6ea044b62c7c2715e532f76f88fce4dd8101ed32ac6fbb73226db654017924e8a2a1e6 languageName: node linkType: hard "@types/debug@npm:^4.1.7": - version: 4.1.12 - resolution: "@types/debug@npm:4.1.12" + version: 4.1.8 + resolution: "@types/debug@npm:4.1.8" dependencies: "@types/ms": "*" - checksum: 47876a852de8240bfdaf7481357af2b88cb660d30c72e73789abf00c499d6bc7cd5e52f41c915d1b9cd8ec9fef5b05688d7b7aef17f7f272c2d04679508d1053 + checksum: a9a9bb40a199e9724aa944e139a7659173a9b274798ea7efbc277cb084bc37d32fc4c00877c3496fac4fed70a23243d284adb75c00b5fdabb38a22154d18e5df languageName: node linkType: hard "@types/detect-node@npm:^2.0.0": - version: 2.0.2 - resolution: "@types/detect-node@npm:2.0.2" - checksum: 064af29e09c5e336174d69b7709510457b1c6704d195a0d1dde9d26091c6cc8aaed39f8e7d329eedbc765655296b5a46db12b50841265a721f5bd4d0b48cbe6f + version: 2.0.0 + resolution: "@types/detect-node@npm:2.0.0" + checksum: f0f5c8ec948f5d4a40944773c8f81460ca7fa08fddf53330166feff1f8e28719bba9a01984872c5823c5de00c8223984381640a41b3c541bc57f3b2d529a0024 languageName: node linkType: hard "@types/elliptic@npm:^6.4.16": - version: 6.4.18 - resolution: "@types/elliptic@npm:6.4.18" + version: 6.4.16 + resolution: "@types/elliptic@npm:6.4.16" dependencies: "@types/bn.js": "*" - checksum: c27613c530fb95441e5e6b456c8c9bc26568ca14c546aae6d7c1d8d46869f79a2272feaef266ac00bdb68b2671e6351ed01b91b82266eac30ca9092720825d16 + checksum: fedecadbab1a469a22bc9f8e44ce730bd945faed82230174c9df4748f29948d34d9d6f7c79122049cd37f048522e28019a470df7a55c86765a82fb0d05f3f415 languageName: node linkType: hard "@types/eslint-scope@npm:^3.7.3": - version: 3.7.7 - resolution: "@types/eslint-scope@npm:3.7.7" + version: 3.7.4 + resolution: "@types/eslint-scope@npm:3.7.4" dependencies: "@types/eslint": "*" "@types/estree": "*" - checksum: e2889a124aaab0b89af1bab5959847c5bec09809209255de0e63b9f54c629a94781daa04adb66bffcdd742f5e25a17614fb933965093c0eea64aacda4309380e + checksum: ea6a9363e92f301cd3888194469f9ec9d0021fe0a397a97a6dd689e7545c75de0bd2153dfb13d3ab532853a278b6572c6f678ce846980669e41029d205653460 languageName: node linkType: hard "@types/eslint@npm:*": - version: 8.56.2 - resolution: "@types/eslint@npm:8.56.2" + version: 8.44.1 + resolution: "@types/eslint@npm:8.44.1" dependencies: "@types/estree": "*" "@types/json-schema": "*" - checksum: 38e054971596f5c0413f66a62dc26b10e0a21ac46ceacb06fbf8cfb838d20820787209b17218b3916e4c23d990ff77cfdb482d655cac0e0d2b837d430fcc5db8 + checksum: 8b45be72d3c22a1ee0b1cc7e7fb0e34e32bbf959e6b7e0e46d160c17894aedf159c1db5c85750f10068884c741eebc37a1cc7ea659de23a8df0c9a3203e2ff9d languageName: node linkType: hard "@types/estree@npm:*, @types/estree@npm:^1.0.0": - version: 1.0.5 - resolution: "@types/estree@npm:1.0.5" - checksum: dd8b5bed28e6213b7acd0fb665a84e693554d850b0df423ac8076cc3ad5823a6bc26b0251d080bdc545af83179ede51dd3f6fa78cad2c46ed1f29624ddf3e41a + version: 1.0.1 + resolution: "@types/estree@npm:1.0.1" + checksum: e9aa175eacb797216fafce4d41e8202c7a75555bc55232dee0f9903d7171f8f19f0ae7d5191bb1a88cb90e65468be508c0df850a9fb81b4433b293a5a749899d languageName: node linkType: hard "@types/express-serve-static-core@npm:^4.17.33": - version: 4.17.41 - resolution: "@types/express-serve-static-core@npm:4.17.41" + version: 4.17.35 + resolution: "@types/express-serve-static-core@npm:4.17.35" dependencies: "@types/node": "*" "@types/qs": "*" "@types/range-parser": "*" "@types/send": "*" - checksum: 12750f6511dd870bbaccfb8208ad1e79361cf197b147f62a3bedc19ec642f3a0f9926ace96705f4bc88ec2ae56f61f7ca8c2438e6b22f5540842b5569c28a121 + checksum: cc8995d10c6feda475ec1b3a0e69eb0f35f21ab6b49129ad5c6f279e0bc5de8175bc04ec51304cb79a43eec3ed2f5a1e01472eb6d5f827b8c35c6ca8ad24eb6e languageName: node linkType: hard "@types/express@npm:*": - version: 4.17.21 - resolution: "@types/express@npm:4.17.21" + version: 4.17.17 + resolution: "@types/express@npm:4.17.17" dependencies: "@types/body-parser": "*" "@types/express-serve-static-core": ^4.17.33 "@types/qs": "*" "@types/serve-static": "*" - checksum: fb238298630370a7392c7abdc80f495ae6c716723e114705d7e3fb67e3850b3859bbfd29391463a3fb8c0b32051847935933d99e719c0478710f8098ee7091c5 + checksum: 0196dacc275ac3ce89d7364885cb08e7fb61f53ca101f65886dbf1daf9b7eb05c0943e2e4bbd01b0cc5e50f37e0eea7e4cbe97d0304094411ac73e1b7998f4da languageName: node linkType: hard -"@types/fs-extra@npm:^11.0.1, @types/fs-extra@npm:^11.0.2": - version: 11.0.4 - resolution: "@types/fs-extra@npm:11.0.4" +"@types/fs-extra@npm:^11.0.1": + version: 11.0.1 + resolution: "@types/fs-extra@npm:11.0.1" dependencies: "@types/jsonfile": "*" "@types/node": "*" - checksum: 242cb84157631f057f76495c8220707541882c00a00195b603d937fb55e471afecebcb089bab50233ed3a59c69fd68bf65c1f69dd7fafe2347e139cc15b9b0e5 + checksum: 3e930346e5d84f419deb8ced1c582beef8cb20d0bd8a0eb145a37d75bab0572a1895f0e48a0d681d386b3a58b9a992b2d2acecc464bcaec2548f53ea00718651 + languageName: node + linkType: hard + +"@types/fs-extra@npm:^11.0.2": + version: 11.0.2 + resolution: "@types/fs-extra@npm:11.0.2" + dependencies: + "@types/jsonfile": "*" + "@types/node": "*" + checksum: 5b3e30343ee62d2e393e1029355f13f64bab6f3416226e22492483f99da840e2e53ca22cbfa4ac3749f2f83f7086d19c009005c8fa175da01df0fae59c2d73e1 languageName: node linkType: hard "@types/graceful-fs@npm:^4.1.3": - version: 4.1.9 - resolution: "@types/graceful-fs@npm:4.1.9" + version: 4.1.6 + resolution: "@types/graceful-fs@npm:4.1.6" dependencies: "@types/node": "*" - checksum: 79d746a8f053954bba36bd3d94a90c78de995d126289d656fb3271dd9f1229d33f678da04d10bce6be440494a5a73438e2e363e92802d16b8315b051036c5256 + checksum: c3070ccdc9ca0f40df747bced1c96c71a61992d6f7c767e8fd24bb6a3c2de26e8b84135ede000b7e79db530a23e7e88dcd9db60eee6395d0f4ce1dae91369dd4 languageName: node linkType: hard "@types/http-assert@npm:*": - version: 1.5.5 - resolution: "@types/http-assert@npm:1.5.5" - checksum: cd6bb7fd42cc6e2a702cb55370b8b25231954ad74c04bcd185b943a74ded3d4c28099c30f77b26951df2426441baff41718816c60b5af80efe2b8888d900bf93 + version: 1.5.3 + resolution: "@types/http-assert@npm:1.5.3" + checksum: 9553e5a0b8bcfdac4b51d3fa3b89a91b5450171861a667a5b4c47204e0f4a1ca865d97396e6ceaf220e87b64d06b7a8bad7bfba15ef97acb41a87507c9940dbc languageName: node linkType: hard "@types/http-errors@npm:*": - version: 2.0.4 - resolution: "@types/http-errors@npm:2.0.4" - checksum: 1f3d7c3b32c7524811a45690881736b3ef741bf9849ae03d32ad1ab7062608454b150a4e7f1351f83d26a418b2d65af9bdc06198f1c079d75578282884c4e8e3 + version: 2.0.1 + resolution: "@types/http-errors@npm:2.0.1" + checksum: 3bb0c50b0a652e679a84c30cd0340d696c32ef6558518268c238840346c077f899315daaf1c26c09c57ddd5dc80510f2a7f46acd52bf949e339e35ed3ee9654f languageName: node linkType: hard "@types/istanbul-lib-coverage@npm:*, @types/istanbul-lib-coverage@npm:^2.0.0, @types/istanbul-lib-coverage@npm:^2.0.1": - version: 2.0.6 - resolution: "@types/istanbul-lib-coverage@npm:2.0.6" - checksum: 3feac423fd3e5449485afac999dcfcb3d44a37c830af898b689fadc65d26526460bedb889db278e0d4d815a670331796494d073a10ee6e3a6526301fe7415778 + version: 2.0.4 + resolution: "@types/istanbul-lib-coverage@npm:2.0.4" + checksum: a25d7589ee65c94d31464c16b72a9dc81dfa0bea9d3e105ae03882d616e2a0712a9c101a599ec482d297c3591e16336962878cb3eb1a0a62d5b76d277a890ce7 languageName: node linkType: hard "@types/istanbul-lib-report@npm:*": - version: 3.0.3 - resolution: "@types/istanbul-lib-report@npm:3.0.3" + version: 3.0.0 + resolution: "@types/istanbul-lib-report@npm:3.0.0" dependencies: "@types/istanbul-lib-coverage": "*" - checksum: b91e9b60f865ff08cb35667a427b70f6c2c63e88105eadd29a112582942af47ed99c60610180aa8dcc22382fa405033f141c119c69b95db78c4c709fbadfeeb4 + checksum: 656398b62dc288e1b5226f8880af98087233cdb90100655c989a09f3052b5775bf98ba58a16c5ae642fb66c61aba402e07a9f2bff1d1569e3b306026c59f3f36 languageName: node linkType: hard "@types/istanbul-reports@npm:^3.0.0": - version: 3.0.4 - resolution: "@types/istanbul-reports@npm:3.0.4" + version: 3.0.1 + resolution: "@types/istanbul-reports@npm:3.0.1" dependencies: "@types/istanbul-lib-report": "*" - checksum: 93eb18835770b3431f68ae9ac1ca91741ab85f7606f310a34b3586b5a34450ec038c3eed7ab19266635499594de52ff73723a54a72a75b9f7d6a956f01edee95 + checksum: f1ad54bc68f37f60b30c7915886b92f86b847033e597f9b34f2415acdbe5ed742fa559a0a40050d74cdba3b6a63c342cac1f3a64dba5b68b66a6941f4abd7903 languageName: node linkType: hard "@types/jest@npm:^29.5.0": - version: 29.5.11 - resolution: "@types/jest@npm:29.5.11" + version: 29.5.3 + resolution: "@types/jest@npm:29.5.3" dependencies: expect: ^29.0.0 pretty-format: ^29.0.0 - checksum: f892a06ec9f0afa9a61cd7fa316ec614e21d4df1ad301b5a837787e046fcb40dfdf7f264a55e813ac6b9b633cb9d366bd5b8d1cea725e84102477b366df23fdd + checksum: e36bb92e0b9e5ea7d6f8832baa42f087fc1697f6cd30ec309a07ea4c268e06ec460f1f0cfd2581daf5eff5763475190ec1ad8ac6520c49ccfe4f5c0a48bfa676 languageName: node linkType: hard "@types/json-schema@npm:*, @types/json-schema@npm:^7.0.12, @types/json-schema@npm:^7.0.8": - version: 7.0.15 - resolution: "@types/json-schema@npm:7.0.15" - checksum: 97ed0cb44d4070aecea772b7b2e2ed971e10c81ec87dd4ecc160322ffa55ff330dace1793489540e3e318d90942064bb697cc0f8989391797792d919737b3b98 + version: 7.0.12 + resolution: "@types/json-schema@npm:7.0.12" + checksum: 00239e97234eeb5ceefb0c1875d98ade6e922bfec39dd365ec6bd360b5c2f825e612ac4f6e5f1d13601b8b30f378f15e6faa805a3a732f4a1bbe61915163d293 languageName: node linkType: hard @@ -3235,46 +3328,46 @@ __metadata: linkType: hard "@types/jsonfile@npm:*": - version: 6.1.4 - resolution: "@types/jsonfile@npm:6.1.4" + version: 6.1.1 + resolution: "@types/jsonfile@npm:6.1.1" dependencies: "@types/node": "*" - checksum: 309fda20eb5f1cf68f2df28931afdf189c5e7e6bec64ac783ce737bb98908d57f6f58757ad5da9be37b815645a6f914e2d4f3ac66c574b8fe1ba6616284d0e97 + checksum: 0f8fe0a9221a00e8413cffba723dfe16553868724b830237256fb0052ecd5cac96498189d1235a001cfa815f352008261c9ceb373f0aa58227f891e0c7a12c4d languageName: node linkType: hard "@types/keygrip@npm:*": - version: 1.0.6 - resolution: "@types/keygrip@npm:1.0.6" - checksum: d157f60bf920492347791d2b26d530d5069ce05796549fbacd4c24d66ffbebbcb0ab67b21e7a1b80a593b9fd4b67dc4843dec04c12bbc2e0fddfb8577a826c41 + version: 1.0.2 + resolution: "@types/keygrip@npm:1.0.2" + checksum: 60bc2738a4f107070ee3d96f44709cb38f3a96c7ccabab09f56c1b2b4d85f869fd8fb9f1f2937e863d0e9e781f005c2223b823bf32b859185b4f52370c352669 languageName: node linkType: hard "@types/koa-bodyparser@npm:^4.3.10": - version: 4.3.12 - resolution: "@types/koa-bodyparser@npm:4.3.12" + version: 4.3.10 + resolution: "@types/koa-bodyparser@npm:4.3.10" dependencies: "@types/koa": "*" - checksum: 645cc253c6b9b2e98252b1cdc75a4812cd6d3c228e426f9893a755324b7a6936559ec659a0ff288cb2642340b3cc4e2110167f24b84efc8e3b89c04fe67ed883 + checksum: 4b4cd176815a6c1fb0d593bfea03de1285e606d3a96e56ad3691144e35061750ed95e4ecf2ff8e25599d360a93646e29dbb167fdfaaa73ccf87ca5b6141ff0db languageName: node linkType: hard "@types/koa-compose@npm:*": - version: 3.2.8 - resolution: "@types/koa-compose@npm:3.2.8" + version: 3.2.5 + resolution: "@types/koa-compose@npm:3.2.5" dependencies: "@types/koa": "*" - checksum: 95c32bdee738ac7c10439bbf6342ca3b9f0aafd7e8118739eac7fb0fa703a23cfe4c88f63e13a69a16fbde702e0bcdc62b272aa734325fc8efa7e5625479752e + checksum: 5d1147c4b057eb158195f442f0384f06503f3e69dba99fb517b30a05261a9f92928945c12bb1cfc17a5b7d60db003f38b455a3a9b125f12e4fc81fffa396b3cf languageName: node linkType: hard "@types/koa-compress@npm:^4.0.3": - version: 4.0.6 - resolution: "@types/koa-compress@npm:4.0.6" + version: 4.0.3 + resolution: "@types/koa-compress@npm:4.0.3" dependencies: "@types/koa": "*" "@types/node": "*" - checksum: 0ec8ffac1bf3c7dc36a9ee4588f83ade0a485b615aff7e9e08082319b1b3f7e2f5954ed5ce4303a7f9ba1b4144081e0700cbf3165d1aef54e5e12130c810b2e4 + checksum: 6f09e4ad8160204fbee9d0a452b83ba62fec503a2eec60cf41fc67a032971027b6858e0b90c6e05bf1ad3b006f7c7a2d02922db4d159d223ab8d33eeeb108757 languageName: node linkType: hard @@ -3288,36 +3381,36 @@ __metadata: linkType: hard "@types/koa-router@npm:^7.4.4": - version: 7.4.8 - resolution: "@types/koa-router@npm:7.4.8" + version: 7.4.4 + resolution: "@types/koa-router@npm:7.4.4" dependencies: "@types/koa": "*" - checksum: 30b9735748f25ac338ec4197430a10f1cf58eeea0445f0b64733ed95df82b9245a31c01bbfdd3c9b71e90aa7a1ccf9546f4dd91bac87f6186152e67146ad9b6c + checksum: 23ff5b725daa1427dc822602f5d4fdcecca5f990595af48879e41338a9c71819ae312326028eef4645beb6ea32ea852416e2f0761a2abd5bf80c2575a3301837 languageName: node linkType: hard "@types/koa-send@npm:*": - version: 4.1.6 - resolution: "@types/koa-send@npm:4.1.6" + version: 4.1.4 + resolution: "@types/koa-send@npm:4.1.4" dependencies: "@types/koa": "*" - checksum: d46d207f1d826ccd74bf3a02180d0475be8456eb3a2244244d19cb3f1737251e163d73958fdcd12111e03c7c0545cc89e7888a6ef2ba370ebf2b2e804efaaaf1 + checksum: 27732c85e97465810bc7631153368daa8e8715bd356eab344d0b3deaec162de09b6cd9d61a524f4d3631493234da7104167e788d593ce27405b70bdfb12fc81b languageName: node linkType: hard "@types/koa-static@npm:^4.0.2": - version: 4.0.4 - resolution: "@types/koa-static@npm:4.0.4" + version: 4.0.2 + resolution: "@types/koa-static@npm:4.0.2" dependencies: "@types/koa": "*" "@types/koa-send": "*" - checksum: 99087a9b6f4214679932008fbed2d4332fca06cd01f2d333439bd1cf0844c313584c8eb6b805360d1c3d6c6c8a475468a5f4f73ecad551c8cc369e290ad41331 + checksum: a9c557a37b25a677f3aae084b2afd267fa78a728cd69aec20821d8acca3ef4bda172d1fd16a23711266d97e77962d037ffd25ee76b24608413032226321f461f languageName: node linkType: hard -"@types/koa@npm:*, @types/koa@npm:^2.13.5, @types/koa@npm:^2.13.6, @types/koa@npm:^2.13.9": - version: 2.14.0 - resolution: "@types/koa@npm:2.14.0" +"@types/koa@npm:*, @types/koa@npm:^2.13.5, @types/koa@npm:^2.13.6": + version: 2.13.8 + resolution: "@types/koa@npm:2.13.8" dependencies: "@types/accepts": "*" "@types/content-disposition": "*" @@ -3327,134 +3420,171 @@ __metadata: "@types/keygrip": "*" "@types/koa-compose": "*" "@types/node": "*" - checksum: 57d809e42350c9ddefa2150306355e40757877468bb027e0bd99f5aeb43cfaf8ba8b14761ea65e419d6fb4c2403a1f3ed0762872a9cf040dbd14357caca56548 + checksum: 76a2a6d219c65f242a43efca42970d864701c58319c346a91dd8c3b4df2021786fd0d600a88dfb098358c9085f9f4a2dfe62563641441cf21e11e2bfe04f4fdf + languageName: node + linkType: hard + +"@types/koa@npm:^2.13.9": + version: 2.13.9 + resolution: "@types/koa@npm:2.13.9" + dependencies: + "@types/accepts": "*" + "@types/content-disposition": "*" + "@types/cookies": "*" + "@types/http-assert": "*" + "@types/http-errors": "*" + "@types/keygrip": "*" + "@types/koa-compose": "*" + "@types/node": "*" + checksum: af9cd599c8e17e2ae0f4168a61d964e343f713d002b65fd995658d7addc6551ccadecfd32b3405cf44e4d360178ee4f972d6881533548261ae1f636a655d24b1 + languageName: node + linkType: hard + +"@types/koa__cors@npm:^4.0.0": + version: 4.0.0 + resolution: "@types/koa__cors@npm:4.0.0" + dependencies: + "@types/koa": "*" + checksum: 0a7f8c2ab9b957befbbe31b9293af05a95282fb984b8468b0c0a0a0101beefe2663ce716ca56fbf4e5ec5ca2d89193ee7d635ee84bb8b5d718df01149286f4d2 languageName: node linkType: hard -"@types/koa__cors@npm:^4.0.0": +"@types/level-errors@npm:*": + version: 3.0.0 + resolution: "@types/level-errors@npm:3.0.0" + checksum: ad9392663439306677ac9cb704f8fa0b64c300dfea4f3494369eb78a2e09c194156cbab2b52c71a361a09b735d54a2de65195dcadba0ec7db1d14a320198133e + languageName: node + linkType: hard + +"@types/leveldown@npm:^4.0.3": version: 4.0.3 - resolution: "@types/koa__cors@npm:4.0.3" + resolution: "@types/leveldown@npm:4.0.3" dependencies: - "@types/koa": "*" - checksum: ca7bfd1ffacf6c425393e2716a88d66dfe1b5e9a32cd92253cc846fa95bd7fd44c1dc848252f13b7febb5bccf23b8e88716b051cfa04d18fa31e0768432dccb7 + "@types/abstract-leveldown": "*" + "@types/node": "*" + checksum: 0a476bd8d3c71266fdb323875d3312bf7828d6ab9f1bf9882a0ff93d04044660e45cd211ca8ec34c5665eaa773f98fe9fee14235792835bd06cb68192fe44669 languageName: node linkType: hard -"@types/level-errors@npm:*": - version: 3.0.2 - resolution: "@types/level-errors@npm:3.0.2" - checksum: 3d9b801f6499f795b60ac723c1b3f93ca105f20ed26966eeb606c804b10c65984c3233fb99914644d75a3223f80f220eca74fda316640a85a5b3d7572cd86925 +"@types/leveldown@npm:^4.0.4": + version: 4.0.4 + resolution: "@types/leveldown@npm:4.0.4" + dependencies: + "@types/abstract-leveldown": "*" + "@types/node": "*" + checksum: 630b2d2d1c48f83d14ab0f6c03ad2af1c427675c3692873c4fd3d673bde4140eabc028ce5736ad3d76aeea20769cf53df6f83468a4f0cf28f6d04dbb435edf48 languageName: node linkType: hard -"@types/leveldown@npm:^4.0.3, @types/leveldown@npm:^4.0.4": - version: 4.0.6 - resolution: "@types/leveldown@npm:4.0.6" +"@types/levelup@npm:^5.1.2": + version: 5.1.2 + resolution: "@types/levelup@npm:5.1.2" dependencies: "@types/abstract-leveldown": "*" + "@types/level-errors": "*" "@types/node": "*" - checksum: 8b06cbc6858f3956fe8e10a8bb58edd75369587e72ce33ab7e35e21e1f1c8e89981a337c977ffd3a635d4441113e434362ba37e343d8a0ec69cd7c8988450977 + checksum: 6740284488b6806ba398bc38842fa789edd5667a342830c544a6b3611ebeed957a08d03dc8bde1e32fe03ac9c439341647c044c1ff0f73a26bcded9ca302a009 languageName: node linkType: hard -"@types/levelup@npm:^5.1.2, @types/levelup@npm:^5.1.3": - version: 5.1.5 - resolution: "@types/levelup@npm:5.1.5" +"@types/levelup@npm:^5.1.3": + version: 5.1.3 + resolution: "@types/levelup@npm:5.1.3" dependencies: "@types/abstract-leveldown": "*" "@types/level-errors": "*" "@types/node": "*" - checksum: 844798bdc805e3c449e478e283eb1196892d3f4fb6b24158faa5f10b283fa785da736917de1ec030ce1b8be9a8c54881cee2354632cb540ef80a325a19d920d1 + checksum: 948642ea481573eec323e5f8b5475c1ab32b5eac0982e5a2a9904ac74336d32fe579d51219410770c1cb88f69f0135fd9be3cbfa5ee6e9b84f81eeddf7ed8e0d languageName: node linkType: hard "@types/lodash.camelcase@npm:^4.3.7": - version: 4.3.9 - resolution: "@types/lodash.camelcase@npm:4.3.9" + version: 4.3.7 + resolution: "@types/lodash.camelcase@npm:4.3.7" dependencies: "@types/lodash": "*" - checksum: f54132d38ffa72b25bce2111e4d28f339599f6d4fcfc1248a89d1d96445512d7a431f0b0e74f6e6c8d6bc09fe53cf94d9426e188d8feacb3ffe04cd9c3a602e7 + checksum: ef068b921ab439f7b1a2c0ebbd890ba64ba437fc6aeb5e3ac18d34da30f9054fceaac5ccfdfa9e12b2fc403e10910439d3ebf92ba2f93de03c2f02261adfc000 languageName: node linkType: hard "@types/lodash.capitalize@npm:^4.2.7": - version: 4.2.9 - resolution: "@types/lodash.capitalize@npm:4.2.9" + version: 4.2.7 + resolution: "@types/lodash.capitalize@npm:4.2.7" dependencies: "@types/lodash": "*" - checksum: 54a9154b2084392986646335d5ed4902a89c24ce675cf8b8cfcd022f6a0eed71c30c2f8cc4b2682cfc5c55a5e1fdad2340609ba58db451dfdd41d82b14c88995 + checksum: dab8b781d7dcc56c18ba0c8286a6ccb61cc598d936a449265453a473e62b2b6d7c109c4447dfeb8ccacc4088769bc3bfd0d39bc8797f03e4e685d4f4b1bc7c01 languageName: node linkType: hard "@types/lodash.chunk@npm:^4.2.7": - version: 4.2.9 - resolution: "@types/lodash.chunk@npm:4.2.9" + version: 4.2.7 + resolution: "@types/lodash.chunk@npm:4.2.7" dependencies: "@types/lodash": "*" - checksum: ccffe7273a0941655d5b988baeffa8f7d4d19a8b43ed728ff4e616013506efe85914ba99d4ec299e0106506e1bca3923b065eabb0aa5f1e4b18f68e790ae6b88 + checksum: 09df5ca00d8866776038bf94658d30b4ea84bffe5f81c14c01568bcb6be692bf59f6ea093de2bb0afbb3f8e54ae96ebd97595f2838ab59b77865427ca77d391e languageName: node linkType: hard "@types/lodash.clonedeep@npm:^4.5.7": - version: 4.5.9 - resolution: "@types/lodash.clonedeep@npm:4.5.9" + version: 4.5.7 + resolution: "@types/lodash.clonedeep@npm:4.5.7" dependencies: "@types/lodash": "*" - checksum: ef85512b7dce7a4f981a818ae44d11982907e1f26b5b26bedf0957c35e8591eb8e1d24fa31ca851d4b40e0a1ee88563853d762412691fe5f357e8335cead2325 + checksum: 20d6a20970b3b54b3c10cf17ace1cea49c4905d7f7cae2575a98108466e8d4c9bea3b3449d11ccaac4da1fc9bab225f477f4c2dbea8ba877cc47f629455efb69 languageName: node linkType: hard "@types/lodash.clonedeepwith@npm:^4.5.7": - version: 4.5.9 - resolution: "@types/lodash.clonedeepwith@npm:4.5.9" + version: 4.5.7 + resolution: "@types/lodash.clonedeepwith@npm:4.5.7" dependencies: "@types/lodash": "*" - checksum: c690fb28126f7248894f08abe13d6c7684dd0a4e9ac545a419a8687438b50d2e6fe32b31176c65a394d3ade4fd16a145ecbf77e7521992414bf657b8b1d936c8 + checksum: 2cfc7c55533abf491a2b6897a233244b187bf55ac51545e4f0b937721fdfb7e82cb3743290aa4dcb41487d653d95fdddc172b39318a893c9a2ca6658b0866430 languageName: node linkType: hard "@types/lodash.every@npm:^4.6.7": - version: 4.6.9 - resolution: "@types/lodash.every@npm:4.6.9" + version: 4.6.7 + resolution: "@types/lodash.every@npm:4.6.7" dependencies: "@types/lodash": "*" - checksum: 9239e078c1aba47ead8d3232d46869a6b9e886387c92c8d9a0a1549b7f21864cfa8428b5725ebbfcd31e5c54761a6fa3d4bbe049a776fe3e973cb10d967fbd0c + checksum: 9bf1475332401948453019a6fc4d9ee1275acfe52dccd2b81746927ef4623f6fb846849a15dfe08ec5b5c50e6302875a19ac469cc820e7f05d7517407ba41dc7 languageName: node linkType: hard "@types/lodash.isequal@npm:^4.5.6": - version: 4.5.8 - resolution: "@types/lodash.isequal@npm:4.5.8" + version: 4.5.6 + resolution: "@types/lodash.isequal@npm:4.5.6" dependencies: "@types/lodash": "*" - checksum: f3180c2d2925514fff1908a1303c11468c9f39b47fd7b053416aad3f1447f8e4a9894dd0460187ac9ac19387e25aec8dd8214d13a50a0967e0dc9cca8e4c5353 + checksum: 0f065989408a9e0584e6c27495be2cd4602e62650f55266aa195812582444463c0c8570c674ae84f947c11748f49ab43fd5b482fa120e08eeee4c23b162edc38 languageName: node linkType: hard "@types/lodash.omit@npm:^4.5.7": - version: 4.5.9 - resolution: "@types/lodash.omit@npm:4.5.9" + version: 4.5.7 + resolution: "@types/lodash.omit@npm:4.5.7" dependencies: "@types/lodash": "*" - checksum: 5be43f3598d6b1fa481fe7046e9e15e2225f547e0e309746c2b87f4e4fa8705e96c564e2762a9312e73e2a223d701ab893b122c506500de49a4b9fb40fb6d17c + checksum: e30600de518e648f4e9590d9238506ceab561726ec2fe56b4ff29b17aa1e47cbf9188be259b7d606dfb2040998c778533ccc0c58f065cca44ab5a6dbc8b6e518 languageName: node linkType: hard "@types/lodash.pick@npm:^4.4.7": - version: 4.4.9 - resolution: "@types/lodash.pick@npm:4.4.9" + version: 4.4.7 + resolution: "@types/lodash.pick@npm:4.4.7" dependencies: "@types/lodash": "*" - checksum: 007c298133b5bc2157f13d6641b139d2782b4af7b6a942cc4b1a427c6ebc6020e46b32ee2e782607389b0c63a5211d51152606424fdfe25f3eeac8afea0bdf02 + checksum: 78428a83b5d85e75bd13fb632030f9adb08dfc5bde3a9b6a302434fe7ac98e62919f87a7337e0ba674017d63bc57a8855ef863f5fcecd25e63b1d39eba2e4697 languageName: node linkType: hard "@types/lodash.startcase@npm:^4.4.7": - version: 4.4.9 - resolution: "@types/lodash.startcase@npm:4.4.9" + version: 4.4.7 + resolution: "@types/lodash.startcase@npm:4.4.7" dependencies: "@types/lodash": "*" - checksum: 448203f0b6d31c1af9fe8292d5417af670bee560bb0af0cac3a6047b90c2d60ba03197367c2defae21e3982c665763197343863ce7d97131efa8e13e6431fe9f + checksum: ee5b903e7cb99a4c747325c38167dea70c10f9929823033f7f2e02aad5e227c84f80c6f7d9d6923a7c0f30a429c5ea4a1b6505bd50a96655b6ab7ac43e8ebe27 languageName: node linkType: hard @@ -3468,159 +3598,180 @@ __metadata: linkType: hard "@types/lodash@npm:*": - version: 4.14.202 - resolution: "@types/lodash@npm:4.14.202" - checksum: a91acf3564a568c6f199912f3eb2c76c99c5a0d7e219394294213b3f2d54f672619f0fde4da22b29dc5d4c31457cd799acc2e5cb6bd90f9af04a1578483b6ff7 + version: 4.14.196 + resolution: "@types/lodash@npm:4.14.196" + checksum: 201d17c3e62ae02a93c99ec78e024b2be9bd75564dd8fd8c26f6ac51a985ab280d28ce2688c3bcdfe785b0991cd9814edff19ee000234c7b45d9a697f09feb6a languageName: node linkType: hard -"@types/memdown@npm:^3.0.0, @types/memdown@npm:^3.0.1, @types/memdown@npm:^3.0.2, @types/memdown@npm:^3.0.3": - version: 3.0.5 - resolution: "@types/memdown@npm:3.0.5" +"@types/memdown@npm:^3.0.0, @types/memdown@npm:^3.0.1": + version: 3.0.1 + resolution: "@types/memdown@npm:3.0.1" dependencies: "@types/abstract-leveldown": "*" - checksum: a0c384858354da754933a83295642ac8710af08e1bf499ff3ae87d86ea34a0d9ebf2bdf75ec45d992cd152580c671c82ea4a0aea03f10eeb3aed33eceb21819e + checksum: 08085fff44f1868d352ec3be81890cfd0034ad1086f3dbc8bbfc412d55434bb6f5bbd512a22a92f2f9c416ccb0784815ecaa0a6fada4478c9a39db3f0f7a1a43 languageName: node linkType: hard -"@types/methods@npm:^1.1.4": - version: 1.1.4 - resolution: "@types/methods@npm:1.1.4" - checksum: ad2a7178486f2fd167750f3eb920ab032a947ff2e26f55c86670a6038632d790b46f52e5b6ead5823f1e53fc68028f1e9ddd15cfead7903e04517c88debd72b1 +"@types/memdown@npm:^3.0.2": + version: 3.0.2 + resolution: "@types/memdown@npm:3.0.2" + dependencies: + "@types/abstract-leveldown": "*" + checksum: bfc36240e32ed6f82b2b858be88aa8814e8c1e0a8922aae8bf44ce07a2c9e0e7bf867e01cab8aad10e1cab2d0bcb0b800b4f63b89d9c791328892acc00683469 + languageName: node + linkType: hard + +"@types/memdown@npm:^3.0.3": + version: 3.0.3 + resolution: "@types/memdown@npm:3.0.3" + dependencies: + "@types/abstract-leveldown": "*" + checksum: 9aa311838574b51e4334878102c80c6abc0e1ec14fe00f98c6ee812f3e6b24db53de632e0f763692730e0147e5c41add0ac4257c3deb3dc7abce176708ed73a4 languageName: node linkType: hard "@types/mime@npm:*": - version: 3.0.4 - resolution: "@types/mime@npm:3.0.4" - checksum: a6139c8e1f705ef2b064d072f6edc01f3c099023ad7c4fce2afc6c2bf0231888202adadbdb48643e8e20da0ce409481a49922e737eca52871b3dc08017455843 + version: 3.0.1 + resolution: "@types/mime@npm:3.0.1" + checksum: 4040fac73fd0cea2460e29b348c1a6173da747f3a87da0dbce80dd7a9355a3d0e51d6d9a401654f3e5550620e3718b5a899b2ec1debf18424e298a2c605346e7 languageName: node linkType: hard "@types/mime@npm:^1": - version: 1.3.5 - resolution: "@types/mime@npm:1.3.5" - checksum: e29a5f9c4776f5229d84e525b7cd7dd960b51c30a0fb9a028c0821790b82fca9f672dab56561e2acd9e8eed51d431bde52eafdfef30f643586c4162f1aecfc78 + version: 1.3.2 + resolution: "@types/mime@npm:1.3.2" + checksum: 0493368244cced1a69cb791b485a260a422e6fcc857782e1178d1e6f219f1b161793e9f87f5fae1b219af0f50bee24fcbe733a18b4be8fdd07a38a8fb91146fd languageName: node linkType: hard "@types/minimist@npm:^1.2.0": - version: 1.2.5 - resolution: "@types/minimist@npm:1.2.5" - checksum: 477047b606005058ab0263c4f58097136268007f320003c348794f74adedc3166ffc47c80ec3e94687787f2ab7f4e72c468223946e79892cf0fd9e25e9970a90 + version: 1.2.2 + resolution: "@types/minimist@npm:1.2.2" + checksum: b8da83c66eb4aac0440e64674b19564d9d86c80ae273144db9681e5eeff66f238ade9515f5006ffbfa955ceff8b89ad2bd8ec577d7caee74ba101431fb07045d languageName: node linkType: hard "@types/ms@npm:*": - version: 0.7.34 - resolution: "@types/ms@npm:0.7.34" - checksum: f38d36e7b6edecd9badc9cf50474159e9da5fa6965a75186cceaf883278611b9df6669dc3a3cc122b7938d317b68a9e3d573d316fcb35d1be47ec9e468c6bd8a + version: 0.7.31 + resolution: "@types/ms@npm:0.7.31" + checksum: daadd354aedde024cce6f5aa873fefe7b71b22cd0e28632a69e8b677aeb48ae8caa1c60e5919bb781df040d116b01cb4316335167a3fc0ef6a63fa3614c0f6da languageName: node linkType: hard -"@types/node@npm:*": - version: 20.11.5 - resolution: "@types/node@npm:20.11.5" - dependencies: - undici-types: ~5.26.4 - checksum: a542727de1334ae20a3ca034b0ecf4b464a57ca01efc4f9cf43bd9ab93896125ab3c2de060ecd8f6ae23b86c6bf3463f681b643e69c032c6a662d376c98a6092 +"@types/node@npm:*, @types/node@npm:>=13.7.0": + version: 20.4.5 + resolution: "@types/node@npm:20.4.5" + checksum: 36a0304a8dc346a1b2d2edac4c4633eecf70875793d61a5274d0df052d7a7af7a8e34f29884eac4fbd094c4f0201477dcb39c0ecd3307ca141688806538d1138 languageName: node linkType: hard "@types/node@npm:^18.14.6, @types/node@npm:^18.15.11, @types/node@npm:^18.15.3, @types/node@npm:^18.7.23": - version: 18.19.8 - resolution: "@types/node@npm:18.19.8" - dependencies: - undici-types: ~5.26.4 - checksum: fa291495d6157a9d9393b4c3bdbf1ce12a8f661dc9da6a4fa19bcdb19af1c62bb8dbf7fb66ae135f29cd788b618e9845b83e9c47edcf39f0953a8561fdacd9a3 + version: 18.17.1 + resolution: "@types/node@npm:18.17.1" + checksum: 56201bda9a2d05d68602df63b4e67b0545ac8c6d0280bd5fb31701350a978a577a027501fbf49db99bf177f2242ebd1244896bfd35e89042d5bd7dfebff28d4e languageName: node linkType: hard "@types/normalize-package-data@npm:^2.4.0": - version: 2.4.4 - resolution: "@types/normalize-package-data@npm:2.4.4" - checksum: 65dff72b543997b7be8b0265eca7ace0e34b75c3e5fee31de11179d08fa7124a7a5587265d53d0409532ecb7f7fba662c2012807963e1f9b059653ec2c83ee05 + version: 2.4.1 + resolution: "@types/normalize-package-data@npm:2.4.1" + checksum: e87bccbf11f95035c89a132b52b79ce69a1e3652fe55962363063c9c0dae0fe2477ebc585e03a9652adc6f381d24ba5589cc5e51849df4ced3d3e004a7d40ed5 languageName: node linkType: hard "@types/pako@npm:^2.0.0": - version: 2.0.3 - resolution: "@types/pako@npm:2.0.3" - checksum: 0746dd5d29eccf5b2e6cceb3ccb093851219e78bd2e2e20d25757e247987139e061e5d4ba37cb5295493f06e3c683c74f8876011cd8a3f3748a09244fbc841d9 + version: 2.0.0 + resolution: "@types/pako@npm:2.0.0" + checksum: 50240a036b5e6acabbf36ac4dca93ec9e619241f0404da8d401cdb427bec3029833324b8a04c4b1ae2ecbc33422fdec31dbf9f43653d9d07cafb82ace78dfccd languageName: node linkType: hard "@types/qs@npm:*": - version: 6.9.11 - resolution: "@types/qs@npm:6.9.11" - checksum: 620ca1628bf3da65662c54ed6ebb120b18a3da477d0bfcc872b696685a9bb1893c3c92b53a1190a8f54d52eaddb6af8b2157755699ac83164604329935e8a7f2 + version: 6.9.7 + resolution: "@types/qs@npm:6.9.7" + checksum: 7fd6f9c25053e9b5bb6bc9f9f76c1d89e6c04f7707a7ba0e44cc01f17ef5284adb82f230f542c2d5557d69407c9a40f0f3515e8319afd14e1e16b5543ac6cdba languageName: node linkType: hard "@types/range-parser@npm:*": - version: 1.2.7 - resolution: "@types/range-parser@npm:1.2.7" - checksum: 95640233b689dfbd85b8c6ee268812a732cf36d5affead89e806fe30da9a430767af8ef2cd661024fd97e19d61f3dec75af2df5e80ec3bea000019ab7028629a + version: 1.2.4 + resolution: "@types/range-parser@npm:1.2.4" + checksum: b7c0dfd5080a989d6c8bb0b6750fc0933d9acabeb476da6fe71d8bdf1ab65e37c136169d84148034802f48378ab94e3c37bb4ef7656b2bec2cb9c0f8d4146a95 + languageName: node + linkType: hard + +"@types/retry@npm:0.12.1": + version: 0.12.1 + resolution: "@types/retry@npm:0.12.1" + checksum: 5f46b2556053655f78262bb33040dc58417c900457cc63ff37d6c35349814471453ef511af0cec76a540c601296cd2b22f64bab1ab649c0dacc0223765ba876c + languageName: node + linkType: hard + +"@types/semver@npm:^7.5.0": + version: 7.5.0 + resolution: "@types/semver@npm:7.5.0" + checksum: 0a64b9b9c7424d9a467658b18dd70d1d781c2d6f033096a6e05762d20ebbad23c1b69b0083b0484722aabf35640b78ccc3de26368bcae1129c87e9df028a22e2 languageName: node linkType: hard -"@types/retry@npm:0.12.2": - version: 0.12.2 - resolution: "@types/retry@npm:0.12.2" - checksum: e5675035717b39ce4f42f339657cae9637cf0c0051cf54314a6a2c44d38d91f6544be9ddc0280587789b6afd056be5d99dbe3e9f4df68c286c36321579b1bf4a +"@types/semver@npm:^7.5.2": + version: 7.5.2 + resolution: "@types/semver@npm:7.5.2" + checksum: 743aa8a2b58e20b329c19bd2459152cb049d12fafab7279b90ac11e0f268c97efbcb606ea0c681cca03f79015381b40d9b1244349b354270bec3f939ed49f6e9 languageName: node linkType: hard -"@types/semver@npm:^7.5.0, @types/semver@npm:^7.5.2, @types/semver@npm:^7.5.4": - version: 7.5.6 - resolution: "@types/semver@npm:7.5.6" - checksum: 563a0120ec0efcc326567db2ed920d5d98346f3638b6324ea6b50222b96f02a8add3c51a916b6897b51523aad8ac227d21d3dcf8913559f1bfc6c15b14d23037 +"@types/semver@npm:^7.5.4": + version: 7.5.4 + resolution: "@types/semver@npm:7.5.4" + checksum: 120c0189f6fec5f2d12d0d71ac8a4cfa952dc17fa3d842e8afddb82bba8828a4052f8799c1653e2b47ae1977435f38e8985658fde971905ce5afb8e23ee97ecf languageName: node linkType: hard "@types/send@npm:*": - version: 0.17.4 - resolution: "@types/send@npm:0.17.4" + version: 0.17.1 + resolution: "@types/send@npm:0.17.1" dependencies: "@types/mime": ^1 "@types/node": "*" - checksum: cf4db48251bbb03cd6452b4de6e8e09e2d75390a92fd798eca4a803df06444adc94ed050246c94c7ed46fb97be1f63607f0e1f13c3ce83d71788b3e08640e5e0 + checksum: 10b620a5960058ef009afbc17686f680d6486277c62f640845381ec4baa0ea683fdd77c3afea4803daf5fcddd3fb2972c8aa32e078939f1d4e96f83195c89793 languageName: node linkType: hard "@types/serve-static@npm:*": - version: 1.15.5 - resolution: "@types/serve-static@npm:1.15.5" + version: 1.15.2 + resolution: "@types/serve-static@npm:1.15.2" dependencies: "@types/http-errors": "*" "@types/mime": "*" "@types/node": "*" - checksum: 0ff4b3703cf20ba89c9f9e345bc38417860a88e85863c8d6fe274a543220ab7f5f647d307c60a71bb57dc9559f0890a661e8dc771a6ec5ef195d91c8afc4a893 + checksum: 15c261dbfc57890f7cc17c04d5b22b418dfa0330c912b46c5d8ae2064da5d6f844ef7f41b63c7f4bbf07675e97ebe6ac804b032635ec742ae45d6f1274259b3e languageName: node linkType: hard "@types/sha256@npm:^0.2.0": - version: 0.2.2 - resolution: "@types/sha256@npm:0.2.2" + version: 0.2.0 + resolution: "@types/sha256@npm:0.2.0" dependencies: "@types/node": "*" - checksum: 7701b9dc105e7b877090c9bb9b02e10953831737b599bfc7658635ae35d2b21927f77028f8090d50ea0281058ee975f190d664e5351c5aaf5535a1c26ba01f1f + checksum: f3c8e0dcaf11d833292b7dd19db567ef41b23036b2fb9ea7335cba100aac8c56e1346171fae6c63885f6c0e1048550506fd165628bc0001902fea010a16d3842 languageName: node linkType: hard -"@types/sinon@npm:^17.0.0": - version: 17.0.3 - resolution: "@types/sinon@npm:17.0.3" +"@types/sinon@npm:^10.0.15": + version: 10.0.16 + resolution: "@types/sinon@npm:10.0.16" dependencies: "@types/sinonjs__fake-timers": "*" - checksum: c8e9956d9c90fe1ec1cc43085ae48897f93f9ea86e909ab47f255ea71f5229651faa070393950fb6923aef426c84e92b375503f9f8886ef44668b82a8ee49e9a + checksum: 1216aac584500d6bf845ca76f57e82f8459cf9de4ed80a55e50aa4438360fc418789a42181e211c5d279e97f86a3a994e3c81e43971d540737caca0193242bbf languageName: node linkType: hard "@types/sinonjs__fake-timers@npm:*": - version: 8.1.5 - resolution: "@types/sinonjs__fake-timers@npm:8.1.5" - checksum: 7e3c08f6c13df44f3ea7d9a5155ddf77e3f7314c156fa1c5a829a4f3763bafe2f75b1283b887f06e6b4296996a2f299b70f64ff82625f9af5885436e2524d10c + version: 8.1.2 + resolution: "@types/sinonjs__fake-timers@npm:8.1.2" + checksum: bbc73a5ab6c0ec974929392f3d6e1e8db4ebad97ec506d785301e1c3d8a4f98a35b1aa95b97035daef02886fd8efd7788a2fa3ced2ec7105988bfd8dce61eedd languageName: node linkType: hard @@ -3634,86 +3785,86 @@ __metadata: linkType: hard "@types/stack-utils@npm:^2.0.0": - version: 2.0.3 - resolution: "@types/stack-utils@npm:2.0.3" - checksum: 72576cc1522090fe497337c2b99d9838e320659ac57fa5560fcbdcbafcf5d0216c6b3a0a8a4ee4fdb3b1f5e3420aa4f6223ab57b82fef3578bec3206425c6cf5 + version: 2.0.1 + resolution: "@types/stack-utils@npm:2.0.1" + checksum: 205fdbe3326b7046d7eaf5e494d8084f2659086a266f3f9cf00bccc549c8e36e407f88168ad4383c8b07099957ad669f75f2532ed4bc70be2b037330f7bae019 languageName: node linkType: hard "@types/superagent@npm:*": - version: 8.1.2 - resolution: "@types/superagent@npm:8.1.2" + version: 4.1.18 + resolution: "@types/superagent@npm:4.1.18" dependencies: - "@types/cookiejar": ^2.1.5 - "@types/methods": ^1.1.4 + "@types/cookiejar": "*" "@types/node": "*" - checksum: 45b4b2db943511938facadc0359877cda5ce62ea6b4b7e3a3e3f5cff8929594cd8eb3b9314885c2befdb6e052bc823f5dcf981d3fe04f556d6ae15fd92db61e9 + checksum: 4e50cb41e6f0ac55917dddae4665e5251ce0ec086f89172c8b53432c0c3ee026b9243ba4c994aa2702720d7c288fd7ae77f241f9fb9fb15d2d7c4b6bc2ee7079 languageName: node linkType: hard "@types/supertest@npm:^2.0.12": - version: 2.0.16 - resolution: "@types/supertest@npm:2.0.16" + version: 2.0.12 + resolution: "@types/supertest@npm:2.0.12" dependencies: "@types/superagent": "*" - checksum: 2fc998ea698e0467cdbe3bea0ebce2027ea3a45a13e51a6cecb0435f44b486faecf99c34d8702d2d7fe033e6e09fdd2b374af52ecc8d0c69a1deec66b8c0dd52 + checksum: f0e2b44f86bec2f708d6a3d0cb209055b487922040773049b0f8c6b557af52d4b5fa904e17dfaa4ce6e610172206bbec7b62420d158fa57b6ffc2de37b1730d3 languageName: node linkType: hard "@types/triple-beam@npm:^1.3.2": - version: 1.3.5 - resolution: "@types/triple-beam@npm:1.3.5" - checksum: 519b6a1b30d4571965c9706ad5400a200b94e4050feca3e7856e3ea7ac00ec9903e32e9a10e2762d0f7e472d5d03e5f4b29c16c0bd8c1f77c8876c683b2231f1 + version: 1.3.2 + resolution: "@types/triple-beam@npm:1.3.2" + checksum: dd7b4a563fb710abc992e5d59eac481bed9e303fada2e276e37b00be31c392e03300ee468e57761e616512872e77935f92472877d0704a19688d15a726cee17b languageName: node linkType: hard "@types/ws@npm:^8.5.4": - version: 8.5.10 - resolution: "@types/ws@npm:8.5.10" + version: 8.5.5 + resolution: "@types/ws@npm:8.5.5" dependencies: "@types/node": "*" - checksum: 3ec416ea2be24042ebd677932a462cf16d2080393d8d7d0b1b3f5d6eaa4a7387aaf0eefb99193c0bfd29444857cf2e0c3ac89899e130550dc6c14ada8a46d25e + checksum: d00bf8070e6938e3ccf933010921c6ce78ac3606696ce37a393b27a9a603f7bd93ea64f3c5fa295a2f743575ba9c9a9fdb904af0f5fe2229bf2adf0630386e4a languageName: node linkType: hard "@types/yargs-parser@npm:*": - version: 21.0.3 - resolution: "@types/yargs-parser@npm:21.0.3" - checksum: ef236c27f9432983e91432d974243e6c4cdae227cb673740320eff32d04d853eed59c92ca6f1142a335cfdc0e17cccafa62e95886a8154ca8891cc2dec4ee6fc + version: 21.0.0 + resolution: "@types/yargs-parser@npm:21.0.0" + checksum: b2f4c8d12ac18a567440379909127cf2cec393daffb73f246d0a25df36ea983b93b7e9e824251f959e9f928cbc7c1aab6728d0a0ff15d6145f66cec2be67d9a2 languageName: node linkType: hard "@types/yargs@npm:^17.0.8": - version: 17.0.32 - resolution: "@types/yargs@npm:17.0.32" + version: 17.0.24 + resolution: "@types/yargs@npm:17.0.24" dependencies: "@types/yargs-parser": "*" - checksum: 4505bdebe8716ff383640c6e928f855b5d337cb3c68c81f7249fc6b983d0aa48de3eee26062b84f37e0d75a5797bc745e0c6e76f42f81771252a758c638f36ba + checksum: 5f3ac4dc4f6e211c1627340160fbe2fd247ceba002190da6cf9155af1798450501d628c9165a183f30a224fc68fa5e700490d740ff4c73e2cdef95bc4e8ba7bf languageName: node linkType: hard "@types/yauzl@npm:^2.9.1": - version: 2.10.3 - resolution: "@types/yauzl@npm:2.10.3" + version: 2.10.0 + resolution: "@types/yauzl@npm:2.10.0" dependencies: "@types/node": "*" - checksum: 5ee966ea7bd6b2802f31ad4281c92c4c0b6dfa593c378a2582c58541fa113bec3d70eb0696b34ad95e8e6861a884cba6c3e351285816693ed176222f840a8c08 + checksum: 55d27ae5d346ea260e40121675c24e112ef0247649073848e5d4e03182713ae4ec8142b98f61a1c6cbe7d3b72fa99bbadb65d8b01873e5e605cdc30f1ff70ef2 languageName: node linkType: hard "@typescript-eslint/eslint-plugin@npm:^6.2.1": - version: 6.19.1 - resolution: "@typescript-eslint/eslint-plugin@npm:6.19.1" + version: 6.2.1 + resolution: "@typescript-eslint/eslint-plugin@npm:6.2.1" dependencies: "@eslint-community/regexpp": ^4.5.1 - "@typescript-eslint/scope-manager": 6.19.1 - "@typescript-eslint/type-utils": 6.19.1 - "@typescript-eslint/utils": 6.19.1 - "@typescript-eslint/visitor-keys": 6.19.1 + "@typescript-eslint/scope-manager": 6.2.1 + "@typescript-eslint/type-utils": 6.2.1 + "@typescript-eslint/utils": 6.2.1 + "@typescript-eslint/visitor-keys": 6.2.1 debug: ^4.3.4 graphemer: ^1.4.0 ignore: ^5.2.4 natural-compare: ^1.4.0 + natural-compare-lite: ^1.4.0 semver: ^7.5.4 ts-api-utils: ^1.0.1 peerDependencies: @@ -3722,44 +3873,44 @@ __metadata: peerDependenciesMeta: typescript: optional: true - checksum: ad04000cd6c15d864ff92655baa3aec99bb0ccf4714fedd145fedde60a27590a5feafe480beb2f0f3864b416098bde1e9431bada7480eb7ca4efad891e1d2f6f + checksum: e73f3fe36519d895037d223f3ddf200b97e17bcde9390984118c38733add1edf996357c809ec2db92cec61bc7c9e5a3d9a583e0d0f92fa9c3919b68716a27b37 languageName: node linkType: hard "@typescript-eslint/parser@npm:^6.2.1": - version: 6.19.1 - resolution: "@typescript-eslint/parser@npm:6.19.1" + version: 6.2.1 + resolution: "@typescript-eslint/parser@npm:6.2.1" dependencies: - "@typescript-eslint/scope-manager": 6.19.1 - "@typescript-eslint/types": 6.19.1 - "@typescript-eslint/typescript-estree": 6.19.1 - "@typescript-eslint/visitor-keys": 6.19.1 + "@typescript-eslint/scope-manager": 6.2.1 + "@typescript-eslint/types": 6.2.1 + "@typescript-eslint/typescript-estree": 6.2.1 + "@typescript-eslint/visitor-keys": 6.2.1 debug: ^4.3.4 peerDependencies: eslint: ^7.0.0 || ^8.0.0 peerDependenciesMeta: typescript: optional: true - checksum: cd29619da08a2d9b7123ba4d8240989c747f8e0d5672179d8b147e413ee1334d1fa48570b0c37cf0ae4e26a275fd2d268cbe702c6fed639d3331abbb3292570a + checksum: cf4768cbfc696ce1d4b15ae55b3d2b52761e91a4a80e738cf3a75c501c2257d735cd6e462567965069d0d693a8cf5463ab9e8b97c36c6ed1fccd3c1c09855bdb languageName: node linkType: hard -"@typescript-eslint/scope-manager@npm:6.19.1": - version: 6.19.1 - resolution: "@typescript-eslint/scope-manager@npm:6.19.1" +"@typescript-eslint/scope-manager@npm:6.2.1": + version: 6.2.1 + resolution: "@typescript-eslint/scope-manager@npm:6.2.1" dependencies: - "@typescript-eslint/types": 6.19.1 - "@typescript-eslint/visitor-keys": 6.19.1 - checksum: 848cdebc16a3803e8a6d6035a7067605309a652bb2425f475f755b5ace4d80d2c17c8c8901f0f4759556da8d0a5b71024d472b85c3f3c70d0e6dcfe2a972ef35 + "@typescript-eslint/types": 6.2.1 + "@typescript-eslint/visitor-keys": 6.2.1 + checksum: 3bb461678c7e729895c5ac16781ec7d66efc6ffa944bb49693ce8e9560f9a6cac70929157c0fc0875b2829ae19a5cdabb97973ddcfb7e81c16e22cdd5d39e3fd languageName: node linkType: hard -"@typescript-eslint/type-utils@npm:6.19.1": - version: 6.19.1 - resolution: "@typescript-eslint/type-utils@npm:6.19.1" +"@typescript-eslint/type-utils@npm:6.2.1": + version: 6.2.1 + resolution: "@typescript-eslint/type-utils@npm:6.2.1" dependencies: - "@typescript-eslint/typescript-estree": 6.19.1 - "@typescript-eslint/utils": 6.19.1 + "@typescript-eslint/typescript-estree": 6.2.1 + "@typescript-eslint/utils": 6.2.1 debug: ^4.3.4 ts-api-utils: ^1.0.1 peerDependencies: @@ -3767,7 +3918,7 @@ __metadata: peerDependenciesMeta: typescript: optional: true - checksum: eab1a30f8d85f7c6e2545de5963fbec2f3bb91913d59623069b4b0db372a671ab048c7018376fc853c3af06ea39417f3e7b27dd665027dd812347a5e64cecd77 + checksum: 7f8d80f03e6ddc1838307a2a4df61dc4bd8400efb9dcc7316063ae293fce54afad238404a0c25cd2cdaceee73ae514f254b850bd7ff11e2def700d5d6b90af05 languageName: node linkType: hard @@ -3785,29 +3936,28 @@ __metadata: languageName: node linkType: hard -"@typescript-eslint/types@npm:6.19.1": - version: 6.19.1 - resolution: "@typescript-eslint/types@npm:6.19.1" - checksum: 598ce222b59c20432d06f60703d0c2dd16d9b2151569c192852136c57b8188e3ef6ef9fddaa2c136c9a756fcc7d873c0e29ec41cfd340564842287ef7b4571cd +"@typescript-eslint/types@npm:6.2.1": + version: 6.2.1 + resolution: "@typescript-eslint/types@npm:6.2.1" + checksum: 388d32f15a9db8ad5d80794caf9ab280d6e5a428efdf4f6a6dfc4069afe4d19da32d628acf638e4c5b92ee77a9a18eecf728a778a3b91cc8a24484af579fc9cf languageName: node linkType: hard -"@typescript-eslint/typescript-estree@npm:6.19.1": - version: 6.19.1 - resolution: "@typescript-eslint/typescript-estree@npm:6.19.1" +"@typescript-eslint/typescript-estree@npm:6.2.1": + version: 6.2.1 + resolution: "@typescript-eslint/typescript-estree@npm:6.2.1" dependencies: - "@typescript-eslint/types": 6.19.1 - "@typescript-eslint/visitor-keys": 6.19.1 + "@typescript-eslint/types": 6.2.1 + "@typescript-eslint/visitor-keys": 6.2.1 debug: ^4.3.4 globby: ^11.1.0 is-glob: ^4.0.3 - minimatch: 9.0.3 semver: ^7.5.4 ts-api-utils: ^1.0.1 peerDependenciesMeta: typescript: optional: true - checksum: fb71a14aeee0468780219c5b8d39075f85d360b04ccd0ee88f4f0a615d2c232a6d3016e36d8c6eda2d9dfda86b4f4cc2c3d7582940fb29d33c7cf305e124d4e2 + checksum: 3d9beeb5e36b8827de5c160ed8e5c111dd66ca00671b183409b051e242b291480679b900bb74aaf4895dcae49497037567d3fcbbe67fa9930786ddd01c685f04 languageName: node linkType: hard @@ -3847,20 +3997,20 @@ __metadata: languageName: node linkType: hard -"@typescript-eslint/utils@npm:6.19.1": - version: 6.19.1 - resolution: "@typescript-eslint/utils@npm:6.19.1" +"@typescript-eslint/utils@npm:6.2.1": + version: 6.2.1 + resolution: "@typescript-eslint/utils@npm:6.2.1" dependencies: "@eslint-community/eslint-utils": ^4.4.0 "@types/json-schema": ^7.0.12 "@types/semver": ^7.5.0 - "@typescript-eslint/scope-manager": 6.19.1 - "@typescript-eslint/types": 6.19.1 - "@typescript-eslint/typescript-estree": 6.19.1 + "@typescript-eslint/scope-manager": 6.2.1 + "@typescript-eslint/types": 6.2.1 + "@typescript-eslint/typescript-estree": 6.2.1 semver: ^7.5.4 peerDependencies: eslint: ^7.0.0 || ^8.0.0 - checksum: fe72e75c3ea17a85772b83f148555ea94ff5d55d13586f3fc038833197a74f8071e14c2bbf1781c40eec20005f052f4be2513a725eea82a15da3cb9af3046c70 + checksum: d16356a633f39d988a9af159da15e28c6a28fa47abce372061c79cf186d193d148e1c32862c9702ff87e2a06f7a2f82773e4b56320a39f432f4b1a989f8005ad languageName: node linkType: hard @@ -3884,20 +4034,13 @@ __metadata: languageName: node linkType: hard -"@typescript-eslint/visitor-keys@npm:6.19.1": - version: 6.19.1 - resolution: "@typescript-eslint/visitor-keys@npm:6.19.1" +"@typescript-eslint/visitor-keys@npm:6.2.1": + version: 6.2.1 + resolution: "@typescript-eslint/visitor-keys@npm:6.2.1" dependencies: - "@typescript-eslint/types": 6.19.1 + "@typescript-eslint/types": 6.2.1 eslint-visitor-keys: ^3.4.1 - checksum: bdf057a42e776970a89cdd568e493e3ea7ec085544d8f318d33084da63c3395ad2c0fb9cef9f61ceeca41f5dab54ab064b7078fe596889005e412ec74d2d1ae4 - languageName: node - linkType: hard - -"@ungap/structured-clone@npm:^1.2.0": - version: 1.2.0 - resolution: "@ungap/structured-clone@npm:1.2.0" - checksum: 4f656b7b4672f2ce6e272f2427d8b0824ed11546a601d8d5412b9d7704e83db38a8d9f402ecdf2b9063fc164af842ad0ec4a55819f621ed7e7ea4d1efcc74524 + checksum: c05a1c45129f2cf9a8c49dadc3da10b675232e59b69dfe9fdc0bfb45d3be077ceff78097baf50e502dab3e71ce9fd799d2015e356a4be2787ee10c6c7a44ea8a languageName: node linkType: hard @@ -4111,10 +4254,10 @@ __metadata: languageName: node linkType: hard -"abbrev@npm:^2.0.0": - version: 2.0.0 - resolution: "abbrev@npm:2.0.0" - checksum: 0e994ad2aa6575f94670d8a2149afe94465de9cedaaaac364e7fb43a40c3691c980ff74899f682f4ca58fa96b4cbd7421a015d3a6defe43a442117d7821a2f36 +"abbrev@npm:^1.0.0": + version: 1.1.1 + resolution: "abbrev@npm:1.1.1" + checksum: a4a97ec07d7ea112c517036882b2ac22f3109b7b19077dc656316d07d308438aac28e4d9746dc4d84bf6b1e75b4a7b0a5f3cb30592419f128ca9a8cee3bcfa17 languageName: node linkType: hard @@ -4133,21 +4276,6 @@ __metadata: languageName: node linkType: hard -"abitype@npm:0.9.8": - version: 0.9.8 - resolution: "abitype@npm:0.9.8" - peerDependencies: - typescript: ">=5.0.4" - zod: ^3 >=3.19.1 - peerDependenciesMeta: - typescript: - optional: true - zod: - optional: true - checksum: d7d887f29d6821e3f7a400de9620511b80ead3f85c5c87308aaec97965d3493e6687ed816e88722b4f512249bd66dee9e69231b49af0e1db8f69400a62c87cf6 - languageName: node - linkType: hard - "abitype@npm:^0.8.11": version: 0.8.11 resolution: "abitype@npm:0.8.11" @@ -4214,18 +4342,27 @@ __metadata: linkType: hard "acorn-walk@npm:^8.1.1": - version: 8.3.2 - resolution: "acorn-walk@npm:8.3.2" - checksum: 3626b9d26a37b1b427796feaa5261faf712307a8920392c8dce9a5739fb31077667f4ad2ec71c7ac6aaf9f61f04a9d3d67ff56f459587206fc04aa31c27ef392 + version: 8.2.0 + resolution: "acorn-walk@npm:8.2.0" + checksum: 1715e76c01dd7b2d4ca472f9c58968516a4899378a63ad5b6c2d668bba8da21a71976c14ec5f5b75f887b6317c4ae0b897ab141c831d741dc76024d8745f1ad1 languageName: node linkType: hard "acorn@npm:^8.4.1, acorn@npm:^8.7.1, acorn@npm:^8.8.2, acorn@npm:^8.9.0": - version: 8.11.3 - resolution: "acorn@npm:8.11.3" + version: 8.10.0 + resolution: "acorn@npm:8.10.0" bin: acorn: bin/acorn - checksum: 76d8e7d559512566b43ab4aadc374f11f563f0a9e21626dd59cb2888444e9445923ae9f3699972767f18af61df89cd89f5eaaf772d1327b055b45cb829b4a88c + checksum: 538ba38af0cc9e5ef983aee196c4b8b4d87c0c94532334fa7e065b2c8a1f85863467bb774231aae91613fcda5e68740c15d97b1967ae3394d20faddddd8af61d + languageName: node + linkType: hard + +"agent-base@npm:6, agent-base@npm:^6.0.2": + version: 6.0.2 + resolution: "agent-base@npm:6.0.2" + dependencies: + debug: 4 + checksum: f52b6872cc96fd5f622071b71ef200e01c7c4c454ee68bc9accca90c98cfb39f2810e3e9aa330435835eedc8c23f4f8a15267f67c6e245d2b33757575bdac49d languageName: node linkType: hard @@ -4238,6 +4375,17 @@ __metadata: languageName: node linkType: hard +"agentkeepalive@npm:^4.2.1": + version: 4.3.0 + resolution: "agentkeepalive@npm:4.3.0" + dependencies: + debug: ^4.1.0 + depd: ^2.0.0 + humanize-ms: ^1.2.1 + checksum: 982453aa44c11a06826c836025e5162c846e1200adb56f2d075400da7d32d87021b3b0a58768d949d824811f5654223d5a8a3dad120921a2439625eb847c6260 + languageName: node + linkType: hard + "aggregate-error@npm:^3.0.0": version: 3.1.0 resolution: "aggregate-error@npm:3.1.0" @@ -4362,6 +4510,23 @@ __metadata: languageName: node linkType: hard +"aproba@npm:^1.0.3 || ^2.0.0": + version: 2.0.0 + resolution: "aproba@npm:2.0.0" + checksum: 5615cadcfb45289eea63f8afd064ab656006361020e1735112e346593856f87435e02d8dcc7ff0d11928bc7d425f27bc7c2a84f6c0b35ab0ff659c814c138a24 + languageName: node + linkType: hard + +"are-we-there-yet@npm:^3.0.0": + version: 3.0.1 + resolution: "are-we-there-yet@npm:3.0.1" + dependencies: + delegates: ^1.0.0 + readable-stream: ^3.6.0 + checksum: 52590c24860fa7173bedeb69a4c05fb573473e860197f618b9a28432ee4379049336727ae3a1f9c4cb083114601c1140cee578376164d0e651217a9843f9fe83 + languageName: node + linkType: hard + "arg@npm:^4.1.0": version: 4.1.3 resolution: "arg@npm:4.1.3" @@ -4402,16 +4567,16 @@ __metadata: languageName: node linkType: hard -"array-includes@npm:^3.1.7": - version: 3.1.7 - resolution: "array-includes@npm:3.1.7" +"array-includes@npm:^3.1.6": + version: 3.1.6 + resolution: "array-includes@npm:3.1.6" dependencies: call-bind: ^1.0.2 - define-properties: ^1.2.0 - es-abstract: ^1.22.1 - get-intrinsic: ^1.2.1 + define-properties: ^1.1.4 + es-abstract: ^1.20.4 + get-intrinsic: ^1.1.3 is-string: ^1.0.7 - checksum: 06f9e4598fac12a919f7c59a3f04f010ea07f0b7f0585465ed12ef528a60e45f374e79d1bddbb34cdd4338357d00023ddbd0ac18b0be36964f5e726e8965d7fc + checksum: f22f8cd8ba8a6448d91eebdc69f04e4e55085d09232b5216ee2d476dab3ef59984e8d1889e662c6a0ed939dcb1b57fd05b2c0209c3370942fc41b752c82a2ca5 languageName: node linkType: hard @@ -4422,55 +4587,54 @@ __metadata: languageName: node linkType: hard -"array.prototype.findlastindex@npm:^1.2.3": - version: 1.2.3 - resolution: "array.prototype.findlastindex@npm:1.2.3" +"array.prototype.findlastindex@npm:^1.2.2": + version: 1.2.2 + resolution: "array.prototype.findlastindex@npm:1.2.2" dependencies: call-bind: ^1.0.2 - define-properties: ^1.2.0 - es-abstract: ^1.22.1 + define-properties: ^1.1.4 + es-abstract: ^1.20.4 es-shim-unscopables: ^1.0.0 - get-intrinsic: ^1.2.1 - checksum: 31f35d7b370c84db56484618132041a9af401b338f51899c2e78ef7690fbba5909ee7ca3c59a7192085b328cc0c68c6fd1f6d1553db01a689a589ae510f3966e + get-intrinsic: ^1.1.3 + checksum: 8a166359f69a2a751c843f26b9c8cd03d0dc396a92cdcb85f4126b5f1cecdae5b2c0c616a71ea8aff026bde68165b44950b3664404bb73db0673e288495ba264 languageName: node linkType: hard -"array.prototype.flat@npm:^1.3.2": - version: 1.3.2 - resolution: "array.prototype.flat@npm:1.3.2" +"array.prototype.flat@npm:^1.3.1": + version: 1.3.1 + resolution: "array.prototype.flat@npm:1.3.1" dependencies: call-bind: ^1.0.2 - define-properties: ^1.2.0 - es-abstract: ^1.22.1 + define-properties: ^1.1.4 + es-abstract: ^1.20.4 es-shim-unscopables: ^1.0.0 - checksum: 5d6b4bf102065fb3f43764bfff6feb3295d372ce89591e6005df3d0ce388527a9f03c909af6f2a973969a4d178ab232ffc9236654149173e0e187ec3a1a6b87b + checksum: 5a8415949df79bf6e01afd7e8839bbde5a3581300e8ad5d8449dea52639e9e59b26a467665622783697917b43bf39940a6e621877c7dd9b3d1c1f97484b9b88b languageName: node linkType: hard -"array.prototype.flatmap@npm:^1.3.2": - version: 1.3.2 - resolution: "array.prototype.flatmap@npm:1.3.2" +"array.prototype.flatmap@npm:^1.3.1": + version: 1.3.1 + resolution: "array.prototype.flatmap@npm:1.3.1" dependencies: call-bind: ^1.0.2 - define-properties: ^1.2.0 - es-abstract: ^1.22.1 + define-properties: ^1.1.4 + es-abstract: ^1.20.4 es-shim-unscopables: ^1.0.0 - checksum: ce09fe21dc0bcd4f30271f8144083aa8c13d4639074d6c8dc82054b847c7fc9a0c97f857491f4da19d4003e507172a78f4bcd12903098adac8b9cd374f734be3 + checksum: 8c1c43a4995f12cf12523436da28515184c753807b3f0bc2ca6c075f71c470b099e2090cc67dba8e5280958fea401c1d0c59e1db0143272aef6cd1103921a987 languageName: node linkType: hard -"arraybuffer.prototype.slice@npm:^1.0.2": - version: 1.0.2 - resolution: "arraybuffer.prototype.slice@npm:1.0.2" +"arraybuffer.prototype.slice@npm:^1.0.1": + version: 1.0.1 + resolution: "arraybuffer.prototype.slice@npm:1.0.1" dependencies: array-buffer-byte-length: ^1.0.0 call-bind: ^1.0.2 define-properties: ^1.2.0 - es-abstract: ^1.22.1 get-intrinsic: ^1.2.1 is-array-buffer: ^3.0.2 is-shared-array-buffer: ^1.0.2 - checksum: c200faf437786f5b2c80d4564ff5481c886a16dee642ef02abdc7306c7edd523d1f01d1dd12b769c7eb42ac9bc53874510db19a92a2c035c0f6696172aafa5d3 + checksum: e3e9b2a3e988ebfeddce4c7e8f69df730c9e48cb04b0d40ff0874ce3d86b3d1339dd520ffde5e39c02610bc172ecfbd4bc93324b1cabd9554c44a56b131ce0ce languageName: node linkType: hard @@ -4531,9 +4695,9 @@ __metadata: linkType: hard "async@npm:^3.2.3": - version: 3.2.5 - resolution: "async@npm:3.2.5" - checksum: 5ec77f1312301dee02d62140a6b1f7ee0edd2a0f983b6fd2b0849b969f245225b990b47b8243e7b9ad16451a53e7f68e753700385b706198ced888beedba3af4 + version: 3.2.4 + resolution: "async@npm:3.2.4" + checksum: 43d07459a4e1d09b84a20772414aa684ff4de085cbcaec6eea3c7a8f8150e8c62aa6cd4e699fe8ee93c3a5b324e777d34642531875a0817a35697522c1b02e89 languageName: node linkType: hard @@ -4558,20 +4722,20 @@ __metadata: languageName: node linkType: hard -"babel-jest@npm:^29.7.0": - version: 29.7.0 - resolution: "babel-jest@npm:29.7.0" +"babel-jest@npm:^29.6.2": + version: 29.6.2 + resolution: "babel-jest@npm:29.6.2" dependencies: - "@jest/transform": ^29.7.0 + "@jest/transform": ^29.6.2 "@types/babel__core": ^7.1.14 babel-plugin-istanbul: ^6.1.1 - babel-preset-jest: ^29.6.3 + babel-preset-jest: ^29.5.0 chalk: ^4.0.0 graceful-fs: ^4.2.9 slash: ^3.0.0 peerDependencies: "@babel/core": ^7.8.0 - checksum: ee6f8e0495afee07cac5e4ee167be705c711a8cc8a737e05a587a131fdae2b3c8f9aa55dfd4d9c03009ac2d27f2de63d8ba96d3e8460da4d00e8af19ef9a83f7 + checksum: 3936b5d6ed6f08670c830ed919e38a4a593d0643b8e30fdeb16f4588b262ea5255fb96fd1849c02fba0b082ecfa4e788ce9a128ad1b9e654d46aac09c3a55504 languageName: node linkType: hard @@ -4588,15 +4752,15 @@ __metadata: languageName: node linkType: hard -"babel-plugin-jest-hoist@npm:^29.6.3": - version: 29.6.3 - resolution: "babel-plugin-jest-hoist@npm:29.6.3" +"babel-plugin-jest-hoist@npm:^29.5.0": + version: 29.5.0 + resolution: "babel-plugin-jest-hoist@npm:29.5.0" dependencies: "@babel/template": ^7.3.3 "@babel/types": ^7.3.3 "@types/babel__core": ^7.1.14 "@types/babel__traverse": ^7.0.6 - checksum: 51250f22815a7318f17214a9d44650ba89551e6d4f47a2dc259128428324b52f5a73979d010cefd921fd5a720d8c1d55ad74ff601cd94c7bd44d5f6292fde2d1 + checksum: 099b5254073b6bc985b6d2d045ad26fb8ed30ff8ae6404c4fe8ee7cd0e98a820f69e3dfb871c7c65aae0f4b65af77046244c07bb92d49ef9005c90eedf681539 languageName: node linkType: hard @@ -4622,15 +4786,15 @@ __metadata: languageName: node linkType: hard -"babel-preset-jest@npm:^29.6.3": - version: 29.6.3 - resolution: "babel-preset-jest@npm:29.6.3" +"babel-preset-jest@npm:^29.5.0": + version: 29.5.0 + resolution: "babel-preset-jest@npm:29.5.0" dependencies: - babel-plugin-jest-hoist: ^29.6.3 + babel-plugin-jest-hoist: ^29.5.0 babel-preset-current-node-syntax: ^1.0.0 peerDependencies: "@babel/core": ^7.0.0 - checksum: aa4ff2a8a728d9d698ed521e3461a109a1e66202b13d3494e41eea30729a5e7cc03b3a2d56c594423a135429c37bf63a9fa8b0b9ce275298be3095a88c69f6fb + checksum: 5566ca2762766c9319b4973d018d2fa08c0fcf6415c72cc54f4c8e7199e851ea8f5e6c6730f03ed7ed44fc8beefa959dd15911f2647dee47c615ff4faeddb1ad languageName: node linkType: hard @@ -4649,9 +4813,9 @@ __metadata: linkType: hard "basic-ftp@npm:^5.0.2": - version: 5.0.4 - resolution: "basic-ftp@npm:5.0.4" - checksum: 57725f24debd8c1b36f9bad1bfee39c5d9f5997f32a23e5c957389dcc64373a13b41711e5723b4a3b616a93530b345686119f480c27a115b2fde944c1652ceb1 + version: 5.0.3 + resolution: "basic-ftp@npm:5.0.3" + checksum: 8b04e88eb85a64de9311721bb0707c9cd70453eefdd854cab85438e6f46fb6c597ddad57ed1acf0a9ede3c677b14e657f51051688a5f23d6f3ea7b5d9073b850 languageName: node linkType: hard @@ -4665,6 +4829,13 @@ __metadata: languageName: node linkType: hard +"big-integer@npm:^1.6.44": + version: 1.6.51 + resolution: "big-integer@npm:1.6.51" + checksum: 3d444173d1b2e20747e2c175568bedeebd8315b0637ea95d75fd27830d3b8e8ba36c6af40374f36bdaea7b5de376dcada1b07587cb2a79a928fccdb6e6e3c518 + languageName: node + linkType: hard + "bl@npm:^4.1.0": version: 4.1.0 resolution: "bl@npm:4.1.0" @@ -4683,13 +4854,22 @@ __metadata: languageName: node linkType: hard -"bn.js@npm:^5.0.0, bn.js@npm:^5.2.1": +"bn.js@npm:^5.0.0, bn.js@npm:^5.1.1, bn.js@npm:^5.2.1": version: 5.2.1 resolution: "bn.js@npm:5.2.1" checksum: 3dd8c8d38055fedfa95c1d5fc3c99f8dd547b36287b37768db0abab3c239711f88ff58d18d155dd8ad902b0b0cee973747b7ae20ea12a09473272b0201c9edd3 languageName: node linkType: hard +"bplist-parser@npm:^0.2.0": + version: 0.2.0 + resolution: "bplist-parser@npm:0.2.0" + dependencies: + big-integer: ^1.6.44 + checksum: d5339dd16afc51de6c88f88f58a45b72ed6a06aa31f5557d09877575f220b7c1d3fbe375da0b62e6a10d4b8ed80523567e351f24014f5bc886ad523758142cdd + languageName: node + linkType: hard + "brace-expansion@npm:^1.1.7": version: 1.1.11 resolution: "brace-expansion@npm:1.1.11" @@ -4762,7 +4942,7 @@ __metadata: languageName: node linkType: hard -"browserify-rsa@npm:^4.0.0, browserify-rsa@npm:^4.1.0": +"browserify-rsa@npm:^4.0.0, browserify-rsa@npm:^4.0.1": version: 4.1.0 resolution: "browserify-rsa@npm:4.1.0" dependencies: @@ -4773,33 +4953,33 @@ __metadata: linkType: hard "browserify-sign@npm:^4.0.0": - version: 4.2.2 - resolution: "browserify-sign@npm:4.2.2" + version: 4.2.1 + resolution: "browserify-sign@npm:4.2.1" dependencies: - bn.js: ^5.2.1 - browserify-rsa: ^4.1.0 + bn.js: ^5.1.1 + browserify-rsa: ^4.0.1 create-hash: ^1.2.0 create-hmac: ^1.1.7 - elliptic: ^6.5.4 + elliptic: ^6.5.3 inherits: ^2.0.4 - parse-asn1: ^5.1.6 - readable-stream: ^3.6.2 - safe-buffer: ^5.2.1 - checksum: b622730c0fc183328c3a1c9fdaaaa5118821ed6822b266fa6b0375db7e20061ebec87301d61931d79b9da9a96ada1cab317fce3c68f233e5e93ed02dbb35544c + parse-asn1: ^5.1.5 + readable-stream: ^3.6.0 + safe-buffer: ^5.2.0 + checksum: 0221f190e3f5b2d40183fa51621be7e838d9caa329fe1ba773406b7637855f37b30f5d83e52ff8f244ed12ffe6278dd9983638609ed88c841ce547e603855707 languageName: node linkType: hard -"browserslist@npm:^4.14.5, browserslist@npm:^4.22.2": - version: 4.22.2 - resolution: "browserslist@npm:4.22.2" +"browserslist@npm:^4.14.5, browserslist@npm:^4.21.9": + version: 4.21.10 + resolution: "browserslist@npm:4.21.10" dependencies: - caniuse-lite: ^1.0.30001565 - electron-to-chromium: ^1.4.601 - node-releases: ^2.0.14 - update-browserslist-db: ^1.0.13 + caniuse-lite: ^1.0.30001517 + electron-to-chromium: ^1.4.477 + node-releases: ^2.0.13 + update-browserslist-db: ^1.0.11 bin: browserslist: cli.js - checksum: 33ddfcd9145220099a7a1ac533cecfe5b7548ffeb29b313e1b57be6459000a1f8fa67e781cf4abee97268ac594d44134fcc4a6b2b4750ceddc9796e3a22076d9 + checksum: 1e27c0f111a35d1dd0e8fc2c61781b0daefabc2c9471b0b10537ce54843014bceb2a1ce4571af1a82b2bf1e6e6e05d38865916689a158f03bc2c7a4ec2577db8 languageName: node linkType: hard @@ -4862,6 +5042,33 @@ __metadata: languageName: node linkType: hard +"bundle-name@npm:^3.0.0": + version: 3.0.0 + resolution: "bundle-name@npm:3.0.0" + dependencies: + run-applescript: ^5.0.0 + checksum: edf2b1fbe6096ed32e7566947ace2ea937ee427391744d7510a2880c4b9a5b3543d3f6c551236a29e5c87d3195f8e2912516290e638c15bcbede7b37cc375615 + languageName: node + linkType: hard + +"busboy@npm:^1.6.0": + version: 1.6.0 + resolution: "busboy@npm:1.6.0" + dependencies: + streamsearch: ^1.1.0 + checksum: 32801e2c0164e12106bf236291a00795c3c4e4b709ae02132883fe8478ba2ae23743b11c5735a0aae8afe65ac4b6ca4568b91f0d9fed1fdbc32ede824a73746e + languageName: node + linkType: hard + +"byte-access@npm:^1.0.0, byte-access@npm:^1.0.1": + version: 1.0.1 + resolution: "byte-access@npm:1.0.1" + dependencies: + uint8arraylist: ^2.0.0 + checksum: 12d5350d9fa6108da80844f5b8c6c80f54bc037cc90f914d81e3a27e374d6c5aac1ba875a8dc2da6b00c6d65c1726bde83a7d57914746cd6d52ff9939a5b21fa + languageName: node + linkType: hard + "bytes@npm:3.1.2, bytes@npm:^3.1.2": version: 3.1.2 resolution: "bytes@npm:3.1.2" @@ -4869,23 +5076,23 @@ __metadata: languageName: node linkType: hard -"cacache@npm:^18.0.0": - version: 18.0.2 - resolution: "cacache@npm:18.0.2" +"cacache@npm:^17.0.0": + version: 17.1.3 + resolution: "cacache@npm:17.1.3" dependencies: "@npmcli/fs": ^3.1.0 fs-minipass: ^3.0.0 glob: ^10.2.2 - lru-cache: ^10.0.1 - minipass: ^7.0.3 - minipass-collect: ^2.0.1 + lru-cache: ^7.7.1 + minipass: ^5.0.0 + minipass-collect: ^1.0.2 minipass-flush: ^1.0.5 minipass-pipeline: ^1.2.4 p-map: ^4.0.0 ssri: ^10.0.0 tar: ^6.1.11 unique-filename: ^3.0.0 - checksum: 0250df80e1ad0c828c956744850c5f742c24244e9deb5b7dc81bca90f8c10e011e132ecc58b64497cc1cad9a98968676147fb6575f4f94722f7619757b17a11b + checksum: 385756781e1e21af089160d89d7462b7ed9883c978e848c7075b90b73cb823680e66092d61513050164588387d2ca87dd6d910e28d64bc13a9ac82cd8580c796 languageName: node linkType: hard @@ -4899,14 +5106,13 @@ __metadata: languageName: node linkType: hard -"call-bind@npm:^1.0.0, call-bind@npm:^1.0.2, call-bind@npm:^1.0.4, call-bind@npm:^1.0.5": - version: 1.0.5 - resolution: "call-bind@npm:1.0.5" +"call-bind@npm:^1.0.0, call-bind@npm:^1.0.2": + version: 1.0.2 + resolution: "call-bind@npm:1.0.2" dependencies: - function-bind: ^1.1.2 - get-intrinsic: ^1.2.1 - set-function-length: ^1.1.1 - checksum: 449e83ecbd4ba48e7eaac5af26fea3b50f8f6072202c2dd7c5a6e7a6308f2421abe5e13a3bbd55221087f76320c5e09f25a8fdad1bab2b77c68ae74d92234ea5 + function-bind: ^1.1.1 + get-intrinsic: ^1.0.2 + checksum: f8e31de9d19988a4b80f3e704788c4a2d6b6f3d17cfec4f57dc29ced450c53a49270dc66bf0fbd693329ee948dd33e6c90a329519aef17474a4d961e8d6426b0 languageName: node linkType: hard @@ -4942,10 +5148,10 @@ __metadata: languageName: node linkType: hard -"caniuse-lite@npm:^1.0.30001565": - version: 1.0.30001579 - resolution: "caniuse-lite@npm:1.0.30001579" - checksum: 7539dcff74d2243a30c428393dc690c87fa34d7da36434731853e9bcfe783757763b2971f5cc878e25242a93e184e53f167d11bd74955af956579f7af71cc764 +"caniuse-lite@npm:^1.0.30001517": + version: 1.0.30001518 + resolution: "caniuse-lite@npm:1.0.30001518" + checksum: 1b63272f6e3d628ac52e2547e0b75fc477004d4b19b63e34b2c045de7f2e48909f9ea513978fc5a46c4ab5ac6c9daf9cc5e6a78466e90684fb824c3f2105e8f5 languageName: node linkType: hard @@ -4956,7 +5162,7 @@ __metadata: languageName: node linkType: hard -"chalk@npm:^2.4.2": +"chalk@npm:^2.0.0": version: 2.4.2 resolution: "chalk@npm:2.4.2" dependencies: @@ -4998,22 +5204,22 @@ __metadata: languageName: node linkType: hard -"chromium-bidi@npm:0.5.2": - version: 0.5.2 - resolution: "chromium-bidi@npm:0.5.2" +"chromium-bidi@npm:0.4.28": + version: 0.4.28 + resolution: "chromium-bidi@npm:0.4.28" dependencies: mitt: 3.0.1 urlpattern-polyfill: 9.0.0 peerDependencies: devtools-protocol: "*" - checksum: baf9ff25065c73264d84bc5c28e81c27ef9c7fca0d38dfbb0f5d6a0446f3687e1988871e031a9c0f62b81f3173bde590a80301831beb60879500374bff0186b2 + checksum: d8ac0aefcf11ebd744e0b97ecded9dac5c03ab55f46267570cdf1b780ad1e05e8cf6987b65178bda99a1ef1ea1bc59721bda85008283ca5f145912b9e1bf578d languageName: node linkType: hard "ci-info@npm:^3.2.0": - version: 3.9.0 - resolution: "ci-info@npm:3.9.0" - checksum: 6b19dc9b2966d1f8c2041a838217299718f15d6c4b63ae36e4674edd2bee48f780e94761286a56aa59eb305a85fbea4ddffb7630ec063e7ec7e7e5ad42549a87 + version: 3.8.0 + resolution: "ci-info@npm:3.8.0" + checksum: d0a4d3160497cae54294974a7246202244fff031b0a6ea20dd57b10ec510aa17399c41a1b0982142c105f3255aff2173e5c0dd7302ee1b2f28ba3debda375098 languageName: node linkType: hard @@ -5051,9 +5257,9 @@ __metadata: linkType: hard "cli-spinners@npm:^2.5.0": - version: 2.9.2 - resolution: "cli-spinners@npm:2.9.2" - checksum: 1bd588289b28432e4676cb5d40505cfe3e53f2e4e10fbe05c8a710a154d6fe0ce7836844b00d6858f740f2ffe67cdc36e0fce9c7b6a8430e80e6388d5aa4956c + version: 2.9.1 + resolution: "cli-spinners@npm:2.9.1" + checksum: 1780618be58309c469205bc315db697934bac68bce78cd5dfd46248e507a533172d623c7348ecfd904734f597ce0a4e5538684843d2cfb7af485d4466699940c languageName: node linkType: hard @@ -5154,6 +5360,15 @@ __metadata: languageName: node linkType: hard +"color-support@npm:^1.1.3": + version: 1.1.3 + resolution: "color-support@npm:1.1.3" + bin: + color-support: bin.js + checksum: 9b7356817670b9a13a26ca5af1c21615463b500783b739b7634a0c2047c16cef4b2865d7576875c31c3cddf9dd621fa19285e628f20198b233a5cfdda6d0793b + languageName: node + linkType: hard + "color@npm:^3.1.3": version: 3.2.1 resolution: "color@npm:3.2.1" @@ -5252,9 +5467,9 @@ __metadata: linkType: hard "component-emitter@npm:^1.3.0": - version: 1.3.1 - resolution: "component-emitter@npm:1.3.1" - checksum: 94550aa462c7bd5a61c1bc480e28554aa306066930152d1b1844a0dd3845d4e5db7e261ddec62ae184913b3e59b55a2ad84093b9d3596a8f17c341514d6c483d + version: 1.3.0 + resolution: "component-emitter@npm:1.3.0" + checksum: b3c46de38ffd35c57d1c02488355be9f218e582aec72d72d1b8bbec95a3ac1b38c96cd6e03ff015577e68f550fbb361a3bfdbd9bb248be9390b7b3745691be6b languageName: node linkType: hard @@ -5295,8 +5510,8 @@ __metadata: linkType: hard "concurrently@npm:^8.0.1": - version: 8.2.2 - resolution: "concurrently@npm:8.2.2" + version: 8.2.0 + resolution: "concurrently@npm:8.2.0" dependencies: chalk: ^4.1.2 date-fns: ^2.30.0 @@ -5310,7 +5525,14 @@ __metadata: bin: conc: dist/bin/concurrently.js concurrently: dist/bin/concurrently.js - checksum: 8ac774df06869773438f1bf91025180c52d5b53139bc86cf47659136c0d97461d0579c515d848d1e945d4e3e0cafe646b2ea18af8d74259b46abddcfe39b2c6c + checksum: eafe6a4d9b7fda87f55ea285cfc6acd937a5286ceec8991ab48e6cc27c45fce6a5c6f45e18d7555defa15dc7d7e8941bc5a9d1ceaf182e31441d420e00333434 + languageName: node + linkType: hard + +"console-control-strings@npm:^1.1.0": + version: 1.1.0 + resolution: "console-control-strings@npm:1.1.0" + checksum: 8755d76787f94e6cf79ce4666f0c5519906d7f5b02d4b884cf41e11dcd759ed69c57da0670afd9236d229a46e0f9cf519db0cd829c6dca820bb5a5c3def584ed languageName: node linkType: hard @@ -5337,6 +5559,13 @@ __metadata: languageName: node linkType: hard +"convert-source-map@npm:^1.6.0, convert-source-map@npm:^1.7.0": + version: 1.9.0 + resolution: "convert-source-map@npm:1.9.0" + checksum: dc55a1f28ddd0e9485ef13565f8f756b342f9a46c4ae18b843fe3c30c675d058d6a4823eff86d472f187b176f0adf51ea7b69ea38be34be4a63cbbf91b0593c8 + languageName: node + linkType: hard + "convert-source-map@npm:^2.0.0": version: 2.0.0 resolution: "convert-source-map@npm:2.0.0" @@ -5358,13 +5587,13 @@ __metadata: languageName: node linkType: hard -"cookies@npm:~0.9.0": - version: 0.9.1 - resolution: "cookies@npm:0.9.1" +"cookies@npm:~0.8.0": + version: 0.8.0 + resolution: "cookies@npm:0.8.0" dependencies: depd: ~2.0.0 keygrip: ~1.1.0 - checksum: 213e4d14847b582fbd8a003203d3621a4b9fa792a315c37954e89332d38fac5bcc34ba92ef316ad6d5fe28f0187aaa115927fbbe2080744ad1707a93b4313247 + checksum: 806055a44f128705265b1bc6a853058da18bf80dea3654ad99be20985b1fa1b14f86c1eef73644aab8071241f8a78acd57202b54c4c5c70769fc694fbb9c4edc languageName: node linkType: hard @@ -5436,23 +5665,6 @@ __metadata: languageName: node linkType: hard -"create-jest@npm:^29.7.0": - version: 29.7.0 - resolution: "create-jest@npm:29.7.0" - dependencies: - "@jest/types": ^29.6.3 - chalk: ^4.0.0 - exit: ^0.1.2 - graceful-fs: ^4.2.9 - jest-config: ^29.7.0 - jest-util: ^29.7.0 - prompts: ^2.0.1 - bin: - create-jest: bin/create-jest.js - checksum: 1427d49458adcd88547ef6fa39041e1fe9033a661293aa8d2c3aa1b4967cb5bf4f0c00436c7a61816558f28ba2ba81a94d5c962e8022ea9a883978fc8e1f2945 - languageName: node - linkType: hard - "create-require@npm:^1.1.0": version: 1.1.1 resolution: "create-require@npm:1.1.1" @@ -5506,31 +5718,31 @@ __metadata: languageName: node linkType: hard -"data-uri-to-buffer@npm:^6.0.0": - version: 6.0.1 - resolution: "data-uri-to-buffer@npm:6.0.1" - checksum: 9140e68c585ae33d950f5943bd476751346c8b789ae80b01a578a33cb8f7f706d1ca7378aff2b1878b2a6d9a8c88c55cc286d88191c8b8ead8255c3c4d934530 +"data-uri-to-buffer@npm:^5.0.1": + version: 5.0.1 + resolution: "data-uri-to-buffer@npm:5.0.1" + checksum: 10958f89c0047b84bd86d572b6b77c9bf238ebe7b55a9a9ab04c90fbf5ab1881783b72e31dc0febdffd30ec914930244f2f728e3629bb8911d922baba129426f languageName: node linkType: hard "datastore-core@npm:^9.0.1": - version: 9.2.7 - resolution: "datastore-core@npm:9.2.7" + version: 9.2.0 + resolution: "datastore-core@npm:9.2.0" dependencies: - "@libp2p/logger": ^4.0.1 + "@libp2p/logger": ^2.0.0 err-code: ^3.0.1 interface-store: ^5.0.0 it-all: ^3.0.1 it-drain: ^3.0.1 it-filter: ^3.0.0 it-map: ^3.0.1 - it-merge: ^3.0.1 + it-merge: ^3.0.0 it-pipe: ^3.0.0 it-pushable: ^3.0.0 it-sort: ^3.0.1 it-take: ^3.0.1 - uint8arrays: ^5.0.0 - checksum: 593f40d8e5ccbc80b073b4ec1553e70bc061d4656ca238c10eb47d799ff8a137f19698268b0639cc5a26cf5e036f72946dad0bd20cd37e57f713c9d7a1b32a67 + uint8arrays: ^4.0.2 + checksum: f955cdea823e3e3b9ec0090e7dcfab54b4818156674555e7ca38b0047c98dd42cb14474495cbb3c5e6b721320269354fe0185ea2101f6542fb5149f47d7b2114 languageName: node linkType: hard @@ -5621,12 +5833,34 @@ __metadata: languageName: node linkType: hard -"default-gateway@npm:^7.2.2": - version: 7.2.2 - resolution: "default-gateway@npm:7.2.2" +"default-browser-id@npm:^3.0.0": + version: 3.0.0 + resolution: "default-browser-id@npm:3.0.0" + dependencies: + bplist-parser: ^0.2.0 + untildify: ^4.0.0 + checksum: 279c7ad492542e5556336b6c254a4eaf31b2c63a5433265655ae6e47301197b6cfb15c595a6fdc6463b2ff8e1a1a1ed3cba56038a60e1527ba4ab1628c6b9941 + languageName: node + linkType: hard + +"default-browser@npm:^4.0.0": + version: 4.0.0 + resolution: "default-browser@npm:4.0.0" dependencies: + bundle-name: ^3.0.0 + default-browser-id: ^3.0.0 execa: ^7.1.1 - checksum: eec8a2a338677322bcdbf339bbdede61225f6145eedd0c3d4948deadfc929dc0e04b153b33674873d36efa403f76649aaacbfc728a438ee9f538a28723515478 + titleize: ^3.0.0 + checksum: 40c5af984799042b140300be5639c9742599bda76dc9eba5ac9ad5943c83dd36cebc4471eafcfddf8e0ec817166d5ba89d56f08e66a126c7c7908a179cead1a7 + languageName: node + linkType: hard + +"default-gateway@npm:^6.0.2": + version: 6.0.3 + resolution: "default-gateway@npm:6.0.3" + dependencies: + execa: ^5.0.0 + checksum: 126f8273ecac8ee9ff91ea778e8784f6cd732d77c3157e8c5bdd6ed03651b5291f71446d05bc02d04073b1e67583604db5394ea3cf992ede0088c70ea15b7378 languageName: node linkType: hard @@ -5649,25 +5883,20 @@ __metadata: languageName: node linkType: hard -"define-data-property@npm:^1.0.1, define-data-property@npm:^1.1.1": - version: 1.1.1 - resolution: "define-data-property@npm:1.1.1" - dependencies: - get-intrinsic: ^1.2.1 - gopd: ^1.0.1 - has-property-descriptors: ^1.0.0 - checksum: a29855ad3f0630ea82e3c5012c812efa6ca3078d5c2aa8df06b5f597c1cde6f7254692df41945851d903e05a1668607b6d34e778f402b9ff9ffb38111f1a3f0d +"define-lazy-prop@npm:^3.0.0": + version: 3.0.0 + resolution: "define-lazy-prop@npm:3.0.0" + checksum: 54884f94caac0791bf6395a3ec530ce901cf71c47b0196b8754f3fd17edb6c0e80149c1214429d851873bb0d689dbe08dcedbb2306dc45c8534a5934723851b6 languageName: node linkType: hard -"define-properties@npm:^1.1.3, define-properties@npm:^1.2.0, define-properties@npm:^1.2.1": - version: 1.2.1 - resolution: "define-properties@npm:1.2.1" +"define-properties@npm:^1.1.3, define-properties@npm:^1.1.4, define-properties@npm:^1.2.0": + version: 1.2.0 + resolution: "define-properties@npm:1.2.0" dependencies: - define-data-property: ^1.0.1 has-property-descriptors: ^1.0.0 object-keys: ^1.1.1 - checksum: b4ccd00597dd46cb2d4a379398f5b19fca84a16f3374e2249201992f36b30f6835949a9429669ee6b41b6e837205a163eadd745e472069e70dfc10f03e5fcc12 + checksum: e60aee6a19b102df4e2b1f301816804e81ab48bb91f00d0d935f269bf4b3f79c88b39e4f89eaa132890d23267335fd1140dfcd8d5ccd61031a0a2c41a54e33a6 languageName: node linkType: hard @@ -5955,10 +6184,10 @@ __metadata: languageName: node linkType: hard -"devtools-protocol@npm:0.0.1203626": - version: 0.0.1203626 - resolution: "devtools-protocol@npm:0.0.1203626" - checksum: 7bbcb1e637cf9c957aeae11284d2c4cd5fb45c0324839c9074b046889982a53f5cfca190f404aa9468523113714909db126b70a04baf1ec59f9905945a4b5975 +"devtools-protocol@npm:0.0.1179426": + version: 0.0.1179426 + resolution: "devtools-protocol@npm:0.0.1179426" + checksum: 38a091bde42d7d0f8e5e6c7a445db6a56d7b80f21f51de47ed1316123f70b2256aa6fe0d87fbe37bcc4928c0b9245e28a17fb7fbf8d8622156ae36870b26c1ed languageName: node linkType: hard @@ -5972,10 +6201,10 @@ __metadata: languageName: node linkType: hard -"diff-sequences@npm:^29.6.3": - version: 29.6.3 - resolution: "diff-sequences@npm:29.6.3" - checksum: f4914158e1f2276343d98ff5b31fc004e7304f5470bf0f1adb2ac6955d85a531a6458d33e87667f98f6ae52ebd3891bb47d420bb48a5bd8b7a27ee25b20e33aa +"diff-sequences@npm:^29.4.3": + version: 29.4.3 + resolution: "diff-sequences@npm:29.4.3" + checksum: 28b265e04fdddcf7f9f814effe102cc95a9dec0564a579b5aed140edb24fc345c611ca52d76d725a3cab55d3888b915b5e8a4702e0f6058968a90fa5f41fcde7 languageName: node linkType: hard @@ -6006,13 +6235,15 @@ __metadata: languageName: node linkType: hard -"dns-over-http-resolver@npm:3.0.0": - version: 3.0.0 - resolution: "dns-over-http-resolver@npm:3.0.0" +"dns-over-http-resolver@npm:^2.1.0": + version: 2.1.1 + resolution: "dns-over-http-resolver@npm:2.1.1" dependencies: - debug: ^4.3.4 + debug: ^4.3.1 + native-fetch: ^4.0.2 receptacle: ^1.3.2 - checksum: 8527d5cda8b01ef6eff356a983abca2e65c967b0d5f368bfd0d3865e9ae4c0d360c38cef5e8bcd8055b3c7d392a15094c98d6b6eefd9cd54734efcd42ac24068 + undici: ^5.12.0 + checksum: 153a0f4ef705cd08c9b0c163d654988dbb087eabe44c8ab243481c108af97b52ec6343b7127ed1a6a5705a497e8dc06cd1e6c33fbe5aae3a08e357c1e93fc93e languageName: node linkType: hard @@ -6035,9 +6266,9 @@ __metadata: linkType: hard "dotenv@npm:^16.0.3": - version: 16.3.2 - resolution: "dotenv@npm:16.3.2" - checksum: 917b27eeb654b95846484009326b1c52af7a7c25f7b09e2939ed49de8f98cb9895dcc04f13a39cdb078d247985e21147311ccb5bfbf2fd151afb20fa8f96de15 + version: 16.3.1 + resolution: "dotenv@npm:16.3.1" + checksum: 15d75e7279018f4bafd0ee9706593dd14455ddb71b3bcba9c52574460b7ccaf67d5cf8b2c08a5af1a9da6db36c956a04a1192b101ee102a3e0cf8817bbcf3dfd languageName: node linkType: hard @@ -6062,10 +6293,10 @@ __metadata: languageName: node linkType: hard -"electron-to-chromium@npm:^1.4.601": - version: 1.4.642 - resolution: "electron-to-chromium@npm:1.4.642" - checksum: 18b729cf672e8663706998683034acc70fe3fccaff8efb70324a0b2e2a1c021fb670cf1061944d2ec7b5cf2dca8f443403942d9fecf53c333debee47e0cd1f90 +"electron-to-chromium@npm:^1.4.477": + version: 1.4.480 + resolution: "electron-to-chromium@npm:1.4.480" + checksum: 074b9d81dffa6ca182f604326c62a12d22139ae141d6ffabdb3b267f42fac88ba3e69d3614f9e939f65a0f03a343f0c81f702e24064bc54c4f138e02378b1e54 languageName: node linkType: hard @@ -6155,11 +6386,11 @@ __metadata: linkType: hard "envinfo@npm:^7.7.3": - version: 7.11.0 - resolution: "envinfo@npm:7.11.0" + version: 7.10.0 + resolution: "envinfo@npm:7.10.0" bin: envinfo: dist/cli.js - checksum: c45a7d20409d5f4cda72483b150d3816b15b434f2944d72c1495d8838bd7c4e7b2f32c12128ffb9b92b5f66f436237b8a525eb3a9a5da2d20013bc4effa28aef + checksum: 05e81a5768c42cbd5c580dc3f274db3401facadd53e9bd52e2aa49dfbb5d8b26f6181c25a6652d79618a6994185bd2b1c137673101690b147f758e4e71d42f7d languageName: node linkType: hard @@ -6186,25 +6417,25 @@ __metadata: languageName: node linkType: hard -"es-abstract@npm:^1.22.1": - version: 1.22.3 - resolution: "es-abstract@npm:1.22.3" +"es-abstract@npm:^1.19.0, es-abstract@npm:^1.20.4, es-abstract@npm:^1.21.2": + version: 1.22.1 + resolution: "es-abstract@npm:1.22.1" dependencies: array-buffer-byte-length: ^1.0.0 - arraybuffer.prototype.slice: ^1.0.2 + arraybuffer.prototype.slice: ^1.0.1 available-typed-arrays: ^1.0.5 - call-bind: ^1.0.5 + call-bind: ^1.0.2 es-set-tostringtag: ^2.0.1 es-to-primitive: ^1.2.1 - function.prototype.name: ^1.1.6 - get-intrinsic: ^1.2.2 + function.prototype.name: ^1.1.5 + get-intrinsic: ^1.2.1 get-symbol-description: ^1.0.0 globalthis: ^1.0.3 gopd: ^1.0.1 + has: ^1.0.3 has-property-descriptors: ^1.0.0 has-proto: ^1.0.1 has-symbols: ^1.0.3 - hasown: ^2.0.0 internal-slot: ^1.0.5 is-array-buffer: ^3.0.2 is-callable: ^1.2.7 @@ -6212,51 +6443,51 @@ __metadata: is-regex: ^1.1.4 is-shared-array-buffer: ^1.0.2 is-string: ^1.0.7 - is-typed-array: ^1.1.12 + is-typed-array: ^1.1.10 is-weakref: ^1.0.2 - object-inspect: ^1.13.1 + object-inspect: ^1.12.3 object-keys: ^1.1.1 object.assign: ^4.1.4 - regexp.prototype.flags: ^1.5.1 - safe-array-concat: ^1.0.1 + regexp.prototype.flags: ^1.5.0 + safe-array-concat: ^1.0.0 safe-regex-test: ^1.0.0 - string.prototype.trim: ^1.2.8 - string.prototype.trimend: ^1.0.7 - string.prototype.trimstart: ^1.0.7 + string.prototype.trim: ^1.2.7 + string.prototype.trimend: ^1.0.6 + string.prototype.trimstart: ^1.0.6 typed-array-buffer: ^1.0.0 typed-array-byte-length: ^1.0.0 typed-array-byte-offset: ^1.0.0 typed-array-length: ^1.0.4 unbox-primitive: ^1.0.2 - which-typed-array: ^1.1.13 - checksum: b1bdc962856836f6e72be10b58dc128282bdf33771c7a38ae90419d920fc3b36cc5d2b70a222ad8016e3fc322c367bf4e9e89fc2bc79b7e933c05b218e83d79a + which-typed-array: ^1.1.10 + checksum: 614e2c1c3717cb8d30b6128ef12ea110e06fd7d75ad77091ca1c5dbfb00da130e62e4bbbbbdda190eada098a22b27fe0f99ae5a1171dac2c8663b1e8be8a3a9b languageName: node linkType: hard "es-module-lexer@npm:^1.2.1": - version: 1.4.1 - resolution: "es-module-lexer@npm:1.4.1" - checksum: a11b5a256d4e8e9c7d94c2fd87415ccd1591617b6edd847e064503f8eaece2d25e2e9078a02c5ce3ed5e83bb748f5b4820efbe78072c8beb07ac619c2edec35d + version: 1.3.0 + resolution: "es-module-lexer@npm:1.3.0" + checksum: 48fd9f504a9d2a894126f75c8b7ccc6273a289983e9b67255f165bfd9ae765d50100218251e94e702ca567826905ea2f7b3b4a0c4d74d3ce99cce3a2a606a238 languageName: node linkType: hard "es-set-tostringtag@npm:^2.0.1": - version: 2.0.2 - resolution: "es-set-tostringtag@npm:2.0.2" + version: 2.0.1 + resolution: "es-set-tostringtag@npm:2.0.1" dependencies: - get-intrinsic: ^1.2.2 + get-intrinsic: ^1.1.3 + has: ^1.0.3 has-tostringtag: ^1.0.0 - hasown: ^2.0.0 - checksum: afcec3a4c9890ae14d7ec606204858441c801ff84f312538e1d1ccf1e5493c8b17bd672235df785f803756472cb4f2d49b87bde5237aef33411e74c22f194e07 + checksum: ec416a12948cefb4b2a5932e62093a7cf36ddc3efd58d6c58ca7ae7064475ace556434b869b0bbeb0c365f1032a8ccd577211101234b69837ad83ad204fff884 languageName: node linkType: hard "es-shim-unscopables@npm:^1.0.0": - version: 1.0.2 - resolution: "es-shim-unscopables@npm:1.0.2" + version: 1.0.0 + resolution: "es-shim-unscopables@npm:1.0.0" dependencies: - hasown: ^2.0.0 - checksum: 432bd527c62065da09ed1d37a3f8e623c423683285e6188108286f4a1e8e164a5bcbfbc0051557c7d14633cd2a41ce24c7048e6bbb66a985413fd32f1be72626 + has: ^1.0.3 + checksum: 83e95cadbb6ee44d3644dfad60dcad7929edbc42c85e66c3e99aefd68a3a5c5665f2686885cddb47dfeabfd77bd5ea5a7060f2092a955a729bbd8834f0d86fa1 languageName: node linkType: hard @@ -6272,31 +6503,31 @@ __metadata: linkType: hard "esbuild@npm:^0.18.10": - version: 0.18.20 - resolution: "esbuild@npm:0.18.20" - dependencies: - "@esbuild/android-arm": 0.18.20 - "@esbuild/android-arm64": 0.18.20 - "@esbuild/android-x64": 0.18.20 - "@esbuild/darwin-arm64": 0.18.20 - "@esbuild/darwin-x64": 0.18.20 - "@esbuild/freebsd-arm64": 0.18.20 - "@esbuild/freebsd-x64": 0.18.20 - "@esbuild/linux-arm": 0.18.20 - "@esbuild/linux-arm64": 0.18.20 - "@esbuild/linux-ia32": 0.18.20 - "@esbuild/linux-loong64": 0.18.20 - "@esbuild/linux-mips64el": 0.18.20 - "@esbuild/linux-ppc64": 0.18.20 - "@esbuild/linux-riscv64": 0.18.20 - "@esbuild/linux-s390x": 0.18.20 - "@esbuild/linux-x64": 0.18.20 - "@esbuild/netbsd-x64": 0.18.20 - "@esbuild/openbsd-x64": 0.18.20 - "@esbuild/sunos-x64": 0.18.20 - "@esbuild/win32-arm64": 0.18.20 - "@esbuild/win32-ia32": 0.18.20 - "@esbuild/win32-x64": 0.18.20 + version: 0.18.17 + resolution: "esbuild@npm:0.18.17" + dependencies: + "@esbuild/android-arm": 0.18.17 + "@esbuild/android-arm64": 0.18.17 + "@esbuild/android-x64": 0.18.17 + "@esbuild/darwin-arm64": 0.18.17 + "@esbuild/darwin-x64": 0.18.17 + "@esbuild/freebsd-arm64": 0.18.17 + "@esbuild/freebsd-x64": 0.18.17 + "@esbuild/linux-arm": 0.18.17 + "@esbuild/linux-arm64": 0.18.17 + "@esbuild/linux-ia32": 0.18.17 + "@esbuild/linux-loong64": 0.18.17 + "@esbuild/linux-mips64el": 0.18.17 + "@esbuild/linux-ppc64": 0.18.17 + "@esbuild/linux-riscv64": 0.18.17 + "@esbuild/linux-s390x": 0.18.17 + "@esbuild/linux-x64": 0.18.17 + "@esbuild/netbsd-x64": 0.18.17 + "@esbuild/openbsd-x64": 0.18.17 + "@esbuild/sunos-x64": 0.18.17 + "@esbuild/win32-arm64": 0.18.17 + "@esbuild/win32-ia32": 0.18.17 + "@esbuild/win32-x64": 0.18.17 dependenciesMeta: "@esbuild/android-arm": optional: true @@ -6344,7 +6575,7 @@ __metadata: optional: true bin: esbuild: bin/esbuild - checksum: 5d253614e50cdb6ec22095afd0c414f15688e7278a7eb4f3720a6dd1306b0909cf431e7b9437a90d065a31b1c57be60130f63fe3e8d0083b588571f31ee6ec7b + checksum: c6e1ffa776978a45697763a07ec9b16411db3d3b3997b2c4a0165a211727fce8b63b87165a28d8ef60d3a28b98197bbbc2833e51b89888a4437e0a483dffc8ff languageName: node linkType: hard @@ -6402,42 +6633,43 @@ __metadata: linkType: hard "eslint-config-prettier@npm:^8.5.0": - version: 8.10.0 - resolution: "eslint-config-prettier@npm:8.10.0" + version: 8.9.0 + resolution: "eslint-config-prettier@npm:8.9.0" peerDependencies: eslint: ">=7.0.0" bin: eslint-config-prettier: bin/cli.js - checksum: 153266badd477e49b0759816246b2132f1dbdb6c7f313ca60a9af5822fd1071c2bc5684a3720d78b725452bbac04bb130878b2513aea5e72b1b792de5a69fec8 + checksum: a675d0dabd76b700ef2d062b5ec6a634e105a8e8c070f95281fd2ccb614527fac60b4c758132058c50f0521fd19313f1f5be45ce9ebf081f2e5f77ae6eb7d8db languageName: node linkType: hard -"eslint-import-resolver-node@npm:^0.3.9": - version: 0.3.9 - resolution: "eslint-import-resolver-node@npm:0.3.9" +"eslint-import-resolver-node@npm:^0.3.7": + version: 0.3.7 + resolution: "eslint-import-resolver-node@npm:0.3.7" dependencies: debug: ^3.2.7 - is-core-module: ^2.13.0 - resolve: ^1.22.4 - checksum: 439b91271236b452d478d0522a44482e8c8540bf9df9bd744062ebb89ab45727a3acd03366a6ba2bdbcde8f9f718bab7fe8db64688aca75acf37e04eafd25e22 + is-core-module: ^2.11.0 + resolve: ^1.22.1 + checksum: 3379aacf1d2c6952c1b9666c6fa5982c3023df695430b0d391c0029f6403a7775414873d90f397e98ba6245372b6c8960e16e74d9e4a3b0c0a4582f3bdbe3d6e languageName: node linkType: hard "eslint-import-resolver-typescript@npm:^3.5.5": - version: 3.6.1 - resolution: "eslint-import-resolver-typescript@npm:3.6.1" + version: 3.5.5 + resolution: "eslint-import-resolver-typescript@npm:3.5.5" dependencies: debug: ^4.3.4 enhanced-resolve: ^5.12.0 eslint-module-utils: ^2.7.4 - fast-glob: ^3.3.1 get-tsconfig: ^4.5.0 + globby: ^13.1.3 is-core-module: ^2.11.0 is-glob: ^4.0.3 + synckit: ^0.8.5 peerDependencies: eslint: "*" eslint-plugin-import: "*" - checksum: 454fa0646533050fb57f13d27daf8c71f51b0bb9156d6a461290ccb8576d892209fcc6702a89553f3f5ea8e5b407395ca2e5de169a952c953685f1f7c46b4496 + checksum: 27e6276fdff5d377c9036362ff736ac29852106e883ff589ea9092dc57d4bc2a67a82d75134221124f05045f9a7e2114a159b2c827d1f9f64d091f7afeab0f58 languageName: node linkType: hard @@ -6454,29 +6686,30 @@ __metadata: linkType: hard "eslint-plugin-import@npm:^2.27.5": - version: 2.29.1 - resolution: "eslint-plugin-import@npm:2.29.1" + version: 2.28.0 + resolution: "eslint-plugin-import@npm:2.28.0" dependencies: - array-includes: ^3.1.7 - array.prototype.findlastindex: ^1.2.3 - array.prototype.flat: ^1.3.2 - array.prototype.flatmap: ^1.3.2 + array-includes: ^3.1.6 + array.prototype.findlastindex: ^1.2.2 + array.prototype.flat: ^1.3.1 + array.prototype.flatmap: ^1.3.1 debug: ^3.2.7 doctrine: ^2.1.0 - eslint-import-resolver-node: ^0.3.9 + eslint-import-resolver-node: ^0.3.7 eslint-module-utils: ^2.8.0 - hasown: ^2.0.0 - is-core-module: ^2.13.1 + has: ^1.0.3 + is-core-module: ^2.12.1 is-glob: ^4.0.3 minimatch: ^3.1.2 - object.fromentries: ^2.0.7 - object.groupby: ^1.0.1 - object.values: ^1.1.7 + object.fromentries: ^2.0.6 + object.groupby: ^1.0.0 + object.values: ^1.1.6 + resolve: ^1.22.3 semver: ^6.3.1 - tsconfig-paths: ^3.15.0 + tsconfig-paths: ^3.14.2 peerDependencies: eslint: ^2 || ^3 || ^4 || ^5 || ^6 || ^7.2.0 || ^8 - checksum: e65159aef808136d26d029b71c8c6e4cb5c628e65e5de77f1eb4c13a379315ae55c9c3afa847f43f4ff9df7e54515c77ffc6489c6a6f81f7dd7359267577468c + checksum: f9eba311b93ca1bb89311856b1f7285bd79e0181d7eb70fe115053ff77e2235fea749b30f538b78927dc65769340b5be61f4c9581d1c82bcdcccb2061f440ad1 languageName: node linkType: hard @@ -6541,25 +6774,24 @@ __metadata: languageName: node linkType: hard -"eslint-visitor-keys@npm:^3.3.0, eslint-visitor-keys@npm:^3.4.1, eslint-visitor-keys@npm:^3.4.3": - version: 3.4.3 - resolution: "eslint-visitor-keys@npm:3.4.3" - checksum: 36e9ef87fca698b6fd7ca5ca35d7b2b6eeaaf106572e2f7fd31c12d3bfdaccdb587bba6d3621067e5aece31c8c3a348b93922ab8f7b2cbc6aaab5e1d89040c60 +"eslint-visitor-keys@npm:^3.3.0, eslint-visitor-keys@npm:^3.4.1, eslint-visitor-keys@npm:^3.4.2": + version: 3.4.2 + resolution: "eslint-visitor-keys@npm:3.4.2" + checksum: 9e0e7e4aaea705c097ae37c97410e5f167d4d2193be2edcb1f0760762ede3df01545e4820ae314f42dcec687745f2c6dcaf6d83575c4a2a241eb0c8517d724f2 languageName: node linkType: hard "eslint@npm:^8.21.0, eslint@npm:^8.35.0, eslint@npm:^8.37.0": - version: 8.56.0 - resolution: "eslint@npm:8.56.0" + version: 8.46.0 + resolution: "eslint@npm:8.46.0" dependencies: "@eslint-community/eslint-utils": ^4.2.0 "@eslint-community/regexpp": ^4.6.1 - "@eslint/eslintrc": ^2.1.4 - "@eslint/js": 8.56.0 - "@humanwhocodes/config-array": ^0.11.13 + "@eslint/eslintrc": ^2.1.1 + "@eslint/js": ^8.46.0 + "@humanwhocodes/config-array": ^0.11.10 "@humanwhocodes/module-importer": ^1.0.1 "@nodelib/fs.walk": ^1.2.8 - "@ungap/structured-clone": ^1.2.0 ajv: ^6.12.4 chalk: ^4.0.0 cross-spawn: ^7.0.2 @@ -6567,7 +6799,7 @@ __metadata: doctrine: ^3.0.0 escape-string-regexp: ^4.0.0 eslint-scope: ^7.2.2 - eslint-visitor-keys: ^3.4.3 + eslint-visitor-keys: ^3.4.2 espree: ^9.6.1 esquery: ^1.4.2 esutils: ^2.0.2 @@ -6592,7 +6824,7 @@ __metadata: text-table: ^0.2.0 bin: eslint: bin/eslint.js - checksum: 883436d1e809b4a25d9eb03d42f584b84c408dbac28b0019f6ea07b5177940bf3cca86208f749a6a1e0039b63e085ee47aca1236c30721e91f0deef5cc5a5136 + checksum: 7a7d36b1a3bbc12e08fbb5bc36fd482a7a5a1797e62e762499dd45601b9e45aaa53a129f31ce0b4444551a9639b8b681ad535f379893dd1e3ae37b31dccd82aa languageName: node linkType: hard @@ -6678,14 +6910,14 @@ __metadata: languageName: node linkType: hard -"eventemitter3@npm:^5.0.1": - version: 5.0.1 - resolution: "eventemitter3@npm:5.0.1" - checksum: 543d6c858ab699303c3c32e0f0f47fc64d360bf73c3daf0ac0b5079710e340d6fe9f15487f94e66c629f5f82cd1a8678d692f3dbb6f6fcd1190e1b97fcad36f8 +"eventemitter3@npm:^4.0.7": + version: 4.0.7 + resolution: "eventemitter3@npm:4.0.7" + checksum: 1875311c42fcfe9c707b2712c32664a245629b42bb0a5a84439762dd0fd637fc54d078155ea83c2af9e0323c9ac13687e03cfba79b03af9f40c89b4960099374 languageName: node linkType: hard -"events@npm:^3.2.0": +"events@npm:^3.2.0, events@npm:^3.3.0": version: 3.3.0 resolution: "events@npm:3.3.0" checksum: f6f487ad2198aa41d878fa31452f1a3c00958f46e9019286ff4787c84aac329332ab45c9cdc8c445928fc6d7ded294b9e005a7fce9426488518017831b272780 @@ -6744,16 +6976,17 @@ __metadata: languageName: node linkType: hard -"expect@npm:^29.0.0, expect@npm:^29.7.0": - version: 29.7.0 - resolution: "expect@npm:29.7.0" +"expect@npm:^29.0.0, expect@npm:^29.6.2": + version: 29.6.2 + resolution: "expect@npm:29.6.2" dependencies: - "@jest/expect-utils": ^29.7.0 - jest-get-type: ^29.6.3 - jest-matcher-utils: ^29.7.0 - jest-message-util: ^29.7.0 - jest-util: ^29.7.0 - checksum: 9257f10288e149b81254a0fda8ffe8d54a7061cd61d7515779998b012579d2b8c22354b0eb901daf0145f347403da582f75f359f4810c007182ad3fb318b5c0c + "@jest/expect-utils": ^29.6.2 + "@types/node": "*" + jest-get-type: ^29.4.3 + jest-matcher-utils: ^29.6.2 + jest-message-util: ^29.6.2 + jest-util: ^29.6.2 + checksum: 71f7b0c560e58bf6d27e0fded261d4bdb7ef81552a6bb4bd1ee09ce7a1f7dca67fbf83cf9b07a6645a88ef52e65085a0dcbe17f6c063b53ff7c2f0f3ea4ef69e languageName: node linkType: hard @@ -6795,16 +7028,16 @@ __metadata: languageName: node linkType: hard -"fast-glob@npm:^3.2.9, fast-glob@npm:^3.3.1": - version: 3.3.2 - resolution: "fast-glob@npm:3.3.2" +"fast-glob@npm:^3.2.9, fast-glob@npm:^3.3.0": + version: 3.3.1 + resolution: "fast-glob@npm:3.3.1" dependencies: "@nodelib/fs.stat": ^2.0.2 "@nodelib/fs.walk": ^1.2.3 glob-parent: ^5.1.2 merge2: ^1.3.0 micromatch: ^4.0.4 - checksum: 900e4979f4dbc3313840078419245621259f349950411ca2fa445a2f9a1a6d98c3b5e7e0660c5ccd563aa61abe133a21765c6c0dec8e57da1ba71d8000b05ec1 + checksum: b6f3add6403e02cf3a798bfbb1183d0f6da2afd368f27456010c0bc1f9640aea308243d4cb2c0ab142f618276e65ecb8be1661d7c62a7b4e5ba774b9ce5432e5 languageName: node linkType: hard @@ -6837,11 +7070,11 @@ __metadata: linkType: hard "fastq@npm:^1.6.0": - version: 1.16.0 - resolution: "fastq@npm:1.16.0" + version: 1.15.0 + resolution: "fastq@npm:1.15.0" dependencies: reusify: ^1.0.4 - checksum: 1d40ed1f100ae625e5720484e8602b7ad07649370f1cbc3e34a6b9630a0bfed6946bab0322d8a368a1e3cde87bb9bbb8d3bc2ae01a0c1f022fac1d07c04e4feb + checksum: 0170e6bfcd5d57a70412440b8ef600da6de3b2a6c5966aeaf0a852d542daff506a0ee92d6de7679d1de82e644bce69d7a574a6c93f0b03964b5337eed75ada1a languageName: node linkType: hard @@ -6958,29 +7191,19 @@ __metadata: linkType: hard "flat-cache@npm:^3.0.4": - version: 3.2.0 - resolution: "flat-cache@npm:3.2.0" + version: 3.0.4 + resolution: "flat-cache@npm:3.0.4" dependencies: - flatted: ^3.2.9 - keyv: ^4.5.3 + flatted: ^3.1.0 rimraf: ^3.0.2 - checksum: e7e0f59801e288b54bee5cb9681e9ee21ee28ef309f886b312c9d08415b79fc0f24ac842f84356ce80f47d6a53de62197ce0e6e148dc42d5db005992e2a756ec + checksum: 4fdd10ecbcbf7d520f9040dd1340eb5dfe951e6f0ecf2252edeec03ee68d989ec8b9a20f4434270e71bcfd57800dc09b3344fca3966b2eb8f613072c7d9a2365 languageName: node linkType: hard -"flat@npm:^5.0.2": - version: 5.0.2 - resolution: "flat@npm:5.0.2" - bin: - flat: cli.js - checksum: 12a1536ac746db74881316a181499a78ef953632ddd28050b7a3a43c62ef5462e3357c8c29d76072bb635f147f7a9a1f0c02efef6b4be28f8db62ceb3d5c7f5d - languageName: node - linkType: hard - -"flatted@npm:^3.2.9": - version: 3.2.9 - resolution: "flatted@npm:3.2.9" - checksum: f14167fbe26a9d20f6fca8d998e8f1f41df72c8e81f9f2c9d61ed2bea058248f5e1cbd05e7f88c0e5087a6a0b822a1e5e2b446e879f3cfbe0b07ba2d7f80b026 +"flatted@npm:^3.1.0": + version: 3.2.7 + resolution: "flatted@npm:3.2.7" + checksum: 427633049d55bdb80201c68f7eb1cbd533e03eac541f97d3aecab8c5526f12a20ccecaeede08b57503e772c769e7f8680b37e8d482d1e5f8d7e2194687f9ea35 languageName: node linkType: hard @@ -7071,13 +7294,13 @@ __metadata: linkType: hard "fs-extra@npm:^11.1.1": - version: 11.2.0 - resolution: "fs-extra@npm:11.2.0" + version: 11.1.1 + resolution: "fs-extra@npm:11.1.1" dependencies: graceful-fs: ^4.2.0 jsonfile: ^6.0.1 universalify: ^2.0.0 - checksum: b12e42fa40ba47104202f57b8480dd098aa931c2724565e5e70779ab87605665594e76ee5fb00545f772ab9ace167fe06d2ab009c416dc8c842c5ae6df7aa7e8 + checksum: fb883c68245b2d777fbc1f2082c9efb084eaa2bbf9fddaa366130d196c03608eebef7fb490541276429ee1ca99f317e2d73e96f5ca0999eefedf5a624ae1edfd languageName: node linkType: hard @@ -7102,11 +7325,11 @@ __metadata: linkType: hard "fs-minipass@npm:^3.0.0": - version: 3.0.3 - resolution: "fs-minipass@npm:3.0.3" + version: 3.0.2 + resolution: "fs-minipass@npm:3.0.2" dependencies: - minipass: ^7.0.3 - checksum: 8722a41109130851d979222d3ec88aabaceeaaf8f57b2a8f744ef8bd2d1ce95453b04a61daa0078822bc5cd21e008814f06fe6586f56fef511e71b8d2394d802 + minipass: ^5.0.0 + checksum: e9cc0e1f2d01c6f6f62f567aee59530aba65c6c7b2ae88c5027bc34c711ebcfcfaefd0caf254afa6adfe7d1fba16bc2537508a6235196bac7276747d078aef0a languageName: node linkType: hard @@ -7118,24 +7341,31 @@ __metadata: linkType: hard "fsevents@npm:^2.3.2, fsevents@npm:~2.3.2": - version: 2.3.3 - resolution: "fsevents@npm:2.3.3" + version: 2.3.2 + resolution: "fsevents@npm:2.3.2" dependencies: node-gyp: latest - checksum: 11e6ea6fea15e42461fc55b4b0e4a0a3c654faa567f1877dbd353f39156f69def97a69936d1746619d656c4b93de2238bf731f6085a03a50cabf287c9d024317 + checksum: 97ade64e75091afee5265e6956cb72ba34db7819b4c3e94c431d4be2b19b8bb7a2d4116da417950c3425f17c8fe693d25e20212cac583ac1521ad066b77ae31f conditions: os=darwin languageName: node linkType: hard "fsevents@patch:fsevents@^2.3.2#~builtin, fsevents@patch:fsevents@~2.3.2#~builtin": - version: 2.3.3 - resolution: "fsevents@patch:fsevents@npm%3A2.3.3#~builtin::version=2.3.3&hash=df0bf1" + version: 2.3.2 + resolution: "fsevents@patch:fsevents@npm%3A2.3.2#~builtin::version=2.3.2&hash=df0bf1" dependencies: node-gyp: latest conditions: os=darwin languageName: node linkType: hard +"function-bind@npm:^1.1.1": + version: 1.1.1 + resolution: "function-bind@npm:1.1.1" + checksum: b32fbaebb3f8ec4969f033073b43f5c8befbb58f1a79e12f1d7490358150359ebd92f49e72ff0144f65f2c48ea2a605bff2d07965f548f6474fd8efd95bf361a + languageName: node + linkType: hard + "function-bind@npm:^1.1.2": version: 1.1.2 resolution: "function-bind@npm:1.1.2" @@ -7143,15 +7373,15 @@ __metadata: languageName: node linkType: hard -"function.prototype.name@npm:^1.1.6": - version: 1.1.6 - resolution: "function.prototype.name@npm:1.1.6" +"function.prototype.name@npm:^1.1.5": + version: 1.1.5 + resolution: "function.prototype.name@npm:1.1.5" dependencies: call-bind: ^1.0.2 - define-properties: ^1.2.0 - es-abstract: ^1.22.1 - functions-have-names: ^1.2.3 - checksum: 7a3f9bd98adab09a07f6e1f03da03d3f7c26abbdeaeee15223f6c04a9fb5674792bdf5e689dac19b97ac71de6aad2027ba3048a9b883aa1b3173eed6ab07f479 + define-properties: ^1.1.3 + es-abstract: ^1.19.0 + functions-have-names: ^1.2.2 + checksum: acd21d733a9b649c2c442f067567743214af5fa248dbeee69d8278ce7df3329ea5abac572be9f7470b4ec1cd4d8f1040e3c5caccf98ebf2bf861a0deab735c27 languageName: node linkType: hard @@ -7162,13 +7392,29 @@ __metadata: languageName: node linkType: hard -"functions-have-names@npm:^1.2.3": +"functions-have-names@npm:^1.2.2, functions-have-names@npm:^1.2.3": version: 1.2.3 resolution: "functions-have-names@npm:1.2.3" checksum: c3f1f5ba20f4e962efb71344ce0a40722163e85bee2101ce25f88214e78182d2d2476aa85ef37950c579eb6cf6ee811c17b3101bb84004bb75655f3e33f3fdb5 languageName: node linkType: hard +"gauge@npm:^4.0.3": + version: 4.0.4 + resolution: "gauge@npm:4.0.4" + dependencies: + aproba: ^1.0.3 || ^2.0.0 + color-support: ^1.1.3 + console-control-strings: ^1.1.0 + has-unicode: ^2.0.1 + signal-exit: ^3.0.7 + string-width: ^4.2.3 + strip-ansi: ^6.0.1 + wide-align: ^1.1.5 + checksum: 788b6bfe52f1dd8e263cda800c26ac0ca2ff6de0b6eee2fe0d9e3abf15e149b651bd27bf5226be10e6e3edb5c4e5d5985a5a1a98137e7a892f75eff76467ad2d + languageName: node + linkType: hard + "gensync@npm:^1.0.0-beta.2": version: 1.0.0-beta.2 resolution: "gensync@npm:1.0.0-beta.2" @@ -7203,15 +7449,15 @@ __metadata: languageName: node linkType: hard -"get-intrinsic@npm:^1.0.2, get-intrinsic@npm:^1.1.1, get-intrinsic@npm:^1.1.3, get-intrinsic@npm:^1.2.0, get-intrinsic@npm:^1.2.1, get-intrinsic@npm:^1.2.2": - version: 1.2.2 - resolution: "get-intrinsic@npm:1.2.2" +"get-intrinsic@npm:^1.0.2, get-intrinsic@npm:^1.1.1, get-intrinsic@npm:^1.1.3, get-intrinsic@npm:^1.2.0, get-intrinsic@npm:^1.2.1": + version: 1.2.1 + resolution: "get-intrinsic@npm:1.2.1" dependencies: - function-bind: ^1.1.2 + function-bind: ^1.1.1 + has: ^1.0.3 has-proto: ^1.0.1 has-symbols: ^1.0.3 - hasown: ^2.0.0 - checksum: 447ff0724df26829908dc033b62732359596fcf66027bc131ab37984afb33842d9cd458fd6cecadfe7eac22fd8a54b349799ed334cf2726025c921c7250e7417 + checksum: 5b61d88552c24b0cf6fa2d1b3bc5459d7306f699de060d76442cce49a4721f52b8c560a33ab392cf5575b7810277d54ded9d4d39a1ea61855619ebc005aa7e5f languageName: node linkType: hard @@ -7222,10 +7468,10 @@ __metadata: languageName: node linkType: hard -"get-iterator@npm:^2.0.0, get-iterator@npm:^2.0.1": - version: 2.0.1 - resolution: "get-iterator@npm:2.0.1" - checksum: 353baac51f5e335c19cb734cbf0401d7c47deeac9d375e2939fed646fe52db2912d61ed2a60112050cf4687080817d159ec938803e48e03cd602edd489a116f2 +"get-iterator@npm:^2.0.0": + version: 2.0.0 + resolution: "get-iterator@npm:2.0.0" + checksum: 4ed81e7d100dd0a8dab11bf7dc7f88d8895a03057fa4a56d24190bdfe490fa4da3005d5b88fc6336429b33b423586f40dbcc40ce723f5dea55593cae0e2937de languageName: node linkType: hard @@ -7270,23 +7516,23 @@ __metadata: linkType: hard "get-tsconfig@npm:^4.5.0": - version: 4.7.2 - resolution: "get-tsconfig@npm:4.7.2" + version: 4.6.2 + resolution: "get-tsconfig@npm:4.6.2" dependencies: resolve-pkg-maps: ^1.0.0 - checksum: 172358903250eff0103943f816e8a4e51d29b8e5449058bdf7266714a908a48239f6884308bd3a6ff28b09f692b9533dbebfd183ab63e4e14f073cda91f1bca9 + checksum: e791e671a9b55e91efea3ca819ecd7a25beae679e31c83234bf3dd62ddd93df070c1b95ae7e29d206358ebb6408f6f79ac6d83a32a3bbd6a6d217babe23de077 languageName: node linkType: hard "get-uri@npm:^6.0.1": - version: 6.0.2 - resolution: "get-uri@npm:6.0.2" + version: 6.0.1 + resolution: "get-uri@npm:6.0.1" dependencies: basic-ftp: ^5.0.2 - data-uri-to-buffer: ^6.0.0 + data-uri-to-buffer: ^5.0.1 debug: ^4.3.4 fs-extra: ^8.1.0 - checksum: 762de3b0e3d4e7afc966e4ce93be587d70c270590da9b4c8fbff888362656c055838d926903d1774cbfeed4d392b4d6def4b2c06d48c050580070426a3a8629b + checksum: a8aec70e1c67386fbe67f66e344ecd671a19f4cfc8e0f0e14d070563af5123d540e77fbceb6e26566f29846fac864d2862699ab134d307f85c85e7d72ce23d14 languageName: node linkType: hard @@ -7315,7 +7561,22 @@ __metadata: languageName: node linkType: hard -"glob@npm:^10.2.2, glob@npm:^10.3.10": +"glob@npm:^10.2.2": + version: 10.3.3 + resolution: "glob@npm:10.3.3" + dependencies: + foreground-child: ^3.1.0 + jackspeak: ^2.0.3 + minimatch: ^9.0.1 + minipass: ^5.0.0 || ^6.0.2 || ^7.0.0 + path-scurry: ^1.10.1 + bin: + glob: dist/cjs/src/bin.js + checksum: 29190d3291f422da0cb40b77a72fc8d2c51a36524e99b8bf412548b7676a6627489528b57250429612b6eec2e6fe7826d328451d3e694a9d15e575389308ec53 + languageName: node + linkType: hard + +"glob@npm:^10.3.10": version: 10.3.10 resolution: "glob@npm:10.3.10" dependencies: @@ -7352,11 +7613,11 @@ __metadata: linkType: hard "globals@npm:^13.19.0": - version: 13.24.0 - resolution: "globals@npm:13.24.0" + version: 13.20.0 + resolution: "globals@npm:13.20.0" dependencies: type-fest: ^0.20.2 - checksum: 56066ef058f6867c04ff203b8a44c15b038346a62efbc3060052a1016be9f56f4cf0b2cd45b74b22b81e521a889fc7786c73691b0549c2f3a6e825b3d394f43c + checksum: ad1ecf914bd051325faad281d02ea2c0b1df5d01bd94d368dcc5513340eac41d14b3c61af325768e3c7f8d44576e72780ec0b6f2d366121f8eec6e03c3a3b97a languageName: node linkType: hard @@ -7383,6 +7644,19 @@ __metadata: languageName: node linkType: hard +"globby@npm:^13.1.3": + version: 13.2.2 + resolution: "globby@npm:13.2.2" + dependencies: + dir-glob: ^3.0.1 + fast-glob: ^3.3.0 + ignore: ^5.2.4 + merge2: ^1.4.1 + slash: ^4.0.0 + checksum: f3d84ced58a901b4fcc29c846983108c426631fe47e94872868b65565495f7bee7b3defd68923bd480582771fd4bbe819217803a164a618ad76f1d22f666f41e + languageName: node + linkType: hard + "gonzales-pe@npm:^4.2.3, gonzales-pe@npm:^4.3.0": version: 4.3.0 resolution: "gonzales-pe@npm:4.3.0" @@ -7452,12 +7726,12 @@ __metadata: languageName: node linkType: hard -"has-property-descriptors@npm:^1.0.0, has-property-descriptors@npm:^1.0.1": - version: 1.0.1 - resolution: "has-property-descriptors@npm:1.0.1" +"has-property-descriptors@npm:^1.0.0": + version: 1.0.0 + resolution: "has-property-descriptors@npm:1.0.0" dependencies: - get-intrinsic: ^1.2.2 - checksum: 2bcc6bf6ec6af375add4e4b4ef586e43674850a91ad4d46666d0b28ba8e1fd69e424c7677d24d60f69470ad0afaa2f3197f508b20b0bb7dd99a8ab77ffc4b7c4 + get-intrinsic: ^1.1.1 + checksum: a6d3f0a266d0294d972e354782e872e2fe1b6495b321e6ef678c9b7a06a40408a6891817350c62e752adced73a94ac903c54734fee05bf65b1905ee1368194bb languageName: node linkType: hard @@ -7484,6 +7758,22 @@ __metadata: languageName: node linkType: hard +"has-unicode@npm:^2.0.1": + version: 2.0.1 + resolution: "has-unicode@npm:2.0.1" + checksum: 1eab07a7436512db0be40a710b29b5dc21fa04880b7f63c9980b706683127e3c1b57cb80ea96d47991bdae2dfe479604f6a1ba410106ee1046a41d1bd0814400 + languageName: node + linkType: hard + +"has@npm:^1.0.3": + version: 1.0.3 + resolution: "has@npm:1.0.3" + dependencies: + function-bind: ^1.1.1 + checksum: b9ad53d53be4af90ce5d1c38331e712522417d017d5ef1ebd0507e07c2fbad8686fffb8e12ddecd4c39ca9b9b47431afbb975b8abf7f3c3b82c98e9aad052792 + languageName: node + linkType: hard + "hash-base@npm:^3.0.0": version: 3.1.0 resolution: "hash-base@npm:3.1.0" @@ -7608,6 +7898,17 @@ __metadata: languageName: node linkType: hard +"http-proxy-agent@npm:^5.0.0": + version: 5.0.0 + resolution: "http-proxy-agent@npm:5.0.0" + dependencies: + "@tootallnate/once": 2 + agent-base: 6 + debug: 4 + checksum: e2ee1ff1656a131953839b2a19cd1f3a52d97c25ba87bd2559af6ae87114abf60971e498021f9b73f9fd78aea8876d1fb0d4656aac8a03c6caa9fc175f22b786 + languageName: node + linkType: hard + "http-proxy-agent@npm:^7.0.0": version: 7.0.0 resolution: "http-proxy-agent@npm:7.0.0" @@ -7618,7 +7919,17 @@ __metadata: languageName: node linkType: hard -"https-proxy-agent@npm:^7.0.1, https-proxy-agent@npm:^7.0.2": +"https-proxy-agent@npm:^5.0.0": + version: 5.0.1 + resolution: "https-proxy-agent@npm:5.0.1" + dependencies: + agent-base: 6 + debug: 4 + checksum: 571fccdf38184f05943e12d37d6ce38197becdd69e58d03f43637f7fa1269cf303a7d228aa27e5b27bbd3af8f09fd938e1c91dcfefff2df7ba77c20ed8dfc765 + languageName: node + linkType: hard + +"https-proxy-agent@npm:^7.0.2": version: 7.0.2 resolution: "https-proxy-agent@npm:7.0.2" dependencies: @@ -7642,6 +7953,15 @@ __metadata: languageName: node linkType: hard +"humanize-ms@npm:^1.2.1": + version: 1.2.1 + resolution: "humanize-ms@npm:1.2.1" + dependencies: + ms: ^2.0.0 + checksum: 9c7a74a2827f9294c009266c82031030eae811ca87b0da3dceb8d6071b9bde22c9f3daef0469c3c533cc67a97d8a167cd9fc0389350e5f415f61a79b171ded16 + languageName: node + linkType: hard + "hyperdyperid@npm:^1.2.0": version: 1.2.0 resolution: "hyperdyperid@npm:1.2.0" @@ -7675,9 +7995,9 @@ __metadata: linkType: hard "ignore@npm:^5.2.0, ignore@npm:^5.2.4": - version: 5.3.0 - resolution: "ignore@npm:5.3.0" - checksum: 2736da6621f14ced652785cb05d86301a66d70248597537176612bd0c8630893564bd5f6421f8806b09e8472e75c591ef01672ab8059c07c6eb2c09cefe04bf9 + version: 5.2.4 + resolution: "ignore@npm:5.2.4" + checksum: 3d4c309c6006e2621659311783eaea7ebcd41fe4ca1d78c91c473157ad6666a57a2df790fe0d07a12300d9aac2888204d7be8d59f9aaf665b1c7fcdb432517ef languageName: node linkType: hard @@ -7732,9 +8052,9 @@ __metadata: linkType: hard "inflation@npm:^2.0.0": - version: 2.1.0 - resolution: "inflation@npm:2.1.0" - checksum: 80c1b5d9ec408105a85f0623c824d668ddf0cadafd8d9716c0737990e5a712ae5f7d6bb0ff216b6648eccb9c6ac69fe06c0d8c58456d168db5bf550c89dd74ed + version: 2.0.0 + resolution: "inflation@npm:2.0.0" + checksum: a0494871b12275afdef9e2710ee1af1e0fc642b04613a9be69c05ef8b5e9627f3bd7d358a937fa47aa20235ee7313a4f30255048533add0ad4918beb918a586e languageName: node linkType: hard @@ -7770,30 +8090,31 @@ __metadata: linkType: hard "interface-datastore@npm:^8.2.0": - version: 8.2.10 - resolution: "interface-datastore@npm:8.2.10" + version: 8.2.3 + resolution: "interface-datastore@npm:8.2.3" dependencies: interface-store: ^5.0.0 - uint8arrays: ^5.0.0 - checksum: 16e12820b8423e457a6255377f373ea20132d7080809756b350e8914fde9abb73c4dd226cb3a9dda31bff790c181a02382bdde0482f9385ea4d2168c35ae93d8 + nanoid: ^4.0.0 + uint8arrays: ^4.0.2 + checksum: 01520fb965281f1352b93244a449d254e2fa9f9e4fb7f7f911b459354efcaa000d6ac311efda632d366281dbad41b3a61f321ce6a6241ab0f31283a81e4084cd languageName: node linkType: hard "interface-store@npm:^5.0.0": - version: 5.1.7 - resolution: "interface-store@npm:5.1.7" - checksum: aeddcdbfe8601f127c485ede6c6323bf2b302c713a1661036403dfd01b6b91e19a314ecf002477f108cb9c94d67781f88d386d3688f1e9c7cd1464a756ac28fa + version: 5.1.2 + resolution: "interface-store@npm:5.1.2" + checksum: 1423c5ffb5e6f9e65a86661cda90fb35d8dda433255bce9f57417650c98df83c84c4dc65741635c3efb16965b0f409f979a524f12b22803db0ab53ecfdf7b7db languageName: node linkType: hard "internal-slot@npm:^1.0.5": - version: 1.0.6 - resolution: "internal-slot@npm:1.0.6" + version: 1.0.5 + resolution: "internal-slot@npm:1.0.5" dependencies: - get-intrinsic: ^1.2.2 - hasown: ^2.0.0 + get-intrinsic: ^1.2.0 + has: ^1.0.3 side-channel: ^1.0.4 - checksum: 7872454888047553ce97a3fa1da7cc054a28ec5400a9c2e9f4dbe4fe7c1d041cb8e8301467614b80d4246d50377aad2fb58860b294ed74d6700cc346b6f89549 + checksum: 97e84046bf9e7574d0956bd98d7162313ce7057883b6db6c5c7b5e5f05688864b0978ba07610c726d15d66544ffe4b1050107d93f8a39ebc59b15d8b429b497a languageName: node linkType: hard @@ -7900,7 +8221,16 @@ __metadata: languageName: node linkType: hard -"is-core-module@npm:^2.1.0, is-core-module@npm:^2.11.0, is-core-module@npm:^2.13.0, is-core-module@npm:^2.13.1": +"is-core-module@npm:^2.1.0, is-core-module@npm:^2.11.0, is-core-module@npm:^2.12.0, is-core-module@npm:^2.12.1": + version: 2.12.1 + resolution: "is-core-module@npm:2.12.1" + dependencies: + has: ^1.0.3 + checksum: f04ea30533b5e62764e7b2e049d3157dc0abd95ef44275b32489ea2081176ac9746ffb1cdb107445cf1ff0e0dfcad522726ca27c27ece64dadf3795428b8e468 + languageName: node + linkType: hard + +"is-core-module@npm:^2.13.0": version: 2.13.1 resolution: "is-core-module@npm:2.13.1" dependencies: @@ -7918,6 +8248,24 @@ __metadata: languageName: node linkType: hard +"is-docker@npm:^2.0.0": + version: 2.2.1 + resolution: "is-docker@npm:2.2.1" + bin: + is-docker: cli.js + checksum: 3fef7ddbf0be25958e8991ad941901bf5922ab2753c46980b60b05c1bf9c9c2402d35e6dc32e4380b980ef5e1970a5d9d5e5aa2e02d77727c3b6b5e918474c56 + languageName: node + linkType: hard + +"is-docker@npm:^3.0.0": + version: 3.0.0 + resolution: "is-docker@npm:3.0.0" + bin: + is-docker: cli.js + checksum: b698118f04feb7eaf3338922bd79cba064ea54a1c3db6ec8c0c8d8ee7613e7e5854d802d3ef646812a8a3ace81182a085dfa0a71cc68b06f3fa794b9783b3c90 + languageName: node + linkType: hard + "is-electron@npm:^2.2.0": version: 2.2.2 resolution: "is-electron@npm:2.2.2" @@ -7964,6 +8312,17 @@ __metadata: languageName: node linkType: hard +"is-inside-container@npm:^1.0.0": + version: 1.0.0 + resolution: "is-inside-container@npm:1.0.0" + dependencies: + is-docker: ^3.0.0 + bin: + is-inside-container: cli.js + checksum: c50b75a2ab66ab3e8b92b3bc534e1ea72ca25766832c0623ac22d134116a98bcf012197d1caabe1d1c4bd5f84363d4aa5c36bb4b585fbcaf57be172cd10a1a03 + languageName: node + linkType: hard + "is-interactive@npm:^1.0.0": version: 1.0.0 resolution: "is-interactive@npm:1.0.0" @@ -7979,9 +8338,9 @@ __metadata: linkType: hard "is-loopback-addr@npm:^2.0.1": - version: 2.0.2 - resolution: "is-loopback-addr@npm:2.0.2" - checksum: c7818bd2815aa7aad1efd410d38f58647759e206b43fcf5b3a35db866ceff9af0b0145f1dcba472267c6e1b965883d1ebf321f3eb18c6a8c1c609c4f91092f2f + version: 2.0.1 + resolution: "is-loopback-addr@npm:2.0.1" + checksum: 77030f7a12875c124b0bb83f172c1fa29fcb14574d15a1d4ed13be71c441b9cdc393370967b2e4bf808c564072933467d48e9ea006a3fd2cb3c7437c6d913108 languageName: node linkType: hard @@ -7992,13 +8351,6 @@ __metadata: languageName: node linkType: hard -"is-network-error@npm:^1.0.0": - version: 1.0.1 - resolution: "is-network-error@npm:1.0.1" - checksum: 165d61500c4186c62db5a3a693d6bfa14ca40fe9b471ef4cd4f27b20ef6760880faf5386dc01ca9867531631782941fedaa94521d09959edf71f046e393c7b91 - languageName: node - linkType: hard - "is-number-object@npm:^1.0.4": version: 1.0.7 resolution: "is-number-object@npm:1.0.7" @@ -8117,7 +8469,7 @@ __metadata: languageName: node linkType: hard -"is-typed-array@npm:^1.1.10, is-typed-array@npm:^1.1.12, is-typed-array@npm:^1.1.3, is-typed-array@npm:^1.1.9": +"is-typed-array@npm:^1.1.10, is-typed-array@npm:^1.1.3, is-typed-array@npm:^1.1.9": version: 1.1.12 resolution: "is-typed-array@npm:1.1.12" dependencies: @@ -8156,6 +8508,15 @@ __metadata: languageName: node linkType: hard +"is-wsl@npm:^2.2.0": + version: 2.2.0 + resolution: "is-wsl@npm:2.2.0" + dependencies: + is-docker: ^2.0.0 + checksum: 20849846ae414997d290b75e16868e5261e86ff5047f104027026fd61d8b5a9b0b3ade16239f35e1a067b3c7cc02f70183cb661010ed16f4b6c7c93dad1b19d8 + languageName: node + linkType: hard + "isarray@npm:^2.0.5": version: 2.0.5 resolution: "isarray@npm:2.0.5" @@ -8177,13 +8538,6 @@ __metadata: languageName: node linkType: hard -"isexe@npm:^3.1.1": - version: 3.1.1 - resolution: "isexe@npm:3.1.1" - checksum: 7fe1931ee4e88eb5aa524cd3ceb8c882537bc3a81b02e438b240e47012eef49c86904d0f0e593ea7c3a9996d18d0f1f3be8d3eaa92333977b0c3a9d353d5563e - languageName: node - linkType: hard - "isobject@npm:^3.0.1": version: 3.0.1 resolution: "isobject@npm:3.0.1" @@ -8200,23 +8554,14 @@ __metadata: languageName: node linkType: hard -"isows@npm:1.0.3": - version: 1.0.3 - resolution: "isows@npm:1.0.3" - peerDependencies: - ws: "*" - checksum: 9cacd5cf59f67deb51e825580cd445ab1725ecb05a67c704050383fb772856f3cd5e7da8ad08f5a3bd2823680d77d099459d0c6a7037972a74d6429af61af440 - languageName: node - linkType: hard - "istanbul-lib-coverage@npm:^3.0.0, istanbul-lib-coverage@npm:^3.2.0": - version: 3.2.2 - resolution: "istanbul-lib-coverage@npm:3.2.2" - checksum: 2367407a8d13982d8f7a859a35e7f8dd5d8f75aae4bb5484ede3a9ea1b426dc245aff28b976a2af48ee759fdd9be374ce2bd2669b644f31e76c5f46a2e29a831 + version: 3.2.0 + resolution: "istanbul-lib-coverage@npm:3.2.0" + checksum: a2a545033b9d56da04a8571ed05c8120bf10e9bce01cf8633a3a2b0d1d83dff4ac4fe78d6d5673c27fc29b7f21a41d75f83a36be09f82a61c367b56aa73c1ff9 languageName: node linkType: hard -"istanbul-lib-instrument@npm:^5.0.4": +"istanbul-lib-instrument@npm:^5.0.4, istanbul-lib-instrument@npm:^5.1.0": version: 5.2.1 resolution: "istanbul-lib-instrument@npm:5.2.1" dependencies: @@ -8229,19 +8574,6 @@ __metadata: languageName: node linkType: hard -"istanbul-lib-instrument@npm:^6.0.0": - version: 6.0.1 - resolution: "istanbul-lib-instrument@npm:6.0.1" - dependencies: - "@babel/core": ^7.12.3 - "@babel/parser": ^7.14.7 - "@istanbuljs/schema": ^0.1.2 - istanbul-lib-coverage: ^3.2.0 - semver: ^7.5.4 - checksum: fb23472e739cfc9b027cefcd7d551d5e7ca7ff2817ae5150fab99fe42786a7f7b56a29a2aa8309c37092e18297b8003f9c274f50ca4360949094d17fbac81472 - languageName: node - linkType: hard - "istanbul-lib-report@npm:^3.0.0": version: 3.0.1 resolution: "istanbul-lib-report@npm:3.0.1" @@ -8275,63 +8607,69 @@ __metadata: linkType: hard "it-all@npm:^3.0.0, it-all@npm:^3.0.1, it-all@npm:^3.0.2": - version: 3.0.4 - resolution: "it-all@npm:3.0.4" - checksum: fb7259660b6555ae268ffde6f0245026e9d4e8afccf9c43a088bb0ff0483aaca95954b6074c1c96d46a57b572bce35fa1bb8542934ce9aee477e1dba46293891 + version: 3.0.2 + resolution: "it-all@npm:3.0.2" + checksum: 37ce90f8ee6a579aa5dd710899912f21d0b2083dad8686d8ca648b2d0c2ffdb4856770e3d004c9e9e4d0a17a9c57221ec501ada98571690ac508021e6227cdab languageName: node linkType: hard "it-batched-bytes@npm:^2.0.2": - version: 2.0.5 - resolution: "it-batched-bytes@npm:2.0.5" + version: 2.0.3 + resolution: "it-batched-bytes@npm:2.0.3" dependencies: p-defer: ^4.0.0 uint8arraylist: ^2.4.1 - checksum: c127881de01aae0d1625b2179ba66f7a084466adef0d5b20ab7129040651545e4aacbff01cf372e5118a2836685463732fa52714a16b16856657f8e05861b899 + checksum: a5bd5c1f2969f1b93dbaa1fbcfe756dee270ffd262485ac1175066a235059b39c09dc9da14f93281bf6ac33381d81483d755ccfc386869ea37441555d98ba30c languageName: node linkType: hard "it-byte-stream@npm:^1.0.0": - version: 1.0.7 - resolution: "it-byte-stream@npm:1.0.7" + version: 1.0.1 + resolution: "it-byte-stream@npm:1.0.1" dependencies: + it-pushable: ^3.2.0 it-stream-types: ^2.0.1 - p-defer: ^4.0.0 - race-signal: ^1.0.1 uint8arraylist: ^2.4.1 - checksum: 9cd63aa3ab2e7ebe1a3cac545f546d9362577901922b7a75803e36caacb0ef46b0cf4bc5334a0acdc548ba976849065527b9467a3c048af64233e70891537aad + checksum: 5ebd79cc48a925937337ba8b9f2ced330f13692e4800c4053f099f190d1d62def8f3071f44fb5f78fd90113e58c9a9612d1e6dd3f2ce153d5c8a87e964bc9a5c languageName: node linkType: hard -"it-drain@npm:^3.0.1, it-drain@npm:^3.0.2": - version: 3.0.5 - resolution: "it-drain@npm:3.0.5" - checksum: 6ab86dc487737a0a87556fab52dadd00f376881b633bd00b8c461f1e8eace47c426e8065700946eb066072e33fc7df7f0e9fa12426bd1d8cac914d52c8f44f43 +"it-drain@npm:^3.0.1": + version: 3.0.2 + resolution: "it-drain@npm:3.0.2" + checksum: 4ad8d8a829c2f35116f07c94bf80a82381b00ee2d431d3d0556c4848013d79fe16df0d71ebf39fc51bc5b75cef296d9a5d4c8101ef1828e1ef31b67087d2e414 + languageName: node + linkType: hard + +"it-drain@npm:^3.0.2": + version: 3.0.3 + resolution: "it-drain@npm:3.0.3" + checksum: 5f918245b6b3de4c0371cad30bdaa2f85f6786305673e325c9c5fe0358f9ec93e1b42ab46790688b0b4cbda71e450f60f3f57fbb8f627a6e96b3ff0d9d35ec2c languageName: node linkType: hard "it-filter@npm:^3.0.0, it-filter@npm:^3.0.1": - version: 3.0.4 - resolution: "it-filter@npm:3.0.4" + version: 3.0.2 + resolution: "it-filter@npm:3.0.2" dependencies: it-peekable: ^3.0.0 - checksum: 8d57903bd99fa1b18ff2c3d0fb7ba0d041a229a33b77ff5ff86ca591e5e0ed0a61b14e937c250754ff1085d8e1c4f88996a4feff76bfc3f73e5fe54726c74dd9 + checksum: 6eea68bcd7ee75ac52dc477149a8e822da8d7626631256179601294412f22635929ee5c0b1566413c721d078ef4bfb8c94b0e2f0d093259909582c6d1d31f1f7 languageName: node linkType: hard "it-first@npm:^3.0.1": - version: 3.0.4 - resolution: "it-first@npm:3.0.4" - checksum: 428cf4b7baaf04dcb0c157cbd6332c2bab9708eeae6df752533d8fd8e21f7c321bfa8a57d35982115f57760baf526a9bf210b7d982d793e8340e22db2aa68fc6 + version: 3.0.2 + resolution: "it-first@npm:3.0.2" + checksum: cb0131bed94e13e13cc02b67fb864c0995dc8deaf8e79a3b4685165f48b331f439caf21d6576e18027308ac03556e9108b517b18c34c0e11ede8a742806dccd8 languageName: node linkType: hard "it-foreach@npm:^2.0.3": - version: 2.0.6 - resolution: "it-foreach@npm:2.0.6" + version: 2.0.4 + resolution: "it-foreach@npm:2.0.4" dependencies: it-peekable: ^3.0.0 - checksum: 95f66b141ced66ca4429711a5d4f36b605005e5607d5e17c2a0357f10ed1b6750e3d49683e029190c1d4ff7a89378fbf9d17b26ded31ddd55741b2a1ddc3d3f2 + checksum: 7133068fb282ac74af2b7482466a72198e1c52393e09276c96e3c40ef5c8cb3f3b76f56a4a7b6dd2d40a7c20c1679bc75192abf6c90579aa803bb4ed5665930d languageName: node linkType: hard @@ -8349,53 +8687,62 @@ __metadata: linkType: hard "it-length-prefixed-stream@npm:^1.0.0": - version: 1.1.6 - resolution: "it-length-prefixed-stream@npm:1.1.6" + version: 1.0.2 + resolution: "it-length-prefixed-stream@npm:1.0.2" dependencies: it-byte-stream: ^1.0.0 + it-length-prefixed: ^9.0.1 it-stream-types: ^2.0.1 uint8-varint: ^2.0.1 uint8arraylist: ^2.4.1 - checksum: 9bba9b781934eb85f68187f4c9128c158a856d0e7d3770e13201cee84829d9d482fb60bcf5eb9ca3ed85f3671a1a27df123e3869c8461cac6929a3a2f349b792 + checksum: 5fcb04352601bc43df96475d1d2c6b9bf40c87a68bc0dece26d8a5280df83f383aa468f154640a28112af9c1f85f24972837cfe538a3fe117da7dc68ac4b57db languageName: node linkType: hard "it-length-prefixed@npm:^9.0.1": - version: 9.0.4 - resolution: "it-length-prefixed@npm:9.0.4" + version: 9.0.1 + resolution: "it-length-prefixed@npm:9.0.1" dependencies: err-code: ^3.0.1 - it-reader: ^6.0.1 it-stream-types: ^2.0.1 - uint8-varint: ^2.0.1 + uint8-varint: ^1.0.1 uint8arraylist: ^2.0.0 - uint8arrays: ^5.0.1 - checksum: 18e7c4a96299c14ec654b2c41784d4e9889d9239ba858acd0a97b6ea0261b504b306c0850b1c28dd9e8678de03ff95f1e81ba03aed6ebf92f6d01b6e9cee3bab + uint8arrays: ^4.0.2 + checksum: a431aedef3450287c9c93cbc4017aebfaf2443cae43abbbd65384a9981f58373ed6cbb505819e60e5e7ac414eefd9f3e9aa1b10cca3726d92c3f0d4671c25b40 languageName: node linkType: hard "it-length@npm:^3.0.1": - version: 3.0.4 - resolution: "it-length@npm:3.0.4" - checksum: 881208cbcad1e3a396b27b35d73acbac9c27eb8b9fa43b1ed1bb4ca1aba489040981e0ea2b3db6fae90d2d9a1e4c610013abef4030ecd80eca64689f07df8dc9 + version: 3.0.2 + resolution: "it-length@npm:3.0.2" + checksum: 688565353598557dc003324fc497896f5d24795f83011d03cc3625ced09207d9e66eda69e055080d1cd3c534673f65c76e5a5c48553b53e66b58a05995b49e81 languageName: node linkType: hard -"it-map@npm:^3.0.1, it-map@npm:^3.0.3": - version: 3.0.5 - resolution: "it-map@npm:3.0.5" +"it-map@npm:^3.0.1": + version: 3.0.3 + resolution: "it-map@npm:3.0.3" dependencies: it-peekable: ^3.0.0 - checksum: bdaa2f1662325457a4eba487dfb04ca8aee0b1d91356b285bf6133aaeda67fba5b7d5c6644838ea8a025e4bd0e8a46910dd7b203f75940ed7ce0d8f3d159bbf3 + checksum: bf48828a40c19d98b49f42730fa3d2821b52056dd755dcdd9b4a1c1077fe8fcd79bf8ae810429e4a82d6cac5b2439e5f3aae8a3c7353c9b5c51ce14d038d9858 languageName: node linkType: hard -"it-merge@npm:^3.0.0, it-merge@npm:^3.0.1": - version: 3.0.3 - resolution: "it-merge@npm:3.0.3" +"it-map@npm:^3.0.3": + version: 3.0.4 + resolution: "it-map@npm:3.0.4" dependencies: - it-pushable: ^3.2.0 - checksum: 031c72302b35db8769c07646c561980c8d97097ce96aa869ebd0cf7b506ea075299b497a177a04bd5eb26398379b3e0b8f4c59a9a1ad0b1e7068d1a921cabf7b + it-peekable: ^3.0.0 + checksum: a5c0cc6ab36b2c2d2aa17189295891ec3b71244e251f99ff79fb5231891de97b79a9851f55f4e30b1cbc06ed1a13e25b1e4c4e4ea326199c86e382acc76de090 + languageName: node + linkType: hard + +"it-merge@npm:^3.0.0": + version: 3.0.1 + resolution: "it-merge@npm:3.0.1" + dependencies: + it-pushable: ^3.1.0 + checksum: 8401b0394868cd02ccd988687a641b2a250668de1a818d7a867650621fb77e6dcd4b041972f738293ab65880b0a3a16b45d94c8b7b42d6b434430c817eee7565 languageName: node linkType: hard @@ -8410,18 +8757,18 @@ __metadata: linkType: hard "it-parallel@npm:^3.0.0": - version: 3.0.6 - resolution: "it-parallel@npm:3.0.6" + version: 3.0.3 + resolution: "it-parallel@npm:3.0.3" dependencies: p-defer: ^4.0.0 - checksum: ca9cc7faea9dee197dd5e683743542da21369c5a3d6991278b0221493d0e801abd7d750ed2860a97e6eeffae6b7c8af9fdd3e61285895317599d8608ccd7576d + checksum: 2326fe2d15134d59dede916b8b72a83092f79b356537921ede24f0b0d95616f2916d8bf1ebd1af6db5326303af2ac654c64dc144df64b8f48b57a95d47c1be5f languageName: node linkType: hard "it-peekable@npm:^3.0.0": - version: 3.0.3 - resolution: "it-peekable@npm:3.0.3" - checksum: 9603045130673b26a572cb2a9bfb7cbf9907fd759aa9dbfb1113b38c07c7b750b75a8dbec317b0cde6e47b6f3be2fddd9785fc7e38f1147ea3ded7eabd590c7a + version: 3.0.1 + resolution: "it-peekable@npm:3.0.1" + checksum: e327caf50bc205672b4133040e4c580cb39f6a8691565f420941ac5b7877ad1ca47c53ecf1ea62e4716abe36786b1cf07f2aea663eda6779fbf0d7a7cb0fbfdc languageName: node linkType: hard @@ -8437,23 +8784,23 @@ __metadata: linkType: hard "it-protobuf-stream@npm:^1.0.0": - version: 1.1.2 - resolution: "it-protobuf-stream@npm:1.1.2" + version: 1.0.2 + resolution: "it-protobuf-stream@npm:1.0.2" dependencies: it-length-prefixed-stream: ^1.0.0 it-stream-types: ^2.0.1 protons-runtime: ^5.0.0 uint8arraylist: ^2.4.1 - checksum: d10601aa530ee53da994377b4704e4f28a45ff26a4da1d64c1beccfcbdc1802da5cf480b692ff692a6557bd2dd0823c4e6992fc525122ab5da8d0ba67f003198 + checksum: a5d724c213f0e9e4f4f42145c43cf0bfe0036dc8f1cae094efd959710046aae399e77a2dd2e6c270a37103ae630386666676dddd6c8cf2138803402eba64dc1a languageName: node linkType: hard -"it-pushable@npm:^3.0.0, it-pushable@npm:^3.1.0, it-pushable@npm:^3.1.2, it-pushable@npm:^3.1.3, it-pushable@npm:^3.2.0, it-pushable@npm:^3.2.1, it-pushable@npm:^3.2.3": - version: 3.2.3 - resolution: "it-pushable@npm:3.2.3" +"it-pushable@npm:^3.0.0, it-pushable@npm:^3.1.0, it-pushable@npm:^3.1.2, it-pushable@npm:^3.1.3, it-pushable@npm:^3.2.0": + version: 3.2.1 + resolution: "it-pushable@npm:3.2.1" dependencies: p-defer: ^4.0.0 - checksum: 8b1d1ceb2a42b31b55119f9721b1f4568c498627470bac18479e6f8db3791fe1185653480cd1c319462bae3d64091bd9ca9e6e90e217e38a5ab7f078559ccca4 + checksum: a23eaac8d1ec86785d0f3e71c67f1544cb1b99aff88c70d3043f3b87a9966c154ca387de76739f91dd744cd7815b40d20e9711a54079cc7ddaf36be54fd10c41 languageName: node linkType: hard @@ -8468,11 +8815,11 @@ __metadata: linkType: hard "it-sort@npm:^3.0.1": - version: 3.0.4 - resolution: "it-sort@npm:3.0.4" + version: 3.0.2 + resolution: "it-sort@npm:3.0.2" dependencies: it-all: ^3.0.0 - checksum: de4f1832c6d12914d51109ca3f8ccebba60fdb050d0af2b3d9b8bcd14cb3d320ba1a01e3ef59de2d3691886c0a903e1c4e46ad354796159d4b0d3d7013bc180c + checksum: d45bb64413d949e1c7c459ad1b4418f14460959d5b73c830368ac18722277c8dcbc606740343ca87441c7c136838b7335a16f9174f0dd8ef03cdc80f8bcfdfbb languageName: node linkType: hard @@ -8484,9 +8831,22 @@ __metadata: linkType: hard "it-take@npm:^3.0.1": - version: 3.0.4 - resolution: "it-take@npm:3.0.4" - checksum: 69dedde350817cba8de80e0432c9b81c35ff2b91f9c80582e657e382ec8c38af003f575353ae22605c963c28605a48cb994c7dba93fedac732db35ee86d7e516 + version: 3.0.2 + resolution: "it-take@npm:3.0.2" + checksum: 50a5449efa0832cd55878b7492f536b558861181786c7a95fb59d93c761646bc4bbc9dd46fd28a5b574b7349fa4caf028bbf66e3a86630473cc5907b811952a3 + languageName: node + linkType: hard + +"jackspeak@npm:^2.0.3": + version: 2.2.2 + resolution: "jackspeak@npm:2.2.2" + dependencies: + "@isaacs/cliui": ^8.0.2 + "@pkgjs/parseargs": ^0.11.0 + dependenciesMeta: + "@pkgjs/parseargs": + optional: true + checksum: 7b1468dd910afc00642db87448f24b062346570b8b47531409aa9012bcb95fdf7ec2b1c48edbb8b57a938c08391f8cc01b5034fc335aa3a2e74dbcc0ee5c555a languageName: node linkType: hard @@ -8510,59 +8870,59 @@ __metadata: languageName: node linkType: hard -"jest-changed-files@npm:^29.7.0": - version: 29.7.0 - resolution: "jest-changed-files@npm:29.7.0" +"jest-changed-files@npm:^29.5.0": + version: 29.5.0 + resolution: "jest-changed-files@npm:29.5.0" dependencies: execa: ^5.0.0 - jest-util: ^29.7.0 p-limit: ^3.1.0 - checksum: 963e203893c396c5dfc75e00a49426688efea7361b0f0e040035809cecd2d46b3c01c02be2d9e8d38b1138357d2de7719ea5b5be21f66c10f2e9685a5a73bb99 + checksum: a67a7cb3c11f8f92bd1b7c79e84f724cbd11a9ad51f3cdadafe3ce7ee3c79ee50dbea128f920f5fddc807e9e4e83f5462143094391feedd959a77dd20ab96cf3 languageName: node linkType: hard -"jest-circus@npm:^29.7.0": - version: 29.7.0 - resolution: "jest-circus@npm:29.7.0" +"jest-circus@npm:^29.6.2": + version: 29.6.2 + resolution: "jest-circus@npm:29.6.2" dependencies: - "@jest/environment": ^29.7.0 - "@jest/expect": ^29.7.0 - "@jest/test-result": ^29.7.0 - "@jest/types": ^29.6.3 + "@jest/environment": ^29.6.2 + "@jest/expect": ^29.6.2 + "@jest/test-result": ^29.6.2 + "@jest/types": ^29.6.1 "@types/node": "*" chalk: ^4.0.0 co: ^4.6.0 dedent: ^1.0.0 is-generator-fn: ^2.0.0 - jest-each: ^29.7.0 - jest-matcher-utils: ^29.7.0 - jest-message-util: ^29.7.0 - jest-runtime: ^29.7.0 - jest-snapshot: ^29.7.0 - jest-util: ^29.7.0 + jest-each: ^29.6.2 + jest-matcher-utils: ^29.6.2 + jest-message-util: ^29.6.2 + jest-runtime: ^29.6.2 + jest-snapshot: ^29.6.2 + jest-util: ^29.6.2 p-limit: ^3.1.0 - pretty-format: ^29.7.0 + pretty-format: ^29.6.2 pure-rand: ^6.0.0 slash: ^3.0.0 stack-utils: ^2.0.3 - checksum: 349437148924a5a109c9b8aad6d393a9591b4dac1918fc97d81b7fc515bc905af9918495055071404af1fab4e48e4b04ac3593477b1d5dcf48c4e71b527c70a7 + checksum: 4f5a96a68c3c808c3d5a9279a2f39a2937386e2cebba5096971f267d79562ce2133a13bc05356a39f8f1ba68fcfe1eb39c4572b3fb0f91affbd932950e89c1e3 languageName: node linkType: hard -"jest-cli@npm:^29.7.0": - version: 29.7.0 - resolution: "jest-cli@npm:29.7.0" +"jest-cli@npm:^29.6.2": + version: 29.6.2 + resolution: "jest-cli@npm:29.6.2" dependencies: - "@jest/core": ^29.7.0 - "@jest/test-result": ^29.7.0 - "@jest/types": ^29.6.3 + "@jest/core": ^29.6.2 + "@jest/test-result": ^29.6.2 + "@jest/types": ^29.6.1 chalk: ^4.0.0 - create-jest: ^29.7.0 exit: ^0.1.2 + graceful-fs: ^4.2.9 import-local: ^3.0.2 - jest-config: ^29.7.0 - jest-util: ^29.7.0 - jest-validate: ^29.7.0 + jest-config: ^29.6.2 + jest-util: ^29.6.2 + jest-validate: ^29.6.2 + prompts: ^2.0.1 yargs: ^17.3.1 peerDependencies: node-notifier: ^8.0.1 || ^9.0.0 || ^10.0.0 @@ -8571,34 +8931,34 @@ __metadata: optional: true bin: jest: bin/jest.js - checksum: 664901277a3f5007ea4870632ed6e7889db9da35b2434e7cb488443e6bf5513889b344b7fddf15112135495b9875892b156faeb2d7391ddb9e2a849dcb7b6c36 + checksum: 0b7b09ae4bd327caf1981eac5a14679ddda3c5c836c9f8ea0ecfe1e5e10e9a39a5ed783fa38d25383604c4d3405595e74b391d955e99aea7e51acb41a59ea108 languageName: node linkType: hard -"jest-config@npm:^29.7.0": - version: 29.7.0 - resolution: "jest-config@npm:29.7.0" +"jest-config@npm:^29.6.2": + version: 29.6.2 + resolution: "jest-config@npm:29.6.2" dependencies: "@babel/core": ^7.11.6 - "@jest/test-sequencer": ^29.7.0 - "@jest/types": ^29.6.3 - babel-jest: ^29.7.0 + "@jest/test-sequencer": ^29.6.2 + "@jest/types": ^29.6.1 + babel-jest: ^29.6.2 chalk: ^4.0.0 ci-info: ^3.2.0 deepmerge: ^4.2.2 glob: ^7.1.3 graceful-fs: ^4.2.9 - jest-circus: ^29.7.0 - jest-environment-node: ^29.7.0 - jest-get-type: ^29.6.3 - jest-regex-util: ^29.6.3 - jest-resolve: ^29.7.0 - jest-runner: ^29.7.0 - jest-util: ^29.7.0 - jest-validate: ^29.7.0 + jest-circus: ^29.6.2 + jest-environment-node: ^29.6.2 + jest-get-type: ^29.4.3 + jest-regex-util: ^29.4.3 + jest-resolve: ^29.6.2 + jest-runner: ^29.6.2 + jest-util: ^29.6.2 + jest-validate: ^29.6.2 micromatch: ^4.0.4 parse-json: ^5.2.0 - pretty-format: ^29.7.0 + pretty-format: ^29.6.2 slash: ^3.0.0 strip-json-comments: ^3.1.1 peerDependencies: @@ -8609,128 +8969,140 @@ __metadata: optional: true ts-node: optional: true - checksum: 4cabf8f894c180cac80b7df1038912a3fc88f96f2622de33832f4b3314f83e22b08fb751da570c0ab2b7988f21604bdabade95e3c0c041068ac578c085cf7dff + checksum: 3bd104a3ac2dd9d34986238142437606354169766dcf88359a7a12ac106d0dc17dcc6b627e4f20db97a58bac5b0502b5436c9cc4722b3629b2a114bba6da9128 languageName: node linkType: hard -"jest-diff@npm:^29.7.0": - version: 29.7.0 - resolution: "jest-diff@npm:29.7.0" +"jest-diff@npm:^29.6.2": + version: 29.6.2 + resolution: "jest-diff@npm:29.6.2" dependencies: chalk: ^4.0.0 - diff-sequences: ^29.6.3 - jest-get-type: ^29.6.3 - pretty-format: ^29.7.0 - checksum: 08e24a9dd43bfba1ef07a6374e5af138f53137b79ec3d5cc71a2303515335898888fa5409959172e1e05de966c9e714368d15e8994b0af7441f0721ee8e1bb77 + diff-sequences: ^29.4.3 + jest-get-type: ^29.4.3 + pretty-format: ^29.6.2 + checksum: 0effd66a0c23f8c139ebf7ca99ed30b479b86fff66f19ad4869f130aaf7ae6a24ca1533f697b7e4930cbe2ddffc85387723fcca673501c653fb77a38f538e959 languageName: node linkType: hard -"jest-docblock@npm:^29.7.0": - version: 29.7.0 - resolution: "jest-docblock@npm:29.7.0" +"jest-docblock@npm:^29.4.3": + version: 29.4.3 + resolution: "jest-docblock@npm:29.4.3" dependencies: detect-newline: ^3.0.0 - checksum: 66390c3e9451f8d96c5da62f577a1dad701180cfa9b071c5025acab2f94d7a3efc2515cfa1654ebe707213241541ce9c5530232cdc8017c91ed64eea1bd3b192 + checksum: e0e9df1485bb8926e5b33478cdf84b3387d9caf3658e7dc1eaa6dc34cb93dea0d2d74797f6e940f0233a88f3dadd60957f2288eb8f95506361f85b84bf8661df languageName: node linkType: hard -"jest-each@npm:^29.7.0": - version: 29.7.0 - resolution: "jest-each@npm:29.7.0" +"jest-each@npm:^29.6.2": + version: 29.6.2 + resolution: "jest-each@npm:29.6.2" dependencies: - "@jest/types": ^29.6.3 + "@jest/types": ^29.6.1 chalk: ^4.0.0 - jest-get-type: ^29.6.3 - jest-util: ^29.7.0 - pretty-format: ^29.7.0 - checksum: e88f99f0184000fc8813f2a0aa79e29deeb63700a3b9b7928b8a418d7d93cd24933608591dbbdea732b473eb2021c72991b5cc51a17966842841c6e28e6f691c + jest-get-type: ^29.4.3 + jest-util: ^29.6.2 + pretty-format: ^29.6.2 + checksum: b64194f4ca27afc6070a42b7ecccbc68be0ded19a849f8cd8f91a2abb23fadae2d38d47559a315f4d1f576927761f3ea437a75ab6cf19206332abb8527d7c165 languageName: node linkType: hard -"jest-environment-node@npm:^29.7.0": - version: 29.7.0 - resolution: "jest-environment-node@npm:29.7.0" +"jest-environment-node@npm:^29.6.2": + version: 29.6.2 + resolution: "jest-environment-node@npm:29.6.2" dependencies: - "@jest/environment": ^29.7.0 - "@jest/fake-timers": ^29.7.0 - "@jest/types": ^29.6.3 + "@jest/environment": ^29.6.2 + "@jest/fake-timers": ^29.6.2 + "@jest/types": ^29.6.1 "@types/node": "*" - jest-mock: ^29.7.0 - jest-util: ^29.7.0 - checksum: 501a9966292cbe0ca3f40057a37587cb6def25e1e0c5e39ac6c650fe78d3c70a2428304341d084ac0cced5041483acef41c477abac47e9a290d5545fd2f15646 + jest-mock: ^29.6.2 + jest-util: ^29.6.2 + checksum: 0b754ac2d3bdb7ce5d6fc28595b9d1c64176f20506b6f773b18b0280ab0b396ed7d927c8519779d3c560fa2b13236ee7077092ccb19a13bea23d40dd30f06450 languageName: node linkType: hard -"jest-get-type@npm:^29.6.3": - version: 29.6.3 - resolution: "jest-get-type@npm:29.6.3" - checksum: 88ac9102d4679d768accae29f1e75f592b760b44277df288ad76ce5bf038c3f5ce3719dea8aa0f035dac30e9eb034b848ce716b9183ad7cc222d029f03e92205 +"jest-get-type@npm:^29.4.3": + version: 29.4.3 + resolution: "jest-get-type@npm:29.4.3" + checksum: 6ac7f2dde1c65e292e4355b6c63b3a4897d7e92cb4c8afcf6d397f2682f8080e094c8b0b68205a74d269882ec06bf696a9de6cd3e1b7333531e5ed7b112605ce languageName: node linkType: hard -"jest-haste-map@npm:^29.7.0": - version: 29.7.0 - resolution: "jest-haste-map@npm:29.7.0" +"jest-haste-map@npm:^29.6.2": + version: 29.6.2 + resolution: "jest-haste-map@npm:29.6.2" dependencies: - "@jest/types": ^29.6.3 + "@jest/types": ^29.6.1 "@types/graceful-fs": ^4.1.3 "@types/node": "*" anymatch: ^3.0.3 fb-watchman: ^2.0.0 fsevents: ^2.3.2 graceful-fs: ^4.2.9 - jest-regex-util: ^29.6.3 - jest-util: ^29.7.0 - jest-worker: ^29.7.0 + jest-regex-util: ^29.4.3 + jest-util: ^29.6.2 + jest-worker: ^29.6.2 micromatch: ^4.0.4 walker: ^1.0.8 dependenciesMeta: fsevents: optional: true - checksum: c2c8f2d3e792a963940fbdfa563ce14ef9e14d4d86da645b96d3cd346b8d35c5ce0b992ee08593939b5f718cf0a1f5a90011a056548a1dbf58397d4356786f01 + checksum: 726233972030eb2e5bce6c9468e497310436b455c88b40e744bd053e20a6f3ff19aec340edcbd89537c629ed5cf8916506bc895d690cc39a0862c74dcd95b7b8 languageName: node linkType: hard -"jest-leak-detector@npm:^29.7.0": - version: 29.7.0 - resolution: "jest-leak-detector@npm:29.7.0" +"jest-leak-detector@npm:^29.6.2": + version: 29.6.2 + resolution: "jest-leak-detector@npm:29.6.2" dependencies: - jest-get-type: ^29.6.3 - pretty-format: ^29.7.0 - checksum: e3950e3ddd71e1d0c22924c51a300a1c2db6cf69ec1e51f95ccf424bcc070f78664813bef7aed4b16b96dfbdeea53fe358f8aeaaea84346ae15c3735758f1605 + jest-get-type: ^29.4.3 + pretty-format: ^29.6.2 + checksum: e00152acdba8aa8f9334775b77375947508051c34646fbeb702275da2b6ac6145f8cad6d5893112e76484d00fa8c0b4fd71b78ab0b4ef34950f5b6a84f37ae67 languageName: node linkType: hard -"jest-matcher-utils@npm:^29.7.0": - version: 29.7.0 - resolution: "jest-matcher-utils@npm:29.7.0" +"jest-matcher-utils@npm:^29.6.2": + version: 29.6.2 + resolution: "jest-matcher-utils@npm:29.6.2" dependencies: chalk: ^4.0.0 - jest-diff: ^29.7.0 - jest-get-type: ^29.6.3 - pretty-format: ^29.7.0 - checksum: d7259e5f995d915e8a37a8fd494cb7d6af24cd2a287b200f831717ba0d015190375f9f5dc35393b8ba2aae9b2ebd60984635269c7f8cff7d85b077543b7744cd + jest-diff: ^29.6.2 + jest-get-type: ^29.4.3 + pretty-format: ^29.6.2 + checksum: 3e1b65dd30d05f75fe56dc45fbe4135aec2ff96a3d1e21afbf6a66f3a45a7e29cd0fd37cf80b9564e0381d6205833f77ccaf766c6f7e1aad6b7924d117be504e languageName: node linkType: hard -"jest-message-util@npm:^29.7.0": - version: 29.7.0 - resolution: "jest-message-util@npm:29.7.0" +"jest-message-util@npm:^29.6.2": + version: 29.6.2 + resolution: "jest-message-util@npm:29.6.2" dependencies: "@babel/code-frame": ^7.12.13 - "@jest/types": ^29.6.3 + "@jest/types": ^29.6.1 "@types/stack-utils": ^2.0.0 chalk: ^4.0.0 graceful-fs: ^4.2.9 micromatch: ^4.0.4 - pretty-format: ^29.7.0 + pretty-format: ^29.6.2 slash: ^3.0.0 stack-utils: ^2.0.3 - checksum: a9d025b1c6726a2ff17d54cc694de088b0489456c69106be6b615db7a51b7beb66788bea7a59991a019d924fbf20f67d085a445aedb9a4d6760363f4d7d09930 + checksum: e8e3c8d2301e2ca4038ed6df8cbba7fedc6949d1ede4c0e3f1f44f53afb56d77eb35983fa460140d0eadeab99a5f3ae04b703fe77cd7b316b40b361228b5aa1a + languageName: node + linkType: hard + +"jest-mock-extended@npm:^3.0.3, jest-mock-extended@npm:^3.0.4": + version: 3.0.4 + resolution: "jest-mock-extended@npm:3.0.4" + dependencies: + ts-essentials: ^7.0.3 + peerDependencies: + jest: ^24.0.0 || ^25.0.0 || ^26.0.0 || ^27.0.0 || ^28.0.0 || ^29.0.0 + typescript: ^3.0.0 || ^4.0.0 || ^5.0.0 + checksum: f861253c63508b30d971fbbbc1bf2911ff4406cd260d0e23483a1d4514898b18ba5efbd43fbdf6d94996dc09b20eb1aad1b46aeaea9c99244ba12dc99814fd3f languageName: node linkType: hard -"jest-mock-extended@npm:^3.0.3, jest-mock-extended@npm:^3.0.4, jest-mock-extended@npm:^3.0.5": +"jest-mock-extended@npm:^3.0.5": version: 3.0.5 resolution: "jest-mock-extended@npm:3.0.5" dependencies: @@ -8742,14 +9114,14 @@ __metadata: languageName: node linkType: hard -"jest-mock@npm:^29.7.0": - version: 29.7.0 - resolution: "jest-mock@npm:29.7.0" +"jest-mock@npm:^29.6.2": + version: 29.6.2 + resolution: "jest-mock@npm:29.6.2" dependencies: - "@jest/types": ^29.6.3 + "@jest/types": ^29.6.1 "@types/node": "*" - jest-util: ^29.7.0 - checksum: 81ba9b68689a60be1482212878973700347cb72833c5e5af09895882b9eb5c4e02843a1bbdf23f94c52d42708bab53a30c45a3482952c9eec173d1eaac5b86c5 + jest-util: ^29.6.2 + checksum: 0bacb5d58441462c0e531ec4d2f7377eecbe21f664d8a460e72f94ba61d22635028931678e7a0f1c3e3f5894973db8e409432f7db4c01283456c8fdbd85f5b3b languageName: node linkType: hard @@ -8765,168 +9137,168 @@ __metadata: languageName: node linkType: hard -"jest-regex-util@npm:^29.6.3": - version: 29.6.3 - resolution: "jest-regex-util@npm:29.6.3" - checksum: 0518beeb9bf1228261695e54f0feaad3606df26a19764bc19541e0fc6e2a3737191904607fb72f3f2ce85d9c16b28df79b7b1ec9443aa08c3ef0e9efda6f8f2a +"jest-regex-util@npm:^29.4.3": + version: 29.4.3 + resolution: "jest-regex-util@npm:29.4.3" + checksum: 96fc7fc28cd4dd73a63c13a526202c4bd8b351d4e5b68b1a2a2c88da3308c2a16e26feaa593083eb0bac38cca1aa9dd05025412e7de013ba963fb8e66af22b8a languageName: node linkType: hard -"jest-resolve-dependencies@npm:^29.7.0": - version: 29.7.0 - resolution: "jest-resolve-dependencies@npm:29.7.0" +"jest-resolve-dependencies@npm:^29.6.2": + version: 29.6.2 + resolution: "jest-resolve-dependencies@npm:29.6.2" dependencies: - jest-regex-util: ^29.6.3 - jest-snapshot: ^29.7.0 - checksum: aeb75d8150aaae60ca2bb345a0d198f23496494677cd6aefa26fc005faf354061f073982175daaf32b4b9d86b26ca928586344516e3e6969aa614cb13b883984 + jest-regex-util: ^29.4.3 + jest-snapshot: ^29.6.2 + checksum: d40ee11af2c9d2ef0dbbcf9a5b7dda37c2b86cf4e5de1705795919fd8927907569115c502116ab56de0dca576d5faa31ec9b636240333b6830a568a63004da17 languageName: node linkType: hard -"jest-resolve@npm:^29.7.0": - version: 29.7.0 - resolution: "jest-resolve@npm:29.7.0" +"jest-resolve@npm:^29.6.2": + version: 29.6.2 + resolution: "jest-resolve@npm:29.6.2" dependencies: chalk: ^4.0.0 graceful-fs: ^4.2.9 - jest-haste-map: ^29.7.0 + jest-haste-map: ^29.6.2 jest-pnp-resolver: ^1.2.2 - jest-util: ^29.7.0 - jest-validate: ^29.7.0 + jest-util: ^29.6.2 + jest-validate: ^29.6.2 resolve: ^1.20.0 resolve.exports: ^2.0.0 slash: ^3.0.0 - checksum: 0ca218e10731aa17920526ec39deaec59ab9b966237905ffc4545444481112cd422f01581230eceb7e82d86f44a543d520a71391ec66e1b4ef1a578bd5c73487 + checksum: 01721957e61821a576b2ded043eeab8b392166e0e6d8d680f75657737e2ea7481ff29c2716b866ccd12e743f3a8da465504b1028e78b6a3c68b9561303de7ec8 languageName: node linkType: hard -"jest-runner@npm:^29.7.0": - version: 29.7.0 - resolution: "jest-runner@npm:29.7.0" +"jest-runner@npm:^29.6.2": + version: 29.6.2 + resolution: "jest-runner@npm:29.6.2" dependencies: - "@jest/console": ^29.7.0 - "@jest/environment": ^29.7.0 - "@jest/test-result": ^29.7.0 - "@jest/transform": ^29.7.0 - "@jest/types": ^29.6.3 + "@jest/console": ^29.6.2 + "@jest/environment": ^29.6.2 + "@jest/test-result": ^29.6.2 + "@jest/transform": ^29.6.2 + "@jest/types": ^29.6.1 "@types/node": "*" chalk: ^4.0.0 emittery: ^0.13.1 graceful-fs: ^4.2.9 - jest-docblock: ^29.7.0 - jest-environment-node: ^29.7.0 - jest-haste-map: ^29.7.0 - jest-leak-detector: ^29.7.0 - jest-message-util: ^29.7.0 - jest-resolve: ^29.7.0 - jest-runtime: ^29.7.0 - jest-util: ^29.7.0 - jest-watcher: ^29.7.0 - jest-worker: ^29.7.0 + jest-docblock: ^29.4.3 + jest-environment-node: ^29.6.2 + jest-haste-map: ^29.6.2 + jest-leak-detector: ^29.6.2 + jest-message-util: ^29.6.2 + jest-resolve: ^29.6.2 + jest-runtime: ^29.6.2 + jest-util: ^29.6.2 + jest-watcher: ^29.6.2 + jest-worker: ^29.6.2 p-limit: ^3.1.0 source-map-support: 0.5.13 - checksum: f0405778ea64812bf9b5c50b598850d94ccf95d7ba21f090c64827b41decd680ee19fcbb494007cdd7f5d0d8906bfc9eceddd8fa583e753e736ecd462d4682fb + checksum: 46bd506a08ddf79628a509aed4105ab74c0b03727a3e24c90bbc2915531860b3da99f7ace2fd9603194440553cffac9cfb1a3b7d0ce03d5fc9c5f2d5ffbb3d3f languageName: node linkType: hard -"jest-runtime@npm:^29.7.0": - version: 29.7.0 - resolution: "jest-runtime@npm:29.7.0" +"jest-runtime@npm:^29.6.2": + version: 29.6.2 + resolution: "jest-runtime@npm:29.6.2" dependencies: - "@jest/environment": ^29.7.0 - "@jest/fake-timers": ^29.7.0 - "@jest/globals": ^29.7.0 - "@jest/source-map": ^29.6.3 - "@jest/test-result": ^29.7.0 - "@jest/transform": ^29.7.0 - "@jest/types": ^29.6.3 + "@jest/environment": ^29.6.2 + "@jest/fake-timers": ^29.6.2 + "@jest/globals": ^29.6.2 + "@jest/source-map": ^29.6.0 + "@jest/test-result": ^29.6.2 + "@jest/transform": ^29.6.2 + "@jest/types": ^29.6.1 "@types/node": "*" chalk: ^4.0.0 cjs-module-lexer: ^1.0.0 collect-v8-coverage: ^1.0.0 glob: ^7.1.3 graceful-fs: ^4.2.9 - jest-haste-map: ^29.7.0 - jest-message-util: ^29.7.0 - jest-mock: ^29.7.0 - jest-regex-util: ^29.6.3 - jest-resolve: ^29.7.0 - jest-snapshot: ^29.7.0 - jest-util: ^29.7.0 + jest-haste-map: ^29.6.2 + jest-message-util: ^29.6.2 + jest-mock: ^29.6.2 + jest-regex-util: ^29.4.3 + jest-resolve: ^29.6.2 + jest-snapshot: ^29.6.2 + jest-util: ^29.6.2 slash: ^3.0.0 strip-bom: ^4.0.0 - checksum: d19f113d013e80691e07047f68e1e3448ef024ff2c6b586ce4f90cd7d4c62a2cd1d460110491019719f3c59bfebe16f0e201ed005ef9f80e2cf798c374eed54e + checksum: 8e7e4486b23b01a9c407313681bed0def39680c2ae21cf01347f111983252ec3a024c56493c5411fed53633f02863eed0816099110cbe04b3889aa5babf1042d languageName: node linkType: hard -"jest-snapshot@npm:^29.7.0": - version: 29.7.0 - resolution: "jest-snapshot@npm:29.7.0" +"jest-snapshot@npm:^29.6.2": + version: 29.6.2 + resolution: "jest-snapshot@npm:29.6.2" dependencies: "@babel/core": ^7.11.6 "@babel/generator": ^7.7.2 "@babel/plugin-syntax-jsx": ^7.7.2 "@babel/plugin-syntax-typescript": ^7.7.2 "@babel/types": ^7.3.3 - "@jest/expect-utils": ^29.7.0 - "@jest/transform": ^29.7.0 - "@jest/types": ^29.6.3 + "@jest/expect-utils": ^29.6.2 + "@jest/transform": ^29.6.2 + "@jest/types": ^29.6.1 babel-preset-current-node-syntax: ^1.0.0 chalk: ^4.0.0 - expect: ^29.7.0 + expect: ^29.6.2 graceful-fs: ^4.2.9 - jest-diff: ^29.7.0 - jest-get-type: ^29.6.3 - jest-matcher-utils: ^29.7.0 - jest-message-util: ^29.7.0 - jest-util: ^29.7.0 + jest-diff: ^29.6.2 + jest-get-type: ^29.4.3 + jest-matcher-utils: ^29.6.2 + jest-message-util: ^29.6.2 + jest-util: ^29.6.2 natural-compare: ^1.4.0 - pretty-format: ^29.7.0 + pretty-format: ^29.6.2 semver: ^7.5.3 - checksum: 86821c3ad0b6899521ce75ee1ae7b01b17e6dfeff9166f2cf17f012e0c5d8c798f30f9e4f8f7f5bed01ea7b55a6bc159f5eda778311162cbfa48785447c237ad + checksum: c1c70a9dbce7fca62ed73ac38234b4ee643e8b667acf71b4417ab67776c1188bb08b8ad450e56a2889ad182903ffd416386fa8082a477724ccf8d8c29a4c6906 languageName: node linkType: hard -"jest-util@npm:^29.0.0, jest-util@npm:^29.7.0": - version: 29.7.0 - resolution: "jest-util@npm:29.7.0" +"jest-util@npm:^29.0.0, jest-util@npm:^29.6.2": + version: 29.6.2 + resolution: "jest-util@npm:29.6.2" dependencies: - "@jest/types": ^29.6.3 + "@jest/types": ^29.6.1 "@types/node": "*" chalk: ^4.0.0 ci-info: ^3.2.0 graceful-fs: ^4.2.9 picomatch: ^2.2.3 - checksum: 042ab4980f4ccd4d50226e01e5c7376a8556b472442ca6091a8f102488c0f22e6e8b89ea874111d2328a2080083bf3225c86f3788c52af0bd0345a00eb57a3ca + checksum: 8aedc0c80083d0cabd6c6c4f04dea1cbcac609fd7bc3b1fc05a3999291bd6e63dd52b0c806f9378d5cae28eff5a6191709a4987861001293f8d03e53984adca4 languageName: node linkType: hard -"jest-validate@npm:^29.7.0": - version: 29.7.0 - resolution: "jest-validate@npm:29.7.0" +"jest-validate@npm:^29.6.2": + version: 29.6.2 + resolution: "jest-validate@npm:29.6.2" dependencies: - "@jest/types": ^29.6.3 + "@jest/types": ^29.6.1 camelcase: ^6.2.0 chalk: ^4.0.0 - jest-get-type: ^29.6.3 + jest-get-type: ^29.4.3 leven: ^3.1.0 - pretty-format: ^29.7.0 - checksum: 191fcdc980f8a0de4dbdd879fa276435d00eb157a48683af7b3b1b98b0f7d9de7ffe12689b617779097ff1ed77601b9f7126b0871bba4f776e222c40f62e9dae + pretty-format: ^29.6.2 + checksum: 32648d002189c0ad8a958eace7c6b7d05ea1dc440a1b91e0f22dc1aef489899446ec80b2d527fd13713862d89dfb4606e24a3bf8a10c4ddac3c911e93b7f0374 languageName: node linkType: hard -"jest-watcher@npm:^29.7.0": - version: 29.7.0 - resolution: "jest-watcher@npm:29.7.0" +"jest-watcher@npm:^29.6.2": + version: 29.6.2 + resolution: "jest-watcher@npm:29.6.2" dependencies: - "@jest/test-result": ^29.7.0 - "@jest/types": ^29.6.3 + "@jest/test-result": ^29.6.2 + "@jest/types": ^29.6.1 "@types/node": "*" ansi-escapes: ^4.2.1 chalk: ^4.0.0 emittery: ^0.13.1 - jest-util: ^29.7.0 + jest-util: ^29.6.2 string-length: ^4.0.1 - checksum: 67e6e7fe695416deff96b93a14a561a6db69389a0667e9489f24485bb85e5b54e12f3b2ba511ec0b777eca1e727235b073e3ebcdd473d68888650489f88df92f + checksum: 14624190fc8b5fbae466a2ec81458a88c15716d99f042bb4674d53e9623d305cb2905bc1dffeda05fd1a10a05c2a83efe5ac41942477e2b15eaebb08d0aaab32 languageName: node linkType: hard @@ -8941,26 +9313,26 @@ __metadata: languageName: node linkType: hard -"jest-worker@npm:^29.7.0": - version: 29.7.0 - resolution: "jest-worker@npm:29.7.0" +"jest-worker@npm:^29.6.2": + version: 29.6.2 + resolution: "jest-worker@npm:29.6.2" dependencies: "@types/node": "*" - jest-util: ^29.7.0 + jest-util: ^29.6.2 merge-stream: ^2.0.0 supports-color: ^8.0.0 - checksum: 30fff60af49675273644d408b650fc2eb4b5dcafc5a0a455f238322a8f9d8a98d847baca9d51ff197b6747f54c7901daa2287799230b856a0f48287d131f8c13 + checksum: 11035564534bf181ead80b25be138c2d42372bd5626151a3e705200d47a74fd9da3ca79f8a7b15806cdc325ad73c3d21d23acceeed99d50941589ff02915ed38 languageName: node linkType: hard "jest@npm:^29.5.0": - version: 29.7.0 - resolution: "jest@npm:29.7.0" + version: 29.6.2 + resolution: "jest@npm:29.6.2" dependencies: - "@jest/core": ^29.7.0 - "@jest/types": ^29.6.3 + "@jest/core": ^29.6.2 + "@jest/types": ^29.6.1 import-local: ^3.0.2 - jest-cli: ^29.7.0 + jest-cli: ^29.6.2 peerDependencies: node-notifier: ^8.0.1 || ^9.0.0 || ^10.0.0 peerDependenciesMeta: @@ -8968,7 +9340,7 @@ __metadata: optional: true bin: jest: bin/jest.js - checksum: 17ca8d67504a7dbb1998cf3c3077ec9031ba3eb512da8d71cb91bcabb2b8995c4e4b292b740cb9bf1cbff5ce3e110b3f7c777b0cefb6f41ab05445f248d0ee0b + checksum: dd63facd4e6aefc35d2c42acd7e4c9fb0d8fe4705df4b3ccedd953605424d7aa89c88af8cf4c9951752709cac081d29c35b264e1794643d5688ea724ccc9a485 languageName: node linkType: hard @@ -9025,16 +9397,9 @@ __metadata: languageName: node linkType: hard -"json-buffer@npm:3.0.1": - version: 3.0.1 - resolution: "json-buffer@npm:3.0.1" - checksum: 9026b03edc2847eefa2e37646c579300a1f3a4586cfb62bf857832b60c852042d0d6ae55d1afb8926163fa54c2b01d83ae24705f34990348bdac6273a29d4581 - languageName: node - linkType: hard - "json-joy@npm:^9.2.0": - version: 9.9.1 - resolution: "json-joy@npm:9.9.1" + version: 9.6.0 + resolution: "json-joy@npm:9.6.0" dependencies: arg: ^5.0.2 hyperdyperid: ^1.2.0 @@ -9051,7 +9416,7 @@ __metadata: json-pointer: bin/json-pointer.js json-pointer-test: bin/json-pointer-test.js json-unpack: bin/json-unpack.js - checksum: d165398682f00019796225faf365cd8d060f3e086af39bb5081c30907b7e52eaf13697d1c0f6ee2b010fe255ae1fd776e05ad7d6ee5fb549e98fe982f560884b + checksum: 517b1b466b1b477bd53ecf23693758e785cebba5bf32dbb04059164b067330246efd35f11df13e8a04fb85ec4c330f806e2752736189a12f1a4ff1a1e423ec27 languageName: node linkType: hard @@ -9087,7 +9452,7 @@ __metadata: languageName: node linkType: hard -"json5@npm:^2.2.3": +"json5@npm:^2.2.2, json5@npm:^2.2.3": version: 2.2.3 resolution: "json5@npm:2.2.3" bin: @@ -9097,9 +9462,9 @@ __metadata: linkType: hard "jsonc-parser@npm:^3.2.0": - version: 3.2.1 - resolution: "jsonc-parser@npm:3.2.1" - checksum: 656d9027b91de98d8ab91b3aa0d0a4cab7dc798a6830845ca664f3e76c82d46b973675bbe9b500fae1de37fd3e81aceacbaa2a57884bf2f8f29192150d2d1ef7 + version: 3.2.0 + resolution: "jsonc-parser@npm:3.2.0" + checksum: 946dd9a5f326b745aa326d48a7257e3f4a4b62c5e98ec8e49fa2bdd8d96cef7e6febf1399f5c7016114fd1f68a1c62c6138826d5d90bc650448e3cf0951c53c7 languageName: node linkType: hard @@ -9149,15 +9514,6 @@ __metadata: languageName: node linkType: hard -"keyv@npm:^4.5.3": - version: 4.5.4 - resolution: "keyv@npm:4.5.4" - dependencies: - json-buffer: 3.0.1 - checksum: 74a24395b1c34bd44ad5cb2b49140d087553e170625240b86755a6604cd65aa16efdbdeae5cdb17ba1284a0fbb25ad06263755dbc71b8d8b06f74232ce3cdd72 - languageName: node - linkType: hard - "kind-of@npm:^6.0.2, kind-of@npm:^6.0.3": version: 6.0.3 resolution: "kind-of@npm:6.0.3" @@ -9227,15 +9583,14 @@ __metadata: linkType: hard "koa-router@npm:^12.0.0": - version: 12.0.1 - resolution: "koa-router@npm:12.0.1" + version: 12.0.0 + resolution: "koa-router@npm:12.0.0" dependencies: - debug: ^4.3.4 http-errors: ^2.0.0 koa-compose: ^4.1.0 methods: ^1.1.2 path-to-regexp: ^6.2.1 - checksum: 62852af6c47dba9327c993594367b8bcd995fd49972d2539eae9afa231571680f75ebe0f439bf886256cdfaf791900c2d11fe31b894d9ba8c60d1c727c03b9ed + checksum: 29b02fd96972c037e805f6ce2626c971f4fd9cba04005bfedc080ab425d31b4b1cfe2ebc000b26e4a45e68215a3a3ed557f836ba486ea0d2f1e7e78fc95f8dca languageName: node linkType: hard @@ -9261,14 +9616,14 @@ __metadata: linkType: hard "koa@npm:^2.14.2": - version: 2.15.0 - resolution: "koa@npm:2.15.0" + version: 2.14.2 + resolution: "koa@npm:2.14.2" dependencies: accepts: ^1.3.5 cache-content-type: ^1.0.0 content-disposition: ~0.5.2 content-type: ^1.0.4 - cookies: ~0.9.0 + cookies: ~0.8.0 debug: ^4.3.2 delegates: ^1.0.0 depd: ^2.0.0 @@ -9287,7 +9642,7 @@ __metadata: statuses: ^1.5.0 type-is: ^1.6.16 vary: ^1.1.2 - checksum: a97741f89f328f25ae94d82d0ee608377d89e086c73f2d868023e6050dea682ef93e0a5c80097f3aaad28121853aea50a7fb3c0c12ecc45798da2fd1255f580b + checksum: 17fe3b8f5e0b4759004a942cc6ba2a9507299943a697dff9766b85f41f45caed4077ca2645ac9ad254d3359fffedfc4c9ebdd7a70493e5df8cdfac159a8ee835 languageName: node linkType: hard @@ -9375,25 +9730,26 @@ __metadata: linkType: hard "libp2p@npm:^0.46.6": - version: 0.46.21 - resolution: "libp2p@npm:0.46.21" + version: 0.46.6 + resolution: "libp2p@npm:0.46.6" dependencies: "@achingbrain/nat-port-mapper": ^1.0.9 - "@libp2p/crypto": ^2.0.8 - "@libp2p/interface": ^0.1.6 - "@libp2p/interface-internal": ^0.1.9 - "@libp2p/keychain": ^3.0.8 - "@libp2p/logger": ^3.1.0 - "@libp2p/multistream-select": ^4.0.6 - "@libp2p/peer-collections": ^4.0.8 - "@libp2p/peer-id": ^3.0.6 - "@libp2p/peer-id-factory": ^3.0.8 - "@libp2p/peer-record": ^6.0.9 - "@libp2p/peer-store": ^9.0.9 - "@libp2p/utils": ^4.0.7 + "@libp2p/crypto": ^2.0.3 + "@libp2p/interface": ^0.1.2 + "@libp2p/interface-internal": ^0.1.4 + "@libp2p/keychain": ^3.0.3 + "@libp2p/logger": ^3.0.2 + "@libp2p/multistream-select": ^4.0.2 + "@libp2p/peer-collections": ^4.0.3 + "@libp2p/peer-id": ^3.0.2 + "@libp2p/peer-id-factory": ^3.0.3 + "@libp2p/peer-record": ^6.0.3 + "@libp2p/peer-store": ^9.0.3 + "@libp2p/utils": ^4.0.2 "@multiformats/mafmt": ^12.1.2 "@multiformats/multiaddr": ^12.1.5 "@multiformats/multiaddr-matcher": ^1.0.0 + abortable-iterator: ^5.0.1 any-signal: ^4.1.1 datastore-core: ^9.0.1 delay: ^6.0.0 @@ -9415,15 +9771,15 @@ __metadata: multiformats: ^12.0.1 p-defer: ^4.0.0 p-queue: ^7.3.4 - p-retry: ^6.0.0 + p-retry: ^5.0.0 private-ip: ^3.0.0 protons-runtime: ^5.0.0 - rate-limiter-flexible: ^3.0.0 + rate-limiter-flexible: ^2.3.11 uint8arraylist: ^2.4.3 uint8arrays: ^4.0.6 wherearewe: ^2.0.1 xsalsa20: ^1.1.0 - checksum: 6f69e89e8f4ac28ee1b365db878379d28865982bf6d4cec9524d9445975b5eab4fa5dca581e704ce1438b196258e88856cf6778048b59a75ae09ca1b9417fbb8 + checksum: c23d6621f1ed7d5c96de440fb08bfc807a8fc38c44e54ed6422f9414414dae0c7e87675f9416eacd5b143a2ff91fba26e6001bdde670f80696c4e73574fc228e languageName: node linkType: hard @@ -9444,15 +9800,15 @@ __metadata: linkType: hard "lmdb@npm:^2.9.1": - version: 2.9.2 - resolution: "lmdb@npm:2.9.2" - dependencies: - "@lmdb/lmdb-darwin-arm64": 2.9.2 - "@lmdb/lmdb-darwin-x64": 2.9.2 - "@lmdb/lmdb-linux-arm": 2.9.2 - "@lmdb/lmdb-linux-arm64": 2.9.2 - "@lmdb/lmdb-linux-x64": 2.9.2 - "@lmdb/lmdb-win32-x64": 2.9.2 + version: 2.9.1 + resolution: "lmdb@npm:2.9.1" + dependencies: + "@lmdb/lmdb-darwin-arm64": 2.9.1 + "@lmdb/lmdb-darwin-x64": 2.9.1 + "@lmdb/lmdb-linux-arm": 2.9.1 + "@lmdb/lmdb-linux-arm64": 2.9.1 + "@lmdb/lmdb-linux-x64": 2.9.1 + "@lmdb/lmdb-win32-x64": 2.9.1 msgpackr: ^1.9.9 node-addon-api: ^6.1.0 node-gyp: latest @@ -9474,7 +9830,7 @@ __metadata: optional: true bin: download-lmdb-prebuilds: bin/download-prebuilds.js - checksum: b2471c4d2c36f15a27233ae1eece86fcbb40613574ec54245cf697b16b1b35c70c72e4092dc739407df145f14b7c1b6b56c4281a439c314e79a5338df8b2b63b + checksum: 1f0a8754cc019586c8e34bd45e4ee1df99f6f5732e8dc04f951cf631895a179dfd913123773206935a580cfe80bce117800a3ccf0a2cc8187821badfdaa71cd4 languageName: node linkType: hard @@ -9631,23 +9987,33 @@ __metadata: linkType: hard "logform@npm:^2.3.2, logform@npm:^2.4.0": - version: 2.6.0 - resolution: "logform@npm:2.6.0" + version: 2.5.1 + resolution: "logform@npm:2.5.1" dependencies: - "@colors/colors": 1.6.0 + "@colors/colors": 1.5.0 "@types/triple-beam": ^1.3.2 fecha: ^4.2.0 ms: ^2.1.1 safe-stable-stringify: ^2.3.1 triple-beam: ^1.3.0 - checksum: b9ea74bb75e55379ad0eb3e4d65ae6e8d02bc45b431c218162878bf663997ab9258a73104c2b30e09dd2db288bb83c8bf8748e46689d75f5e7e34cf69378d6df + checksum: 08fdf03be5bb69af33bac214eb4f6a0c83ad3821a30de498925fccb61e993e5a4a87470aab356ca2110c11e4643685bed5597ca5f46dd1cd11437c44a0e0e3c2 + languageName: node + linkType: hard + +"long@npm:^5.0.0": + version: 5.2.3 + resolution: "long@npm:5.2.3" + checksum: 885ede7c3de4facccbd2cacc6168bae3a02c3e836159ea4252c87b6e34d40af819824b2d4edce330bfb5c4d6e8ce3ec5864bdcf9473fa1f53a4f8225860e5897 languageName: node linkType: hard -"lru-cache@npm:^10.0.1, lru-cache@npm:^9.1.1 || ^10.0.0": - version: 10.1.0 - resolution: "lru-cache@npm:10.1.0" - checksum: 58056d33e2500fbedce92f8c542e7c11b50d7d086578f14b7074d8c241422004af0718e08a6eaae8705cee09c77e39a61c1c79e9370ba689b7010c152e6a76ab +"longbits@npm:^1.1.0": + version: 1.1.0 + resolution: "longbits@npm:1.1.0" + dependencies: + byte-access: ^1.0.1 + uint8arraylist: ^2.0.0 + checksum: 7f8ec8ddef64b160da22c31875fa549b40a4cff626c948c423dc2cb73c0d85a5c6a13ce3a611b5229fcf65dd6ffb4fcd1d5eaef61458570194172ad485112521 languageName: node linkType: hard @@ -9669,13 +10035,20 @@ __metadata: languageName: node linkType: hard -"lru-cache@npm:^7.14.1": +"lru-cache@npm:^7.14.1, lru-cache@npm:^7.7.1": version: 7.18.3 resolution: "lru-cache@npm:7.18.3" checksum: e550d772384709deea3f141af34b6d4fa392e2e418c1498c078de0ee63670f1f46f5eee746e8ef7e69e1c895af0d4224e62ee33e66a543a14763b0f2e74c1356 languageName: node linkType: hard +"lru-cache@npm:^9.1.1 || ^10.0.0": + version: 10.0.0 + resolution: "lru-cache@npm:10.0.0" + checksum: 18f101675fe283bc09cda0ef1e3cc83781aeb8373b439f086f758d1d91b28730950db785999cd060d3c825a8571c03073e8c14512b6655af2188d623031baf50 + languageName: node + linkType: hard + "ltgt@npm:^2.2.0": version: 2.2.1 resolution: "ltgt@npm:2.2.1" @@ -9743,22 +10116,26 @@ __metadata: languageName: node linkType: hard -"make-fetch-happen@npm:^13.0.0": - version: 13.0.0 - resolution: "make-fetch-happen@npm:13.0.0" +"make-fetch-happen@npm:^11.0.3": + version: 11.1.1 + resolution: "make-fetch-happen@npm:11.1.1" dependencies: - "@npmcli/agent": ^2.0.0 - cacache: ^18.0.0 + agentkeepalive: ^4.2.1 + cacache: ^17.0.0 http-cache-semantics: ^4.1.1 + http-proxy-agent: ^5.0.0 + https-proxy-agent: ^5.0.0 is-lambda: ^1.0.1 - minipass: ^7.0.2 + lru-cache: ^7.7.1 + minipass: ^5.0.0 minipass-fetch: ^3.0.0 minipass-flush: ^1.0.5 minipass-pipeline: ^1.2.4 negotiator: ^0.6.3 promise-retry: ^2.0.1 + socks-proxy-agent: ^7.0.0 ssri: ^10.0.0 - checksum: 7c7a6d381ce919dd83af398b66459a10e2fe8f4504f340d1d090d3fa3d1b0c93750220e1d898114c64467223504bd258612ba83efbc16f31b075cd56de24b4af + checksum: 7268bf274a0f6dcf0343829489a4506603ff34bd0649c12058753900b0eb29191dce5dba12680719a5d0a983d3e57810f594a12f3c18494e93a1fbc6348a4540 languageName: node linkType: hard @@ -9975,15 +10352,6 @@ __metadata: languageName: node linkType: hard -"minimatch@npm:9.0.3, minimatch@npm:^9.0.0, minimatch@npm:^9.0.1": - version: 9.0.3 - resolution: "minimatch@npm:9.0.3" - dependencies: - brace-expansion: ^2.0.1 - checksum: 253487976bf485b612f16bf57463520a14f512662e592e95c571afdab1442a6a6864b6c88f248ce6fc4ff0b6de04ac7aa6c8bb51e868e99d1d65eb0658a708b5 - languageName: node - linkType: hard - "minimatch@npm:^3.0.4, minimatch@npm:^3.0.5, minimatch@npm:^3.1.1, minimatch@npm:^3.1.2": version: 3.1.2 resolution: "minimatch@npm:3.1.2" @@ -9993,6 +10361,15 @@ __metadata: languageName: node linkType: hard +"minimatch@npm:^9.0.0, minimatch@npm:^9.0.1": + version: 9.0.3 + resolution: "minimatch@npm:9.0.3" + dependencies: + brace-expansion: ^2.0.1 + checksum: 253487976bf485b612f16bf57463520a14f512662e592e95c571afdab1442a6a6864b6c88f248ce6fc4ff0b6de04ac7aa6c8bb51e868e99d1d65eb0658a708b5 + languageName: node + linkType: hard + "minimist-options@npm:4.1.0": version: 4.1.0 resolution: "minimist-options@npm:4.1.0" @@ -10011,27 +10388,27 @@ __metadata: languageName: node linkType: hard -"minipass-collect@npm:^2.0.1": - version: 2.0.1 - resolution: "minipass-collect@npm:2.0.1" +"minipass-collect@npm:^1.0.2": + version: 1.0.2 + resolution: "minipass-collect@npm:1.0.2" dependencies: - minipass: ^7.0.3 - checksum: b251bceea62090f67a6cced7a446a36f4cd61ee2d5cea9aee7fff79ba8030e416327a1c5aa2908dc22629d06214b46d88fdab8c51ac76bacbf5703851b5ad342 + minipass: ^3.0.0 + checksum: 14df761028f3e47293aee72888f2657695ec66bd7d09cae7ad558da30415fdc4752bbfee66287dcc6fd5e6a2fa3466d6c484dc1cbd986525d9393b9523d97f10 languageName: node linkType: hard "minipass-fetch@npm:^3.0.0": - version: 3.0.4 - resolution: "minipass-fetch@npm:3.0.4" + version: 3.0.3 + resolution: "minipass-fetch@npm:3.0.3" dependencies: encoding: ^0.1.13 - minipass: ^7.0.3 + minipass: ^5.0.0 minipass-sized: ^1.0.3 minizlib: ^2.1.2 dependenciesMeta: encoding: optional: true - checksum: af7aad15d5c128ab1ebe52e043bdf7d62c3c6f0cecb9285b40d7b395e1375b45dcdfd40e63e93d26a0e8249c9efd5c325c65575aceee192883970ff8cb11364a + checksum: af5ab2552a16fcf505d35fd7ffb84b57f4a0eeb269e6e1d9a2a75824dda48b36e527083250b7cca4a4def21d9544e2ade441e4730e233c0bc2133f6abda31e18 languageName: node linkType: hard @@ -10078,10 +10455,10 @@ __metadata: languageName: node linkType: hard -"minipass@npm:^5.0.0 || ^6.0.2 || ^7.0.0, minipass@npm:^7.0.2, minipass@npm:^7.0.3": - version: 7.0.4 - resolution: "minipass@npm:7.0.4" - checksum: 87585e258b9488caf2e7acea242fd7856bbe9a2c84a7807643513a338d66f368c7d518200ad7b70a508664d408aa000517647b2930c259a8b1f9f0984f344a21 +"minipass@npm:^5.0.0 || ^6.0.2 || ^7.0.0": + version: 7.0.2 + resolution: "minipass@npm:7.0.2" + checksum: 46776de732eb7cef2c7404a15fb28c41f5c54a22be50d47b03c605bf21f5c18d61a173c0a20b49a97e7a65f78d887245066410642551e45fffe04e9ac9e325bc languageName: node linkType: hard @@ -10158,20 +10535,21 @@ __metadata: linkType: hard "moment@npm:^2.29.1": - version: 2.30.1 - resolution: "moment@npm:2.30.1" - checksum: 859236bab1e88c3e5802afcf797fc801acdbd0ee509d34ea3df6eea21eb6bcc2abd4ae4e4e64aa7c986aa6cba563c6e62806218e6412a765010712e5fa121ba6 + version: 2.29.4 + resolution: "moment@npm:2.29.4" + checksum: 0ec3f9c2bcba38dc2451b1daed5daded747f17610b92427bebe1d08d48d8b7bdd8d9197500b072d14e326dd0ccf3e326b9e3d07c5895d3d49e39b6803b76e80e languageName: node linkType: hard "mortice@npm:^3.0.1": - version: 3.0.4 - resolution: "mortice@npm:3.0.4" + version: 3.0.1 + resolution: "mortice@npm:3.0.1" dependencies: + nanoid: ^4.0.0 observable-webworkers: ^2.0.1 - p-queue: ^8.0.1 + p-queue: ^7.2.0 p-timeout: ^6.0.0 - checksum: 64d63b6d724636e94f59a8f72208561d621f601707df17e8c1ea5e236bc3f208eae98609586898458851228733908028d61b1bf5a2b6db58bd2aa9c1f7e8166b + checksum: a6b23c98bdea9a18f25d0431424f0c9df54e6af5f2ebe9d5752b50bccc1fa19e2559007d813f7fc14171a9253ebc60c8702f0cda51a57c8fd0ae420abde83761 languageName: node linkType: hard @@ -10182,7 +10560,7 @@ __metadata: languageName: node linkType: hard -"ms@npm:^2.1.1": +"ms@npm:^2.0.0, ms@npm:^2.1.1": version: 2.1.3 resolution: "ms@npm:2.1.3" checksum: aa92de608021b242401676e35cfa5aa42dd70cbdc082b916da7fb925c542173e36bce97ea3e804923fe92c0ad991434e4a38327e15a1b5b5f945d66df615ae6d @@ -10221,14 +10599,14 @@ __metadata: linkType: hard "msgpackr@npm:^1.9.9": - version: 1.10.1 - resolution: "msgpackr@npm:1.10.1" + version: 1.9.9 + resolution: "msgpackr@npm:1.9.9" dependencies: msgpackr-extract: ^3.0.2 dependenciesMeta: msgpackr-extract: optional: true - checksum: e422d18b01051598b23701eebeb4b9e2c686b9c7826b20f564724837ba2b5cd4af74c91a549eaeaf8186645cc95e8196274a4a19442aa3286ac611b98069c194 + checksum: b63182d99f479d79f0d082fd2688ce7cf699b1aee71e20f28591c30b48743bb57868fdd72656759a892891072d186d864702c756434520709e8fe7e0d350a119 languageName: node linkType: hard @@ -10240,25 +10618,27 @@ __metadata: linkType: hard "multiformats@npm:^12.0.1": - version: 12.1.3 - resolution: "multiformats@npm:12.1.3" - checksum: 1060488612f8e6729c600f47a8741b91aa6e7b807ce165eca3c8cf07ab7465d2d9b212415a9c18886938b9e35b30ea7b9ae19b5ab5122589c65063440643e6bb + version: 12.0.1 + resolution: "multiformats@npm:12.0.1" + checksum: 227f5a0bb835e998d105028ffaa4cec944b6461c7b7f3ddc4f3aca22f6917ad59643302898c67af167a6f9d7abddcab094e501488664e2b9437f0b6a9b2ab68d languageName: node linkType: hard -"multiformats@npm:^13.0.0": - version: 13.0.1 - resolution: "multiformats@npm:13.0.1" - checksum: 63e5d6ee2c2a1d1e8fbd4b8c76fa41cf3d8204ceed1d57bc44cb30ff3d06b880ad58c3de52ae6d4397a662a6f1e3285dae74ee5d445fd1597516e1baec96d22a +"nanoid@npm:^3.3.6": + version: 3.3.6 + resolution: "nanoid@npm:3.3.6" + bin: + nanoid: bin/nanoid.cjs + checksum: 7d0eda657002738aa5206107bd0580aead6c95c460ef1bdd0b1a87a9c7ae6277ac2e9b945306aaa5b32c6dcb7feaf462d0f552e7f8b5718abfc6ead5c94a71b3 languageName: node linkType: hard -"nanoid@npm:^3.3.7": - version: 3.3.7 - resolution: "nanoid@npm:3.3.7" +"nanoid@npm:^4.0.0": + version: 4.0.2 + resolution: "nanoid@npm:4.0.2" bin: - nanoid: bin/nanoid.cjs - checksum: d36c427e530713e4ac6567d488b489a36582ef89da1d6d4e3b87eded11eb10d7042a877958c6f104929809b2ab0bafa17652b076cdf84324aa75b30b722204f2 + nanoid: bin/nanoid.js + checksum: 747c399cea4664dd0be1d0ec498ffd1ef8f1f5221676fc8b577e3f46f66d9afcddb9595d63d19a2e78d0bc6cc33984f65e66bf1682c850b9e26288883d96b53f languageName: node linkType: hard @@ -10269,6 +10649,22 @@ __metadata: languageName: node linkType: hard +"native-fetch@npm:^4.0.2": + version: 4.0.2 + resolution: "native-fetch@npm:4.0.2" + peerDependencies: + undici: "*" + checksum: 11e6d075aa03d40665a5fc438c56b535622fb4ee98eb2b035277c5ba47733cb4c7bc3ddb45e5ab8154869b509fc18ca1c0188ab271139ae89db14f9f552fc064 + languageName: node + linkType: hard + +"natural-compare-lite@npm:^1.4.0": + version: 1.4.0 + resolution: "natural-compare-lite@npm:1.4.0" + checksum: 5222ac3986a2b78dd6069ac62cbb52a7bf8ffc90d972ab76dfe7b01892485d229530ed20d0c62e79a6b363a663b273db3bde195a1358ce9e5f779d4453887225 + languageName: node + linkType: hard + "natural-compare@npm:^1.4.0": version: 1.4.0 resolution: "natural-compare@npm:1.4.0" @@ -10321,8 +10717,8 @@ __metadata: linkType: hard "node-fetch@npm:^2.6.12": - version: 2.7.0 - resolution: "node-fetch@npm:2.7.0" + version: 2.6.12 + resolution: "node-fetch@npm:2.6.12" dependencies: whatwg-url: ^5.0.0 peerDependencies: @@ -10330,7 +10726,7 @@ __metadata: peerDependenciesMeta: encoding: optional: true - checksum: d76d2f5edb451a3f05b15115ec89fc6be39de37c6089f1b6368df03b91e1633fd379a7e01b7ab05089a25034b2023d959b47e59759cb38d88341b2459e89d6e5 + checksum: 3bc1655203d47ee8e313c0d96664b9673a3d4dd8002740318e9d27d14ef306693a4b2ef8d6525775056fd912a19e23f3ac0d7111ad8925877b7567b29a625592 languageName: node linkType: hard @@ -10377,33 +10773,34 @@ __metadata: linkType: hard "node-gyp-build@npm:^4.3.0": - version: 4.8.0 - resolution: "node-gyp-build@npm:4.8.0" + version: 4.6.0 + resolution: "node-gyp-build@npm:4.6.0" bin: node-gyp-build: bin.js node-gyp-build-optional: optional.js node-gyp-build-test: build-test.js - checksum: b82a56f866034b559dd3ed1ad04f55b04ae381b22ec2affe74b488d1582473ca6e7f85fccf52da085812d3de2b0bf23109e752a57709ac7b9963951c710fea40 + checksum: 25d78c5ef1f8c24291f4a370c47ba52fcea14f39272041a90a7894cd50d766f7c8cb8fb06c0f42bf6f69b204b49d9be3c8fc344aac09714d5bdb95965499eb15 languageName: node linkType: hard "node-gyp@npm:latest": - version: 10.0.1 - resolution: "node-gyp@npm:10.0.1" + version: 9.4.0 + resolution: "node-gyp@npm:9.4.0" dependencies: env-paths: ^2.2.0 exponential-backoff: ^3.1.1 - glob: ^10.3.10 + glob: ^7.1.4 graceful-fs: ^4.2.6 - make-fetch-happen: ^13.0.0 - nopt: ^7.0.0 - proc-log: ^3.0.0 + make-fetch-happen: ^11.0.3 + nopt: ^6.0.0 + npmlog: ^6.0.0 + rimraf: ^3.0.2 semver: ^7.3.5 tar: ^6.1.2 - which: ^4.0.0 + which: ^2.0.2 bin: node-gyp: bin/node-gyp.js - checksum: 60a74e66d364903ce02049966303a57f898521d139860ac82744a5fdd9f7b7b3b61f75f284f3bfe6e6add3b8f1871ce305a1d41f775c7482de837b50c792223f + checksum: 78b404e2e0639d64e145845f7f5a3cb20c0520cdaf6dda2f6e025e9b644077202ea7de1232396ba5bde3fee84cdc79604feebe6ba3ec84d464c85d407bb5da99 languageName: node linkType: hard @@ -10414,10 +10811,10 @@ __metadata: languageName: node linkType: hard -"node-releases@npm:^2.0.14": - version: 2.0.14 - resolution: "node-releases@npm:2.0.14" - checksum: 59443a2f77acac854c42d321bf1b43dea0aef55cd544c6a686e9816a697300458d4e82239e2d794ea05f7bbbc8a94500332e2d3ac3f11f52e4b16cbe638b3c41 +"node-releases@npm:^2.0.13": + version: 2.0.13 + resolution: "node-releases@npm:2.0.13" + checksum: 17ec8f315dba62710cae71a8dad3cd0288ba943d2ece43504b3b1aa8625bf138637798ab470b1d9035b0545996f63000a8a926e0f6d35d0996424f8b6d36dda3 languageName: node linkType: hard @@ -10439,14 +10836,14 @@ __metadata: languageName: node linkType: hard -"nopt@npm:^7.0.0": - version: 7.2.0 - resolution: "nopt@npm:7.2.0" +"nopt@npm:^6.0.0": + version: 6.0.0 + resolution: "nopt@npm:6.0.0" dependencies: - abbrev: ^2.0.0 + abbrev: ^1.0.0 bin: nopt: bin/nopt.js - checksum: a9c0f57fb8cb9cc82ae47192ca2b7ef00e199b9480eed202482c962d61b59a7fbe7541920b2a5839a97b42ee39e288c0aed770e38057a608d7f579389dfde410 + checksum: 82149371f8be0c4b9ec2f863cc6509a7fd0fa729929c009f3a58e4eb0c9e4cae9920e8f1f8eb46e7d032fec8fb01bede7f0f41a67eb3553b7b8e14fa53de1dac languageName: node linkType: hard @@ -10479,11 +10876,23 @@ __metadata: linkType: hard "npm-run-path@npm:^5.1.0": - version: 5.2.0 - resolution: "npm-run-path@npm:5.2.0" + version: 5.1.0 + resolution: "npm-run-path@npm:5.1.0" dependencies: path-key: ^4.0.0 - checksum: c5325e016014e715689c4014f7e0be16cc4cbf529f32a1723e511bc4689b5f823b704d2bca61ac152ce2bda65e0205dc8b3ba0ec0f5e4c3e162d302f6f5b9efb + checksum: dc184eb5ec239d6a2b990b43236845332ef12f4e0beaa9701de724aa797fe40b6bbd0157fb7639d24d3ab13f5d5cf22d223a19c6300846b8126f335f788bee66 + languageName: node + linkType: hard + +"npmlog@npm:^6.0.0": + version: 6.0.2 + resolution: "npmlog@npm:6.0.2" + dependencies: + are-we-there-yet: ^3.0.0 + console-control-strings: ^1.1.0 + gauge: ^4.0.3 + set-blocking: ^2.0.0 + checksum: ae238cd264a1c3f22091cdd9e2b106f684297d3c184f1146984ecbe18aaa86343953f26b9520dedd1b1372bc0316905b736c1932d778dbeb1fcf5a1001390e2a languageName: node linkType: hard @@ -10494,10 +10903,10 @@ __metadata: languageName: node linkType: hard -"object-inspect@npm:^1.13.1, object-inspect@npm:^1.9.0": - version: 1.13.1 - resolution: "object-inspect@npm:1.13.1" - checksum: 7d9fa9221de3311dcb5c7c307ee5dc011cdd31dc43624b7c184b3840514e118e05ef0002be5388304c416c0eb592feb46e983db12577fc47e47d5752fbbfb61f +"object-inspect@npm:^1.12.3, object-inspect@npm:^1.9.0": + version: 1.12.3 + resolution: "object-inspect@npm:1.12.3" + checksum: dabfd824d97a5f407e6d5d24810d888859f6be394d8b733a77442b277e0808860555176719c5905e765e3743a7cada6b8b0a3b85e5331c530fd418cc8ae991db languageName: node linkType: hard @@ -10509,48 +10918,48 @@ __metadata: linkType: hard "object.assign@npm:^4.1.4": - version: 4.1.5 - resolution: "object.assign@npm:4.1.5" + version: 4.1.4 + resolution: "object.assign@npm:4.1.4" dependencies: - call-bind: ^1.0.5 - define-properties: ^1.2.1 + call-bind: ^1.0.2 + define-properties: ^1.1.4 has-symbols: ^1.0.3 object-keys: ^1.1.1 - checksum: f9aeac0541661370a1fc86e6a8065eb1668d3e771f7dbb33ee54578201336c057b21ee61207a186dd42db0c62201d91aac703d20d12a79fc79c353eed44d4e25 + checksum: 76cab513a5999acbfe0ff355f15a6a125e71805fcf53de4e9d4e082e1989bdb81d1e329291e1e4e0ae7719f0e4ef80e88fb2d367ae60500d79d25a6224ac8864 languageName: node linkType: hard -"object.fromentries@npm:^2.0.7": - version: 2.0.7 - resolution: "object.fromentries@npm:2.0.7" +"object.fromentries@npm:^2.0.6": + version: 2.0.6 + resolution: "object.fromentries@npm:2.0.6" dependencies: call-bind: ^1.0.2 - define-properties: ^1.2.0 - es-abstract: ^1.22.1 - checksum: 7341ce246e248b39a431b87a9ddd331ff52a454deb79afebc95609f94b1f8238966cf21f52188f2a353f0fdf83294f32f1ebf1f7826aae915ebad21fd0678065 + define-properties: ^1.1.4 + es-abstract: ^1.20.4 + checksum: 453c6d694180c0c30df451b60eaf27a5b9bca3fb43c37908fd2b78af895803dc631242bcf05582173afa40d8d0e9c96e16e8874b39471aa53f3ac1f98a085d85 languageName: node linkType: hard -"object.groupby@npm:^1.0.1": - version: 1.0.1 - resolution: "object.groupby@npm:1.0.1" +"object.groupby@npm:^1.0.0": + version: 1.0.0 + resolution: "object.groupby@npm:1.0.0" dependencies: call-bind: ^1.0.2 define-properties: ^1.2.0 - es-abstract: ^1.22.1 + es-abstract: ^1.21.2 get-intrinsic: ^1.2.1 - checksum: d7959d6eaaba358b1608066fc67ac97f23ce6f573dc8fc661f68c52be165266fcb02937076aedb0e42722fdda0bdc0bbf74778196ac04868178888e9fd3b78b5 + checksum: 64b00b287d57580111c958e7ff375c9b61811fa356f2cf0d35372d43cab61965701f00fac66c19fd8f49c4dfa28744bee6822379c69a73648ad03e09fcdeae70 languageName: node linkType: hard -"object.values@npm:^1.1.7": - version: 1.1.7 - resolution: "object.values@npm:1.1.7" +"object.values@npm:^1.1.6": + version: 1.1.6 + resolution: "object.values@npm:1.1.6" dependencies: call-bind: ^1.0.2 - define-properties: ^1.2.0 - es-abstract: ^1.22.1 - checksum: f3e4ae4f21eb1cc7cebb6ce036d4c67b36e1c750428d7b7623c56a0db90edced63d08af8a316d81dfb7c41a3a5fa81b05b7cc9426e98d7da986b1682460f0777 + define-properties: ^1.1.4 + es-abstract: ^1.20.4 + checksum: f6fff9fd817c24cfd8107f50fb33061d81cd11bacc4e3dbb3852e9ff7692fde4dbce823d4333ea27cd9637ef1b6690df5fbb61f1ed314fa2959598dc3ae23d8e languageName: node linkType: hard @@ -10613,6 +11022,18 @@ __metadata: languageName: node linkType: hard +"open@npm:^9.1.0": + version: 9.1.0 + resolution: "open@npm:9.1.0" + dependencies: + default-browser: ^4.0.0 + define-lazy-prop: ^3.0.0 + is-inside-container: ^1.0.0 + is-wsl: ^2.2.0 + checksum: 3993c0f61d51fed8ac290e99c9c3cf45d3b6cfb3e2aa2b74cafd312c3486c22fd81df16ac8f3ab91dd8a4e3e729a16fc2480cfc406c4833416cf908acf1ae7c9 + languageName: node + linkType: hard + "optionator@npm:^0.9.3": version: 0.9.3 resolution: "optionator@npm:0.9.3" @@ -10645,9 +11066,9 @@ __metadata: linkType: hard "ordered-binary@npm:^1.4.1": - version: 1.5.1 - resolution: "ordered-binary@npm:1.5.1" - checksum: ec4d3a6bd7f8c84afec9def1e599e7d460a45d11f94d07b16fdf62db4d2bc16405d79ef0277c2fdf86332fd2539761278981787d2ecf52376ade8b678104a0e6 + version: 1.4.1 + resolution: "ordered-binary@npm:1.4.1" + checksum: 274940b4ef983562e11371c84415c265432a4e1337ab85f8e7669eeab6afee8f655c6c12ecee1cd121aaf399c32f5c781b0d50e460bd42da004eba16dcc66574 languageName: node linkType: hard @@ -10712,34 +11133,23 @@ __metadata: languageName: node linkType: hard -"p-queue@npm:^7.3.4": - version: 7.4.1 - resolution: "p-queue@npm:7.4.1" +"p-queue@npm:^7.2.0, p-queue@npm:^7.3.4": + version: 7.3.4 + resolution: "p-queue@npm:7.3.4" dependencies: - eventemitter3: ^5.0.1 + eventemitter3: ^4.0.7 p-timeout: ^5.0.2 - checksum: 1c6888aa994d399262a9fbdd49c7066f8359732397f7a42ecf03f22875a1d65899797b46413f97e44acc18dddafbcc101eb135c284714c931dbbc83c3967f450 - languageName: node - linkType: hard - -"p-queue@npm:^8.0.1": - version: 8.0.1 - resolution: "p-queue@npm:8.0.1" - dependencies: - eventemitter3: ^5.0.1 - p-timeout: ^6.1.2 - checksum: 84a27a5b1faf2dcc96b8c0e423c34b5984b241acc07353d3cc6d8d3d1dadefb250b4ec84ce278cb1c946466999c6bf2a36ff718a75810bad8e11c7ca47ce80f5 + checksum: a21b8a4dd75f64a4988e4468cc344d1b45132506ddd2c771932d3de446d108ee68713b629e0d3f0809c227bc10eafc613edde6ae741d9f60db89b6031e40921c languageName: node linkType: hard -"p-retry@npm:^6.0.0": - version: 6.2.0 - resolution: "p-retry@npm:6.2.0" +"p-retry@npm:^5.0.0": + version: 5.1.2 + resolution: "p-retry@npm:5.1.2" dependencies: - "@types/retry": 0.12.2 - is-network-error: ^1.0.0 + "@types/retry": 0.12.1 retry: ^0.13.1 - checksum: 6003573c559ee812329c9c3ede7ba12a783fdc8dd70602116646e850c920b4597dc502fe001c3f9526fca4e93275045db7a27341c458e51db179c1374a01ac44 + checksum: f063c08b1adc3cf7c01de01eb2dbda841970229f9f229c5167ebf4e2080d8a38b1f4e6eccefac74bca97cfaf4436d0a0eeb0b551175b26bc8b3116195f61bba8 languageName: node linkType: hard @@ -10814,7 +11224,7 @@ __metadata: languageName: node linkType: hard -"parse-asn1@npm:^5.0.0, parse-asn1@npm:^5.1.6": +"parse-asn1@npm:^5.0.0, parse-asn1@npm:^5.1.5": version: 5.1.6 resolution: "parse-asn1@npm:5.1.6" dependencies: @@ -11009,14 +11419,25 @@ __metadata: languageName: node linkType: hard -"postcss@npm:^8.1.7, postcss@npm:^8.4.23, postcss@npm:^8.4.27": - version: 8.4.33 - resolution: "postcss@npm:8.4.33" +"postcss@npm:^8.1.7, postcss@npm:^8.4.23": + version: 8.4.31 + resolution: "postcss@npm:8.4.31" + dependencies: + nanoid: ^3.3.6 + picocolors: ^1.0.0 + source-map-js: ^1.0.2 + checksum: 1d8611341b073143ad90486fcdfeab49edd243377b1f51834dc4f6d028e82ce5190e4f11bb2633276864503654fb7cab28e67abdc0fbf9d1f88cad4a0ff0beea + languageName: node + linkType: hard + +"postcss@npm:^8.4.26": + version: 8.4.27 + resolution: "postcss@npm:8.4.27" dependencies: - nanoid: ^3.3.7 + nanoid: ^3.3.6 picocolors: ^1.0.0 source-map-js: ^1.0.2 - checksum: 6f98b2af4b76632a3de20c4f47bf0e984a1ce1a531cf11adcb0b1d63a6cbda0aae4165e578b66c32ca4879038e3eaad386a6be725a8fb4429c78e3c1ab858fe9 + checksum: 1cdd0c298849df6cd65f7e646a3ba36870a37b65f55fd59d1a165539c263e9b4872a402bf4ed1ca1bc31f58b68b2835545e33ea1a23b161a1f8aa6d5ded81e78 languageName: node linkType: hard @@ -11081,14 +11502,14 @@ __metadata: languageName: node linkType: hard -"pretty-format@npm:^29.0.0, pretty-format@npm:^29.7.0": - version: 29.7.0 - resolution: "pretty-format@npm:29.7.0" +"pretty-format@npm:^29.0.0, pretty-format@npm:^29.6.2": + version: 29.6.2 + resolution: "pretty-format@npm:29.6.2" dependencies: - "@jest/schemas": ^29.6.3 + "@jest/schemas": ^29.6.0 ansi-styles: ^5.0.0 react-is: ^18.0.0 - checksum: 032c1602383e71e9c0c02a01bbd25d6759d60e9c7cf21937dde8357aa753da348fcec5def5d1002c9678a8524d5fe099ad98861286550ef44de8808cc61e43b6 + checksum: a0f972a44f959023c0df9cdfe9eed7540264d7f7ddf74667db8a5294444d5aa153fd47d20327df10ae86964e2ceec10e46ea06b1a5c9c12e02348b78c952c9fc languageName: node linkType: hard @@ -11113,13 +11534,6 @@ __metadata: languageName: node linkType: hard -"proc-log@npm:^3.0.0": - version: 3.0.0 - resolution: "proc-log@npm:3.0.0" - checksum: 02b64e1b3919e63df06f836b98d3af002b5cd92655cab18b5746e37374bfb73e03b84fe305454614b34c25b485cc687a9eebdccf0242cda8fda2475dd2c97e02 - languageName: node - linkType: hard - "process-nextick-args@npm:~2.0.0": version: 2.0.1 resolution: "process-nextick-args@npm:2.0.1" @@ -11168,13 +11582,35 @@ __metadata: languageName: node linkType: hard +"protobufjs@npm:^7.0.0": + version: 7.2.4 + resolution: "protobufjs@npm:7.2.4" + dependencies: + "@protobufjs/aspromise": ^1.1.2 + "@protobufjs/base64": ^1.1.2 + "@protobufjs/codegen": ^2.0.4 + "@protobufjs/eventemitter": ^1.1.0 + "@protobufjs/fetch": ^1.1.0 + "@protobufjs/float": ^1.0.2 + "@protobufjs/inquire": ^1.1.0 + "@protobufjs/path": ^1.1.2 + "@protobufjs/pool": ^1.1.0 + "@protobufjs/utf8": ^1.1.0 + "@types/node": ">=13.7.0" + long: ^5.0.0 + checksum: a952cdf2a5e5250c16ae651b570849b6f5b20a5475c3eef63ffb290ad239aa2916adfc1cc676f7fc93c69f48113df268761c0c246f7f023118c85bdd1a170044 + languageName: node + linkType: hard + "protons-runtime@npm:^5.0.0": - version: 5.2.2 - resolution: "protons-runtime@npm:5.2.2" + version: 5.0.1 + resolution: "protons-runtime@npm:5.0.1" dependencies: + protobufjs: ^7.0.0 uint8arraylist: ^2.4.3 - uint8arrays: ^5.0.1 - checksum: b2c0c3612406eb49539e3f2be7e51bbb3333969b6c1052c8e2cf5cb3eb3aed86eb825eeecd3b5c579e02a545b658b90f046b7e06cc624a843130782bc8e22f6b + peerDependencies: + uint8arraylist: ^2.3.2 + checksum: 08d14fb7ea57e6329765873bb7933189925cfd2a488419735fb2547b8075f06a34638bc3b95c24c803a6ac179b7b5cff9b0894b6adfa46c38b5b430424a77605 languageName: node linkType: hard @@ -11237,43 +11673,41 @@ __metadata: linkType: hard "punycode@npm:^2.1.0": - version: 2.3.1 - resolution: "punycode@npm:2.3.1" - checksum: bb0a0ceedca4c3c57a9b981b90601579058903c62be23c5e8e843d2c2d4148a3ecf029d5133486fb0e1822b098ba8bba09e89d6b21742d02fa26bda6441a6fb2 + version: 2.3.0 + resolution: "punycode@npm:2.3.0" + checksum: 39f760e09a2a3bbfe8f5287cf733ecdad69d6af2fe6f97ca95f24b8921858b91e9ea3c9eeec6e08cede96181b3bb33f95c6ffd8c77e63986508aa2e8159fa200 languageName: node linkType: hard -"puppeteer-core@npm:21.7.0": - version: 21.7.0 - resolution: "puppeteer-core@npm:21.7.0" +"puppeteer-core@npm:21.3.5": + version: 21.3.5 + resolution: "puppeteer-core@npm:21.3.5" dependencies: - "@puppeteer/browsers": 1.9.1 - chromium-bidi: 0.5.2 + "@puppeteer/browsers": 1.7.1 + chromium-bidi: 0.4.28 cross-fetch: 4.0.0 debug: 4.3.4 - devtools-protocol: 0.0.1203626 - ws: 8.16.0 - checksum: 8166137f9cee88509ec60340435442915f82419ed10054403a3c43a9d638013f13e2a80409aa4bb6b14ad671d4c2cf117d9f4d26c5db8992b05bba6fb09a1380 + devtools-protocol: 0.0.1179426 + ws: 8.14.2 + checksum: c2e904e59c9d58ada08016ff893c6b574ab36848db8e0f214b6c02fef446cfa142d346384cb2bb49c39df45622caf6ef813e7cc10f613024e595aa4406fb77bc languageName: node linkType: hard "puppeteer@npm:^21.3.4": - version: 21.7.0 - resolution: "puppeteer@npm:21.7.0" + version: 21.3.5 + resolution: "puppeteer@npm:21.3.5" dependencies: - "@puppeteer/browsers": 1.9.1 + "@puppeteer/browsers": 1.7.1 cosmiconfig: 8.3.6 - puppeteer-core: 21.7.0 - bin: - puppeteer: lib/esm/puppeteer/node/cli.js - checksum: bb5f0f76f16cf09ae9a52d68a6ed7c8c06256b33e89c77898cba9b0f1863b9e76014d01b86371265fc7d1ea6b5f7c7a12e164159744145ee3a4251777ef59bfa + puppeteer-core: 21.3.5 + checksum: 0bb3f0f2dead30f4a18b94066d73e27a0ec418ba3e1b73fc882ba4b747405c91ee2592eef0137b984f26bca5e52007543ecd01d1ac843dd68b8d78489a296f69 languageName: node linkType: hard "pure-rand@npm:^6.0.0": - version: 6.0.4 - resolution: "pure-rand@npm:6.0.4" - checksum: e1c4e69f8bf7303e5252756d67c3c7551385cd34d94a1f511fe099727ccbab74c898c03a06d4c4a24a89b51858781057b83ebbfe740d984240cdc04fead36068 + version: 6.0.2 + resolution: "pure-rand@npm:6.0.2" + checksum: 79de33876a4f515d759c48e98d00756bbd916b4ea260cc572d7adfa4b62cace9952e89f0241d0410214554503d25061140fe325c66f845213d2b1728ba8d413e languageName: node linkType: hard @@ -11314,13 +11748,6 @@ __metadata: languageName: node linkType: hard -"race-signal@npm:^1.0.0, race-signal@npm:^1.0.1": - version: 1.0.2 - resolution: "race-signal@npm:1.0.2" - checksum: 01ea1f70059673cd239acbe9523eaf1649f3b02ec786b5266770d9b045018aa96e316150447f0a12e7b0f8aa02522deb23e7d3a2c3a58d37135c505f595f2e49 - languageName: node - linkType: hard - "randombytes@npm:^2.0.0, randombytes@npm:^2.0.1, randombytes@npm:^2.0.5, randombytes@npm:^2.1.0": version: 2.1.0 resolution: "randombytes@npm:2.1.0" @@ -11340,10 +11767,10 @@ __metadata: languageName: node linkType: hard -"rate-limiter-flexible@npm:^3.0.0": - version: 3.0.6 - resolution: "rate-limiter-flexible@npm:3.0.6" - checksum: 65cf8edacde55b78255d5de0286b1297ec897fee128f4f18734f36a230297d75433fa599ea423f7726b61b8f8ebf108d66ee09e7155e37aa25f506015b5166a4 +"rate-limiter-flexible@npm:^2.3.11": + version: 2.4.2 + resolution: "rate-limiter-flexible@npm:2.4.2" + checksum: 039e58b664991963ba2668a83d0406a72e5822683103acbe416854deb92ed834b840ce6e0acfea35917d9b49685bd53946ae47435a9f5916c2e7550395dec9dc languageName: node linkType: hard @@ -11403,7 +11830,7 @@ __metadata: languageName: node linkType: hard -"readable-stream@npm:^3.4.0, readable-stream@npm:^3.5.0, readable-stream@npm:^3.6.0, readable-stream@npm:^3.6.2": +"readable-stream@npm:^3.4.0, readable-stream@npm:^3.5.0, readable-stream@npm:^3.6.0": version: 3.6.2 resolution: "readable-stream@npm:3.6.2" dependencies: @@ -11457,21 +11884,21 @@ __metadata: languageName: node linkType: hard -"regenerator-runtime@npm:^0.14.0": - version: 0.14.1 - resolution: "regenerator-runtime@npm:0.14.1" - checksum: 9f57c93277b5585d3c83b0cf76be47b473ae8c6d9142a46ce8b0291a04bb2cf902059f0f8445dcabb3fb7378e5fe4bb4ea1e008876343d42e46d3b484534ce38 +"regenerator-runtime@npm:^0.13.11": + version: 0.13.11 + resolution: "regenerator-runtime@npm:0.13.11" + checksum: 27481628d22a1c4e3ff551096a683b424242a216fee44685467307f14d58020af1e19660bf2e26064de946bad7eff28950eae9f8209d55723e2d9351e632bbb4 languageName: node linkType: hard -"regexp.prototype.flags@npm:^1.5.1": - version: 1.5.1 - resolution: "regexp.prototype.flags@npm:1.5.1" +"regexp.prototype.flags@npm:^1.5.0": + version: 1.5.0 + resolution: "regexp.prototype.flags@npm:1.5.0" dependencies: call-bind: ^1.0.2 define-properties: ^1.2.0 - set-function-name: ^2.0.0 - checksum: 869edff00288442f8d7fa4c9327f91d85f3b3acf8cbbef9ea7a220345cf23e9241b6def9263d2c1ebcf3a316b0aa52ad26a43a84aa02baca3381717b3e307f47 + functions-have-names: ^1.2.3 + checksum: c541687cdbdfff1b9a07f6e44879f82c66bbf07665f9a7544c5fd16acdb3ec8d1436caab01662d2fbcad403f3499d49ab0b77fbc7ef29ef961d98cc4bc9755b4 languageName: node linkType: hard @@ -11574,7 +12001,20 @@ __metadata: languageName: node linkType: hard -"resolve@npm:^1.10.0, resolve@npm:^1.20.0, resolve@npm:^1.21.0, resolve@npm:^1.22.4": +"resolve@npm:^1.10.0, resolve@npm:^1.20.0, resolve@npm:^1.22.1, resolve@npm:^1.22.3": + version: 1.22.3 + resolution: "resolve@npm:1.22.3" + dependencies: + is-core-module: ^2.12.0 + path-parse: ^1.0.7 + supports-preserve-symlinks-flag: ^1.0.0 + bin: + resolve: bin/resolve + checksum: fb834b81348428cb545ff1b828a72ea28feb5a97c026a1cf40aa1008352c72811ff4d4e71f2035273dc536dcfcae20c13604ba6283c612d70fa0b6e44519c374 + languageName: node + linkType: hard + +"resolve@npm:^1.21.0": version: 1.22.8 resolution: "resolve@npm:1.22.8" dependencies: @@ -11597,7 +12037,20 @@ __metadata: languageName: node linkType: hard -"resolve@patch:resolve@^1.10.0#~builtin, resolve@patch:resolve@^1.20.0#~builtin, resolve@patch:resolve@^1.21.0#~builtin, resolve@patch:resolve@^1.22.4#~builtin": +"resolve@patch:resolve@^1.10.0#~builtin, resolve@patch:resolve@^1.20.0#~builtin, resolve@patch:resolve@^1.22.1#~builtin, resolve@patch:resolve@^1.22.3#~builtin": + version: 1.22.3 + resolution: "resolve@patch:resolve@npm%3A1.22.3#~builtin::version=1.22.3&hash=c3c19d" + dependencies: + is-core-module: ^2.12.0 + path-parse: ^1.0.7 + supports-preserve-symlinks-flag: ^1.0.0 + bin: + resolve: bin/resolve + checksum: ad59734723b596d0891321c951592ed9015a77ce84907f89c9d9307dd0c06e11a67906a3e628c4cae143d3e44898603478af0ddeb2bba3f229a9373efe342665 + languageName: node + linkType: hard + +"resolve@patch:resolve@^1.21.0#~builtin": version: 1.22.8 resolution: "resolve@patch:resolve@npm%3A1.22.8#~builtin::version=1.22.8&hash=c3c19d" dependencies: @@ -11672,9 +12125,9 @@ __metadata: languageName: node linkType: hard -"rollup@npm:^3.27.1": - version: 3.29.4 - resolution: "rollup@npm:3.29.4" +"rollup@npm:^3.25.2": + version: 3.27.0 + resolution: "rollup@npm:3.27.0" dependencies: fsevents: ~2.3.2 dependenciesMeta: @@ -11682,7 +12135,16 @@ __metadata: optional: true bin: rollup: dist/bin/rollup - checksum: 8bb20a39c8d91130825159c3823eccf4dc2295c9a0a5c4ed851a5bf2167dbf24d9a29f23461a54c955e5506395e6cc188eafc8ab0e20399d7489fb33793b184e + checksum: f60c2c288d039dc14e1f6e7fd673b7fcb11928b5a781675791b37a741f63b7af110fc5d040d60d603175b6e03ff978bed83db018dd2ac542ef809fe1a5b32dae + languageName: node + linkType: hard + +"run-applescript@npm:^5.0.0": + version: 5.0.0 + resolution: "run-applescript@npm:5.0.0" + dependencies: + execa: ^5.0.0 + checksum: d00c2dbfa5b2d774de7451194b8b125f40f65fc183de7d9dcae97f57f59433586d3c39b9001e111c38bfa24c3436c99df1bb4066a2a0c90d39a8c4cd6889af77 languageName: node linkType: hard @@ -11704,19 +12166,19 @@ __metadata: languageName: node linkType: hard -"safe-array-concat@npm:^1.0.1": - version: 1.1.0 - resolution: "safe-array-concat@npm:1.1.0" +"safe-array-concat@npm:^1.0.0": + version: 1.0.0 + resolution: "safe-array-concat@npm:1.0.0" dependencies: - call-bind: ^1.0.5 - get-intrinsic: ^1.2.2 + call-bind: ^1.0.2 + get-intrinsic: ^1.2.0 has-symbols: ^1.0.3 isarray: ^2.0.5 - checksum: 5c71eaa999168ee7474929f1cd3aae80f486353a651a094d9968936692cf90aa065224929a6486dcda66334a27dce4250a83612f9e0fef6dced1a925d3ac7296 + checksum: f43cb98fe3b566327d0c09284de2b15fb85ae964a89495c1b1a5d50c7c8ed484190f4e5e71aacc167e16231940079b326f2c0807aea633d47cc7322f40a6b57f languageName: node linkType: hard -"safe-buffer@npm:5.2.1, safe-buffer@npm:^5.0.1, safe-buffer@npm:^5.1.0, safe-buffer@npm:^5.1.1, safe-buffer@npm:^5.1.2, safe-buffer@npm:^5.2.0, safe-buffer@npm:^5.2.1, safe-buffer@npm:~5.2.0": +"safe-buffer@npm:5.2.1, safe-buffer@npm:^5.0.1, safe-buffer@npm:^5.1.0, safe-buffer@npm:^5.1.1, safe-buffer@npm:^5.1.2, safe-buffer@npm:^5.2.0, safe-buffer@npm:~5.2.0": version: 5.2.1 resolution: "safe-buffer@npm:5.2.1" checksum: b99c4b41fdd67a6aaf280fcd05e9ffb0813654894223afb78a31f14a19ad220bba8aba1cb14eddce1fcfb037155fe6de4e861784eb434f7d11ed58d1e70dd491 @@ -11731,13 +12193,13 @@ __metadata: linkType: hard "safe-regex-test@npm:^1.0.0": - version: 1.0.2 - resolution: "safe-regex-test@npm:1.0.2" + version: 1.0.0 + resolution: "safe-regex-test@npm:1.0.0" dependencies: - call-bind: ^1.0.5 - get-intrinsic: ^1.2.2 + call-bind: ^1.0.2 + get-intrinsic: ^1.1.3 is-regex: ^1.1.4 - checksum: 4af5ce05a2daa4f6d4bfd5a3c64fc33d6b886f6592122e93c0efad52f7147b9b605e5ffc03c269a1e3d1f8db2a23bc636628a961c9fd65bafdc09503330673fd + checksum: bc566d8beb8b43c01b94e67de3f070fd2781685e835959bbbaaec91cc53381145ca91f69bd837ce6ec244817afa0a5e974fc4e40a2957f0aca68ac3add1ddd34 languageName: node linkType: hard @@ -11776,9 +12238,9 @@ __metadata: linkType: hard "sax@npm:>=0.6.0": - version: 1.3.0 - resolution: "sax@npm:1.3.0" - checksum: 238ab3a9ba8c8f8aaf1c5ea9120386391f6ee0af52f1a6a40bbb6df78241dd05d782f2359d614ac6aae08c4c4125208b456548a6cf68625aa4fe178486e63ecd + version: 1.2.4 + resolution: "sax@npm:1.2.4" + checksum: d3df7d32b897a2c2f28e941f732c71ba90e27c24f62ee918bd4d9a8cfb3553f2f81e5493c7f0be94a11c1911b643a9108f231dd6f60df3fa9586b5d2e3e9e1fe languageName: node linkType: hard @@ -11832,35 +12294,18 @@ __metadata: linkType: hard "serialize-javascript@npm:^6.0.1": - version: 6.0.2 - resolution: "serialize-javascript@npm:6.0.2" + version: 6.0.1 + resolution: "serialize-javascript@npm:6.0.1" dependencies: randombytes: ^2.1.0 - checksum: c4839c6206c1d143c0f80763997a361310305751171dd95e4b57efee69b8f6edd8960a0b7fbfc45042aadff98b206d55428aee0dc276efe54f100899c7fa8ab7 - languageName: node - linkType: hard - -"set-function-length@npm:^1.1.1": - version: 1.2.0 - resolution: "set-function-length@npm:1.2.0" - dependencies: - define-data-property: ^1.1.1 - function-bind: ^1.1.2 - get-intrinsic: ^1.2.2 - gopd: ^1.0.1 - has-property-descriptors: ^1.0.1 - checksum: 63e34b45a2ff9abb419f52583481bf8ba597d33c0c85e56999085eb6078a0f7fbb4222051981c287feceeb358aa7789e7803cea2c82ac94c0ab37059596aff79 + checksum: 3c4f4cb61d0893b988415bdb67243637333f3f574e9e9cc9a006a2ced0b390b0b3b44aef8d51c951272a9002ec50885eefdc0298891bc27eb2fe7510ea87dc4f languageName: node linkType: hard -"set-function-name@npm:^2.0.0": - version: 2.0.1 - resolution: "set-function-name@npm:2.0.1" - dependencies: - define-data-property: ^1.0.1 - functions-have-names: ^1.2.3 - has-property-descriptors: ^1.0.0 - checksum: 4975d17d90c40168eee2c7c9c59d023429f0a1690a89d75656306481ece0c3c1fb1ebcc0150ea546d1913e35fbd037bace91372c69e543e51fc5d1f31a9fa126 +"set-blocking@npm:^2.0.0": + version: 2.0.0 + resolution: "set-blocking@npm:2.0.0" + checksum: 6e65a05f7cf7ebdf8b7c75b101e18c0b7e3dff4940d480efed8aad3a36a4005140b660fa1d804cb8bce911cac290441dc728084a30504d3516ac2ff7ad607b02 languageName: node linkType: hard @@ -11949,14 +12394,14 @@ __metadata: linkType: hard "shiki@npm:^0.14.1": - version: 0.14.7 - resolution: "shiki@npm:0.14.7" + version: 0.14.3 + resolution: "shiki@npm:0.14.3" dependencies: ansi-sequence-parser: ^1.1.0 jsonc-parser: ^3.2.0 vscode-oniguruma: ^1.7.0 vscode-textmate: ^8.0.0 - checksum: 2aec3b3519df977c4391df9e1825cb496e9a4d7e11395f05a0da77e4fa2f7c3d9d6e6ee94029ac699533017f2b25637ee68f6d39f05f311535c2704d0329b520 + checksum: a4dd98e3b2a5dd8be207448f111ffb9ad2ed6c530f215714d8b61cbf91ec3edbabb09109b8ec58a26678aacd24e8161d5a9bc0c1fa1b4f64b27ceb180cbd0c89 languageName: node linkType: hard @@ -12008,6 +12453,13 @@ __metadata: languageName: node linkType: hard +"slash@npm:^4.0.0": + version: 4.0.0 + resolution: "slash@npm:4.0.0" + checksum: da8e4af73712253acd21b7853b7e0dbba776b786e82b010a5bfc8b5051a1db38ed8aba8e1e8f400dd2c9f373be91eb1c42b66e91abb407ff42b10feece5e1d2d + languageName: node + linkType: hard + "smart-buffer@npm:^4.2.0": version: 4.2.0 resolution: "smart-buffer@npm:4.2.0" @@ -12015,7 +12467,18 @@ __metadata: languageName: node linkType: hard -"socks-proxy-agent@npm:^8.0.1, socks-proxy-agent@npm:^8.0.2": +"socks-proxy-agent@npm:^7.0.0": + version: 7.0.0 + resolution: "socks-proxy-agent@npm:7.0.0" + dependencies: + agent-base: ^6.0.2 + debug: ^4.3.3 + socks: ^2.6.2 + checksum: 720554370154cbc979e2e9ce6a6ec6ced205d02757d8f5d93fe95adae454fc187a5cbfc6b022afab850a5ce9b4c7d73e0f98e381879cf45f66317a4895953846 + languageName: node + linkType: hard + +"socks-proxy-agent@npm:^8.0.2": version: 8.0.2 resolution: "socks-proxy-agent@npm:8.0.2" dependencies: @@ -12026,7 +12489,7 @@ __metadata: languageName: node linkType: hard -"socks@npm:^2.7.1": +"socks@npm:^2.6.2, socks@npm:^2.7.1": version: 2.7.1 resolution: "socks@npm:2.7.1" dependencies: @@ -12077,13 +12540,6 @@ __metadata: languageName: node linkType: hard -"source-map@npm:^0.7.4": - version: 0.7.4 - resolution: "source-map@npm:0.7.4" - checksum: 01cc5a74b1f0e1d626a58d36ad6898ea820567e87f18dfc9d24a9843a351aaa2ec09b87422589906d6ff1deed29693e176194dc88bcae7c9a852dc74b311dbf5 - languageName: node - linkType: hard - "spawn-command@npm:0.0.2, spawn-command@npm:^0.0.2-1": version: 0.0.2 resolution: "spawn-command@npm:0.0.2" @@ -12119,9 +12575,9 @@ __metadata: linkType: hard "spdx-license-ids@npm:^3.0.0": - version: 3.0.16 - resolution: "spdx-license-ids@npm:3.0.16" - checksum: 5cdaa85aaa24bd02f9353a2e357b4df0a4f205cb35655f3fd0a5674a4fb77081f28ffd425379214bc3be2c2b7593ce1215df6bcc75884aeee0a9811207feabe2 + version: 3.0.13 + resolution: "spdx-license-ids@npm:3.0.13" + checksum: 3469d85c65f3245a279fa11afc250c3dca96e9e847f2f79d57f466940c5bb8495da08a542646086d499b7f24a74b8d0b42f3fc0f95d50ff99af1f599f6360ad7 languageName: node linkType: hard @@ -12142,11 +12598,11 @@ __metadata: linkType: hard "ssri@npm:^10.0.0": - version: 10.0.5 - resolution: "ssri@npm:10.0.5" + version: 10.0.4 + resolution: "ssri@npm:10.0.4" dependencies: - minipass: ^7.0.3 - checksum: 0a31b65f21872dea1ed3f7c200d7bc1c1b91c15e419deca14f282508ba917cbb342c08a6814c7f68ca4ca4116dd1a85da2bbf39227480e50125a1ceffeecb750 + minipass: ^5.0.0 + checksum: fb14da9f8a72b04eab163eb13a9dda11d5962cd2317f85457c4e0b575e9a6e0e3a6a87b5bf122c75cb36565830cd5f263fb457571bf6f1587eb5f95d095d6165 languageName: node linkType: hard @@ -12217,13 +12673,20 @@ __metadata: languageName: node linkType: hard +"streamsearch@npm:^1.1.0": + version: 1.1.0 + resolution: "streamsearch@npm:1.1.0" + checksum: 1cce16cea8405d7a233d32ca5e00a00169cc0e19fbc02aa839959985f267335d435c07f96e5e0edd0eadc6d39c98d5435fb5bbbdefc62c41834eadc5622ad942 + languageName: node + linkType: hard + "streamx@npm:^2.15.0": - version: 2.15.6 - resolution: "streamx@npm:2.15.6" + version: 2.15.1 + resolution: "streamx@npm:2.15.1" dependencies: fast-fifo: ^1.1.0 queue-tick: ^1.0.1 - checksum: 37a245f5cee4c33fcb8b018ccb935bad6eab423f05b0d14d018e63dbd2670bb109a69442e961a195b750c2c774f613c19476d11bd727d645eedb655d2dba234b + checksum: 6f2b4fed68caacd28efbd44d4264f5d3c2b81b0a5de14419333dac57f2075c49ae648df8d03db632a33587a6c8ab7cb9cdb4f9a2f8305be0c2cd79af35742b15 languageName: node linkType: hard @@ -12244,7 +12707,7 @@ __metadata: languageName: node linkType: hard -"string-width-cjs@npm:string-width@^4.2.0, string-width@npm:^4.1.0, string-width@npm:^4.2.0, string-width@npm:^4.2.3": +"string-width-cjs@npm:string-width@^4.2.0, string-width@npm:^1.0.2 || 2 || 3 || 4, string-width@npm:^4.1.0, string-width@npm:^4.2.0, string-width@npm:^4.2.3": version: 4.2.3 resolution: "string-width@npm:4.2.3" dependencies: @@ -12266,36 +12729,36 @@ __metadata: languageName: node linkType: hard -"string.prototype.trim@npm:^1.2.8": - version: 1.2.8 - resolution: "string.prototype.trim@npm:1.2.8" +"string.prototype.trim@npm:^1.2.7": + version: 1.2.7 + resolution: "string.prototype.trim@npm:1.2.7" dependencies: call-bind: ^1.0.2 - define-properties: ^1.2.0 - es-abstract: ^1.22.1 - checksum: 49eb1a862a53aba73c3fb6c2a53f5463173cb1f4512374b623bcd6b43ad49dd559a06fb5789bdec771a40fc4d2a564411c0a75d35fb27e76bbe738c211ecff07 + define-properties: ^1.1.4 + es-abstract: ^1.20.4 + checksum: 05b7b2d6af63648e70e44c4a8d10d8cc457536df78b55b9d6230918bde75c5987f6b8604438c4c8652eb55e4fc9725d2912789eb4ec457d6995f3495af190c09 languageName: node linkType: hard -"string.prototype.trimend@npm:^1.0.7": - version: 1.0.7 - resolution: "string.prototype.trimend@npm:1.0.7" +"string.prototype.trimend@npm:^1.0.6": + version: 1.0.6 + resolution: "string.prototype.trimend@npm:1.0.6" dependencies: call-bind: ^1.0.2 - define-properties: ^1.2.0 - es-abstract: ^1.22.1 - checksum: 2375516272fd1ba75992f4c4aa88a7b5f3c7a9ca308d963bcd5645adf689eba6f8a04ebab80c33e30ec0aefc6554181a3a8416015c38da0aa118e60ec896310c + define-properties: ^1.1.4 + es-abstract: ^1.20.4 + checksum: 0fdc34645a639bd35179b5a08227a353b88dc089adf438f46be8a7c197fc3f22f8514c1c9be4629b3cd29c281582730a8cbbad6466c60f76b5f99cf2addb132e languageName: node linkType: hard -"string.prototype.trimstart@npm:^1.0.7": - version: 1.0.7 - resolution: "string.prototype.trimstart@npm:1.0.7" +"string.prototype.trimstart@npm:^1.0.6": + version: 1.0.6 + resolution: "string.prototype.trimstart@npm:1.0.6" dependencies: call-bind: ^1.0.2 - define-properties: ^1.2.0 - es-abstract: ^1.22.1 - checksum: 13d0c2cb0d5ff9e926fa0bec559158b062eed2b68cd5be777ffba782c96b2b492944e47057274e064549b94dd27cf81f48b27a31fee8af5b574cff253e7eb613 + define-properties: ^1.1.4 + es-abstract: ^1.20.4 + checksum: 89080feef416621e6ef1279588994305477a7a91648d9436490d56010a1f7adc39167cddac7ce0b9884b8cdbef086987c4dcb2960209f2af8bac0d23ceff4f41 languageName: node linkType: hard @@ -12409,9 +12872,9 @@ __metadata: languageName: node linkType: hard -"superagent@npm:^8.1.2": - version: 8.1.2 - resolution: "superagent@npm:8.1.2" +"superagent@npm:^8.0.5": + version: 8.0.9 + resolution: "superagent@npm:8.0.9" dependencies: component-emitter: ^1.3.0 cookiejar: ^2.1.4 @@ -12423,17 +12886,17 @@ __metadata: mime: 2.6.0 qs: ^6.11.0 semver: ^7.3.8 - checksum: f3601c5ccae34d5ba684a03703394b5d25931f4ae2e1e31a1de809f88a9400e997ece037f9accf148a21c408f950dc829db1e4e23576a7f9fe0efa79fd5c9d2f + checksum: 5d00cdc7ceb5570663da80604965750e6b1b8d7d7442b7791e285c62bcd8d578a8ead0242a2426432b59a255fb42eb3a196d636157538a1392e7b6c5f1624810 languageName: node linkType: hard "supertest@npm:^6.3.3": - version: 6.3.4 - resolution: "supertest@npm:6.3.4" + version: 6.3.3 + resolution: "supertest@npm:6.3.3" dependencies: methods: ^1.1.2 - superagent: ^8.1.2 - checksum: 875c6fa7940f21e5be9bb646579cdb030d4057bf2da643e125e1f0480add1200395d2b17e10b8e54e1009efc63e047422501e9eb30e12828668498c0910f295f + superagent: ^8.0.5 + checksum: 38239e517f7ba62b7a139a79c5c48d55f8d67b5ff4b6e51d5b07732ca8bbc4a28ffa1b10916fbb403dd013a054dbf028edc5850057d9a43aecbff439d494673e languageName: node linkType: hard @@ -12471,6 +12934,16 @@ __metadata: languageName: node linkType: hard +"synckit@npm:^0.8.5": + version: 0.8.5 + resolution: "synckit@npm:0.8.5" + dependencies: + "@pkgr/utils": ^2.3.1 + tslib: ^2.5.0 + checksum: 8a9560e5d8f3d94dc3cf5f7b9c83490ffa30d320093560a37b88f59483040771fd1750e76b9939abfbb1b5a23fd6dfbae77f6b338abffe7cae7329cd9b9bb86b + languageName: node + linkType: hard + "tapable@npm:^2.1.1, tapable@npm:^2.2.0": version: 2.2.1 resolution: "tapable@npm:2.2.1" @@ -12490,19 +12963,19 @@ __metadata: linkType: hard "tar-stream@npm:^3.1.5": - version: 3.1.7 - resolution: "tar-stream@npm:3.1.7" + version: 3.1.6 + resolution: "tar-stream@npm:3.1.6" dependencies: b4a: ^1.6.4 fast-fifo: ^1.2.0 streamx: ^2.15.0 - checksum: 6393a6c19082b17b8dcc8e7fd349352bb29b4b8bfe1075912b91b01743ba6bb4298f5ff0b499a3bbaf82121830e96a1a59d4f21a43c0df339e54b01789cb8cc6 + checksum: f3627f918581976e954ff03cb8d370551053796b82564f8c7ca8fac84c48e4d042026d0854fc222171a34ff9c682b72fae91be9c9b0a112d4c54f9e4f443e9c5 languageName: node linkType: hard "tar@npm:^6.1.11, tar@npm:^6.1.2": - version: 6.2.0 - resolution: "tar@npm:6.2.0" + version: 6.1.15 + resolution: "tar@npm:6.1.15" dependencies: chownr: ^2.0.0 fs-minipass: ^2.0.0 @@ -12510,19 +12983,19 @@ __metadata: minizlib: ^2.1.1 mkdirp: ^1.0.3 yallist: ^4.0.0 - checksum: db4d9fe74a2082c3a5016630092c54c8375ff3b280186938cfd104f2e089c4fd9bad58688ef6be9cf186a889671bf355c7cda38f09bbf60604b281715ca57f5c + checksum: f23832fceeba7578bf31907aac744ae21e74a66f4a17a9e94507acf460e48f6db598c7023882db33bab75b80e027c21f276d405e4a0322d58f51c7088d428268 languageName: node linkType: hard "terser-webpack-plugin@npm:^5.3.7": - version: 5.3.10 - resolution: "terser-webpack-plugin@npm:5.3.10" + version: 5.3.9 + resolution: "terser-webpack-plugin@npm:5.3.9" dependencies: - "@jridgewell/trace-mapping": ^0.3.20 + "@jridgewell/trace-mapping": ^0.3.17 jest-worker: ^27.4.5 schema-utils: ^3.1.1 serialize-javascript: ^6.0.1 - terser: ^5.26.0 + terser: ^5.16.8 peerDependencies: webpack: ^5.1.0 peerDependenciesMeta: @@ -12532,13 +13005,13 @@ __metadata: optional: true uglify-js: optional: true - checksum: bd6e7596cf815f3353e2a53e79cbdec959a1b0276f5e5d4e63e9d7c3c5bb5306df567729da287d1c7b39d79093e56863c569c42c6c24cc34c76aa313bd2cbcea + checksum: 41705713d6f9cb83287936b21e27c658891c78c4392159f5148b5623f0e8c48559869779619b058382a4c9758e7820ea034695e57dc7c474b4962b79f553bc5f languageName: node linkType: hard -"terser@npm:^5.26.0": - version: 5.27.0 - resolution: "terser@npm:5.27.0" +"terser@npm:^5.16.8": + version: 5.19.2 + resolution: "terser@npm:5.19.2" dependencies: "@jridgewell/source-map": ^0.3.3 acorn: ^8.8.2 @@ -12546,7 +13019,7 @@ __metadata: source-map-support: ~0.5.20 bin: terser: bin/terser - checksum: c165052cfea061e8512e9b9ba42a098c2ff6382886ae122b040fd5b6153443070cc2dcb4862269f1669c09c716763e856125a355ff984aa72be525d6fffd8729 + checksum: e059177775b4d4f4cff219ad89293175aefbd1b081252270444dc83e42a2c5f07824eb2a85eae6e22ef6eb7ef04b21af36dd7d1dd7cfb93912310e57d416a205 languageName: node linkType: hard @@ -12576,11 +13049,11 @@ __metadata: linkType: hard "thingies@npm:^1.11.1": - version: 1.16.0 - resolution: "thingies@npm:1.16.0" + version: 1.12.0 + resolution: "thingies@npm:1.12.0" peerDependencies: tslib: ^2 - checksum: 9afbe70a9777fc31ac2567d06c9f64511585437298948330c39c076c6bd54bae6a700dee4cf4074486ef26c83f51031241ef4f4309c386555fb016a93be8aa06 + checksum: 04b75d264e1880676fb9f400b2c0e069abdd00de1fa006d9daee527ad6d899828169fd46bb17e7cabf2802b7dbd64053106e29e7a7aa271659f5b41b3a11657f languageName: node linkType: hard @@ -12591,6 +13064,13 @@ __metadata: languageName: node linkType: hard +"titleize@npm:^3.0.0": + version: 3.0.0 + resolution: "titleize@npm:3.0.0" + checksum: 71fbbeabbfb36ccd840559f67f21e356e1d03da2915b32d2ae1a60ddcc13a124be2739f696d2feb884983441d159a18649e8d956648d591bdad35c430a6b6d28 + languageName: node + linkType: hard + "tmpl@npm:1.0.5": version: 1.0.5 resolution: "tmpl@npm:1.0.5" @@ -12661,11 +13141,11 @@ __metadata: linkType: hard "ts-api-utils@npm:^1.0.1": - version: 1.0.3 - resolution: "ts-api-utils@npm:1.0.3" + version: 1.0.1 + resolution: "ts-api-utils@npm:1.0.1" peerDependencies: typescript: ">=4.2.0" - checksum: 441cc4489d65fd515ae6b0f4eb8690057add6f3b6a63a36073753547fb6ce0c9ea0e0530220a0b282b0eec535f52c4dfc315d35f8a4c9a91c0def0707a714ca6 + checksum: 78794fc7270d295b36c1ac613465b5dc7e7226907a533125b30f177efef9dd630d4e503b00be31b44335eb2ebf9e136ebe97353f8fc5d383885d5fead9d54c09 languageName: node linkType: hard @@ -12752,24 +13232,23 @@ __metadata: linkType: hard "ts-loader@npm:^9.4.4": - version: 9.5.1 - resolution: "ts-loader@npm:9.5.1" + version: 9.4.4 + resolution: "ts-loader@npm:9.4.4" dependencies: chalk: ^4.1.0 enhanced-resolve: ^5.0.0 micromatch: ^4.0.0 semver: ^7.3.4 - source-map: ^0.7.4 peerDependencies: typescript: "*" webpack: ^5.0.0 - checksum: 7cf396e656d905388ea2a9b5e82f16d3c955fda8d3df2fbf219f4bee16ff50a3c995c44ae3e584634e9443f056cec70bb3151add3917ffb4588ecd7394bac0ec + checksum: 8e5e6b839b0edfa40d2156c880d88ccab58226894ea5978221bc48c7db3215e2e856bfd0093f148e925a2befc42d6c94cafa9a994a7da274541efaa916012b63 languageName: node linkType: hard "ts-node@npm:^10.9.1": - version: 10.9.2 - resolution: "ts-node@npm:10.9.2" + version: 10.9.1 + resolution: "ts-node@npm:10.9.1" dependencies: "@cspotcode/source-map-support": ^0.8.0 "@tsconfig/node10": ^1.0.7 @@ -12801,7 +13280,7 @@ __metadata: ts-node-script: dist/bin-script.js ts-node-transpile-only: dist/bin-transpile.js ts-script: dist/bin-script-deprecated.js - checksum: fde256c9073969e234526e2cfead42591b9a2aec5222bac154b0de2fa9e4ceb30efcd717ee8bc785a56f3a119bdd5aa27b333d9dbec94ed254bd26f8944c67ac + checksum: 090adff1302ab20bd3486e6b4799e90f97726ed39e02b39e566f8ab674fd5bd5f727f43615debbfc580d33c6d9d1c6b1b3ce7d8e3cca3e20530a145ffa232c35 languageName: node linkType: hard @@ -12821,15 +13300,15 @@ __metadata: languageName: node linkType: hard -"tsconfig-paths@npm:^3.10.1, tsconfig-paths@npm:^3.15.0": - version: 3.15.0 - resolution: "tsconfig-paths@npm:3.15.0" +"tsconfig-paths@npm:^3.10.1, tsconfig-paths@npm:^3.14.2": + version: 3.14.2 + resolution: "tsconfig-paths@npm:3.14.2" dependencies: "@types/json5": ^0.0.29 json5: ^1.0.2 minimist: ^1.2.6 strip-bom: ^3.0.0 - checksum: 59f35407a390d9482b320451f52a411a256a130ff0e7543d18c6f20afab29ac19fbe55c360a93d6476213cc335a4d76ce90f67df54c4e9037f7d240920832201 + checksum: a6162eaa1aed680537f93621b82399c7856afd10ec299867b13a0675e981acac4e0ec00896860480efc59fc10fd0b16fdc928c0b885865b52be62cadac692447 languageName: node linkType: hard @@ -12847,13 +13326,20 @@ __metadata: languageName: node linkType: hard -"tslib@npm:^2.0.1, tslib@npm:^2.1.0, tslib@npm:^2.4.0, tslib@npm:^2.5.0": +"tslib@npm:^2.0.1": version: 2.6.2 resolution: "tslib@npm:2.6.2" checksum: 329ea56123005922f39642318e3d1f0f8265d1e7fcb92c633e0809521da75eeaca28d2cf96d7248229deb40e5c19adf408259f4b9640afd20d13aecc1430f3ad languageName: node linkType: hard +"tslib@npm:^2.1.0, tslib@npm:^2.4.0, tslib@npm:^2.5.0, tslib@npm:^2.6.0": + version: 2.6.1 + resolution: "tslib@npm:2.6.1" + checksum: b0d176d176487905b66ae4d5856647df50e37beea7571c53b8d10ba9222c074b81f1410fb91da13debaf2cbc970663609068bdebafa844ea9d69b146527c38fe + languageName: node + linkType: hard + "tsscmp@npm:1.0.6": version: 1.0.6 resolution: "tsscmp@npm:1.0.6" @@ -13024,12 +13510,12 @@ __metadata: linkType: hard "typescript@npm:^5.0.4": - version: 5.3.3 - resolution: "typescript@npm:5.3.3" + version: 5.1.6 + resolution: "typescript@npm:5.1.6" bin: tsc: bin/tsc tsserver: bin/tsserver - checksum: 2007ccb6e51bbbf6fde0a78099efe04dc1c3dfbdff04ca3b6a8bc717991862b39fd6126c0c3ebf2d2d98ac5e960bcaa873826bb2bb241f14277034148f41f6a2 + checksum: b2f2c35096035fe1f5facd1e38922ccb8558996331405eb00a5111cc948b2e733163cc22fab5db46992aba7dd520fff637f2c1df4996ff0e134e77d3249a7350 languageName: node linkType: hard @@ -13054,49 +13540,61 @@ __metadata: linkType: hard "typescript@patch:typescript@^5.0.4#~builtin": - version: 5.3.3 - resolution: "typescript@patch:typescript@npm%3A5.3.3#~builtin::version=5.3.3&hash=f3b441" + version: 5.1.6 + resolution: "typescript@patch:typescript@npm%3A5.1.6#~builtin::version=5.1.6&hash=5da071" bin: tsc: bin/tsc tsserver: bin/tsserver - checksum: f61375590b3162599f0f0d5b8737877ac0a7bc52761dbb585d67e7b8753a3a4c42d9a554c4cc929f591ffcf3a2b0602f65ae3ce74714fd5652623a816862b610 + checksum: f53bfe97f7c8b2b6d23cf572750d4e7d1e0c5fff1c36d859d0ec84556a827b8785077bc27676bf7e71fae538e517c3ecc0f37e7f593be913d884805d931bc8be + languageName: node + linkType: hard + +"uint8-varint@npm:^1.0.1, uint8-varint@npm:^1.0.2": + version: 1.0.6 + resolution: "uint8-varint@npm:1.0.6" + dependencies: + byte-access: ^1.0.0 + longbits: ^1.1.0 + uint8arraylist: ^2.0.0 + uint8arrays: ^4.0.2 + checksum: 9d97312b9d032cc20976c3d9ea9e7548bc761a5f4483156e349d5dcd9ffed7c71f597dc564591cbe93816ee39d258751dee2efa6b43859d3662c83ad1cf6cd1a languageName: node linkType: hard "uint8-varint@npm:^2.0.0, uint8-varint@npm:^2.0.1": - version: 2.0.3 - resolution: "uint8-varint@npm:2.0.3" + version: 2.0.1 + resolution: "uint8-varint@npm:2.0.1" dependencies: uint8arraylist: ^2.0.0 - uint8arrays: ^5.0.0 - checksum: 8c20b02f803ade88d3aad0c01368e8bfebedca3cb43f2d74347f32b92a219ab734b8df4bb0dee5540261de984896ef4ffb3c58e83b200d4b1d01a7e3f7757dee + uint8arrays: ^4.0.2 + checksum: b03431e7fb87224726524b6dcc4ac0ff2c61c122134be0b16394c04bac4a3b59328800f302e3d3df7715bdb676b2e1fd0a17332cf6dbdb148cbb9f81332cedef languageName: node linkType: hard -"uint8arraylist@npm:^2.0.0, uint8arraylist@npm:^2.4.1, uint8arraylist@npm:^2.4.3, uint8arraylist@npm:^2.4.7": - version: 2.4.8 - resolution: "uint8arraylist@npm:2.4.8" +"uint8arraylist@npm:^2.0.0, uint8arraylist@npm:^2.4.1, uint8arraylist@npm:^2.4.3": + version: 2.4.3 + resolution: "uint8arraylist@npm:2.4.3" dependencies: - uint8arrays: ^5.0.1 - checksum: 8259124cf5c7acd29edeed346489d898f3eb12f129dadedb1c263ad8d637e1a2f689968934a94c16804e39f6e8765178507be6d7b3c3c6b67147ad7546d34186 + uint8arrays: ^4.0.2 + checksum: 95225fe2b8f6a4d8919b6c8e3dcff32ced35a08a53efaef757a50bc0082fb5b91f09e7e46f0d1c234243899930f7cb02ef9eb44b97ce03e2381d416de15e16c9 languageName: node linkType: hard -"uint8arrays@npm:^4.0.4, uint8arrays@npm:^4.0.6": - version: 4.0.10 - resolution: "uint8arrays@npm:4.0.10" +"uint8arrays@npm:^4.0.2": + version: 4.0.4 + resolution: "uint8arrays@npm:4.0.4" dependencies: - multiformats: ^12.0.1 - checksum: 784677a00f67d18d3aaaf441422b4055576e1ab76dbf276e474b86c91ddb95945ac1cc95a97979ab1f3b3c9a0ebeea74dd803ec6056adbd1ee6ef2f231f00f97 + multiformats: ^11.0.0 + checksum: 49b2f53cf41aecd18b4623eea71d4e32fbcc572341cc687bb3e6e3372122b022079b2f0beca7c21648b1bd8a019ee596c71861043babc677e5c74773071e5d55 languageName: node linkType: hard -"uint8arrays@npm:^5.0.0, uint8arrays@npm:^5.0.1": - version: 5.0.1 - resolution: "uint8arrays@npm:5.0.1" +"uint8arrays@npm:^4.0.4, uint8arrays@npm:^4.0.6": + version: 4.0.6 + resolution: "uint8arrays@npm:4.0.6" dependencies: - multiformats: ^13.0.0 - checksum: 29b27d41e1b5fe2b3de0ce502556e9ac7caabf53c0a40f27d3654c4cb08f06913b14b7722325a1a4287664d15938abf48dd987340d966ca61c2daa40030b5f82 + multiformats: ^12.0.1 + checksum: 0d55d74fe8d791ee24396bf6175ffe8ff73aae763cfaca5bf774e43315ee57bc69cc3af854de5e7b20bc7e6b7bde731f73a478bc43c295ea8115bff8a49621e0 languageName: node linkType: hard @@ -13122,10 +13620,12 @@ __metadata: languageName: node linkType: hard -"undici-types@npm:~5.26.4": - version: 5.26.5 - resolution: "undici-types@npm:5.26.5" - checksum: 3192ef6f3fd5df652f2dc1cd782b49d6ff14dc98e5dced492aa8a8c65425227da5da6aafe22523c67f035a272c599bb89cfe803c1db6311e44bed3042fc25487 +"undici@npm:^5.12.0": + version: 5.22.1 + resolution: "undici@npm:5.22.1" + dependencies: + busboy: ^1.6.0 + checksum: 048a3365f622be44fb319316cedfaa241c59cf7f3368ae7667a12323447e1822e8cc3d00f6956c852d1478a6fde1cbbe753f49e05f2fdaed229693e716ebaf35 languageName: node linkType: hard @@ -13162,9 +13662,9 @@ __metadata: linkType: hard "universalify@npm:^2.0.0": - version: 2.0.1 - resolution: "universalify@npm:2.0.1" - checksum: ecd8469fe0db28e7de9e5289d32bd1b6ba8f7183db34f3bfc4ca53c49891c2d6aa05f3fb3936a81285a905cc509fb641a0c3fc131ec786167eff41236ae32e60 + version: 2.0.0 + resolution: "universalify@npm:2.0.0" + checksum: 2406a4edf4a8830aa6813278bab1f953a8e40f2f63a37873ffa9a3bc8f9745d06cc8e88f3572cb899b7e509013f7f6fcc3e37e8a6d914167a5381d8440518c44 languageName: node linkType: hard @@ -13175,6 +13675,13 @@ __metadata: languageName: node linkType: hard +"untildify@npm:^4.0.0": + version: 4.0.0 + resolution: "untildify@npm:4.0.0" + checksum: 39ced9c418a74f73f0a56e1ba4634b4d959422dff61f4c72a8e39f60b99380c1b45ed776fbaa0a4101b157e4310d873ad7d114e8534ca02609b4916bb4187fb9 + languageName: node + linkType: hard + "unzipit@npm:^1.4.3": version: 1.4.3 resolution: "unzipit@npm:1.4.3" @@ -13191,9 +13698,9 @@ __metadata: languageName: node linkType: hard -"update-browserslist-db@npm:^1.0.13": - version: 1.0.13 - resolution: "update-browserslist-db@npm:1.0.13" +"update-browserslist-db@npm:^1.0.11": + version: 1.0.11 + resolution: "update-browserslist-db@npm:1.0.11" dependencies: escalade: ^3.1.1 picocolors: ^1.0.0 @@ -13201,7 +13708,7 @@ __metadata: browserslist: ">= 4.21.0" bin: update-browserslist-db: cli.js - checksum: 1e47d80182ab6e4ad35396ad8b61008ae2a1330221175d0abd37689658bdb61af9b705bfc41057fd16682474d79944fb2d86767c5ed5ae34b6276b9bed353322 + checksum: b98327518f9a345c7cad5437afae4d2ae7d865f9779554baf2a200fdf4bac4969076b679b1115434bd6557376bdd37ca7583d0f9b8f8e302d7d4cc1e91b5f231 languageName: node linkType: hard @@ -13263,13 +13770,13 @@ __metadata: linkType: hard "v8-to-istanbul@npm:^9.0.1": - version: 9.2.0 - resolution: "v8-to-istanbul@npm:9.2.0" + version: 9.1.0 + resolution: "v8-to-istanbul@npm:9.1.0" dependencies: "@jridgewell/trace-mapping": ^0.3.12 "@types/istanbul-lib-coverage": ^2.0.1 - convert-source-map: ^2.0.0 - checksum: 31ef98c6a31b1dab6be024cf914f235408cd4c0dc56a5c744a5eea1a9e019ba279e1b6f90d695b78c3186feed391ed492380ccf095009e2eb91f3d058f0b4491 + convert-source-map: ^1.6.0 + checksum: 2069d59ee46cf8d83b4adfd8a5c1a90834caffa9f675e4360f1157ffc8578ef0f763c8f32d128334424159bb6b01f3876acd39cd13297b2769405a9da241f8d1 languageName: node linkType: hard @@ -13283,6 +13790,13 @@ __metadata: languageName: node linkType: hard +"varint@npm:^6.0.0": + version: 6.0.0 + resolution: "varint@npm:6.0.0" + checksum: 7684113c9d497c01e40396e50169c502eb2176203219b96e1c5ac965a3e15b4892bd22b7e48d87148e10fffe638130516b6dbeedd0efde2b2d0395aa1772eea7 + languageName: node + linkType: hard + "vary@npm:^1.1.2": version: 1.1.2 resolution: "vary@npm:1.1.2" @@ -13313,34 +13827,35 @@ __metadata: linkType: hard "viem@npm:^1.2.5": - version: 1.21.4 - resolution: "viem@npm:1.21.4" + version: 1.5.0 + resolution: "viem@npm:1.5.0" dependencies: - "@adraffy/ens-normalize": 1.10.0 - "@noble/curves": 1.2.0 - "@noble/hashes": 1.3.2 - "@scure/bip32": 1.3.2 - "@scure/bip39": 1.2.1 - abitype: 0.9.8 - isows: 1.0.3 - ws: 8.13.0 + "@adraffy/ens-normalize": 1.9.0 + "@noble/curves": 1.0.0 + "@noble/hashes": 1.3.0 + "@scure/bip32": 1.3.0 + "@scure/bip39": 1.2.0 + "@wagmi/chains": 1.6.0 + abitype: 0.9.3 + isomorphic-ws: 5.0.0 + ws: 8.12.0 peerDependencies: typescript: ">=5.0.4" peerDependenciesMeta: typescript: optional: true - checksum: c351fdea2d53d2d781ac73c964348b3b9fc5dd46f9eb53903e867705fc9e30a893cb9f2c8d7a00acdcdeca27d14eeebf976eed9f948c28c47018dc9211369117 + checksum: 28ff9e64a3076b339182a3bab5dc442222400da4c66d258bf35e961f9a2e555ba2b0f3b30bdb87f63142682530c25f0807113580fbf888f840930f39df4bd8e3 languageName: node linkType: hard "vite@npm:^4.2.3": - version: 4.5.2 - resolution: "vite@npm:4.5.2" + version: 4.4.8 + resolution: "vite@npm:4.4.8" dependencies: esbuild: ^0.18.10 fsevents: ~2.3.2 - postcss: ^8.4.27 - rollup: ^3.27.1 + postcss: ^8.4.26 + rollup: ^3.25.2 peerDependencies: "@types/node": ">= 14" less: "*" @@ -13369,7 +13884,7 @@ __metadata: optional: true bin: vite: bin/vite.js - checksum: 9d1f84f703c2660aced34deee7f309278ed368880f66e9570ac115c793d91f7fffb80ab19c602b3c8bc1341fe23437d86a3fcca2a9ef82f7ef0cdac5a40d0c86 + checksum: e8ffe688f8a7396b1357778f00cb06d1f3dadad200823c47a1955cf52774a0cbff5ac4d6a8f8d09e26c1d4e588e5815956f9eba02ae301e77a36c3d181a1bc86 languageName: node linkType: hard @@ -13430,9 +13945,9 @@ __metadata: linkType: hard "web-streams-polyfill@npm:^3.0.3": - version: 3.3.2 - resolution: "web-streams-polyfill@npm:3.3.2" - checksum: 0292f4113c1bda40d8e8ecebee39eb14cc2e2e560a65a6867980e394537a2645130e2c73f5ef6e641fd3697d2f71720ccf659aebaf69a9d5a773f653a0fdf39d + version: 3.2.1 + resolution: "web-streams-polyfill@npm:3.2.1" + checksum: b119c78574b6d65935e35098c2afdcd752b84268e18746606af149e3c424e15621b6f1ff0b42b2676dc012fc4f0d313f964b41a4b5031e525faa03997457da02 languageName: node linkType: hard @@ -13476,13 +13991,12 @@ __metadata: linkType: hard "webpack-merge@npm:^5.7.3": - version: 5.10.0 - resolution: "webpack-merge@npm:5.10.0" + version: 5.9.0 + resolution: "webpack-merge@npm:5.9.0" dependencies: clone-deep: ^4.0.1 - flat: ^5.0.2 wildcard: ^2.0.0 - checksum: 1fe8bf5309add7298e1ac72fb3f2090e1dfa80c48c7e79fa48aa60b5961332c7d0d61efa8851acb805e6b91a4584537a347bc106e05e9aec87fa4f7088c62f2f + checksum: 64fe2c23aacc5f19684452a0e84ec02c46b990423aee6fcc5c18d7d471155bd14e9a6adb02bd3656eb3e0ac2532c8e97d69412ad14c97eeafe32fa6d10050872 languageName: node linkType: hard @@ -13494,8 +14008,8 @@ __metadata: linkType: hard "webpack@npm:^5.88.2": - version: 5.89.0 - resolution: "webpack@npm:5.89.0" + version: 5.88.2 + resolution: "webpack@npm:5.88.2" dependencies: "@types/eslint-scope": ^3.7.3 "@types/estree": ^1.0.0 @@ -13526,7 +14040,7 @@ __metadata: optional: true bin: webpack: bin/webpack.js - checksum: 43fe0dbc30e168a685ef5a86759d5016a705f6563b39a240aa00826a80637d4a3deeb8062e709d6a4b05c63e796278244c84b04174704dc4a37bedb0f565c5ed + checksum: 79476a782da31a21f6dd38fbbd06b68da93baf6a62f0d08ca99222367f3b8668f5a1f2086b7bb78e23172e31fa6df6fa7ab09b25e827866c4fc4dc2b30443ce2 languageName: node linkType: hard @@ -13562,20 +14076,20 @@ __metadata: languageName: node linkType: hard -"which-typed-array@npm:^1.1.11, which-typed-array@npm:^1.1.13, which-typed-array@npm:^1.1.2": - version: 1.1.13 - resolution: "which-typed-array@npm:1.1.13" +"which-typed-array@npm:^1.1.10, which-typed-array@npm:^1.1.11, which-typed-array@npm:^1.1.2": + version: 1.1.11 + resolution: "which-typed-array@npm:1.1.11" dependencies: available-typed-arrays: ^1.0.5 - call-bind: ^1.0.4 + call-bind: ^1.0.2 for-each: ^0.3.3 gopd: ^1.0.1 has-tostringtag: ^1.0.0 - checksum: 3828a0d5d72c800e369d447e54c7620742a4cc0c9baf1b5e8c17e9b6ff90d8d861a3a6dd4800f1953dbf80e5e5cec954a289e5b4a223e3bee4aeb1f8c5f33309 + checksum: 711ffc8ef891ca6597b19539075ec3e08bb9b4c2ca1f78887e3c07a977ab91ac1421940505a197758fb5939aa9524976d0a5bbcac34d07ed6faa75cedbb17206 languageName: node linkType: hard -"which@npm:^2.0.1": +"which@npm:^2.0.1, which@npm:^2.0.2": version: 2.0.2 resolution: "which@npm:2.0.2" dependencies: @@ -13586,14 +14100,12 @@ __metadata: languageName: node linkType: hard -"which@npm:^4.0.0": - version: 4.0.0 - resolution: "which@npm:4.0.0" +"wide-align@npm:^1.1.5": + version: 1.1.5 + resolution: "wide-align@npm:1.1.5" dependencies: - isexe: ^3.1.1 - bin: - node-which: bin/which.js - checksum: f17e84c042592c21e23c8195108cff18c64050b9efb8459589116999ea9da6dd1509e6a1bac3aeebefd137be00fabbb61b5c2bc0aa0f8526f32b58ee2f545651 + string-width: ^1.0.2 || 2 || 3 || 4 + checksum: d5fc37cd561f9daee3c80e03b92ed3e84d80dde3365a8767263d03dacfc8fa06b065ffe1df00d8c2a09f731482fcacae745abfbb478d4af36d0a891fad4834d3 languageName: node linkType: hard @@ -13619,21 +14131,21 @@ __metadata: linkType: hard "winston-transport@npm:^4.4.0, winston-transport@npm:^4.5.0": - version: 4.6.0 - resolution: "winston-transport@npm:4.6.0" + version: 4.5.0 + resolution: "winston-transport@npm:4.5.0" dependencies: logform: ^2.3.2 readable-stream: ^3.6.0 triple-beam: ^1.3.0 - checksum: 19f06ebdbb57cb14cdd48a23145d418d3bbe538851053303f84f04a8a849bb530b78b1495a175059c1299f92945dc61d5421c4914fee32d9a41bc397d84f26d7 + checksum: a56e5678a80b88a73e77ed998fc6e19d0db19c989a356b137ec236782f2bf58ae4511b11c29163f99391fa4dc12102c7bc5738dcb6543f28877fa2819adc3ee9 languageName: node linkType: hard "winston@npm:^3.10.0": - version: 3.11.0 - resolution: "winston@npm:3.11.0" + version: 3.10.0 + resolution: "winston@npm:3.10.0" dependencies: - "@colors/colors": ^1.6.0 + "@colors/colors": 1.5.0 "@dabh/diagnostics": ^2.0.2 async: ^3.2.3 is-stream: ^2.0.0 @@ -13644,7 +14156,7 @@ __metadata: stack-trace: 0.0.x triple-beam: ^1.3.0 winston-transport: ^4.5.0 - checksum: ca4454070f7a71b19f53c8c1765c59a013dab220edb49161b2e81917751d3e9edc3382430e4fb050feda04fb8463290ecab7cbc9240ec8d3d3b32a121849bbb0 + checksum: 47df0361220d12b46d1b3c98a1c380a3718321739d527a182ce7984fc20715e5b0b55db0bcd3fd076d1b1d3261903b890b053851cfd4bc028bda7951fa8ca2e0 languageName: node linkType: hard @@ -13702,9 +14214,9 @@ __metadata: languageName: node linkType: hard -"ws@npm:8.13.0": - version: 8.13.0 - resolution: "ws@npm:8.13.0" +"ws@npm:8.14.2": + version: 8.14.2 + resolution: "ws@npm:8.14.2" peerDependencies: bufferutil: ^4.0.1 utf-8-validate: ">=5.0.2" @@ -13713,13 +14225,13 @@ __metadata: optional: true utf-8-validate: optional: true - checksum: 53e991bbf928faf5dc6efac9b8eb9ab6497c69feeb94f963d648b7a3530a720b19ec2e0ec037344257e05a4f35bd9ad04d9de6f289615ffb133282031b18c61c + checksum: 3ca0dad26e8cc6515ff392b622a1467430814c463b3368b0258e33696b1d4bed7510bc7030f7b72838b9fdeb8dbd8839cbf808367d6aae2e1d668ce741d4308b languageName: node linkType: hard -"ws@npm:8.16.0, ws@npm:^8.13.0": - version: 8.16.0 - resolution: "ws@npm:8.16.0" +"ws@npm:^8.13.0": + version: 8.13.0 + resolution: "ws@npm:8.13.0" peerDependencies: bufferutil: ^4.0.1 utf-8-validate: ">=5.0.2" @@ -13728,11 +14240,21 @@ __metadata: optional: true utf-8-validate: optional: true - checksum: feb3eecd2bae82fa8a8beef800290ce437d8b8063bdc69712725f21aef77c49cb2ff45c6e5e7fce622248f9c7abaee506bae0a9064067ffd6935460c7357321b + checksum: 53e991bbf928faf5dc6efac9b8eb9ab6497c69feeb94f963d648b7a3530a720b19ec2e0ec037344257e05a4f35bd9ad04d9de6f289615ffb133282031b18c61c + languageName: node + linkType: hard + +"xml2js@npm:^0.5.0": + version: 0.5.0 + resolution: "xml2js@npm:0.5.0" + dependencies: + sax: ">=0.6.0" + xmlbuilder: ~11.0.0 + checksum: 1aa71d62e5bc2d89138e3929b9ea46459157727759cbc62ef99484b778641c0cd21fb637696c052d901a22f82d092a3e740a16b4ce218e81ac59b933535124ea languageName: node linkType: hard -"xml2js@npm:^0.6.0, xml2js@npm:^0.6.2": +"xml2js@npm:^0.6.0": version: 0.6.2 resolution: "xml2js@npm:0.6.2" dependencies: @@ -13778,9 +14300,9 @@ __metadata: linkType: hard "yaml@npm:^2.1.3": - version: 2.3.4 - resolution: "yaml@npm:2.3.4" - checksum: e6d1dae1c6383bcc8ba11796eef3b8c02d5082911c6723efeeb5ba50fc8e881df18d645e64de68e421b577296000bea9c75d6d9097c2f6699da3ae0406c030d8 + version: 2.3.1 + resolution: "yaml@npm:2.3.1" + checksum: 2c7bc9a7cd4c9f40d3b0b0a98e370781b68b8b7c4515720869aced2b00d92f5da1762b4ffa947f9e795d6cd6b19f410bd4d15fdd38aca7bd96df59bd9486fb54 languageName: node linkType: hard @@ -13801,7 +14323,22 @@ __metadata: languageName: node linkType: hard -"yargs@npm:17.7.2, yargs@npm:^17.3.1, yargs@npm:^17.7.2": +"yargs@npm:17.7.1": + version: 17.7.1 + resolution: "yargs@npm:17.7.1" + dependencies: + cliui: ^8.0.1 + escalade: ^3.1.1 + get-caller-file: ^2.0.5 + require-directory: ^2.1.1 + string-width: ^4.2.3 + y18n: ^5.0.5 + yargs-parser: ^21.1.1 + checksum: 3d8a43c336a4942bc68080768664aca85c7bd406f018bad362fd255c41c8f4e650277f42fd65d543fce99e084124ddafee7bbfc1a5c6a8fda4cec78609dcf8d4 + languageName: node + linkType: hard + +"yargs@npm:^17.3.1, yargs@npm:^17.7.2": version: 17.7.2 resolution: "yargs@npm:17.7.2" dependencies: From 09dafa2c3fb415180ca3500d7bf191d518e0ad48 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Jos=C3=A9=20Pedro=20Sousa?= Date: Tue, 23 Jan 2024 19:12:08 +0000 Subject: [PATCH 04/16] extra stuff and some mistakes --- boxes/token/.gitignore | 4 ++-- boxes/token/tests/token.contract.test.ts | 10 +++++----- 2 files changed, 7 insertions(+), 7 deletions(-) diff --git a/boxes/token/.gitignore b/boxes/token/.gitignore index 1270997806b..d37f6988611 100644 --- a/boxes/token/.gitignore +++ b/boxes/token/.gitignore @@ -3,5 +3,5 @@ node_modules dest -src/artifacts -src/contracts/target \ No newline at end of file +artifacts +src/contracts/target diff --git a/boxes/token/tests/token.contract.test.ts b/boxes/token/tests/token.contract.test.ts index 6634e3834af..aaa500f63dd 100644 --- a/boxes/token/tests/token.contract.test.ts +++ b/boxes/token/tests/token.contract.test.ts @@ -214,7 +214,7 @@ describe('e2e_token_contract', () => { }); }); - describe.skip('Transfer', () => { + describe('Transfer', () => { describe('public', () => { it('transfer less than balance', async () => { const balance0 = await asset.methods.balance_of_public(accounts[0].address).view(); @@ -366,7 +366,7 @@ describe('e2e_token_contract', () => { }); }); - describe.skip('private', () => { + describe('private', () => { it('transfer less than balance', async () => { const balance0 = await asset.methods.balance_of_private(accounts[0].address).view(); const amount = balance0 / 2n; @@ -506,7 +506,7 @@ describe('e2e_token_contract', () => { }); }); - describe.skip('Shielding (shield + redeem_shield)', () => { + describe('Shielding (shield + redeem_shield)', () => { const secret = Fr.random(); let secretHash: Fr; @@ -631,7 +631,7 @@ describe('e2e_token_contract', () => { }); }); - describe.skip('Unshielding', () => { + describe('Unshielding', () => { it('on behalf of self', async () => { const balancePriv = await asset.methods.balance_of_private(accounts[0].address).view(); const amount = balancePriv / 2n; @@ -743,7 +743,7 @@ describe('e2e_token_contract', () => { }); }); - describe.skip('Burn', () => { + describe('Burn', () => { describe('public', () => { it('burn less than balance', async () => { const balance0 = await asset.methods.balance_of_public(accounts[0].address).view(); From 533b83e88e8f91d60aa3e59a174b001272105698 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Jos=C3=A9=20Pedro=20Sousa?= Date: Tue, 23 Jan 2024 19:13:44 +0000 Subject: [PATCH 05/16] removed cached git stuff --- barretenberg/cpp/.clangd | 78 ------------------ boxes/blank/artifacts/Blank.ts | 91 --------------------- boxes/token/artifacts/Token.ts | 145 --------------------------------- 3 files changed, 314 deletions(-) delete mode 100644 barretenberg/cpp/.clangd delete mode 100644 boxes/blank/artifacts/Blank.ts delete mode 100644 boxes/token/artifacts/Token.ts diff --git a/barretenberg/cpp/.clangd b/barretenberg/cpp/.clangd deleted file mode 100644 index bb22a6eed3e..00000000000 --- a/barretenberg/cpp/.clangd +++ /dev/null @@ -1,78 +0,0 @@ -CompileFlags: # Tweak the parse settings - Remove: -fconstexpr-ops-limit=* ---- -# Applies all barretenberg source files -If: - PathMatch: [src/.*\.hpp, src/.*\.cpp, src/.*\.tcc] -Diagnostics: - # Value Strict checks whether we are including unused header files - # Note that some headers may be _implicitly_ used and still - # need to be included. This is very noisy, and is probably best used - # by occasionally toggling it on. - UnusedIncludes: None - - # Static analysis configuration - ClangTidy: - Add: - - cert-* - - google-* - - cppcoreguidelines-* - - readability-* - - modernize-* - - bugprone-* - - misc-* - - performance-* - Remove: - # Useful but check is buggy in clang-tidy 15.0.6 - - misc-const-correctness - # Huge diff; obscure benefits. - - modernize-use-trailing-return-type - # Huge diff; we use lots of C-style arrays. - - modernize-avoid-c-arrays - # Huge diff; we do lots of pointer arithmetic. - - cppcoreguidelines-pro-bounds-pointer-arithmetic - # Huge diff. - - readability-magic-numbers - - cppcoreguidelines-avoid-magic-numbers - # We use short names because we do math. Also, huge diff. - - readability-identifier-length - # Fixing this would be a lot of work. - - bugprone-easily-swappable-parameters - # Huge diff - - misc-non-private-member-variables-in-classes - - cppcoreguidelines-non-private-member-variables-in-classes - # We have many `for` loops that violate this part of the bounds safety profile - - cppcoreguidelines-pro-bounds-constant-array-index - # Large diff; we often `use` an entire namespace. - - google-build-using-namespace - # Large diff - - cppcoreguidelines-pro-bounds-array-to-pointer-decay - # Large, potentially complicated diff - - readability-container-data-pointer - # Many hits; potential for false positives. - - cppcoreguidelines-pro-type-member-init - # As cryptographers, we often think of bools as 0/1 values; would bloat code in some places. - - modernize-use-bool-literals - # Triggers on every TYPED_TEST - - cert-err58-cpp - # Triggers on some tests that are not complex - - readability-function-cognitive-complexity - # It is often nicer to not be explicit - - google-explicit-constructor - # Not honouring. - - cppcoreguidelines-owning-memory - # "This check is deprecated since it’s no longer part of the CERT standard. It will be removed in clang-tidy version 19." - - cert-dc21-cpp - # Noisy. As we don't need to return error types or raw allocations, really unlikely we'd cause problems by ignoring a return type. - - modernize-use-nodiscard - ---- # this divider is necessary -# Disable some checks for Google Test/Bench -If: - PathMatch: [src/.*\.test\.cpp, src/.*\.bench\.cpp] -Diagnostics: - ClangTidy: - # these checks get triggered by the Google macros - Remove: - - cppcoreguidelines-avoid-non-const-global-variables - - cppcoreguidelines-special-member-functions diff --git a/boxes/blank/artifacts/Blank.ts b/boxes/blank/artifacts/Blank.ts deleted file mode 100644 index 4b1bad68380..00000000000 --- a/boxes/blank/artifacts/Blank.ts +++ /dev/null @@ -1,91 +0,0 @@ - -/* Autogenerated file, do not edit! */ - -/* eslint-disable */ -import { - AztecAddress, - AztecAddressLike, - CompleteAddress, - Contract, - ContractArtifact, - ContractBase, - ContractFunctionInteraction, - ContractMethod, - DeployMethod, - EthAddress, - EthAddressLike, - FieldLike, - Fr, - FunctionSelectorLike, - loadContractArtifact, - NoirCompiledContract, - Point, - PublicKey, - Wallet, -} from '@aztec/aztec.js'; -import BlankContractArtifactJson from '../src/contracts/target/blank-Blank.json' assert { type: 'json' }; -export const BlankContractArtifact = loadContractArtifact(BlankContractArtifactJson as NoirCompiledContract); - -/** - * Type-safe interface for contract Blank; - */ -export class BlankContract extends ContractBase { - - private constructor( - completeAddress: CompleteAddress, - wallet: Wallet, - portalContract = EthAddress.ZERO - ) { - super(completeAddress, BlankContractArtifact, wallet, portalContract); - } - - - - /** - * Creates a contract instance. - * @param address - The deployed contract's address. - * @param wallet - The wallet to use when interacting with the contract. - * @returns A promise that resolves to a new Contract instance. - */ - public static async at( - address: AztecAddress, - wallet: Wallet, - ) { - return Contract.at(address, BlankContract.artifact, wallet) as Promise; - } - - - /** - * Creates a tx to deploy a new instance of this contract. - */ - public static deploy(wallet: Wallet, ) { - return new DeployMethod(Point.ZERO, wallet, BlankContractArtifact, BlankContract.at, Array.from(arguments).slice(1)); - } - - /** - * Creates a tx to deploy a new instance of this contract using the specified public key to derive the address. - */ - public static deployWithPublicKey(publicKey: PublicKey, wallet: Wallet, ) { - return new DeployMethod(publicKey, wallet, BlankContractArtifact, BlankContract.at, Array.from(arguments).slice(2)); - } - - - - /** - * Returns this contract's artifact. - */ - public static get artifact(): ContractArtifact { - return BlankContractArtifact; - } - - - /** Type-safe wrappers for the public methods exposed by the contract. */ - public methods!: { - - /** compute_note_hash_and_nullifier(contract_address: struct, nonce: field, storage_slot: field, serialized_note: array) */ - compute_note_hash_and_nullifier: ((contract_address: AztecAddressLike, nonce: FieldLike, storage_slot: FieldLike, serialized_note: FieldLike[]) => ContractFunctionInteraction) & Pick; - - /** getPublicKey(address: struct) */ - getPublicKey: ((address: AztecAddressLike) => ContractFunctionInteraction) & Pick; - }; -} diff --git a/boxes/token/artifacts/Token.ts b/boxes/token/artifacts/Token.ts deleted file mode 100644 index bfc61debb06..00000000000 --- a/boxes/token/artifacts/Token.ts +++ /dev/null @@ -1,145 +0,0 @@ - -/* Autogenerated file, do not edit! */ - -/* eslint-disable */ -import { - AztecAddress, - AztecAddressLike, - CompleteAddress, - Contract, - ContractArtifact, - ContractBase, - ContractFunctionInteraction, - ContractMethod, - DeployMethod, - EthAddress, - EthAddressLike, - FieldLike, - Fr, - FunctionSelectorLike, - loadContractArtifact, - NoirCompiledContract, - Point, - PublicKey, - Wallet, -} from '@aztec/aztec.js'; -import TokenContractArtifactJson from '../src/contracts/target/token_contract-Token.json' assert { type: 'json' }; -export const TokenContractArtifact = loadContractArtifact(TokenContractArtifactJson as NoirCompiledContract); - -/** - * Type-safe interface for contract Token; - */ -export class TokenContract extends ContractBase { - - private constructor( - completeAddress: CompleteAddress, - wallet: Wallet, - portalContract = EthAddress.ZERO - ) { - super(completeAddress, TokenContractArtifact, wallet, portalContract); - } - - - - /** - * Creates a contract instance. - * @param address - The deployed contract's address. - * @param wallet - The wallet to use when interacting with the contract. - * @returns A promise that resolves to a new Contract instance. - */ - public static async at( - address: AztecAddress, - wallet: Wallet, - ) { - return Contract.at(address, TokenContract.artifact, wallet) as Promise; - } - - - /** - * Creates a tx to deploy a new instance of this contract. - */ - public static deploy(wallet: Wallet, admin: AztecAddressLike) { - return new DeployMethod(Point.ZERO, wallet, TokenContractArtifact, TokenContract.at, Array.from(arguments).slice(1)); - } - - /** - * Creates a tx to deploy a new instance of this contract using the specified public key to derive the address. - */ - public static deployWithPublicKey(publicKey: PublicKey, wallet: Wallet, admin: AztecAddressLike) { - return new DeployMethod(publicKey, wallet, TokenContractArtifact, TokenContract.at, Array.from(arguments).slice(2)); - } - - - - /** - * Returns this contract's artifact. - */ - public static get artifact(): ContractArtifact { - return TokenContractArtifact; - } - - - /** Type-safe wrappers for the public methods exposed by the contract. */ - public methods!: { - - /** mint_private(amount: field, secret_hash: field) */ - mint_private: ((amount: FieldLike, secret_hash: FieldLike) => ContractFunctionInteraction) & Pick; - - /** burn_public(from: struct, amount: field, nonce: field) */ - burn_public: ((from: AztecAddressLike, amount: FieldLike, nonce: FieldLike) => ContractFunctionInteraction) & Pick; - - /** is_minter(minter: struct) */ - is_minter: ((minter: AztecAddressLike) => ContractFunctionInteraction) & Pick; - - /** unshield(from: struct, to: struct, amount: field, nonce: field) */ - unshield: ((from: AztecAddressLike, to: AztecAddressLike, amount: FieldLike, nonce: FieldLike) => ContractFunctionInteraction) & Pick; - - /** total_supply() */ - total_supply: (() => ContractFunctionInteraction) & Pick; - - /** shield(from: struct, amount: field, secret_hash: field, nonce: field) */ - shield: ((from: AztecAddressLike, amount: FieldLike, secret_hash: FieldLike, nonce: FieldLike) => ContractFunctionInteraction) & Pick; - - /** compute_note_hash_and_nullifier(contract_address: struct, nonce: field, storage_slot: field, serialized_note: array) */ - compute_note_hash_and_nullifier: ((contract_address: AztecAddressLike, nonce: FieldLike, storage_slot: FieldLike, serialized_note: FieldLike[]) => ContractFunctionInteraction) & Pick; - - /** set_minter(minter: struct, approve: boolean) */ - set_minter: ((minter: AztecAddressLike, approve: boolean) => ContractFunctionInteraction) & Pick; - - /** burn(from: struct, amount: field, nonce: field) */ - burn: ((from: AztecAddressLike, amount: FieldLike, nonce: FieldLike) => ContractFunctionInteraction) & Pick; - - /** transfer_public(from: struct, to: struct, amount: field, nonce: field) */ - transfer_public: ((from: AztecAddressLike, to: AztecAddressLike, amount: FieldLike, nonce: FieldLike) => ContractFunctionInteraction) & Pick; - - /** mint_public(to: struct, amount: field) */ - mint_public: ((to: AztecAddressLike, amount: FieldLike) => ContractFunctionInteraction) & Pick; - - /** transfer(from: struct, to: struct, amount: field, nonce: field) */ - transfer: ((from: AztecAddressLike, to: AztecAddressLike, amount: FieldLike, nonce: FieldLike) => ContractFunctionInteraction) & Pick; - - /** _initialize(new_admin: struct) */ - _initialize: ((new_admin: AztecAddressLike) => ContractFunctionInteraction) & Pick; - - /** _reduce_total_supply(amount: field) */ - _reduce_total_supply: ((amount: FieldLike) => ContractFunctionInteraction) & Pick; - - /** redeem_shield(to: struct, amount: field, secret: field) */ - redeem_shield: ((to: AztecAddressLike, amount: FieldLike, secret: FieldLike) => ContractFunctionInteraction) & Pick; - - /** balance_of_public(owner: struct) */ - balance_of_public: ((owner: AztecAddressLike) => ContractFunctionInteraction) & Pick; - - /** set_admin(new_admin: struct) */ - set_admin: ((new_admin: AztecAddressLike) => ContractFunctionInteraction) & Pick; - - /** _increase_public_balance(to: struct, amount: field) */ - _increase_public_balance: ((to: AztecAddressLike, amount: FieldLike) => ContractFunctionInteraction) & Pick; - - /** balance_of_private(owner: struct) */ - balance_of_private: ((owner: AztecAddressLike) => ContractFunctionInteraction) & Pick; - - /** admin() */ - admin: (() => ContractFunctionInteraction) & Pick; - }; -} From 1bbe42b0b544df42a4f9d0e65c363051bcb88693 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Jos=C3=A9=20Pedro=20Sousa?= Date: Fri, 26 Jan 2024 15:51:25 +0000 Subject: [PATCH 06/16] wip --- barretenberg/.DS_Store | Bin 0 -> 6148 bytes boxes/blank-react/src/app/home.tsx | 41 ----------------------------- boxes/yarn.lock | 3 +-- 3 files changed, 1 insertion(+), 43 deletions(-) create mode 100644 barretenberg/.DS_Store diff --git a/barretenberg/.DS_Store b/barretenberg/.DS_Store new file mode 100644 index 0000000000000000000000000000000000000000..132e9099e2d2ce045d6753a503d73905e2c664e2 GIT binary patch literal 6148 zcmeHK%}T>S5Z-O8O({YS3Oz1(E!bKSi|dG+Q=ER#p@RM}M)Au&J<5Cg=(W;0;U z1*^N+G|=ja0b-zr0o)%1G(^W>sZnhm(BbtN<1IuK(D5ySC=5CVON|f#;kp!1mvZyO z;JO_A!sIyyOO3jmaWylHV`i=%FI>$IexcGCcQjH@3=jkB3^cWA(); const [selectWalletError, setSelectedWalletError] = useState(''); - const [privateMode, setPrivateMode] = useState(false); - const konamiIndex = useRef(0); - - const konamiCode = [ - 'ArrowUp', - 'ArrowUp', - 'ArrowDown', - 'ArrowDown', - 'ArrowLeft', - 'ArrowRight', - 'ArrowLeft', - 'ArrowRight', - 'KeyB', - 'KeyA', - ]; - - useEffect(() => { - const handleKeyDown = (e: KeyboardEvent) => { - if (e.code === konamiCode[konamiIndex.current]) { - konamiIndex.current++; - if (konamiIndex.current === konamiCode.length) { - setPrivateMode(true); - konamiIndex.current = 0; - } - } else { - konamiIndex.current = 0; - } - }; - - window.addEventListener('keydown', handleKeyDown); - return () => { - window.removeEventListener('keydown', handleKeyDown); - }; - }, []); const handleSelectWallet = (address: CompleteAddress | undefined) => { setSelectedWallet(address); @@ -55,15 +21,8 @@ export function Home() { setIsLoadingWallet(false); }; - function generatePrivateString() { - const word = 'PRIVATE'; - const times = 4000; - return Array(times).fill(word).join(' '); - } - return (
- {privateMode ?
{generatePrivateString()}
: null} Aztec <> {isLoadingWallet && } diff --git a/boxes/yarn.lock b/boxes/yarn.lock index 5719e430a8d..1b1478efeb4 100644 --- a/boxes/yarn.lock +++ b/boxes/yarn.lock @@ -96,7 +96,6 @@ __metadata: "@aztec/aztec-ui": "npm:^0.1.14" "@aztec/aztec.js": "npm:^0.16.9" "@types/jest": "npm:^29.5.0" - "@types/mocha": "npm:^10.0.3" "@types/node": "npm:^20.5.9" "@types/react": "npm:^18.2.15" "@types/react-dom": "npm:^18.2.7" @@ -255,7 +254,6 @@ __metadata: "@aztec/foundation": "workspace:^" eslint: "npm:^8.35.0" lodash.chunk: "npm:^4.2.0" - lodash.times: "npm:^4.3.2" tslib: "npm:^2.4.0" languageName: node linkType: soft @@ -302,6 +300,7 @@ __metadata: resolution: "@aztec/types@portal:../yarn-project/types::locator=%40aztec%2Fboxes%40workspace%3A." dependencies: "@aztec/ethereum": "workspace:^" + "@aztec/foundation": "workspace:^" languageName: node linkType: soft From 6f5e9ac3ded0099b749a2566c6d1c658ab7af741 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Jos=C3=A9=20Pedro=20Sousa?= Date: Thu, 1 Feb 2024 10:37:53 +0000 Subject: [PATCH 07/16] wip --- .circleci/config.yml | 56 +- .github/workflows/publish-bb.yml | 49 +- .release-please-manifest.json | 6 +- .vscode/c_cpp_properties.json | 18 - .vscode/launch.json | 36 - .vscode/settings.json | 22 +- CHANGELOG.md | 92 + README.md | 2 +- avm-transpiler/.gitignore | 2 + avm-transpiler/Cargo.lock | 2019 +++++ avm-transpiler/Cargo.toml | 21 + avm-transpiler/Dockerfile | 13 + avm-transpiler/Dockerfile.dockerignore | 7 + avm-transpiler/README.md | 15 + avm-transpiler/bootstrap.sh | 19 + avm-transpiler/rust-toolchain.toml | 5 + avm-transpiler/scripts/bootstrap_native.sh | 20 + avm-transpiler/src/instructions.rs | 115 + avm-transpiler/src/main.rs | 46 + avm-transpiler/src/opcodes.rs | 168 + avm-transpiler/src/transpile.rs | 289 + avm-transpiler/src/transpile_contract.rs | 120 + avm-transpiler/src/utils.rs | 41 + aztec-up/bin/aztec | 19 +- aztec-up/bin/aztec-cli | 3 +- aztec-up/bin/aztec-install | 6 +- aztec-up/bin/docker-compose.yml | 6 +- barretenberg/.gitrepo | 4 +- barretenberg/CHANGELOG.md | 41 + barretenberg/README.md | 14 +- barretenberg/cpp/.gitignore | 4 +- barretenberg/cpp/CMakeLists.txt | 24 +- barretenberg/cpp/CMakePresets.json | 38 +- barretenberg/cpp/cmake/benchmark.cmake | 36 +- barretenberg/cpp/cmake/gtest.cmake | 84 +- barretenberg/cpp/cmake/module.cmake | 74 +- barretenberg/cpp/docs/Fuzzing.md | 8 +- .../cpp/scripts/_benchmark_remote_lock.sh | 27 + .../scripts/barretenberg_module_digraph.sh | 24 + .../barretenberg_module_digraph_edges.awk | 35 + barretenberg/cpp/scripts/benchmark.sh | 17 + barretenberg/cpp/scripts/benchmark_remote.sh | 28 + barretenberg/cpp/scripts/benchmark_wasm.sh | 17 + .../cpp/scripts/benchmark_wasm_remote.sh | 25 + barretenberg/cpp/src/CMakeLists.txt | 8 +- .../cpp/src/barretenberg/barretenberg.hpp | 2 + .../src/barretenberg/benchmark/CMakeLists.txt | 1 - .../benchmark/basics_bench/CMakeLists.txt | 19 +- .../benchmark/commit_bench/CMakeLists.txt | 18 - .../benchmark/commit_bench/main.bench.cpp | 3 - .../benchmark/decrypt_bench/CMakeLists.txt | 22 +- .../benchmark/goblin_bench/CMakeLists.txt | 22 +- .../benchmark/goblin_bench/eccvm.bench.cpp | 4 +- .../benchmark/goblin_bench/goblin.bench.cpp | 181 +- .../benchmark/goblin_bench/main.bench.cpp | 3 - .../benchmark/ipa_bench/CMakeLists.txt | 19 +- .../benchmark/pippenger_bench/CMakeLists.txt | 24 +- .../benchmark/plonk_bench/CMakeLists.txt | 21 +- .../benchmark/plonk_bench/main.bench.cpp | 3 - .../benchmark/plonk_bench/plonk.bench.cpp | 4 +- .../plonk_bench/standard_plonk.bench.cpp | 4 +- .../protogalaxy_bench/CMakeLists.txt | 21 +- .../protogalaxy_bench/main.bench.cpp | 3 - .../protogalaxy_bench/protogalaxy.bench.cpp | 6 +- .../benchmark/relations_bench/CMakeLists.txt | 21 +- .../relations_bench/barycentric.bench.cpp | 4 +- .../benchmark/relations_bench/main.bench.cpp | 3 - .../relations_bench/relations.bench.cpp | 2 + .../benchmark/ultra_bench/CMakeLists.txt | 23 +- .../benchmark/ultra_bench/main.bench.cpp | 3 - .../benchmark/ultra_bench/mock_proofs.hpp | 118 +- .../ultra_bench/ultra_honk.bench.cpp | 16 +- .../ultra_bench/ultra_honk_rounds.bench.cpp | 4 +- .../ultra_bench/ultra_plonk.bench.cpp | 16 +- .../ultra_bench/ultra_plonk_rounds.bench.cpp | 4 +- .../benchmark/widgets_bench/CMakeLists.txt | 22 +- .../benchmark/widgets_bench/main.bench.cpp | 3 - .../benchmark/widgets_bench/widget.bench.cpp | 2 + .../commit.bench.cpp | 6 +- .../commitment_schemes/ipa/ipa.hpp | 4 +- .../commitment_schemes/ipa/ipa.test.cpp | 4 +- .../zeromorph/zeromorph.hpp | 8 +- .../cpp/src/barretenberg/common/thread.cpp | 2 +- .../cpp/src/barretenberg/common/utils.cpp | 17 + .../cpp/src/barretenberg/common/utils.hpp | 17 + .../barretenberg/crypto/ecdsa/ecdsa.test.cpp | 24 +- .../poseidon2/poseidon2_permutation.hpp | 5 +- .../dsl/acir_format/acir_format.cpp | 15 +- .../dsl/acir_format/acir_format.hpp | 9 +- .../dsl/acir_format/acir_format.test.cpp | 18 +- .../acir_format/acir_to_constraint_buf.hpp | 44 + .../dsl/acir_format/bigint_constraint.cpp | 33 + .../dsl/acir_format/bigint_constraint.hpp | 35 + .../acir_format/bigint_constraint.test.cpp | 87 + .../dsl/acir_format/block_constraint.test.cpp | 3 +- .../dsl/acir_format/ec_operations.cpp | 50 +- .../dsl/acir_format/ec_operations.hpp | 16 +- .../dsl/acir_format/ec_operations.test.cpp | 86 + .../dsl/acir_format/ecdsa_secp256k1.test.cpp | 9 +- .../dsl/acir_format/ecdsa_secp256r1.test.cpp | 12 +- .../acir_format/recursion_constraint.test.cpp | 6 +- .../dsl/acir_format/serde/acir.hpp | 1960 +++- .../barretenberg/dsl/acir_proofs/c_bind.cpp | 24 +- .../dsl/acir_proofs/goblin_acir_composer.cpp | 12 +- .../dsl/acir_proofs/goblin_acir_composer.hpp | 10 +- .../cpp/src/barretenberg/ecc/CMakeLists.txt | 2 +- .../ecc/fields/field_conversion.cpp | 116 + .../ecc/fields/field_conversion.hpp | 211 + .../ecc/fields/field_conversion.test.cpp | 134 + .../ecc/fields/field_declarations.hpp | 7 + .../src/barretenberg/eccvm/eccvm_prover.cpp | 6 +- .../src/barretenberg/eccvm/eccvm_prover.hpp | 8 +- .../eccvm/eccvm_transcript.test.cpp | 184 +- .../src/barretenberg/eccvm/eccvm_verifier.cpp | 4 +- .../src/barretenberg/eccvm/eccvm_verifier.hpp | 4 +- .../barretenberg/env/hardware_concurrency.cpp | 11 +- .../cpp/src/barretenberg/flavor/ecc_vm.hpp | 336 +- .../flavor/generated/AvmMini_flavor.hpp | 144 +- .../flavor/generated/Toy_flavor.hpp | 48 +- .../barretenberg/flavor/goblin_translator.hpp | 4 +- .../src/barretenberg/flavor/goblin_ultra.hpp | 119 +- .../flavor/goblin_ultra_recursive.hpp | 20 +- .../cpp/src/barretenberg/flavor/ultra.hpp | 85 +- .../barretenberg/flavor/ultra_recursive.hpp | 96 +- .../src/barretenberg/goblin/CMakeLists.txt | 2 +- .../goblin/full_goblin_recursion.test.cpp | 2 +- .../cpp/src/barretenberg/goblin/goblin.hpp | 57 +- .../src/barretenberg/goblin/mock_circuits.hpp | 111 +- .../goblin/mock_circuits_pinning.test.cpp | 61 + .../cpp/src/barretenberg/honk/CMakeLists.txt | 2 +- .../honk/proof_system/permutation_library.hpp | 36 +- .../honk/proof_system/types/proof.hpp | 9 + .../goblin_translator_circuit_builder.hpp | 2 +- .../circuit_builder/ultra_circuit_builder.hpp | 4 +- .../proof_system/types/circuit_type.hpp | 1 - .../barretenberg/protogalaxy/CMakeLists.txt | 2 +- .../protogalaxy/decider_prover.cpp | 6 +- .../protogalaxy/decider_prover.hpp | 8 +- .../protogalaxy/decider_verifier.cpp | 4 +- .../protogalaxy/decider_verifier.hpp | 4 +- .../protogalaxy/folding_result.hpp | 2 +- .../protogalaxy/protogalaxy_prover.cpp | 4 - .../protogalaxy/protogalaxy_verifier.cpp | 19 +- .../protogalaxy/protogalaxy_verifier.hpp | 4 +- .../cpp/src/barretenberg/srs/global_crs.cpp | 42 +- .../srs/scalar_multiplication.test.cpp | 2 +- .../stdlib/encryption/ecdsa/ecdsa.hpp | 2 + .../stdlib/encryption/ecdsa/ecdsa_impl.hpp | 51 + .../stdlib/hash/benchmarks/CMakeLists.txt | 4 +- .../hash/benchmarks/celer/CMakeLists.txt | 1 - ...ha256.bench.cpp => celer_sha256.bench.cpp} | 2 +- .../hash/benchmarks/external/CMakeLists.txt | 1 - ....cpp => external_sha256_blake3s.bench.cpp} | 0 .../hash/benchmarks/sha256/CMakeLists.txt | 1 - ...a256.bench.cpp => stdlib_sha256.bench.cpp} | 2 +- .../stdlib/hash/keccak/keccak.cpp | 20 + .../stdlib/hash/keccak/keccak.hpp | 2 + .../stdlib/hash/sha256/sha256.cpp | 19 + .../stdlib/hash/sha256/sha256.hpp | 2 + .../stdlib/merkle_tree/membership.hpp | 38 + .../stdlib/primitives/curves/bn254.hpp | 2 + .../stdlib/recursion/CMakeLists.txt | 2 +- .../recursion/honk/transcript/transcript.hpp | 11 +- .../verifier/decider_recursive_verifier.cpp | 96 + .../verifier/decider_recursive_verifier.hpp | 30 + .../verifier/merge_recursive_verifier.cpp | 4 +- .../verifier/merge_recursive_verifier.hpp | 4 +- .../protogalaxy_recursive_verifier.cpp | 320 + .../protogalaxy_recursive_verifier.hpp | 118 + .../protogalaxy_recursive_verifier.test.cpp | 350 + .../verifier/ultra_recursive_verifier.cpp | 4 +- .../verifier/ultra_recursive_verifier.hpp | 9 +- .../barretenberg/stdlib/utility/utility.hpp | 4 + .../barretenberg/transcript/CMakeLists.txt | 2 +- .../barretenberg/transcript/transcript.hpp | 157 +- .../transcript/transcript.test.cpp | 84 +- .../goblin_translator_composer.cpp | 20 +- .../goblin_translator_composer.hpp | 3 +- .../goblin_translator_prover.cpp | 6 +- .../goblin_translator_prover.hpp | 11 +- .../goblin_translator_verifier.cpp | 4 +- .../goblin_translator_verifier.hpp | 4 +- .../goblin_ultra_transcript.test.cpp | 56 +- .../barretenberg/ultra_honk/merge_prover.cpp | 6 +- .../barretenberg/ultra_honk/merge_prover.hpp | 6 +- .../ultra_honk/merge_verifier.cpp | 6 +- .../ultra_honk/merge_verifier.hpp | 4 +- .../ultra_honk/protogalaxy.test.cpp | 4 +- .../ultra_honk/relation_correctness.test.cpp | 38 +- .../ultra_honk/ultra_composer.hpp | 3 +- .../barretenberg/ultra_honk/ultra_prover.cpp | 6 +- .../barretenberg/ultra_honk/ultra_prover.hpp | 8 +- .../ultra_honk/ultra_transcript.test.cpp | 43 +- .../ultra_honk/ultra_verifier.cpp | 4 +- .../ultra_honk/ultra_verifier.hpp | 4 +- .../vm/avm_trace/AvmMini_common.hpp | 4 + .../vm/avm_trace/AvmMini_execution.cpp | 238 + .../vm/avm_trace/AvmMini_execution.hpp | 28 + .../vm/avm_trace/AvmMini_instructions.hpp | 23 + .../vm/avm_trace/AvmMini_opcode.cpp | 153 + .../vm/avm_trace/AvmMini_opcode.hpp | 105 + .../vm/avm_trace/AvmMini_trace.cpp | 16 +- .../vm/avm_trace/AvmMini_trace.hpp | 11 +- .../vm/generated/AvmMini_prover.cpp | 7 +- .../vm/generated/AvmMini_prover.hpp | 8 +- .../vm/generated/AvmMini_verifier.cpp | 4 +- .../vm/generated/AvmMini_verifier.hpp | 4 +- .../barretenberg/vm/generated/Toy_prover.cpp | 6 +- .../barretenberg/vm/generated/Toy_prover.hpp | 6 +- .../vm/generated/Toy_verifier.cpp | 4 +- .../vm/generated/Toy_verifier.hpp | 4 +- .../vm/tests/AvmMini_arithmetic.test.cpp | 38 +- .../vm/tests/AvmMini_control_flow.test.cpp | 3 +- .../vm/tests/AvmMini_execution.test.cpp | 505 ++ .../vm/tests/AvmMini_memory.test.cpp | 17 +- barretenberg/ts/CHANGELOG.md | 7 + barretenberg/ts/package.json | 2 +- bootstrap.sh | 1 + boxes/Dockerfile | 4 +- .../contract_function_form.module.scss | 66 - .../app/components/contract_function_form.tsx | 352 +- .../src/app/components/copy.module.scss | 6 - boxes/blank-react/src/app/components/copy.tsx | 28 - .../src/app/components/dropdown.module.scss | 68 - .../src/app/components/dropdown.tsx | 83 - .../src/app/components/popup.module.scss | 27 - .../blank-react/src/app/components/popup.tsx | 17 +- .../src/app/components/select.module.scss | 65 - .../blank-react/src/app/components/select.tsx | 81 - .../blank-react/src/app/components/terms.tsx | 10 - .../components/wallet_dropdown.module.scss | 9 - .../src/app/components/wallet_dropdown.tsx | 60 - .../blank-react/src/app/contract.module.scss | 53 - boxes/blank-react/src/app/contract.tsx | 253 +- boxes/blank-react/src/app/home.module.scss | 28 - boxes/blank-react/src/app/home.tsx | 67 +- .../blank-react/src/app/hooks/useAccounts.tsx | 20 + .../blank-react/src/app/hooks/useContract.tsx | 33 + boxes/blank-react/src/app/index.css | 46 - boxes/blank-react/src/contracts/Nargo.toml | 3 + ...7411b6b375064925b8088b0cd141771-audit.json | 15 + .../log/aztec-sandbox-2024-02-01.debug.log | 5 + .../src/contracts/log/aztec-sandbox.debug.log | 1 + boxes/blank-react/src/contracts/src/main.nr | 54 +- .../src/scripts/deploy_contract.ts | 26 - .../blank-react/tests/blank.contract.test.ts | 23 +- boxes/blank-react/tsconfig.json | 2 +- boxes/blank/webpack.config.js | 1 + boxes/docker-compose.yml | 2 +- boxes/token/src/contracts/src/main.nr | 38 +- boxes/token/src/contracts/src/types.nr | 1 - .../src/contracts/src/types/balance_set.nr | 12 +- .../src/contracts/src/types/balances_map.nr | 5 +- .../src/types/safe_u120_serialization.nr | 18 - .../src/contracts/src/types/token_note.nr | 97 +- .../contracts/src/types/transparent_note.nr | 127 +- build_manifest.yml | 17 +- circuits/.gitignore | 6 - circuits/CODING_STANDARD.md | 308 - circuits/README.md | 295 - circuits/cpp/.clang-format | 92 - circuits/cpp/.clang-tidy | 212 - circuits/cpp/.clang-tidy.slow | 139 - circuits/cpp/.clangd | 73 - circuits/cpp/.gitignore | 7 - circuits/cpp/.rebuild_patterns | 3 - circuits/cpp/CMakeLists.txt | 145 - circuits/cpp/CMakePresets.json | 311 - circuits/cpp/barretenberg | 1 - circuits/cpp/bootstrap.sh | 27 - circuits/cpp/cmake/arch.cmake | 10 - circuits/cpp/cmake/benchmark.cmake | 25 - circuits/cpp/cmake/build.cmake | 9 - circuits/cpp/cmake/gtest.cmake | 51 - circuits/cpp/cmake/module.cmake | 235 - circuits/cpp/cmake/msgpack.cmake | 16 - circuits/cpp/cmake/threading.cmake | 37 - .../cpp/cmake/toolchains/aarch64-darwin.cmake | 11 - .../cpp/cmake/toolchains/aarch64-linux.cmake | 2 - .../cpp/cmake/toolchains/i386-linux.cmake | 7 - .../cpp/cmake/toolchains/wasm32-wasi.cmake | 3 - .../cpp/cmake/toolchains/x86_64-darwin.cmake | 11 - .../cpp/cmake/toolchains/x86_64-linux.cmake | 2 - circuits/cpp/format.sh | 21 - circuits/cpp/scripts/a3-tests | 4 - .../cpp/scripts/build_run_tests_docker_local | 41 - .../scripts/collect_coverage_information.sh | 75 - circuits/cpp/scripts/run_coverage | 77 - circuits/cpp/scripts/run_tests | 43 - circuits/cpp/scripts/run_tests_local | 76 - circuits/cpp/scripts/tidy.sh | 105 - circuits/cpp/src/CMakeLists.txt | 29 - circuits/cpp/src/aztec3/CMakeLists.txt | 33 - .../cpp/src/aztec3/circuits/CMakeLists.txt | 5 - .../cpp/src/aztec3/circuits/abis/.test.cpp | 123 - .../src/aztec3/circuits/abis/CMakeLists.txt | 4 - .../cpp/src/aztec3/circuits/abis/README.md | 9 - .../abis/append_only_tree_snapshot.hpp | 18 - .../src/aztec3/circuits/abis/block_header.hpp | 138 - .../cpp/src/aztec3/circuits/abis/c_bind.cpp | 588 -- .../cpp/src/aztec3/circuits/abis/c_bind.h | 38 - .../src/aztec3/circuits/abis/c_bind.test.cpp | 338 - .../src/aztec3/circuits/abis/call_context.hpp | 127 - .../aztec3/circuits/abis/call_stack_item.hpp | 109 - .../abis/combined_accumulated_data.hpp | 230 - .../circuits/abis/combined_constant_data.hpp | 67 - .../aztec3/circuits/abis/complete_address.hpp | 90 - .../abis/contract_deployment_data.hpp | 105 - .../circuits/abis/contract_storage_read.hpp | 75 - .../abis/contract_storage_update_request.hpp | 81 - .../src/aztec3/circuits/abis/coordinate.hpp | 69 - .../circuits/abis/final_accumulated_data.hpp | 194 - .../aztec3/circuits/abis/function_data.hpp | 91 - .../circuits/abis/function_leaf_preimage.hpp | 97 - .../circuits/abis/function_selector.hpp | 61 - .../aztec3/circuits/abis/global_variables.hpp | 88 - .../cpp/src/aztec3/circuits/abis/index.hpp | 6 - .../abis/kernel_circuit_public_inputs.hpp | 78 - .../kernel_circuit_public_inputs_final.hpp | 79 - .../circuits/abis/membership_witness.hpp | 71 - .../circuits/abis/new_contract_data.hpp | 100 - .../abis/optionally_revealed_data.hpp | 105 - .../cpp/src/aztec3/circuits/abis/packers.hpp | 230 - .../cpp/src/aztec3/circuits/abis/point.hpp | 66 - .../circuits/abis/previous_kernel_data.hpp | 73 - .../abis/private_circuit_public_inputs.hpp | 691 -- .../call_context_reconciliation_data.hpp | 58 - .../circuits/abis/private_kernel/globals.hpp | 42 - .../abis/private_kernel/private_call_data.hpp | 102 - .../private_kernel_inputs_init.hpp | 43 - .../private_kernel_inputs_inner.hpp | 44 - .../private_kernel_inputs_ordering.hpp | 49 - .../abis/public_circuit_public_inputs.hpp | 140 - .../aztec3/circuits/abis/public_data_read.hpp | 76 - .../abis/public_data_update_request.hpp | 81 - .../abis/public_kernel/public_call_data.hpp | 75 - .../public_kernel/public_kernel_inputs.hpp | 44 - .../abis/read_request_membership_witness.hpp | 87 - .../base_or_merge_rollup_public_inputs.hpp | 62 - .../abis/rollup/base/base_rollup_inputs.hpp | 67 - .../abis/rollup/constant_rollup_data.hpp | 34 - .../abis/rollup/merge/merge_rollup_inputs.hpp | 16 - .../rollup/merge/previous_rollup_data.hpp | 27 - .../abis/rollup/nullifier_leaf_preimage.hpp | 32 - .../abis/rollup/root/root_rollup_inputs.hpp | 41 - .../rollup/root/root_rollup_public_inputs.hpp | 128 - .../src/aztec3/circuits/abis/tx_context.hpp | 108 - .../src/aztec3/circuits/abis/tx_request.hpp | 64 - .../cpp/src/aztec3/circuits/abis/types.hpp | 20 - .../cpp/src/aztec3/circuits/apps/.test.cpp | 383 - .../src/aztec3/circuits/apps/CMakeLists.txt | 4 - .../cpp/src/aztec3/circuits/apps/README.md | 59 - .../cpp/src/aztec3/circuits/apps/contract.hpp | 103 - .../cpp/src/aztec3/circuits/apps/contract.tpp | 65 - .../circuits/apps/function_declaration.hpp | 23 - .../apps/function_execution_context.hpp | 336 - .../apps/notes/default_private_note/note.hpp | 130 - .../apps/notes/default_private_note/note.tpp | 195 - .../default_private_note/note_preimage.hpp | 102 - .../nullifier_preimage.hpp | 44 - .../default_singleton_private_note/note.hpp | 111 - .../default_singleton_private_note/note.tpp | 251 - .../note_preimage.hpp | 106 - .../nullifier_preimage.hpp | 45 - .../circuits/apps/notes/note_interface.hpp | 54 - .../aztec3/circuits/apps/opcodes/opcodes.hpp | 83 - .../aztec3/circuits/apps/opcodes/opcodes.tpp | 153 - .../aztec3/circuits/apps/oracle_wrapper.hpp | 133 - .../circuits/apps/private_state.test.cpp | 119 - .../apps/state_vars/field_state_var.hpp | 52 - .../apps/state_vars/mapping_state_var.hpp | 79 - .../apps/state_vars/mapping_state_var.tpp | 129 - .../apps/state_vars/state_var_base.hpp | 98 - .../apps/state_vars/state_var_base.tpp | 36 - .../apps/state_vars/utxo_set_state_var.hpp | 64 - .../apps/state_vars/utxo_set_state_var.tpp | 27 - .../apps/state_vars/utxo_state_var.hpp | 73 - .../apps/state_vars/utxo_state_var.tpp | 33 - .../basic_contract_deployment.cpp | 53 - .../basic_contract_deployment.hpp | 14 - .../basic_contract_deployment/contract.hpp | 23 - .../basic_contract_deployment/init.hpp | 43 - .../circuits/apps/test_apps/escrow/.test.cpp | 117 - .../circuits/apps/test_apps/escrow/README.md | 3 - .../apps/test_apps/escrow/contract.hpp | 26 - .../apps/test_apps/escrow/deposit.cpp | 67 - .../apps/test_apps/escrow/deposit.hpp | 14 - .../circuits/apps/test_apps/escrow/index.hpp | 5 - .../circuits/apps/test_apps/escrow/init.hpp | 43 - .../apps/test_apps/escrow/transfer.cpp | 105 - .../apps/test_apps/escrow/transfer.hpp | 20 - .../apps/test_apps/escrow/withdraw.cpp | 96 - .../apps/test_apps/escrow/withdraw.hpp | 19 - .../.test.cpp | 66 - .../README.md | 2 - .../contract.hpp | 43 - .../function_1_1.cpp | 86 - .../function_1_1.hpp | 11 - .../function_2_1.cpp | 73 - .../function_2_1.hpp | 13 - .../index.hpp | 4 - .../private_to_private_function_call/init.hpp | 40 - .../src/aztec3/circuits/apps/utxo_datum.hpp | 49 - circuits/cpp/src/aztec3/circuits/hash.hpp | 458 - .../src/aztec3/circuits/kernel/CMakeLists.txt | 9 - .../aztec3/circuits/kernel/private/.test.cpp | 104 - .../aztec3/circuits/kernel/private/c_bind.cpp | 49 - .../aztec3/circuits/kernel/private/c_bind.h | 10 - .../aztec3/circuits/kernel/private/common.cpp | 438 - .../aztec3/circuits/kernel/private/common.hpp | 81 - .../private/fixtures/ultra_plonk_proof.dat | Bin 3520 -> 0 bytes .../fixtures/ultra_plonk_verification_key.dat | Bin 1718 -> 0 bytes .../aztec3/circuits/kernel/private/index.hpp | 5 - .../aztec3/circuits/kernel/private/init.hpp | 31 - .../native_private_kernel_circuit.test.cpp | 221 - .../native_private_kernel_circuit_init.cpp | 192 - .../native_private_kernel_circuit_init.hpp | 18 - ...ative_private_kernel_circuit_init.test.cpp | 827 -- .../native_private_kernel_circuit_inner.cpp | 145 - .../native_private_kernel_circuit_inner.hpp | 18 - ...tive_private_kernel_circuit_inner.test.cpp | 986 --- ...native_private_kernel_circuit_ordering.cpp | 192 - ...native_private_kernel_circuit_ordering.hpp | 20 - ...e_private_kernel_circuit_ordering.test.cpp | 510 -- .../kernel/private/private_kernel_circuit.cpp | 343 - .../kernel/private/private_kernel_circuit.hpp | 17 - .../kernel/private/testing_harness.cpp | 555 -- .../kernel/private/testing_harness.hpp | 200 - .../aztec3/circuits/kernel/private/utils.cpp | 137 - .../aztec3/circuits/kernel/private/utils.hpp | 18 - .../aztec3/circuits/kernel/public/.test.cpp | 1292 --- .../aztec3/circuits/kernel/public/c_bind.cpp | 33 - .../aztec3/circuits/kernel/public/c_bind.h | 6 - .../aztec3/circuits/kernel/public/common.cpp | 75 - .../aztec3/circuits/kernel/public/common.hpp | 459 - .../aztec3/circuits/kernel/public/index.hpp | 3 - .../aztec3/circuits/kernel/public/init.hpp | 30 - ...kernel_circuit_private_previous_kernel.cpp | 79 - ...kernel_circuit_private_previous_kernel.hpp | 18 - ..._kernel_circuit_public_previous_kernel.cpp | 73 - ..._kernel_circuit_public_previous_kernel.hpp | 18 - .../src/aztec3/circuits/mock/mock_circuit.hpp | 18 - .../circuits/mock/mock_kernel_circuit.hpp | 50 - .../aztec3/circuits/recursion/CMakeLists.txt | 4 - .../aztec3/circuits/recursion/aggregator.hpp | 22 - .../src/aztec3/circuits/recursion/init.hpp | 21 - .../src/aztec3/circuits/rollup/CMakeLists.txt | 5 - .../src/aztec3/circuits/rollup/base/.test.cpp | 899 -- .../circuits/rollup/base/CMakeLists.txt | 5 - .../aztec3/circuits/rollup/base/c_bind.cpp | 26 - .../src/aztec3/circuits/rollup/base/c_bind.h | 8 - .../src/aztec3/circuits/rollup/base/index.hpp | 2 - .../src/aztec3/circuits/rollup/base/init.hpp | 41 - .../base/native_base_rollup_circuit.cpp | 549 -- .../base/native_base_rollup_circuit.hpp | 17 - .../circuits/rollup/components/components.cpp | 277 - .../circuits/rollup/components/components.hpp | 58 - .../circuits/rollup/components/init.hpp | 30 - .../aztec3/circuits/rollup/merge/.test.cpp | 309 - .../circuits/rollup/merge/CMakeLists.txt | 4 - .../aztec3/circuits/rollup/merge/c_bind.cpp | 22 - .../src/aztec3/circuits/rollup/merge/c_bind.h | 8 - .../aztec3/circuits/rollup/merge/index.hpp | 2 - .../src/aztec3/circuits/rollup/merge/init.hpp | 32 - .../merge/native_merge_rollup_circuit.cpp | 55 - .../merge/native_merge_rollup_circuit.hpp | 7 - .../src/aztec3/circuits/rollup/root/.test.cpp | 337 - .../circuits/rollup/root/CMakeLists.txt | 5 - .../aztec3/circuits/rollup/root/c_bind.cpp | 26 - .../src/aztec3/circuits/rollup/root/c_bind.h | 8 - .../src/aztec3/circuits/rollup/root/index.hpp | 2 - .../src/aztec3/circuits/rollup/root/init.hpp | 31 - .../root/native_root_rollup_circuit.cpp | 152 - .../root/native_root_rollup_circuit.hpp | 16 - .../circuits/rollup/test_utils/init.hpp | 46 - .../nullifier_tree_testing_harness.cpp | 155 - .../nullifier_tree_testing_harness.hpp | 58 - .../circuits/rollup/test_utils/utils.cpp | 599 -- .../circuits/rollup/test_utils/utils.hpp | 130 - circuits/cpp/src/aztec3/constants.hpp | 364 - circuits/cpp/src/aztec3/dbs/CMakeLists.txt | 33 - .../cpp/src/aztec3/dbs/private_state_db.hpp | 151 - circuits/cpp/src/aztec3/oracle/CMakeLists.txt | 5 - circuits/cpp/src/aztec3/oracle/README.md | 13 - circuits/cpp/src/aztec3/oracle/fake_db.hpp | 161 - circuits/cpp/src/aztec3/oracle/oracle.hpp | 150 - circuits/cpp/src/aztec3/utils/CMakeLists.txt | 6 - circuits/cpp/src/aztec3/utils/array.hpp | 290 - circuits/cpp/src/aztec3/utils/array.test.cpp | 298 - .../cpp/src/aztec3/utils/circuit_errors.hpp | 125 - .../aztec3/utils/dummy_circuit_builder.hpp | 76 - .../aztec3/utils/msgpack_derived_equals.hpp | 29 - .../aztec3/utils/msgpack_derived_output.hpp | 38 - .../cpp/src/aztec3/utils/types/CMakeLists.txt | 4 - .../src/aztec3/utils/types/circuit_types.hpp | 97 - .../cpp/src/aztec3/utils/types/convert.hpp | 278 - .../src/aztec3/utils/types/native_types.hpp | 86 - circuits/cpp/srs_db | 1 - cspell.json | 7 + docs/.dockerignore | 3 - docs/Dockerfile.dockerignore | 1 - .../history/differences_to_aztec_connect.md | 15 - .../advanced/circuits/kernels/main.md | 5 - .../concepts/advanced/data_structures/main.md | 5 - docs/docs/concepts/advanced/main.md | 5 - .../dev_docs/contracts/syntax/functions.md | 323 - .../dev_docs/contracts/syntax/storage/main.md | 531 -- .../dev_docs/getting_started/core-concepts.md | 88 - .../{dev_docs => developers}/aztecjs/main.md | 0 .../{dev_docs => developers}/cli/blank_box.md | 0 .../cli/cli-commands.md | 4 +- .../docs/{dev_docs => developers}/cli/main.md | 0 .../cli/run_more_than_one_pxe_sandbox.md | 35 + .../cli/sandbox-reference.md | 23 +- .../{dev_docs => developers}/contracts/abi.md | 0 .../contracts/artifacts.md | 0 .../contracts/compiling.md | 6 +- .../contracts/deploying.md | 2 +- .../contracts/example-contract.md | 0 .../contracts/layout.md | 2 +- .../contracts/main.md | 0 .../contracts/portals/data_structures.md | 0 .../contracts/portals/inbox.md | 0 .../contracts/portals/main.md | 2 +- .../contracts/portals/outbox.md | 0 .../contracts/portals/registry.md | 0 .../resources/common_patterns/authwit.md | 6 +- .../resources/common_patterns/main.md | 6 +- .../contracts/resources/dependencies.md | 0 .../contracts/resources/main.md | 0 .../contracts/resources/style_guide.md | 0 .../security/breaking_changes/main.md | 0 .../contracts/security/breaking_changes/v0.md | 0 .../contracts/security/main.md | 0 .../contracts/setup.md | 0 .../contracts/syntax/constrain.md | 0 .../contracts/syntax/context.mdx | 16 +- .../contracts/syntax/control_structure.md | 0 .../contracts/syntax/events.md | 4 +- .../syntax/functions/calling_functions.md | 132 + .../contracts/syntax/functions/constructor.md | 15 + .../syntax/functions/inner_workings.md | 100 + .../contracts/syntax/functions/main.md | 20 + .../contracts/syntax/functions/oracles.md | 23 + .../functions/public_private_unconstrained.md | 35 + .../contracts/syntax/functions/visibility.md | 25 + .../contracts/syntax/globals.md | 0 .../history_lib_reference.md | 119 + .../historical_access/how_to_prove_history.md | 102 + .../contracts/syntax/main.md | 2 +- .../developers/contracts/syntax/oracles.md | 49 + .../contracts/syntax/slow_updates_tree.md | 2 +- .../contracts/syntax/storage/main.md | 133 + .../contracts/syntax/storage/private_state.md | 361 + .../contracts/syntax/storage/public_state.md | 134 + .../contracts/syntax/storage/storage_slots.md | 4 +- .../contracts/workflow.md | 0 .../debugging/aztecnr-errors.md | 2 +- .../debugging/main.md | 0 .../debugging/sandbox-errors.md | 12 +- .../aztecjs-getting-started.md | 6 +- .../aztecnr-getting-started.md | 6 +- .../getting_started/main.md | 2 +- .../getting_started/quickstart.md | 8 +- .../limitations/main.md | 16 +- .../{dev_docs => developers}/privacy/main.md | 0 .../testing/cheat_codes.md | 8 +- .../{dev_docs => developers}/testing/main.md | 0 .../tutorials/main.md | 0 .../tutorials/testing.md | 4 +- .../token_portal/cancelling_deposits.md | 0 .../token_portal/depositing_to_aztec.md | 15 +- .../tutorials/token_portal/main.md | 2 +- .../token_portal/minting_on_aztec.md | 0 .../tutorials/token_portal/setup.md | 29 +- .../token_portal/typescript_glue_code.md | 17 +- .../token_portal/withdrawing_to_l1.md | 22 +- .../uniswap/execute_private_swap_on_l1.md | 0 .../uniswap/execute_public_swap_on_l1.md | 0 .../tutorials/uniswap/l1_portal.md | 0 .../tutorials/uniswap/l2_contract_setup.md | 0 .../tutorials/uniswap/main.md | 0 .../uniswap/redeeming_swapped_assets_on_l2.md | 0 .../tutorials/uniswap/setup.md | 0 .../tutorials/uniswap/swap_privately.md | 0 .../tutorials/uniswap/swap_publicly.md | 0 .../tutorials/uniswap/typescript_glue_code.md | 0 .../writing_dapp/contract_deployment.md | 3 +- .../writing_dapp/contract_interaction.md | 4 +- .../tutorials/writing_dapp/main.md | 0 .../tutorials/writing_dapp/project_setup.md | 0 .../tutorials/writing_dapp/pxe_service.md | 0 .../tutorials/writing_dapp/testing.md | 2 +- .../writing_private_voting_contract.md | 8 +- .../tutorials/writing_token_contract.md | 11 +- .../docs/{dev_docs => developers}/updating.md | 2 +- .../wallets/architecture.md | 2 +- .../wallets/creating_schnorr_accounts.md | 2 +- .../{dev_docs => developers}/wallets/main.md | 12 +- .../wallets/writing_an_account_contract.md | 8 +- .../about_aztec/technical_overview.md} | 18 +- docs/docs/{ => learn}/about_aztec/vision.md | 2 +- .../about_aztec/what_is_aztec.mdx} | 6 +- .../concepts}/accounts/authwit.md | 6 +- .../concepts}/accounts/keys.md | 2 +- .../concepts}/accounts/main.md | 2 +- .../concepts}/block_production.md | 0 .../foundation => learn/concepts}/blocks.md | 0 .../circuits/kernels/private_kernel.md | 4 +- .../circuits/kernels/public_kernel.md | 2 +- .../concepts}/circuits/main.md | 2 +- .../circuits/rollup_circuits/main.md | 2 +- .../communication/cross_chain_calls.md | 0 .../concepts}/communication/main.md | 0 .../public_private_calls/main.md | 2 +- .../public_private_calls/slow_updates_tree.md | 2 +- .../foundation => learn/concepts}/globals.md | 0 .../concepts/hybrid_state}/main.md | 16 +- .../concepts/hybrid_state}/public_vm.md | 0 docs/docs/learn/concepts/main.md | 75 + .../nodes_clients/execution_client.md | 0 .../concepts}/nodes_clients/prover_client.md | 0 .../concepts/nodes_clients/sequencer/main.md} | 2 +- .../sequencer}/sequencer_selection.md | 0 .../concepts/pxe}/acir_simulator.md | 4 +- docs/docs/learn/concepts/pxe/main.md | 70 + .../smart_contracts}/contract_creation.md | 4 +- .../concepts/smart_contracts/main.md} | 2 +- .../concepts/storage}/storage_slots.md | 8 +- .../storage/trees}/indexed_merkle_tree.md | 0 .../concepts/storage/trees/main.md} | 38 +- .../concepts}/transactions.md | 4 +- .../concepts}/upgrade_mechanism.md | 0 docs/docs/misc/common/_disclaimer.mdx | 2 +- .../how_to_contribute.md | 0 docs/docs/misc/migration_notes.md | 331 + .../roadmap/cryptography_roadmap.md | 0 .../roadmap/engineering_roadmap.md | 2 +- .../roadmap/features_initial_ldt.md | 0 .../{about_aztec => misc}/roadmap/main.md | 0 docs/docs/{intro.md => welcome.md} | 12 +- docs/docusaurus.config.js | 4 +- docs/internal_notes/building_dapps.md | 2 +- docs/netlify.toml | 80 +- docs/sidebars.js | 367 +- l1-contracts/Dockerfile | 17 +- l1-contracts/README.md | 17 +- l1-contracts/package.json | 4 +- l1-contracts/slither.config.json | 3 + l1-contracts/slither_has_diff.sh | 12 + l1-contracts/slither_output.md | 383 + .../src/core/libraries/ConstantsGen.sol | 14 +- l1-contracts/src/core/libraries/Errors.sol | 3 + l1-contracts/src/core/libraries/HeaderLib.sol | 134 +- .../periphery/ContractDeploymentEmitter.sol | 20 +- .../interfaces/IContractDeploymentEmitter.sol | 20 +- l1-contracts/test/Rollup.t.sol | 6 +- l1-contracts/test/decoders/Decoder.t.sol | 6 +- ...rDecoderHelper.sol => HeaderLibHelper.sol} | 2 +- l1-contracts/test/fixtures/empty_block_0.json | 2 +- l1-contracts/test/fixtures/empty_block_1.json | 8 +- l1-contracts/test/fixtures/mixed_block_0.json | 10 +- l1-contracts/test/fixtures/mixed_block_1.json | 14 +- l1-contracts/test/portals/TokenPortal.sol | 2 +- noir/.github/ISSUE_TEMPLATE/config.yml | 4 - ...ea_action_plan.yml => feature_request.yml} | 4 +- noir/.github/scripts/wasm-bindgen-install.sh | 4 +- noir/.github/workflows/docs-pr.yml | 9 + .../.github/workflows/publish-es-packages.yml | 51 +- noir/.github/workflows/release.yml | 11 +- noir/.gitrepo | 4 +- noir/Cargo.toml | 1 - .../acir/{acir_docs.md => README.md} | 0 noir/acvm-repo/acir/codegen/acir.cpp | 1641 +++- .../acir/src/circuit/black_box_functions.rs | 42 +- .../opcodes/black_box_function_call.rs | 100 +- noir/acvm-repo/acir/src/lib.rs | 6 +- .../acir/tests/test_program_serialization.rs | 83 +- .../compiler/optimizers/redundant_range.rs | 72 +- .../acvm/src/compiler/transformers/mod.rs | 46 +- noir/acvm-repo/acvm/src/lib.rs | 10 + .../src/pwg/blackbox/fixed_base_scalar_mul.rs | 21 + noir/acvm-repo/acvm/src/pwg/blackbox/mod.rs | 25 +- noir/acvm-repo/acvm/src/pwg/brillig.rs | 56 +- noir/acvm-repo/acvm/tests/solver.rs | 86 +- noir/acvm-repo/acvm_js/src/logging.rs | 25 +- .../test/shared/complex_foreign_call.ts | 11 +- .../acvm_js/test/shared/foreign_call.ts | 8 +- .../src/curve_specific_solver.rs | 12 - .../src/fixed_base_scalar_mul.rs | 20 + .../bn254_blackbox_solver/src/lib.rs | 20 +- noir/acvm-repo/brillig/src/black_box.rs | 104 +- noir/acvm-repo/brillig/src/lib.rs | 4 +- noir/acvm-repo/brillig/src/opcodes.rs | 97 +- noir/acvm-repo/brillig/src/value.rs | 4 +- noir/acvm-repo/brillig_vm/src/black_box.rs | 172 +- noir/acvm-repo/brillig_vm/src/lib.rs | 612 +- noir/acvm-repo/brillig_vm/src/memory.rs | 42 +- noir/acvm-repo/brillig_vm/src/registers.rs | 43 - noir/aztec_macros/src/lib.rs | 135 +- noir/compiler/noirc_driver/src/abi_gen.rs | 2 +- noir/compiler/noirc_driver/src/contract.rs | 4 +- noir/compiler/noirc_driver/src/lib.rs | 25 +- noir/compiler/noirc_driver/tests/contracts.rs | 42 + .../brillig/brillig_gen/brillig_black_box.rs | 149 +- .../src/brillig/brillig_gen/brillig_block.rs | 74 +- .../brillig_gen/brillig_block_variables.rs | 4 +- .../brillig/brillig_gen/brillig_directive.rs | 38 +- .../brillig/brillig_gen/brillig_slice_ops.rs | 213 +- .../noirc_evaluator/src/brillig/brillig_ir.rs | 211 +- .../src/brillig/brillig_ir/artifact.rs | 2 +- .../brillig/brillig_ir/brillig_variable.rs | 34 +- .../src/brillig/brillig_ir/debug_show.rs | 156 +- .../src/brillig/brillig_ir/entry_point.rs | 621 +- .../src/brillig/brillig_ir/registers.rs | 22 +- noir/compiler/noirc_evaluator/src/errors.rs | 9 +- noir/compiler/noirc_evaluator/src/ssa.rs | 1 - .../src/ssa/acir_gen/acir_ir.rs | 1 + .../src/ssa/acir_gen/acir_ir/acir_variable.rs | 259 +- .../src/ssa/acir_gen/acir_ir/big_int.rs | 57 + .../ssa/acir_gen/acir_ir/generated_acir.rs | 98 +- .../noirc_evaluator/src/ssa/acir_gen/mod.rs | 29 +- .../noirc_evaluator/src/ssa/ir/instruction.rs | 529 +- .../src/ssa/ir/instruction/binary.rs | 349 + .../src/ssa/ir/instruction/call.rs | 15 +- .../src/ssa/ir/instruction/cast.rs | 58 + .../src/ssa/ir/instruction/constrain.rs | 126 + .../src/ssa/opt/flatten_cfg.rs | 26 +- .../noirc_evaluator/src/ssa/ssa_gen/mod.rs | 6 +- .../noirc_frontend/src/ast/function.rs | 2 +- .../src/hir/def_collector/dc_crate.rs | 16 +- .../src/hir/resolution/errors.rs | 7 + .../src/hir/resolution/resolver.rs | 38 +- .../noirc_frontend/src/hir/type_check/expr.rs | 326 +- .../noirc_frontend/src/hir/type_check/stmt.rs | 37 +- .../noirc_frontend/src/hir_def/expr.rs | 16 +- .../noirc_frontend/src/hir_def/types.rs | 112 +- .../noirc_frontend/src/lexer/token.rs | 4 + noir/compiler/noirc_frontend/src/lib.rs | 6 +- .../src/monomorphization/printer.rs | 4 +- .../noirc_frontend/src/node_interner.rs | 10 +- .../noirc_frontend/src/parser/parser.rs | 43 +- .../noirc_frontend/src/resolve_locations.rs | 13 +- noir/compiler/noirc_frontend/src/tests.rs | 2 + noir/compiler/wasm/src/compile.rs | 5 +- noir/compiler/wasm/src/compile_new.rs | 4 +- noir/cspell.json | 9 +- noir/docs/.gitignore | 2 + noir/docs/README.md | 18 +- .../docs/explainers/explainer-recursion.md | 23 +- .../hello_noir/_category_.json | 5 + .../index.md} | 10 +- .../{ => hello_noir}/project_breakdown.md | 22 +- .../getting_started/installation/index.md | 3 + .../getting_started/tooling/_category_.json | 2 +- noir/docs/docs/how_to/how-to-oracles.md | 2 +- noir/docs/docs/how_to/how-to-recursion.md | 23 +- .../docs/how_to/how-to-solidity-verifier.md | 231 + noir/docs/docs/how_to/solidity_verifier.md | 130 - noir/docs/docs/index.md | 85 - noir/docs/docs/index.mdx | 67 + .../docs/noir/concepts/data_types/arrays.md | 20 +- .../docs/noir/concepts/data_types/fields.md | 17 + .../noir/standard_library/black_box_fns.md | 22 +- .../cryptographic_primitives/ec_primitives.md | 2 +- .../ecdsa_sig_verification.mdx | 8 +- .../cryptographic_primitives/hashes.mdx | 60 +- .../cryptographic_primitives/scalar.mdx | 4 +- .../cryptographic_primitives/schnorr.mdx | 4 +- .../docs/noir/standard_library/recursion.md | 48 +- .../docs/docs/noir/standard_library/traits.md | 110 +- noir/docs/docs/tutorials/noirjs_app.md | 7 +- noir/docs/docusaurus.config.ts | 5 +- noir/docs/package.json | 8 +- .../explainers/explainer-oracle.md | 57 + .../explainers/explainer-recursion.md | 177 + .../getting_started/_category_.json | 5 + .../hello_noir/_category_.json | 5 + .../getting_started/hello_noir/index.md | 142 + .../hello_noir/project_breakdown.md | 199 + .../installation/_category_.json | 6 + .../getting_started/installation/index.md | 48 + .../installation/other_install_methods.md | 190 + .../getting_started/tooling/_category_.json | 6 + .../getting_started/tooling/index.mdx | 38 + .../tooling/language_server.md | 43 + .../getting_started/tooling/testing.md | 62 + .../how_to/_category_.json | 5 + .../how_to/how-to-oracles.md | 280 + .../how_to/how-to-recursion.md | 184 + .../how_to/how-to-solidity-verifier.md | 231 + .../how_to/merkle-proof.mdx | 48 + .../how_to/using-devcontainers.mdx | 110 + noir/docs/processed-docs-cache/index.mdx | 67 + .../processed-docs-cache/migration_notes.md | 91 + .../noir/concepts/_category_.json | 6 + .../noir/concepts/assert.md | 27 + .../noir/concepts/comments.md | 33 + .../noir/concepts/control_flow.md | 45 + .../noir/concepts/data_bus.md | 21 + .../noir/concepts/data_types/_category_.json | 5 + .../noir/concepts/data_types/arrays.md | 251 + .../noir/concepts/data_types/booleans.md | 31 + .../noir/concepts/data_types/fields.md | 183 + .../concepts/data_types/function_types.md | 26 + .../noir/concepts/data_types/index.md | 96 + .../noir/concepts/data_types/integers.md | 113 + .../noir/concepts/data_types/references.md | 23 + .../noir/concepts/data_types/slices.mdx | 147 + .../noir/concepts/data_types/strings.md | 80 + .../noir/concepts/data_types/structs.md | 70 + .../noir/concepts/data_types/tuples.md | 48 + .../noir/concepts/data_types/vectors.mdx | 171 + .../noir/concepts/distinct.md | 64 + .../noir/concepts/functions.md | 226 + .../noir/concepts/generics.md | 106 + .../noir/concepts/lambdas.md | 81 + .../noir/concepts/mutability.md | 93 + .../processed-docs-cache/noir/concepts/ops.md | 98 + .../noir/concepts/oracles.md | 23 + .../noir/concepts/shadowing.md | 44 + .../noir/concepts/traits.md | 389 + .../noir/concepts/unconstrained.md | 95 + .../modules_packages_crates/_category_.json | 6 + .../crates_and_packages.md | 43 + .../modules_packages_crates/dependencies.md | 124 + .../noir/modules_packages_crates/modules.md | 105 + .../modules_packages_crates/workspaces.md | 40 + .../noir/standard_library/_category_.json | 6 + .../noir/standard_library/black_box_fns.md | 31 + .../cryptographic_primitives/_category_.json | 5 + .../cryptographic_primitives/ec_primitives.md | 102 + .../ecdsa_sig_verification.mdx | 60 + .../cryptographic_primitives/eddsa.mdx | 18 + .../cryptographic_primitives/hashes.mdx | 234 + .../cryptographic_primitives/index.md | 14 + .../cryptographic_primitives/scalar.mdx | 33 + .../cryptographic_primitives/schnorr.mdx | 45 + .../noir/standard_library/logging.md | 78 + .../noir/standard_library/merkle_trees.md | 58 + .../noir/standard_library/options.md | 97 + .../noir/standard_library/recursion.md | 90 + .../noir/standard_library/traits.md | 408 + .../noir/standard_library/zeroed.md | 25 + .../reference/_category_.json | 5 + .../reference/nargo_commands.md | 253 + .../tutorials/noirjs_app.md | 279 + .../explainers/explainer-oracle.md | 57 + .../explainers/explainer-recursion.md | 177 + .../getting_started/_category_.json | 5 + .../hello_noir/_category_.json | 5 + .../getting_started/hello_noir/index.md | 142 + .../hello_noir/project_breakdown.md | 199 + .../installation/_category_.json | 6 + .../getting_started/installation/index.md | 48 + .../installation/other_install_methods.md | 190 + .../getting_started/tooling/_category_.json | 6 + .../getting_started/tooling/index.mdx | 38 + .../tooling/language_server.md | 43 + .../getting_started/tooling/testing.md | 62 + .../processed-docs/how_to/_category_.json | 5 + .../processed-docs/how_to/how-to-oracles.md | 280 + .../processed-docs/how_to/how-to-recursion.md | 184 + .../how_to/how-to-solidity-verifier.md | 231 + .../processed-docs/how_to/merkle-proof.mdx | 48 + .../how_to/using-devcontainers.mdx | 110 + noir/docs/processed-docs/index.mdx | 67 + noir/docs/processed-docs/migration_notes.md | 91 + .../noir/concepts/_category_.json | 6 + .../processed-docs/noir/concepts/assert.md | 27 + .../processed-docs/noir/concepts/comments.md | 33 + .../noir/concepts/control_flow.md | 45 + .../processed-docs/noir/concepts/data_bus.md | 21 + .../noir/concepts/data_types/_category_.json | 5 + .../noir/concepts/data_types/arrays.md | 251 + .../noir/concepts/data_types/booleans.md | 31 + .../noir/concepts/data_types/fields.md | 183 + .../concepts/data_types/function_types.md | 26 + .../noir/concepts/data_types/index.md | 96 + .../noir/concepts/data_types/integers.md | 113 + .../noir/concepts/data_types/references.md | 23 + .../noir/concepts/data_types/slices.mdx | 147 + .../noir/concepts/data_types/strings.md | 80 + .../noir/concepts/data_types/structs.md | 70 + .../noir/concepts/data_types/tuples.md | 48 + .../noir/concepts/data_types/vectors.mdx | 171 + .../processed-docs/noir/concepts/distinct.md | 64 + .../processed-docs/noir/concepts/functions.md | 226 + .../processed-docs/noir/concepts/generics.md | 106 + .../processed-docs/noir/concepts/lambdas.md | 81 + .../noir/concepts/mutability.md | 93 + noir/docs/processed-docs/noir/concepts/ops.md | 98 + .../processed-docs/noir/concepts/oracles.md | 23 + .../processed-docs/noir/concepts/shadowing.md | 44 + .../processed-docs/noir/concepts/traits.md | 389 + .../noir/concepts/unconstrained.md | 95 + .../modules_packages_crates/_category_.json | 6 + .../crates_and_packages.md | 43 + .../modules_packages_crates/dependencies.md | 124 + .../noir/modules_packages_crates/modules.md | 105 + .../modules_packages_crates/workspaces.md | 40 + .../noir/standard_library/_category_.json | 6 + .../noir/standard_library/black_box_fns.md | 31 + .../cryptographic_primitives/_category_.json | 5 + .../cryptographic_primitives/ec_primitives.md | 102 + .../ecdsa_sig_verification.mdx | 60 + .../cryptographic_primitives/eddsa.mdx | 18 + .../cryptographic_primitives/hashes.mdx | 234 + .../cryptographic_primitives/index.md | 14 + .../cryptographic_primitives/scalar.mdx | 33 + .../cryptographic_primitives/schnorr.mdx | 45 + .../noir/standard_library/logging.md | 78 + .../noir/standard_library/merkle_trees.md | 58 + .../noir/standard_library/options.md | 97 + .../noir/standard_library/recursion.md | 90 + .../noir/standard_library/traits.md | 408 + .../noir/standard_library/zeroed.md | 25 + .../NoirJS/backend_barretenberg/.nojekyll | 0 .../classes/BarretenbergBackend.md | 202 + .../NoirJS/backend_barretenberg/index.md | 0 .../interfaces/Backend.md | 0 .../type-aliases/BackendOptions.md | 0 .../type-aliases/CompiledCircuit.md | 0 .../type-aliases/ProofData.md | 0 .../backend_barretenberg/typedoc-sidebar.cjs | 0 .../reference/NoirJS/noir_js/.nojekyll | 0 .../reference/NoirJS/noir_js/classes/Noir.md | 0 .../reference/NoirJS/noir_js/functions/and.md | 0 .../NoirJS/noir_js/functions/blake2s256.md | 0 .../functions/ecdsa_secp256k1_verify.md | 0 .../functions/ecdsa_secp256r1_verify.md | 0 .../NoirJS/noir_js/functions/keccak256.md | 0 .../NoirJS/noir_js/functions/sha256.md | 0 .../reference/NoirJS/noir_js/functions/xor.md | 0 .../reference/NoirJS/noir_js/index.md | 0 .../noir_js/type-aliases/CompiledCircuit.md | 0 .../type-aliases/ForeignCallHandler.md | 0 .../noir_js/type-aliases/ForeignCallInput.md | 0 .../noir_js/type-aliases/ForeignCallOutput.md | 0 .../NoirJS/noir_js/type-aliases/InputMap.md | 0 .../NoirJS/noir_js/type-aliases/ProofData.md | 0 .../NoirJS/noir_js/type-aliases/WitnessMap.md | 0 .../NoirJS/noir_js/typedoc-sidebar.cjs | 0 .../processed-docs/reference/_category_.json | 5 + .../reference/nargo_commands.md | 253 + .../processed-docs/tutorials/noirjs_app.md | 279 + noir/docs/scripts/preprocess/include_code.js | 312 + noir/docs/scripts/preprocess/index.js | 141 + noir/docs/src/css/custom.css | 84 +- noir/docs/src/pages/index.jsx | 60 +- noir/docs/static/img/aztec_logo.png | Bin 0 -> 38182 bytes .../img/how-tos/solidity_verifier_1.png | Bin 0 -> 33611 bytes .../img/how-tos/solidity_verifier_2.png | Bin 0 -> 66931 bytes .../img/how-tos/solidity_verifier_3.png | Bin 0 -> 63153 bytes .../img/how-tos/solidity_verifier_4.png | Bin 0 -> 79069 bytes .../img/how-tos/solidity_verifier_5.png | Bin 0 -> 48989 bytes noir/docs/static/img/logo.png | Bin 0 -> 178782 bytes noir/docs/static/img/solidity_verifier_ex.png | Bin 0 -> 1177990 bytes .../getting_started/01_hello_world.md | 2 +- .../language_concepts/06_generics.md | 2 +- .../getting_started/01_tiny_noir_app.md | 2 +- .../04_ec_primitives.md | 2 +- .../getting_started/01_hello_world.md | 2 +- .../getting_started/01_tiny_noir_app.md | 2 +- .../04_ec_primitives.md | 2 +- .../getting_started/01_hello_world.md | 2 +- .../language_concepts/06_generics.md | 2 +- .../getting_started/01_tiny_noir_app.md | 2 +- .../04_ec_primitives.md | 2 +- .../getting_started/01_hello_world.md | 2 +- .../language_concepts/06_generics.md | 2 +- .../getting_started/01_tiny_noir_app.md | 2 +- .../04_ec_primitives.md | 2 +- .../getting_started/01_hello_world.md | 2 +- .../language_concepts/06_generics.md | 2 +- .../getting_started/01_tiny_noir_app.md | 2 +- .../04_ec_primitives.md | 2 +- .../getting_started/01_hello_world.md | 2 +- .../language_concepts/06_generics.md | 2 +- .../getting_started/01_tiny_noir_app.md | 2 +- .../04_ec_primitives.md | 2 +- .../how_to/how-to-recursion.md | 2 +- .../cryptographic_primitives/ec_primitives.md | 2 +- .../version-v0.22.0/noir/syntax/generics.md | 2 +- .../version-v0.22.0/tutorials/noirjs_app.md | 2 +- .../explainers/explainer-oracle.md | 57 + .../explainers/explainer-recursion.md | 176 + .../getting_started/_category_.json | 5 + .../hello_noir/_category_.json | 5 + .../getting_started/hello_noir/index.md | 142 + .../hello_noir/project_breakdown.md | 199 + .../installation/_category_.json | 6 + .../getting_started/installation/index.md | 48 + .../installation/other_install_methods.md | 190 + .../getting_started/tooling/_category_.json | 6 + .../getting_started/tooling/index.mdx | 38 + .../tooling/language_server.md | 43 + .../getting_started/tooling/testing.md | 62 + .../version-v0.23.0/how_to/_category_.json | 5 + .../version-v0.23.0/how_to/how-to-oracles.md | 280 + .../how_to/how-to-recursion.md | 179 + .../how_to/how-to-solidity-verifier.md | 231 + .../version-v0.23.0/how_to/merkle-proof.mdx | 48 + .../how_to/using-devcontainers.mdx | 110 + .../versioned_docs/version-v0.23.0/index.mdx | 67 + .../version-v0.23.0/migration_notes.md | 91 + .../noir/concepts/_category_.json | 6 + .../version-v0.23.0/noir/concepts/assert.md | 27 + .../version-v0.23.0/noir/concepts/comments.md | 33 + .../noir/concepts/control_flow.md | 45 + .../version-v0.23.0/noir/concepts/data_bus.md | 21 + .../noir/concepts/data_types/_category_.json | 5 + .../noir/concepts/data_types/arrays.md | 249 + .../noir/concepts/data_types/booleans.md | 31 + .../noir/concepts/data_types/fields.md | 166 + .../concepts/data_types/function_types.md | 26 + .../noir/concepts/data_types/index.md | 96 + .../noir/concepts/data_types/integers.md | 113 + .../noir/concepts/data_types/references.md | 23 + .../noir/concepts/data_types/slices.mdx | 147 + .../noir/concepts/data_types/strings.md | 80 + .../noir/concepts/data_types/structs.md | 70 + .../noir/concepts/data_types/tuples.md | 48 + .../noir/concepts/data_types/vectors.mdx | 171 + .../version-v0.23.0/noir/concepts/distinct.md | 64 + .../noir/concepts/functions.md | 226 + .../version-v0.23.0/noir/concepts/generics.md | 106 + .../version-v0.23.0/noir/concepts/lambdas.md | 81 + .../noir/concepts/mutability.md | 93 + .../version-v0.23.0/noir/concepts/ops.md | 98 + .../version-v0.23.0/noir/concepts/oracles.md | 23 + .../noir/concepts/shadowing.md | 44 + .../version-v0.23.0/noir/concepts/traits.md | 389 + .../noir/concepts/unconstrained.md | 95 + .../modules_packages_crates/_category_.json | 6 + .../crates_and_packages.md | 43 + .../modules_packages_crates/dependencies.md | 124 + .../noir/modules_packages_crates/modules.md | 105 + .../modules_packages_crates/workspaces.md | 40 + .../noir/standard_library/_category_.json | 6 + .../noir/standard_library/black_box_fns.md | 45 + .../cryptographic_primitives/_category_.json | 5 + .../cryptographic_primitives/ec_primitives.md | 102 + .../ecdsa_sig_verification.mdx | 46 + .../cryptographic_primitives/eddsa.mdx | 18 + .../cryptographic_primitives/hashes.mdx | 167 + .../cryptographic_primitives/index.md | 14 + .../cryptographic_primitives/scalar.mdx | 28 + .../cryptographic_primitives/schnorr.mdx | 38 + .../noir/standard_library/logging.md | 78 + .../noir/standard_library/merkle_trees.md | 58 + .../noir/standard_library/options.md | 97 + .../noir/standard_library/recursion.md | 68 + .../noir/standard_library/traits.md | 284 + .../noir/standard_library/zeroed.md | 25 + .../reference/backend_barretenberg/.nojekyll | 1 + .../classes/BarretenbergBackend.md | 0 .../reference/backend_barretenberg/index.md | 45 + .../interfaces/Backend.md | 132 + .../type-aliases/BackendOptions.md | 19 + .../type-aliases/CompiledCircuit.md | 20 + .../type-aliases/ProofData.md | 20 + .../backend_barretenberg/typedoc-sidebar.cjs | 4 + .../noir_js/reference/noir_js/.nojekyll | 1 + .../noir_js/reference/noir_js/classes/Noir.md | 131 + .../reference/noir_js/functions/and.md | 22 + .../reference/noir_js/functions/blake2s256.md | 21 + .../functions/ecdsa_secp256k1_verify.md | 29 + .../functions/ecdsa_secp256r1_verify.md | 28 + .../reference/noir_js/functions/keccak256.md | 21 + .../reference/noir_js/functions/sha256.md | 21 + .../reference/noir_js/functions/xor.md | 22 + .../noir_js/reference/noir_js/index.md | 37 + .../noir_js/type-aliases/CompiledCircuit.md | 20 + .../type-aliases/ForeignCallHandler.md | 24 + .../noir_js/type-aliases/ForeignCallInput.md | 9 + .../noir_js/type-aliases/ForeignCallOutput.md | 9 + .../noir_js/type-aliases/InputMap.md | 13 + .../noir_js/type-aliases/ProofData.md | 20 + .../noir_js/type-aliases/WitnessMap.md | 9 + .../reference/noir_js/typedoc-sidebar.cjs | 4 + .../NoirJS/backend_barretenberg/.nojekyll | 1 + .../classes/BarretenbergBackend.md | 185 + .../NoirJS/backend_barretenberg/index.md | 46 + .../interfaces/Backend.md | 132 + .../type-aliases/BackendOptions.md | 19 + .../type-aliases/CompiledCircuit.md | 20 + .../type-aliases/ProofData.md | 20 + .../backend_barretenberg/typedoc-sidebar.cjs | 4 + .../reference/NoirJS/noir_js/.nojekyll | 1 + .../reference/NoirJS/noir_js/classes/Noir.md | 132 + .../reference/NoirJS/noir_js/functions/and.md | 22 + .../NoirJS/noir_js/functions/blake2s256.md | 21 + .../functions/ecdsa_secp256k1_verify.md | 28 + .../functions/ecdsa_secp256r1_verify.md | 28 + .../NoirJS/noir_js/functions/keccak256.md | 21 + .../NoirJS/noir_js/functions/sha256.md | 21 + .../reference/NoirJS/noir_js/functions/xor.md | 22 + .../reference/NoirJS/noir_js/index.md | 37 + .../noir_js/type-aliases/CompiledCircuit.md | 20 + .../type-aliases/ForeignCallHandler.md | 24 + .../noir_js/type-aliases/ForeignCallInput.md | 9 + .../noir_js/type-aliases/ForeignCallOutput.md | 9 + .../NoirJS/noir_js/type-aliases/InputMap.md | 13 + .../NoirJS/noir_js/type-aliases/ProofData.md | 20 + .../NoirJS/noir_js/type-aliases/WitnessMap.md | 9 + .../NoirJS/noir_js/typedoc-sidebar.cjs | 4 + .../version-v0.23.0/reference/_category_.json | 5 + .../reference/nargo_commands.md | 253 + .../version-v0.23.0/tutorials/noirjs_app.md | 279 + .../version-v0.23.0-sidebars.json | 83 + noir/noir_stdlib/src/array.nr | 4 +- noir/noir_stdlib/src/bigint.nr | 53 + noir/noir_stdlib/src/cmp.nr | 6 +- noir/noir_stdlib/src/collections.nr | 1 + .../src/collections/bounded_vec.nr | 88 + noir/noir_stdlib/src/convert.nr | 61 + noir/noir_stdlib/src/default.nr | 2 + noir/noir_stdlib/src/ecdsa_secp256k1.nr | 13 +- noir/noir_stdlib/src/ecdsa_secp256r1.nr | 13 +- noir/noir_stdlib/src/field.nr | 10 +- noir/noir_stdlib/src/hash.nr | 43 +- noir/noir_stdlib/src/lib.nr | 12 +- noir/noir_stdlib/src/ops.nr | 23 +- noir/noir_stdlib/src/prelude.nr | 2 + noir/noir_stdlib/src/scalar_mul.nr | 30 +- noir/noir_stdlib/src/schnorr.nr | 13 +- noir/noir_stdlib/src/slice.nr | 12 +- noir/noir_stdlib/src/string.nr | 2 +- noir/noir_stdlib/src/test.nr | 10 +- noir/scripts/bootstrap_native.sh | 6 + noir/scripts/install_wasm-bindgen.sh | 6 +- .../builtin_function_declaration/Nargo.toml | 7 + .../builtin_function_declaration/src/main.nr | 10 + .../foreign_function_declaration/Nargo.toml | 7 + .../foreign_function_declaration/src/main.nr | 10 + .../multiple_contracts/Nargo.toml | 5 - .../multiple_contracts/src/main.nr | 3 - .../intrinsic_die/src/main.nr | 2 - .../trait_generics/src/main.nr | 9 +- .../brillig_nested_arrays/src/main.nr | 17 +- .../execution_success/databus/src/main.nr | 14 +- .../execution_success/keccak256/src/main.nr | 7 +- .../pedersen_commitment/Nargo.toml | 6 + .../pedersen_commitment/Prover.toml | 6 + .../pedersen_commitment/src/main.nr | 10 + .../pedersen_hash/Nargo.toml | 6 + .../pedersen_hash/Prover.toml | 4 + .../pedersen_hash/src/main.nr | 9 + .../poseidon_bn254_hash/src/main.nr | 2 + .../regression_4088/Nargo.toml | 5 + .../regression_4088/Prover.toml | 2 + .../regression_4088/src/main.nr | 27 + .../regression_4124/Nargo.toml | 7 + .../regression_4124/Prover.toml | 1 + .../regression_4124/src/main.nr | 39 + .../execution_success/scalar_mul/src/main.nr | 8 + .../execution_success/to_le_bytes/Prover.toml | 1 + .../execution_success/to_le_bytes/src/main.nr | 9 +- .../noir_test_success/bounded_vec/Nargo.toml | 7 + .../noir_test_success/bounded_vec/Prover.toml | 0 .../noir_test_success/bounded_vec/src/main.nr | 105 + .../out_of_bounds_alignment/Nargo.toml | 5 + .../out_of_bounds_alignment/Prover.toml | 0 .../out_of_bounds_alignment/src/main.nr | 17 + .../regression_4080/Nargo.toml | 5 + .../regression_4080/Prover.toml | 1 + .../regression_4080/src/main.nr | 8 + noir/tooling/backend_interface/src/cli/mod.rs | 2 +- noir/tooling/bb_abstraction_leaks/build.rs | 2 +- noir/tooling/debugger/src/context.rs | 66 +- noir/tooling/debugger/src/repl.rs | 61 +- noir/tooling/lsp/src/lib.rs | 58 +- noir/tooling/lsp/src/requests/profile_run.rs | 20 +- noir/tooling/lsp/src/solver.rs | 8 - noir/tooling/nargo/src/artifacts/debug.rs | 12 +- noir/tooling/nargo/src/ops/compile.rs | 35 +- noir/tooling/nargo/src/ops/foreign_calls.rs | 4 +- noir/tooling/nargo/src/ops/mod.rs | 3 + noir/tooling/nargo/src/ops/optimize.rs | 18 +- noir/tooling/nargo/src/ops/transform.rs | 30 + noir/tooling/nargo/src/workspace.rs | 2 + .../nargo_cli/src/cli/codegen_verifier_cmd.rs | 23 +- noir/tooling/nargo_cli/src/cli/compile_cmd.rs | 54 +- noir/tooling/nargo_cli/src/cli/dap_cmd.rs | 55 +- noir/tooling/nargo_cli/src/cli/debug_cmd.rs | 21 +- noir/tooling/nargo_cli/src/cli/execute_cmd.rs | 25 +- noir/tooling/nargo_cli/src/cli/info_cmd.rs | 17 +- noir/tooling/nargo_cli/src/cli/prove_cmd.rs | 26 +- noir/tooling/nargo_cli/src/cli/test_cmd.rs | 98 +- noir/tooling/nargo_cli/src/cli/verify_cmd.rs | 30 +- noir/tooling/nargo_toml/src/lib.rs | 8 +- .../noir_js_backend_barretenberg/package.json | 2 +- .../noir_js_backend_barretenberg/src/index.ts | 63 +- noir/tooling/noirc_abi/src/lib.rs | 2 +- noir/yarn.lock | 10 +- yarn-project/Dockerfile.prod | 64 +- yarn-project/accounts/src/defaults/index.ts | 2 +- yarn-project/accounts/src/ecdsa/index.ts | 10 +- yarn-project/accounts/src/schnorr/index.ts | 8 +- yarn-project/accounts/src/single_key/index.ts | 11 +- yarn-project/acir-simulator/package.json | 1 + .../acir-simulator/src/acvm/deserialize.ts | 176 +- .../acir-simulator/src/acvm/oracle/oracle.ts | 28 +- .../src/acvm/oracle/typed_oracle.ts | 26 +- .../acir-simulator/src/acvm/serialize.ts | 40 +- .../acir-simulator/src/avm/avm_context.ts | 112 +- .../src/avm/avm_execution_environment.test.ts | 55 + .../src/avm/avm_execution_environment.ts | 93 + .../src/avm/avm_machine_state.ts | 66 +- .../src/avm/avm_memory_types.test.ts | 22 + .../src/avm/avm_memory_types.ts | 313 + .../src/avm/avm_message_call_result.ts | 3 +- .../src/avm/avm_state_manager.ts | 45 - .../acir-simulator/src/avm/fixtures/index.ts | 39 + .../acir-simulator/src/avm/index.test.ts | 38 +- yarn-project/acir-simulator/src/avm/index.ts | 3 - .../src/avm/interpreter/interpreter.test.ts | 34 +- .../src/avm/interpreter/interpreter.ts | 84 +- .../src/avm/journal/host_storage.ts | 5 +- .../src/avm/journal/journal.test.ts | 55 +- .../acir-simulator/src/avm/journal/journal.ts | 176 +- .../src/avm/opcodes/.eslintrc.cjs | 8 + .../src/avm/opcodes/accrued_substate.test.ts | 89 + .../src/avm/opcodes/accrued_substate.ts | 84 + .../src/avm/opcodes/arithmetic.test.ts | 110 +- .../src/avm/opcodes/arithmetic.ts | 48 +- .../src/avm/opcodes/bitwise.test.ts | 199 +- .../acir-simulator/src/avm/opcodes/bitwise.ts | 98 +- .../src/avm/opcodes/comparators.test.ts | 147 + .../src/avm/opcodes/comparators.ts | 54 +- .../src/avm/opcodes/control_flow.test.ts | 260 +- .../src/avm/opcodes/control_flow.ts | 55 +- .../src/avm/opcodes/decode_bytecode.ts | 5 +- .../src/avm/opcodes/encode_to_bytecode.ts | 6 +- .../avm/opcodes/environment_getters.test.ts | 111 + .../src/avm/opcodes/environment_getters.ts | 275 + .../src/avm/opcodes/external_calls.test.ts | 132 + .../src/avm/opcodes/external_calls.ts | 100 + .../acir-simulator/src/avm/opcodes/index.ts | 1 - .../src/avm/opcodes/instruction.ts | 43 +- .../src/avm/opcodes/instruction_set.ts | 14 +- .../src/avm/opcodes/memory.test.ts | 335 +- .../acir-simulator/src/avm/opcodes/memory.ts | 73 +- .../acir-simulator/src/avm/opcodes/opcodes.ts | 2 +- .../src/avm/opcodes/storage.test.ts | 68 + .../acir-simulator/src/avm/opcodes/storage.ts | 65 + .../src/client/client_execution_context.ts | 53 +- .../acir-simulator/src/client/db_oracle.ts | 11 +- .../src/client/pick_notes.test.ts | 121 +- .../acir-simulator/src/client/pick_notes.ts | 21 +- .../src/client/private_execution.test.ts | 326 +- .../acir-simulator/src/client/simulator.ts | 7 +- .../client/unconstrained_execution.test.ts | 4 +- .../src/client/unconstrained_execution.ts | 7 +- .../src/client/view_data_oracle.ts | 58 +- .../acir-simulator/src/public/executor.ts | 6 +- .../acir-simulator/src/public/index.test.ts | 305 +- .../src/public/public_execution_context.ts | 22 +- yarn-project/acir-simulator/tsconfig.json | 3 + yarn-project/archiver/package.json | 5 +- .../archiver/src/archiver/archiver.test.ts | 6 +- .../archiver/src/archiver/archiver.ts | 74 +- .../archiver/src/archiver/archiver_store.ts | 29 + .../src/archiver/archiver_store_test_suite.ts | 42 + .../archiver/src/archiver/eth_log_handlers.ts | 15 +- .../archiver/kv_archiver_store/block_store.ts | 16 +- .../kv_archiver_store/contract_class_store.ts | 26 + .../contract_instance_store.ts | 26 + .../kv_archiver_store/contract_store.ts | 2 +- .../kv_archiver_store.test.ts | 3 +- .../kv_archiver_store/kv_archiver_store.ts | 23 + .../archiver/kv_archiver_store/log_store.ts | 4 +- .../kv_archiver_store/message_store.ts | 8 +- .../memory_archiver_store.ts | 27 + yarn-project/archiver/src/index.ts | 1 + .../archiver/src/rpc/archiver_client.ts | 33 + .../archiver/src/rpc/archiver_server.ts | 37 + yarn-project/archiver/src/rpc/index.ts | 2 + yarn-project/archiver/tsconfig.json | 3 + yarn-project/aztec-node/package.json | 6 - .../aztec-node/src/aztec-node/config.ts | 4 + yarn-project/aztec-node/src/aztec-node/db.ts | 92 - .../src/aztec-node/http_rpc_server.ts | 5 +- .../aztec-node/src/aztec-node/server.ts | 101 +- yarn-project/aztec-node/src/declaration.d.ts | 16 - yarn-project/aztec-node/terraform/main.tf | 4 +- yarn-project/aztec-nr/.gitrepo | 4 +- .../aztec-nr/address-note/src/address_note.nr | 102 +- yarn-project/aztec-nr/authwit/src/account.nr | 5 +- .../aztec-nr/authwit/src/entrypoint.nr | 28 +- yarn-project/aztec-nr/aztec/src/abi.nr | 21 +- yarn-project/aztec-nr/aztec/src/context.nr | 100 +- .../aztec/src/history/contract_inclusion.nr | 45 +- .../aztec/src/history/note_inclusion.nr | 11 +- .../aztec/src/history/note_validity.nr | 7 +- .../aztec/src/history/nullifier_inclusion.nr | 4 +- .../src/history/nullifier_non_inclusion.nr | 9 +- .../src/history/public_value_inclusion.nr | 22 +- .../aztec-nr/aztec/src/key/nullifier_key.nr | 18 +- yarn-project/aztec-nr/aztec/src/lib.nr | 1 - yarn-project/aztec-nr/aztec/src/messaging.nr | 25 +- .../aztec/src/messaging/l1_to_l2_message.nr | 6 +- .../messaging/l1_to_l2_message_getter_data.nr | 8 +- yarn-project/aztec-nr/aztec/src/note.nr | 1 - .../aztec-nr/aztec/src/note/lifecycle.nr | 61 +- .../aztec-nr/aztec/src/note/note_getter.nr | 108 +- .../aztec/src/note/note_getter_options.nr | 66 +- .../aztec-nr/aztec/src/note/note_hash.nr | 23 - .../aztec-nr/aztec/src/note/note_interface.nr | 23 +- .../aztec/src/note/note_viewer_options.nr | 27 +- yarn-project/aztec-nr/aztec/src/note/utils.nr | 100 +- yarn-project/aztec-nr/aztec/src/oracle.nr | 2 +- .../aztec/src/oracle/get_block_header.nr | 56 - .../src/oracle/get_public_data_witness.nr | 16 +- .../aztec/src/oracle/get_public_key.nr | 3 +- .../aztec-nr/aztec/src/oracle/header.nr | 57 + .../aztec-nr/aztec/src/oracle/notes.nr | 28 +- .../aztec/src/oracle/nullifier_key.nr | 18 +- .../aztec-nr/aztec/src/oracle/storage.nr | 8 +- yarn-project/aztec-nr/aztec/src/state_vars.nr | 1 + .../src/state_vars/immutable_singleton.nr | 49 +- .../aztec/src/state_vars/public_state.nr | 21 +- .../aztec-nr/aztec/src/state_vars/set.nr | 34 +- .../aztec/src/state_vars/singleton.nr | 83 +- .../src/state_vars/stable_public_state.nr | 69 + yarn-project/aztec-nr/aztec/src/types.nr | 2 - .../aztec/src/types/type_serialization.nr | 15 - .../address_serialization.nr | 22 - .../type_serialization/bool_serialization.nr | 16 - .../type_serialization/field_serialization.nr | 18 - .../type_serialization/u32_serialization.nr | 16 - .../type_serialization/u8_serialization.nr | 16 - yarn-project/aztec-nr/aztec/src/types/vec.nr | 126 - .../src/compressed_string.nr | 38 +- .../aztec-nr/compressed-string/src/lib.nr | 2 +- .../src/easy_private_state.nr | 7 +- .../aztec-nr/field-note/src/field_note.nr | 81 +- yarn-project/aztec-nr/safe-math/Nargo.toml | 3 +- yarn-project/aztec-nr/safe-math/src/lib.nr | 2 +- .../aztec-nr/safe-math/src/safe_u120.nr | 16 + .../slow-updates-tree/src/slow_map.nr | 28 +- .../aztec-nr/value-note/src/balance_utils.nr | 4 +- yarn-project/aztec-nr/value-note/src/utils.nr | 16 +- .../aztec-nr/value-note/src/value_note.nr | 100 +- yarn-project/aztec-sandbox/src/bin/index.ts | 244 - .../aztec.js/src/account_manager/index.ts | 51 +- .../aztec.js/src/contract/contract.test.ts | 4 + .../aztec.js/src/contract/contract.ts | 13 +- .../aztec.js/src/contract/contract_base.ts | 26 +- .../aztec.js/src/contract/deploy_method.ts | 41 +- .../aztec.js/src/contract/deploy_sent_tx.ts | 14 +- yarn-project/aztec.js/src/contract/index.ts | 4 +- yarn-project/aztec.js/src/index.ts | 9 +- .../aztec.js/src/rpc_clients/index.ts | 1 + .../src/{ => rpc_clients}/pxe_client.ts | 3 +- .../aztec.js/src/utils/l1_contracts.ts | 2 +- .../wallet/account_wallet_with_private_key.ts | 9 +- .../aztec.js/src/wallet/base_wallet.ts | 5 +- .../{aztec-sandbox => aztec}/.eslintrc.cjs | 0 .../{aztec-sandbox => aztec}/.gitignore | 0 .../{aztec-sandbox => aztec}/Dockerfile | 4 +- .../{aztec-sandbox => aztec}/README.md | 8 +- .../docker-compose.yml | 7 +- .../{aztec-sandbox => aztec}/package.json | 7 +- yarn-project/aztec/src/aztec_client.ts | 0 yarn-project/aztec/src/bin/index.ts | 66 + yarn-project/aztec/src/cli/cli.ts | 70 + .../aztec/src/cli/cmds/start_archiver.ts | 31 + yarn-project/aztec/src/cli/cmds/start_node.ts | 87 + .../aztec/src/cli/cmds/start_p2p_bootstrap.ts | 21 + yarn-project/aztec/src/cli/cmds/start_pxe.ts | 38 + yarn-project/aztec/src/cli/index.ts | 1 + yarn-project/aztec/src/cli/texts.ts | 71 + yarn-project/aztec/src/cli/util.ts | 139 + .../src/examples/token.ts | 0 .../src/examples/util.ts | 0 .../{aztec-sandbox => aztec}/src/index.ts | 0 .../{aztec-sandbox => aztec}/src/logging.ts | 4 +- .../{aztec-sandbox => aztec}/src/sandbox.ts | 10 +- .../{aztec-sandbox => aztec}/src/splash.ts | 0 .../{aztec-sandbox => aztec}/tsconfig.json | 6 + .../src/aztec_node/rpc/aztec_node_client.ts | 5 +- .../circuit-types/src/contract_dao.test.ts | 7 +- .../circuit-types/src/contract_dao.ts | 23 +- .../circuit-types/src/contract_data.ts | 73 +- .../src/interfaces/aztec-node.ts | 11 +- .../src/interfaces/deployed-contract.ts | 11 +- .../circuit-types/src/interfaces/pxe.ts | 9 + .../circuit-types/src/keys/key_store.ts | 9 + .../circuit-types/src/l1_to_l2_message.ts | 14 + yarn-project/circuit-types/src/l2_block.ts | 71 +- yarn-project/circuit-types/src/l2_tx.ts | 8 +- .../src/logs/l2_block_l2_logs.ts | 18 + yarn-project/circuit-types/src/mocks.ts | 9 +- .../circuit-types/src/notes/note_filter.ts | 23 + .../fixtures/Benchmarking.test.json | 1699 ++++ yarn-project/circuits.js/package.json | 4 +- .../src/abis/__snapshots__/abis.test.ts.snap | 427 +- .../circuits.js/src/abis/abis.test.ts | 91 +- yarn-project/circuits.js/src/abis/abis.ts | 205 +- .../src/abis/merkle_tree_calculator.ts | 16 +- yarn-project/circuits.js/src/constants.gen.ts | 14 +- .../contract_address.test.ts.snap | 259 + .../__snapshots__/contract_class.test.ts.snap | 49 + .../src/contract/artifact_hash.test.ts | 11 + .../circuits.js/src/contract/artifact_hash.ts | 67 + .../src/contract/contract_address.test.ts | 75 + .../src/contract/contract_address.ts | 102 + .../src/contract/contract_class.test.ts | 15 + .../src/contract/contract_class.ts | 45 + .../src/contract/contract_class_id.test.ts | 34 + .../src/contract/contract_class_id.ts | 78 + .../src/contract/contract_deployment_info.ts | 55 - .../src/contract/contract_instance.ts | 51 + .../circuits.js/src/contract/index.ts | 6 +- yarn-project/circuits.js/src/keys/index.ts | 13 +- .../structs/__snapshots__/header.test.ts.snap | 44 + .../circuits.js/src/structs/call_context.ts | 16 +- .../src/structs/complete_address.ts | 53 +- .../src/structs/global_variables.ts | 10 +- .../circuits.js/src/structs/header.test.ts | 18 +- .../circuits.js/src/structs/header.ts | 96 +- yarn-project/circuits.js/src/structs/index.ts | 2 +- .../src/structs/kernel/block_header.test.ts | 17 - .../src/structs/kernel/block_header.ts | 140 - .../kernel/combined_accumulated_data.ts | 19 +- .../structs/kernel/combined_constant_data.ts | 17 +- .../src/structs/kernel/private_kernel.ts | 38 +- .../nullifier_key_validation_request.ts | 101 + .../src/structs/partial_state_reference.ts | 27 + .../structs/private_circuit_public_inputs.ts | 57 +- .../structs/public_circuit_public_inputs.ts | 13 +- .../rollup/append_only_tree_snapshot.ts | 10 +- .../circuits.js/src/structs/side_effects.ts | 18 +- .../src/structs/state_reference.ts | 15 +- .../circuits.js/src/structs/tx_context.ts | 26 +- .../circuits.js/src/tests/factories.ts | 93 +- .../circuits.js/src/tests/fixtures.ts | 13 + .../circuits.js/src/types/partial_address.ts | 2 +- yarn-project/circuits.js/tsconfig.json | 3 + yarn-project/cli/package.json | 1 + yarn-project/cli/src/bin/index.ts | 2 +- yarn-project/cli/src/client.test.ts | 2 +- yarn-project/cli/src/cmds/add_contract.ts | 40 +- yarn-project/cli/src/cmds/add_note.ts | 3 - yarn-project/cli/src/cmds/block_number.ts | 3 - yarn-project/cli/src/cmds/call.ts | 3 - yarn-project/cli/src/cmds/check_deploy.ts | 3 - yarn-project/cli/src/cmds/compute_selector.ts | 3 - yarn-project/cli/src/cmds/create_account.ts | 3 - yarn-project/cli/src/cmds/deploy.ts | 11 +- .../cli/src/cmds/deploy_l1_contracts.ts | 3 - .../cli/src/cmds/example_contracts.ts | 5 +- .../cli/src/cmds/generate_p2p_private_key.ts | 3 - .../cli/src/cmds/generate_private_key.ts | 3 - yarn-project/cli/src/cmds/get_account.ts | 3 - yarn-project/cli/src/cmds/get_accounts.ts | 3 - .../cli/src/cmds/get_contract_data.ts | 3 - yarn-project/cli/src/cmds/get_logs.ts | 3 - yarn-project/cli/src/cmds/get_node_info.ts | 3 - yarn-project/cli/src/cmds/get_recipient.ts | 3 - yarn-project/cli/src/cmds/get_recipients.ts | 3 - yarn-project/cli/src/cmds/get_tx_receipt.ts | 3 - yarn-project/cli/src/cmds/inspect_contract.ts | 3 - .../cli/src/cmds/parse_parameter_struct.ts | 3 - yarn-project/cli/src/cmds/register_account.ts | 3 - .../cli/src/cmds/register_recipient.ts | 3 - yarn-project/cli/src/cmds/send.ts | 3 - yarn-project/cli/src/cmds/unbox.ts | 3 - yarn-project/cli/src/index.ts | 11 +- yarn-project/cli/src/parse_args.ts | 6 +- yarn-project/cli/src/test/utils.test.ts | 6 +- yarn-project/cli/src/update/update.ts | 2 +- yarn-project/cli/src/utils.ts | 4 +- yarn-project/cli/tsconfig.json | 3 + yarn-project/end-to-end/package.json | 1 + .../scripts/docker-compose-browser.yml | 2 +- .../end-to-end/scripts/docker-compose-p2p.yml | 5 +- .../end-to-end/scripts/docker-compose.yml | 2 +- .../benchmarks/bench_process_history.test.ts | 6 +- .../benchmarks/bench_publish_rollup.test.ts | 7 +- .../end-to-end/src/e2e_2_pxes.test.ts | 50 +- .../src/e2e_account_contracts.test.ts | 50 +- .../src/e2e_blacklist_token_contract.test.ts | 9 +- .../end-to-end/src/e2e_block_building.test.ts | 2 +- .../end-to-end/src/e2e_card_game.test.ts | 96 +- yarn-project/end-to-end/src/e2e_cli.test.ts | 2 +- .../src/e2e_cross_chain_messaging.test.ts | 6 +- .../src/e2e_deploy_contract.test.ts | 20 +- .../src/e2e_escrow_contract.test.ts | 14 +- .../src/e2e_inclusion_proofs_contract.test.ts | 44 +- .../src/e2e_nested_contract.test.ts | 32 +- .../end-to-end/src/e2e_note_getter.test.ts | 257 + .../end-to-end/src/e2e_p2p_network.test.ts | 13 +- .../end-to-end/src/e2e_persistence.test.ts | 56 +- .../e2e_public_cross_chain_messaging.test.ts | 4 +- .../end-to-end/src/e2e_singleton.test.ts | 31 - .../end-to-end/src/e2e_slow_tree.test.ts | 16 +- .../end-to-end/src/e2e_state_vars.test.ts | 160 + .../end-to-end/src/e2e_token_contract.test.ts | 98 +- .../end-to-end/src/fixtures/fixtures.ts | 3 - yarn-project/end-to-end/src/fixtures/utils.ts | 24 +- .../writing_an_account_contract.test.ts | 3 +- .../src/integration_archiver_l1_to_l2.test.ts | 2 +- .../src/integration_l1_publisher.test.ts | 24 +- .../src/shared/cross_chain_test_harness.ts | 3 +- yarn-project/foundation/.eslintrc.docs.cjs | 7 +- yarn-project/foundation/src/abi/decoder.ts | 8 +- yarn-project/foundation/src/abi/encoder.ts | 2 +- yarn-project/foundation/src/abi/selector.ts | 18 +- .../foundation/src/aztec-address/index.ts | 6 + .../foundation/src/eth-address/index.ts | 2 +- yarn-project/foundation/src/fields/fields.ts | 4 + yarn-project/foundation/src/fields/point.ts | 7 +- .../src/json-rpc/class_converter.ts | 4 + .../json-rpc/client/json_rpc_client.test.ts | 26 +- .../src/json-rpc/client/json_rpc_client.ts | 17 +- .../foundation/src/json-rpc/server/index.ts | 2 +- .../src/json-rpc/server/json_proxy.ts | 29 +- .../json-rpc/server/json_rpc_server.test.ts | 15 +- .../src/json-rpc/server/json_rpc_server.ts | 210 +- .../foundation/src/serialize/buffer_reader.ts | 21 + .../src/serialize/field_reader.test.ts | 93 + .../foundation/src/serialize/field_reader.ts | 143 + .../foundation/src/serialize/index.ts | 1 + yarn-project/key-store/src/test_key_store.ts | 18 +- yarn-project/kv-store/package.json | 2 +- .../kv-store/src/interfaces/singleton.ts | 1 + yarn-project/kv-store/src/interfaces/store.ts | 10 +- yarn-project/kv-store/src/lmdb/store.ts | 18 +- yarn-project/merkle-tree/package.json | 5 +- yarn-project/merkle-tree/src/index.ts | 2 +- .../src/interfaces/indexed_tree.ts | 36 +- .../merkle-tree/src/interfaces/merkle_tree.ts | 4 +- yarn-project/merkle-tree/src/load_tree.ts | 19 +- yarn-project/merkle-tree/src/new_tree.ts | 9 +- .../snapshots/append_only_snapshot.test.ts | 7 +- .../src/snapshots/append_only_snapshot.ts | 222 +- .../src/snapshots/base_full_snapshot.ts | 185 +- .../src/snapshots/full_snapshot.test.ts | 7 +- .../src/snapshots/full_snapshot.ts | 2 +- .../snapshots/indexed_tree_snapshot.test.ts | 22 +- .../src/snapshots/indexed_tree_snapshot.ts | 46 +- .../src/snapshots/snapshot_builder.ts | 12 +- .../snapshots/snapshot_builder_test_suite.ts | 14 +- .../src/sparse_tree/sparse_tree.test.ts | 28 +- .../src/sparse_tree/sparse_tree.ts | 15 +- .../standard_indexed_tree.ts | 179 +- .../test/standard_indexed_tree.test.ts | 43 +- .../test/standard_indexed_tree_with_append.ts | 18 +- .../src/standard_tree/standard_tree.test.ts | 24 +- .../src/standard_tree/standard_tree.ts | 23 +- .../src/test/standard_based_test_suite.ts | 11 +- .../merkle-tree/src/test/test_suite.ts | 30 +- .../src/test/utils/create_mem_down.ts | 3 - yarn-project/merkle-tree/src/tree_base.ts | 89 +- yarn-project/merkle-tree/tsconfig.json | 3 + .../add_noir_compiler_commander_actions.ts | 6 - .../src/contract-interface-gen/typescript.ts | 6 +- yarn-project/noir-contracts/Nargo.toml | 7 +- .../contracts/avm_test_contract/Nargo.toml | 8 + .../contracts/avm_test_contract/src/main.nr | 26 + .../benchmarking_contract/src/main.nr | 16 +- .../contracts/card_game_contract/src/cards.nr | 5 +- .../contracts/card_game_contract/src/game.nr | 105 +- .../contracts/card_game_contract/src/main.nr | 10 +- .../contracts/child_contract/src/main.nr | 4 +- .../contracts/counter_contract/src/main.nr | 4 +- .../docs_example_contract/src/main.nr | 120 +- .../docs_example_contract/src/options.nr | 8 +- .../src/types/card_note.nr | 85 +- .../docs_example_contract/src/types/leader.nr | 22 +- .../easy_private_token_contract/src/main.nr | 6 +- .../easy_private_voting_contract/src/main.nr | 22 +- .../src/ecdsa_public_key_note.nr | 127 +- .../ecdsa_account_contract/src/main.nr | 10 +- .../contracts/escrow_contract/src/main.nr | 9 +- .../inclusion_proofs_contract/src/main.nr | 94 +- .../contracts/lending_contract/src/asset.nr | 49 +- .../contracts/lending_contract/src/main.nr | 34 +- .../pending_commitments_contract/src/main.nr | 8 +- .../price_feed_contract/src/asset.nr | 23 +- .../contracts/price_feed_contract/src/main.nr | 5 +- .../contracts/reader_contract/src/main.nr | 33 +- .../schnorr_account_contract/src/main.nr | 10 +- .../src/public_key_note.nr | 107 +- .../src/main.nr | 1 - .../src/util.nr | 7 +- .../slow_tree_contract/src/capsule.nr | 3 + .../contracts/slow_tree_contract/src/main.nr | 10 +- .../stateful_test_contract/src/main.nr | 14 +- .../contracts/test_contract/Nargo.toml | 1 + .../contracts/test_contract/src/main.nr | 124 +- .../token_blacklist_contract/src/main.nr | 46 +- .../token_blacklist_contract/src/types.nr | 1 - .../src/types/balance_set.nr | 6 +- .../src/types/safe_u120_serialization.nr | 18 - .../src/types/token_note.nr | 98 +- .../src/types/transparent_note.nr | 127 +- .../token_bridge_contract/src/main.nr | 6 +- .../contracts/token_contract/src/main.nr | 113 +- .../contracts/token_contract/src/types.nr | 1 - .../token_contract/src/types/balance_set.nr | 8 +- .../src/types/safe_u120_serialization.nr | 18 - .../token_contract/src/types/token_note.nr | 100 +- .../src/types/transparent_note.nr | 135 +- .../contracts/uniswap_contract/src/main.nr | 14 +- yarn-project/noir-contracts/package.json | 2 +- .../noir-contracts/package.local.json | 2 +- .../noir-contracts/scripts/transpile.sh | 8 + .../noir-protocol-circuits/package.json | 1 + .../src/__snapshots__/index.test.ts.snap | 7863 ++++++++++------- .../crates/private-kernel-lib/src/common.nr | 100 +- .../src/private_kernel_init.nr | 74 +- .../src/private_kernel_inner.nr | 43 +- .../src/private_kernel_ordering.nr | 35 +- .../crates/public-kernel-lib/src/common.nr | 14 +- .../src/public_kernel_private_previous.nr | 5 +- .../src/public_kernel_public_previous.nr | 8 +- .../rollup-lib/src/base/base_rollup_inputs.nr | 13 +- .../src/crates/rollup-lib/src/hash.nr | 13 - .../src/crates/rollup-lib/src/lib.nr | 2 - .../src/crates/rollup-lib/src/root.nr | 22 +- .../src/crates/types/src/abis.nr | 4 +- .../src/abis/append_only_tree_snapshot.nr | 22 + .../src/crates/types/src/abis/block_header.nr | 87 - .../src/abis/combined_accumulated_data.nr | 9 +- .../types/src/abis/combined_constant_data.nr | 8 +- .../crates/types/src/abis/complete_address.nr | 31 - .../types/src/abis/function_selector.nr | 25 +- .../crates/types/src/abis/global_variables.nr | 45 +- .../types/src/abis/new_contract_data.nr | 10 +- .../abis/nullifier_key_validation_request.nr | 108 + .../src/abis/private_circuit_public_inputs.nr | 60 +- .../src/abis/public_circuit_public_inputs.nr | 37 +- .../src/crates/types/src/abis/side_effect.nr | 29 +- .../src/crates/types/src/address.nr | 156 +- .../src/crates/types/src/constants.nr | 20 +- .../types/src/contrakt/deployment_data.nr | 27 +- .../src/contrakt/storage_update_request.nr | 8 +- .../src/crates/types/src/grumpkin_point.nr | 19 +- .../crates/types/src/grumpkin_private_key.nr | 45 + .../src/crates/types/src/hash.nr | 5 +- .../src/crates/types/src/header.nr | 84 +- .../src/crates/types/src/interop_testing.nr | 64 +- .../src/crates/types/src/lib.nr | 2 + .../types/src/partial_state_reference.nr | 55 +- .../src/crates/types/src/state_reference.nr | 57 +- .../src/crates/types/src/tests/fixtures.nr | 56 +- .../types/src/tests/fixtures/contracts.nr | 6 +- .../types/src/tests/fixtures/read_requests.nr | 1 - .../src/tests/previous_kernel_data_builder.nr | 15 +- .../src/tests/private_call_data_builder.nr | 21 +- .../private_circuit_public_inputs_builder.nr | 30 +- .../src/tests/public_call_data_builder.nr | 1 - .../public_circuit_public_inputs_builder.nr | 9 +- .../crates/types/src/tests/testing_harness.nr | 13 +- .../src/crates/types/src/traits.nr | 12 + .../crates/types/src/type_serialization.nr | 56 + .../src/crates/types/src/utils.nr | 9 +- .../src/crates/types/src/utils/arrays.nr | 1 - .../src/crates/types/src/utils/bounded_vec.nr | 194 - .../nested-call-private-kernel-init.hex | 1 + .../nested-call-private-kernel-inner.hex | 2 +- .../nested-call-private-kernel-ordering.hex | 1 + .../noir-protocol-circuits/src/index.test.ts | 316 +- .../src/noir_test_gen.test.ts | 8 +- .../src/type_conversion.test.ts | 19 +- .../src/type_conversion.ts | 183 +- .../noir-protocol-circuits/tsconfig.json | 3 + .../scripts/docker-compose-bootstrap.yml | 4 +- yarn-project/p2p-bootstrap/terraform/main.tf | 7 +- .../p2p/src/client/p2p_client.test.ts | 3 +- yarn-project/p2p/src/client/p2p_client.ts | 6 +- yarn-project/p2p/src/config.ts | 4 +- .../p2p/src/tx_pool/aztec_kv_tx_pool.test.ts | 3 +- .../p2p/src/tx_pool/aztec_kv_tx_pool.ts | 2 +- yarn-project/package.json | 8 +- yarn-project/pxe/package.json | 1 + yarn-project/pxe/src/config/index.ts | 7 +- .../pxe/src/contract_data_oracle/index.ts | 4 +- .../memory_contract_database.ts | 4 +- yarn-project/pxe/src/contract_tree/index.ts | 93 +- .../contracts/contract_artifact_db.ts | 19 + .../contracts/contract_instance_db.ts | 18 + yarn-project/pxe/src/database/index.ts | 1 - .../pxe/src/database/kv_pxe_database.test.ts | 3 +- .../pxe/src/database/kv_pxe_database.ts | 352 +- .../pxe/src/database/memory_db.test.ts | 67 - yarn-project/pxe/src/database/memory_db.ts | 223 - .../pxe/src/database/note_dao.test.ts | 2 +- yarn-project/pxe/src/database/pxe_database.ts | 26 +- .../src/database/pxe_database_test_suite.ts | 96 +- yarn-project/pxe/src/kernel_oracle/index.ts | 21 +- .../pxe/src/kernel_prover/kernel_prover.ts | 27 + .../src/kernel_prover/proving_data_oracle.ts | 10 + .../src/note_processor/note_processor.test.ts | 4 +- .../pxe/src/pxe_http/pxe_http_server.ts | 4 - .../pxe/src/pxe_service/create_pxe_service.ts | 7 +- .../pxe/src/pxe_service/pxe_service.ts | 44 +- .../src/pxe_service/test/pxe_service.test.ts | 4 +- .../src/pxe_service/test/pxe_test_suite.ts | 2 +- .../pxe/src/simulator_oracle/index.ts | 19 +- .../pxe/src/synchronizer/synchronizer.test.ts | 61 +- .../pxe/src/synchronizer/synchronizer.ts | 30 +- yarn-project/pxe/tsconfig.json | 3 + yarn-project/sequencer-client/package.json | 1 + .../block_builder/solo_block_builder.test.ts | 53 +- .../src/block_builder/solo_block_builder.ts | 94 +- .../src/publisher/viem-tx-sender.ts | 6 +- .../sequencer-client/src/sequencer/index.ts | 1 - .../src/sequencer/processed_tx.ts | 6 +- .../src/sequencer/public_processor.test.ts | 6 +- .../src/sequencer/public_processor.ts | 22 +- .../src/sequencer/sequencer.test.ts | 10 +- .../src/sequencer/sequencer.ts | 17 +- .../sequencer-client/src/sequencer/utils.ts | 25 - yarn-project/sequencer-client/tsconfig.json | 3 + yarn-project/tsconfig.json | 2 +- yarn-project/typedoc.json | 2 +- yarn-project/types/package.json | 1 + .../types/src/abi/contract_artifact.ts | 20 + .../src/contracts/contract_class.test.ts | 8 + .../types/src/contracts/contract_class.ts | 175 + .../src/contracts/contract_instance.test.ts | 8 + .../types/src/contracts/contract_instance.ts | 83 + yarn-project/types/src/contracts/index.ts | 2 + yarn-project/world-state/package.json | 3 +- .../server_world_state_synchronizer.test.ts | 79 +- .../server_world_state_synchronizer.ts | 61 +- .../world-state-db/merkle_tree_operations.ts | 47 +- .../merkle_tree_operations_facade.ts | 42 +- .../merkle_tree_snapshot_operations_facade.ts | 48 +- .../src/world-state-db/merkle_trees.ts | 287 +- yarn-project/world-state/tsconfig.json | 3 + yarn-project/yarn-project-base/Dockerfile | 3 + yarn-project/yarn.lock | 4985 +++++------ yarn.lock | 4 + .../diversified-and-stealth.md | 4 - .../docs/addresses-and-keys/specification.md | 47 +- .../docs/circuits/private-function.md | 4 +- .../docs/circuits/private-kernel-initial.md | 6 +- .../docs/circuits/private-kernel-inner.md | 2 +- .../docs/circuits/private-kernel-reset.md | 2 +- .../docs/circuits/public-kernel-inner.md | 6 +- .../docs/contract-deployment/classes.md | 223 +- .../docs/contract-deployment/instances.md | 177 +- yellow-paper/docs/public-vm/avm-circuit.md | 27 +- yellow-paper/docs/public-vm/avm.md | 8 +- 1750 files changed, 62444 insertions(+), 49278 deletions(-) delete mode 100644 .vscode/c_cpp_properties.json create mode 100644 avm-transpiler/.gitignore create mode 100644 avm-transpiler/Cargo.lock create mode 100644 avm-transpiler/Cargo.toml create mode 100644 avm-transpiler/Dockerfile create mode 100644 avm-transpiler/Dockerfile.dockerignore create mode 100644 avm-transpiler/README.md create mode 100755 avm-transpiler/bootstrap.sh create mode 100644 avm-transpiler/rust-toolchain.toml create mode 100755 avm-transpiler/scripts/bootstrap_native.sh create mode 100644 avm-transpiler/src/instructions.rs create mode 100644 avm-transpiler/src/main.rs create mode 100644 avm-transpiler/src/opcodes.rs create mode 100644 avm-transpiler/src/transpile.rs create mode 100644 avm-transpiler/src/transpile_contract.rs create mode 100644 avm-transpiler/src/utils.rs create mode 100644 barretenberg/cpp/scripts/_benchmark_remote_lock.sh create mode 100755 barretenberg/cpp/scripts/barretenberg_module_digraph.sh create mode 100644 barretenberg/cpp/scripts/barretenberg_module_digraph_edges.awk create mode 100755 barretenberg/cpp/scripts/benchmark.sh create mode 100755 barretenberg/cpp/scripts/benchmark_remote.sh create mode 100755 barretenberg/cpp/scripts/benchmark_wasm.sh create mode 100755 barretenberg/cpp/scripts/benchmark_wasm_remote.sh delete mode 100644 barretenberg/cpp/src/barretenberg/benchmark/commit_bench/CMakeLists.txt delete mode 100644 barretenberg/cpp/src/barretenberg/benchmark/commit_bench/main.bench.cpp delete mode 100644 barretenberg/cpp/src/barretenberg/benchmark/goblin_bench/main.bench.cpp delete mode 100644 barretenberg/cpp/src/barretenberg/benchmark/plonk_bench/main.bench.cpp delete mode 100644 barretenberg/cpp/src/barretenberg/benchmark/protogalaxy_bench/main.bench.cpp delete mode 100644 barretenberg/cpp/src/barretenberg/benchmark/relations_bench/main.bench.cpp delete mode 100644 barretenberg/cpp/src/barretenberg/benchmark/ultra_bench/main.bench.cpp delete mode 100644 barretenberg/cpp/src/barretenberg/benchmark/widgets_bench/main.bench.cpp rename barretenberg/cpp/src/barretenberg/{benchmark/commit_bench => commitment_schemes}/commit.bench.cpp (86%) create mode 100644 barretenberg/cpp/src/barretenberg/common/utils.cpp create mode 100644 barretenberg/cpp/src/barretenberg/common/utils.hpp create mode 100644 barretenberg/cpp/src/barretenberg/dsl/acir_format/bigint_constraint.cpp create mode 100644 barretenberg/cpp/src/barretenberg/dsl/acir_format/bigint_constraint.hpp create mode 100644 barretenberg/cpp/src/barretenberg/dsl/acir_format/bigint_constraint.test.cpp create mode 100644 barretenberg/cpp/src/barretenberg/dsl/acir_format/ec_operations.test.cpp create mode 100644 barretenberg/cpp/src/barretenberg/ecc/fields/field_conversion.cpp create mode 100644 barretenberg/cpp/src/barretenberg/ecc/fields/field_conversion.hpp create mode 100644 barretenberg/cpp/src/barretenberg/ecc/fields/field_conversion.test.cpp create mode 100644 barretenberg/cpp/src/barretenberg/goblin/mock_circuits_pinning.test.cpp create mode 100644 barretenberg/cpp/src/barretenberg/honk/proof_system/types/proof.hpp delete mode 100644 barretenberg/cpp/src/barretenberg/stdlib/hash/benchmarks/celer/CMakeLists.txt rename barretenberg/cpp/src/barretenberg/stdlib/hash/benchmarks/{celer/sha256.bench.cpp => celer_sha256.bench.cpp} (100%) delete mode 100644 barretenberg/cpp/src/barretenberg/stdlib/hash/benchmarks/external/CMakeLists.txt rename barretenberg/cpp/src/barretenberg/stdlib/hash/benchmarks/{external/external.bench.cpp => external_sha256_blake3s.bench.cpp} (100%) delete mode 100644 barretenberg/cpp/src/barretenberg/stdlib/hash/benchmarks/sha256/CMakeLists.txt rename barretenberg/cpp/src/barretenberg/stdlib/hash/benchmarks/{sha256/sha256.bench.cpp => stdlib_sha256.bench.cpp} (100%) create mode 100644 barretenberg/cpp/src/barretenberg/stdlib/recursion/honk/verifier/decider_recursive_verifier.cpp create mode 100644 barretenberg/cpp/src/barretenberg/stdlib/recursion/honk/verifier/decider_recursive_verifier.hpp create mode 100644 barretenberg/cpp/src/barretenberg/stdlib/recursion/honk/verifier/protogalaxy_recursive_verifier.cpp create mode 100644 barretenberg/cpp/src/barretenberg/stdlib/recursion/honk/verifier/protogalaxy_recursive_verifier.hpp create mode 100644 barretenberg/cpp/src/barretenberg/stdlib/recursion/honk/verifier/protogalaxy_recursive_verifier.test.cpp create mode 100644 barretenberg/cpp/src/barretenberg/vm/avm_trace/AvmMini_execution.cpp create mode 100644 barretenberg/cpp/src/barretenberg/vm/avm_trace/AvmMini_execution.hpp create mode 100644 barretenberg/cpp/src/barretenberg/vm/avm_trace/AvmMini_instructions.hpp create mode 100644 barretenberg/cpp/src/barretenberg/vm/avm_trace/AvmMini_opcode.cpp create mode 100644 barretenberg/cpp/src/barretenberg/vm/avm_trace/AvmMini_opcode.hpp create mode 100644 barretenberg/cpp/src/barretenberg/vm/tests/AvmMini_execution.test.cpp delete mode 100644 boxes/blank-react/src/app/components/contract_function_form.module.scss delete mode 100644 boxes/blank-react/src/app/components/copy.module.scss delete mode 100644 boxes/blank-react/src/app/components/copy.tsx delete mode 100644 boxes/blank-react/src/app/components/dropdown.module.scss delete mode 100644 boxes/blank-react/src/app/components/dropdown.tsx delete mode 100644 boxes/blank-react/src/app/components/popup.module.scss delete mode 100644 boxes/blank-react/src/app/components/select.module.scss delete mode 100644 boxes/blank-react/src/app/components/select.tsx delete mode 100644 boxes/blank-react/src/app/components/terms.tsx delete mode 100644 boxes/blank-react/src/app/components/wallet_dropdown.module.scss delete mode 100644 boxes/blank-react/src/app/components/wallet_dropdown.tsx delete mode 100644 boxes/blank-react/src/app/contract.module.scss delete mode 100644 boxes/blank-react/src/app/home.module.scss create mode 100644 boxes/blank-react/src/app/hooks/useAccounts.tsx create mode 100644 boxes/blank-react/src/app/hooks/useContract.tsx create mode 100644 boxes/blank-react/src/contracts/log/.b7ec86e927411b6b375064925b8088b0cd141771-audit.json create mode 100644 boxes/blank-react/src/contracts/log/aztec-sandbox-2024-02-01.debug.log create mode 120000 boxes/blank-react/src/contracts/log/aztec-sandbox.debug.log delete mode 100644 boxes/blank-react/src/scripts/deploy_contract.ts delete mode 100644 boxes/token/src/contracts/src/types/safe_u120_serialization.nr delete mode 100644 circuits/.gitignore delete mode 100644 circuits/CODING_STANDARD.md delete mode 100644 circuits/README.md delete mode 100644 circuits/cpp/.clang-format delete mode 100644 circuits/cpp/.clang-tidy delete mode 100644 circuits/cpp/.clang-tidy.slow delete mode 100644 circuits/cpp/.clangd delete mode 100644 circuits/cpp/.gitignore delete mode 100644 circuits/cpp/.rebuild_patterns delete mode 100644 circuits/cpp/CMakeLists.txt delete mode 100644 circuits/cpp/CMakePresets.json delete mode 120000 circuits/cpp/barretenberg delete mode 100755 circuits/cpp/bootstrap.sh delete mode 100644 circuits/cpp/cmake/arch.cmake delete mode 100644 circuits/cpp/cmake/benchmark.cmake delete mode 100644 circuits/cpp/cmake/build.cmake delete mode 100644 circuits/cpp/cmake/gtest.cmake delete mode 100644 circuits/cpp/cmake/module.cmake delete mode 100644 circuits/cpp/cmake/msgpack.cmake delete mode 100644 circuits/cpp/cmake/threading.cmake delete mode 100644 circuits/cpp/cmake/toolchains/aarch64-darwin.cmake delete mode 100644 circuits/cpp/cmake/toolchains/aarch64-linux.cmake delete mode 100644 circuits/cpp/cmake/toolchains/i386-linux.cmake delete mode 100644 circuits/cpp/cmake/toolchains/wasm32-wasi.cmake delete mode 100644 circuits/cpp/cmake/toolchains/x86_64-darwin.cmake delete mode 100644 circuits/cpp/cmake/toolchains/x86_64-linux.cmake delete mode 100755 circuits/cpp/format.sh delete mode 100644 circuits/cpp/scripts/a3-tests delete mode 100755 circuits/cpp/scripts/build_run_tests_docker_local delete mode 100755 circuits/cpp/scripts/collect_coverage_information.sh delete mode 100755 circuits/cpp/scripts/run_coverage delete mode 100755 circuits/cpp/scripts/run_tests delete mode 100755 circuits/cpp/scripts/run_tests_local delete mode 100755 circuits/cpp/scripts/tidy.sh delete mode 100644 circuits/cpp/src/CMakeLists.txt delete mode 100644 circuits/cpp/src/aztec3/CMakeLists.txt delete mode 100644 circuits/cpp/src/aztec3/circuits/CMakeLists.txt delete mode 100644 circuits/cpp/src/aztec3/circuits/abis/.test.cpp delete mode 100644 circuits/cpp/src/aztec3/circuits/abis/CMakeLists.txt delete mode 100644 circuits/cpp/src/aztec3/circuits/abis/README.md delete mode 100644 circuits/cpp/src/aztec3/circuits/abis/append_only_tree_snapshot.hpp delete mode 100644 circuits/cpp/src/aztec3/circuits/abis/block_header.hpp delete mode 100644 circuits/cpp/src/aztec3/circuits/abis/c_bind.cpp delete mode 100644 circuits/cpp/src/aztec3/circuits/abis/c_bind.h delete mode 100644 circuits/cpp/src/aztec3/circuits/abis/c_bind.test.cpp delete mode 100644 circuits/cpp/src/aztec3/circuits/abis/call_context.hpp delete mode 100644 circuits/cpp/src/aztec3/circuits/abis/call_stack_item.hpp delete mode 100644 circuits/cpp/src/aztec3/circuits/abis/combined_accumulated_data.hpp delete mode 100644 circuits/cpp/src/aztec3/circuits/abis/combined_constant_data.hpp delete mode 100644 circuits/cpp/src/aztec3/circuits/abis/complete_address.hpp delete mode 100644 circuits/cpp/src/aztec3/circuits/abis/contract_deployment_data.hpp delete mode 100644 circuits/cpp/src/aztec3/circuits/abis/contract_storage_read.hpp delete mode 100644 circuits/cpp/src/aztec3/circuits/abis/contract_storage_update_request.hpp delete mode 100644 circuits/cpp/src/aztec3/circuits/abis/coordinate.hpp delete mode 100644 circuits/cpp/src/aztec3/circuits/abis/final_accumulated_data.hpp delete mode 100644 circuits/cpp/src/aztec3/circuits/abis/function_data.hpp delete mode 100644 circuits/cpp/src/aztec3/circuits/abis/function_leaf_preimage.hpp delete mode 100644 circuits/cpp/src/aztec3/circuits/abis/function_selector.hpp delete mode 100644 circuits/cpp/src/aztec3/circuits/abis/global_variables.hpp delete mode 100644 circuits/cpp/src/aztec3/circuits/abis/index.hpp delete mode 100644 circuits/cpp/src/aztec3/circuits/abis/kernel_circuit_public_inputs.hpp delete mode 100644 circuits/cpp/src/aztec3/circuits/abis/kernel_circuit_public_inputs_final.hpp delete mode 100644 circuits/cpp/src/aztec3/circuits/abis/membership_witness.hpp delete mode 100644 circuits/cpp/src/aztec3/circuits/abis/new_contract_data.hpp delete mode 100644 circuits/cpp/src/aztec3/circuits/abis/optionally_revealed_data.hpp delete mode 100644 circuits/cpp/src/aztec3/circuits/abis/packers.hpp delete mode 100644 circuits/cpp/src/aztec3/circuits/abis/point.hpp delete mode 100644 circuits/cpp/src/aztec3/circuits/abis/previous_kernel_data.hpp delete mode 100644 circuits/cpp/src/aztec3/circuits/abis/private_circuit_public_inputs.hpp delete mode 100644 circuits/cpp/src/aztec3/circuits/abis/private_kernel/call_context_reconciliation_data.hpp delete mode 100644 circuits/cpp/src/aztec3/circuits/abis/private_kernel/globals.hpp delete mode 100644 circuits/cpp/src/aztec3/circuits/abis/private_kernel/private_call_data.hpp delete mode 100644 circuits/cpp/src/aztec3/circuits/abis/private_kernel/private_kernel_inputs_init.hpp delete mode 100644 circuits/cpp/src/aztec3/circuits/abis/private_kernel/private_kernel_inputs_inner.hpp delete mode 100644 circuits/cpp/src/aztec3/circuits/abis/private_kernel/private_kernel_inputs_ordering.hpp delete mode 100644 circuits/cpp/src/aztec3/circuits/abis/public_circuit_public_inputs.hpp delete mode 100644 circuits/cpp/src/aztec3/circuits/abis/public_data_read.hpp delete mode 100644 circuits/cpp/src/aztec3/circuits/abis/public_data_update_request.hpp delete mode 100644 circuits/cpp/src/aztec3/circuits/abis/public_kernel/public_call_data.hpp delete mode 100644 circuits/cpp/src/aztec3/circuits/abis/public_kernel/public_kernel_inputs.hpp delete mode 100644 circuits/cpp/src/aztec3/circuits/abis/read_request_membership_witness.hpp delete mode 100644 circuits/cpp/src/aztec3/circuits/abis/rollup/base/base_or_merge_rollup_public_inputs.hpp delete mode 100644 circuits/cpp/src/aztec3/circuits/abis/rollup/base/base_rollup_inputs.hpp delete mode 100644 circuits/cpp/src/aztec3/circuits/abis/rollup/constant_rollup_data.hpp delete mode 100644 circuits/cpp/src/aztec3/circuits/abis/rollup/merge/merge_rollup_inputs.hpp delete mode 100644 circuits/cpp/src/aztec3/circuits/abis/rollup/merge/previous_rollup_data.hpp delete mode 100644 circuits/cpp/src/aztec3/circuits/abis/rollup/nullifier_leaf_preimage.hpp delete mode 100644 circuits/cpp/src/aztec3/circuits/abis/rollup/root/root_rollup_inputs.hpp delete mode 100644 circuits/cpp/src/aztec3/circuits/abis/rollup/root/root_rollup_public_inputs.hpp delete mode 100644 circuits/cpp/src/aztec3/circuits/abis/tx_context.hpp delete mode 100644 circuits/cpp/src/aztec3/circuits/abis/tx_request.hpp delete mode 100644 circuits/cpp/src/aztec3/circuits/abis/types.hpp delete mode 100644 circuits/cpp/src/aztec3/circuits/apps/.test.cpp delete mode 100644 circuits/cpp/src/aztec3/circuits/apps/CMakeLists.txt delete mode 100644 circuits/cpp/src/aztec3/circuits/apps/README.md delete mode 100644 circuits/cpp/src/aztec3/circuits/apps/contract.hpp delete mode 100644 circuits/cpp/src/aztec3/circuits/apps/contract.tpp delete mode 100644 circuits/cpp/src/aztec3/circuits/apps/function_declaration.hpp delete mode 100644 circuits/cpp/src/aztec3/circuits/apps/function_execution_context.hpp delete mode 100644 circuits/cpp/src/aztec3/circuits/apps/notes/default_private_note/note.hpp delete mode 100644 circuits/cpp/src/aztec3/circuits/apps/notes/default_private_note/note.tpp delete mode 100644 circuits/cpp/src/aztec3/circuits/apps/notes/default_private_note/note_preimage.hpp delete mode 100644 circuits/cpp/src/aztec3/circuits/apps/notes/default_private_note/nullifier_preimage.hpp delete mode 100644 circuits/cpp/src/aztec3/circuits/apps/notes/default_singleton_private_note/note.hpp delete mode 100644 circuits/cpp/src/aztec3/circuits/apps/notes/default_singleton_private_note/note.tpp delete mode 100644 circuits/cpp/src/aztec3/circuits/apps/notes/default_singleton_private_note/note_preimage.hpp delete mode 100644 circuits/cpp/src/aztec3/circuits/apps/notes/default_singleton_private_note/nullifier_preimage.hpp delete mode 100644 circuits/cpp/src/aztec3/circuits/apps/notes/note_interface.hpp delete mode 100644 circuits/cpp/src/aztec3/circuits/apps/opcodes/opcodes.hpp delete mode 100644 circuits/cpp/src/aztec3/circuits/apps/opcodes/opcodes.tpp delete mode 100644 circuits/cpp/src/aztec3/circuits/apps/oracle_wrapper.hpp delete mode 100644 circuits/cpp/src/aztec3/circuits/apps/private_state.test.cpp delete mode 100644 circuits/cpp/src/aztec3/circuits/apps/state_vars/field_state_var.hpp delete mode 100644 circuits/cpp/src/aztec3/circuits/apps/state_vars/mapping_state_var.hpp delete mode 100644 circuits/cpp/src/aztec3/circuits/apps/state_vars/mapping_state_var.tpp delete mode 100644 circuits/cpp/src/aztec3/circuits/apps/state_vars/state_var_base.hpp delete mode 100644 circuits/cpp/src/aztec3/circuits/apps/state_vars/state_var_base.tpp delete mode 100644 circuits/cpp/src/aztec3/circuits/apps/state_vars/utxo_set_state_var.hpp delete mode 100644 circuits/cpp/src/aztec3/circuits/apps/state_vars/utxo_set_state_var.tpp delete mode 100644 circuits/cpp/src/aztec3/circuits/apps/state_vars/utxo_state_var.hpp delete mode 100644 circuits/cpp/src/aztec3/circuits/apps/state_vars/utxo_state_var.tpp delete mode 100644 circuits/cpp/src/aztec3/circuits/apps/test_apps/basic_contract_deployment/basic_contract_deployment.cpp delete mode 100644 circuits/cpp/src/aztec3/circuits/apps/test_apps/basic_contract_deployment/basic_contract_deployment.hpp delete mode 100644 circuits/cpp/src/aztec3/circuits/apps/test_apps/basic_contract_deployment/contract.hpp delete mode 100644 circuits/cpp/src/aztec3/circuits/apps/test_apps/basic_contract_deployment/init.hpp delete mode 100644 circuits/cpp/src/aztec3/circuits/apps/test_apps/escrow/.test.cpp delete mode 100644 circuits/cpp/src/aztec3/circuits/apps/test_apps/escrow/README.md delete mode 100644 circuits/cpp/src/aztec3/circuits/apps/test_apps/escrow/contract.hpp delete mode 100644 circuits/cpp/src/aztec3/circuits/apps/test_apps/escrow/deposit.cpp delete mode 100644 circuits/cpp/src/aztec3/circuits/apps/test_apps/escrow/deposit.hpp delete mode 100644 circuits/cpp/src/aztec3/circuits/apps/test_apps/escrow/index.hpp delete mode 100644 circuits/cpp/src/aztec3/circuits/apps/test_apps/escrow/init.hpp delete mode 100644 circuits/cpp/src/aztec3/circuits/apps/test_apps/escrow/transfer.cpp delete mode 100644 circuits/cpp/src/aztec3/circuits/apps/test_apps/escrow/transfer.hpp delete mode 100644 circuits/cpp/src/aztec3/circuits/apps/test_apps/escrow/withdraw.cpp delete mode 100644 circuits/cpp/src/aztec3/circuits/apps/test_apps/escrow/withdraw.hpp delete mode 100644 circuits/cpp/src/aztec3/circuits/apps/test_apps/private_to_private_function_call/.test.cpp delete mode 100644 circuits/cpp/src/aztec3/circuits/apps/test_apps/private_to_private_function_call/README.md delete mode 100644 circuits/cpp/src/aztec3/circuits/apps/test_apps/private_to_private_function_call/contract.hpp delete mode 100644 circuits/cpp/src/aztec3/circuits/apps/test_apps/private_to_private_function_call/function_1_1.cpp delete mode 100644 circuits/cpp/src/aztec3/circuits/apps/test_apps/private_to_private_function_call/function_1_1.hpp delete mode 100644 circuits/cpp/src/aztec3/circuits/apps/test_apps/private_to_private_function_call/function_2_1.cpp delete mode 100644 circuits/cpp/src/aztec3/circuits/apps/test_apps/private_to_private_function_call/function_2_1.hpp delete mode 100644 circuits/cpp/src/aztec3/circuits/apps/test_apps/private_to_private_function_call/index.hpp delete mode 100644 circuits/cpp/src/aztec3/circuits/apps/test_apps/private_to_private_function_call/init.hpp delete mode 100644 circuits/cpp/src/aztec3/circuits/apps/utxo_datum.hpp delete mode 100644 circuits/cpp/src/aztec3/circuits/hash.hpp delete mode 100644 circuits/cpp/src/aztec3/circuits/kernel/CMakeLists.txt delete mode 100644 circuits/cpp/src/aztec3/circuits/kernel/private/.test.cpp delete mode 100644 circuits/cpp/src/aztec3/circuits/kernel/private/c_bind.cpp delete mode 100644 circuits/cpp/src/aztec3/circuits/kernel/private/c_bind.h delete mode 100644 circuits/cpp/src/aztec3/circuits/kernel/private/common.cpp delete mode 100644 circuits/cpp/src/aztec3/circuits/kernel/private/common.hpp delete mode 100644 circuits/cpp/src/aztec3/circuits/kernel/private/fixtures/ultra_plonk_proof.dat delete mode 100644 circuits/cpp/src/aztec3/circuits/kernel/private/fixtures/ultra_plonk_verification_key.dat delete mode 100644 circuits/cpp/src/aztec3/circuits/kernel/private/index.hpp delete mode 100644 circuits/cpp/src/aztec3/circuits/kernel/private/init.hpp delete mode 100644 circuits/cpp/src/aztec3/circuits/kernel/private/native_private_kernel_circuit.test.cpp delete mode 100644 circuits/cpp/src/aztec3/circuits/kernel/private/native_private_kernel_circuit_init.cpp delete mode 100644 circuits/cpp/src/aztec3/circuits/kernel/private/native_private_kernel_circuit_init.hpp delete mode 100644 circuits/cpp/src/aztec3/circuits/kernel/private/native_private_kernel_circuit_init.test.cpp delete mode 100644 circuits/cpp/src/aztec3/circuits/kernel/private/native_private_kernel_circuit_inner.cpp delete mode 100644 circuits/cpp/src/aztec3/circuits/kernel/private/native_private_kernel_circuit_inner.hpp delete mode 100644 circuits/cpp/src/aztec3/circuits/kernel/private/native_private_kernel_circuit_inner.test.cpp delete mode 100644 circuits/cpp/src/aztec3/circuits/kernel/private/native_private_kernel_circuit_ordering.cpp delete mode 100644 circuits/cpp/src/aztec3/circuits/kernel/private/native_private_kernel_circuit_ordering.hpp delete mode 100644 circuits/cpp/src/aztec3/circuits/kernel/private/native_private_kernel_circuit_ordering.test.cpp delete mode 100644 circuits/cpp/src/aztec3/circuits/kernel/private/private_kernel_circuit.cpp delete mode 100644 circuits/cpp/src/aztec3/circuits/kernel/private/private_kernel_circuit.hpp delete mode 100644 circuits/cpp/src/aztec3/circuits/kernel/private/testing_harness.cpp delete mode 100644 circuits/cpp/src/aztec3/circuits/kernel/private/testing_harness.hpp delete mode 100644 circuits/cpp/src/aztec3/circuits/kernel/private/utils.cpp delete mode 100644 circuits/cpp/src/aztec3/circuits/kernel/private/utils.hpp delete mode 100644 circuits/cpp/src/aztec3/circuits/kernel/public/.test.cpp delete mode 100644 circuits/cpp/src/aztec3/circuits/kernel/public/c_bind.cpp delete mode 100644 circuits/cpp/src/aztec3/circuits/kernel/public/c_bind.h delete mode 100644 circuits/cpp/src/aztec3/circuits/kernel/public/common.cpp delete mode 100644 circuits/cpp/src/aztec3/circuits/kernel/public/common.hpp delete mode 100644 circuits/cpp/src/aztec3/circuits/kernel/public/index.hpp delete mode 100644 circuits/cpp/src/aztec3/circuits/kernel/public/init.hpp delete mode 100644 circuits/cpp/src/aztec3/circuits/kernel/public/native_public_kernel_circuit_private_previous_kernel.cpp delete mode 100644 circuits/cpp/src/aztec3/circuits/kernel/public/native_public_kernel_circuit_private_previous_kernel.hpp delete mode 100644 circuits/cpp/src/aztec3/circuits/kernel/public/native_public_kernel_circuit_public_previous_kernel.cpp delete mode 100644 circuits/cpp/src/aztec3/circuits/kernel/public/native_public_kernel_circuit_public_previous_kernel.hpp delete mode 100644 circuits/cpp/src/aztec3/circuits/mock/mock_circuit.hpp delete mode 100644 circuits/cpp/src/aztec3/circuits/mock/mock_kernel_circuit.hpp delete mode 100644 circuits/cpp/src/aztec3/circuits/recursion/CMakeLists.txt delete mode 100644 circuits/cpp/src/aztec3/circuits/recursion/aggregator.hpp delete mode 100644 circuits/cpp/src/aztec3/circuits/recursion/init.hpp delete mode 100644 circuits/cpp/src/aztec3/circuits/rollup/CMakeLists.txt delete mode 100644 circuits/cpp/src/aztec3/circuits/rollup/base/.test.cpp delete mode 100644 circuits/cpp/src/aztec3/circuits/rollup/base/CMakeLists.txt delete mode 100644 circuits/cpp/src/aztec3/circuits/rollup/base/c_bind.cpp delete mode 100644 circuits/cpp/src/aztec3/circuits/rollup/base/c_bind.h delete mode 100644 circuits/cpp/src/aztec3/circuits/rollup/base/index.hpp delete mode 100644 circuits/cpp/src/aztec3/circuits/rollup/base/init.hpp delete mode 100644 circuits/cpp/src/aztec3/circuits/rollup/base/native_base_rollup_circuit.cpp delete mode 100644 circuits/cpp/src/aztec3/circuits/rollup/base/native_base_rollup_circuit.hpp delete mode 100644 circuits/cpp/src/aztec3/circuits/rollup/components/components.cpp delete mode 100644 circuits/cpp/src/aztec3/circuits/rollup/components/components.hpp delete mode 100644 circuits/cpp/src/aztec3/circuits/rollup/components/init.hpp delete mode 100644 circuits/cpp/src/aztec3/circuits/rollup/merge/.test.cpp delete mode 100644 circuits/cpp/src/aztec3/circuits/rollup/merge/CMakeLists.txt delete mode 100644 circuits/cpp/src/aztec3/circuits/rollup/merge/c_bind.cpp delete mode 100644 circuits/cpp/src/aztec3/circuits/rollup/merge/c_bind.h delete mode 100644 circuits/cpp/src/aztec3/circuits/rollup/merge/index.hpp delete mode 100644 circuits/cpp/src/aztec3/circuits/rollup/merge/init.hpp delete mode 100644 circuits/cpp/src/aztec3/circuits/rollup/merge/native_merge_rollup_circuit.cpp delete mode 100644 circuits/cpp/src/aztec3/circuits/rollup/merge/native_merge_rollup_circuit.hpp delete mode 100644 circuits/cpp/src/aztec3/circuits/rollup/root/.test.cpp delete mode 100644 circuits/cpp/src/aztec3/circuits/rollup/root/CMakeLists.txt delete mode 100644 circuits/cpp/src/aztec3/circuits/rollup/root/c_bind.cpp delete mode 100644 circuits/cpp/src/aztec3/circuits/rollup/root/c_bind.h delete mode 100644 circuits/cpp/src/aztec3/circuits/rollup/root/index.hpp delete mode 100644 circuits/cpp/src/aztec3/circuits/rollup/root/init.hpp delete mode 100644 circuits/cpp/src/aztec3/circuits/rollup/root/native_root_rollup_circuit.cpp delete mode 100644 circuits/cpp/src/aztec3/circuits/rollup/root/native_root_rollup_circuit.hpp delete mode 100644 circuits/cpp/src/aztec3/circuits/rollup/test_utils/init.hpp delete mode 100644 circuits/cpp/src/aztec3/circuits/rollup/test_utils/nullifier_tree_testing_harness.cpp delete mode 100644 circuits/cpp/src/aztec3/circuits/rollup/test_utils/nullifier_tree_testing_harness.hpp delete mode 100644 circuits/cpp/src/aztec3/circuits/rollup/test_utils/utils.cpp delete mode 100644 circuits/cpp/src/aztec3/circuits/rollup/test_utils/utils.hpp delete mode 100644 circuits/cpp/src/aztec3/constants.hpp delete mode 100644 circuits/cpp/src/aztec3/dbs/CMakeLists.txt delete mode 100644 circuits/cpp/src/aztec3/dbs/private_state_db.hpp delete mode 100644 circuits/cpp/src/aztec3/oracle/CMakeLists.txt delete mode 100644 circuits/cpp/src/aztec3/oracle/README.md delete mode 100644 circuits/cpp/src/aztec3/oracle/fake_db.hpp delete mode 100644 circuits/cpp/src/aztec3/oracle/oracle.hpp delete mode 100644 circuits/cpp/src/aztec3/utils/CMakeLists.txt delete mode 100644 circuits/cpp/src/aztec3/utils/array.hpp delete mode 100644 circuits/cpp/src/aztec3/utils/array.test.cpp delete mode 100644 circuits/cpp/src/aztec3/utils/circuit_errors.hpp delete mode 100644 circuits/cpp/src/aztec3/utils/dummy_circuit_builder.hpp delete mode 100644 circuits/cpp/src/aztec3/utils/msgpack_derived_equals.hpp delete mode 100644 circuits/cpp/src/aztec3/utils/msgpack_derived_output.hpp delete mode 100644 circuits/cpp/src/aztec3/utils/types/CMakeLists.txt delete mode 100644 circuits/cpp/src/aztec3/utils/types/circuit_types.hpp delete mode 100644 circuits/cpp/src/aztec3/utils/types/convert.hpp delete mode 100644 circuits/cpp/src/aztec3/utils/types/native_types.hpp delete mode 120000 circuits/cpp/srs_db delete mode 100644 docs/docs/about_aztec/history/differences_to_aztec_connect.md delete mode 100644 docs/docs/concepts/advanced/circuits/kernels/main.md delete mode 100644 docs/docs/concepts/advanced/data_structures/main.md delete mode 100644 docs/docs/concepts/advanced/main.md delete mode 100644 docs/docs/dev_docs/contracts/syntax/functions.md delete mode 100644 docs/docs/dev_docs/contracts/syntax/storage/main.md delete mode 100644 docs/docs/dev_docs/getting_started/core-concepts.md rename docs/docs/{dev_docs => developers}/aztecjs/main.md (100%) rename docs/docs/{dev_docs => developers}/cli/blank_box.md (100%) rename docs/docs/{dev_docs => developers}/cli/cli-commands.md (97%) rename docs/docs/{dev_docs => developers}/cli/main.md (100%) create mode 100644 docs/docs/developers/cli/run_more_than_one_pxe_sandbox.md rename docs/docs/{dev_docs => developers}/cli/sandbox-reference.md (93%) rename docs/docs/{dev_docs => developers}/contracts/abi.md (100%) rename docs/docs/{dev_docs => developers}/contracts/artifacts.md (100%) rename docs/docs/{dev_docs => developers}/contracts/compiling.md (94%) rename docs/docs/{dev_docs => developers}/contracts/deploying.md (98%) rename docs/docs/{dev_docs => developers}/contracts/example-contract.md (100%) rename docs/docs/{dev_docs => developers}/contracts/layout.md (71%) rename docs/docs/{dev_docs => developers}/contracts/main.md (100%) rename docs/docs/{dev_docs => developers}/contracts/portals/data_structures.md (100%) rename docs/docs/{dev_docs => developers}/contracts/portals/inbox.md (100%) rename docs/docs/{dev_docs => developers}/contracts/portals/main.md (97%) rename docs/docs/{dev_docs => developers}/contracts/portals/outbox.md (100%) rename docs/docs/{dev_docs => developers}/contracts/portals/registry.md (100%) rename docs/docs/{dev_docs => developers}/contracts/resources/common_patterns/authwit.md (96%) rename docs/docs/{dev_docs => developers}/contracts/resources/common_patterns/main.md (96%) rename docs/docs/{dev_docs => developers}/contracts/resources/dependencies.md (100%) rename docs/docs/{dev_docs => developers}/contracts/resources/main.md (100%) rename docs/docs/{dev_docs => developers}/contracts/resources/style_guide.md (100%) rename docs/docs/{dev_docs => developers}/contracts/security/breaking_changes/main.md (100%) rename docs/docs/{dev_docs => developers}/contracts/security/breaking_changes/v0.md (100%) rename docs/docs/{dev_docs => developers}/contracts/security/main.md (100%) rename docs/docs/{dev_docs => developers}/contracts/setup.md (100%) rename docs/docs/{dev_docs => developers}/contracts/syntax/constrain.md (100%) rename docs/docs/{dev_docs => developers}/contracts/syntax/context.mdx (87%) rename docs/docs/{dev_docs => developers}/contracts/syntax/control_structure.md (100%) rename docs/docs/{dev_docs => developers}/contracts/syntax/events.md (98%) create mode 100644 docs/docs/developers/contracts/syntax/functions/calling_functions.md create mode 100644 docs/docs/developers/contracts/syntax/functions/constructor.md create mode 100644 docs/docs/developers/contracts/syntax/functions/inner_workings.md create mode 100644 docs/docs/developers/contracts/syntax/functions/main.md create mode 100644 docs/docs/developers/contracts/syntax/functions/oracles.md create mode 100644 docs/docs/developers/contracts/syntax/functions/public_private_unconstrained.md create mode 100644 docs/docs/developers/contracts/syntax/functions/visibility.md rename docs/docs/{dev_docs => developers}/contracts/syntax/globals.md (100%) create mode 100644 docs/docs/developers/contracts/syntax/historical_access/history_lib_reference.md create mode 100644 docs/docs/developers/contracts/syntax/historical_access/how_to_prove_history.md rename docs/docs/{dev_docs => developers}/contracts/syntax/main.md (94%) create mode 100644 docs/docs/developers/contracts/syntax/oracles.md rename docs/docs/{dev_docs => developers}/contracts/syntax/slow_updates_tree.md (98%) create mode 100644 docs/docs/developers/contracts/syntax/storage/main.md create mode 100644 docs/docs/developers/contracts/syntax/storage/private_state.md create mode 100644 docs/docs/developers/contracts/syntax/storage/public_state.md rename docs/docs/{dev_docs => developers}/contracts/syntax/storage/storage_slots.md (87%) rename docs/docs/{dev_docs => developers}/contracts/workflow.md (100%) rename docs/docs/{dev_docs => developers}/debugging/aztecnr-errors.md (98%) rename docs/docs/{dev_docs => developers}/debugging/main.md (100%) rename docs/docs/{dev_docs => developers}/debugging/sandbox-errors.md (89%) rename docs/docs/{dev_docs => developers}/getting_started/aztecjs-getting-started.md (97%) rename docs/docs/{dev_docs => developers}/getting_started/aztecnr-getting-started.md (96%) rename docs/docs/{dev_docs => developers}/getting_started/main.md (83%) rename docs/docs/{dev_docs => developers}/getting_started/quickstart.md (90%) rename docs/docs/{dev_docs => developers}/limitations/main.md (92%) rename docs/docs/{dev_docs => developers}/privacy/main.md (100%) rename docs/docs/{dev_docs => developers}/testing/cheat_codes.md (97%) rename docs/docs/{dev_docs => developers}/testing/main.md (100%) rename docs/docs/{dev_docs => developers}/tutorials/main.md (100%) rename docs/docs/{dev_docs => developers}/tutorials/testing.md (95%) rename docs/docs/{dev_docs => developers}/tutorials/token_portal/cancelling_deposits.md (100%) rename docs/docs/{dev_docs => developers}/tutorials/token_portal/depositing_to_aztec.md (91%) rename docs/docs/{dev_docs => developers}/tutorials/token_portal/main.md (97%) rename docs/docs/{dev_docs => developers}/tutorials/token_portal/minting_on_aztec.md (100%) rename docs/docs/{dev_docs => developers}/tutorials/token_portal/setup.md (87%) rename docs/docs/{dev_docs => developers}/tutorials/token_portal/typescript_glue_code.md (90%) rename docs/docs/{dev_docs => developers}/tutorials/token_portal/withdrawing_to_l1.md (90%) rename docs/docs/{dev_docs => developers}/tutorials/uniswap/execute_private_swap_on_l1.md (100%) rename docs/docs/{dev_docs => developers}/tutorials/uniswap/execute_public_swap_on_l1.md (100%) rename docs/docs/{dev_docs => developers}/tutorials/uniswap/l1_portal.md (100%) rename docs/docs/{dev_docs => developers}/tutorials/uniswap/l2_contract_setup.md (100%) rename docs/docs/{dev_docs => developers}/tutorials/uniswap/main.md (100%) rename docs/docs/{dev_docs => developers}/tutorials/uniswap/redeeming_swapped_assets_on_l2.md (100%) rename docs/docs/{dev_docs => developers}/tutorials/uniswap/setup.md (100%) rename docs/docs/{dev_docs => developers}/tutorials/uniswap/swap_privately.md (100%) rename docs/docs/{dev_docs => developers}/tutorials/uniswap/swap_publicly.md (100%) rename docs/docs/{dev_docs => developers}/tutorials/uniswap/typescript_glue_code.md (100%) rename docs/docs/{dev_docs => developers}/tutorials/writing_dapp/contract_deployment.md (94%) rename docs/docs/{dev_docs => developers}/tutorials/writing_dapp/contract_interaction.md (93%) rename docs/docs/{dev_docs => developers}/tutorials/writing_dapp/main.md (100%) rename docs/docs/{dev_docs => developers}/tutorials/writing_dapp/project_setup.md (100%) rename docs/docs/{dev_docs => developers}/tutorials/writing_dapp/pxe_service.md (100%) rename docs/docs/{dev_docs => developers}/tutorials/writing_dapp/testing.md (98%) rename docs/docs/{dev_docs => developers}/tutorials/writing_private_voting_contract.md (93%) rename docs/docs/{dev_docs => developers}/tutorials/writing_token_contract.md (96%) rename docs/docs/{dev_docs => developers}/updating.md (96%) rename docs/docs/{dev_docs => developers}/wallets/architecture.md (80%) rename docs/docs/{dev_docs => developers}/wallets/creating_schnorr_accounts.md (96%) rename docs/docs/{dev_docs => developers}/wallets/main.md (75%) rename docs/docs/{dev_docs => developers}/wallets/writing_an_account_contract.md (88%) rename docs/docs/{concepts/foundation/main.md => learn/about_aztec/technical_overview.md} (74%) rename docs/docs/{ => learn}/about_aztec/vision.md (96%) rename docs/docs/{about_aztec/overview.mdx => learn/about_aztec/what_is_aztec.mdx} (95%) rename docs/docs/{concepts/foundation => learn/concepts}/accounts/authwit.md (95%) rename docs/docs/{concepts/foundation => learn/concepts}/accounts/keys.md (98%) rename docs/docs/{concepts/foundation => learn/concepts}/accounts/main.md (99%) rename docs/docs/{concepts/foundation => learn/concepts}/block_production.md (100%) rename docs/docs/{concepts/foundation => learn/concepts}/blocks.md (100%) rename docs/docs/{concepts/advanced => learn/concepts}/circuits/kernels/private_kernel.md (93%) rename docs/docs/{concepts/advanced => learn/concepts}/circuits/kernels/public_kernel.md (82%) rename docs/docs/{concepts/advanced => learn/concepts}/circuits/main.md (98%) rename docs/docs/{concepts/advanced => learn/concepts}/circuits/rollup_circuits/main.md (96%) rename docs/docs/{concepts/foundation => learn/concepts}/communication/cross_chain_calls.md (100%) rename docs/docs/{concepts/foundation => learn/concepts}/communication/main.md (100%) rename docs/docs/{concepts/foundation => learn/concepts}/communication/public_private_calls/main.md (99%) rename docs/docs/{concepts/foundation => learn/concepts}/communication/public_private_calls/slow_updates_tree.md (97%) rename docs/docs/{concepts/foundation => learn/concepts}/globals.md (100%) rename docs/docs/{concepts/foundation/state_model => learn/concepts/hybrid_state}/main.md (74%) rename docs/docs/{concepts/advanced => learn/concepts/hybrid_state}/public_vm.md (100%) create mode 100644 docs/docs/learn/concepts/main.md rename docs/docs/{concepts/foundation => learn/concepts}/nodes_clients/execution_client.md (100%) rename docs/docs/{concepts/foundation => learn/concepts}/nodes_clients/prover_client.md (100%) rename docs/docs/{concepts/foundation/nodes_clients/sequencer.md => learn/concepts/nodes_clients/sequencer/main.md} (97%) rename docs/docs/{concepts/advanced => learn/concepts/nodes_clients/sequencer}/sequencer_selection.md (100%) rename docs/docs/{concepts/advanced => learn/concepts/pxe}/acir_simulator.md (88%) create mode 100644 docs/docs/learn/concepts/pxe/main.md rename docs/docs/{concepts/advanced => learn/concepts/smart_contracts}/contract_creation.md (99%) rename docs/docs/{concepts/foundation/contracts.md => learn/concepts/smart_contracts/main.md} (95%) rename docs/docs/{concepts/foundation/state_model => learn/concepts/storage}/storage_slots.md (77%) rename docs/docs/{concepts/advanced/data_structures => learn/concepts/storage/trees}/indexed_merkle_tree.md (100%) rename docs/docs/{concepts/advanced/data_structures/trees.md => learn/concepts/storage/trees/main.md} (81%) rename docs/docs/{concepts/foundation => learn/concepts}/transactions.md (93%) rename docs/docs/{concepts/foundation => learn/concepts}/upgrade_mechanism.md (100%) rename docs/docs/{about_aztec => misc}/how_to_contribute.md (100%) rename docs/docs/{about_aztec => misc}/roadmap/cryptography_roadmap.md (100%) rename docs/docs/{about_aztec => misc}/roadmap/engineering_roadmap.md (98%) rename docs/docs/{about_aztec => misc}/roadmap/features_initial_ldt.md (100%) rename docs/docs/{about_aztec => misc}/roadmap/main.md (100%) rename docs/docs/{intro.md => welcome.md} (62%) create mode 100644 l1-contracts/slither.config.json create mode 100755 l1-contracts/slither_has_diff.sh create mode 100644 l1-contracts/slither_output.md rename l1-contracts/test/decoders/helpers/{HeaderDecoderHelper.sol => HeaderLibHelper.sol} (92%) delete mode 100644 noir/.github/ISSUE_TEMPLATE/config.yml rename noir/.github/ISSUE_TEMPLATE/{idea_action_plan.yml => feature_request.yml} (90%) rename noir/acvm-repo/acir/{acir_docs.md => README.md} (100%) delete mode 100644 noir/acvm-repo/brillig_vm/src/registers.rs create mode 100644 noir/compiler/noirc_driver/tests/contracts.rs create mode 100644 noir/compiler/noirc_evaluator/src/ssa/acir_gen/acir_ir/big_int.rs create mode 100644 noir/compiler/noirc_evaluator/src/ssa/ir/instruction/binary.rs create mode 100644 noir/compiler/noirc_evaluator/src/ssa/ir/instruction/cast.rs create mode 100644 noir/compiler/noirc_evaluator/src/ssa/ir/instruction/constrain.rs create mode 100644 noir/docs/docs/getting_started/hello_noir/_category_.json rename noir/docs/docs/getting_started/{create_a_project.md => hello_noir/index.md} (92%) rename noir/docs/docs/getting_started/{ => hello_noir}/project_breakdown.md (86%) create mode 100644 noir/docs/docs/how_to/how-to-solidity-verifier.md delete mode 100644 noir/docs/docs/how_to/solidity_verifier.md delete mode 100644 noir/docs/docs/index.md create mode 100644 noir/docs/docs/index.mdx create mode 100644 noir/docs/processed-docs-cache/explainers/explainer-oracle.md create mode 100644 noir/docs/processed-docs-cache/explainers/explainer-recursion.md create mode 100644 noir/docs/processed-docs-cache/getting_started/_category_.json create mode 100644 noir/docs/processed-docs-cache/getting_started/hello_noir/_category_.json create mode 100644 noir/docs/processed-docs-cache/getting_started/hello_noir/index.md create mode 100644 noir/docs/processed-docs-cache/getting_started/hello_noir/project_breakdown.md create mode 100644 noir/docs/processed-docs-cache/getting_started/installation/_category_.json create mode 100644 noir/docs/processed-docs-cache/getting_started/installation/index.md create mode 100644 noir/docs/processed-docs-cache/getting_started/installation/other_install_methods.md create mode 100644 noir/docs/processed-docs-cache/getting_started/tooling/_category_.json create mode 100644 noir/docs/processed-docs-cache/getting_started/tooling/index.mdx create mode 100644 noir/docs/processed-docs-cache/getting_started/tooling/language_server.md create mode 100644 noir/docs/processed-docs-cache/getting_started/tooling/testing.md create mode 100644 noir/docs/processed-docs-cache/how_to/_category_.json create mode 100644 noir/docs/processed-docs-cache/how_to/how-to-oracles.md create mode 100644 noir/docs/processed-docs-cache/how_to/how-to-recursion.md create mode 100644 noir/docs/processed-docs-cache/how_to/how-to-solidity-verifier.md create mode 100644 noir/docs/processed-docs-cache/how_to/merkle-proof.mdx create mode 100644 noir/docs/processed-docs-cache/how_to/using-devcontainers.mdx create mode 100644 noir/docs/processed-docs-cache/index.mdx create mode 100644 noir/docs/processed-docs-cache/migration_notes.md create mode 100644 noir/docs/processed-docs-cache/noir/concepts/_category_.json create mode 100644 noir/docs/processed-docs-cache/noir/concepts/assert.md create mode 100644 noir/docs/processed-docs-cache/noir/concepts/comments.md create mode 100644 noir/docs/processed-docs-cache/noir/concepts/control_flow.md create mode 100644 noir/docs/processed-docs-cache/noir/concepts/data_bus.md create mode 100644 noir/docs/processed-docs-cache/noir/concepts/data_types/_category_.json create mode 100644 noir/docs/processed-docs-cache/noir/concepts/data_types/arrays.md create mode 100644 noir/docs/processed-docs-cache/noir/concepts/data_types/booleans.md create mode 100644 noir/docs/processed-docs-cache/noir/concepts/data_types/fields.md create mode 100644 noir/docs/processed-docs-cache/noir/concepts/data_types/function_types.md create mode 100644 noir/docs/processed-docs-cache/noir/concepts/data_types/index.md create mode 100644 noir/docs/processed-docs-cache/noir/concepts/data_types/integers.md create mode 100644 noir/docs/processed-docs-cache/noir/concepts/data_types/references.md create mode 100644 noir/docs/processed-docs-cache/noir/concepts/data_types/slices.mdx create mode 100644 noir/docs/processed-docs-cache/noir/concepts/data_types/strings.md create mode 100644 noir/docs/processed-docs-cache/noir/concepts/data_types/structs.md create mode 100644 noir/docs/processed-docs-cache/noir/concepts/data_types/tuples.md create mode 100644 noir/docs/processed-docs-cache/noir/concepts/data_types/vectors.mdx create mode 100644 noir/docs/processed-docs-cache/noir/concepts/distinct.md create mode 100644 noir/docs/processed-docs-cache/noir/concepts/functions.md create mode 100644 noir/docs/processed-docs-cache/noir/concepts/generics.md create mode 100644 noir/docs/processed-docs-cache/noir/concepts/lambdas.md create mode 100644 noir/docs/processed-docs-cache/noir/concepts/mutability.md create mode 100644 noir/docs/processed-docs-cache/noir/concepts/ops.md create mode 100644 noir/docs/processed-docs-cache/noir/concepts/oracles.md create mode 100644 noir/docs/processed-docs-cache/noir/concepts/shadowing.md create mode 100644 noir/docs/processed-docs-cache/noir/concepts/traits.md create mode 100644 noir/docs/processed-docs-cache/noir/concepts/unconstrained.md create mode 100644 noir/docs/processed-docs-cache/noir/modules_packages_crates/_category_.json create mode 100644 noir/docs/processed-docs-cache/noir/modules_packages_crates/crates_and_packages.md create mode 100644 noir/docs/processed-docs-cache/noir/modules_packages_crates/dependencies.md create mode 100644 noir/docs/processed-docs-cache/noir/modules_packages_crates/modules.md create mode 100644 noir/docs/processed-docs-cache/noir/modules_packages_crates/workspaces.md create mode 100644 noir/docs/processed-docs-cache/noir/standard_library/_category_.json create mode 100644 noir/docs/processed-docs-cache/noir/standard_library/black_box_fns.md create mode 100644 noir/docs/processed-docs-cache/noir/standard_library/cryptographic_primitives/_category_.json create mode 100644 noir/docs/processed-docs-cache/noir/standard_library/cryptographic_primitives/ec_primitives.md create mode 100644 noir/docs/processed-docs-cache/noir/standard_library/cryptographic_primitives/ecdsa_sig_verification.mdx create mode 100644 noir/docs/processed-docs-cache/noir/standard_library/cryptographic_primitives/eddsa.mdx create mode 100644 noir/docs/processed-docs-cache/noir/standard_library/cryptographic_primitives/hashes.mdx create mode 100644 noir/docs/processed-docs-cache/noir/standard_library/cryptographic_primitives/index.md create mode 100644 noir/docs/processed-docs-cache/noir/standard_library/cryptographic_primitives/scalar.mdx create mode 100644 noir/docs/processed-docs-cache/noir/standard_library/cryptographic_primitives/schnorr.mdx create mode 100644 noir/docs/processed-docs-cache/noir/standard_library/logging.md create mode 100644 noir/docs/processed-docs-cache/noir/standard_library/merkle_trees.md create mode 100644 noir/docs/processed-docs-cache/noir/standard_library/options.md create mode 100644 noir/docs/processed-docs-cache/noir/standard_library/recursion.md create mode 100644 noir/docs/processed-docs-cache/noir/standard_library/traits.md create mode 100644 noir/docs/processed-docs-cache/noir/standard_library/zeroed.md create mode 100644 noir/docs/processed-docs-cache/reference/_category_.json create mode 100644 noir/docs/processed-docs-cache/reference/nargo_commands.md create mode 100644 noir/docs/processed-docs-cache/tutorials/noirjs_app.md create mode 100644 noir/docs/processed-docs/explainers/explainer-oracle.md create mode 100644 noir/docs/processed-docs/explainers/explainer-recursion.md create mode 100644 noir/docs/processed-docs/getting_started/_category_.json create mode 100644 noir/docs/processed-docs/getting_started/hello_noir/_category_.json create mode 100644 noir/docs/processed-docs/getting_started/hello_noir/index.md create mode 100644 noir/docs/processed-docs/getting_started/hello_noir/project_breakdown.md create mode 100644 noir/docs/processed-docs/getting_started/installation/_category_.json create mode 100644 noir/docs/processed-docs/getting_started/installation/index.md create mode 100644 noir/docs/processed-docs/getting_started/installation/other_install_methods.md create mode 100644 noir/docs/processed-docs/getting_started/tooling/_category_.json create mode 100644 noir/docs/processed-docs/getting_started/tooling/index.mdx create mode 100644 noir/docs/processed-docs/getting_started/tooling/language_server.md create mode 100644 noir/docs/processed-docs/getting_started/tooling/testing.md create mode 100644 noir/docs/processed-docs/how_to/_category_.json create mode 100644 noir/docs/processed-docs/how_to/how-to-oracles.md create mode 100644 noir/docs/processed-docs/how_to/how-to-recursion.md create mode 100644 noir/docs/processed-docs/how_to/how-to-solidity-verifier.md create mode 100644 noir/docs/processed-docs/how_to/merkle-proof.mdx create mode 100644 noir/docs/processed-docs/how_to/using-devcontainers.mdx create mode 100644 noir/docs/processed-docs/index.mdx create mode 100644 noir/docs/processed-docs/migration_notes.md create mode 100644 noir/docs/processed-docs/noir/concepts/_category_.json create mode 100644 noir/docs/processed-docs/noir/concepts/assert.md create mode 100644 noir/docs/processed-docs/noir/concepts/comments.md create mode 100644 noir/docs/processed-docs/noir/concepts/control_flow.md create mode 100644 noir/docs/processed-docs/noir/concepts/data_bus.md create mode 100644 noir/docs/processed-docs/noir/concepts/data_types/_category_.json create mode 100644 noir/docs/processed-docs/noir/concepts/data_types/arrays.md create mode 100644 noir/docs/processed-docs/noir/concepts/data_types/booleans.md create mode 100644 noir/docs/processed-docs/noir/concepts/data_types/fields.md create mode 100644 noir/docs/processed-docs/noir/concepts/data_types/function_types.md create mode 100644 noir/docs/processed-docs/noir/concepts/data_types/index.md create mode 100644 noir/docs/processed-docs/noir/concepts/data_types/integers.md create mode 100644 noir/docs/processed-docs/noir/concepts/data_types/references.md create mode 100644 noir/docs/processed-docs/noir/concepts/data_types/slices.mdx create mode 100644 noir/docs/processed-docs/noir/concepts/data_types/strings.md create mode 100644 noir/docs/processed-docs/noir/concepts/data_types/structs.md create mode 100644 noir/docs/processed-docs/noir/concepts/data_types/tuples.md create mode 100644 noir/docs/processed-docs/noir/concepts/data_types/vectors.mdx create mode 100644 noir/docs/processed-docs/noir/concepts/distinct.md create mode 100644 noir/docs/processed-docs/noir/concepts/functions.md create mode 100644 noir/docs/processed-docs/noir/concepts/generics.md create mode 100644 noir/docs/processed-docs/noir/concepts/lambdas.md create mode 100644 noir/docs/processed-docs/noir/concepts/mutability.md create mode 100644 noir/docs/processed-docs/noir/concepts/ops.md create mode 100644 noir/docs/processed-docs/noir/concepts/oracles.md create mode 100644 noir/docs/processed-docs/noir/concepts/shadowing.md create mode 100644 noir/docs/processed-docs/noir/concepts/traits.md create mode 100644 noir/docs/processed-docs/noir/concepts/unconstrained.md create mode 100644 noir/docs/processed-docs/noir/modules_packages_crates/_category_.json create mode 100644 noir/docs/processed-docs/noir/modules_packages_crates/crates_and_packages.md create mode 100644 noir/docs/processed-docs/noir/modules_packages_crates/dependencies.md create mode 100644 noir/docs/processed-docs/noir/modules_packages_crates/modules.md create mode 100644 noir/docs/processed-docs/noir/modules_packages_crates/workspaces.md create mode 100644 noir/docs/processed-docs/noir/standard_library/_category_.json create mode 100644 noir/docs/processed-docs/noir/standard_library/black_box_fns.md create mode 100644 noir/docs/processed-docs/noir/standard_library/cryptographic_primitives/_category_.json create mode 100644 noir/docs/processed-docs/noir/standard_library/cryptographic_primitives/ec_primitives.md create mode 100644 noir/docs/processed-docs/noir/standard_library/cryptographic_primitives/ecdsa_sig_verification.mdx create mode 100644 noir/docs/processed-docs/noir/standard_library/cryptographic_primitives/eddsa.mdx create mode 100644 noir/docs/processed-docs/noir/standard_library/cryptographic_primitives/hashes.mdx create mode 100644 noir/docs/processed-docs/noir/standard_library/cryptographic_primitives/index.md create mode 100644 noir/docs/processed-docs/noir/standard_library/cryptographic_primitives/scalar.mdx create mode 100644 noir/docs/processed-docs/noir/standard_library/cryptographic_primitives/schnorr.mdx create mode 100644 noir/docs/processed-docs/noir/standard_library/logging.md create mode 100644 noir/docs/processed-docs/noir/standard_library/merkle_trees.md create mode 100644 noir/docs/processed-docs/noir/standard_library/options.md create mode 100644 noir/docs/processed-docs/noir/standard_library/recursion.md create mode 100644 noir/docs/processed-docs/noir/standard_library/traits.md create mode 100644 noir/docs/processed-docs/noir/standard_library/zeroed.md rename noir/docs/{docs => processed-docs}/reference/NoirJS/backend_barretenberg/.nojekyll (100%) create mode 100644 noir/docs/processed-docs/reference/NoirJS/backend_barretenberg/classes/BarretenbergBackend.md rename noir/docs/{docs => processed-docs}/reference/NoirJS/backend_barretenberg/index.md (100%) rename noir/docs/{docs => processed-docs}/reference/NoirJS/backend_barretenberg/interfaces/Backend.md (100%) rename noir/docs/{docs => processed-docs}/reference/NoirJS/backend_barretenberg/type-aliases/BackendOptions.md (100%) rename noir/docs/{docs => processed-docs}/reference/NoirJS/backend_barretenberg/type-aliases/CompiledCircuit.md (100%) rename noir/docs/{docs => processed-docs}/reference/NoirJS/backend_barretenberg/type-aliases/ProofData.md (100%) rename noir/docs/{docs => processed-docs}/reference/NoirJS/backend_barretenberg/typedoc-sidebar.cjs (100%) rename noir/docs/{docs => processed-docs}/reference/NoirJS/noir_js/.nojekyll (100%) rename noir/docs/{docs => processed-docs}/reference/NoirJS/noir_js/classes/Noir.md (100%) rename noir/docs/{docs => processed-docs}/reference/NoirJS/noir_js/functions/and.md (100%) rename noir/docs/{docs => processed-docs}/reference/NoirJS/noir_js/functions/blake2s256.md (100%) rename noir/docs/{docs => processed-docs}/reference/NoirJS/noir_js/functions/ecdsa_secp256k1_verify.md (100%) rename noir/docs/{docs => processed-docs}/reference/NoirJS/noir_js/functions/ecdsa_secp256r1_verify.md (100%) rename noir/docs/{docs => processed-docs}/reference/NoirJS/noir_js/functions/keccak256.md (100%) rename noir/docs/{docs => processed-docs}/reference/NoirJS/noir_js/functions/sha256.md (100%) rename noir/docs/{docs => processed-docs}/reference/NoirJS/noir_js/functions/xor.md (100%) rename noir/docs/{docs => processed-docs}/reference/NoirJS/noir_js/index.md (100%) rename noir/docs/{docs => processed-docs}/reference/NoirJS/noir_js/type-aliases/CompiledCircuit.md (100%) rename noir/docs/{docs => processed-docs}/reference/NoirJS/noir_js/type-aliases/ForeignCallHandler.md (100%) rename noir/docs/{docs => processed-docs}/reference/NoirJS/noir_js/type-aliases/ForeignCallInput.md (100%) rename noir/docs/{docs => processed-docs}/reference/NoirJS/noir_js/type-aliases/ForeignCallOutput.md (100%) rename noir/docs/{docs => processed-docs}/reference/NoirJS/noir_js/type-aliases/InputMap.md (100%) rename noir/docs/{docs => processed-docs}/reference/NoirJS/noir_js/type-aliases/ProofData.md (100%) rename noir/docs/{docs => processed-docs}/reference/NoirJS/noir_js/type-aliases/WitnessMap.md (100%) rename noir/docs/{docs => processed-docs}/reference/NoirJS/noir_js/typedoc-sidebar.cjs (100%) create mode 100644 noir/docs/processed-docs/reference/_category_.json create mode 100644 noir/docs/processed-docs/reference/nargo_commands.md create mode 100644 noir/docs/processed-docs/tutorials/noirjs_app.md create mode 100644 noir/docs/scripts/preprocess/include_code.js create mode 100644 noir/docs/scripts/preprocess/index.js create mode 100644 noir/docs/static/img/aztec_logo.png create mode 100644 noir/docs/static/img/how-tos/solidity_verifier_1.png create mode 100644 noir/docs/static/img/how-tos/solidity_verifier_2.png create mode 100644 noir/docs/static/img/how-tos/solidity_verifier_3.png create mode 100644 noir/docs/static/img/how-tos/solidity_verifier_4.png create mode 100644 noir/docs/static/img/how-tos/solidity_verifier_5.png create mode 100644 noir/docs/static/img/logo.png create mode 100644 noir/docs/static/img/solidity_verifier_ex.png create mode 100644 noir/docs/versioned_docs/version-v0.23.0/explainers/explainer-oracle.md create mode 100644 noir/docs/versioned_docs/version-v0.23.0/explainers/explainer-recursion.md create mode 100644 noir/docs/versioned_docs/version-v0.23.0/getting_started/_category_.json create mode 100644 noir/docs/versioned_docs/version-v0.23.0/getting_started/hello_noir/_category_.json create mode 100644 noir/docs/versioned_docs/version-v0.23.0/getting_started/hello_noir/index.md create mode 100644 noir/docs/versioned_docs/version-v0.23.0/getting_started/hello_noir/project_breakdown.md create mode 100644 noir/docs/versioned_docs/version-v0.23.0/getting_started/installation/_category_.json create mode 100644 noir/docs/versioned_docs/version-v0.23.0/getting_started/installation/index.md create mode 100644 noir/docs/versioned_docs/version-v0.23.0/getting_started/installation/other_install_methods.md create mode 100644 noir/docs/versioned_docs/version-v0.23.0/getting_started/tooling/_category_.json create mode 100644 noir/docs/versioned_docs/version-v0.23.0/getting_started/tooling/index.mdx create mode 100644 noir/docs/versioned_docs/version-v0.23.0/getting_started/tooling/language_server.md create mode 100644 noir/docs/versioned_docs/version-v0.23.0/getting_started/tooling/testing.md create mode 100644 noir/docs/versioned_docs/version-v0.23.0/how_to/_category_.json create mode 100644 noir/docs/versioned_docs/version-v0.23.0/how_to/how-to-oracles.md create mode 100644 noir/docs/versioned_docs/version-v0.23.0/how_to/how-to-recursion.md create mode 100644 noir/docs/versioned_docs/version-v0.23.0/how_to/how-to-solidity-verifier.md create mode 100644 noir/docs/versioned_docs/version-v0.23.0/how_to/merkle-proof.mdx create mode 100644 noir/docs/versioned_docs/version-v0.23.0/how_to/using-devcontainers.mdx create mode 100644 noir/docs/versioned_docs/version-v0.23.0/index.mdx create mode 100644 noir/docs/versioned_docs/version-v0.23.0/migration_notes.md create mode 100644 noir/docs/versioned_docs/version-v0.23.0/noir/concepts/_category_.json create mode 100644 noir/docs/versioned_docs/version-v0.23.0/noir/concepts/assert.md create mode 100644 noir/docs/versioned_docs/version-v0.23.0/noir/concepts/comments.md create mode 100644 noir/docs/versioned_docs/version-v0.23.0/noir/concepts/control_flow.md create mode 100644 noir/docs/versioned_docs/version-v0.23.0/noir/concepts/data_bus.md create mode 100644 noir/docs/versioned_docs/version-v0.23.0/noir/concepts/data_types/_category_.json create mode 100644 noir/docs/versioned_docs/version-v0.23.0/noir/concepts/data_types/arrays.md create mode 100644 noir/docs/versioned_docs/version-v0.23.0/noir/concepts/data_types/booleans.md create mode 100644 noir/docs/versioned_docs/version-v0.23.0/noir/concepts/data_types/fields.md create mode 100644 noir/docs/versioned_docs/version-v0.23.0/noir/concepts/data_types/function_types.md create mode 100644 noir/docs/versioned_docs/version-v0.23.0/noir/concepts/data_types/index.md create mode 100644 noir/docs/versioned_docs/version-v0.23.0/noir/concepts/data_types/integers.md create mode 100644 noir/docs/versioned_docs/version-v0.23.0/noir/concepts/data_types/references.md create mode 100644 noir/docs/versioned_docs/version-v0.23.0/noir/concepts/data_types/slices.mdx create mode 100644 noir/docs/versioned_docs/version-v0.23.0/noir/concepts/data_types/strings.md create mode 100644 noir/docs/versioned_docs/version-v0.23.0/noir/concepts/data_types/structs.md create mode 100644 noir/docs/versioned_docs/version-v0.23.0/noir/concepts/data_types/tuples.md create mode 100644 noir/docs/versioned_docs/version-v0.23.0/noir/concepts/data_types/vectors.mdx create mode 100644 noir/docs/versioned_docs/version-v0.23.0/noir/concepts/distinct.md create mode 100644 noir/docs/versioned_docs/version-v0.23.0/noir/concepts/functions.md create mode 100644 noir/docs/versioned_docs/version-v0.23.0/noir/concepts/generics.md create mode 100644 noir/docs/versioned_docs/version-v0.23.0/noir/concepts/lambdas.md create mode 100644 noir/docs/versioned_docs/version-v0.23.0/noir/concepts/mutability.md create mode 100644 noir/docs/versioned_docs/version-v0.23.0/noir/concepts/ops.md create mode 100644 noir/docs/versioned_docs/version-v0.23.0/noir/concepts/oracles.md create mode 100644 noir/docs/versioned_docs/version-v0.23.0/noir/concepts/shadowing.md create mode 100644 noir/docs/versioned_docs/version-v0.23.0/noir/concepts/traits.md create mode 100644 noir/docs/versioned_docs/version-v0.23.0/noir/concepts/unconstrained.md create mode 100644 noir/docs/versioned_docs/version-v0.23.0/noir/modules_packages_crates/_category_.json create mode 100644 noir/docs/versioned_docs/version-v0.23.0/noir/modules_packages_crates/crates_and_packages.md create mode 100644 noir/docs/versioned_docs/version-v0.23.0/noir/modules_packages_crates/dependencies.md create mode 100644 noir/docs/versioned_docs/version-v0.23.0/noir/modules_packages_crates/modules.md create mode 100644 noir/docs/versioned_docs/version-v0.23.0/noir/modules_packages_crates/workspaces.md create mode 100644 noir/docs/versioned_docs/version-v0.23.0/noir/standard_library/_category_.json create mode 100644 noir/docs/versioned_docs/version-v0.23.0/noir/standard_library/black_box_fns.md create mode 100644 noir/docs/versioned_docs/version-v0.23.0/noir/standard_library/cryptographic_primitives/_category_.json create mode 100644 noir/docs/versioned_docs/version-v0.23.0/noir/standard_library/cryptographic_primitives/ec_primitives.md create mode 100644 noir/docs/versioned_docs/version-v0.23.0/noir/standard_library/cryptographic_primitives/ecdsa_sig_verification.mdx create mode 100644 noir/docs/versioned_docs/version-v0.23.0/noir/standard_library/cryptographic_primitives/eddsa.mdx create mode 100644 noir/docs/versioned_docs/version-v0.23.0/noir/standard_library/cryptographic_primitives/hashes.mdx create mode 100644 noir/docs/versioned_docs/version-v0.23.0/noir/standard_library/cryptographic_primitives/index.md create mode 100644 noir/docs/versioned_docs/version-v0.23.0/noir/standard_library/cryptographic_primitives/scalar.mdx create mode 100644 noir/docs/versioned_docs/version-v0.23.0/noir/standard_library/cryptographic_primitives/schnorr.mdx create mode 100644 noir/docs/versioned_docs/version-v0.23.0/noir/standard_library/logging.md create mode 100644 noir/docs/versioned_docs/version-v0.23.0/noir/standard_library/merkle_trees.md create mode 100644 noir/docs/versioned_docs/version-v0.23.0/noir/standard_library/options.md create mode 100644 noir/docs/versioned_docs/version-v0.23.0/noir/standard_library/recursion.md create mode 100644 noir/docs/versioned_docs/version-v0.23.0/noir/standard_library/traits.md create mode 100644 noir/docs/versioned_docs/version-v0.23.0/noir/standard_library/zeroed.md create mode 100644 noir/docs/versioned_docs/version-v0.23.0/noir_js/reference/backend_barretenberg/.nojekyll rename noir/docs/{docs/reference/NoirJS => versioned_docs/version-v0.23.0/noir_js/reference}/backend_barretenberg/classes/BarretenbergBackend.md (100%) create mode 100644 noir/docs/versioned_docs/version-v0.23.0/noir_js/reference/backend_barretenberg/index.md create mode 100644 noir/docs/versioned_docs/version-v0.23.0/noir_js/reference/backend_barretenberg/interfaces/Backend.md create mode 100644 noir/docs/versioned_docs/version-v0.23.0/noir_js/reference/backend_barretenberg/type-aliases/BackendOptions.md create mode 100644 noir/docs/versioned_docs/version-v0.23.0/noir_js/reference/backend_barretenberg/type-aliases/CompiledCircuit.md create mode 100644 noir/docs/versioned_docs/version-v0.23.0/noir_js/reference/backend_barretenberg/type-aliases/ProofData.md create mode 100644 noir/docs/versioned_docs/version-v0.23.0/noir_js/reference/backend_barretenberg/typedoc-sidebar.cjs create mode 100644 noir/docs/versioned_docs/version-v0.23.0/noir_js/reference/noir_js/.nojekyll create mode 100644 noir/docs/versioned_docs/version-v0.23.0/noir_js/reference/noir_js/classes/Noir.md create mode 100644 noir/docs/versioned_docs/version-v0.23.0/noir_js/reference/noir_js/functions/and.md create mode 100644 noir/docs/versioned_docs/version-v0.23.0/noir_js/reference/noir_js/functions/blake2s256.md create mode 100644 noir/docs/versioned_docs/version-v0.23.0/noir_js/reference/noir_js/functions/ecdsa_secp256k1_verify.md create mode 100644 noir/docs/versioned_docs/version-v0.23.0/noir_js/reference/noir_js/functions/ecdsa_secp256r1_verify.md create mode 100644 noir/docs/versioned_docs/version-v0.23.0/noir_js/reference/noir_js/functions/keccak256.md create mode 100644 noir/docs/versioned_docs/version-v0.23.0/noir_js/reference/noir_js/functions/sha256.md create mode 100644 noir/docs/versioned_docs/version-v0.23.0/noir_js/reference/noir_js/functions/xor.md create mode 100644 noir/docs/versioned_docs/version-v0.23.0/noir_js/reference/noir_js/index.md create mode 100644 noir/docs/versioned_docs/version-v0.23.0/noir_js/reference/noir_js/type-aliases/CompiledCircuit.md create mode 100644 noir/docs/versioned_docs/version-v0.23.0/noir_js/reference/noir_js/type-aliases/ForeignCallHandler.md create mode 100644 noir/docs/versioned_docs/version-v0.23.0/noir_js/reference/noir_js/type-aliases/ForeignCallInput.md create mode 100644 noir/docs/versioned_docs/version-v0.23.0/noir_js/reference/noir_js/type-aliases/ForeignCallOutput.md create mode 100644 noir/docs/versioned_docs/version-v0.23.0/noir_js/reference/noir_js/type-aliases/InputMap.md create mode 100644 noir/docs/versioned_docs/version-v0.23.0/noir_js/reference/noir_js/type-aliases/ProofData.md create mode 100644 noir/docs/versioned_docs/version-v0.23.0/noir_js/reference/noir_js/type-aliases/WitnessMap.md create mode 100644 noir/docs/versioned_docs/version-v0.23.0/noir_js/reference/noir_js/typedoc-sidebar.cjs create mode 100644 noir/docs/versioned_docs/version-v0.23.0/reference/NoirJS/backend_barretenberg/.nojekyll create mode 100644 noir/docs/versioned_docs/version-v0.23.0/reference/NoirJS/backend_barretenberg/classes/BarretenbergBackend.md create mode 100644 noir/docs/versioned_docs/version-v0.23.0/reference/NoirJS/backend_barretenberg/index.md create mode 100644 noir/docs/versioned_docs/version-v0.23.0/reference/NoirJS/backend_barretenberg/interfaces/Backend.md create mode 100644 noir/docs/versioned_docs/version-v0.23.0/reference/NoirJS/backend_barretenberg/type-aliases/BackendOptions.md create mode 100644 noir/docs/versioned_docs/version-v0.23.0/reference/NoirJS/backend_barretenberg/type-aliases/CompiledCircuit.md create mode 100644 noir/docs/versioned_docs/version-v0.23.0/reference/NoirJS/backend_barretenberg/type-aliases/ProofData.md create mode 100644 noir/docs/versioned_docs/version-v0.23.0/reference/NoirJS/backend_barretenberg/typedoc-sidebar.cjs create mode 100644 noir/docs/versioned_docs/version-v0.23.0/reference/NoirJS/noir_js/.nojekyll create mode 100644 noir/docs/versioned_docs/version-v0.23.0/reference/NoirJS/noir_js/classes/Noir.md create mode 100644 noir/docs/versioned_docs/version-v0.23.0/reference/NoirJS/noir_js/functions/and.md create mode 100644 noir/docs/versioned_docs/version-v0.23.0/reference/NoirJS/noir_js/functions/blake2s256.md create mode 100644 noir/docs/versioned_docs/version-v0.23.0/reference/NoirJS/noir_js/functions/ecdsa_secp256k1_verify.md create mode 100644 noir/docs/versioned_docs/version-v0.23.0/reference/NoirJS/noir_js/functions/ecdsa_secp256r1_verify.md create mode 100644 noir/docs/versioned_docs/version-v0.23.0/reference/NoirJS/noir_js/functions/keccak256.md create mode 100644 noir/docs/versioned_docs/version-v0.23.0/reference/NoirJS/noir_js/functions/sha256.md create mode 100644 noir/docs/versioned_docs/version-v0.23.0/reference/NoirJS/noir_js/functions/xor.md create mode 100644 noir/docs/versioned_docs/version-v0.23.0/reference/NoirJS/noir_js/index.md create mode 100644 noir/docs/versioned_docs/version-v0.23.0/reference/NoirJS/noir_js/type-aliases/CompiledCircuit.md create mode 100644 noir/docs/versioned_docs/version-v0.23.0/reference/NoirJS/noir_js/type-aliases/ForeignCallHandler.md create mode 100644 noir/docs/versioned_docs/version-v0.23.0/reference/NoirJS/noir_js/type-aliases/ForeignCallInput.md create mode 100644 noir/docs/versioned_docs/version-v0.23.0/reference/NoirJS/noir_js/type-aliases/ForeignCallOutput.md create mode 100644 noir/docs/versioned_docs/version-v0.23.0/reference/NoirJS/noir_js/type-aliases/InputMap.md create mode 100644 noir/docs/versioned_docs/version-v0.23.0/reference/NoirJS/noir_js/type-aliases/ProofData.md create mode 100644 noir/docs/versioned_docs/version-v0.23.0/reference/NoirJS/noir_js/type-aliases/WitnessMap.md create mode 100644 noir/docs/versioned_docs/version-v0.23.0/reference/NoirJS/noir_js/typedoc-sidebar.cjs create mode 100644 noir/docs/versioned_docs/version-v0.23.0/reference/_category_.json create mode 100644 noir/docs/versioned_docs/version-v0.23.0/reference/nargo_commands.md create mode 100644 noir/docs/versioned_docs/version-v0.23.0/tutorials/noirjs_app.md create mode 100644 noir/docs/versioned_sidebars/version-v0.23.0-sidebars.json create mode 100644 noir/noir_stdlib/src/bigint.nr create mode 100644 noir/noir_stdlib/src/collections/bounded_vec.nr create mode 100644 noir/noir_stdlib/src/convert.nr create mode 100644 noir/test_programs/compile_failure/builtin_function_declaration/Nargo.toml create mode 100644 noir/test_programs/compile_failure/builtin_function_declaration/src/main.nr create mode 100644 noir/test_programs/compile_failure/foreign_function_declaration/Nargo.toml create mode 100644 noir/test_programs/compile_failure/foreign_function_declaration/src/main.nr delete mode 100644 noir/test_programs/compile_failure/multiple_contracts/Nargo.toml delete mode 100644 noir/test_programs/compile_failure/multiple_contracts/src/main.nr create mode 100644 noir/test_programs/execution_success/pedersen_commitment/Nargo.toml create mode 100644 noir/test_programs/execution_success/pedersen_commitment/Prover.toml create mode 100644 noir/test_programs/execution_success/pedersen_commitment/src/main.nr create mode 100644 noir/test_programs/execution_success/pedersen_hash/Nargo.toml create mode 100644 noir/test_programs/execution_success/pedersen_hash/Prover.toml create mode 100644 noir/test_programs/execution_success/pedersen_hash/src/main.nr create mode 100644 noir/test_programs/execution_success/regression_4088/Nargo.toml create mode 100644 noir/test_programs/execution_success/regression_4088/Prover.toml create mode 100644 noir/test_programs/execution_success/regression_4088/src/main.nr create mode 100644 noir/test_programs/execution_success/regression_4124/Nargo.toml create mode 100644 noir/test_programs/execution_success/regression_4124/Prover.toml create mode 100644 noir/test_programs/execution_success/regression_4124/src/main.nr create mode 100644 noir/test_programs/noir_test_success/bounded_vec/Nargo.toml rename yarn-project/acir-simulator/src/avm/opcodes/call.ts => noir/test_programs/noir_test_success/bounded_vec/Prover.toml (100%) create mode 100644 noir/test_programs/noir_test_success/bounded_vec/src/main.nr create mode 100644 noir/test_programs/noir_test_success/out_of_bounds_alignment/Nargo.toml create mode 100644 noir/test_programs/noir_test_success/out_of_bounds_alignment/Prover.toml create mode 100644 noir/test_programs/noir_test_success/out_of_bounds_alignment/src/main.nr create mode 100644 noir/test_programs/noir_test_success/regression_4080/Nargo.toml create mode 100644 noir/test_programs/noir_test_success/regression_4080/Prover.toml create mode 100644 noir/test_programs/noir_test_success/regression_4080/src/main.nr create mode 100644 noir/tooling/nargo/src/ops/transform.rs create mode 100644 yarn-project/acir-simulator/src/avm/avm_execution_environment.test.ts create mode 100644 yarn-project/acir-simulator/src/avm/avm_execution_environment.ts create mode 100644 yarn-project/acir-simulator/src/avm/avm_memory_types.test.ts create mode 100644 yarn-project/acir-simulator/src/avm/avm_memory_types.ts delete mode 100644 yarn-project/acir-simulator/src/avm/avm_state_manager.ts delete mode 100644 yarn-project/acir-simulator/src/avm/index.ts create mode 100644 yarn-project/acir-simulator/src/avm/opcodes/.eslintrc.cjs create mode 100644 yarn-project/acir-simulator/src/avm/opcodes/accrued_substate.test.ts create mode 100644 yarn-project/acir-simulator/src/avm/opcodes/accrued_substate.ts create mode 100644 yarn-project/acir-simulator/src/avm/opcodes/comparators.test.ts create mode 100644 yarn-project/acir-simulator/src/avm/opcodes/environment_getters.test.ts create mode 100644 yarn-project/acir-simulator/src/avm/opcodes/environment_getters.ts create mode 100644 yarn-project/acir-simulator/src/avm/opcodes/external_calls.test.ts create mode 100644 yarn-project/acir-simulator/src/avm/opcodes/external_calls.ts create mode 100644 yarn-project/acir-simulator/src/avm/opcodes/storage.test.ts create mode 100644 yarn-project/acir-simulator/src/avm/opcodes/storage.ts create mode 100644 yarn-project/archiver/src/archiver/kv_archiver_store/contract_class_store.ts create mode 100644 yarn-project/archiver/src/archiver/kv_archiver_store/contract_instance_store.ts create mode 100644 yarn-project/archiver/src/rpc/archiver_client.ts create mode 100644 yarn-project/archiver/src/rpc/archiver_server.ts create mode 100644 yarn-project/archiver/src/rpc/index.ts delete mode 100644 yarn-project/aztec-node/src/aztec-node/db.ts delete mode 100644 yarn-project/aztec-node/src/declaration.d.ts delete mode 100644 yarn-project/aztec-nr/aztec/src/note/note_hash.nr delete mode 100644 yarn-project/aztec-nr/aztec/src/oracle/get_block_header.nr create mode 100644 yarn-project/aztec-nr/aztec/src/oracle/header.nr create mode 100644 yarn-project/aztec-nr/aztec/src/state_vars/stable_public_state.nr delete mode 100644 yarn-project/aztec-nr/aztec/src/types.nr delete mode 100644 yarn-project/aztec-nr/aztec/src/types/type_serialization.nr delete mode 100644 yarn-project/aztec-nr/aztec/src/types/type_serialization/address_serialization.nr delete mode 100644 yarn-project/aztec-nr/aztec/src/types/type_serialization/bool_serialization.nr delete mode 100644 yarn-project/aztec-nr/aztec/src/types/type_serialization/field_serialization.nr delete mode 100644 yarn-project/aztec-nr/aztec/src/types/type_serialization/u32_serialization.nr delete mode 100644 yarn-project/aztec-nr/aztec/src/types/type_serialization/u8_serialization.nr delete mode 100644 yarn-project/aztec-nr/aztec/src/types/vec.nr delete mode 100644 yarn-project/aztec-sandbox/src/bin/index.ts create mode 100644 yarn-project/aztec.js/src/rpc_clients/index.ts rename yarn-project/aztec.js/src/{ => rpc_clients}/pxe_client.ts (94%) rename yarn-project/{aztec-sandbox => aztec}/.eslintrc.cjs (100%) rename yarn-project/{aztec-sandbox => aztec}/.gitignore (100%) rename yarn-project/{aztec-sandbox => aztec}/Dockerfile (85%) rename yarn-project/{aztec-sandbox => aztec}/README.md (72%) rename yarn-project/{aztec-sandbox => aztec}/docker-compose.yml (85%) rename yarn-project/{aztec-sandbox => aztec}/package.json (92%) create mode 100644 yarn-project/aztec/src/aztec_client.ts create mode 100644 yarn-project/aztec/src/bin/index.ts create mode 100644 yarn-project/aztec/src/cli/cli.ts create mode 100644 yarn-project/aztec/src/cli/cmds/start_archiver.ts create mode 100644 yarn-project/aztec/src/cli/cmds/start_node.ts create mode 100644 yarn-project/aztec/src/cli/cmds/start_p2p_bootstrap.ts create mode 100644 yarn-project/aztec/src/cli/cmds/start_pxe.ts create mode 100644 yarn-project/aztec/src/cli/index.ts create mode 100644 yarn-project/aztec/src/cli/texts.ts create mode 100644 yarn-project/aztec/src/cli/util.ts rename yarn-project/{aztec-sandbox => aztec}/src/examples/token.ts (100%) rename yarn-project/{aztec-sandbox => aztec}/src/examples/util.ts (100%) rename yarn-project/{aztec-sandbox => aztec}/src/index.ts (100%) rename yarn-project/{aztec-sandbox => aztec}/src/logging.ts (92%) rename yarn-project/{aztec-sandbox => aztec}/src/sandbox.ts (94%) rename yarn-project/{aztec-sandbox => aztec}/src/splash.ts (100%) rename yarn-project/{aztec-sandbox => aztec}/tsconfig.json (89%) create mode 100644 yarn-project/circuits.js/fixtures/Benchmarking.test.json create mode 100644 yarn-project/circuits.js/src/contract/__snapshots__/contract_address.test.ts.snap create mode 100644 yarn-project/circuits.js/src/contract/__snapshots__/contract_class.test.ts.snap create mode 100644 yarn-project/circuits.js/src/contract/artifact_hash.test.ts create mode 100644 yarn-project/circuits.js/src/contract/artifact_hash.ts create mode 100644 yarn-project/circuits.js/src/contract/contract_address.test.ts create mode 100644 yarn-project/circuits.js/src/contract/contract_address.ts create mode 100644 yarn-project/circuits.js/src/contract/contract_class.test.ts create mode 100644 yarn-project/circuits.js/src/contract/contract_class.ts create mode 100644 yarn-project/circuits.js/src/contract/contract_class_id.test.ts create mode 100644 yarn-project/circuits.js/src/contract/contract_class_id.ts delete mode 100644 yarn-project/circuits.js/src/contract/contract_deployment_info.ts create mode 100644 yarn-project/circuits.js/src/contract/contract_instance.ts create mode 100644 yarn-project/circuits.js/src/structs/__snapshots__/header.test.ts.snap delete mode 100644 yarn-project/circuits.js/src/structs/kernel/block_header.test.ts delete mode 100644 yarn-project/circuits.js/src/structs/kernel/block_header.ts create mode 100644 yarn-project/circuits.js/src/structs/nullifier_key_validation_request.ts create mode 100644 yarn-project/circuits.js/src/tests/fixtures.ts create mode 100644 yarn-project/end-to-end/src/e2e_note_getter.test.ts delete mode 100644 yarn-project/end-to-end/src/e2e_singleton.test.ts create mode 100644 yarn-project/end-to-end/src/e2e_state_vars.test.ts create mode 100644 yarn-project/foundation/src/serialize/field_reader.test.ts create mode 100644 yarn-project/foundation/src/serialize/field_reader.ts delete mode 100644 yarn-project/merkle-tree/src/test/utils/create_mem_down.ts create mode 100644 yarn-project/noir-contracts/contracts/avm_test_contract/Nargo.toml create mode 100644 yarn-project/noir-contracts/contracts/avm_test_contract/src/main.nr delete mode 100644 yarn-project/noir-contracts/contracts/token_blacklist_contract/src/types/safe_u120_serialization.nr delete mode 100644 yarn-project/noir-contracts/contracts/token_contract/src/types/safe_u120_serialization.nr create mode 100755 yarn-project/noir-contracts/scripts/transpile.sh delete mode 100644 yarn-project/noir-protocol-circuits/src/crates/rollup-lib/src/hash.nr delete mode 100644 yarn-project/noir-protocol-circuits/src/crates/types/src/abis/block_header.nr delete mode 100644 yarn-project/noir-protocol-circuits/src/crates/types/src/abis/complete_address.nr create mode 100644 yarn-project/noir-protocol-circuits/src/crates/types/src/abis/nullifier_key_validation_request.nr create mode 100644 yarn-project/noir-protocol-circuits/src/crates/types/src/grumpkin_private_key.nr create mode 100644 yarn-project/noir-protocol-circuits/src/crates/types/src/type_serialization.nr delete mode 100644 yarn-project/noir-protocol-circuits/src/crates/types/src/utils/bounded_vec.nr create mode 100644 yarn-project/noir-protocol-circuits/src/fixtures/nested-call-private-kernel-init.hex create mode 100644 yarn-project/noir-protocol-circuits/src/fixtures/nested-call-private-kernel-ordering.hex create mode 100644 yarn-project/pxe/src/database/contracts/contract_artifact_db.ts create mode 100644 yarn-project/pxe/src/database/contracts/contract_instance_db.ts delete mode 100644 yarn-project/pxe/src/database/memory_db.test.ts delete mode 100644 yarn-project/pxe/src/database/memory_db.ts delete mode 100644 yarn-project/sequencer-client/src/sequencer/utils.ts create mode 100644 yarn-project/types/src/contracts/contract_class.test.ts create mode 100644 yarn-project/types/src/contracts/contract_class.ts create mode 100644 yarn-project/types/src/contracts/contract_instance.test.ts create mode 100644 yarn-project/types/src/contracts/contract_instance.ts create mode 100644 yarn-project/types/src/contracts/index.ts create mode 100644 yarn.lock diff --git a/.circleci/config.yml b/.circleci/config.yml index 07dd2a3ee5f..b036b70684a 100644 --- a/.circleci/config.yml +++ b/.circleci/config.yml @@ -125,6 +125,17 @@ jobs: name: "Build" command: cond_spot_run_build noir-compile-acir-tests 32 + avm-transpiler: + docker: + - image: aztecprotocol/alpine-build-image + resource_class: small + steps: + - *checkout + - *setup_env + - run: + name: "Build" + command: cond_spot_run_build avm-transpiler 32 + # Barretenberg barretenberg-wasm-linux-clang: docker: @@ -411,7 +422,7 @@ jobs: name: Test command: cond_spot_run_container yarn-project 64 test | add_timestamps - aztec-sandbox: + aztec-package: machine: image: ubuntu-2204:2023.07.2 resource_class: large @@ -420,7 +431,7 @@ jobs: - *setup_env - run: name: "Build and test" - command: build aztec-sandbox + command: build aztec cli: machine: @@ -521,6 +532,17 @@ jobs: name: "Test" command: cond_spot_run_compose end-to-end 4 ./scripts/docker-compose.yml TEST=e2e_2_pxes.test.ts + e2e-note-getter: + docker: + - image: aztecprotocol/alpine-build-image + resource_class: small + steps: + - *checkout + - *setup_env + - run: + name: "Test" + command: cond_spot_run_compose end-to-end 4 ./scripts/docker-compose.yml TEST=e2e_note_getter.test.ts + e2e-multiple-accounts-1-enc-key: docker: - image: aztecprotocol/alpine-build-image @@ -599,7 +621,7 @@ jobs: name: "Test" command: cond_spot_run_compose end-to-end 4 ./scripts/docker-compose.yml TEST=e2e_sandbox_example.test.ts - e2e-singleton: + e2e-state-vars: docker: - image: aztecprotocol/alpine-build-image resource_class: small @@ -608,7 +630,7 @@ jobs: - *setup_env - run: name: "Test" - command: cond_spot_run_compose end-to-end 4 ./scripts/docker-compose.yml TEST=e2e_singleton.test.ts + command: cond_spot_run_compose end-to-end 4 ./scripts/docker-compose.yml TEST=e2e_state_vars.test.ts e2e-block-building: docker: @@ -982,7 +1004,7 @@ jobs: command: | should_release || exit 0 deploy_dockerhub noir - deploy_dockerhub aztec-sandbox + deploy_dockerhub aztec deploy_dockerhub cli deploy_dockerhub aztec-faucet deploy_dockerhub mainnet-fork @@ -1034,7 +1056,7 @@ jobs: # Export variables for Terraform. export TF_VAR_BOOTNODE_1_PRIVATE_KEY=$BOOTNODE_1_PRIVATE_KEY export TF_VAR_BOOTNODE_2_PRIVATE_KEY=$BOOTNODE_2_PRIVATE_KEY - deploy_terraform_services yarn-project/p2p-bootstrap aztec-sandbox + deploy_terraform_services yarn-project/p2p-bootstrap aztec - run: name: "Deploy Aztec Nodes to AWS" command: | @@ -1048,16 +1070,16 @@ jobs: # Check if l1-contracts have changed if [ "$CONTRACTS_DEPLOYED" -eq 1 ]; then echo "Contracts have changed, taint nodes to force redeploy.." - deploy_terraform_services yarn-project/aztec-node aztec-sandbox aztec-node "aws_ecs_task_definition.aztec-node[0],aws_ecs_task_definition.aztec-node[1]" + deploy_terraform_services yarn-project/aztec-node aztec aztec-node "aws_ecs_task_definition.aztec-node[0],aws_ecs_task_definition.aztec-node[1]" else - deploy_terraform_services yarn-project/aztec-node aztec-sandbox + deploy_terraform_services yarn-project/aztec-node aztec fi - run: name: "Deploy Aztec Faucet to AWS" command: | should_deploy 0 || exit 0 export TF_VAR_FAUCET_PRIVATE_KEY=$FAUCET_PRIVATE_KEY - deploy_terraform_services yarn-project/aztec-faucet aztec-sandbox + deploy_terraform_services yarn-project/aztec-faucet aztec # Repeatable config for defining the workflow below. defaults: &defaults @@ -1126,6 +1148,9 @@ workflows: - noir-ecr-manifest <<: *defaults + # Transpiler + - avm-transpiler: *defaults + # Barretenberg - barretenberg-x86_64-linux-gcc: *defaults - barretenberg-x86_64-linux-clang: *defaults @@ -1174,6 +1199,7 @@ workflows: # Yarn Project - yarn-project-base: requires: + - avm-transpiler - l1-contracts - bb-js - noir-ecr-manifest @@ -1191,14 +1217,14 @@ workflows: - build-docs: *defaults_yarn_project # Artifacts - - aztec-sandbox: *defaults_yarn_project_prod + - aztec-package: *defaults_yarn_project_prod - cli: *defaults_yarn_project_prod - aztec-faucet: *defaults_yarn_project_prod # Boxes. - boxes: requires: - - aztec-sandbox + - aztec-package <<: *defaults - boxes-blank: requires: @@ -1217,10 +1243,11 @@ workflows: - e2e-join: requires: - end-to-end - - aztec-sandbox + - aztec-package - cli <<: *defaults - e2e-2-pxes: *e2e_test + - e2e-note-getter: *e2e_test - e2e-deploy-contract: *e2e_test - e2e-lending-contract: *e2e_test - e2e-token-contract: *e2e_test @@ -1228,7 +1255,7 @@ workflows: # TODO(3458): Investigate intermittent failure # - e2e-slow-tree: *e2e_test - e2e-sandbox-example: *e2e_test - - e2e-singleton: *e2e_test + - e2e-state-vars: *e2e_test - e2e-block-building: *e2e_test - e2e-nested-contract: *e2e_test - e2e-non-contract-account: *e2e_test @@ -1260,12 +1287,13 @@ workflows: requires: - mainnet-fork - e2e-2-pxes + - e2e-note-getter - e2e-deploy-contract - e2e-lending-contract - e2e-token-contract - e2e-blacklist-token-contract - e2e-sandbox-example - - e2e-singleton + - e2e-state-vars - e2e-block-building - e2e-nested-contract - e2e-non-contract-account diff --git a/.github/workflows/publish-bb.yml b/.github/workflows/publish-bb.yml index 27c345b34e6..6793f999745 100644 --- a/.github/workflows/publish-bb.yml +++ b/.github/workflows/publish-bb.yml @@ -139,12 +139,9 @@ jobs: ./barretenberg/cpp/build-wasm/bin/barretenberg.wasm.tar.gz ./barretenberg/cpp/build-wasm/bin/acvm_backend.wasm.tar.gz - build-mac: - name: Build on Mac (${{ matrix.target }}) + build-mac-intel: + name: Build on Mac x86_64-apple-darwin runs-on: macos-13 - strategy: - matrix: - target: [x86_64-apple-darwin, aarch64-apple-darwin] steps: - name: Checkout uses: actions/checkout@v3 @@ -154,18 +151,42 @@ jobs: - name: Create Mac Build Environment run: brew install cmake ninja - - name: Compile Barretenberg (x86_64) - if: matrix.target == 'x86_64-apple-darwin' + - name: Compile Barretenberg working-directory: barretenberg/cpp run: | cmake --preset default -DCMAKE_BUILD_TYPE=RelWithAssert cmake --build --preset default --target bb - - name: Compile Barretenberg (ARM) - if: matrix.target == 'aarch64-apple-darwin' + - name: Package barretenberg artifact + working-directory: barretenberg/cpp/build/bin + run: | + mkdir dist + cp ./bb ./dist/bb + 7z a -ttar -so -an ./dist/* | 7z a -si ./barretenberg-x86_64-apple-darwin.tar.gz + + - name: Upload artifact + uses: actions/upload-artifact@v3 + with: + name: barretenberg-x86_64-apple-darwin + path: ./barretenberg/cpp/build/bin/barretenberg-x86_64-apple-darwin.tar.gz + retention-days: 3 + + build-mac-m1: + name: Build on Mac aarch64-apple-darwin + runs-on: macos-14 + steps: + - name: Checkout + uses: actions/checkout@v3 + with: + ref: ${{ inputs.tag || env.GITHUB_REF }} + + - name: Create Mac Build Environment + run: brew install cmake ninja + + - name: Compile Barretenberg working-directory: barretenberg/cpp run: | - cmake --toolchain ./cmake/toolchains/aarch64-darwin.cmake --preset default -DCMAKE_BUILD_TYPE=RelWithAssert + cmake --preset default -DCMAKE_BUILD_TYPE=RelWithAssert cmake --build --preset default --target bb - name: Package barretenberg artifact @@ -173,18 +194,18 @@ jobs: run: | mkdir dist cp ./bb ./dist/bb - 7z a -ttar -so -an ./dist/* | 7z a -si ./barretenberg-${{ matrix.target }}.tar.gz + 7z a -ttar -so -an ./dist/* | 7z a -si ./barretenberg-aarch64-apple-darwin.tar.gz - name: Upload artifact uses: actions/upload-artifact@v3 with: - name: barretenberg-${{ matrix.target }} - path: ./barretenberg/cpp/build/bin/barretenberg-${{ matrix.target }}.tar.gz + name: barretenberg-aarch64-apple-darwin + path: ./barretenberg/cpp/build/bin/barretenberg-aarch64-apple-darwin.tar.gz retention-days: 3 release: name: Publish - needs: [build-x86_64-linux-gnu, build-mac, build-wasm-ts] + needs: [build-x86_64-linux-gnu, build-mac-intel, build-mac-m1,build-wasm-ts] runs-on: ubuntu-latest steps: - name: Download files from Linux Runner diff --git a/.release-please-manifest.json b/.release-please-manifest.json index fce3dcc81f6..6bed8a5067f 100644 --- a/.release-please-manifest.json +++ b/.release-please-manifest.json @@ -1,5 +1,5 @@ { - ".": "0.20.0", - "barretenberg": "0.20.0", - "barretenberg/ts": "0.20.0" + ".": "0.21.0", + "barretenberg": "0.21.0", + "barretenberg/ts": "0.21.0" } diff --git a/.vscode/c_cpp_properties.json b/.vscode/c_cpp_properties.json deleted file mode 100644 index 2dc5f952c11..00000000000 --- a/.vscode/c_cpp_properties.json +++ /dev/null @@ -1,18 +0,0 @@ -{ - "configurations": [ - { - "name": "Linux", - "includePath": [ - "${workspaceFolder}/**", - "${workspaceFolder}/circuits/src", - "${workspaceFolder}/circuits/barretenberg/cpp/src/aztec" - ], - "defines": [], - "compilerPath": "/usr/bin/clang", - "cStandard": "c11", - "cppStandard": "c++20", - "intelliSenseMode": "clang-x64" - } - ], - "version": 4 -} diff --git a/.vscode/launch.json b/.vscode/launch.json index 6ee6323034c..fa4b77b4ad0 100644 --- a/.vscode/launch.json +++ b/.vscode/launch.json @@ -14,41 +14,5 @@ "localRoot": "${workspaceFolder}", "sourceMaps": true }, - /////////////////////////////////////// - // C++/Circuits targets - /////////////////////////////////////// - { - "name": "(lldb) Launch native - Circuits C++", - "type": "lldb", - "request": "launch", - "program": "${command:cmake.launchTargetPath}", - "args": [ "--gtest_filter=*" ], - "cwd": "${workspaceFolder}/circuits/cpp/build", - "internalConsoleOptions": "openOnSessionStart", - "console": "internalConsole" - }, - { - "name": "(lldb) Launch in WASM - Circuits C++", - "type": "lldb", - "request": "launch", - "program": "~/.wasmtime/bin/wasmtime", - "args": [ - "-g", // tell wasmtime to generate debug info - "--disable-cache", // necessary or wasmtime throws a error: - // thread 'main' panicked at 'called `Result::unwrap()` on an `Err` value: ()', crates/jit/src/debug.rs:147:58 - // in: wasmtime_jit::debug::create_gdbjit_image - "--dir", // give wasmtime the parent dir as a sandbox - "..", // needs this to find srs - "${command:cmake.launchTargetPath}", // the test exe - "--", - "--gtest_filter=*", // filter tests - "--gtest_color=1" // color gtest output (off by default in wasm) - ], - "cwd": "${workspaceFolder}/circuits/cpp/build-wasm", - "internalConsoleOptions": "openOnSessionStart", - "console": "internalConsole" - }, - // End C++/Circuits targets - /////////////////////////////////////// ] } \ No newline at end of file diff --git a/.vscode/settings.json b/.vscode/settings.json index 663bec37526..bb29728a078 100644 --- a/.vscode/settings.json +++ b/.vscode/settings.json @@ -114,7 +114,6 @@ "**/.yalc": true, "**/node_modules": true, "**/.pnp.*": true, - "circuits/cpp/barretenberg/**": true, "**/msgpack-c/**": true }, "[terraform]": { @@ -141,12 +140,6 @@ // "clangd.path": "clangd-15", // - // CMake - // - // Location of base CMakeLists file - "cmake.sourceDirectory": "${workspaceFolder}/circuits/cpp/", - "cmake.buildDirectory": "${workspaceFolder}/circuits/cpp/build", - // // C/C++ (should be disabled) // // Make sure all C++ IntelliSense features are disabled @@ -160,15 +153,8 @@ "C_Cpp.default.enableConfigurationSquiggles": false, "C_Cpp.formatting": "disabled", "C_Cpp.vcpkg.enabled": false, - "C_Cpp.default.includePath": ["cpp/barretenberg/cpp/src"], - // - // TestMate C++ - // - // Ensures tests are run from the `build` directory - // which ensures SRS can be read - "testMate.cpp.test.workingDirectory": "${workspaceFolder}/circuits/cpp/build", - // Filter all binaries that are not tests - "testMate.cpp.test.executables": "${workspaceFolder}/circuits/cpp/build/bin/*{test,Test,TEST}*" - // End C++/Circuits settings - /////////////////////////////////////// + "C_Cpp.default.includePath": [ + "barretenberg/cpp/src" + ], + "cmake.sourceDirectory": "/mnt/user-data/adam/aztec-packages/barretenberg/cpp", } diff --git a/CHANGELOG.md b/CHANGELOG.md index d71d1a92c8c..f5528b86d2d 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,5 +1,97 @@ # Changelog +## [0.21.0](https://github.com/AztecProtocol/aztec-packages/compare/aztec-packages-v0.20.0...aztec-packages-v0.21.0) (2024-01-30) + + +### ⚠ BREAKING CHANGES + +* aztec binary ([#3927](https://github.com/AztecProtocol/aztec-packages/issues/3927)) +* add opcode for sha256 compression function ([#4229](https://github.com/AztecProtocol/aztec-packages/issues/4229)) +* add opcode for poseidon2 permutation ([#4214](https://github.com/AztecProtocol/aztec-packages/issues/4214)) +* remove ec_double opcode ([#4210](https://github.com/AztecProtocol/aztec-packages/issues/4210)) +* Updates singleton usage ([#4186](https://github.com/AztecProtocol/aztec-packages/issues/4186)) +* Add big int opcodes (without implementation) ([#4050](https://github.com/AztecProtocol/aztec-packages/issues/4050)) + +### Features + +* **3738:** AVM basic arithmetic operations for non ff types ([#3881](https://github.com/AztecProtocol/aztec-packages/issues/3881)) ([457a3f9](https://github.com/AztecProtocol/aztec-packages/commit/457a3f9b0c05f58cc88ef43763c5d55b6debaf05)), closes [#3996](https://github.com/AztecProtocol/aztec-packages/issues/3996) +* Accrued substate instructions ([#4197](https://github.com/AztecProtocol/aztec-packages/issues/4197)) ([bfe30d2](https://github.com/AztecProtocol/aztec-packages/commit/bfe30d27785fb659bea5bb716ccf0eadb0f19167)) +* Add big int opcodes (without implementation) ([#4050](https://github.com/AztecProtocol/aztec-packages/issues/4050)) ([bcab9ce](https://github.com/AztecProtocol/aztec-packages/commit/bcab9ceab62bede3bc1c105b3e639e7c64e3217a)) +* Add comparators to get/view note ([#3136](https://github.com/AztecProtocol/aztec-packages/issues/3136)) ([#4205](https://github.com/AztecProtocol/aztec-packages/issues/4205)) ([6de36b3](https://github.com/AztecProtocol/aztec-packages/commit/6de36b39825785ec96a967b1ff4f2cbb83943ebb)) +* Add inclusion check l1->l2 ([#4141](https://github.com/AztecProtocol/aztec-packages/issues/4141)) ([bef65c3](https://github.com/AztecProtocol/aztec-packages/commit/bef65c38c58427325b4481ab794f0fb4f12196b0)) +* Add opcode for poseidon2 permutation ([#4214](https://github.com/AztecProtocol/aztec-packages/issues/4214)) ([53c5ba5](https://github.com/AztecProtocol/aztec-packages/commit/53c5ba5fa2a86aba16bba8aa8d3a594a789e3e24)) +* Add opcode for sha256 compression function ([#4229](https://github.com/AztecProtocol/aztec-packages/issues/4229)) ([ac25ff7](https://github.com/AztecProtocol/aztec-packages/commit/ac25ff737a934a5f260605f4497e4074c3ed5824)) +* Adding slither to l1-contracts ([#4226](https://github.com/AztecProtocol/aztec-packages/issues/4226)) ([b4dc31d](https://github.com/AztecProtocol/aztec-packages/commit/b4dc31dd9fb02c096db6c40d848aea9f03e36a8c)) +* **avm:** Add tests for memory and bitwise instructions ([#4184](https://github.com/AztecProtocol/aztec-packages/issues/4184)) ([6dac650](https://github.com/AztecProtocol/aztec-packages/commit/6dac6504fdbe85c61ffd7aad7c37cc1b52ebf6d4)) +* **avm:** Bytecode avm control flow ([#4253](https://github.com/AztecProtocol/aztec-packages/issues/4253)) ([fb1d742](https://github.com/AztecProtocol/aztec-packages/commit/fb1d7420860a35e68b987e790abdaba18595219b)), closes [#4209](https://github.com/AztecProtocol/aztec-packages/issues/4209) +* **avm:** Bytecode parsing and proof generation ([#4191](https://github.com/AztecProtocol/aztec-packages/issues/4191)) ([6c70548](https://github.com/AztecProtocol/aztec-packages/commit/6c70548a98c8e01bc7925d98ece9a2eda4139f69)), closes [#3791](https://github.com/AztecProtocol/aztec-packages/issues/3791) +* **avm:** Environment getters ([#4203](https://github.com/AztecProtocol/aztec-packages/issues/4203)) ([60d2377](https://github.com/AztecProtocol/aztec-packages/commit/60d237771d129fc9a75e5f0806fd2d002c6e92c8)) +* **avm:** Implement comparator opcodes ([#4232](https://github.com/AztecProtocol/aztec-packages/issues/4232)) ([973ff2f](https://github.com/AztecProtocol/aztec-packages/commit/973ff2f0ad11b78b5dcab1537abe5cb611af8db2)) +* **avm:** Initial external calls ([#4194](https://github.com/AztecProtocol/aztec-packages/issues/4194)) ([d8aa966](https://github.com/AztecProtocol/aztec-packages/commit/d8aa9662730028824a4e7de2cd1dc6b95359ff21)) +* **avm:** Link up storage ([#4150](https://github.com/AztecProtocol/aztec-packages/issues/4150)) ([3e86870](https://github.com/AztecProtocol/aztec-packages/commit/3e868705ca9b1ca05d962a7f5399a41ce470b120)) +* **avm:** Revert instruction ([#4206](https://github.com/AztecProtocol/aztec-packages/issues/4206)) ([bd6e797](https://github.com/AztecProtocol/aztec-packages/commit/bd6e79727bb207978634d70df3bb213a222a4bb7)) +* **avm:** Tagged memory ([#4213](https://github.com/AztecProtocol/aztec-packages/issues/4213)) ([e5ff2f6](https://github.com/AztecProtocol/aztec-packages/commit/e5ff2f60e20e9e85972515d845d5d45a0117409f)) +* Aztec binary ([#3927](https://github.com/AztecProtocol/aztec-packages/issues/3927)) ([12356d9](https://github.com/AztecProtocol/aztec-packages/commit/12356d9e34994a239d5612798c1bc82fa3d26562)) +* Contract classes and instances ([#4192](https://github.com/AztecProtocol/aztec-packages/issues/4192)) ([1858126](https://github.com/AztecProtocol/aztec-packages/commit/18581265dfd6d6aff42f9b90fd8425159d501f46)), closes [#4053](https://github.com/AztecProtocol/aztec-packages/issues/4053) +* Deserialize AztecAddress when contract's view function returns it in Aztec.js [#3641](https://github.com/AztecProtocol/aztec-packages/issues/3641) ([#4224](https://github.com/AztecProtocol/aztec-packages/issues/4224)) ([11f400f](https://github.com/AztecProtocol/aztec-packages/commit/11f400f6580d4c3fee52a5e97d84fcdf0dbad779)) +* **docs:** DIP1: Extract Explanations ([#4228](https://github.com/AztecProtocol/aztec-packages/issues/4228)) ([3b25737](https://github.com/AztecProtocol/aztec-packages/commit/3b25737324e45bdfb49233f73065569301282cc0)) +* **docs:** Historical trees docs ([#3895](https://github.com/AztecProtocol/aztec-packages/issues/3895)) ([8c3efba](https://github.com/AztecProtocol/aztec-packages/commit/8c3efba92f74905709760f3d8838df50076aaa92)) +* **docs:** PXE docs ([#4021](https://github.com/AztecProtocol/aztec-packages/issues/4021)) ([a656034](https://github.com/AztecProtocol/aztec-packages/commit/a6560343fb333a6f725bc9d8c41e8594ea2e00b0)) +* Implement bigint in Noir, using bigint opcodes ([#4198](https://github.com/AztecProtocol/aztec-packages/issues/4198)) ([3720415](https://github.com/AztecProtocol/aztec-packages/commit/3720415c8bf2b6f3292d961795eb13f08cb9dff5)) +* Implement Embedded EC add and double opcodes ([#3982](https://github.com/AztecProtocol/aztec-packages/issues/3982)) ([ccb7bff](https://github.com/AztecProtocol/aztec-packages/commit/ccb7bff8e16ea9c8bc4bd48754db59857137507e)) +* Limit exposed functions on note utils ([#4207](https://github.com/AztecProtocol/aztec-packages/issues/4207)) ([8338f39](https://github.com/AztecProtocol/aztec-packages/commit/8338f390fd826bc85f6789a1124ae34251a042dd)) +* Nullifier key validation ([#4176](https://github.com/AztecProtocol/aztec-packages/issues/4176)) ([1c72c0d](https://github.com/AztecProtocol/aztec-packages/commit/1c72c0d2978af94cb147f143977557fa1540c419)) +* Produce graph of internal Barretenberg dependencies ([#4225](https://github.com/AztecProtocol/aztec-packages/issues/4225)) ([88e7923](https://github.com/AztecProtocol/aztec-packages/commit/88e7923ed2ecd747b65f72c5955016c6a1b80b9f)) +* Recursive folding and decider verifier for Protogalaxy ([#4156](https://github.com/AztecProtocol/aztec-packages/issues/4156)) ([9342048](https://github.com/AztecProtocol/aztec-packages/commit/93420480603b2dfa126e5bddb08cd768b7093352)) +* Remove ec_double opcode ([#4210](https://github.com/AztecProtocol/aztec-packages/issues/4210)) ([75f26c4](https://github.com/AztecProtocol/aztec-packages/commit/75f26c4f2a9cf185891234eab6ec4f213d31fc50)) +* Replace single bit range constraints with basic bool gates ([#4164](https://github.com/AztecProtocol/aztec-packages/issues/4164)) ([0a3553b](https://github.com/AztecProtocol/aztec-packages/commit/0a3553b10e02374843181901709933975dc36bb4)) +* Updates singleton usage ([#4186](https://github.com/AztecProtocol/aztec-packages/issues/4186)) ([301f0e6](https://github.com/AztecProtocol/aztec-packages/commit/301f0e6d0832a999a31d0e9a5b4e8267474de6ab)) + + +### Bug Fixes + +* **avm:** Fix usage of Fr with tagged memory ([#4240](https://github.com/AztecProtocol/aztec-packages/issues/4240)) ([b82e70c](https://github.com/AztecProtocol/aztec-packages/commit/b82e70c61771c8a3cef4026dc522f2c99147180b)) +* **bb:** .gitignore ([#4201](https://github.com/AztecProtocol/aztec-packages/issues/4201)) ([a56e418](https://github.com/AztecProtocol/aztec-packages/commit/a56e418b0fe90b77b7a9fd6bcb0e40cd15260fd6)) +* **docs:** Add missing deps to token tutorial references ([#4265](https://github.com/AztecProtocol/aztec-packages/issues/4265)) ([d7e2d9c](https://github.com/AztecProtocol/aztec-packages/commit/d7e2d9c80262dd4dff714caac575785b3bf14482)) +* Generic Honk dependencies ([#4239](https://github.com/AztecProtocol/aztec-packages/issues/4239)) ([382dfbe](https://github.com/AztecProtocol/aztec-packages/commit/382dfbed6aa4c6da7b3c897f8a5f9639843d7037)) + + +### Miscellaneous + +* Add note getter test to cci ([#4236](https://github.com/AztecProtocol/aztec-packages/issues/4236)) ([e1184ff](https://github.com/AztecProtocol/aztec-packages/commit/e1184ffc2f4ac2d2de0e8a106614bdd266a06cf4)) +* **avm-simulator:** Cleanup, tags as first instruction constructor args ([#4244](https://github.com/AztecProtocol/aztec-packages/issues/4244)) ([e46b865](https://github.com/AztecProtocol/aztec-packages/commit/e46b865f4c53c8916061147c85fd298db31a384f)) +* **avm:** Remove the state manager in favour of journal ([#4195](https://github.com/AztecProtocol/aztec-packages/issues/4195)) ([40f9324](https://github.com/AztecProtocol/aztec-packages/commit/40f9324a88fef3f762f5e6a21ccd3f200f8b8c4a)) +* **bb:** Rearrange namespaces ([#4147](https://github.com/AztecProtocol/aztec-packages/issues/4147)) ([5de0a8e](https://github.com/AztecProtocol/aztec-packages/commit/5de0a8e8dce2483230cccb1d716613966089f2f6)) +* Cleaning up circuits test setup ([#4235](https://github.com/AztecProtocol/aztec-packages/issues/4235)) ([fa6915a](https://github.com/AztecProtocol/aztec-packages/commit/fa6915a5f35c3b1b1283f122666d15f836ac682b)), closes [#4237](https://github.com/AztecProtocol/aztec-packages/issues/4237) +* Delete C++ PK circuits ([#4219](https://github.com/AztecProtocol/aztec-packages/issues/4219)) ([9136d32](https://github.com/AztecProtocol/aztec-packages/commit/9136d32268db350779d51e45884368be3a694220)) +* Delete MemoryDB ([#4241](https://github.com/AztecProtocol/aztec-packages/issues/4241)) ([9e6250a](https://github.com/AztecProtocol/aztec-packages/commit/9e6250aacbe2d47aa71dee9fa5e43c66eec73e75)) +* **docs:** Fix a few links to docs ([#4260](https://github.com/AztecProtocol/aztec-packages/issues/4260)) ([1c8ea49](https://github.com/AztecProtocol/aztec-packages/commit/1c8ea497fb1d64da64cb240917a60d57bd1efef8)) +* **docs:** Fix autogen docs ([#4261](https://github.com/AztecProtocol/aztec-packages/issues/4261)) ([3b9927a](https://github.com/AztecProtocol/aztec-packages/commit/3b9927ab7ef2e7e50193bbfcb6ab4db66734e481)) +* **docs:** Fix public and private storage not in docs ([#4257](https://github.com/AztecProtocol/aztec-packages/issues/4257)) ([48ceafd](https://github.com/AztecProtocol/aztec-packages/commit/48ceafd085f56464e65ae17a6f4931fdbdb575b6)) +* **docs:** Fix token bridge tutorial ([#3935](https://github.com/AztecProtocol/aztec-packages/issues/3935)) ([84c9fdb](https://github.com/AztecProtocol/aztec-packages/commit/84c9fdbecf0b44a0897badcc12e5a9b333da4ec0)) +* **docs:** Split contract storage pages ([#4202](https://github.com/AztecProtocol/aztec-packages/issues/4202)) ([1e05f33](https://github.com/AztecProtocol/aztec-packages/commit/1e05f33c58feb30f073e6dd5369cbed336343e54)) +* Fix typo in yellow paper ([#4247](https://github.com/AztecProtocol/aztec-packages/issues/4247)) ([ac82e6b](https://github.com/AztecProtocol/aztec-packages/commit/ac82e6ba57a2a868e79399248fa2505c383e241c)) +* Fixes test file from [#4205](https://github.com/AztecProtocol/aztec-packages/issues/4205) ([#4216](https://github.com/AztecProtocol/aztec-packages/issues/4216)) ([18a9b72](https://github.com/AztecProtocol/aztec-packages/commit/18a9b72dc9df95f517bb90fbcf0ebe45e7430d9a)) +* Git subrepo pull (merge) noir ([#4252](https://github.com/AztecProtocol/aztec-packages/issues/4252)) ([80be57d](https://github.com/AztecProtocol/aztec-packages/commit/80be57d612ebdd0aac9384c4051e01823c9222da)) +* Nuking old `BlockHeader` ([#4154](https://github.com/AztecProtocol/aztec-packages/issues/4154)) ([997791a](https://github.com/AztecProtocol/aztec-packages/commit/997791a06061eaab6c219948576565b457051ba2)), closes [#3937](https://github.com/AztecProtocol/aztec-packages/issues/3937) [#3564](https://github.com/AztecProtocol/aztec-packages/issues/3564) [#4134](https://github.com/AztecProtocol/aztec-packages/issues/4134) +* Remove flaky e2e p2p test ([#4181](https://github.com/AztecProtocol/aztec-packages/issues/4181)) ([688e4af](https://github.com/AztecProtocol/aztec-packages/commit/688e4afc2b8be8b7766baf5180c89fe985ebf6cd)) +* Remove mandatory jsdoc ([#4180](https://github.com/AztecProtocol/aztec-packages/issues/4180)) ([9625b43](https://github.com/AztecProtocol/aztec-packages/commit/9625b4350a54c43f55841b508e3f86e7d7d6635b)), closes [#3860](https://github.com/AztecProtocol/aztec-packages/issues/3860) +* Remove stubbed docs ([#4196](https://github.com/AztecProtocol/aztec-packages/issues/4196)) ([25a4bc4](https://github.com/AztecProtocol/aztec-packages/commit/25a4bc490a53304110e7e1f79e99f4c8b7639164)) +* Replace leveldb with lmdb for merkle trees ([#4119](https://github.com/AztecProtocol/aztec-packages/issues/4119)) ([84967b2](https://github.com/AztecProtocol/aztec-packages/commit/84967b246180e8ae94db98b32c4ed5439958d8d2)), closes [#3362](https://github.com/AztecProtocol/aztec-packages/issues/3362) +* Replace relative paths to noir-protocol-circuits ([a9839a8](https://github.com/AztecProtocol/aztec-packages/commit/a9839a8b9c0dce68d8af3b17e1140f669cd1a8d1)) +* Replace relative paths to noir-protocol-circuits ([2ef6e56](https://github.com/AztecProtocol/aztec-packages/commit/2ef6e56052c6d35ef0cbc1e07cd3d06fca504747)) +* Replace relative paths to noir-protocol-circuits ([426c17d](https://github.com/AztecProtocol/aztec-packages/commit/426c17d23981eaccf3a93c4a86d366a12141895f)) +* Replace relative paths to noir-protocol-circuits ([12adb71](https://github.com/AztecProtocol/aztec-packages/commit/12adb71d3c7c03eca8b315d3d1192368d315ca45)) +* Replace relative paths to noir-protocol-circuits ([fcd048d](https://github.com/AztecProtocol/aztec-packages/commit/fcd048d6a1ef6d84506f29d4e2aa49657fe6ca21)) +* Unifying Header serialization accross domains ([#4230](https://github.com/AztecProtocol/aztec-packages/issues/4230)) ([92080a0](https://github.com/AztecProtocol/aztec-packages/commit/92080a02819e563680a18e38ce49d983275f2bf5)) +* Update .gitrepo with correct parent hash ([#4279](https://github.com/AztecProtocol/aztec-packages/issues/4279)) ([9253c8a](https://github.com/AztecProtocol/aztec-packages/commit/9253c8a6944ef36e2d61ba6bf86953afcbb4966f)) + + +### Documentation + +* **bb:** How to use docker_interactive.sh ([#4220](https://github.com/AztecProtocol/aztec-packages/issues/4220)) ([f44c6b1](https://github.com/AztecProtocol/aztec-packages/commit/f44c6b173856331a6ca4d00d50436671735172a2)) +* Update welcome page and dev pages ([#4143](https://github.com/AztecProtocol/aztec-packages/issues/4143)) ([d2a86ff](https://github.com/AztecProtocol/aztec-packages/commit/d2a86ff1f1eb79a47f6297f665c11e8aafcb584b)) + ## [0.20.0](https://github.com/AztecProtocol/aztec-packages/compare/aztec-packages-v0.19.0...aztec-packages-v0.20.0) (2024-01-22) diff --git a/README.md b/README.md index dc4c5d8cf62..95e9503fa2f 100644 --- a/README.md +++ b/README.md @@ -9,7 +9,7 @@ All the packages that make up [Aztec](https://docs.aztec.network). ## Popular packages - [Aztec.nr](./yarn-project/aztec-nr/): A [Noir](https://noir-lang.org) framework for smart contracts on Aztec. -- [Aztec Sandbox](./yarn-project/aztec-sandbox/): A package for setting up a local dev net, including a local Ethereum network, deployed rollup contracts and Aztec execution environment. +- [Aztec](./yarn-project/aztec/): A package for starting up local dev net modules, including a local 'sandbox' devnet, an Ethereum network, deployed rollup contracts and Aztec execution environment. - [Aztec.js](./yarn-project/aztec.js/): A tool for interacting with the Aztec network. It communicates via the [Private Execution Environment (PXE)](./yarn-project/pxe/). - [Example contracts](./yarn-project/noir-contracts/): Example contracts for the Aztec network, written in Noir. - [End to end tests](./yarn-project/end-to-end/): Integration tests written in Typescript--a good reference for how to use the packages for specific tasks. diff --git a/avm-transpiler/.gitignore b/avm-transpiler/.gitignore new file mode 100644 index 00000000000..212de442f4e --- /dev/null +++ b/avm-transpiler/.gitignore @@ -0,0 +1,2 @@ +/target +.DS_Store \ No newline at end of file diff --git a/avm-transpiler/Cargo.lock b/avm-transpiler/Cargo.lock new file mode 100644 index 00000000000..718f61220a5 --- /dev/null +++ b/avm-transpiler/Cargo.lock @@ -0,0 +1,2019 @@ +# This file is automatically @generated by Cargo. +# It is not intended for manual editing. +version = 3 + +[[package]] +name = "acir" +version = "0.39.0" +dependencies = [ + "acir_field", + "base64 0.21.7", + "bincode", + "brillig", + "flate2", + "serde", + "thiserror", +] + +[[package]] +name = "acir_field" +version = "0.39.0" +dependencies = [ + "ark-bn254", + "ark-ff", + "cfg-if", + "hex", + "num-bigint", + "num-traits", + "serde", +] + +[[package]] +name = "acvm" +version = "0.39.0" +dependencies = [ + "acir", + "acvm_blackbox_solver", + "brillig_vm", + "indexmap 1.9.3", + "num-bigint", + "thiserror", + "tracing", +] + +[[package]] +name = "acvm_blackbox_solver" +version = "0.39.0" +dependencies = [ + "acir", + "blake2", + "blake3", + "k256", + "keccak", + "p256", + "sha2", + "sha3", + "thiserror", +] + +[[package]] +name = "adler" +version = "1.0.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f26201604c87b1e01bd3d98f8d5d9a8fcbb815e8cedb41ffccbeb4bf593a35fe" + +[[package]] +name = "ahash" +version = "0.7.7" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5a824f2aa7e75a0c98c5a504fceb80649e9c35265d44525b5f94de4771a395cd" +dependencies = [ + "getrandom", + "once_cell", + "version_check", +] + +[[package]] +name = "ahash" +version = "0.8.7" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "77c3a9648d43b9cd48db467b3f87fdd6e146bcc88ab0180006cef2179fe11d01" +dependencies = [ + "cfg-if", + "once_cell", + "version_check", + "zerocopy", +] + +[[package]] +name = "aho-corasick" +version = "1.1.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b2969dcb958b36655471fc61f7e416fa76033bdd4bfed0678d8fee1e2d07a1f0" +dependencies = [ + "memchr", +] + +[[package]] +name = "android-tzdata" +version = "0.1.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e999941b234f3131b00bc13c22d06e8c5ff726d1b6318ac7eb276997bbb4fef0" + +[[package]] +name = "android_system_properties" +version = "0.1.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "819e7219dbd41043ac279b19830f2efc897156490d7fd6ea916720117ee66311" +dependencies = [ + "libc", +] + +[[package]] +name = "anstream" +version = "0.6.11" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "6e2e1ebcb11de5c03c67de28a7df593d32191b44939c482e97702baaaa6ab6a5" +dependencies = [ + "anstyle", + "anstyle-parse", + "anstyle-query", + "anstyle-wincon", + "colorchoice", + "utf8parse", +] + +[[package]] +name = "anstyle" +version = "1.0.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "2faccea4cc4ab4a667ce676a30e8ec13922a692c99bb8f5b11f1502c72e04220" + +[[package]] +name = "anstyle-parse" +version = "0.2.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c75ac65da39e5fe5ab759307499ddad880d724eed2f6ce5b5e8a26f4f387928c" +dependencies = [ + "utf8parse", +] + +[[package]] +name = "anstyle-query" +version = "1.0.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e28923312444cdd728e4738b3f9c9cac739500909bb3d3c94b43551b16517648" +dependencies = [ + "windows-sys", +] + +[[package]] +name = "anstyle-wincon" +version = "3.0.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1cd54b81ec8d6180e24654d0b371ad22fc3dd083b6ff8ba325b72e00c87660a7" +dependencies = [ + "anstyle", + "windows-sys", +] + +[[package]] +name = "arena" +version = "0.23.0" +dependencies = [ + "generational-arena", +] + +[[package]] +name = "ark-bn254" +version = "0.4.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a22f4561524cd949590d78d7d4c5df8f592430d221f7f3c9497bbafd8972120f" +dependencies = [ + "ark-ec", + "ark-ff", + "ark-std", +] + +[[package]] +name = "ark-ec" +version = "0.4.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "defd9a439d56ac24968cca0571f598a61bc8c55f71d50a89cda591cb750670ba" +dependencies = [ + "ark-ff", + "ark-poly", + "ark-serialize", + "ark-std", + "derivative", + "hashbrown 0.13.2", + "itertools", + "num-traits", + "zeroize", +] + +[[package]] +name = "ark-ff" +version = "0.4.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ec847af850f44ad29048935519032c33da8aa03340876d351dfab5660d2966ba" +dependencies = [ + "ark-ff-asm", + "ark-ff-macros", + "ark-serialize", + "ark-std", + "derivative", + "digest", + "itertools", + "num-bigint", + "num-traits", + "paste", + "rustc_version", + "zeroize", +] + +[[package]] +name = "ark-ff-asm" +version = "0.4.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3ed4aa4fe255d0bc6d79373f7e31d2ea147bcf486cba1be5ba7ea85abdb92348" +dependencies = [ + "quote", + "syn 1.0.109", +] + +[[package]] +name = "ark-ff-macros" +version = "0.4.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7abe79b0e4288889c4574159ab790824d0033b9fdcb2a112a3182fac2e514565" +dependencies = [ + "num-bigint", + "num-traits", + "proc-macro2", + "quote", + "syn 1.0.109", +] + +[[package]] +name = "ark-poly" +version = "0.4.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d320bfc44ee185d899ccbadfa8bc31aab923ce1558716e1997a1e74057fe86bf" +dependencies = [ + "ark-ff", + "ark-serialize", + "ark-std", + "derivative", + "hashbrown 0.13.2", +] + +[[package]] +name = "ark-serialize" +version = "0.4.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "adb7b85a02b83d2f22f89bd5cac66c9c89474240cb6207cb1efc16d098e822a5" +dependencies = [ + "ark-serialize-derive", + "ark-std", + "digest", + "num-bigint", +] + +[[package]] +name = "ark-serialize-derive" +version = "0.4.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ae3281bc6d0fd7e549af32b52511e1302185bd688fd3359fa36423346ff682ea" +dependencies = [ + "proc-macro2", + "quote", + "syn 1.0.109", +] + +[[package]] +name = "ark-std" +version = "0.4.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "94893f1e0c6eeab764ade8dc4c0db24caf4fe7cbbaafc0eba0a9030f447b5185" +dependencies = [ + "num-traits", + "rand", +] + +[[package]] +name = "arrayref" +version = "0.3.7" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "6b4930d2cb77ce62f89ee5d5289b4ac049559b1c45539271f5ed4fdc7db34545" + +[[package]] +name = "arrayvec" +version = "0.7.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "96d30a06541fbafbc7f82ed10c06164cfbd2c401138f6addd8404629c4b16711" + +[[package]] +name = "autocfg" +version = "1.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d468802bab17cbc0cc575e9b053f41e72aa36bfa6b7f55e3529ffa43161b97fa" + +[[package]] +name = "avm-transpiler" +version = "0.1.0" +dependencies = [ + "acvm", + "base64 0.21.7", + "env_logger", + "log", + "noirc_driver", + "regex", + "serde", + "serde_json", +] + +[[package]] +name = "aztec_macros" +version = "0.23.0" +dependencies = [ + "iter-extended", + "noirc_frontend", +] + +[[package]] +name = "base16ct" +version = "0.1.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "349a06037c7bf932dd7e7d1f653678b2038b9ad46a74102f1fc7bd7872678cce" + +[[package]] +name = "base64" +version = "0.13.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9e1b586273c5702936fe7b7d6896644d8be71e6314cfe09d3167c95f712589e8" + +[[package]] +name = "base64" +version = "0.21.7" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9d297deb1925b89f2ccc13d7635fa0714f12c87adce1c75356b39ca9b7178567" + +[[package]] +name = "base64ct" +version = "1.6.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8c3c1a368f70d6cf7302d78f8f7093da241fb8e8807c05cc9e51a125895a6d5b" + +[[package]] +name = "bincode" +version = "1.3.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b1f45e9417d87227c7a56d22e471c6206462cba514c7590c09aff4cf6d1ddcad" +dependencies = [ + "serde", +] + +[[package]] +name = "bitmaps" +version = "2.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "031043d04099746d8db04daf1fa424b2bc8bd69d92b25962dcde24da39ab64a2" +dependencies = [ + "typenum", +] + +[[package]] +name = "blake2" +version = "0.10.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "46502ad458c9a52b69d4d4d32775c788b7a1b85e8bc9d482d92250fc0e3f8efe" +dependencies = [ + "digest", +] + +[[package]] +name = "blake3" +version = "1.5.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0231f06152bf547e9c2b5194f247cd97aacf6dcd8b15d8e5ec0663f64580da87" +dependencies = [ + "arrayref", + "arrayvec", + "cc", + "cfg-if", + "constant_time_eq", +] + +[[package]] +name = "block-buffer" +version = "0.10.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3078c7629b62d3f0439517fa394996acacc5cbc91c5a20d8c658e77abd503a71" +dependencies = [ + "generic-array", +] + +[[package]] +name = "brillig" +version = "0.39.0" +dependencies = [ + "acir_field", + "serde", +] + +[[package]] +name = "brillig_vm" +version = "0.39.0" +dependencies = [ + "acir", + "acvm_blackbox_solver", + "num-bigint", + "num-traits", +] + +[[package]] +name = "build-data" +version = "0.1.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "aed3884e2cab7c973c8fd2d150314b6a932df7fdc830edcaf1e8e7c4ae9db3c0" +dependencies = [ + "chrono", + "safe-lock", + "safe-regex", +] + +[[package]] +name = "bumpalo" +version = "3.14.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7f30e7476521f6f8af1a1c4c0b8cc94f0bee37d91763d0ca2665f299b6cd8aec" + +[[package]] +name = "byteorder" +version = "1.5.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1fd0f2584146f6f2ef48085050886acf353beff7305ebd1ae69500e27c67f64b" + +[[package]] +name = "cc" +version = "1.0.83" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f1174fb0b6ec23863f8b971027804a42614e347eafb0a95bf0b12cdae21fc4d0" +dependencies = [ + "libc", +] + +[[package]] +name = "cfg-if" +version = "1.0.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "baf1de4339761588bc0619e3cbc0120ee582ebb74b53b4efbf79117bd2da40fd" + +[[package]] +name = "chrono" +version = "0.4.33" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9f13690e35a5e4ace198e7beea2895d29f3a9cc55015fcebe6336bd2010af9eb" +dependencies = [ + "android-tzdata", + "iana-time-zone", + "num-traits", + "serde", + "windows-targets", +] + +[[package]] +name = "chumsky" +version = "0.8.0" +source = "git+https://github.com/jfecher/chumsky?rev=ad9d312#ad9d312d9ffbc66c14514fa2b5752f4127b44f1e" +dependencies = [ + "hashbrown 0.11.2", +] + +[[package]] +name = "clap" +version = "4.4.18" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1e578d6ec4194633722ccf9544794b71b1385c3c027efe0c55db226fc880865c" +dependencies = [ + "clap_builder", + "clap_derive", +] + +[[package]] +name = "clap_builder" +version = "4.4.18" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "4df4df40ec50c46000231c914968278b1eb05098cf8f1b3a518a95030e71d1c7" +dependencies = [ + "anstream", + "anstyle", + "clap_lex", + "strsim", +] + +[[package]] +name = "clap_derive" +version = "4.4.7" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "cf9804afaaf59a91e75b022a30fb7229a7901f60c755489cc61c9b423b836442" +dependencies = [ + "heck", + "proc-macro2", + "quote", + "syn 2.0.48", +] + +[[package]] +name = "clap_lex" +version = "0.6.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "702fc72eb24e5a1e48ce58027a675bc24edd52096d5397d4aea7c6dd9eca0bd1" + +[[package]] +name = "codespan" +version = "0.11.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3362992a0d9f1dd7c3d0e89e0ab2bb540b7a95fea8cd798090e758fda2899b5e" +dependencies = [ + "codespan-reporting", + "serde", +] + +[[package]] +name = "codespan-reporting" +version = "0.11.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3538270d33cc669650c4b093848450d380def10c331d38c768e34cac80576e6e" +dependencies = [ + "serde", + "termcolor", + "unicode-width", +] + +[[package]] +name = "colorchoice" +version = "1.0.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "acbf1af155f9b9ef647e42cdc158db4b64a1b61f743629225fde6f3e0be2a7c7" + +[[package]] +name = "const-oid" +version = "0.9.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c2459377285ad874054d797f3ccebf984978aa39129f6eafde5cdc8315b612f8" + +[[package]] +name = "constant_time_eq" +version = "0.3.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f7144d30dcf0fafbce74250a3963025d8d52177934239851c917d29f1df280c2" + +[[package]] +name = "core-foundation-sys" +version = "0.8.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "06ea2b9bc92be3c2baa9334a323ebca2d6f074ff852cd1d7b11064035cd3868f" + +[[package]] +name = "cpufeatures" +version = "0.2.12" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "53fe5e26ff1b7aef8bca9c6080520cfb8d9333c7568e1829cef191a9723e5504" +dependencies = [ + "libc", +] + +[[package]] +name = "crc32fast" +version = "1.3.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b540bd8bc810d3885c6ea91e2018302f68baba2129ab3e88f32389ee9370880d" +dependencies = [ + "cfg-if", +] + +[[package]] +name = "crypto-bigint" +version = "0.4.9" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ef2b4b23cddf68b89b8f8069890e8c270d54e2d5fe1b143820234805e4cb17ef" +dependencies = [ + "generic-array", + "rand_core", + "subtle", + "zeroize", +] + +[[package]] +name = "crypto-common" +version = "0.1.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1bfb12502f3fc46cca1bb51ac28df9d618d813cdc3d2f25b9fe775a34af26bb3" +dependencies = [ + "generic-array", + "typenum", +] + +[[package]] +name = "darling" +version = "0.20.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "fc5d6b04b3fd0ba9926f945895de7d806260a2d7431ba82e7edaecb043c4c6b8" +dependencies = [ + "darling_core", + "darling_macro", +] + +[[package]] +name = "darling_core" +version = "0.20.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "04e48a959bcd5c761246f5d090ebc2fbf7b9cd527a492b07a67510c108f1e7e3" +dependencies = [ + "fnv", + "ident_case", + "proc-macro2", + "quote", + "strsim", + "syn 2.0.48", +] + +[[package]] +name = "darling_macro" +version = "0.20.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1d1545d67a2149e1d93b7e5c7752dce5a7426eb5d1357ddcfd89336b94444f77" +dependencies = [ + "darling_core", + "quote", + "syn 2.0.48", +] + +[[package]] +name = "der" +version = "0.6.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f1a467a65c5e759bce6e65eaf91cc29f466cdc57cb65777bd646872a8a1fd4de" +dependencies = [ + "const-oid", + "zeroize", +] + +[[package]] +name = "deranged" +version = "0.3.11" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b42b6fa04a440b495c8b04d0e71b707c585f83cb9cb28cf8cd0d976c315e31b4" +dependencies = [ + "powerfmt", + "serde", +] + +[[package]] +name = "derivative" +version = "2.2.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "fcc3dd5e9e9c0b295d6e1e4d811fb6f157d5ffd784b8d202fc62eac8035a770b" +dependencies = [ + "proc-macro2", + "quote", + "syn 1.0.109", +] + +[[package]] +name = "digest" +version = "0.10.7" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9ed9a281f7bc9b7576e61468ba615a66a5c8cfdff42420a70aa82701a3b1e292" +dependencies = [ + "block-buffer", + "crypto-common", + "subtle", +] + +[[package]] +name = "ecdsa" +version = "0.14.8" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "413301934810f597c1d19ca71c8710e99a3f1ba28a0d2ebc01551a2daeea3c5c" +dependencies = [ + "der", + "elliptic-curve", + "rfc6979", + "signature", +] + +[[package]] +name = "either" +version = "1.9.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a26ae43d7bcc3b814de94796a5e736d4029efb0ee900c12e2d54c993ad1a1e07" + +[[package]] +name = "elliptic-curve" +version = "0.12.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e7bb888ab5300a19b8e5bceef25ac745ad065f3c9f7efc6de1b91958110891d3" +dependencies = [ + "base16ct", + "crypto-bigint", + "der", + "digest", + "ff", + "generic-array", + "group", + "pkcs8", + "rand_core", + "sec1", + "subtle", + "zeroize", +] + +[[package]] +name = "env_filter" +version = "0.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a009aa4810eb158359dda09d0c87378e4bbb89b5a801f016885a4707ba24f7ea" +dependencies = [ + "log", + "regex", +] + +[[package]] +name = "env_logger" +version = "0.11.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "05e7cf40684ae96ade6232ed84582f40ce0a66efcd43a5117aef610534f8e0b8" +dependencies = [ + "anstream", + "anstyle", + "env_filter", + "humantime", + "log", +] + +[[package]] +name = "equivalent" +version = "1.0.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5443807d6dff69373d433ab9ef5378ad8df50ca6298caf15de6e52e24aaf54d5" + +[[package]] +name = "ff" +version = "0.12.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d013fc25338cc558c5c2cfbad646908fb23591e2404481826742b651c9af7160" +dependencies = [ + "rand_core", + "subtle", +] + +[[package]] +name = "flate2" +version = "1.0.28" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "46303f565772937ffe1d394a4fac6f411c6013172fadde9dcdb1e147a086940e" +dependencies = [ + "crc32fast", + "miniz_oxide", +] + +[[package]] +name = "fm" +version = "0.23.0" +dependencies = [ + "codespan-reporting", + "serde", +] + +[[package]] +name = "fnv" +version = "1.0.7" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3f9eec918d3f24069decb9af1554cad7c880e2da24a9afd88aca000531ab82c1" + +[[package]] +name = "fxhash" +version = "0.2.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c31b6d751ae2c7f11320402d34e41349dd1016f8d5d45e48c4312bc8625af50c" +dependencies = [ + "byteorder", +] + +[[package]] +name = "generational-arena" +version = "0.2.9" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "877e94aff08e743b651baaea359664321055749b398adff8740a7399af7796e7" +dependencies = [ + "cfg-if", +] + +[[package]] +name = "generic-array" +version = "0.14.7" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "85649ca51fd72272d7821adaf274ad91c288277713d9c18820d8499a7ff69e9a" +dependencies = [ + "typenum", + "version_check", +] + +[[package]] +name = "getrandom" +version = "0.2.12" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "190092ea657667030ac6a35e305e62fc4dd69fd98ac98631e5d3a2b1575a12b5" +dependencies = [ + "cfg-if", + "libc", + "wasi", +] + +[[package]] +name = "group" +version = "0.12.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5dfbfb3a6cfbd390d5c9564ab283a0349b9b9fcd46a706c1eb10e0db70bfbac7" +dependencies = [ + "ff", + "rand_core", + "subtle", +] + +[[package]] +name = "hashbrown" +version = "0.11.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ab5ef0d4909ef3724cc8cce6ccc8572c5c817592e9285f5464f8e86f8bd3726e" +dependencies = [ + "ahash 0.7.7", +] + +[[package]] +name = "hashbrown" +version = "0.12.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8a9ee70c43aaf417c914396645a0fa852624801b24ebb7ae78fe8272889ac888" + +[[package]] +name = "hashbrown" +version = "0.13.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "43a3c133739dddd0d2990f9a4bdf8eb4b21ef50e4851ca85ab661199821d510e" +dependencies = [ + "ahash 0.8.7", +] + +[[package]] +name = "hashbrown" +version = "0.14.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "290f1a1d9242c78d09ce40a5e87e7554ee637af1351968159f4952f028f75604" + +[[package]] +name = "heck" +version = "0.4.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "95505c38b4572b2d910cecb0281560f54b440a19336cbbcb27bf6ce6adc6f5a8" + +[[package]] +name = "hex" +version = "0.4.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7f24254aa9a54b5c858eaee2f5bccdb46aaf0e486a595ed5fd8f86ba55232a70" + +[[package]] +name = "hmac" +version = "0.12.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "6c49c37c09c17a53d937dfbb742eb3a961d65a994e6bcdcf37e7399d0cc8ab5e" +dependencies = [ + "digest", +] + +[[package]] +name = "humantime" +version = "2.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9a3a5bfb195931eeb336b2a7b4d761daec841b97f947d34394601737a7bba5e4" + +[[package]] +name = "iana-time-zone" +version = "0.1.59" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b6a67363e2aa4443928ce15e57ebae94fd8949958fd1223c4cfc0cd473ad7539" +dependencies = [ + "android_system_properties", + "core-foundation-sys", + "iana-time-zone-haiku", + "js-sys", + "wasm-bindgen", + "windows-core", +] + +[[package]] +name = "iana-time-zone-haiku" +version = "0.1.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f31827a206f56af32e590ba56d5d2d085f558508192593743f16b2306495269f" +dependencies = [ + "cc", +] + +[[package]] +name = "ident_case" +version = "1.0.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b9e0384b61958566e926dc50660321d12159025e767c18e043daf26b70104c39" + +[[package]] +name = "im" +version = "15.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d0acd33ff0285af998aaf9b57342af478078f53492322fafc47450e09397e0e9" +dependencies = [ + "bitmaps", + "rand_core", + "rand_xoshiro", + "serde", + "sized-chunks", + "typenum", + "version_check", +] + +[[package]] +name = "indexmap" +version = "1.9.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "bd070e393353796e801d209ad339e89596eb4c8d430d18ede6a1cced8fafbd99" +dependencies = [ + "autocfg", + "hashbrown 0.12.3", + "serde", +] + +[[package]] +name = "indexmap" +version = "2.2.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "433de089bd45971eecf4668ee0ee8f4cec17db4f8bd8f7bc3197a6ce37aa7d9b" +dependencies = [ + "equivalent", + "hashbrown 0.14.3", + "serde", +] + +[[package]] +name = "iter-extended" +version = "0.23.0" + +[[package]] +name = "itertools" +version = "0.10.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b0fd2260e829bddf4cb6ea802289de2f86d6a7a690192fbe91b3f46e0f2c8473" +dependencies = [ + "either", +] + +[[package]] +name = "itoa" +version = "1.0.10" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b1a46d1a171d865aa5f83f92695765caa047a9b4cbae2cbf37dbd613a793fd4c" + +[[package]] +name = "js-sys" +version = "0.3.67" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9a1d36f1235bc969acba30b7f5990b864423a6068a10f7c90ae8f0112e3a59d1" +dependencies = [ + "wasm-bindgen", +] + +[[package]] +name = "jsonrpc" +version = "0.16.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "34efde8d2422fb79ed56db1d3aea8fa5b583351d15a26770cdee2f88813dd702" +dependencies = [ + "base64 0.13.1", + "minreq", + "serde", + "serde_json", +] + +[[package]] +name = "k256" +version = "0.11.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "72c1e0b51e7ec0a97369623508396067a486bd0cbed95a2659a4b863d28cfc8b" +dependencies = [ + "cfg-if", + "ecdsa", + "elliptic-curve", + "sha2", +] + +[[package]] +name = "keccak" +version = "0.1.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ecc2af9a1119c51f12a14607e783cb977bde58bc069ff0c3da1095e635d70654" +dependencies = [ + "cpufeatures", +] + +[[package]] +name = "libc" +version = "0.2.153" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9c198f91728a82281a64e1f4f9eeb25d82cb32a5de251c6bd1b5154d63a8e7bd" + +[[package]] +name = "log" +version = "0.4.20" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b5e6163cb8c49088c2c36f57875e58ccd8c87c7427f7fbd50ea6710b2f3f2e8f" + +[[package]] +name = "memchr" +version = "2.7.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "523dc4f511e55ab87b694dc30d0f820d60906ef06413f93d4d7a1385599cc149" + +[[package]] +name = "miniz_oxide" +version = "0.7.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e7810e0be55b428ada41041c41f32c9f1a42817901b4ccf45fa3d4b6561e74c7" +dependencies = [ + "adler", +] + +[[package]] +name = "minreq" +version = "2.11.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "cb3371dfc7b772c540da1380123674a8e20583aca99907087d990ca58cf44203" +dependencies = [ + "log", + "serde", + "serde_json", +] + +[[package]] +name = "noirc_abi" +version = "0.23.0" +dependencies = [ + "acvm", + "iter-extended", + "noirc_frontend", + "num-bigint", + "num-traits", + "serde", + "serde_json", + "thiserror", + "toml", +] + +[[package]] +name = "noirc_driver" +version = "0.23.0" +dependencies = [ + "acvm", + "aztec_macros", + "build-data", + "clap", + "fm", + "fxhash", + "iter-extended", + "noirc_abi", + "noirc_errors", + "noirc_evaluator", + "noirc_frontend", + "rust-embed", + "serde", + "tracing", +] + +[[package]] +name = "noirc_errors" +version = "0.23.0" +dependencies = [ + "acvm", + "base64 0.21.7", + "chumsky", + "codespan", + "codespan-reporting", + "flate2", + "fm", + "serde", + "serde_json", + "serde_with", + "tracing", +] + +[[package]] +name = "noirc_evaluator" +version = "0.23.0" +dependencies = [ + "acvm", + "fxhash", + "im", + "iter-extended", + "noirc_errors", + "noirc_frontend", + "num-bigint", + "serde", + "thiserror", + "tracing", +] + +[[package]] +name = "noirc_frontend" +version = "0.23.0" +dependencies = [ + "acvm", + "arena", + "chumsky", + "fm", + "iter-extended", + "noirc_errors", + "noirc_printable_type", + "regex", + "rustc-hash", + "serde", + "serde_json", + "small-ord-set", + "smol_str", + "thiserror", + "tracing", +] + +[[package]] +name = "noirc_printable_type" +version = "0.23.0" +dependencies = [ + "acvm", + "iter-extended", + "jsonrpc", + "regex", + "serde", + "serde_json", + "thiserror", +] + +[[package]] +name = "num-bigint" +version = "0.4.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "608e7659b5c3d7cba262d894801b9ec9d00de989e8a82bd4bef91d08da45cdc0" +dependencies = [ + "autocfg", + "num-integer", + "num-traits", +] + +[[package]] +name = "num-integer" +version = "0.1.45" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "225d3389fb3509a24c93f5c29eb6bde2586b98d9f016636dff58d7c6f7569cd9" +dependencies = [ + "autocfg", + "num-traits", +] + +[[package]] +name = "num-traits" +version = "0.2.17" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "39e3200413f237f41ab11ad6d161bc7239c84dcb631773ccd7de3dfe4b5c267c" +dependencies = [ + "autocfg", +] + +[[package]] +name = "once_cell" +version = "1.19.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3fdb12b2476b595f9358c5161aa467c2438859caa136dec86c26fdd2efe17b92" + +[[package]] +name = "p256" +version = "0.11.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "51f44edd08f51e2ade572f141051021c5af22677e42b7dd28a88155151c33594" +dependencies = [ + "ecdsa", + "elliptic-curve", + "sha2", +] + +[[package]] +name = "paste" +version = "1.0.14" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "de3145af08024dea9fa9914f381a17b8fc6034dfb00f3a84013f7ff43f29ed4c" + +[[package]] +name = "pin-project-lite" +version = "0.2.13" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8afb450f006bf6385ca15ef45d71d2288452bc3683ce2e2cacc0d18e4be60b58" + +[[package]] +name = "pkcs8" +version = "0.9.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9eca2c590a5f85da82668fa685c09ce2888b9430e83299debf1f34b65fd4a4ba" +dependencies = [ + "der", + "spki", +] + +[[package]] +name = "powerfmt" +version = "0.2.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "439ee305def115ba05938db6eb1644ff94165c5ab5e9420d1c1bcedbba909391" + +[[package]] +name = "ppv-lite86" +version = "0.2.17" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5b40af805b3121feab8a3c29f04d8ad262fa8e0561883e7653e024ae4479e6de" + +[[package]] +name = "proc-macro2" +version = "1.0.78" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e2422ad645d89c99f8f3e6b88a9fdeca7fabeac836b1002371c4367c8f984aae" +dependencies = [ + "unicode-ident", +] + +[[package]] +name = "quote" +version = "1.0.35" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "291ec9ab5efd934aaf503a6466c5d5251535d108ee747472c3977cc5acc868ef" +dependencies = [ + "proc-macro2", +] + +[[package]] +name = "rand" +version = "0.8.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "34af8d1a0e25924bc5b7c43c079c942339d8f0a8b57c39049bef581b46327404" +dependencies = [ + "rand_chacha", + "rand_core", +] + +[[package]] +name = "rand_chacha" +version = "0.3.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e6c10a63a0fa32252be49d21e7709d4d4baf8d231c2dbce1eaa8141b9b127d88" +dependencies = [ + "ppv-lite86", + "rand_core", +] + +[[package]] +name = "rand_core" +version = "0.6.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ec0be4795e2f6a28069bec0b5ff3e2ac9bafc99e6a9a7dc3547996c5c816922c" +dependencies = [ + "getrandom", +] + +[[package]] +name = "rand_xoshiro" +version = "0.6.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "6f97cdb2a36ed4183de61b2f824cc45c9f1037f28afe0a322e9fff4c108b5aaa" +dependencies = [ + "rand_core", +] + +[[package]] +name = "regex" +version = "1.10.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b62dbe01f0b06f9d8dc7d49e05a0785f153b00b2c227856282f671e0318c9b15" +dependencies = [ + "aho-corasick", + "memchr", + "regex-automata", + "regex-syntax", +] + +[[package]] +name = "regex-automata" +version = "0.4.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5bb987efffd3c6d0d8f5f89510bb458559eab11e4f869acb20bf845e016259cd" +dependencies = [ + "aho-corasick", + "memchr", + "regex-syntax", +] + +[[package]] +name = "regex-syntax" +version = "0.8.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c08c74e62047bb2de4ff487b251e4a92e24f48745648451635cec7d591162d9f" + +[[package]] +name = "rfc6979" +version = "0.3.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7743f17af12fa0b03b803ba12cd6a8d9483a587e89c69445e3909655c0b9fabb" +dependencies = [ + "crypto-bigint", + "hmac", + "zeroize", +] + +[[package]] +name = "rust-embed" +version = "6.8.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a36224c3276f8c4ebc8c20f158eca7ca4359c8db89991c4925132aaaf6702661" +dependencies = [ + "rust-embed-impl", + "rust-embed-utils", + "walkdir", +] + +[[package]] +name = "rust-embed-impl" +version = "6.8.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "49b94b81e5b2c284684141a2fb9e2a31be90638caf040bf9afbc5a0416afe1ac" +dependencies = [ + "proc-macro2", + "quote", + "rust-embed-utils", + "syn 2.0.48", + "walkdir", +] + +[[package]] +name = "rust-embed-utils" +version = "7.8.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9d38ff6bf570dc3bb7100fce9f7b60c33fa71d80e88da3f2580df4ff2bdded74" +dependencies = [ + "sha2", + "walkdir", +] + +[[package]] +name = "rustc-hash" +version = "1.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "08d43f7aa6b08d49f382cde6a7982047c3426db949b1424bc4b7ec9ae12c6ce2" + +[[package]] +name = "rustc_version" +version = "0.4.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "bfa0f585226d2e68097d4f95d113b15b83a82e819ab25717ec0590d9584ef366" +dependencies = [ + "semver", +] + +[[package]] +name = "ryu" +version = "1.0.16" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f98d2aa92eebf49b69786be48e4477826b256916e84a57ff2a4f21923b48eb4c" + +[[package]] +name = "safe-lock" +version = "0.1.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "077d73db7973cccf63eb4aff1e5a34dc2459baa867512088269ea5f2f4253c90" + +[[package]] +name = "safe-proc-macro2" +version = "1.0.67" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7fd85be67db87168aa3c13fd0da99f48f2ab005dccad5af5626138dc1df20eb6" +dependencies = [ + "unicode-ident", +] + +[[package]] +name = "safe-quote" +version = "1.0.15" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "77e530f7831f3feafcd5f1aae406ac205dd998436b4007c8e80f03eca78a88f7" +dependencies = [ + "safe-proc-macro2", +] + +[[package]] +name = "safe-regex" +version = "0.2.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a15289bf322e0673d52756a18194167f2378ec1a15fe884af6e2d2cb934822b0" +dependencies = [ + "safe-regex-macro", +] + +[[package]] +name = "safe-regex-compiler" +version = "0.2.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "fba76fae590a2aa665279deb1f57b5098cbace01a0c5e60e262fcf55f7c51542" +dependencies = [ + "safe-proc-macro2", + "safe-quote", +] + +[[package]] +name = "safe-regex-macro" +version = "0.2.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "96c2e96b5c03f158d1b16ba79af515137795f4ad4e8de3f790518aae91f1d127" +dependencies = [ + "safe-proc-macro2", + "safe-regex-compiler", +] + +[[package]] +name = "same-file" +version = "1.0.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "93fc1dc3aaa9bfed95e02e6eadabb4baf7e3078b0bd1b4d7b6b0b68378900502" +dependencies = [ + "winapi-util", +] + +[[package]] +name = "sec1" +version = "0.3.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3be24c1842290c45df0a7bf069e0c268a747ad05a192f2fd7dcfdbc1cba40928" +dependencies = [ + "base16ct", + "der", + "generic-array", + "pkcs8", + "subtle", + "zeroize", +] + +[[package]] +name = "semver" +version = "1.0.21" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b97ed7a9823b74f99c7742f5336af7be5ecd3eeafcb1507d1fa93347b1d589b0" + +[[package]] +name = "serde" +version = "1.0.196" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "870026e60fa08c69f064aa766c10f10b1d62db9ccd4d0abb206472bee0ce3b32" +dependencies = [ + "serde_derive", +] + +[[package]] +name = "serde_derive" +version = "1.0.196" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "33c85360c95e7d137454dc81d9a4ed2b8efd8fbe19cee57357b32b9771fccb67" +dependencies = [ + "proc-macro2", + "quote", + "syn 2.0.48", +] + +[[package]] +name = "serde_json" +version = "1.0.113" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "69801b70b1c3dac963ecb03a364ba0ceda9cf60c71cfe475e99864759c8b8a79" +dependencies = [ + "itoa", + "ryu", + "serde", +] + +[[package]] +name = "serde_spanned" +version = "0.6.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "eb3622f419d1296904700073ea6cc23ad690adbd66f13ea683df73298736f0c1" +dependencies = [ + "serde", +] + +[[package]] +name = "serde_with" +version = "3.6.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1b0ed1662c5a68664f45b76d18deb0e234aff37207086803165c961eb695e981" +dependencies = [ + "base64 0.21.7", + "chrono", + "hex", + "indexmap 1.9.3", + "indexmap 2.2.1", + "serde", + "serde_json", + "serde_with_macros", + "time", +] + +[[package]] +name = "serde_with_macros" +version = "3.6.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "568577ff0ef47b879f736cd66740e022f3672788cdf002a05a4e609ea5a6fb15" +dependencies = [ + "darling", + "proc-macro2", + "quote", + "syn 2.0.48", +] + +[[package]] +name = "sha2" +version = "0.10.8" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "793db75ad2bcafc3ffa7c68b215fee268f537982cd901d132f89c6343f3a3dc8" +dependencies = [ + "cfg-if", + "cpufeatures", + "digest", +] + +[[package]] +name = "sha3" +version = "0.10.8" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "75872d278a8f37ef87fa0ddbda7802605cb18344497949862c0d4dcb291eba60" +dependencies = [ + "digest", + "keccak", +] + +[[package]] +name = "signature" +version = "1.6.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "74233d3b3b2f6d4b006dc19dee745e73e2a6bfb6f93607cd3b02bd5b00797d7c" +dependencies = [ + "digest", + "rand_core", +] + +[[package]] +name = "sized-chunks" +version = "0.6.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "16d69225bde7a69b235da73377861095455d298f2b970996eec25ddbb42b3d1e" +dependencies = [ + "bitmaps", + "typenum", +] + +[[package]] +name = "small-ord-set" +version = "0.1.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "cf7035a2b2268a5be8c1395738565b06beda836097e12021cdefc06b127a0e7e" +dependencies = [ + "smallvec", +] + +[[package]] +name = "smallvec" +version = "1.13.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e6ecd384b10a64542d77071bd64bd7b231f4ed5940fba55e98c3de13824cf3d7" + +[[package]] +name = "smol_str" +version = "0.1.24" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "fad6c857cbab2627dcf01ec85a623ca4e7dcb5691cbaa3d7fb7653671f0d09c9" +dependencies = [ + "serde", +] + +[[package]] +name = "spki" +version = "0.6.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "67cf02bbac7a337dc36e4f5a693db6c21e7863f45070f7064577eb4367a3212b" +dependencies = [ + "base64ct", + "der", +] + +[[package]] +name = "strsim" +version = "0.10.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "73473c0e59e6d5812c5dfe2a064a6444949f089e20eec9a2e5506596494e4623" + +[[package]] +name = "subtle" +version = "2.5.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "81cdd64d312baedb58e21336b31bc043b77e01cc99033ce76ef539f78e965ebc" + +[[package]] +name = "syn" +version = "1.0.109" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "72b64191b275b66ffe2469e8af2c1cfe3bafa67b529ead792a6d0160888b4237" +dependencies = [ + "proc-macro2", + "quote", + "unicode-ident", +] + +[[package]] +name = "syn" +version = "2.0.48" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0f3531638e407dfc0814761abb7c00a5b54992b849452a0646b7f65c9f770f3f" +dependencies = [ + "proc-macro2", + "quote", + "unicode-ident", +] + +[[package]] +name = "termcolor" +version = "1.4.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "06794f8f6c5c898b3275aebefa6b8a1cb24cd2c6c79397ab15774837a0bc5755" +dependencies = [ + "winapi-util", +] + +[[package]] +name = "thiserror" +version = "1.0.56" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d54378c645627613241d077a3a79db965db602882668f9136ac42af9ecb730ad" +dependencies = [ + "thiserror-impl", +] + +[[package]] +name = "thiserror-impl" +version = "1.0.56" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "fa0faa943b50f3db30a20aa7e265dbc66076993efed8463e8de414e5d06d3471" +dependencies = [ + "proc-macro2", + "quote", + "syn 2.0.48", +] + +[[package]] +name = "time" +version = "0.3.31" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f657ba42c3f86e7680e53c8cd3af8abbe56b5491790b46e22e19c0d57463583e" +dependencies = [ + "deranged", + "itoa", + "powerfmt", + "serde", + "time-core", + "time-macros", +] + +[[package]] +name = "time-core" +version = "0.1.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ef927ca75afb808a4d64dd374f00a2adf8d0fcff8e7b184af886c3c87ec4a3f3" + +[[package]] +name = "time-macros" +version = "0.2.16" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "26197e33420244aeb70c3e8c78376ca46571bc4e701e4791c2cd9f57dcb3a43f" +dependencies = [ + "time-core", +] + +[[package]] +name = "toml" +version = "0.7.8" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "dd79e69d3b627db300ff956027cc6c3798cef26d22526befdfcd12feeb6d2257" +dependencies = [ + "serde", + "serde_spanned", + "toml_datetime", + "toml_edit", +] + +[[package]] +name = "toml_datetime" +version = "0.6.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3550f4e9685620ac18a50ed434eb3aec30db8ba93b0287467bca5826ea25baf1" +dependencies = [ + "serde", +] + +[[package]] +name = "toml_edit" +version = "0.19.15" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1b5bb770da30e5cbfde35a2d7b9b8a2c4b8ef89548a7a6aeab5c9a576e3e7421" +dependencies = [ + "indexmap 2.2.1", + "serde", + "serde_spanned", + "toml_datetime", + "winnow", +] + +[[package]] +name = "tracing" +version = "0.1.40" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c3523ab5a71916ccf420eebdf5521fcef02141234bbc0b8a49f2fdc4544364ef" +dependencies = [ + "pin-project-lite", + "tracing-attributes", + "tracing-core", +] + +[[package]] +name = "tracing-attributes" +version = "0.1.27" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "34704c8d6ebcbc939824180af020566b01a7c01f80641264eba0999f6c2b6be7" +dependencies = [ + "proc-macro2", + "quote", + "syn 2.0.48", +] + +[[package]] +name = "tracing-core" +version = "0.1.32" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c06d3da6113f116aaee68e4d601191614c9053067f9ab7f6edbcb161237daa54" +dependencies = [ + "once_cell", +] + +[[package]] +name = "typenum" +version = "1.17.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "42ff0bf0c66b8238c6f3b578df37d0b7848e55df8577b3f74f92a69acceeb825" + +[[package]] +name = "unicode-ident" +version = "1.0.12" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3354b9ac3fae1ff6755cb6db53683adb661634f67557942dea4facebec0fee4b" + +[[package]] +name = "unicode-width" +version = "0.1.11" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e51733f11c9c4f72aa0c160008246859e340b00807569a0da0e7a1079b27ba85" + +[[package]] +name = "utf8parse" +version = "0.2.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "711b9620af191e0cdc7468a8d14e709c3dcdb115b36f838e601583af800a370a" + +[[package]] +name = "version_check" +version = "0.9.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "49874b5167b65d7193b8aba1567f5c7d93d001cafc34600cee003eda787e483f" + +[[package]] +name = "walkdir" +version = "2.4.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d71d857dc86794ca4c280d616f7da00d2dbfd8cd788846559a6813e6aa4b54ee" +dependencies = [ + "same-file", + "winapi-util", +] + +[[package]] +name = "wasi" +version = "0.11.0+wasi-snapshot-preview1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9c8d87e72b64a3b4db28d11ce29237c246188f4f51057d65a7eab63b7987e423" + +[[package]] +name = "wasm-bindgen" +version = "0.2.90" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b1223296a201415c7fad14792dbefaace9bd52b62d33453ade1c5b5f07555406" +dependencies = [ + "cfg-if", + "wasm-bindgen-macro", +] + +[[package]] +name = "wasm-bindgen-backend" +version = "0.2.90" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "fcdc935b63408d58a32f8cc9738a0bffd8f05cc7c002086c6ef20b7312ad9dcd" +dependencies = [ + "bumpalo", + "log", + "once_cell", + "proc-macro2", + "quote", + "syn 2.0.48", + "wasm-bindgen-shared", +] + +[[package]] +name = "wasm-bindgen-macro" +version = "0.2.90" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3e4c238561b2d428924c49815533a8b9121c664599558a5d9ec51f8a1740a999" +dependencies = [ + "quote", + "wasm-bindgen-macro-support", +] + +[[package]] +name = "wasm-bindgen-macro-support" +version = "0.2.90" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "bae1abb6806dc1ad9e560ed242107c0f6c84335f1749dd4e8ddb012ebd5e25a7" +dependencies = [ + "proc-macro2", + "quote", + "syn 2.0.48", + "wasm-bindgen-backend", + "wasm-bindgen-shared", +] + +[[package]] +name = "wasm-bindgen-shared" +version = "0.2.90" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "4d91413b1c31d7539ba5ef2451af3f0b833a005eb27a631cec32bc0635a8602b" + +[[package]] +name = "winapi" +version = "0.3.9" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5c839a674fcd7a98952e593242ea400abe93992746761e38641405d28b00f419" +dependencies = [ + "winapi-i686-pc-windows-gnu", + "winapi-x86_64-pc-windows-gnu", +] + +[[package]] +name = "winapi-i686-pc-windows-gnu" +version = "0.4.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ac3b87c63620426dd9b991e5ce0329eff545bccbbb34f3be09ff6fb6ab51b7b6" + +[[package]] +name = "winapi-util" +version = "0.1.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f29e6f9198ba0d26b4c9f07dbe6f9ed633e1f3d5b8b414090084349e46a52596" +dependencies = [ + "winapi", +] + +[[package]] +name = "winapi-x86_64-pc-windows-gnu" +version = "0.4.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "712e227841d057c1ee1cd2fb22fa7e5a5461ae8e48fa2ca79ec42cfc1931183f" + +[[package]] +name = "windows-core" +version = "0.52.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "33ab640c8d7e35bf8ba19b884ba838ceb4fba93a4e8c65a9059d08afcfc683d9" +dependencies = [ + "windows-targets", +] + +[[package]] +name = "windows-sys" +version = "0.52.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "282be5f36a8ce781fad8c8ae18fa3f9beff57ec1b52cb3de0789201425d9a33d" +dependencies = [ + "windows-targets", +] + +[[package]] +name = "windows-targets" +version = "0.52.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8a18201040b24831fbb9e4eb208f8892e1f50a37feb53cc7ff887feb8f50e7cd" +dependencies = [ + "windows_aarch64_gnullvm", + "windows_aarch64_msvc", + "windows_i686_gnu", + "windows_i686_msvc", + "windows_x86_64_gnu", + "windows_x86_64_gnullvm", + "windows_x86_64_msvc", +] + +[[package]] +name = "windows_aarch64_gnullvm" +version = "0.52.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "cb7764e35d4db8a7921e09562a0304bf2f93e0a51bfccee0bd0bb0b666b015ea" + +[[package]] +name = "windows_aarch64_msvc" +version = "0.52.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "bbaa0368d4f1d2aaefc55b6fcfee13f41544ddf36801e793edbbfd7d7df075ef" + +[[package]] +name = "windows_i686_gnu" +version = "0.52.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a28637cb1fa3560a16915793afb20081aba2c92ee8af57b4d5f28e4b3e7df313" + +[[package]] +name = "windows_i686_msvc" +version = "0.52.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ffe5e8e31046ce6230cc7215707b816e339ff4d4d67c65dffa206fd0f7aa7b9a" + +[[package]] +name = "windows_x86_64_gnu" +version = "0.52.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3d6fa32db2bc4a2f5abeacf2b69f7992cd09dca97498da74a151a3132c26befd" + +[[package]] +name = "windows_x86_64_gnullvm" +version = "0.52.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1a657e1e9d3f514745a572a6846d3c7aa7dbe1658c056ed9c3344c4109a6949e" + +[[package]] +name = "windows_x86_64_msvc" +version = "0.52.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "dff9641d1cd4be8d1a070daf9e3773c5f67e78b4d9d42263020c057706765c04" + +[[package]] +name = "winnow" +version = "0.5.36" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "818ce546a11a9986bc24f93d0cdf38a8a1a400f1473ea8c82e59f6e0ffab9249" +dependencies = [ + "memchr", +] + +[[package]] +name = "zerocopy" +version = "0.7.32" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "74d4d3961e53fa4c9a25a8637fc2bfaf2595b3d3ae34875568a5cf64787716be" +dependencies = [ + "zerocopy-derive", +] + +[[package]] +name = "zerocopy-derive" +version = "0.7.32" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9ce1b18ccd8e73a9321186f97e46f9f04b778851177567b1975109d26a08d2a6" +dependencies = [ + "proc-macro2", + "quote", + "syn 2.0.48", +] + +[[package]] +name = "zeroize" +version = "1.7.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "525b4ec142c6b68a2d10f01f7bbf6755599ca3f81ea53b8431b7dd348f5fdb2d" +dependencies = [ + "zeroize_derive", +] + +[[package]] +name = "zeroize_derive" +version = "1.4.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ce36e65b0d2999d2aafac989fb249189a141aee1f53c612c1f37d72631959f69" +dependencies = [ + "proc-macro2", + "quote", + "syn 2.0.48", +] diff --git a/avm-transpiler/Cargo.toml b/avm-transpiler/Cargo.toml new file mode 100644 index 00000000000..9d4f0a001ea --- /dev/null +++ b/avm-transpiler/Cargo.toml @@ -0,0 +1,21 @@ +[package] +name = "avm-transpiler" +version = "0.1.0" +authors = ["The Aztec Team "] +edition = "2021" +license = "MIT OR Apache-2.0" + +# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html + +[dependencies] +# local +acvm = { path = "../noir/acvm-repo/acvm" } +noirc_driver = { path = "../noir/compiler/noirc_driver" } + +# external +base64 = "0.21" +regex = "1.10" +env_logger = "0.11" +log = "0.4" +serde_json = "1.0" +serde = { version = "1.0.136", features = ["derive"]} \ No newline at end of file diff --git a/avm-transpiler/Dockerfile b/avm-transpiler/Dockerfile new file mode 100644 index 00000000000..1f55f8ba40d --- /dev/null +++ b/avm-transpiler/Dockerfile @@ -0,0 +1,13 @@ +FROM rust:bookworm + +WORKDIR /usr/src +COPY ./avm-transpiler ./avm-transpiler +COPY ./noir ./noir + +WORKDIR /usr/src/avm-transpiler +RUN apt-get update && apt-get install -y git +RUN ./scripts/bootstrap_native.sh + +FROM ubuntu:lunar +COPY --from=0 /usr/src/avm-transpiler/target/release/avm-transpiler /usr/src/avm-transpiler/target/release/avm-transpiler +ENTRYPOINT ["sh", "-c"] \ No newline at end of file diff --git a/avm-transpiler/Dockerfile.dockerignore b/avm-transpiler/Dockerfile.dockerignore new file mode 100644 index 00000000000..8edf9d12d62 --- /dev/null +++ b/avm-transpiler/Dockerfile.dockerignore @@ -0,0 +1,7 @@ +** + +!avm-transpiler/ +!noir/ +**/target/ +**/node_modules/ +**/packages/ \ No newline at end of file diff --git a/avm-transpiler/README.md b/avm-transpiler/README.md new file mode 100644 index 00000000000..2cd932c2451 --- /dev/null +++ b/avm-transpiler/README.md @@ -0,0 +1,15 @@ +# AVM Transpiler + +This component transpiles Aztec public contracts code from Noir's Brillig bytecode to AVM (Aztec Virtual Machine) bytecode. + +## Build + +``` +./boostrap.sh +``` + +## Run + +``` +cargo run +``` diff --git a/avm-transpiler/bootstrap.sh b/avm-transpiler/bootstrap.sh new file mode 100755 index 00000000000..916d8107876 --- /dev/null +++ b/avm-transpiler/bootstrap.sh @@ -0,0 +1,19 @@ +#!/usr/bin/env bash +set -eu + +cd $(dirname "$0") + +CMD=${1:-} + +if [ -n "$CMD" ]; then + if [ "$CMD" = "clean" ]; then + cargo clean + git clean -fdx + exit 0 + else + echo "Unknown command: $CMD" + exit 1 + fi +fi + +./scripts/bootstrap_native.sh \ No newline at end of file diff --git a/avm-transpiler/rust-toolchain.toml b/avm-transpiler/rust-toolchain.toml new file mode 100644 index 00000000000..6ed42dc02c2 --- /dev/null +++ b/avm-transpiler/rust-toolchain.toml @@ -0,0 +1,5 @@ +[toolchain] +channel = "1.71.1" +components = [ "rust-src" ] +targets = [] +profile = "default" diff --git a/avm-transpiler/scripts/bootstrap_native.sh b/avm-transpiler/scripts/bootstrap_native.sh new file mode 100755 index 00000000000..3e0e2ed853a --- /dev/null +++ b/avm-transpiler/scripts/bootstrap_native.sh @@ -0,0 +1,20 @@ +#!/usr/bin/env bash +set -eu + +cd $(dirname "$0")/.. + +# If this project has been subrepod into another project, set build data manually. +export SOURCE_DATE_EPOCH=$(date +%s) +export GIT_DIRTY=false +if [ -f ".gitrepo" ]; then + export GIT_COMMIT=$(awk '/commit =/ {print $3}' .gitrepo) +else + export GIT_COMMIT=$(git rev-parse --verify HEAD) +fi + +# Build native. +if [ -n "${DEBUG:-}" ]; then + cargo build +else + cargo build --release +fi diff --git a/avm-transpiler/src/instructions.rs b/avm-transpiler/src/instructions.rs new file mode 100644 index 00000000000..efc195cc5b0 --- /dev/null +++ b/avm-transpiler/src/instructions.rs @@ -0,0 +1,115 @@ +use crate::opcodes::AvmOpcode; + +/// Common values of the indirect instruction flag +pub const ZEROTH_OPERAND_INDIRECT: u8 = 0b00000001; +pub const FIRST_OPERAND_INDIRECT: u8 = 0b00000010; +pub const ZEROTH_FIRST_OPERANDS_INDIRECT: u8 = 0b00000011; + +/// A simple representation of an AVM instruction for the purpose +/// of generating an AVM bytecode from Brillig. +/// Note: this does structure not impose rules like "ADD instruction must have 3 operands" +/// That job is left to the instruction decoder, not this thin transpiler. +pub struct AvmInstruction { + pub opcode: AvmOpcode, + + /// Any instructions with memory offset operands have the indirect flag + /// Each bit is a boolean: 0:direct, 1:indirect + /// The 0th bit corresponds to an instruction's 0th offset arg, 1st to 1st, etc... + pub indirect: Option, + + /// Some instructions have a destination or input tag + // TODO(4271): add in_tag alongside its support in TS + //pub in_tag: Option, + pub dst_tag: Option, + + /// Different instructions have different numbers of operands + pub operands: Vec, +} +impl AvmInstruction { + /// String representation for printing AVM programs + pub fn to_string(&self) -> String { + let mut out_str = format!("opcode {}", self.opcode.name()); + if let Some(indirect) = self.indirect { + out_str += format!(", indirect: {}", indirect).as_str(); + } + // TODO(4271): add in_tag alongside its support in TS + if let Some(dst_tag) = self.dst_tag { + out_str += format!(", dst_tag: {}", dst_tag as u8).as_str(); + } + if self.operands.len() > 0 { + out_str += ", operands: ["; + for operand in &self.operands { + out_str += format!("{}, ", operand.to_string()).as_str(); + } + out_str += "]"; + } + out_str + } + /// Bytes representation for generating AVM bytecode + pub fn to_bytes(&self) -> Vec { + let mut bytes = Vec::new(); + bytes.push(self.opcode as u8); + if let Some(indirect) = self.indirect { + bytes.push(indirect); + } + // TODO(4271): add in_tag alongside its support in TS + if let Some(dst_tag) = self.dst_tag { + // TODO(4271): make 8 bits when TS supports deserialization of 8 bit flags + //bytes.push(dst_tag as u8); + bytes.extend_from_slice(&(dst_tag as u32).to_be_bytes()); + } + for operand in &self.operands { + bytes.extend_from_slice(&operand.to_be_bytes()); + } + bytes + } +} +impl Default for AvmInstruction { + fn default() -> Self { + AvmInstruction { + opcode: AvmOpcode::ADD, + // TODO(4266): default to Some(0), since all instructions have indirect flag except jumps + indirect: None, + dst_tag: None, + operands: vec![], + } + } +} + +/// AVM instructions may include a type tag +#[derive(Copy, Clone)] +pub enum AvmTypeTag { + UNINITIALIZED, + UINT8, + UINT16, + UINT32, + UINT64, + UINT128, + FIELD, + INVALID, +} + +/// Operands are usually 32 bits (offsets or jump destinations) +/// Constants (as used by the SET instruction) can have size +/// different from 32 bits +pub enum AvmOperand { + U32 { value: u32 }, + // TODO(4267): Support operands of size other than 32 bits (for SET) + //U128 { value: u128 }, +} +impl AvmOperand { + pub fn to_string(&self) -> String { + match self { + AvmOperand::U32 { value } => format!(" U32:{}", value), + // TODO(4267): Support operands of size other than 32 bits (for SET) + //AvmOperand::U128 { value } => format!("U128:{}", value), + } + } + pub fn to_be_bytes(&self) -> Vec { + match self { + AvmOperand::U32 { value } => value.to_be_bytes().to_vec(), + // TODO(4267): Support operands of size other than 32 bits (for SET) + //AvmOperand::U128 { value } => value.to_be_bytes().to_vec(), + } + } +} diff --git a/avm-transpiler/src/main.rs b/avm-transpiler/src/main.rs new file mode 100644 index 00000000000..064bf343bbf --- /dev/null +++ b/avm-transpiler/src/main.rs @@ -0,0 +1,46 @@ +use log::warn; +use std::env; +use std::fs; +use std::path::Path; + +mod instructions; +mod opcodes; +mod transpile; +mod transpile_contract; +mod utils; + +use transpile_contract::{CompiledAcirContract, TranspiledContract}; + +fn main() { + env_logger::init(); + + let args: Vec = env::args().collect(); + let in_contract_artifact_path = &args[1]; + let out_transpiled_artifact_path = &args[2]; + + // Parse original (pre-transpile) contract + let contract_json = + fs::read_to_string(Path::new(in_contract_artifact_path)).expect("Unable to read file"); + let raw_json_obj: serde_json::Value = + serde_json::from_str(&contract_json).expect("Unable to parse json"); + + // Skip if contract has "transpiled: true" flag! + if let Some(transpiled) = raw_json_obj.get("transpiled") { + match transpiled { + serde_json::Value::Bool(true) => { + warn!("Contract already transpiled. Skipping."); + return; // nothing to transpile + } + _ => (), + } + } + // Parse json into contract object + let contract: CompiledAcirContract = + serde_json::from_str(&contract_json).expect("Unable to parse json"); + + // Transpile contract to AVM bytecode + let transpiled_contract = TranspiledContract::from(contract); + let transpiled_json = + serde_json::to_string(&transpiled_contract).expect("Unable to serialize json"); + fs::write(out_transpiled_artifact_path, transpiled_json).expect("Unable to write file"); +} diff --git a/avm-transpiler/src/opcodes.rs b/avm-transpiler/src/opcodes.rs new file mode 100644 index 00000000000..cc3c828d2ec --- /dev/null +++ b/avm-transpiler/src/opcodes.rs @@ -0,0 +1,168 @@ +/// All AVM opcodes +/// Keep updated with TS and yellow paper! +#[derive(Copy, Clone)] +pub enum AvmOpcode { + // Compute + // Compute - Arithmetic + ADD, + SUB, + MUL, + DIV, + // Compute - Comparators + EQ, + LT, + LTE, + // Compute - Bitwise + AND, + OR, + XOR, + NOT, + SHL, + SHR, + // Compute - Type Conversions + CAST, + + // Execution Environment + ADDRESS, + STORAGEADDRESS, + ORIGIN, + SENDER, + PORTAL, + FEEPERL1GAS, + FEEPERL2GAS, + FEEPERDAGAS, + CONTRACTCALLDEPTH, + // Execution Environment - Globals + CHAINID, + VERSION, + BLOCKNUMBER, + TIMESTAMP, + COINBASE, + BLOCKL1GASLIMIT, + BLOCKL2GASLIMIT, + BLOCKDAGASLIMIT, + // Execution Environment - Calldata + CALLDATACOPY, + + // Machine State + // Machine State - Gas + L1GASLEFT, + L2GASLEFT, + DAGASLEFT, + // Machine State - Internal Control Flow + JUMP, + JUMPI, + INTERNALCALL, + INTERNALRETURN, + // Machine State - Memory + SET, + MOV, + CMOV, + + // World State + BLOCKHEADERBYNUMBER, + SLOAD, // Public Storage + SSTORE, // Public Storage + READL1TOL2MSG, // Messages + SENDL2TOL1MSG, // Messages + EMITNOTEHASH, // Notes & Nullifiers + EMITNULLIFIER, // Notes & Nullifiers + + // Accrued Substate + EMITUNENCRYPTEDLOG, + + // Control Flow - Contract Calls + CALL, + STATICCALL, + RETURN, + REVERT, + + // Gadgets + KECCAK, + POSEIDON, +} + +impl AvmOpcode { + pub fn name(&self) -> &'static str { + match self { + // Compute + // Compute - Arithmetic + AvmOpcode::ADD => "ADD", + AvmOpcode::SUB => "SUB", + AvmOpcode::MUL => "MUL", + AvmOpcode::DIV => "DIV", + // Compute - Comparators + AvmOpcode::EQ => "EQ", + AvmOpcode::LT => "LT", + AvmOpcode::LTE => "LTE", + // Compute - Bitwise + AvmOpcode::AND => "AND", + AvmOpcode::OR => "OR", + AvmOpcode::XOR => "XOR", + AvmOpcode::NOT => "NOT", + AvmOpcode::SHL => "SHL", + AvmOpcode::SHR => "SHR", + // Compute - Type Conversions + AvmOpcode::CAST => "CAST", + + // Execution Environment + AvmOpcode::ADDRESS => "ADDRESS", + AvmOpcode::STORAGEADDRESS => "STORAGEADDRESS", + AvmOpcode::ORIGIN => "ORIGIN", + AvmOpcode::SENDER => "SENDER", + AvmOpcode::PORTAL => "PORTAL", + AvmOpcode::FEEPERL1GAS => "FEEPERL1GAS", + AvmOpcode::FEEPERL2GAS => "FEEPERL2GAS", + AvmOpcode::FEEPERDAGAS => "FEEPERDAGAS", + AvmOpcode::CONTRACTCALLDEPTH => "CONTRACTCALLDEPTH", + // Execution Environment - Globals + AvmOpcode::CHAINID => "CHAINID", + AvmOpcode::VERSION => "VERSION", + AvmOpcode::BLOCKNUMBER => "BLOCKNUMBER", + AvmOpcode::TIMESTAMP => "TIMESTAMP", + AvmOpcode::COINBASE => "COINBASE", + AvmOpcode::BLOCKL1GASLIMIT => "BLOCKL1GASLIMIT", + AvmOpcode::BLOCKL2GASLIMIT => "BLOCKL2GASLIMIT", + AvmOpcode::BLOCKDAGASLIMIT => "BLOCKDAGASLIMIT", + // Execution Environment - Calldata + AvmOpcode::CALLDATACOPY => "CALLDATACOPY", + + // Machine State + // Machine State - Gas + AvmOpcode::L1GASLEFT => "L1GASLEFT", + AvmOpcode::L2GASLEFT => "L2GASLEFT", + AvmOpcode::DAGASLEFT => "DAGASLEFT", + // Machine State - Internal Control Flow + AvmOpcode::JUMP => "JUMP", + AvmOpcode::JUMPI => "JUMPI", + AvmOpcode::INTERNALCALL => "INTERNALCALL", + AvmOpcode::INTERNALRETURN => "INTERNALRETURN", + // Machine State - Memory + AvmOpcode::SET => "SET", + AvmOpcode::MOV => "MOV", + AvmOpcode::CMOV => "CMOV", + + // World State + AvmOpcode::BLOCKHEADERBYNUMBER => "BLOCKHEADERBYNUMBER", + AvmOpcode::SLOAD => "SLOAD", // Public Storage + AvmOpcode::SSTORE => "SSTORE", // Public Storage + AvmOpcode::READL1TOL2MSG => "READL1TOL2MSG", // Messages + AvmOpcode::SENDL2TOL1MSG => "SENDL2TOL1MSG", // Messages + AvmOpcode::EMITNOTEHASH => "EMITNOTEHASH", // Notes & Nullifiers + AvmOpcode::EMITNULLIFIER => "EMITNULLIFIER", // Notes & Nullifiers + + // Accrued Substate + AvmOpcode::EMITUNENCRYPTEDLOG => "EMITUNENCRYPTEDLOG", + + // Control Flow - Contract Calls + AvmOpcode::CALL => "CALL", + AvmOpcode::STATICCALL => "STATICCALL", + AvmOpcode::RETURN => "RETURN", + AvmOpcode::REVERT => "REVERT", + + // Gadgets + AvmOpcode::KECCAK => "KECCAK", + AvmOpcode::POSEIDON => "POSEIDON", + } + } +} diff --git a/avm-transpiler/src/transpile.rs b/avm-transpiler/src/transpile.rs new file mode 100644 index 00000000000..9785e41c7a1 --- /dev/null +++ b/avm-transpiler/src/transpile.rs @@ -0,0 +1,289 @@ +use acvm::acir::brillig::Opcode as BrilligOpcode; +use acvm::acir::circuit::brillig::Brillig; + +use acvm::brillig_vm::brillig::{BinaryFieldOp, BinaryIntOp}; + +use crate::instructions::{ + AvmInstruction, AvmOperand, AvmTypeTag, FIRST_OPERAND_INDIRECT, ZEROTH_OPERAND_INDIRECT, +}; +use crate::opcodes::AvmOpcode; +use crate::utils::{dbg_print_avm_program, dbg_print_brillig_program}; + +/// Transpile a Brillig program to AVM bytecode +pub fn brillig_to_avm(brillig: &Brillig) -> Vec { + dbg_print_brillig_program(&brillig); + + let mut avm_instrs: Vec = Vec::new(); + + // Map Brillig pcs to AVM pcs + // (some Brillig instructions map to >1 AVM instruction) + let brillig_pcs_to_avm_pcs = map_brillig_pcs_to_avm_pcs(avm_instrs.len(), brillig); + + // Transpile a Brillig instruction to one or more AVM instructions + for brillig_instr in &brillig.bytecode { + match brillig_instr { + BrilligOpcode::BinaryFieldOp { + destination, + op, + lhs, + rhs, + } => { + let avm_opcode = match op { + BinaryFieldOp::Add => AvmOpcode::ADD, + BinaryFieldOp::Sub => AvmOpcode::SUB, + BinaryFieldOp::Mul => AvmOpcode::MUL, + BinaryFieldOp::Div => AvmOpcode::DIV, + BinaryFieldOp::Equals => AvmOpcode::EQ, + }; + // TODO(4268): set in_tag to `field` + avm_instrs.push(AvmInstruction { + opcode: avm_opcode, + operands: vec![ + AvmOperand::U32 { + value: lhs.to_usize() as u32, + }, + AvmOperand::U32 { + value: rhs.to_usize() as u32, + }, + AvmOperand::U32 { + value: destination.to_usize() as u32, + }, + ], + ..Default::default() + }); + } + BrilligOpcode::BinaryIntOp { + destination, + op, + bit_size: _, // TODO(4268): support u8..u128 and use in_tag + lhs, + rhs, + } => { + let avm_opcode = match op { + BinaryIntOp::Add => AvmOpcode::ADD, + BinaryIntOp::Sub => AvmOpcode::SUB, + BinaryIntOp::Mul => AvmOpcode::MUL, + BinaryIntOp::UnsignedDiv => AvmOpcode::DIV, + BinaryIntOp::Equals => AvmOpcode::EQ, + BinaryIntOp::LessThan => AvmOpcode::LT, + BinaryIntOp::LessThanEquals => AvmOpcode::LTE, + BinaryIntOp::And => AvmOpcode::AND, + BinaryIntOp::Or => AvmOpcode::OR, + BinaryIntOp::Xor => AvmOpcode::XOR, + BinaryIntOp::Shl => AvmOpcode::SHL, + BinaryIntOp::Shr => AvmOpcode::SHR, + _ => panic!( + "Transpiler doesn't know how to process BinaryIntOp {:?}", + brillig_instr + ), + }; + // TODO(4268): support u8..u128 and use in_tag + avm_instrs.push(AvmInstruction { + opcode: avm_opcode, + operands: vec![ + AvmOperand::U32 { + value: lhs.to_usize() as u32, + }, + AvmOperand::U32 { + value: rhs.to_usize() as u32, + }, + AvmOperand::U32 { + value: destination.to_usize() as u32, + }, + ], + ..Default::default() + }); + } + BrilligOpcode::CalldataCopy { destination_address, size, offset } => { + avm_instrs.push(AvmInstruction { + opcode: AvmOpcode::CALLDATACOPY, + operands: vec![ + AvmOperand::U32 { + value: *offset as u32, // cdOffset (calldata offset) + }, AvmOperand::U32 { + value: *size as u32, + }, AvmOperand::U32 { + value: destination_address.to_usize() as u32, // dstOffset + }], + ..Default::default() + }); + } + BrilligOpcode::Jump { location } => { + let avm_loc = brillig_pcs_to_avm_pcs[*location]; + avm_instrs.push(AvmInstruction { + opcode: AvmOpcode::JUMP, + operands: vec![AvmOperand::U32 { + value: avm_loc as u32, + }], + ..Default::default() + }); + } + BrilligOpcode::JumpIf { + condition, + location, + } => { + let avm_loc = brillig_pcs_to_avm_pcs[*location]; + avm_instrs.push(AvmInstruction { + opcode: AvmOpcode::JUMPI, + operands: vec![ + AvmOperand::U32 { + value: avm_loc as u32, + }, + AvmOperand::U32 { + value: condition.to_usize() as u32, + }, + ], + ..Default::default() + }); + } + BrilligOpcode::Const { destination, value } => { + avm_instrs.push(AvmInstruction { + opcode: AvmOpcode::SET, + dst_tag: Some(AvmTypeTag::UINT32), + operands: vec![ + // TODO(4267): support u8..u128 and use dst_tag + AvmOperand::U32 { + value: value.to_usize() as u32, + }, + AvmOperand::U32 { + value: destination.to_usize() as u32, + }, + ], + ..Default::default() + }); + } + BrilligOpcode::Mov { + destination, + source, + } => { + avm_instrs.push(AvmInstruction { + opcode: AvmOpcode::MOV, + operands: vec![ + AvmOperand::U32 { + value: source.to_usize() as u32, + }, + AvmOperand::U32 { + value: destination.to_usize() as u32, + }, + ], + ..Default::default() + }); + } + BrilligOpcode::Load { + destination, + source_pointer, + } => { + avm_instrs.push(AvmInstruction { + opcode: AvmOpcode::MOV, + indirect: Some(ZEROTH_OPERAND_INDIRECT), // indirect srcOffset operand + operands: vec![ + AvmOperand::U32 { + value: source_pointer.to_usize() as u32, + }, + AvmOperand::U32 { + value: destination.to_usize() as u32, + }, + ], + ..Default::default() + }); + } + BrilligOpcode::Store { + destination_pointer, + source, + } => { + // INDIRECT dstOffset operand (bit 1 set high) + avm_instrs.push(AvmInstruction { + opcode: AvmOpcode::MOV, + indirect: Some(FIRST_OPERAND_INDIRECT), // indirect dstOffset operand + operands: vec![ + AvmOperand::U32 { + value: source.to_usize() as u32, + }, + AvmOperand::U32 { + value: destination_pointer.to_usize() as u32, + }, + ], + ..Default::default() + }); + } + BrilligOpcode::Call { location } => { + let avm_loc = brillig_pcs_to_avm_pcs[*location]; + avm_instrs.push(AvmInstruction { + opcode: AvmOpcode::INTERNALCALL, + operands: vec![AvmOperand::U32 { + value: avm_loc as u32, + }], + ..Default::default() + }); + } + BrilligOpcode::Return {} => avm_instrs.push(AvmInstruction { + opcode: AvmOpcode::INTERNALRETURN, + ..Default::default() + }), + BrilligOpcode::Stop { return_data_offset, return_data_size } => { + avm_instrs.push(AvmInstruction { + opcode: AvmOpcode::RETURN, + operands: vec![ + AvmOperand::U32 { value: *return_data_offset as u32}, + AvmOperand::U32 { value: *return_data_size as u32}, + ], + ..Default::default() + }); + } + BrilligOpcode::Trap { /*return_data_offset, return_data_size*/ } => { + // TODO(https://github.com/noir-lang/noir/issues/3113): Trap should support return data + avm_instrs.push(AvmInstruction { + opcode: AvmOpcode::REVERT, + operands: vec![ + //AvmOperand::U32 { value: *return_data_offset as u32}, + //AvmOperand::U32 { value: *return_data_size as u32}, + AvmOperand::U32 { value: 0}, + AvmOperand::U32 { value: 0}, + ], + ..Default::default() + }); + } + _ => panic!( + "Transpiler doesn't know how to process {:?} brillig instruction", + brillig_instr + ), + } + } + + dbg_print_avm_program(&avm_instrs); + + // Constructing bytecode from instructions + let mut bytecode = Vec::new(); + for i in 0..avm_instrs.len() { + let instr_bytes = avm_instrs[i].to_bytes(); + bytecode.extend_from_slice(&instr_bytes); + } + bytecode +} + +/// Compute an array that maps each Brillig pc to an AVM pc. +/// This must be done before transpiling to properly transpile jump destinations. +/// This is necessary for two reasons: +/// 1. The transpiler injects `initial_offset` instructions at the beginning of the program. +/// 2. Some brillig instructions (_e.g._ Stop, or certain ForeignCalls) map to multiple AVM instructions +/// args: +/// initial_offset: how many AVM instructions were inserted at the start of the program +/// brillig: the Brillig program +/// returns: an array where each index is a Brillig pc, +/// and each value is the corresponding AVM pc. +fn map_brillig_pcs_to_avm_pcs(initial_offset: usize, brillig: &Brillig) -> Vec { + let mut pc_map = Vec::with_capacity(brillig.bytecode.len()); + pc_map.resize(brillig.bytecode.len(), 0); + pc_map[0] = initial_offset; + for i in 0..brillig.bytecode.len() - 1 { + let num_avm_instrs_for_this_brillig_instr = match &brillig.bytecode[i] { + BrilligOpcode::Load { .. } => 2, + BrilligOpcode::Store { .. } => 2, + _ => 1, + }; + // next Brillig pc will map to an AVM pc offset by the + // number of AVM instructions generated for this Brillig one + pc_map[i + 1] = pc_map[i] + num_avm_instrs_for_this_brillig_instr; + } + pc_map +} diff --git a/avm-transpiler/src/transpile_contract.rs b/avm-transpiler/src/transpile_contract.rs new file mode 100644 index 00000000000..2a8b762139f --- /dev/null +++ b/avm-transpiler/src/transpile_contract.rs @@ -0,0 +1,120 @@ +use log::info; +use regex::Regex; +use serde::{Deserialize, Serialize}; + +use acvm::acir::circuit::Circuit; +use noirc_driver::ContractFunctionType; + +use crate::transpile::brillig_to_avm; +use crate::utils::extract_brillig_from_acir; + +/// Representation of a contract with some transpiled functions +#[derive(Debug, Serialize, Deserialize)] +pub struct TranspiledContract { + pub transpiled: bool, + pub noir_version: String, + pub name: String, + // Functions can be ACIR or AVM + pub functions: Vec, + pub events: serde_json::Value, + pub file_map: serde_json::Value, + //pub warnings: serde_json::Value, +} + +/// A regular contract with ACIR+Brillig functions +/// but with fields irrelevant to transpilation +/// represented as catch-all serde Values +#[derive(Debug, Serialize, Deserialize)] +pub struct CompiledAcirContract { + pub noir_version: String, + pub name: String, + pub functions: Vec, + pub events: serde_json::Value, + pub file_map: serde_json::Value, + //pub warnings: serde_json::Value, +} + +/// Representation of a contract function +/// with AVM bytecode as a base64 string +#[derive(Debug, Serialize, Deserialize)] +pub struct AvmContractFunction { + pub name: String, + pub function_type: ContractFunctionType, + pub is_internal: bool, + pub abi: serde_json::Value, + pub bytecode: String, // base64 + pub debug_symbols: serde_json::Value, +} + +/// Representation of an ACIR contract function but with +/// catch-all serde Values for fields irrelevant to transpilation +#[derive(Debug, Serialize, Deserialize)] +pub struct AcirContractFunction { + pub name: String, + pub function_type: ContractFunctionType, + pub is_internal: bool, + pub abi: serde_json::Value, + #[serde( + serialize_with = "Circuit::serialize_circuit_base64", + deserialize_with = "Circuit::deserialize_circuit_base64" + )] + pub bytecode: Circuit, + pub debug_symbols: serde_json::Value, +} + +/// An enum that allows the TranspiledContract struct to contain +/// functions with either ACIR or AVM bytecode +#[derive(Debug, Serialize, Deserialize)] +#[serde(untagged)] // omit Acir/Avm tag for these objects in json +pub enum AvmOrAcirContractFunction { + Acir(AcirContractFunction), + Avm(AvmContractFunction), +} + +/// Transpilation is performed when a TranspiledContract +/// is constructed from a CompiledAcirContract +impl From for TranspiledContract { + fn from(contract: CompiledAcirContract) -> Self { + let mut functions = Vec::new(); + for function in contract.functions { + // TODO(4269): once functions are tagged for transpilation to AVM, check tag + let re = Regex::new(r"avm_.*$").unwrap(); + if function.function_type == ContractFunctionType::Unconstrained + && re.is_match(function.name.as_str()) + { + info!( + "Transpiling AVM function {} on contract {}", + function.name, contract.name + ); + // Extract Brillig Opcodes from acir + let acir_circuit = function.bytecode.clone(); + let brillig = extract_brillig_from_acir(&acir_circuit.opcodes); + + // Transpile to AVM + let avm_bytecode = brillig_to_avm(&brillig); + + // Push modified function entry to ABI + functions.push(AvmOrAcirContractFunction::Avm(AvmContractFunction { + name: function.name, + function_type: function.function_type, + is_internal: function.is_internal, + abi: function.abi, + bytecode: base64::encode(avm_bytecode), + debug_symbols: function.debug_symbols, + })); + } else { + // This function is not flagged for transpilation. Push original entry. + functions.push(AvmOrAcirContractFunction::Acir(function)); + } + } + TranspiledContract { + transpiled: true, + noir_version: contract.noir_version, + name: contract.name, + functions, // some acir, some transpiled avm functions + events: contract.events, + file_map: contract.file_map, + //warnings: contract.warnings, + } + } +} diff --git a/avm-transpiler/src/utils.rs b/avm-transpiler/src/utils.rs new file mode 100644 index 00000000000..2f31c900450 --- /dev/null +++ b/avm-transpiler/src/utils.rs @@ -0,0 +1,41 @@ +use log::debug; + +use acvm::acir::circuit::brillig::Brillig; +use acvm::acir::circuit::Opcode; + +use crate::instructions::AvmInstruction; + +/// Extract the Brillig program from its ACIR wrapper instruction. +/// An Noir unconstrained function compiles to one ACIR instruction +/// wrapping a Brillig program. This function just extracts that Brillig +/// assuming the 0th ACIR opcode is the wrapper. +pub fn extract_brillig_from_acir(opcodes: &Vec) -> &Brillig { + if opcodes.len() != 1 { + panic!("There should only be one brillig opcode"); + } + let opcode = &opcodes[0]; + let brillig = match opcode { + Opcode::Brillig(brillig) => brillig, + _ => panic!("Tried to extract a Brillig program from its ACIR wrapper opcode, but the opcode doesn't contain Brillig!"), + }; + brillig +} + +/// Print inputs, outputs, and instructions in a Brillig program +pub fn dbg_print_brillig_program(brillig: &Brillig) { + debug!("Printing Brillig program..."); + debug!("\tInputs: {:?}", brillig.inputs); + for i in 0..brillig.bytecode.len() { + let instr = &brillig.bytecode[i]; + debug!("\tPC:{0} {1:?}", i, instr); + } + debug!("\tOutputs: {:?}", brillig.outputs); +} + +/// Print each instruction in an AVM program +pub fn dbg_print_avm_program(avm_program: &Vec) { + debug!("Printing AVM program..."); + for i in 0..avm_program.len() { + debug!("\tPC:{0}: {1}", i, &avm_program[i].to_string()); + } +} diff --git a/aztec-up/bin/aztec b/aztec-up/bin/aztec index 30ef8a66fab..93c53907c14 100755 --- a/aztec-up/bin/aztec +++ b/aztec-up/bin/aztec @@ -1,4 +1,21 @@ #!/usr/bin/env bash set -euo pipefail -$(dirname $0)/.aztec-run aztecprotocol/aztec-sandbox $@ \ No newline at end of file +# Call cli image if used with `aztec cli ...args` +if [ -n "${1-}" ] && [ "$1" != "--help" ]; then + if [ "$1" == "cli" ]; then + shift + $(dirname $0)/.aztec-run aztecprotocol/cli "$@" + elif [ "$1" == "sandbox" ]; then + $(dirname $0)/aztec-sandbox + else + $(dirname $0)/.aztec-run aztecprotocol/aztec "$@" + fi +else + # TODO - display help message + echo + echo "Using 'aztec' CLI:" + echo " aztec start - Start aztec infrastructure components. See 'aztec start --help' for detailed command info." + echo " aztec sandbox - Run a local sandbox network (same as aztec-sandbox)." + echo " aztec cli - Run the aztec client CLI. See 'aztec cli --help' for detailed command info." +fi diff --git a/aztec-up/bin/aztec-cli b/aztec-up/bin/aztec-cli index 6bdbb0473ba..3624338b5c3 100755 --- a/aztec-up/bin/aztec-cli +++ b/aztec-up/bin/aztec-cli @@ -3,5 +3,6 @@ set -euo pipefail export ENV_VARS_TO_INJECT="PXE_URL PRIVATE_KEY DEBUG" export PXE_URL=${PXE_URL:-"http://host.docker.internal:8080"} +export ETHEREUM_HOST=${ETHEREUM_HOST:-"http://host.docker.internal:8545"} -$(dirname $0)/.aztec-run aztecprotocol/cli $@ \ No newline at end of file +$(dirname $0)/.aztec-run aztecprotocol/cli $@ diff --git a/aztec-up/bin/aztec-install b/aztec-up/bin/aztec-install index a49c37fefff..3c7dec94844 100755 --- a/aztec-up/bin/aztec-install +++ b/aztec-up/bin/aztec-install @@ -40,7 +40,7 @@ function title() { echo -e "${r}" fi echo -e "This will install the following scripts and update your PATH if necessary:" - echo -e " ${bold}${g}aztec${r} - launches various infrastructure subsystems (sequencer, prover, pxe, etc)." + echo -e " ${bold}${g}aztec${r} - launches various infrastructure subsystems (node, sequencer, prover, pxe, etc)." echo -e " ${bold}${g}aztec-cli${r} - a command line tool for interfacing and experimenting with infrastructure." echo -e " ${bold}${g}aztec-nargo${r} - aztec's build of nargo, the noir compiler toolchain." echo -e " ${bold}${g}aztec-sandbox${r} - a wrapper around docker-compose that launches services needed for sandbox testing." @@ -106,12 +106,12 @@ export DOCKER_CLI_HINTS=false if [ -z "${SKIP_PULL:-}" ]; then info "Pulling aztec version $VERSION..." - pull_container aztec-sandbox + pull_container aztec pull_container cli pull_container noir fi -# Download the Docker Compose file. Used by aztec-sandbox. +# Download the Docker Compose file. Used by aztec. curl -fsSL http://$INSTALL_HOST/docker-compose.yml -o $AZTEC_PATH/docker-compose.yml function install_bin { diff --git a/aztec-up/bin/docker-compose.yml b/aztec-up/bin/docker-compose.yml index a1d1e3e646c..7b3cab5e311 100644 --- a/aztec-up/bin/docker-compose.yml +++ b/aztec-up/bin/docker-compose.yml @@ -17,9 +17,8 @@ services: ANVIL_PORT: ${ANVIL_PORT:-8545} aztec: - image: "aztecprotocol/aztec-sandbox" + image: "aztecprotocol/aztec" ports: - - "${AZTEC_NODE_PORT:-8079}:${AZTEC_NODE_PORT:-8079}" - "${PXE_PORT:-8080}:${PXE_PORT:-8080}" environment: DEBUG: # Loaded from the user shell if explicitly set @@ -32,7 +31,6 @@ services: WS_BLOCK_CHECK_INTERVAL_MS: 50 PXE_BLOCK_POLLING_INTERVAL_MS: 50 ARCHIVER_VIEM_POLLING_INTERVAL_MS: 500 - AZTEC_NODE_PORT: ${AZTEC_NODE_PORT:-8079} PXE_PORT: ${PXE_PORT:-8080} volumes: - - ./log:/usr/src/yarn-project/aztec-sandbox/log:rw + - ./log:/usr/src/yarn-project/aztec/log:rw diff --git a/barretenberg/.gitrepo b/barretenberg/.gitrepo index 74419f2a3a6..ca8c2516afd 100644 --- a/barretenberg/.gitrepo +++ b/barretenberg/.gitrepo @@ -6,7 +6,7 @@ [subrepo] remote = https://github.com/AztecProtocol/barretenberg branch = master - commit = 8a57980e2a99a96ad165245242f1372263efe0ec - parent = ea840857d8134c9af6f233b414f6d990cd2abd6d + commit = d041c3e8e59132328e0171fb72d61ea50936ee48 + parent = 09d0730bad4be2f4954cbb6d27538f7860d0f21f method = merge cmdver = 0.4.6 diff --git a/barretenberg/CHANGELOG.md b/barretenberg/CHANGELOG.md index 5b5cb064b64..351958744a9 100644 --- a/barretenberg/CHANGELOG.md +++ b/barretenberg/CHANGELOG.md @@ -1,5 +1,46 @@ # Changelog +## [0.21.0](https://github.com/AztecProtocol/aztec-packages/compare/barretenberg-v0.20.0...barretenberg-v0.21.0) (2024-01-30) + + +### ⚠ BREAKING CHANGES + +* add opcode for sha256 compression function ([#4229](https://github.com/AztecProtocol/aztec-packages/issues/4229)) +* add opcode for poseidon2 permutation ([#4214](https://github.com/AztecProtocol/aztec-packages/issues/4214)) +* remove ec_double opcode ([#4210](https://github.com/AztecProtocol/aztec-packages/issues/4210)) +* Add big int opcodes (without implementation) ([#4050](https://github.com/AztecProtocol/aztec-packages/issues/4050)) + +### Features + +* **3738:** AVM basic arithmetic operations for non ff types ([#3881](https://github.com/AztecProtocol/aztec-packages/issues/3881)) ([457a3f9](https://github.com/AztecProtocol/aztec-packages/commit/457a3f9b0c05f58cc88ef43763c5d55b6debaf05)), closes [#3996](https://github.com/AztecProtocol/aztec-packages/issues/3996) +* Add big int opcodes (without implementation) ([#4050](https://github.com/AztecProtocol/aztec-packages/issues/4050)) ([bcab9ce](https://github.com/AztecProtocol/aztec-packages/commit/bcab9ceab62bede3bc1c105b3e639e7c64e3217a)) +* Add opcode for poseidon2 permutation ([#4214](https://github.com/AztecProtocol/aztec-packages/issues/4214)) ([53c5ba5](https://github.com/AztecProtocol/aztec-packages/commit/53c5ba5fa2a86aba16bba8aa8d3a594a789e3e24)) +* Add opcode for sha256 compression function ([#4229](https://github.com/AztecProtocol/aztec-packages/issues/4229)) ([ac25ff7](https://github.com/AztecProtocol/aztec-packages/commit/ac25ff737a934a5f260605f4497e4074c3ed5824)) +* **avm:** Bytecode avm control flow ([#4253](https://github.com/AztecProtocol/aztec-packages/issues/4253)) ([fb1d742](https://github.com/AztecProtocol/aztec-packages/commit/fb1d7420860a35e68b987e790abdaba18595219b)), closes [#4209](https://github.com/AztecProtocol/aztec-packages/issues/4209) +* **avm:** Bytecode parsing and proof generation ([#4191](https://github.com/AztecProtocol/aztec-packages/issues/4191)) ([6c70548](https://github.com/AztecProtocol/aztec-packages/commit/6c70548a98c8e01bc7925d98ece9a2eda4139f69)), closes [#3791](https://github.com/AztecProtocol/aztec-packages/issues/3791) +* Implement Embedded EC add and double opcodes ([#3982](https://github.com/AztecProtocol/aztec-packages/issues/3982)) ([ccb7bff](https://github.com/AztecProtocol/aztec-packages/commit/ccb7bff8e16ea9c8bc4bd48754db59857137507e)) +* Produce graph of internal Barretenberg dependencies ([#4225](https://github.com/AztecProtocol/aztec-packages/issues/4225)) ([88e7923](https://github.com/AztecProtocol/aztec-packages/commit/88e7923ed2ecd747b65f72c5955016c6a1b80b9f)) +* Recursive folding and decider verifier for Protogalaxy ([#4156](https://github.com/AztecProtocol/aztec-packages/issues/4156)) ([9342048](https://github.com/AztecProtocol/aztec-packages/commit/93420480603b2dfa126e5bddb08cd768b7093352)) +* Remove ec_double opcode ([#4210](https://github.com/AztecProtocol/aztec-packages/issues/4210)) ([75f26c4](https://github.com/AztecProtocol/aztec-packages/commit/75f26c4f2a9cf185891234eab6ec4f213d31fc50)) +* Replace single bit range constraints with basic bool gates ([#4164](https://github.com/AztecProtocol/aztec-packages/issues/4164)) ([0a3553b](https://github.com/AztecProtocol/aztec-packages/commit/0a3553b10e02374843181901709933975dc36bb4)) + + +### Bug Fixes + +* **bb:** .gitignore ([#4201](https://github.com/AztecProtocol/aztec-packages/issues/4201)) ([a56e418](https://github.com/AztecProtocol/aztec-packages/commit/a56e418b0fe90b77b7a9fd6bcb0e40cd15260fd6)) +* Generic Honk dependencies ([#4239](https://github.com/AztecProtocol/aztec-packages/issues/4239)) ([382dfbe](https://github.com/AztecProtocol/aztec-packages/commit/382dfbed6aa4c6da7b3c897f8a5f9639843d7037)) + + +### Miscellaneous + +* **bb:** Rearrange namespaces ([#4147](https://github.com/AztecProtocol/aztec-packages/issues/4147)) ([5de0a8e](https://github.com/AztecProtocol/aztec-packages/commit/5de0a8e8dce2483230cccb1d716613966089f2f6)) +* Delete C++ PK circuits ([#4219](https://github.com/AztecProtocol/aztec-packages/issues/4219)) ([9136d32](https://github.com/AztecProtocol/aztec-packages/commit/9136d32268db350779d51e45884368be3a694220)) + + +### Documentation + +* **bb:** How to use docker_interactive.sh ([#4220](https://github.com/AztecProtocol/aztec-packages/issues/4220)) ([f44c6b1](https://github.com/AztecProtocol/aztec-packages/commit/f44c6b173856331a6ca4d00d50436671735172a2)) + ## [0.20.0](https://github.com/AztecProtocol/aztec-packages/compare/barretenberg-v0.19.0...barretenberg-v0.20.0) (2024-01-22) diff --git a/barretenberg/README.md b/barretenberg/README.md index b87cf333b54..687bb4c67b2 100644 --- a/barretenberg/README.md +++ b/barretenberg/README.md @@ -242,10 +242,22 @@ A default configuration for VS Code is provided by the file [`barretenberg.code- ### Integration tests with Aztec in Monorepo -CI will automatically run integration tests against Aztec. The tests in `circuits/cpp` folder use the embedded barretenberg, and can be used to integration test it. +CI will automatically run integration tests against Aztec. It is located in the `barretenberg` folder. ### Integration tests with Aztec in Barretenberg Standalone Repo CI will automatically run integration tests against Aztec's circuits which live [here](https://github.com/AztecProtocol/aztec-packages/tree/master/circuits). To change which Aztec branch or commit for CI to test against, modify [`.aztec-packages-commit`](./cpp/.aztec-packages-commit). When working on a PR, you may want to point this file to a different Aztec branch or commit, but then it should probably be pointed back to master before merging. + +### Testing locally in docker + +A common issue that arises is that our CI system has a different compiler version e.g. namely for GCC. If you need to mimic the CI operating system locally you can use bootstrap_docker.sh or run dockerfiles directly. However, there is a more efficient workflow for iterative development: + +``` +cd barretenberg/cpp +./scripts/docker_interactive.sh +mv build build-native # your native build folders are mounted, but will not work! have to clear them +cmake --preset gcc ; cmake --build build +``` +This will allow you to rebuild as efficiently as if you were running native code, and not have to see a full compile cycle. diff --git a/barretenberg/cpp/.gitignore b/barretenberg/cpp/.gitignore index 811aef3620e..860dfe85cf9 100644 --- a/barretenberg/cpp/.gitignore +++ b/barretenberg/cpp/.gitignore @@ -6,8 +6,8 @@ src/barretenberg/rollup/proofs/*/fixtures srs_db/*/*/transcript* CMakeUserPresets.json .vscode/settings.json -# to be unignored when we agree on clang-tidy rules -.clangd acir_tests # we may download go in scripts/collect_heap_information.sh go*.tar.gz +barretenberg_modules.dot +barretenberg_modules.png \ No newline at end of file diff --git a/barretenberg/cpp/CMakeLists.txt b/barretenberg/cpp/CMakeLists.txt index 1bd35206df0..056233c1c14 100644 --- a/barretenberg/cpp/CMakeLists.txt +++ b/barretenberg/cpp/CMakeLists.txt @@ -6,7 +6,7 @@ cmake_minimum_required(VERSION 3.24 FATAL_ERROR) project( Barretenberg DESCRIPTION "BN254 elliptic curve library, and PLONK SNARK prover" - VERSION 0.20.0 # x-release-please-version + VERSION 0.21.0 # x-release-please-version LANGUAGES CXX C ) # Insert version into `bb` config file @@ -16,14 +16,11 @@ configure_file( @ONLY ) - option(DISABLE_ASM "Disable custom assembly" OFF) option(DISABLE_ADX "Disable ADX assembly variant" OFF) option(MULTITHREADING "Enable multi-threading" ON) option(OMP_MULTITHREADING "Enable OMP multi-threading" OFF) -option(TESTING "Build tests" ON) -option(BENCHMARKS "Build benchmarks" ON) -option(FUZZING "Build fuzzing harnesses" OFF) +option(FUZZING "Build ONLY fuzzing harnesses" OFF) option(DISABLE_TBB "Intel Thread Building Blocks" ON) option(COVERAGE "Enable collecting coverage from tests" OFF) option(ENABLE_ASAN "Address sanitizer for debugging tricky memory corruption" OFF) @@ -52,22 +49,10 @@ if(FUZZING) add_definitions(-DDISABLE_CUSTOM_MUTATORS=1) endif() - set(SANITIZER_OPTIONS "") - - if(ADDRESS_SANITIZER) - set(SANITIZER_OPTIONS ${SANITIZER_OPTIONS} -fsanitize=address) - endif() - - if(UNDEFINED_BEHAVIOUR_SANITIZER) - set(SANITIZER_OPTIONS ${SANITIZER_OPTIONS} -fsanitize=undefined -fno-sanitize=alignment) - endif() - - add_compile_options(-fsanitize=fuzzer-no-link ${SANITIZER_OPTIONS}) + add_compile_options(-fsanitize=fuzzer) + add_link_options(-fsanitize=fuzzer) - set(WASM OFF) - set(BENCHMARKS OFF) set(MULTITHREADING OFF) - set(TESTING OFF) endif() if(CMAKE_SYSTEM_PROCESSOR MATCHES "wasm32") @@ -75,7 +60,6 @@ if(CMAKE_SYSTEM_PROCESSOR MATCHES "wasm32") set(WASM ON) set(DISABLE_ASM ON) set(OMP_MULTITHREADING OFF) - set(BENCHMARKS OFF) set(DISABLE_TBB 1) add_compile_definitions(_WASI_EMULATED_PROCESS_CLOCKS=1) endif() diff --git a/barretenberg/cpp/CMakePresets.json b/barretenberg/cpp/CMakePresets.json index 7b22541e785..075068e6cd4 100644 --- a/barretenberg/cpp/CMakePresets.json +++ b/barretenberg/cpp/CMakePresets.json @@ -140,6 +140,19 @@ "LDFLAGS": "-fsanitize=thread" } }, + { + "name": "ubsan", + "displayName": "Debugging build with undefined behaviour sanitizer on Clang-16", + "description": "Build with undefined behaviour sanitizer on clang16 with debugging information", + "inherits": "clang16-dbg", + "binaryDir": "build-ubsan", + "generator": "Unix Makefiles", + "environment": { + "CFLAGS": "-fsanitize=undefined", + "CXXFLAGS": "-fsanitize=undefined", + "LDFLAGS": "-fsanitize=undefined" + } + }, { "name": "msan", "displayName": "Debugging build with memory sanitizer on Clang-16", @@ -151,10 +164,6 @@ "CFLAGS": "-fsanitize=memory", "CXXFLAGS": "-fsanitize=memory", "LDFLAGS": "-fsanitize=memory" - }, - "cacheVariables": { - "BENCHMARK": "OFF", - "TESTING": "OFF" } }, { @@ -228,6 +237,16 @@ "MULTITHREADING": "ON" } }, + { + "name": "wasm-bench", + "displayName": "WASM benchmarking.", + "description": "WASM benchmarking.", + "inherits": "wasm-threads", + "binaryDir": "build-wasm-bench", + "environment": { + "CXXFLAGS": "-DWASMTIME_ENV_HACK" + } + }, { "name": "xray", "displayName": "Build with multi-threaded XRay Profiling", @@ -336,6 +355,11 @@ "inherits": "default", "configurePreset": "tsan" }, + { + "name": "ubsan", + "inherits": "default", + "configurePreset": "ubsan" + }, { "name": "coverage", "inherits": "default", @@ -368,6 +392,12 @@ "jobs": 0, "targets": ["barretenberg.wasm"] }, + { + "name": "wasm-bench", + "configurePreset": "wasm-bench", + "inheritConfigureEnvironment": true, + "jobs": 0 + }, { "name": "xray", "configurePreset": "xray", diff --git a/barretenberg/cpp/cmake/benchmark.cmake b/barretenberg/cpp/cmake/benchmark.cmake index c8f90548d66..10e13113b92 100644 --- a/barretenberg/cpp/cmake/benchmark.cmake +++ b/barretenberg/cpp/cmake/benchmark.cmake @@ -1,25 +1,19 @@ -if(NOT TESTING) - set(BENCHMARKS OFF) -endif() - -if(BENCHMARKS) - include(FetchContent) +include(FetchContent) - FetchContent_Declare( - benchmark - GIT_REPOSITORY https://github.com/google/benchmark - GIT_TAG v1.7.1 - FIND_PACKAGE_ARGS - ) +FetchContent_Declare( + benchmark + GIT_REPOSITORY https://github.com/AztecProtocol/google-benchmark + GIT_TAG 7638387d2727853d970fc9420dcf95cf3e9bd112 + FIND_PACKAGE_ARGS +) - set(BENCHMARK_ENABLE_TESTING OFF CACHE BOOL "Benchmark tests off") - set(BENCHMARK_ENABLE_INSTALL OFF CACHE BOOL "Benchmark installation off") +set(BENCHMARK_ENABLE_TESTING OFF CACHE BOOL "Benchmark tests off") +set(BENCHMARK_ENABLE_INSTALL OFF CACHE BOOL "Benchmark installation off") - FetchContent_MakeAvailable(benchmark) - if(NOT benchmark_FOUND) - # FetchContent_MakeAvailable calls FetchContent_Populate if `find_package` is unsuccessful - # so these variables will be available if we reach this case - set_property(DIRECTORY ${benchmark_SOURCE_DIR} PROPERTY EXCLUDE_FROM_ALL) - set_property(DIRECTORY ${benchmark_BINARY_DIR} PROPERTY EXCLUDE_FROM_ALL) - endif() +FetchContent_MakeAvailable(benchmark) +if(NOT benchmark_FOUND) + # FetchContent_MakeAvailable calls FetchContent_Populate if `find_package` is unsuccessful + # so these variables will be available if we reach this case + set_property(DIRECTORY ${benchmark_SOURCE_DIR} PROPERTY EXCLUDE_FROM_ALL) + set_property(DIRECTORY ${benchmark_BINARY_DIR} PROPERTY EXCLUDE_FROM_ALL) endif() diff --git a/barretenberg/cpp/cmake/gtest.cmake b/barretenberg/cpp/cmake/gtest.cmake index e86bf49756e..c3551ea481a 100644 --- a/barretenberg/cpp/cmake/gtest.cmake +++ b/barretenberg/cpp/cmake/gtest.cmake @@ -1,51 +1,49 @@ -if(TESTING) - include(GoogleTest) - include(FetchContent) - - FetchContent_Declare( - GTest - GIT_REPOSITORY https://github.com/google/googletest.git - # Version 1.12.1 is not compatible with WASI-SDK 12 - GIT_TAG release-1.10.0 - FIND_PACKAGE_ARGS +include(GoogleTest) +include(FetchContent) + +FetchContent_Declare( + GTest + GIT_REPOSITORY https://github.com/google/googletest.git + # Version 1.12.1 is not compatible with WASI-SDK 12 + GIT_TAG release-1.10.0 + FIND_PACKAGE_ARGS +) + +set(BUILD_GMOCK OFF CACHE BOOL "Build with gMock disabled") +set(INSTALL_GTEST OFF CACHE BOOL "gTest installation disabled") + +FetchContent_MakeAvailable(GTest) + +if (NOT GTest_FOUND) + # FetchContent_MakeAvailable calls FetchContent_Populate if `find_package` is unsuccessful + # so these variables will be available if we reach this case + set_property(DIRECTORY ${gtest_SOURCE_DIR} PROPERTY EXCLUDE_FROM_ALL) + set_property(DIRECTORY ${gtest_BINARY_DIR} PROPERTY EXCLUDE_FROM_ALL) + + # Disable all warning when compiling gtest + target_compile_options( + gtest + PRIVATE + -w ) - set(BUILD_GMOCK OFF CACHE BOOL "Build with gMock disabled") - set(INSTALL_GTEST OFF CACHE BOOL "gTest installation disabled") - - FetchContent_MakeAvailable(GTest) - - if (NOT GTest_FOUND) - # FetchContent_MakeAvailable calls FetchContent_Populate if `find_package` is unsuccessful - # so these variables will be available if we reach this case - set_property(DIRECTORY ${gtest_SOURCE_DIR} PROPERTY EXCLUDE_FROM_ALL) - set_property(DIRECTORY ${gtest_BINARY_DIR} PROPERTY EXCLUDE_FROM_ALL) - - # Disable all warning when compiling gtest - target_compile_options( + if(WASM) + target_compile_definitions( gtest PRIVATE - -w - ) - - if(WASM) - target_compile_definitions( - gtest - PRIVATE - -DGTEST_HAS_EXCEPTIONS=0 - -DGTEST_HAS_STREAM_REDIRECTION=0 - ) - endif() - - mark_as_advanced( - BUILD_GMOCK BUILD_GTEST BUILD_SHARED_LIBS - gmock_build_tests gtest_build_samples gtest_build_tests - gtest_disable_pthreads gtest_force_shared_crt gtest_hide_internal_symbols + -DGTEST_HAS_EXCEPTIONS=0 + -DGTEST_HAS_STREAM_REDIRECTION=0 ) - - add_library(GTest::gtest ALIAS gtest) - add_library(GTest::gtest_main ALIAS gtest_main) endif() - enable_testing() + mark_as_advanced( + BUILD_GMOCK BUILD_GTEST BUILD_SHARED_LIBS + gmock_build_tests gtest_build_samples gtest_build_tests + gtest_disable_pthreads gtest_force_shared_crt gtest_hide_internal_symbols + ) + + add_library(GTest::gtest ALIAS gtest) + add_library(GTest::gtest_main ALIAS gtest_main) endif() + +enable_testing() diff --git a/barretenberg/cpp/cmake/module.cmake b/barretenberg/cpp/cmake/module.cmake index 40bb18932be..d394c024b9f 100644 --- a/barretenberg/cpp/cmake/module.cmake +++ b/barretenberg/cpp/cmake/module.cmake @@ -65,7 +65,7 @@ function(barretenberg_module MODULE_NAME) endif() file(GLOB_RECURSE TEST_SOURCE_FILES *.test.cpp) - if(TESTING AND TEST_SOURCE_FILES) + if(TEST_SOURCE_FILES AND NOT FUZZING) add_library( ${MODULE_NAME}_test_objects OBJECT @@ -184,7 +184,6 @@ function(barretenberg_module MODULE_NAME) ${MODULE_NAME}_${FUZZER_NAME_STEM}_fuzzer PRIVATE "-fsanitize=fuzzer" - ${SANITIZER_OPTIONS} ) target_link_libraries( @@ -196,44 +195,47 @@ function(barretenberg_module MODULE_NAME) endif() file(GLOB_RECURSE BENCH_SOURCE_FILES *.bench.cpp) - if(BENCHMARKS AND BENCH_SOURCE_FILES) - add_library( - ${MODULE_NAME}_bench_objects - OBJECT - ${BENCH_SOURCE_FILES} - ) - list(APPEND lib_targets ${MODULE_NAME}_bench_objects) + if(BENCH_SOURCE_FILES AND NOT FUZZING) + foreach(BENCHMARK_SOURCE ${BENCH_SOURCE_FILES}) + get_filename_component(BENCHMARK_NAME ${BENCHMARK_SOURCE} NAME_WE) # extract name without extension + add_library( + ${BENCHMARK_NAME}_bench_objects + OBJECT + ${BENCHMARK_SOURCE} + ) + list(APPEND lib_targets ${BENCHMARK_NAME}_bench_objects) - target_link_libraries( - ${MODULE_NAME}_bench_objects - PRIVATE - benchmark::benchmark - ${TBB_IMPORTED_TARGETS} - ) + target_link_libraries( + ${BENCHMARK_NAME}_bench_objects + PRIVATE + benchmark::benchmark + ${TBB_IMPORTED_TARGETS} + ) - add_executable( - ${MODULE_NAME}_bench - $ - ) - list(APPEND exe_targets ${MODULE_NAME}_bench) + add_executable( + ${BENCHMARK_NAME}_bench + $ + ) + list(APPEND exe_targets ${MODULE_NAME}_bench) - target_link_libraries( - ${MODULE_NAME}_bench - PRIVATE - ${MODULE_LINK_NAME} - ${ARGN} - benchmark::benchmark - ${TBB_IMPORTED_TARGETS} - ) + target_link_libraries( + ${BENCHMARK_NAME}_bench + PRIVATE + ${MODULE_LINK_NAME} + ${ARGN} + benchmark::benchmark + ${TBB_IMPORTED_TARGETS} + ) - # enable msgpack downloading via dependency (solves race condition) - add_dependencies(${MODULE_NAME}_bench_objects msgpack-c) - add_dependencies(${MODULE_NAME}_bench msgpack-c) - add_custom_target( - run_${MODULE_NAME}_bench - COMMAND ${MODULE_NAME}_bench - WORKING_DIRECTORY ${CMAKE_BINARY_DIR} - ) + # enable msgpack downloading via dependency (solves race condition) + add_dependencies(${BENCHMARK_NAME}_bench_objects msgpack-c) + add_dependencies(${BENCHMARK_NAME}_bench msgpack-c) + add_custom_target( + run_${BENCHMARK_NAME}_bench + COMMAND ${BENCHMARK_NAME}_bench + WORKING_DIRECTORY ${CMAKE_BINARY_DIR} + ) + endforeach() endif() set(${MODULE_NAME}_lib_targets ${lib_targets} PARENT_SCOPE) diff --git a/barretenberg/cpp/docs/Fuzzing.md b/barretenberg/cpp/docs/Fuzzing.md index bbe85a62a1f..c413ba3ddfb 100644 --- a/barretenberg/cpp/docs/Fuzzing.md +++ b/barretenberg/cpp/docs/Fuzzing.md @@ -15,15 +15,11 @@ cmake --build --preset fuzzing Fuzzing build turns off building tests and benchmarks, since they are incompatible with libfuzzer interface. -To turn on address sanitizer add `-DADDRESS_SANITIZER=ON`. Note that address sanitizer can be used to explore crashes. +To turn on address sanitizer instead run `cmake --preset asan -DFUZZING=ON`. Note that address sanitizer can be used to explore crashes. Sometimes you might have to specify the address of llvm-symbolizer. You have to do it with `export ASAN_SYMBOLIZER_PATH=`. -For undefined behavior sanitizer `-DUNDEFINED_BEHAVIOUR_SANITIZER=ON`. +For undefined behavior sanitizer use `cmake --preset ubsan -DFUZZING=ON`. Note that the fuzzer can be orders of magnitude slower with ASan (2-3x slower) or UBSan on, so it is best to run a non-sanitized build first, minimize the testcase and then run it for a bit of time with sanitizers. -Building with clang 13 or later is recommended, since libfuzzer contains and by default utilizes the entropic power schedule, which is considered more efficient -than the standard one present in previous versions. -You can downloadload the latest clang+llvm release here: https://github.com/llvm/llvm-project/releases - To set up cmake with another version of clang and fuzzing on: ```bash diff --git a/barretenberg/cpp/scripts/_benchmark_remote_lock.sh b/barretenberg/cpp/scripts/_benchmark_remote_lock.sh new file mode 100644 index 00000000000..551a26b9070 --- /dev/null +++ b/barretenberg/cpp/scripts/_benchmark_remote_lock.sh @@ -0,0 +1,27 @@ +# NOTE: This script is NOT meant to be ran, only sourced. +# This sets up all the necessary machinery to lock ~/BENCHMARK_IN_PROGRESS +# + +# Function to clean up lock file +function cleanup() { + ssh $BB_SSH_KEY $BB_SSH_INSTANCE "rm -f ~/BENCHMARK_IN_PROGRESS" + echo "Benchmarking lock deleted." +} + +# Check for existing lock file +if ssh $BB_SSH_KEY $BB_SSH_INSTANCE "test -f ~/BENCHMARK_IN_PROGRESS"; then + echo "Benchmarking is already in progress. If htop on the remote machine is not active, ~/BENCHMARK_IN_PROGRESS may need to be deleted." + # Important: Exits the script that called this! + # This implements the lock + exit 1 +fi + +# Create lock file +ssh $BB_SSH_KEY $BB_SSH_INSTANCE "touch ~/BENCHMARK_IN_PROGRESS" + +echo "Benchmarking lock created at ~/BENCHMARK_IN_PROGRESS." + +# Trap to ensure cleanup runs on ANY exit, including from a signal +trap cleanup EXIT + +# don't exit, the caller script will run \ No newline at end of file diff --git a/barretenberg/cpp/scripts/barretenberg_module_digraph.sh b/barretenberg/cpp/scripts/barretenberg_module_digraph.sh new file mode 100755 index 00000000000..f02b6a01aac --- /dev/null +++ b/barretenberg/cpp/scripts/barretenberg_module_digraph.sh @@ -0,0 +1,24 @@ +#!/usr/bin/env bash +set -eu + +TMP=tmp.dot +RESULT_DOT=barretenberg_modules.dot +RESULT_PNG=barretenberg_modules.png + +# initialize a directed graph for graphviz +echo digraph BarretenbergModules { > $TMP +# populate the directed graph +for file in $(find ./src/barretenberg/ -iname CMakeLists.txt); do + opening_chars=$(head -c 19 "$file") + if [ "$opening_chars" == barretenberg_module ]; then + awk -f ./scripts/barretenberg_module_digraph_edges.awk $file >> $TMP + fi +done +echo } >> $TMP + +# apply transitive reduction to remove dependcies that are implied by other dependencies +cat $TMP | tred > $RESULT_DOT +rm $TMP + +# produce a PNG of the graph +dot -Tpng $RESULT_DOT -o $RESULT_PNG \ No newline at end of file diff --git a/barretenberg/cpp/scripts/barretenberg_module_digraph_edges.awk b/barretenberg/cpp/scripts/barretenberg_module_digraph_edges.awk new file mode 100644 index 00000000000..c5d4c4c2e35 --- /dev/null +++ b/barretenberg/cpp/scripts/barretenberg_module_digraph_edges.awk @@ -0,0 +1,35 @@ +# Function to extract words between parentheses +function extract_edges(line) { + match(line, /\(.*\)/); # Find the portion within parentheses + line = substr(line, RSTART + 1, RLENGTH - 2); # Extract the words + gsub(/^[ ]+/, "", line); # Remove leading spaces and tabs + gsub(/[ ]+$/, "", line); # Remove trailing spaces and tabs + gsub(/[ ]+/, " ", line); # Sub multiple spaces for a single space + split(line, modules, " "); # Split into an array of words + + # If node has no dependencies, just add the node + if (length(modules)==1) { + print modules[1]; + } + else { # add edges + for (i = 2; i <= length(modules); i++) { + print modules[1]" -> "modules[i]; + } + } +} + +# Main AWK script +{ + # Concatenate lines if the opening parenthesis is not closed + while (!/\)/) { + current_line = $0; + getline; + $0 = current_line $0; + } + + # Check if the line begins with "barretenberg_module". If so, extact the digraph edges + function_name = "barretenberg_module"; + if ($0 ~ "^" function_name "\\(") { + extract_edges($0); + } +} diff --git a/barretenberg/cpp/scripts/benchmark.sh b/barretenberg/cpp/scripts/benchmark.sh new file mode 100755 index 00000000000..93b96377173 --- /dev/null +++ b/barretenberg/cpp/scripts/benchmark.sh @@ -0,0 +1,17 @@ +#!/usr/bin/env bash +set -eu + +BENCHMARK=${1:-goblin_bench} +COMMAND=${2:-./bin/$BENCHMARK} + +# Move above script dir. +cd $(dirname $0)/.. + +# Configure and build. +cmake --preset clang16 +cmake --build --preset clang16 --target $BENCHMARK + +cd build +# Consistency with _wasm.sh targets / shorter $COMMAND. +cp ./bin/$BENCHMARK . +$COMMAND \ No newline at end of file diff --git a/barretenberg/cpp/scripts/benchmark_remote.sh b/barretenberg/cpp/scripts/benchmark_remote.sh new file mode 100755 index 00000000000..e1d5907eab2 --- /dev/null +++ b/barretenberg/cpp/scripts/benchmark_remote.sh @@ -0,0 +1,28 @@ +#!/usr/bin/env bash +# This script automates the process of benchmarking native on a remote EC2 instance. +# Prerequisites: +# 1. Define the following environment variables: +# - BB_SSH_KEY: SSH key for EC2 instance, e.g., '-i key.pem' +# - BB_SSH_INSTANCE: EC2 instance URL +# - BB_SSH_CPP_PATH: Path to barretenberg/cpp in a cloned repository on the EC2 instance +set -eu + +BENCHMARK=${1:-goblin_bench} +COMMAND=${2:-./$BENCHMARK} + +# Move above script dir. +cd $(dirname $0)/.. + +# Create lock file +ssh $BB_SSH_KEY $BB_SSH_INSTANCE "touch ~/BENCHMARKING_IN_PROGRESS" + +# Configure and build. +cmake --preset clang16 +cmake --build --preset clang16 --target $BENCHMARK + +source scripts/_benchmark_remote_lock.sh + +cd build +scp $BB_SSH_KEY ./bin/$BENCHMARK $BB_SSH_INSTANCE:$BB_SSH_CPP_PATH/build +ssh $BB_SSH_KEY $BB_SSH_INSTANCE \ + "cd $BB_SSH_CPP_PATH/build ; $COMMAND" diff --git a/barretenberg/cpp/scripts/benchmark_wasm.sh b/barretenberg/cpp/scripts/benchmark_wasm.sh new file mode 100755 index 00000000000..a7565485bdf --- /dev/null +++ b/barretenberg/cpp/scripts/benchmark_wasm.sh @@ -0,0 +1,17 @@ +#!/usr/bin/env bash +set -eu + +BENCHMARK=${1:-goblin_bench} +COMMAND=${2:-./bin/$BENCHMARK} + +# Move above script dir. +cd $(dirname $0)/.. + +# Configure and build. +cmake --preset wasm-bench +cmake --build --preset wasm-bench --target $BENCHMARK + +cd build-wasm-bench +# Consistency with _wasm.sh targets / shorter $COMMAND. +cp ./bin/$BENCHMARK . +wasmtime run -Wthreads=y -Sthreads=y $COMMAND \ No newline at end of file diff --git a/barretenberg/cpp/scripts/benchmark_wasm_remote.sh b/barretenberg/cpp/scripts/benchmark_wasm_remote.sh new file mode 100755 index 00000000000..f131cc63a6f --- /dev/null +++ b/barretenberg/cpp/scripts/benchmark_wasm_remote.sh @@ -0,0 +1,25 @@ +#!/usr/bin/env bash +# This script automates the process of benchmarking WASM on a remote EC2 instance. +# Prerequisites: +# 1. Define the following environment variables: +# - BB_SSH_KEY: SSH key for EC2 instance, e.g., '-i key.pem' +# - BB_SSH_INSTANCE: EC2 instance URL +# - BB_SSH_CPP_PATH: Path to barretenberg/cpp in a cloned repository on the EC2 instance +set -eu + +BENCHMARK=${1:-goblin_bench} +COMMAND=${2:-./$BENCHMARK} + +# Move above script dir. +cd $(dirname $0)/.. + +# Configure and build. +cmake --preset wasm-bench +cmake --build --preset wasm-bench --target $BENCHMARK + +source scripts/_benchmark_remote_lock.sh + +cd build-wasm-bench +scp $BB_SSH_KEY ./bin/$BENCHMARK $BB_SSH_INSTANCE:$BB_SSH_CPP_PATH/build-wasm-bench +ssh $BB_SSH_KEY $BB_SSH_INSTANCE \ + "cd $BB_SSH_CPP_PATH/build-wasm-bench ; /home/ubuntu/.wasmtime/bin/wasmtime run -Wthreads=y -Sthreads=y $COMMAND" diff --git a/barretenberg/cpp/src/CMakeLists.txt b/barretenberg/cpp/src/CMakeLists.txt index 352a7d1cafe..0ca963d4f87 100644 --- a/barretenberg/cpp/src/CMakeLists.txt +++ b/barretenberg/cpp/src/CMakeLists.txt @@ -12,7 +12,7 @@ set(CMAKE_ARCHIVE_OUTPUT_DIRECTORY ${CMAKE_BINARY_DIR}/lib) # Specifying `-Wno-${ERROR_NAME}` will silence the error completely. # To preserve the warning, but prevent them from causing the build to fail, # use the flag `-Wno-error=${ERROR_NAME}` -add_compile_options(-Werror -Wall -Wextra -Wconversion -Wsign-conversion) +add_compile_options(-Werror -Wall -Wextra -Wconversion -Wsign-conversion -Wfatal-errors) if(CMAKE_CXX_COMPILER_ID MATCHES "Clang") add_compile_options(-fcolor-diagnostics -fconstexpr-steps=100000000) @@ -85,9 +85,7 @@ if(SMT) add_subdirectory(barretenberg/smt_verification) endif() -if(BENCHMARKS) - add_subdirectory(barretenberg/benchmark) -endif() +add_subdirectory(barretenberg/benchmark) include(GNUInstallDirs) @@ -107,6 +105,7 @@ set(BARRETENBERG_TARGET_OBJECTS $ $ $ + $ $ $ $ @@ -130,6 +129,7 @@ set(BARRETENBERG_TARGET_OBJECTS $ $ $ + $ $ $ $ diff --git a/barretenberg/cpp/src/barretenberg/barretenberg.hpp b/barretenberg/cpp/src/barretenberg/barretenberg.hpp index eaa55743c88..5bc5639a805 100644 --- a/barretenberg/cpp/src/barretenberg/barretenberg.hpp +++ b/barretenberg/cpp/src/barretenberg/barretenberg.hpp @@ -15,6 +15,7 @@ #include "crypto/keccak/keccak.hpp" #include "crypto/pedersen_commitment/pedersen.hpp" #include "crypto/pedersen_hash/pedersen.hpp" +#include "crypto/poseidon2/poseidon2.hpp" #include "crypto/schnorr/schnorr.hpp" #include "crypto/sha256/sha256.hpp" #include "ecc/curves/bn254/fq.hpp" @@ -41,6 +42,7 @@ #include "stdlib/hash/blake2s/blake2s.hpp" #include "stdlib/hash/blake3s/blake3s.hpp" #include "stdlib/hash/pedersen/pedersen.hpp" +#include "stdlib/hash/poseidon2/poseidon2.hpp" #include "stdlib/merkle_tree/hash.hpp" #include "stdlib/merkle_tree/membership.hpp" #include "stdlib/merkle_tree/memory_store.hpp" diff --git a/barretenberg/cpp/src/barretenberg/benchmark/CMakeLists.txt b/barretenberg/cpp/src/barretenberg/benchmark/CMakeLists.txt index e86e72df09d..285f2bb5937 100644 --- a/barretenberg/cpp/src/barretenberg/benchmark/CMakeLists.txt +++ b/barretenberg/cpp/src/barretenberg/benchmark/CMakeLists.txt @@ -1,4 +1,3 @@ -add_subdirectory(commit_bench) add_subdirectory(decrypt_bench) add_subdirectory(ipa_bench) add_subdirectory(pippenger_bench) diff --git a/barretenberg/cpp/src/barretenberg/benchmark/basics_bench/CMakeLists.txt b/barretenberg/cpp/src/barretenberg/benchmark/basics_bench/CMakeLists.txt index d23c4f6597f..1740ea9ecb6 100644 --- a/barretenberg/cpp/src/barretenberg/benchmark/basics_bench/CMakeLists.txt +++ b/barretenberg/cpp/src/barretenberg/benchmark/basics_bench/CMakeLists.txt @@ -1,18 +1 @@ -# Each source represents a separate benchmark suite -set(BENCHMARK_SOURCES - basics.bench.cpp -) - -# Required libraries for benchmark suites -set(LINKED_LIBRARIES - benchmark::benchmark - ecc -) - -# Add executable and custom target for each suite, e.g. ultra_honk_bench -foreach(BENCHMARK_SOURCE ${BENCHMARK_SOURCES}) - get_filename_component(BENCHMARK_NAME ${BENCHMARK_SOURCE} NAME_WE) # extract name without extension - add_executable(${BENCHMARK_NAME}_bench ${BENCHMARK_SOURCE}) - target_link_libraries(${BENCHMARK_NAME}_bench ${LINKED_LIBRARIES}) - add_custom_target(run_${BENCHMARK_NAME} COMMAND ${BENCHMARK_NAME} WORKING_DIRECTORY ${CMAKE_BINARY_DIR}) -endforeach() \ No newline at end of file +barretenberg_module(basics_bench ecc) \ No newline at end of file diff --git a/barretenberg/cpp/src/barretenberg/benchmark/commit_bench/CMakeLists.txt b/barretenberg/cpp/src/barretenberg/benchmark/commit_bench/CMakeLists.txt deleted file mode 100644 index 3bca58463b1..00000000000 --- a/barretenberg/cpp/src/barretenberg/benchmark/commit_bench/CMakeLists.txt +++ /dev/null @@ -1,18 +0,0 @@ -# Each source represents a separate benchmark suite -set(BENCHMARK_SOURCES - commit.bench.cpp -) - -# Required libraries for benchmark suites -set(LINKED_LIBRARIES - commitment_schemes - benchmark::benchmark -) - -# Add executable and custom target for each suite, e.g. ultra_honk_bench -foreach(BENCHMARK_SOURCE ${BENCHMARK_SOURCES}) - get_filename_component(BENCHMARK_NAME ${BENCHMARK_SOURCE} NAME_WE) # extract name without extension - add_executable(${BENCHMARK_NAME}_bench main.bench.cpp ${BENCHMARK_SOURCE}) - target_link_libraries(${BENCHMARK_NAME}_bench ${LINKED_LIBRARIES}) - add_custom_target(run_${BENCHMARK_NAME} COMMAND ${BENCHMARK_NAME} WORKING_DIRECTORY ${CMAKE_BINARY_DIR}) -endforeach() \ No newline at end of file diff --git a/barretenberg/cpp/src/barretenberg/benchmark/commit_bench/main.bench.cpp b/barretenberg/cpp/src/barretenberg/benchmark/commit_bench/main.bench.cpp deleted file mode 100644 index 71fefa04722..00000000000 --- a/barretenberg/cpp/src/barretenberg/benchmark/commit_bench/main.bench.cpp +++ /dev/null @@ -1,3 +0,0 @@ -#include - -BENCHMARK_MAIN(); diff --git a/barretenberg/cpp/src/barretenberg/benchmark/decrypt_bench/CMakeLists.txt b/barretenberg/cpp/src/barretenberg/benchmark/decrypt_bench/CMakeLists.txt index c58e1e64171..da70f1faed8 100644 --- a/barretenberg/cpp/src/barretenberg/benchmark/decrypt_bench/CMakeLists.txt +++ b/barretenberg/cpp/src/barretenberg/benchmark/decrypt_bench/CMakeLists.txt @@ -1,11 +1,13 @@ -add_executable( - decrypt_bench - main.cpp -) +if (NOT FUZZING) + add_executable( + decrypt_bench + main.cpp + ) -target_link_libraries( - decrypt_bench - PRIVATE - ecc - common -) \ No newline at end of file + target_link_libraries( + decrypt_bench + PRIVATE + ecc + common + ) +endif() \ No newline at end of file diff --git a/barretenberg/cpp/src/barretenberg/benchmark/goblin_bench/CMakeLists.txt b/barretenberg/cpp/src/barretenberg/benchmark/goblin_bench/CMakeLists.txt index 5b3734936f2..41d407c044d 100644 --- a/barretenberg/cpp/src/barretenberg/benchmark/goblin_bench/CMakeLists.txt +++ b/barretenberg/cpp/src/barretenberg/benchmark/goblin_bench/CMakeLists.txt @@ -1,21 +1 @@ -# Each source represents a separate benchmark suite -set(BENCHMARK_SOURCES - goblin.bench.cpp - eccvm.bench.cpp -) - -# Required libraries for benchmark suites -set(LINKED_LIBRARIES - ultra_honk - eccvm - stdlib_recursion - benchmark::benchmark -) - -# Add executable and custom target for each suite, e.g. ultra_honk_bench -foreach(BENCHMARK_SOURCE ${BENCHMARK_SOURCES}) - get_filename_component(BENCHMARK_NAME ${BENCHMARK_SOURCE} NAME_WE) # extract name without extension - add_executable(${BENCHMARK_NAME}_bench main.bench.cpp ${BENCHMARK_SOURCE}) - target_link_libraries(${BENCHMARK_NAME}_bench ${LINKED_LIBRARIES}) - add_custom_target(run_${BENCHMARK_NAME} COMMAND ${BENCHMARK_NAME} WORKING_DIRECTORY ${CMAKE_BINARY_DIR}) -endforeach() \ No newline at end of file +barretenberg_module(goblin_bench ultra_honk eccvm stdlib_recursion stdlib_sha256 stdlib_merkle_tree stdlib_primitives) diff --git a/barretenberg/cpp/src/barretenberg/benchmark/goblin_bench/eccvm.bench.cpp b/barretenberg/cpp/src/barretenberg/benchmark/goblin_bench/eccvm.bench.cpp index 4c89b375de5..949658c5bd0 100644 --- a/barretenberg/cpp/src/barretenberg/benchmark/goblin_bench/eccvm.bench.cpp +++ b/barretenberg/cpp/src/barretenberg/benchmark/goblin_bench/eccvm.bench.cpp @@ -70,4 +70,6 @@ void eccvm_prove(State& state) noexcept BENCHMARK(eccvm_generate_prover)->Unit(kMillisecond)->DenseRange(10, 20); BENCHMARK(eccvm_prove)->Unit(kMillisecond)->DenseRange(10, 20); -} // namespace \ No newline at end of file +} // namespace + +BENCHMARK_MAIN(); diff --git a/barretenberg/cpp/src/barretenberg/benchmark/goblin_bench/goblin.bench.cpp b/barretenberg/cpp/src/barretenberg/benchmark/goblin_bench/goblin.bench.cpp index 839c8237de4..9c8fd4e51b7 100644 --- a/barretenberg/cpp/src/barretenberg/benchmark/goblin_bench/goblin.bench.cpp +++ b/barretenberg/cpp/src/barretenberg/benchmark/goblin_bench/goblin.bench.cpp @@ -11,126 +11,143 @@ using namespace benchmark; using namespace bb; namespace { -void goblin_full(State& state) noexcept -{ - bb::srs::init_crs_factory("../srs_db/ignition"); - bb::srs::init_grumpkin_crs_factory("../srs_db/grumpkin"); - Goblin goblin; +class GoblinBench : public benchmark::Fixture { + public: + Goblin::AccumulationOutput kernel_accum; - // Construct an initial circuit; its proof will be recursively verified by the first kernel - GoblinUltraCircuitBuilder initial_circuit{ goblin.op_queue }; - GoblinMockCircuits::construct_simple_initial_circuit(initial_circuit); - Goblin::AccumulationOutput kernel_input = goblin.accumulate(initial_circuit); + // Number of function circuits to accumulate(based on Zacs target numbers) + static constexpr size_t NUM_ITERATIONS_MEDIUM_COMPLEXITY = 6; - Goblin::Proof proof; - for (auto _ : state) { - // Construct a series of simple Goblin circuits; generate and verify their proofs - size_t NUM_CIRCUITS = 1 << static_cast(state.range(0)); + void SetUp([[maybe_unused]] const ::benchmark::State& state) override + { + bb::srs::init_crs_factory("../srs_db/ignition"); + bb::srs::init_grumpkin_crs_factory("../srs_db/grumpkin"); + } + + /** + * @brief Perform a specified number of function circuit accumulation rounds + * @details Each round "accumulates" a mock function circuit and a mock kernel circuit. Each round thus consists of + * the generation of two circuits, two UGH proofs and two Merge proofs. To match the sizes called out in the spec + * (https://github.com/AztecProtocol/aztec-packages/blob/master/yellow-paper/docs/cryptography/performance-targets.md) + * we set the size of the function circuit to be 2^17 except for the first one which is 2^19. + * + * @param state + */ + void perform_goblin_accumulation_rounds(State& state, Goblin& goblin) + { + auto NUM_CIRCUITS = static_cast(state.range(0)); for (size_t circuit_idx = 0; circuit_idx < NUM_CIRCUITS; ++circuit_idx) { - // Construct a circuit with logic resembling that of the "kernel circuit" - GoblinUltraCircuitBuilder circuit_builder{ goblin.op_queue }; - GoblinMockCircuits::construct_mock_kernel_circuit(circuit_builder, kernel_input); - // Construct proof of the current kernel circuit to be recursively verified by the next one - kernel_input = goblin.accumulate(circuit_builder); - } + // Construct and accumulate a mock function circuit + GoblinUltraCircuitBuilder function_circuit{ goblin.op_queue }; + // On the first iteration construct a "large" function circuit (2^19), otherwise medium (2^17) + GoblinMockCircuits::construct_mock_function_circuit(function_circuit, /*large=*/circuit_idx == 0); + auto function_accum = goblin.accumulate(function_circuit); - proof = goblin.prove(); - // Verify the final ultra proof + // Construct and accumulate the mock kernel circuit + // Note: in first round, kernel_accum is empty since there is no previous kernel to recursively verify + GoblinUltraCircuitBuilder circuit_builder{ goblin.op_queue }; + GoblinMockCircuits::construct_mock_kernel_circuit(circuit_builder, function_accum, kernel_accum); + kernel_accum = goblin.accumulate(circuit_builder); + } } - honk::GoblinUltraVerifier ultra_verifier{ kernel_input.verification_key }; - ultra_verifier.verify_proof(kernel_input.proof); - // Verify the goblin proof (eccvm, translator, merge) - goblin.verify(proof); -} +}; -void goblin_accumulate(State& state) noexcept +/** + * @brief Benchmark the full Goblin IVC protocol + * + */ +BENCHMARK_DEFINE_F(GoblinBench, GoblinFull)(benchmark::State& state) { - bb::srs::init_crs_factory("../srs_db/ignition"); - bb::srs::init_grumpkin_crs_factory("../srs_db/grumpkin"); - Goblin goblin; - // Construct an initial circuit; its proof will be recursively verified by the first kernel - GoblinUltraCircuitBuilder initial_circuit{ goblin.op_queue }; - GoblinMockCircuits::construct_simple_initial_circuit(initial_circuit); - Goblin::AccumulationOutput kernel_input = goblin.accumulate(initial_circuit); + // TODO(https://github.com/AztecProtocol/barretenberg/issues/723): Simply populate the OpQueue with some data + // and corresponding commitments so the merge protocol has "prev" data into which it can accumulate + GoblinMockCircuits::perform_op_queue_interactions_for_mock_first_circuit(goblin.op_queue); - // Construct a series of simple Goblin circuits; generate and verify their proofs - size_t NUM_CIRCUITS = 1 << static_cast(state.range(0)); for (auto _ : state) { - for (size_t circuit_idx = 0; circuit_idx < NUM_CIRCUITS; ++circuit_idx) { - // Construct a circuit with logic resembling that of the "kernel circuit" - GoblinUltraCircuitBuilder circuit_builder{ goblin.op_queue }; - GoblinMockCircuits::construct_mock_kernel_circuit(circuit_builder, kernel_input); + // Perform a specified number of iterations of function/kernel accumulation + perform_goblin_accumulation_rounds(state, goblin); - // Construct proof of the current kernel circuit to be recursively verified by the next one - kernel_input = goblin.accumulate(circuit_builder); - } + // Construct proofs for ECCVM and Translator + goblin.prove(); } } -void goblin_eccvm_prove(State& state) noexcept +/** + * @brief Benchmark only the accumulation rounds + * + */ +BENCHMARK_DEFINE_F(GoblinBench, GoblinAccumulate)(benchmark::State& state) { - bb::srs::init_crs_factory("../srs_db/ignition"); - bb::srs::init_grumpkin_crs_factory("../srs_db/grumpkin"); - Goblin goblin; - // Construct an initial circuit; its proof will be recursively verified by the first kernel - GoblinUltraCircuitBuilder initial_circuit{ goblin.op_queue }; - GoblinMockCircuits::construct_simple_initial_circuit(initial_circuit); - Goblin::AccumulationOutput kernel_input = goblin.accumulate(initial_circuit); - - // Construct a series of simple Goblin circuits; generate and verify their proofs - size_t NUM_CIRCUITS = 1 << static_cast(state.range(0)); - for (size_t circuit_idx = 0; circuit_idx < NUM_CIRCUITS; ++circuit_idx) { - // Construct a circuit with logic resembling that of the "kernel circuit" - GoblinUltraCircuitBuilder circuit_builder{ goblin.op_queue }; - GoblinMockCircuits::construct_mock_kernel_circuit(circuit_builder, kernel_input); + // TODO(https://github.com/AztecProtocol/barretenberg/issues/723) + GoblinMockCircuits::perform_op_queue_interactions_for_mock_first_circuit(goblin.op_queue); - // Construct proof of the current kernel circuit to be recursively verified by the next one - kernel_input = goblin.accumulate(circuit_builder); + // Perform a specified number of iterations of function/kernel accumulation + for (auto _ : state) { + perform_goblin_accumulation_rounds(state, goblin); } +} + +/** + * @brief Benchmark only the ECCVM component + * + */ +BENCHMARK_DEFINE_F(GoblinBench, GoblinECCVMProve)(benchmark::State& state) +{ + Goblin goblin; + // TODO(https://github.com/AztecProtocol/barretenberg/issues/723) + GoblinMockCircuits::perform_op_queue_interactions_for_mock_first_circuit(goblin.op_queue); + + // Perform a specified number of iterations of function/kernel accumulation + perform_goblin_accumulation_rounds(state, goblin); + + // Prove ECCVM only for (auto _ : state) { goblin.prove_eccvm(); } } -void goblin_translator_prove(State& state) noexcept +/** + * @brief Benchmark only the Translator component + * + */ +BENCHMARK_DEFINE_F(GoblinBench, GoblinTranslatorProve)(benchmark::State& state) { - bb::srs::init_crs_factory("../srs_db/ignition"); - bb::srs::init_grumpkin_crs_factory("../srs_db/grumpkin"); - Goblin goblin; - // Construct an initial circuit; its proof will be recursively verified by the first kernel - GoblinUltraCircuitBuilder initial_circuit{ goblin.op_queue }; - GoblinMockCircuits::construct_simple_initial_circuit(initial_circuit); - Goblin::AccumulationOutput kernel_input = goblin.accumulate(initial_circuit); + // TODO(https://github.com/AztecProtocol/barretenberg/issues/723) + GoblinMockCircuits::perform_op_queue_interactions_for_mock_first_circuit(goblin.op_queue); - // Construct a series of simple Goblin circuits; generate and verify their proofs - size_t NUM_CIRCUITS = 1 << static_cast(state.range(0)); - for (size_t circuit_idx = 0; circuit_idx < NUM_CIRCUITS; ++circuit_idx) { - // Construct a circuit with logic resembling that of the "kernel circuit" - GoblinUltraCircuitBuilder circuit_builder{ goblin.op_queue }; - GoblinMockCircuits::construct_mock_kernel_circuit(circuit_builder, kernel_input); - - // Construct proof of the current kernel circuit to be recursively verified by the next one - kernel_input = goblin.accumulate(circuit_builder); - } + // Perform a specified number of iterations of function/kernel accumulation + perform_goblin_accumulation_rounds(state, goblin); + // Prove ECCVM (unmeasured) and Translator (measured) goblin.prove_eccvm(); for (auto _ : state) { goblin.prove_translator(); } } +#define ARGS \ + Arg(GoblinBench::NUM_ITERATIONS_MEDIUM_COMPLEXITY) \ + ->Arg(1 << 0) \ + ->Arg(1 << 1) \ + ->Arg(1 << 2) \ + ->Arg(1 << 3) \ + ->Arg(1 << 4) \ + ->Arg(1 << 5) \ + ->Arg(1 << 6) + +BENCHMARK_REGISTER_F(GoblinBench, GoblinFull)->Unit(benchmark::kMillisecond)->ARGS; +BENCHMARK_REGISTER_F(GoblinBench, GoblinAccumulate)->Unit(benchmark::kMillisecond)->ARGS; +BENCHMARK_REGISTER_F(GoblinBench, GoblinECCVMProve)->Unit(benchmark::kMillisecond)->ARGS; +BENCHMARK_REGISTER_F(GoblinBench, GoblinTranslatorProve)->Unit(benchmark::kMillisecond)->ARGS; + } // namespace -BENCHMARK(goblin_full)->Unit(kMillisecond)->DenseRange(0, 7); -BENCHMARK(goblin_accumulate)->Unit(kMillisecond)->DenseRange(0, 7); -BENCHMARK(goblin_eccvm_prove)->Unit(kMillisecond)->DenseRange(0, 7); -BENCHMARK(goblin_translator_prove)->Unit(kMillisecond)->DenseRange(0, 7); \ No newline at end of file +BENCHMARK_MAIN(); diff --git a/barretenberg/cpp/src/barretenberg/benchmark/goblin_bench/main.bench.cpp b/barretenberg/cpp/src/barretenberg/benchmark/goblin_bench/main.bench.cpp deleted file mode 100644 index 71fefa04722..00000000000 --- a/barretenberg/cpp/src/barretenberg/benchmark/goblin_bench/main.bench.cpp +++ /dev/null @@ -1,3 +0,0 @@ -#include - -BENCHMARK_MAIN(); diff --git a/barretenberg/cpp/src/barretenberg/benchmark/ipa_bench/CMakeLists.txt b/barretenberg/cpp/src/barretenberg/benchmark/ipa_bench/CMakeLists.txt index 6e66ce2cf36..25d49f8ddea 100644 --- a/barretenberg/cpp/src/barretenberg/benchmark/ipa_bench/CMakeLists.txt +++ b/barretenberg/cpp/src/barretenberg/benchmark/ipa_bench/CMakeLists.txt @@ -1,18 +1 @@ -# Each source represents a separate benchmark suite -set(BENCHMARK_SOURCES - ipa.bench.cpp -) - -# Required libraries for benchmark suites -set(LINKED_LIBRARIES - benchmark::benchmark - ultra_honk -) - -# Add executable and custom target for each suite, e.g. ultra_honk_bench -foreach(BENCHMARK_SOURCE ${BENCHMARK_SOURCES}) - get_filename_component(BENCHMARK_NAME ${BENCHMARK_SOURCE} NAME_WE) # extract name without extension - add_executable(${BENCHMARK_NAME}_bench ${BENCHMARK_SOURCE}) - target_link_libraries(${BENCHMARK_NAME}_bench ${LINKED_LIBRARIES}) - add_custom_target(run_${BENCHMARK_NAME} COMMAND ${BENCHMARK_NAME} WORKING_DIRECTORY ${CMAKE_BINARY_DIR}) -endforeach() \ No newline at end of file +barretenberg_module(ipa_bench ultra_honk) diff --git a/barretenberg/cpp/src/barretenberg/benchmark/pippenger_bench/CMakeLists.txt b/barretenberg/cpp/src/barretenberg/benchmark/pippenger_bench/CMakeLists.txt index bbcbaa8677d..95bf621c1e0 100644 --- a/barretenberg/cpp/src/barretenberg/benchmark/pippenger_bench/CMakeLists.txt +++ b/barretenberg/cpp/src/barretenberg/benchmark/pippenger_bench/CMakeLists.txt @@ -1,13 +1,15 @@ -add_executable(pippenger_bench main.cpp) +if (NOT FUZZING) + add_executable(pippenger_bench main.cpp) -target_link_libraries( - pippenger_bench - polynomials - srs -) + target_link_libraries( + pippenger_bench + polynomials + srs + ) -add_custom_target( - run_pippenger_bench - COMMAND pippenger_bench - WORKING_DIRECTORY ${CMAKE_BINARY_DIR} -) \ No newline at end of file + add_custom_target( + run_pippenger_bench + COMMAND pippenger_bench + WORKING_DIRECTORY ${CMAKE_BINARY_DIR} + ) +endif() \ No newline at end of file diff --git a/barretenberg/cpp/src/barretenberg/benchmark/plonk_bench/CMakeLists.txt b/barretenberg/cpp/src/barretenberg/benchmark/plonk_bench/CMakeLists.txt index 0746ecaa44e..ddcf9da9d11 100644 --- a/barretenberg/cpp/src/barretenberg/benchmark/plonk_bench/CMakeLists.txt +++ b/barretenberg/cpp/src/barretenberg/benchmark/plonk_bench/CMakeLists.txt @@ -1,20 +1 @@ -# Each source represents a separate benchmark suite -set(BENCHMARK_SOURCES - plonk.bench.cpp - standard_plonk.bench.cpp -) - -# Required libraries for benchmark suites -set(LINKED_LIBRARIES - plonk - stdlib_primitives - benchmark::benchmark -) - -# Add executable and custom target for each suite, e.g. ultra_honk_bench -foreach(BENCHMARK_SOURCE ${BENCHMARK_SOURCES}) - get_filename_component(BENCHMARK_NAME ${BENCHMARK_SOURCE} NAME_WE) # extract name without extension - add_executable(${BENCHMARK_NAME}_bench main.bench.cpp ${BENCHMARK_SOURCE}) - target_link_libraries(${BENCHMARK_NAME}_bench ${LINKED_LIBRARIES}) - add_custom_target(run_${BENCHMARK_NAME} COMMAND ${BENCHMARK_NAME} WORKING_DIRECTORY ${CMAKE_BINARY_DIR}) -endforeach() +barretenberg_module(plonk_bench plonk stdlib_primitives) diff --git a/barretenberg/cpp/src/barretenberg/benchmark/plonk_bench/main.bench.cpp b/barretenberg/cpp/src/barretenberg/benchmark/plonk_bench/main.bench.cpp deleted file mode 100644 index 71fefa04722..00000000000 --- a/barretenberg/cpp/src/barretenberg/benchmark/plonk_bench/main.bench.cpp +++ /dev/null @@ -1,3 +0,0 @@ -#include - -BENCHMARK_MAIN(); diff --git a/barretenberg/cpp/src/barretenberg/benchmark/plonk_bench/plonk.bench.cpp b/barretenberg/cpp/src/barretenberg/benchmark/plonk_bench/plonk.bench.cpp index 0089fa48b42..64cfe4f92f2 100644 --- a/barretenberg/cpp/src/barretenberg/benchmark/plonk_bench/plonk.bench.cpp +++ b/barretenberg/cpp/src/barretenberg/benchmark/plonk_bench/plonk.bench.cpp @@ -107,4 +107,6 @@ void verify_proofs_bench(State& state) noexcept state.ResumeTiming(); } } -BENCHMARK(verify_proofs_bench)->RangeMultiplier(2)->Range(START, MAX_GATES); \ No newline at end of file +BENCHMARK(verify_proofs_bench)->RangeMultiplier(2)->Range(START, MAX_GATES); + +BENCHMARK_MAIN(); diff --git a/barretenberg/cpp/src/barretenberg/benchmark/plonk_bench/standard_plonk.bench.cpp b/barretenberg/cpp/src/barretenberg/benchmark/plonk_bench/standard_plonk.bench.cpp index 1a1eff70e7a..c4d85d3fde0 100644 --- a/barretenberg/cpp/src/barretenberg/benchmark/plonk_bench/standard_plonk.bench.cpp +++ b/barretenberg/cpp/src/barretenberg/benchmark/plonk_bench/standard_plonk.bench.cpp @@ -20,4 +20,6 @@ static void construct_proof_standard_power_of_2(State& state) noexcept BENCHMARK(construct_proof_standard_power_of_2) // 2**15 gates to 2**20 gates ->DenseRange(15, 20) - ->Unit(::benchmark::kMillisecond); \ No newline at end of file + ->Unit(::benchmark::kMillisecond); + +BENCHMARK_MAIN(); \ No newline at end of file diff --git a/barretenberg/cpp/src/barretenberg/benchmark/protogalaxy_bench/CMakeLists.txt b/barretenberg/cpp/src/barretenberg/benchmark/protogalaxy_bench/CMakeLists.txt index 75ba98e2d9e..5547f9311d3 100644 --- a/barretenberg/cpp/src/barretenberg/benchmark/protogalaxy_bench/CMakeLists.txt +++ b/barretenberg/cpp/src/barretenberg/benchmark/protogalaxy_bench/CMakeLists.txt @@ -1,20 +1 @@ -# Each source represents a separate benchmark suite -set(BENCHMARK_SOURCES - protogalaxy.bench.cpp -) - -# Required libraries for benchmark suites -set(LINKED_LIBRARIES - ultra_honk - protogalaxy - stdlib_primitives - benchmark::benchmark -) - -# Add executable and custom target for each suite, e.g. ultra_honk_bench -foreach(BENCHMARK_SOURCE ${BENCHMARK_SOURCES}) - get_filename_component(BENCHMARK_NAME ${BENCHMARK_SOURCE} NAME_WE) # extract name without extension - add_executable(${BENCHMARK_NAME}_bench main.bench.cpp ${BENCHMARK_SOURCE}) - target_link_libraries(${BENCHMARK_NAME}_bench ${LINKED_LIBRARIES}) - add_custom_target(run_${BENCHMARK_NAME} COMMAND ${BENCHMARK_NAME} WORKING_DIRECTORY ${CMAKE_BINARY_DIR}) -endforeach() \ No newline at end of file +barretenberg_module(protogalaxy_bench ultra_honk protogalaxy stdlib_primitives) diff --git a/barretenberg/cpp/src/barretenberg/benchmark/protogalaxy_bench/main.bench.cpp b/barretenberg/cpp/src/barretenberg/benchmark/protogalaxy_bench/main.bench.cpp deleted file mode 100644 index 71fefa04722..00000000000 --- a/barretenberg/cpp/src/barretenberg/benchmark/protogalaxy_bench/main.bench.cpp +++ /dev/null @@ -1,3 +0,0 @@ -#include - -BENCHMARK_MAIN(); diff --git a/barretenberg/cpp/src/barretenberg/benchmark/protogalaxy_bench/protogalaxy.bench.cpp b/barretenberg/cpp/src/barretenberg/benchmark/protogalaxy_bench/protogalaxy.bench.cpp index a4164418e4a..26bebca3da7 100644 --- a/barretenberg/cpp/src/barretenberg/benchmark/protogalaxy_bench/protogalaxy.bench.cpp +++ b/barretenberg/cpp/src/barretenberg/benchmark/protogalaxy_bench/protogalaxy.bench.cpp @@ -30,7 +30,7 @@ void fold_one(State& state) noexcept std::shared_ptr instance_1 = construct_instance(); std::shared_ptr instance_2 = construct_instance(); - auto folding_prover = composer.create_folding_prover({ instance_1, instance_2 }, composer.commitment_key); + auto folding_prover = composer.create_folding_prover({ instance_1, instance_2 }); for (auto _ : state) { auto proof = folding_prover.fold_instances(); @@ -38,4 +38,6 @@ void fold_one(State& state) noexcept } BENCHMARK(fold_one)->/* vary the circuit size */ DenseRange(14, 20)->Unit(kMillisecond); -} // namespace bb::honk \ No newline at end of file +} // namespace bb::honk + +BENCHMARK_MAIN(); diff --git a/barretenberg/cpp/src/barretenberg/benchmark/relations_bench/CMakeLists.txt b/barretenberg/cpp/src/barretenberg/benchmark/relations_bench/CMakeLists.txt index 31f4834f4bc..7b4f4e0307b 100644 --- a/barretenberg/cpp/src/barretenberg/benchmark/relations_bench/CMakeLists.txt +++ b/barretenberg/cpp/src/barretenberg/benchmark/relations_bench/CMakeLists.txt @@ -1,20 +1 @@ -# Each source represents a separate benchmark suite -set(BENCHMARK_SOURCES - barycentric.bench.cpp - relations.bench.cpp -) - -# Required libraries for benchmark suites -set(LINKED_LIBRARIES - proof_system - benchmark::benchmark - transcript -) - -# Add executable and custom target for each suite, e.g. ultra_honk_bench -foreach(BENCHMARK_SOURCE ${BENCHMARK_SOURCES}) - get_filename_component(BENCHMARK_NAME ${BENCHMARK_SOURCE} NAME_WE) # extract name without extension - add_executable(${BENCHMARK_NAME}_bench main.bench.cpp ${BENCHMARK_SOURCE}) - target_link_libraries(${BENCHMARK_NAME}_bench ${LINKED_LIBRARIES}) - add_custom_target(run_${BENCHMARK_NAME} COMMAND ${BENCHMARK_NAME} WORKING_DIRECTORY ${CMAKE_BINARY_DIR}) -endforeach() \ No newline at end of file +barretenberg_module(relations_bench proof_system transcript) diff --git a/barretenberg/cpp/src/barretenberg/benchmark/relations_bench/barycentric.bench.cpp b/barretenberg/cpp/src/barretenberg/benchmark/relations_bench/barycentric.bench.cpp index b11d6b4771a..64db936c71a 100644 --- a/barretenberg/cpp/src/barretenberg/benchmark/relations_bench/barycentric.bench.cpp +++ b/barretenberg/cpp/src/barretenberg/benchmark/relations_bench/barycentric.bench.cpp @@ -23,4 +23,6 @@ void extend_2_to_6(State& state) noexcept } BENCHMARK(extend_2_to_6); -} // namespace bb::benchmark \ No newline at end of file +} // namespace bb::benchmark + +BENCHMARK_MAIN(); \ No newline at end of file diff --git a/barretenberg/cpp/src/barretenberg/benchmark/relations_bench/main.bench.cpp b/barretenberg/cpp/src/barretenberg/benchmark/relations_bench/main.bench.cpp deleted file mode 100644 index 71fefa04722..00000000000 --- a/barretenberg/cpp/src/barretenberg/benchmark/relations_bench/main.bench.cpp +++ /dev/null @@ -1,3 +0,0 @@ -#include - -BENCHMARK_MAIN(); diff --git a/barretenberg/cpp/src/barretenberg/benchmark/relations_bench/relations.bench.cpp b/barretenberg/cpp/src/barretenberg/benchmark/relations_bench/relations.bench.cpp index 7541c609754..076da57aa17 100644 --- a/barretenberg/cpp/src/barretenberg/benchmark/relations_bench/relations.bench.cpp +++ b/barretenberg/cpp/src/barretenberg/benchmark/relations_bench/relations.bench.cpp @@ -57,3 +57,5 @@ BENCHMARK(execute_relation>); BENCHMARK(execute_relation>); } // namespace bb::benchmark::relations + +BENCHMARK_MAIN(); \ No newline at end of file diff --git a/barretenberg/cpp/src/barretenberg/benchmark/ultra_bench/CMakeLists.txt b/barretenberg/cpp/src/barretenberg/benchmark/ultra_bench/CMakeLists.txt index 9c0826d009c..323ad1613b0 100644 --- a/barretenberg/cpp/src/barretenberg/benchmark/ultra_bench/CMakeLists.txt +++ b/barretenberg/cpp/src/barretenberg/benchmark/ultra_bench/CMakeLists.txt @@ -1,25 +1,6 @@ -# Each source represents a separate benchmark suite -set(BENCHMARK_SOURCES - ultra_honk.bench.cpp - ultra_honk_rounds.bench.cpp - ultra_plonk.bench.cpp - ultra_plonk_rounds.bench.cpp -) - -# Required libraries for benchmark suites -set(LINKED_LIBRARIES +barretenberg_module(ultra_bench ultra_honk stdlib_sha256 stdlib_keccak stdlib_merkle_tree - stdlib_recursion - benchmark::benchmark -) - -# Add executable and custom target for each suite, e.g. ultra_honk_bench -foreach(BENCHMARK_SOURCE ${BENCHMARK_SOURCES}) - get_filename_component(BENCHMARK_NAME ${BENCHMARK_SOURCE} NAME_WE) # extract name without extension - add_executable(${BENCHMARK_NAME}_bench main.bench.cpp ${BENCHMARK_SOURCE}) - target_link_libraries(${BENCHMARK_NAME}_bench ${LINKED_LIBRARIES}) - add_custom_target(run_${BENCHMARK_NAME} COMMAND ${BENCHMARK_NAME} WORKING_DIRECTORY ${CMAKE_BINARY_DIR}) -endforeach() \ No newline at end of file + stdlib_recursion) diff --git a/barretenberg/cpp/src/barretenberg/benchmark/ultra_bench/main.bench.cpp b/barretenberg/cpp/src/barretenberg/benchmark/ultra_bench/main.bench.cpp deleted file mode 100644 index 71fefa04722..00000000000 --- a/barretenberg/cpp/src/barretenberg/benchmark/ultra_bench/main.bench.cpp +++ /dev/null @@ -1,3 +0,0 @@ -#include - -BENCHMARK_MAIN(); diff --git a/barretenberg/cpp/src/barretenberg/benchmark/ultra_bench/mock_proofs.hpp b/barretenberg/cpp/src/barretenberg/benchmark/ultra_bench/mock_proofs.hpp index 6bbc81a5872..369a5bd8576 100644 --- a/barretenberg/cpp/src/barretenberg/benchmark/ultra_bench/mock_proofs.hpp +++ b/barretenberg/cpp/src/barretenberg/benchmark/ultra_bench/mock_proofs.hpp @@ -2,6 +2,7 @@ #include #include +#include "barretenberg/goblin/mock_circuits.hpp" #include "barretenberg/plonk/composer/standard_composer.hpp" #include "barretenberg/plonk/composer/ultra_composer.hpp" #include "barretenberg/proof_system/types/circuit_type.hpp" @@ -35,7 +36,7 @@ template void generate_basic_arithmetic_circuit(Builder& buil stdlib::field_t c(&builder); size_t passes = (1UL << log2_num_gates) / 4 - 4; if (static_cast(passes) <= 0) { - throw std::runtime_error("too few gates"); + throw_or_abort("too few gates"); } for (size_t i = 0; i < passes; ++i) { @@ -46,121 +47,6 @@ template void generate_basic_arithmetic_circuit(Builder& buil } } -/** - * @brief Generate test circuit with specified number of sha256 hashes - * - * @param builder - * @param num_iterations - */ -template void generate_sha256_test_circuit(Builder& builder, size_t num_iterations) -{ - std::string in; - in.resize(32); - stdlib::packed_byte_array input(&builder, in); - for (size_t i = 0; i < num_iterations; i++) { - input = stdlib::sha256(input); - } -} - -/** - * @brief Generate test circuit with specified number of keccak hashes - * - * @param builder - * @param num_iterations - */ -template void generate_keccak_test_circuit(Builder& builder, size_t num_iterations) -{ - std::string in = "abcdefghijklmnopqrstuvwxyz0123456789abcdefghijklmnopqrstuvwxyz01"; - - stdlib::byte_array input(&builder, in); - for (size_t i = 0; i < num_iterations; i++) { - input = stdlib::keccak::hash(input); - } -} - -/** - * @brief Generate test circuit with specified number of ecdsa verifications - * - * @param builder - * @param num_iterations - */ -template void generate_ecdsa_verification_test_circuit(Builder& builder, size_t num_iterations) -{ - using curve = stdlib::secp256k1; - using fr = typename curve::fr; - using fq = typename curve::fq; - using g1 = typename curve::g1; - - std::string message_string = "Instructions unclear, ask again later."; - - crypto::ecdsa_key_pair account; - for (size_t i = 0; i < num_iterations; i++) { - // Generate unique signature for each iteration - account.private_key = curve::fr::random_element(); - account.public_key = curve::g1::one * account.private_key; - - crypto::ecdsa_signature signature = - crypto::ecdsa_construct_signature(message_string, account); - - bool first_result = - crypto::ecdsa_verify_signature(message_string, account.public_key, signature); - static_cast(first_result); // TODO(Cody): This is not used anywhere. - - std::vector rr(signature.r.begin(), signature.r.end()); - std::vector ss(signature.s.begin(), signature.s.end()); - uint8_t vv = signature.v; - - typename curve::g1_bigfr_ct public_key = curve::g1_bigfr_ct::from_witness(&builder, account.public_key); - - stdlib::ecdsa_signature sig{ typename curve::byte_array_ct(&builder, rr), - typename curve::byte_array_ct(&builder, ss), - stdlib::uint8(&builder, vv) }; - - typename curve::byte_array_ct message(&builder, message_string); - - // Verify ecdsa signature - stdlib::ecdsa_verify_signature(message, public_key, sig); - } -} - -/** - * @brief Generate test circuit with specified number of merkle membership checks - * - * @param builder - * @param num_iterations - */ -template void generate_merkle_membership_test_circuit(Builder& builder, size_t num_iterations) -{ - using namespace stdlib; - using field_ct = field_t; - using witness_ct = witness_t; - using witness_ct = witness_t; - using MemStore = merkle_tree::MemoryStore; - using MerkleTree_ct = merkle_tree::MerkleTree; - - MemStore store; - const size_t tree_depth = 7; - auto merkle_tree = MerkleTree_ct(store, tree_depth); - - for (size_t i = 0; i < num_iterations; i++) { - // For each iteration update and check the membership of a different value - size_t idx = i; - size_t value = i * 2; - merkle_tree.update_element(idx, value); - - field_ct root_ct = witness_ct(&builder, merkle_tree.root()); - auto idx_ct = field_ct(witness_ct(&builder, fr(idx))).decompose_into_bits(); - auto value_ct = field_ct(value); - - merkle_tree::check_membership( - root_ct, merkle_tree::create_witness_hash_path(builder, merkle_tree.get_hash_path(idx)), value_ct, idx_ct); - } -} - // ultrahonk inline honk::UltraProver get_prover(honk::UltraComposer& composer, void (*test_circuit_function)(honk::UltraComposer::CircuitBuilder&, size_t), diff --git a/barretenberg/cpp/src/barretenberg/benchmark/ultra_bench/ultra_honk.bench.cpp b/barretenberg/cpp/src/barretenberg/benchmark/ultra_bench/ultra_honk.bench.cpp index f560475a7af..c0320b135a3 100644 --- a/barretenberg/cpp/src/barretenberg/benchmark/ultra_bench/ultra_honk.bench.cpp +++ b/barretenberg/cpp/src/barretenberg/benchmark/ultra_bench/ultra_honk.bench.cpp @@ -29,24 +29,22 @@ static void construct_proof_ultrahonk_power_of_2(State& state) noexcept } // Define benchmarks -BENCHMARK_CAPTURE(construct_proof_ultrahonk, - sha256, - &bb::mock_proofs::generate_sha256_test_circuit) +BENCHMARK_CAPTURE(construct_proof_ultrahonk, sha256, &stdlib::generate_sha256_test_circuit) ->Unit(kMillisecond); -BENCHMARK_CAPTURE(construct_proof_ultrahonk, - keccak, - &bb::mock_proofs::generate_keccak_test_circuit) +BENCHMARK_CAPTURE(construct_proof_ultrahonk, keccak, &stdlib::generate_keccak_test_circuit) ->Unit(kMillisecond); BENCHMARK_CAPTURE(construct_proof_ultrahonk, ecdsa_verification, - &bb::mock_proofs::generate_ecdsa_verification_test_circuit) + &stdlib::generate_ecdsa_verification_test_circuit) ->Unit(kMillisecond); BENCHMARK_CAPTURE(construct_proof_ultrahonk, merkle_membership, - &bb::mock_proofs::generate_merkle_membership_test_circuit) + &stdlib::generate_merkle_membership_test_circuit) ->Unit(kMillisecond); BENCHMARK(construct_proof_ultrahonk_power_of_2) // 2**15 gates to 2**20 gates ->DenseRange(15, 20) - ->Unit(kMillisecond); \ No newline at end of file + ->Unit(kMillisecond); + +BENCHMARK_MAIN(); diff --git a/barretenberg/cpp/src/barretenberg/benchmark/ultra_bench/ultra_honk_rounds.bench.cpp b/barretenberg/cpp/src/barretenberg/benchmark/ultra_bench/ultra_honk_rounds.bench.cpp index 8fd06307cec..18066f4d370 100644 --- a/barretenberg/cpp/src/barretenberg/benchmark/ultra_bench/ultra_honk_rounds.bench.cpp +++ b/barretenberg/cpp/src/barretenberg/benchmark/ultra_bench/ultra_honk_rounds.bench.cpp @@ -56,7 +56,7 @@ BBERG_PROFILE static void test_round(State& state, size_t index) noexcept honk::UltraComposer composer; // TODO(https://github.com/AztecProtocol/barretenberg/issues/761) benchmark both sparse and dense circuits honk::UltraProver prover = bb::mock_proofs::get_prover( - composer, &bb::mock_proofs::generate_ecdsa_verification_test_circuit, 10); + composer, &bb::stdlib::generate_ecdsa_verification_test_circuit, 10); test_round_inner(state, prover, index); state.ResumeTiming(); // NOTE: google bench is very finnicky, must end in ResumeTiming() for correctness @@ -78,3 +78,5 @@ ROUND_BENCHMARK(LOG_DERIVATIVE_INVERSE)->Iterations(1); ROUND_BENCHMARK(GRAND_PRODUCT_COMPUTATION)->Iterations(1); ROUND_BENCHMARK(RELATION_CHECK); ROUND_BENCHMARK(ZEROMORPH); + +BENCHMARK_MAIN(); diff --git a/barretenberg/cpp/src/barretenberg/benchmark/ultra_bench/ultra_plonk.bench.cpp b/barretenberg/cpp/src/barretenberg/benchmark/ultra_bench/ultra_plonk.bench.cpp index a02fba5eb8a..6682aeb5d90 100644 --- a/barretenberg/cpp/src/barretenberg/benchmark/ultra_bench/ultra_plonk.bench.cpp +++ b/barretenberg/cpp/src/barretenberg/benchmark/ultra_bench/ultra_plonk.bench.cpp @@ -27,24 +27,22 @@ static void construct_proof_ultraplonk_power_of_2(State& state) noexcept } // Define benchmarks -BENCHMARK_CAPTURE(construct_proof_ultraplonk, - sha256, - &bb::mock_proofs::generate_sha256_test_circuit) +BENCHMARK_CAPTURE(construct_proof_ultraplonk, sha256, &stdlib::generate_sha256_test_circuit) ->Unit(kMillisecond); -BENCHMARK_CAPTURE(construct_proof_ultraplonk, - keccak, - &bb::mock_proofs::generate_keccak_test_circuit) +BENCHMARK_CAPTURE(construct_proof_ultraplonk, keccak, &stdlib::generate_keccak_test_circuit) ->Unit(kMillisecond); BENCHMARK_CAPTURE(construct_proof_ultraplonk, ecdsa_verification, - &bb::mock_proofs::generate_ecdsa_verification_test_circuit) + &stdlib::generate_ecdsa_verification_test_circuit) ->Unit(kMillisecond); BENCHMARK_CAPTURE(construct_proof_ultraplonk, merkle_membership, - &bb::mock_proofs::generate_merkle_membership_test_circuit) + &stdlib::generate_merkle_membership_test_circuit) ->Unit(kMillisecond); BENCHMARK(construct_proof_ultraplonk_power_of_2) // 2**15 gates to 2**20 gates ->DenseRange(15, 20) - ->Unit(kMillisecond); \ No newline at end of file + ->Unit(kMillisecond); + +BENCHMARK_MAIN(); diff --git a/barretenberg/cpp/src/barretenberg/benchmark/ultra_bench/ultra_plonk_rounds.bench.cpp b/barretenberg/cpp/src/barretenberg/benchmark/ultra_bench/ultra_plonk_rounds.bench.cpp index b78b825ce77..45e82fe144e 100644 --- a/barretenberg/cpp/src/barretenberg/benchmark/ultra_bench/ultra_plonk_rounds.bench.cpp +++ b/barretenberg/cpp/src/barretenberg/benchmark/ultra_bench/ultra_plonk_rounds.bench.cpp @@ -55,7 +55,7 @@ BBERG_PROFILE static void test_round(State& state, size_t index) noexcept plonk::UltraComposer composer; // TODO: https://github.com/AztecProtocol/barretenberg/issues/761 benchmark both sparse and dense circuits plonk::UltraProver prover = bb::mock_proofs::get_prover( - composer, &bb::mock_proofs::generate_ecdsa_verification_test_circuit, 10); + composer, &bb::stdlib::generate_ecdsa_verification_test_circuit, 10); test_round_inner(state, prover, index); // NOTE: google bench is very finnicky, must end in ResumeTiming() for correctness state.ResumeTiming(); @@ -77,3 +77,5 @@ ROUND_BENCHMARK(THIRD_FIAT_SHAMIR_BETA_GAMMA)->Iterations(1); ROUND_BENCHMARK(FOURTH_FIAT_SHAMIR_ALPHA_AND_COMMIT)->Iterations(1); ROUND_BENCHMARK(FIFTH_COMPUTE_QUOTIENT_EVALUTION)->Iterations(1); ROUND_BENCHMARK(SIXTH_BATCH_OPEN)->Iterations(1); + +BENCHMARK_MAIN(); diff --git a/barretenberg/cpp/src/barretenberg/benchmark/widgets_bench/CMakeLists.txt b/barretenberg/cpp/src/barretenberg/benchmark/widgets_bench/CMakeLists.txt index 35eebc6d27d..588ffe92d33 100644 --- a/barretenberg/cpp/src/barretenberg/benchmark/widgets_bench/CMakeLists.txt +++ b/barretenberg/cpp/src/barretenberg/benchmark/widgets_bench/CMakeLists.txt @@ -1,21 +1 @@ -# Each source represents a separate benchmark suite -set(BENCHMARK_SOURCES - widget.bench.cpp -) - -# Required libraries for benchmark suites -set(LINKED_LIBRARIES - polynomials - proof_system - benchmark::benchmark - transcript - stdlib_primitives -) - -# Add executable and custom target for each suite, e.g. ultra_honk_bench -foreach(BENCHMARK_SOURCE ${BENCHMARK_SOURCES}) - get_filename_component(BENCHMARK_NAME ${BENCHMARK_SOURCE} NAME_WE) # extract name without extension - add_executable(${BENCHMARK_NAME}_bench main.bench.cpp ${BENCHMARK_SOURCE}) - target_link_libraries(${BENCHMARK_NAME}_bench ${LINKED_LIBRARIES}) - add_custom_target(run_${BENCHMARK_NAME} COMMAND ${BENCHMARK_NAME} WORKING_DIRECTORY ${CMAKE_BINARY_DIR}) -endforeach() \ No newline at end of file +barretenberg_module(widgets_bench polynomials proof_system transcript stdlib_primitives) diff --git a/barretenberg/cpp/src/barretenberg/benchmark/widgets_bench/main.bench.cpp b/barretenberg/cpp/src/barretenberg/benchmark/widgets_bench/main.bench.cpp deleted file mode 100644 index 71fefa04722..00000000000 --- a/barretenberg/cpp/src/barretenberg/benchmark/widgets_bench/main.bench.cpp +++ /dev/null @@ -1,3 +0,0 @@ -#include - -BENCHMARK_MAIN(); diff --git a/barretenberg/cpp/src/barretenberg/benchmark/widgets_bench/widget.bench.cpp b/barretenberg/cpp/src/barretenberg/benchmark/widgets_bench/widget.bench.cpp index 9b94063e3c2..061d58cb83c 100644 --- a/barretenberg/cpp/src/barretenberg/benchmark/widgets_bench/widget.bench.cpp +++ b/barretenberg/cpp/src/barretenberg/benchmark/widgets_bench/widget.bench.cpp @@ -114,3 +114,5 @@ BENCHMARK(accumulate_contribution>); BENCHMARK(accumulate_contribution>); } // namespace bb::plonk + +BENCHMARK_MAIN(); diff --git a/barretenberg/cpp/src/barretenberg/benchmark/commit_bench/commit.bench.cpp b/barretenberg/cpp/src/barretenberg/commitment_schemes/commit.bench.cpp similarity index 86% rename from barretenberg/cpp/src/barretenberg/benchmark/commit_bench/commit.bench.cpp rename to barretenberg/cpp/src/barretenberg/commitment_schemes/commit.bench.cpp index 76990104285..e0b87c902d6 100644 --- a/barretenberg/cpp/src/barretenberg/benchmark/commit_bench/commit.bench.cpp +++ b/barretenberg/cpp/src/barretenberg/commitment_schemes/commit.bench.cpp @@ -1,5 +1,6 @@ #include "barretenberg/commitment_schemes/commitment_key.hpp" +#include "barretenberg/srs/factories/mem_bn254_crs_factory.hpp" #include namespace bb { @@ -14,8 +15,7 @@ std::shared_ptr> create_commitment_key(const siz static_assert(std::same_as); srs_path = "../srs_db/grumpkin"; } - std::shared_ptr> crs_factory( - new bb::srs::factories::FileCrsFactory(srs_path, num_points)); + auto crs_factory = std::make_shared>(srs_path, num_points); return std::make_shared>(num_points, crs_factory); } @@ -36,3 +36,5 @@ template void bench_commit(::benchmark::State& state) BENCHMARK(bench_commit)->DenseRange(10, MAX_LOG_NUM_POINTS)->Unit(benchmark::kMillisecond); } // namespace bb + +BENCHMARK_MAIN(); diff --git a/barretenberg/cpp/src/barretenberg/commitment_schemes/ipa/ipa.hpp b/barretenberg/cpp/src/barretenberg/commitment_schemes/ipa/ipa.hpp index 3a874566602..666ac255aa4 100644 --- a/barretenberg/cpp/src/barretenberg/commitment_schemes/ipa/ipa.hpp +++ b/barretenberg/cpp/src/barretenberg/commitment_schemes/ipa/ipa.hpp @@ -49,7 +49,7 @@ template class IPA { "The poly_degree should be positive and a power of two"); auto a_vec = polynomial; - auto srs_elements = ck->srs->get_monomial_points(); + auto* srs_elements = ck->srs->get_monomial_points(); std::vector G_vec_local(poly_degree); // The SRS stored in the commitment key is the result after applying the pippenger point table so the @@ -254,7 +254,7 @@ template class IPA { /*finite_field_additions_per_iteration=*/0, /*finite_field_multiplications_per_iteration=*/log_poly_degree); - auto srs_elements = vk->srs->get_monomial_points(); + auto* srs_elements = vk->srs->get_monomial_points(); // Copy the G_vector to local memory. std::vector G_vec_local(poly_degree); diff --git a/barretenberg/cpp/src/barretenberg/commitment_schemes/ipa/ipa.test.cpp b/barretenberg/cpp/src/barretenberg/commitment_schemes/ipa/ipa.test.cpp index 6aafab2fd15..e68f79f4861 100644 --- a/barretenberg/cpp/src/barretenberg/commitment_schemes/ipa/ipa.test.cpp +++ b/barretenberg/cpp/src/barretenberg/commitment_schemes/ipa/ipa.test.cpp @@ -34,7 +34,7 @@ TEST_F(IPATest, CommitOnManyZeroCoeffPolyWorks) } p[3] = Fr::one(); GroupElement commitment = this->commit(p); - auto srs_elements = this->ck()->srs->get_monomial_points(); + auto* srs_elements = this->ck()->srs->get_monomial_points(); GroupElement expected = srs_elements[0] * p[0]; // The SRS stored in the commitment key is the result after applying the pippenger point table so the // values at odd indices contain the point {srs[i-1].x * beta, srs[i-1].y}, where beta is the endomorphism @@ -50,7 +50,7 @@ TEST_F(IPATest, Commit) constexpr size_t n = 128; auto poly = this->random_polynomial(n); GroupElement commitment = this->commit(poly); - auto srs_elements = this->ck()->srs->get_monomial_points(); + auto* srs_elements = this->ck()->srs->get_monomial_points(); GroupElement expected = srs_elements[0] * poly[0]; // The SRS stored in the commitment key is the result after applying the pippenger point table so the // values at odd indices contain the point {srs[i-1].x * beta, srs[i-1].y}, where beta is the endomorphism diff --git a/barretenberg/cpp/src/barretenberg/commitment_schemes/zeromorph/zeromorph.hpp b/barretenberg/cpp/src/barretenberg/commitment_schemes/zeromorph/zeromorph.hpp index 9878925c236..8e805a54b6a 100644 --- a/barretenberg/cpp/src/barretenberg/commitment_schemes/zeromorph/zeromorph.hpp +++ b/barretenberg/cpp/src/barretenberg/commitment_schemes/zeromorph/zeromorph.hpp @@ -563,17 +563,17 @@ template class ZeroMorphVerifier_ { // If applicable, add contribution from concatenated polynomial commitments // Note: this is an implementation detail related to Goblin Translator and is not part of the standard protocol. if (!concatenation_groups_commitments.empty()) { - size_t CONCATENATION_INDEX = concatenation_groups_commitments[0].size(); - size_t MINICIRCUIT_N = N / CONCATENATION_INDEX; + size_t CONCATENATION_GROUP_SIZE = concatenation_groups_commitments[0].size(); + size_t MINICIRCUIT_N = N / CONCATENATION_GROUP_SIZE; std::vector x_shifts; auto current_x_shift = x_challenge; auto x_to_minicircuit_n = x_challenge.pow(MINICIRCUIT_N); - for (size_t i = 0; i < CONCATENATION_INDEX; ++i) { + for (size_t i = 0; i < CONCATENATION_GROUP_SIZE; ++i) { x_shifts.emplace_back(current_x_shift); current_x_shift *= x_to_minicircuit_n; } for (auto& concatenation_group_commitment : concatenation_groups_commitments) { - for (size_t i = 0; i < CONCATENATION_INDEX; ++i) { + for (size_t i = 0; i < CONCATENATION_GROUP_SIZE; ++i) { scalars.emplace_back(rho_pow * x_shifts[i]); commitments.emplace_back(concatenation_group_commitment[i]); } diff --git a/barretenberg/cpp/src/barretenberg/common/thread.cpp b/barretenberg/cpp/src/barretenberg/common/thread.cpp index 12334d272c6..22cc724b32e 100644 --- a/barretenberg/cpp/src/barretenberg/common/thread.cpp +++ b/barretenberg/cpp/src/barretenberg/common/thread.cpp @@ -20,7 +20,7 @@ * and OMP seems well designed to handle this. It actually looks like OMP consumes more cpu time in htop, and this * maybe due to aggressive spin-locking and may explain why it performs well in these scenarios. * - * My theory as to why spawning seems to counter-intuitively perfrom so well, is that spawning a new thread may actually + * My theory as to why spawning seems to counter-intuitively perform so well, is that spawning a new thread may actually * be cheaper than waking a sleeping thread. Or joining is somehow very efficient. Or it's because there's very low * other overhead. Or libc++ STL does some magic. Ok, that's not much of a theory... * diff --git a/barretenberg/cpp/src/barretenberg/common/utils.cpp b/barretenberg/cpp/src/barretenberg/common/utils.cpp new file mode 100644 index 00000000000..00431bd4a74 --- /dev/null +++ b/barretenberg/cpp/src/barretenberg/common/utils.cpp @@ -0,0 +1,17 @@ +#include "./utils.hpp" + +namespace bb::utils { + +std::vector hex_to_bytes(const std::string& hex) +{ + std::vector bytes; + + for (unsigned int i = 0; i < hex.length(); i += 2) { + std::string byteString = hex.substr(i, 2); + bytes.push_back(static_cast(strtol(byteString.c_str(), nullptr, 16))); + } + + return bytes; +} + +} // namespace bb::utils \ No newline at end of file diff --git a/barretenberg/cpp/src/barretenberg/common/utils.hpp b/barretenberg/cpp/src/barretenberg/common/utils.hpp new file mode 100644 index 00000000000..f1ff5acffcb --- /dev/null +++ b/barretenberg/cpp/src/barretenberg/common/utils.hpp @@ -0,0 +1,17 @@ +#pragma once + +#include +#include +#include + +namespace bb::utils { + +/** + * @brief Routine to transform hexstring to vector of bytes. + * + * @param Hexadecimal string representation. + * @return Vector of uint8_t values. + */ +std::vector hex_to_bytes(const std::string& hex); + +} // namespace bb::utils \ No newline at end of file diff --git a/barretenberg/cpp/src/barretenberg/crypto/ecdsa/ecdsa.test.cpp b/barretenberg/cpp/src/barretenberg/crypto/ecdsa/ecdsa.test.cpp index 76b324682a0..507c3378f26 100644 --- a/barretenberg/cpp/src/barretenberg/crypto/ecdsa/ecdsa.test.cpp +++ b/barretenberg/cpp/src/barretenberg/crypto/ecdsa/ecdsa.test.cpp @@ -1,5 +1,6 @@ #include "ecdsa.hpp" #include "barretenberg/common/serialize.hpp" +#include "barretenberg/common/utils.hpp" #include "barretenberg/ecc/curves/grumpkin/grumpkin.hpp" #include "barretenberg/ecc/curves/secp256r1/secp256r1.hpp" #include "barretenberg/serialize/test_helper.hpp" @@ -89,23 +90,10 @@ TEST(ecdsa, recover_public_key_secp256r1_sha256) EXPECT_EQ(recovered_public_key, account.public_key); } -std::vector HexToBytes(const std::string& hex) -{ - std::vector bytes; - - for (unsigned int i = 0; i < hex.length(); i += 2) { - std::string byteString = hex.substr(i, 2); - uint8_t byte = (uint8_t)strtol(byteString.c_str(), NULL, 16); - bytes.push_back(byte); - } - - return bytes; -} - TEST(ecdsa, check_overflowing_r_and_s_are_rejected) { - std::vector message_vec = HexToBytes("41414141"); + std::vector message_vec = utils::hex_to_bytes("41414141"); std::string message(message_vec.begin(), message_vec.end()); crypto::ecdsa_signature signature; @@ -181,10 +169,10 @@ TEST(ecdsa, verify_signature_secp256r1_sha256_NIST_1) }; crypto::ecdsa_signature sig{ r, s, 27 }; - std::vector message_vec = - HexToBytes("5905238877c77421f73e43ee3da6f2d9e2ccad5fc942dcec0cbd25482935faaf416983fe165b1a045ee2bcd2e6dca3bdf46" - "c4310a7461f9a37960ca672d3feb5473e253605fb1ddfd28065b53cb5858a8ad28175bf9bd386a5e471ea7a65c17cc934a9" - "d791e91491eb3754d03799790fe2d308d16146d5c9b0d0debd97d79ce8"); + std::vector message_vec = utils::hex_to_bytes( + "5905238877c77421f73e43ee3da6f2d9e2ccad5fc942dcec0cbd25482935faaf416983fe165b1a045ee2bcd2e6dca3bdf46" + "c4310a7461f9a37960ca672d3feb5473e253605fb1ddfd28065b53cb5858a8ad28175bf9bd386a5e471ea7a65c17cc934a9" + "d791e91491eb3754d03799790fe2d308d16146d5c9b0d0debd97d79ce8"); std::string message(message_vec.begin(), message_vec.end()); bool result = crypto::ecdsa_verify_signature( diff --git a/barretenberg/cpp/src/barretenberg/crypto/poseidon2/poseidon2_permutation.hpp b/barretenberg/cpp/src/barretenberg/crypto/poseidon2/poseidon2_permutation.hpp index 15a3a8bd555..12f79c040d9 100644 --- a/barretenberg/cpp/src/barretenberg/crypto/poseidon2/poseidon2_permutation.hpp +++ b/barretenberg/cpp/src/barretenberg/crypto/poseidon2/poseidon2_permutation.hpp @@ -40,9 +40,8 @@ template class Poseidon2Permutation { using MatrixDiagonal = std::array; using RoundConstantsContainer = std::array; - static constexpr MatrixDiagonal internal_matrix_diagonal = - Poseidon2Bn254ScalarFieldParams::internal_matrix_diagonal; - static constexpr RoundConstantsContainer round_constants = Poseidon2Bn254ScalarFieldParams::round_constants; + static constexpr MatrixDiagonal internal_matrix_diagonal = Params::internal_matrix_diagonal; + static constexpr RoundConstantsContainer round_constants = Params::round_constants; static constexpr void matrix_multiplication_4x4(State& input) { diff --git a/barretenberg/cpp/src/barretenberg/dsl/acir_format/acir_format.cpp b/barretenberg/cpp/src/barretenberg/dsl/acir_format/acir_format.cpp index 02fc648a321..360d43324e9 100644 --- a/barretenberg/cpp/src/barretenberg/dsl/acir_format/acir_format.cpp +++ b/barretenberg/cpp/src/barretenberg/dsl/acir_format/acir_format.cpp @@ -81,12 +81,7 @@ void build_constraints(Builder& builder, AcirFormat const& constraint_system, bo // Add ec add constraints for (const auto& constraint : constraint_system.ec_add_constraints) { - create_ec_add_constraint(builder, constraint); - } - - // Add ec double - for (const auto& constraint : constraint_system.ec_double_constraints) { - create_ec_double_constraint(builder, constraint); + create_ec_add_constraint(builder, constraint, has_valid_witness_assignments); } // Add block constraints @@ -94,6 +89,14 @@ void build_constraints(Builder& builder, AcirFormat const& constraint_system, bo create_block_constraints(builder, constraint, has_valid_witness_assignments); } + // Add big_int constraints + for (const auto& constraint : constraint_system.bigint_operations) { + create_bigint_operations_constraint(builder, constraint); + } + for (const auto& constraint : constraint_system.bigint_from_le_bytes_constraints) { + create_bigint_from_le_bytes_constraint(builder, constraint); + } + // TODO(https://github.com/AztecProtocol/barretenberg/issues/817): disable these for UGH for now since we're not yet // dealing with proper recursion if constexpr (IsGoblinBuilder) { diff --git a/barretenberg/cpp/src/barretenberg/dsl/acir_format/acir_format.hpp b/barretenberg/cpp/src/barretenberg/dsl/acir_format/acir_format.hpp index a8768d340f4..f88777038b0 100644 --- a/barretenberg/cpp/src/barretenberg/dsl/acir_format/acir_format.hpp +++ b/barretenberg/cpp/src/barretenberg/dsl/acir_format/acir_format.hpp @@ -1,6 +1,7 @@ #pragma once #include "barretenberg/common/slab_allocator.hpp" #include "barretenberg/serialize/msgpack.hpp" +#include "bigint_constraint.hpp" #include "blake2s_constraint.hpp" #include "blake3_constraint.hpp" #include "block_constraint.hpp" @@ -39,8 +40,9 @@ struct AcirFormat { std::vector pedersen_hash_constraints; std::vector fixed_base_scalar_mul_constraints; std::vector ec_add_constraints; - std::vector ec_double_constraints; std::vector recursion_constraints; + std::vector bigint_from_le_bytes_constraints; + std::vector bigint_operations; // A standard plonk arithmetic constraint, as defined in the poly_triple struct, consists of selector values // for q_M,q_L,q_R,q_O,q_C and indices of three variables taking the role of left, right and output wire @@ -67,9 +69,12 @@ struct AcirFormat { pedersen_constraints, pedersen_hash_constraints, fixed_base_scalar_mul_constraints, + ec_add_constraints, recursion_constraints, constraints, - block_constraints); + block_constraints, + bigint_from_le_bytes_constraints, + bigint_operations); friend bool operator==(AcirFormat const& lhs, AcirFormat const& rhs) = default; }; diff --git a/barretenberg/cpp/src/barretenberg/dsl/acir_format/acir_format.test.cpp b/barretenberg/cpp/src/barretenberg/dsl/acir_format/acir_format.test.cpp index 92e70b64a16..3c85f61d4d1 100644 --- a/barretenberg/cpp/src/barretenberg/dsl/acir_format/acir_format.test.cpp +++ b/barretenberg/cpp/src/barretenberg/dsl/acir_format/acir_format.test.cpp @@ -45,8 +45,9 @@ TEST_F(AcirFormatTests, TestASingleConstraintNoPubInputs) .pedersen_hash_constraints = {}, .fixed_base_scalar_mul_constraints = {}, .ec_add_constraints = {}, - .ec_double_constraints = {}, .recursion_constraints = {}, + .bigint_from_le_bytes_constraints = {}, + .bigint_operations = {}, .constraints = { constraint }, .block_constraints = {}, }; @@ -156,8 +157,9 @@ TEST_F(AcirFormatTests, TestLogicGateFromNoirCircuit) .pedersen_hash_constraints = {}, .fixed_base_scalar_mul_constraints = {}, .ec_add_constraints = {}, - .ec_double_constraints = {}, .recursion_constraints = {}, + .bigint_from_le_bytes_constraints = {}, + .bigint_operations = {}, .constraints = { expr_a, expr_b, expr_c, expr_d }, .block_constraints = {} }; @@ -219,8 +221,9 @@ TEST_F(AcirFormatTests, TestSchnorrVerifyPass) .pedersen_hash_constraints = {}, .fixed_base_scalar_mul_constraints = {}, .ec_add_constraints = {}, - .ec_double_constraints = {}, .recursion_constraints = {}, + .bigint_from_le_bytes_constraints = {}, + .bigint_operations = {}, .constraints = { poly_triple{ .a = schnorr_constraint.result, .b = schnorr_constraint.result, @@ -310,8 +313,9 @@ TEST_F(AcirFormatTests, TestSchnorrVerifySmallRange) .pedersen_hash_constraints = {}, .fixed_base_scalar_mul_constraints = {}, .ec_add_constraints = {}, - .ec_double_constraints = {}, .recursion_constraints = {}, + .bigint_from_le_bytes_constraints = {}, + .bigint_operations = {}, .constraints = { poly_triple{ .a = schnorr_constraint.result, .b = schnorr_constraint.result, @@ -420,8 +424,9 @@ TEST_F(AcirFormatTests, TestVarKeccak) .pedersen_hash_constraints = {}, .fixed_base_scalar_mul_constraints = {}, .ec_add_constraints = {}, - .ec_double_constraints = {}, .recursion_constraints = {}, + .bigint_from_le_bytes_constraints = {}, + .bigint_operations = {}, .constraints = { dummy }, .block_constraints = {}, }; @@ -462,8 +467,9 @@ TEST_F(AcirFormatTests, TestKeccakPermutation) .pedersen_hash_constraints = {}, .fixed_base_scalar_mul_constraints = {}, .ec_add_constraints = {}, - .ec_double_constraints = {}, .recursion_constraints = {}, + .bigint_from_le_bytes_constraints = {}, + .bigint_operations = {}, .constraints = {}, .block_constraints = {} }; diff --git a/barretenberg/cpp/src/barretenberg/dsl/acir_format/acir_to_constraint_buf.hpp b/barretenberg/cpp/src/barretenberg/dsl/acir_format/acir_to_constraint_buf.hpp index ee539a47ba2..058feb5e6a9 100644 --- a/barretenberg/cpp/src/barretenberg/dsl/acir_format/acir_to_constraint_buf.hpp +++ b/barretenberg/cpp/src/barretenberg/dsl/acir_format/acir_to_constraint_buf.hpp @@ -2,6 +2,7 @@ #include "acir_format.hpp" #include "barretenberg/common/container.hpp" #include "barretenberg/common/throw_or_abort.hpp" +#include "barretenberg/dsl/acir_format/bigint_constraint.hpp" #include "barretenberg/dsl/acir_format/blake2s_constraint.hpp" #include "barretenberg/dsl/acir_format/blake3_constraint.hpp" #include "barretenberg/dsl/acir_format/block_constraint.hpp" @@ -204,6 +205,15 @@ void handle_blackbox_func_call(Circuit::Opcode::BlackBoxFuncCall const& arg, Aci .pub_key_x = arg.outputs[0].value, .pub_key_y = arg.outputs[1].value, }); + } else if constexpr (std::is_same_v) { + af.ec_add_constraints.push_back(EcAdd{ + .input1_x = arg.input1_x.witness.value, + .input1_y = arg.input1_y.witness.value, + .input2_x = arg.input2_x.witness.value, + .input2_y = arg.input2_y.witness.value, + .result_x = arg.outputs[0].value, + .result_y = arg.outputs[1].value, + }); } else if constexpr (std::is_same_v) { af.keccak_constraints.push_back(KeccakConstraint{ .inputs = map(arg.inputs, @@ -240,6 +250,40 @@ void handle_blackbox_func_call(Circuit::Opcode::BlackBoxFuncCall const& arg, Aci .key_hash = arg.key_hash.witness.value, }; af.recursion_constraints.push_back(c); + } else if constexpr (std::is_same_v) { + af.bigint_from_le_bytes_constraints.push_back(BigIntFromLeBytes{ + .inputs = map(arg.inputs, [](auto& e) { return e.witness.value; }), + .modulus = map(arg.modulus, [](auto& e) -> uint32_t { return e; }), + .result = arg.output, + }); + } else if constexpr (std::is_same_v) { + af.bigint_operations.push_back(BigIntOperation{ + .lhs = arg.lhs, + .rhs = arg.rhs, + .result = arg.output, + .opcode = BigIntOperationType::Add, + }); + } else if constexpr (std::is_same_v) { + af.bigint_operations.push_back(BigIntOperation{ + .lhs = arg.lhs, + .rhs = arg.rhs, + .result = arg.output, + .opcode = BigIntOperationType::Neg, + }); + } else if constexpr (std::is_same_v) { + af.bigint_operations.push_back(BigIntOperation{ + .lhs = arg.lhs, + .rhs = arg.rhs, + .result = arg.output, + .opcode = BigIntOperationType::Mul, + }); + } else if constexpr (std::is_same_v) { + af.bigint_operations.push_back(BigIntOperation{ + .lhs = arg.lhs, + .rhs = arg.rhs, + .result = arg.output, + .opcode = BigIntOperationType::Div, + }); } }, arg.value.value); diff --git a/barretenberg/cpp/src/barretenberg/dsl/acir_format/bigint_constraint.cpp b/barretenberg/cpp/src/barretenberg/dsl/acir_format/bigint_constraint.cpp new file mode 100644 index 00000000000..4780e5ca36c --- /dev/null +++ b/barretenberg/cpp/src/barretenberg/dsl/acir_format/bigint_constraint.cpp @@ -0,0 +1,33 @@ +#include "bigint_constraint.hpp" +#include "barretenberg/dsl/types.hpp" +#include "barretenberg/numeric/uint256/uint256.hpp" +#include "barretenberg/stdlib/primitives/bigfield/bigfield.hpp" + +namespace acir_format { + +template void create_bigint_operations_constraint(Builder& builder, const BigIntOperation& input) +{ + // TODO + (void)builder; + info(input); +} + +template void create_bigint_operations_constraint(UltraCircuitBuilder& builder, + const BigIntOperation& input); +template void create_bigint_operations_constraint(GoblinUltraCircuitBuilder& builder, + const BigIntOperation& input); + +template +void create_bigint_from_le_bytes_constraint(Builder& builder, const BigIntFromLeBytes& input) +{ + // TODO + (void)builder; + info(input); +} + +template void create_bigint_from_le_bytes_constraint(UltraCircuitBuilder& builder, + const BigIntFromLeBytes& input); +template void create_bigint_from_le_bytes_constraint(GoblinUltraCircuitBuilder& builder, + const BigIntFromLeBytes& input); + +} // namespace acir_format diff --git a/barretenberg/cpp/src/barretenberg/dsl/acir_format/bigint_constraint.hpp b/barretenberg/cpp/src/barretenberg/dsl/acir_format/bigint_constraint.hpp new file mode 100644 index 00000000000..8b21ee5e784 --- /dev/null +++ b/barretenberg/cpp/src/barretenberg/dsl/acir_format/bigint_constraint.hpp @@ -0,0 +1,35 @@ +#pragma once +#include "barretenberg/dsl/types.hpp" +#include "barretenberg/serialize/msgpack.hpp" +#include +#include + +namespace acir_format { + +struct BigIntFromLeBytes { + std::vector inputs; + std::vector modulus; + uint32_t result; + + // For serialization, update with any new fields + MSGPACK_FIELDS(inputs, result); + friend bool operator==(BigIntFromLeBytes const& lhs, BigIntFromLeBytes const& rhs) = default; +}; + +enum BigIntOperationType { Add, Neg, Mul, Div }; + +struct BigIntOperation { + uint32_t lhs; + uint32_t rhs; + uint32_t result; + BigIntOperationType opcode; + + // For serialization, update with any new fields + MSGPACK_FIELDS(lhs, rhs, opcode, result); + friend bool operator==(BigIntOperation const& lhs, BigIntOperation const& rhs) = default; +}; + +template void create_bigint_operations_constraint(Builder& builder, const BigIntOperation& input); +template +void create_bigint_from_le_bytes_constraint(Builder& builder, const BigIntFromLeBytes& input); +} // namespace acir_format \ No newline at end of file diff --git a/barretenberg/cpp/src/barretenberg/dsl/acir_format/bigint_constraint.test.cpp b/barretenberg/cpp/src/barretenberg/dsl/acir_format/bigint_constraint.test.cpp new file mode 100644 index 00000000000..e8c4b22b4bf --- /dev/null +++ b/barretenberg/cpp/src/barretenberg/dsl/acir_format/bigint_constraint.test.cpp @@ -0,0 +1,87 @@ +#include "bigint_constraint.hpp" +#include "acir_format.hpp" +#include "barretenberg/plonk/proof_system/types/proof.hpp" +#include "barretenberg/plonk/proof_system/verification_key/verification_key.hpp" + +#include +#include + +namespace acir_format::tests { + +class BigIntTests : public ::testing::Test { + protected: + static void SetUpTestSuite() { bb::srs::init_crs_factory("../srs_db/ignition"); } +}; + +TEST_F(BigIntTests, TestBigIntConstraintDummy) +{ + // Dummy Test: to be updated when big ints opcodes are implemented + BigIntOperation add_constraint{ + .lhs = 1, + .rhs = 2, + .result = 3, + .opcode = BigIntOperationType::Add, + }; + BigIntOperation neg_constraint{ + .lhs = 1, + .rhs = 2, + .result = 3, + .opcode = BigIntOperationType::Neg, + }; + BigIntOperation mul_constraint{ + .lhs = 1, + .rhs = 2, + .result = 3, + .opcode = BigIntOperationType::Mul, + }; + BigIntOperation div_constraint{ + .lhs = 1, + .rhs = 2, + .result = 3, + .opcode = BigIntOperationType::Div, + }; + BigIntFromLeBytes from_le_bytes_constraint{ + .inputs = { 0 }, + .modulus = { 23 }, + .result = 1, + }; + + AcirFormat constraint_system{ + .varnum = 4, + .public_inputs = {}, + .logic_constraints = {}, + .range_constraints = {}, + .sha256_constraints = {}, + .schnorr_constraints = {}, + .ecdsa_k1_constraints = {}, + .ecdsa_r1_constraints = {}, + .blake2s_constraints = {}, + .blake3_constraints = {}, + .keccak_constraints = {}, + .keccak_var_constraints = {}, + .keccak_permutations = {}, + .pedersen_constraints = {}, + .pedersen_hash_constraints = {}, + .fixed_base_scalar_mul_constraints = {}, + .ec_add_constraints = {}, + .recursion_constraints = {}, + .bigint_from_le_bytes_constraints = { from_le_bytes_constraint }, + .bigint_operations = { add_constraint, neg_constraint, mul_constraint, div_constraint }, + .constraints = {}, + .block_constraints = {}, + + }; + + WitnessVector witness{ 0, 0, 1 }; + auto builder = create_circuit(constraint_system, /*size_hint*/ 0, witness); + + auto composer = Composer(); + auto prover = composer.create_ultra_with_keccak_prover(builder); + auto proof = prover.construct_proof(); + + auto verifier = composer.create_ultra_with_keccak_verifier(builder); + + EXPECT_EQ(verifier.verify_proof(proof), true); +} + +} // namespace acir_format::tests \ No newline at end of file diff --git a/barretenberg/cpp/src/barretenberg/dsl/acir_format/block_constraint.test.cpp b/barretenberg/cpp/src/barretenberg/dsl/acir_format/block_constraint.test.cpp index d90a2a7879b..384520e7f80 100644 --- a/barretenberg/cpp/src/barretenberg/dsl/acir_format/block_constraint.test.cpp +++ b/barretenberg/cpp/src/barretenberg/dsl/acir_format/block_constraint.test.cpp @@ -126,8 +126,9 @@ TEST_F(UltraPlonkRAM, TestBlockConstraint) .pedersen_hash_constraints = {}, .fixed_base_scalar_mul_constraints = {}, .ec_add_constraints = {}, - .ec_double_constraints = {}, .recursion_constraints = {}, + .bigint_from_le_bytes_constraints = {}, + .bigint_operations = {}, .constraints = {}, .block_constraints = { block }, }; diff --git a/barretenberg/cpp/src/barretenberg/dsl/acir_format/ec_operations.cpp b/barretenberg/cpp/src/barretenberg/dsl/acir_format/ec_operations.cpp index fc657ccebb1..805ea1d8427 100644 --- a/barretenberg/cpp/src/barretenberg/dsl/acir_format/ec_operations.cpp +++ b/barretenberg/cpp/src/barretenberg/dsl/acir_format/ec_operations.cpp @@ -2,30 +2,48 @@ #include "barretenberg/dsl/types.hpp" #include "barretenberg/ecc/curves/bn254/fr.hpp" #include "barretenberg/ecc/curves/grumpkin/grumpkin.hpp" +#include "barretenberg/ecc/groups/affine_element.hpp" #include "barretenberg/proof_system/arithmetization/gate_data.hpp" namespace acir_format { -template void create_ec_add_constraint(Builder& builder, const EcAdd& input) +template +void create_ec_add_constraint(Builder& builder, const EcAdd& input, bool has_valid_witness_assignments) { - // TODO - builder.assert_equal(input.input1_x, input.input1_x); - ASSERT(false); -} + // Input to cycle_group points + using cycle_group_ct = bb::stdlib::cycle_group; + using field_ct = bb::stdlib::field_t; -template void create_ec_add_constraint(UltraCircuitBuilder& builder, const EcAdd& input); -template void create_ec_add_constraint(GoblinUltraCircuitBuilder& builder, - const EcAdd& input); + auto x1 = field_ct::from_witness_index(&builder, input.input1_x); + auto y1 = field_ct::from_witness_index(&builder, input.input1_y); + auto x2 = field_ct::from_witness_index(&builder, input.input2_x); + auto y2 = field_ct::from_witness_index(&builder, input.input2_y); + if (!has_valid_witness_assignments) { + auto g1 = grumpkin::g1::affine_one; + // We need to have correct values representing points on the curve + builder.variables[input.input1_x] = g1.x; + builder.variables[input.input1_y] = g1.y; + builder.variables[input.input2_x] = g1.x; + builder.variables[input.input2_y] = g1.y; + } -template void create_ec_double_constraint(Builder& builder, const EcDouble& input) -{ - // TODO - builder.assert_equal(input.input_x, input.input_x); - ASSERT(false); + cycle_group_ct input1_point(x1, y1, false); + cycle_group_ct input2_point(x2, y2, false); + + // Addition + cycle_group_ct result = input1_point + input2_point; + + auto x_normalized = result.x.normalize(); + auto y_normalized = result.y.normalize(); + builder.assert_equal(x_normalized.witness_index, input.result_x); + builder.assert_equal(y_normalized.witness_index, input.result_y); } -template void create_ec_double_constraint(UltraCircuitBuilder& builder, const EcDouble& input); -template void create_ec_double_constraint(GoblinUltraCircuitBuilder& builder, - const EcDouble& input); +template void create_ec_add_constraint(UltraCircuitBuilder& builder, + const EcAdd& input, + bool has_valid_witness_assignments); +template void create_ec_add_constraint(GoblinUltraCircuitBuilder& builder, + const EcAdd& input, + bool has_valid_witness_assignments); } // namespace acir_format diff --git a/barretenberg/cpp/src/barretenberg/dsl/acir_format/ec_operations.hpp b/barretenberg/cpp/src/barretenberg/dsl/acir_format/ec_operations.hpp index f6d8e6168eb..de28743d16a 100644 --- a/barretenberg/cpp/src/barretenberg/dsl/acir_format/ec_operations.hpp +++ b/barretenberg/cpp/src/barretenberg/dsl/acir_format/ec_operations.hpp @@ -18,18 +18,6 @@ struct EcAdd { friend bool operator==(EcAdd const& lhs, EcAdd const& rhs) = default; }; -template void create_ec_add_constraint(Builder& builder, const EcAdd& input); - -struct EcDouble { - uint32_t input_x; - uint32_t input_y; - uint32_t result_x; - uint32_t result_y; - - // for serialization, update with any new fields - MSGPACK_FIELDS(input_x, input_y, result_x, result_y); - friend bool operator==(EcDouble const& lhs, EcDouble const& rhs) = default; -}; - -template void create_ec_double_constraint(Builder& builder, const EcDouble& input); +template +void create_ec_add_constraint(Builder& builder, const EcAdd& input, bool has_valid_witness_assignments); } // namespace acir_format diff --git a/barretenberg/cpp/src/barretenberg/dsl/acir_format/ec_operations.test.cpp b/barretenberg/cpp/src/barretenberg/dsl/acir_format/ec_operations.test.cpp new file mode 100644 index 00000000000..11ea6a77b9d --- /dev/null +++ b/barretenberg/cpp/src/barretenberg/dsl/acir_format/ec_operations.test.cpp @@ -0,0 +1,86 @@ +#include "ec_operations.hpp" +#include "acir_format.hpp" +#include "barretenberg/plonk/proof_system/types/proof.hpp" +#include "barretenberg/plonk/proof_system/verification_key/verification_key.hpp" + +#include +#include + +namespace acir_format::tests { +using curve_ct = bb::stdlib::secp256k1; + +class EcOperations : public ::testing::Test { + protected: + static void SetUpTestSuite() { bb::srs::init_crs_factory("../srs_db/ignition"); } +}; + +size_t generate_ec_add_constraint(EcAdd& ec_add_constraint, WitnessVector& witness_values) +{ + using cycle_group_ct = bb::stdlib::cycle_group; + witness_values.push_back(0); + auto g1 = grumpkin::g1::affine_one; + cycle_group_ct input_point(g1); + // Doubling + cycle_group_ct result = input_point.dbl(); + // add: x,y,x2,y2 + witness_values.push_back(g1.x); + witness_values.push_back(g1.y); + witness_values.push_back(g1.x); + witness_values.push_back(g1.y); + witness_values.push_back(result.x.get_value()); + witness_values.push_back(result.y.get_value()); + ec_add_constraint = EcAdd{ + .input1_x = 1, + .input1_y = 2, + .input2_x = 3, + .input2_y = 4, + .result_x = 5, + .result_y = 6, + }; + return witness_values.size(); +} + +TEST_F(EcOperations, TestECOperations) +{ + EcAdd ec_add_constraint; + + WitnessVector witness_values; + size_t num_variables = generate_ec_add_constraint(ec_add_constraint, witness_values); + + AcirFormat constraint_system{ + .varnum = static_cast(num_variables + 1), + .public_inputs = {}, + .logic_constraints = {}, + .range_constraints = {}, + .sha256_constraints = {}, + .schnorr_constraints = {}, + .ecdsa_k1_constraints = {}, + .ecdsa_r1_constraints = {}, + .blake2s_constraints = {}, + .blake3_constraints = {}, + .keccak_constraints = {}, + .keccak_var_constraints = {}, + .keccak_permutations = {}, + .pedersen_constraints = {}, + .pedersen_hash_constraints = {}, + .fixed_base_scalar_mul_constraints = {}, + .ec_add_constraints = { ec_add_constraint }, + .recursion_constraints = {}, + .bigint_from_le_bytes_constraints = {}, + .bigint_operations = {}, + .constraints = {}, + .block_constraints = {}, + }; + + auto builder = create_circuit(constraint_system, /*size_hint*/ 0, witness_values); + + auto composer = Composer(); + auto prover = composer.create_prover(builder); + + auto proof = prover.construct_proof(); + EXPECT_TRUE(builder.check_circuit()); + auto verifier = composer.create_verifier(builder); + EXPECT_EQ(verifier.verify_proof(proof), true); +} + +} // namespace acir_format::tests diff --git a/barretenberg/cpp/src/barretenberg/dsl/acir_format/ecdsa_secp256k1.test.cpp b/barretenberg/cpp/src/barretenberg/dsl/acir_format/ecdsa_secp256k1.test.cpp index 5554fdd8d6d..fb3e405e2f1 100644 --- a/barretenberg/cpp/src/barretenberg/dsl/acir_format/ecdsa_secp256k1.test.cpp +++ b/barretenberg/cpp/src/barretenberg/dsl/acir_format/ecdsa_secp256k1.test.cpp @@ -105,8 +105,9 @@ TEST_F(ECDSASecp256k1, TestECDSAConstraintSucceed) .pedersen_hash_constraints = {}, .fixed_base_scalar_mul_constraints = {}, .ec_add_constraints = {}, - .ec_double_constraints = {}, .recursion_constraints = {}, + .bigint_from_le_bytes_constraints = {}, + .bigint_operations = {}, .constraints = {}, .block_constraints = {}, }; @@ -149,8 +150,9 @@ TEST_F(ECDSASecp256k1, TestECDSACompilesForVerifier) .pedersen_hash_constraints = {}, .fixed_base_scalar_mul_constraints = {}, .ec_add_constraints = {}, - .ec_double_constraints = {}, .recursion_constraints = {}, + .bigint_from_le_bytes_constraints = {}, + .bigint_operations = {}, .constraints = {}, .block_constraints = {}, }; @@ -188,8 +190,9 @@ TEST_F(ECDSASecp256k1, TestECDSAConstraintFail) .pedersen_hash_constraints = {}, .fixed_base_scalar_mul_constraints = {}, .ec_add_constraints = {}, - .ec_double_constraints = {}, .recursion_constraints = {}, + .bigint_from_le_bytes_constraints = {}, + .bigint_operations = {}, .constraints = {}, .block_constraints = {}, }; diff --git a/barretenberg/cpp/src/barretenberg/dsl/acir_format/ecdsa_secp256r1.test.cpp b/barretenberg/cpp/src/barretenberg/dsl/acir_format/ecdsa_secp256r1.test.cpp index ebd78e1b672..fe64491c90c 100644 --- a/barretenberg/cpp/src/barretenberg/dsl/acir_format/ecdsa_secp256r1.test.cpp +++ b/barretenberg/cpp/src/barretenberg/dsl/acir_format/ecdsa_secp256r1.test.cpp @@ -140,8 +140,9 @@ TEST(ECDSASecp256r1, test_hardcoded) .pedersen_hash_constraints = {}, .fixed_base_scalar_mul_constraints = {}, .ec_add_constraints = {}, - .ec_double_constraints = {}, .recursion_constraints = {}, + .bigint_from_le_bytes_constraints = {}, + .bigint_operations = {}, .constraints = {}, .block_constraints = {}, }; @@ -185,8 +186,9 @@ TEST(ECDSASecp256r1, TestECDSAConstraintSucceed) .pedersen_hash_constraints = {}, .fixed_base_scalar_mul_constraints = {}, .ec_add_constraints = {}, - .ec_double_constraints = {}, .recursion_constraints = {}, + .bigint_from_le_bytes_constraints = {}, + .bigint_operations = {}, .constraints = {}, .block_constraints = {}, }; @@ -228,8 +230,9 @@ TEST(ECDSASecp256r1, TestECDSACompilesForVerifier) .pedersen_hash_constraints = {}, .fixed_base_scalar_mul_constraints = {}, .ec_add_constraints = {}, - .ec_double_constraints = {}, .recursion_constraints = {}, + .bigint_from_le_bytes_constraints = {}, + .bigint_operations = {}, .constraints = {}, .block_constraints = {}, }; @@ -266,8 +269,9 @@ TEST(ECDSASecp256r1, TestECDSAConstraintFail) .pedersen_hash_constraints = {}, .fixed_base_scalar_mul_constraints = {}, .ec_add_constraints = {}, - .ec_double_constraints = {}, .recursion_constraints = {}, + .bigint_from_le_bytes_constraints = {}, + .bigint_operations = {}, .constraints = {}, .block_constraints = {}, }; diff --git a/barretenberg/cpp/src/barretenberg/dsl/acir_format/recursion_constraint.test.cpp b/barretenberg/cpp/src/barretenberg/dsl/acir_format/recursion_constraint.test.cpp index cbbdd2a22b9..f281f965eea 100644 --- a/barretenberg/cpp/src/barretenberg/dsl/acir_format/recursion_constraint.test.cpp +++ b/barretenberg/cpp/src/barretenberg/dsl/acir_format/recursion_constraint.test.cpp @@ -98,8 +98,9 @@ Builder create_inner_circuit() .pedersen_hash_constraints = {}, .fixed_base_scalar_mul_constraints = {}, .ec_add_constraints = {}, - .ec_double_constraints = {}, .recursion_constraints = {}, + .bigint_from_le_bytes_constraints = {}, + .bigint_operations = {}, .constraints = { expr_a, expr_b, expr_c, expr_d }, .block_constraints = {} }; @@ -250,8 +251,9 @@ Builder create_outer_circuit(std::vector& inner_circuits) .pedersen_hash_constraints = {}, .fixed_base_scalar_mul_constraints = {}, .ec_add_constraints = {}, - .ec_double_constraints = {}, .recursion_constraints = recursion_constraints, + .bigint_from_le_bytes_constraints = {}, + .bigint_operations = {}, .constraints = {}, .block_constraints = {} }; diff --git a/barretenberg/cpp/src/barretenberg/dsl/acir_format/serde/acir.hpp b/barretenberg/cpp/src/barretenberg/dsl/acir_format/serde/acir.hpp index c85f609bbd1..c983baf68b0 100644 --- a/barretenberg/cpp/src/barretenberg/dsl/acir_format/serde/acir.hpp +++ b/barretenberg/cpp/src/barretenberg/dsl/acir_format/serde/acir.hpp @@ -157,16 +157,6 @@ struct BlackBoxFuncCall { static EmbeddedCurveAdd bincodeDeserialize(std::vector); }; - struct EmbeddedCurveDouble { - Circuit::FunctionInput input_x; - Circuit::FunctionInput input_y; - std::array outputs; - - friend bool operator==(const EmbeddedCurveDouble&, const EmbeddedCurveDouble&); - std::vector bincodeSerialize() const; - static EmbeddedCurveDouble bincodeDeserialize(std::vector); - }; - struct Keccak256 { std::vector inputs; std::vector outputs; @@ -206,6 +196,85 @@ struct BlackBoxFuncCall { static RecursiveAggregation bincodeDeserialize(std::vector); }; + struct BigIntAdd { + uint32_t lhs; + uint32_t rhs; + uint32_t output; + + friend bool operator==(const BigIntAdd&, const BigIntAdd&); + std::vector bincodeSerialize() const; + static BigIntAdd bincodeDeserialize(std::vector); + }; + + struct BigIntNeg { + uint32_t lhs; + uint32_t rhs; + uint32_t output; + + friend bool operator==(const BigIntNeg&, const BigIntNeg&); + std::vector bincodeSerialize() const; + static BigIntNeg bincodeDeserialize(std::vector); + }; + + struct BigIntMul { + uint32_t lhs; + uint32_t rhs; + uint32_t output; + + friend bool operator==(const BigIntMul&, const BigIntMul&); + std::vector bincodeSerialize() const; + static BigIntMul bincodeDeserialize(std::vector); + }; + + struct BigIntDiv { + uint32_t lhs; + uint32_t rhs; + uint32_t output; + + friend bool operator==(const BigIntDiv&, const BigIntDiv&); + std::vector bincodeSerialize() const; + static BigIntDiv bincodeDeserialize(std::vector); + }; + + struct BigIntFromLeBytes { + std::vector inputs; + std::vector modulus; + uint32_t output; + + friend bool operator==(const BigIntFromLeBytes&, const BigIntFromLeBytes&); + std::vector bincodeSerialize() const; + static BigIntFromLeBytes bincodeDeserialize(std::vector); + }; + + struct BigIntToLeBytes { + uint32_t input; + std::vector outputs; + + friend bool operator==(const BigIntToLeBytes&, const BigIntToLeBytes&); + std::vector bincodeSerialize() const; + static BigIntToLeBytes bincodeDeserialize(std::vector); + }; + + struct Poseidon2Permutation { + std::vector inputs; + std::vector outputs; + uint32_t len; + + friend bool operator==(const Poseidon2Permutation&, const Poseidon2Permutation&); + std::vector bincodeSerialize() const; + static Poseidon2Permutation bincodeDeserialize(std::vector); + }; + + struct Sha256Compression { + std::vector inputs; + std::vector hash_values; + std::vector outputs; + + friend bool operator==(const Sha256Compression&, const Sha256Compression&); + std::vector bincodeSerialize() const; + static Sha256Compression bincodeDeserialize(std::vector); + }; + std::variant + RecursiveAggregation, + BigIntAdd, + BigIntNeg, + BigIntMul, + BigIntDiv, + BigIntFromLeBytes, + BigIntToLeBytes, + Poseidon2Permutation, + Sha256Compression> value; friend bool operator==(const BlackBoxFuncCall&, const BlackBoxFuncCall&); @@ -239,8 +315,6 @@ struct BlockId { static BlockId bincodeDeserialize(std::vector); }; -// TODO(https://github.com/AztecProtocol/barretenberg/issues/825): This struct is more general than it needs / should be -// allowed to be. We can only accommodate 1 quadratic term and 3 linear terms. struct Expression { std::vector> mul_terms; std::vector> linear_combinations; @@ -402,16 +476,16 @@ struct BinaryIntOp { static BinaryIntOp bincodeDeserialize(std::vector); }; -struct RegisterIndex { +struct MemoryAddress { uint64_t value; - friend bool operator==(const RegisterIndex&, const RegisterIndex&); + friend bool operator==(const MemoryAddress&, const MemoryAddress&); std::vector bincodeSerialize() const; - static RegisterIndex bincodeDeserialize(std::vector); + static MemoryAddress bincodeDeserialize(std::vector); }; struct HeapArray { - Circuit::RegisterIndex pointer; + Circuit::MemoryAddress pointer; uint64_t size; friend bool operator==(const HeapArray&, const HeapArray&); @@ -420,8 +494,8 @@ struct HeapArray { }; struct HeapVector { - Circuit::RegisterIndex pointer; - Circuit::RegisterIndex size; + Circuit::MemoryAddress pointer; + Circuit::MemoryAddress size; friend bool operator==(const HeapVector&, const HeapVector&); std::vector bincodeSerialize() const; @@ -480,7 +554,7 @@ struct BlackBoxOp { Circuit::HeapArray public_key_x; Circuit::HeapArray public_key_y; Circuit::HeapArray signature; - Circuit::RegisterIndex result; + Circuit::MemoryAddress result; friend bool operator==(const EcdsaSecp256k1&, const EcdsaSecp256k1&); std::vector bincodeSerialize() const; @@ -492,7 +566,7 @@ struct BlackBoxOp { Circuit::HeapArray public_key_x; Circuit::HeapArray public_key_y; Circuit::HeapArray signature; - Circuit::RegisterIndex result; + Circuit::MemoryAddress result; friend bool operator==(const EcdsaSecp256r1&, const EcdsaSecp256r1&); std::vector bincodeSerialize() const; @@ -500,11 +574,11 @@ struct BlackBoxOp { }; struct SchnorrVerify { - Circuit::RegisterIndex public_key_x; - Circuit::RegisterIndex public_key_y; + Circuit::MemoryAddress public_key_x; + Circuit::MemoryAddress public_key_y; Circuit::HeapVector message; Circuit::HeapVector signature; - Circuit::RegisterIndex result; + Circuit::MemoryAddress result; friend bool operator==(const SchnorrVerify&, const SchnorrVerify&); std::vector bincodeSerialize() const; @@ -513,7 +587,7 @@ struct BlackBoxOp { struct PedersenCommitment { Circuit::HeapVector inputs; - Circuit::RegisterIndex domain_separator; + Circuit::MemoryAddress domain_separator; Circuit::HeapArray output; friend bool operator==(const PedersenCommitment&, const PedersenCommitment&); @@ -523,8 +597,8 @@ struct BlackBoxOp { struct PedersenHash { Circuit::HeapVector inputs; - Circuit::RegisterIndex domain_separator; - Circuit::RegisterIndex output; + Circuit::MemoryAddress domain_separator; + Circuit::MemoryAddress output; friend bool operator==(const PedersenHash&, const PedersenHash&); std::vector bincodeSerialize() const; @@ -532,8 +606,8 @@ struct BlackBoxOp { }; struct FixedBaseScalarMul { - Circuit::RegisterIndex low; - Circuit::RegisterIndex high; + Circuit::MemoryAddress low; + Circuit::MemoryAddress high; Circuit::HeapArray result; friend bool operator==(const FixedBaseScalarMul&, const FixedBaseScalarMul&); @@ -542,10 +616,10 @@ struct BlackBoxOp { }; struct EmbeddedCurveAdd { - Circuit::RegisterIndex input1_x; - Circuit::RegisterIndex input1_y; - Circuit::RegisterIndex input2_x; - Circuit::RegisterIndex input2_y; + Circuit::MemoryAddress input1_x; + Circuit::MemoryAddress input1_y; + Circuit::MemoryAddress input2_x; + Circuit::MemoryAddress input2_y; Circuit::HeapArray result; friend bool operator==(const EmbeddedCurveAdd&, const EmbeddedCurveAdd&); @@ -553,14 +627,83 @@ struct BlackBoxOp { static EmbeddedCurveAdd bincodeDeserialize(std::vector); }; - struct EmbeddedCurveDouble { - Circuit::RegisterIndex input1_x; - Circuit::RegisterIndex input1_y; - Circuit::HeapArray result; + struct BigIntAdd { + Circuit::MemoryAddress lhs; + Circuit::MemoryAddress rhs; + Circuit::MemoryAddress output; + + friend bool operator==(const BigIntAdd&, const BigIntAdd&); + std::vector bincodeSerialize() const; + static BigIntAdd bincodeDeserialize(std::vector); + }; + + struct BigIntNeg { + Circuit::MemoryAddress lhs; + Circuit::MemoryAddress rhs; + Circuit::MemoryAddress output; + + friend bool operator==(const BigIntNeg&, const BigIntNeg&); + std::vector bincodeSerialize() const; + static BigIntNeg bincodeDeserialize(std::vector); + }; + + struct BigIntMul { + Circuit::MemoryAddress lhs; + Circuit::MemoryAddress rhs; + Circuit::MemoryAddress output; + + friend bool operator==(const BigIntMul&, const BigIntMul&); + std::vector bincodeSerialize() const; + static BigIntMul bincodeDeserialize(std::vector); + }; + + struct BigIntDiv { + Circuit::MemoryAddress lhs; + Circuit::MemoryAddress rhs; + Circuit::MemoryAddress output; + + friend bool operator==(const BigIntDiv&, const BigIntDiv&); + std::vector bincodeSerialize() const; + static BigIntDiv bincodeDeserialize(std::vector); + }; + + struct BigIntFromLeBytes { + Circuit::HeapVector inputs; + Circuit::HeapVector modulus; + Circuit::MemoryAddress output; + + friend bool operator==(const BigIntFromLeBytes&, const BigIntFromLeBytes&); + std::vector bincodeSerialize() const; + static BigIntFromLeBytes bincodeDeserialize(std::vector); + }; - friend bool operator==(const EmbeddedCurveDouble&, const EmbeddedCurveDouble&); + struct BigIntToLeBytes { + Circuit::MemoryAddress input; + Circuit::HeapVector output; + + friend bool operator==(const BigIntToLeBytes&, const BigIntToLeBytes&); + std::vector bincodeSerialize() const; + static BigIntToLeBytes bincodeDeserialize(std::vector); + }; + + struct Poseidon2Permutation { + Circuit::HeapVector message; + Circuit::HeapArray output; + Circuit::MemoryAddress len; + + friend bool operator==(const Poseidon2Permutation&, const Poseidon2Permutation&); + std::vector bincodeSerialize() const; + static Poseidon2Permutation bincodeDeserialize(std::vector); + }; + + struct Sha256Compression { + Circuit::HeapVector input; + Circuit::HeapVector hash_values; + Circuit::HeapArray output; + + friend bool operator==(const Sha256Compression&, const Sha256Compression&); std::vector bincodeSerialize() const; - static EmbeddedCurveDouble bincodeDeserialize(std::vector); + static Sha256Compression bincodeDeserialize(std::vector); }; std::variant + BigIntAdd, + BigIntNeg, + BigIntMul, + BigIntDiv, + BigIntFromLeBytes, + BigIntToLeBytes, + Poseidon2Permutation, + Sha256Compression> value; friend bool operator==(const BlackBoxOp&, const BlackBoxOp&); @@ -583,14 +733,22 @@ struct BlackBoxOp { static BlackBoxOp bincodeDeserialize(std::vector); }; -struct RegisterOrMemory { +struct Value { + std::string inner; + + friend bool operator==(const Value&, const Value&); + std::vector bincodeSerialize() const; + static Value bincodeDeserialize(std::vector); +}; + +struct ValueOrArray { - struct RegisterIndex { - Circuit::RegisterIndex value; + struct MemoryAddress { + Circuit::MemoryAddress value; - friend bool operator==(const RegisterIndex&, const RegisterIndex&); + friend bool operator==(const MemoryAddress&, const MemoryAddress&); std::vector bincodeSerialize() const; - static RegisterIndex bincodeDeserialize(std::vector); + static MemoryAddress bincodeDeserialize(std::vector); }; struct HeapArray { @@ -609,28 +767,20 @@ struct RegisterOrMemory { static HeapVector bincodeDeserialize(std::vector); }; - std::variant value; - - friend bool operator==(const RegisterOrMemory&, const RegisterOrMemory&); - std::vector bincodeSerialize() const; - static RegisterOrMemory bincodeDeserialize(std::vector); -}; - -struct Value { - std::string inner; + std::variant value; - friend bool operator==(const Value&, const Value&); + friend bool operator==(const ValueOrArray&, const ValueOrArray&); std::vector bincodeSerialize() const; - static Value bincodeDeserialize(std::vector); + static ValueOrArray bincodeDeserialize(std::vector); }; struct BrilligOpcode { struct BinaryFieldOp { - Circuit::RegisterIndex destination; + Circuit::MemoryAddress destination; Circuit::BinaryFieldOp op; - Circuit::RegisterIndex lhs; - Circuit::RegisterIndex rhs; + Circuit::MemoryAddress lhs; + Circuit::MemoryAddress rhs; friend bool operator==(const BinaryFieldOp&, const BinaryFieldOp&); std::vector bincodeSerialize() const; @@ -638,11 +788,11 @@ struct BrilligOpcode { }; struct BinaryIntOp { - Circuit::RegisterIndex destination; + Circuit::MemoryAddress destination; Circuit::BinaryIntOp op; uint32_t bit_size; - Circuit::RegisterIndex lhs; - Circuit::RegisterIndex rhs; + Circuit::MemoryAddress lhs; + Circuit::MemoryAddress rhs; friend bool operator==(const BinaryIntOp&, const BinaryIntOp&); std::vector bincodeSerialize() const; @@ -650,7 +800,7 @@ struct BrilligOpcode { }; struct JumpIfNot { - Circuit::RegisterIndex condition; + Circuit::MemoryAddress condition; uint64_t location; friend bool operator==(const JumpIfNot&, const JumpIfNot&); @@ -659,7 +809,7 @@ struct BrilligOpcode { }; struct JumpIf { - Circuit::RegisterIndex condition; + Circuit::MemoryAddress condition; uint64_t location; friend bool operator==(const JumpIf&, const JumpIf&); @@ -675,6 +825,16 @@ struct BrilligOpcode { static Jump bincodeDeserialize(std::vector); }; + struct CalldataCopy { + Circuit::MemoryAddress destination_address; + uint64_t size; + uint64_t offset; + + friend bool operator==(const CalldataCopy&, const CalldataCopy&); + std::vector bincodeSerialize() const; + static CalldataCopy bincodeDeserialize(std::vector); + }; + struct Call { uint64_t location; @@ -684,7 +844,7 @@ struct BrilligOpcode { }; struct Const { - Circuit::RegisterIndex destination; + Circuit::MemoryAddress destination; Circuit::Value value; friend bool operator==(const Const&, const Const&); @@ -700,8 +860,8 @@ struct BrilligOpcode { struct ForeignCall { std::string function; - std::vector destinations; - std::vector inputs; + std::vector destinations; + std::vector inputs; friend bool operator==(const ForeignCall&, const ForeignCall&); std::vector bincodeSerialize() const; @@ -709,8 +869,8 @@ struct BrilligOpcode { }; struct Mov { - Circuit::RegisterIndex destination; - Circuit::RegisterIndex source; + Circuit::MemoryAddress destination; + Circuit::MemoryAddress source; friend bool operator==(const Mov&, const Mov&); std::vector bincodeSerialize() const; @@ -718,8 +878,8 @@ struct BrilligOpcode { }; struct Load { - Circuit::RegisterIndex destination; - Circuit::RegisterIndex source_pointer; + Circuit::MemoryAddress destination; + Circuit::MemoryAddress source_pointer; friend bool operator==(const Load&, const Load&); std::vector bincodeSerialize() const; @@ -727,8 +887,8 @@ struct BrilligOpcode { }; struct Store { - Circuit::RegisterIndex destination_pointer; - Circuit::RegisterIndex source; + Circuit::MemoryAddress destination_pointer; + Circuit::MemoryAddress source; friend bool operator==(const Store&, const Store&); std::vector bincodeSerialize() const; @@ -750,6 +910,9 @@ struct BrilligOpcode { }; struct Stop { + uint64_t return_data_offset; + uint64_t return_data_size; + friend bool operator==(const Stop&, const Stop&); std::vector bincodeSerialize() const; static Stop bincodeDeserialize(std::vector); @@ -760,6 +923,7 @@ struct BrilligOpcode { JumpIfNot, JumpIf, Jump, + CalldataCopy, Call, Const, Return, @@ -2597,65 +2761,6 @@ Circuit::BlackBoxFuncCall::EmbeddedCurveAdd serde::Deserializable< namespace Circuit { -inline bool operator==(const BlackBoxFuncCall::EmbeddedCurveDouble& lhs, - const BlackBoxFuncCall::EmbeddedCurveDouble& rhs) -{ - if (!(lhs.input_x == rhs.input_x)) { - return false; - } - if (!(lhs.input_y == rhs.input_y)) { - return false; - } - if (!(lhs.outputs == rhs.outputs)) { - return false; - } - return true; -} - -inline std::vector BlackBoxFuncCall::EmbeddedCurveDouble::bincodeSerialize() const -{ - auto serializer = serde::BincodeSerializer(); - serde::Serializable::serialize(*this, serializer); - return std::move(serializer).bytes(); -} - -inline BlackBoxFuncCall::EmbeddedCurveDouble BlackBoxFuncCall::EmbeddedCurveDouble::bincodeDeserialize( - std::vector input) -{ - auto deserializer = serde::BincodeDeserializer(input); - auto value = serde::Deserializable::deserialize(deserializer); - if (deserializer.get_buffer_offset() < input.size()) { - throw_or_abort("Some input bytes were not read"); - } - return value; -} - -} // end of namespace Circuit - -template <> -template -void serde::Serializable::serialize( - const Circuit::BlackBoxFuncCall::EmbeddedCurveDouble& obj, Serializer& serializer) -{ - serde::Serializable::serialize(obj.input_x, serializer); - serde::Serializable::serialize(obj.input_y, serializer); - serde::Serializable::serialize(obj.outputs, serializer); -} - -template <> -template -Circuit::BlackBoxFuncCall::EmbeddedCurveDouble serde::Deserializable< - Circuit::BlackBoxFuncCall::EmbeddedCurveDouble>::deserialize(Deserializer& deserializer) -{ - Circuit::BlackBoxFuncCall::EmbeddedCurveDouble obj; - obj.input_x = serde::Deserializable::deserialize(deserializer); - obj.input_y = serde::Deserializable::deserialize(deserializer); - obj.outputs = serde::Deserializable::deserialize(deserializer); - return obj; -} - -namespace Circuit { - inline bool operator==(const BlackBoxFuncCall::Keccak256& lhs, const BlackBoxFuncCall::Keccak256& rhs) { if (!(lhs.inputs == rhs.inputs)) { @@ -2883,25 +2988,31 @@ Circuit::BlackBoxFuncCall::RecursiveAggregation serde::Deserializable< namespace Circuit { -inline bool operator==(const BlackBoxOp& lhs, const BlackBoxOp& rhs) +inline bool operator==(const BlackBoxFuncCall::BigIntAdd& lhs, const BlackBoxFuncCall::BigIntAdd& rhs) { - if (!(lhs.value == rhs.value)) { + if (!(lhs.lhs == rhs.lhs)) { + return false; + } + if (!(lhs.rhs == rhs.rhs)) { + return false; + } + if (!(lhs.output == rhs.output)) { return false; } return true; } -inline std::vector BlackBoxOp::bincodeSerialize() const +inline std::vector BlackBoxFuncCall::BigIntAdd::bincodeSerialize() const { auto serializer = serde::BincodeSerializer(); - serde::Serializable::serialize(*this, serializer); + serde::Serializable::serialize(*this, serializer); return std::move(serializer).bytes(); } -inline BlackBoxOp BlackBoxOp::bincodeDeserialize(std::vector input) +inline BlackBoxFuncCall::BigIntAdd BlackBoxFuncCall::BigIntAdd::bincodeDeserialize(std::vector input) { auto deserializer = serde::BincodeDeserializer(input); - auto value = serde::Deserializable::deserialize(deserializer); + auto value = serde::Deserializable::deserialize(deserializer); if (deserializer.get_buffer_offset() < input.size()) { throw_or_abort("Some input bytes were not read"); } @@ -2912,29 +3023,34 @@ inline BlackBoxOp BlackBoxOp::bincodeDeserialize(std::vector input) template <> template -void serde::Serializable::serialize(const Circuit::BlackBoxOp& obj, Serializer& serializer) +void serde::Serializable::serialize( + const Circuit::BlackBoxFuncCall::BigIntAdd& obj, Serializer& serializer) { - serializer.increase_container_depth(); - serde::Serializable::serialize(obj.value, serializer); - serializer.decrease_container_depth(); + serde::Serializable::serialize(obj.lhs, serializer); + serde::Serializable::serialize(obj.rhs, serializer); + serde::Serializable::serialize(obj.output, serializer); } template <> template -Circuit::BlackBoxOp serde::Deserializable::deserialize(Deserializer& deserializer) +Circuit::BlackBoxFuncCall::BigIntAdd serde::Deserializable::deserialize( + Deserializer& deserializer) { - deserializer.increase_container_depth(); - Circuit::BlackBoxOp obj; - obj.value = serde::Deserializable::deserialize(deserializer); - deserializer.decrease_container_depth(); + Circuit::BlackBoxFuncCall::BigIntAdd obj; + obj.lhs = serde::Deserializable::deserialize(deserializer); + obj.rhs = serde::Deserializable::deserialize(deserializer); + obj.output = serde::Deserializable::deserialize(deserializer); return obj; } namespace Circuit { -inline bool operator==(const BlackBoxOp::Sha256& lhs, const BlackBoxOp::Sha256& rhs) +inline bool operator==(const BlackBoxFuncCall::BigIntNeg& lhs, const BlackBoxFuncCall::BigIntNeg& rhs) { - if (!(lhs.message == rhs.message)) { + if (!(lhs.lhs == rhs.lhs)) { + return false; + } + if (!(lhs.rhs == rhs.rhs)) { return false; } if (!(lhs.output == rhs.output)) { @@ -2943,17 +3059,17 @@ inline bool operator==(const BlackBoxOp::Sha256& lhs, const BlackBoxOp::Sha256& return true; } -inline std::vector BlackBoxOp::Sha256::bincodeSerialize() const +inline std::vector BlackBoxFuncCall::BigIntNeg::bincodeSerialize() const { auto serializer = serde::BincodeSerializer(); - serde::Serializable::serialize(*this, serializer); + serde::Serializable::serialize(*this, serializer); return std::move(serializer).bytes(); } -inline BlackBoxOp::Sha256 BlackBoxOp::Sha256::bincodeDeserialize(std::vector input) +inline BlackBoxFuncCall::BigIntNeg BlackBoxFuncCall::BigIntNeg::bincodeDeserialize(std::vector input) { auto deserializer = serde::BincodeDeserializer(input); - auto value = serde::Deserializable::deserialize(deserializer); + auto value = serde::Deserializable::deserialize(deserializer); if (deserializer.get_buffer_offset() < input.size()) { throw_or_abort("Some input bytes were not read"); } @@ -2964,28 +3080,34 @@ inline BlackBoxOp::Sha256 BlackBoxOp::Sha256::bincodeDeserialize(std::vector template -void serde::Serializable::serialize(const Circuit::BlackBoxOp::Sha256& obj, - Serializer& serializer) +void serde::Serializable::serialize( + const Circuit::BlackBoxFuncCall::BigIntNeg& obj, Serializer& serializer) { - serde::Serializable::serialize(obj.message, serializer); + serde::Serializable::serialize(obj.lhs, serializer); + serde::Serializable::serialize(obj.rhs, serializer); serde::Serializable::serialize(obj.output, serializer); } template <> template -Circuit::BlackBoxOp::Sha256 serde::Deserializable::deserialize(Deserializer& deserializer) +Circuit::BlackBoxFuncCall::BigIntNeg serde::Deserializable::deserialize( + Deserializer& deserializer) { - Circuit::BlackBoxOp::Sha256 obj; - obj.message = serde::Deserializable::deserialize(deserializer); + Circuit::BlackBoxFuncCall::BigIntNeg obj; + obj.lhs = serde::Deserializable::deserialize(deserializer); + obj.rhs = serde::Deserializable::deserialize(deserializer); obj.output = serde::Deserializable::deserialize(deserializer); return obj; } namespace Circuit { -inline bool operator==(const BlackBoxOp::Blake2s& lhs, const BlackBoxOp::Blake2s& rhs) +inline bool operator==(const BlackBoxFuncCall::BigIntMul& lhs, const BlackBoxFuncCall::BigIntMul& rhs) { - if (!(lhs.message == rhs.message)) { + if (!(lhs.lhs == rhs.lhs)) { + return false; + } + if (!(lhs.rhs == rhs.rhs)) { return false; } if (!(lhs.output == rhs.output)) { @@ -2994,17 +3116,17 @@ inline bool operator==(const BlackBoxOp::Blake2s& lhs, const BlackBoxOp::Blake2s return true; } -inline std::vector BlackBoxOp::Blake2s::bincodeSerialize() const +inline std::vector BlackBoxFuncCall::BigIntMul::bincodeSerialize() const { auto serializer = serde::BincodeSerializer(); - serde::Serializable::serialize(*this, serializer); + serde::Serializable::serialize(*this, serializer); return std::move(serializer).bytes(); } -inline BlackBoxOp::Blake2s BlackBoxOp::Blake2s::bincodeDeserialize(std::vector input) +inline BlackBoxFuncCall::BigIntMul BlackBoxFuncCall::BigIntMul::bincodeDeserialize(std::vector input) { auto deserializer = serde::BincodeDeserializer(input); - auto value = serde::Deserializable::deserialize(deserializer); + auto value = serde::Deserializable::deserialize(deserializer); if (deserializer.get_buffer_offset() < input.size()) { throw_or_abort("Some input bytes were not read"); } @@ -3015,29 +3137,34 @@ inline BlackBoxOp::Blake2s BlackBoxOp::Blake2s::bincodeDeserialize(std::vector template -void serde::Serializable::serialize(const Circuit::BlackBoxOp::Blake2s& obj, - Serializer& serializer) +void serde::Serializable::serialize( + const Circuit::BlackBoxFuncCall::BigIntMul& obj, Serializer& serializer) { - serde::Serializable::serialize(obj.message, serializer); + serde::Serializable::serialize(obj.lhs, serializer); + serde::Serializable::serialize(obj.rhs, serializer); serde::Serializable::serialize(obj.output, serializer); } template <> template -Circuit::BlackBoxOp::Blake2s serde::Deserializable::deserialize( +Circuit::BlackBoxFuncCall::BigIntMul serde::Deserializable::deserialize( Deserializer& deserializer) { - Circuit::BlackBoxOp::Blake2s obj; - obj.message = serde::Deserializable::deserialize(deserializer); + Circuit::BlackBoxFuncCall::BigIntMul obj; + obj.lhs = serde::Deserializable::deserialize(deserializer); + obj.rhs = serde::Deserializable::deserialize(deserializer); obj.output = serde::Deserializable::deserialize(deserializer); return obj; } namespace Circuit { -inline bool operator==(const BlackBoxOp::Blake3& lhs, const BlackBoxOp::Blake3& rhs) +inline bool operator==(const BlackBoxFuncCall::BigIntDiv& lhs, const BlackBoxFuncCall::BigIntDiv& rhs) { - if (!(lhs.message == rhs.message)) { + if (!(lhs.lhs == rhs.lhs)) { + return false; + } + if (!(lhs.rhs == rhs.rhs)) { return false; } if (!(lhs.output == rhs.output)) { @@ -3046,17 +3173,17 @@ inline bool operator==(const BlackBoxOp::Blake3& lhs, const BlackBoxOp::Blake3& return true; } -inline std::vector BlackBoxOp::Blake3::bincodeSerialize() const +inline std::vector BlackBoxFuncCall::BigIntDiv::bincodeSerialize() const { auto serializer = serde::BincodeSerializer(); - serde::Serializable::serialize(*this, serializer); + serde::Serializable::serialize(*this, serializer); return std::move(serializer).bytes(); } -inline BlackBoxOp::Blake3 BlackBoxOp::Blake3::bincodeDeserialize(std::vector input) +inline BlackBoxFuncCall::BigIntDiv BlackBoxFuncCall::BigIntDiv::bincodeDeserialize(std::vector input) { auto deserializer = serde::BincodeDeserializer(input); - auto value = serde::Deserializable::deserialize(deserializer); + auto value = serde::Deserializable::deserialize(deserializer); if (deserializer.get_buffer_offset() < input.size()) { throw_or_abort("Some input bytes were not read"); } @@ -3067,28 +3194,34 @@ inline BlackBoxOp::Blake3 BlackBoxOp::Blake3::bincodeDeserialize(std::vector template -void serde::Serializable::serialize(const Circuit::BlackBoxOp::Blake3& obj, - Serializer& serializer) +void serde::Serializable::serialize( + const Circuit::BlackBoxFuncCall::BigIntDiv& obj, Serializer& serializer) { - serde::Serializable::serialize(obj.message, serializer); + serde::Serializable::serialize(obj.lhs, serializer); + serde::Serializable::serialize(obj.rhs, serializer); serde::Serializable::serialize(obj.output, serializer); } template <> template -Circuit::BlackBoxOp::Blake3 serde::Deserializable::deserialize(Deserializer& deserializer) +Circuit::BlackBoxFuncCall::BigIntDiv serde::Deserializable::deserialize( + Deserializer& deserializer) { - Circuit::BlackBoxOp::Blake3 obj; - obj.message = serde::Deserializable::deserialize(deserializer); + Circuit::BlackBoxFuncCall::BigIntDiv obj; + obj.lhs = serde::Deserializable::deserialize(deserializer); + obj.rhs = serde::Deserializable::deserialize(deserializer); obj.output = serde::Deserializable::deserialize(deserializer); return obj; } namespace Circuit { -inline bool operator==(const BlackBoxOp::Keccak256& lhs, const BlackBoxOp::Keccak256& rhs) +inline bool operator==(const BlackBoxFuncCall::BigIntFromLeBytes& lhs, const BlackBoxFuncCall::BigIntFromLeBytes& rhs) { - if (!(lhs.message == rhs.message)) { + if (!(lhs.inputs == rhs.inputs)) { + return false; + } + if (!(lhs.modulus == rhs.modulus)) { return false; } if (!(lhs.output == rhs.output)) { @@ -3097,17 +3230,18 @@ inline bool operator==(const BlackBoxOp::Keccak256& lhs, const BlackBoxOp::Kecca return true; } -inline std::vector BlackBoxOp::Keccak256::bincodeSerialize() const +inline std::vector BlackBoxFuncCall::BigIntFromLeBytes::bincodeSerialize() const { auto serializer = serde::BincodeSerializer(); - serde::Serializable::serialize(*this, serializer); + serde::Serializable::serialize(*this, serializer); return std::move(serializer).bytes(); } -inline BlackBoxOp::Keccak256 BlackBoxOp::Keccak256::bincodeDeserialize(std::vector input) +inline BlackBoxFuncCall::BigIntFromLeBytes BlackBoxFuncCall::BigIntFromLeBytes::bincodeDeserialize( + std::vector input) { auto deserializer = serde::BincodeDeserializer(input); - auto value = serde::Deserializable::deserialize(deserializer); + auto value = serde::Deserializable::deserialize(deserializer); if (deserializer.get_buffer_offset() < input.size()) { throw_or_abort("Some input bytes were not read"); } @@ -3118,48 +3252,788 @@ inline BlackBoxOp::Keccak256 BlackBoxOp::Keccak256::bincodeDeserialize(std::vect template <> template -void serde::Serializable::serialize(const Circuit::BlackBoxOp::Keccak256& obj, - Serializer& serializer) +void serde::Serializable::serialize( + const Circuit::BlackBoxFuncCall::BigIntFromLeBytes& obj, Serializer& serializer) { - serde::Serializable::serialize(obj.message, serializer); + serde::Serializable::serialize(obj.inputs, serializer); + serde::Serializable::serialize(obj.modulus, serializer); serde::Serializable::serialize(obj.output, serializer); } template <> template -Circuit::BlackBoxOp::Keccak256 serde::Deserializable::deserialize( - Deserializer& deserializer) +Circuit::BlackBoxFuncCall::BigIntFromLeBytes serde::Deserializable< + Circuit::BlackBoxFuncCall::BigIntFromLeBytes>::deserialize(Deserializer& deserializer) { - Circuit::BlackBoxOp::Keccak256 obj; - obj.message = serde::Deserializable::deserialize(deserializer); + Circuit::BlackBoxFuncCall::BigIntFromLeBytes obj; + obj.inputs = serde::Deserializable::deserialize(deserializer); + obj.modulus = serde::Deserializable::deserialize(deserializer); obj.output = serde::Deserializable::deserialize(deserializer); return obj; } namespace Circuit { -inline bool operator==(const BlackBoxOp::Keccakf1600& lhs, const BlackBoxOp::Keccakf1600& rhs) +inline bool operator==(const BlackBoxFuncCall::BigIntToLeBytes& lhs, const BlackBoxFuncCall::BigIntToLeBytes& rhs) { - if (!(lhs.message == rhs.message)) { + if (!(lhs.input == rhs.input)) { return false; } - if (!(lhs.output == rhs.output)) { + if (!(lhs.outputs == rhs.outputs)) { return false; } return true; } -inline std::vector BlackBoxOp::Keccakf1600::bincodeSerialize() const +inline std::vector BlackBoxFuncCall::BigIntToLeBytes::bincodeSerialize() const { auto serializer = serde::BincodeSerializer(); - serde::Serializable::serialize(*this, serializer); + serde::Serializable::serialize(*this, serializer); + return std::move(serializer).bytes(); +} + +inline BlackBoxFuncCall::BigIntToLeBytes BlackBoxFuncCall::BigIntToLeBytes::bincodeDeserialize( + std::vector input) +{ + auto deserializer = serde::BincodeDeserializer(input); + auto value = serde::Deserializable::deserialize(deserializer); + if (deserializer.get_buffer_offset() < input.size()) { + throw_or_abort("Some input bytes were not read"); + } + return value; +} + +} // end of namespace Circuit + +template <> +template +void serde::Serializable::serialize( + const Circuit::BlackBoxFuncCall::BigIntToLeBytes& obj, Serializer& serializer) +{ + serde::Serializable::serialize(obj.input, serializer); + serde::Serializable::serialize(obj.outputs, serializer); +} + +template <> +template +Circuit::BlackBoxFuncCall::BigIntToLeBytes serde::Deserializable< + Circuit::BlackBoxFuncCall::BigIntToLeBytes>::deserialize(Deserializer& deserializer) +{ + Circuit::BlackBoxFuncCall::BigIntToLeBytes obj; + obj.input = serde::Deserializable::deserialize(deserializer); + obj.outputs = serde::Deserializable::deserialize(deserializer); + return obj; +} + +namespace Circuit { + +inline bool operator==(const BlackBoxFuncCall::Poseidon2Permutation& lhs, + const BlackBoxFuncCall::Poseidon2Permutation& rhs) +{ + if (!(lhs.inputs == rhs.inputs)) { + return false; + } + if (!(lhs.outputs == rhs.outputs)) { + return false; + } + if (!(lhs.len == rhs.len)) { + return false; + } + return true; +} + +inline std::vector BlackBoxFuncCall::Poseidon2Permutation::bincodeSerialize() const +{ + auto serializer = serde::BincodeSerializer(); + serde::Serializable::serialize(*this, serializer); + return std::move(serializer).bytes(); +} + +inline BlackBoxFuncCall::Poseidon2Permutation BlackBoxFuncCall::Poseidon2Permutation::bincodeDeserialize( + std::vector input) +{ + auto deserializer = serde::BincodeDeserializer(input); + auto value = serde::Deserializable::deserialize(deserializer); + if (deserializer.get_buffer_offset() < input.size()) { + throw_or_abort("Some input bytes were not read"); + } + return value; +} + +} // end of namespace Circuit + +template <> +template +void serde::Serializable::serialize( + const Circuit::BlackBoxFuncCall::Poseidon2Permutation& obj, Serializer& serializer) +{ + serde::Serializable::serialize(obj.inputs, serializer); + serde::Serializable::serialize(obj.outputs, serializer); + serde::Serializable::serialize(obj.len, serializer); +} + +template <> +template +Circuit::BlackBoxFuncCall::Poseidon2Permutation serde::Deserializable< + Circuit::BlackBoxFuncCall::Poseidon2Permutation>::deserialize(Deserializer& deserializer) +{ + Circuit::BlackBoxFuncCall::Poseidon2Permutation obj; + obj.inputs = serde::Deserializable::deserialize(deserializer); + obj.outputs = serde::Deserializable::deserialize(deserializer); + obj.len = serde::Deserializable::deserialize(deserializer); + return obj; +} + +namespace Circuit { + +inline bool operator==(const BlackBoxFuncCall::Sha256Compression& lhs, const BlackBoxFuncCall::Sha256Compression& rhs) +{ + if (!(lhs.inputs == rhs.inputs)) { + return false; + } + if (!(lhs.hash_values == rhs.hash_values)) { + return false; + } + if (!(lhs.outputs == rhs.outputs)) { + return false; + } + return true; +} + +inline std::vector BlackBoxFuncCall::Sha256Compression::bincodeSerialize() const +{ + auto serializer = serde::BincodeSerializer(); + serde::Serializable::serialize(*this, serializer); + return std::move(serializer).bytes(); +} + +inline BlackBoxFuncCall::Sha256Compression BlackBoxFuncCall::Sha256Compression::bincodeDeserialize( + std::vector input) +{ + auto deserializer = serde::BincodeDeserializer(input); + auto value = serde::Deserializable::deserialize(deserializer); + if (deserializer.get_buffer_offset() < input.size()) { + throw_or_abort("Some input bytes were not read"); + } + return value; +} + +} // end of namespace Circuit + +template <> +template +void serde::Serializable::serialize( + const Circuit::BlackBoxFuncCall::Sha256Compression& obj, Serializer& serializer) +{ + serde::Serializable::serialize(obj.inputs, serializer); + serde::Serializable::serialize(obj.hash_values, serializer); + serde::Serializable::serialize(obj.outputs, serializer); +} + +template <> +template +Circuit::BlackBoxFuncCall::Sha256Compression serde::Deserializable< + Circuit::BlackBoxFuncCall::Sha256Compression>::deserialize(Deserializer& deserializer) +{ + Circuit::BlackBoxFuncCall::Sha256Compression obj; + obj.inputs = serde::Deserializable::deserialize(deserializer); + obj.hash_values = serde::Deserializable::deserialize(deserializer); + obj.outputs = serde::Deserializable::deserialize(deserializer); + return obj; +} + +namespace Circuit { + +inline bool operator==(const BlackBoxOp& lhs, const BlackBoxOp& rhs) +{ + if (!(lhs.value == rhs.value)) { + return false; + } + return true; +} + +inline std::vector BlackBoxOp::bincodeSerialize() const +{ + auto serializer = serde::BincodeSerializer(); + serde::Serializable::serialize(*this, serializer); + return std::move(serializer).bytes(); +} + +inline BlackBoxOp BlackBoxOp::bincodeDeserialize(std::vector input) +{ + auto deserializer = serde::BincodeDeserializer(input); + auto value = serde::Deserializable::deserialize(deserializer); + if (deserializer.get_buffer_offset() < input.size()) { + throw_or_abort("Some input bytes were not read"); + } + return value; +} + +} // end of namespace Circuit + +template <> +template +void serde::Serializable::serialize(const Circuit::BlackBoxOp& obj, Serializer& serializer) +{ + serializer.increase_container_depth(); + serde::Serializable::serialize(obj.value, serializer); + serializer.decrease_container_depth(); +} + +template <> +template +Circuit::BlackBoxOp serde::Deserializable::deserialize(Deserializer& deserializer) +{ + deserializer.increase_container_depth(); + Circuit::BlackBoxOp obj; + obj.value = serde::Deserializable::deserialize(deserializer); + deserializer.decrease_container_depth(); + return obj; +} + +namespace Circuit { + +inline bool operator==(const BlackBoxOp::Sha256& lhs, const BlackBoxOp::Sha256& rhs) +{ + if (!(lhs.message == rhs.message)) { + return false; + } + if (!(lhs.output == rhs.output)) { + return false; + } + return true; +} + +inline std::vector BlackBoxOp::Sha256::bincodeSerialize() const +{ + auto serializer = serde::BincodeSerializer(); + serde::Serializable::serialize(*this, serializer); + return std::move(serializer).bytes(); +} + +inline BlackBoxOp::Sha256 BlackBoxOp::Sha256::bincodeDeserialize(std::vector input) +{ + auto deserializer = serde::BincodeDeserializer(input); + auto value = serde::Deserializable::deserialize(deserializer); + if (deserializer.get_buffer_offset() < input.size()) { + throw_or_abort("Some input bytes were not read"); + } + return value; +} + +} // end of namespace Circuit + +template <> +template +void serde::Serializable::serialize(const Circuit::BlackBoxOp::Sha256& obj, + Serializer& serializer) +{ + serde::Serializable::serialize(obj.message, serializer); + serde::Serializable::serialize(obj.output, serializer); +} + +template <> +template +Circuit::BlackBoxOp::Sha256 serde::Deserializable::deserialize(Deserializer& deserializer) +{ + Circuit::BlackBoxOp::Sha256 obj; + obj.message = serde::Deserializable::deserialize(deserializer); + obj.output = serde::Deserializable::deserialize(deserializer); + return obj; +} + +namespace Circuit { + +inline bool operator==(const BlackBoxOp::Blake2s& lhs, const BlackBoxOp::Blake2s& rhs) +{ + if (!(lhs.message == rhs.message)) { + return false; + } + if (!(lhs.output == rhs.output)) { + return false; + } + return true; +} + +inline std::vector BlackBoxOp::Blake2s::bincodeSerialize() const +{ + auto serializer = serde::BincodeSerializer(); + serde::Serializable::serialize(*this, serializer); + return std::move(serializer).bytes(); +} + +inline BlackBoxOp::Blake2s BlackBoxOp::Blake2s::bincodeDeserialize(std::vector input) +{ + auto deserializer = serde::BincodeDeserializer(input); + auto value = serde::Deserializable::deserialize(deserializer); + if (deserializer.get_buffer_offset() < input.size()) { + throw_or_abort("Some input bytes were not read"); + } + return value; +} + +} // end of namespace Circuit + +template <> +template +void serde::Serializable::serialize(const Circuit::BlackBoxOp::Blake2s& obj, + Serializer& serializer) +{ + serde::Serializable::serialize(obj.message, serializer); + serde::Serializable::serialize(obj.output, serializer); +} + +template <> +template +Circuit::BlackBoxOp::Blake2s serde::Deserializable::deserialize( + Deserializer& deserializer) +{ + Circuit::BlackBoxOp::Blake2s obj; + obj.message = serde::Deserializable::deserialize(deserializer); + obj.output = serde::Deserializable::deserialize(deserializer); + return obj; +} + +namespace Circuit { + +inline bool operator==(const BlackBoxOp::Blake3& lhs, const BlackBoxOp::Blake3& rhs) +{ + if (!(lhs.message == rhs.message)) { + return false; + } + if (!(lhs.output == rhs.output)) { + return false; + } + return true; +} + +inline std::vector BlackBoxOp::Blake3::bincodeSerialize() const +{ + auto serializer = serde::BincodeSerializer(); + serde::Serializable::serialize(*this, serializer); + return std::move(serializer).bytes(); +} + +inline BlackBoxOp::Blake3 BlackBoxOp::Blake3::bincodeDeserialize(std::vector input) +{ + auto deserializer = serde::BincodeDeserializer(input); + auto value = serde::Deserializable::deserialize(deserializer); + if (deserializer.get_buffer_offset() < input.size()) { + throw_or_abort("Some input bytes were not read"); + } + return value; +} + +} // end of namespace Circuit + +template <> +template +void serde::Serializable::serialize(const Circuit::BlackBoxOp::Blake3& obj, + Serializer& serializer) +{ + serde::Serializable::serialize(obj.message, serializer); + serde::Serializable::serialize(obj.output, serializer); +} + +template <> +template +Circuit::BlackBoxOp::Blake3 serde::Deserializable::deserialize(Deserializer& deserializer) +{ + Circuit::BlackBoxOp::Blake3 obj; + obj.message = serde::Deserializable::deserialize(deserializer); + obj.output = serde::Deserializable::deserialize(deserializer); + return obj; +} + +namespace Circuit { + +inline bool operator==(const BlackBoxOp::Keccak256& lhs, const BlackBoxOp::Keccak256& rhs) +{ + if (!(lhs.message == rhs.message)) { + return false; + } + if (!(lhs.output == rhs.output)) { + return false; + } + return true; +} + +inline std::vector BlackBoxOp::Keccak256::bincodeSerialize() const +{ + auto serializer = serde::BincodeSerializer(); + serde::Serializable::serialize(*this, serializer); + return std::move(serializer).bytes(); +} + +inline BlackBoxOp::Keccak256 BlackBoxOp::Keccak256::bincodeDeserialize(std::vector input) +{ + auto deserializer = serde::BincodeDeserializer(input); + auto value = serde::Deserializable::deserialize(deserializer); + if (deserializer.get_buffer_offset() < input.size()) { + throw_or_abort("Some input bytes were not read"); + } + return value; +} + +} // end of namespace Circuit + +template <> +template +void serde::Serializable::serialize(const Circuit::BlackBoxOp::Keccak256& obj, + Serializer& serializer) +{ + serde::Serializable::serialize(obj.message, serializer); + serde::Serializable::serialize(obj.output, serializer); +} + +template <> +template +Circuit::BlackBoxOp::Keccak256 serde::Deserializable::deserialize( + Deserializer& deserializer) +{ + Circuit::BlackBoxOp::Keccak256 obj; + obj.message = serde::Deserializable::deserialize(deserializer); + obj.output = serde::Deserializable::deserialize(deserializer); + return obj; +} + +namespace Circuit { + +inline bool operator==(const BlackBoxOp::Keccakf1600& lhs, const BlackBoxOp::Keccakf1600& rhs) +{ + if (!(lhs.message == rhs.message)) { + return false; + } + if (!(lhs.output == rhs.output)) { + return false; + } + return true; +} + +inline std::vector BlackBoxOp::Keccakf1600::bincodeSerialize() const +{ + auto serializer = serde::BincodeSerializer(); + serde::Serializable::serialize(*this, serializer); return std::move(serializer).bytes(); } inline BlackBoxOp::Keccakf1600 BlackBoxOp::Keccakf1600::bincodeDeserialize(std::vector input) { auto deserializer = serde::BincodeDeserializer(input); - auto value = serde::Deserializable::deserialize(deserializer); + auto value = serde::Deserializable::deserialize(deserializer); + if (deserializer.get_buffer_offset() < input.size()) { + throw_or_abort("Some input bytes were not read"); + } + return value; +} + +} // end of namespace Circuit + +template <> +template +void serde::Serializable::serialize(const Circuit::BlackBoxOp::Keccakf1600& obj, + Serializer& serializer) +{ + serde::Serializable::serialize(obj.message, serializer); + serde::Serializable::serialize(obj.output, serializer); +} + +template <> +template +Circuit::BlackBoxOp::Keccakf1600 serde::Deserializable::deserialize( + Deserializer& deserializer) +{ + Circuit::BlackBoxOp::Keccakf1600 obj; + obj.message = serde::Deserializable::deserialize(deserializer); + obj.output = serde::Deserializable::deserialize(deserializer); + return obj; +} + +namespace Circuit { + +inline bool operator==(const BlackBoxOp::EcdsaSecp256k1& lhs, const BlackBoxOp::EcdsaSecp256k1& rhs) +{ + if (!(lhs.hashed_msg == rhs.hashed_msg)) { + return false; + } + if (!(lhs.public_key_x == rhs.public_key_x)) { + return false; + } + if (!(lhs.public_key_y == rhs.public_key_y)) { + return false; + } + if (!(lhs.signature == rhs.signature)) { + return false; + } + if (!(lhs.result == rhs.result)) { + return false; + } + return true; +} + +inline std::vector BlackBoxOp::EcdsaSecp256k1::bincodeSerialize() const +{ + auto serializer = serde::BincodeSerializer(); + serde::Serializable::serialize(*this, serializer); + return std::move(serializer).bytes(); +} + +inline BlackBoxOp::EcdsaSecp256k1 BlackBoxOp::EcdsaSecp256k1::bincodeDeserialize(std::vector input) +{ + auto deserializer = serde::BincodeDeserializer(input); + auto value = serde::Deserializable::deserialize(deserializer); + if (deserializer.get_buffer_offset() < input.size()) { + throw_or_abort("Some input bytes were not read"); + } + return value; +} + +} // end of namespace Circuit + +template <> +template +void serde::Serializable::serialize(const Circuit::BlackBoxOp::EcdsaSecp256k1& obj, + Serializer& serializer) +{ + serde::Serializable::serialize(obj.hashed_msg, serializer); + serde::Serializable::serialize(obj.public_key_x, serializer); + serde::Serializable::serialize(obj.public_key_y, serializer); + serde::Serializable::serialize(obj.signature, serializer); + serde::Serializable::serialize(obj.result, serializer); +} + +template <> +template +Circuit::BlackBoxOp::EcdsaSecp256k1 serde::Deserializable::deserialize( + Deserializer& deserializer) +{ + Circuit::BlackBoxOp::EcdsaSecp256k1 obj; + obj.hashed_msg = serde::Deserializable::deserialize(deserializer); + obj.public_key_x = serde::Deserializable::deserialize(deserializer); + obj.public_key_y = serde::Deserializable::deserialize(deserializer); + obj.signature = serde::Deserializable::deserialize(deserializer); + obj.result = serde::Deserializable::deserialize(deserializer); + return obj; +} + +namespace Circuit { + +inline bool operator==(const BlackBoxOp::EcdsaSecp256r1& lhs, const BlackBoxOp::EcdsaSecp256r1& rhs) +{ + if (!(lhs.hashed_msg == rhs.hashed_msg)) { + return false; + } + if (!(lhs.public_key_x == rhs.public_key_x)) { + return false; + } + if (!(lhs.public_key_y == rhs.public_key_y)) { + return false; + } + if (!(lhs.signature == rhs.signature)) { + return false; + } + if (!(lhs.result == rhs.result)) { + return false; + } + return true; +} + +inline std::vector BlackBoxOp::EcdsaSecp256r1::bincodeSerialize() const +{ + auto serializer = serde::BincodeSerializer(); + serde::Serializable::serialize(*this, serializer); + return std::move(serializer).bytes(); +} + +inline BlackBoxOp::EcdsaSecp256r1 BlackBoxOp::EcdsaSecp256r1::bincodeDeserialize(std::vector input) +{ + auto deserializer = serde::BincodeDeserializer(input); + auto value = serde::Deserializable::deserialize(deserializer); + if (deserializer.get_buffer_offset() < input.size()) { + throw_or_abort("Some input bytes were not read"); + } + return value; +} + +} // end of namespace Circuit + +template <> +template +void serde::Serializable::serialize(const Circuit::BlackBoxOp::EcdsaSecp256r1& obj, + Serializer& serializer) +{ + serde::Serializable::serialize(obj.hashed_msg, serializer); + serde::Serializable::serialize(obj.public_key_x, serializer); + serde::Serializable::serialize(obj.public_key_y, serializer); + serde::Serializable::serialize(obj.signature, serializer); + serde::Serializable::serialize(obj.result, serializer); +} + +template <> +template +Circuit::BlackBoxOp::EcdsaSecp256r1 serde::Deserializable::deserialize( + Deserializer& deserializer) +{ + Circuit::BlackBoxOp::EcdsaSecp256r1 obj; + obj.hashed_msg = serde::Deserializable::deserialize(deserializer); + obj.public_key_x = serde::Deserializable::deserialize(deserializer); + obj.public_key_y = serde::Deserializable::deserialize(deserializer); + obj.signature = serde::Deserializable::deserialize(deserializer); + obj.result = serde::Deserializable::deserialize(deserializer); + return obj; +} + +namespace Circuit { + +inline bool operator==(const BlackBoxOp::SchnorrVerify& lhs, const BlackBoxOp::SchnorrVerify& rhs) +{ + if (!(lhs.public_key_x == rhs.public_key_x)) { + return false; + } + if (!(lhs.public_key_y == rhs.public_key_y)) { + return false; + } + if (!(lhs.message == rhs.message)) { + return false; + } + if (!(lhs.signature == rhs.signature)) { + return false; + } + if (!(lhs.result == rhs.result)) { + return false; + } + return true; +} + +inline std::vector BlackBoxOp::SchnorrVerify::bincodeSerialize() const +{ + auto serializer = serde::BincodeSerializer(); + serde::Serializable::serialize(*this, serializer); + return std::move(serializer).bytes(); +} + +inline BlackBoxOp::SchnorrVerify BlackBoxOp::SchnorrVerify::bincodeDeserialize(std::vector input) +{ + auto deserializer = serde::BincodeDeserializer(input); + auto value = serde::Deserializable::deserialize(deserializer); + if (deserializer.get_buffer_offset() < input.size()) { + throw_or_abort("Some input bytes were not read"); + } + return value; +} + +} // end of namespace Circuit + +template <> +template +void serde::Serializable::serialize(const Circuit::BlackBoxOp::SchnorrVerify& obj, + Serializer& serializer) +{ + serde::Serializable::serialize(obj.public_key_x, serializer); + serde::Serializable::serialize(obj.public_key_y, serializer); + serde::Serializable::serialize(obj.message, serializer); + serde::Serializable::serialize(obj.signature, serializer); + serde::Serializable::serialize(obj.result, serializer); +} + +template <> +template +Circuit::BlackBoxOp::SchnorrVerify serde::Deserializable::deserialize( + Deserializer& deserializer) +{ + Circuit::BlackBoxOp::SchnorrVerify obj; + obj.public_key_x = serde::Deserializable::deserialize(deserializer); + obj.public_key_y = serde::Deserializable::deserialize(deserializer); + obj.message = serde::Deserializable::deserialize(deserializer); + obj.signature = serde::Deserializable::deserialize(deserializer); + obj.result = serde::Deserializable::deserialize(deserializer); + return obj; +} + +namespace Circuit { + +inline bool operator==(const BlackBoxOp::PedersenCommitment& lhs, const BlackBoxOp::PedersenCommitment& rhs) +{ + if (!(lhs.inputs == rhs.inputs)) { + return false; + } + if (!(lhs.domain_separator == rhs.domain_separator)) { + return false; + } + if (!(lhs.output == rhs.output)) { + return false; + } + return true; +} + +inline std::vector BlackBoxOp::PedersenCommitment::bincodeSerialize() const +{ + auto serializer = serde::BincodeSerializer(); + serde::Serializable::serialize(*this, serializer); + return std::move(serializer).bytes(); +} + +inline BlackBoxOp::PedersenCommitment BlackBoxOp::PedersenCommitment::bincodeDeserialize(std::vector input) +{ + auto deserializer = serde::BincodeDeserializer(input); + auto value = serde::Deserializable::deserialize(deserializer); + if (deserializer.get_buffer_offset() < input.size()) { + throw_or_abort("Some input bytes were not read"); + } + return value; +} + +} // end of namespace Circuit + +template <> +template +void serde::Serializable::serialize( + const Circuit::BlackBoxOp::PedersenCommitment& obj, Serializer& serializer) +{ + serde::Serializable::serialize(obj.inputs, serializer); + serde::Serializable::serialize(obj.domain_separator, serializer); + serde::Serializable::serialize(obj.output, serializer); +} + +template <> +template +Circuit::BlackBoxOp::PedersenCommitment serde::Deserializable::deserialize( + Deserializer& deserializer) +{ + Circuit::BlackBoxOp::PedersenCommitment obj; + obj.inputs = serde::Deserializable::deserialize(deserializer); + obj.domain_separator = serde::Deserializable::deserialize(deserializer); + obj.output = serde::Deserializable::deserialize(deserializer); + return obj; +} + +namespace Circuit { + +inline bool operator==(const BlackBoxOp::PedersenHash& lhs, const BlackBoxOp::PedersenHash& rhs) +{ + if (!(lhs.inputs == rhs.inputs)) { + return false; + } + if (!(lhs.domain_separator == rhs.domain_separator)) { + return false; + } + if (!(lhs.output == rhs.output)) { + return false; + } + return true; +} + +inline std::vector BlackBoxOp::PedersenHash::bincodeSerialize() const +{ + auto serializer = serde::BincodeSerializer(); + serde::Serializable::serialize(*this, serializer); + return std::move(serializer).bytes(); +} + +inline BlackBoxOp::PedersenHash BlackBoxOp::PedersenHash::bincodeDeserialize(std::vector input) +{ + auto deserializer = serde::BincodeDeserializer(input); + auto value = serde::Deserializable::deserialize(deserializer); if (deserializer.get_buffer_offset() < input.size()) { throw_or_abort("Some input bytes were not read"); } @@ -3170,38 +4044,34 @@ inline BlackBoxOp::Keccakf1600 BlackBoxOp::Keccakf1600::bincodeDeserialize(std:: template <> template -void serde::Serializable::serialize(const Circuit::BlackBoxOp::Keccakf1600& obj, - Serializer& serializer) +void serde::Serializable::serialize(const Circuit::BlackBoxOp::PedersenHash& obj, + Serializer& serializer) { - serde::Serializable::serialize(obj.message, serializer); + serde::Serializable::serialize(obj.inputs, serializer); + serde::Serializable::serialize(obj.domain_separator, serializer); serde::Serializable::serialize(obj.output, serializer); } template <> template -Circuit::BlackBoxOp::Keccakf1600 serde::Deserializable::deserialize( +Circuit::BlackBoxOp::PedersenHash serde::Deserializable::deserialize( Deserializer& deserializer) { - Circuit::BlackBoxOp::Keccakf1600 obj; - obj.message = serde::Deserializable::deserialize(deserializer); + Circuit::BlackBoxOp::PedersenHash obj; + obj.inputs = serde::Deserializable::deserialize(deserializer); + obj.domain_separator = serde::Deserializable::deserialize(deserializer); obj.output = serde::Deserializable::deserialize(deserializer); return obj; } namespace Circuit { -inline bool operator==(const BlackBoxOp::EcdsaSecp256k1& lhs, const BlackBoxOp::EcdsaSecp256k1& rhs) +inline bool operator==(const BlackBoxOp::FixedBaseScalarMul& lhs, const BlackBoxOp::FixedBaseScalarMul& rhs) { - if (!(lhs.hashed_msg == rhs.hashed_msg)) { - return false; - } - if (!(lhs.public_key_x == rhs.public_key_x)) { - return false; - } - if (!(lhs.public_key_y == rhs.public_key_y)) { + if (!(lhs.low == rhs.low)) { return false; } - if (!(lhs.signature == rhs.signature)) { + if (!(lhs.high == rhs.high)) { return false; } if (!(lhs.result == rhs.result)) { @@ -3210,17 +4080,17 @@ inline bool operator==(const BlackBoxOp::EcdsaSecp256k1& lhs, const BlackBoxOp:: return true; } -inline std::vector BlackBoxOp::EcdsaSecp256k1::bincodeSerialize() const +inline std::vector BlackBoxOp::FixedBaseScalarMul::bincodeSerialize() const { auto serializer = serde::BincodeSerializer(); - serde::Serializable::serialize(*this, serializer); + serde::Serializable::serialize(*this, serializer); return std::move(serializer).bytes(); } -inline BlackBoxOp::EcdsaSecp256k1 BlackBoxOp::EcdsaSecp256k1::bincodeDeserialize(std::vector input) +inline BlackBoxOp::FixedBaseScalarMul BlackBoxOp::FixedBaseScalarMul::bincodeDeserialize(std::vector input) { auto deserializer = serde::BincodeDeserializer(input); - auto value = serde::Deserializable::deserialize(deserializer); + auto value = serde::Deserializable::deserialize(deserializer); if (deserializer.get_buffer_offset() < input.size()) { throw_or_abort("Some input bytes were not read"); } @@ -3231,44 +4101,40 @@ inline BlackBoxOp::EcdsaSecp256k1 BlackBoxOp::EcdsaSecp256k1::bincodeDeserialize template <> template -void serde::Serializable::serialize(const Circuit::BlackBoxOp::EcdsaSecp256k1& obj, - Serializer& serializer) +void serde::Serializable::serialize( + const Circuit::BlackBoxOp::FixedBaseScalarMul& obj, Serializer& serializer) { - serde::Serializable::serialize(obj.hashed_msg, serializer); - serde::Serializable::serialize(obj.public_key_x, serializer); - serde::Serializable::serialize(obj.public_key_y, serializer); - serde::Serializable::serialize(obj.signature, serializer); + serde::Serializable::serialize(obj.low, serializer); + serde::Serializable::serialize(obj.high, serializer); serde::Serializable::serialize(obj.result, serializer); } template <> template -Circuit::BlackBoxOp::EcdsaSecp256k1 serde::Deserializable::deserialize( +Circuit::BlackBoxOp::FixedBaseScalarMul serde::Deserializable::deserialize( Deserializer& deserializer) { - Circuit::BlackBoxOp::EcdsaSecp256k1 obj; - obj.hashed_msg = serde::Deserializable::deserialize(deserializer); - obj.public_key_x = serde::Deserializable::deserialize(deserializer); - obj.public_key_y = serde::Deserializable::deserialize(deserializer); - obj.signature = serde::Deserializable::deserialize(deserializer); + Circuit::BlackBoxOp::FixedBaseScalarMul obj; + obj.low = serde::Deserializable::deserialize(deserializer); + obj.high = serde::Deserializable::deserialize(deserializer); obj.result = serde::Deserializable::deserialize(deserializer); return obj; } namespace Circuit { -inline bool operator==(const BlackBoxOp::EcdsaSecp256r1& lhs, const BlackBoxOp::EcdsaSecp256r1& rhs) +inline bool operator==(const BlackBoxOp::EmbeddedCurveAdd& lhs, const BlackBoxOp::EmbeddedCurveAdd& rhs) { - if (!(lhs.hashed_msg == rhs.hashed_msg)) { + if (!(lhs.input1_x == rhs.input1_x)) { return false; } - if (!(lhs.public_key_x == rhs.public_key_x)) { + if (!(lhs.input1_y == rhs.input1_y)) { return false; } - if (!(lhs.public_key_y == rhs.public_key_y)) { + if (!(lhs.input2_x == rhs.input2_x)) { return false; } - if (!(lhs.signature == rhs.signature)) { + if (!(lhs.input2_y == rhs.input2_y)) { return false; } if (!(lhs.result == rhs.result)) { @@ -3277,17 +4143,17 @@ inline bool operator==(const BlackBoxOp::EcdsaSecp256r1& lhs, const BlackBoxOp:: return true; } -inline std::vector BlackBoxOp::EcdsaSecp256r1::bincodeSerialize() const +inline std::vector BlackBoxOp::EmbeddedCurveAdd::bincodeSerialize() const { auto serializer = serde::BincodeSerializer(); - serde::Serializable::serialize(*this, serializer); + serde::Serializable::serialize(*this, serializer); return std::move(serializer).bytes(); } -inline BlackBoxOp::EcdsaSecp256r1 BlackBoxOp::EcdsaSecp256r1::bincodeDeserialize(std::vector input) +inline BlackBoxOp::EmbeddedCurveAdd BlackBoxOp::EmbeddedCurveAdd::bincodeDeserialize(std::vector input) { auto deserializer = serde::BincodeDeserializer(input); - auto value = serde::Deserializable::deserialize(deserializer); + auto value = serde::Deserializable::deserialize(deserializer); if (deserializer.get_buffer_offset() < input.size()) { throw_or_abort("Some input bytes were not read"); } @@ -3298,63 +4164,114 @@ inline BlackBoxOp::EcdsaSecp256r1 BlackBoxOp::EcdsaSecp256r1::bincodeDeserialize template <> template -void serde::Serializable::serialize(const Circuit::BlackBoxOp::EcdsaSecp256r1& obj, - Serializer& serializer) +void serde::Serializable::serialize( + const Circuit::BlackBoxOp::EmbeddedCurveAdd& obj, Serializer& serializer) { - serde::Serializable::serialize(obj.hashed_msg, serializer); - serde::Serializable::serialize(obj.public_key_x, serializer); - serde::Serializable::serialize(obj.public_key_y, serializer); - serde::Serializable::serialize(obj.signature, serializer); + serde::Serializable::serialize(obj.input1_x, serializer); + serde::Serializable::serialize(obj.input1_y, serializer); + serde::Serializable::serialize(obj.input2_x, serializer); + serde::Serializable::serialize(obj.input2_y, serializer); serde::Serializable::serialize(obj.result, serializer); } template <> template -Circuit::BlackBoxOp::EcdsaSecp256r1 serde::Deserializable::deserialize( +Circuit::BlackBoxOp::EmbeddedCurveAdd serde::Deserializable::deserialize( Deserializer& deserializer) { - Circuit::BlackBoxOp::EcdsaSecp256r1 obj; - obj.hashed_msg = serde::Deserializable::deserialize(deserializer); - obj.public_key_x = serde::Deserializable::deserialize(deserializer); - obj.public_key_y = serde::Deserializable::deserialize(deserializer); - obj.signature = serde::Deserializable::deserialize(deserializer); + Circuit::BlackBoxOp::EmbeddedCurveAdd obj; + obj.input1_x = serde::Deserializable::deserialize(deserializer); + obj.input1_y = serde::Deserializable::deserialize(deserializer); + obj.input2_x = serde::Deserializable::deserialize(deserializer); + obj.input2_y = serde::Deserializable::deserialize(deserializer); obj.result = serde::Deserializable::deserialize(deserializer); return obj; } namespace Circuit { -inline bool operator==(const BlackBoxOp::SchnorrVerify& lhs, const BlackBoxOp::SchnorrVerify& rhs) +inline bool operator==(const BlackBoxOp::BigIntAdd& lhs, const BlackBoxOp::BigIntAdd& rhs) { - if (!(lhs.public_key_x == rhs.public_key_x)) { + if (!(lhs.lhs == rhs.lhs)) { return false; } - if (!(lhs.public_key_y == rhs.public_key_y)) { + if (!(lhs.rhs == rhs.rhs)) { return false; } - if (!(lhs.message == rhs.message)) { + if (!(lhs.output == rhs.output)) { return false; } - if (!(lhs.signature == rhs.signature)) { + return true; +} + +inline std::vector BlackBoxOp::BigIntAdd::bincodeSerialize() const +{ + auto serializer = serde::BincodeSerializer(); + serde::Serializable::serialize(*this, serializer); + return std::move(serializer).bytes(); +} + +inline BlackBoxOp::BigIntAdd BlackBoxOp::BigIntAdd::bincodeDeserialize(std::vector input) +{ + auto deserializer = serde::BincodeDeserializer(input); + auto value = serde::Deserializable::deserialize(deserializer); + if (deserializer.get_buffer_offset() < input.size()) { + throw_or_abort("Some input bytes were not read"); + } + return value; +} + +} // end of namespace Circuit + +template <> +template +void serde::Serializable::serialize(const Circuit::BlackBoxOp::BigIntAdd& obj, + Serializer& serializer) +{ + serde::Serializable::serialize(obj.lhs, serializer); + serde::Serializable::serialize(obj.rhs, serializer); + serde::Serializable::serialize(obj.output, serializer); +} + +template <> +template +Circuit::BlackBoxOp::BigIntAdd serde::Deserializable::deserialize( + Deserializer& deserializer) +{ + Circuit::BlackBoxOp::BigIntAdd obj; + obj.lhs = serde::Deserializable::deserialize(deserializer); + obj.rhs = serde::Deserializable::deserialize(deserializer); + obj.output = serde::Deserializable::deserialize(deserializer); + return obj; +} + +namespace Circuit { + +inline bool operator==(const BlackBoxOp::BigIntNeg& lhs, const BlackBoxOp::BigIntNeg& rhs) +{ + if (!(lhs.lhs == rhs.lhs)) { return false; } - if (!(lhs.result == rhs.result)) { + if (!(lhs.rhs == rhs.rhs)) { + return false; + } + if (!(lhs.output == rhs.output)) { return false; } return true; } -inline std::vector BlackBoxOp::SchnorrVerify::bincodeSerialize() const +inline std::vector BlackBoxOp::BigIntNeg::bincodeSerialize() const { auto serializer = serde::BincodeSerializer(); - serde::Serializable::serialize(*this, serializer); + serde::Serializable::serialize(*this, serializer); return std::move(serializer).bytes(); } -inline BlackBoxOp::SchnorrVerify BlackBoxOp::SchnorrVerify::bincodeDeserialize(std::vector input) +inline BlackBoxOp::BigIntNeg BlackBoxOp::BigIntNeg::bincodeDeserialize(std::vector input) { auto deserializer = serde::BincodeDeserializer(input); - auto value = serde::Deserializable::deserialize(deserializer); + auto value = serde::Deserializable::deserialize(deserializer); if (deserializer.get_buffer_offset() < input.size()) { throw_or_abort("Some input bytes were not read"); } @@ -3365,38 +4282,34 @@ inline BlackBoxOp::SchnorrVerify BlackBoxOp::SchnorrVerify::bincodeDeserialize(s template <> template -void serde::Serializable::serialize(const Circuit::BlackBoxOp::SchnorrVerify& obj, - Serializer& serializer) +void serde::Serializable::serialize(const Circuit::BlackBoxOp::BigIntNeg& obj, + Serializer& serializer) { - serde::Serializable::serialize(obj.public_key_x, serializer); - serde::Serializable::serialize(obj.public_key_y, serializer); - serde::Serializable::serialize(obj.message, serializer); - serde::Serializable::serialize(obj.signature, serializer); - serde::Serializable::serialize(obj.result, serializer); + serde::Serializable::serialize(obj.lhs, serializer); + serde::Serializable::serialize(obj.rhs, serializer); + serde::Serializable::serialize(obj.output, serializer); } template <> template -Circuit::BlackBoxOp::SchnorrVerify serde::Deserializable::deserialize( +Circuit::BlackBoxOp::BigIntNeg serde::Deserializable::deserialize( Deserializer& deserializer) { - Circuit::BlackBoxOp::SchnorrVerify obj; - obj.public_key_x = serde::Deserializable::deserialize(deserializer); - obj.public_key_y = serde::Deserializable::deserialize(deserializer); - obj.message = serde::Deserializable::deserialize(deserializer); - obj.signature = serde::Deserializable::deserialize(deserializer); - obj.result = serde::Deserializable::deserialize(deserializer); + Circuit::BlackBoxOp::BigIntNeg obj; + obj.lhs = serde::Deserializable::deserialize(deserializer); + obj.rhs = serde::Deserializable::deserialize(deserializer); + obj.output = serde::Deserializable::deserialize(deserializer); return obj; } namespace Circuit { -inline bool operator==(const BlackBoxOp::PedersenCommitment& lhs, const BlackBoxOp::PedersenCommitment& rhs) +inline bool operator==(const BlackBoxOp::BigIntMul& lhs, const BlackBoxOp::BigIntMul& rhs) { - if (!(lhs.inputs == rhs.inputs)) { + if (!(lhs.lhs == rhs.lhs)) { return false; } - if (!(lhs.domain_separator == rhs.domain_separator)) { + if (!(lhs.rhs == rhs.rhs)) { return false; } if (!(lhs.output == rhs.output)) { @@ -3405,17 +4318,74 @@ inline bool operator==(const BlackBoxOp::PedersenCommitment& lhs, const BlackBox return true; } -inline std::vector BlackBoxOp::PedersenCommitment::bincodeSerialize() const +inline std::vector BlackBoxOp::BigIntMul::bincodeSerialize() const { auto serializer = serde::BincodeSerializer(); - serde::Serializable::serialize(*this, serializer); + serde::Serializable::serialize(*this, serializer); return std::move(serializer).bytes(); } -inline BlackBoxOp::PedersenCommitment BlackBoxOp::PedersenCommitment::bincodeDeserialize(std::vector input) +inline BlackBoxOp::BigIntMul BlackBoxOp::BigIntMul::bincodeDeserialize(std::vector input) { auto deserializer = serde::BincodeDeserializer(input); - auto value = serde::Deserializable::deserialize(deserializer); + auto value = serde::Deserializable::deserialize(deserializer); + if (deserializer.get_buffer_offset() < input.size()) { + throw_or_abort("Some input bytes were not read"); + } + return value; +} + +} // end of namespace Circuit + +template <> +template +void serde::Serializable::serialize(const Circuit::BlackBoxOp::BigIntMul& obj, + Serializer& serializer) +{ + serde::Serializable::serialize(obj.lhs, serializer); + serde::Serializable::serialize(obj.rhs, serializer); + serde::Serializable::serialize(obj.output, serializer); +} + +template <> +template +Circuit::BlackBoxOp::BigIntMul serde::Deserializable::deserialize( + Deserializer& deserializer) +{ + Circuit::BlackBoxOp::BigIntMul obj; + obj.lhs = serde::Deserializable::deserialize(deserializer); + obj.rhs = serde::Deserializable::deserialize(deserializer); + obj.output = serde::Deserializable::deserialize(deserializer); + return obj; +} + +namespace Circuit { + +inline bool operator==(const BlackBoxOp::BigIntDiv& lhs, const BlackBoxOp::BigIntDiv& rhs) +{ + if (!(lhs.lhs == rhs.lhs)) { + return false; + } + if (!(lhs.rhs == rhs.rhs)) { + return false; + } + if (!(lhs.output == rhs.output)) { + return false; + } + return true; +} + +inline std::vector BlackBoxOp::BigIntDiv::bincodeSerialize() const +{ + auto serializer = serde::BincodeSerializer(); + serde::Serializable::serialize(*this, serializer); + return std::move(serializer).bytes(); +} + +inline BlackBoxOp::BigIntDiv BlackBoxOp::BigIntDiv::bincodeDeserialize(std::vector input) +{ + auto deserializer = serde::BincodeDeserializer(input); + auto value = serde::Deserializable::deserialize(deserializer); if (deserializer.get_buffer_offset() < input.size()) { throw_or_abort("Some input bytes were not read"); } @@ -3426,34 +4396,34 @@ inline BlackBoxOp::PedersenCommitment BlackBoxOp::PedersenCommitment::bincodeDes template <> template -void serde::Serializable::serialize( - const Circuit::BlackBoxOp::PedersenCommitment& obj, Serializer& serializer) +void serde::Serializable::serialize(const Circuit::BlackBoxOp::BigIntDiv& obj, + Serializer& serializer) { - serde::Serializable::serialize(obj.inputs, serializer); - serde::Serializable::serialize(obj.domain_separator, serializer); + serde::Serializable::serialize(obj.lhs, serializer); + serde::Serializable::serialize(obj.rhs, serializer); serde::Serializable::serialize(obj.output, serializer); } template <> template -Circuit::BlackBoxOp::PedersenCommitment serde::Deserializable::deserialize( +Circuit::BlackBoxOp::BigIntDiv serde::Deserializable::deserialize( Deserializer& deserializer) { - Circuit::BlackBoxOp::PedersenCommitment obj; - obj.inputs = serde::Deserializable::deserialize(deserializer); - obj.domain_separator = serde::Deserializable::deserialize(deserializer); + Circuit::BlackBoxOp::BigIntDiv obj; + obj.lhs = serde::Deserializable::deserialize(deserializer); + obj.rhs = serde::Deserializable::deserialize(deserializer); obj.output = serde::Deserializable::deserialize(deserializer); return obj; } namespace Circuit { -inline bool operator==(const BlackBoxOp::PedersenHash& lhs, const BlackBoxOp::PedersenHash& rhs) +inline bool operator==(const BlackBoxOp::BigIntFromLeBytes& lhs, const BlackBoxOp::BigIntFromLeBytes& rhs) { if (!(lhs.inputs == rhs.inputs)) { return false; } - if (!(lhs.domain_separator == rhs.domain_separator)) { + if (!(lhs.modulus == rhs.modulus)) { return false; } if (!(lhs.output == rhs.output)) { @@ -3462,17 +4432,17 @@ inline bool operator==(const BlackBoxOp::PedersenHash& lhs, const BlackBoxOp::Pe return true; } -inline std::vector BlackBoxOp::PedersenHash::bincodeSerialize() const +inline std::vector BlackBoxOp::BigIntFromLeBytes::bincodeSerialize() const { auto serializer = serde::BincodeSerializer(); - serde::Serializable::serialize(*this, serializer); + serde::Serializable::serialize(*this, serializer); return std::move(serializer).bytes(); } -inline BlackBoxOp::PedersenHash BlackBoxOp::PedersenHash::bincodeDeserialize(std::vector input) +inline BlackBoxOp::BigIntFromLeBytes BlackBoxOp::BigIntFromLeBytes::bincodeDeserialize(std::vector input) { auto deserializer = serde::BincodeDeserializer(input); - auto value = serde::Deserializable::deserialize(deserializer); + auto value = serde::Deserializable::deserialize(deserializer); if (deserializer.get_buffer_offset() < input.size()) { throw_or_abort("Some input bytes were not read"); } @@ -3483,53 +4453,50 @@ inline BlackBoxOp::PedersenHash BlackBoxOp::PedersenHash::bincodeDeserialize(std template <> template -void serde::Serializable::serialize(const Circuit::BlackBoxOp::PedersenHash& obj, - Serializer& serializer) +void serde::Serializable::serialize( + const Circuit::BlackBoxOp::BigIntFromLeBytes& obj, Serializer& serializer) { serde::Serializable::serialize(obj.inputs, serializer); - serde::Serializable::serialize(obj.domain_separator, serializer); + serde::Serializable::serialize(obj.modulus, serializer); serde::Serializable::serialize(obj.output, serializer); } template <> template -Circuit::BlackBoxOp::PedersenHash serde::Deserializable::deserialize( +Circuit::BlackBoxOp::BigIntFromLeBytes serde::Deserializable::deserialize( Deserializer& deserializer) { - Circuit::BlackBoxOp::PedersenHash obj; + Circuit::BlackBoxOp::BigIntFromLeBytes obj; obj.inputs = serde::Deserializable::deserialize(deserializer); - obj.domain_separator = serde::Deserializable::deserialize(deserializer); + obj.modulus = serde::Deserializable::deserialize(deserializer); obj.output = serde::Deserializable::deserialize(deserializer); return obj; } namespace Circuit { -inline bool operator==(const BlackBoxOp::FixedBaseScalarMul& lhs, const BlackBoxOp::FixedBaseScalarMul& rhs) +inline bool operator==(const BlackBoxOp::BigIntToLeBytes& lhs, const BlackBoxOp::BigIntToLeBytes& rhs) { - if (!(lhs.low == rhs.low)) { - return false; - } - if (!(lhs.high == rhs.high)) { + if (!(lhs.input == rhs.input)) { return false; } - if (!(lhs.result == rhs.result)) { + if (!(lhs.output == rhs.output)) { return false; } return true; } -inline std::vector BlackBoxOp::FixedBaseScalarMul::bincodeSerialize() const +inline std::vector BlackBoxOp::BigIntToLeBytes::bincodeSerialize() const { auto serializer = serde::BincodeSerializer(); - serde::Serializable::serialize(*this, serializer); + serde::Serializable::serialize(*this, serializer); return std::move(serializer).bytes(); } -inline BlackBoxOp::FixedBaseScalarMul BlackBoxOp::FixedBaseScalarMul::bincodeDeserialize(std::vector input) +inline BlackBoxOp::BigIntToLeBytes BlackBoxOp::BigIntToLeBytes::bincodeDeserialize(std::vector input) { auto deserializer = serde::BincodeDeserializer(input); - auto value = serde::Deserializable::deserialize(deserializer); + auto value = serde::Deserializable::deserialize(deserializer); if (deserializer.get_buffer_offset() < input.size()) { throw_or_abort("Some input bytes were not read"); } @@ -3540,59 +4507,51 @@ inline BlackBoxOp::FixedBaseScalarMul BlackBoxOp::FixedBaseScalarMul::bincodeDes template <> template -void serde::Serializable::serialize( - const Circuit::BlackBoxOp::FixedBaseScalarMul& obj, Serializer& serializer) +void serde::Serializable::serialize( + const Circuit::BlackBoxOp::BigIntToLeBytes& obj, Serializer& serializer) { - serde::Serializable::serialize(obj.low, serializer); - serde::Serializable::serialize(obj.high, serializer); - serde::Serializable::serialize(obj.result, serializer); + serde::Serializable::serialize(obj.input, serializer); + serde::Serializable::serialize(obj.output, serializer); } template <> template -Circuit::BlackBoxOp::FixedBaseScalarMul serde::Deserializable::deserialize( +Circuit::BlackBoxOp::BigIntToLeBytes serde::Deserializable::deserialize( Deserializer& deserializer) { - Circuit::BlackBoxOp::FixedBaseScalarMul obj; - obj.low = serde::Deserializable::deserialize(deserializer); - obj.high = serde::Deserializable::deserialize(deserializer); - obj.result = serde::Deserializable::deserialize(deserializer); + Circuit::BlackBoxOp::BigIntToLeBytes obj; + obj.input = serde::Deserializable::deserialize(deserializer); + obj.output = serde::Deserializable::deserialize(deserializer); return obj; } namespace Circuit { -inline bool operator==(const BlackBoxOp::EmbeddedCurveAdd& lhs, const BlackBoxOp::EmbeddedCurveAdd& rhs) +inline bool operator==(const BlackBoxOp::Poseidon2Permutation& lhs, const BlackBoxOp::Poseidon2Permutation& rhs) { - if (!(lhs.input1_x == rhs.input1_x)) { - return false; - } - if (!(lhs.input1_y == rhs.input1_y)) { - return false; - } - if (!(lhs.input2_x == rhs.input2_x)) { + if (!(lhs.message == rhs.message)) { return false; } - if (!(lhs.input2_y == rhs.input2_y)) { + if (!(lhs.output == rhs.output)) { return false; } - if (!(lhs.result == rhs.result)) { + if (!(lhs.len == rhs.len)) { return false; } return true; } -inline std::vector BlackBoxOp::EmbeddedCurveAdd::bincodeSerialize() const +inline std::vector BlackBoxOp::Poseidon2Permutation::bincodeSerialize() const { auto serializer = serde::BincodeSerializer(); - serde::Serializable::serialize(*this, serializer); + serde::Serializable::serialize(*this, serializer); return std::move(serializer).bytes(); } -inline BlackBoxOp::EmbeddedCurveAdd BlackBoxOp::EmbeddedCurveAdd::bincodeDeserialize(std::vector input) +inline BlackBoxOp::Poseidon2Permutation BlackBoxOp::Poseidon2Permutation::bincodeDeserialize(std::vector input) { auto deserializer = serde::BincodeDeserializer(input); - auto value = serde::Deserializable::deserialize(deserializer); + auto value = serde::Deserializable::deserialize(deserializer); if (deserializer.get_buffer_offset() < input.size()) { throw_or_abort("Some input bytes were not read"); } @@ -3603,57 +4562,53 @@ inline BlackBoxOp::EmbeddedCurveAdd BlackBoxOp::EmbeddedCurveAdd::bincodeDeseria template <> template -void serde::Serializable::serialize( - const Circuit::BlackBoxOp::EmbeddedCurveAdd& obj, Serializer& serializer) +void serde::Serializable::serialize( + const Circuit::BlackBoxOp::Poseidon2Permutation& obj, Serializer& serializer) { - serde::Serializable::serialize(obj.input1_x, serializer); - serde::Serializable::serialize(obj.input1_y, serializer); - serde::Serializable::serialize(obj.input2_x, serializer); - serde::Serializable::serialize(obj.input2_y, serializer); - serde::Serializable::serialize(obj.result, serializer); + serde::Serializable::serialize(obj.message, serializer); + serde::Serializable::serialize(obj.output, serializer); + serde::Serializable::serialize(obj.len, serializer); } template <> template -Circuit::BlackBoxOp::EmbeddedCurveAdd serde::Deserializable::deserialize( +Circuit::BlackBoxOp::Poseidon2Permutation serde::Deserializable::deserialize( Deserializer& deserializer) { - Circuit::BlackBoxOp::EmbeddedCurveAdd obj; - obj.input1_x = serde::Deserializable::deserialize(deserializer); - obj.input1_y = serde::Deserializable::deserialize(deserializer); - obj.input2_x = serde::Deserializable::deserialize(deserializer); - obj.input2_y = serde::Deserializable::deserialize(deserializer); - obj.result = serde::Deserializable::deserialize(deserializer); + Circuit::BlackBoxOp::Poseidon2Permutation obj; + obj.message = serde::Deserializable::deserialize(deserializer); + obj.output = serde::Deserializable::deserialize(deserializer); + obj.len = serde::Deserializable::deserialize(deserializer); return obj; } namespace Circuit { -inline bool operator==(const BlackBoxOp::EmbeddedCurveDouble& lhs, const BlackBoxOp::EmbeddedCurveDouble& rhs) +inline bool operator==(const BlackBoxOp::Sha256Compression& lhs, const BlackBoxOp::Sha256Compression& rhs) { - if (!(lhs.input1_x == rhs.input1_x)) { + if (!(lhs.input == rhs.input)) { return false; } - if (!(lhs.input1_y == rhs.input1_y)) { + if (!(lhs.hash_values == rhs.hash_values)) { return false; } - if (!(lhs.result == rhs.result)) { + if (!(lhs.output == rhs.output)) { return false; } return true; } -inline std::vector BlackBoxOp::EmbeddedCurveDouble::bincodeSerialize() const +inline std::vector BlackBoxOp::Sha256Compression::bincodeSerialize() const { auto serializer = serde::BincodeSerializer(); - serde::Serializable::serialize(*this, serializer); + serde::Serializable::serialize(*this, serializer); return std::move(serializer).bytes(); } -inline BlackBoxOp::EmbeddedCurveDouble BlackBoxOp::EmbeddedCurveDouble::bincodeDeserialize(std::vector input) +inline BlackBoxOp::Sha256Compression BlackBoxOp::Sha256Compression::bincodeDeserialize(std::vector input) { auto deserializer = serde::BincodeDeserializer(input); - auto value = serde::Deserializable::deserialize(deserializer); + auto value = serde::Deserializable::deserialize(deserializer); if (deserializer.get_buffer_offset() < input.size()) { throw_or_abort("Some input bytes were not read"); } @@ -3664,23 +4619,23 @@ inline BlackBoxOp::EmbeddedCurveDouble BlackBoxOp::EmbeddedCurveDouble::bincodeD template <> template -void serde::Serializable::serialize( - const Circuit::BlackBoxOp::EmbeddedCurveDouble& obj, Serializer& serializer) +void serde::Serializable::serialize( + const Circuit::BlackBoxOp::Sha256Compression& obj, Serializer& serializer) { - serde::Serializable::serialize(obj.input1_x, serializer); - serde::Serializable::serialize(obj.input1_y, serializer); - serde::Serializable::serialize(obj.result, serializer); + serde::Serializable::serialize(obj.input, serializer); + serde::Serializable::serialize(obj.hash_values, serializer); + serde::Serializable::serialize(obj.output, serializer); } template <> template -Circuit::BlackBoxOp::EmbeddedCurveDouble serde::Deserializable::deserialize( +Circuit::BlackBoxOp::Sha256Compression serde::Deserializable::deserialize( Deserializer& deserializer) { - Circuit::BlackBoxOp::EmbeddedCurveDouble obj; - obj.input1_x = serde::Deserializable::deserialize(deserializer); - obj.input1_y = serde::Deserializable::deserialize(deserializer); - obj.result = serde::Deserializable::deserialize(deserializer); + Circuit::BlackBoxOp::Sha256Compression obj; + obj.input = serde::Deserializable::deserialize(deserializer); + obj.hash_values = serde::Deserializable::deserialize(deserializer); + obj.output = serde::Deserializable::deserialize(deserializer); return obj; } @@ -4271,6 +5226,63 @@ Circuit::BrilligOpcode::Jump serde::Deserializable namespace Circuit { +inline bool operator==(const BrilligOpcode::CalldataCopy& lhs, const BrilligOpcode::CalldataCopy& rhs) +{ + if (!(lhs.destination_address == rhs.destination_address)) { + return false; + } + if (!(lhs.size == rhs.size)) { + return false; + } + if (!(lhs.offset == rhs.offset)) { + return false; + } + return true; +} + +inline std::vector BrilligOpcode::CalldataCopy::bincodeSerialize() const +{ + auto serializer = serde::BincodeSerializer(); + serde::Serializable::serialize(*this, serializer); + return std::move(serializer).bytes(); +} + +inline BrilligOpcode::CalldataCopy BrilligOpcode::CalldataCopy::bincodeDeserialize(std::vector input) +{ + auto deserializer = serde::BincodeDeserializer(input); + auto value = serde::Deserializable::deserialize(deserializer); + if (deserializer.get_buffer_offset() < input.size()) { + throw_or_abort("Some input bytes were not read"); + } + return value; +} + +} // end of namespace Circuit + +template <> +template +void serde::Serializable::serialize( + const Circuit::BrilligOpcode::CalldataCopy& obj, Serializer& serializer) +{ + serde::Serializable::serialize(obj.destination_address, serializer); + serde::Serializable::serialize(obj.size, serializer); + serde::Serializable::serialize(obj.offset, serializer); +} + +template <> +template +Circuit::BrilligOpcode::CalldataCopy serde::Deserializable::deserialize( + Deserializer& deserializer) +{ + Circuit::BrilligOpcode::CalldataCopy obj; + obj.destination_address = serde::Deserializable::deserialize(deserializer); + obj.size = serde::Deserializable::deserialize(deserializer); + obj.offset = serde::Deserializable::deserialize(deserializer); + return obj; +} + +namespace Circuit { + inline bool operator==(const BrilligOpcode::Call& lhs, const BrilligOpcode::Call& rhs) { if (!(lhs.location == rhs.location)) { @@ -4713,6 +5725,12 @@ namespace Circuit { inline bool operator==(const BrilligOpcode::Stop& lhs, const BrilligOpcode::Stop& rhs) { + if (!(lhs.return_data_offset == rhs.return_data_offset)) { + return false; + } + if (!(lhs.return_data_size == rhs.return_data_size)) { + return false; + } return true; } @@ -4739,7 +5757,10 @@ template <> template void serde::Serializable::serialize(const Circuit::BrilligOpcode::Stop& obj, Serializer& serializer) -{} +{ + serde::Serializable::serialize(obj.return_data_offset, serializer); + serde::Serializable::serialize(obj.return_data_size, serializer); +} template <> template @@ -4747,6 +5768,8 @@ Circuit::BrilligOpcode::Stop serde::Deserializable Deserializer& deserializer) { Circuit::BrilligOpcode::Stop obj; + obj.return_data_offset = serde::Deserializable::deserialize(deserializer); + obj.return_data_size = serde::Deserializable::deserialize(deserializer); return obj; } @@ -5417,6 +6440,55 @@ Circuit::MemOp serde::Deserializable::deserialize(Deserializer& namespace Circuit { +inline bool operator==(const MemoryAddress& lhs, const MemoryAddress& rhs) +{ + if (!(lhs.value == rhs.value)) { + return false; + } + return true; +} + +inline std::vector MemoryAddress::bincodeSerialize() const +{ + auto serializer = serde::BincodeSerializer(); + serde::Serializable::serialize(*this, serializer); + return std::move(serializer).bytes(); +} + +inline MemoryAddress MemoryAddress::bincodeDeserialize(std::vector input) +{ + auto deserializer = serde::BincodeDeserializer(input); + auto value = serde::Deserializable::deserialize(deserializer); + if (deserializer.get_buffer_offset() < input.size()) { + throw_or_abort("Some input bytes were not read"); + } + return value; +} + +} // end of namespace Circuit + +template <> +template +void serde::Serializable::serialize(const Circuit::MemoryAddress& obj, Serializer& serializer) +{ + serializer.increase_container_depth(); + serde::Serializable::serialize(obj.value, serializer); + serializer.decrease_container_depth(); +} + +template <> +template +Circuit::MemoryAddress serde::Deserializable::deserialize(Deserializer& deserializer) +{ + deserializer.increase_container_depth(); + Circuit::MemoryAddress obj; + obj.value = serde::Deserializable::deserialize(deserializer); + deserializer.decrease_container_depth(); + return obj; +} + +namespace Circuit { + inline bool operator==(const Opcode& lhs, const Opcode& rhs) { if (!(lhs.value == rhs.value)) { @@ -5955,25 +7027,25 @@ Circuit::PublicInputs serde::Deserializable::deserialize( namespace Circuit { -inline bool operator==(const RegisterIndex& lhs, const RegisterIndex& rhs) +inline bool operator==(const Value& lhs, const Value& rhs) { - if (!(lhs.value == rhs.value)) { + if (!(lhs.inner == rhs.inner)) { return false; } return true; } -inline std::vector RegisterIndex::bincodeSerialize() const +inline std::vector Value::bincodeSerialize() const { auto serializer = serde::BincodeSerializer(); - serde::Serializable::serialize(*this, serializer); + serde::Serializable::serialize(*this, serializer); return std::move(serializer).bytes(); } -inline RegisterIndex RegisterIndex::bincodeDeserialize(std::vector input) +inline Value Value::bincodeDeserialize(std::vector input) { auto deserializer = serde::BincodeDeserializer(input); - auto value = serde::Deserializable::deserialize(deserializer); + auto value = serde::Deserializable::deserialize(deserializer); if (deserializer.get_buffer_offset() < input.size()) { throw_or_abort("Some input bytes were not read"); } @@ -5984,27 +7056,27 @@ inline RegisterIndex RegisterIndex::bincodeDeserialize(std::vector inpu template <> template -void serde::Serializable::serialize(const Circuit::RegisterIndex& obj, Serializer& serializer) +void serde::Serializable::serialize(const Circuit::Value& obj, Serializer& serializer) { serializer.increase_container_depth(); - serde::Serializable::serialize(obj.value, serializer); + serde::Serializable::serialize(obj.inner, serializer); serializer.decrease_container_depth(); } template <> template -Circuit::RegisterIndex serde::Deserializable::deserialize(Deserializer& deserializer) +Circuit::Value serde::Deserializable::deserialize(Deserializer& deserializer) { deserializer.increase_container_depth(); - Circuit::RegisterIndex obj; - obj.value = serde::Deserializable::deserialize(deserializer); + Circuit::Value obj; + obj.inner = serde::Deserializable::deserialize(deserializer); deserializer.decrease_container_depth(); return obj; } namespace Circuit { -inline bool operator==(const RegisterOrMemory& lhs, const RegisterOrMemory& rhs) +inline bool operator==(const ValueOrArray& lhs, const ValueOrArray& rhs) { if (!(lhs.value == rhs.value)) { return false; @@ -6012,17 +7084,17 @@ inline bool operator==(const RegisterOrMemory& lhs, const RegisterOrMemory& rhs) return true; } -inline std::vector RegisterOrMemory::bincodeSerialize() const +inline std::vector ValueOrArray::bincodeSerialize() const { auto serializer = serde::BincodeSerializer(); - serde::Serializable::serialize(*this, serializer); + serde::Serializable::serialize(*this, serializer); return std::move(serializer).bytes(); } -inline RegisterOrMemory RegisterOrMemory::bincodeDeserialize(std::vector input) +inline ValueOrArray ValueOrArray::bincodeDeserialize(std::vector input) { auto deserializer = serde::BincodeDeserializer(input); - auto value = serde::Deserializable::deserialize(deserializer); + auto value = serde::Deserializable::deserialize(deserializer); if (deserializer.get_buffer_offset() < input.size()) { throw_or_abort("Some input bytes were not read"); } @@ -6033,8 +7105,7 @@ inline RegisterOrMemory RegisterOrMemory::bincodeDeserialize(std::vector template -void serde::Serializable::serialize(const Circuit::RegisterOrMemory& obj, - Serializer& serializer) +void serde::Serializable::serialize(const Circuit::ValueOrArray& obj, Serializer& serializer) { serializer.increase_container_depth(); serde::Serializable::serialize(obj.value, serializer); @@ -6043,10 +7114,10 @@ void serde::Serializable::serialize(const Circuit::Re template <> template -Circuit::RegisterOrMemory serde::Deserializable::deserialize(Deserializer& deserializer) +Circuit::ValueOrArray serde::Deserializable::deserialize(Deserializer& deserializer) { deserializer.increase_container_depth(); - Circuit::RegisterOrMemory obj; + Circuit::ValueOrArray obj; obj.value = serde::Deserializable::deserialize(deserializer); deserializer.decrease_container_depth(); return obj; @@ -6054,7 +7125,7 @@ Circuit::RegisterOrMemory serde::Deserializable::dese namespace Circuit { -inline bool operator==(const RegisterOrMemory::RegisterIndex& lhs, const RegisterOrMemory::RegisterIndex& rhs) +inline bool operator==(const ValueOrArray::MemoryAddress& lhs, const ValueOrArray::MemoryAddress& rhs) { if (!(lhs.value == rhs.value)) { return false; @@ -6062,17 +7133,17 @@ inline bool operator==(const RegisterOrMemory::RegisterIndex& lhs, const Registe return true; } -inline std::vector RegisterOrMemory::RegisterIndex::bincodeSerialize() const +inline std::vector ValueOrArray::MemoryAddress::bincodeSerialize() const { auto serializer = serde::BincodeSerializer(); - serde::Serializable::serialize(*this, serializer); + serde::Serializable::serialize(*this, serializer); return std::move(serializer).bytes(); } -inline RegisterOrMemory::RegisterIndex RegisterOrMemory::RegisterIndex::bincodeDeserialize(std::vector input) +inline ValueOrArray::MemoryAddress ValueOrArray::MemoryAddress::bincodeDeserialize(std::vector input) { auto deserializer = serde::BincodeDeserializer(input); - auto value = serde::Deserializable::deserialize(deserializer); + auto value = serde::Deserializable::deserialize(deserializer); if (deserializer.get_buffer_offset() < input.size()) { throw_or_abort("Some input bytes were not read"); } @@ -6083,25 +7154,25 @@ inline RegisterOrMemory::RegisterIndex RegisterOrMemory::RegisterIndex::bincodeD template <> template -void serde::Serializable::serialize( - const Circuit::RegisterOrMemory::RegisterIndex& obj, Serializer& serializer) +void serde::Serializable::serialize( + const Circuit::ValueOrArray::MemoryAddress& obj, Serializer& serializer) { serde::Serializable::serialize(obj.value, serializer); } template <> template -Circuit::RegisterOrMemory::RegisterIndex serde::Deserializable::deserialize( +Circuit::ValueOrArray::MemoryAddress serde::Deserializable::deserialize( Deserializer& deserializer) { - Circuit::RegisterOrMemory::RegisterIndex obj; + Circuit::ValueOrArray::MemoryAddress obj; obj.value = serde::Deserializable::deserialize(deserializer); return obj; } namespace Circuit { -inline bool operator==(const RegisterOrMemory::HeapArray& lhs, const RegisterOrMemory::HeapArray& rhs) +inline bool operator==(const ValueOrArray::HeapArray& lhs, const ValueOrArray::HeapArray& rhs) { if (!(lhs.value == rhs.value)) { return false; @@ -6109,17 +7180,17 @@ inline bool operator==(const RegisterOrMemory::HeapArray& lhs, const RegisterOrM return true; } -inline std::vector RegisterOrMemory::HeapArray::bincodeSerialize() const +inline std::vector ValueOrArray::HeapArray::bincodeSerialize() const { auto serializer = serde::BincodeSerializer(); - serde::Serializable::serialize(*this, serializer); + serde::Serializable::serialize(*this, serializer); return std::move(serializer).bytes(); } -inline RegisterOrMemory::HeapArray RegisterOrMemory::HeapArray::bincodeDeserialize(std::vector input) +inline ValueOrArray::HeapArray ValueOrArray::HeapArray::bincodeDeserialize(std::vector input) { auto deserializer = serde::BincodeDeserializer(input); - auto value = serde::Deserializable::deserialize(deserializer); + auto value = serde::Deserializable::deserialize(deserializer); if (deserializer.get_buffer_offset() < input.size()) { throw_or_abort("Some input bytes were not read"); } @@ -6130,25 +7201,25 @@ inline RegisterOrMemory::HeapArray RegisterOrMemory::HeapArray::bincodeDeseriali template <> template -void serde::Serializable::serialize( - const Circuit::RegisterOrMemory::HeapArray& obj, Serializer& serializer) +void serde::Serializable::serialize(const Circuit::ValueOrArray::HeapArray& obj, + Serializer& serializer) { serde::Serializable::serialize(obj.value, serializer); } template <> template -Circuit::RegisterOrMemory::HeapArray serde::Deserializable::deserialize( +Circuit::ValueOrArray::HeapArray serde::Deserializable::deserialize( Deserializer& deserializer) { - Circuit::RegisterOrMemory::HeapArray obj; + Circuit::ValueOrArray::HeapArray obj; obj.value = serde::Deserializable::deserialize(deserializer); return obj; } namespace Circuit { -inline bool operator==(const RegisterOrMemory::HeapVector& lhs, const RegisterOrMemory::HeapVector& rhs) +inline bool operator==(const ValueOrArray::HeapVector& lhs, const ValueOrArray::HeapVector& rhs) { if (!(lhs.value == rhs.value)) { return false; @@ -6156,17 +7227,17 @@ inline bool operator==(const RegisterOrMemory::HeapVector& lhs, const RegisterOr return true; } -inline std::vector RegisterOrMemory::HeapVector::bincodeSerialize() const +inline std::vector ValueOrArray::HeapVector::bincodeSerialize() const { auto serializer = serde::BincodeSerializer(); - serde::Serializable::serialize(*this, serializer); + serde::Serializable::serialize(*this, serializer); return std::move(serializer).bytes(); } -inline RegisterOrMemory::HeapVector RegisterOrMemory::HeapVector::bincodeDeserialize(std::vector input) +inline ValueOrArray::HeapVector ValueOrArray::HeapVector::bincodeDeserialize(std::vector input) { auto deserializer = serde::BincodeDeserializer(input); - auto value = serde::Deserializable::deserialize(deserializer); + auto value = serde::Deserializable::deserialize(deserializer); if (deserializer.get_buffer_offset() < input.size()) { throw_or_abort("Some input bytes were not read"); } @@ -6177,73 +7248,24 @@ inline RegisterOrMemory::HeapVector RegisterOrMemory::HeapVector::bincodeDeseria template <> template -void serde::Serializable::serialize( - const Circuit::RegisterOrMemory::HeapVector& obj, Serializer& serializer) +void serde::Serializable::serialize(const Circuit::ValueOrArray::HeapVector& obj, + Serializer& serializer) { serde::Serializable::serialize(obj.value, serializer); } template <> template -Circuit::RegisterOrMemory::HeapVector serde::Deserializable::deserialize( +Circuit::ValueOrArray::HeapVector serde::Deserializable::deserialize( Deserializer& deserializer) { - Circuit::RegisterOrMemory::HeapVector obj; + Circuit::ValueOrArray::HeapVector obj; obj.value = serde::Deserializable::deserialize(deserializer); return obj; } namespace Circuit { -inline bool operator==(const Value& lhs, const Value& rhs) -{ - if (!(lhs.inner == rhs.inner)) { - return false; - } - return true; -} - -inline std::vector Value::bincodeSerialize() const -{ - auto serializer = serde::BincodeSerializer(); - serde::Serializable::serialize(*this, serializer); - return std::move(serializer).bytes(); -} - -inline Value Value::bincodeDeserialize(std::vector input) -{ - auto deserializer = serde::BincodeDeserializer(input); - auto value = serde::Deserializable::deserialize(deserializer); - if (deserializer.get_buffer_offset() < input.size()) { - throw_or_abort("Some input bytes were not read"); - } - return value; -} - -} // end of namespace Circuit - -template <> -template -void serde::Serializable::serialize(const Circuit::Value& obj, Serializer& serializer) -{ - serializer.increase_container_depth(); - serde::Serializable::serialize(obj.inner, serializer); - serializer.decrease_container_depth(); -} - -template <> -template -Circuit::Value serde::Deserializable::deserialize(Deserializer& deserializer) -{ - deserializer.increase_container_depth(); - Circuit::Value obj; - obj.inner = serde::Deserializable::deserialize(deserializer); - deserializer.decrease_container_depth(); - return obj; -} - -namespace Circuit { - inline bool operator==(const Witness& lhs, const Witness& rhs) { if (!(lhs.value == rhs.value)) { diff --git a/barretenberg/cpp/src/barretenberg/dsl/acir_proofs/c_bind.cpp b/barretenberg/cpp/src/barretenberg/dsl/acir_proofs/c_bind.cpp index 18d44941e56..6572e08a6a2 100644 --- a/barretenberg/cpp/src/barretenberg/dsl/acir_proofs/c_bind.cpp +++ b/barretenberg/cpp/src/barretenberg/dsl/acir_proofs/c_bind.cpp @@ -59,8 +59,8 @@ WASM_EXPORT void acir_create_proof(in_ptr acir_composer_ptr, acir_composer->create_circuit(constraint_system, witness); acir_composer->init_proving_key(); - auto proof_data = acir_composer->create_proof(*is_recursive); - *out = to_heap_buffer(proof_data); + auto proof = acir_composer->create_proof(*is_recursive); + *out = to_heap_buffer(proof); } WASM_EXPORT void acir_goblin_accumulate(in_ptr acir_composer_ptr, @@ -73,8 +73,11 @@ WASM_EXPORT void acir_goblin_accumulate(in_ptr acir_composer_ptr, auto witness = acir_format::witness_buf_to_witness_data(from_buffer>(witness_vec)); acir_composer->create_circuit(constraint_system, witness); - auto proof_data = acir_composer->accumulate(); - *out = to_heap_buffer(proof_data); + auto proof = acir_composer->accumulate(); + auto proof_data_buf = to_buffer( + proof); // template parameter needs to be set so that vector deserialization from + // buffer, which reads the size at the beginning can be done properly + *out = to_heap_buffer(proof_data_buf); } WASM_EXPORT void acir_goblin_prove(in_ptr acir_composer_ptr, @@ -87,8 +90,11 @@ WASM_EXPORT void acir_goblin_prove(in_ptr acir_composer_ptr, auto witness = acir_format::witness_buf_to_witness_data(from_buffer>(witness_vec)); acir_composer->create_circuit(constraint_system, witness); - auto proof_data = acir_composer->accumulate_and_prove(); - *out = to_heap_buffer(proof_data); + auto proof = acir_composer->accumulate_and_prove(); + auto proof_data_buf = to_buffer( + proof); // template parameter needs to be set so that vector deserialization from + // buffer, which reads the size at the beginning can be done properly + *out = to_heap_buffer(proof_data_buf); } WASM_EXPORT void acir_load_verification_key(in_ptr acir_composer_ptr, uint8_t const* vk_buf) @@ -125,14 +131,16 @@ WASM_EXPORT void acir_get_proving_key(in_ptr acir_composer_ptr, uint8_t const* a WASM_EXPORT void acir_goblin_verify_accumulator(in_ptr acir_composer_ptr, uint8_t const* proof_buf, bool* result) { auto acir_composer = reinterpret_cast(*acir_composer_ptr); - auto proof = from_buffer>(proof_buf); + auto proof_data_buf = from_buffer>(proof_buf); + auto proof = from_buffer>(proof_data_buf); *result = acir_composer->verify_accumulator(proof); } WASM_EXPORT void acir_goblin_verify(in_ptr acir_composer_ptr, uint8_t const* proof_buf, bool* result) { auto acir_composer = reinterpret_cast(*acir_composer_ptr); - auto proof = from_buffer>(proof_buf); + auto proof_data_buf = from_buffer>(proof_buf); + auto proof = from_buffer>(proof_data_buf); *result = acir_composer->verify(proof); } diff --git a/barretenberg/cpp/src/barretenberg/dsl/acir_proofs/goblin_acir_composer.cpp b/barretenberg/cpp/src/barretenberg/dsl/acir_proofs/goblin_acir_composer.cpp index 3b0adc0ae5c..f287e5c9105 100644 --- a/barretenberg/cpp/src/barretenberg/dsl/acir_proofs/goblin_acir_composer.cpp +++ b/barretenberg/cpp/src/barretenberg/dsl/acir_proofs/goblin_acir_composer.cpp @@ -21,13 +21,13 @@ void GoblinAcirComposer::create_circuit(acir_format::AcirFormat& constraint_syst GoblinMockCircuits::construct_goblin_ecc_op_circuit(builder_); } -std::vector GoblinAcirComposer::accumulate() +std::vector GoblinAcirComposer::accumulate() { // // Construct a GUH proof for the circuit via the accumulate mechanism // return goblin.accumulate_for_acir(builder_); // Construct one final GUH proof via the accumulate mechanism - std::vector ultra_proof = goblin.accumulate_for_acir(builder_); + std::vector ultra_proof = goblin.accumulate_for_acir(builder_); // Construct a Goblin proof (ECCVM, Translator, Merge); result stored internally goblin.prove_for_acir(); @@ -35,15 +35,15 @@ std::vector GoblinAcirComposer::accumulate() return ultra_proof; } -bool GoblinAcirComposer::verify_accumulator(std::vector const& proof) +bool GoblinAcirComposer::verify_accumulator(std::vector const& proof) { return goblin.verify_accumulator_for_acir(proof); } -std::vector GoblinAcirComposer::accumulate_and_prove() +std::vector GoblinAcirComposer::accumulate_and_prove() { // Construct one final GUH proof via the accumulate mechanism - std::vector ultra_proof = goblin.accumulate_for_acir(builder_); + std::vector ultra_proof = goblin.accumulate_for_acir(builder_); // Construct a Goblin proof (ECCVM, Translator, Merge); result stored internally goblin.prove_for_acir(); @@ -51,7 +51,7 @@ std::vector GoblinAcirComposer::accumulate_and_prove() return ultra_proof; } -bool GoblinAcirComposer::verify(std::vector const& proof) +bool GoblinAcirComposer::verify(std::vector const& proof) { // Verify the final GUH proof bool ultra_verified = goblin.verify_accumulator_for_acir(proof); diff --git a/barretenberg/cpp/src/barretenberg/dsl/acir_proofs/goblin_acir_composer.hpp b/barretenberg/cpp/src/barretenberg/dsl/acir_proofs/goblin_acir_composer.hpp index 6556b548045..a533cba1830 100644 --- a/barretenberg/cpp/src/barretenberg/dsl/acir_proofs/goblin_acir_composer.hpp +++ b/barretenberg/cpp/src/barretenberg/dsl/acir_proofs/goblin_acir_composer.hpp @@ -28,9 +28,9 @@ class GoblinAcirComposer { * @brief Accumulate a circuit via Goblin * @details For the present circuit, construct a GUH proof and the vkey needed to verify it * - * @return std::vector The GUH proof bytes + * @return std::vector The GUH proof bytes */ - std::vector accumulate(); + std::vector accumulate(); /** * @brief Verify the Goblin accumulator (the GUH proof) using the vkey internal to Goblin @@ -38,7 +38,7 @@ class GoblinAcirComposer { * @param proof * @return bool Whether or not the proof was verified */ - bool verify_accumulator(std::vector const& proof); + bool verify_accumulator(std::vector const& proof); /** * @brief Accumulate a final circuit and construct a full Goblin proof @@ -48,14 +48,14 @@ class GoblinAcirComposer { * accumulation phase. * */ - std::vector accumulate_and_prove(); + std::vector accumulate_and_prove(); /** * @brief Verify the final GUH proof and the full Goblin proof * * @return bool verified */ - bool verify(std::vector const& proof); + bool verify(std::vector const& proof); private: acir_format::GoblinBuilder builder_; diff --git a/barretenberg/cpp/src/barretenberg/ecc/CMakeLists.txt b/barretenberg/cpp/src/barretenberg/ecc/CMakeLists.txt index 35e543283ee..ce7c008ee65 100644 --- a/barretenberg/cpp/src/barretenberg/ecc/CMakeLists.txt +++ b/barretenberg/cpp/src/barretenberg/ecc/CMakeLists.txt @@ -17,4 +17,4 @@ target_precompile_headers( $<$:"${CMAKE_CURRENT_SOURCE_DIR}/fields/field_impl_generic.hpp"> $<$:"${CMAKE_CURRENT_SOURCE_DIR}/fields/field_impl_x64.hpp"> $<$:"${CMAKE_CURRENT_SOURCE_DIR}/fields/field.hpp"> -) +) \ No newline at end of file diff --git a/barretenberg/cpp/src/barretenberg/ecc/fields/field_conversion.cpp b/barretenberg/cpp/src/barretenberg/ecc/fields/field_conversion.cpp new file mode 100644 index 00000000000..3c34ad7561a --- /dev/null +++ b/barretenberg/cpp/src/barretenberg/ecc/fields/field_conversion.cpp @@ -0,0 +1,116 @@ + +#include "barretenberg/ecc/fields/field_conversion.hpp" + +namespace bb::field_conversion { + +static constexpr uint64_t NUM_CONVERSION_LIMB_BITS = 68; // set to be 68 because bigfield has 68 bit limbs +static constexpr uint64_t TOTAL_BITS = 254; + +bb::fr convert_from_bn254_frs(std::span fr_vec, bb::fr* /*unused*/) +{ + ASSERT(fr_vec.size() == 1); + return fr_vec[0]; +} + +bool convert_from_bn254_frs(std::span fr_vec, bool* /*unused*/) +{ + ASSERT(fr_vec.size() == 1); + return fr_vec[0] != 0; +} + +/** + * @brief Converts 2 bb::fr elements to grumpkin::fr + * @details First, this function must take in 2 bb::fr elements because the grumpkin::fr field has a larger modulus than + * the bb::fr field, so we choose to send 1 grumpkin::fr element to 2 bb::fr elements to maintain injectivity. + * For the implementation, we want to minimize the number of constraints created by the circuit form, which happens to + * use 68 bit limbs to represent a grumpkin::fr (as a bigfield). Therefore, our mapping will split a grumpkin::fr into a + * 136 bit chunk for the lower two bigfield limbs and the upper chunk for the upper two limbs. The upper chunk ends up + * being 254 - 2*68 = 118 bits as a result. This is why we check that the bb::frs must be at most 136 and 118 bits + * respectively (to ensure no overflow). Then, we converts the two chunks to a grumpkin::fr using uint256_t conversions. + * @param low_bits_in + * @param high_bits_in + * @return grumpkin::fr + */ +grumpkin::fr convert_from_bn254_frs(std::span fr_vec, grumpkin::fr* /*unused*/) +{ + // Combines the two elements into one uint256_t, and then convert that to a grumpkin::fr + ASSERT(uint256_t(fr_vec[0]) < (uint256_t(1) << (NUM_CONVERSION_LIMB_BITS * 2))); // lower 136 bits + ASSERT(uint256_t(fr_vec[1]) < + (uint256_t(1) << (TOTAL_BITS - NUM_CONVERSION_LIMB_BITS * 2))); // upper 254-136=118 bits + uint256_t value = uint256_t(fr_vec[0]) + (uint256_t(fr_vec[1]) << (NUM_CONVERSION_LIMB_BITS * 2)); + grumpkin::fr result(value); + return result; +} + +curve::BN254::AffineElement convert_from_bn254_frs(std::span fr_vec, + curve::BN254::AffineElement* /*unused*/) +{ + curve::BN254::AffineElement val; + val.x = convert_from_bn254_frs(fr_vec.subspan(0, 2)); + val.y = convert_from_bn254_frs(fr_vec.subspan(2, 2)); + return val; +} + +curve::Grumpkin::AffineElement convert_from_bn254_frs(std::span fr_vec, + curve::Grumpkin::AffineElement* /*unused*/) +{ + ASSERT(fr_vec.size() == 2); + curve::Grumpkin::AffineElement val; + val.x = fr_vec[0]; + val.y = fr_vec[1]; + return val; +} + +/** + * @brief Converts grumpkin::fr to 2 bb::fr elements + * @details First, this function must return 2 bb::fr elements because the grumpkin::fr field has a larger modulus than + * the bb::fr field, so we choose to send 1 grumpkin::fr element to 2 bb::fr elements to maintain injectivity. + * This function the reverse of convert_from_bn254_frs(std::span fr_vec, grumpkin::fr*) by merging the two + * pairs of limbs back into the 2 bb::fr elements. For the implementation, we want to minimize the number of constraints + * created by the circuit form, which happens to use 68 bit limbs to represent a grumpkin::fr (as a bigfield). + * Therefore, our mapping will split a grumpkin::fr into a 136 bit chunk for the lower two bigfield limbs and the upper + * chunk for the upper two limbs. The upper chunk ends up being 254 - 2*68 = 118 bits as a result. We manipulate the + * value using bitwise masks and shifts to obtain our two chunks. + * @param input + * @return std::array + */ +std::vector convert_to_bn254_frs(const grumpkin::fr& val) +{ + // Goal is to slice up the 64 bit limbs of grumpkin::fr/uint256_t to mirror the 68 bit limbs of bigfield + // We accomplish this by dividing the grumpkin::fr's value into two 68*2=136 bit pieces. + constexpr uint64_t LOWER_BITS = 2 * NUM_CONVERSION_LIMB_BITS; + constexpr uint256_t LOWER_MASK = (uint256_t(1) << LOWER_BITS) - 1; + auto value = uint256_t(val); + ASSERT(value < (uint256_t(1) << TOTAL_BITS)); + std::vector result(2); + result[0] = static_cast(value & LOWER_MASK); + result[1] = static_cast(value >> LOWER_BITS); + ASSERT(static_cast(result[1]) < (uint256_t(1) << (TOTAL_BITS - LOWER_BITS))); + return result; +} + +std::vector convert_to_bn254_frs(const bb::fr& val) +{ + std::vector fr_vec{ val }; + return fr_vec; +} + +std::vector convert_to_bn254_frs(const curve::BN254::AffineElement& val) +{ + auto fr_vec_x = convert_to_bn254_frs(val.x); + auto fr_vec_y = convert_to_bn254_frs(val.y); + std::vector fr_vec(fr_vec_x.begin(), fr_vec_x.end()); + fr_vec.insert(fr_vec.end(), fr_vec_y.begin(), fr_vec_y.end()); + return fr_vec; +} + +std::vector convert_to_bn254_frs(const curve::Grumpkin::AffineElement& val) +{ + auto fr_vec_x = convert_to_bn254_frs(val.x); + auto fr_vec_y = convert_to_bn254_frs(val.y); + std::vector fr_vec(fr_vec_x.begin(), fr_vec_x.end()); + fr_vec.insert(fr_vec.end(), fr_vec_y.begin(), fr_vec_y.end()); + return fr_vec; +} + +} // namespace bb::field_conversion \ No newline at end of file diff --git a/barretenberg/cpp/src/barretenberg/ecc/fields/field_conversion.hpp b/barretenberg/cpp/src/barretenberg/ecc/fields/field_conversion.hpp new file mode 100644 index 00000000000..cf5b12d1def --- /dev/null +++ b/barretenberg/cpp/src/barretenberg/ecc/fields/field_conversion.hpp @@ -0,0 +1,211 @@ +#pragma once + +#include "barretenberg/ecc/curves/bn254/bn254.hpp" +#include "barretenberg/ecc/curves/bn254/fr.hpp" +#include "barretenberg/ecc/curves/grumpkin/grumpkin.hpp" +#include "barretenberg/polynomials/univariate.hpp" + +namespace bb::field_conversion { + +/** + * @brief Calculates number of bb::fr required to represent the input type + * @details We want to support the following types: bool, size_t, uint32_t, uint64_t, bb::fr, grumpkin::fr, + * curve::BN254::AffineElement, curve::Grumpkin::AffineElement, bb::Univariate, std::array, for + * FF = bb::fr/grumpkin::fr, and N is arbitrary + * @tparam T + * @return constexpr size_t + */ +template constexpr size_t calc_num_bn254_frs(); + +constexpr size_t calc_num_bn254_frs(bb::fr* /*unused*/) +{ + return 1; +} + +constexpr size_t calc_num_bn254_frs(grumpkin::fr* /*unused*/) +{ + return 2; +} + +template constexpr size_t calc_num_bn254_frs(T* /*unused*/) +{ + return 1; // meant for integral types that are less than 254 bits +} + +constexpr size_t calc_num_bn254_frs(curve::BN254::AffineElement* /*unused*/) +{ + return 2 * calc_num_bn254_frs(); +} + +constexpr size_t calc_num_bn254_frs(curve::Grumpkin::AffineElement* /*unused*/) +{ + return 2 * calc_num_bn254_frs(); +} + +template constexpr size_t calc_num_bn254_frs(std::array* /*unused*/) +{ + return N * calc_num_bn254_frs(); +} + +template constexpr size_t calc_num_bn254_frs(bb::Univariate* /*unused*/) +{ + return N * calc_num_bn254_frs(); +} + +template constexpr size_t calc_num_bn254_frs() +{ + return calc_num_bn254_frs(static_cast(nullptr)); +} + +/** + * @brief Conversions from vector of bb::fr elements to transcript types. + * @details We want to support the following types: bool, size_t, uint32_t, uint64_t, bb::fr, grumpkin::fr, + * curve::BN254::AffineElement, curve::Grumpkin::AffineElement, bb::Univariate, std::array, for + * FF = bb::fr/grumpkin::fr, and N is arbitrary. + * The only nontrivial implementation is the conversion for grumpkin::fr. More details are given in the function comment + * below. + * @tparam T + * @param fr_vec + * @return T + */ +template T convert_from_bn254_frs(std::span fr_vec); + +bool convert_from_bn254_frs(std::span fr_vec, bool* /*unused*/); + +template inline T convert_from_bn254_frs(std::span fr_vec, T* /*unused*/) +{ + ASSERT(fr_vec.size() == 1); + return static_cast(fr_vec[0]); +} + +bb::fr convert_from_bn254_frs(std::span fr_vec, bb::fr* /*unused*/); + +grumpkin::fr convert_from_bn254_frs(std::span fr_vec, grumpkin::fr* /*unused*/); + +curve::BN254::AffineElement convert_from_bn254_frs(std::span fr_vec, + curve::BN254::AffineElement* /*unused*/); + +curve::Grumpkin::AffineElement convert_from_bn254_frs(std::span fr_vec, + curve::Grumpkin::AffineElement* /*unused*/); + +template +inline std::array convert_from_bn254_frs(std::span fr_vec, std::array* /*unused*/) +{ + std::array val; + for (size_t i = 0; i < N; ++i) { + val[i] = fr_vec[i]; + } + return val; +} + +template +inline std::array convert_from_bn254_frs(std::span fr_vec, + std::array* /*unused*/) +{ + std::array val; + for (size_t i = 0; i < N; ++i) { + std::vector fr_vec_tmp{ fr_vec[2 * i], + fr_vec[2 * i + 1] }; // each pair of consecutive elements is a grumpkin::fr + val[i] = convert_from_bn254_frs(fr_vec_tmp); + } + return val; +} + +template +inline Univariate convert_from_bn254_frs(std::span fr_vec, Univariate* /*unused*/) +{ + Univariate val; + for (size_t i = 0; i < N; ++i) { + val.evaluations[i] = fr_vec[i]; + } + return val; +} + +template +inline Univariate convert_from_bn254_frs(std::span fr_vec, + Univariate* /*unused*/) +{ + Univariate val; + for (size_t i = 0; i < N; ++i) { + std::vector fr_vec_tmp{ fr_vec[2 * i], fr_vec[2 * i + 1] }; + val.evaluations[i] = convert_from_bn254_frs(fr_vec_tmp); + } + return val; +} + +template T convert_from_bn254_frs(std::span fr_vec) +{ + return convert_from_bn254_frs(fr_vec, static_cast(nullptr)); +} + +/** + * @brief Conversion from transcript values to bb::frs + * @details We want to support the following types: bool, size_t, uint32_t, uint64_t, bb::fr, grumpkin::fr, + * curve::BN254::AffineElement, curve::Grumpkin::AffineElement, bb::Univariate, std::array, for + * FF = bb::fr/grumpkin::fr, and N is arbitrary. + * @tparam T + * @param val + * @return std::vector + */ +template std::vector inline convert_to_bn254_frs(const T& val) +{ + std::vector fr_vec{ val }; + return fr_vec; +} + +std::vector convert_to_bn254_frs(const grumpkin::fr& val); + +std::vector convert_to_bn254_frs(const bb::fr& val); + +std::vector convert_to_bn254_frs(const curve::BN254::AffineElement& val); + +std::vector convert_to_bn254_frs(const curve::Grumpkin::AffineElement& val); + +template std::vector inline convert_to_bn254_frs(const std::array& val) +{ + std::vector fr_vec(val.begin(), val.end()); + return fr_vec; +} + +template std::vector inline convert_to_bn254_frs(const std::array& val) +{ + std::vector fr_vec; + for (size_t i = 0; i < N; ++i) { + auto tmp_vec = convert_to_bn254_frs(val[i]); + fr_vec.insert(fr_vec.end(), tmp_vec.begin(), tmp_vec.end()); + } + return fr_vec; +} + +template std::vector inline convert_to_bn254_frs(const bb::Univariate& val) +{ + std::vector fr_vec; + for (size_t i = 0; i < N; ++i) { + auto tmp_vec = convert_to_bn254_frs(val.evaluations[i]); + fr_vec.insert(fr_vec.end(), tmp_vec.begin(), tmp_vec.end()); + } + return fr_vec; +} + +template std::vector inline convert_to_bn254_frs(const bb::Univariate& val) +{ + std::vector fr_vec; + for (size_t i = 0; i < N; ++i) { + auto tmp_vec = convert_to_bn254_frs(val.evaluations[i]); + fr_vec.insert(fr_vec.end(), tmp_vec.begin(), tmp_vec.end()); + } + return fr_vec; +} + +template std::vector inline convert_to_bn254_frs(const AllValues& val) +{ + auto data = val.get_all(); + std::vector fr_vec; + for (auto& item : data) { + auto tmp_vec = convert_to_bn254_frs(item); + fr_vec.insert(fr_vec.end(), tmp_vec.begin(), tmp_vec.end()); + } + return fr_vec; +} + +} // namespace bb::field_conversion diff --git a/barretenberg/cpp/src/barretenberg/ecc/fields/field_conversion.test.cpp b/barretenberg/cpp/src/barretenberg/ecc/fields/field_conversion.test.cpp new file mode 100644 index 00000000000..0a024f82545 --- /dev/null +++ b/barretenberg/cpp/src/barretenberg/ecc/fields/field_conversion.test.cpp @@ -0,0 +1,134 @@ +#include "barretenberg/ecc/fields/field_conversion.hpp" +#include + +namespace bb::field_conversion_tests { + +class FieldConversionTest : public ::testing::Test { + public: + template void check_conversion(T x) + { + size_t len = bb::field_conversion::calc_num_bn254_frs(); + auto frs = bb::field_conversion::convert_to_bn254_frs(x); + EXPECT_EQ(len, frs.size()); + auto y = bb::field_conversion::convert_from_bn254_frs(frs); + EXPECT_EQ(x, y); + } +}; + +/** + * @brief Field conversion test for uint32_t + */ +TEST_F(FieldConversionTest, FieldConversionUint32) +{ + auto x = static_cast(1) << 31; + check_conversion(x); +} + +/** + * @brief Field conversion test for bb::fr + */ +TEST_F(FieldConversionTest, FieldConversionFr) +{ + bb::fr x1(std::string("9a807b615c4d3e2fa0b1c2d3e4f56789fedcba9876543210abcdef0123456789")); // 256 bits + check_conversion(x1); + + bb::fr x2(bb::fr::modulus_minus_two); // modulus - 2 + check_conversion(x2); +} + +/** + * @brief Field conversion test for grumpkin::fr + * + */ +TEST_F(FieldConversionTest, FieldConversionGrumpkinFr) +{ + grumpkin::fr x1(std::string("9a807b615c4d3e2fa0b1c2d3e4f56789fedcba9876543210abcdef0123456789")); // 256 bits + check_conversion(x1); +} + +/** + * @brief Field conversion test for curve::BN254::AffineElement + * + */ +TEST_F(FieldConversionTest, FieldConversionBN254AffineElement) +{ + curve::BN254::AffineElement x1(1, 2); + check_conversion(x1); + + curve::BN254::AffineElement x2(grumpkin::fr::modulus_minus_two, grumpkin::fr::modulus_minus_two); + check_conversion(x2); +} + +/** + * @brief Field conversion test for curve::Grumpkin::AffineElement + */ +TEST_F(FieldConversionTest, FieldConversionGrumpkinAffineElement) +{ + curve::Grumpkin::AffineElement x1(1, 2); + check_conversion(x1); + + curve::Grumpkin::AffineElement x2(bb::fr::modulus_minus_two, bb::fr::modulus_minus_two); + check_conversion(x2); +} + +/** + * @brief Field conversion test for std::array + * + */ +TEST_F(FieldConversionTest, FieldConversionArrayBn254Fr) +{ + std::array x1{ 1, 2, 3, 4 }; + check_conversion(x1); + + std::array x2{ bb::fr::modulus_minus_two, + bb::fr::modulus_minus_two - 123, + 215215125, + 102701750, + 367032, + 12985028, + bb::fr::modulus_minus_two - 125015028 }; + check_conversion(x2); +} + +/** + * @brief Field conversion test for std::array + * + */ +TEST_F(FieldConversionTest, FieldConversionArrayGrumpkinFr) +{ + std::array x1{ 1, 2, 3, 4 }; + check_conversion(x1); + + std::array x2{ grumpkin::fr::modulus_minus_two, + grumpkin::fr::modulus_minus_two - 123, + 215215125, + 102701750, + 367032, + 12985028, + grumpkin::fr::modulus_minus_two - 125015028 }; + check_conversion(x2); +} + +/** + * @brief Field conversion test for bb::Univariate + * + */ +TEST_F(FieldConversionTest, FieldConversionUnivariateBn254Fr) +{ + std::array x1_arr{ 1, 2, 3, 4 }; + bb::Univariate x1{ x1_arr }; + check_conversion(x1); +} + +/** + * @brief Field conversion test for bb::Univariate + * + */ +TEST_F(FieldConversionTest, FieldConversionUnivariateGrumpkinFr) +{ + std::array x1_arr{ 1, 2, 3, 4 }; + bb::Univariate x1{ x1_arr }; + check_conversion(x1); +} + +} // namespace bb::field_conversion_tests diff --git a/barretenberg/cpp/src/barretenberg/ecc/fields/field_declarations.hpp b/barretenberg/cpp/src/barretenberg/ecc/fields/field_declarations.hpp index 3a43b2ac69e..217e2b42bd2 100644 --- a/barretenberg/cpp/src/barretenberg/ecc/fields/field_declarations.hpp +++ b/barretenberg/cpp/src/barretenberg/ecc/fields/field_declarations.hpp @@ -112,6 +112,13 @@ template struct alignas(32) field { *this = field(value); } + constexpr explicit operator bool() const + { + field out = from_montgomery_form(); + ASSERT(out.data[0] == 0 || out.data[0] == 1); + return static_cast(out.data[0]); + } + constexpr explicit operator uint32_t() const { field out = from_montgomery_form(); diff --git a/barretenberg/cpp/src/barretenberg/eccvm/eccvm_prover.cpp b/barretenberg/cpp/src/barretenberg/eccvm/eccvm_prover.cpp index 3ab2a27fcce..071a63dfb4a 100644 --- a/barretenberg/cpp/src/barretenberg/eccvm/eccvm_prover.cpp +++ b/barretenberg/cpp/src/barretenberg/eccvm/eccvm_prover.cpp @@ -274,13 +274,13 @@ template void ECCVMProver_::execute_transcript_cons translation_batching_challenge_v = transcript->get_challenge("Translation:batching_challenge"); } -template plonk::proof& ECCVMProver_::export_proof() +template honk::proof& ECCVMProver_::export_proof() { - proof.proof_data = transcript->export_proof(); + proof = transcript->export_proof(); return proof; } -template plonk::proof& ECCVMProver_::construct_proof() +template honk::proof& ECCVMProver_::construct_proof() { execute_preamble_round(); diff --git a/barretenberg/cpp/src/barretenberg/eccvm/eccvm_prover.hpp b/barretenberg/cpp/src/barretenberg/eccvm/eccvm_prover.hpp index b613a0697ef..96f3e76a309 100644 --- a/barretenberg/cpp/src/barretenberg/eccvm/eccvm_prover.hpp +++ b/barretenberg/cpp/src/barretenberg/eccvm/eccvm_prover.hpp @@ -3,7 +3,7 @@ #include "barretenberg/commitment_schemes/shplonk/shplonk.hpp" #include "barretenberg/flavor/ecc_vm.hpp" #include "barretenberg/goblin/translation_evaluations.hpp" -#include "barretenberg/plonk/proof_system/types/proof.hpp" +#include "barretenberg/honk/proof_system/types/proof.hpp" #include "barretenberg/relations/relation_parameters.hpp" #include "barretenberg/sumcheck/sumcheck_output.hpp" #include "barretenberg/transcript/transcript.hpp" @@ -42,8 +42,8 @@ template class ECCVMProver_ { BBERG_PROFILE void execute_final_pcs_round(); BBERG_PROFILE void execute_transcript_consistency_univariate_opening_round(); - plonk::proof& export_proof(); - plonk::proof& construct_proof(); + honk::proof& export_proof(); + honk::proof& construct_proof(); std::shared_ptr transcript; @@ -80,7 +80,7 @@ template class ECCVMProver_ { using Shplonk = pcs::shplonk::ShplonkProver_; private: - plonk::proof proof; + honk::proof proof; }; } // namespace bb::honk diff --git a/barretenberg/cpp/src/barretenberg/eccvm/eccvm_transcript.test.cpp b/barretenberg/cpp/src/barretenberg/eccvm/eccvm_transcript.test.cpp index c513bc991c4..2b4e69d3624 100644 --- a/barretenberg/cpp/src/barretenberg/eccvm/eccvm_transcript.test.cpp +++ b/barretenberg/cpp/src/barretenberg/eccvm/eccvm_transcript.test.cpp @@ -39,94 +39,94 @@ template class ECCVMTranscriptTests : public ::testing::Test { auto log_n = numeric::get_msb(circuit_size); size_t MAX_PARTIAL_RELATION_LENGTH = Flavor::BATCHED_RELATION_PARTIAL_LENGTH; - size_t size_FF = sizeof(FF); - size_t size_G = 2 * size_FF; - size_t size_uni = MAX_PARTIAL_RELATION_LENGTH * size_FF; - size_t size_evals = (Flavor::NUM_ALL_ENTITIES)*size_FF; - size_t size_uint32 = 4; - size_t size_uint64 = 8; + // Size of types is number of bb::frs needed to represent the type + size_t frs_per_Fr = bb::field_conversion::calc_num_bn254_frs(); + size_t frs_per_G = bb::field_conversion::calc_num_bn254_frs(); + size_t frs_per_uni = MAX_PARTIAL_RELATION_LENGTH * frs_per_Fr; + size_t frs_per_evals = (Flavor::NUM_ALL_ENTITIES)*frs_per_Fr; + size_t frs_per_uint32 = bb::field_conversion::calc_num_bn254_frs(); size_t round = 0; - manifest_expected.add_entry(round, "circuit_size", size_uint32); - manifest_expected.add_entry(round, "TRANSCRIPT_ADD", size_G); - manifest_expected.add_entry(round, "TRANSCRIPT_MUL", size_G); - manifest_expected.add_entry(round, "TRANSCRIPT_EQ", size_G); - manifest_expected.add_entry(round, "TRANSCRIPT_COLLISION_CHECK", size_G); - manifest_expected.add_entry(round, "TRANSCRIPT_MSM_TRANSITION", size_G); - manifest_expected.add_entry(round, "TRANSCRIPT_PC", size_G); - manifest_expected.add_entry(round, "TRANSCRIPT_MSM_COUNT", size_G); - manifest_expected.add_entry(round, "TRANSCRIPT_PX", size_G); - manifest_expected.add_entry(round, "TRANSCRIPT_PY", size_G); - manifest_expected.add_entry(round, "TRANSCRIPT_Z1", size_G); - manifest_expected.add_entry(round, "TRANSCRIPT_Z2", size_G); - manifest_expected.add_entry(round, "TRANSCRIPT_Z1ZERO", size_G); - manifest_expected.add_entry(round, "TRANSCRIPT_Z2ZERO", size_G); - manifest_expected.add_entry(round, "TRANSCRIPT_OP", size_G); - manifest_expected.add_entry(round, "TRANSCRIPT_ACCUMULATOR_X", size_G); - manifest_expected.add_entry(round, "TRANSCRIPT_ACCUMULATOR_Y", size_G); - manifest_expected.add_entry(round, "TRANSCRIPT_MSM_X", size_G); - manifest_expected.add_entry(round, "TRANSCRIPT_MSM_Y", size_G); - manifest_expected.add_entry(round, "PRECOMPUTE_PC", size_G); - manifest_expected.add_entry(round, "PRECOMPUTE_POINT_TRANSITION", size_G); - manifest_expected.add_entry(round, "PRECOMPUTE_ROUND", size_G); - manifest_expected.add_entry(round, "PRECOMPUTE_SCALAR_SUM", size_G); - manifest_expected.add_entry(round, "PRECOMPUTE_S1HI", size_G); - manifest_expected.add_entry(round, "PRECOMPUTE_S1LO", size_G); - manifest_expected.add_entry(round, "PRECOMPUTE_S2HI", size_G); - manifest_expected.add_entry(round, "PRECOMPUTE_S2LO", size_G); - manifest_expected.add_entry(round, "PRECOMPUTE_S3HI", size_G); - manifest_expected.add_entry(round, "PRECOMPUTE_S3LO", size_G); - manifest_expected.add_entry(round, "PRECOMPUTE_S4HI", size_G); - manifest_expected.add_entry(round, "PRECOMPUTE_S4LO", size_G); - manifest_expected.add_entry(round, "PRECOMPUTE_SKEW", size_G); - manifest_expected.add_entry(round, "PRECOMPUTE_DX", size_G); - manifest_expected.add_entry(round, "PRECOMPUTE_DY", size_G); - manifest_expected.add_entry(round, "PRECOMPUTE_TX", size_G); - manifest_expected.add_entry(round, "PRECOMPUTE_TY", size_G); - manifest_expected.add_entry(round, "MSM_TRANSITION", size_G); - manifest_expected.add_entry(round, "MSM_ADD", size_G); - manifest_expected.add_entry(round, "MSM_DOUBLE", size_G); - manifest_expected.add_entry(round, "MSM_SKEW", size_G); - manifest_expected.add_entry(round, "MSM_ACCUMULATOR_X", size_G); - manifest_expected.add_entry(round, "MSM_ACCUMULATOR_Y", size_G); - manifest_expected.add_entry(round, "MSM_PC", size_G); - manifest_expected.add_entry(round, "MSM_SIZE_OF_MSM", size_G); - manifest_expected.add_entry(round, "MSM_COUNT", size_G); - manifest_expected.add_entry(round, "MSM_ROUND", size_G); - manifest_expected.add_entry(round, "MSM_ADD1", size_G); - manifest_expected.add_entry(round, "MSM_ADD2", size_G); - manifest_expected.add_entry(round, "MSM_ADD3", size_G); - manifest_expected.add_entry(round, "MSM_ADD4", size_G); - manifest_expected.add_entry(round, "MSM_X1", size_G); - manifest_expected.add_entry(round, "MSM_Y1", size_G); - manifest_expected.add_entry(round, "MSM_X2", size_G); - manifest_expected.add_entry(round, "MSM_Y2", size_G); - manifest_expected.add_entry(round, "MSM_X3", size_G); - manifest_expected.add_entry(round, "MSM_Y3", size_G); - manifest_expected.add_entry(round, "MSM_X4", size_G); - manifest_expected.add_entry(round, "MSM_Y4", size_G); - manifest_expected.add_entry(round, "MSM_COLLISION_X1", size_G); - manifest_expected.add_entry(round, "MSM_COLLISION_X2", size_G); - manifest_expected.add_entry(round, "MSM_COLLISION_X3", size_G); - manifest_expected.add_entry(round, "MSM_COLLISION_X4", size_G); - manifest_expected.add_entry(round, "MSM_LAMBDA1", size_G); - manifest_expected.add_entry(round, "MSM_LAMBDA2", size_G); - manifest_expected.add_entry(round, "MSM_LAMBDA3", size_G); - manifest_expected.add_entry(round, "MSM_LAMBDA4", size_G); - manifest_expected.add_entry(round, "MSM_SLICE1", size_G); - manifest_expected.add_entry(round, "MSM_SLICE2", size_G); - manifest_expected.add_entry(round, "MSM_SLICE3", size_G); - manifest_expected.add_entry(round, "MSM_SLICE4", size_G); - manifest_expected.add_entry(round, "TRANSCRIPT_ACCUMULATOR_EMPTY", size_G); - manifest_expected.add_entry(round, "TRANSCRIPT_RESET_ACCUMULATOR", size_G); - manifest_expected.add_entry(round, "PRECOMPUTE_SELECT", size_G); - manifest_expected.add_entry(round, "LOOKUP_READ_COUNTS_0", size_G); - manifest_expected.add_entry(round, "LOOKUP_READ_COUNTS_1", size_G); + manifest_expected.add_entry(round, "circuit_size", frs_per_uint32); + manifest_expected.add_entry(round, "TRANSCRIPT_ADD", frs_per_G); + manifest_expected.add_entry(round, "TRANSCRIPT_MUL", frs_per_G); + manifest_expected.add_entry(round, "TRANSCRIPT_EQ", frs_per_G); + manifest_expected.add_entry(round, "TRANSCRIPT_COLLISION_CHECK", frs_per_G); + manifest_expected.add_entry(round, "TRANSCRIPT_MSM_TRANSITION", frs_per_G); + manifest_expected.add_entry(round, "TRANSCRIPT_PC", frs_per_G); + manifest_expected.add_entry(round, "TRANSCRIPT_MSM_COUNT", frs_per_G); + manifest_expected.add_entry(round, "TRANSCRIPT_PX", frs_per_G); + manifest_expected.add_entry(round, "TRANSCRIPT_PY", frs_per_G); + manifest_expected.add_entry(round, "TRANSCRIPT_Z1", frs_per_G); + manifest_expected.add_entry(round, "TRANSCRIPT_Z2", frs_per_G); + manifest_expected.add_entry(round, "TRANSCRIPT_Z1ZERO", frs_per_G); + manifest_expected.add_entry(round, "TRANSCRIPT_Z2ZERO", frs_per_G); + manifest_expected.add_entry(round, "TRANSCRIPT_OP", frs_per_G); + manifest_expected.add_entry(round, "TRANSCRIPT_ACCUMULATOR_X", frs_per_G); + manifest_expected.add_entry(round, "TRANSCRIPT_ACCUMULATOR_Y", frs_per_G); + manifest_expected.add_entry(round, "TRANSCRIPT_MSM_X", frs_per_G); + manifest_expected.add_entry(round, "TRANSCRIPT_MSM_Y", frs_per_G); + manifest_expected.add_entry(round, "PRECOMPUTE_PC", frs_per_G); + manifest_expected.add_entry(round, "PRECOMPUTE_POINT_TRANSITION", frs_per_G); + manifest_expected.add_entry(round, "PRECOMPUTE_ROUND", frs_per_G); + manifest_expected.add_entry(round, "PRECOMPUTE_SCALAR_SUM", frs_per_G); + manifest_expected.add_entry(round, "PRECOMPUTE_S1HI", frs_per_G); + manifest_expected.add_entry(round, "PRECOMPUTE_S1LO", frs_per_G); + manifest_expected.add_entry(round, "PRECOMPUTE_S2HI", frs_per_G); + manifest_expected.add_entry(round, "PRECOMPUTE_S2LO", frs_per_G); + manifest_expected.add_entry(round, "PRECOMPUTE_S3HI", frs_per_G); + manifest_expected.add_entry(round, "PRECOMPUTE_S3LO", frs_per_G); + manifest_expected.add_entry(round, "PRECOMPUTE_S4HI", frs_per_G); + manifest_expected.add_entry(round, "PRECOMPUTE_S4LO", frs_per_G); + manifest_expected.add_entry(round, "PRECOMPUTE_SKEW", frs_per_G); + manifest_expected.add_entry(round, "PRECOMPUTE_DX", frs_per_G); + manifest_expected.add_entry(round, "PRECOMPUTE_DY", frs_per_G); + manifest_expected.add_entry(round, "PRECOMPUTE_TX", frs_per_G); + manifest_expected.add_entry(round, "PRECOMPUTE_TY", frs_per_G); + manifest_expected.add_entry(round, "MSM_TRANSITION", frs_per_G); + manifest_expected.add_entry(round, "MSM_ADD", frs_per_G); + manifest_expected.add_entry(round, "MSM_DOUBLE", frs_per_G); + manifest_expected.add_entry(round, "MSM_SKEW", frs_per_G); + manifest_expected.add_entry(round, "MSM_ACCUMULATOR_X", frs_per_G); + manifest_expected.add_entry(round, "MSM_ACCUMULATOR_Y", frs_per_G); + manifest_expected.add_entry(round, "MSM_PC", frs_per_G); + manifest_expected.add_entry(round, "MSM_SIZE_OF_MSM", frs_per_G); + manifest_expected.add_entry(round, "MSM_COUNT", frs_per_G); + manifest_expected.add_entry(round, "MSM_ROUND", frs_per_G); + manifest_expected.add_entry(round, "MSM_ADD1", frs_per_G); + manifest_expected.add_entry(round, "MSM_ADD2", frs_per_G); + manifest_expected.add_entry(round, "MSM_ADD3", frs_per_G); + manifest_expected.add_entry(round, "MSM_ADD4", frs_per_G); + manifest_expected.add_entry(round, "MSM_X1", frs_per_G); + manifest_expected.add_entry(round, "MSM_Y1", frs_per_G); + manifest_expected.add_entry(round, "MSM_X2", frs_per_G); + manifest_expected.add_entry(round, "MSM_Y2", frs_per_G); + manifest_expected.add_entry(round, "MSM_X3", frs_per_G); + manifest_expected.add_entry(round, "MSM_Y3", frs_per_G); + manifest_expected.add_entry(round, "MSM_X4", frs_per_G); + manifest_expected.add_entry(round, "MSM_Y4", frs_per_G); + manifest_expected.add_entry(round, "MSM_COLLISION_X1", frs_per_G); + manifest_expected.add_entry(round, "MSM_COLLISION_X2", frs_per_G); + manifest_expected.add_entry(round, "MSM_COLLISION_X3", frs_per_G); + manifest_expected.add_entry(round, "MSM_COLLISION_X4", frs_per_G); + manifest_expected.add_entry(round, "MSM_LAMBDA1", frs_per_G); + manifest_expected.add_entry(round, "MSM_LAMBDA2", frs_per_G); + manifest_expected.add_entry(round, "MSM_LAMBDA3", frs_per_G); + manifest_expected.add_entry(round, "MSM_LAMBDA4", frs_per_G); + manifest_expected.add_entry(round, "MSM_SLICE1", frs_per_G); + manifest_expected.add_entry(round, "MSM_SLICE2", frs_per_G); + manifest_expected.add_entry(round, "MSM_SLICE3", frs_per_G); + manifest_expected.add_entry(round, "MSM_SLICE4", frs_per_G); + manifest_expected.add_entry(round, "TRANSCRIPT_ACCUMULATOR_EMPTY", frs_per_G); + manifest_expected.add_entry(round, "TRANSCRIPT_RESET_ACCUMULATOR", frs_per_G); + manifest_expected.add_entry(round, "PRECOMPUTE_SELECT", frs_per_G); + manifest_expected.add_entry(round, "LOOKUP_READ_COUNTS_0", frs_per_G); + manifest_expected.add_entry(round, "LOOKUP_READ_COUNTS_1", frs_per_G); manifest_expected.add_challenge(round, "beta", "gamma"); round++; - manifest_expected.add_entry(round, "LOOKUP_INVERSES", size_G); - manifest_expected.add_entry(round, "Z_PERM", size_G); + manifest_expected.add_entry(round, "LOOKUP_INVERSES", frs_per_G); + manifest_expected.add_entry(round, "Z_PERM", frs_per_G); manifest_expected.add_challenge(round, "Sumcheck:alpha"); for (size_t i = 0; i < log_n; i++) { @@ -138,49 +138,49 @@ template class ECCVMTranscriptTests : public ::testing::Test { for (size_t i = 0; i < log_n; ++i) { round++; std::string idx = std::to_string(i); - manifest_expected.add_entry(round, "Sumcheck:univariate_" + idx, size_uni); + manifest_expected.add_entry(round, "Sumcheck:univariate_" + idx, frs_per_uni); std::string label = "Sumcheck:u_" + idx; manifest_expected.add_challenge(round, label); } round++; - manifest_expected.add_entry(round, "Sumcheck:evaluations", size_evals); + manifest_expected.add_entry(round, "Sumcheck:evaluations", frs_per_evals); manifest_expected.add_challenge(round, "rho"); round++; for (size_t i = 1; i < log_n; ++i) { std::string idx = std::to_string(i); - manifest_expected.add_entry(round, "Gemini:FOLD_" + idx, size_G); + manifest_expected.add_entry(round, "Gemini:FOLD_" + idx, frs_per_G); } manifest_expected.add_challenge(round, "Gemini:r"); round++; for (size_t i = 0; i < log_n; ++i) { std::string idx = std::to_string(i); - manifest_expected.add_entry(round, "Gemini:a_" + idx, size_FF); + manifest_expected.add_entry(round, "Gemini:a_" + idx, frs_per_Fr); } manifest_expected.add_challenge(round, "Shplonk:nu"); round++; - manifest_expected.add_entry(round, "Shplonk:Q", size_G); + manifest_expected.add_entry(round, "Shplonk:Q", frs_per_G); manifest_expected.add_challenge(round, "Shplonk:z"); round++; - manifest_expected.add_entry(round, "IPA:poly_degree", size_uint64); + manifest_expected.add_entry(round, "IPA:poly_degree", frs_per_uint32); manifest_expected.add_challenge(round, "IPA:generator_challenge"); auto log_poly_degree = static_cast(numeric::get_msb(ipa_poly_degree)); for (size_t i = 0; i < log_poly_degree; ++i) { round++; std::string idx = std::to_string(i); - manifest_expected.add_entry(round, "IPA:L_" + idx, size_G); - manifest_expected.add_entry(round, "IPA:R_" + idx, size_G); + manifest_expected.add_entry(round, "IPA:L_" + idx, frs_per_G); + manifest_expected.add_entry(round, "IPA:R_" + idx, frs_per_G); std::string label = "IPA:round_challenge_" + idx; manifest_expected.add_challenge(round, label); } round++; - manifest_expected.add_entry(round, "IPA:a_0", size_FF); + manifest_expected.add_entry(round, "IPA:a_0", frs_per_Fr); return manifest_expected; } diff --git a/barretenberg/cpp/src/barretenberg/eccvm/eccvm_verifier.cpp b/barretenberg/cpp/src/barretenberg/eccvm/eccvm_verifier.cpp index 198cd654089..82780b09eb3 100644 --- a/barretenberg/cpp/src/barretenberg/eccvm/eccvm_verifier.cpp +++ b/barretenberg/cpp/src/barretenberg/eccvm/eccvm_verifier.cpp @@ -32,7 +32,7 @@ template ECCVMVerifier_& ECCVMVerifier_::opera * @brief This function verifies an ECCVM Honk proof for given program settings. * */ -template bool ECCVMVerifier_::verify_proof(const plonk::proof& proof) +template bool ECCVMVerifier_::verify_proof(const honk::proof& proof) { using FF = typename Flavor::FF; using GroupElement = typename Flavor::GroupElement; @@ -48,7 +48,7 @@ template bool ECCVMVerifier_::verify_proof(const plonk RelationParameters relation_parameters; - transcript = std::make_shared(proof.proof_data); + transcript = std::make_shared(proof); VerifierCommitments commitments{ key }; CommitmentLabels commitment_labels; diff --git a/barretenberg/cpp/src/barretenberg/eccvm/eccvm_verifier.hpp b/barretenberg/cpp/src/barretenberg/eccvm/eccvm_verifier.hpp index 44ae6c720d4..998bd5ed034 100644 --- a/barretenberg/cpp/src/barretenberg/eccvm/eccvm_verifier.hpp +++ b/barretenberg/cpp/src/barretenberg/eccvm/eccvm_verifier.hpp @@ -1,6 +1,6 @@ #pragma once #include "barretenberg/flavor/ecc_vm.hpp" -#include "barretenberg/plonk/proof_system/types/proof.hpp" +#include "barretenberg/honk/proof_system/types/proof.hpp" #include "barretenberg/sumcheck/sumcheck.hpp" namespace bb::honk { @@ -30,7 +30,7 @@ template class ECCVMVerifier_ { ECCVMVerifier_& operator=(ECCVMVerifier_&& other) noexcept; ~ECCVMVerifier_() = default; - bool verify_proof(const plonk::proof& proof); + bool verify_proof(const honk::proof& proof); std::shared_ptr key; std::map commitments; diff --git a/barretenberg/cpp/src/barretenberg/env/hardware_concurrency.cpp b/barretenberg/cpp/src/barretenberg/env/hardware_concurrency.cpp index 3aedfcbd83c..7814a02e406 100644 --- a/barretenberg/cpp/src/barretenberg/env/hardware_concurrency.cpp +++ b/barretenberg/cpp/src/barretenberg/env/hardware_concurrency.cpp @@ -12,9 +12,14 @@ uint32_t env_hardware_concurrency() #ifndef __wasm__ try { #endif - static auto val = std::getenv("HARDWARE_CONCURRENCY"); - static const uint32_t cores = val ? (uint32_t)std::stoul(val) : std::thread::hardware_concurrency(); - return cores; +#ifdef WASMTIME_ENV_HACK + // TODO(https://github.com/AztecProtocol/barretenberg/issues/837): Undo this hack, rely on WASI. + return 16; +#else + static auto val = std::getenv("HARDWARE_CONCURRENCY"); + static const uint32_t cores = val ? (uint32_t)std::stoul(val) : std::thread::hardware_concurrency(); + return cores; +#endif #ifndef __wasm__ } catch (std::exception const&) { throw std::runtime_error("HARDWARE_CONCURRENCY invalid."); diff --git a/barretenberg/cpp/src/barretenberg/flavor/ecc_vm.hpp b/barretenberg/cpp/src/barretenberg/flavor/ecc_vm.hpp index 2bf24f727f6..8745101e633 100644 --- a/barretenberg/cpp/src/barretenberg/flavor/ecc_vm.hpp +++ b/barretenberg/cpp/src/barretenberg/flavor/ecc_vm.hpp @@ -613,201 +613,201 @@ template class ECCVMBa Transcript() = default; - Transcript(const std::vector& proof) + Transcript(const honk::proof& proof) : BaseTranscript(proof) {} void deserialize_full_transcript() { // take current proof and put them into the struct - size_t num_bytes_read = 0; + size_t num_frs_read = 0; circuit_size = - BaseTranscript::template deserialize_from_buffer(BaseTranscript::proof_data, num_bytes_read); + BaseTranscript::template deserialize_from_buffer(BaseTranscript::proof_data, num_frs_read); size_t log_n = numeric::get_msb(circuit_size); - transcript_add_comm = BaseTranscript::template deserialize_from_buffer( - BaseTranscript::proof_data, num_bytes_read); - transcript_mul_comm = BaseTranscript::template deserialize_from_buffer( - BaseTranscript::proof_data, num_bytes_read); - transcript_eq_comm = BaseTranscript::template deserialize_from_buffer( - BaseTranscript::proof_data, num_bytes_read); - transcript_collision_check_comm = BaseTranscript::template deserialize_from_buffer( - BaseTranscript::proof_data, num_bytes_read); - transcript_msm_transition_comm = BaseTranscript::template deserialize_from_buffer( - BaseTranscript::proof_data, num_bytes_read); - transcript_pc_comm = BaseTranscript::template deserialize_from_buffer( - BaseTranscript::proof_data, num_bytes_read); - transcript_msm_count_comm = BaseTranscript::template deserialize_from_buffer( - BaseTranscript::proof_data, num_bytes_read); - transcript_Px_comm = BaseTranscript::template deserialize_from_buffer( - BaseTranscript::proof_data, num_bytes_read); - transcript_Py_comm = BaseTranscript::template deserialize_from_buffer( - BaseTranscript::proof_data, num_bytes_read); - transcript_z1_comm = BaseTranscript::template deserialize_from_buffer( - BaseTranscript::proof_data, num_bytes_read); - transcript_z2_comm = BaseTranscript::template deserialize_from_buffer( - BaseTranscript::proof_data, num_bytes_read); - transcript_z1zero_comm = BaseTranscript::template deserialize_from_buffer( - BaseTranscript::proof_data, num_bytes_read); - transcript_z2zero_comm = BaseTranscript::template deserialize_from_buffer( - BaseTranscript::proof_data, num_bytes_read); - transcript_op_comm = BaseTranscript::template deserialize_from_buffer( - BaseTranscript::proof_data, num_bytes_read); - transcript_accumulator_x_comm = BaseTranscript::template deserialize_from_buffer( - BaseTranscript::proof_data, num_bytes_read); - transcript_accumulator_y_comm = BaseTranscript::template deserialize_from_buffer( - BaseTranscript::proof_data, num_bytes_read); - transcript_msm_x_comm = BaseTranscript::template deserialize_from_buffer( - BaseTranscript::proof_data, num_bytes_read); - transcript_msm_y_comm = BaseTranscript::template deserialize_from_buffer( - BaseTranscript::proof_data, num_bytes_read); - precompute_pc_comm = BaseTranscript::template deserialize_from_buffer( - BaseTranscript::proof_data, num_bytes_read); - precompute_point_transition_comm = BaseTranscript::template deserialize_from_buffer( - BaseTranscript::proof_data, num_bytes_read); - precompute_round_comm = BaseTranscript::template deserialize_from_buffer( - BaseTranscript::proof_data, num_bytes_read); - precompute_scalar_sum_comm = BaseTranscript::template deserialize_from_buffer( - BaseTranscript::proof_data, num_bytes_read); - precompute_s1hi_comm = BaseTranscript::template deserialize_from_buffer( - BaseTranscript::proof_data, num_bytes_read); - precompute_s1lo_comm = BaseTranscript::template deserialize_from_buffer( - BaseTranscript::proof_data, num_bytes_read); - precompute_s2hi_comm = BaseTranscript::template deserialize_from_buffer( - BaseTranscript::proof_data, num_bytes_read); - precompute_s2lo_comm = BaseTranscript::template deserialize_from_buffer( - BaseTranscript::proof_data, num_bytes_read); - precompute_s3hi_comm = BaseTranscript::template deserialize_from_buffer( - BaseTranscript::proof_data, num_bytes_read); - precompute_s3lo_comm = BaseTranscript::template deserialize_from_buffer( - BaseTranscript::proof_data, num_bytes_read); - precompute_s4hi_comm = BaseTranscript::template deserialize_from_buffer( - BaseTranscript::proof_data, num_bytes_read); - precompute_s4lo_comm = BaseTranscript::template deserialize_from_buffer( - BaseTranscript::proof_data, num_bytes_read); - precompute_skew_comm = BaseTranscript::template deserialize_from_buffer( - BaseTranscript::proof_data, num_bytes_read); - precompute_dx_comm = BaseTranscript::template deserialize_from_buffer( - BaseTranscript::proof_data, num_bytes_read); - precompute_dy_comm = BaseTranscript::template deserialize_from_buffer( - BaseTranscript::proof_data, num_bytes_read); - precompute_tx_comm = BaseTranscript::template deserialize_from_buffer( - BaseTranscript::proof_data, num_bytes_read); - precompute_ty_comm = BaseTranscript::template deserialize_from_buffer( - BaseTranscript::proof_data, num_bytes_read); - msm_transition_comm = BaseTranscript::template deserialize_from_buffer( - BaseTranscript::proof_data, num_bytes_read); - msm_add_comm = BaseTranscript::template deserialize_from_buffer(BaseTranscript::proof_data, - num_bytes_read); - msm_double_comm = BaseTranscript::template deserialize_from_buffer(BaseTranscript::proof_data, - num_bytes_read); - msm_skew_comm = BaseTranscript::template deserialize_from_buffer(BaseTranscript::proof_data, - num_bytes_read); - msm_accumulator_x_comm = BaseTranscript::template deserialize_from_buffer( - BaseTranscript::proof_data, num_bytes_read); - msm_accumulator_y_comm = BaseTranscript::template deserialize_from_buffer( - BaseTranscript::proof_data, num_bytes_read); - msm_pc_comm = BaseTranscript::template deserialize_from_buffer(BaseTranscript::proof_data, - num_bytes_read); - msm_size_of_msm_comm = BaseTranscript::template deserialize_from_buffer( - BaseTranscript::proof_data, num_bytes_read); - msm_count_comm = BaseTranscript::template deserialize_from_buffer(BaseTranscript::proof_data, - num_bytes_read); - msm_round_comm = BaseTranscript::template deserialize_from_buffer(BaseTranscript::proof_data, - num_bytes_read); - msm_add1_comm = BaseTranscript::template deserialize_from_buffer(BaseTranscript::proof_data, - num_bytes_read); - msm_add2_comm = BaseTranscript::template deserialize_from_buffer(BaseTranscript::proof_data, - num_bytes_read); - msm_add3_comm = BaseTranscript::template deserialize_from_buffer(BaseTranscript::proof_data, - num_bytes_read); - msm_add4_comm = BaseTranscript::template deserialize_from_buffer(BaseTranscript::proof_data, - num_bytes_read); - msm_x1_comm = BaseTranscript::template deserialize_from_buffer(BaseTranscript::proof_data, - num_bytes_read); - msm_y1_comm = BaseTranscript::template deserialize_from_buffer(BaseTranscript::proof_data, - num_bytes_read); - msm_x2_comm = BaseTranscript::template deserialize_from_buffer(BaseTranscript::proof_data, - num_bytes_read); - msm_y2_comm = BaseTranscript::template deserialize_from_buffer(BaseTranscript::proof_data, - num_bytes_read); - msm_x3_comm = BaseTranscript::template deserialize_from_buffer(BaseTranscript::proof_data, - num_bytes_read); - msm_y3_comm = BaseTranscript::template deserialize_from_buffer(BaseTranscript::proof_data, - num_bytes_read); - msm_x4_comm = BaseTranscript::template deserialize_from_buffer(BaseTranscript::proof_data, - num_bytes_read); - msm_y4_comm = BaseTranscript::template deserialize_from_buffer(BaseTranscript::proof_data, - num_bytes_read); - msm_collision_x1_comm = BaseTranscript::template deserialize_from_buffer( - BaseTranscript::proof_data, num_bytes_read); - msm_collision_x2_comm = BaseTranscript::template deserialize_from_buffer( - BaseTranscript::proof_data, num_bytes_read); - msm_collision_x3_comm = BaseTranscript::template deserialize_from_buffer( - BaseTranscript::proof_data, num_bytes_read); - msm_collision_x4_comm = BaseTranscript::template deserialize_from_buffer( - BaseTranscript::proof_data, num_bytes_read); - msm_lambda1_comm = BaseTranscript::template deserialize_from_buffer(BaseTranscript::proof_data, - num_bytes_read); - msm_lambda2_comm = BaseTranscript::template deserialize_from_buffer(BaseTranscript::proof_data, - num_bytes_read); - msm_lambda3_comm = BaseTranscript::template deserialize_from_buffer(BaseTranscript::proof_data, - num_bytes_read); - msm_lambda4_comm = BaseTranscript::template deserialize_from_buffer(BaseTranscript::proof_data, - num_bytes_read); - msm_slice1_comm = BaseTranscript::template deserialize_from_buffer(BaseTranscript::proof_data, - num_bytes_read); - msm_slice2_comm = BaseTranscript::template deserialize_from_buffer(BaseTranscript::proof_data, - num_bytes_read); - msm_slice3_comm = BaseTranscript::template deserialize_from_buffer(BaseTranscript::proof_data, - num_bytes_read); - msm_slice4_comm = BaseTranscript::template deserialize_from_buffer(BaseTranscript::proof_data, - num_bytes_read); - transcript_accumulator_empty_comm = BaseTranscript::template deserialize_from_buffer( - BaseTranscript::proof_data, num_bytes_read); - transcript_reset_accumulator_comm = BaseTranscript::template deserialize_from_buffer( - BaseTranscript::proof_data, num_bytes_read); - precompute_select_comm = BaseTranscript::template deserialize_from_buffer( - BaseTranscript::proof_data, num_bytes_read); - lookup_read_counts_0_comm = BaseTranscript::template deserialize_from_buffer( - BaseTranscript::proof_data, num_bytes_read); - lookup_read_counts_1_comm = BaseTranscript::template deserialize_from_buffer( - BaseTranscript::proof_data, num_bytes_read); - lookup_inverses_comm = BaseTranscript::template deserialize_from_buffer( - BaseTranscript::proof_data, num_bytes_read); - z_perm_comm = BaseTranscript::template deserialize_from_buffer(BaseTranscript::proof_data, - num_bytes_read); + transcript_add_comm = + BaseTranscript::template deserialize_from_buffer(BaseTranscript::proof_data, num_frs_read); + transcript_mul_comm = + BaseTranscript::template deserialize_from_buffer(BaseTranscript::proof_data, num_frs_read); + transcript_eq_comm = + BaseTranscript::template deserialize_from_buffer(BaseTranscript::proof_data, num_frs_read); + transcript_collision_check_comm = + BaseTranscript::template deserialize_from_buffer(BaseTranscript::proof_data, num_frs_read); + transcript_msm_transition_comm = + BaseTranscript::template deserialize_from_buffer(BaseTranscript::proof_data, num_frs_read); + transcript_pc_comm = + BaseTranscript::template deserialize_from_buffer(BaseTranscript::proof_data, num_frs_read); + transcript_msm_count_comm = + BaseTranscript::template deserialize_from_buffer(BaseTranscript::proof_data, num_frs_read); + transcript_Px_comm = + BaseTranscript::template deserialize_from_buffer(BaseTranscript::proof_data, num_frs_read); + transcript_Py_comm = + BaseTranscript::template deserialize_from_buffer(BaseTranscript::proof_data, num_frs_read); + transcript_z1_comm = + BaseTranscript::template deserialize_from_buffer(BaseTranscript::proof_data, num_frs_read); + transcript_z2_comm = + BaseTranscript::template deserialize_from_buffer(BaseTranscript::proof_data, num_frs_read); + transcript_z1zero_comm = + BaseTranscript::template deserialize_from_buffer(BaseTranscript::proof_data, num_frs_read); + transcript_z2zero_comm = + BaseTranscript::template deserialize_from_buffer(BaseTranscript::proof_data, num_frs_read); + transcript_op_comm = + BaseTranscript::template deserialize_from_buffer(BaseTranscript::proof_data, num_frs_read); + transcript_accumulator_x_comm = + BaseTranscript::template deserialize_from_buffer(BaseTranscript::proof_data, num_frs_read); + transcript_accumulator_y_comm = + BaseTranscript::template deserialize_from_buffer(BaseTranscript::proof_data, num_frs_read); + transcript_msm_x_comm = + BaseTranscript::template deserialize_from_buffer(BaseTranscript::proof_data, num_frs_read); + transcript_msm_y_comm = + BaseTranscript::template deserialize_from_buffer(BaseTranscript::proof_data, num_frs_read); + precompute_pc_comm = + BaseTranscript::template deserialize_from_buffer(BaseTranscript::proof_data, num_frs_read); + precompute_point_transition_comm = + BaseTranscript::template deserialize_from_buffer(BaseTranscript::proof_data, num_frs_read); + precompute_round_comm = + BaseTranscript::template deserialize_from_buffer(BaseTranscript::proof_data, num_frs_read); + precompute_scalar_sum_comm = + BaseTranscript::template deserialize_from_buffer(BaseTranscript::proof_data, num_frs_read); + precompute_s1hi_comm = + BaseTranscript::template deserialize_from_buffer(BaseTranscript::proof_data, num_frs_read); + precompute_s1lo_comm = + BaseTranscript::template deserialize_from_buffer(BaseTranscript::proof_data, num_frs_read); + precompute_s2hi_comm = + BaseTranscript::template deserialize_from_buffer(BaseTranscript::proof_data, num_frs_read); + precompute_s2lo_comm = + BaseTranscript::template deserialize_from_buffer(BaseTranscript::proof_data, num_frs_read); + precompute_s3hi_comm = + BaseTranscript::template deserialize_from_buffer(BaseTranscript::proof_data, num_frs_read); + precompute_s3lo_comm = + BaseTranscript::template deserialize_from_buffer(BaseTranscript::proof_data, num_frs_read); + precompute_s4hi_comm = + BaseTranscript::template deserialize_from_buffer(BaseTranscript::proof_data, num_frs_read); + precompute_s4lo_comm = + BaseTranscript::template deserialize_from_buffer(BaseTranscript::proof_data, num_frs_read); + precompute_skew_comm = + BaseTranscript::template deserialize_from_buffer(BaseTranscript::proof_data, num_frs_read); + precompute_dx_comm = + BaseTranscript::template deserialize_from_buffer(BaseTranscript::proof_data, num_frs_read); + precompute_dy_comm = + BaseTranscript::template deserialize_from_buffer(BaseTranscript::proof_data, num_frs_read); + precompute_tx_comm = + BaseTranscript::template deserialize_from_buffer(BaseTranscript::proof_data, num_frs_read); + precompute_ty_comm = + BaseTranscript::template deserialize_from_buffer(BaseTranscript::proof_data, num_frs_read); + msm_transition_comm = + BaseTranscript::template deserialize_from_buffer(BaseTranscript::proof_data, num_frs_read); + msm_add_comm = + BaseTranscript::template deserialize_from_buffer(BaseTranscript::proof_data, num_frs_read); + msm_double_comm = + BaseTranscript::template deserialize_from_buffer(BaseTranscript::proof_data, num_frs_read); + msm_skew_comm = + BaseTranscript::template deserialize_from_buffer(BaseTranscript::proof_data, num_frs_read); + msm_accumulator_x_comm = + BaseTranscript::template deserialize_from_buffer(BaseTranscript::proof_data, num_frs_read); + msm_accumulator_y_comm = + BaseTranscript::template deserialize_from_buffer(BaseTranscript::proof_data, num_frs_read); + msm_pc_comm = + BaseTranscript::template deserialize_from_buffer(BaseTranscript::proof_data, num_frs_read); + msm_size_of_msm_comm = + BaseTranscript::template deserialize_from_buffer(BaseTranscript::proof_data, num_frs_read); + msm_count_comm = + BaseTranscript::template deserialize_from_buffer(BaseTranscript::proof_data, num_frs_read); + msm_round_comm = + BaseTranscript::template deserialize_from_buffer(BaseTranscript::proof_data, num_frs_read); + msm_add1_comm = + BaseTranscript::template deserialize_from_buffer(BaseTranscript::proof_data, num_frs_read); + msm_add2_comm = + BaseTranscript::template deserialize_from_buffer(BaseTranscript::proof_data, num_frs_read); + msm_add3_comm = + BaseTranscript::template deserialize_from_buffer(BaseTranscript::proof_data, num_frs_read); + msm_add4_comm = + BaseTranscript::template deserialize_from_buffer(BaseTranscript::proof_data, num_frs_read); + msm_x1_comm = + BaseTranscript::template deserialize_from_buffer(BaseTranscript::proof_data, num_frs_read); + msm_y1_comm = + BaseTranscript::template deserialize_from_buffer(BaseTranscript::proof_data, num_frs_read); + msm_x2_comm = + BaseTranscript::template deserialize_from_buffer(BaseTranscript::proof_data, num_frs_read); + msm_y2_comm = + BaseTranscript::template deserialize_from_buffer(BaseTranscript::proof_data, num_frs_read); + msm_x3_comm = + BaseTranscript::template deserialize_from_buffer(BaseTranscript::proof_data, num_frs_read); + msm_y3_comm = + BaseTranscript::template deserialize_from_buffer(BaseTranscript::proof_data, num_frs_read); + msm_x4_comm = + BaseTranscript::template deserialize_from_buffer(BaseTranscript::proof_data, num_frs_read); + msm_y4_comm = + BaseTranscript::template deserialize_from_buffer(BaseTranscript::proof_data, num_frs_read); + msm_collision_x1_comm = + BaseTranscript::template deserialize_from_buffer(BaseTranscript::proof_data, num_frs_read); + msm_collision_x2_comm = + BaseTranscript::template deserialize_from_buffer(BaseTranscript::proof_data, num_frs_read); + msm_collision_x3_comm = + BaseTranscript::template deserialize_from_buffer(BaseTranscript::proof_data, num_frs_read); + msm_collision_x4_comm = + BaseTranscript::template deserialize_from_buffer(BaseTranscript::proof_data, num_frs_read); + msm_lambda1_comm = + BaseTranscript::template deserialize_from_buffer(BaseTranscript::proof_data, num_frs_read); + msm_lambda2_comm = + BaseTranscript::template deserialize_from_buffer(BaseTranscript::proof_data, num_frs_read); + msm_lambda3_comm = + BaseTranscript::template deserialize_from_buffer(BaseTranscript::proof_data, num_frs_read); + msm_lambda4_comm = + BaseTranscript::template deserialize_from_buffer(BaseTranscript::proof_data, num_frs_read); + msm_slice1_comm = + BaseTranscript::template deserialize_from_buffer(BaseTranscript::proof_data, num_frs_read); + msm_slice2_comm = + BaseTranscript::template deserialize_from_buffer(BaseTranscript::proof_data, num_frs_read); + msm_slice3_comm = + BaseTranscript::template deserialize_from_buffer(BaseTranscript::proof_data, num_frs_read); + msm_slice4_comm = + BaseTranscript::template deserialize_from_buffer(BaseTranscript::proof_data, num_frs_read); + transcript_accumulator_empty_comm = + BaseTranscript::template deserialize_from_buffer(BaseTranscript::proof_data, num_frs_read); + transcript_reset_accumulator_comm = + BaseTranscript::template deserialize_from_buffer(BaseTranscript::proof_data, num_frs_read); + precompute_select_comm = + BaseTranscript::template deserialize_from_buffer(BaseTranscript::proof_data, num_frs_read); + lookup_read_counts_0_comm = + BaseTranscript::template deserialize_from_buffer(BaseTranscript::proof_data, num_frs_read); + lookup_read_counts_1_comm = + BaseTranscript::template deserialize_from_buffer(BaseTranscript::proof_data, num_frs_read); + lookup_inverses_comm = + BaseTranscript::template deserialize_from_buffer(BaseTranscript::proof_data, num_frs_read); + z_perm_comm = + BaseTranscript::template deserialize_from_buffer(BaseTranscript::proof_data, num_frs_read); for (size_t i = 0; i < log_n; ++i) { - sumcheck_univariates.emplace_back(BaseTranscript::template deserialize_from_buffer< - bb::Univariate>( - BaseTranscript::proof_data, num_bytes_read)); + sumcheck_univariates.emplace_back( + BaseTranscript::template deserialize_from_buffer< + bb::Univariate>(BaseTranscript::proof_data, num_frs_read)); } sumcheck_evaluations = BaseTranscript::template deserialize_from_buffer>( - BaseTranscript::proof_data, num_bytes_read); + BaseTranscript::proof_data, num_frs_read); for (size_t i = 0; i < log_n - 1; ++i) { gemini_univariate_comms.emplace_back(BaseTranscript::template deserialize_from_buffer( - BaseTranscript::proof_data, num_bytes_read)); + BaseTranscript::proof_data, num_frs_read)); } for (size_t i = 0; i < log_n; ++i) { gemini_a_evals.emplace_back( - BaseTranscript::template deserialize_from_buffer(BaseTranscript::proof_data, num_bytes_read)); + BaseTranscript::template deserialize_from_buffer(BaseTranscript::proof_data, num_frs_read)); } - shplonk_q_comm = BaseTranscript::template deserialize_from_buffer(BaseTranscript::proof_data, - num_bytes_read); + shplonk_q_comm = + BaseTranscript::template deserialize_from_buffer(BaseTranscript::proof_data, num_frs_read); if (std::is_same>::value) { kzg_w_comm = BaseTranscript::template deserialize_from_buffer(BaseTranscript::proof_data, - num_bytes_read); + num_frs_read); } else if (std::is_same>::value) { ipa_poly_degree = BaseTranscript::template deserialize_from_buffer(BaseTranscript::proof_data, - num_bytes_read); + num_frs_read); auto log_poly_degree = static_cast(numeric::get_msb(ipa_poly_degree)); for (size_t i = 0; i < log_poly_degree; ++i) { ipa_l_comms.emplace_back(BaseTranscript::template deserialize_from_buffer( - BaseTranscript::proof_data, num_bytes_read)); + BaseTranscript::proof_data, num_frs_read)); ipa_r_comms.emplace_back(BaseTranscript::template deserialize_from_buffer( - BaseTranscript::proof_data, num_bytes_read)); + BaseTranscript::proof_data, num_frs_read)); } ipa_a_0_eval = - BaseTranscript::template deserialize_from_buffer(BaseTranscript::proof_data, num_bytes_read); + BaseTranscript::template deserialize_from_buffer(BaseTranscript::proof_data, num_frs_read); } else { throw_or_abort("Unsupported PCS"); } diff --git a/barretenberg/cpp/src/barretenberg/flavor/generated/AvmMini_flavor.hpp b/barretenberg/cpp/src/barretenberg/flavor/generated/AvmMini_flavor.hpp index c1f672eb177..19b18ec3d79 100644 --- a/barretenberg/cpp/src/barretenberg/flavor/generated/AvmMini_flavor.hpp +++ b/barretenberg/cpp/src/barretenberg/flavor/generated/AvmMini_flavor.hpp @@ -720,93 +720,93 @@ class AvmMiniFlavor { Transcript() = default; - Transcript(const std::vector& proof) + Transcript(const std::vector& proof) : BaseTranscript(proof) {} void deserialize_full_transcript() { - size_t num_bytes_read = 0; - circuit_size = deserialize_from_buffer(proof_data, num_bytes_read); + size_t num_frs_read = 0; + circuit_size = deserialize_from_buffer(proof_data, num_frs_read); size_t log_n = numeric::get_msb(circuit_size); - memTrace_m_clk = deserialize_from_buffer(Transcript::proof_data, num_bytes_read); - memTrace_m_sub_clk = deserialize_from_buffer(Transcript::proof_data, num_bytes_read); - memTrace_m_addr = deserialize_from_buffer(Transcript::proof_data, num_bytes_read); - memTrace_m_tag = deserialize_from_buffer(Transcript::proof_data, num_bytes_read); - memTrace_m_val = deserialize_from_buffer(Transcript::proof_data, num_bytes_read); - memTrace_m_lastAccess = deserialize_from_buffer(Transcript::proof_data, num_bytes_read); - memTrace_m_last = deserialize_from_buffer(Transcript::proof_data, num_bytes_read); - memTrace_m_rw = deserialize_from_buffer(Transcript::proof_data, num_bytes_read); - memTrace_m_in_tag = deserialize_from_buffer(Transcript::proof_data, num_bytes_read); - memTrace_m_tag_err = deserialize_from_buffer(Transcript::proof_data, num_bytes_read); - memTrace_m_one_min_inv = deserialize_from_buffer(Transcript::proof_data, num_bytes_read); - aluChip_alu_clk = deserialize_from_buffer(Transcript::proof_data, num_bytes_read); - aluChip_alu_ia = deserialize_from_buffer(Transcript::proof_data, num_bytes_read); - aluChip_alu_ib = deserialize_from_buffer(Transcript::proof_data, num_bytes_read); - aluChip_alu_ic = deserialize_from_buffer(Transcript::proof_data, num_bytes_read); - aluChip_alu_op_add = deserialize_from_buffer(Transcript::proof_data, num_bytes_read); - aluChip_alu_op_sub = deserialize_from_buffer(Transcript::proof_data, num_bytes_read); - aluChip_alu_op_mul = deserialize_from_buffer(Transcript::proof_data, num_bytes_read); - aluChip_alu_op_div = deserialize_from_buffer(Transcript::proof_data, num_bytes_read); - aluChip_alu_ff_tag = deserialize_from_buffer(Transcript::proof_data, num_bytes_read); - aluChip_alu_u8_tag = deserialize_from_buffer(Transcript::proof_data, num_bytes_read); - aluChip_alu_u16_tag = deserialize_from_buffer(Transcript::proof_data, num_bytes_read); - aluChip_alu_u32_tag = deserialize_from_buffer(Transcript::proof_data, num_bytes_read); - aluChip_alu_u64_tag = deserialize_from_buffer(Transcript::proof_data, num_bytes_read); - aluChip_alu_u128_tag = deserialize_from_buffer(Transcript::proof_data, num_bytes_read); - aluChip_alu_u8_r0 = deserialize_from_buffer(Transcript::proof_data, num_bytes_read); - aluChip_alu_u8_r1 = deserialize_from_buffer(Transcript::proof_data, num_bytes_read); - aluChip_alu_u16_r0 = deserialize_from_buffer(Transcript::proof_data, num_bytes_read); - aluChip_alu_u16_r1 = deserialize_from_buffer(Transcript::proof_data, num_bytes_read); - aluChip_alu_u16_r2 = deserialize_from_buffer(Transcript::proof_data, num_bytes_read); - aluChip_alu_u16_r3 = deserialize_from_buffer(Transcript::proof_data, num_bytes_read); - aluChip_alu_u16_r4 = deserialize_from_buffer(Transcript::proof_data, num_bytes_read); - aluChip_alu_u16_r5 = deserialize_from_buffer(Transcript::proof_data, num_bytes_read); - aluChip_alu_u16_r6 = deserialize_from_buffer(Transcript::proof_data, num_bytes_read); - aluChip_alu_u16_r7 = deserialize_from_buffer(Transcript::proof_data, num_bytes_read); - aluChip_alu_u64_r0 = deserialize_from_buffer(Transcript::proof_data, num_bytes_read); - aluChip_alu_cf = deserialize_from_buffer(Transcript::proof_data, num_bytes_read); - avmMini_pc = deserialize_from_buffer(Transcript::proof_data, num_bytes_read); - avmMini_internal_return_ptr = deserialize_from_buffer(Transcript::proof_data, num_bytes_read); - avmMini_sel_internal_call = deserialize_from_buffer(Transcript::proof_data, num_bytes_read); - avmMini_sel_internal_return = deserialize_from_buffer(Transcript::proof_data, num_bytes_read); - avmMini_sel_jump = deserialize_from_buffer(Transcript::proof_data, num_bytes_read); - avmMini_sel_halt = deserialize_from_buffer(Transcript::proof_data, num_bytes_read); - avmMini_sel_op_add = deserialize_from_buffer(Transcript::proof_data, num_bytes_read); - avmMini_sel_op_sub = deserialize_from_buffer(Transcript::proof_data, num_bytes_read); - avmMini_sel_op_mul = deserialize_from_buffer(Transcript::proof_data, num_bytes_read); - avmMini_sel_op_div = deserialize_from_buffer(Transcript::proof_data, num_bytes_read); - avmMini_in_tag = deserialize_from_buffer(Transcript::proof_data, num_bytes_read); - avmMini_op_err = deserialize_from_buffer(Transcript::proof_data, num_bytes_read); - avmMini_tag_err = deserialize_from_buffer(Transcript::proof_data, num_bytes_read); - avmMini_inv = deserialize_from_buffer(Transcript::proof_data, num_bytes_read); - avmMini_ia = deserialize_from_buffer(Transcript::proof_data, num_bytes_read); - avmMini_ib = deserialize_from_buffer(Transcript::proof_data, num_bytes_read); - avmMini_ic = deserialize_from_buffer(Transcript::proof_data, num_bytes_read); - avmMini_mem_op_a = deserialize_from_buffer(Transcript::proof_data, num_bytes_read); - avmMini_mem_op_b = deserialize_from_buffer(Transcript::proof_data, num_bytes_read); - avmMini_mem_op_c = deserialize_from_buffer(Transcript::proof_data, num_bytes_read); - avmMini_rwa = deserialize_from_buffer(Transcript::proof_data, num_bytes_read); - avmMini_rwb = deserialize_from_buffer(Transcript::proof_data, num_bytes_read); - avmMini_rwc = deserialize_from_buffer(Transcript::proof_data, num_bytes_read); - avmMini_mem_idx_a = deserialize_from_buffer(Transcript::proof_data, num_bytes_read); - avmMini_mem_idx_b = deserialize_from_buffer(Transcript::proof_data, num_bytes_read); - avmMini_mem_idx_c = deserialize_from_buffer(Transcript::proof_data, num_bytes_read); - avmMini_last = deserialize_from_buffer(Transcript::proof_data, num_bytes_read); + memTrace_m_clk = deserialize_from_buffer(Transcript::proof_data, num_frs_read); + memTrace_m_sub_clk = deserialize_from_buffer(Transcript::proof_data, num_frs_read); + memTrace_m_addr = deserialize_from_buffer(Transcript::proof_data, num_frs_read); + memTrace_m_tag = deserialize_from_buffer(Transcript::proof_data, num_frs_read); + memTrace_m_val = deserialize_from_buffer(Transcript::proof_data, num_frs_read); + memTrace_m_lastAccess = deserialize_from_buffer(Transcript::proof_data, num_frs_read); + memTrace_m_last = deserialize_from_buffer(Transcript::proof_data, num_frs_read); + memTrace_m_rw = deserialize_from_buffer(Transcript::proof_data, num_frs_read); + memTrace_m_in_tag = deserialize_from_buffer(Transcript::proof_data, num_frs_read); + memTrace_m_tag_err = deserialize_from_buffer(Transcript::proof_data, num_frs_read); + memTrace_m_one_min_inv = deserialize_from_buffer(Transcript::proof_data, num_frs_read); + aluChip_alu_clk = deserialize_from_buffer(Transcript::proof_data, num_frs_read); + aluChip_alu_ia = deserialize_from_buffer(Transcript::proof_data, num_frs_read); + aluChip_alu_ib = deserialize_from_buffer(Transcript::proof_data, num_frs_read); + aluChip_alu_ic = deserialize_from_buffer(Transcript::proof_data, num_frs_read); + aluChip_alu_op_add = deserialize_from_buffer(Transcript::proof_data, num_frs_read); + aluChip_alu_op_sub = deserialize_from_buffer(Transcript::proof_data, num_frs_read); + aluChip_alu_op_mul = deserialize_from_buffer(Transcript::proof_data, num_frs_read); + aluChip_alu_op_div = deserialize_from_buffer(Transcript::proof_data, num_frs_read); + aluChip_alu_ff_tag = deserialize_from_buffer(Transcript::proof_data, num_frs_read); + aluChip_alu_u8_tag = deserialize_from_buffer(Transcript::proof_data, num_frs_read); + aluChip_alu_u16_tag = deserialize_from_buffer(Transcript::proof_data, num_frs_read); + aluChip_alu_u32_tag = deserialize_from_buffer(Transcript::proof_data, num_frs_read); + aluChip_alu_u64_tag = deserialize_from_buffer(Transcript::proof_data, num_frs_read); + aluChip_alu_u128_tag = deserialize_from_buffer(Transcript::proof_data, num_frs_read); + aluChip_alu_u8_r0 = deserialize_from_buffer(Transcript::proof_data, num_frs_read); + aluChip_alu_u8_r1 = deserialize_from_buffer(Transcript::proof_data, num_frs_read); + aluChip_alu_u16_r0 = deserialize_from_buffer(Transcript::proof_data, num_frs_read); + aluChip_alu_u16_r1 = deserialize_from_buffer(Transcript::proof_data, num_frs_read); + aluChip_alu_u16_r2 = deserialize_from_buffer(Transcript::proof_data, num_frs_read); + aluChip_alu_u16_r3 = deserialize_from_buffer(Transcript::proof_data, num_frs_read); + aluChip_alu_u16_r4 = deserialize_from_buffer(Transcript::proof_data, num_frs_read); + aluChip_alu_u16_r5 = deserialize_from_buffer(Transcript::proof_data, num_frs_read); + aluChip_alu_u16_r6 = deserialize_from_buffer(Transcript::proof_data, num_frs_read); + aluChip_alu_u16_r7 = deserialize_from_buffer(Transcript::proof_data, num_frs_read); + aluChip_alu_u64_r0 = deserialize_from_buffer(Transcript::proof_data, num_frs_read); + aluChip_alu_cf = deserialize_from_buffer(Transcript::proof_data, num_frs_read); + avmMini_pc = deserialize_from_buffer(Transcript::proof_data, num_frs_read); + avmMini_internal_return_ptr = deserialize_from_buffer(Transcript::proof_data, num_frs_read); + avmMini_sel_internal_call = deserialize_from_buffer(Transcript::proof_data, num_frs_read); + avmMini_sel_internal_return = deserialize_from_buffer(Transcript::proof_data, num_frs_read); + avmMini_sel_jump = deserialize_from_buffer(Transcript::proof_data, num_frs_read); + avmMini_sel_halt = deserialize_from_buffer(Transcript::proof_data, num_frs_read); + avmMini_sel_op_add = deserialize_from_buffer(Transcript::proof_data, num_frs_read); + avmMini_sel_op_sub = deserialize_from_buffer(Transcript::proof_data, num_frs_read); + avmMini_sel_op_mul = deserialize_from_buffer(Transcript::proof_data, num_frs_read); + avmMini_sel_op_div = deserialize_from_buffer(Transcript::proof_data, num_frs_read); + avmMini_in_tag = deserialize_from_buffer(Transcript::proof_data, num_frs_read); + avmMini_op_err = deserialize_from_buffer(Transcript::proof_data, num_frs_read); + avmMini_tag_err = deserialize_from_buffer(Transcript::proof_data, num_frs_read); + avmMini_inv = deserialize_from_buffer(Transcript::proof_data, num_frs_read); + avmMini_ia = deserialize_from_buffer(Transcript::proof_data, num_frs_read); + avmMini_ib = deserialize_from_buffer(Transcript::proof_data, num_frs_read); + avmMini_ic = deserialize_from_buffer(Transcript::proof_data, num_frs_read); + avmMini_mem_op_a = deserialize_from_buffer(Transcript::proof_data, num_frs_read); + avmMini_mem_op_b = deserialize_from_buffer(Transcript::proof_data, num_frs_read); + avmMini_mem_op_c = deserialize_from_buffer(Transcript::proof_data, num_frs_read); + avmMini_rwa = deserialize_from_buffer(Transcript::proof_data, num_frs_read); + avmMini_rwb = deserialize_from_buffer(Transcript::proof_data, num_frs_read); + avmMini_rwc = deserialize_from_buffer(Transcript::proof_data, num_frs_read); + avmMini_mem_idx_a = deserialize_from_buffer(Transcript::proof_data, num_frs_read); + avmMini_mem_idx_b = deserialize_from_buffer(Transcript::proof_data, num_frs_read); + avmMini_mem_idx_c = deserialize_from_buffer(Transcript::proof_data, num_frs_read); + avmMini_last = deserialize_from_buffer(Transcript::proof_data, num_frs_read); for (size_t i = 0; i < log_n; ++i) { sumcheck_univariates.emplace_back( deserialize_from_buffer>(Transcript::proof_data, - num_bytes_read)); + num_frs_read)); } sumcheck_evaluations = - deserialize_from_buffer>(Transcript::proof_data, num_bytes_read); + deserialize_from_buffer>(Transcript::proof_data, num_frs_read); for (size_t i = 0; i < log_n; ++i) { - zm_cq_comms.push_back(deserialize_from_buffer(proof_data, num_bytes_read)); + zm_cq_comms.push_back(deserialize_from_buffer(proof_data, num_frs_read)); } - zm_cq_comm = deserialize_from_buffer(proof_data, num_bytes_read); - zm_pi_comm = deserialize_from_buffer(proof_data, num_bytes_read); + zm_cq_comm = deserialize_from_buffer(proof_data, num_frs_read); + zm_pi_comm = deserialize_from_buffer(proof_data, num_frs_read); } void serialize_full_transcript() diff --git a/barretenberg/cpp/src/barretenberg/flavor/generated/Toy_flavor.hpp b/barretenberg/cpp/src/barretenberg/flavor/generated/Toy_flavor.hpp index faac5550be0..258d4c3d8a0 100644 --- a/barretenberg/cpp/src/barretenberg/flavor/generated/Toy_flavor.hpp +++ b/barretenberg/cpp/src/barretenberg/flavor/generated/Toy_flavor.hpp @@ -285,45 +285,45 @@ class ToyFlavor { Transcript() = default; - Transcript(const std::vector& proof) + Transcript(const std::vector& proof) : BaseTranscript(proof) {} void deserialize_full_transcript() { - size_t num_bytes_read = 0; - circuit_size = deserialize_from_buffer(proof_data, num_bytes_read); + size_t num_frs_read = 0; + circuit_size = deserialize_from_buffer(proof_data, num_frs_read); size_t log_n = numeric::get_msb(circuit_size); - toy_q_tuple_set = deserialize_from_buffer(Transcript::proof_data, num_bytes_read); - toy_set_1_column_1 = deserialize_from_buffer(Transcript::proof_data, num_bytes_read); - toy_set_1_column_2 = deserialize_from_buffer(Transcript::proof_data, num_bytes_read); - toy_set_2_column_1 = deserialize_from_buffer(Transcript::proof_data, num_bytes_read); - toy_set_2_column_2 = deserialize_from_buffer(Transcript::proof_data, num_bytes_read); - toy_xor_a = deserialize_from_buffer(Transcript::proof_data, num_bytes_read); - toy_xor_b = deserialize_from_buffer(Transcript::proof_data, num_bytes_read); - toy_xor_c = deserialize_from_buffer(Transcript::proof_data, num_bytes_read); - toy_table_xor_a = deserialize_from_buffer(Transcript::proof_data, num_bytes_read); - toy_table_xor_b = deserialize_from_buffer(Transcript::proof_data, num_bytes_read); - toy_table_xor_c = deserialize_from_buffer(Transcript::proof_data, num_bytes_read); - toy_q_xor = deserialize_from_buffer(Transcript::proof_data, num_bytes_read); - toy_q_xor_table = deserialize_from_buffer(Transcript::proof_data, num_bytes_read); - two_column_perm = deserialize_from_buffer(Transcript::proof_data, num_bytes_read); - lookup_xor = deserialize_from_buffer(Transcript::proof_data, num_bytes_read); - lookup_xor_counts = deserialize_from_buffer(Transcript::proof_data, num_bytes_read); + toy_q_tuple_set = deserialize_from_buffer(Transcript::proof_data, num_frs_read); + toy_set_1_column_1 = deserialize_from_buffer(Transcript::proof_data, num_frs_read); + toy_set_1_column_2 = deserialize_from_buffer(Transcript::proof_data, num_frs_read); + toy_set_2_column_1 = deserialize_from_buffer(Transcript::proof_data, num_frs_read); + toy_set_2_column_2 = deserialize_from_buffer(Transcript::proof_data, num_frs_read); + toy_xor_a = deserialize_from_buffer(Transcript::proof_data, num_frs_read); + toy_xor_b = deserialize_from_buffer(Transcript::proof_data, num_frs_read); + toy_xor_c = deserialize_from_buffer(Transcript::proof_data, num_frs_read); + toy_table_xor_a = deserialize_from_buffer(Transcript::proof_data, num_frs_read); + toy_table_xor_b = deserialize_from_buffer(Transcript::proof_data, num_frs_read); + toy_table_xor_c = deserialize_from_buffer(Transcript::proof_data, num_frs_read); + toy_q_xor = deserialize_from_buffer(Transcript::proof_data, num_frs_read); + toy_q_xor_table = deserialize_from_buffer(Transcript::proof_data, num_frs_read); + two_column_perm = deserialize_from_buffer(Transcript::proof_data, num_frs_read); + lookup_xor = deserialize_from_buffer(Transcript::proof_data, num_frs_read); + lookup_xor_counts = deserialize_from_buffer(Transcript::proof_data, num_frs_read); for (size_t i = 0; i < log_n; ++i) { sumcheck_univariates.emplace_back( deserialize_from_buffer>(Transcript::proof_data, - num_bytes_read)); + num_frs_read)); } sumcheck_evaluations = - deserialize_from_buffer>(Transcript::proof_data, num_bytes_read); + deserialize_from_buffer>(Transcript::proof_data, num_frs_read); for (size_t i = 0; i < log_n; ++i) { - zm_cq_comms.push_back(deserialize_from_buffer(proof_data, num_bytes_read)); + zm_cq_comms.push_back(deserialize_from_buffer(proof_data, num_frs_read)); } - zm_cq_comm = deserialize_from_buffer(proof_data, num_bytes_read); - zm_pi_comm = deserialize_from_buffer(proof_data, num_bytes_read); + zm_cq_comm = deserialize_from_buffer(proof_data, num_frs_read); + zm_pi_comm = deserialize_from_buffer(proof_data, num_frs_read); } void serialize_full_transcript() diff --git a/barretenberg/cpp/src/barretenberg/flavor/goblin_translator.hpp b/barretenberg/cpp/src/barretenberg/flavor/goblin_translator.hpp index 062c0bf4814..f18a480f176 100644 --- a/barretenberg/cpp/src/barretenberg/flavor/goblin_translator.hpp +++ b/barretenberg/cpp/src/barretenberg/flavor/goblin_translator.hpp @@ -44,13 +44,13 @@ class GoblinTranslator { // None of this parameters can be changed // How many mini_circuit_size polynomials are concatenated in one concatenated_* - static constexpr size_t CONCATENATION_INDEX = 16; + static constexpr size_t CONCATENATION_GROUP_SIZE = 16; // The number of concatenated_* wires static constexpr size_t NUM_CONCATENATED_WIRES = 4; // Actual circuit size - static constexpr size_t FULL_CIRCUIT_SIZE = MINI_CIRCUIT_SIZE * CONCATENATION_INDEX; + static constexpr size_t FULL_CIRCUIT_SIZE = MINI_CIRCUIT_SIZE * CONCATENATION_GROUP_SIZE; // Number of wires static constexpr size_t NUM_WIRES = CircuitBuilder::NUM_WIRES; diff --git a/barretenberg/cpp/src/barretenberg/flavor/goblin_ultra.hpp b/barretenberg/cpp/src/barretenberg/flavor/goblin_ultra.hpp index 8629356b726..ed247351c52 100644 --- a/barretenberg/cpp/src/barretenberg/flavor/goblin_ultra.hpp +++ b/barretenberg/cpp/src/barretenberg/flavor/goblin_ultra.hpp @@ -3,6 +3,7 @@ #include "barretenberg/common/ref_vector.hpp" #include "barretenberg/flavor/flavor.hpp" #include "barretenberg/flavor/flavor_macros.hpp" +#include "barretenberg/honk/proof_system/types/proof.hpp" #include "barretenberg/polynomials/univariate.hpp" #include "barretenberg/proof_system/circuit_builder/goblin_ultra_circuit_builder.hpp" #include "barretenberg/relations/auxiliary_relation.hpp" @@ -155,7 +156,7 @@ class GoblinUltra { // GoblinUltra needs to expose more public classes than most flavors due to GoblinUltraRecursive reuse, but these // are internal: - private: + public: // WireEntities for basic witness entities template class WireEntities { public: @@ -418,42 +419,8 @@ class GoblinUltra { template class VerifierCommitments_ : public AllEntities { public: - VerifierCommitments_(const std::shared_ptr& verification_key) - { - this->q_m = verification_key->q_m; - this->q_l = verification_key->q_l; - this->q_r = verification_key->q_r; - this->q_o = verification_key->q_o; - this->q_4 = verification_key->q_4; - this->q_c = verification_key->q_c; - this->q_arith = verification_key->q_arith; - this->q_sort = verification_key->q_sort; - this->q_elliptic = verification_key->q_elliptic; - this->q_aux = verification_key->q_aux; - this->q_lookup = verification_key->q_lookup; - this->q_busread = verification_key->q_busread; - this->q_poseidon2_external = verification_key->q_poseidon2_external; - this->q_poseidon2_internal = verification_key->q_poseidon2_internal; - this->sigma_1 = verification_key->sigma_1; - this->sigma_2 = verification_key->sigma_2; - this->sigma_3 = verification_key->sigma_3; - this->sigma_4 = verification_key->sigma_4; - this->id_1 = verification_key->id_1; - this->id_2 = verification_key->id_2; - this->id_3 = verification_key->id_3; - this->id_4 = verification_key->id_4; - this->table_1 = verification_key->table_1; - this->table_2 = verification_key->table_2; - this->table_3 = verification_key->table_3; - this->table_4 = verification_key->table_4; - this->lagrange_first = verification_key->lagrange_first; - this->lagrange_last = verification_key->lagrange_last; - this->lagrange_ecc_op = verification_key->lagrange_ecc_op; - this->databus_id = verification_key->databus_id; - } - VerifierCommitments_(const std::shared_ptr& verification_key, - const WitnessCommitments& witness_commitments) + const std::optional>& witness_commitments = std::nullopt) { this->q_m = verification_key->q_m; this->q_l = verification_key->q_l; @@ -486,19 +453,22 @@ class GoblinUltra { this->lagrange_ecc_op = verification_key->lagrange_ecc_op; this->databus_id = verification_key->databus_id; - this->w_l = witness_commitments.w_l; - this->w_r = witness_commitments.w_r; - this->w_o = witness_commitments.w_o; - this->sorted_accum = witness_commitments.sorted_accum; - this->w_4 = witness_commitments.w_4; - this->z_perm = witness_commitments.z_perm; - this->z_lookup = witness_commitments.z_lookup; - this->ecc_op_wire_1 = witness_commitments.ecc_op_wire_1; - this->ecc_op_wire_2 = witness_commitments.ecc_op_wire_2; - this->ecc_op_wire_3 = witness_commitments.ecc_op_wire_3; - this->calldata = witness_commitments.calldata; - this->calldata = witness_commitments.calldata_read_counts; - this->lookup_inverses = witness_commitments.lookup_inverses; + if (witness_commitments.has_value()) { + auto commitments = witness_commitments.value(); + this->w_l = commitments.w_l; + this->w_r = commitments.w_r; + this->w_o = commitments.w_o; + this->sorted_accum = commitments.sorted_accum; + this->w_4 = commitments.w_4; + this->z_perm = commitments.z_perm; + this->z_lookup = commitments.z_lookup; + this->ecc_op_wire_1 = commitments.ecc_op_wire_1; + this->ecc_op_wire_2 = commitments.ecc_op_wire_2; + this->ecc_op_wire_3 = commitments.ecc_op_wire_3; + this->calldata = commitments.calldata; + this->calldata = commitments.calldata_read_counts; + this->lookup_inverses = commitments.lookup_inverses; + } } }; // Specialize for GoblinUltra (general case used in GoblinUltraRecursive). @@ -536,48 +506,47 @@ class GoblinUltra { Transcript_() = default; - Transcript_(const std::vector& proof) + Transcript_(const honk::proof& proof) : BaseTranscript(proof) {} void deserialize_full_transcript() { // take current proof and put them into the struct - size_t num_bytes_read = 0; - circuit_size = deserialize_from_buffer(proof_data, num_bytes_read); + size_t num_frs_read = 0; + circuit_size = deserialize_from_buffer(proof_data, num_frs_read); size_t log_n = numeric::get_msb(circuit_size); - public_input_size = deserialize_from_buffer(proof_data, num_bytes_read); - pub_inputs_offset = deserialize_from_buffer(proof_data, num_bytes_read); + public_input_size = deserialize_from_buffer(proof_data, num_frs_read); + pub_inputs_offset = deserialize_from_buffer(proof_data, num_frs_read); for (size_t i = 0; i < public_input_size; ++i) { - public_inputs.push_back(deserialize_from_buffer(proof_data, num_bytes_read)); + public_inputs.push_back(deserialize_from_buffer(proof_data, num_frs_read)); } - w_l_comm = deserialize_from_buffer(proof_data, num_bytes_read); - w_r_comm = deserialize_from_buffer(proof_data, num_bytes_read); - w_o_comm = deserialize_from_buffer(proof_data, num_bytes_read); - ecc_op_wire_1_comm = deserialize_from_buffer(proof_data, num_bytes_read); - ecc_op_wire_2_comm = deserialize_from_buffer(proof_data, num_bytes_read); - ecc_op_wire_3_comm = deserialize_from_buffer(proof_data, num_bytes_read); - ecc_op_wire_4_comm = deserialize_from_buffer(proof_data, num_bytes_read); - calldata_comm = deserialize_from_buffer(proof_data, num_bytes_read); - calldata_read_counts_comm = deserialize_from_buffer(proof_data, num_bytes_read); - lookup_inverses_comm = deserialize_from_buffer(proof_data, num_bytes_read); - sorted_accum_comm = deserialize_from_buffer(proof_data, num_bytes_read); - w_4_comm = deserialize_from_buffer(proof_data, num_bytes_read); - z_perm_comm = deserialize_from_buffer(proof_data, num_bytes_read); - z_lookup_comm = deserialize_from_buffer(proof_data, num_bytes_read); + w_l_comm = deserialize_from_buffer(proof_data, num_frs_read); + w_r_comm = deserialize_from_buffer(proof_data, num_frs_read); + w_o_comm = deserialize_from_buffer(proof_data, num_frs_read); + ecc_op_wire_1_comm = deserialize_from_buffer(proof_data, num_frs_read); + ecc_op_wire_2_comm = deserialize_from_buffer(proof_data, num_frs_read); + ecc_op_wire_3_comm = deserialize_from_buffer(proof_data, num_frs_read); + ecc_op_wire_4_comm = deserialize_from_buffer(proof_data, num_frs_read); + calldata_comm = deserialize_from_buffer(proof_data, num_frs_read); + calldata_read_counts_comm = deserialize_from_buffer(proof_data, num_frs_read); + lookup_inverses_comm = deserialize_from_buffer(proof_data, num_frs_read); + sorted_accum_comm = deserialize_from_buffer(proof_data, num_frs_read); + w_4_comm = deserialize_from_buffer(proof_data, num_frs_read); + z_perm_comm = deserialize_from_buffer(proof_data, num_frs_read); + z_lookup_comm = deserialize_from_buffer(proof_data, num_frs_read); for (size_t i = 0; i < log_n; ++i) { sumcheck_univariates.push_back( deserialize_from_buffer>(proof_data, - num_bytes_read)); + num_frs_read)); } - sumcheck_evaluations = - deserialize_from_buffer>(proof_data, num_bytes_read); + sumcheck_evaluations = deserialize_from_buffer>(proof_data, num_frs_read); for (size_t i = 0; i < log_n; ++i) { - zm_cq_comms.push_back(deserialize_from_buffer(proof_data, num_bytes_read)); + zm_cq_comms.push_back(deserialize_from_buffer(proof_data, num_frs_read)); } - zm_cq_comm = deserialize_from_buffer(proof_data, num_bytes_read); - zm_pi_comm = deserialize_from_buffer(proof_data, num_bytes_read); + zm_cq_comm = deserialize_from_buffer(proof_data, num_frs_read); + zm_pi_comm = deserialize_from_buffer(proof_data, num_frs_read); } void serialize_full_transcript() diff --git a/barretenberg/cpp/src/barretenberg/flavor/goblin_ultra_recursive.hpp b/barretenberg/cpp/src/barretenberg/flavor/goblin_ultra_recursive.hpp index 8c47654f6fe..e1146ea4b70 100644 --- a/barretenberg/cpp/src/barretenberg/flavor/goblin_ultra_recursive.hpp +++ b/barretenberg/cpp/src/barretenberg/flavor/goblin_ultra_recursive.hpp @@ -67,12 +67,14 @@ template class GoblinUltraRecursive_ { using Relations = GoblinUltra::Relations_; static constexpr size_t MAX_PARTIAL_RELATION_LENGTH = compute_max_partial_relation_length(); + static constexpr size_t MAX_TOTAL_RELATION_LENGTH = compute_max_total_relation_length(); // BATCHED_RELATION_PARTIAL_LENGTH = algebraic degree of sumcheck relation *after* multiplying by the `pow_zeta` // random polynomial e.g. For \sum(x) [A(x) * B(x) + C(x)] * PowZeta(X), relation length = 2 and random relation // length = 3 static constexpr size_t BATCHED_RELATION_PARTIAL_LENGTH = MAX_PARTIAL_RELATION_LENGTH + 1; - static constexpr size_t NUM_RELATIONS = std::tuple_size::value; + static constexpr size_t BATCHED_RELATION_TOTAL_LENGTH = MAX_TOTAL_RELATION_LENGTH + 1; + static constexpr size_t NUM_RELATIONS = std::tuple_size_v; // For instances of this flavour, used in folding, we need a unique sumcheck batching challenge for each // subrelation. This is because using powers of alpha would increase the degree of Protogalaxy polynomial $G$ (the @@ -104,6 +106,12 @@ template class GoblinUltraRecursive_ { */ class VerificationKey : public VerificationKey_> { public: + VerificationKey(const size_t circuit_size, const size_t num_public_inputs) + { + this->circuit_size = circuit_size; + this->log_circuit_size = numeric::get_msb(circuit_size); + this->num_public_inputs = num_public_inputs; + }; /** * @brief Construct a new Verification Key with stdlib types from a provided native verification * key @@ -112,9 +120,10 @@ template class GoblinUltraRecursive_ { * @param native_key Native verification key from which to extract the precomputed commitments */ VerificationKey(CircuitBuilder* builder, const std::shared_ptr& native_key) - : VerificationKey_>(native_key->circuit_size, - native_key->num_public_inputs) { + this->circuit_size = native_key->circuit_size; + this->log_circuit_size = numeric::get_msb(this->circuit_size); + this->num_public_inputs = native_key->num_public_inputs; this->q_m = Commitment::from_witness(builder, native_key->q_m); this->q_l = Commitment::from_witness(builder, native_key->q_l); this->q_r = Commitment::from_witness(builder, native_key->q_r); @@ -148,6 +157,11 @@ template class GoblinUltraRecursive_ { }; }; + /** + * @brief A container for the witness commitments. + */ + using WitnessCommitments = GoblinUltra::WitnessEntities; + using CommitmentLabels = GoblinUltra::CommitmentLabels; // Reuse the VerifierCommitments from GoblinUltra using VerifierCommitments = GoblinUltra::VerifierCommitments_; diff --git a/barretenberg/cpp/src/barretenberg/flavor/ultra.hpp b/barretenberg/cpp/src/barretenberg/flavor/ultra.hpp index 3bf08e29b8f..466cac1c869 100644 --- a/barretenberg/cpp/src/barretenberg/flavor/ultra.hpp +++ b/barretenberg/cpp/src/barretenberg/flavor/ultra.hpp @@ -412,37 +412,8 @@ class Ultra { */ class VerifierCommitments : public AllEntities { public: - VerifierCommitments(const std::shared_ptr& verification_key) - { - q_m = verification_key->q_m; - q_c = verification_key->q_c; - q_l = verification_key->q_l; - q_r = verification_key->q_r; - q_o = verification_key->q_o; - q_4 = verification_key->q_4; - q_arith = verification_key->q_arith; - q_sort = verification_key->q_sort; - q_elliptic = verification_key->q_elliptic; - q_aux = verification_key->q_aux; - q_lookup = verification_key->q_lookup; - sigma_1 = verification_key->sigma_1; - sigma_2 = verification_key->sigma_2; - sigma_3 = verification_key->sigma_3; - sigma_4 = verification_key->sigma_4; - id_1 = verification_key->id_1; - id_2 = verification_key->id_2; - id_3 = verification_key->id_3; - id_4 = verification_key->id_4; - table_1 = verification_key->table_1; - table_2 = verification_key->table_2; - table_3 = verification_key->table_3; - table_4 = verification_key->table_4; - lagrange_first = verification_key->lagrange_first; - lagrange_last = verification_key->lagrange_last; - } - VerifierCommitments(const std::shared_ptr& verification_key, - const WitnessCommitments& witness_commitments) + const std::optional& witness_commitments = std::nullopt) { q_m = verification_key->q_m; q_c = verification_key->q_c; @@ -470,13 +441,16 @@ class Ultra { lagrange_first = verification_key->lagrange_first; lagrange_last = verification_key->lagrange_last; - w_l = witness_commitments.w_l; - w_r = witness_commitments.w_r; - w_o = witness_commitments.w_o; - sorted_accum = witness_commitments.sorted_accum; - w_4 = witness_commitments.w_4; - z_perm = witness_commitments.z_perm; - z_lookup = witness_commitments.z_lookup; + if (witness_commitments.has_value()) { + auto commitments = witness_commitments.value(); + this->w_l = commitments.w_l; + this->w_r = commitments.w_r; + this->w_o = commitments.w_o; + this->sorted_accum = commitments.sorted_accum; + this->w_4 = commitments.w_4; + this->z_perm = commitments.z_perm; + this->z_lookup = commitments.z_lookup; + } } }; @@ -507,7 +481,7 @@ class Ultra { Transcript() = default; // Used by verifier to initialize the transcript - Transcript(const std::vector& proof) + Transcript(const std::vector& proof) : BaseTranscript(proof) {} @@ -534,34 +508,33 @@ class Ultra { void deserialize_full_transcript() { // take current proof and put them into the struct - size_t num_bytes_read = 0; - circuit_size = deserialize_from_buffer(proof_data, num_bytes_read); + size_t num_frs_read = 0; + circuit_size = deserialize_from_buffer(proof_data, num_frs_read); size_t log_n = numeric::get_msb(circuit_size); - public_input_size = deserialize_from_buffer(proof_data, num_bytes_read); - pub_inputs_offset = deserialize_from_buffer(proof_data, num_bytes_read); + public_input_size = deserialize_from_buffer(proof_data, num_frs_read); + pub_inputs_offset = deserialize_from_buffer(proof_data, num_frs_read); for (size_t i = 0; i < public_input_size; ++i) { - public_inputs.push_back(deserialize_from_buffer(proof_data, num_bytes_read)); + public_inputs.push_back(deserialize_from_buffer(proof_data, num_frs_read)); } - w_l_comm = deserialize_from_buffer(proof_data, num_bytes_read); - w_r_comm = deserialize_from_buffer(proof_data, num_bytes_read); - w_o_comm = deserialize_from_buffer(proof_data, num_bytes_read); - sorted_accum_comm = deserialize_from_buffer(proof_data, num_bytes_read); - w_4_comm = deserialize_from_buffer(proof_data, num_bytes_read); - z_perm_comm = deserialize_from_buffer(proof_data, num_bytes_read); - z_lookup_comm = deserialize_from_buffer(proof_data, num_bytes_read); + w_l_comm = deserialize_from_buffer(proof_data, num_frs_read); + w_r_comm = deserialize_from_buffer(proof_data, num_frs_read); + w_o_comm = deserialize_from_buffer(proof_data, num_frs_read); + sorted_accum_comm = deserialize_from_buffer(proof_data, num_frs_read); + w_4_comm = deserialize_from_buffer(proof_data, num_frs_read); + z_perm_comm = deserialize_from_buffer(proof_data, num_frs_read); + z_lookup_comm = deserialize_from_buffer(proof_data, num_frs_read); for (size_t i = 0; i < log_n; ++i) { sumcheck_univariates.push_back( deserialize_from_buffer>(proof_data, - num_bytes_read)); + num_frs_read)); } - sumcheck_evaluations = - deserialize_from_buffer>(proof_data, num_bytes_read); + sumcheck_evaluations = deserialize_from_buffer>(proof_data, num_frs_read); for (size_t i = 0; i < log_n; ++i) { - zm_cq_comms.push_back(deserialize_from_buffer(proof_data, num_bytes_read)); + zm_cq_comms.push_back(deserialize_from_buffer(proof_data, num_frs_read)); } - zm_cq_comm = deserialize_from_buffer(proof_data, num_bytes_read); - zm_pi_comm = deserialize_from_buffer(proof_data, num_bytes_read); + zm_cq_comm = deserialize_from_buffer(proof_data, num_frs_read); + zm_pi_comm = deserialize_from_buffer(proof_data, num_frs_read); } /** * @brief Serializes the structure variables into a FULL Ultra proof. Should be called only if diff --git a/barretenberg/cpp/src/barretenberg/flavor/ultra_recursive.hpp b/barretenberg/cpp/src/barretenberg/flavor/ultra_recursive.hpp index ea9303c7c87..94955d72f75 100644 --- a/barretenberg/cpp/src/barretenberg/flavor/ultra_recursive.hpp +++ b/barretenberg/cpp/src/barretenberg/flavor/ultra_recursive.hpp @@ -79,11 +79,15 @@ template class UltraRecursive_ { bb::AuxiliaryRelation>; static constexpr size_t MAX_PARTIAL_RELATION_LENGTH = compute_max_partial_relation_length(); + static_assert(MAX_PARTIAL_RELATION_LENGTH == 6); + static constexpr size_t MAX_TOTAL_RELATION_LENGTH = compute_max_total_relation_length(); + static_assert(MAX_TOTAL_RELATION_LENGTH == 12); // BATCHED_RELATION_PARTIAL_LENGTH = algebraic degree of sumcheck relation *after* multiplying by the `pow_zeta` // random polynomial e.g. For \sum(x) [A(x) * B(x) + C(x)] * PowZeta(X), relation length = 2 and random relation // length = 3 static constexpr size_t BATCHED_RELATION_PARTIAL_LENGTH = MAX_PARTIAL_RELATION_LENGTH + 1; + static constexpr size_t BATCHED_RELATION_TOTAL_LENGTH = MAX_TOTAL_RELATION_LENGTH + 1; static constexpr size_t NUM_RELATIONS = std::tuple_size::value; // For instances of this flavour, used in folding, we need a unique sumcheck batching challenges for each @@ -161,6 +165,12 @@ template class UltraRecursive_ { RefVector get_wires() { return { w_l, w_r, w_o, w_4 }; }; }; + public: + /** + * @brief A container for the witness commitments. + */ + using WitnessCommitments = WitnessEntities; + /** * @brief A base class labelling all entities (for instance, all of the polynomials used by the prover during * sumcheck) in this Honk variant along with particular subsets of interest @@ -229,6 +239,17 @@ template class UltraRecursive_ { }; }; + RefVector get_precomputed() + { + return { q_m, q_c, q_l, q_r, q_o, q_4, q_arith, q_sort, + q_elliptic, q_aux, q_lookup, sigma_1, sigma_2, sigma_3, sigma_4, id_1, + id_2, id_3, id_4, table_1, table_2, table_3, table_4, lagrange_first, + lagrange_last + + }; + } + + RefVector get_witness() { return { w_l, w_r, w_o, w_4, sorted_accum, z_perm, z_lookup }; }; RefVector get_to_be_shifted() { return { table_1, table_2, table_3, table_4, w_l, w_r, w_o, w_4, sorted_accum, z_perm, z_lookup }; @@ -251,6 +272,12 @@ template class UltraRecursive_ { */ class VerificationKey : public VerificationKey_> { public: + VerificationKey(const size_t circuit_size, const size_t num_public_inputs) + { + this->circuit_size = circuit_size; + this->log_circuit_size = numeric::get_msb(circuit_size); + this->num_public_inputs = num_public_inputs; + }; /** * @brief Construct a new Verification Key with stdlib types from a provided native verification key * @@ -258,8 +285,10 @@ template class UltraRecursive_ { * @param native_key Native verification key from which to extract the precomputed commitments */ VerificationKey(CircuitBuilder* builder, const std::shared_ptr& native_key) - : VerificationKey_>(native_key->circuit_size, native_key->num_public_inputs) { + this->circuit_size = native_key->circuit_size; + this->log_circuit_size = numeric::get_msb(this->circuit_size); + this->num_public_inputs = native_key->num_public_inputs; this->q_m = Commitment::from_witness(builder, native_key->q_m); this->q_l = Commitment::from_witness(builder, native_key->q_l); this->q_r = Commitment::from_witness(builder, native_key->q_r); @@ -317,38 +346,38 @@ template class UltraRecursive_ { this->z_lookup = "Z_LOOKUP"; this->sorted_accum = "SORTED_ACCUM"; - // The ones beginning with "__" are only used for debugging - this->q_c = "__Q_C"; - this->q_l = "__Q_L"; - this->q_r = "__Q_R"; - this->q_o = "__Q_O"; - this->q_4 = "__Q_4"; - this->q_m = "__Q_M"; - this->q_arith = "__Q_ARITH"; - this->q_sort = "__Q_SORT"; - this->q_elliptic = "__Q_ELLIPTIC"; - this->q_aux = "__Q_AUX"; - this->q_lookup = "__Q_LOOKUP"; - this->sigma_1 = "__SIGMA_1"; - this->sigma_2 = "__SIGMA_2"; - this->sigma_3 = "__SIGMA_3"; - this->sigma_4 = "__SIGMA_4"; - this->id_1 = "__ID_1"; - this->id_2 = "__ID_2"; - this->id_3 = "__ID_3"; - this->id_4 = "__ID_4"; - this->table_1 = "__TABLE_1"; - this->table_2 = "__TABLE_2"; - this->table_3 = "__TABLE_3"; - this->table_4 = "__TABLE_4"; - this->lagrange_first = "__LAGRANGE_FIRST"; - this->lagrange_last = "__LAGRANGE_LAST"; + this->q_c = "Q_C"; + this->q_l = "Q_L"; + this->q_r = "Q_R"; + this->q_o = "Q_O"; + this->q_4 = "Q_4"; + this->q_m = "Q_M"; + this->q_arith = "Q_ARITH"; + this->q_sort = "Q_SORT"; + this->q_elliptic = "Q_ELLIPTIC"; + this->q_aux = "Q_AUX"; + this->q_lookup = "Q_LOOKUP"; + this->sigma_1 = "SIGMA_1"; + this->sigma_2 = "SIGMA_2"; + this->sigma_3 = "SIGMA_3"; + this->sigma_4 = "SIGMA_4"; + this->id_1 = "ID_1"; + this->id_2 = "ID_2"; + this->id_3 = "ID_3"; + this->id_4 = "ID_4"; + this->table_1 = "TABLE_1"; + this->table_2 = "TABLE_2"; + this->table_3 = "TABLE_3"; + this->table_4 = "TABLE_4"; + this->lagrange_first = "LAGRANGE_FIRST"; + this->lagrange_last = "LAGRANGE_LAST"; }; }; class VerifierCommitments : public AllEntities { public: - VerifierCommitments(const std::shared_ptr& verification_key) + VerifierCommitments(const std::shared_ptr& verification_key, + const std::optional& witness_commitments = std::nullopt) { this->q_m = verification_key->q_m; this->q_l = verification_key->q_l; @@ -375,6 +404,17 @@ template class UltraRecursive_ { this->table_4 = verification_key->table_4; this->lagrange_first = verification_key->lagrange_first; this->lagrange_last = verification_key->lagrange_last; + + if (witness_commitments.has_value()) { + auto commitments = witness_commitments.value(); + this->w_l = commitments.w_l; + this->w_r = commitments.w_r; + this->w_o = commitments.w_o; + this->sorted_accum = commitments.sorted_accum; + this->w_4 = commitments.w_4; + this->z_perm = commitments.z_perm; + this->z_lookup = commitments.z_lookup; + } } }; diff --git a/barretenberg/cpp/src/barretenberg/goblin/CMakeLists.txt b/barretenberg/cpp/src/barretenberg/goblin/CMakeLists.txt index adaa9814aed..ee4b04affe8 100644 --- a/barretenberg/cpp/src/barretenberg/goblin/CMakeLists.txt +++ b/barretenberg/cpp/src/barretenberg/goblin/CMakeLists.txt @@ -1 +1 @@ -barretenberg_module(goblin stdlib_recursion ultra_honk eccvm translator_vm) \ No newline at end of file +barretenberg_module(goblin stdlib_recursion ultra_honk eccvm translator_vm stdlib_sha256 stdlib_merkle_tree stdlib_primitives) \ No newline at end of file diff --git a/barretenberg/cpp/src/barretenberg/goblin/full_goblin_recursion.test.cpp b/barretenberg/cpp/src/barretenberg/goblin/full_goblin_recursion.test.cpp index 5049b197e0a..04968199532 100644 --- a/barretenberg/cpp/src/barretenberg/goblin/full_goblin_recursion.test.cpp +++ b/barretenberg/cpp/src/barretenberg/goblin/full_goblin_recursion.test.cpp @@ -44,7 +44,7 @@ TEST_F(GoblinRecursionTests, Pseudo) for (size_t circuit_idx = 0; circuit_idx < NUM_CIRCUITS; ++circuit_idx) { // Construct a circuit with logic resembling that of the "kernel circuit" GoblinUltraBuilder circuit_builder{ goblin.op_queue }; - GoblinMockCircuits::construct_mock_kernel_circuit(circuit_builder, kernel_input); + GoblinMockCircuits::construct_mock_kernel_circuit(circuit_builder, kernel_input, kernel_input); // Construct proof of the current kernel circuit to be recursively verified by the next one kernel_input = goblin.accumulate(circuit_builder); diff --git a/barretenberg/cpp/src/barretenberg/goblin/goblin.hpp b/barretenberg/cpp/src/barretenberg/goblin/goblin.hpp index aac0ef305ca..4913695c918 100644 --- a/barretenberg/cpp/src/barretenberg/goblin/goblin.hpp +++ b/barretenberg/cpp/src/barretenberg/goblin/goblin.hpp @@ -12,8 +12,7 @@ namespace bb { class Goblin { - using HonkProof = bb::plonk::proof; - + using HonkProof = bb::honk::proof; using GUHFlavor = bb::honk::flavor::GoblinUltra; using GoblinUltraCircuitBuilder = bb::GoblinUltraCircuitBuilder; @@ -22,6 +21,21 @@ class Goblin { using FF = GUHFlavor::FF; public: + using Builder = GoblinUltraCircuitBuilder; + using Fr = bb::fr; + using Transcript = bb::honk::BaseTranscript; + + using GoblinUltraComposer = bb::honk::UltraComposer_; + using GoblinUltraVerifier = bb::honk::UltraVerifier_; + using OpQueue = bb::ECCOpQueue; + using ECCVMFlavor = bb::honk::flavor::ECCVM; + using ECCVMBuilder = bb::ECCVMCircuitBuilder; + using ECCVMComposer = bb::honk::ECCVMComposer; + using ECCVMProver = bb::honk::ECCVMProver_; + using TranslatorBuilder = bb::GoblinTranslatorCircuitBuilder; + using TranslatorComposer = bb::honk::GoblinTranslatorComposer; + using RecursiveMergeVerifier = bb::stdlib::recursion::goblin::MergeRecursiveVerifier_; + using MergeVerifier = bb::honk::MergeVerifier_; /** * @brief Output of goblin::accumulate; an Ultra proof and the corresponding verification key * @@ -36,38 +50,25 @@ class Goblin { HonkProof eccvm_proof; HonkProof translator_proof; TranslationEvaluations translation_evaluations; - std::vector to_buffer() + std::vector to_buffer() { // ACIRHACK: so much copying and duplication added here and elsewhere - std::vector translation_evaluations_buf = translation_evaluations.to_buffer(); - size_t proof_size = merge_proof.proof_data.size() + eccvm_proof.proof_data.size() + - translator_proof.proof_data.size() + translation_evaluations_buf.size(); + std::vector translation_evaluations_buf; // = translation_evaluations.to_buffer(); + size_t proof_size = + merge_proof.size() + eccvm_proof.size() + translator_proof.size() + translation_evaluations_buf.size(); - std::vector result(proof_size); - const auto insert = [&result](const std::vector& buf) { + std::vector result(proof_size); + const auto insert = [&result](const std::vector& buf) { result.insert(result.end(), buf.begin(), buf.end()); }; - insert(merge_proof.proof_data); - insert(eccvm_proof.proof_data); - insert(translator_proof.proof_data); + insert(merge_proof); + insert(eccvm_proof); + insert(translator_proof); insert(translation_evaluations_buf); return result; } }; - using GoblinUltraComposer = bb::honk::UltraComposer_; - using GoblinUltraVerifier = bb::honk::UltraVerifier_; - using Builder = GoblinUltraCircuitBuilder; - using OpQueue = bb::ECCOpQueue; - using ECCVMFlavor = bb::honk::flavor::ECCVM; - using ECCVMBuilder = bb::ECCVMCircuitBuilder; - using ECCVMComposer = bb::honk::ECCVMComposer; - using ECCVMProver = bb::honk::ECCVMProver_; - using TranslatorBuilder = bb::GoblinTranslatorCircuitBuilder; - using TranslatorComposer = bb::honk::GoblinTranslatorComposer; - using RecursiveMergeVerifier = bb::stdlib::recursion::goblin::MergeRecursiveVerifier_; - using MergeVerifier = bb::honk::MergeVerifier_; - std::shared_ptr op_queue = std::make_shared(); HonkProof merge_proof; @@ -190,9 +191,9 @@ class Goblin { * @brief Construct a GUH proof for the given circuit. (No merge proof for now) * * @param circuit_builder - * @return std::vector + * @return std::vector */ - std::vector accumulate_for_acir(GoblinUltraCircuitBuilder& circuit_builder) + std::vector accumulate_for_acir(GoblinUltraCircuitBuilder& circuit_builder) { // TODO(https://github.com/AztecProtocol/barretenberg/issues/811): no merge prover for now // // Complete the circuit logic by recursively verifying previous merge proof if it exists @@ -219,7 +220,7 @@ class Goblin { // merge_proof_exists = true; // } - return ultra_proof.proof_data; + return ultra_proof; }; /** @@ -229,7 +230,7 @@ class Goblin { * @return true * @return false */ - bool verify_accumulator_for_acir(const std::vector& proof_buf) const + bool verify_accumulator_for_acir(const std::vector& proof_buf) const { GoblinUltraVerifier verifier{ accumulator.verification_key }; HonkProof proof{ proof_buf }; diff --git a/barretenberg/cpp/src/barretenberg/goblin/mock_circuits.hpp b/barretenberg/cpp/src/barretenberg/goblin/mock_circuits.hpp index 78fd8903fc9..db98486ddbb 100644 --- a/barretenberg/cpp/src/barretenberg/goblin/mock_circuits.hpp +++ b/barretenberg/cpp/src/barretenberg/goblin/mock_circuits.hpp @@ -1,10 +1,18 @@ #pragma once #include "barretenberg/commitment_schemes/commitment_key.hpp" +#include "barretenberg/crypto/ecdsa/ecdsa.hpp" #include "barretenberg/flavor/goblin_ultra.hpp" #include "barretenberg/goblin/goblin.hpp" #include "barretenberg/proof_system/circuit_builder/goblin_ultra_circuit_builder.hpp" #include "barretenberg/srs/global_crs.hpp" +#include "barretenberg/stdlib/encryption/ecdsa/ecdsa.hpp" +#include "barretenberg/stdlib/hash/sha256/sha256.hpp" +#include "barretenberg/stdlib/merkle_tree/membership.hpp" +#include "barretenberg/stdlib/merkle_tree/memory_store.hpp" +#include "barretenberg/stdlib/merkle_tree/merkle_tree.hpp" +#include "barretenberg/stdlib/primitives/curves/secp256k1.hpp" +#include "barretenberg/stdlib/primitives/packed_byte_array/packed_byte_array.hpp" #include "barretenberg/stdlib/recursion/honk/verifier/ultra_recursive_verifier.hpp" namespace bb { @@ -23,10 +31,16 @@ class GoblinMockCircuits { using KernelInput = Goblin::AccumulationOutput; static constexpr size_t NUM_OP_QUEUE_COLUMNS = Flavor::NUM_WIRES; + /** + * @brief Populate a builder with a specified number of arithmetic gates; includes a PI + * + * @param builder + * @param num_gates + */ static void construct_arithmetic_circuit(GoblinUltraBuilder& builder, size_t num_gates = 1) { - // Add some arithmetic gates that utilize public inputs - for (size_t i = 0; i < num_gates; ++i) { + // For good measure, include a gate with some public inputs + { FF a = FF::random_element(); FF b = FF::random_element(); FF c = FF::random_element(); @@ -36,10 +50,28 @@ class GoblinMockCircuits { uint32_t c_idx = builder.add_variable(c); uint32_t d_idx = builder.add_variable(d); + builder.create_big_add_gate({ a_idx, b_idx, c_idx, d_idx, FF(1), FF(1), FF(1), FF(-1), FF(0) }); + } + // Add arbitrary arithmetic gates to obtain a total of num_gates-many gates + for (size_t i = 0; i < num_gates - 1; ++i) { + FF a = FF::random_element(); + FF b = FF::random_element(); + FF c = FF::random_element(); + FF d = a + b + c; + uint32_t a_idx = builder.add_variable(a); + uint32_t b_idx = builder.add_variable(b); + uint32_t c_idx = builder.add_variable(c); + uint32_t d_idx = builder.add_variable(d); + builder.create_big_add_gate({ a_idx, b_idx, c_idx, d_idx, FF(1), FF(1), FF(1), FF(-1), FF(0) }); } } + /** + * @brief Populate a builder with some arbitrary goblinized ECC ops + * + * @param builder + */ static void construct_goblin_ecc_op_circuit(GoblinUltraBuilder& builder) { // Add a mul accum op and an equality op @@ -49,14 +81,40 @@ class GoblinMockCircuits { builder.queue_ecc_eq(); } + /** + * @brief Populate a builder with some arbitrary but nontrivial constraints + * @details Although the details of the circuit constructed here are arbitrary, the intent is to mock something a + * bit more realistic than a circuit comprised entirely of arithmetic gates. E.g. the circuit should respond + * realistically to efforts to parallelize circuit construction. + * + * @param builder + * @param large If true, construct a "large" circuit (2^19), else a medium circuit (2^17) + */ + static void construct_mock_function_circuit(GoblinUltraBuilder& builder, bool large = false) + { + // Determine number of times to execute the below operations that constitute the mock circuit logic. Note that + // the circuit size does not scale linearly with number of iterations due to e.g. amortization of lookup costs + const size_t NUM_ITERATIONS_LARGE = 13; // results in circuit size 2^19 (521327 gates) + const size_t NUM_ITERATIONS_MEDIUM = 3; // results in circuit size 2^17 (124843 gates) + const size_t NUM_ITERATIONS = large ? NUM_ITERATIONS_LARGE : NUM_ITERATIONS_MEDIUM; + + stdlib::generate_sha256_test_circuit(builder, NUM_ITERATIONS); // min gates: ~39k + stdlib::generate_ecdsa_verification_test_circuit(builder, NUM_ITERATIONS); // min gates: ~41k + stdlib::generate_merkle_membership_test_circuit(builder, NUM_ITERATIONS); // min gates: ~29k + + // Note: its not clear whether goblin ops will be supported for function circuits initially but currently UGH + // can only be used if some op gates are included so for now we'll assume each function circuit has some. + construct_goblin_ecc_op_circuit(builder); + } + /** * @brief Mock the interactions of a simple curcuit with the op_queue * @todo The transcript aggregation protocol in the Goblin proof system can not yet support an empty "previous * transcript" (see issue #723) because the corresponding commitments are zero / the point at infinity. This * function mocks the interactions with the op queue of a fictional "first" circuit. This way, when we go to - * generate a proof over our first "real" circuit, the transcript aggregation protocol can proceed nominally. The - * mock data is valid in the sense that it can be processed by all stages of Goblin as if it came from a genuine - * circuit. + * generate a proof over our first "real" circuit, the transcript aggregation protocol can proceed nominally. + * The mock data is valid in the sense that it can be processed by all stages of Goblin as if it came from a + * genuine circuit. * * * @param op_queue @@ -102,27 +160,42 @@ class GoblinMockCircuits { // queues the result of the preceding ECC builder.queue_ecc_eq(); // should be eq and reset - construct_arithmetic_circuit(builder); + construct_arithmetic_circuit(builder, 1 << 10); } /** * @brief Construct a mock kernel circuit - * @details This circuit contains (1) some basic/arbitrary arithmetic gates, (2) a genuine recursive verification of - * the proof provided as input. It does not contain any other real kernel logic. + * @details This circuit contains (1) some arbitrary operations representing general kernel logic, (2) recursive + * verification of a function circuit proof, and optionally (3) recursive verification of a previous kernel circuit + * proof. The arbitrary kernel logic is structured to bring the final dyadic circuit size of the kernel to 2^17. * + * TODO(https://github.com/AztecProtocol/barretenberg/issues/801): Pairing point aggregation not implemented * @param builder - * @param kernel_input A proof to be recursively verified and the corresponding native verification key + * @param function_accum {proof, vkey} for function circuit to be recursively verified + * @param prev_kernel_accum {proof, vkey} for previous kernel circuit to be recursively verified */ - static void construct_mock_kernel_circuit(GoblinUltraBuilder& builder, KernelInput& kernel_input) + static void construct_mock_kernel_circuit(GoblinUltraBuilder& builder, + const KernelInput& function_accum, + const KernelInput& prev_kernel_accum) { - // Generic operations e.g. state updates (just arith gates for now) - GoblinMockCircuits::construct_arithmetic_circuit(builder, /*num_gates=*/1 << 4); - - // Execute recursive aggregation of previous kernel proof - RecursiveVerifier verifier{ &builder, kernel_input.verification_key }; - // TODO(https://github.com/AztecProtocol/barretenberg/issues/801): Aggregation - auto pairing_points = verifier.verify_proof(kernel_input.proof); // app function proof - pairing_points = verifier.verify_proof(kernel_input.proof); // previous kernel proof + // Add operations representing general kernel logic e.g. state updates. Note: these are structured to make the + // kernel "full" within the dyadic size 2^17 (130914 gates) + const size_t NUM_MERKLE_CHECKS = 45; + const size_t NUM_ECDSA_VERIFICATIONS = 1; + const size_t NUM_SHA_HASHES = 1; + stdlib::generate_merkle_membership_test_circuit(builder, NUM_MERKLE_CHECKS); + stdlib::generate_ecdsa_verification_test_circuit(builder, NUM_ECDSA_VERIFICATIONS); + stdlib::generate_sha256_test_circuit(builder, NUM_SHA_HASHES); + + // Execute recursive aggregation of function proof + RecursiveVerifier verifier1{ &builder, function_accum.verification_key }; + verifier1.verify_proof(function_accum.proof); + + // Execute recursive aggregation of previous kernel proof if one exists + if (!prev_kernel_accum.proof.empty()) { + RecursiveVerifier verifier2{ &builder, prev_kernel_accum.verification_key }; + verifier2.verify_proof(prev_kernel_accum.proof); + } } }; -} // namespace bb \ No newline at end of file +} // namespace bb diff --git a/barretenberg/cpp/src/barretenberg/goblin/mock_circuits_pinning.test.cpp b/barretenberg/cpp/src/barretenberg/goblin/mock_circuits_pinning.test.cpp new file mode 100644 index 00000000000..1135a559b92 --- /dev/null +++ b/barretenberg/cpp/src/barretenberg/goblin/mock_circuits_pinning.test.cpp @@ -0,0 +1,61 @@ +#include "barretenberg/goblin/goblin.hpp" +#include "barretenberg/goblin/mock_circuits.hpp" +#include "barretenberg/proof_system/circuit_builder/goblin_ultra_circuit_builder.hpp" +#include "barretenberg/ultra_honk/ultra_composer.hpp" +#include + +using namespace bb; +using namespace bb::honk; + +/** + * @brief For benchmarking, we want to be sure that our mocking functions create circuits of a known size. We control + * this, to the degree that matters for proof construction time, using these "pinning tests" that fix values. + * + */ +class MockCircuits : public ::testing::Test { + protected: + static void SetUpTestSuite() { srs::init_crs_factory("../srs_db/ignition"); } +}; + +TEST_F(MockCircuits, PinFunctionSizes) +{ + const auto run_test = [](bool large) { + Goblin goblin; + GoblinUltraCircuitBuilder app_circuit{ goblin.op_queue }; + GoblinMockCircuits::construct_mock_function_circuit(app_circuit, large); + GoblinUltraComposer composer; + auto instance = composer.create_instance(app_circuit); + if (large) { + EXPECT_EQ(instance->proving_key->log_circuit_size, 19); + } else { + EXPECT_EQ(instance->proving_key->log_circuit_size, 17); + }; + }; + run_test(true); + run_test(false); +} + +TEST_F(MockCircuits, PinKernelSizes) +{ + const auto run_test = [](bool large) { + { + Goblin goblin; + GoblinMockCircuits::perform_op_queue_interactions_for_mock_first_circuit(goblin.op_queue); + Goblin::AccumulationOutput kernel_accum; + GoblinUltraCircuitBuilder app_circuit{ goblin.op_queue }; + GoblinMockCircuits::construct_mock_function_circuit(app_circuit, large); + auto function_accum = goblin.accumulate(app_circuit); + GoblinUltraCircuitBuilder kernel_circuit{ goblin.op_queue }; + GoblinMockCircuits::construct_mock_kernel_circuit(kernel_circuit, function_accum, kernel_accum); + GoblinUltraComposer composer; + auto instance = composer.create_instance(kernel_circuit); + if (large) { + EXPECT_EQ(instance->proving_key->log_circuit_size, 17); + } else { + EXPECT_EQ(instance->proving_key->log_circuit_size, 17); + }; + } + }; + run_test(true); + run_test(false); +} \ No newline at end of file diff --git a/barretenberg/cpp/src/barretenberg/honk/CMakeLists.txt b/barretenberg/cpp/src/barretenberg/honk/CMakeLists.txt index a0e5fccbd8a..984909805e9 100644 --- a/barretenberg/cpp/src/barretenberg/honk/CMakeLists.txt +++ b/barretenberg/cpp/src/barretenberg/honk/CMakeLists.txt @@ -1 +1 @@ - barretenberg_module(honk proof_system commitment_schemes sumcheck) +barretenberg_module(honk polynomials) \ No newline at end of file diff --git a/barretenberg/cpp/src/barretenberg/honk/proof_system/permutation_library.hpp b/barretenberg/cpp/src/barretenberg/honk/proof_system/permutation_library.hpp index 31800d41bb8..6deaa03df7a 100644 --- a/barretenberg/cpp/src/barretenberg/honk/proof_system/permutation_library.hpp +++ b/barretenberg/cpp/src/barretenberg/honk/proof_system/permutation_library.hpp @@ -1,6 +1,5 @@ #pragma once #include "barretenberg/common/ref_vector.hpp" -#include "barretenberg/plonk/proof_system/proving_key/proving_key.hpp" #include "barretenberg/polynomials/polynomial.hpp" #include @@ -191,6 +190,10 @@ template void compute_concatenated_pol // Resulting concatenated polynomials auto targets = proving_key->get_concatenated_constraints(); + // Targets have to be full-sized polynomials. We can compute the mini circuit size from them by dividing by + // concatenation index + const size_t MINI_CIRCUIT_SIZE = targets[0].size() / Flavor::CONCATENATION_GROUP_SIZE; + ASSERT(MINI_CIRCUIT_SIZE * Flavor::CONCATENATION_GROUP_SIZE == targets[0].size()); // A function that produces 1 concatenated polynomial // TODO(#756): This can be rewritten to use more cores. Currently uses at maximum the number of concatenated // polynomials (4 in Goblin Translator) @@ -202,8 +205,8 @@ template void compute_concatenated_pol for (size_t j = 0; j < my_group.size(); j++) { auto starting_write_offset = current_target.begin(); auto finishing_read_offset = my_group[j].begin(); - std::advance(starting_write_offset, j * Flavor::MINI_CIRCUIT_SIZE); - std::advance(finishing_read_offset, Flavor::MINI_CIRCUIT_SIZE); + std::advance(starting_write_offset, j * MINI_CIRCUIT_SIZE); + std::advance(finishing_read_offset, MINI_CIRCUIT_SIZE); // Copy into appropriate position in the concatenated polynomial std::copy(my_group[j].begin(), finishing_read_offset, starting_write_offset); } @@ -234,7 +237,8 @@ template void compute_concatenated_pol * @param proving_key */ template -void compute_goblin_translator_range_constraint_ordered_polynomials(StorageHandle* proving_key) +void compute_goblin_translator_range_constraint_ordered_polynomials(StorageHandle* proving_key, + size_t mini_circuit_dyadic_size) { using FF = typename Flavor::FF; @@ -242,8 +246,8 @@ void compute_goblin_translator_range_constraint_ordered_polynomials(StorageHandl // Get constants constexpr auto sort_step = Flavor::SORT_STEP; constexpr auto num_concatenated_wires = Flavor::NUM_CONCATENATED_WIRES; - constexpr auto full_circuit_size = Flavor::FULL_CIRCUIT_SIZE; - constexpr auto mini_circuit_size = Flavor::MINI_CIRCUIT_SIZE; + const auto mini_circuit_size = mini_circuit_dyadic_size; + const auto full_circuit_size = mini_circuit_dyadic_size * Flavor::CONCATENATION_GROUP_SIZE; // The value we have to end polynomials with constexpr uint32_t max_value = (1 << Flavor::MICRO_LIMB_BITS) - 1; @@ -252,7 +256,7 @@ void compute_goblin_translator_range_constraint_ordered_polynomials(StorageHandl constexpr size_t sorted_elements_count = (max_value / sort_step) + 1 + (max_value % sort_step == 0 ? 0 : 1); // Check if we can construct these polynomials - static_assert((num_concatenated_wires + 1) * sorted_elements_count < full_circuit_size); + ASSERT((num_concatenated_wires + 1) * sorted_elements_count < full_circuit_size); // First use integers (easier to sort) std::vector sorted_elements(sorted_elements_count); @@ -279,7 +283,7 @@ void compute_goblin_translator_range_constraint_ordered_polynomials(StorageHandl // Get the group and the main target vector auto my_group = concatenation_groups[i]; auto& current_vector = ordered_vectors_uint[i]; - current_vector.resize(Flavor::FULL_CIRCUIT_SIZE); + current_vector.resize(full_circuit_size); // Calculate how much space there is for values from the original polynomials auto free_space_before_runway = full_circuit_size - sorted_elements_count; @@ -288,7 +292,7 @@ void compute_goblin_translator_range_constraint_ordered_polynomials(StorageHandl size_t extra_denominator_offset = i * sorted_elements_count; // Go through each polynomial in the concatenation group - for (size_t j = 0; j < Flavor::CONCATENATION_INDEX; j++) { + for (size_t j = 0; j < Flavor::CONCATENATION_GROUP_SIZE; j++) { // Calculate the offset in the target vector auto current_offset = j * mini_circuit_size; @@ -363,12 +367,14 @@ void compute_goblin_translator_range_constraint_ordered_polynomials(StorageHandl * contains 5 MAX_VALUE, 5 (MAX_VALUE-STEP),... values * * @param key Proving key where we will save the polynomials + * @param dyadic_circuit_size The full size of the circuit */ -template inline void compute_extra_range_constraint_numerator(auto proving_key) +template +inline void compute_extra_range_constraint_numerator(auto proving_key, size_t dyadic_circuit_size) { // Get the full goblin circuits size (this is the length of concatenated range constraint polynomials) - auto full_circuit_size = Flavor::FULL_CIRCUIT_SIZE; + auto full_circuit_size = dyadic_circuit_size; auto sort_step = Flavor::SORT_STEP; auto num_concatenated_wires = Flavor::NUM_CONCATENATED_WIRES; @@ -404,8 +410,10 @@ template inline void compute_extra_range_constraint_numerator( * @brief Compute odd and even largrange polynomials (up to mini_circuit length) and put them in the polynomial cache * * @param key Proving key where we will save the polynomials + * @param mini_circuit_dyadic_size The size of the part of the circuit where the computation of translated value happens */ -template inline void compute_lagrange_polynomials_for_goblin_translator(auto proving_key) +template +inline void compute_lagrange_polynomials_for_goblin_translator(auto proving_key, size_t mini_circuit_dyadic_size) { const size_t n = proving_key->circuit_size; @@ -414,7 +422,7 @@ template inline void compute_lagrange_polynomials_for_goblin_t typename Flavor::Polynomial lagrange_polynomial_second(n); typename Flavor::Polynomial lagrange_polynomial_second_to_last_in_minicircuit(n); - for (size_t i = 1; i < Flavor::MINI_CIRCUIT_SIZE - 1; i += 2) { + for (size_t i = 1; i < mini_circuit_dyadic_size - 1; i += 2) { lagrange_polynomial_odd_in_minicircuit[i] = 1; lagrange_polynomial_even_in_minicircut[i + 1] = 1; } @@ -422,7 +430,7 @@ template inline void compute_lagrange_polynomials_for_goblin_t proving_key->lagrange_even_in_minicircuit = lagrange_polynomial_even_in_minicircut.share(); lagrange_polynomial_second[1] = 1; - lagrange_polynomial_second_to_last_in_minicircuit[Flavor::MINI_CIRCUIT_SIZE - 2] = 1; + lagrange_polynomial_second_to_last_in_minicircuit[mini_circuit_dyadic_size - 2] = 1; proving_key->lagrange_second_to_last_in_minicircuit = lagrange_polynomial_second_to_last_in_minicircuit.share(); proving_key->lagrange_second = lagrange_polynomial_second.share(); } diff --git a/barretenberg/cpp/src/barretenberg/honk/proof_system/types/proof.hpp b/barretenberg/cpp/src/barretenberg/honk/proof_system/types/proof.hpp new file mode 100644 index 00000000000..1e40a20d95d --- /dev/null +++ b/barretenberg/cpp/src/barretenberg/honk/proof_system/types/proof.hpp @@ -0,0 +1,9 @@ +#pragma once +#include "barretenberg/ecc/curves/bn254/fr.hpp" +#include + +namespace bb::honk { + +using proof = std::vector; + +} // namespace bb::honk \ No newline at end of file diff --git a/barretenberg/cpp/src/barretenberg/proof_system/circuit_builder/goblin_translator_circuit_builder.hpp b/barretenberg/cpp/src/barretenberg/proof_system/circuit_builder/goblin_translator_circuit_builder.hpp index 79cbd878335..41865d083a1 100644 --- a/barretenberg/cpp/src/barretenberg/proof_system/circuit_builder/goblin_translator_circuit_builder.hpp +++ b/barretenberg/cpp/src/barretenberg/proof_system/circuit_builder/goblin_translator_circuit_builder.hpp @@ -184,7 +184,7 @@ class GoblinTranslatorCircuitBuilder : public CircuitBuilderBase { }; - // Basic goblin translator has the minicircuit size of 2048, so optimize for that case + // Basic goblin translator has the minimum minicircuit size of 2048, so optimize for that case // For context, minicircuit is the part of the final polynomials fed into the proving system, where we have all the // arithmetic logic. However, the full circuit is several times larger (we use a trick to bring down the degree of // the permutation argument) diff --git a/barretenberg/cpp/src/barretenberg/proof_system/circuit_builder/ultra_circuit_builder.hpp b/barretenberg/cpp/src/barretenberg/proof_system/circuit_builder/ultra_circuit_builder.hpp index b6e285c984b..2ef60b71cc3 100644 --- a/barretenberg/cpp/src/barretenberg/proof_system/circuit_builder/ultra_circuit_builder.hpp +++ b/barretenberg/cpp/src/barretenberg/proof_system/circuit_builder/ultra_circuit_builder.hpp @@ -779,7 +779,9 @@ class UltraCircuitBuilder_ : public CircuitBuilderBase namespace bb { -// TODO(#731): Changing the explicit value of these enum elements breaks brittle and outdated tests in circuits/cpp. enum class CircuitType : uint32_t { STANDARD = 0, ULTRA = 2, UNDEFINED = 3 }; template diff --git a/barretenberg/cpp/src/barretenberg/protogalaxy/CMakeLists.txt b/barretenberg/cpp/src/barretenberg/protogalaxy/CMakeLists.txt index f1467c96f4c..da9bde256e7 100644 --- a/barretenberg/cpp/src/barretenberg/protogalaxy/CMakeLists.txt +++ b/barretenberg/cpp/src/barretenberg/protogalaxy/CMakeLists.txt @@ -1 +1 @@ -barretenberg_module(protogalaxy honk flavor relations) \ No newline at end of file +barretenberg_module(protogalaxy honk flavor relations sumcheck) \ No newline at end of file diff --git a/barretenberg/cpp/src/barretenberg/protogalaxy/decider_prover.cpp b/barretenberg/cpp/src/barretenberg/protogalaxy/decider_prover.cpp index d1e835e2a29..f8f5e651e37 100644 --- a/barretenberg/cpp/src/barretenberg/protogalaxy/decider_prover.cpp +++ b/barretenberg/cpp/src/barretenberg/protogalaxy/decider_prover.cpp @@ -94,13 +94,13 @@ template void DeciderProver_::execute_zeromorph_rou transcript); } -template plonk::proof& DeciderProver_::export_proof() +template honk::proof& DeciderProver_::export_proof() { - proof.proof_data = transcript->proof_data; + proof = transcript->proof_data; return proof; } -template plonk::proof& DeciderProver_::construct_proof() +template honk::proof& DeciderProver_::construct_proof() { // Add ϕ, \vec{β*}, e* to transcript execute_preamble_round(); diff --git a/barretenberg/cpp/src/barretenberg/protogalaxy/decider_prover.hpp b/barretenberg/cpp/src/barretenberg/protogalaxy/decider_prover.hpp index 5d962a6b71c..3114446b9c6 100644 --- a/barretenberg/cpp/src/barretenberg/protogalaxy/decider_prover.hpp +++ b/barretenberg/cpp/src/barretenberg/protogalaxy/decider_prover.hpp @@ -2,7 +2,7 @@ #include "barretenberg/commitment_schemes/zeromorph/zeromorph.hpp" #include "barretenberg/flavor/goblin_ultra.hpp" #include "barretenberg/flavor/ultra.hpp" -#include "barretenberg/plonk/proof_system/types/proof.hpp" +#include "barretenberg/honk/proof_system/types/proof.hpp" #include "barretenberg/relations/relation_parameters.hpp" #include "barretenberg/sumcheck/instance/prover_instance.hpp" #include "barretenberg/sumcheck/sumcheck_output.hpp" @@ -32,8 +32,8 @@ template class DeciderProver_ { BBERG_PROFILE void execute_relation_check_rounds(); BBERG_PROFILE void execute_zeromorph_rounds(); - plonk::proof& export_proof(); - plonk::proof& construct_proof(); + honk::proof& export_proof(); + honk::proof& construct_proof(); std::shared_ptr accumulator; @@ -52,7 +52,7 @@ template class DeciderProver_ { using ZeroMorph = pcs::zeromorph::ZeroMorphProver_; private: - plonk::proof proof; + honk::proof proof; }; using DeciderProver = DeciderProver_; diff --git a/barretenberg/cpp/src/barretenberg/protogalaxy/decider_verifier.cpp b/barretenberg/cpp/src/barretenberg/protogalaxy/decider_verifier.cpp index 8ab1eb8c06e..ef86acfcb2e 100644 --- a/barretenberg/cpp/src/barretenberg/protogalaxy/decider_verifier.cpp +++ b/barretenberg/cpp/src/barretenberg/protogalaxy/decider_verifier.cpp @@ -26,7 +26,7 @@ DeciderVerifier_::DeciderVerifier_() * e*). * */ -template bool DeciderVerifier_::verify_proof(const plonk::proof& proof) +template bool DeciderVerifier_::verify_proof(const honk::proof& proof) { using FF = typename Flavor::FF; using Commitment = typename Flavor::Commitment; @@ -36,7 +36,7 @@ template bool DeciderVerifier_::verify_proof(const plo using VerifierCommitments = typename Flavor::VerifierCommitments; static constexpr size_t NUM_SUBRELATIONS = Flavor::NUM_SUBRELATIONS; - transcript = std::make_shared(proof.proof_data); + transcript = std::make_shared(proof); auto inst = std::make_unique(); inst->instance_size = transcript->template receive_from_prover("instance_size"); diff --git a/barretenberg/cpp/src/barretenberg/protogalaxy/decider_verifier.hpp b/barretenberg/cpp/src/barretenberg/protogalaxy/decider_verifier.hpp index d3637372d3d..3e9b5607673 100644 --- a/barretenberg/cpp/src/barretenberg/protogalaxy/decider_verifier.hpp +++ b/barretenberg/cpp/src/barretenberg/protogalaxy/decider_verifier.hpp @@ -1,7 +1,7 @@ #pragma once #include "barretenberg/flavor/goblin_ultra.hpp" #include "barretenberg/flavor/ultra.hpp" -#include "barretenberg/plonk/proof_system/types/proof.hpp" +#include "barretenberg/honk/proof_system/types/proof.hpp" #include "barretenberg/srs/global_crs.hpp" #include "barretenberg/sumcheck/sumcheck.hpp" @@ -18,7 +18,7 @@ template class DeciderVerifier_ { explicit DeciderVerifier_(const std::shared_ptr& transcript, const std::shared_ptr& verifier_key = nullptr); - bool verify_proof(const plonk::proof& proof); + bool verify_proof(const honk::proof& proof); std::shared_ptr key; std::map commitments; diff --git a/barretenberg/cpp/src/barretenberg/protogalaxy/folding_result.hpp b/barretenberg/cpp/src/barretenberg/protogalaxy/folding_result.hpp index d3a4dbf8e65..55bbbd826ac 100644 --- a/barretenberg/cpp/src/barretenberg/protogalaxy/folding_result.hpp +++ b/barretenberg/cpp/src/barretenberg/protogalaxy/folding_result.hpp @@ -13,6 +13,6 @@ template struct FoldingResult { public: std::shared_ptr> accumulator; // TODO(https://github.com/AztecProtocol/barretenberg/issues/656): turn folding data into a struct - std::vector folding_data; + std::vector folding_data; }; } // namespace bb::honk \ No newline at end of file diff --git a/barretenberg/cpp/src/barretenberg/protogalaxy/protogalaxy_prover.cpp b/barretenberg/cpp/src/barretenberg/protogalaxy/protogalaxy_prover.cpp index ca78b870604..6a138c51a51 100644 --- a/barretenberg/cpp/src/barretenberg/protogalaxy/protogalaxy_prover.cpp +++ b/barretenberg/cpp/src/barretenberg/protogalaxy/protogalaxy_prover.cpp @@ -269,14 +269,10 @@ std::shared_ptr ProtoGalaxyProver_ FoldingResult ProtoGalaxyProver_::fold_instances() { prepare_for_folding(); - - // TODO(#https://github.com/AztecProtocol/barretenberg/issues/740): Handle the case where we are folding for the - // first time and accumulator is 0 FF delta = transcript->get_challenge("delta"); auto accumulator = get_accumulator(); diff --git a/barretenberg/cpp/src/barretenberg/protogalaxy/protogalaxy_verifier.cpp b/barretenberg/cpp/src/barretenberg/protogalaxy/protogalaxy_verifier.cpp index 0e33b27cd77..5c8331e0aaf 100644 --- a/barretenberg/cpp/src/barretenberg/protogalaxy/protogalaxy_verifier.cpp +++ b/barretenberg/cpp/src/barretenberg/protogalaxy/protogalaxy_verifier.cpp @@ -6,17 +6,20 @@ template void ProtoGalaxyVerifier_::receive_accumulator(const std::shared_ptr& inst, const std::string& domain_separator) { + // Get circuit parameters inst->instance_size = transcript->template receive_from_prover(domain_separator + "_instance_size"); inst->log_instance_size = static_cast(numeric::get_msb(inst->instance_size)); inst->public_input_size = transcript->template receive_from_prover(domain_separator + "_public_input_size"); + // Get folded public inputs for (size_t i = 0; i < inst->public_input_size; ++i) { auto public_input_i = transcript->template receive_from_prover(domain_separator + "_public_input_" + std::to_string(i)); inst->public_inputs.emplace_back(public_input_i); } + // Get folded relation parameters auto eta = transcript->template receive_from_prover(domain_separator + "_eta"); auto beta = transcript->template receive_from_prover(domain_separator + "_beta"); auto gamma = transcript->template receive_from_prover(domain_separator + "_gamma"); @@ -26,6 +29,7 @@ void ProtoGalaxyVerifier_::receive_accumulator(const std::sha inst->relation_parameters = RelationParameters{ eta, beta, gamma, public_input_delta, lookup_grand_product_delta }; + // Get the folded relation separator challenges \vec{α} for (size_t idx = 0; idx < NUM_SUBRELATIONS - 1; idx++) { inst->alphas[idx] = transcript->template receive_from_prover(domain_separator + "_alpha_" + std::to_string(idx)); @@ -33,11 +37,14 @@ void ProtoGalaxyVerifier_::receive_accumulator(const std::sha inst->target_sum = transcript->template receive_from_prover(domain_separator + "_target_sum"); + // Get the folded gate challenges, \vec{β} in the paper inst->gate_challenges = std::vector(inst->log_instance_size); for (size_t idx = 0; idx < inst->log_instance_size; idx++) { inst->gate_challenges[idx] = transcript->template receive_from_prover(domain_separator + "_gate_challenge_" + std::to_string(idx)); } + + // Get the folded commitments to all witness polynomials auto comm_view = inst->witness_commitments.get_all(); auto witness_labels = inst->commitment_labels.get_witness(); for (size_t idx = 0; idx < witness_labels.size(); idx++) { @@ -45,6 +52,7 @@ void ProtoGalaxyVerifier_::receive_accumulator(const std::sha transcript->template receive_from_prover(domain_separator + "_" + witness_labels[idx]); } + // Get the folded commitments to selector polynomials inst->verification_key = std::make_shared(inst->instance_size, inst->public_input_size); auto vk_view = inst->verification_key->get_all(); auto vk_labels = inst->commitment_labels.get_precomputed(); @@ -57,6 +65,7 @@ template void ProtoGalaxyVerifier_::receive_and_finalise_instance(const std::shared_ptr& inst, const std::string& domain_separator) { + // Get circuit parameters and the public inputs inst->instance_size = transcript->template receive_from_prover(domain_separator + "_instance_size"); inst->log_instance_size = static_cast(numeric::get_msb(inst->instance_size)); inst->public_input_size = @@ -71,33 +80,39 @@ void ProtoGalaxyVerifier_::receive_and_finalise_instance(cons inst->pub_inputs_offset = transcript->template receive_from_prover(domain_separator + "_pub_inputs_offset"); + // Get commitments to first three wire polynomials auto labels = inst->commitment_labels; auto& witness_commitments = inst->witness_commitments; witness_commitments.w_l = transcript->template receive_from_prover(domain_separator + "_" + labels.w_l); witness_commitments.w_r = transcript->template receive_from_prover(domain_separator + "_" + labels.w_r); witness_commitments.w_o = transcript->template receive_from_prover(domain_separator + "_" + labels.w_o); + // Get challenge for sorted list batching and wire four memory records commitment auto eta = transcript->get_challenge(domain_separator + "_eta"); witness_commitments.sorted_accum = transcript->template receive_from_prover(domain_separator + "_" + labels.sorted_accum); witness_commitments.w_4 = transcript->template receive_from_prover(domain_separator + "_" + labels.w_4); + // Get permutation challenges and commitment to permutation and lookup grand products auto [beta, gamma] = transcript->get_challenges(domain_separator + "_beta", domain_separator + "_gamma"); witness_commitments.z_perm = transcript->template receive_from_prover(domain_separator + "_" + labels.z_perm); witness_commitments.z_lookup = transcript->template receive_from_prover(domain_separator + "_" + labels.z_lookup); + // Compute correction terms for grand products const FF public_input_delta = compute_public_input_delta( inst->public_inputs, beta, gamma, inst->instance_size, inst->pub_inputs_offset); const FF lookup_grand_product_delta = compute_lookup_grand_product_delta(beta, gamma, inst->instance_size); inst->relation_parameters = RelationParameters{ eta, beta, gamma, public_input_delta, lookup_grand_product_delta }; + // Get the relation separation challenges for (size_t idx = 0; idx < NUM_SUBRELATIONS - 1; idx++) { inst->alphas[idx] = transcript->get_challenge(domain_separator + "_alpha_" + std::to_string(idx)); } + // Get the commitments to the selector polynomials for the given instance inst->verification_key = std::make_shared(inst->instance_size, inst->public_input_size); auto vk_view = inst->verification_key->get_all(); auto vk_labels = labels.get_precomputed(); @@ -109,7 +124,7 @@ void ProtoGalaxyVerifier_::receive_and_finalise_instance(cons // TODO(https://github.com/AztecProtocol/barretenberg/issues/795): The rounds prior to actual verifying are common // between decider and folding verifier and could be somehow shared so we do not duplicate code so much. template -void ProtoGalaxyVerifier_::prepare_for_folding(const std::vector& fold_data) +void ProtoGalaxyVerifier_::prepare_for_folding(const std::vector& fold_data) { transcript = std::make_shared(fold_data); auto index = 0; @@ -142,7 +157,7 @@ void ProtoGalaxyVerifier_::prepare_for_folding(const std::vec } template -bool ProtoGalaxyVerifier_::verify_folding_proof(std::vector fold_data) +bool ProtoGalaxyVerifier_::verify_folding_proof(const std::vector& fold_data) { prepare_for_folding(fold_data); diff --git a/barretenberg/cpp/src/barretenberg/protogalaxy/protogalaxy_verifier.hpp b/barretenberg/cpp/src/barretenberg/protogalaxy/protogalaxy_verifier.hpp index 6a3f62889c8..cbb23d6cc72 100644 --- a/barretenberg/cpp/src/barretenberg/protogalaxy/protogalaxy_verifier.hpp +++ b/barretenberg/cpp/src/barretenberg/protogalaxy/protogalaxy_verifier.hpp @@ -64,7 +64,7 @@ template class ProtoGalaxyVerifier_ { * * @param fold_data The data transmitted via the transcript by the prover. */ - void prepare_for_folding(const std::vector&); + void prepare_for_folding(const std::vector&); /** * @brief Instantiatied the accumulator (i.e. the relaxed instance) from the transcript. @@ -83,7 +83,7 @@ template class ProtoGalaxyVerifier_ { * accumulator, received from the prover is the same as that produced by the verifier. * */ - bool verify_folding_proof(std::vector); + bool verify_folding_proof(const std::vector&); }; } // namespace bb::honk diff --git a/barretenberg/cpp/src/barretenberg/srs/global_crs.cpp b/barretenberg/cpp/src/barretenberg/srs/global_crs.cpp index f6ac2e05c71..fcfbac3e30c 100644 --- a/barretenberg/cpp/src/barretenberg/srs/global_crs.cpp +++ b/barretenberg/cpp/src/barretenberg/srs/global_crs.cpp @@ -21,18 +21,56 @@ void init_crs_factory(std::vector const& points, g2::affine_ // Initializes crs from a file path this we use in the entire codebase void init_crs_factory(std::string crs_path) { - crs_factory = std::make_shared>(crs_path); + if (crs_factory != nullptr) { + return; + } +#ifdef WASMTIME_ENV_HACK + static_cast(crs_path); + // We only need this codepath in wasmtime because the SRS cannot be loaded in our usual ways + // and we don't need a real CRS for our purposes. + // TODO(https://github.com/AztecProtocol/barretenberg/issues/837): make this a real SRS. + std::cout << "WASMTIME_ENV_HACK: started generating fake bn254 curve" << std::endl; + std::vector points; + // 2**19 points + points.reserve(1 << 19); + for (int i = 0; i < (1 << 19); i++) { + points.push_back(g1::affine_element::random_element()); + } + init_crs_factory(points, g2::affine_element{ fq::random_element(), fq::random_element() }); + std::cout << "WASMTIME_ENV_HACK: finished generating fake bn254 curve" << std::endl; +#else + crs_factory = std::make_shared>(crs_path); +#endif } // Initializes the crs using the memory buffers -void init_grumpkin_crs_factory(std::vector const& points) +void init_grumpkin_crs_factory(std::vector const& points) { grumpkin_crs_factory = std::make_shared(points); } void init_grumpkin_crs_factory(std::string crs_path) { + if (grumpkin_crs_factory != nullptr) { + return; + } +#ifdef WASMTIME_ENV_HACK + // We only need this codepath in wasmtime because the SRS cannot be loaded in our usual ways + // and we don't need a real CRS for our purposes. + // TODO(https://github.com/AztecProtocol/barretenberg/issues/837): make this a real SRS. + static_cast(crs_path); + std::cout << "WASMTIME_ENV_HACK: started generating fake grumpkin curve" << std::endl; + std::vector points; + // 2**18 points + points.reserve(1 << 18); + for (int i = 0; i < (1 << 18); i++) { + points.push_back(curve::Grumpkin::AffineElement::random_element()); + } + std::cout << "WASMTIME_ENV_HACK: finished generating fake grumpkin curve" << std::endl; + init_grumpkin_crs_factory(points); +#else grumpkin_crs_factory = std::make_shared>(crs_path); +#endif } std::shared_ptr> get_crs_factory() diff --git a/barretenberg/cpp/src/barretenberg/srs/scalar_multiplication.test.cpp b/barretenberg/cpp/src/barretenberg/srs/scalar_multiplication.test.cpp index 9ed7c2e4048..c43bb64a60d 100644 --- a/barretenberg/cpp/src/barretenberg/srs/scalar_multiplication.test.cpp +++ b/barretenberg/cpp/src/barretenberg/srs/scalar_multiplication.test.cpp @@ -70,7 +70,7 @@ TYPED_TEST(ScalarMultiplicationTests, ReduceBucketsSimple) TestFixture::read_transcript_g2(TestFixture::SRS_PATH); } auto crs = srs::factories::FileProverCrs(num_points / 2, TestFixture::SRS_PATH); - auto monomials = crs.get_monomial_points(); + auto* monomials = crs.get_monomial_points(); std::vector point_schedule(bb::scalar_multiplication::point_table_size(num_points / 2)); std::array bucket_empty_status; diff --git a/barretenberg/cpp/src/barretenberg/stdlib/encryption/ecdsa/ecdsa.hpp b/barretenberg/cpp/src/barretenberg/stdlib/encryption/ecdsa/ecdsa.hpp index 1c2649ed1b3..7ac633dd71c 100644 --- a/barretenberg/cpp/src/barretenberg/stdlib/encryption/ecdsa/ecdsa.hpp +++ b/barretenberg/cpp/src/barretenberg/stdlib/encryption/ecdsa/ecdsa.hpp @@ -41,6 +41,8 @@ static ecdsa_signature ecdsa_from_witness(Builder* ctx, const crypto::e return out; } +template void generate_ecdsa_verification_test_circuit(Builder& builder, size_t num_iterations); + } // namespace bb::stdlib #include "./ecdsa_impl.hpp" \ No newline at end of file diff --git a/barretenberg/cpp/src/barretenberg/stdlib/encryption/ecdsa/ecdsa_impl.hpp b/barretenberg/cpp/src/barretenberg/stdlib/encryption/ecdsa/ecdsa_impl.hpp index fa67089fd4e..ee0078f0f33 100644 --- a/barretenberg/cpp/src/barretenberg/stdlib/encryption/ecdsa/ecdsa_impl.hpp +++ b/barretenberg/cpp/src/barretenberg/stdlib/encryption/ecdsa/ecdsa_impl.hpp @@ -3,6 +3,7 @@ #include "barretenberg/stdlib/encryption/ecdsa/ecdsa.hpp" #include "barretenberg/stdlib/hash/sha256/sha256.hpp" #include "barretenberg/stdlib/primitives//bit_array/bit_array.hpp" +#include "barretenberg/stdlib/primitives/curves/secp256k1.hpp" namespace bb::stdlib { @@ -221,4 +222,54 @@ bool_t ecdsa_verify_signature_noassert(const stdlib::byte_array void generate_ecdsa_verification_test_circuit(Builder& builder, size_t num_iterations) +{ + using curve = stdlib::secp256k1; + using fr = typename curve::fr; + using fq = typename curve::fq; + using g1 = typename curve::g1; + + std::string message_string = "Instructions unclear, ask again later."; + + crypto::ecdsa_key_pair account; + for (size_t i = 0; i < num_iterations; i++) { + // Generate unique signature for each iteration + account.private_key = curve::fr::random_element(); + account.public_key = curve::g1::one * account.private_key; + + crypto::ecdsa_signature signature = + crypto::ecdsa_construct_signature(message_string, account); + + bool first_result = + crypto::ecdsa_verify_signature(message_string, account.public_key, signature); + static_cast(first_result); // TODO(Cody): This is not used anywhere. + + std::vector rr(signature.r.begin(), signature.r.end()); + std::vector ss(signature.s.begin(), signature.s.end()); + uint8_t vv = signature.v; + + typename curve::g1_bigfr_ct public_key = curve::g1_bigfr_ct::from_witness(&builder, account.public_key); + + stdlib::ecdsa_signature sig{ typename curve::byte_array_ct(&builder, rr), + typename curve::byte_array_ct(&builder, ss), + stdlib::uint8(&builder, vv) }; + + typename curve::byte_array_ct message(&builder, message_string); + + // Verify ecdsa signature + stdlib::ecdsa_verify_signature(message, public_key, sig); + } +} + } // namespace bb::stdlib \ No newline at end of file diff --git a/barretenberg/cpp/src/barretenberg/stdlib/hash/benchmarks/CMakeLists.txt b/barretenberg/cpp/src/barretenberg/stdlib/hash/benchmarks/CMakeLists.txt index ef200979db3..7b46659e0ed 100644 --- a/barretenberg/cpp/src/barretenberg/stdlib/hash/benchmarks/CMakeLists.txt +++ b/barretenberg/cpp/src/barretenberg/stdlib/hash/benchmarks/CMakeLists.txt @@ -1,3 +1 @@ -add_subdirectory(sha256) -add_subdirectory(external) -add_subdirectory(celer) \ No newline at end of file +barretenberg_module(hash_benchmarks stdlib_primitives crypto_sha256 stdlib_sha256 stdlib_blake3s) \ No newline at end of file diff --git a/barretenberg/cpp/src/barretenberg/stdlib/hash/benchmarks/celer/CMakeLists.txt b/barretenberg/cpp/src/barretenberg/stdlib/hash/benchmarks/celer/CMakeLists.txt deleted file mode 100644 index e79cbdf5776..00000000000 --- a/barretenberg/cpp/src/barretenberg/stdlib/hash/benchmarks/celer/CMakeLists.txt +++ /dev/null @@ -1 +0,0 @@ -barretenberg_module(celer stdlib_sha256) \ No newline at end of file diff --git a/barretenberg/cpp/src/barretenberg/stdlib/hash/benchmarks/celer/sha256.bench.cpp b/barretenberg/cpp/src/barretenberg/stdlib/hash/benchmarks/celer_sha256.bench.cpp similarity index 100% rename from barretenberg/cpp/src/barretenberg/stdlib/hash/benchmarks/celer/sha256.bench.cpp rename to barretenberg/cpp/src/barretenberg/stdlib/hash/benchmarks/celer_sha256.bench.cpp index e7defb06836..ac65931761d 100644 --- a/barretenberg/cpp/src/barretenberg/stdlib/hash/benchmarks/celer/sha256.bench.cpp +++ b/barretenberg/cpp/src/barretenberg/stdlib/hash/benchmarks/celer_sha256.bench.cpp @@ -6,8 +6,8 @@ * @date 2023-08-02 * */ -#include "barretenberg/stdlib/hash/sha256/sha256.hpp" #include "barretenberg/plonk/composer/ultra_composer.hpp" +#include "barretenberg/stdlib/hash/sha256/sha256.hpp" #include #include diff --git a/barretenberg/cpp/src/barretenberg/stdlib/hash/benchmarks/external/CMakeLists.txt b/barretenberg/cpp/src/barretenberg/stdlib/hash/benchmarks/external/CMakeLists.txt deleted file mode 100644 index 2ef6b1656c7..00000000000 --- a/barretenberg/cpp/src/barretenberg/stdlib/hash/benchmarks/external/CMakeLists.txt +++ /dev/null @@ -1 +0,0 @@ -barretenberg_module(external stdlib_primitives crypto_sha256 stdlib_sha256 stdlib_blake3s) \ No newline at end of file diff --git a/barretenberg/cpp/src/barretenberg/stdlib/hash/benchmarks/external/external.bench.cpp b/barretenberg/cpp/src/barretenberg/stdlib/hash/benchmarks/external_sha256_blake3s.bench.cpp similarity index 100% rename from barretenberg/cpp/src/barretenberg/stdlib/hash/benchmarks/external/external.bench.cpp rename to barretenberg/cpp/src/barretenberg/stdlib/hash/benchmarks/external_sha256_blake3s.bench.cpp diff --git a/barretenberg/cpp/src/barretenberg/stdlib/hash/benchmarks/sha256/CMakeLists.txt b/barretenberg/cpp/src/barretenberg/stdlib/hash/benchmarks/sha256/CMakeLists.txt deleted file mode 100644 index f3f408e1fe5..00000000000 --- a/barretenberg/cpp/src/barretenberg/stdlib/hash/benchmarks/sha256/CMakeLists.txt +++ /dev/null @@ -1 +0,0 @@ -barretenberg_module(stdlib_sha256_just stdlib_sha256) \ No newline at end of file diff --git a/barretenberg/cpp/src/barretenberg/stdlib/hash/benchmarks/sha256/sha256.bench.cpp b/barretenberg/cpp/src/barretenberg/stdlib/hash/benchmarks/stdlib_sha256.bench.cpp similarity index 100% rename from barretenberg/cpp/src/barretenberg/stdlib/hash/benchmarks/sha256/sha256.bench.cpp rename to barretenberg/cpp/src/barretenberg/stdlib/hash/benchmarks/stdlib_sha256.bench.cpp index d0c7cfd2f79..605b86e3397 100644 --- a/barretenberg/cpp/src/barretenberg/stdlib/hash/benchmarks/sha256/sha256.bench.cpp +++ b/barretenberg/cpp/src/barretenberg/stdlib/hash/benchmarks/stdlib_sha256.bench.cpp @@ -1,5 +1,5 @@ -#include "barretenberg/stdlib/hash/sha256/sha256.hpp" #include "barretenberg/plonk/composer/ultra_composer.hpp" +#include "barretenberg/stdlib/hash/sha256/sha256.hpp" #include using namespace benchmark; diff --git a/barretenberg/cpp/src/barretenberg/stdlib/hash/keccak/keccak.cpp b/barretenberg/cpp/src/barretenberg/stdlib/hash/keccak/keccak.cpp index 2392853e335..9543603660f 100644 --- a/barretenberg/cpp/src/barretenberg/stdlib/hash/keccak/keccak.cpp +++ b/barretenberg/cpp/src/barretenberg/stdlib/hash/keccak/keccak.cpp @@ -888,7 +888,27 @@ stdlib::byte_array keccak::sponge_squeeze_for_permutation_opco } return result; } + +/** + * @brief Generate a simple keccak circuit for testing purposes + * + * @tparam Builder + * @param builder + * @param num_iterations number of hashes to perform + */ +template void generate_keccak_test_circuit(Builder& builder, size_t num_iterations) +{ + std::string in = "abcdefghijklmnopqrstuvwxyz0123456789abcdefghijklmnopqrstuvwxyz01"; + + stdlib::byte_array input(&builder, in); + for (size_t i = 0; i < num_iterations; i++) { + input = stdlib::keccak::hash(input); + } +} + template class keccak; template class keccak; +template void generate_keccak_test_circuit(bb::UltraCircuitBuilder&, size_t); +template void generate_keccak_test_circuit(bb::GoblinUltraCircuitBuilder&, size_t); } // namespace bb::stdlib diff --git a/barretenberg/cpp/src/barretenberg/stdlib/hash/keccak/keccak.hpp b/barretenberg/cpp/src/barretenberg/stdlib/hash/keccak/keccak.hpp index b4a9ae8f8ec..bd399af7a39 100644 --- a/barretenberg/cpp/src/barretenberg/stdlib/hash/keccak/keccak.hpp +++ b/barretenberg/cpp/src/barretenberg/stdlib/hash/keccak/keccak.hpp @@ -201,4 +201,6 @@ template class keccak { Builder* context); }; +template void generate_keccak_test_circuit(Builder& builder, size_t num_iterations); + } // namespace bb::stdlib diff --git a/barretenberg/cpp/src/barretenberg/stdlib/hash/sha256/sha256.cpp b/barretenberg/cpp/src/barretenberg/stdlib/hash/sha256/sha256.cpp index 57264c24d06..a685f0af145 100644 --- a/barretenberg/cpp/src/barretenberg/stdlib/hash/sha256/sha256.cpp +++ b/barretenberg/cpp/src/barretenberg/stdlib/hash/sha256/sha256.cpp @@ -178,6 +178,23 @@ template packed_byte_array sha256(const packed_byte_ return packed_byte_array(output, 4); } +/** + * @brief Generate a simple sha256 circuit for testing purposes + * + * @tparam Builder + * @param builder + * @param num_iterations number of hashes to perform + */ +template void generate_sha256_test_circuit(Builder& builder, size_t num_iterations) +{ + std::string in; + in.resize(32); + stdlib::packed_byte_array input(&builder, in); + for (size_t i = 0; i < num_iterations; i++) { + input = stdlib::sha256(input); + } +} + template byte_array sha256_block(const byte_array& input); template byte_array sha256_block(const byte_array& input); template byte_array sha256_block(const byte_array& input); @@ -186,4 +203,6 @@ template packed_byte_array sha256( template packed_byte_array sha256(const packed_byte_array& input); template packed_byte_array sha256( const packed_byte_array& input); +template void generate_sha256_test_circuit(bb::UltraCircuitBuilder&, size_t); +template void generate_sha256_test_circuit(bb::GoblinUltraCircuitBuilder&, size_t); } // namespace bb::stdlib diff --git a/barretenberg/cpp/src/barretenberg/stdlib/hash/sha256/sha256.hpp b/barretenberg/cpp/src/barretenberg/stdlib/hash/sha256/sha256.hpp index 2593fe10fc1..8f88ce2e946 100644 --- a/barretenberg/cpp/src/barretenberg/stdlib/hash/sha256/sha256.hpp +++ b/barretenberg/cpp/src/barretenberg/stdlib/hash/sha256/sha256.hpp @@ -23,4 +23,6 @@ template field_t sha256_to_field(const packed_byte_a return slices[1] + (slices[0] * (uint256_t(1) << 128)); } +template void generate_sha256_test_circuit(Builder& builder, size_t num_iterations); + } // namespace bb::stdlib diff --git a/barretenberg/cpp/src/barretenberg/stdlib/merkle_tree/membership.hpp b/barretenberg/cpp/src/barretenberg/stdlib/merkle_tree/membership.hpp index f72dab1960f..ddb743b6e8e 100644 --- a/barretenberg/cpp/src/barretenberg/stdlib/merkle_tree/membership.hpp +++ b/barretenberg/cpp/src/barretenberg/stdlib/merkle_tree/membership.hpp @@ -1,5 +1,7 @@ #pragma once #include "barretenberg/stdlib/hash/pedersen/pedersen.hpp" +#include "barretenberg/stdlib/merkle_tree/memory_store.hpp" +#include "barretenberg/stdlib/merkle_tree/merkle_tree.hpp" #include "barretenberg/stdlib/primitives/byte_array/byte_array.hpp" #include "barretenberg/stdlib/primitives/field/field.hpp" #include "hash_path.hpp" @@ -315,3 +317,39 @@ void batch_update_membership(field_t const& new_root, } } // namespace bb::stdlib::merkle_tree + +namespace bb::stdlib { +/** + * @brief Generate a simple merkle tree membership circuit for testing purposes + * + * @tparam Builder + * @param builder + * @param num_iterations number of membership checks to perform + */ +template static void generate_merkle_membership_test_circuit(Builder& builder, size_t num_iterations) +{ + using namespace stdlib; + using field_ct = field_t; + using witness_ct = witness_t; + using MemStore = merkle_tree::MemoryStore; + using MerkleTree_ct = merkle_tree::MerkleTree; + + MemStore store; + const size_t tree_depth = 7; + auto merkle_tree = MerkleTree_ct(store, tree_depth); + + for (size_t i = 0; i < num_iterations; i++) { + // For each iteration update and check the membership of a different value + size_t idx = i; + size_t value = i * 2; + merkle_tree.update_element(idx, value); + + field_ct root_ct = witness_ct(&builder, merkle_tree.root()); + auto idx_ct = field_ct(witness_ct(&builder, fr(idx))).decompose_into_bits(); + auto value_ct = field_ct(value); + + merkle_tree::check_membership( + root_ct, merkle_tree::create_witness_hash_path(builder, merkle_tree.get_hash_path(idx)), value_ct, idx_ct); + } +} +} // namespace bb::stdlib \ No newline at end of file diff --git a/barretenberg/cpp/src/barretenberg/stdlib/primitives/curves/bn254.hpp b/barretenberg/cpp/src/barretenberg/stdlib/primitives/curves/bn254.hpp index 594ac29d75d..d437cee5044 100644 --- a/barretenberg/cpp/src/barretenberg/stdlib/primitives/curves/bn254.hpp +++ b/barretenberg/cpp/src/barretenberg/stdlib/primitives/curves/bn254.hpp @@ -17,6 +17,8 @@ template struct bn254 { using ScalarFieldNative = curve::BN254::ScalarField; using BaseFieldNative = curve::BN254::BaseField; using GroupNative = curve::BN254::Group; + using ElementNative = GroupNative::element; + using AffineElementNative = GroupNative::affine_element; // Stdlib types corresponding to those defined in the native description of the curve. // Note: its useful to have these type names match the native analog exactly so that components that digest a Curve diff --git a/barretenberg/cpp/src/barretenberg/stdlib/recursion/CMakeLists.txt b/barretenberg/cpp/src/barretenberg/stdlib/recursion/CMakeLists.txt index 3b7a634c740..09dd4c932a3 100644 --- a/barretenberg/cpp/src/barretenberg/stdlib/recursion/CMakeLists.txt +++ b/barretenberg/cpp/src/barretenberg/stdlib/recursion/CMakeLists.txt @@ -1 +1 @@ -barretenberg_module(stdlib_recursion ecc proof_system stdlib_primitives stdlib_pedersen_commitment stdlib_blake3s ultra_honk eccvm translator_vm) \ No newline at end of file +barretenberg_module(stdlib_recursion ecc proof_system stdlib_primitives stdlib_pedersen_commitment stdlib_blake3s ultra_honk eccvm translator_vm stdlib_poseidon2) \ No newline at end of file diff --git a/barretenberg/cpp/src/barretenberg/stdlib/recursion/honk/transcript/transcript.hpp b/barretenberg/cpp/src/barretenberg/stdlib/recursion/honk/transcript/transcript.hpp index 4bf08cc99c0..723ad1f7cf0 100644 --- a/barretenberg/cpp/src/barretenberg/stdlib/recursion/honk/transcript/transcript.hpp +++ b/barretenberg/cpp/src/barretenberg/stdlib/recursion/honk/transcript/transcript.hpp @@ -3,6 +3,7 @@ #include "barretenberg/ecc/curves/bn254/fq.hpp" #include "barretenberg/ecc/curves/bn254/fr.hpp" #include "barretenberg/ecc/curves/bn254/g1.hpp" +#include "barretenberg/honk/proof_system/types/proof.hpp" #include "barretenberg/polynomials/univariate.hpp" #include "barretenberg/transcript/transcript.hpp" @@ -17,17 +18,17 @@ template class Transcript { public: using field_ct = field_t; using FF = bb::fr; - using BaseTranscript = bb::honk::BaseTranscript; + using NativeTranscript = bb::honk::BaseTranscript; using StdlibTypes = utility::StdlibTypesUtility; - static constexpr size_t HASH_OUTPUT_SIZE = BaseTranscript::HASH_OUTPUT_SIZE; + static constexpr size_t HASH_OUTPUT_SIZE = NativeTranscript::HASH_OUTPUT_SIZE; - BaseTranscript native_transcript; + NativeTranscript native_transcript; Builder* builder; Transcript() = default; - Transcript(Builder* builder, auto proof_data) + Transcript(Builder* builder, const bb::honk::proof& proof_data) : native_transcript(proof_data) , builder(builder){}; @@ -59,7 +60,7 @@ template class Transcript { */ std::array challenges; for (size_t i = 0; i < num_challenges; ++i) { - challenges[i] = field_ct::from_witness(builder, static_cast(native_challenges[i])); + challenges[i] = field_ct::from_witness(builder, native_challenges[i]); } return challenges; diff --git a/barretenberg/cpp/src/barretenberg/stdlib/recursion/honk/verifier/decider_recursive_verifier.cpp b/barretenberg/cpp/src/barretenberg/stdlib/recursion/honk/verifier/decider_recursive_verifier.cpp new file mode 100644 index 00000000000..101615f51e9 --- /dev/null +++ b/barretenberg/cpp/src/barretenberg/stdlib/recursion/honk/verifier/decider_recursive_verifier.cpp @@ -0,0 +1,96 @@ +#include "barretenberg/stdlib/recursion/honk/verifier/decider_recursive_verifier.hpp" +#include "barretenberg/commitment_schemes/zeromorph/zeromorph.hpp" +#include "barretenberg/numeric/bitop/get_msb.hpp" +#include "barretenberg/sumcheck/instance/verifier_instance.hpp" +#include "barretenberg/transcript/transcript.hpp" + +namespace bb::stdlib::recursion::honk { + +template +DeciderRecursiveVerifier_::DeciderRecursiveVerifier_(Builder* builder) + : builder(builder) +{} + +/** + * @brief This function verifies an Ultra Honk proof for a given Flavor, produced for a relaxed instance (ϕ, \vec{β*}, + * e*). + * + */ +template +std::array DeciderRecursiveVerifier_::verify_proof( + const bb::honk::proof& proof) +{ + using Sumcheck = ::bb::honk::sumcheck::SumcheckVerifier; + using Curve = typename Flavor::Curve; + using ZeroMorph = ::bb::honk::pcs::zeromorph::ZeroMorphVerifier_; + using VerifierCommitments = typename Flavor::VerifierCommitments; + using Transcript = typename Flavor::Transcript; + using Instance = typename ::bb::honk::VerifierInstance_; + + static constexpr size_t NUM_SUBRELATIONS = Flavor::NUM_SUBRELATIONS; + transcript = std::make_shared(builder, proof); + auto inst = std::make_unique(); + + const auto instance_size = transcript->template receive_from_prover("instance_size"); + const auto public_input_size = transcript->template receive_from_prover("public_input_size"); + const auto log_instance_size = static_cast(numeric::get_msb(uint32_t(instance_size.get_value()))); + + for (size_t i = 0; i < uint32_t(public_input_size.get_value()); ++i) { + auto public_input_i = transcript->template receive_from_prover("public_input_" + std::to_string(i)); + inst->public_inputs.emplace_back(public_input_i); + } + + auto eta = transcript->template receive_from_prover("eta"); + auto beta = transcript->template receive_from_prover("beta"); + auto gamma = transcript->template receive_from_prover("gamma"); + auto public_input_delta = transcript->template receive_from_prover("public_input_delta"); + auto lookup_grand_product_delta = transcript->template receive_from_prover("lookup_grand_product_delta"); + inst->relation_parameters = + RelationParameters{ eta, beta, gamma, public_input_delta, lookup_grand_product_delta }; + + for (size_t idx = 0; idx < NUM_SUBRELATIONS - 1; idx++) { + inst->alphas[idx] = transcript->template receive_from_prover("alpha" + std::to_string(idx)); + } + + inst->target_sum = transcript->template receive_from_prover("target_sum"); + + inst->gate_challenges = std::vector(log_instance_size); + for (size_t idx = 0; idx < log_instance_size; idx++) { + inst->gate_challenges[idx] = + transcript->template receive_from_prover("gate_challenge_" + std::to_string(idx)); + } + auto comm_view = inst->witness_commitments.get_all(); + auto witness_labels = inst->commitment_labels.get_witness(); + for (size_t idx = 0; idx < witness_labels.size(); idx++) { + comm_view[idx] = transcript->template receive_from_prover(witness_labels[idx]); + } + + inst->verification_key = std::make_shared(inst->instance_size, inst->public_input_size); + auto vk_view = inst->verification_key->get_all(); + auto vk_labels = inst->commitment_labels.get_precomputed(); + for (size_t idx = 0; idx < vk_labels.size(); idx++) { + vk_view[idx] = transcript->template receive_from_prover(vk_labels[idx]); + } + + VerifierCommitments commitments{ inst->verification_key, inst->witness_commitments }; + + auto sumcheck = Sumcheck(log_instance_size, transcript, inst->target_sum); + + auto [multivariate_challenge, claimed_evaluations, sumcheck_verified] = + sumcheck.verify(inst->relation_parameters, inst->alphas, inst->gate_challenges); + + // Execute ZeroMorph rounds. See https://hackmd.io/dlf9xEwhTQyE3hiGbq4FsA?view for a complete description of the + // unrolled protocol. + auto pairing_points = ZeroMorph::verify(commitments.get_unshifted(), + commitments.get_to_be_shifted(), + claimed_evaluations.get_unshifted(), + claimed_evaluations.get_shifted(), + multivariate_challenge, + transcript); + + return pairing_points; +} + +template class DeciderRecursiveVerifier_>; +template class DeciderRecursiveVerifier_>; +} // namespace bb::stdlib::recursion::honk diff --git a/barretenberg/cpp/src/barretenberg/stdlib/recursion/honk/verifier/decider_recursive_verifier.hpp b/barretenberg/cpp/src/barretenberg/stdlib/recursion/honk/verifier/decider_recursive_verifier.hpp new file mode 100644 index 00000000000..ba6dc021bed --- /dev/null +++ b/barretenberg/cpp/src/barretenberg/stdlib/recursion/honk/verifier/decider_recursive_verifier.hpp @@ -0,0 +1,30 @@ +#pragma once +#include "barretenberg/flavor/goblin_ultra_recursive.hpp" +#include "barretenberg/flavor/ultra_recursive.hpp" +#include "barretenberg/honk/proof_system/types/proof.hpp" +#include "barretenberg/stdlib/recursion/honk/transcript/transcript.hpp" +#include "barretenberg/sumcheck/sumcheck.hpp" + +namespace bb::stdlib::recursion::honk { +template class DeciderRecursiveVerifier_ { + using FF = typename Flavor::FF; + using Commitment = typename Flavor::Commitment; + using GroupElement = typename Flavor::GroupElement; + using VerificationKey = typename Flavor::VerificationKey; + using VerifierCommitmentKey = typename Flavor::VerifierCommitmentKey; + using Builder = typename Flavor::CircuitBuilder; + using RelationSeparator = typename Flavor::RelationSeparator; + using PairingPoints = std::array; + + public: + explicit DeciderRecursiveVerifier_(Builder* builder); + + PairingPoints verify_proof(const bb::honk::proof& proof); + + std::map commitments; + std::shared_ptr pcs_verification_key; + Builder* builder; + std::shared_ptr> transcript; +}; + +} // namespace bb::stdlib::recursion::honk \ No newline at end of file diff --git a/barretenberg/cpp/src/barretenberg/stdlib/recursion/honk/verifier/merge_recursive_verifier.cpp b/barretenberg/cpp/src/barretenberg/stdlib/recursion/honk/verifier/merge_recursive_verifier.cpp index eaa93a42d3a..6d809e7ad5a 100644 --- a/barretenberg/cpp/src/barretenberg/stdlib/recursion/honk/verifier/merge_recursive_verifier.cpp +++ b/barretenberg/cpp/src/barretenberg/stdlib/recursion/honk/verifier/merge_recursive_verifier.cpp @@ -16,9 +16,9 @@ MergeRecursiveVerifier_::MergeRecursiveVerifier_(CircuitBuilder* */ template std::array::Element, 2> MergeRecursiveVerifier_::verify_proof( - const plonk::proof& proof) + const bb::honk::proof& proof) { - transcript = std::make_shared(builder, proof.proof_data); + transcript = std::make_shared(builder, proof); // Receive commitments [t_i^{shift}], [T_{i-1}], and [T_i] std::array C_T_prev; diff --git a/barretenberg/cpp/src/barretenberg/stdlib/recursion/honk/verifier/merge_recursive_verifier.hpp b/barretenberg/cpp/src/barretenberg/stdlib/recursion/honk/verifier/merge_recursive_verifier.hpp index 76815921f4b..411b44fd472 100644 --- a/barretenberg/cpp/src/barretenberg/stdlib/recursion/honk/verifier/merge_recursive_verifier.hpp +++ b/barretenberg/cpp/src/barretenberg/stdlib/recursion/honk/verifier/merge_recursive_verifier.hpp @@ -1,6 +1,6 @@ #pragma once #include "barretenberg/commitment_schemes/kzg/kzg.hpp" -#include "barretenberg/plonk/proof_system/types/proof.hpp" +#include "barretenberg/honk/proof_system/types/proof.hpp" #include "barretenberg/stdlib/primitives/curves/bn254.hpp" #include "barretenberg/stdlib/recursion/honk/transcript/transcript.hpp" @@ -23,7 +23,7 @@ template class MergeRecursiveVerifier_ { explicit MergeRecursiveVerifier_(CircuitBuilder* builder); - PairingPoints verify_proof(const plonk::proof& proof); + PairingPoints verify_proof(const bb::honk::proof& proof); }; } // namespace bb::stdlib::recursion::goblin diff --git a/barretenberg/cpp/src/barretenberg/stdlib/recursion/honk/verifier/protogalaxy_recursive_verifier.cpp b/barretenberg/cpp/src/barretenberg/stdlib/recursion/honk/verifier/protogalaxy_recursive_verifier.cpp new file mode 100644 index 00000000000..d1fdaeee01b --- /dev/null +++ b/barretenberg/cpp/src/barretenberg/stdlib/recursion/honk/verifier/protogalaxy_recursive_verifier.cpp @@ -0,0 +1,320 @@ +#include "protogalaxy_recursive_verifier.hpp" +#include "barretenberg/polynomials/polynomial.hpp" +#include "barretenberg/proof_system/library/grand_product_delta.hpp" +namespace bb::stdlib::recursion::honk { + +template +void ProtoGalaxyRecursiveVerifier_::receive_accumulator(const std::shared_ptr& inst, + const std::string& domain_separator) +{ + // Get circuit parameters + const auto instance_size = transcript->template receive_from_prover(domain_separator + "_instance_size"); + const auto public_input_size = + transcript->template receive_from_prover(domain_separator + "_public_input_size"); + inst->instance_size = uint32_t(instance_size.get_value()); + inst->log_instance_size = uint32_t(numeric::get_msb(inst->instance_size)); + inst->public_input_size = uint32_t(public_input_size.get_value()); + + // Get folded public inputs + for (size_t i = 0; i < inst->public_input_size; ++i) { + auto public_input_i = + transcript->template receive_from_prover(domain_separator + "_public_input_" + std::to_string(i)); + inst->public_inputs.emplace_back(public_input_i); + } + + // Get folded relation parameters + auto eta = transcript->template receive_from_prover(domain_separator + "_eta"); + auto beta = transcript->template receive_from_prover(domain_separator + "_beta"); + auto gamma = transcript->template receive_from_prover(domain_separator + "_gamma"); + auto public_input_delta = transcript->template receive_from_prover(domain_separator + "_public_input_delta"); + auto lookup_grand_product_delta = + transcript->template receive_from_prover(domain_separator + "_lookup_grand_product_delta"); + inst->relation_parameters = + RelationParameters{ eta, beta, gamma, public_input_delta, lookup_grand_product_delta }; + + // Get the folded relation separator challenges \vec{α} + for (size_t idx = 0; idx < NUM_SUBRELATIONS - 1; idx++) { + inst->alphas[idx] = + transcript->template receive_from_prover(domain_separator + "_alpha_" + std::to_string(idx)); + } + + inst->target_sum = transcript->template receive_from_prover(domain_separator + "_target_sum"); + + // Get the folded gate challenges, \vec{β} in the paper + inst->gate_challenges = std::vector(inst->log_instance_size); + for (size_t idx = 0; idx < inst->log_instance_size; idx++) { + inst->gate_challenges[idx] = + transcript->template receive_from_prover(domain_separator + "_gate_challenge_" + std::to_string(idx)); + } + + // Get the folded commitments to all witness polynomials + auto comm_view = inst->witness_commitments.get_all(); + auto witness_labels = inst->commitment_labels.get_witness(); + for (size_t idx = 0; idx < witness_labels.size(); idx++) { + comm_view[idx] = + transcript->template receive_from_prover(domain_separator + "_" + witness_labels[idx]); + } + + // Get the folded commitments to selector polynomials + inst->verification_key = std::make_shared(inst->instance_size, inst->public_input_size); + auto vk_view = inst->verification_key->get_all(); + auto vk_labels = inst->commitment_labels.get_precomputed(); + for (size_t idx = 0; idx < vk_labels.size(); idx++) { + vk_view[idx] = transcript->template receive_from_prover(domain_separator + "_" + vk_labels[idx]); + } +} + +template +void ProtoGalaxyRecursiveVerifier_::receive_and_finalise_instance( + const std::shared_ptr& inst, const std::string& domain_separator) +{ + // Get circuit parameters and the public inputs + const auto instance_size = transcript->template receive_from_prover(domain_separator + "_instance_size"); + const auto public_input_size = + transcript->template receive_from_prover(domain_separator + "_public_input_size"); + inst->instance_size = uint32_t(instance_size.get_value()); + inst->log_instance_size = static_cast(numeric::get_msb(inst->instance_size)); + inst->public_input_size = uint32_t(public_input_size.get_value()); + + for (size_t i = 0; i < inst->public_input_size; ++i) { + auto public_input_i = + transcript->template receive_from_prover(domain_separator + "_public_input_" + std::to_string(i)); + inst->public_inputs.emplace_back(public_input_i); + } + + const auto pub_inputs_offset = + transcript->template receive_from_prover(domain_separator + "_pub_inputs_offset"); + + inst->pub_inputs_offset = uint32_t(pub_inputs_offset.get_value()); + + // Get commitments to first three wire polynomials + auto labels = inst->commitment_labels; + auto& witness_commitments = inst->witness_commitments; + witness_commitments.w_l = transcript->template receive_from_prover(domain_separator + "_" + labels.w_l); + witness_commitments.w_r = transcript->template receive_from_prover(domain_separator + "_" + labels.w_r); + witness_commitments.w_o = transcript->template receive_from_prover(domain_separator + "_" + labels.w_o); + + // Get challenge for sorted list batching and wire four memory records commitment + auto eta = transcript->get_challenge(domain_separator + "_eta"); + witness_commitments.sorted_accum = + transcript->template receive_from_prover(domain_separator + "_" + labels.sorted_accum); + witness_commitments.w_4 = transcript->template receive_from_prover(domain_separator + "_" + labels.w_4); + + // Get permutation challenges and commitment to permutation and lookup grand products + auto [beta, gamma] = transcript->get_challenges(domain_separator + "_beta", domain_separator + "_gamma"); + witness_commitments.z_perm = + transcript->template receive_from_prover(domain_separator + "_" + labels.z_perm); + witness_commitments.z_lookup = + transcript->template receive_from_prover(domain_separator + "_" + labels.z_lookup); + + // Compute correction terms for grand products + const FF public_input_delta = bb::honk::compute_public_input_delta( + inst->public_inputs, beta, gamma, inst->instance_size, inst->pub_inputs_offset); + const FF lookup_grand_product_delta = + bb::honk::compute_lookup_grand_product_delta(beta, gamma, inst->instance_size); + inst->relation_parameters = + RelationParameters{ eta, beta, gamma, public_input_delta, lookup_grand_product_delta }; + + // Get the relation separation challenges + for (size_t idx = 0; idx < NUM_SUBRELATIONS - 1; idx++) { + inst->alphas[idx] = transcript->get_challenge(domain_separator + "_alpha_" + std::to_string(idx)); + } + + // Get the commitments to the selector polynomials for the given instance + inst->verification_key = std::make_shared(inst->instance_size, inst->public_input_size); + auto vk_view = inst->verification_key->get_all(); + auto vk_labels = labels.get_precomputed(); + for (size_t idx = 0; idx < vk_labels.size(); idx++) { + vk_view[idx] = transcript->template receive_from_prover(domain_separator + "_" + vk_labels[idx]); + } +} + +// TODO(https://github.com/AztecProtocol/barretenberg/issues/795): The rounds prior to actual verifying are common +// between decider and folding verifier and could be somehow shared so we do not duplicate code so much. +template void ProtoGalaxyRecursiveVerifier_::prepare_for_folding() +{ + auto index = 0; + auto inst = instances[0]; + auto domain_separator = std::to_string(index); + const auto is_accumulator = transcript->template receive_from_prover(domain_separator + "is_accumulator"); + inst->is_accumulator = static_cast(is_accumulator.get_value()); + if (inst->is_accumulator) { + receive_accumulator(inst, domain_separator); + } else { + // This is the first round of folding and we need to generate some gate challenges. + // TODO(https://github.com/AztecProtocol/barretenberg/issues/740): implement option 2 to make this more + // efficient by avoiding the computation of the perturbator + receive_and_finalise_instance(inst, domain_separator); + inst->target_sum = 0; + auto beta = transcript->get_challenge(domain_separator + "_initial_gate_challenge"); + std::vector gate_challenges(inst->log_instance_size); + gate_challenges[0] = beta; + for (size_t i = 1; i < inst->log_instance_size; i++) { + gate_challenges[i] = gate_challenges[i - 1].sqr(); + } + inst->gate_challenges = gate_challenges; + } + index++; + + for (auto it = instances.begin() + 1; it != instances.end(); it++, index++) { + auto inst = *it; + auto domain_separator = std::to_string(index); + receive_and_finalise_instance(inst, domain_separator); + } +} + +template +void ProtoGalaxyRecursiveVerifier_::verify_folding_proof(const bb::honk::proof& proof) +{ + using Transcript = typename Flavor::Transcript; + using ElementNative = typename Flavor::Curve::ElementNative; + using AffineElementNative = typename Flavor::Curve::AffineElementNative; + using ScalarNative = typename Flavor::Curve::ScalarFieldNative; + + transcript = std::make_shared(builder, proof); + prepare_for_folding(); + + auto delta = transcript->get_challenge("delta"); + auto accumulator = get_accumulator(); + auto deltas = compute_round_challenge_pows(accumulator->log_instance_size, delta); + + std::vector perturbator_coeffs(accumulator->log_instance_size + 1); + for (size_t idx = 0; idx <= accumulator->log_instance_size; idx++) { + perturbator_coeffs[idx] = transcript->template receive_from_prover("perturbator_" + std::to_string(idx)); + } + + // TODO(https://github.com/AztecProtocol/barretenberg/issues/833): As currently the stdlib transcript is not + // creating proper constraints linked to Fiat-Shamir we add an additonal gate to ensure assert_equal is correct. + // This comparison to 0 can be removed here and below once we have merged the transcript. + auto zero = FF::from_witness(builder, ScalarNative(0)); + zero.assert_equal(accumulator->target_sum - perturbator_coeffs[0], "F(0) != e"); + + FF perturbator_challenge = transcript->get_challenge("perturbator_challenge"); + + auto perturbator_at_challenge = evaluate_perturbator(perturbator_coeffs, perturbator_challenge); + // The degree of K(X) is dk - k - 1 = k(d - 1) - 1. Hence we need k(d - 1) evaluations to represent it. + std::array combiner_quotient_evals; + for (size_t idx = 0; idx < VerifierInstances::BATCHED_EXTENDED_LENGTH - VerifierInstances::NUM; idx++) { + combiner_quotient_evals[idx] = transcript->template receive_from_prover( + "combiner_quotient_" + std::to_string(idx + VerifierInstances::NUM)); + } + Univariate combiner_quotient( + combiner_quotient_evals); + FF combiner_challenge = transcript->get_challenge("combiner_quotient_challenge"); + auto combiner_quotient_at_challenge = combiner_quotient.evaluate(combiner_challenge); // fine recursive i think + + auto vanishing_polynomial_at_challenge = combiner_challenge * (combiner_challenge - FF(1)); + auto lagranges = std::vector{ FF(1) - combiner_challenge, combiner_challenge }; + + // Compute next folding parameters and verify against the ones received from the prover + auto expected_next_target_sum = + perturbator_at_challenge * lagranges[0] + vanishing_polynomial_at_challenge * combiner_quotient_at_challenge; + auto next_target_sum = transcript->template receive_from_prover("next_target_sum"); + zero.assert_equal(expected_next_target_sum - next_target_sum, "next target sum mismatch"); + + auto expected_betas_star = update_gate_challenges(perturbator_challenge, accumulator->gate_challenges, deltas); + for (size_t idx = 0; idx < accumulator->log_instance_size; idx++) { + auto beta_star = transcript->template receive_from_prover("next_gate_challenge_" + std::to_string(idx)); + zero.assert_equal(beta_star - expected_betas_star[idx], + " next gate challenge mismatch at: " + std::to_string(idx)); + } + + // Compute ϕ and verify against the data received from the prover + WitnessCommitments acc_witness_commitments; + auto witness_labels = commitment_labels.get_witness(); + size_t comm_idx = 0; + auto random_generator = Commitment::from_witness(builder, AffineElementNative(ElementNative::random_element())); + for (auto& expected_comm : acc_witness_commitments.get_all()) { + expected_comm = random_generator; + size_t inst = 0; + for (auto& instance : instances) { + expected_comm = expected_comm + instance->witness_commitments.get_all()[comm_idx] * lagranges[inst]; + inst++; + } + auto comm = transcript->template receive_from_prover("next_" + witness_labels[comm_idx]); + auto res = expected_comm - comm; + random_generator.x.assert_equal(res.x); + random_generator.y.assert_equal(res.y); + comm_idx++; + } + + std::vector folded_public_inputs(instances[0]->public_inputs.size(), 0); + size_t public_input_idx = 0; + for (auto& expected_public_input : folded_public_inputs) { + size_t inst = 0; + for (auto& instance : instances) { + expected_public_input += instance->public_inputs[public_input_idx] * lagranges[inst]; + inst++; + } + auto next_public_input = + transcript->template receive_from_prover("next_public_input" + std::to_string(public_input_idx)); + zero.assert_equal(expected_public_input - next_public_input, + "folded public input mismatch at: " + std::to_string(public_input_idx)); + public_input_idx++; + } + + for (size_t alpha_idx = 0; alpha_idx < NUM_SUBRELATIONS - 1; alpha_idx++) { + FF expected_alpha(0); + size_t instance_idx = 0; + for (auto& instance : instances) { + expected_alpha += instance->alphas[alpha_idx] * lagranges[instance_idx]; + instance_idx++; + } + auto next_alpha = transcript->template receive_from_prover("next_alpha_" + std::to_string(alpha_idx)); + zero.assert_equal(expected_alpha - next_alpha, + "folded relation separator mismatch at: " + std::to_string(alpha_idx)); + } + + auto expected_parameters = bb::RelationParameters{}; + for (size_t inst_idx = 0; inst_idx < VerifierInstances::NUM; inst_idx++) { + auto instance = instances[inst_idx]; + expected_parameters.eta += instance->relation_parameters.eta * lagranges[inst_idx]; + expected_parameters.beta += instance->relation_parameters.beta * lagranges[inst_idx]; + expected_parameters.gamma += instance->relation_parameters.gamma * lagranges[inst_idx]; + expected_parameters.public_input_delta += + instance->relation_parameters.public_input_delta * lagranges[inst_idx]; + expected_parameters.lookup_grand_product_delta += + instance->relation_parameters.lookup_grand_product_delta * lagranges[inst_idx]; + } + + auto next_eta = transcript->template receive_from_prover("next_eta"); + zero.assert_equal(expected_parameters.eta - next_eta, "relation parameter eta mismatch"); + + auto next_beta = transcript->template receive_from_prover("next_beta"); + zero.assert_equal(expected_parameters.beta - next_beta, "relation parameter beta mismatch"); + + auto next_gamma = transcript->template receive_from_prover("next_gamma"); + zero.assert_equal(expected_parameters.gamma - next_gamma, "relation parameter gamma mismatch"); + + auto next_public_input_delta = transcript->template receive_from_prover("next_public_input_delta"); + zero.assert_equal(expected_parameters.public_input_delta - next_public_input_delta, + "relation parameter public input delta mismatch"); + + auto next_lookup_grand_product_delta = + transcript->template receive_from_prover("next_lookup_grand_product_delta"); + zero.assert_equal(expected_parameters.lookup_grand_product_delta - next_lookup_grand_product_delta, + "relation parameter lookup grand product delta mismatch"); + + auto acc_vk = std::make_shared(instances[0]->instance_size, instances[0]->public_input_size); + auto vk_labels = commitment_labels.get_precomputed(); + size_t vk_idx = 0; + for (auto& expected_vk : acc_vk->get_all()) { + size_t inst = 0; + expected_vk = random_generator; + for (auto& instance : instances) { + expected_vk = expected_vk + instance->verification_key->get_all()[vk_idx] * lagranges[inst]; + inst++; + } + auto vk = transcript->template receive_from_prover("next_" + vk_labels[vk_idx]); + auto res = expected_vk - vk; + random_generator.x.assert_equal(res.x); + random_generator.y.assert_equal(res.y); + vk_idx++; + } +} + +template class ProtoGalaxyRecursiveVerifier_< + bb::honk::VerifierInstances_, 2>>; +template class ProtoGalaxyRecursiveVerifier_< + bb::honk::VerifierInstances_, 2>>; +} // namespace bb::stdlib::recursion::honk \ No newline at end of file diff --git a/barretenberg/cpp/src/barretenberg/stdlib/recursion/honk/verifier/protogalaxy_recursive_verifier.hpp b/barretenberg/cpp/src/barretenberg/stdlib/recursion/honk/verifier/protogalaxy_recursive_verifier.hpp new file mode 100644 index 00000000000..7d4305a7ff5 --- /dev/null +++ b/barretenberg/cpp/src/barretenberg/stdlib/recursion/honk/verifier/protogalaxy_recursive_verifier.hpp @@ -0,0 +1,118 @@ +#pragma once +#include "barretenberg/flavor/flavor.hpp" +#include "barretenberg/flavor/goblin_ultra_recursive.hpp" +#include "barretenberg/flavor/ultra_recursive.hpp" +#include "barretenberg/honk/proof_system/types/proof.hpp" +#include "barretenberg/protogalaxy/folding_result.hpp" +#include "barretenberg/stdlib/recursion/honk/transcript/transcript.hpp" +#include "barretenberg/sumcheck/instance/instances.hpp" + +namespace bb::stdlib::recursion::honk { +template class ProtoGalaxyRecursiveVerifier_ { + public: + using Flavor = typename VerifierInstances::Flavor; + using FF = typename Flavor::FF; + using Commitment = typename Flavor::Commitment; + using GroupElement = typename Flavor::GroupElement; + using Instance = typename VerifierInstances::Instance; + using VerificationKey = typename Flavor::VerificationKey; + using WitnessCommitments = typename Flavor::WitnessCommitments; + using CommitmentLabels = typename Flavor::CommitmentLabels; + using Builder = typename Flavor::CircuitBuilder; + using RelationSeparator = typename Flavor::RelationSeparator; + using PairingPoints = std::array; + + static constexpr size_t NUM_SUBRELATIONS = Flavor::NUM_SUBRELATIONS; + + VerifierInstances instances; + + CommitmentLabels commitment_labels; + + Builder* builder; + std::shared_ptr> transcript; + + explicit ProtoGalaxyRecursiveVerifier_(Builder* builder) + : instances(VerifierInstances()) + , builder(builder){}; + /** + * @brief Given a new round challenge δ for each iteration of the full ProtoGalaxy protocol, compute the vector + * [δ, δ^2,..., δ^t] where t = logn and n is the size of the instance. + */ + static std::vector compute_round_challenge_pows(size_t log_instance_size, FF round_challenge) + { + std::vector pows(log_instance_size); + pows[0] = round_challenge; + for (size_t i = 1; i < log_instance_size; i++) { + pows[i] = pows[i - 1].sqr(); + } + return pows; + } + + static std::vector update_gate_challenges(const FF perturbator_challenge, + const std::vector& gate_challenges, + const std::vector& round_challenges) + { + auto log_instance_size = gate_challenges.size(); + std::vector next_gate_challenges(log_instance_size); + + for (size_t idx = 0; idx < log_instance_size; idx++) { + next_gate_challenges[idx] = gate_challenges[idx] + perturbator_challenge * round_challenges[idx]; + } + return next_gate_challenges; + } + + std::shared_ptr get_accumulator() { return instances[0]; } + + /** + * @brief Instatiate the instances and the transcript. + * + * @param fold_data The data transmitted via the transcript by the prover. + */ + void prepare_for_folding(); + + /** + * @brief Instantiate the accumulator (i.e. the relaxed instance) from the transcript. + * + */ + void receive_accumulator(const std::shared_ptr&, const std::string&); + + /** + * @brief Process the public data ϕ for the Instances to be folded. + * + */ + void receive_and_finalise_instance(const std::shared_ptr&, const std::string&); + + /** + * @brief Run the folding protocol on the verifier side to establish whether the public data ϕ of the new + * accumulator, received from the prover is the same as that produced by the verifier. + * + * @details In the recursive setting this function doesn't return anything because the equality checks performed by + * the recursive verifier, ensuring the folded ϕ*, e* and β* on the verifier side correspond to what has been sent + * by the prover, are expressed as constraints. + + */ + void verify_folding_proof(const bb::honk::proof& proof); + + /** + * @brief Evaluates the perturbator at a given scalar, in a sequential manner for the recursive setting. + * + * @details This method is equivalent to the one in the Polynomial class for evaluating a polynomial, represented by + * coefficients in monomial basis, at a given point. The Polynomial class is used in the native verifier for + * constructing and computing the perturbator. We implement this separate functionality here in the recursive + * folding verifier to avoid instantiating the entire Polynomial class on stdlib::bn254. Furthermore, the evaluation + * needs to be done sequentially as we don't support a parallel_for in circuits. + * + */ + static FF evaluate_perturbator(std::vector coeffs, FF point) + { + FF point_acc = FF(1); + FF result = FF(0); + for (size_t i = 0; i < coeffs.size(); i++) { + result += coeffs[i] * point_acc; + point_acc *= point; + } + return result; + }; +}; + +} // namespace bb::stdlib::recursion::honk \ No newline at end of file diff --git a/barretenberg/cpp/src/barretenberg/stdlib/recursion/honk/verifier/protogalaxy_recursive_verifier.test.cpp b/barretenberg/cpp/src/barretenberg/stdlib/recursion/honk/verifier/protogalaxy_recursive_verifier.test.cpp new file mode 100644 index 00000000000..61dee74084f --- /dev/null +++ b/barretenberg/cpp/src/barretenberg/stdlib/recursion/honk/verifier/protogalaxy_recursive_verifier.test.cpp @@ -0,0 +1,350 @@ +#include "barretenberg/stdlib/recursion/honk/verifier/protogalaxy_recursive_verifier.hpp" +#include "barretenberg/common/test.hpp" +#include "barretenberg/flavor/ultra_recursive.hpp" +#include "barretenberg/stdlib/hash/blake3s/blake3s.hpp" +#include "barretenberg/stdlib/hash/pedersen/pedersen.hpp" +#include "barretenberg/stdlib/primitives/curves/bn254.hpp" +#include "barretenberg/stdlib/recursion/honk/verifier/decider_recursive_verifier.hpp" +#include "barretenberg/ultra_honk/ultra_composer.hpp" + +namespace bb::stdlib::recursion::honk { +class ProtogalaxyRecursiveTest : public testing::Test { + public: + // Define types relevant for testing + using UltraFlavor = ::bb::honk::flavor::Ultra; + using GoblinUltraFlavor = ::bb::honk::flavor::GoblinUltra; + using UltraComposer = ::bb::honk::UltraComposer_; + using GoblinUltraComposer = ::bb::honk::UltraComposer_; + + using InnerFlavor = UltraFlavor; + using InnerComposer = UltraComposer; + using Instance = ::bb::honk::ProverInstance_; + using InnerBuilder = typename InnerComposer::CircuitBuilder; + using InnerCurve = bn254; + using Commitment = InnerFlavor::Commitment; + using FF = InnerFlavor::FF; + + // Types for recursive verifier circuit + // cannot do on Goblin + using OuterBuilder = GoblinUltraCircuitBuilder; + using RecursiveFlavor = ::bb::honk::flavor::UltraRecursive_; + using RecursiveVerifierInstances = ::bb::honk::VerifierInstances_; + using FoldingRecursiveVerifier = ProtoGalaxyRecursiveVerifier_; + using DeciderRecursiveVerifier = DeciderRecursiveVerifier_; + using DeciderVerifier = ::bb::honk::DeciderVerifier_; + using NativeVerifierInstances = ::bb::honk::VerifierInstances_; + using NativeFoldingVerifier = bb::honk::ProtoGalaxyVerifier_; + + // Helper for getting composer for prover/verifier of recursive (outer) circuit + template static auto get_outer_composer() + { + if constexpr (IsGoblinBuilder) { + return GoblinUltraComposer(); + } else { + return UltraComposer(); + } + } + + /** + * @brief Create a non-trivial arbitrary inner circuit, the proof of which will be recursively verified + * + * @param builder + * @param public_inputs + * @param log_num_gates + * + * TODO(https://github.com/AztecProtocol/barretenberg/issues/744): make testing utility with functionality shared + * amongst test files + */ + static void create_inner_circuit(InnerBuilder& builder, size_t log_num_gates = 10) + { + using fr_ct = InnerCurve::ScalarField; + using fq_ct = InnerCurve::BaseField; + using public_witness_ct = InnerCurve::public_witness_ct; + using witness_ct = InnerCurve::witness_ct; + using byte_array_ct = InnerCurve::byte_array_ct; + using fr = typename InnerCurve::ScalarFieldNative; + + // Create 2^log_n many add gates based on input log num gates + const size_t num_gates = 1 << log_num_gates; + for (size_t i = 0; i < num_gates; ++i) { + fr a = fr::random_element(); + uint32_t a_idx = builder.add_variable(a); + + fr b = fr::random_element(); + fr c = fr::random_element(); + fr d = a + b + c; + uint32_t b_idx = builder.add_variable(b); + uint32_t c_idx = builder.add_variable(c); + uint32_t d_idx = builder.add_variable(d); + + builder.create_big_add_gate({ a_idx, b_idx, c_idx, d_idx, fr(1), fr(1), fr(1), fr(-1), fr(0) }); + } + + // Define some additional non-trivial but arbitrary circuit logic + fr_ct a(public_witness_ct(&builder, fr::random_element())); + fr_ct b(public_witness_ct(&builder, fr::random_element())); + fr_ct c(public_witness_ct(&builder, fr::random_element())); + + for (size_t i = 0; i < 32; ++i) { + a = (a * b) + b + a; + a = a.madd(b, c); + } + pedersen_hash::hash({ a, b }); + byte_array_ct to_hash(&builder, "nonsense test data"); + blake3s(to_hash); + + fr bigfield_data = fr::random_element(); + fr bigfield_data_a{ bigfield_data.data[0], bigfield_data.data[1], 0, 0 }; + fr bigfield_data_b{ bigfield_data.data[2], bigfield_data.data[3], 0, 0 }; + + fq_ct big_a(fr_ct(witness_ct(&builder, bigfield_data_a.to_montgomery_form())), fr_ct(witness_ct(&builder, 0))); + fq_ct big_b(fr_ct(witness_ct(&builder, bigfield_data_b.to_montgomery_form())), fr_ct(witness_ct(&builder, 0))); + + big_a* big_b; + }; + + public: + static void SetUpTestSuite() { bb::srs::init_crs_factory("../srs_db/ignition"); } + + static std::shared_ptr fold_and_verify(const std::vector>& instances, + InnerComposer& inner_composer) + { + // Generate a folding proof + auto inner_folding_prover = inner_composer.create_folding_prover(instances); + auto inner_folding_proof = inner_folding_prover.fold_instances(); + + // Create a recursive folding verifier circuit for the folding proof of the two instances + OuterBuilder outer_folding_circuit; + FoldingRecursiveVerifier verifier{ &outer_folding_circuit }; + verifier.verify_folding_proof(inner_folding_proof.folding_data); + info("Recursive Verifier with Ultra instances: num gates = ", outer_folding_circuit.num_gates); + + // Perform native folding verification and ensure it returns the same result (either true or false) as calling + // check_circuit on the recursive folding verifier + auto native_folding_verifier = inner_composer.create_folding_verifier(); + auto native_folding_result = native_folding_verifier.verify_folding_proof(inner_folding_proof.folding_data); + EXPECT_EQ(native_folding_result, outer_folding_circuit.check_circuit()); + + // Ensure that the underlying native and recursive folding verification algorithms agree by ensuring + // the manifests produced by each agree. + auto recursive_folding_manifest = verifier.transcript->get_manifest(); + auto native_folding_manifest = native_folding_verifier.transcript->get_manifest(); + + for (size_t i = 0; i < recursive_folding_manifest.size(); ++i) { + EXPECT_EQ(recursive_folding_manifest[i], native_folding_manifest[i]); + } + + // Check for a failure flag in the recursive verifier circuit + EXPECT_EQ(outer_folding_circuit.failed(), false) << outer_folding_circuit.err(); + + return inner_folding_proof.accumulator; + } +}; +/** + * @brief Create inner circuit and call check_circuit on it + * + */ +TEST_F(ProtogalaxyRecursiveTest, InnerCircuit) +{ + InnerBuilder builder; + + create_inner_circuit(builder); + + bool result = builder.check_circuit(); + EXPECT_EQ(result, true); +} + +/** + * @brief Ensure that evaluating the perturbator in the recursive folding verifier returns the same result as + * evaluating in Polynomial class. + * + */ +TEST_F(ProtogalaxyRecursiveTest, NewEvaluate) +{ + OuterBuilder builder; + using fr_ct = bn254::ScalarField; + using fr = bn254::ScalarFieldNative; + + std::vector coeffs; + std::vector coeffs_ct; + for (size_t idx = 0; idx < 8; idx++) { + auto el = fr::random_element(); + coeffs.emplace_back(el); + coeffs_ct.emplace_back(fr_ct(&builder, el)); + } + Polynomial poly(coeffs); + fr point = fr::random_element(); + fr_ct point_ct(fr_ct(&builder, point)); + auto res1 = poly.evaluate(point); + + auto res2 = FoldingRecursiveVerifier::evaluate_perturbator(coeffs_ct, point_ct); + EXPECT_EQ(res1, res2.get_value()); +} + +/** + * @brief Tests a simple recursive fold that is valid works as expected. + * + */ +TEST_F(ProtogalaxyRecursiveTest, RecursiveFoldingTest) +{ + // Create two arbitrary circuits for the first round of folding + InnerBuilder builder1; + + create_inner_circuit(builder1); + InnerBuilder builder2; + builder2.add_public_variable(FF(1)); + create_inner_circuit(builder2); + + InnerComposer inner_composer = InnerComposer(); + auto instance1 = inner_composer.create_instance(builder1); + auto instance2 = inner_composer.create_instance(builder2); + auto instances = std::vector>{ instance1, instance2 }; + + fold_and_verify(instances, inner_composer); +} + +/** + * @brief Recursively verify two rounds of folding valid circuits and then recursive verify the final decider proof, + * make sure the verifer circuits pass check_circuit(). Ensure that the algorithm of the recursive and native verifiers + * are identical by checking the manifests + + */ +TEST_F(ProtogalaxyRecursiveTest, FullProtogalaxyRecursiveTest) +{ + + // Create two arbitrary circuits for the first round of folding + InnerBuilder builder1; + + create_inner_circuit(builder1); + InnerBuilder builder2; + builder2.add_public_variable(FF(1)); + create_inner_circuit(builder2); + + InnerComposer inner_composer = InnerComposer(); + auto instance1 = inner_composer.create_instance(builder1); + auto instance2 = inner_composer.create_instance(builder2); + auto instances = std::vector>{ instance1, instance2 }; + + auto accumulator = fold_and_verify(instances, inner_composer); + + // Create another circuit to do a second round of folding + InnerBuilder builder3; + create_inner_circuit(builder3); + auto instance3 = inner_composer.create_instance(builder3); + instances = std::vector>{ accumulator, instance3 }; + + accumulator = fold_and_verify(instances, inner_composer); + + // Create a decider proof for the relaxed instance obtained through folding + auto inner_decider_prover = inner_composer.create_decider_prover(accumulator); + auto inner_decider_proof = inner_decider_prover.construct_proof(); + + // Create a decider verifier circuit for recursively verifying the decider proof + OuterBuilder outer_decider_circuit; + DeciderRecursiveVerifier decider_verifier{ &outer_decider_circuit }; + auto pairing_points = decider_verifier.verify_proof(inner_decider_proof); + info("Decider Recursive Verifier: num gates = ", outer_decider_circuit.num_gates); + // Check for a failure flag in the recursive verifier circuit + EXPECT_EQ(outer_decider_circuit.failed(), false) << outer_decider_circuit.err(); + + // Perform native verification then perform the pairing on the outputs of the recursive + // decider verifier and check that the result agrees. + DeciderVerifier native_decider_verifier = inner_composer.create_decider_verifier(accumulator); + auto native_result = native_decider_verifier.verify_proof(inner_decider_proof); + auto recursive_result = native_decider_verifier.pcs_verification_key->pairing_check(pairing_points[0].get_value(), + pairing_points[1].get_value()); + EXPECT_EQ(native_result, recursive_result); + + // Ensure that the underlying native and recursive decider verification algorithms agree by ensuring + // the manifests produced are the same. + auto recursive_decider_manifest = decider_verifier.transcript->get_manifest(); + auto native_decider_manifest = native_decider_verifier.transcript->get_manifest(); + for (size_t i = 0; i < recursive_decider_manifest.size(); ++i) { + EXPECT_EQ(recursive_decider_manifest[i], native_decider_manifest[i]); + } + + // Construct and verify a proof of the recursive decider verifier circuit + { + auto composer = get_outer_composer(); + auto instance = composer.create_instance(outer_decider_circuit); + auto prover = composer.create_prover(instance); + auto verifier = composer.create_verifier(instance); + auto proof = prover.construct_proof(); + bool verified = verifier.verify_proof(proof); + + ASSERT(verified); + } +} + +TEST_F(ProtogalaxyRecursiveTest, TamperedDeciderProof) +{ + // Create two arbitrary circuits for the first round of folding + InnerBuilder builder1; + + create_inner_circuit(builder1); + InnerBuilder builder2; + builder2.add_public_variable(FF(1)); + create_inner_circuit(builder2); + + InnerComposer inner_composer = InnerComposer(); + auto instance1 = inner_composer.create_instance(builder1); + auto instance2 = inner_composer.create_instance(builder2); + auto instances = std::vector>{ instance1, instance2 }; + + auto accumulator = fold_and_verify(instances, inner_composer); + + // Tamper with the accumulator by changing the target sum + accumulator->target_sum = FF::random_element(); + + // Create a decider proof for the relaxed instance obtained through folding + auto inner_decider_prover = inner_composer.create_decider_prover(accumulator); + auto inner_decider_proof = inner_decider_prover.construct_proof(); + + // Create a decider verifier circuit for recursively verifying the decider proof + OuterBuilder outer_decider_circuit; + DeciderRecursiveVerifier decider_verifier{ &outer_decider_circuit }; + decider_verifier.verify_proof(inner_decider_proof); + info("Decider Recursive Verifier: num gates = ", outer_decider_circuit.num_gates); + + // We expect the decider circuit check to fail due to the bad proof + EXPECT_FALSE(outer_decider_circuit.check_circuit()); +} + +TEST_F(ProtogalaxyRecursiveTest, TamperedAccumulator) +{ + // Create two arbitrary circuits for the first round of folding + InnerBuilder builder1; + + create_inner_circuit(builder1); + InnerBuilder builder2; + builder2.add_public_variable(FF(1)); + create_inner_circuit(builder2); + + InnerComposer inner_composer = InnerComposer(); + auto instance1 = inner_composer.create_instance(builder1); + auto instance2 = inner_composer.create_instance(builder2); + auto instances = std::vector>{ instance1, instance2 }; + + auto accumulator = fold_and_verify(instances, inner_composer); + + // Create another circuit to do a second round of folding + InnerBuilder builder3; + create_inner_circuit(builder3); + auto instance3 = inner_composer.create_instance(builder3); + + // Tamper with the accumulator + instances = std::vector>{ accumulator, instance3 }; + accumulator->prover_polynomials.w_l[1] = FF::random_element(); + + // Generate a folding proof + auto inner_folding_prover = inner_composer.create_folding_prover(instances); + auto inner_folding_proof = inner_folding_prover.fold_instances(); + + // Create a recursive folding verifier circuit for the folding proof of the two instances + OuterBuilder outer_folding_circuit; + FoldingRecursiveVerifier verifier{ &outer_folding_circuit }; + verifier.verify_folding_proof(inner_folding_proof.folding_data); + EXPECT_EQ(outer_folding_circuit.check_circuit(), false); +} + +} // namespace bb::stdlib::recursion::honk \ No newline at end of file diff --git a/barretenberg/cpp/src/barretenberg/stdlib/recursion/honk/verifier/ultra_recursive_verifier.cpp b/barretenberg/cpp/src/barretenberg/stdlib/recursion/honk/verifier/ultra_recursive_verifier.cpp index 4a147608e44..8e86410d3d0 100644 --- a/barretenberg/cpp/src/barretenberg/stdlib/recursion/honk/verifier/ultra_recursive_verifier.cpp +++ b/barretenberg/cpp/src/barretenberg/stdlib/recursion/honk/verifier/ultra_recursive_verifier.cpp @@ -18,7 +18,7 @@ UltraRecursiveVerifier_::UltraRecursiveVerifier_( * */ template -std::array UltraRecursiveVerifier_::verify_proof(const plonk::proof& proof) +std::array UltraRecursiveVerifier_::verify_proof(const bb::honk::proof& proof) { using Sumcheck = ::bb::honk::sumcheck::SumcheckVerifier; using Curve = typename Flavor::Curve; @@ -30,7 +30,7 @@ std::array UltraRecursiveVerifier_::ve RelationParams relation_parameters; - transcript = std::make_shared(builder, proof.proof_data); + transcript = std::make_shared(builder, proof); VerifierCommitments commitments{ key }; CommitmentLabels commitment_labels; diff --git a/barretenberg/cpp/src/barretenberg/stdlib/recursion/honk/verifier/ultra_recursive_verifier.hpp b/barretenberg/cpp/src/barretenberg/stdlib/recursion/honk/verifier/ultra_recursive_verifier.hpp index f26b44d1116..00bb9a9c780 100644 --- a/barretenberg/cpp/src/barretenberg/stdlib/recursion/honk/verifier/ultra_recursive_verifier.hpp +++ b/barretenberg/cpp/src/barretenberg/stdlib/recursion/honk/verifier/ultra_recursive_verifier.hpp @@ -1,7 +1,7 @@ #pragma once #include "barretenberg/flavor/goblin_ultra_recursive.hpp" #include "barretenberg/flavor/ultra_recursive.hpp" -#include "barretenberg/plonk/proof_system/types/proof.hpp" +#include "barretenberg/honk/proof_system/types/proof.hpp" #include "barretenberg/stdlib/recursion/honk/transcript/transcript.hpp" #include "barretenberg/sumcheck/sumcheck.hpp" @@ -20,15 +20,10 @@ template class UltraRecursiveVerifier_ { explicit UltraRecursiveVerifier_(Builder* builder, const std::shared_ptr& native_verifier_key); - UltraRecursiveVerifier_(UltraRecursiveVerifier_&& other) = delete; - UltraRecursiveVerifier_(const UltraRecursiveVerifier_& other) = delete; - UltraRecursiveVerifier_& operator=(const UltraRecursiveVerifier_& other) = delete; - UltraRecursiveVerifier_& operator=(UltraRecursiveVerifier_&& other) = delete; - ~UltraRecursiveVerifier_() = default; // TODO(luke): Eventually this will return something like aggregation_state but I'm simplifying for now until we // determine the exact interface. Simply returns the two pairing points. - PairingPoints verify_proof(const plonk::proof& proof); + PairingPoints verify_proof(const bb::honk::proof& proof); std::shared_ptr key; std::map commitments; diff --git a/barretenberg/cpp/src/barretenberg/stdlib/utility/utility.hpp b/barretenberg/cpp/src/barretenberg/stdlib/utility/utility.hpp index bec32c7a78c..1759f5fc412 100644 --- a/barretenberg/cpp/src/barretenberg/stdlib/utility/utility.hpp +++ b/barretenberg/cpp/src/barretenberg/stdlib/utility/utility.hpp @@ -117,6 +117,10 @@ template class StdlibTypesUtility { using type = uint32_t; }; + template struct NativeType { + using type = bool; + }; + template struct NativeType { using type = FF; }; diff --git a/barretenberg/cpp/src/barretenberg/transcript/CMakeLists.txt b/barretenberg/cpp/src/barretenberg/transcript/CMakeLists.txt index 5f44c7b2463..ff33c7197e5 100644 --- a/barretenberg/cpp/src/barretenberg/transcript/CMakeLists.txt +++ b/barretenberg/cpp/src/barretenberg/transcript/CMakeLists.txt @@ -1 +1 @@ -barretenberg_module(transcript crypto_blake3s crypto_pedersen_hash) \ No newline at end of file +barretenberg_module(transcript crypto_poseidon2) \ No newline at end of file diff --git a/barretenberg/cpp/src/barretenberg/transcript/transcript.hpp b/barretenberg/cpp/src/barretenberg/transcript/transcript.hpp index cf791175c98..52bd7fe2119 100644 --- a/barretenberg/cpp/src/barretenberg/transcript/transcript.hpp +++ b/barretenberg/cpp/src/barretenberg/transcript/transcript.hpp @@ -1,8 +1,11 @@ #pragma once #include "barretenberg/common/serialize.hpp" -#include "barretenberg/crypto/blake3s/blake3s.hpp" -#include "barretenberg/crypto/pedersen_hash/pedersen.hpp" +#include "barretenberg/crypto/poseidon2/poseidon2.hpp" +#include "barretenberg/ecc/curves/bn254/g1.hpp" +#include "barretenberg/ecc/curves/grumpkin/grumpkin.hpp" +#include "barretenberg/ecc/fields/field_conversion.hpp" +#include "barretenberg/honk/proof_system/types/proof.hpp" // #define LOG_CHALLENGES // #define LOG_INTERACTIONS @@ -10,9 +13,9 @@ namespace bb::honk { template -concept Loggable = (std::same_as || std::same_as || - std::same_as || std::same_as || - std::same_as); +concept Loggable = + (std::same_as || std::same_as || std::same_as || + std::same_as || std::same_as); // class TranscriptManifest; class TranscriptManifest { @@ -61,7 +64,9 @@ class TranscriptManifest { */ class BaseTranscript { public: - using Proof = std::vector; + using Fr = bb::fr; + using Poseidon2Params = crypto::Poseidon2Bn254ScalarFieldParams; + using Proof = honk::proof; BaseTranscript() = default; @@ -76,15 +81,14 @@ class BaseTranscript { static constexpr size_t HASH_OUTPUT_SIZE = 32; std::ptrdiff_t proof_start = 0; - size_t num_bytes_written = 0; // the number of bytes written to proof_data by the prover or the verifier - size_t num_bytes_read = 0; // the number of bytes read from proof_data by the verifier - size_t round_number = 0; // current round for manifest + size_t num_frs_written = 0; // the number of bb::frs written to proof_data by the prover or the verifier + size_t num_frs_read = 0; // the number of bb::frs read from proof_data by the verifier + size_t round_number = 0; // current round for manifest private: - static constexpr size_t MIN_BYTES_PER_CHALLENGE = 128 / 8; // 128 bit challenges bool is_first_challenge = true; // indicates if this is the first challenge this transcript is generating - std::array previous_challenge_buffer{}; // default-initialized to zeros - std::vector current_round_data; + Fr previous_challenge{}; // default-initialized to zeros + std::vector current_round_data; // "Manifest" object that records a summary of the transcript interactions TranscriptManifest manifest; @@ -93,11 +97,11 @@ class BaseTranscript { * @brief Compute next challenge c_next = H( Compress(c_prev || round_buffer) ) * @details This function computes a new challenge for the current round using the previous challenge * and the current round data, if they are exist. It clears the current_round_data if nonempty after - * computing the challenge to minimize how much we compress. It also sets previous_challenge_buffer + * computing the challenge to minimize how much we compress. It also sets previous_challenge * to the current challenge buffer to set up next function call. - * @return std::array + * @return std::array */ - [[nodiscard]] std::array get_next_challenge_buffer() + [[nodiscard]] Fr get_next_challenge_buffer() { // Prevent challenge generation if this is the first challenge we're generating, // AND nothing was sent by the prover. @@ -109,33 +113,31 @@ class BaseTranscript { // TODO(Adrian): Do we want to use a domain separator as the initial challenge buffer? // We could be cheeky and use the hash of the manifest as domain separator, which would prevent us from having // to domain separate all the data. (See https://safe-hash.dev) - std::vector full_buffer; + std::vector full_buffer; if (!is_first_challenge) { - // if not the first challenge, we can use the previous_challenge_buffer - full_buffer.insert(full_buffer.end(), previous_challenge_buffer.begin(), previous_challenge_buffer.end()); + // if not the first challenge, we can use the previous_challenge + full_buffer.emplace_back(previous_challenge); } else { // Update is_first_challenge for the future is_first_challenge = false; } if (!current_round_data.empty()) { - full_buffer.insert(full_buffer.end(), current_round_data.begin(), current_round_data.end()); + // TODO(https://github.com/AztecProtocol/barretenberg/issues/832): investigate why + // full_buffer.insert(full_buffer.end(), current_round_data.begin(), current_round_data.end()); fails to + // compile with gcc + std::copy(current_round_data.begin(), current_round_data.end(), std::back_inserter(full_buffer)); current_round_data.clear(); // clear the round data buffer since it has been used } - // Pre-hash the full buffer to minimize the amount of data passed to the cryptographic hash function. - // Only a collision-resistant hash-function like Pedersen is required for this step. - // Note: this pre-hashing is an efficiency trick that may be discareded if using a SNARK-friendly or in contexts - // (eg smart contract verification) where the cost of elliptic curve operations is high. - std::vector compressed_buffer = to_buffer(crypto::pedersen_hash::hash_buffer(full_buffer)); + // Hash the full buffer with poseidon2, which is believed to be a collision resistant hash function and a random + // oracle, removing the need to pre-hash to compress and then hash with a random oracle, as we previously did + // with Pedersen and Blake3s. + Fr base_hash = crypto::Poseidon2::hash(full_buffer); - // Use a strong hash function to derive the new challenge_buffer. - auto base_hash = blake3::blake3s(compressed_buffer); - - std::array new_challenge_buffer; - std::copy_n(base_hash.begin(), HASH_OUTPUT_SIZE, new_challenge_buffer.begin()); + Fr new_challenge = base_hash; // update previous challenge buffer for next time we call this function - previous_challenge_buffer = new_challenge_buffer; - return new_challenge_buffer; + previous_challenge = new_challenge; + return new_challenge; }; protected: @@ -143,35 +145,35 @@ class BaseTranscript { * @brief Adds challenge elements to the current_round_buffer and updates the manifest. * * @param label of the element sent - * @param element_bytes serialized + * @param element_frs serialized */ - void consume_prover_element_bytes(const std::string& label, std::span element_bytes) + void consume_prover_element_frs(const std::string& label, std::span element_frs) { // Add an entry to the current round of the manifest - manifest.add_entry(round_number, label, element_bytes.size()); + manifest.add_entry(round_number, label, element_frs.size()); - current_round_data.insert(current_round_data.end(), element_bytes.begin(), element_bytes.end()); + current_round_data.insert(current_round_data.end(), element_frs.begin(), element_frs.end()); - num_bytes_written += element_bytes.size(); + num_frs_written += element_frs.size(); } /** * @brief Serializes object and appends it to proof_data * @details Calls to_buffer on element to serialize, and modifies proof_data object by appending the serialized - * bytes to it. + * frs to it. * @tparam T * @param element * @param proof_data */ template void serialize_to_buffer(const T& element, Proof& proof_data) { - auto element_bytes = to_buffer(element); - proof_data.insert(proof_data.end(), element_bytes.begin(), element_bytes.end()); + auto element_frs = bb::field_conversion::convert_to_bn254_frs(element); + proof_data.insert(proof_data.end(), element_frs.begin(), element_frs.end()); } /** - * @brief Deserializes the bytes starting at offset into the typed element and returns that element. - * @details Using the template parameter and the offset argument, this function deserializes the bytes with - * from_buffer and then increments the offset appropriately based on the number of bytes that were deserialized. + * @brief Deserializes the frs starting at offset into the typed element and returns that element. + * @details Using the template parameter and the offset argument, this function deserializes the frs with + * from_buffer and then increments the offset appropriately based on the number of frs that were deserialized. * @tparam T * @param proof_data * @param offset @@ -179,13 +181,13 @@ class BaseTranscript { */ template T deserialize_from_buffer(const Proof& proof_data, size_t& offset) const { - constexpr size_t element_size = sizeof(T); - ASSERT(offset + element_size <= proof_data.size()); + constexpr size_t element_fr_size = bb::field_conversion::calc_num_bn254_frs(); + ASSERT(offset + element_fr_size <= proof_data.size()); - auto element_bytes = std::span{ proof_data }.subspan(offset, element_size); - offset += element_size; + auto element_frs = std::span{ proof_data }.subspan(offset, element_fr_size); + offset += element_fr_size; - T element = from_buffer(element_bytes); + auto element = bb::field_conversion::convert_from_bn254_frs(element_frs); return element; } @@ -198,16 +200,16 @@ class BaseTranscript { * @brief Return the proof data starting at proof_start * @details This is useful for when two different provers share a transcript. */ - std::vector export_proof() + std::vector export_proof() { - std::vector result(num_bytes_written); - std::copy_n(proof_data.begin() + proof_start, num_bytes_written, result.begin()); - proof_start += static_cast(num_bytes_written); - num_bytes_written = 0; + std::vector result(num_frs_written); + std::copy_n(proof_data.begin() + proof_start, num_frs_written, result.begin()); + proof_start += static_cast(num_frs_written); + num_frs_written = 0; return result; }; - void load_proof(const std::vector& proof) + void load_proof(const std::vector& proof) { std::copy(proof.begin(), proof.end(), std::back_inserter(proof_data)); } @@ -221,7 +223,7 @@ class BaseTranscript { * multiple challenges. * * @param labels human-readable names for the challenges for the manifest - * @return std::array challenges for this round. + * @return std::array challenges for this round. */ template std::array get_challenges(const Strings&... labels) { @@ -232,20 +234,23 @@ class BaseTranscript { // Compute the new challenge buffer from which we derive the challenges. - // Create challenges from bytes. + // Create challenges from Frs. std::array challenges{}; // Generate the challenges by iteratively hashing over the previous challenge. for (size_t i = 0; i < num_challenges; i++) { + // TODO(https://github.com/AztecProtocol/barretenberg/issues/741): Optimize this by truncating hash to 128 + // bits or by splitting hash into 2 challenges. + /* auto next_challenge_buffer = get_next_challenge_buffer(); // get next challenge buffer - std::array field_element_buffer{}; - // copy half of the hash to lower 128 bits of challenge - // Note: because of how read() from buffers to fields works (in field_declarations.hpp), - // we use the later half of the buffer - std::copy_n(next_challenge_buffer.begin(), + Fr field_element_buffer = next_challenge_buffer; + // copy half of the hash to lower 128 bits of challenge Note: because of how read() from buffers to fields + works (in field_declarations.hpp), we use the later half of the buffer + // std::copy_n(next_challenge_buffer.begin(), HASH_OUTPUT_SIZE / 2, field_element_buffer.begin() + HASH_OUTPUT_SIZE / 2); - challenges[i] = from_buffer(field_element_buffer); + */ + challenges[i] = static_cast(get_next_challenge_buffer()); } // Prepare for next round. @@ -258,7 +263,7 @@ class BaseTranscript { * @brief Adds a prover message to the transcript, only intended to be used by the prover. * * @details Serializes the provided object into `proof_data`, and updates the current round state in - * consume_prover_element_bytes. + * consume_prover_element_frs. * * @param label Description/name of the object being added. * @param element Serializable object that will be added to the transcript @@ -269,19 +274,19 @@ class BaseTranscript { */ template void send_to_verifier(const std::string& label, const T& element) { - using serialize::write; // TODO(Adrian): Ensure that serialization of affine elements (including point at infinity) is consistent. // TODO(Adrian): Consider restricting serialization (via concepts) to types T for which sizeof(T) reliably - // returns the size of T in bytes. (E.g. this is true for std::array but not for std::vector). - auto element_bytes = to_buffer(element); - proof_data.insert(proof_data.end(), element_bytes.begin(), element_bytes.end()); + // returns the size of T in frs. (E.g. this is true for std::array but not for std::vector). + // convert element to field elements + auto element_frs = bb::field_conversion::convert_to_bn254_frs(element); + proof_data.insert(proof_data.end(), element_frs.begin(), element_frs.end()); #ifdef LOG_INTERACTIONS if constexpr (Loggable) { info("sent: ", label, ": ", element); } #endif - BaseTranscript::consume_prover_element_bytes(label, element_bytes); + BaseTranscript::consume_prover_element_frs(label, element_frs); } /** @@ -292,15 +297,15 @@ class BaseTranscript { */ template T receive_from_prover(const std::string& label) { - constexpr size_t element_size = sizeof(T); - ASSERT(num_bytes_read + element_size <= proof_data.size()); + constexpr size_t element_size = bb::field_conversion::calc_num_bn254_frs(); + ASSERT(num_frs_read + element_size <= proof_data.size()); - auto element_bytes = std::span{ proof_data }.subspan(num_bytes_read, element_size); - num_bytes_read += element_size; + auto element_frs = std::span{ proof_data }.subspan(num_frs_read, element_size); + num_frs_read += element_size; - BaseTranscript::consume_prover_element_bytes(label, element_bytes); + BaseTranscript::consume_prover_element_frs(label, element_frs); - T element = from_buffer(element_bytes); + auto element = bb::field_conversion::convert_from_bn254_frs(element_frs); #ifdef LOG_INTERACTIONS if constexpr (Loggable) { @@ -354,12 +359,12 @@ class BaseTranscript { /** * @brief Convert an array of uint256_t's to an array of field elements - * @details The syntax `std::array [a, b] = transcript.get_challenges("a", "b")` is unfortunately not allowed + * @details The syntax `std::array [a, b] = transcript.get_challenges("a", "b")` is unfortunately not allowed * (structured bindings must be defined with auto return type), so we need a workaround. */ -template std::array challenges_to_field_elements(std::array&& arr) +template std::array challenges_to_field_elements(std::array&& arr) { - std::array result; + std::array result; std::move(arr.begin(), arr.end(), result.begin()); return result; } diff --git a/barretenberg/cpp/src/barretenberg/transcript/transcript.test.cpp b/barretenberg/cpp/src/barretenberg/transcript/transcript.test.cpp index f9698f1a6c8..05ff79c73e5 100644 --- a/barretenberg/cpp/src/barretenberg/transcript/transcript.test.cpp +++ b/barretenberg/cpp/src/barretenberg/transcript/transcript.test.cpp @@ -16,8 +16,8 @@ TEST(BaseTranscript, TwoProversTwoFields) { const auto EXPECT_STATE = [](const Transcript& transcript, size_t start, size_t written, size_t read) { EXPECT_EQ(transcript.proof_start, static_cast(start)); - EXPECT_EQ(transcript.num_bytes_written, written); - EXPECT_EQ(transcript.num_bytes_read, read); + EXPECT_EQ(transcript.num_frs_written, written); + EXPECT_EQ(transcript.num_frs_read, read); }; Transcript prover_transcript; @@ -25,25 +25,87 @@ TEST(BaseTranscript, TwoProversTwoFields) EXPECT_STATE(prover_transcript, /*start*/ 0, /*written*/ 0, /*read*/ 0); Fr elt_a = 1377; prover_transcript.send_to_verifier("a", elt_a); - EXPECT_STATE(prover_transcript, /*start*/ 0, /*written*/ 32, /*read*/ 0); + EXPECT_STATE(prover_transcript, /*start*/ 0, /*written*/ 1, /*read*/ 0); Transcript verifier_transcript{ prover_transcript.export_proof() }; // export resets read/write state and sets start in prep for next export - EXPECT_STATE(prover_transcript, /*start*/ 32, /*written*/ 0, /*read*/ 0); + EXPECT_STATE(prover_transcript, /*start*/ 1, /*written*/ 0, /*read*/ 0); // state initializes to zero EXPECT_STATE(verifier_transcript, /*start*/ 0, /*written*/ 0, /*read*/ 0); Fr received_a = verifier_transcript.receive_from_prover("a"); - // receiving is reading bytes input and writing them to an internal proof_data buffer - EXPECT_STATE(verifier_transcript, /*start*/ 0, /*written*/ 32, /*read*/ 32); + // receiving is reading frs input and writing them to an internal proof_data buffer + EXPECT_STATE(verifier_transcript, /*start*/ 0, /*written*/ 1, /*read*/ 1); EXPECT_EQ(received_a, elt_a); + // send grumpkin::fr Fq elt_b = 773; prover_transcript.send_to_verifier("b", elt_b); - EXPECT_STATE(prover_transcript, /*start*/ 32, /*written*/ 32, /*read*/ 0); + EXPECT_STATE(prover_transcript, /*start*/ 1, /*written*/ 2, /*read*/ 0); verifier_transcript.load_proof(prover_transcript.export_proof()); - EXPECT_STATE(prover_transcript, /*start*/ 64, /*written*/ 0, /*read*/ 0); - // load proof is not an action by a prover or verifeir, so it does not change read/write counts - EXPECT_STATE(verifier_transcript, /*start*/ 0, /*written*/ 32, /*read*/ 32); + EXPECT_STATE(prover_transcript, /*start*/ 3, /*written*/ 0, /*read*/ 0); + // load proof is not an action by a prover or verifier, so it does not change read/write counts + EXPECT_STATE(verifier_transcript, /*start*/ 0, /*written*/ 1, /*read*/ 1); Fq received_b = verifier_transcript.receive_from_prover("b"); - EXPECT_STATE(verifier_transcript, 0, 64, 64); + EXPECT_STATE(verifier_transcript, /*start*/ 0, /*written*/ 3, /*read*/ 3); EXPECT_EQ(received_b, elt_b); + + // send uint32_t + uint32_t elt_c = 43; + prover_transcript.send_to_verifier("c", elt_c); + EXPECT_STATE(prover_transcript, /*start*/ 3, /*written*/ 1, /*read*/ 0); + verifier_transcript.load_proof(prover_transcript.export_proof()); + EXPECT_STATE(prover_transcript, /*start*/ 4, /*written*/ 0, /*read*/ 0); + EXPECT_STATE(verifier_transcript, /*start*/ 0, /*written*/ 3, /*read*/ 3); + auto received_c = verifier_transcript.receive_from_prover("c"); + EXPECT_STATE(verifier_transcript, /*start*/ 0, /*written*/ 4, /*read*/ 4); + EXPECT_EQ(received_c, elt_c); + + // send curve::BN254::AffineElement + curve::BN254::AffineElement elt_d = bb::g1::affine_one; + prover_transcript.send_to_verifier("d", elt_d); + EXPECT_STATE(prover_transcript, /*start*/ 4, /*written*/ 4, /*read*/ 0); + verifier_transcript.load_proof(prover_transcript.export_proof()); + EXPECT_STATE(prover_transcript, /*start*/ 8, /*written*/ 0, /*read*/ 0); + auto received_d = verifier_transcript.receive_from_prover("d"); + EXPECT_STATE(verifier_transcript, /*start*/ 0, /*written*/ 8, /*read*/ 8); + EXPECT_EQ(received_d, elt_d); + + // send std::array + std::array elt_e = { 1, 2, 3, 4, 5 }; + prover_transcript.send_to_verifier("e", elt_e); + EXPECT_STATE(prover_transcript, /*start*/ 8, /*written*/ 5, /*read*/ 0); + verifier_transcript.load_proof(prover_transcript.export_proof()); + EXPECT_STATE(prover_transcript, /*start*/ 13, /*written*/ 0, /*read*/ 0); + auto received_e = verifier_transcript.receive_from_prover>("e"); + EXPECT_STATE(verifier_transcript, /*start*/ 0, /*written*/ 13, /*read*/ 13); + EXPECT_EQ(received_e, elt_e); + + // send std::array + std::array elt_f = { 9, 12515, 1231, 745, 124, 6231, 957 }; + prover_transcript.send_to_verifier("f", elt_f); + EXPECT_STATE(prover_transcript, /*start*/ 13, /*written*/ 14, /*read*/ 0); + verifier_transcript.load_proof(prover_transcript.export_proof()); + EXPECT_STATE(prover_transcript, /*start*/ 27, /*written*/ 0, /*read*/ 0); + auto received_f = verifier_transcript.receive_from_prover>("f"); + EXPECT_STATE(verifier_transcript, /*start*/ 0, /*written*/ 27, /*read*/ 27); + EXPECT_EQ(received_f, elt_f); + + // send Univariate + bb::Univariate elt_g{ std::array({ 5, 6, 7, 8 }) }; + prover_transcript.send_to_verifier("g", elt_g); + EXPECT_STATE(prover_transcript, /*start*/ 27, /*written*/ 4, /*read*/ 0); + verifier_transcript.load_proof(prover_transcript.export_proof()); + EXPECT_STATE(prover_transcript, /*start*/ 31, /*written*/ 0, /*read*/ 0); + auto received_g = verifier_transcript.receive_from_prover>("g"); + EXPECT_STATE(verifier_transcript, /*start*/ 0, /*written*/ 31, /*read*/ 31); + EXPECT_EQ(received_g, elt_g); + + // send Univariate + bb::Univariate elt_h{ std::array({ 9, 10, 11 }) }; + prover_transcript.send_to_verifier("h", elt_h); + EXPECT_STATE(prover_transcript, /*start*/ 31, /*written*/ 6, /*read*/ 0); + verifier_transcript.load_proof(prover_transcript.export_proof()); + EXPECT_STATE(prover_transcript, /*start*/ 37, /*written*/ 0, /*read*/ 0); + auto received_h = verifier_transcript.receive_from_prover>("h"); + EXPECT_STATE(verifier_transcript, /*start*/ 0, /*written*/ 37, /*read*/ 37); + EXPECT_EQ(received_h, elt_h); } diff --git a/barretenberg/cpp/src/barretenberg/translator_vm/goblin_translator_composer.cpp b/barretenberg/cpp/src/barretenberg/translator_vm/goblin_translator_composer.cpp index 285546ee4b7..64846fcc7bb 100644 --- a/barretenberg/cpp/src/barretenberg/translator_vm/goblin_translator_composer.cpp +++ b/barretenberg/cpp/src/barretenberg/translator_vm/goblin_translator_composer.cpp @@ -33,22 +33,14 @@ using Transcript = typename Flavor::Transcript; void GoblinTranslatorComposer::compute_circuit_size_parameters(CircuitBuilder& circuit_builder) { - const size_t num_gates = circuit_builder.num_gates; - - // number of populated rows in the execution trace - size_t num_rows_populated_in_execution_trace = num_gates; - - // Goblin translator circuits always have a predefined size and are structured as a VM (no concept of selectors) - ASSERT(MINI_CIRCUIT_SIZE >= num_rows_populated_in_execution_trace); - - total_num_gates = std::max(MINI_CIRCUIT_SIZE, num_rows_populated_in_execution_trace); + total_num_gates = std::max(circuit_builder.num_gates, MINIMUM_MINI_CIRCUIT_SIZE); // Next power of 2 mini_circuit_dyadic_size = circuit_builder.get_circuit_subgroup_size(total_num_gates); // The actual circuit size is several times bigger than the trace in the builder, because we use concatenation to // bring the degree of relations down, while extending the length. - dyadic_circuit_size = mini_circuit_dyadic_size * Flavor::CONCATENATION_INDEX; + dyadic_circuit_size = mini_circuit_dyadic_size * Flavor::CONCATENATION_GROUP_SIZE; } /** @@ -189,7 +181,7 @@ void GoblinTranslatorComposer::compute_witness(CircuitBuilder& circuit_builder) // We also contruct ordered polynomials, which have the same values as concatenated ones + enough values to bridge // the range from 0 to maximum range defined by the range constraint. bb::honk::permutation_library::compute_goblin_translator_range_constraint_ordered_polynomials( - proving_key.get()); + proving_key.get(), mini_circuit_dyadic_size); computed_witness = true; } @@ -273,11 +265,13 @@ std::shared_ptr GoblinTranslatorComposer::compute_p // Compute polynomials with odd and even indices set to 1 up to the minicircuit margin + lagrange polynomials at // second and second to last indices in the minicircuit - bb::honk::permutation_library::compute_lagrange_polynomials_for_goblin_translator(proving_key.get()); + bb::honk::permutation_library::compute_lagrange_polynomials_for_goblin_translator(proving_key.get(), + mini_circuit_dyadic_size); // Compute the numerator for the permutation argument with several repetitions of steps bridging 0 and maximum range // constraint - bb::honk::permutation_library::compute_extra_range_constraint_numerator(proving_key.get()); + bb::honk::permutation_library::compute_extra_range_constraint_numerator(proving_key.get(), + dyadic_circuit_size); return proving_key; } diff --git a/barretenberg/cpp/src/barretenberg/translator_vm/goblin_translator_composer.hpp b/barretenberg/cpp/src/barretenberg/translator_vm/goblin_translator_composer.hpp index 4f1d4a9d219..55e2ed944ba 100644 --- a/barretenberg/cpp/src/barretenberg/translator_vm/goblin_translator_composer.hpp +++ b/barretenberg/cpp/src/barretenberg/translator_vm/goblin_translator_composer.hpp @@ -20,10 +20,11 @@ class GoblinTranslatorComposer { using VerifierCommitmentKey = typename Flavor::VerifierCommitmentKey; using Polynomial = typename Flavor::Polynomial; using Transcript = BaseTranscript; - static constexpr size_t MINI_CIRCUIT_SIZE = Flavor::MINI_CIRCUIT_SIZE; static constexpr std::string_view NAME_STRING = "GoblinTranslator"; static constexpr size_t NUM_WIRES = CircuitBuilder::NUM_WIRES; + // The minimum size of the mini-circuit (or sorted constraints won't work) + static constexpr size_t MINIMUM_MINI_CIRCUIT_SIZE = 2048; std::shared_ptr proving_key; std::shared_ptr verification_key; diff --git a/barretenberg/cpp/src/barretenberg/translator_vm/goblin_translator_prover.cpp b/barretenberg/cpp/src/barretenberg/translator_vm/goblin_translator_prover.cpp index b25a3cb5059..da407ae15bd 100644 --- a/barretenberg/cpp/src/barretenberg/translator_vm/goblin_translator_prover.cpp +++ b/barretenberg/cpp/src/barretenberg/translator_vm/goblin_translator_prover.cpp @@ -168,13 +168,13 @@ void GoblinTranslatorProver::execute_zeromorph_rounds() prover_polynomials.get_concatenation_groups()); } -plonk::proof& GoblinTranslatorProver::export_proof() +honk::proof& GoblinTranslatorProver::export_proof() { - proof.proof_data = transcript->export_proof(); + proof = transcript->export_proof(); return proof; } -plonk::proof& GoblinTranslatorProver::construct_proof() +honk::proof& GoblinTranslatorProver::construct_proof() { // Add circuit size public input size and public inputs to transcript. execute_preamble_round(); diff --git a/barretenberg/cpp/src/barretenberg/translator_vm/goblin_translator_prover.hpp b/barretenberg/cpp/src/barretenberg/translator_vm/goblin_translator_prover.hpp index f9988f38ed2..5709f504e9d 100644 --- a/barretenberg/cpp/src/barretenberg/translator_vm/goblin_translator_prover.hpp +++ b/barretenberg/cpp/src/barretenberg/translator_vm/goblin_translator_prover.hpp @@ -1,6 +1,6 @@ #pragma once #include "barretenberg/flavor/goblin_translator.hpp" -#include "barretenberg/plonk/proof_system/types/proof.hpp" +#include "barretenberg/honk/proof_system/types/proof.hpp" #include "barretenberg/relations/relation_parameters.hpp" #include "barretenberg/sumcheck/sumcheck_output.hpp" @@ -22,9 +22,6 @@ class GoblinTranslatorProver { using Curve = typename Flavor::Curve; using Transcript = typename Flavor::Transcript; - static size_t constexpr MINI_CIRCUIT_SIZE = Flavor::MINI_CIRCUIT_SIZE; - static size_t constexpr FULL_CIRCUIT_SIZE = Flavor::FULL_CIRCUIT_SIZE; - public: explicit GoblinTranslatorProver(const std::shared_ptr& input_key, const std::shared_ptr& commitment_key, @@ -35,8 +32,8 @@ class GoblinTranslatorProver { BBERG_PROFILE void execute_grand_product_computation_round(); BBERG_PROFILE void execute_relation_check_rounds(); BBERG_PROFILE void execute_zeromorph_rounds(); - plonk::proof& export_proof(); - plonk::proof& construct_proof(); + honk::proof& export_proof(); + honk::proof& construct_proof(); std::shared_ptr transcript = std::make_shared(); @@ -54,7 +51,7 @@ class GoblinTranslatorProver { sumcheck::SumcheckOutput sumcheck_output; private: - plonk::proof proof; + honk::proof proof; }; } // namespace bb::honk diff --git a/barretenberg/cpp/src/barretenberg/translator_vm/goblin_translator_verifier.cpp b/barretenberg/cpp/src/barretenberg/translator_vm/goblin_translator_verifier.cpp index 0cced54df80..b83f0b449a8 100644 --- a/barretenberg/cpp/src/barretenberg/translator_vm/goblin_translator_verifier.cpp +++ b/barretenberg/cpp/src/barretenberg/translator_vm/goblin_translator_verifier.cpp @@ -65,10 +65,10 @@ void GoblinTranslatorVerifier::put_translation_data_in_relation_parameters(const /** * @brief This function verifies an GoblinTranslator Honk proof for given program settings. */ -bool GoblinTranslatorVerifier::verify_proof(const plonk::proof& proof) +bool GoblinTranslatorVerifier::verify_proof(const honk::proof& proof) { batching_challenge_v = transcript->get_challenge("Translation:batching_challenge"); - transcript->load_proof(proof.proof_data); + transcript->load_proof(proof); Flavor::VerifierCommitments commitments{ key }; Flavor::CommitmentLabels commitment_labels; diff --git a/barretenberg/cpp/src/barretenberg/translator_vm/goblin_translator_verifier.hpp b/barretenberg/cpp/src/barretenberg/translator_vm/goblin_translator_verifier.hpp index 3418501fcc4..977dc9ed8a8 100644 --- a/barretenberg/cpp/src/barretenberg/translator_vm/goblin_translator_verifier.hpp +++ b/barretenberg/cpp/src/barretenberg/translator_vm/goblin_translator_verifier.hpp @@ -1,7 +1,7 @@ #pragma once #include "barretenberg/flavor/goblin_translator.hpp" #include "barretenberg/goblin/translation_evaluations.hpp" -#include "barretenberg/plonk/proof_system/types/proof.hpp" +#include "barretenberg/honk/proof_system/types/proof.hpp" namespace bb::honk { class GoblinTranslatorVerifier { @@ -35,7 +35,7 @@ class GoblinTranslatorVerifier { void put_translation_data_in_relation_parameters(const uint256_t& evaluation_input_x, const BF& batching_challenge_v, const uint256_t& accumulated_result); - bool verify_proof(const plonk::proof& proof); + bool verify_proof(const honk::proof& proof); bool verify_translation(const TranslationEvaluations& translation_evaluations); }; } // namespace bb::honk diff --git a/barretenberg/cpp/src/barretenberg/ultra_honk/goblin_ultra_transcript.test.cpp b/barretenberg/cpp/src/barretenberg/ultra_honk/goblin_ultra_transcript.test.cpp index 0865eff99bb..70247e48a0d 100644 --- a/barretenberg/cpp/src/barretenberg/ultra_honk/goblin_ultra_transcript.test.cpp +++ b/barretenberg/cpp/src/barretenberg/ultra_honk/goblin_ultra_transcript.test.cpp @@ -36,37 +36,37 @@ class GoblinUltraTranscriptTests : public ::testing::Test { size_t MAX_PARTIAL_RELATION_LENGTH = Flavor::BATCHED_RELATION_PARTIAL_LENGTH; size_t NUM_SUBRELATIONS = Flavor::NUM_SUBRELATIONS; - size_t size_FF = sizeof(FF); - size_t size_G = 2 * size_FF; - size_t size_uni = MAX_PARTIAL_RELATION_LENGTH * size_FF; - size_t size_evals = (Flavor::NUM_ALL_ENTITIES)*size_FF; - size_t size_uint32 = 4; + size_t frs_per_Fr = bb::field_conversion::calc_num_bn254_frs(); + size_t frs_per_G = bb::field_conversion::calc_num_bn254_frs(); + size_t frs_per_uni = MAX_PARTIAL_RELATION_LENGTH * frs_per_Fr; + size_t frs_per_evals = (Flavor::NUM_ALL_ENTITIES)*frs_per_Fr; + size_t frs_per_uint32 = bb::field_conversion::calc_num_bn254_frs(); size_t round = 0; - manifest_expected.add_entry(round, "circuit_size", size_uint32); - manifest_expected.add_entry(round, "public_input_size", size_uint32); - manifest_expected.add_entry(round, "pub_inputs_offset", size_uint32); - manifest_expected.add_entry(round, "public_input_0", size_FF); - manifest_expected.add_entry(round, "W_L", size_G); - manifest_expected.add_entry(round, "W_R", size_G); - manifest_expected.add_entry(round, "W_O", size_G); - manifest_expected.add_entry(round, "ECC_OP_WIRE_1", size_G); - manifest_expected.add_entry(round, "ECC_OP_WIRE_2", size_G); - manifest_expected.add_entry(round, "ECC_OP_WIRE_3", size_G); - manifest_expected.add_entry(round, "ECC_OP_WIRE_4", size_G); - manifest_expected.add_entry(round, "CALLDATA", size_G); - manifest_expected.add_entry(round, "CALLDATA_READ_COUNTS", size_G); + manifest_expected.add_entry(round, "circuit_size", frs_per_uint32); + manifest_expected.add_entry(round, "public_input_size", frs_per_uint32); + manifest_expected.add_entry(round, "pub_inputs_offset", frs_per_uint32); + manifest_expected.add_entry(round, "public_input_0", frs_per_Fr); + manifest_expected.add_entry(round, "W_L", frs_per_G); + manifest_expected.add_entry(round, "W_R", frs_per_G); + manifest_expected.add_entry(round, "W_O", frs_per_G); + manifest_expected.add_entry(round, "ECC_OP_WIRE_1", frs_per_G); + manifest_expected.add_entry(round, "ECC_OP_WIRE_2", frs_per_G); + manifest_expected.add_entry(round, "ECC_OP_WIRE_3", frs_per_G); + manifest_expected.add_entry(round, "ECC_OP_WIRE_4", frs_per_G); + manifest_expected.add_entry(round, "CALLDATA", frs_per_G); + manifest_expected.add_entry(round, "CALLDATA_READ_COUNTS", frs_per_G); manifest_expected.add_challenge(round, "eta"); round++; - manifest_expected.add_entry(round, "SORTED_ACCUM", size_G); - manifest_expected.add_entry(round, "W_4", size_G); + manifest_expected.add_entry(round, "SORTED_ACCUM", frs_per_G); + manifest_expected.add_entry(round, "W_4", frs_per_G); manifest_expected.add_challenge(round, "beta", "gamma"); round++; - manifest_expected.add_entry(round, "LOOKUP_INVERSES", size_G); - manifest_expected.add_entry(round, "Z_PERM", size_G); - manifest_expected.add_entry(round, "Z_LOOKUP", size_G); + manifest_expected.add_entry(round, "LOOKUP_INVERSES", frs_per_G); + manifest_expected.add_entry(round, "Z_PERM", frs_per_G); + manifest_expected.add_entry(round, "Z_LOOKUP", frs_per_G); for (size_t i = 0; i < NUM_SUBRELATIONS - 1; i++) { std::string label = "Sumcheck:alpha_" + std::to_string(i); @@ -82,29 +82,29 @@ class GoblinUltraTranscriptTests : public ::testing::Test { for (size_t i = 0; i < log_n; ++i) { std::string idx = std::to_string(i); - manifest_expected.add_entry(round, "Sumcheck:univariate_" + idx, size_uni); + manifest_expected.add_entry(round, "Sumcheck:univariate_" + idx, frs_per_uni); std::string label = "Sumcheck:u_" + idx; manifest_expected.add_challenge(round, label); round++; } - manifest_expected.add_entry(round, "Sumcheck:evaluations", size_evals); + manifest_expected.add_entry(round, "Sumcheck:evaluations", frs_per_evals); manifest_expected.add_challenge(round, "rho"); round++; for (size_t i = 0; i < log_n; ++i) { std::string idx = std::to_string(i); - manifest_expected.add_entry(round, "ZM:C_q_" + idx, size_G); + manifest_expected.add_entry(round, "ZM:C_q_" + idx, frs_per_G); } manifest_expected.add_challenge(round, "ZM:y"); round++; - manifest_expected.add_entry(round, "ZM:C_q", size_G); + manifest_expected.add_entry(round, "ZM:C_q", frs_per_G); manifest_expected.add_challenge(round, "ZM:x", "ZM:z"); round++; // TODO(Mara): Make testing more flavor agnostic so we can test this with all flavors - manifest_expected.add_entry(round, "ZM:PI", size_G); + manifest_expected.add_entry(round, "ZM:PI", frs_per_G); manifest_expected.add_challenge(round); // no challenge return manifest_expected; diff --git a/barretenberg/cpp/src/barretenberg/ultra_honk/merge_prover.cpp b/barretenberg/cpp/src/barretenberg/ultra_honk/merge_prover.cpp index f0f603f276a..c79fe9a8689 100644 --- a/barretenberg/cpp/src/barretenberg/ultra_honk/merge_prover.cpp +++ b/barretenberg/cpp/src/barretenberg/ultra_honk/merge_prover.cpp @@ -27,9 +27,9 @@ MergeProver_::MergeProver_(const std::shared_ptr& commitm * for details (https://github.com/AztecProtocol/barretenberg/issues/746). * * @tparam Flavor - * @return plonk::proof& + * @return honk::proof& */ -template plonk::proof& MergeProver_::construct_proof() +template honk::proof& MergeProver_::construct_proof() { size_t N = op_queue->get_current_size(); @@ -112,7 +112,7 @@ template plonk::proof& MergeProver_::construct_proof() auto quotient_commitment = pcs_commitment_key->commit(quotient); transcript->send_to_verifier("KZG:W", quotient_commitment); - proof.proof_data = transcript->proof_data; + proof = transcript->proof_data; return proof; } diff --git a/barretenberg/cpp/src/barretenberg/ultra_honk/merge_prover.hpp b/barretenberg/cpp/src/barretenberg/ultra_honk/merge_prover.hpp index ab16e7fcc2b..8693f77ed8c 100644 --- a/barretenberg/cpp/src/barretenberg/ultra_honk/merge_prover.hpp +++ b/barretenberg/cpp/src/barretenberg/ultra_honk/merge_prover.hpp @@ -3,7 +3,7 @@ #include "barretenberg/commitment_schemes/claim.hpp" #include "barretenberg/flavor/goblin_ultra.hpp" #include "barretenberg/flavor/ultra.hpp" -#include "barretenberg/plonk/proof_system/types/proof.hpp" +#include "barretenberg/honk/proof_system/types/proof.hpp" #include "barretenberg/proof_system/op_queue/ecc_op_queue.hpp" #include "barretenberg/transcript/transcript.hpp" @@ -33,10 +33,10 @@ template class MergeProver_ { explicit MergeProver_(const std::shared_ptr&, const std::shared_ptr&, const std::shared_ptr& transcript = std::make_shared()); - BBERG_PROFILE plonk::proof& construct_proof(); + BBERG_PROFILE honk::proof& construct_proof(); private: - plonk::proof proof; + honk::proof proof; }; } // namespace bb::honk diff --git a/barretenberg/cpp/src/barretenberg/ultra_honk/merge_verifier.cpp b/barretenberg/cpp/src/barretenberg/ultra_honk/merge_verifier.cpp index 33ec28e80b6..a50c25a0d57 100644 --- a/barretenberg/cpp/src/barretenberg/ultra_honk/merge_verifier.cpp +++ b/barretenberg/cpp/src/barretenberg/ultra_honk/merge_verifier.cpp @@ -16,11 +16,11 @@ MergeVerifier_::MergeVerifier_() * queue has been constructed correctly via a simple Schwartz-Zippel check. Evaluations are checked via batched KZG. * * @tparam Flavor - * @return plonk::proof& + * @return honk::proof& */ -template bool MergeVerifier_::verify_proof(const plonk::proof& proof) +template bool MergeVerifier_::verify_proof(const honk::proof& proof) { - transcript = std::make_shared(proof.proof_data); + transcript = std::make_shared(proof); // Receive commitments [t_i^{shift}], [T_{i-1}], and [T_i] std::array C_T_prev; diff --git a/barretenberg/cpp/src/barretenberg/ultra_honk/merge_verifier.hpp b/barretenberg/cpp/src/barretenberg/ultra_honk/merge_verifier.hpp index 87abe72d49e..a32d071eb9b 100644 --- a/barretenberg/cpp/src/barretenberg/ultra_honk/merge_verifier.hpp +++ b/barretenberg/cpp/src/barretenberg/ultra_honk/merge_verifier.hpp @@ -3,7 +3,7 @@ #include "barretenberg/commitment_schemes/claim.hpp" #include "barretenberg/flavor/goblin_ultra.hpp" #include "barretenberg/flavor/ultra.hpp" -#include "barretenberg/plonk/proof_system/types/proof.hpp" +#include "barretenberg/honk/proof_system/types/proof.hpp" #include "barretenberg/proof_system/op_queue/ecc_op_queue.hpp" #include "barretenberg/srs/global_crs.hpp" #include "barretenberg/transcript/transcript.hpp" @@ -33,7 +33,7 @@ template class MergeVerifier_ { std::shared_ptr pcs_verification_key; explicit MergeVerifier_(); - bool verify_proof(const plonk::proof& proof); + bool verify_proof(const honk::proof& proof); }; } // namespace bb::honk diff --git a/barretenberg/cpp/src/barretenberg/ultra_honk/protogalaxy.test.cpp b/barretenberg/cpp/src/barretenberg/ultra_honk/protogalaxy.test.cpp index ab2e65df40a..1f41c5325ab 100644 --- a/barretenberg/cpp/src/barretenberg/ultra_honk/protogalaxy.test.cpp +++ b/barretenberg/cpp/src/barretenberg/ultra_honk/protogalaxy.test.cpp @@ -47,7 +47,7 @@ std::shared_ptr fold_and_verify(const std::vector& accumulator, } void decide_and_verify(std::shared_ptr& accumulator, UltraComposer& composer, bool expected_result) { - auto decider_prover = composer.create_decider_prover(accumulator, composer.commitment_key); + auto decider_prover = composer.create_decider_prover(accumulator); auto decider_verifier = composer.create_decider_verifier(accumulator); auto decision = decider_prover.construct_proof(); auto verified = decider_verifier.verify_proof(decision); diff --git a/barretenberg/cpp/src/barretenberg/ultra_honk/relation_correctness.test.cpp b/barretenberg/cpp/src/barretenberg/ultra_honk/relation_correctness.test.cpp index bbecb252bf6..b9313be2496 100644 --- a/barretenberg/cpp/src/barretenberg/ultra_honk/relation_correctness.test.cpp +++ b/barretenberg/cpp/src/barretenberg/ultra_honk/relation_correctness.test.cpp @@ -379,7 +379,8 @@ TEST_F(RelationCorrectnessTests, GoblinTranslatorPermutationRelationCorrectness) using Polynomial = bb::Polynomial; using namespace bb::honk::permutation_library; auto& engine = numeric::get_debug_randomness(); - auto circuit_size = Flavor::MINI_CIRCUIT_SIZE * Flavor::CONCATENATION_INDEX; + const size_t mini_circuit_size = 2048; + auto full_circuit_size = mini_circuit_size * Flavor::CONCATENATION_GROUP_SIZE; // We only need gamma, because permutationr elation only uses gamma FF gamma = FF::random_element(); @@ -391,16 +392,16 @@ TEST_F(RelationCorrectnessTests, GoblinTranslatorPermutationRelationCorrectness) // Create storage for polynomials ProverPolynomials prover_polynomials; for (Polynomial& prover_poly : prover_polynomials.get_all()) { - prover_poly = Polynomial{ circuit_size }; + prover_poly = Polynomial{ full_circuit_size }; } // Fill in lagrange polynomials used in the permutation relation prover_polynomials.lagrange_first[0] = 1; - prover_polynomials.lagrange_last[circuit_size - 1] = 1; + prover_polynomials.lagrange_last[full_circuit_size - 1] = 1; // Put random values in all the non-concatenated constraint polynomials used to range constrain the values auto fill_polynomial_with_random_14_bit_values = [&](auto& polynomial) { - for (size_t i = 0; i < Flavor::MINI_CIRCUIT_SIZE; i++) { + for (size_t i = 0; i < mini_circuit_size; i++) { polynomial[i] = engine.get_random_uint16() & ((1 << Flavor::MICRO_LIMB_BITS) - 1); } }; @@ -470,23 +471,23 @@ TEST_F(RelationCorrectnessTests, GoblinTranslatorPermutationRelationCorrectness) fill_polynomial_with_random_14_bit_values(prover_polynomials.relation_wide_limbs_range_constraint_3); // Compute ordered range constraint polynomials that go in the denominator of the grand product polynomial - compute_goblin_translator_range_constraint_ordered_polynomials(&prover_polynomials); + compute_goblin_translator_range_constraint_ordered_polynomials(&prover_polynomials, mini_circuit_size); // Compute the fixed numerator (part of verification key) - compute_extra_range_constraint_numerator(&prover_polynomials); + compute_extra_range_constraint_numerator(&prover_polynomials, full_circuit_size); // Compute concatenated polynomials (4 polynomials produced from other constraint polynomials by concatenation) compute_concatenated_polynomials(&prover_polynomials); // Compute the grand product polynomial grand_product_library::compute_grand_product>( - circuit_size, prover_polynomials, params); + full_circuit_size, prover_polynomials, params); prover_polynomials.z_perm_shift = prover_polynomials.z_perm.shifted(); using Relations = typename Flavor::Relations; // Check that permutation relation is satisfied across each row of the prover polynomials - check_relation>(circuit_size, prover_polynomials, params); + check_relation>(full_circuit_size, prover_polynomials, params); } TEST_F(RelationCorrectnessTests, GoblinTranslatorGenPermSortRelationCorrectness) @@ -496,8 +497,8 @@ TEST_F(RelationCorrectnessTests, GoblinTranslatorGenPermSortRelationCorrectness) using ProverPolynomials = typename Flavor::ProverPolynomials; using Polynomial = bb::Polynomial; auto& engine = numeric::get_debug_randomness(); - - const auto circuit_size = Flavor::FULL_CIRCUIT_SIZE; + const size_t mini_circuit_size = 2048; + const auto circuit_size = Flavor::CONCATENATION_GROUP_SIZE * mini_circuit_size; const auto sort_step = Flavor::SORT_STEP; const auto max_value = (1 << Flavor::MICRO_LIMB_BITS) - 1; @@ -579,8 +580,8 @@ TEST_F(RelationCorrectnessTests, GoblinTranslatorExtraRelationsCorrectness) auto& engine = numeric::get_debug_randomness(); - auto circuit_size = Flavor::FULL_CIRCUIT_SIZE; - auto mini_circuit_size = Flavor::MINI_CIRCUIT_SIZE; + const size_t mini_circuit_size = 2048; + const auto circuit_size = Flavor::CONCATENATION_GROUP_SIZE * mini_circuit_size; // We only use accumulated_result from relation parameters in this relation RelationParameters params; @@ -681,7 +682,8 @@ TEST_F(RelationCorrectnessTests, GoblinTranslatorDecompositionRelationCorrectnes using Polynomial = bb::Polynomial; auto& engine = numeric::get_debug_randomness(); - auto circuit_size = Flavor::FULL_CIRCUIT_SIZE; + constexpr size_t mini_circuit_size = 2048; + const auto circuit_size = Flavor::CONCATENATION_GROUP_SIZE * mini_circuit_size; // Decomposition relation doesn't use any relation parameters RelationParameters params; @@ -723,7 +725,7 @@ TEST_F(RelationCorrectnessTests, GoblinTranslatorDecompositionRelationCorrectnes } // Fill in lagrange odd polynomial (the only non-witness one we are using) - for (size_t i = 1; i < Flavor::MINI_CIRCUIT_SIZE - 1; i += 2) { + for (size_t i = 1; i < mini_circuit_size - 1; i += 2) { prover_polynomials.lagrange_odd_in_minicircuit[i] = 1; } @@ -811,7 +813,7 @@ TEST_F(RelationCorrectnessTests, GoblinTranslatorDecompositionRelationCorrectnes }; // Put random values in all the non-concatenated constraint polynomials used to range constrain the values - for (size_t i = 1; i < Flavor::MINI_CIRCUIT_SIZE - 1; i += 2) { + for (size_t i = 1; i < mini_circuit_size - 1; i += 2) { // P.x prover_polynomials.x_lo_y_hi[i] = FF(engine.get_random_uint256() & ((uint256_t(1) << LOW_WIDE_LIMB_WIDTH) - 1)); prover_polynomials.x_hi_z_1[i] = FF(engine.get_random_uint256() & ((uint256_t(1) << HIGH_WIDE_LIMB_WIDTH) - 1)); @@ -1055,15 +1057,15 @@ TEST_F(RelationCorrectnessTests, GoblinTranslatorNonNativeRelationCorrectness) using Polynomial = bb::Polynomial; constexpr size_t NUM_LIMB_BITS = Flavor::NUM_LIMB_BITS; - constexpr auto circuit_size = Flavor::FULL_CIRCUIT_SIZE; - constexpr auto mini_circuit_size = Flavor::MINI_CIRCUIT_SIZE; + constexpr auto mini_circuit_size = 2048; + constexpr auto circuit_size = Flavor::CONCATENATION_GROUP_SIZE * mini_circuit_size; auto& engine = numeric::get_debug_randomness(); auto op_queue = std::make_shared(); // Generate random EccOpQueue actions - for (size_t i = 0; i < ((Flavor::MINI_CIRCUIT_SIZE >> 1) - 1); i++) { + for (size_t i = 0; i < ((mini_circuit_size >> 1) - 1); i++) { switch (engine.get_random_uint8() & 3) { case 0: op_queue->empty_row(); diff --git a/barretenberg/cpp/src/barretenberg/ultra_honk/ultra_composer.hpp b/barretenberg/cpp/src/barretenberg/ultra_honk/ultra_composer.hpp index b81d6a42178..5d2a6a71ea2 100644 --- a/barretenberg/cpp/src/barretenberg/ultra_honk/ultra_composer.hpp +++ b/barretenberg/cpp/src/barretenberg/ultra_honk/ultra_composer.hpp @@ -110,8 +110,7 @@ template class UltraComposer_ { */ MergeVerifier_ create_merge_verifier() { return MergeVerifier_(); } - ProtoGalaxyProver_ create_folding_prover(const std::vector>& instances, - const std::shared_ptr& commitment_key) + ProtoGalaxyProver_ create_folding_prover(const std::vector>& instances) { ProtoGalaxyProver_ output_state(instances, commitment_key); diff --git a/barretenberg/cpp/src/barretenberg/ultra_honk/ultra_prover.cpp b/barretenberg/cpp/src/barretenberg/ultra_honk/ultra_prover.cpp index 2622a247fc4..40fc15a3369 100644 --- a/barretenberg/cpp/src/barretenberg/ultra_honk/ultra_prover.cpp +++ b/barretenberg/cpp/src/barretenberg/ultra_honk/ultra_prover.cpp @@ -178,13 +178,13 @@ template void UltraProver_::execute_zeromorph_round transcript); } -template plonk::proof& UltraProver_::export_proof() +template honk::proof& UltraProver_::export_proof() { - proof.proof_data = transcript->proof_data; + proof = transcript->proof_data; return proof; } -template plonk::proof& UltraProver_::construct_proof() +template honk::proof& UltraProver_::construct_proof() { // Add circuit size public input size and public inputs to transcript-> execute_preamble_round(); diff --git a/barretenberg/cpp/src/barretenberg/ultra_honk/ultra_prover.hpp b/barretenberg/cpp/src/barretenberg/ultra_honk/ultra_prover.hpp index ba73af94125..13d3a1cc1b8 100644 --- a/barretenberg/cpp/src/barretenberg/ultra_honk/ultra_prover.hpp +++ b/barretenberg/cpp/src/barretenberg/ultra_honk/ultra_prover.hpp @@ -2,7 +2,7 @@ #include "barretenberg/commitment_schemes/zeromorph/zeromorph.hpp" #include "barretenberg/flavor/goblin_ultra.hpp" #include "barretenberg/flavor/ultra.hpp" -#include "barretenberg/plonk/proof_system/types/proof.hpp" +#include "barretenberg/honk/proof_system/types/proof.hpp" #include "barretenberg/relations/relation_parameters.hpp" #include "barretenberg/sumcheck/instance/prover_instance.hpp" #include "barretenberg/sumcheck/sumcheck_output.hpp" @@ -35,8 +35,8 @@ template class UltraProver_ { BBERG_PROFILE void execute_relation_check_rounds(); BBERG_PROFILE void execute_zeromorph_rounds(); - plonk::proof& export_proof(); - plonk::proof& construct_proof(); + honk::proof& export_proof(); + honk::proof& construct_proof(); std::shared_ptr instance; @@ -55,7 +55,7 @@ template class UltraProver_ { using ZeroMorph = pcs::zeromorph::ZeroMorphProver_; private: - plonk::proof proof; + honk::proof proof; }; using UltraProver = UltraProver_; diff --git a/barretenberg/cpp/src/barretenberg/ultra_honk/ultra_transcript.test.cpp b/barretenberg/cpp/src/barretenberg/ultra_honk/ultra_transcript.test.cpp index 9fe521be717..3dc2e8f1d3b 100644 --- a/barretenberg/cpp/src/barretenberg/ultra_honk/ultra_transcript.test.cpp +++ b/barretenberg/cpp/src/barretenberg/ultra_honk/ultra_transcript.test.cpp @@ -35,30 +35,31 @@ class UltraTranscriptTests : public ::testing::Test { size_t MAX_PARTIAL_RELATION_LENGTH = Flavor::BATCHED_RELATION_PARTIAL_LENGTH; size_t NUM_SUBRELATIONS = Flavor::NUM_SUBRELATIONS; - size_t size_FF = sizeof(FF); - size_t size_G = 2 * size_FF; - size_t size_uni = MAX_PARTIAL_RELATION_LENGTH * size_FF; - size_t size_evals = (Flavor::NUM_ALL_ENTITIES)*size_FF; - size_t size_uint32 = 4; + // Size of types is number of bb::frs needed to represent the types + size_t frs_per_Fr = bb::field_conversion::calc_num_bn254_frs(); + size_t frs_per_G = bb::field_conversion::calc_num_bn254_frs(); + size_t frs_per_uni = MAX_PARTIAL_RELATION_LENGTH * frs_per_Fr; + size_t frs_per_evals = (Flavor::NUM_ALL_ENTITIES)*frs_per_Fr; + size_t frs_per_uint32 = bb::field_conversion::calc_num_bn254_frs(); size_t round = 0; - manifest_expected.add_entry(round, "circuit_size", size_uint32); - manifest_expected.add_entry(round, "public_input_size", size_uint32); - manifest_expected.add_entry(round, "pub_inputs_offset", size_uint32); - manifest_expected.add_entry(round, "public_input_0", size_FF); - manifest_expected.add_entry(round, "W_L", size_G); - manifest_expected.add_entry(round, "W_R", size_G); - manifest_expected.add_entry(round, "W_O", size_G); + manifest_expected.add_entry(round, "circuit_size", frs_per_uint32); + manifest_expected.add_entry(round, "public_input_size", frs_per_uint32); + manifest_expected.add_entry(round, "pub_inputs_offset", frs_per_uint32); + manifest_expected.add_entry(round, "public_input_0", frs_per_Fr); + manifest_expected.add_entry(round, "W_L", frs_per_G); + manifest_expected.add_entry(round, "W_R", frs_per_G); + manifest_expected.add_entry(round, "W_O", frs_per_G); manifest_expected.add_challenge(round, "eta"); round++; - manifest_expected.add_entry(round, "SORTED_ACCUM", size_G); - manifest_expected.add_entry(round, "W_4", size_G); + manifest_expected.add_entry(round, "SORTED_ACCUM", frs_per_G); + manifest_expected.add_entry(round, "W_4", frs_per_G); manifest_expected.add_challenge(round, "beta", "gamma"); round++; - manifest_expected.add_entry(round, "Z_PERM", size_G); - manifest_expected.add_entry(round, "Z_LOOKUP", size_G); + manifest_expected.add_entry(round, "Z_PERM", frs_per_G); + manifest_expected.add_entry(round, "Z_LOOKUP", frs_per_G); for (size_t i = 0; i < NUM_SUBRELATIONS - 1; i++) { std::string label = "Sumcheck:alpha_" + std::to_string(i); @@ -74,29 +75,29 @@ class UltraTranscriptTests : public ::testing::Test { for (size_t i = 0; i < log_n; ++i) { std::string idx = std::to_string(i); - manifest_expected.add_entry(round, "Sumcheck:univariate_" + idx, size_uni); + manifest_expected.add_entry(round, "Sumcheck:univariate_" + idx, frs_per_uni); std::string label = "Sumcheck:u_" + idx; manifest_expected.add_challenge(round, label); round++; } - manifest_expected.add_entry(round, "Sumcheck:evaluations", size_evals); + manifest_expected.add_entry(round, "Sumcheck:evaluations", frs_per_evals); manifest_expected.add_challenge(round, "rho"); round++; for (size_t i = 0; i < log_n; ++i) { std::string idx = std::to_string(i); - manifest_expected.add_entry(round, "ZM:C_q_" + idx, size_G); + manifest_expected.add_entry(round, "ZM:C_q_" + idx, frs_per_G); } manifest_expected.add_challenge(round, "ZM:y"); round++; - manifest_expected.add_entry(round, "ZM:C_q", size_G); + manifest_expected.add_entry(round, "ZM:C_q", frs_per_G); manifest_expected.add_challenge(round, "ZM:x", "ZM:z"); round++; // TODO(Mara): Make testing more flavor agnostic so we can test this with all flavors - manifest_expected.add_entry(round, "ZM:PI", size_G); + manifest_expected.add_entry(round, "ZM:PI", frs_per_G); manifest_expected.add_challenge(round); // no challenge return manifest_expected; diff --git a/barretenberg/cpp/src/barretenberg/ultra_honk/ultra_verifier.cpp b/barretenberg/cpp/src/barretenberg/ultra_honk/ultra_verifier.cpp index 36c25477e4c..b0ee1339dd1 100644 --- a/barretenberg/cpp/src/barretenberg/ultra_honk/ultra_verifier.cpp +++ b/barretenberg/cpp/src/barretenberg/ultra_honk/ultra_verifier.cpp @@ -45,7 +45,7 @@ template UltraVerifier_& UltraVerifier_::opera * @brief This function verifies an Ultra Honk proof for a given Flavor. * */ -template bool UltraVerifier_::verify_proof(const plonk::proof& proof) +template bool UltraVerifier_::verify_proof(const honk::proof& proof) { using FF = typename Flavor::FF; using Commitment = typename Flavor::Commitment; @@ -56,7 +56,7 @@ template bool UltraVerifier_::verify_proof(const plonk bb::RelationParameters relation_parameters; - transcript = std::make_shared(proof.proof_data); + transcript = std::make_shared(proof); VerifierCommitments commitments{ key }; CommitmentLabels commitment_labels; diff --git a/barretenberg/cpp/src/barretenberg/ultra_honk/ultra_verifier.hpp b/barretenberg/cpp/src/barretenberg/ultra_honk/ultra_verifier.hpp index 77a770a8f93..0c5dd4a9f1d 100644 --- a/barretenberg/cpp/src/barretenberg/ultra_honk/ultra_verifier.hpp +++ b/barretenberg/cpp/src/barretenberg/ultra_honk/ultra_verifier.hpp @@ -1,7 +1,7 @@ #pragma once #include "barretenberg/flavor/goblin_ultra.hpp" #include "barretenberg/flavor/ultra.hpp" -#include "barretenberg/plonk/proof_system/types/proof.hpp" +#include "barretenberg/honk/proof_system/types/proof.hpp" #include "barretenberg/srs/global_crs.hpp" #include "barretenberg/sumcheck/sumcheck.hpp" @@ -24,7 +24,7 @@ template class UltraVerifier_ { UltraVerifier_& operator=(const UltraVerifier_& other) = delete; UltraVerifier_& operator=(UltraVerifier_&& other); - bool verify_proof(const plonk::proof& proof); + bool verify_proof(const honk::proof& proof); std::shared_ptr key; std::map commitments; diff --git a/barretenberg/cpp/src/barretenberg/vm/avm_trace/AvmMini_common.hpp b/barretenberg/cpp/src/barretenberg/vm/avm_trace/AvmMini_common.hpp index 10b3c347330..f8e06ef180b 100644 --- a/barretenberg/cpp/src/barretenberg/vm/avm_trace/AvmMini_common.hpp +++ b/barretenberg/cpp/src/barretenberg/vm/avm_trace/AvmMini_common.hpp @@ -2,6 +2,7 @@ #include "barretenberg/proof_system/circuit_builder/circuit_builder_base.hpp" #include "barretenberg/proof_system/circuit_builder/generated/AvmMini_circuit_builder.hpp" +#include using Flavor = bb::honk::flavor::AvmMiniFlavor; using FF = Flavor::FF; @@ -12,6 +13,9 @@ namespace avm_trace { // Number of rows static const size_t AVM_TRACE_SIZE = 256; enum class IntermRegister : uint32_t { IA = 0, IB = 1, IC = 2 }; + +// Keep following enum in sync with MAX_NEM_TAG below enum class AvmMemoryTag : uint32_t { U0 = 0, U8 = 1, U16 = 2, U32 = 3, U64 = 4, U128 = 5, FF = 6 }; +static const uint32_t MAX_MEM_TAG = 6; } // namespace avm_trace \ No newline at end of file diff --git a/barretenberg/cpp/src/barretenberg/vm/avm_trace/AvmMini_execution.cpp b/barretenberg/cpp/src/barretenberg/vm/avm_trace/AvmMini_execution.cpp new file mode 100644 index 00000000000..22703836848 --- /dev/null +++ b/barretenberg/cpp/src/barretenberg/vm/avm_trace/AvmMini_execution.cpp @@ -0,0 +1,238 @@ +#include "AvmMini_execution.hpp" +#include "barretenberg/common/serialize.hpp" +#include "barretenberg/proof_system/circuit_builder/generated/AvmMini_circuit_builder.hpp" +#include "barretenberg/vm/avm_trace/AvmMini_common.hpp" +#include "barretenberg/vm/avm_trace/AvmMini_instructions.hpp" +#include "barretenberg/vm/avm_trace/AvmMini_opcode.hpp" +#include "barretenberg/vm/avm_trace/AvmMini_trace.hpp" +#include "barretenberg/vm/generated/AvmMini_composer.hpp" +#include +#include +#include +#include + +namespace avm_trace { + +/** + * @brief Run the bytecode, generate the corresponding execution trace and prove the correctness + * of the execution of the supplied bytecode. + * + * @param bytecode A vector of bytes representing the bytecode to execute. + * @param calldata expressed as a vector of finite field elements. + * @throws runtime_error exception when the bytecode is invalid. + * @return A zk proof of the execution. + */ +honk::proof Execution::run_and_prove(std::vector const& bytecode, std::vector const& calldata) +{ + auto instructions = parse(bytecode); + auto trace = gen_trace(instructions, calldata); + auto circuit_builder = bb::AvmMiniCircuitBuilder(); + circuit_builder.set_trace(std::move(trace)); + + auto composer = bb::honk::AvmMiniComposer(); + auto prover = composer.create_prover(circuit_builder); + return prover.construct_proof(); +} + +/** + * @brief Parsing of the supplied bytecode into a vector of instructions. It essentially + * checks that each opcode value is in the defined range and extracts the operands + * for each opcode. + * + * @param bytecode The bytecode to be parsed as a vector of bytes/uint8_t + * @throws runtime_error exception when the bytecode is invalid. + * @return Vector of instructions + */ +std::vector Execution::parse(std::vector const& bytecode) +{ + std::vector instructions; + size_t pos = 0; + const auto length = bytecode.size(); + + while (pos < length) { + const uint8_t opcode_byte = bytecode.at(pos); + pos += AVM_OPCODE_BYTE_LENGTH; + + if (!Bytecode::is_valid(opcode_byte)) { + throw std::runtime_error("Invalid opcode byte: " + std::to_string(opcode_byte)); + } + + const auto opcode = static_cast(opcode_byte); + auto in_tag_u8 = static_cast(AvmMemoryTag::U0); + + if (Bytecode::has_in_tag(opcode)) { + if (pos + AVM_IN_TAG_BYTE_LENGTH > length) { + throw std::runtime_error("Instruction tag missing at position " + std::to_string(pos)); + } + in_tag_u8 = bytecode.at(pos); + if (in_tag_u8 == static_cast(AvmMemoryTag::U0) || in_tag_u8 > MAX_MEM_TAG) { + throw std::runtime_error("Instruction tag is invalid at position " + std::to_string(pos) + + " value: " + std::to_string(in_tag_u8)); + } + pos += AVM_IN_TAG_BYTE_LENGTH; + } + + auto const in_tag = static_cast(in_tag_u8); + std::vector operands{}; + size_t num_of_operands{}; + size_t operands_size{}; + + // SET opcode particularity about the number of operands depending on the + // instruction tag. Namely, a constant of type instruction tag and not a + // memory address is passed in the operands. + // The bytecode of the operands is of the form CONSTANT || dst_offset + // CONSTANT is of size k bits for type Uk, k=8,16,32,64,128 + // dst_offset is of size 32 bits + // CONSTANT has to be decomposed into 32-bit chunks + if (opcode == OpCode::SET) { + switch (in_tag) { + case AvmMemoryTag::U8: + num_of_operands = 2; + operands_size = 5; + break; + case AvmMemoryTag::U16: + num_of_operands = 2; + operands_size = 6; + break; + case AvmMemoryTag::U32: + num_of_operands = 2; + operands_size = 8; + break; + case AvmMemoryTag::U64: + num_of_operands = 3; + operands_size = 12; + break; + case AvmMemoryTag::U128: + num_of_operands = 5; + operands_size = 20; + break; + default: + throw std::runtime_error("Instruction tag for SET opcode is invalid at position " + + std::to_string(pos) + " value: " + std::to_string(in_tag_u8)); + break; + } + } else { + num_of_operands = Bytecode::OPERANDS_NUM.at(opcode); + operands_size = AVM_OPERAND_BYTE_LENGTH * num_of_operands; + } + + if (pos + operands_size > length) { + throw std::runtime_error("Operand is missing at position " + std::to_string(pos)); + } + + // We handle operands which are encoded with less than 4 bytes. + // This occurs for opcode SET and tag U8 and U16. + if (opcode == OpCode::SET && in_tag == AvmMemoryTag::U8) { + operands.push_back(static_cast(bytecode.at(pos))); + pos++; + num_of_operands--; + } else if (opcode == OpCode::SET && in_tag == AvmMemoryTag::U16) { + uint8_t const* ptr = &bytecode.at(pos); + uint16_t operand{}; + serialize::read(ptr, operand); + operands.push_back(static_cast(operand)); + pos += 2; + num_of_operands--; + } + + // Operands of size of 32 bits. + for (size_t i = 0; i < num_of_operands; i++) { + uint8_t const* ptr = &bytecode.at(pos); + uint32_t operand{}; + serialize::read(ptr, operand); + operands.push_back(operand); + pos += AVM_OPERAND_BYTE_LENGTH; + } + + instructions.emplace_back(opcode, operands, static_cast(in_tag)); + } + + return instructions; +} + +/** + * @brief Generate the execution trace pertaining to the supplied instructions. + * + * @param instructions A vector of the instructions to be executed. + * @param calldata expressed as a vector of finite field elements. + * @return The trace as a vector of Row. + */ +std::vector Execution::gen_trace(std::vector const& instructions, std::vector const& calldata) +{ + AvmMiniTraceBuilder trace_builder{}; + + // copied version of pc maintained in trace builder. The value of pc is evolving based + // on opcode logic and therefore is not maintained here. However, the next opcode in the execution + // is determined by this value which require read access to the code below. + uint32_t pc = 0; + auto const inst_size = instructions.size(); + + while ((pc = trace_builder.getPc()) < inst_size) { + auto inst = instructions.at(pc); + + switch (inst.op_code) { + case OpCode::ADD: + trace_builder.add(inst.operands.at(0), inst.operands.at(1), inst.operands.at(2), inst.in_tag); + break; + case OpCode::SUB: + trace_builder.sub(inst.operands.at(0), inst.operands.at(1), inst.operands.at(2), inst.in_tag); + break; + case OpCode::MUL: + trace_builder.mul(inst.operands.at(0), inst.operands.at(1), inst.operands.at(2), inst.in_tag); + break; + case OpCode::DIV: + trace_builder.div(inst.operands.at(0), inst.operands.at(1), inst.operands.at(2), inst.in_tag); + break; + case OpCode::CALLDATACOPY: + trace_builder.calldata_copy(inst.operands.at(0), inst.operands.at(1), inst.operands.at(2), calldata); + break; + case OpCode::JUMP: + trace_builder.jump(inst.operands.at(0)); + break; + case OpCode::INTERNALCALL: + trace_builder.internal_call(inst.operands.at(0)); + break; + case OpCode::INTERNALRETURN: + trace_builder.internal_return(); + break; + case OpCode::SET: { + uint32_t dst_offset{}; + uint128_t val{}; + switch (inst.in_tag) { + case AvmMemoryTag::U8: + case AvmMemoryTag::U16: + case AvmMemoryTag::U32: + // U8, U16, U32 value represented in a single uint32_t operand + val = inst.operands.at(0); + dst_offset = inst.operands.at(1); + break; + case AvmMemoryTag::U64: // value represented as 2 uint32_t operands + val = inst.operands.at(0); + val <<= 32; + val += inst.operands.at(1); + dst_offset = inst.operands.at(2); + break; + case AvmMemoryTag::U128: // value represented as 4 uint32_t operands + for (size_t i = 0; i < 4; i++) { + val += inst.operands.at(i); + val <<= 32; + } + dst_offset = inst.operands.at(4); + break; + default: + break; + } + trace_builder.set(val, dst_offset, inst.in_tag); + break; + } + case OpCode::RETURN: + trace_builder.return_op(inst.operands.at(0), inst.operands.at(1)); + break; + default: + break; + } + } + return trace_builder.finalize(); +} + +} // namespace avm_trace \ No newline at end of file diff --git a/barretenberg/cpp/src/barretenberg/vm/avm_trace/AvmMini_execution.hpp b/barretenberg/cpp/src/barretenberg/vm/avm_trace/AvmMini_execution.hpp new file mode 100644 index 00000000000..db6ffce56b0 --- /dev/null +++ b/barretenberg/cpp/src/barretenberg/vm/avm_trace/AvmMini_execution.hpp @@ -0,0 +1,28 @@ +#pragma once + +#include "barretenberg/honk/proof_system/types/proof.hpp" +#include "barretenberg/vm/avm_trace/AvmMini_common.hpp" +#include "barretenberg/vm/avm_trace/AvmMini_instructions.hpp" +#include "barretenberg/vm/avm_trace/AvmMini_trace.hpp" +#include +#include +#include + +namespace avm_trace { + +class Execution { + public: + Execution() = default; + + static size_t const AVM_OPERAND_BYTE_LENGTH = 4; // Keep in sync with TS code + static_assert(sizeof(uint32_t) / sizeof(uint8_t) == AVM_OPERAND_BYTE_LENGTH); + + static size_t const AVM_OPCODE_BYTE_LENGTH = 1; // Keep in sync with TS code + static size_t const AVM_IN_TAG_BYTE_LENGTH = 1; // Keep in sync with TS code + + static std::vector parse(std::vector const& bytecode); + static std::vector gen_trace(std::vector const& instructions, std::vector const& calldata); + static honk::proof run_and_prove(std::vector const& bytecode, std::vector const& calldata); +}; + +} // namespace avm_trace \ No newline at end of file diff --git a/barretenberg/cpp/src/barretenberg/vm/avm_trace/AvmMini_instructions.hpp b/barretenberg/cpp/src/barretenberg/vm/avm_trace/AvmMini_instructions.hpp new file mode 100644 index 00000000000..0cc18e56087 --- /dev/null +++ b/barretenberg/cpp/src/barretenberg/vm/avm_trace/AvmMini_instructions.hpp @@ -0,0 +1,23 @@ +#pragma once + +#include "barretenberg/vm/avm_trace/AvmMini_common.hpp" +#include "barretenberg/vm/avm_trace/AvmMini_opcode.hpp" +#include +#include + +namespace avm_trace { + +class Instruction { + public: + OpCode op_code; + std::vector operands; + AvmMemoryTag in_tag; + + Instruction() = delete; + explicit Instruction(OpCode op_code, std::vector operands, AvmMemoryTag in_tag) + : op_code(op_code) + , operands(std::move(operands)) + , in_tag(in_tag){}; +}; + +} // namespace avm_trace \ No newline at end of file diff --git a/barretenberg/cpp/src/barretenberg/vm/avm_trace/AvmMini_opcode.cpp b/barretenberg/cpp/src/barretenberg/vm/avm_trace/AvmMini_opcode.cpp new file mode 100644 index 00000000000..ec3d7568f09 --- /dev/null +++ b/barretenberg/cpp/src/barretenberg/vm/avm_trace/AvmMini_opcode.cpp @@ -0,0 +1,153 @@ +#include "AvmMini_opcode.hpp" +#include + +namespace avm_trace { + +const std::unordered_map Bytecode::OPERANDS_NUM = { + // Compute + // Compute - Arithmetic + { OpCode::ADD, 3 }, + { OpCode::SUB, 3 }, + { OpCode::MUL, 3 }, + { OpCode::DIV, 3 }, + //// Compute - Comparators + //{OpCode::EQ, }, + //{OpCode::LT, }, + //{OpCode::LTE, }, + //// Compute - Bitwise + //{OpCode::AND, }, + //{OpCode::OR, }, + //{OpCode::XOR, }, + //{OpCode::NOT, }, + //{OpCode::SHL, }, + //{OpCode::SHR, }, + //// Compute - Type Conversions + //{OpCode::CAST, }, + + //// Execution Environment + //{OpCode::ADDRESS, }, + //{OpCode::STORAGEADDRESS, }, + //{OpCode::ORIGIN, }, + //{OpCode::SENDER, }, + //{OpCode::PORTAL, }, + //{OpCode::FEEPERL1GAS, }, + //{OpCode::FEEPERL2GAS, }, + //{OpCode::FEEPERDAGAS, }, + //{OpCode::CONTRACTCALLDEPTH, }, + //// Execution Environment - Globals + //{OpCode::CHAINID, }, + //{OpCode::VERSION, }, + //{OpCode::BLOCKNUMBER, }, + //{OpCode::TIMESTAMP, }, + //{OpCode::COINBASE, }, + //{OpCode::BLOCKL1GASLIMIT, }, + //{OpCode::BLOCKL2GASLIMIT, }, + //{OpCode::BLOCKDAGASLIMIT, }, + // Execution Environment - Calldata + { OpCode::CALLDATACOPY, 3 }, + + //// Machine State + // Machine State - Gas + //{ OpCode::L1GASLEFT, }, + //{ OpCode::L2GASLEFT, }, + //{ OpCode::DAGASLEFT, }, + //// Machine State - Internal Control Flow + { OpCode::JUMP, 1 }, + { OpCode::JUMPI, 1 }, + { OpCode::INTERNALCALL, 1 }, + { OpCode::INTERNALRETURN, 0 }, + + //// Machine State - Memory + { OpCode::SET, 5 }, + //{ OpCode::MOV, }, + //{ OpCode::CMOV, }, + + //// World State + //{ OpCode::BLOCKHEADERBYNUMBER, }, + //{ OpCode::SLOAD, }, // Public Storage + //{ OpCode::SSTORE, }, // Public Storage + //{ OpCode::READL1TOL2MSG, }, // Messages + //{ OpCode::SENDL2TOL1MSG, }, // Messages + //{ OpCode::EMITNOTEHASH, }, // Notes & Nullifiers + //{ OpCode::EMITNULLIFIER, }, // Notes & Nullifiers + + //// Accrued Substate + //{ OpCode::EMITUNENCRYPTEDLOG, }, + + //// Control Flow - Contract Calls + //{ OpCode::CALL, }, + //{ OpCode::STATICCALL, }, + { OpCode::RETURN, 2 }, + // { OpCode::REVERT, }, + + //// Gadgets + //{ OpCode::KECCAK, }, + //{ OpCode::POSEIDON, }, +}; + +/** + * @brief Test whether a given byte reprents a valid opcode. + * + * @param byte The input byte. + * @return A boolean telling whether a corresponding opcode does match the input byte. + */ +bool Bytecode::is_valid(const uint8_t byte) +{ + return byte <= static_cast(OpCode::POSEIDON); +} + +/** + * @brief A function returning whether a supplied opcode has an instruction tag as argument. + * + * @param op_code The opcode + * @return A boolean set to true if the corresponding instruction needs a tag as argument. + */ +bool Bytecode::has_in_tag(OpCode const op_code) +{ + switch (op_code) { + case OpCode::ADDRESS: + case OpCode::STORAGEADDRESS: + case OpCode::ORIGIN: + case OpCode::SENDER: + case OpCode::PORTAL: + case OpCode::FEEPERL1GAS: + case OpCode::FEEPERL2GAS: + case OpCode::FEEPERDAGAS: + case OpCode::CONTRACTCALLDEPTH: + case OpCode::CHAINID: + case OpCode::VERSION: + case OpCode::BLOCKNUMBER: + case OpCode::TIMESTAMP: + case OpCode::COINBASE: + case OpCode::BLOCKL1GASLIMIT: + case OpCode::BLOCKL2GASLIMIT: + case OpCode::BLOCKDAGASLIMIT: + case OpCode::CALLDATACOPY: + case OpCode::L1GASLEFT: + case OpCode::L2GASLEFT: + case OpCode::DAGASLEFT: + case OpCode::JUMP: + case OpCode::JUMPI: + case OpCode::INTERNALCALL: + case OpCode::INTERNALRETURN: + case OpCode::MOV: + case OpCode::CMOV: + case OpCode::BLOCKHEADERBYNUMBER: + case OpCode::SLOAD: + case OpCode::SSTORE: + case OpCode::READL1TOL2MSG: + case OpCode::SENDL2TOL1MSG: + case OpCode::EMITNOTEHASH: + case OpCode::EMITNULLIFIER: + case OpCode::EMITUNENCRYPTEDLOG: + case OpCode::CALL: + case OpCode::STATICCALL: + case OpCode::RETURN: + case OpCode::REVERT: + return false; + default: + return true; + } +} + +} // namespace avm_trace \ No newline at end of file diff --git a/barretenberg/cpp/src/barretenberg/vm/avm_trace/AvmMini_opcode.hpp b/barretenberg/cpp/src/barretenberg/vm/avm_trace/AvmMini_opcode.hpp new file mode 100644 index 00000000000..c945f6158c6 --- /dev/null +++ b/barretenberg/cpp/src/barretenberg/vm/avm_trace/AvmMini_opcode.hpp @@ -0,0 +1,105 @@ +#pragma once + +#include +#include + +namespace avm_trace { +using std::size_t; + +/** + * All AVM opcodes (Keep in sync with TS counterpart code opcodes.ts) + * TODO: Once opcode values are definitive, we should assign them explicitly in the enum below + * and typescript code. This would increase robustness against unintended modifications. + * i.e.: ADD = 0, SUB = 1, etc, .... + * CAUTION: Any change in the list below needs to be carefully followed by + * a potential adaptation of Bytecode::is_valid method. + */ +enum class OpCode : uint8_t { + // Compute + // Compute - Arithmetic + ADD, + SUB, + MUL, + DIV, + // Compute - Comparators + EQ, + LT, + LTE, + // Compute - Bitwise + AND, + OR, + XOR, + NOT, + SHL, + SHR, + // Compute - Type Conversions + CAST, + + // Execution Environment + ADDRESS, + STORAGEADDRESS, + ORIGIN, + SENDER, + PORTAL, + FEEPERL1GAS, + FEEPERL2GAS, + FEEPERDAGAS, + CONTRACTCALLDEPTH, + // Execution Environment - Globals + CHAINID, + VERSION, + BLOCKNUMBER, + TIMESTAMP, + COINBASE, + BLOCKL1GASLIMIT, + BLOCKL2GASLIMIT, + BLOCKDAGASLIMIT, + // Execution Environment - Calldata + CALLDATACOPY, + + // Machine State + // Machine State - Gas + L1GASLEFT, + L2GASLEFT, + DAGASLEFT, + // Machine State - Internal Control Flow + JUMP, + JUMPI, + INTERNALCALL, + INTERNALRETURN, + // Machine State - Memory + SET, + MOV, + CMOV, + + // World State + BLOCKHEADERBYNUMBER, + SLOAD, // Public Storage + SSTORE, // Public Storage + READL1TOL2MSG, // Messages + SENDL2TOL1MSG, // Messages + EMITNOTEHASH, // Notes & Nullifiers + EMITNULLIFIER, // Notes & Nullifiers + + // Accrued Substate + EMITUNENCRYPTEDLOG, + + // Control Flow - Contract Calls + CALL, + STATICCALL, + RETURN, + REVERT, + + // Gadgets + KECCAK, + POSEIDON, +}; + +class Bytecode { + public: + static bool is_valid(uint8_t byte); + static bool has_in_tag(OpCode); + static const std::unordered_map OPERANDS_NUM; +}; + +} // namespace avm_trace \ No newline at end of file diff --git a/barretenberg/cpp/src/barretenberg/vm/avm_trace/AvmMini_trace.cpp b/barretenberg/cpp/src/barretenberg/vm/avm_trace/AvmMini_trace.cpp index 771a1590ea1..88aac4a09e6 100644 --- a/barretenberg/cpp/src/barretenberg/vm/avm_trace/AvmMini_trace.cpp +++ b/barretenberg/cpp/src/barretenberg/vm/avm_trace/AvmMini_trace.cpp @@ -279,10 +279,10 @@ void AvmMiniTraceBuilder::set(uint128_t val, uint32_t dst_offset, AvmMemoryTag i * @param dst_offset The starting index of memory where calldata will be copied to. * @param call_data_mem The vector containing calldata. */ -void AvmMiniTraceBuilder::call_data_copy(uint32_t cd_offset, - uint32_t copy_size, - uint32_t dst_offset, - std::vector const& call_data_mem) +void AvmMiniTraceBuilder::calldata_copy(uint32_t cd_offset, + uint32_t copy_size, + uint32_t dst_offset, + std::vector const& call_data_mem) { // We parallelize storing memory operations in chunk of 3, i.e., 1 per intermediate register. // The variable pos is an index pointing to the first storing operation (pertaining to intermediate @@ -375,6 +375,11 @@ void AvmMiniTraceBuilder::call_data_copy(uint32_t cd_offset, */ std::vector AvmMiniTraceBuilder::return_op(uint32_t ret_offset, uint32_t ret_size) { + if (ret_size == 0) { + halt(); + return std::vector{}; + } + // We parallelize loading memory operations in chunk of 3, i.e., 1 per intermediate register. // The variable pos is an index pointing to the first storing operation (pertaining to intermediate // register Ia) relative to ret_offset: @@ -447,6 +452,7 @@ std::vector AvmMiniTraceBuilder::return_op(uint32_t ret_offset, uint32_t ret pos = ret_size; } } + pc = UINT32_MAX; // This ensures that no subsequent opcode will be executed. return returnMem; } @@ -466,6 +472,8 @@ void AvmMiniTraceBuilder::halt() .avmMini_internal_return_ptr = FF(internal_return_ptr), .avmMini_sel_halt = FF(1), }); + + pc = UINT32_MAX; // This ensures that no subsequent opcode will be executed. } /** diff --git a/barretenberg/cpp/src/barretenberg/vm/avm_trace/AvmMini_trace.hpp b/barretenberg/cpp/src/barretenberg/vm/avm_trace/AvmMini_trace.hpp index af09f2e4d14..381f8f1586b 100644 --- a/barretenberg/cpp/src/barretenberg/vm/avm_trace/AvmMini_trace.hpp +++ b/barretenberg/cpp/src/barretenberg/vm/avm_trace/AvmMini_trace.hpp @@ -4,6 +4,7 @@ #include "AvmMini_alu_trace.hpp" #include "AvmMini_common.hpp" +#include "AvmMini_instructions.hpp" #include "AvmMini_mem_trace.hpp" #include "barretenberg/common/throw_or_abort.hpp" @@ -25,6 +26,8 @@ class AvmMiniTraceBuilder { std::vector finalize(); void reset(); + uint32_t getPc() const { return pc; } + // Addition with direct memory access. void add(uint32_t a_offset, uint32_t b_offset, uint32_t dst_offset, AvmMemoryTag in_tag); @@ -55,10 +58,10 @@ class AvmMiniTraceBuilder { // CALLDATACOPY opcode with direct memory access, i.e., // M[dst_offset:dst_offset+copy_size] = calldata[cd_offset:cd_offset+copy_size] - void call_data_copy(uint32_t cd_offset, - uint32_t copy_size, - uint32_t dst_offset, - std::vector const& call_data_mem); + void calldata_copy(uint32_t cd_offset, + uint32_t copy_size, + uint32_t dst_offset, + std::vector const& call_data_mem); // RETURN opcode with direct memory access, i.e., // return(M[ret_offset:ret_offset+ret_size]) diff --git a/barretenberg/cpp/src/barretenberg/vm/generated/AvmMini_prover.cpp b/barretenberg/cpp/src/barretenberg/vm/generated/AvmMini_prover.cpp index 552582558c4..5dc17405239 100644 --- a/barretenberg/cpp/src/barretenberg/vm/generated/AvmMini_prover.cpp +++ b/barretenberg/cpp/src/barretenberg/vm/generated/AvmMini_prover.cpp @@ -14,6 +14,7 @@ namespace bb::honk { using Flavor = honk::flavor::AvmMiniFlavor; +using FF = Flavor::FF; /** * Create AvmMiniProver from proving key, witness and manifest. @@ -98,13 +99,13 @@ void AvmMiniProver::execute_zeromorph_rounds() transcript); } -plonk::proof& AvmMiniProver::export_proof() +honk::proof& AvmMiniProver::export_proof() { - proof.proof_data = transcript->proof_data; + proof = transcript->proof_data; return proof; } -plonk::proof& AvmMiniProver::construct_proof() +bb::honk::proof& AvmMiniProver::construct_proof() { // Add circuit size public input size and public inputs to transcript. execute_preamble_round(); diff --git a/barretenberg/cpp/src/barretenberg/vm/generated/AvmMini_prover.hpp b/barretenberg/cpp/src/barretenberg/vm/generated/AvmMini_prover.hpp index 42b89a228f2..7c88d3b455e 100644 --- a/barretenberg/cpp/src/barretenberg/vm/generated/AvmMini_prover.hpp +++ b/barretenberg/cpp/src/barretenberg/vm/generated/AvmMini_prover.hpp @@ -3,7 +3,7 @@ #pragma once #include "barretenberg/commitment_schemes/zeromorph/zeromorph.hpp" #include "barretenberg/flavor/generated/AvmMini_flavor.hpp" -#include "barretenberg/plonk/proof_system/types/proof.hpp" +#include "barretenberg/honk/proof_system/types/proof.hpp" #include "barretenberg/relations/relation_parameters.hpp" #include "barretenberg/sumcheck/sumcheck_output.hpp" #include "barretenberg/transcript/transcript.hpp" @@ -31,8 +31,8 @@ class AvmMiniProver { void execute_relation_check_rounds(); void execute_zeromorph_rounds(); - plonk::proof& export_proof(); - plonk::proof& construct_proof(); + honk::proof& export_proof(); + honk::proof& construct_proof(); std::shared_ptr transcript = std::make_shared(); @@ -56,7 +56,7 @@ class AvmMiniProver { using ZeroMorph = pcs::zeromorph::ZeroMorphProver_; private: - plonk::proof proof; + honk::proof proof; }; } // namespace bb::honk diff --git a/barretenberg/cpp/src/barretenberg/vm/generated/AvmMini_verifier.cpp b/barretenberg/cpp/src/barretenberg/vm/generated/AvmMini_verifier.cpp index 09beb8a866e..5d18e651ac7 100644 --- a/barretenberg/cpp/src/barretenberg/vm/generated/AvmMini_verifier.cpp +++ b/barretenberg/cpp/src/barretenberg/vm/generated/AvmMini_verifier.cpp @@ -30,7 +30,7 @@ AvmMiniVerifier& AvmMiniVerifier::operator=(AvmMiniVerifier&& other) noexcept * @brief This function verifies an AvmMini Honk proof for given program settings. * */ -bool AvmMiniVerifier::verify_proof(const plonk::proof& proof) +bool AvmMiniVerifier::verify_proof(const honk::proof& proof) { using Flavor = honk::flavor::AvmMiniFlavor; using FF = Flavor::FF; @@ -42,7 +42,7 @@ bool AvmMiniVerifier::verify_proof(const plonk::proof& proof) RelationParameters relation_parameters; - transcript = std::make_shared(proof.proof_data); + transcript = std::make_shared(proof); VerifierCommitments commitments{ key }; CommitmentLabels commitment_labels; diff --git a/barretenberg/cpp/src/barretenberg/vm/generated/AvmMini_verifier.hpp b/barretenberg/cpp/src/barretenberg/vm/generated/AvmMini_verifier.hpp index 31b04749640..81aa4eea673 100644 --- a/barretenberg/cpp/src/barretenberg/vm/generated/AvmMini_verifier.hpp +++ b/barretenberg/cpp/src/barretenberg/vm/generated/AvmMini_verifier.hpp @@ -2,7 +2,7 @@ #pragma once #include "barretenberg/flavor/generated/AvmMini_flavor.hpp" -#include "barretenberg/plonk/proof_system/types/proof.hpp" +#include "barretenberg/honk/proof_system/types/proof.hpp" #include "barretenberg/sumcheck/sumcheck.hpp" namespace bb::honk { @@ -22,7 +22,7 @@ class AvmMiniVerifier { AvmMiniVerifier& operator=(const AvmMiniVerifier& other) = delete; AvmMiniVerifier& operator=(AvmMiniVerifier&& other) noexcept; - bool verify_proof(const plonk::proof& proof); + bool verify_proof(const honk::proof& proof); std::shared_ptr key; std::map commitments; diff --git a/barretenberg/cpp/src/barretenberg/vm/generated/Toy_prover.cpp b/barretenberg/cpp/src/barretenberg/vm/generated/Toy_prover.cpp index df3c1148216..482261dda9f 100644 --- a/barretenberg/cpp/src/barretenberg/vm/generated/Toy_prover.cpp +++ b/barretenberg/cpp/src/barretenberg/vm/generated/Toy_prover.cpp @@ -94,13 +94,13 @@ void ToyProver::execute_zeromorph_rounds() transcript); } -plonk::proof& ToyProver::export_proof() +honk::proof& ToyProver::export_proof() { - proof.proof_data = transcript->proof_data; + proof = transcript->proof_data; return proof; } -plonk::proof& ToyProver::construct_proof() +honk::proof& ToyProver::construct_proof() { // Add circuit size public input size and public inputs to transcript. execute_preamble_round(); diff --git a/barretenberg/cpp/src/barretenberg/vm/generated/Toy_prover.hpp b/barretenberg/cpp/src/barretenberg/vm/generated/Toy_prover.hpp index 292c29f5d50..a0b0ec62cfa 100644 --- a/barretenberg/cpp/src/barretenberg/vm/generated/Toy_prover.hpp +++ b/barretenberg/cpp/src/barretenberg/vm/generated/Toy_prover.hpp @@ -31,8 +31,8 @@ class ToyProver { void execute_relation_check_rounds(); void execute_zeromorph_rounds(); - plonk::proof& export_proof(); - plonk::proof& construct_proof(); + honk::proof& export_proof(); + honk::proof& construct_proof(); std::shared_ptr transcript = std::make_shared(); @@ -56,7 +56,7 @@ class ToyProver { using ZeroMorph = pcs::zeromorph::ZeroMorphProver_; private: - plonk::proof proof; + honk::proof proof; }; } // namespace bb::honk diff --git a/barretenberg/cpp/src/barretenberg/vm/generated/Toy_verifier.cpp b/barretenberg/cpp/src/barretenberg/vm/generated/Toy_verifier.cpp index f2711e8316e..f153991ed90 100644 --- a/barretenberg/cpp/src/barretenberg/vm/generated/Toy_verifier.cpp +++ b/barretenberg/cpp/src/barretenberg/vm/generated/Toy_verifier.cpp @@ -30,7 +30,7 @@ ToyVerifier& ToyVerifier::operator=(ToyVerifier&& other) noexcept * @brief This function verifies an Toy Honk proof for given program settings. * */ -bool ToyVerifier::verify_proof(const plonk::proof& proof) +bool ToyVerifier::verify_proof(const honk::proof& proof) { using Flavor = honk::flavor::ToyFlavor; using FF = Flavor::FF; @@ -42,7 +42,7 @@ bool ToyVerifier::verify_proof(const plonk::proof& proof) RelationParameters relation_parameters; - transcript = std::make_shared(proof.proof_data); + transcript = std::make_shared(proof); VerifierCommitments commitments{ key }; CommitmentLabels commitment_labels; diff --git a/barretenberg/cpp/src/barretenberg/vm/generated/Toy_verifier.hpp b/barretenberg/cpp/src/barretenberg/vm/generated/Toy_verifier.hpp index f69e6996f70..ce84ed8ab6b 100644 --- a/barretenberg/cpp/src/barretenberg/vm/generated/Toy_verifier.hpp +++ b/barretenberg/cpp/src/barretenberg/vm/generated/Toy_verifier.hpp @@ -2,7 +2,7 @@ #pragma once #include "barretenberg/flavor/generated/Toy_flavor.hpp" -#include "barretenberg/plonk/proof_system/types/proof.hpp" +#include "barretenberg/honk/proof_system/types/proof.hpp" #include "barretenberg/sumcheck/sumcheck.hpp" namespace bb::honk { @@ -22,7 +22,7 @@ class ToyVerifier { ToyVerifier& operator=(const ToyVerifier& other) = delete; ToyVerifier& operator=(ToyVerifier&& other) noexcept; - bool verify_proof(const plonk::proof& proof); + bool verify_proof(const honk::proof& proof); std::shared_ptr key; std::map commitments; diff --git a/barretenberg/cpp/src/barretenberg/vm/tests/AvmMini_arithmetic.test.cpp b/barretenberg/cpp/src/barretenberg/vm/tests/AvmMini_arithmetic.test.cpp index 15f4e0a37ee..6cde3d10186 100644 --- a/barretenberg/cpp/src/barretenberg/vm/tests/AvmMini_arithmetic.test.cpp +++ b/barretenberg/cpp/src/barretenberg/vm/tests/AvmMini_arithmetic.test.cpp @@ -3,8 +3,8 @@ #include "barretenberg/numeric/uint128/uint128.hpp" using namespace numeric; -using namespace tests_avm; namespace { +using namespace tests_avm; void common_validate_arithmetic_op(Row const& main_row, Row const& alu_row, @@ -35,7 +35,7 @@ void common_validate_arithmetic_op(Row const& main_row, // Check the instruction tag EXPECT_EQ(main_row.avmMini_in_tag, FF(static_cast(tag))); - // Check that intermediate rgiesters are correctly copied in Alu trace + // Check that intermediate registers are correctly copied in Alu trace EXPECT_EQ(alu_row.aluChip_alu_ia, a); EXPECT_EQ(alu_row.aluChip_alu_ib, b); EXPECT_EQ(alu_row.aluChip_alu_ic, c); @@ -184,7 +184,9 @@ std::vector gen_mutated_trace_mul(FF const& a, FF const& b, FF const& c_mut } // anonymous namespace +namespace tests_avm { using namespace avm_trace; + class AvmMiniArithmeticTests : public ::testing::Test { public: AvmMiniTraceBuilder trace_builder; @@ -244,7 +246,7 @@ class AvmMiniArithmeticNegativeTestsU128 : public AvmMiniArithmeticTests {}; TEST_F(AvmMiniArithmeticTestsFF, addition) { // trace_builder - trace_builder.call_data_copy(0, 3, 0, std::vector{ 37, 4, 11 }); + trace_builder.calldata_copy(0, 3, 0, std::vector{ 37, 4, 11 }); // Memory layout: [37,4,11,0,0,0,....] trace_builder.add(0, 1, 4, AvmMemoryTag::FF); // [37,4,11,0,41,0,....] @@ -263,7 +265,7 @@ TEST_F(AvmMiniArithmeticTestsFF, addition) // Test on basic subtraction over finite field type. TEST_F(AvmMiniArithmeticTestsFF, subtraction) { - trace_builder.call_data_copy(0, 3, 0, std::vector{ 8, 4, 17 }); + trace_builder.calldata_copy(0, 3, 0, std::vector{ 8, 4, 17 }); // Memory layout: [8,4,17,0,0,0,....] trace_builder.sub(2, 0, 1, AvmMemoryTag::FF); // [8,9,17,0,0,0....] @@ -282,7 +284,7 @@ TEST_F(AvmMiniArithmeticTestsFF, subtraction) // Test on basic multiplication over finite field type. TEST_F(AvmMiniArithmeticTestsFF, multiplication) { - trace_builder.call_data_copy(0, 3, 0, std::vector{ 5, 0, 20 }); + trace_builder.calldata_copy(0, 3, 0, std::vector{ 5, 0, 20 }); // Memory layout: [5,0,20,0,0,0,....] trace_builder.mul(2, 0, 1, AvmMemoryTag::FF); // [5,100,20,0,0,0....] @@ -302,7 +304,7 @@ TEST_F(AvmMiniArithmeticTestsFF, multiplication) // Test on multiplication by zero over finite field type. TEST_F(AvmMiniArithmeticTestsFF, multiplicationByZero) { - trace_builder.call_data_copy(0, 1, 0, std::vector{ 127 }); + trace_builder.calldata_copy(0, 1, 0, std::vector{ 127 }); // Memory layout: [127,0,0,0,0,0,....] trace_builder.mul(0, 1, 2, AvmMemoryTag::FF); // [127,0,0,0,0,0....] @@ -322,7 +324,7 @@ TEST_F(AvmMiniArithmeticTestsFF, multiplicationByZero) // Test on basic division over finite field type. TEST_F(AvmMiniArithmeticTestsFF, division) { - trace_builder.call_data_copy(0, 2, 0, std::vector{ 15, 315 }); + trace_builder.calldata_copy(0, 2, 0, std::vector{ 15, 315 }); // Memory layout: [15,315,0,0,0,0,....] trace_builder.div(1, 0, 2, AvmMemoryTag::FF); // [15,315,21,0,0,0....] @@ -345,7 +347,7 @@ TEST_F(AvmMiniArithmeticTestsFF, division) // Test on division with zero numerator over finite field type. TEST_F(AvmMiniArithmeticTestsFF, divisionNumeratorZero) { - trace_builder.call_data_copy(0, 1, 0, std::vector{ 15 }); + trace_builder.calldata_copy(0, 1, 0, std::vector{ 15 }); // Memory layout: [15,0,0,0,0,0,....] trace_builder.div(1, 0, 0, AvmMemoryTag::FF); // [0,0,0,0,0,0....] @@ -369,7 +371,7 @@ TEST_F(AvmMiniArithmeticTestsFF, divisionNumeratorZero) // We check that the operator error flag is raised. TEST_F(AvmMiniArithmeticTestsFF, divisionByZeroError) { - trace_builder.call_data_copy(0, 1, 0, std::vector{ 15 }); + trace_builder.calldata_copy(0, 1, 0, std::vector{ 15 }); // Memory layout: [15,0,0,0,0,0,....] trace_builder.div(0, 1, 2, AvmMemoryTag::FF); // [15,0,0,0,0,0....] @@ -419,7 +421,7 @@ TEST_F(AvmMiniArithmeticTestsFF, divisionZeroByZeroError) // No check on the evaluation is performed here. TEST_F(AvmMiniArithmeticTestsFF, mixedOperationsWithError) { - trace_builder.call_data_copy(0, 3, 2, std::vector{ 45, 23, 12 }); + trace_builder.calldata_copy(0, 3, 2, std::vector{ 45, 23, 12 }); // Memory layout: [0,0,45,23,12,0,0,0,....] trace_builder.add(2, 3, 4, AvmMemoryTag::FF); // [0,0,45,23,68,0,0,0,....] @@ -1397,7 +1399,7 @@ TEST_F(AvmMiniArithmeticNegativeTestsFF, multiplication) // Test on basic incorrect division over finite field type. TEST_F(AvmMiniArithmeticNegativeTestsFF, divisionFF) { - trace_builder.call_data_copy(0, 2, 0, std::vector{ 15, 315 }); + trace_builder.calldata_copy(0, 2, 0, std::vector{ 15, 315 }); // Memory layout: [15,315,0,0,0,0,....] trace_builder.div(1, 0, 2, AvmMemoryTag::FF); // [15,315,21,0,0,0....] @@ -1414,7 +1416,7 @@ TEST_F(AvmMiniArithmeticNegativeTestsFF, divisionFF) // in the trace. TEST_F(AvmMiniArithmeticNegativeTestsFF, divisionNoZeroButError) { - trace_builder.call_data_copy(0, 2, 0, std::vector{ 15, 315 }); + trace_builder.calldata_copy(0, 2, 0, std::vector{ 15, 315 }); // Memory layout: [15,315,0,0,0,0,....] trace_builder.div(1, 0, 2, AvmMemoryTag::FF); // [15,315,21,0,0,0....] @@ -1440,7 +1442,7 @@ TEST_F(AvmMiniArithmeticNegativeTestsFF, divisionNoZeroButError) // Test with division by zero occurs and no error is raised (remove error flag) TEST_F(AvmMiniArithmeticNegativeTestsFF, divisionByZeroNoError) { - trace_builder.call_data_copy(0, 1, 0, std::vector{ 15 }); + trace_builder.calldata_copy(0, 1, 0, std::vector{ 15 }); // Memory layout: [15,0,0,0,0,0,....] trace_builder.div(0, 1, 2, AvmMemoryTag::FF); // [15,0,0,0,0,0....] @@ -1477,7 +1479,7 @@ TEST_F(AvmMiniArithmeticNegativeTestsFF, divisionZeroByZeroNoError) // the addition, subtraction, multiplication. TEST_F(AvmMiniArithmeticNegativeTestsFF, operationWithErrorFlag) { - trace_builder.call_data_copy(0, 3, 0, std::vector{ 37, 4, 11 }); + trace_builder.calldata_copy(0, 3, 0, std::vector{ 37, 4, 11 }); // Memory layout: [37,4,11,0,0,0,....] trace_builder.add(0, 1, 4, AvmMemoryTag::FF); // [37,4,11,0,41,0,....] @@ -1495,7 +1497,7 @@ TEST_F(AvmMiniArithmeticNegativeTestsFF, operationWithErrorFlag) trace_builder.reset(); - trace_builder.call_data_copy(0, 3, 0, std::vector{ 8, 4, 17 }); + trace_builder.calldata_copy(0, 3, 0, std::vector{ 8, 4, 17 }); // Memory layout: [8,4,17,0,0,0,....] trace_builder.sub(2, 0, 1, AvmMemoryTag::FF); // [8,9,17,0,0,0....] @@ -1512,7 +1514,7 @@ TEST_F(AvmMiniArithmeticNegativeTestsFF, operationWithErrorFlag) trace_builder.reset(); - trace_builder.call_data_copy(0, 3, 0, std::vector{ 5, 0, 20 }); + trace_builder.calldata_copy(0, 3, 0, std::vector{ 5, 0, 20 }); // Memory layout: [5,0,20,0,0,0,....] trace_builder.mul(2, 0, 1, AvmMemoryTag::FF); // [5,100,20,0,0,0....] @@ -1675,4 +1677,6 @@ TEST_F(AvmMiniArithmeticNegativeTestsU128, multiplication) FF{ uint256_t::from_uint128(c) }, AvmMemoryTag::U128); EXPECT_THROW_WITH_MESSAGE(validate_trace_proof(std::move(trace)), "ALU_MULTIPLICATION_OUT_U128"); -} \ No newline at end of file +} + +} // namespace tests_avm \ No newline at end of file diff --git a/barretenberg/cpp/src/barretenberg/vm/tests/AvmMini_control_flow.test.cpp b/barretenberg/cpp/src/barretenberg/vm/tests/AvmMini_control_flow.test.cpp index 657ae8e25b1..11ede61acfc 100644 --- a/barretenberg/cpp/src/barretenberg/vm/tests/AvmMini_control_flow.test.cpp +++ b/barretenberg/cpp/src/barretenberg/vm/tests/AvmMini_control_flow.test.cpp @@ -1,7 +1,7 @@ #include "AvmMini_common.test.hpp" +namespace tests_avm { using namespace avm_trace; -using namespace tests_avm; class AvmMiniControlFlowTests : public ::testing::Test { public: @@ -285,3 +285,4 @@ TEST_F(AvmMiniControlFlowTests, multipleCallsAndReturns) validate_trace_proof(std::move(trace)); } +} // namespace tests_avm \ No newline at end of file diff --git a/barretenberg/cpp/src/barretenberg/vm/tests/AvmMini_execution.test.cpp b/barretenberg/cpp/src/barretenberg/vm/tests/AvmMini_execution.test.cpp new file mode 100644 index 00000000000..afe4386816d --- /dev/null +++ b/barretenberg/cpp/src/barretenberg/vm/tests/AvmMini_execution.test.cpp @@ -0,0 +1,505 @@ +#include "barretenberg/vm/avm_trace/AvmMini_execution.hpp" +#include "AvmMini_common.test.hpp" +#include "barretenberg/common/utils.hpp" +#include "barretenberg/vm/avm_trace/AvmMini_common.hpp" +#include "barretenberg/vm/avm_trace/AvmMini_helper.hpp" +#include "barretenberg/vm/avm_trace/AvmMini_opcode.hpp" +#include "barretenberg/vm/tests/helpers.test.hpp" +#include +#include +#include +#include + +namespace { +void gen_proof_and_validate(std::vector const& bytecode, + std::vector&& trace, + std::vector const& calldata) +{ + auto circuit_builder = AvmMiniCircuitBuilder(); + circuit_builder.set_trace(std::move(trace)); + EXPECT_TRUE(circuit_builder.check_circuit()); + + auto composer = honk::AvmMiniComposer(); + auto verifier = composer.create_verifier(circuit_builder); + + auto proof = avm_trace::Execution::run_and_prove(bytecode, calldata); + + EXPECT_TRUE(verifier.verify_proof(proof)); +} +} // namespace + +namespace tests_avm { +using namespace avm_trace; +using bb::utils::hex_to_bytes; + +class AvmMiniExecutionTests : public ::testing::Test { + public: + AvmMiniTraceBuilder trace_builder; + + protected: + // TODO(640): The Standard Honk on Grumpkin test suite fails unless the SRS is initialised for every test. + void SetUp() override + { + srs::init_crs_factory("../srs_db/ignition"); + trace_builder = AvmMiniTraceBuilder(); // Clean instance for every run. + }; +}; + +// Basic positive test with an ADD and RETURN opcode. +// Parsing, trace generation and proving is verified. +TEST_F(AvmMiniExecutionTests, basicAddReturn) +{ + std::string bytecode_hex = "00" // ADD + "01" // U8 + "00000007" // addr a 7 + "00000009" // addr b 9 + "00000001" // addr c 1 + "34" // RETURN + "00000000" // ret offset 0 + "00000000"; // ret size 0 + + auto bytecode = hex_to_bytes(bytecode_hex); + auto instructions = Execution::parse(bytecode); + + // 2 instructions + EXPECT_EQ(instructions.size(), 2); + + // ADD + EXPECT_EQ(instructions.at(0).op_code, OpCode::ADD); + EXPECT_EQ(instructions.at(0).operands.size(), 3); + EXPECT_EQ(instructions.at(0).operands.at(0), 7); + EXPECT_EQ(instructions.at(0).operands.at(1), 9); + EXPECT_EQ(instructions.at(0).operands.at(2), 1); + EXPECT_EQ(instructions.at(0).in_tag, AvmMemoryTag::U8); + + // RETURN + EXPECT_EQ(instructions.at(1).op_code, OpCode::RETURN); + EXPECT_EQ(instructions.at(1).operands.size(), 2); + EXPECT_EQ(instructions.at(1).operands.at(0), 0); + EXPECT_EQ(instructions.at(1).operands.at(0), 0); + + auto trace = Execution::gen_trace(instructions, std::vector{}); + + gen_proof_and_validate(bytecode, std::move(trace), std::vector{}); +} + +// Positive test for SET and SUB opcodes +TEST_F(AvmMiniExecutionTests, setAndSubOpcodes) +{ + std::string bytecode_hex = "27" // SET 39 = 0x27 + "02" // U16 + "B813" // val 47123 + "000000AA" // dst_offset 170 + "27" // SET 39 = 0x27 + "02" // U16 + "9103" // val 37123 + "00000033" // dst_offset 51 + "01" // SUB + "02" // U16 + "000000AA" // addr a + "00000033" // addr b + "00000001" // addr c 1 + "34" // RETURN + "00000000" // ret offset 0 + "00000000"; // ret size 0 + + auto bytecode = hex_to_bytes(bytecode_hex); + auto instructions = Execution::parse(bytecode); + + EXPECT_EQ(instructions.size(), 4); + + // SET + EXPECT_EQ(instructions.at(0).op_code, OpCode::SET); + EXPECT_EQ(instructions.at(0).operands.size(), 2); + EXPECT_EQ(instructions.at(0).operands.at(0), 47123); + EXPECT_EQ(instructions.at(0).operands.at(1), 170); + EXPECT_EQ(instructions.at(0).in_tag, AvmMemoryTag::U16); + + // SET + EXPECT_EQ(instructions.at(1).op_code, OpCode::SET); + EXPECT_EQ(instructions.at(1).operands.size(), 2); + EXPECT_EQ(instructions.at(1).operands.at(0), 37123); + EXPECT_EQ(instructions.at(1).operands.at(1), 51); + EXPECT_EQ(instructions.at(1).in_tag, AvmMemoryTag::U16); + + // SUB + EXPECT_EQ(instructions.at(2).op_code, OpCode::SUB); + EXPECT_EQ(instructions.at(2).operands.size(), 3); + EXPECT_EQ(instructions.at(2).operands.at(0), 170); + EXPECT_EQ(instructions.at(2).operands.at(1), 51); + EXPECT_EQ(instructions.at(2).in_tag, AvmMemoryTag::U16); + + auto trace = Execution::gen_trace(instructions, std::vector{}); + + // Find the first row enabling the subtraction selector + auto row = std::ranges::find_if(trace.begin(), trace.end(), [](Row r) { return r.avmMini_sel_op_sub == 1; }); + EXPECT_EQ(row->avmMini_ic, 10000); // 47123 - 37123 = 10000 + + gen_proof_and_validate(bytecode, std::move(trace), std::vector{}); +} + +// Positive test for multiple MUL opcodes +// We compute 5^12 based on U64 multiplications +// 5 is stored at offset 0 and 1 at offset 1 +// Repeat 12 times a multiplication of value +// at offset 0 (5) with value at offset 1 and store +// the result at offset 1. +TEST_F(AvmMiniExecutionTests, powerWithMulOpcodes) +{ + std::string bytecode_hex = "27" // SET 39 = 0x27 + "04" // U64 + "00000000" // val 5 higher 32 bits + "00000005" // val 5 lower 32 bits + "00000000" // dst_offset 0 + "27" // SET 39 = 0x27 + "04" // U64 + "00000000" // val 1 higher 32 bits + "00000001" // val 1 lower 32 bits + "00000001"; // dst_offset 1 + + std::string const mul_hex = "02" // MUL + "04" // U64 + "00000000" // addr a + "00000001" // addr b + "00000001"; // addr c 1 + + std::string const ret_hex = "34" // RETURN + "00000000" // ret offset 0 + "00000000"; // ret size 0 + + uint8_t num = 12; + while (num-- > 0) { + bytecode_hex.append(mul_hex); + } + + bytecode_hex.append(ret_hex); + + auto bytecode = hex_to_bytes(bytecode_hex); + auto instructions = Execution::parse(bytecode); + + EXPECT_EQ(instructions.size(), 15); + + // MUL first pos + EXPECT_EQ(instructions.at(2).op_code, OpCode::MUL); + EXPECT_EQ(instructions.at(2).operands.size(), 3); + EXPECT_EQ(instructions.at(2).operands.at(0), 0); + EXPECT_EQ(instructions.at(2).operands.at(1), 1); + EXPECT_EQ(instructions.at(2).operands.at(2), 1); + EXPECT_EQ(instructions.at(2).in_tag, AvmMemoryTag::U64); + + // MUL last pos + EXPECT_EQ(instructions.at(13).op_code, OpCode::MUL); + EXPECT_EQ(instructions.at(13).operands.size(), 3); + EXPECT_EQ(instructions.at(13).operands.at(0), 0); + EXPECT_EQ(instructions.at(13).operands.at(1), 1); + EXPECT_EQ(instructions.at(13).operands.at(2), 1); + EXPECT_EQ(instructions.at(13).in_tag, AvmMemoryTag::U64); + + // RETURN + EXPECT_EQ(instructions.at(14).op_code, OpCode::RETURN); + EXPECT_EQ(instructions.at(14).operands.size(), 2); + EXPECT_EQ(instructions.at(14).operands.at(0), 0); + EXPECT_EQ(instructions.at(14).operands.at(0), 0); + + auto trace = Execution::gen_trace(instructions, std::vector{}); + + // Find the first row enabling the multiplication selector and pc = 13 + auto row = std::ranges::find_if( + trace.begin(), trace.end(), [](Row r) { return r.avmMini_sel_op_mul == 1 && r.avmMini_pc == 13; }); + EXPECT_EQ(row->avmMini_ic, 244140625); // 5^12 = 244140625 + + gen_proof_and_validate(bytecode, std::move(trace), std::vector{}); +} + +// Positive test about a single internal_call and internal_return +// Code of internal routine is SET U32 value 123456789 at memory address 7 +// The bytecode execution is: +// SET U32 val. 222111000 at memory address 4 +// CALL internal routine +// ADD M[4] with M[7] and output in M[9] +// Internal routine bytecode is at the end. +// Bytecode layout: SET INTERNAL_CALL ADD RETURN SET INTERNAL_RETURN +// 0 1 2 3 4 5 +TEST_F(AvmMiniExecutionTests, simpleInternalCall) +{ + std::string bytecode_hex = "27" // SET 39 = 0x27 + "03" // U32 + "0D3D2518" // val 222111000 = 0xD3D2518 + "00000004" // dst_offset 4 + "25" // INTERNALCALL 37 + "00000004" // jmp_dest + "00" // ADD + "03" // U32 + "00000004" // addr a 4 + "00000007" // addr b 7 + "00000009" // addr c9 + "34" // RETURN + "00000000" // ret offset 0 + "00000000" // ret size 0 + "27" // SET 39 = 0x27 + "03" // U32 + "075BCD15" // val 123456789 = 0x75BCD15 + "00000007" // dst_offset 7 + "26" // INTERNALRETURN 38 + ; + + auto bytecode = hex_to_bytes(bytecode_hex); + auto instructions = Execution::parse(bytecode); + + EXPECT_EQ(instructions.size(), 6); + + // We test parsing step for INTERNALCALL and INTERNALRETURN. + + // INTERNALCALL + EXPECT_EQ(instructions.at(1).op_code, OpCode::INTERNALCALL); + EXPECT_EQ(instructions.at(1).operands.size(), 1); + EXPECT_EQ(instructions.at(1).operands.at(0), 4); + + // INTERNALRETURN + EXPECT_EQ(instructions.at(5).op_code, OpCode::INTERNALRETURN); + + auto trace = Execution::gen_trace(instructions, std::vector{}); + + // Expected sequence of PCs during execution + std::vector pc_sequence{ 0, 1, 4, 5, 2, 3 }; + + for (size_t i = 0; i < 6; i++) { + EXPECT_EQ(trace.at(i + 1).avmMini_pc, pc_sequence.at(i)); + } + + // Find the first row enabling the addition selector. + auto row = std::ranges::find_if(trace.begin(), trace.end(), [](Row r) { return r.avmMini_sel_op_add == 1; }); + EXPECT_EQ(row->avmMini_ic, 345567789); + + gen_proof_and_validate(bytecode, std::move(trace), std::vector{}); +} + +// Positive test with some nested internall calls +// We use the following functions (internal calls): +// F1: ADD(2,3,2) M[2] = M[2] + M[3] +// F2: MUL(2,3,2) M[2] = M[2] * M[3] +// G: F1 SET(17,3) F2 where SET(17,3) means M[3] = 17 +// MAIN: SET(4,2) SET(7,3) G +// Whole execution should compute: (4 + 7) * 17 = 187 +// Bytecode layout: SET(4,2) SET(7,3) INTERNAL_CALL_G RETURN BYTECODE(F2) BYTECODE(F1) BYTECODE(G) +// 0 1 2 3 4 6 8 +// BYTECODE(F1): ADD(2,3,2) INTERNAL_RETURN +// BYTECODE(F2): MUL(2,3,2) INTERNAL_RETURN +// BYTECODE(G): INTERNAL_CALL(6) SET(17,3) INTERNAL_CALL(4) INTERNAL_RETURN +TEST_F(AvmMiniExecutionTests, nestedInternalCalls) +{ + auto internalCallHex = [](std::string const& dst_offset) { + return "25" + "000000" + + dst_offset; + }; + + auto setHex = [](std::string const& val, std::string const& dst_offset) { + return "2701" // SET U8 + + val + "000000" + dst_offset; + }; + + const std::string tag_address_arguments = "01" // U8 + "00000002" // addr a 2 + "00000003" // addr b 3 + "00000002"; // addr c 2 + + const std::string return_hex = "34" // RETURN + "00000000" // ret offset 0 + "00000000"; // ret size 0 + + const std::string internal_ret_hex = "26"; + const std::string add_hex = "00"; + const std::string mul_hex = "02"; + + const std::string bytecode_f1 = add_hex + tag_address_arguments + internal_ret_hex; + const std::string bytecode_f2 = mul_hex + tag_address_arguments + internal_ret_hex; + const std::string bytecode_g = + internalCallHex("06") + setHex("11", "03") + internalCallHex("04") + internal_ret_hex; + + std::string bytecode_hex = setHex("04", "02") + setHex("07", "03") + internalCallHex("08") + return_hex + + bytecode_f2 + bytecode_f1 + bytecode_g; + + auto bytecode = hex_to_bytes(bytecode_hex); + auto instructions = Execution::parse(bytecode); + + EXPECT_EQ(instructions.size(), 12); + + // Expected sequence of opcodes + std::vector const opcode_sequence{ OpCode::SET, OpCode::SET, + OpCode::INTERNALCALL, OpCode::RETURN, + OpCode::MUL, OpCode::INTERNALRETURN, + OpCode::ADD, OpCode::INTERNALRETURN, + OpCode::INTERNALCALL, OpCode::SET, + OpCode::INTERNALCALL, OpCode::INTERNALRETURN }; + + for (size_t i = 0; i < 12; i++) { + EXPECT_EQ(instructions.at(i).op_code, opcode_sequence.at(i)); + } + + auto trace = Execution::gen_trace(instructions, std::vector{}); + + // Expected sequence of PCs during execution + std::vector pc_sequence{ 0, 1, 2, 8, 6, 7, 9, 10, 4, 5, 11, 3 }; + + for (size_t i = 0; i < 6; i++) { + EXPECT_EQ(trace.at(i + 1).avmMini_pc, pc_sequence.at(i)); + } + + // Find the first row enabling the multiplication selector. + auto row = std::ranges::find_if(trace.begin(), trace.end(), [](Row r) { return r.avmMini_sel_op_mul == 1; }); + EXPECT_EQ(row->avmMini_ic, 187); + EXPECT_EQ(row->avmMini_pc, 4); + + gen_proof_and_validate(bytecode, std::move(trace), std::vector{}); +} + +// Positive test with JUMP and CALLDATACOPY +// We test bytecode which first invoke CALLDATACOPY on a FF array of two values. +// Then, a JUMP call skips a SUB opcode to land to a DIV operation and RETURN. +// Calldata: [13, 156] +// Bytecode layout: CALLDATACOPY JUMP SUB DIV RETURN +// 0 1 2 3 4 +TEST_F(AvmMiniExecutionTests, jumpAndCalldatacopy) +{ + std::string bytecode_hex = "1F" // CALLDATACOPY 31 (no in_tag) + "00000000" // cd_offset + "00000002" // copy_size + "0000000A" // dst_offset // M[10] = 13, M[11] = 156 + "23" // JUMP 35 + "00000003" // jmp_dest (DIV located at 3) + "01" // SUB + "06" // FF + "0000000B" // addr 11 + "0000000A" // addr 10 + "00000001" // addr c 1 (If executed would be 156 - 13 = 143) + "03" // DIV + "06" // FF + "0000000B" // addr 11 + "0000000A" // addr 10 + "00000001" // addr c 1 (156 / 13 = 12) + "34" // RETURN + "00000000" // ret offset 0 + "00000000" // ret size 0 + ; + + auto bytecode = hex_to_bytes(bytecode_hex); + auto instructions = Execution::parse(bytecode); + + EXPECT_EQ(instructions.size(), 5); + + // We test parsing steps for CALLDATACOPY and JUMP. + + // CALLDATACOPY + EXPECT_EQ(instructions.at(0).op_code, OpCode::CALLDATACOPY); + EXPECT_EQ(instructions.at(0).operands.size(), 3); + EXPECT_EQ(instructions.at(0).operands.at(0), 0); + EXPECT_EQ(instructions.at(0).operands.at(1), 2); + EXPECT_EQ(instructions.at(0).operands.at(2), 10); + + // JUMP + EXPECT_EQ(instructions.at(1).op_code, OpCode::JUMP); + EXPECT_EQ(instructions.at(1).operands.size(), 1); + EXPECT_EQ(instructions.at(1).operands.at(0), 3); + + auto trace = Execution::gen_trace(instructions, std::vector{ 13, 156 }); + + // Expected sequence of PCs during execution + std::vector pc_sequence{ 0, 1, 3, 4 }; + + for (size_t i = 0; i < 4; i++) { + EXPECT_EQ(trace.at(i + 1).avmMini_pc, pc_sequence.at(i)); + } + + // Find the first row enabling the division selector. + auto row = std::ranges::find_if(trace.begin(), trace.end(), [](Row r) { return r.avmMini_sel_op_div == 1; }); + EXPECT_EQ(row->avmMini_ic, 12); + + // Find the first row enabling the subtraction selector. + row = std::ranges::find_if(trace.begin(), trace.end(), [](Row r) { return r.avmMini_sel_op_sub == 1; }); + // It must have failed as subtraction was "jumped over". + EXPECT_EQ(row, trace.end()); + + gen_proof_and_validate(bytecode, std::move(trace), std::vector{ 13, 156 }); +} + +// Negative test detecting an invalid opcode byte. +TEST_F(AvmMiniExecutionTests, invalidOpcode) +{ + std::string bytecode_hex = "00" // ADD + "02" // U16 + "00000007" // addr a 7 + "00000009" // addr b 9 + "00000001" // addr c 1 + "AB" // Invalid opcode byte + "00000000" // ret offset 0 + "00000000"; // ret size 0 + + auto bytecode = hex_to_bytes(bytecode_hex); + EXPECT_THROW_WITH_MESSAGE(Execution::parse(bytecode), "opcode"); +} + +// Negative test detecting an invalid memmory instruction tag. +TEST_F(AvmMiniExecutionTests, invalidInstructionTag) +{ + std::string bytecode_hex = "00" // ADD + "00" // Wrong type + "00000007" // addr a 7 + "00000009" // addr b 9 + "00000001" // addr c 1 + "34" // RETURN + "00000000" // ret offset 0 + "00000000"; // ret size 0 + + auto bytecode = hex_to_bytes(bytecode_hex); + EXPECT_THROW_WITH_MESSAGE(Execution::parse(bytecode), "Instruction tag is invalid"); +} + +// Negative test detecting SET opcode with instruction memory tag set to FF. +TEST_F(AvmMiniExecutionTests, ffInstructionTagSetOpcode) +{ + std::string bytecode_hex = "00" // ADD + "05" // U128 + "00000007" // addr a 7 + "00000009" // addr b 9 + "00000001" // addr c 1 + "27" // SET 39 = 0x27 + "06" // tag FF + "00002344"; // + + auto bytecode = hex_to_bytes(bytecode_hex); + EXPECT_THROW_WITH_MESSAGE(Execution::parse(bytecode), "Instruction tag for SET opcode is invalid"); +} + +// Negative test detecting an incomplete instruction: missing instruction tag +TEST_F(AvmMiniExecutionTests, truncatedInstructionNoTag) +{ + std::string bytecode_hex = "00" // ADD + "02" // U16 + "00000007" // addr a 7 + "00000009" // addr b 9 + "00000001" // addr c 1 + "01"; // SUB + + auto bytecode = hex_to_bytes(bytecode_hex); + EXPECT_THROW_WITH_MESSAGE(Execution::parse(bytecode), "Instruction tag missing"); +} + +// Negative test detecting an incomplete instruction: instruction tag present but an operand is missing +TEST_F(AvmMiniExecutionTests, truncatedInstructionNoOperand) +{ + std::string bytecode_hex = "00" // ADD + "02" // U16 + "00000007" // addr a 7 + "00000009" // addr b 9 + "00000001" // addr c 1 + "01" // SUB + "04" // U64 + "AB2373E7" // addr a + "FFFFFFBB"; // addr b and missing address for c = a-b + + auto bytecode = hex_to_bytes(bytecode_hex); + EXPECT_THROW_WITH_MESSAGE(Execution::parse(bytecode), "Operand is missing"); +} + +} // namespace tests_avm \ No newline at end of file diff --git a/barretenberg/cpp/src/barretenberg/vm/tests/AvmMini_memory.test.cpp b/barretenberg/cpp/src/barretenberg/vm/tests/AvmMini_memory.test.cpp index a5ccfd1076a..f51e4139539 100644 --- a/barretenberg/cpp/src/barretenberg/vm/tests/AvmMini_memory.test.cpp +++ b/barretenberg/cpp/src/barretenberg/vm/tests/AvmMini_memory.test.cpp @@ -1,7 +1,7 @@ #include "AvmMini_common.test.hpp" - -using namespace tests_avm; +namespace tests_avm { using namespace avm_trace; + class AvmMiniMemoryTests : public ::testing::Test { public: AvmMiniTraceBuilder trace_builder; @@ -32,7 +32,7 @@ class AvmMiniMemoryTests : public ::testing::Test { // The proof must pass and we check that the AVM error is raised. TEST_F(AvmMiniMemoryTests, mismatchedTag) { - trace_builder.call_data_copy(0, 2, 0, std::vector{ 98, 12 }); + trace_builder.calldata_copy(0, 2, 0, std::vector{ 98, 12 }); trace_builder.add(0, 1, 4, AvmMemoryTag::U8); trace_builder.halt(); @@ -79,7 +79,7 @@ TEST_F(AvmMiniMemoryTests, mismatchedTag) // in the memory trace TEST_F(AvmMiniMemoryTests, mLastAccessViolation) { - trace_builder.call_data_copy(0, 2, 0, std::vector{ 4, 9 }); + trace_builder.calldata_copy(0, 2, 0, std::vector{ 4, 9 }); // Memory layout: [4,9,0,0,0,0,....] trace_builder.sub(1, 0, 2, AvmMemoryTag::U8); // [4,9,5,0,0,0.....] @@ -109,7 +109,7 @@ TEST_F(AvmMiniMemoryTests, mLastAccessViolation) // written into memory TEST_F(AvmMiniMemoryTests, readWriteConsistencyValViolation) { - trace_builder.call_data_copy(0, 2, 0, std::vector{ 4, 9 }); + trace_builder.calldata_copy(0, 2, 0, std::vector{ 4, 9 }); // Memory layout: [4,9,0,0,0,0,....] trace_builder.mul(1, 0, 2, AvmMemoryTag::U8); // [4,9,36,0,0,0.....] @@ -139,7 +139,7 @@ TEST_F(AvmMiniMemoryTests, readWriteConsistencyValViolation) // written into memory TEST_F(AvmMiniMemoryTests, readWriteConsistencyTagViolation) { - trace_builder.call_data_copy(0, 2, 0, std::vector{ 4, 9 }); + trace_builder.calldata_copy(0, 2, 0, std::vector{ 4, 9 }); // Memory layout: [4,9,0,0,0,0,....] trace_builder.mul(1, 0, 2, AvmMemoryTag::U8); // [4,9,36,0,0,0.....] @@ -180,7 +180,7 @@ TEST_F(AvmMiniMemoryTests, readUninitializedMemoryViolation) // must raise a VM error. TEST_F(AvmMiniMemoryTests, mismatchedTagErrorViolation) { - trace_builder.call_data_copy(0, 2, 0, std::vector{ 98, 12 }); + trace_builder.calldata_copy(0, 2, 0, std::vector{ 98, 12 }); trace_builder.sub(0, 1, 4, AvmMemoryTag::U8); trace_builder.halt(); @@ -214,7 +214,7 @@ TEST_F(AvmMiniMemoryTests, mismatchedTagErrorViolation) // must not set a VM error. TEST_F(AvmMiniMemoryTests, consistentTagNoErrorViolation) { - trace_builder.call_data_copy(0, 2, 0, std::vector{ 84, 7 }); + trace_builder.calldata_copy(0, 2, 0, std::vector{ 84, 7 }); trace_builder.div(0, 1, 4, AvmMemoryTag::FF); trace_builder.halt(); @@ -236,3 +236,4 @@ TEST_F(AvmMiniMemoryTests, consistentTagNoErrorViolation) EXPECT_THROW_WITH_MESSAGE(validate_trace_proof(std::move(trace)), "MEM_IN_TAG_CONSISTENCY_1"); } +} // namespace tests_avm \ No newline at end of file diff --git a/barretenberg/ts/CHANGELOG.md b/barretenberg/ts/CHANGELOG.md index 6feb64173df..300ec84bb10 100644 --- a/barretenberg/ts/CHANGELOG.md +++ b/barretenberg/ts/CHANGELOG.md @@ -1,5 +1,12 @@ # Changelog +## [0.21.0](https://github.com/AztecProtocol/aztec-packages/compare/barretenberg.js-v0.20.0...barretenberg.js-v0.21.0) (2024-01-30) + + +### Miscellaneous + +* **barretenberg.js:** Synchronize aztec-packages versions + ## [0.20.0](https://github.com/AztecProtocol/aztec-packages/compare/barretenberg.js-v0.19.0...barretenberg.js-v0.20.0) (2024-01-22) diff --git a/barretenberg/ts/package.json b/barretenberg/ts/package.json index 59b2a7236b0..cb561061d12 100644 --- a/barretenberg/ts/package.json +++ b/barretenberg/ts/package.json @@ -1,6 +1,6 @@ { "name": "@aztec/bb.js", - "version": "0.20.0", + "version": "0.21.0", "homepage": "https://github.com/AztecProtocol/aztec-packages/tree/master/barretenberg/ts", "license": "MIT", "type": "module", diff --git a/bootstrap.sh b/bootstrap.sh index 8e8b31e9b0d..502bdc19956 100755 --- a/bootstrap.sh +++ b/bootstrap.sh @@ -54,6 +54,7 @@ PROJECTS=( barretenberg noir l1-contracts + avm-transpiler yarn-project ) diff --git a/boxes/Dockerfile b/boxes/Dockerfile index 963e664c5db..4191b3559a6 100644 --- a/boxes/Dockerfile +++ b/boxes/Dockerfile @@ -1,11 +1,11 @@ # Builds the boxes (they were copied into yarn-project-base so the cli can unbox). # Produces a container that can be run to test a specific box. See docker-compose.yml. -FROM 278380418400.dkr.ecr.eu-west-2.amazonaws.com/aztec-sandbox AS aztec-sandbox +FROM 278380418400.dkr.ecr.eu-west-2.amazonaws.com/aztec AS aztec FROM 278380418400.dkr.ecr.eu-west-2.amazonaws.com/noir as noir # We need yarn. Start fresh container. FROM node:18.19.0 -COPY --from=aztec-sandbox /usr/src /usr/src +COPY --from=aztec /usr/src /usr/src COPY --from=noir /usr/src/noir/target/release/nargo /usr/src/noir/target/release/nargo WORKDIR /usr/src/boxes ENV AZTEC_NARGO=/usr/src/noir/target/release/nargo diff --git a/boxes/blank-react/src/app/components/contract_function_form.module.scss b/boxes/blank-react/src/app/components/contract_function_form.module.scss deleted file mode 100644 index 056dcc719ff..00000000000 --- a/boxes/blank-react/src/app/components/contract_function_form.module.scss +++ /dev/null @@ -1,66 +0,0 @@ -.input { - border: none; - outline-width: 0; - outline-color: rgba(0, 0, 0, 0); - padding: 2px 20px 0 20px; - width: 100%; - height: 45px; - color: #000; - border: 1px solid rgba(0, 0, 0, 0); - font-size: 16px; - text-align: left; - font-weight: 400; - border-radius: 10px; - text-align: left; - text-overflow: ellipsis; - transition: box-shadow 0.2s; - box-shadow: 0px 4px 10px rgba(0, 0, 0, 0.1); - background-color: white; - -webkit-appearance: none; - - &:disabled { - color: #4a4a4a; - background-color: rgba(239, 239, 239, 0.3); - background: radial-gradient(rgba(239, 239, 239, 0.3), rgba(239, 239, 239, 0.3)); - -webkit-text-fill-color: #4a4a4a; - cursor: not-allowed; - } -} - -.label { - font-weight: 450; - font-size: 18px; - display: flex; - width: 100%; - flex-direction: column; - text-align: left; - margin-bottom: 15px; - justify-content: space-between; -} - -.inputWrapper { - width: 100%; - display: flex; - gap: 15px; -} - -.field { - display: flex; - justify-content: start; - flex-direction: column; - align-items: flex-start; -} - -.content { - display: flex; - justify-content: space-between; - flex-direction: column; - margin: 30px; - width: 450px; - gap: 30px; -} - -.actionButton { - width: 100%; - align-self: center; -} diff --git a/boxes/blank-react/src/app/components/contract_function_form.tsx b/boxes/blank-react/src/app/components/contract_function_form.tsx index 99c4bb08337..8623cf0779a 100644 --- a/boxes/blank-react/src/app/components/contract_function_form.tsx +++ b/boxes/blank-react/src/app/components/contract_function_form.tsx @@ -1,184 +1,188 @@ -import { CONTRACT_ADDRESS_PARAM_NAMES, pxe } from '../../config.js'; -import { callContractFunction, deployContract, viewContractFunction } from '../../scripts/index.js'; -import { convertArgs } from '../../scripts/util.js'; -import styles from './contract_function_form.module.scss'; -import { Button, Loader } from '@aztec/aztec-ui'; -import { AztecAddress, CompleteAddress, ContractArtifact, Fr, FunctionArtifact } from '@aztec/aztec.js'; -import { useFormik } from 'formik'; -import * as Yup from 'yup'; +// import { useEffect } from 'react'; +// import { CONTRACT_ADDRESS_PARAM_NAMES, pxe } from '../../config.js'; +// import { callContractFunction, viewContractFunction } from '../../scripts/index.js'; +// import { convertArgs } from '../../scripts/util.js'; +// import { AztecAddress, CompleteAddress, ContractArtifact, Fr, FunctionArtifact } from '@aztec/aztec.js'; +// import { useFormik } from 'formik'; +// import * as Yup from 'yup'; -type NoirFunctionYupSchema = { - // hack: add `any` at the end to get the array schema to typecheck - // eslint-disable-next-line @typescript-eslint/no-explicit-any - [key: string]: Yup.NumberSchema | Yup.ArraySchema | Yup.BooleanSchema | any; -}; +// type NoirFunctionYupSchema = { +// // hack: add `any` at the end to get the array schema to typecheck +// // eslint-disable-next-line @typescript-eslint/no-explicit-any +// [key: string]: Yup.NumberSchema | Yup.ArraySchema | Yup.BooleanSchema | any; +// }; -type NoirFunctionFormValues = { - [key: string]: string | number | number[] | boolean; -}; +// type NoirFunctionFormValues = { +// [key: string]: string | number | number[] | boolean; +// }; -function generateYupSchema(functionAbi: FunctionArtifact, defaultAddress: string) { - const parameterSchema: NoirFunctionYupSchema = {}; - const initialValues: NoirFunctionFormValues = {}; - for (const param of functionAbi.parameters) { - if (CONTRACT_ADDRESS_PARAM_NAMES.includes(param.name)) { - // these are hex strings instead, but yup doesn't support bigint so we convert back to bigint on execution - parameterSchema[param.name] = Yup.string().required(); - initialValues[param.name] = defaultAddress; - continue; - } - switch (param.type.kind) { - case 'field': - parameterSchema[param.name] = Yup.number().required(); - initialValues[param.name] = 100; - break; - // not really needed for private token, since we hide the nullifier helper method which has the array input - case 'array': - // eslint-disable-next-line no-case-declarations - const arrayLength = param.type.length; - parameterSchema[param.name] = Yup.array() - .of(Yup.number()) - .min(arrayLength) - .max(arrayLength) - .transform(function (value: number[], originalValue: string) { - if (typeof originalValue === 'string') { - return originalValue.split(',').map(Number); - } - return value; - }); - initialValues[param.name] = Array(arrayLength).fill( - CONTRACT_ADDRESS_PARAM_NAMES.includes(param.name) ? defaultAddress : 200, - ); - break; - case 'boolean': - parameterSchema[param.name] = Yup.boolean().required(); - initialValues[param.name] = false; - break; - } - } - return { validationSchema: Yup.object().shape(parameterSchema), initialValues }; -} +// function generateYupSchema(functionAbi: FunctionArtifact, defaultAddress: string) { +// const parameterSchema: NoirFunctionYupSchema = {}; +// const initialValues: NoirFunctionFormValues = {}; +// for (const param of functionAbi.parameters) { +// if (CONTRACT_ADDRESS_PARAM_NAMES.includes(param.name)) { +// // these are hex strings instead, but yup doesn't support bigint so we convert back to bigint on execution +// parameterSchema[param.name] = Yup.string().required(); +// initialValues[param.name] = defaultAddress; +// continue; +// } +// switch (param.type.kind) { +// case 'field': +// parameterSchema[param.name] = Yup.number().required(); +// initialValues[param.name] = 100; +// break; +// // not really needed for private token, since we hide the nullifier helper method which has the array input +// case 'array': +// // eslint-disable-next-line no-case-declarations +// const arrayLength = param.type.length; +// parameterSchema[param.name] = Yup.array() +// .of(Yup.number()) +// .min(arrayLength) +// .max(arrayLength) +// .transform(function (value: number[], originalValue: string) { +// if (typeof originalValue === 'string') { +// return originalValue.split(',').map(Number); +// } +// return value; +// }); +// initialValues[param.name] = Array(arrayLength).fill( +// CONTRACT_ADDRESS_PARAM_NAMES.includes(param.name) ? defaultAddress : 200, +// ); +// break; +// case 'boolean': +// parameterSchema[param.name] = Yup.boolean().required(); +// initialValues[param.name] = false; +// break; +// } +// } +// return { validationSchema: Yup.object().shape(parameterSchema), initialValues }; +// } -async function handleFunctionCall( - contractAddress: AztecAddress | undefined, - contractArtifact: ContractArtifact, - functionName: string, - args: any, - wallet: CompleteAddress, -) { - const functionAbi = contractArtifact.functions.find(f => f.name === functionName)!; - const typedArgs: any[] = convertArgs(functionAbi, args); +// async function handleFunctionCall( +// contractAddress: AztecAddress | undefined, +// contractArtifact: ContractArtifact, +// functionName: string, +// args: any, +// wallet: CompleteAddress, +// ) { +// const functionAbi = contractArtifact.functions.find(f => f.name === functionName)!; +// const typedArgs: any[] = convertArgs(functionAbi, args); - if (functionName === 'constructor' && !!wallet) { - if (functionAbi === undefined) { - throw new Error('Cannot find constructor in the ABI.'); - } - // hack: addresses are stored as string in the form to avoid bigint compatibility issues with formik - // convert those back to bigints before sending +// if (functionName === 'constructor' && !!wallet) { +// if (functionAbi === undefined) { +// throw new Error('Cannot find constructor in the ABI.'); +// } +// // hack: addresses are stored as string in the form to avoid bigint compatibility issues with formik +// // convert those back to bigints before sending - // for now, dont let user change the salt. requires some change to the form generation if we want to let user choose one - // since everything is currently based on parsing the contractABI, and the salt parameter is not present there - const salt = Fr.random(); - return await deployContract(wallet, contractArtifact, typedArgs, salt, pxe.getPxe()); - } +// // for now, dont let user change the salt. requires some change to the form generation if we want to let user choose one +// // since everything is currently based on parsing the contractABI, and the salt parameter is not present there +// const salt = Fr.random(); +// return await deployContract(wallet, contractArtifact, typedArgs, salt, pxe.getPxe()); +// } - if (functionAbi.functionType === 'unconstrained') { - return await viewContractFunction( - contractAddress!, - contractArtifact, - functionName, - typedArgs, - pxe.getPxe(), - wallet, - ); - } else { - const txnReceipt = await callContractFunction( - contractAddress!, - contractArtifact, - functionName, - typedArgs, - pxe.getPxe(), - wallet, - ); - return `Transaction ${txnReceipt.status} on block number ${txnReceipt.blockNumber}`; - } -} +// if (functionAbi.functionType === 'unconstrained') { +// return await viewContractFunction( +// contractAddress!, +// contractArtifact, +// functionName, +// typedArgs, +// pxe.getPxe(), +// wallet, +// ); +// } else { +// const txnReceipt = await callContractFunction( +// contractAddress!, +// contractArtifact, +// functionName, +// typedArgs, +// pxe.getPxe(), +// wallet, +// ); +// return `Transaction ${txnReceipt.status} on block number ${txnReceipt.blockNumber}`; +// } +// } -interface ContractFunctionFormProps { - wallet: CompleteAddress; - contractAddress?: AztecAddress; - contractArtifact: ContractArtifact; - functionArtifact: FunctionArtifact; - defaultAddress: string; - title?: string; - buttonText?: string; - isLoading: boolean; - disabled: boolean; - onSubmit: () => void; - onSuccess: (result: any) => void; - onError: (msg: string) => void; -} +// interface ContractFunctionFormProps { +// wallet: CompleteAddress; +// contractAddress?: AztecAddress; +// contractArtifact: ContractArtifact; +// functionArtifact: FunctionArtifact; +// defaultAddress: string; +// title?: string; +// buttonText?: string; +// isLoading: boolean; +// disabled: boolean; +// onSubmit: () => void; +// onSuccess: (result: any) => void; +// onError: (msg: string) => void; +// } -export function ContractFunctionForm({ - wallet, - contractAddress, - contractArtifact, - functionArtifact, - defaultAddress, - buttonText = 'Submit', - isLoading, - disabled, - onSubmit, - onSuccess, - onError, -}: ContractFunctionFormProps) { - const { validationSchema, initialValues } = generateYupSchema(functionArtifact, defaultAddress); - const formik = useFormik({ - initialValues: initialValues, - validationSchema: validationSchema, - onSubmit: async (values: any) => { - onSubmit(); - try { - const result = await handleFunctionCall( - contractAddress, - contractArtifact, - functionArtifact.name, - values, - wallet, - ); - onSuccess(result); - } catch (e: any) { - onError(e.message); - } - }, - }); +// export function ContractFunctionForm({ +// wallet, +// contractAddress, +// contractArtifact, +// functionArtifact, +// defaultAddress, +// buttonText = 'Submit', +// isLoading, +// disabled, +// onSubmit, +// onSuccess, +// onError, +// }: ContractFunctionFormProps) { +// const { validationSchema, initialValues } = generateYupSchema(functionArtifact, defaultAddress); +// const formik = useFormik({ +// initialValues: initialValues, +// validationSchema: validationSchema, +// onSubmit: async (values: any) => { +// onSubmit(); +// try { +// const result = await handleFunctionCall( +// contractAddress, +// contractArtifact, +// functionArtifact.name, +// values, +// wallet, +// ); +// onSuccess(result); +// } catch (e: any) { +// onError(e.message); +// } +// }, +// }); - return ( -
- {functionArtifact.parameters.map(input => ( -
- - - {formik.touched[input.name] && formik.errors[input.name] && ( -
{formik.errors[input.name]?.toString()}
- )} -
- ))} - {isLoading ? ( - - ) : ( - +// )} +// +// ); +// } diff --git a/boxes/blank-react/src/app/components/copy.module.scss b/boxes/blank-react/src/app/components/copy.module.scss deleted file mode 100644 index 26b2360c58a..00000000000 --- a/boxes/blank-react/src/app/components/copy.module.scss +++ /dev/null @@ -1,6 +0,0 @@ -.copy { - cursor: pointer; - width: 35px; - height: 25px; - padding: 2px 8px; -} diff --git a/boxes/blank-react/src/app/components/copy.tsx b/boxes/blank-react/src/app/components/copy.tsx deleted file mode 100644 index 04c9aa0ecd6..00000000000 --- a/boxes/blank-react/src/app/components/copy.tsx +++ /dev/null @@ -1,28 +0,0 @@ -import styles from './copy.module.scss'; -import { useState } from 'react'; - -export function Copy({ value }: { value: string }) { - const [copied, setCopied] = useState(false); - - return ( - { - navigator.clipboard - .writeText(value) - .then(() => { - setCopied(true); - setTimeout(() => { - setCopied(false); - }, 3e3); - }) - .catch(() => { - // eslint-disable-next-line no-console - console.error('Could not copy address'); - }); - }} - src={copied ? 'check.svg' : 'copy.svg'} - alt="Copy" - className={styles.copy} - /> - ); -} diff --git a/boxes/blank-react/src/app/components/dropdown.module.scss b/boxes/blank-react/src/app/components/dropdown.module.scss deleted file mode 100644 index 18bd40b356b..00000000000 --- a/boxes/blank-react/src/app/components/dropdown.module.scss +++ /dev/null @@ -1,68 +0,0 @@ -.dropdownWrapper { - position: absolute; - top: 60px; - right: 0px; - border-radius: 10px; - display: flex; - overflow: hidden; - flex-direction: column; - gap: 1px; - border: 1px solid #ebeaea; - background-color: #ebeaea; - z-index: 1; -} - -.dropdownOptionBackground { - background-color: white; -} - -.dropdownOption { - font-size: 14px; - padding: 10px 25px; - white-space: nowrap; - cursor: pointer; - font-weight: 600; - justify-content: space-between; - letter-spacing: 0.5px; - display: flex; -} - -.singleOption { - text-align: center; - align-items: center; - justify-content: center; -} - -.dropdownOption.disabled { - background-image: initial; - cursor: default; - background-color: #c4c4c4; -} - -.dropdownOptionBackground:hover { - background-color: #ebeaea; -} - -.dropdownOptionBackground.disabled:hover { - background-color: white; -} - -.sublabel { - text-align: right; -} - -.sublabels { - display: flex; - flex-direction: row; - font-weight: 450; -} - -.feeOption { - gap: 5px; - display: flex; - flex-direction: column; -} - -.label { - color: #2f1f49; -} diff --git a/boxes/blank-react/src/app/components/dropdown.tsx b/boxes/blank-react/src/app/components/dropdown.tsx deleted file mode 100644 index 6470d2c80f3..00000000000 --- a/boxes/blank-react/src/app/components/dropdown.tsx +++ /dev/null @@ -1,83 +0,0 @@ -import style from './dropdown.module.scss'; -import classnames from 'classnames'; -import { CSSProperties, useEffect, useRef } from 'react'; - -export enum DropdownType { - Simple = 'Simple', - Fees = 'Fees', -} - -function useOutsideAlerter(ref: any, cb: any) { - useEffect(() => { - /** - * Alert if clicked on outside of element - */ - function handleClickOutside(event: any) { - if (ref.current && !ref.current.contains(event.target)) { - cb('You clicked outside of me!'); - } - } - - // Bind the event listener - document.addEventListener('mousedown', handleClickOutside); - return () => { - // Unbind the event listener on clean up - document.removeEventListener('mousedown', handleClickOutside); - }; - }, [ref, cb]); -} - -interface DropdownProps { - options: DropdownOption[]; - isOpen?: boolean; - className?: string; - style?: CSSProperties; - onClose?: () => void; - onClick?: (option: DropdownOption) => void; -} - -export interface DropdownOption { - value: T; - label: string; - sublabel?: string; - image?: string; - disabled?: boolean; -} - -export function Dropdown(props: DropdownProps) { - const wrapperRef = useRef(null); - useOutsideAlerter(wrapperRef, () => props.onClose && props.onClose()); - - if (!props.isOpen) { - return null; - } - - const handleClick = (option: DropdownOption) => { - if (option.disabled) { - return; - } - if (props.onClick) { - props.onClick(option); - } - if (props.onClose) { - props.onClose(); - } - }; - - return ( -
- {props.options.map((option: DropdownOption) => ( -
handleClick(option)} - key={option.label} - > -
- {option.image && {option.label}} -
{option.label}
-
-
- ))} -
- ); -} diff --git a/boxes/blank-react/src/app/components/popup.module.scss b/boxes/blank-react/src/app/components/popup.module.scss deleted file mode 100644 index 0917ca93f41..00000000000 --- a/boxes/blank-react/src/app/components/popup.module.scss +++ /dev/null @@ -1,27 +0,0 @@ -.popup { - width: 66vw; - position: absolute; - display: flex; - flex-direction: column; - justify-content: center; - align-items: center; - background-color: white; - border-radius: 20px; - justify-content: space-around; - box-shadow: 0px 4px 10px rgba(0, 0, 0, 0.1); - border: 3px solid rgb(47, 31, 73); - align-items: center; - gap: 30px; - padding: 30px; - overflow: scroll; - min-width: 600px; - z-index: 100; -} - -.alert { - width: 20px; -} - -.content { - width: 100%; -} diff --git a/boxes/blank-react/src/app/components/popup.tsx b/boxes/blank-react/src/app/components/popup.tsx index bfbdc7d3f58..36be1e5852c 100644 --- a/boxes/blank-react/src/app/components/popup.tsx +++ b/boxes/blank-react/src/app/components/popup.tsx @@ -1,6 +1,3 @@ -import styles from './popup.module.scss'; -import { Button } from '@aztec/aztec-ui'; - interface Props { children: string; buttonText?: string; @@ -10,15 +7,9 @@ interface Props { export function Popup({ children, buttonText = 'Close', isWarning = false, onClose }: Props) { return ( -
+
{isWarning && ( -
{children}
-
); } diff --git a/boxes/blank-react/src/app/components/select.module.scss b/boxes/blank-react/src/app/components/select.module.scss deleted file mode 100644 index d639c52f47d..00000000000 --- a/boxes/blank-react/src/app/components/select.module.scss +++ /dev/null @@ -1,65 +0,0 @@ -.selectBox { - border-radius: 10px; - background-color: white; - position: relative; - font-family: Sohne; - font-size: 16px; - height: 45px; - cursor: pointer; - font-weight: 450; - - @media (max-width: 480px) { - margin: 0; - } -} - -.icon { - min-width: 12px; -} - -.border { - box-shadow: 0px 4px 14px rgba(0, 0, 0, 0.1); -} - -.innerFrame { - height: 45px; - padding: 0 15px; - display: flex; - align-items: center; - justify-content: space-between; - overflow: hidden; - gap: 10px; -} - -.innerFrameWithButton { - padding-right: 0px; -} - -.value { - white-space: nowrap; -} - -.dropdown { - top: 45px; - left: 0%; - min-width: 130px; - width: 100%; -} - -.closeButton { - height: 30px; - padding: 15px 15px 15px 5px; - display: flex; - justify-content: flex-end; - align-items: center; -} - -.disabled { - background-color: rgba(239, 239, 239, 0.3); - color: #4a4a4a; - cursor: not-allowed; -} - -.placeholder { - color: #757575; -} diff --git a/boxes/blank-react/src/app/components/select.tsx b/boxes/blank-react/src/app/components/select.tsx deleted file mode 100644 index 562f5e12fdf..00000000000 --- a/boxes/blank-react/src/app/components/select.tsx +++ /dev/null @@ -1,81 +0,0 @@ -import { DropdownOption, DropdownType, Dropdown } from './dropdown.js'; -import style from './select.module.scss'; -import classnames from 'classnames'; -import { useState, useEffect, useRef } from 'react'; - -interface SelectProps { - options: DropdownOption[]; - dropdownType?: DropdownType; - showBorder?: boolean; - allowEmptyValue?: boolean; - disabled?: boolean; - placeholder?: string; - className?: string; - value?: T; - onChange?: (value?: T) => void; -} - -function useOutsideAlerter(ref: any, setIsOpen: any) { - useEffect(() => { - function handleClickOutside(event: { target: any }) { - if (ref.current && !ref.current.contains(event.target)) { - setIsOpen(false); - } - } - document.addEventListener('mousedown', handleClickOutside); - return () => { - document.removeEventListener('mousedown', handleClickOutside); - }; - }, [ref, setIsOpen]); -} - -export function Select(props: SelectProps) { - const { showBorder = true } = props; - const [isOpen, setIsOpen] = useState(false); - const wrapperRef = useRef(null); - - useOutsideAlerter(wrapperRef, setIsOpen); - - useEffect(() => { - setIsOpen(false); - }, [props.value]); - - const handleTriggerDropdown = () => { - if (props.disabled) return; - setIsOpen(prevValue => !prevValue); - }; - - const handleOptionSelect = (option: DropdownOption) => { - handleChange(option.value); - setIsOpen(false); - }; - - const handleChange = (value?: T) => { - if (props.onChange) { - props.onChange(value); - } - }; - - const hasButton = props.value && props.allowEmptyValue; - const activeLabel = props.options.find(x => x.value === props.value)?.label; - - return ( -
-
- - {activeLabel || props.placeholder} - - -
-
- ); -} diff --git a/boxes/blank-react/src/app/components/terms.tsx b/boxes/blank-react/src/app/components/terms.tsx deleted file mode 100644 index 70d989c2c15..00000000000 --- a/boxes/blank-react/src/app/components/terms.tsx +++ /dev/null @@ -1,10 +0,0 @@ -export function Terms() { - return ( -
- Please note that any example token contract, user interface, or demonstration set out herein is provided solely - for informational/academic purposes only and does not constitute any inducement to use, deploy and/or any - confirmation of any Aztec token. Any implementation of any such contract with an interface or any other - infrastructure should be used in accordance with applicable laws and regulations. -
- ); -} diff --git a/boxes/blank-react/src/app/components/wallet_dropdown.module.scss b/boxes/blank-react/src/app/components/wallet_dropdown.module.scss deleted file mode 100644 index 27fdfc4f7f7..00000000000 --- a/boxes/blank-react/src/app/components/wallet_dropdown.module.scss +++ /dev/null @@ -1,9 +0,0 @@ -.walletSelector { - position: fixed; - top: 30px; - right: 30px; - display: flex; - gap: 10px; - flex-direction: row; - align-items: center; -} diff --git a/boxes/blank-react/src/app/components/wallet_dropdown.tsx b/boxes/blank-react/src/app/components/wallet_dropdown.tsx deleted file mode 100644 index 456bac8c45b..00000000000 --- a/boxes/blank-react/src/app/components/wallet_dropdown.tsx +++ /dev/null @@ -1,60 +0,0 @@ -import { pxe } from '../../config.js'; -import { Copy } from './copy.js'; -import { Select } from './select.js'; -import styles from './wallet_dropdown.module.scss'; -import { Loader } from '@aztec/aztec-ui'; -import { CompleteAddress } from '@aztec/aztec.js'; -import { useEffect, useState } from 'react'; - -interface Props { - selected: CompleteAddress | undefined; - onSelectChange: (value: CompleteAddress) => void; - onError: (msg: string) => void; -} - -export function WalletDropdown({ selected, onSelectChange, onError }: Props) { - const [wallets, setOptions] = useState(); - - useEffect(() => { - if (wallets) { - return; - } - const loadOptions = async () => { - const fetchedOptions = await pxe.getPxe().getRegisteredAccounts(); - setOptions(fetchedOptions); - onSelectChange(fetchedOptions[0]); - }; - loadOptions().catch(e => { - setOptions([]); - onError(e.message); - }); - }); - - const addresses = wallets - ? wallets.map(({ address }: CompleteAddress) => { - return { label: address.toShortString(), value: address.toString() }; - }) - : null; - - return ( -
- {addresses ? ( - <> - + {contractArtifact.functions.map((fn, index) => { + console.log(fn.functionType); + return ( + fn.functionType === 'unconstrained' && ( + + ) + ); + })} + +
+ ); +} + +// export function Contract({ wallet }: Props) { +// const [contractAddress, setContractAddress] = useState(); +// const [processingFunction, setProcessingFunction] = useState(''); +// const [errorMsg, setError] = useState(''); +// const [selectedFunctionIndex, setSelectedFunctionIndex] = useState(-1); +// const [result, setResult] = useState(''); - function renderCardContent(contractAddress?: AztecAddress): { content: ReactNode; header: string } { - if (contractAddress) { - const functions = contractArtifact.functions - .filter(f => f.name !== 'constructor' && !f.isInternal) - .sort((a, b) => functionTypeSortOrder[a.functionType] - functionTypeSortOrder[b.functionType]); +// const handleSubmitForm = (functionName: string) => setProcessingFunction(functionName); +// const handleContractDeployed = (address: AztecAddress) => { +// setContractAddress(address); +// setResult(`Contract deployed at: ${address}`); +// }; +// const handleResult = (returnValues: any) => { +// // TODO: serialize returnValues to string according to the returnTypes defined in the function abi. +// setResult(`Return values: ${returnValues}`); +// }; +// const handleClosePopup = () => { +// setResult(''); +// setError(''); +// setProcessingFunction(''); +// }; - if (selectedFunctionIndex === -1) { - return { - header: 'Available Functions', - content: ( -
-
-
{`${contractArtifact.name}`}
- {!!contractAddress && ( -
- {`${contractAddress.toShortString()}`} - -
- )} -
-
- {functions.map((functionAbi: FunctionArtifact, index: number) => ( - { - setSelectedFunctionIndex(index); - }} - /> - ))} -
-
- ), - }; - } +// const constructorAbi = contractArtifact.functions.find(f => f.name === 'constructor')!; +// const hasResult = !!(result || errorMsg); - const selectedFunctionAbi = functions[selectedFunctionIndex]; +// function renderCardContent(contractAddress?: AztecAddress): ReactNode { +// if (contractAddress) { +// const functions = contractArtifact.functions +// .filter(f => f.name !== 'constructor' && !f.isInternal) +// .sort((a, b) => functionTypeSortOrder[a.functionType] - functionTypeSortOrder[b.functionType]); - return { - header: selectedFunctionAbi.name, - content: ( - <> - +// ))} +//
+// +// ); +// } - return { - header: `Deploy Contract (${contractArtifact.name})`, - content: ( - handleSubmitForm('constructor')} - onSuccess={handleContractDeployed} - onError={setError} - /> - ), - }; - } +// const selectedFunctionAbi = functions[selectedFunctionIndex]; - const { header, content } = renderCardContent(contractAddress); +// return ( +// <> +// handleSubmitForm(selectedFunctionAbi.name)} +// onSuccess={handleResult} +// onError={setError} +// /> +// +// ); +// } - return ( - <> - -
setTermsOpen(true)}> - Terms of Service -
- {!!(errorMsg || result) && ( - - {errorMsg || result} - - )} - {isTermsOpen && ( - setTermsOpen(false)}> - Please note that any example token contract, user interface, or demonstration set out herein is provided - solely for informational/academic purposes only and does not constitute any inducement to use, deploy and/or - any confirmation of any Aztec token. Any implementation of any such contract with an interface or any other - infrastructure should be used in accordance with applicable laws and regulations. - - )} - - ); -} +// return ( +// handleSubmitForm('constructor')} +// onSuccess={handleContractDeployed} +// onError={setError} +// /> +// ); +// } + +// return ( +// <> +// {renderCardContent(contractAddress)} +// {!!(errorMsg || result) && ( +// +// {errorMsg || result} +// +// )} +// +// ); +// } diff --git a/boxes/blank-react/src/app/home.module.scss b/boxes/blank-react/src/app/home.module.scss deleted file mode 100644 index 44ff7f76446..00000000000 --- a/boxes/blank-react/src/app/home.module.scss +++ /dev/null @@ -1,28 +0,0 @@ -.main { - display: flex; - gap: 20px; - flex-direction: column; - justify-content: center; - align-items: center; -} - -.logo { - position: fixed; - cursor: pointer; - top: 35px; - left: 30px; - height: 35px; -} - -.privateBackground { - transform: rotate(-20deg); - width: 150vw; - height: 150vh; - font-weight: 500; - opacity: 0.3; - font-size: 14px; - pointer-events: none; - position: absolute; - font-size: 14px; - z-index: 0; -} diff --git a/boxes/blank-react/src/app/home.tsx b/boxes/blank-react/src/app/home.tsx index d0e1cd17674..56944311f20 100644 --- a/boxes/blank-react/src/app/home.tsx +++ b/boxes/blank-react/src/app/home.tsx @@ -1,50 +1,37 @@ import { pxe } from '../config.js'; -import { WalletDropdown } from './components/wallet_dropdown.js'; import { Contract } from './contract.js'; -import styles from './home.module.scss'; -import { Loader } from '@aztec/aztec-ui'; -import { CompleteAddress } from '@aztec/aztec.js'; import { useEffect, useRef, useState } from 'react'; +import { useAccount } from './hooks/useAccounts.js'; +import { convertArgs } from '../scripts/util.js'; +import { AztecAddress, CompleteAddress, ContractArtifact, DeployMethod, Fr, PXE } from '@aztec/aztec.js'; +import { useContract } from './hooks/useContract.js'; export function Home() { - const [isLoadingWallet, setIsLoadingWallet] = useState(true); - const [selectedWallet, setSelectedWallet] = useState(); - const [selectWalletError, setSelectedWalletError] = useState(''); + const [deploymentAccount, setDeploymentAccount] = useState(); + const accounts = useAccount(); + const { deploy, contract } = useContract({ deployer: deploymentAccount }); - const handleSelectWallet = (address: CompleteAddress | undefined) => { - setSelectedWallet(address); - setIsLoadingWallet(false); + const selectWallet = ({ currentTarget }: React.FormEvent) => { + if (!accounts) return; + const index = parseInt(currentTarget.value); + setDeploymentAccount(accounts[index]); }; - const handleSelectWalletError = (msg: string) => { - setSelectedWalletError(msg); - setIsLoadingWallet(false); - }; + if (!contract) { + return ( +
+ + + +
+ ); + } - return ( -
- Aztec - <> - {isLoadingWallet && } - {!isLoadingWallet && ( - <> - {!!selectWalletError && ( - <> - {`Failed to load accounts. Error: ${selectWalletError}`} -
- {`Make sure PXE from Aztec Sandbox is running at: ${pxe.getPxeUrl()}`} - - )} - {!selectWalletError && !selectedWallet && `No accounts.`} - {!selectWalletError && !!selectedWallet && } - - )} - - -
- ); + return ; } diff --git a/boxes/blank-react/src/app/hooks/useAccounts.tsx b/boxes/blank-react/src/app/hooks/useAccounts.tsx new file mode 100644 index 00000000000..4dd99b395f3 --- /dev/null +++ b/boxes/blank-react/src/app/hooks/useAccounts.tsx @@ -0,0 +1,20 @@ +import { pxe } from '../../config.js'; +import { CompleteAddress } from '@aztec/aztec.js'; +import { useEffect, useState } from 'react'; + +export function useAccount() { + const [accounts, setAccounts] = useState(); + + const getAccounts = async () => { + const acc = await pxe.getPxe().getRegisteredAccounts(); + setAccounts(acc || []); + }; + + useEffect(() => { + if (!accounts) { + getAccounts(); + } + }, []); + + return accounts; +} diff --git a/boxes/blank-react/src/app/hooks/useContract.tsx b/boxes/blank-react/src/app/hooks/useContract.tsx new file mode 100644 index 00000000000..42fb47676d1 --- /dev/null +++ b/boxes/blank-react/src/app/hooks/useContract.tsx @@ -0,0 +1,33 @@ +import { useState } from 'react'; +import { contractArtifact, pxe } from '../../config.js'; + +import { convertArgs } from '../../scripts/util.js'; +import { AztecAddress, CompleteAddress, Contract, DeployMethod, Fr } from '@aztec/aztec.js'; + +export function useContract({ deployer }: { deployer: CompleteAddress | undefined }) { + const [contract, setContract] = useState(); + + const deploy = async (e: React.FormEvent) => { + e.preventDefault(); + if (!deployer) return; + + const salt = Fr.random(); + const functionAbi = contractArtifact.functions.find(f => f.name === 'constructor')!; + const typedArgs: any[] = convertArgs(functionAbi, []); + + const tx = new DeployMethod( + deployer.publicKey, + pxe.getPxe(), + contractArtifact, + (a, w) => Contract.at(a, contractArtifact, w), + typedArgs, + ).send({ + contractAddressSalt: salt, + }); + + const receipt = await tx.wait(); + setContract(receipt.contractAddress); + }; + + return { deploy, contract }; +} diff --git a/boxes/blank-react/src/app/index.css b/boxes/blank-react/src/app/index.css index 9bc5f2e9e7f..bcc32ed8c27 100644 --- a/boxes/blank-react/src/app/index.css +++ b/boxes/blank-react/src/app/index.css @@ -1,47 +1,4 @@ -@font-face { - font-family: Sohne; - font-style: normal; - font-weight: 400; - src: url(../assets/soehne-web-leicht.woff2); -} - -@font-face { - font-family: Sohne; - font-style: normal; - font-weight: 450; - src: url(../assets/soehne-web-buch.woff2); -} - -@font-face { - font-family: Sohne; - font-style: normal; - font-weight: 500; - src: url(../assets/soehne-web-kraftig.woff2); -} - -@font-face { - font-family: Sohne; - font-style: normal; - font-weight: 600; - src: url(../assets/soehne-web-halbfett.woff2); -} - -@font-face { - font-family: Sohne; - font-style: italic; - font-weight: 400; - src: url(../assets/soehne-leicht-kursiv.ttf); -} - -@font-face { - font-family: Sohne; - font-style: italic; - font-weight: 500; - src: url(../assets/soehne-web-halbfett-kursiv.woff2); -} - :root { - font-family: Sohne, Helvetica, Arial, sans-serif; line-height: 1.5; font-weight: 400; @@ -49,10 +6,7 @@ color: rgba(255, 255, 255, 0.87); background-color: #242424; - font-synthesis: none; text-rendering: optimizeLegibility; - -webkit-font-smoothing: antialiased; - -moz-osx-font-smoothing: grayscale; -webkit-text-size-adjust: 100%; overflow: hidden; diff --git a/boxes/blank-react/src/contracts/Nargo.toml b/boxes/blank-react/src/contracts/Nargo.toml index 66986b634b5..007d75a293b 100644 --- a/boxes/blank-react/src/contracts/Nargo.toml +++ b/boxes/blank-react/src/contracts/Nargo.toml @@ -6,3 +6,6 @@ type = "contract" [dependencies] aztec = { path = "../../../../yarn-project/aztec-nr/aztec" } +easy_private_state = { path = "../../../../yarn-project/aztec-nr/easy-private-state"} + +value_note = { path = "../../../../yarn-project/aztec-nr/value-note" } diff --git a/boxes/blank-react/src/contracts/log/.b7ec86e927411b6b375064925b8088b0cd141771-audit.json b/boxes/blank-react/src/contracts/log/.b7ec86e927411b6b375064925b8088b0cd141771-audit.json new file mode 100644 index 00000000000..fa7254f7300 --- /dev/null +++ b/boxes/blank-react/src/contracts/log/.b7ec86e927411b6b375064925b8088b0cd141771-audit.json @@ -0,0 +1,15 @@ +{ + "keep": { + "days": false, + "amount": 5 + }, + "auditLog": "log/.b7ec86e927411b6b375064925b8088b0cd141771-audit.json", + "files": [ + { + "date": 1706781698517, + "name": "log/aztec-sandbox-2024-02-01.debug.log", + "hash": "be6605552baab8ccac80b85f9b173dfbf2652f4b6ae69d90dcfa0b09ddf4594c" + } + ], + "hashType": "sha256" +} \ No newline at end of file diff --git a/boxes/blank-react/src/contracts/log/aztec-sandbox-2024-02-01.debug.log b/boxes/blank-react/src/contracts/log/aztec-sandbox-2024-02-01.debug.log new file mode 100644 index 00000000000..028e25bee4f --- /dev/null +++ b/boxes/blank-react/src/contracts/log/aztec-sandbox-2024-02-01.debug.log @@ -0,0 +1,5 @@ +{"level":"info","message":"Debug logs will be written to /Users/zpedro/Documents/GitHub/aztec-packages/boxes/blank-react/src/contracts/log/aztec-sandbox.debug.log","namespace":"aztec:sandbox","timestamp":"2024-02-01T10:01:38.521Z"} +{"level":"info","message":"Setting up Aztec Sandbox v0.1.0 please stand by...","namespace":"aztec:sandbox","timestamp":"2024-02-01T10:01:38.523Z"} +{"level":"warn","message":"Failed to connect to Ethereum node at http://127.0.0.1:8545/. Retrying...","namespace":"aztec:sandbox","timestamp":"2024-02-01T10:01:39.704Z"} +{"level":"warn","message":"Failed to connect to Ethereum node at http://127.0.0.1:8545/. Retrying...","namespace":"aztec:sandbox","timestamp":"2024-02-01T10:01:41.801Z"} +{"level":"info","message":"Shutting down...","namespace":"aztec:sandbox","timestamp":"2024-02-01T10:01:42.205Z"} diff --git a/boxes/blank-react/src/contracts/log/aztec-sandbox.debug.log b/boxes/blank-react/src/contracts/log/aztec-sandbox.debug.log new file mode 120000 index 00000000000..fd04a7caf58 --- /dev/null +++ b/boxes/blank-react/src/contracts/log/aztec-sandbox.debug.log @@ -0,0 +1 @@ +aztec-sandbox-2024-02-01.debug.log \ No newline at end of file diff --git a/boxes/blank-react/src/contracts/src/main.nr b/boxes/blank-react/src/contracts/src/main.nr index 84750a2da70..4ff9f9739ea 100644 --- a/boxes/blank-react/src/contracts/src/main.nr +++ b/boxes/blank-react/src/contracts/src/main.nr @@ -4,16 +4,60 @@ contract Blank { oracle::{ get_public_key::get_public_key, }, - protocol_types::address::AztecAddress + protocol_types::address::AztecAddress, + state_vars::singleton::Singleton, + context::{PrivateContext, PublicContext, Context}, + note::{ + utils as note_utils, + note_interface::NoteInterface, + note_header::NoteHeader, + }, }; + + use dep::value_note::value_note::{ValueNote, ValueNoteMethods, VALUE_NOTE_LEN}; + + use dep::easy_private_state::easy_private_state::EasyPrivateUint; + + struct Storage { + number: Singleton, + } + + impl Storage { + fn init(context: Context) -> pub Self { + Storage { + number: Singleton::new( + context, + 1, + ValueNoteMethods + ), + } + } + } #[aztec(private)] - fn constructor() {} + fn constructor(number: Field) { + let mut new_number = ValueNote::new(number, context.msg_sender()); + storage.number.initialize(&mut new_number, Option::none(), true); + } #[aztec(private)] - fn getPublicKey(address: AztecAddress) -> [Field; 2] { - let pub_key = get_public_key(address); + fn setNumber(number: Field) { + let mut new_number = ValueNote::new(number, context.msg_sender()); + let new_number = storage.number.replace(&mut new_number, true); + } + + #[aztec(public)] + unconstrained fn getNumber(owner: AztecAddress) -> Note { + storage.number.get_note(true) + } - [pub_key.x, pub_key.y] + unconstrained fn compute_note_hash_and_nullifier( + contract_address: AztecAddress, + nonce: Field, + storage_slot: Field, + serialized_note: [Field; VALUE_NOTE_LEN] + ) -> pub [Field; 4] { + let note_header = NoteHeader::new(contract_address, nonce, storage_slot); + note_utils::compute_note_hash_and_nullifier(ValueNoteMethods, note_header, serialized_note) } } diff --git a/boxes/blank-react/src/scripts/deploy_contract.ts b/boxes/blank-react/src/scripts/deploy_contract.ts deleted file mode 100644 index 909a3587532..00000000000 --- a/boxes/blank-react/src/scripts/deploy_contract.ts +++ /dev/null @@ -1,26 +0,0 @@ -import { AztecAddress, CompleteAddress, Contract, ContractArtifact, DeployMethod, Fr, PXE } from '@aztec/aztec.js'; - -export async function deployContract( - activeWallet: CompleteAddress, - contractArtifact: ContractArtifact, - typedArgs: Fr[], // encode prior to passing in - salt: Fr, - pxe: PXE, -): Promise { - const tx = new DeployMethod( - activeWallet.publicKey, - pxe, - contractArtifact, - (a, w) => Contract.at(a, contractArtifact, w), - typedArgs, - ).send({ - contractAddressSalt: salt, - }); - await tx.wait(); - const receipt = await tx.getReceipt(); - if (receipt.contractAddress) { - return receipt.contractAddress; - } else { - throw new Error(`Contract not deployed (${receipt.toJSON()})`); - } -} diff --git a/boxes/blank-react/tests/blank.contract.test.ts b/boxes/blank-react/tests/blank.contract.test.ts index 09f62128f39..ce5f6be158f 100644 --- a/boxes/blank-react/tests/blank.contract.test.ts +++ b/boxes/blank-react/tests/blank.contract.test.ts @@ -1,26 +1,41 @@ import { BlankContract } from '../artifacts/Blank.js'; -import { callContractFunction, deployContract, getWallet } from '../src/scripts/index.js'; +import { callContractFunction, getWallet } from '../src/scripts/index.js'; import { AccountWallet, AztecAddress, CompleteAddress, Contract, + DeployMethod, Fr, - PXE, TxStatus, Wallet, createDebugLogger, } from '@aztec/aztec.js'; import { pxe } from '../src/config.js'; +import { convertArgs } from '../src/scripts/util.js'; const logger = createDebugLogger('aztec:http-pxe-client'); async function deployZKContract(owner: CompleteAddress, wallet: Wallet, pxe: PXE) { logger('Deploying Blank contract...'); - const contractAddress = await deployContract(owner, BlankContract.artifact, [], Fr.random(), pxe); + const { artifact } = BlankContract; + const salt = Fr.random(); + const functionAbi = artifact.functions.find(f => f.name === 'constructor')!; + const typedArgs: any[] = convertArgs(functionAbi, []); + const tx = new DeployMethod( + owner.publicKey, + pxe.getPxe(), + artifact, + (a, w) => Contract.at(a, artifact, w), + typedArgs, + ).send({ + contractAddressSalt: salt, + }); + + const { contractAddress } = await tx.wait(); logger(`L2 contract deployed at ${contractAddress}`); - return BlankContract.at(contractAddress, wallet); + return BlankContract.at(contractAddress!, wallet); } describe('ZK Contract Tests', () => { diff --git a/boxes/blank-react/tsconfig.json b/boxes/blank-react/tsconfig.json index 3b4c57677db..c5f7d9ab2ad 100644 --- a/boxes/blank-react/tsconfig.json +++ b/boxes/blank-react/tsconfig.json @@ -20,5 +20,5 @@ "skipLibCheck": true, "jsx": "react-jsx" }, - "include": ["src", "tests", "src/contracts/target/*.json", "artifacts/*"] + "include": ["src/**/*.ts", "tests", "src/contracts/target/*.json", "artifacts/**/*.ts"] } diff --git a/boxes/blank/webpack.config.js b/boxes/blank/webpack.config.js index abbbfa719b5..606e154a4f8 100644 --- a/boxes/blank/webpack.config.js +++ b/boxes/blank/webpack.config.js @@ -56,6 +56,7 @@ export default (_, argv) => ({ crypto: false, fs: false, path: false, + events: require.resolve('events/'), stream: require.resolve('stream-browserify'), tty: require.resolve('tty-browserify'), util: require.resolve('util/'), diff --git a/boxes/docker-compose.yml b/boxes/docker-compose.yml index f916d56c810..e731d25c01e 100644 --- a/boxes/docker-compose.yml +++ b/boxes/docker-compose.yml @@ -5,7 +5,7 @@ services: command: "'anvil --silent -p 8545 --host 0.0.0.0 --chain-id 31337'" aztec: - image: aztecprotocol/aztec-sandbox + image: aztecprotocol/aztec environment: ETHEREUM_HOST: http://ethereum:8545 CHAIN_ID: 31337 diff --git a/boxes/token/src/contracts/src/main.nr b/boxes/token/src/contracts/src/main.nr index da7b7cb74ca..5b85b2e4d74 100644 --- a/boxes/token/src/contracts/src/main.nr +++ b/boxes/token/src/contracts/src/main.nr @@ -24,17 +24,12 @@ contract Token { context::{PrivateContext, PublicContext, Context}, hash::{compute_secret_hash}, state_vars::{map::Map, public_state::PublicState, set::Set}, - types::type_serialization::{ - field_serialization::{FieldSerializationMethods, FIELD_SERIALIZED_LEN}, - bool_serialization::{BoolSerializationMethods, BOOL_SERIALIZED_LEN}, - address_serialization::{AddressSerializationMethods, AZTEC_ADDRESS_SERIALIZED_LEN}, - }, protocol_types::{ abis::function_selector::FunctionSelector, address::AztecAddress, } }; - + // docs:start:import_authwit use dep::authwit::{ auth::{ @@ -45,29 +40,28 @@ contract Token { // docs:end:import_authwit use crate::types::{ - transparent_note::{TransparentNote, TransparentNoteMethods, TRANSPARENT_NOTE_LEN}, - token_note::{TokenNote, TokenNoteMethods, TOKEN_NOTE_LEN}, + transparent_note::TransparentNote, + token_note::{TokenNote, TOKEN_NOTE_LEN}, balances_map::{BalancesMap}, - safe_u120_serialization::{SafeU120SerializationMethods, SAFE_U120_SERIALIZED_LEN} }; // docs:end::imports // docs:start:storage_struct struct Storage { // docs:start:storage_admin - admin: PublicState, + admin: PublicState, // docs:end:storage_admin // docs:start:storage_minters - minters: Map>, + minters: Map>, // docs:end:storage_minters // docs:start:storage_balances balances: BalancesMap, // docs:end:storage_balances - total_supply: PublicState, + total_supply: PublicState, // docs:start:storage_pending_shields - pending_shields: Set, + pending_shields: Set, // docs:end:storage_pending_shields - public_balances: Map>, + public_balances: Map>, } // docs:end:storage_struct @@ -79,7 +73,6 @@ contract Token { admin: PublicState::new( context, 1, - AddressSerializationMethods, ), // docs:end:storage_admin_init // docs:start:storage_minters_init @@ -90,7 +83,6 @@ contract Token { PublicState::new( context, slot, - BoolSerializationMethods, ) }, ), @@ -101,10 +93,9 @@ contract Token { total_supply: PublicState::new( context, 4, - SafeU120SerializationMethods, ), // docs:start:storage_pending_shields_init - pending_shields: Set::new(context, 5, TransparentNoteMethods), + pending_shields: Set::new(context, 5), // docs:end:storage_pending_shields_init public_balances: Map::new( context, @@ -113,7 +104,6 @@ contract Token { PublicState::new( context, slot, - SafeU120SerializationMethods, ) }, ), @@ -246,9 +236,9 @@ contract Token { fn redeem_shield(to: AztecAddress, amount: Field, secret: Field) { let pending_shields = storage.pending_shields; let secret_hash = compute_secret_hash(secret); - // Get 1 note (set_limit(1)) which has amount stored in field with index 0 (select(0, amount)) and secret_hash - // stored in field with index 1 (select(1, secret_hash)). - let options = NoteGetterOptions::new().select(0, amount).select(1, secret_hash).set_limit(1); + // Get 1 note (set_limit(1)) which has amount stored in field with index 0 (select(0, amount, Option::none())) and secret_hash + // stored in field with index 1 (select(1, secret_hash, Option::none())). + let options = NoteGetterOptions::new().select(0, amount, Option::none()).select(1, secret_hash, Option::none()).set_limit(1); let notes = pending_shields.get_notes(options); let note = notes[0].unwrap_unchecked(); // Remove the note from the pending shields set @@ -385,9 +375,9 @@ contract Token { ) -> pub [Field; 4] { let note_header = NoteHeader::new(contract_address, nonce, storage_slot); if (storage_slot == 5) { - note_utils::compute_note_hash_and_nullifier(TransparentNoteMethods, note_header, serialized_note) + note_utils::compute_note_hash_and_nullifier(TransparentNote::deserialize, note_header, serialized_note) } else { - note_utils::compute_note_hash_and_nullifier(TokenNoteMethods, note_header, serialized_note) + note_utils::compute_note_hash_and_nullifier(TokenNote::deserialize, note_header, serialized_note) } } // docs:end:compute_note_hash_and_nullifier diff --git a/boxes/token/src/contracts/src/types.nr b/boxes/token/src/contracts/src/types.nr index e29a8151e9f..d3b3b1c9e77 100644 --- a/boxes/token/src/contracts/src/types.nr +++ b/boxes/token/src/contracts/src/types.nr @@ -2,4 +2,3 @@ mod transparent_note; mod balance_set; mod balances_map; mod token_note; -mod safe_u120_serialization; diff --git a/boxes/token/src/contracts/src/types/balance_set.nr b/boxes/token/src/contracts/src/types/balance_set.nr index 231320de558..184fd5fe925 100644 --- a/boxes/token/src/contracts/src/types/balance_set.nr +++ b/boxes/token/src/contracts/src/types/balance_set.nr @@ -13,23 +13,19 @@ use dep::aztec::note::{ note_getter_options::{NoteGetterOptions, SortOrder}, note_viewer_options::NoteViewerOptions }; -use dep::aztec::note::{ - note_header::NoteHeader, - note_interface::NoteInterface, - utils::compute_note_hash_for_read_or_nullify, -}; -use crate::types::token_note::{TokenNote, TOKEN_NOTE_LEN, TokenNoteMethods}; + +use crate::types::token_note::TokenNote; // A set implementing standard manipulation of balances. // Does not require spending key, but only knowledge. // Spending key requirement should be enforced by the contract using this. struct BalanceSet { owner: AztecAddress, - set: Set + set: Set } impl BalanceSet { - pub fn new(set: Set, owner: AztecAddress) -> Self { + pub fn new(set: Set, owner: AztecAddress) -> Self { Self { owner, set, diff --git a/boxes/token/src/contracts/src/types/balances_map.nr b/boxes/token/src/contracts/src/types/balances_map.nr index 9d6f92c0ac0..e8cb09f62a3 100644 --- a/boxes/token/src/contracts/src/types/balances_map.nr +++ b/boxes/token/src/contracts/src/types/balances_map.nr @@ -4,11 +4,11 @@ use crate::types::balance_set::BalanceSet; use dep::aztec::hash::pedersen_hash; use dep::aztec::protocol_types::address::AztecAddress; -use crate::types::token_note::{TokenNote, TOKEN_NOTE_LEN, TokenNoteMethods}; +use crate::types::token_note::TokenNote; use dep::aztec::state_vars::{map::Map, set::Set}; struct BalancesMap { - store: Map>, + store: Map>, } impl BalancesMap { @@ -20,7 +20,6 @@ impl BalancesMap { Set { context, storage_slot, - note_interface: TokenNoteMethods, } }); Self { diff --git a/boxes/token/src/contracts/src/types/safe_u120_serialization.nr b/boxes/token/src/contracts/src/types/safe_u120_serialization.nr deleted file mode 100644 index 876007184fe..00000000000 --- a/boxes/token/src/contracts/src/types/safe_u120_serialization.nr +++ /dev/null @@ -1,18 +0,0 @@ -use dep::aztec::types::type_serialization::TypeSerializationInterface; -use dep::safe_math::SafeU120; - -global SAFE_U120_SERIALIZED_LEN: Field = 1; - -// This is safe when reading from storage IF only correct safeu120 was written to storage -fn deserializeU120(fields: [Field; SAFE_U120_SERIALIZED_LEN]) -> SafeU120 { - SafeU120 { value: fields[0] as u120 } -} - -fn serializeU120(value: SafeU120) -> [Field; SAFE_U120_SERIALIZED_LEN] { - [value.value as Field] -} - -global SafeU120SerializationMethods = TypeSerializationInterface { - deserialize: deserializeU120, - serialize: serializeU120, -}; diff --git a/boxes/token/src/contracts/src/types/token_note.nr b/boxes/token/src/contracts/src/types/token_note.nr index f8adf31959d..a0283d4a95a 100644 --- a/boxes/token/src/contracts/src/types/token_note.nr +++ b/boxes/token/src/contracts/src/types/token_note.nr @@ -8,12 +8,13 @@ use dep::aztec::{ note::{ note_header::NoteHeader, note_interface::NoteInterface, - utils::compute_note_hash_for_read_or_nullify, + utils::compute_note_hash_for_consumption, }, context::PrivateContext, state_vars::set::Set, log::emit_encrypted_log, hash::pedersen_hash, + protocol_types::traits::{Serialize, Deserialize}, }; use dep::aztec::oracle::{ rand::rand, @@ -39,21 +40,14 @@ struct TokenNote { header: NoteHeader, } -impl TokenNote { - pub fn new(amount: SafeU120, owner: AztecAddress) -> Self { - Self { - amount, - owner, - randomness: rand(), - header: NoteHeader::empty(), - } - } - - pub fn serialize(self) -> [Field; TOKEN_NOTE_LEN] { +impl Serialize for TokenNote { + fn serialize(self) -> [Field; TOKEN_NOTE_LEN] { [self.amount.value as Field, self.owner.to_field(), self.randomness] } +} - pub fn deserialize(serialized_note: [Field; TOKEN_NOTE_LEN]) -> Self { +impl Deserialize for TokenNote { + fn deserialize(serialized_note: [Field; TOKEN_NOTE_LEN]) -> Self { Self { amount: SafeU120::new(serialized_note[0]), owner: AztecAddress::from_field(serialized_note[1]), @@ -61,19 +55,17 @@ impl TokenNote { header: NoteHeader::empty(), } } +} - pub fn compute_note_hash(self) -> Field { +impl NoteInterface for TokenNote { + fn compute_note_content_hash(self) -> Field { // TODO(#1205) Should use a non-zero generator index. - pedersen_hash([ - self.amount.value as Field, - self.owner.to_field(), - self.randomness, - ],0) + pedersen_hash(self.serialize(), 0) } // docs:start:nullifier - pub fn compute_nullifier(self, context: &mut PrivateContext) -> Field { - let note_hash_for_nullify = compute_note_hash_for_read_or_nullify(TokenNoteMethods, self); + fn compute_nullifier(self, context: &mut PrivateContext) -> Field { + let note_hash_for_nullify = compute_note_hash_for_consumption(self); let secret = context.request_nullifier_secret_key(self.owner); // TODO(#1205) Should use a non-zero generator index. pedersen_hash([ @@ -84,8 +76,8 @@ impl TokenNote { } // docs:end:nullifier - pub fn compute_nullifier_without_context(self) -> Field { - let note_hash_for_nullify = compute_note_hash_for_read_or_nullify(TokenNoteMethods, self); + fn compute_nullifier_without_context(self) -> Field { + let note_hash_for_nullify = compute_note_hash_for_consumption(self); let secret = get_nullifier_secret_key(self.owner); // TODO(#1205) Should use a non-zero generator index. pedersen_hash([ @@ -95,12 +87,16 @@ impl TokenNote { ],0) } - pub fn set_header(&mut self, header: NoteHeader) { + fn set_header(&mut self, header: NoteHeader) { self.header = header; } + fn get_header(self) -> NoteHeader { + self.header + } + // Broadcasts the note as an encrypted log on L1. - pub fn broadcast(self, context: &mut PrivateContext, slot: Field) { + fn broadcast(self, context: &mut PrivateContext, slot: Field) { // We only bother inserting the note if non-empty to save funds on gas. if !self.amount.is_zero() { let encryption_pub_key = get_public_key(self.owner); @@ -115,46 +111,13 @@ impl TokenNote { } } -fn deserialize(serialized_note: [Field; TOKEN_NOTE_LEN]) -> TokenNote { - TokenNote::deserialize(serialized_note) -} - -fn serialize(note: TokenNote) -> [Field; TOKEN_NOTE_LEN] { - note.serialize() -} - -fn compute_note_hash(note: TokenNote) -> Field { - note.compute_note_hash() -} - -fn compute_nullifier(note: TokenNote, context: &mut PrivateContext) -> Field { - note.compute_nullifier(context) -} - -fn compute_nullifier_without_context(note: TokenNote) -> Field { - note.compute_nullifier_without_context() -} - -fn get_header(note: TokenNote) -> NoteHeader { - note.header -} - -fn set_header(note: &mut TokenNote, header: NoteHeader) { - note.set_header(header) -} - -// Broadcasts the note as an encrypted log on L1. -fn broadcast(context: &mut PrivateContext, slot: Field, note: TokenNote) { - note.broadcast(context, slot); +impl TokenNote { + pub fn new(amount: SafeU120, owner: AztecAddress) -> Self { + Self { + amount, + owner, + randomness: rand(), + header: NoteHeader::empty(), + } + } } - -global TokenNoteMethods = NoteInterface { - deserialize, - serialize, - compute_note_hash, - compute_nullifier, - compute_nullifier_without_context, - get_header, - set_header, - broadcast, -}; diff --git a/boxes/token/src/contracts/src/types/transparent_note.nr b/boxes/token/src/contracts/src/types/transparent_note.nr index deb2bcdf6f1..b8d6812330d 100644 --- a/boxes/token/src/contracts/src/types/transparent_note.nr +++ b/boxes/token/src/contracts/src/types/transparent_note.nr @@ -3,10 +3,11 @@ use dep::aztec::{ note::{ note_header::NoteHeader, note_interface::NoteInterface, - utils::compute_siloed_note_hash, + utils::compute_note_hash_for_consumption, }, hash::{compute_secret_hash, pedersen_hash}, context::PrivateContext, + protocol_types::traits::{Serialize, Deserialize} }; global TRANSPARENT_NOTE_LEN: Field = 2; @@ -22,38 +23,14 @@ struct TransparentNote { header: NoteHeader, } -impl TransparentNote { - - // CONSTRUCTORS - - pub fn new(amount: Field, secret_hash: Field) -> Self { - TransparentNote { - amount: amount, - secret_hash: secret_hash, - secret: 0, - header: NoteHeader::empty(), - } - } - - // new oracle call primitive - // get me the secret corresponding to this hash - pub fn new_from_secret(amount: Field, secret: Field) -> Self { - TransparentNote { - amount: amount, - secret_hash: compute_secret_hash(secret), - secret: secret, - header: NoteHeader::empty(), - } - } - - - // STANDARD NOTE_INTERFACE FUNCTIONS - - pub fn serialize(self) -> [Field; TRANSPARENT_NOTE_LEN] { +impl Serialize for TransparentNote { + fn serialize(self) -> [Field; TRANSPARENT_NOTE_LEN] { [self.amount, self.secret_hash] } +} - pub fn deserialize(serialized_note: [Field; TRANSPARENT_NOTE_LEN]) -> Self { +impl Deserialize for TransparentNote { + fn deserialize(serialized_note: [Field; TRANSPARENT_NOTE_LEN]) -> Self { TransparentNote { amount: serialized_note[0], secret_hash: serialized_note[1], @@ -61,79 +38,67 @@ impl TransparentNote { header: NoteHeader::empty(), } } +} + +impl NoteInterface for TransparentNote { - pub fn compute_note_hash(self) -> Field { + fn compute_note_content_hash(self) -> Field { // TODO(#1205) Should use a non-zero generator index. - pedersen_hash([ - self.amount, - self.secret_hash, - ],0) + pedersen_hash(self.serialize(), 0) } - pub fn compute_nullifier(self, _context: &mut PrivateContext) -> Field { + fn compute_nullifier(self, _context: &mut PrivateContext) -> Field { self.compute_nullifier_without_context() } - pub fn compute_nullifier_without_context(self) -> Field { - // TODO(#1386): should use `compute_note_hash_for_read_or_nullify` once public functions inject nonce! - let siloed_note_hash = compute_siloed_note_hash(TransparentNoteMethods, self); + fn compute_nullifier_without_context(self) -> Field { + let siloed_note_hash = compute_note_hash_for_consumption(self); // TODO(#1205) Should use a non-zero generator index. pedersen_hash([self.secret, siloed_note_hash],0) } - pub fn set_header(&mut self, header: NoteHeader) { + fn set_header(&mut self, header: NoteHeader) { self.header = header; } - - // CUSTOM FUNCTIONS FOR THIS NOTE TYPE - - pub fn knows_secret(self, secret: Field) { - let hash = compute_secret_hash(secret); - assert(self.secret_hash == hash); + fn get_header(self) -> NoteHeader { + self.header } -} -fn deserialize(serialized_note: [Field; TRANSPARENT_NOTE_LEN]) -> TransparentNote { - TransparentNote::deserialize(serialized_note) -} - -fn serialize(note: TransparentNote) -> [Field; TRANSPARENT_NOTE_LEN] { - note.serialize() + fn broadcast(self, context: &mut PrivateContext, slot: Field) { + assert(false, "TransparentNote does not support broadcast"); + } } -fn compute_note_hash(note: TransparentNote) -> Field { - note.compute_note_hash() -} +impl TransparentNote { -fn compute_nullifier(note: TransparentNote, context: &mut PrivateContext) -> Field { - note.compute_nullifier(context) -} + // CONSTRUCTORS -fn compute_nullifier_without_context(note: TransparentNote) -> Field { - note.compute_nullifier_without_context() -} + pub fn new(amount: Field, secret_hash: Field) -> Self { + TransparentNote { + amount: amount, + secret_hash: secret_hash, + secret: 0, + header: NoteHeader::empty(), + } + } -fn get_header(note: TransparentNote) -> NoteHeader { - note.header -} + // new oracle call primitive + // get me the secret corresponding to this hash + pub fn new_from_secret(amount: Field, secret: Field) -> Self { + TransparentNote { + amount: amount, + secret_hash: compute_secret_hash(secret), + secret: secret, + header: NoteHeader::empty(), + } + } -fn set_header(note: &mut TransparentNote, header: NoteHeader) { - note.set_header(header) -} + // CUSTOM FUNCTIONS FOR THIS NOTE TYPE -fn broadcast(context: &mut PrivateContext, slot: Field, note: TransparentNote) { - assert(false, "TransparentNote does not support broadcast"); + pub fn knows_secret(self, secret: Field) { + let hash = compute_secret_hash(secret); + assert(self.secret_hash == hash); + } } - -global TransparentNoteMethods = NoteInterface { - deserialize, - serialize, - compute_note_hash, - compute_nullifier, - compute_nullifier_without_context, - get_header, - set_header, - broadcast, -}; // docs:end:token_types_all \ No newline at end of file diff --git a/build_manifest.yml b/build_manifest.yml index ea8880f74d0..00af54931b9 100644 --- a/build_manifest.yml +++ b/build_manifest.yml @@ -133,6 +133,7 @@ yarn-project-base: - noir - noir-packages - boxes-files + - avm-transpiler yarn-project: buildDir: yarn-project @@ -152,9 +153,9 @@ yarn-project-prod: - yarn-project multiarch: buildx -aztec-sandbox: +aztec: buildDir: yarn-project - projectDir: yarn-project/aztec-sandbox + projectDir: yarn-project/aztec dependencies: - yarn-project-prod multiarch: buildx @@ -175,10 +176,10 @@ cli: boxes: buildDir: boxes dependencies: - - aztec-sandbox + - aztec - noir runDependencies: - - aztec-sandbox + - aztec end-to-end: buildDir: yarn-project @@ -186,7 +187,7 @@ end-to-end: dependencies: - yarn-project runDependencies: - - aztec-sandbox + - aztec mainnet-fork: buildDir: iac/mainnet-fork @@ -209,3 +210,9 @@ yellow-paper: buildDir: yellow-paper rebuildPatterns: - ^yellow-paper/ + +avm-transpiler: + buildDir: . + dockerfile: avm-transpiler/Dockerfile + rebuildPatterns: + - ^avm-transpiler/ \ No newline at end of file diff --git a/circuits/.gitignore b/circuits/.gitignore deleted file mode 100644 index 3dd5576ae74..00000000000 --- a/circuits/.gitignore +++ /dev/null @@ -1,6 +0,0 @@ -.vscode -node_modules -.yarn/cache -.yarn/install-state.gz -ts/dest -/build diff --git a/circuits/CODING_STANDARD.md b/circuits/CODING_STANDARD.md deleted file mode 100644 index dc77bd5c71d..00000000000 --- a/circuits/CODING_STANDARD.md +++ /dev/null @@ -1,308 +0,0 @@ -## C++ Standard for Aztec Circuits - -### Sticking to the Standard - -Read the standards and stick to them! There is some automation to help with this, but **the automation is far from comprehensive**. - -Here are the types of automation that should help stick to the standard: -1. The VSCode workspace file `circuits.code-workspace` is configured to automatically format your code nicely when you save a C++ file. - * It uses `cpp/.clang-format` for this -2. These workspace settings are also configured to warn you (with yellow squiggles) when something does not follow some of the rules. - * It uses `cpp/.clangd` for this -3. A tidy check is run in CI - * Job fails if your code would be changed by `./scripts/tidy.sh fix` -4. To perform some auto-tidying of your code, run `./scripts/tidy.sh fix` from `cpp/` - * **Commit your code first** since tidying will occasionally mess up your code! - * This will run `clang-tidy` on all C++ source files - * It uses `cpp/.clang-tidy` * and formats tidied code with `cpp/.clang-format` - * **Manually review any fixes to your code!** - * If you disagree with an auto-fix or if it is buggy, use `// NOLINT...` ([more here](https://clang.llvm.org/extra/clang-tidy/#suppressing-undesired-diagnostics)) - * If you believe we should reject an entire class of tidy fixes, consider explicitly omitting from our checks or errors in `./clang-tidy` - * Discuss with others first - * _Note:_ tidying takes a while! - * **You may need to run this multiple times!** - * An error (with an auto-fix) in one round may prevent certain subsequent fixes - * A fix in the one round may introduce more potential for fixes - -### The Standard - -1. **general** - * when something is not covered below, fall back to [Google's style guide](https://google.github.io/styleguide/cppguide.html) - * all TODOs should mention a username, email or bug number - ``` - // TODO(dbanks12) - // TODO(david@aztecprotocol.com) - // TODO(bug 12345) - ``` - * if your editor warns you about a line of code fix it! - * consider doing so even if you didn't write that code -1. **spacing** - * 4 spaces except for access-specifiers (`public`/`protected`/`private`) which can use 2 - * namespaces are not indented - * for continued indentation, just be sane - * remove trailing spaces at end of line (use a plugin) - * include a newline at the end of a file - ``` - namespace my_namespace { - class MyClass { - public: - // ... public stuff - - protected: - // ... protected stuff - - private: - // ... private stuff - - void my_private_function0(int arg0, - int arg1) - { - // ... - } - - void my_private_function1( - int arg0, - int arg1) - { - // ... - } - } - } // namespace my_namespace - - ``` -1. **braces** - * functions use curly brace alone on newline - * namespaces, classes, structs, enums, ifs, and loops use curely brace on same line - * examples - ``` - void my_function() - { - // ... - } - - for (int i = 0; i < max; i++) { - // ... - } - - if (something_is_true) { - // ... - } - - struct MyStruct { - // ... - } - ``` -1. **naming** - * `snake_case` for files, namespaces and local variables/members - * `CamelCase` for classes, structs, enums, types - * exceptions types can be made for types if trying to mimic a std type's name like `uint` or `field_ct` - * `ALL_CAPS` for `constexpr`s, global constants, and macros - * do use - * Clear names - * Descriptive names - * don't use - * abbreviations - * words with letters removed - * acronyms - * single letter names - * Unless writing maths, in which case use your best judgement and follow the naming of a _linked_ paper -1. `auto` - * include `*`, `&`, and/or `const` even when using `auto` - * use when type is evident - * use when type should be deduced automatically based on expr - * use in loops to iterate over members of a container - * don't use if it makes type unclear - * don't use you need to enforce a type - * examples - ``` - auto my_var = my_function_with_unclear_return_type(); // BAD - auto my_var = get_new_of_type_a(); // GOOD - ``` -1. `const` and `constexpr` - * use `const` whenever possible to express immutability - * use `constexpr` whenever a `const` can be computed at compile-time - * place `const`/`constexpr` BEFORE the core type as is done in bberg stdlib - * examples - ``` - const int my_const = 0; - constexpr int MY_CONST = 0; - ``` -1. `namespace` and `using` - * never do `using namespace my_namespace;` which causes namespace pollution and reduces readability - * [see here for google's corresponding rule](https://clang.llvm.org/extra/clang-tidy/checks/google/build-using-namespace.html) - * avoid doing `typedef my::OldType NewType` and instead do `using NewType = my::OldType` - * namespaces should exactly match directory structure. If you create a nested namespace, create a nested directory for it - * example for directory `aztec3/circuits/abis/private_kernel`: - ``` - namespace aztec3::circuits::abis::private_kernel { - // ... - } // namespace aztec3::circuits::abis::private_kernel - ``` - * use`init.hpp` *only* for core/critical renames like `NT/CT` and for toggling core types like `CircuitBuilder` - * use unnamed/anonymous namespaces to import and shorten external names into *just this one file* - * all of a file's external imports belong in a single anonymous namespace `namespace { ...\n } // namespace` at the very top of the file directly after `#include`s - * use `using Rename = old::namespace::prefix::Name;` to import and shorten names from external namespaces - * avoid using renames to obscure template params (`using A = A;`) - * never use renames to remove the `std::` prefix - * never use renames to remove a `NT::` or `CT::` prefix - * `test.cpp` tests must always explicitly import every single name they intend to use - * they might want to test over multiple namespaces, native and circuit types, and builder types - * avoid calling barretenberg's functions directly and instead go through interface files like `circuit_types` and -`native_types` - * `using` statements should be sorted case according to the `LexicographicNumeric` rules - * see the `SortUsingDeclarations` section of the [LLVM Clang Format Style Options document](https://clang.llvm.org/docs/ClangFormatStyleOptions.html) - * if your IDE is telling you that an include or name is unused in a file, remove it! -1. **includes** - * start every header with `#pragma once` - * `index.hpp` should include common headers that will be referenced by most cpp/hpp files in the current directory - * `init.hpp` should inject ONLY critical renames (like `NT`/`CT`) and type toggles (like CircuitBuilder) - * example `using NT = aztec3::utils::types::NativeTypes;` - * avoid including headers via relative paths (`../../other_dir`) unless they are a subdir (`subdir/header.hpp`) - * use full path like `aztec3/circuits/hash.hpp` - * ordering of includes - * this source file's header - * essentials (if present) - * `"index.hpp"` - * `"init.hpp"` - * headers nearby - * headers from this directory (no `/`) - * headers from this project using relative path (`"private/private_kernel_inputs.hpp"`) - * Note: headers in this group are sorted in the above order (no `/` first, relative paths second) - * headers from this project using full path (starts with aztec3: `"aztec3/constants.hpp"`) - * barretenberg headers - * `` or other third party headers specified in `.clang-format` - * C++ standard library headers - * use quotes internal headers - * use angle braces for std library and external library headers - * this includes barretenberg - * each group of includes should be sorted case sensitive alphabetically - * each group of includes should be newline-separated - * example: - ``` - #include "this_file.hpp" - - #include "index.hpp" - #include "init.hpp" - - #include "my_file_a_in_this_dir.hpp" - #include "my_file_b_in_this_dir.hpp" - #include "other_dir/file_a_nearby.hpp" - #include "other_dir/file_b_nearby.hpp" - - #include "aztec3/file_a_in_project.hpp" - #include "aztec3/file_b_in_project.hpp" - - #include - #include - - #include - - #include - #include - ``` -1. **access specifiers** - * order them `public`, `protected`, `private` -1. **struct and array initialization** - * use `MyStruct my_inst{};` - * will call default constructor if exists and otherwise will value-initialize members to zero (or will call *their* default constructors if they exist) - * explicitly initialize struct members with default values: `NT::fr my_fr = 0;` - * initialize arrays using `std::array my_arr{};` - * this value-initializes all entries to 0 if T has no default constructor, otherwise calls default constructor for each - * For arrays of fields `fr`, it is particularly important to use the direct initialization form with empty initializer list - ` - std::array vk_path{}; - ` - because the default constructor of `fr` is NOT initializing the array field values to 0. For large arrays and performance considerations, it may be useful to not initialize the field array elements using `std::array vk_path();` -1. **references** - * use them whenever possible for function arguments since pass by reference is cheaper - * make arg references "const" if they should not be modified inside a function -1. **avoid C-style coding** - * avoid `malloc/free` - * use `std::array/vector` instead of `int[]` - * use references instead of pointers when possible - * if pointers are necessary, use smart pointers (`std::unique_ptr/shared_ptr`) instead of raw pointers - * avoid C-style casts (use `static_cast` or `reinterpret_cast`) -1. **comments** - * use doxygen docstrings (will include format example) - ``` - /** - * @brief Brief description - * @details more details - * @tparam mytemplateparam description - * @param myfunctionarg description - * @return describe return value - * @see otherRelevantFunction() - * @see [mylink](url) - */ - ``` - * every file should have a meaningful comment - * every class/struct/function/test should have a meaningful comment - * class/struct comment might == file comment - * comment function preconditions ("arg x must be < 100") -1. **side-effects** - * avoid functions with side effects when it is easy enough to just have pure functions - * if a function modifies its arguments, it should be made very clear that this is happening - * same with class methods that modify members - * function arguments should be `const` when they will not be modified -1. **global state** - * no -1. **docs** - * every subdir should have a readme -1. **functions** - * use `[[nodiscard]]` if it makes no sense to call this function and discard return value - ``` - [[nodiscard] int my_function() - { - // ... - return some_int; - } - - // later can't do - my_function(); - - // can only do - int capture_ret = my_function(); - ``` - * if there is a name clash, prefix parameter with underscore like `_myparam` - ``` - void my_function(int _my_var) - { - my_var = _my_var; - } - ``` -1. **macros** - * avoid macros as much as possible with exceptions for - * testing - * debug utilities - * agreed upon macro infrastructure (like cbinds) -1. **misc** - * use `uintN_t` instead of a primitive type (e.g. `size_t`) when a specific type width must be guaranteed - * avoid signed types (`int`, `long`, `char` etc) unless signedness is required - * signed types are susceptible to undefined behavior on overflow/underflow - * initialize pointers to `nullptr` - * constructors with single arguments should be marked `explicit` to prevent unwanted conversions - * if a constructor is meant to do nothing, do `A() = default;` instead of `A(){}` ([explanation here](https://clang.llvm.org/extra/clang-tidy/checks/modernize/use-equals-default.html)) - * definitely don't do `A(){};` (with semicolon) which can't even be auto-fixed by `clang-tidy` - * explicitly use `override` when overriding a parent class' member ([explanation here](https://clang.llvm.org/extra/clang-tidy/checks/modernize/use-override.html)) - * avoid multiple declarations on the same line - * do: - ``` - int a = 0; - int b = 0; - ``` - * dont: - ``` - int a, b = 0, 0; - ``` - * use `std::vector::emplace_back` instead of `push_back` - * don't use `std::make_pair` when using `emplace_back` ([unnecessary as explained here](https://clang.llvm.org/extra/clang-tidy/checks/modernize/use-emplace.html)) - * **no magic numbers** even if there is a comment explaining them - - -## References - -1. [Mike's Draft C++ Standard](https://hackmd.io/@aztec-network/B1r36lhmj?type=view) -2. [Barretenberg's `.clangd`](https://github.com/AztecProtocol/barretenberg/blob/master/cpp/.clangd) -3. [Barretenberg's `.clang-format`](https://github.com/AztecProtocol/barretenberg/blob/master/cpp/.clang-format) -4. [LLVM's Clang Format Style Options](https://clang.llvm.org/docs/ClangFormatStyleOptions.html) -5. [Google's Style Guide](https://google.github.io/styleguide/cppguide.html) \ No newline at end of file diff --git a/circuits/README.md b/circuits/README.md deleted file mode 100644 index e9e03574020..00000000000 --- a/circuits/README.md +++ /dev/null @@ -1,295 +0,0 @@ -# Aztec 3 Circuit Onboarding - -###### tags: `aztec-3, circuits` - -## Contributing - -See [CODING_STANDARD.md](./CODING_STANDARD.md)\*\* before contributing! - -## Repository Overview - -The [`aztec3-circuits`](https://github.com/AztecProtocol/aztec3-packages) circuits folder contains circuits and related C++ code (`cpp/`) for Aztec3 along with Typescript wrappers (`ts/`). - -### Dependencies - -- cmake >= 3.24 -- Ninja (used by the presets as the default generator) -- clang16 -- clang-format -- wasm-opt (part of the [Binaryen](https://github.com/WebAssembly/binaryen) toolkit) -- [wasmtime](https://docs.wasmtime.dev/cli-install.html) for running tests in a wasm environment - -### Resources - -- [Circuits project board](https://github.com/orgs/AztecProtocol/projects/22/views/2) -- [[DO NOT EDIT] Diagram with pseudocode for circuits](https://miro.com/app/board/uXjVPlafJWM=/) - - This diagram's contents are likely more up-to-date than code and than the other links below -- Kernel circuits - - [Slides - Dive into Kernel Circuits](https://drive.google.com/file/d/1BaspihHDUgny6MHAKMtTkWKvfah7PYtv/view?usp=share_link) - - [Recording of presentation](https://drive.google.com/file/d/1Uh-vLdc1_rsMUHL_c4HZ93jrjpuDsqD3/view?usp=share_link) - - [Kernel circuit mentioned in M1.1 Contract Deployment document](https://hackmd.io/ouVCnacHQRq2o1oRc5ksNA#Kernel-Circuit-functionality) - - [More info on the kernel circuit for Contract Creation](https://hackmd.io/@aztec-network/rkyRaXqPj#Kernel-Circuit-Logic) - - _Note:_ [Base rollup circuits in Aztec Connect](https://github.com/AztecProtocol/aztec-connect-cpp/tree/defi-bridge-project/src/rollup/proofs/rollup) are relevant to kernel circuits -- Rollup circuits - - [Rollup circuit mentioned in M1.1 Contract Deployment document](https://hackmd.io/ouVCnacHQRq2o1oRc5ksNA#Rollup-Circuit-functionality) - - [More info on the rollup circuit for Contract Creation](https://hackmd.io/@aztec-network/rkyRaXqPj#Rollup-Circuit-Logic) - - [Rollup circuits in Aztec Connect](https://github.com/AztecProtocol/aztec-connect-cpp/tree/defi-bridge-project/src/rollup/proofs) - - [[DO NOT EDIT] Diagram with different options for merkle insertions in rollup circuits](https://miro.com/app/board/uXjVMfITC3c=/) -- [Outdated specs](https://github.com/AztecProtocol/aztec2-internal/blob/3.0/markdown/specs/aztec3/src/SUMMARY.md) -- [Explanation of Indexed Merkle Tree (for nullifiers)](https://hackmd.io/AjR1uGh8SzSc7k3Gcu02mQ) - -### Getting Started with C++ - -Clone the repo and build the C++: - -``` -git clone git@github.com:AztecProtocol/aztec3-packages.git -cd circuits -git submodule update --init --recursive -cd cpp -./bootstrap.sh -``` - -Here is an example of rapidly rebuilding and running all tests for `x86_64`: - -``` -./bootstrap.sh -./scripts/run_tests_local x86_64 glob -``` - -> **WARNING:** the `x86_64` (and `wasm` used below) as well as the keyword `glob` **MUST BE LOWERCASE**! - -Here is an example of rapidly rebuilding and running only the abis tests for `wasm`: - -``` -./bootstrap.sh aztec3_circuits_abis_tests -./scripts/run_tests_local wasm aztec3_circuits_abis_tests -``` - -> _Note:_ to run wasm tests you must first follow the [instructions here](https://docs.wasmtime.dev/cli-install.html) to install `wasmtime`. - -You can choose which tests will run via a gtest filter. This one below runs only tests that _omit_ the string '.circuit': - -``` -./scripts/run_tests_local wasm aztec3_circuits_abis_tests -*.circuit* -``` - ---- - -Here's a list of the tests currently available (conveniently combined with the command to build, then execute them on `x86_64`, for easy copy-pasta): - -- `aztec3_circuits_abis_tests` - - - `./bootstrap.sh aztec3_circuits_abis_tests && ./scripts/run_tests_local x86_64 aztec3_circuits_abis_tests` - -- `aztec3_circuits_apps_tests` - - - `./bootstrap.sh aztec3_circuits_apps_tests && ./scripts/run_tests_local x86_64 aztec3_circuits_apps_tests` - -- `aztec3_circuits_kernel_tests` - - - `./bootstrap.sh aztec3_circuits_kernel_tests && ./scripts/run_tests_local x86_64 aztec3_circuits_kernel_tests` - -- `aztec3_circuits_recursion_tests` - - - `./bootstrap.sh aztec3_circuits_recursion_tests && ./scripts/run_tests_local x86_64 aztec3_circuits_recursion_tests` - -- `aztec3_circuits_rollup_tests` - - `./bootstrap.sh aztec3_circuits_rollup_tests && ./scripts/run_tests_local x86_64 aztec3_circuits_rollup_tests` - ---- - -#### Using docker to replicate CI failures - -You can also run tests in docker. This is useful for replicating CI failures that you can't replicate with your standard local environment. - -To build and run all tests in an `x86_64` docker image: - -``` -./bootstrap.sh -./scripts/build_run_tests_docker_local 1 x86_64 glob -``` - -You can choose `wasm` instead of `x86_64`. You can also specify individual test executables instead of `glob` and can use gtest filters exactly as described for `run_tests_local`. - -> At this time, it is common to run wasm tests with the filter `-*.circuit*` as there are circuit issues in wasm. - -> The `build_run_tests_docker_local` script builds the chosen docker image (`x86_64` or `wasm`) and then launches a container from that image to run the `run_tests_local` script (used above). - -#### Generating code coverage reports - -You can generate coverage reports for your tests. -To build and run coverage on all tests: - -``` -./bootstrap.sh -./scripts/run_coverage -``` - -Producing coverage reports is computationally intensive -You can select a specific test suite to run coverage on by supplying it as an argument to the `run_coverage`. For example, to only compile and produce - -``` -./bootstrap.sh -./scripts/run_coverage aztec3_circuits_abis_tests -``` - -**Toggles** -Running with the `CLEAN` environment variable set will delete the existing `build-coverage` folder. -Running with the `CLEAR_COV` environment variable will delete any existing `lcov.info` file. - -#### Viewing coverage reports - -Once a report has been generated, you can view them within the `build-coverage` folder in html format. If you ran coverage with any tests in mind, the report will exist in a folder prefixed with its name, otherwise they can be found in one labelled all_tests. - -#### Viewing coverage reports inside vscode - -It may be useful to view coverage information from within vscode. The `./scripts/run_coverage` will produce an `lcov.info` file that should automatically be picked up by the `coverage-gutters` vscode extension. - ---- - -#### Using the VSCode debugger - -> **WARNING:** to debug in WASM (to use the `-g` option to `wasmtime`) you will unfortunately need to revert to `wasmtime` version `1.0.0` until [this bug](https://github.com/bytecodealliance/wasmtime/issues/3999) is fixed. To install that version, remove the `~/.wasmtime` directory and run `curl https://wasmtime.dev/install.sh -sSf | bash /dev/stdin --version v1.0.0` - -1. Make sure you have the recommended plugins installed - - Open the command palette (`Ctrl+Shift+P`) - - `Cmd+Shift+P` on Macs - - Type and select "Extensions: Show Recommended Extensions" - - Install any plugins shown not already installed -1. Configure CMake for whichever preset you'd like to use - - Open the command palette (`Ctrl+Shift+P`) - - Type and select "CMake: Select Configure Preset" - - Choose a debug preset such as: - - "Debugging build with Clang-16" - - "Debugging build for WASM" - - Redo this step later to switch between Clang-16/native and wasm -1. Go to the "Run and Debug" panel - - Button (usually on left) that looks like a play button with a bug - - Or `Ctrl+Shift+D` -1. Select the proper launch option at the top of the "Run and Debug" panel - - "Launch native" - - "Launch in WASM" -1. Select the test executable to debug - - Open the command palette (`Ctrl+Shift+P`) - - Type and select "CMake: Set Debug Target" - - Select executable to debug like `aztec3_circuits_abis_tests` -1. Check output for progress -1. [OPTIONAL] change `gtest_filter` args to filter specific test cases - - In `circuits.code-workspace`'s `launch->configurations->` - - Don't commit these changes -1. [OPTIONAL] set breakpoints in C++ files - -> _Note:_ redo steps 3-5 to switch between debugging Clang-16/native test executables and WASM test executables - ---- - -### C++ Repository Layout - -This repository submodules [`barretenberg`](https://github.com/AztecProtocol/barretenberg) as a C++ library containing proving systems and utilities at `cpp/barretenberg/`. - -The core Aztec 3 C++ code lives in `cpp/src/aztec3/`, and is split into the following subdirectories/files: - -- `constants.hpp`: top-level constants relevant to Aztec 3 -- `circuits`: circuits and their types and interfaces - - `apps`: infrastructure and early prototypes for application circuits ([more here](https://github.com/AztecProtocol/aztec3-packages/tree/master/circuits/cpp/src/aztec3/circuits/apps)) - - `abis`: types, interfaces, and cbinds for representing/constructing outputs of application circuits that will be fed into kernel circuits ([more here](https://github.com/AztecProtocol/aztec3-packages/tree/master/circuits/cpp/src/aztec3/circuits/abis)) - - `kernel`: kernel circuits, their interfaces, and their tests - - `rollup`: rollup circuits, their interfaces and tests - - `recursion`: types and examples for aggregation of recursive proof objects - - `mock`: mock circuits -- `oracle`: used to fetch external information (like private data notes) and inject them as inputs into the circuit during execution of circuit logic ([more here](https://github.com/AztecProtocol/aztec3-packages/tree/master/circuits/cpp/src/aztec3/oracle)) -- `dbs`: database infrastructure (_e.g._ PrivateStateDb) - -### Typescript - -All typescript code was moved from here into `aztec3-packages/yarn-project/circuits.js`. - -## Private Kernel Circuit - -The private kernel circuit validates that a particular private function was correctly executed by the user. Therefore, the private kernel circuit is going to be run on the user's device. A private function execution can involve calls to other private functions from the same contract or private functions from other contracts. Each call to another private function needs to be proven that the execution was correct. Therefore, each nested call to another private function will have its own circuit execution proof, and that proof must then be validated by the private kernel circuit. The proof generated by the private kernel circuit will be submitted to the transaction pool, from where rollup providers will include those private kernel proofs in their L2 blocks. - -The private kernel circuit in Aztec 3 is implemented in [`circuit/kernel/private`](https://github.com/AztecProtocol/aztec3-packages/tree/master/circuits/cpp/src/aztec3/circuits/kernel/private) directory. The input and output interface of the private kernel circuit is written in [`circuits/abis/private_kernel`](https://github.com/AztecProtocol/aztec3-packages/tree/master/circuits/cpp/src/aztec3/circuits/abis/private_kernel). - -See pseudocode in [this diagram](https://miro.com/app/board/uXjVPlafJWM=/) and the slides [here](https://drive.google.com/file/d/1BaspihHDUgny6MHAKMtTkWKvfah7PYtv/view?usp=share_link) for a deeper dive into the private kernel circuit. - -## Public Kernel Circuit - -The public kernel circuit performs many of the functions of the private kernel circuit but for public functions. Public functions differ from public functions in that they they are executed by the sequencer and can modify the public state tree. Like private functions, public functions can include calls to other public functions and these calls are validated within the circuit. - -The public kernel circuit in Aztec 3 is implemented in [`circuit/kernel/public`](https://github.com/AztecProtocol/aztec3-packages/tree/master/circuits/cpp/src/aztec3/circuits/kernel/publlic) directory. The input and output interface of the private kernel circuit is written in [`circuits/abis/public_kernel`](https://github.com/AztecProtocol/aztec3-packages/tree/master/circuits/cpp/src/aztec3/circuits/abis/public_kernel). - -## Circuit Overviews - -### Base Rollup Circuit - -The rollup providers (or sequencers - nomenclature is yet to be decided) pull up transactions from the pool to be rolled up. Each of these transactions is essentially a proof generated by the private kernel circuit including its public inputs. To accumulate several such transactions in a single L2 block, the rollup provider needs to aggregate the private kernel proofs. The base rollup circuit is meant to aggregate these private kernel proofs. In simple words, the rollup circuit validates that the private kernel circuit was correctly executed. - -In our design, we allow the rollup providers to only aggregate _two_ private kernel proofs at once. This would mean that if a rollup provider wishes to roll-up 1024 transactions in one L2 block, for example, he would need $\frac{1024}{2} = 512$ invocations of the base rollup circuit. This hard-limit on the number of private kernel proofs that one can aggregate is enable generating rollup proofs on commodity hardware, effectively reducing the entry-barrier for common users to become rollup providers. - -:::info -At a very high-level, the rollup circuits need to perform the following checks: - -1. Aggregate the inner proofs (most expensive part taking up appx 75% circuit size) -2. Merkle membership checks (second most expensive part taking up appx 15% circuit size) -3. Public input hashing using SHA-256 (third most expensive part, appx 10%, can blow up if you need to hash tons of public inputs) - -We have a limit on the first point: you can aggregate a maximum of two proofs per rollup circuit. We still need to decide if we wish to put any limits on the second step. Mike has an idea of splitting up the Merkle membership checks across multiple rollup circuits so that all of the Merkle membership computation doesn't need to be performed by a single circuit. Similarly, we need to ensure that a rollup circuit doesn't need to hash huge amounts of data in one stage. -::: - -:::warning -**Note**: For the first milestone, we do not include public function execution in our circuit design. -::: - -See pseudocode in [this diagram](https://miro.com/app/board/uXjVPlafJWM=/) for a deep dive into the Base Rollup Circuit's functionality. - -### Merge Rollup Circuit - -The base rollup proofs further need to be validated if they were executed correctly. The merge rollup circuit is supposed to verify that the base rollup proofs are correct. In principle, the design of the merge rollup circuit would be very similar to the base rollup circuit except the change in the ABIs. As with the base rollup circuit, every merge rollup circuit validates two base rollup proofs. - -Furthermore, we can use the same merge rollup circuit to verify two merge rollup proofs to further compress the proof validation. This leads to a formation of a tree-like structure to create an L2 block. - -See pseudocode in [this diagram](https://miro.com/app/board/uXjVPlafJWM=/) for a deep dive into the Merge Rollup Circuit's functionality. - -:::warning -**Note**: We might not need the merge rollup circuit for the offsite but its anyway going to be very similar to the base rollup circuit. -::: - -### Root Rollup Circuit - -The root rollup circuit is the final circuit execution layer before the proof is sent to L1 for on-chain verification. The root rollup circuit verifies that the final merge rollup circuit was correctly executed. The proof from the root rollup circuit is verified by the rollup contract on Ethereum. So effectively, verifying that one proof on-chain gives a final green flag to whatever number of transactions that were included in that particular L2 block. - -It is interesting to note that the root rollup circuit takes _one_ proof and outputs _one_ proof. The reason we do this is to switch the types of the proof: i.e. ultra-plonk/honk to standard-plonk. This is because standard-plonk proofs are cheaper to verify on-chain (in terms of gas costs). - -See pseudocode in [this diagram](https://miro.com/app/board/uXjVPlafJWM=/) for a deep dive into the Root Rollup Circuit's functionality. - -## Circuit Logic - -### Private Kernel Circuit Logic - -The private kernel circuit is recursive in that it will perform validation of a single function call, and then recursively verify its previous iteration along with the next function call. - -Below is a list of the private kernel circuit's high-level responsibilities: - -1. For the first iteration: - - **[M1.1]** Validate the signature of a signed tx object - - Validate the function in that tx object matches the one currently being processed -2. For all subsequent iterations: - - Pop an item off of the kernel's dynamic callstack - - Validate that this function matches the one currently being processed -3. Verify a 'previous' kernel circuit (mock for first iteration, always mocked for **[M1.1]**) -4. Verify the proof of execution for the function (or constructor **[M1.1]**) currently being processed -5. **[M1.1]** If this is a contract deployment, check the contract deployment logic - - _[After M1.1]_ Includes checks for private circuit execution logic -6. **[M1.1]** Generate `contract_address` and its nullifier (inserted in later circuit) -7. **[M1.1]** Generate `new_contract_data` which is the contract tree leaf preimage -8. Copy the current function's callstack into kernel's dynamic callstack -9. Validate the function data against `function_tree_root` - - Includes a membership check of the function leaf -10. Validate the contract data against `contract_tree_root` - - Includes a membership check of the contract leaf -11. Perform membership checks for commitments accessed by this function -12. Collect new commitments, nullifiers, contracts, and messages to L1 -13. Add recursion byproducts to `aggregation_object` -14. TODO: L1 messages -15. Section in progress... diff --git a/circuits/cpp/.clang-format b/circuits/cpp/.clang-format deleted file mode 100644 index 7e309b5090f..00000000000 --- a/circuits/cpp/.clang-format +++ /dev/null @@ -1,92 +0,0 @@ -Language: Cpp -BasedOnStyle: Google -ColumnLimit: 120 - -# Whitespace -IndentWidth: 4 -UseTab: Never -#LineEnding: LF -MaxEmptyLinesToKeep: 2 - -# Auto-insertions -#InsertNewlineAtEOF: true -#InsertTrailingCommas: true -#InsertBraces: true - -# Alignment -#ReferenceAlignment: Left -PointerAlignment: Left -#QualifierAlignment: Left # only in clang-format 14+ - -# Misc spacing/linebreaks -AccessModifierOffset: -2 -AllowShortFunctionsOnASingleLine: Inline -AllowShortIfStatementsOnASingleLine: Never -AlwaysBreakAfterReturnType: None -AlwaysBreakAfterDefinitionReturnType: None -AlwaysBreakTemplateDeclarations: No -PenaltyReturnTypeOnItsOwnLine: 1000000 -BreakConstructorInitializers: BeforeComma - -# Includes -SortIncludes: true # Consider CaseSensitive in clang-format 13+ -IncludeBlocks: Regroup -IncludeCategories: - # 1. Special headers - - Regex: '"\(index\.hpp\)|\(init\.hpp\)"' - Priority: 1 - # 2. Headers in "" with no '/' (current dir). - - Regex: '"([A-Za-z0-9.\Q-_\E])+"' - Priority: 2 - SortPriority: 2 - # 2. Headers in "" that start with '../'. - - Regex: '"\.\./([A-Za-z0-9.\Q-_\E])+"' - Priority: 2 - SortPriority: 4 # same priority/group as 2, but sorted to bottom. - # 5. Aztec3 headers in "". - - Regex: '["<]aztec3/([A-Za-z0-9.\Q/-_\E])+[">]' - Priority: 5 - # 6. Barretenberg headers in "" - - Regex: '["<]barretenberg/.*[">]' - Priority: 6 - # 2. All other headers in "". - # Note: Must be below aztec3/barretenberg groups or it captures them too. - - Regex: '"([A-Za-z0-9.\Q/-_\E])+"' - Priority: 2 - SortPriority: 3 # same priority/group as 2, but sorted to middle. - # 6. Headers in <> with extension. - - Regex: '<([A-Za-z0-9\Q/-_\E])+\.[A-Za-z0-9.\Q/-_\E]>' - Priority: 7 - # 7. Headers in <> from specific external libraries. - - Regex: '<(gtest|placeHolderForOthers)>' - Priority: 8 - # 8. Headers in <> without extension. - - Regex: '<([A-Za-z0-9\Q/-_\E])+>' - Priority: 9 - -# Namespaces and using -FixNamespaceComments: true -NamespaceIndentation: None -SortUsingDeclarations: true # LexicographicNumeric - -# Bin packing -BinPackArguments: false -BinPackParameters: false - -# Braces -Cpp11BracedListStyle: false -#BreakBeforeBraces: Allman -BreakBeforeBraces: Custom -BraceWrapping: - AfterClass: false - AfterEnum: false - AfterFunction: true - AfterNamespace: false - AfterStruct: false - AfterUnion: false - AfterExternBlock: false - BeforeCatch: false - BeforeElse: false - SplitEmptyFunction: false - SplitEmptyRecord: false - SplitEmptyNamespace: false diff --git a/circuits/cpp/.clang-tidy b/circuits/cpp/.clang-tidy deleted file mode 100644 index cd140f4d0e6..00000000000 --- a/circuits/cpp/.clang-tidy +++ /dev/null @@ -1,212 +0,0 @@ -# Note there is some overlap between this file and .clangd -# .clangd has no concept of warnings versus errors, but since it -# won't actually fail/error (since its really just an LSP for -# showing issues in an editor), it is acceptable to treat most -# .clang-tidy warnings as .clangd errors. Also, some error codes -# with buggy-autofixes need to be omitted from checks in .clang-tidy -# but can be included in .clangd since it won't change code without -# a user action. -# -# .clang-tidy on the other hand will error in CI for any check that -# is flagged (not explicitly omitted after '*') in WarningsAsErrors. -# So, it needs to omit certain checks or keep them as warnings only -# if they are too strict. - -# TODO(AD): Picking a limited subset for now, pending a deeper dive into the performance issues. See #1152. -Checks: ' - clang-analyzer-apiModeling.Errno - clang-analyzer-apiModeling.StdCLibraryFunctions - clang-analyzer-apiModeling.TrustNonnull - clang-analyzer-apiModeling.TrustReturnsNonnull - clang-analyzer-apiModeling.google.GTest - clang-analyzer-apiModeling.llvm.CastValue - clang-analyzer-apiModeling.llvm.ReturnValue - clang-analyzer-core.CallAndMessage - clang-analyzer-core.CallAndMessageModeling - clang-analyzer-core.DivideZero - clang-analyzer-core.DynamicTypePropagation - clang-analyzer-core.NonNullParamChecker - clang-analyzer-core.NonnilStringConstants - clang-analyzer-core.NullDereference - clang-analyzer-core.StackAddrEscapeBase - clang-analyzer-core.StackAddressEscape - clang-analyzer-core.VLASize - clang-analyzer-core.builtin.BuiltinFunctions - clang-analyzer-core.builtin.NoReturnFunctions - clang-analyzer-core.uninitialized.ArraySubscript - clang-analyzer-core.uninitialized.Assign - clang-analyzer-core.uninitialized.Branch - clang-analyzer-core.uninitialized.CapturedBlockVariable - clang-analyzer-core.uninitialized.UndefReturn - clang-analyzer-cplusplus.InnerPointer - clang-analyzer-cplusplus.Move - clang-analyzer-cplusplus.NewDelete - clang-analyzer-cplusplus.PlacementNew - clang-analyzer-cplusplus.PureVirtualCall - clang-analyzer-cplusplus.SmartPtrModeling - clang-analyzer-cplusplus.StringChecker - clang-analyzer-cplusplus.VirtualCallModeling - clang-analyzer-deadcode.DeadStores - clang-analyzer-fuchsia.HandleChecker - clang-analyzer-nullability.NullPassedToNonnull - clang-analyzer-nullability.NullReturnedFromNonnull - clang-analyzer-nullability.NullabilityBase - clang-analyzer-nullability.NullableDereferenced - clang-analyzer-nullability.NullablePassedToNonnull - clang-analyzer-nullability.NullableReturnedFromNonnull - clang-analyzer-optin.cplusplus.UninitializedObject - clang-analyzer-optin.cplusplus.VirtualCall - clang-analyzer-optin.mpi.MPI-Checker - clang-analyzer-optin.osx.OSObjectCStyleCast - clang-analyzer-optin.osx.cocoa.localizability.EmptyLocalizationContextChecker - clang-analyzer-optin.osx.cocoa.localizability.NonLocalizedStringChecker - clang-analyzer-optin.performance.GCDAntipattern - clang-analyzer-optin.portability.UnixAPI - clang-analyzer-security.FloatLoopCounter - clang-analyzer-security.insecureAPI.DeprecatedOrUnsafeBufferHandling - clang-analyzer-security.insecureAPI.SecuritySyntaxChecker - clang-analyzer-security.insecureAPI.UncheckedReturn - clang-analyzer-security.insecureAPI.bcmp - clang-analyzer-security.insecureAPI.bcopy - clang-analyzer-security.insecureAPI.bzero - clang-analyzer-security.insecureAPI.decodeValueOfObjCType - clang-analyzer-security.insecureAPI.getpw - clang-analyzer-security.insecureAPI.gets - clang-analyzer-security.insecureAPI.mkstemp - clang-analyzer-security.insecureAPI.mktemp - clang-analyzer-security.insecureAPI.rand - clang-analyzer-security.insecureAPI.strcpy - clang-analyzer-security.insecureAPI.vfork - clang-analyzer-unix.API - clang-analyzer-unix.DynamicMemoryModeling - clang-analyzer-unix.Malloc - clang-analyzer-unix.MallocSizeof - clang-analyzer-unix.MismatchedDeallocator - clang-analyzer-unix.Vfork - clang-analyzer-unix.cstring.BadSizeArg - clang-analyzer-unix.cstring.CStringModeling - clang-analyzer-unix.cstring.NullArg - clang-analyzer-valist.CopyToSelf - clang-analyzer-valist.Uninitialized - clang-analyzer-valist.Unterminated - clang-analyzer-valist.ValistBase - clang-analyzer-webkit.NoUncountedMemberChecker - clang-analyzer-webkit.RefCntblBaseVirtualDtor - clang-analyzer-webkit.UncountedLambdaCapturesChecker - cppcoreguidelines-avoid-c-arrays - cppcoreguidelines-avoid-goto - cppcoreguidelines-explicit-virtual-functions - cppcoreguidelines-init-variables - cppcoreguidelines-interfaces-global-init - cppcoreguidelines-macro-usage - cppcoreguidelines-narrowing-conversions - cppcoreguidelines-prefer-member-initializer - cppcoreguidelines-pro-type-const-cast - cppcoreguidelines-pro-type-static-cast-downcast - cppcoreguidelines-pro-type-union-access - cppcoreguidelines-pro-type-vararg - cppcoreguidelines-slicing - cppcoreguidelines-virtual-class-destructor - performance-faster-string-find - performance-for-range-copy - performance-implicit-conversion-in-loop - performance-inefficient-algorithm - performance-inefficient-string-concatenation - performance-inefficient-vector-operation - performance-move-const-arg - performance-move-constructor-init - performance-no-automatic-move - performance-no-int-to-ptr - performance-noexcept-move-constructor - performance-trivially-destructible - performance-type-promotion-in-math-fn - performance-unnecessary-copy-initialization - portability-restrict-system-includes - portability-simd-intrinsics - portability-std-allocator-const - -bugprone-unchecked-optional-access - -bugprone-unhandled-self-assignment - -clang-analyzer-core.UndefinedBinaryOperatorResult - -clang-analyzer-cplusplus.NewDeleteLeaks - -clang-analyzer-optin.performance.Padding - -cert-err58-cpp - -cert-oop54-cpp - -cppcoreguidelines-avoid-non-const-global-variables - -cppcoreguidelines-avoid-magic-numbers - -cppcoreguidelines-c-copy-assignment-signature - -cppcoreguidelines-no-malloc - -cppcoreguidelines-owning-memory - -cppcoreguidelines-pro-type-cstyle-cast - -cppcoreguidelines-pro-type-reinterpret-cast - -cppcoreguidelines-special-member-functions - -google-build-using-namespace - -google-global-names-in-headers - -google-readability-casting - -misc-definitions-in-headers - -misc-no-recursion - -misc-unconventional-assign-operator - -modernize-return-braced-init-list - -performance-unnecessary-value-param - -readability-function-cognitive-complexity - -readability-magic-numbers -' - -# We treat all warnings as errors. -WarningsAsErrors: '*' - -# Notes on specific Checks and WarningsAsErrors -# -# These checks rename our cbinds and consts that have `__` in the name: -# -bugprone-reserved-identifier, -# -cert-dcl37-c, -# -cert-dcl51-cpp, -# `any_of/all_of` loops are not objectively more readable: -# -readability-use-anyofallof, -# Will need to refactor our usage of `malloc/free` with `gsl::owner`: -# -cppcoreguidelines-owning-memory, -# Flags way too many functions: -# -bugprone-easily-swappable-parameters, -# We use anon namespaces to import types: -# -google-build-namespaces, -# -cert-dcl59-cpp, -# Not sure we want to use trailing return type: -# -modernize-use-trailing-return-type, -# Buggy in clang-tidy 15.0.6: -# -modernize-use-nodiscard, -# ^ TODO(david): re-enable if we move to newer clang version -# We use c-arrays in low-level code logic relating to c_bind: -# -modernize-avoid-c-arrays, -# This was changing code in ways that made it harder to follow: -# -modernize-pass-by-value, -# ^ TODO(david) we probably want to re-enable this one -# We like (a == true) in circuits: -# -readability-simplify-boolean-expr, -# We have a lot of one-letter variable names...: -# -readability-identifier-length, -# All of our tests use underscores in names: -# -google-readability-avoid-underscore-in-googletest-name, -# All of our circuit structs have non-private members: -# -misc-non-private-member-variables-in-classes, -# -cppcoreguidelines-non-private-member-variables-in-classes, -# We have many `for` loops that violate this part of the bounds safety profile -# -cppcoreguidelines-pro-bounds-constant-array-index, -# Many hits potential for false positives: -# -cppcoreguidelines-pro-type-member-init, -# Triggers on some tests that are not complex. We should re-enable this for tests: -# -readability-function-cognitive-complexity, -# Triggers for globals in tests and TEST macros -# -cert-err58-cpp, -# Useful but check is buggy in clang-tidy 15.0.6: -# -misc-const-correctness, -# ^ TODO(david): re-evaluate whether this one should be included here -# its auto-fixes are buggy -# if disabled, re-enable if fixed in newer clang version -# -# We should be able to fix: -# -google-build-using-namespace, # using whole::namespace; is bad -# -google-readability-todo, # the auto-fix for this inputs current user's name for all -# -cppcoreguidelines-avoid-magic-numbers, # we shouldn't use magic numbers! -# -readability-magic-numbers, - -HeaderFilterRegex: 'src/aztec3/' -FormatStyle: file diff --git a/circuits/cpp/.clang-tidy.slow b/circuits/cpp/.clang-tidy.slow deleted file mode 100644 index 60cbb30315f..00000000000 --- a/circuits/cpp/.clang-tidy.slow +++ /dev/null @@ -1,139 +0,0 @@ -# Disabled until #1152 is fully investigated (why this took 15 minutes in CI) - -# Note there is some overlap between this file and .clangd -# .clangd has no concept of warnings versus errors, but since it -# won't actually fail/error (since its really just an LSP for -# showing issues in an editor), it is acceptable to treat most -# .clang-tidy warnings as .clangd errors. Also, some error codes -# with buggy-autofixes need to be omitted from checks in .clang-tidy -# but can be included in .clangd since it won't change code without -# a user action. -# -# .clang-tidy on the other hand will error in CI for any check that -# is flagged (not explicitly omitted after '*') in WarningsAsErrors. -# So, it needs to omit certain checks or keep them as warnings only -# if they are too strict. - -Checks: ' - cert-*, - google-*, - cppcoreguidelines-*, - readability-*, - modernize-*, - bugprone-*, - misc-*, - performance-*, - clang-analyzer-*, - concurrency-*, - portability-*, - -bugprone-easily-swappable-parameters, - -bugprone-reserved-identifier, - -cppcoreguidelines-non-private-member-variables-in-classes, - -cppcoreguidelines-pro-bounds-constant-array-index, - -cppcoreguidelines-pro-bounds-pointer-arithmetic, - -cppcoreguidelines-pro-type-member-init, - -cert-dcl37-c, - -cert-dcl51-cpp, - -cert-dcl59-cpp, - -google-build-namespaces, - -google-readability-avoid-underscore-in-googletest-name, - -google-readability-todo, - -misc-non-private-member-variables-in-classes, - -modernize-avoid-c-arrays, - -modernize-pass-by-value, - -modernize-use-nodiscard, - -modernize-use-trailing-return-type, - -readability-identifier-length, - -readability-simplify-boolean-expr, - -readability-use-anyofallof, -' - -# We treat all warnings as errors except for these few. -# Some of these exceptions like 'google-build-using-namespace' -# we should be able to manually fix project-wide and then -# remove from the omissions list. -WarningsAsErrors: ' - *, - -bugprone-unchecked-optional-access, - -bugprone-unhandled-self-assignment, - -clang-analyzer-core.UndefinedBinaryOperatorResult, - -clang-analyzer-cplusplus.NewDeleteLeaks, - -clang-analyzer-optin.performance.Padding, - -cert-err58-cpp, - -cert-oop54-cpp, - -cppcoreguidelines-avoid-non-const-global-variables, - -cppcoreguidelines-avoid-magic-numbers, - -cppcoreguidelines-c-copy-assignment-signature, - -cppcoreguidelines-no-malloc, - -cppcoreguidelines-owning-memory, - -cppcoreguidelines-pro-type-cstyle-cast, - -cppcoreguidelines-pro-type-reinterpret-cast, - -cppcoreguidelines-special-member-functions, - -google-build-using-namespace, - -google-global-names-in-headers, - -google-readability-casting, - -misc-definitions-in-headers, - -misc-no-recursion, - -misc-unconventional-assign-operator, - -modernize-return-braced-init-list, - -performance-unnecessary-value-param, - -readability-function-cognitive-complexity, - -readability-magic-numbers, -' - -# Notes on specific Checks and WarningsAsErrors -# -# These checks rename our cbinds and consts that have `__` in the name: -# -bugprone-reserved-identifier, -# -cert-dcl37-c, -# -cert-dcl51-cpp, -# `any_of/all_of` loops are not objectively more readable: -# -readability-use-anyofallof, -# Will need to refactor our usage of `malloc/free` with `gsl::owner`: -# -cppcoreguidelines-owning-memory, -# Flags way too many functions: -# -bugprone-easily-swappable-parameters, -# We use anon namespaces to import types: -# -google-build-namespaces, -# -cert-dcl59-cpp, -# Not sure we want to use trailing return type: -# -modernize-use-trailing-return-type, -# Buggy in clang-tidy 15.0.6: -# -modernize-use-nodiscard, -# ^ TODO(david): re-enable if we move to newer clang version -# We use c-arrays in low-level code logic relating to c_bind: -# -modernize-avoid-c-arrays, -# This was changing code in ways that made it harder to follow: -# -modernize-pass-by-value, -# ^ TODO(david) we probably want to re-enable this one -# We like (a == true) in circuits: -# -readability-simplify-boolean-expr, -# We have a lot of one-letter variable names...: -# -readability-identifier-length, -# All of our tests use underscores in names: -# -google-readability-avoid-underscore-in-googletest-name, -# All of our circuit structs have non-private members: -# -misc-non-private-member-variables-in-classes, -# -cppcoreguidelines-non-private-member-variables-in-classes, -# We have many `for` loops that violate this part of the bounds safety profile -# -cppcoreguidelines-pro-bounds-constant-array-index, -# Many hits potential for false positives: -# -cppcoreguidelines-pro-type-member-init, -# Triggers on some tests that are not complex. We should re-enable this for tests: -# -readability-function-cognitive-complexity, -# Triggers for globals in tests and TEST macros -# -cert-err58-cpp, -# Useful but check is buggy in clang-tidy 15.0.6: -# -misc-const-correctness, -# ^ TODO(david): re-evaluate whether this one should be included here -# its auto-fixes are buggy -# if disabled, re-enable if fixed in newer clang version -# -# We should be able to fix: -# -google-build-using-namespace, # using whole::namespace; is bad -# -google-readability-todo, # the auto-fix for this inputs current user's name for all -# -cppcoreguidelines-avoid-magic-numbers, # we shouldn't use magic numbers! -# -readability-magic-numbers, - -HeaderFilterRegex: 'src/aztec3/' -FormatStyle: file diff --git a/circuits/cpp/.clangd b/circuits/cpp/.clangd deleted file mode 100644 index a2fd014d990..00000000000 --- a/circuits/cpp/.clangd +++ /dev/null @@ -1,73 +0,0 @@ -# Language Server Protocol for showing code problems -# and suggesting fixes in an editor. -# -# See .clang-tidy comments for more information. - -CompileFlags: # Tweak the parse settings - Remove: -fconstexpr-ops-limit=* ---- -# Applies all barretenberg source files -If: - PathMatch: [src/.*\.hpp, src/.*\.cpp, src/.*\.tcc, src/.*\.h] -Diagnostics: - # Checks whether we are including unused header files - # Note that some headers may be _implicitly_ used and still - # need to be included, so be careful before removing them. - UnusedIncludes: Strict - - # Static analysis configuration - ClangTidy: - Add: - - cert-* - - google-* - - cppcoreguidelines-* - - readability-* - - modernize-* - - bugprone-* - - misc-* - - performance-* - - clang-analyzer-* - - concurrency-* - - portability-* - Remove: - # Fixing this would be a lot of work. - - bugprone-easily-swappable-parameters - # These checks rename our cbinds and consts that have `__` in the name - - bugprone-reserved-identifier - # All of our circuit structs have non-private members - - cppcoreguidelines-non-private-member-variables-in-classes - # We have many `for` loops that violate this part of the bounds safety profile - - cppcoreguidelines-pro-bounds-constant-array-index - # These checks rename our cbinds and consts that have `__` in the name - - cert-dcl37-c - - cert-dcl51-cpp - # We use anon namespaces to import types - - cert-dcl59-cpp - # We use anon namespaces to import types - - google-build-namespaces - # Large diff; we often `use` an entire namespace - - google-readability-avoid-underscore-in-googletest-name - # All of our circuit structs have non-private members - - misc-non-private-member-variables-in-classes - # Huge diff. Not sure we want to use trailing return type - - modernize-use-trailing-return-type - # We like (a == true) in circuits - - readability-simplify-boolean-expr - # `any_of/all_of` loops are not objectively more readable - - readability-use-anyofallof - ---- # this divider is necessary -# Disable some checks for Google Test/Bench -If: - PathMatch: [src/.*\.test\.cpp, src/.*\.bench\.cpp] -Diagnostics: - ClangTidy: - # these checks get triggered by the Google macros - Remove: - # Triggers for globals in tests and TEST macros - - cert-err58-cpp - - cppcoreguidelines-avoid-non-const-global-variables - - cppcoreguidelines-owning-memory - - cppcoreguidelines-special-member-functions - # Triggers on some tests that are not complex - #- readability-function-cognitive-complexity \ No newline at end of file diff --git a/circuits/cpp/.gitignore b/circuits/cpp/.gitignore deleted file mode 100644 index 94b21237eb8..00000000000 --- a/circuits/cpp/.gitignore +++ /dev/null @@ -1,7 +0,0 @@ -.cache/ -build*/ -src/wasi-sdk-* -CMakeUserPresets.json - -# ctest log output dir -**/Testing/* \ No newline at end of file diff --git a/circuits/cpp/.rebuild_patterns b/circuits/cpp/.rebuild_patterns deleted file mode 100644 index d2edf385649..00000000000 --- a/circuits/cpp/.rebuild_patterns +++ /dev/null @@ -1,3 +0,0 @@ -^circuits/.*\.(cpp|cc|cxx|c\+\+|h|hpp|hxx|h\+\+|c|h|inl|inc|ipp|tpp|cmake)$ -^circuits/.*CMakeLists\.txt$ -^circuits/.*Dockerfile.*$ \ No newline at end of file diff --git a/circuits/cpp/CMakeLists.txt b/circuits/cpp/CMakeLists.txt deleted file mode 100644 index fb2b82d6278..00000000000 --- a/circuits/cpp/CMakeLists.txt +++ /dev/null @@ -1,145 +0,0 @@ -# aztec3 circuits package -# copyright 2019 Spilsbury Holdings Ltd - -cmake_minimum_required(VERSION 3.24) - -# Get the full path to barretenberg. This is helpful because the required -# relative path changes based on where in cmake the path is used. -# `BBERG_DIR` must be set before toolchain.cmake is imported because -# `BBERG_DIR` is used in toolchain.cmake to determine `WASI_SDK_PREFIX` -get_filename_component(BBERG_DIR ../barretenberg/cpp - REALPATH BASE_DIR "${CMAKE_BINARY_DIR}") - -project( - Aztec3Circuits - DESCRIPTION "Project containing Aztec3 Circuits Infrastructure in C++." - VERSION 0.1.0 - LANGUAGES CXX C -) - -option(DISABLE_ASM "Disable custom assembly" OFF) -option(DISABLE_ADX "Disable ADX assembly variant" OFF) -option(MULTITHREADING "Enable multi-threading" ON) -option(TESTING "Build tests" ON) -option(ENABLE_ASAN "Address sanitizer for debugging tricky memory corruption" OFF) -option(BENCHMARKS "Build benchmarks" ON) -option(FUZZING "Build fuzzing harnesses" OFF) -option(DISABLE_TBB "Intel Thread Building Blocks" ON) -option(COVERAGE "Enable collecting coverage from tests" OFF) -option(ENABLE_HEAVY_TESTS "Enable heavy tests when collecting coverage" OFF) - -message(STATUS "Building barretenberg for UltraPlonk Composer.") - -if(ENABLE_ASAN) - add_compile_options(-fsanitize=address) - add_link_options(-fsanitize=address) -endif() - -if(CMAKE_SYSTEM_PROCESSOR MATCHES "aarch64" OR CMAKE_SYSTEM_PROCESSOR MATCHES "arm64") - message(STATUS "Compiling for ARM.") - set(ARM ON) - set(DISABLE_ASM ON) - set(DISABLE_ADX ON) - set(RUN_HAVE_STD_REGEX 0) - set(RUN_HAVE_POSIX_REGEX 0) - set(DISABLE_TBB 0) -endif() - -if(FUZZING) - add_definitions(-DFUZZING=1) - - if(DISABLE_CUSTOM_MUTATORS) - add_definitions(-DDISABLE_CUSTOM_MUTATORS=1) - endif() - - set(SANITIZER_OPTIONS "") - - if(ADDRESS_SANITIZER) - set(SANITIZER_OPTIONS ${SANITIZER_OPTIONS} -fsanitize=address) - endif() - - if(UNDEFINED_BEHAVIOUR_SANITIZER) - set(SANITIZER_OPTIONS ${SANITIZER_OPTIONS} -fsanitize=undefined -fno-sanitize=alignment) - endif() - - add_compile_options(-fsanitize=fuzzer-no-link ${SANITIZER_OPTIONS}) - - set(WASM OFF) - set(BENCHMARKS OFF) - set(MULTITHREADING OFF) - set(TESTING OFF) -endif() - -if(CMAKE_SYSTEM_PROCESSOR MATCHES "wasm32") - message(STATUS "Compiling for WebAssembly.") - set(WASM ON) - set(DISABLE_ASM ON) - set(MULTITHREADING OFF) - set(BENCHMARKS OFF) - set(DISABLE_TBB 1) - add_compile_definitions(_WASI_EMULATED_PROCESS_CLOCKS=1) -endif() - -set(CMAKE_C_STANDARD 11) -set(CMAKE_C_EXTENSIONS ON) -set(CMAKE_CXX_STANDARD 20) -set(CMAKE_CXX_STANDARD_REQUIRED TRUE) -set(CMAKE_CXX_EXTENSIONS ON) - -if(CMAKE_CXX_COMPILER_ID MATCHES "Clang") - if (CMAKE_CXX_COMPILER_VERSION VERSION_LESS "10") - message(WARNING "Clang <10 is not supported") - endif() -elseif(CMAKE_CXX_COMPILER_ID MATCHES "GNU") - if (CMAKE_CXX_COMPILER_VERSION VERSION_LESS "10") - message(WARNING "GCC <10 is not supported") - endif() -else() - message(WARNING "Unsupported compiler, use Clang >10 or GCC >10") -endif() - -if(COVERAGE) - - # We've only set up LLVM coverage - if(NOT(CMAKE_CXX_COMPILER_ID MATCHES "Clang")) - message(FATAL_ERROR "Creating coverage is only available for clang") - endif() - - # Get major clang version - string(REPLACE "." ";" VERSION_LIST ${CMAKE_CXX_COMPILER_VERSION}) - list(GET VERSION_LIST 0 CLANG_VERSION_MAJOR) - - # Find llvm-profdata - set(PROFDATA_EXECUTABLE_NAME "llvm-profdata-${CLANG_VERSION_MAJOR}") - find_program(PROFDATA_EXECUTABLE ${PROFDATA_EXECUTABLE_NAME}) - if(PROFDATA_EXECUTABLE MATCHES "NOTFOUND") - message(FATAL_ERROR "Couldn't find ${PROFDATA_EXECUTABLE_NAME}") - endif() - - # Find llvm-cov - set(COV_EXECUTABLE_NAME "llvm-cov-${CLANG_VERSION_MAJOR}") - find_program(COV_EXECUTABLE ${COV_EXECUTABLE_NAME}) - if(COV_EXECUTABLE MATCHES "NOTFOUND") - message(FATAL_ERROR "Couldn't find ${COV_EXECUTABLE_NAME}") - endif() - - # Add profiling compile options and disable optimizations - add_compile_options(-fprofile-instr-generate -fcoverage-mapping -O0) - - # Add a custom target for creating the report - add_custom_target(create_full_coverage_report - COMMAND "${CMAKE_SOURCE_DIR}/scripts/collect_coverage_information.sh" ${PROFDATA_EXECUTABLE} ${COV_EXECUTABLE} - VERBATIM - ) -endif() - -include(cmake/build.cmake) -include(GNUInstallDirs) -include(cmake/arch.cmake) -include(cmake/threading.cmake) -include(cmake/gtest.cmake) -include(cmake/benchmark.cmake) -include(cmake/module.cmake) -include(cmake/msgpack.cmake) - -add_subdirectory(src) diff --git a/circuits/cpp/CMakePresets.json b/circuits/cpp/CMakePresets.json deleted file mode 100644 index 583fa90a9b2..00000000000 --- a/circuits/cpp/CMakePresets.json +++ /dev/null @@ -1,311 +0,0 @@ -{ - "version": 5, - "cmakeMinimumRequired": { - "major": 3, - "minor": 24, - "patch": 0 - }, - "configurePresets": [ - { - "name": "default", - "displayName": "Build with Clang", - "description": "Build with globally installed Clang", - "binaryDir": "build", - "generator": "Ninja", - "environment": { - "CC": "clang", - "CXX": "clang++", - "CMAKE_EXPORT_COMPILE_COMMANDS": "ON" - }, - "cacheVariables": { - "CMAKE_BUILD_TYPE": "Release" - } - }, - { - "name": "homebrew", - "displayName": "Homebrew + Clang", - "description": "Build with Clang installed via Homebrew", - "inherits": "default", - "environment": { - "CC": "$env{BREW_PREFIX}/opt/llvm/bin/clang", - "CXX": "$env{BREW_PREFIX}/opt/llvm/bin/clang++", - "LDFLAGS": "-L$env{BREW_PREFIX}/opt/libomp/lib", - "CPPFLAGS": "-I$env{BREW_PREFIX}/opt/libomp/include" - } - }, - { - "name": "clang15", - "displayName": "Build with Clang-15", - "description": "Build with globally installed Clang-15", - "inherits": "default", - "environment": { - "CC": "clang-15", - "CXX": "clang++-15" - } - }, - { - "name": "clang15-assert", - "displayName": "Build with Clang-15 and assertions enabled", - "description": "Build with globally installed Clang-15 and assertions", - "inherits": "clang15", - "cacheVariables": { - "CMAKE_BUILD_TYPE": "RelWithAssert" - } - }, - { - "name": "clang15-dbg", - "displayName": "Debugging build with Clang-15", - "description": "Build with globally installed Clang-15 in debug mode", - "inherits": "clang15", - "cacheVariables": { - "CMAKE_BUILD_TYPE": "Debug" - } - }, - { - "name": "clang16", - "displayName": "Build with Clang-16", - "description": "Build with globally installed Clang-16", - "inherits": "default", - "environment": { - "CC": "clang-16", - "CXX": "clang++-16" - } - }, - { - "name": "clang16-assert", - "displayName": "Build with Clang-16 and assertions enabled", - "description": "Build with globally installed Clang-16 and assertions", - "inherits": "clang16", - "cacheVariables": { - "CMAKE_BUILD_TYPE": "RelWithAssert" - } - }, - { - "name": "clang16-dbg", - "displayName": "Debugging build with Clang-16", - "description": "Build with globally installed Clang-16 in debug mode", - "inherits": "clang16", - "cacheVariables": { - "CMAKE_BUILD_TYPE": "Debug" - } - }, - { - "name": "gcc", - "displayName": "Build with GCC", - "description": "Build with globally installed GCC", - "inherits": "default", - "environment": { - "CC": "gcc", - "CXX": "g++" - } - }, - { - "name": "gcc10", - "displayName": "Build with GCC-10", - "description": "Build with globally installed GCC-10", - "inherits": "default", - "environment": { - "CC": "gcc-10", - "CXX": "g++-10" - } - }, - { - "name": "bench", - "displayName": "Build benchmarks", - "description": "Build default preset but with a special benchmark directory", - "inherits": "default", - "binaryDir": "build-bench" - }, - { - "name": "fuzzing", - "displayName": "Build with fuzzing", - "description": "Build default preset but with fuzzing enabled", - "inherits": "default", - "binaryDir": "build-fuzzing", - "cacheVariables": { - "FUZZING": "ON" - } - }, - { - "name": "coverage", - "displayName": "Build with coverage", - "description": "Build default preset but with coverage enabled", - "inherits": "default", - "binaryDir": "build-coverage", - "cacheVariables": { - "COVERAGE": "ON", - "CMAKE_BUILD_TYPE": "Debug" - } - }, - { - "name": "wasm", - "displayName": "Build for WASM", - "description": "Build with a specific wasm-sdk to create wasm", - "binaryDir": "build-wasm", - "toolchainFile": "cmake/toolchains/wasm32-wasi.cmake", - "environment": { - "WASI_SDK_PREFIX": "${sourceDir}/barretenberg/cpp/src/wasi-sdk-20.0", - "CC": "$env{WASI_SDK_PREFIX}/bin/clang", - "CXX": "$env{WASI_SDK_PREFIX}/bin/clang++", - "AR": "$env{WASI_SDK_PREFIX}/bin/llvm-ar", - "RANLIB": "$env{WASI_SDK_PREFIX}/bin/llvm-ranlib" - }, - "cacheVariables": { - "CMAKE_SYSROOT": "$env{WASI_SDK_PREFIX}/share/wasi-sysroot", - "CMAKE_STAGING_PREFIX": "$env{WASI_SDK_PREFIX}/share/wasi-sysroot", - "CMAKE_FIND_ROOT_PATH_MODE_PROGRAM": "NEVER", - "CMAKE_FIND_ROOT_PATH_MODE_LIBRARY": "ONLY", - "CMAKE_FIND_ROOT_PATH_MODE_INCLUDE": "ONLY", - "CMAKE_FIND_ROOT_PATH_MODE_PACKAGE": "ONLY", - "CMAKE_C_COMPILER_WORKS": "ON", - "CMAKE_CXX_COMPILER_WORKS": "ON", - "CMAKE_EXE_LINKER_FLAGS": "-O3", - "CMAKE_BUILD_TYPE": "Release" - } - }, - { - "name": "wasm-dbg", - "displayName": "Debugging build for WASM", - "description": "Build in debug mode with a specific wasm-sdk to create wasm", - "inherits": "wasm", - "cacheVariables": { - "CMAKE_BUILD_TYPE": "Debug", - "CMAKE_EXE_LINKER_FLAGS": "" - } - } - ], - "buildPresets": [ - { - "name": "default", - "configurePreset": "default", - "inheritConfigureEnvironment": true, - "jobs": 0 - }, - { - "name": "homebrew", - "inherits": "default", - "configurePreset": "homebrew" - }, - { - "name": "clang15", - "inherits": "default", - "configurePreset": "clang15" - }, - { - "name": "clang15-dbg", - "inherits": "default", - "configurePreset": "clang15-dbg" - }, - { - "name": "clang16", - "inherits": "default", - "configurePreset": "clang16" - }, - { - "name": "clang16-dbg", - "inherits": "default", - "configurePreset": "clang16-dbg" - }, - { - "name": "gcc", - "inherits": "default", - "configurePreset": "gcc" - }, - { - "name": "gcc10", - "inherits": "default", - "configurePreset": "gcc10" - }, - { - "name": "bench", - "inherits": "default", - "configurePreset": "bench" - }, - { - "name": "fuzzing", - "inherits": "default", - "configurePreset": "fuzzing" - }, - { - "name": "coverage", - "inherits": "default", - "configurePreset": "coverage" - }, - { - "name": "wasm", - "configurePreset": "wasm", - "inheritConfigureEnvironment": true, - "jobs": 0 - }, - { - "name": "wasm-dbg", - "inherits": "wasm" - } - ], - "testPresets": [ - { - "name": "default", - "configurePreset": "default", - "inheritConfigureEnvironment": true - }, - { - "name": "homebrew", - "inherits": "default", - "configurePreset": "homebrew" - }, - { - "name": "clang15", - "inherits": "default", - "configurePreset": "clang15" - }, - { - "name": "clang15-dbg", - "inherits": "default", - "configurePreset": "clang15-dbg" - }, - { - "name": "clang16", - "inherits": "default", - "configurePreset": "clang16" - }, - { - "name": "clang16-dbg", - "inherits": "default", - "configurePreset": "clang16-dbg" - }, - { - "name": "gcc", - "inherits": "default", - "configurePreset": "gcc" - }, - { - "name": "gcc10", - "inherits": "default", - "configurePreset": "gcc10" - }, - { - "name": "bench", - "inherits": "default", - "configurePreset": "bench" - }, - { - "name": "fuzzing", - "inherits": "default", - "configurePreset": "fuzzing" - }, - { - "name": "coverage", - "inherits": "default", - "configurePreset": "coverage" - }, - { - "name": "wasm", - "configurePreset": "wasm", - "inheritConfigureEnvironment": true - }, - { - "name": "wasm-dbg", - "inherits": "wasm" - } - ] -} \ No newline at end of file diff --git a/circuits/cpp/barretenberg b/circuits/cpp/barretenberg deleted file mode 120000 index e9b54f1df3e..00000000000 --- a/circuits/cpp/barretenberg +++ /dev/null @@ -1 +0,0 @@ -../../barretenberg \ No newline at end of file diff --git a/circuits/cpp/bootstrap.sh b/circuits/cpp/bootstrap.sh deleted file mode 100755 index 440bf1e44c6..00000000000 --- a/circuits/cpp/bootstrap.sh +++ /dev/null @@ -1,27 +0,0 @@ -#!/usr/bin/env bash -set -eu - -cd "$(dirname "$0")" - -CMD=${1:-} - -if [ -n "$CMD" ]; then - if [ "$CMD" = "clean" ]; then - git clean -fdx - exit 0 - else - echo "Unknown command: $CMD" - exit 1 - fi -fi - -rm -f build-wasm/CMakeCache.txt - -# Build WASM. -if [ -n "${WASM_DEBUG:-}" ] ; then - cmake --preset wasm-dbg - cmake --build --preset wasm-dbg --target aztec3-circuits.wasm -else - cmake --preset wasm - cmake --build --preset wasm --target aztec3-circuits.wasm -fi diff --git a/circuits/cpp/cmake/arch.cmake b/circuits/cpp/cmake/arch.cmake deleted file mode 100644 index 1812e8eb3ed..00000000000 --- a/circuits/cpp/cmake/arch.cmake +++ /dev/null @@ -1,10 +0,0 @@ -if(WASM) - # Disable SLP vectorization on WASM as it's brokenly slow. To give an idea, with this off it still takes - # 2m:18s to compile scalar_multiplication.cpp, and with it on I estimate it's 50-100 times longer. I never - # had the patience to wait it out... - add_compile_options(-fno-exceptions -fno-slp-vectorize) -endif() - -if(NOT WASM AND NOT APPLE AND NOT ARM) - add_compile_options(-march=skylake) -endif() diff --git a/circuits/cpp/cmake/benchmark.cmake b/circuits/cpp/cmake/benchmark.cmake deleted file mode 100644 index c8f90548d66..00000000000 --- a/circuits/cpp/cmake/benchmark.cmake +++ /dev/null @@ -1,25 +0,0 @@ -if(NOT TESTING) - set(BENCHMARKS OFF) -endif() - -if(BENCHMARKS) - include(FetchContent) - - FetchContent_Declare( - benchmark - GIT_REPOSITORY https://github.com/google/benchmark - GIT_TAG v1.7.1 - FIND_PACKAGE_ARGS - ) - - set(BENCHMARK_ENABLE_TESTING OFF CACHE BOOL "Benchmark tests off") - set(BENCHMARK_ENABLE_INSTALL OFF CACHE BOOL "Benchmark installation off") - - FetchContent_MakeAvailable(benchmark) - if(NOT benchmark_FOUND) - # FetchContent_MakeAvailable calls FetchContent_Populate if `find_package` is unsuccessful - # so these variables will be available if we reach this case - set_property(DIRECTORY ${benchmark_SOURCE_DIR} PROPERTY EXCLUDE_FROM_ALL) - set_property(DIRECTORY ${benchmark_BINARY_DIR} PROPERTY EXCLUDE_FROM_ALL) - endif() -endif() diff --git a/circuits/cpp/cmake/build.cmake b/circuits/cpp/cmake/build.cmake deleted file mode 100644 index 3eb2eddd060..00000000000 --- a/circuits/cpp/cmake/build.cmake +++ /dev/null @@ -1,9 +0,0 @@ -if(NOT CMAKE_BUILD_TYPE AND NOT CMAKE_CONFIGURATION_TYPES) - set(CMAKE_BUILD_TYPE "Release" CACHE STRING "Choose the type of build." FORCE) -endif() -message(STATUS "Build type: ${CMAKE_BUILD_TYPE}") - -if(CMAKE_BUILD_TYPE STREQUAL "RelWithAssert") - add_compile_options(-O3) - remove_definitions(-DNDEBUG) -endif() \ No newline at end of file diff --git a/circuits/cpp/cmake/gtest.cmake b/circuits/cpp/cmake/gtest.cmake deleted file mode 100644 index e86bf49756e..00000000000 --- a/circuits/cpp/cmake/gtest.cmake +++ /dev/null @@ -1,51 +0,0 @@ -if(TESTING) - include(GoogleTest) - include(FetchContent) - - FetchContent_Declare( - GTest - GIT_REPOSITORY https://github.com/google/googletest.git - # Version 1.12.1 is not compatible with WASI-SDK 12 - GIT_TAG release-1.10.0 - FIND_PACKAGE_ARGS - ) - - set(BUILD_GMOCK OFF CACHE BOOL "Build with gMock disabled") - set(INSTALL_GTEST OFF CACHE BOOL "gTest installation disabled") - - FetchContent_MakeAvailable(GTest) - - if (NOT GTest_FOUND) - # FetchContent_MakeAvailable calls FetchContent_Populate if `find_package` is unsuccessful - # so these variables will be available if we reach this case - set_property(DIRECTORY ${gtest_SOURCE_DIR} PROPERTY EXCLUDE_FROM_ALL) - set_property(DIRECTORY ${gtest_BINARY_DIR} PROPERTY EXCLUDE_FROM_ALL) - - # Disable all warning when compiling gtest - target_compile_options( - gtest - PRIVATE - -w - ) - - if(WASM) - target_compile_definitions( - gtest - PRIVATE - -DGTEST_HAS_EXCEPTIONS=0 - -DGTEST_HAS_STREAM_REDIRECTION=0 - ) - endif() - - mark_as_advanced( - BUILD_GMOCK BUILD_GTEST BUILD_SHARED_LIBS - gmock_build_tests gtest_build_samples gtest_build_tests - gtest_disable_pthreads gtest_force_shared_crt gtest_hide_internal_symbols - ) - - add_library(GTest::gtest ALIAS gtest) - add_library(GTest::gtest_main ALIAS gtest_main) - endif() - - enable_testing() -endif() diff --git a/circuits/cpp/cmake/module.cmake b/circuits/cpp/cmake/module.cmake deleted file mode 100644 index 725220be58a..00000000000 --- a/circuits/cpp/cmake/module.cmake +++ /dev/null @@ -1,235 +0,0 @@ -# copyright 2020 Spilsbury Holdings -# -# usage: circuits_cmake_module(module_name [dependencies ...]) -# -# Scans for all .cpp files in a subdirectory, and creates a library named . -# Scans for all .test.cpp files in a subdirectory, and creates a gtest binary named _tests. -# Scans for all .bench.cpp files in a subdirectory, and creates a benchmark binary named _bench. -# -# We have to get a bit complicated here, due to the fact CMake will not parallelize the building of object files -# between dependent targets, due to the potential of post-build code generation steps etc. -# To work around this, we create "object libraries" containing the object files. -# Then we declare executables/libraries that are to be built from these object files. -# These assets will only be linked as their dependencies complete, but we can parallelize the compilation at least. - -function(circuits_cmake_module MODULE_NAME) - file(GLOB_RECURSE SOURCE_FILES *.cpp) - file(GLOB_RECURSE HEADER_FILES *.hpp *.tcc) - list(FILTER SOURCE_FILES EXCLUDE REGEX ".*\.(fuzzer|test|bench).cpp$") - - if(SOURCE_FILES) - add_library( - ${MODULE_NAME}_objects - OBJECT - ${SOURCE_FILES} - ) - list(APPEND lib_targets ${MODULE_NAME}_objects) - - add_library( - ${MODULE_NAME} - STATIC - $ - ) - - target_link_libraries( - ${MODULE_NAME} - PUBLIC - ${ARGN} - ${TBB_IMPORTED_TARGETS} - ) - list(APPEND lib_targets ${MODULE_NAME}) - - # enable msgpack downloading via dependency (solves race condition) - add_dependencies(${MODULE_NAME}_objects msgpack-c) - add_dependencies(${MODULE_NAME} msgpack-c) - - set(MODULE_LINK_NAME ${MODULE_NAME}) - endif() - - file(GLOB_RECURSE TEST_SOURCE_FILES *.test.cpp) - if(TESTING AND TEST_SOURCE_FILES) - add_library( - ${MODULE_NAME}_test_objects - OBJECT - ${TEST_SOURCE_FILES} - ) - list(APPEND lib_targets ${MODULE_NAME}_test_objects) - - target_link_libraries( - ${MODULE_NAME}_test_objects - PRIVATE - GTest::gtest - env - ${TBB_IMPORTED_TARGETS} - ) - - add_executable( - ${MODULE_NAME}_tests - $ - ) - list(APPEND exe_targets ${MODULE_NAME}_tests) - - if(WASM) - target_link_options( - ${MODULE_NAME}_tests - PRIVATE - -Wl,-z,stack-size=8388608 - ) - endif() - - if(CI) - target_compile_definitions( - ${MODULE_NAME}_test_objects - PRIVATE - -DCI=1 - ) - endif() - - if((COVERAGE AND NOT ENABLE_HEAVY_TESTS) OR (DISABLE_HEAVY_TESTS)) - # Heavy tests take hours when we are using profiling instrumentation - target_compile_definitions( - ${MODULE_NAME}_test_objects - PRIVATE - -DDISABLE_HEAVY_TESTS=1 - ) - endif() - - target_link_libraries( - ${MODULE_NAME}_tests - PRIVATE - ${MODULE_LINK_NAME} - ${ARGN} - GTest::gtest - GTest::gtest_main - env - ${TBB_IMPORTED_TARGETS} - ) - # enable msgpack downloading via dependency (solves race condition) - add_dependencies(${MODULE_NAME}_test_objects msgpack-c) - add_dependencies(${MODULE_NAME}_tests msgpack-c) - - if(NOT WASM AND NOT CI) - # If collecting coverage data, set profile - # For some reason processor affinity doesn't work, so the developer has to set it manually anyway - if(COVERAGE) - # Profile filename has to be dependent on some process characteristic, because ctest calls all tests individually and the profiles get overwritten - gtest_discover_tests(${MODULE_NAME}_tests - PROPERTIES ENVIRONMENT "LLVM_PROFILE_FILE=${CMAKE_RUNTIME_OUTPUT_DIRECTORY}/profdata/${MODULE_NAME}.%p.profraw" - PROPERTIES PROCESSOR_AFFINITY ON - PROPERTIES PROCESSORS 16 - WORKING_DIRECTORY ${CMAKE_BINARY_DIR}) - else() - # Currently haven't found a way to easily wrap the calls in wasmtime when run from ctest. - # Needed to add `TEST_DISCOVERY_TIMEOUT` to work around: - # ``` - # Error running test executable. - # ... - # Result: Process terminated due to timeout - # ``` - gtest_discover_tests(${MODULE_NAME}_tests WORKING_DIRECTORY ${CMAKE_BINARY_DIR} PROPERTIES TEST_DISCOVERY_TIMEOUT 600) - endif() - endif() - - if(COVERAGE) - target_link_options( - ${MODULE_NAME}_tests - PRIVATE - -fprofile-instr-generate -fcoverage-mapping - ) - add_custom_target( - run_${MODULE_NAME}_tests - COMMAND mkdir -p ${CMAKE_RUNTIME_OUTPUT_DIRECTORY}/profdata - COMMAND LLVM_PROFILE_FILE=${CMAKE_RUNTIME_OUTPUT_DIRECTORY}/profdata/${MODULE_NAME}.%p.profraw ${CMAKE_RUNTIME_OUTPUT_DIRECTORY}/${MODULE_NAME}_tests - BYPRODUCTS ${CMAKE_RUNTIME_OUTPUT_DIRECTORY}/profdata/${MODULE_NAME}.profraw - WORKING_DIRECTORY ${CMAKE_BINARY_DIR} - DEPENDS ${MODULE_NAME}_tests - ) - add_custom_target( - generate_${MODULE_NAME}_tests_coverage - COMMAND ${PROFDATA_EXECUTABLE} merge -sparse ${CMAKE_RUNTIME_OUTPUT_DIRECTORY}/profdata/${MODULE_NAME}.profraw -o ${CMAKE_RUNTIME_OUTPUT_DIRECTORY}/profdata/${MODULE_NAME}.profdata - DEPENDS ${CMAKE_RUNTIME_OUTPUT_DIRECTORY}/profdata/${MODULE_NAME}.profraw - BYPRODUCTS ${CMAKE_RUNTIME_OUTPUT_DIRECTORY}/profdata/${MODULE_NAME}.profdata - WORKING_DIRECTORY ${CMAKE_BINARY_DIR} - ) - else() - add_custom_target( - run_${MODULE_NAME}_tests - COMMAND ${MODULE_NAME}_tests - WORKING_DIRECTORY ${CMAKE_BINARY_DIR} - ) - endif() - endif() - - file(GLOB_RECURSE FUZZERS_SOURCE_FILES *.fuzzer.cpp) - if(FUZZING AND FUZZERS_SOURCE_FILES) - foreach(FUZZER_SOURCE_FILE ${FUZZERS_SOURCE_FILES}) - get_filename_component(FUZZER_NAME_STEM ${FUZZER_SOURCE_FILE} NAME_WE) - add_executable( - ${MODULE_NAME}_${FUZZER_NAME_STEM}_fuzzer - ${FUZZER_SOURCE_FILE} - ) - list(APPEND exe_targets ${MODULE_NAME}_${FUZZER_NAME_STEM}_fuzzer) - - target_link_options( - ${MODULE_NAME}_${FUZZER_NAME_STEM}_fuzzer - PRIVATE - "-fsanitize=fuzzer" - ${SANITIZER_OPTIONS} - ) - - target_link_libraries( - ${MODULE_NAME}_${FUZZER_NAME_STEM}_fuzzer - PRIVATE - ${MODULE_LINK_NAME} - env - ) - # enable msgpack downloading via dependency (solves race condition) - add_dependencies(${MODULE_NAME}_${FUZZER_NAME_STEM}_fuzzer msgpack-c) - endforeach() - endif() - - file(GLOB_RECURSE BENCH_SOURCE_FILES *.bench.cpp) - if(BENCHMARKS AND BENCH_SOURCE_FILES) - add_library( - ${MODULE_NAME}_bench_objects - OBJECT - ${BENCH_SOURCE_FILES} - ) - list(APPEND lib_targets ${MODULE_NAME}_bench_objects) - - target_link_libraries( - ${MODULE_NAME}_bench_objects - PRIVATE - benchmark::benchmark - env - ${TBB_IMPORTED_TARGETS} - ) - - add_executable( - ${MODULE_NAME}_bench - $ - ) - list(APPEND exe_targets ${MODULE_NAME}_bench) - - target_link_libraries( - ${MODULE_NAME}_bench - PRIVATE - ${MODULE_LINK_NAME} - ${ARGN} - benchmark::benchmark - env - ${TBB_IMPORTED_TARGETS} - ) - - add_custom_target( - run_${MODULE_NAME}_bench - COMMAND ${MODULE_NAME}_bench - WORKING_DIRECTORY ${CMAKE_BINARY_DIR} - ) - add_dependencies(${MODULE_NAME}_bench_objects msgpack-c) - add_dependencies(${MODULE_NAME}_bench msgpack-c) - endif() - - set(${MODULE_NAME}_lib_targets ${lib_targets} PARENT_SCOPE) - set(${MODULE_NAME}_exe_targets ${exe_targets} PARENT_SCOPE) -endfunction() diff --git a/circuits/cpp/cmake/msgpack.cmake b/circuits/cpp/cmake/msgpack.cmake deleted file mode 100644 index 8ce70891552..00000000000 --- a/circuits/cpp/cmake/msgpack.cmake +++ /dev/null @@ -1,16 +0,0 @@ -include(ExternalProject) - -# External project: Download msgpack-c from GitHu -set(MSGPACK_PREFIX "${CMAKE_BINARY_DIR}/_deps/msgpack-c") -set(MSGPACK_INCLUDE "${MSGPACK_PREFIX}/src/msgpack-c/include") - -ExternalProject_Add( - msgpack-c - PREFIX ${MSGPACK_PREFIX} - GIT_REPOSITORY "https://github.com/AztecProtocol/msgpack-c.git" - GIT_TAG af447c28f0bafe761290a72754212436e530941b - CONFIGURE_COMMAND "" # No configure step - BUILD_COMMAND "" # No build step - INSTALL_COMMAND "" # No install step - UPDATE_COMMAND "" # No update step -) \ No newline at end of file diff --git a/circuits/cpp/cmake/threading.cmake b/circuits/cpp/cmake/threading.cmake deleted file mode 100644 index ff60f240a16..00000000000 --- a/circuits/cpp/cmake/threading.cmake +++ /dev/null @@ -1,37 +0,0 @@ -if(MULTITHREADING) - message(STATUS "Multithreading is enabled.") - add_compile_options(-pthread) - add_link_options(-pthread) - if(WASM) - add_compile_options(--target=wasm32-wasi-threads) - add_link_options(--target=wasm32-wasi-threads) - endif() - #add_compile_options(-fsanitize=thread) - #add_link_options(-fsanitize=thread) -else() - message(STATUS "Multithreading is disabled.") - add_definitions(-DNO_MULTITHREADING) - set(OMP_MULTITHREADING OFF) -endif() - -if(OMP_MULTITHREADING) - find_package(OpenMP REQUIRED) - message(STATUS "OMP multithreading is enabled.") - link_libraries(OpenMP::OpenMP_CXX) -else() - message(STATUS "OMP multithreading is disabled.") - add_definitions(-DNO_OMP_MULTITHREADING) -endif() - -if(DISABLE_TBB) - message(STATUS "Intel Thread Building Blocks is disabled.") - add_definitions(-DNO_TBB) -else() - find_package(TBB QUIET OPTIONAL_COMPONENTS tbb) - if(${TBB_FOUND}) - message(STATUS "Intel Thread Building Blocks is enabled.") - else() - message(STATUS "Could not locate TBB.") - add_definitions(-DNO_TBB) - endif() -endif() diff --git a/circuits/cpp/cmake/toolchains/aarch64-darwin.cmake b/circuits/cpp/cmake/toolchains/aarch64-darwin.cmake deleted file mode 100644 index 227680938b4..00000000000 --- a/circuits/cpp/cmake/toolchains/aarch64-darwin.cmake +++ /dev/null @@ -1,11 +0,0 @@ -set(CMAKE_SYSTEM_NAME Darwin) -set(CMAKE_SYSTEM_PROCESSOR aarch64) - -if(CMAKE_CXX_COMPILER_ID MATCHES "Clang") - # Clang allows us to cross compile on Mac - # so we explicitly specify the arch to the compiler - # If you just select the arch toolchain and are on an - # x86_64, it will compile for x86_64 mac. - set(CMAKE_C_FLAGS "${CMAKE_C_FLAGS} -arch arm64") - set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -arch arm64") -endif() diff --git a/circuits/cpp/cmake/toolchains/aarch64-linux.cmake b/circuits/cpp/cmake/toolchains/aarch64-linux.cmake deleted file mode 100644 index d79a26e90fd..00000000000 --- a/circuits/cpp/cmake/toolchains/aarch64-linux.cmake +++ /dev/null @@ -1,2 +0,0 @@ -set(CMAKE_SYSTEM_NAME Linux) -set(CMAKE_SYSTEM_PROCESSOR aarch64) diff --git a/circuits/cpp/cmake/toolchains/i386-linux.cmake b/circuits/cpp/cmake/toolchains/i386-linux.cmake deleted file mode 100644 index db424698ee8..00000000000 --- a/circuits/cpp/cmake/toolchains/i386-linux.cmake +++ /dev/null @@ -1,7 +0,0 @@ -set(CMAKE_SYSTEM_NAME Linux) -set(CMAKE_SYSTEM_PROCESSOR i386) - -add_compile_options("-m32") -add_link_options("-m32") -set(MULTITHREADING OFF) -add_definitions(-DDISABLE_SHENANIGANS=1) diff --git a/circuits/cpp/cmake/toolchains/wasm32-wasi.cmake b/circuits/cpp/cmake/toolchains/wasm32-wasi.cmake deleted file mode 100644 index 17e19376ed8..00000000000 --- a/circuits/cpp/cmake/toolchains/wasm32-wasi.cmake +++ /dev/null @@ -1,3 +0,0 @@ -set(CMAKE_SYSTEM_NAME Generic) -set(CMAKE_SYSTEM_VERSION 1) -set(CMAKE_SYSTEM_PROCESSOR wasm32) diff --git a/circuits/cpp/cmake/toolchains/x86_64-darwin.cmake b/circuits/cpp/cmake/toolchains/x86_64-darwin.cmake deleted file mode 100644 index 47b58dcc854..00000000000 --- a/circuits/cpp/cmake/toolchains/x86_64-darwin.cmake +++ /dev/null @@ -1,11 +0,0 @@ -set(CMAKE_SYSTEM_NAME Darwin) -set(CMAKE_SYSTEM_PROCESSOR x86_64) - -if(CMAKE_CXX_COMPILER_ID MATCHES "Clang") - # Clang allows us to cross compile on Mac - # so we explicitly specify the arch to the compiler - # If you just select the x86_64 toolchain and are on an - # M1/arm64 mac, it will compile for arm64. - set(CMAKE_C_FLAGS "${CMAKE_C_FLAGS} -arch x86_64") - set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -arch x86_64") -endif() diff --git a/circuits/cpp/cmake/toolchains/x86_64-linux.cmake b/circuits/cpp/cmake/toolchains/x86_64-linux.cmake deleted file mode 100644 index 69c1e6d234a..00000000000 --- a/circuits/cpp/cmake/toolchains/x86_64-linux.cmake +++ /dev/null @@ -1,2 +0,0 @@ -set(CMAKE_SYSTEM_NAME Linux) -set(CMAKE_SYSTEM_PROCESSOR x86_64) diff --git a/circuits/cpp/format.sh b/circuits/cpp/format.sh deleted file mode 100755 index 54047bd6def..00000000000 --- a/circuits/cpp/format.sh +++ /dev/null @@ -1,21 +0,0 @@ -#!/usr/bin/env bash -set -eu - -if [ "${1:-}" == "staged" ]; then - echo Formatting circuits staged files... - for FILE in $(git diff-index --diff-filter=d --relative --cached --name-only HEAD | grep -e '\.\(cpp\|hpp\|tcc\)$'); do - clang-format -i $FILE - sed -i.bak 's/\r$//' $FILE && rm ${FILE}.bak - git add $FILE - done -elif [ -n "${1:-}" ]; then - for FILE in $(git diff-index --relative --name-only $1 | grep -e '\.\(cpp\|hpp\|tcc\)$'); do - clang-format -i $FILE - sed -i.bak 's/\r$//' $FILE && rm ${FILE}.bak - done -else - for FILE in $(find ./{src,barretenberg} -iname *.hpp -o -iname *.cpp -o -iname *.tcc | grep -v src/boost); do - clang-format -i $FILE - sed -i.bak 's/\r$//' $FILE && rm ${FILE}.bak - done -fi diff --git a/circuits/cpp/scripts/a3-tests b/circuits/cpp/scripts/a3-tests deleted file mode 100644 index ed332ef3457..00000000000 --- a/circuits/cpp/scripts/a3-tests +++ /dev/null @@ -1,4 +0,0 @@ -aztec3_circuits_abis_tests -aztec3_circuits_apps_tests -aztec3_circuits_kernel_tests -aztec3_circuits_rollup_tests \ No newline at end of file diff --git a/circuits/cpp/scripts/build_run_tests_docker_local b/circuits/cpp/scripts/build_run_tests_docker_local deleted file mode 100755 index f861fecbfa8..00000000000 --- a/circuits/cpp/scripts/build_run_tests_docker_local +++ /dev/null @@ -1,41 +0,0 @@ -#!/usr/bin/env bash -[ -n "${BUILD_SYSTEM_DEBUG:-}" ] && set -x # conditionally trace -set -eu - -# To be called only LOCALLY for testing WITH docker. -# Builds a docker image and runs tests in it. -# -# Run from circuits/cpp/ -# Example -# ./scripts/build_run_tests_docker_local 1 x86_64 glob -*.circuit* - -############################################################################### -# ARGS -############################################################################### -NUM_TRANSCRIPTS=$1 # integer (MANDATORY) -ARCH=$2 # x86_64 or wasm (MUST BE LOWERCASE) (MANDATORY) used in `run_tests_local` -# TESTS=$3 (MANDATORY) used in `run_tests_local` -# GTEST_FILTER=$4 (optional) used in `run_tests_local` -# *** See `run_tests_local` for the args forwarded to that script - -shift # arg1 (num transcripts) is not forwarded to `run_tests_local` - -# END ARGS -############################################################################### - -DOCKERFILE=dockerfiles/Dockerfile.${ARCH}-linux-clang-assert -IMAGE_URI=local-testing/circuits-${ARCH}-linux-clang-assert:latest - -# build docker image -time docker build -f $DOCKERFILE -t $IMAGE_URI . - -# run tests in docker image -RUN_ARGS="$@" # helper var necessary for some reason to pass all args to docker run -time docker run --rm -t $IMAGE_URI /bin/sh -c "\ - set -xe; \ - cd /usr/src/barretenberg/cpp/srs_db; \ - ln -sf /usr/src/barretenberg/cpp/srs_db /usr/src/circuits/cpp/srs_db; \ - ./download_ignition.sh $NUM_TRANSCRIPTS; \ - cd /usr/src/circuits/cpp; \ - export PATH=\$PATH:~/.wasmtime/bin/; \ - ./scripts/run_tests_local $RUN_ARGS;" diff --git a/circuits/cpp/scripts/collect_coverage_information.sh b/circuits/cpp/scripts/collect_coverage_information.sh deleted file mode 100755 index 45ad2764a22..00000000000 --- a/circuits/cpp/scripts/collect_coverage_information.sh +++ /dev/null @@ -1,75 +0,0 @@ -#!/usr/bin/env bash - -# Check that the correct number of args have been provided -if [ $# -ne 2 ]; then - echo "Usage: $0 " - exit 1 -fi - -# Check that the llvm-profdata command exists -llvm_profdata_command="$1" -if ! command -v "$llvm_profdata_command" >/dev/null; then - echo "$llvm_profdata_command could not be found" - exit 1 -fi - -# Check that the llvm-cov command exists -llvm_cov_command="$2" -if ! command -v "$llvm_cov_command" >/dev/null; then - echo "$llvm_cov_command could not be found" - exit 1 -fi - -# Check for existence of test binaries -WORKING_DIRECTORY=$(pwd) -if [ ! -d "$WORKING_DIRECTORY/bin" ]; then - echo "No binary directory. Are you sure that you are in a build directory and you've compiled binaries?" - exit 1 -fi - -# Check for existence of profdata files, run make sure tests have been run and compiled with Debug and coverage flags enabled -if [ ! -d "$WORKING_DIRECTORY/bin/profdata" ]; then - echo "No profdata directory. Have you run any tests with profiling?" - exit 1 -fi - -# Find non empty profiles and store them in an array -declare -A non_empty_profiles -for profdata_file in $(ls "$WORKING_DIRECTORY/bin/profdata"); do - if [ -s "$WORKING_DIRECTORY/bin/profdata/$profdata_file" ]; then - non_empty_profiles["${profdata_file%%.*}"]=1 - fi -done -if [ ${#non_empty_profiles[@]} -eq 0 ]; then - echo "No profiles found" - exit 1 -fi - -# If there is one profile, output it into its coverate report into its own folder -if [ ${#non_empty_profiles[@]} -eq 1 ]; then - mkdir -p "$WORKING_DIRECTORY/merged_profdata/" - rm -f "$WORKING_DIRECTORY/merged_profdata/default.profdata" - $llvm_profdata_command merge -sparse "$WORKING_DIRECTORY/bin/profdata/${!non_empty_profiles[@]}."*.profraw -o "$WORKING_DIRECTORY/merged_profdata/default.profdata" - rm -rf "$WORKING_DIRECTORY/${non_empty_profiles[0]}_coverage_report" - mkdir "$WORKING_DIRECTORY/${non_empty_profiles[0]}_coverage_report" - $llvm_cov_command show -output-dir="$WORKING_DIRECTORY/${!non_empty_profiles[@]}_coverage_report" -format=html "$WORKING_DIRECTORY/bin/${!non_empty_profiles[@]}_tests" -instr-profile="$WORKING_DIRECTORY/merged_profdata/default.profdata" -ignore-filename-regex=".*_deps.*" -fi - -# If there are many reports, output all of the coverage reports into one folder -if [ ${#non_empty_profiles[@]} -gt 1 ]; then - mkdir -p "$WORKING_DIRECTORY/merged_profdata/" - rm -f "$WORKING_DIRECTORY/merged_profdata/default.profdata" - - # Merge related profdata files into one file named default.profdata - $llvm_profdata_command merge -sparse "$WORKING_DIRECTORY/bin/profdata/"*.profraw -o "$WORKING_DIRECTORY/merged_profdata/default.profdata" - additional_objects="" - for non_empty_profile_base in "${!non_empty_profiles[@]}"; do - additional_objects+="-object $WORKING_DIRECTORY/bin/${non_empty_profile_base}_tests " - done - object_string=${additional_objects#"-object"} - - # Output the coverage report into `all_tests_coverage_report` folder - rm -rf "$WORKING_DIRECTORY/all_tests_coverage_report" - mkdir "$WORKING_DIRECTORY/all_tests_coverage_report" - $llvm_cov_command show -output-dir="$WORKING_DIRECTORY/all_tests_coverage_report" -format=html $object_string -instr-profile="$WORKING_DIRECTORY/merged_profdata/default.profdata" -ignore-filename-regex=".*_deps.*" -fi \ No newline at end of file diff --git a/circuits/cpp/scripts/run_coverage b/circuits/cpp/scripts/run_coverage deleted file mode 100755 index ae40b20cda4..00000000000 --- a/circuits/cpp/scripts/run_coverage +++ /dev/null @@ -1,77 +0,0 @@ -#!bin/bash -[ -n "${BUILD_SYSTEM_DEBUG:-}" ] && set -x # conditionally trace -set -eu - -# To be called LOCALLY for testing WITHOUT docker. -# -# Contains core logic for collecting test coverage in the current environment. -# -# Run from circuits/cpp/ -# Example: -# ./scripts/run_coverage glob -# -# Running with the CLEAN environment variable set will delete the build-coverage folder and start again. -# Running with the CLEAR_COV environment variable set will clear existing vscode coverage data (lcov.info). -# -# This will create two outputs: lcov.info and an html coverage report in build-coverage/coverage_report -# -# Note: Assumes ignition SRS transcripts have already been downloaded - -# TESTS: -# 1. names of test executables to run -# - (like "aztec3_circuits_kernel_tests aztec3_circuits_abis_tests") -# 2. or "glob" (LOWERCASE) to run all built tests -# 3. or a file containing a list of test -TESTS=$1 - -# Set coverage build directory -BUILD_DIR="build-coverage" - -# If the clean file is set, wipe all existing coverage data -if [ -n "${CLEAN:-}" ]; then - echo "The CLEAN environment variable is set. Clearing existing coverage data." - rm -rf ./$BUILD_DIR || true -fi - -# Perform coverage build -echo "Setting cmake to run with preset coverage" -cmake -DUSE_TURBO=true --preset=coverage . - -# if TESTS is GLOB or empty, run all built tests -# if TESTS is a file, assume it contains a list of tests to run -# otherwise assume TESTS itself is a list of tests to run -if [ "$TESTS" = "glob" ] || [ -z "$TESTS" ]; then - echo " No test file specified. Building all test files." - TESTS=$(cat ./scripts/a3-tests) -elif [ -f "$TESTS" ]; then - TESTS=$(cat $TESTS | tr '\n' ' ') -fi - -# if just the CLEAR_COV env var is set then we will clear existing coveage report information -if [ -n "${CLEAR_COV:-}" ]; then - echo "The CLEAR_COV environment variable is set. Clearing existing coverage data." - rm ./$BUILD_DIR/lcov.info || true - rm ./$BUILD_DIR/merged_profdata/default.profdata || true - rm ./$BUILD_DIR/default.profraw || true - rm -rf ./$BUILD_DIR/bin/profdata || true -fi - -for BIN in $TESTS; do - echo "*** Building $BIN ***" - cmake --build --preset coverage --target $BIN -done - -echo "Testing $BIN" -echo "*** Collecting Profiling Information ***" -ctest --test-dir $BUILD_DIR || true # Ctest will fail here if there are unbuilt tests, but we would like to continue to generate reports -echo "Completed profiling" - -echo "*** Generating Coverage Reports ***" -cmake --build --preset coverage --target create_full_coverage_report - -echo "*** Generating VScode line coverage data ***" -for BIN in $TESTS; do - echo "Generating line coverage data for $BIN" - llvm-cov-15 export ./$BUILD_DIR/bin/$BIN --format=lcov -instr-profile="./$BUILD_DIR/merged_profdata/default.profdata" > $BUILD_DIR/lcov.info -done - diff --git a/circuits/cpp/scripts/run_tests b/circuits/cpp/scripts/run_tests deleted file mode 100755 index 62fbd90153b..00000000000 --- a/circuits/cpp/scripts/run_tests +++ /dev/null @@ -1,43 +0,0 @@ -#!/usr/bin/env bash -[ -n "${BUILD_SYSTEM_DEBUG:-}" ] && set -x # conditionally trace -set -eu - -# To be called from CI for testing with docker and AWS. -# Can't be called locally unless AWS credentials are set up. -# -# Call from config.yml -# Example: -# command: cond_spot_run_tests circuits-wasm-linux-clang-assert 1 wasm scripts/a3-tests -*.skip*:*.circuit* - - -############################################################################### -# ARGS -############################################################################### -NUM_TRANSCRIPTS=$1 # integer (MANDATORY) -ARCH=$2 # x86_64 or wasm (MUST BE LOWERCASE) (MANDATORY) used in `run_tests_local` -# TESTS=$3 (MANDATORY) used in `run_tests_local` -# GTEST_FILTER=$4 (optional) used in `run_tests_local` -# *** See `run_tests_local` for the args forwarded to that script - -shift # arg1 (num transcripts) is not forwarded to `run_tests_local` - -# END ARGS -############################################################################### - -$(aws ecr get-login --region us-east-2 --no-include-email) 2> /dev/null - -REPOSITORY="circuits-$ARCH-linux-clang-assert" -IMAGE_URI=$($(git rev-parse --show-toplevel)/build-system/scripts/calculate_image_uri $REPOSITORY) -$(git rev-parse --show-toplevel)/build-system/scripts/retry docker pull $IMAGE_URI - -# run tests in docker image -RUN_ARGS="$@" # helper var necessary for some reason to pass all args to docker run -time docker run --rm -t $IMAGE_URI /bin/sh -c "\ - set -xe; \ - mv /usr/src/barretenberg /usr/src/circuits/cpp/; \ - cd /usr/src/circuits/cpp/barretenberg/cpp/srs_db; \ - ./download_ignition.sh $NUM_TRANSCRIPTS; \ - cd /usr/src/circuits/cpp; \ - ln -sf /usr/src/circuits/cpp/barretenberg/cpp/srs_db /usr/src/circuits/cpp/srs_db; \ - export PATH=\$PATH:~/.wasmtime/bin/; \ - ./scripts/run_tests_local $RUN_ARGS;" diff --git a/circuits/cpp/scripts/run_tests_local b/circuits/cpp/scripts/run_tests_local deleted file mode 100755 index 4c12807414e..00000000000 --- a/circuits/cpp/scripts/run_tests_local +++ /dev/null @@ -1,76 +0,0 @@ -#!/usr/bin/env bash -[ -n "${BUILD_SYSTEM_DEBUG:-}" ] && set -x # conditionally trace -set -eu - -DIR="$(dirname "$0")" - -# To be called LOCALLY for testing WITHOUT docker. -# Also serves as a core/helper script called by -# `run_tests` and `build_run_tests_docker_local` -# when testing WITH docker. -# -# Contains core logic for running tests in the current environment. -# -# Run from circuits/cpp/ -# Example: -# ./scripts/run_tests_local x86_64 glob -*.circuit* -# -# Note: Assumes ignition SRS transcripts have already been downloaded - -############################################################################### -# ARGS -############################################################################### -# ARCH: x86_64 or wasm (MUST BE LOWERCASE) -ARCH=$1 -# TESTS: -# 1. names of test executables to run -# - (like "aztec3_circuits_kernel_tests aztec3_circuits_abis_tests") -# 2. or "glob" (LOWERCASE) to run all built tests -# 3. or a file containing a list of test -TESTS=$2 -# GTEST_FILTER: optional pattern like "*native*" to filter gtests -GTEST_FILTER=${3:-} - -# Shift away the args -shift -shift -if [ -n "$GTEST_FILTER" ]; then - shift -fi -# END ARGS -############################################################################### - -if [ "$ARCH" != "wasm" ]; then - # x86_64 / anything other than wasm - BUILD_DIR=$DIR/../build -else - BUILD_DIR=$DIR/../build-wasm -fi - -# if TESTS is GLOB or empty, run all built tests -# if TESTS is a file, assume it contains a list of tests to run -# otherwise assume TESTS itself is a list of tests to run -if [ "$TESTS" == "glob" ] || [ -z "$TESTS" ]; then - echo " No test file specified. Globbing $BUILD_DIR/bin for tests." - TESTS=$(cd $BUILD_DIR/bin && find -type f -executable | grep "test" | tr '\n' ' ') -elif [ -f "$TESTS" ]; then - TESTS=$(cat $TESTS | tr '\n' ' ') -fi - -echo "*** Running $ARCH tests${GTEST_FILTER:+ (with filter: $GTEST_FILTER)}: $TESTS ***" - -cd $BUILD_DIR; -for BIN in $TESTS; do - echo "Running tests from executable '$BIN'" - if [ "$ARCH" != "wasm" ]; then - # run test executables directly - # if gtest filter is non-empty, use it - # gtest filter is a command line arg to native executable - ./bin/$BIN ${GTEST_FILTER:+--gtest_filter=$GTEST_FILTER} $@ - else - # run test executables via wasmtime - # if gtest filter is non-empty, use it - # gtest filter is an env var to wasmtime - wasmtime --dir .. ./bin/$BIN --env ${GTEST_FILTER:+--env GTEST_FILTER=$GTEST_FILTER} --env GTEST_COLOR=1 - fi -done diff --git a/circuits/cpp/scripts/tidy.sh b/circuits/cpp/scripts/tidy.sh deleted file mode 100755 index 7faab2658c6..00000000000 --- a/circuits/cpp/scripts/tidy.sh +++ /dev/null @@ -1,105 +0,0 @@ -#!/usr/bin/env bash -set -eu - -# Run clang-tidy on all C++ source files -# -# CMake must be configured for clang15 before running this script: -# cmake --preset clang15 -# -# Run examples (from circuits/cpp) -# ./scripts/tidy.sh -# or -# ./scripts/tidy.sh fix - -############################################################################### -# ARGS -############################################################################### -# FIX: or fix (MUST BE LOWERCASE) -# if 'fix', apply fixes to actual source file -# else, print warnings -FIX=$1 -# END ARGS -############################################################################### - -cmake --preset default -# Ensure we have downloaded msgpack headers -cmake --build --preset default --target msgpack-c -# CMake build dir which should contain `compile_commands.json` -BUILD_DIR=build -echo "*************************************************************************" -if [ "$FIX" == "fix" ]; then - echo "Tidy all source files - fix and format the files themselves" - FIX_OPT="-fix -format" -else - EXPORT_DIR=$BUILD_DIR/tidy-fixes - # Clean old tidy fixes - rm -f $EXPORT_DIR/* - mkdir -p $EXPORT_DIR - YAML_FILE=$EXPORT_DIR/tidy-all.yaml - - # run tidy on each source file and export fixes to yaml files - echo "Checking tidy on all source files and exporting fixes to directory: $EXPORT_DIR" - FIX_OPT="-export-fixes $YAML_FILE" - echo "To apply these fixes later, run the following from circuits/cpp:" - echo " clang-apply-replacements --format --style file $EXPORT_DIR" -fi - - -# find all C++ source files to tidy up -SOURCES=$(\ - find src/aztec3/ \ - -name *.cpp \ - -o -name *.hpp \ - -o -name *.cxx \ - -o -name *.hxx \ - -o -name *.tpp \ - -o -name *.cc \ - -o -name *.hh \ - -o -name *.c \ - -o -name *.h \ - | LC_ALL=C sort \ -) - -# MD5 of all source files before running clang-tidy -BEFORE_MD5=$( - for src in ${SOURCES[@]}; do - md5sum $src - done | md5sum) -echo "Before running clang-tidy, MD5 of all C++ files was: $BEFORE_MD5" -echo "*************************************************************************" - -# Need run-clang-tidy version 15, but it doesn't have a --version flag -RUN_TIDY=$(which run-clang-tidy-15 || which run-clang-tidy || which run-clang-tidy-mp-15) -# tidy all sources -$RUN_TIDY -p $BUILD_DIR $SOURCES $FIX_OPT -use-color || {\ - echo "Errors encountered when running clang-tidy!" && - echo "Check the output above before trying again." && \ - exit 1; -} - -echo "*************************************************************************" -if [ "$FIX" == "fix" ]; then - # MD5 of all source files after running clang-tidy - AFTER_MD5=$( - for src in ${SOURCES[@]}; do - md5sum $src - done | md5sum) - echo "AFTER running clang-tidy, MD5 of all C++ files was: $AFTER_MD5" - - echo "$BEFORE_MD5 ?= $AFTER_MD5" - if [ "$BEFORE_MD5" == "$AFTER_MD5" ]; then - echo "No tidying necessary!" - exit 0 - else - echo "WARNING: Some tidying was necessary!" - echo "If you are seeing this in CI, run the following" - echo "in circuits/cpp to tidy up the C++:" - echo " ./scripts/tidy.sh fix" - exit 1 - fi -else - echo "Reminder!" - echo "To apply these fixes later, run the following from circuits/cpp:" - echo " clang-apply-replacements --format --style file $EXPORT_DIR" -fi -echo "*************************************************************************" diff --git a/circuits/cpp/src/CMakeLists.txt b/circuits/cpp/src/CMakeLists.txt deleted file mode 100644 index a29ea8c7129..00000000000 --- a/circuits/cpp/src/CMakeLists.txt +++ /dev/null @@ -1,29 +0,0 @@ -set(CMAKE_RUNTIME_OUTPUT_DIRECTORY ${CMAKE_BINARY_DIR}/bin) - -add_compile_options(-Werror -Wall -Wextra -Wconversion -Wsign-conversion -Wno-deprecated -Wno-tautological-compare -Wfatal-errors) - -if(CMAKE_CXX_COMPILER_ID MATCHES "Clang") - add_compile_options(-fconstexpr-steps=100000000) - if(MEMORY_CHECKS) - message(STATUS "Compiling with memory checks.") - set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -fsanitize=address") - endif() -endif() - -if(CMAKE_CXX_COMPILER_ID MATCHES "GNU") - add_compile_options(-fconstexpr-ops-limit=100000000) -endif() - -include_directories(${CMAKE_SOURCE_DIR}/barretenberg/cpp/src) -include_directories(${CMAKE_CURRENT_SOURCE_DIR} ${MSGPACK_INCLUDE}) - -# I feel this should be limited to ecc, however it's currently used in headers that go across libraries, -# and there currently isn't an easy way to inherit the DDISABLE_SHENANIGANS parameter. -if(DISABLE_ASM) - message(STATUS "Using fallback non-assembly methods for field multiplications.") - add_definitions(-DDISABLE_SHENANIGANS=1) -else() - message(STATUS "Using optimized assembly for field arithmetic.") -endif() - -add_subdirectory(aztec3) \ No newline at end of file diff --git a/circuits/cpp/src/aztec3/CMakeLists.txt b/circuits/cpp/src/aztec3/CMakeLists.txt deleted file mode 100644 index 27ddfeda761..00000000000 --- a/circuits/cpp/src/aztec3/CMakeLists.txt +++ /dev/null @@ -1,33 +0,0 @@ -if (WASM) - link_directories(${CMAKE_SOURCE_DIR}/barretenberg/cpp/build-wasm/lib) -else() - link_directories(${CMAKE_SOURCE_DIR}/barretenberg/cpp/build/lib) -endif() - -add_subdirectory(circuits) -add_subdirectory(oracle) -add_subdirectory(dbs) -add_subdirectory(utils) - -if (WASM) - # We can't build a wasm module by just linking to the libraries as that produces, nothing. - # There are a couple of other ways to avoiding listing all the object files here and leveraging the dependency - # tree, but they come with the problem that they will import the 'env' object files. We explicitly want to avoid - # that as functions in 'env' should be implemented in JS itself. - # It turns out that just explicitly telling the wasm module which object files to include was easiest. - add_executable( - aztec3-circuits.wasm - $ - $ - $ - $ - ) - target_link_libraries(aztec3-circuits.wasm barretenberg wasi) - - target_link_options( - aztec3-circuits.wasm - PRIVATE - # TODO revisit implications of whole-archive - -nostartfiles -Wl,--whole-archive -Wl,--no-entry -Wl,--export-dynamic -Wl,--import-memory -Wl,--allow-undefined -Wl,--stack-first -Wl,-z,stack-size=1048576 - ) -endif() diff --git a/circuits/cpp/src/aztec3/circuits/CMakeLists.txt b/circuits/cpp/src/aztec3/circuits/CMakeLists.txt deleted file mode 100644 index ae3cfdda297..00000000000 --- a/circuits/cpp/src/aztec3/circuits/CMakeLists.txt +++ /dev/null @@ -1,5 +0,0 @@ -add_subdirectory(apps) -add_subdirectory(abis) -add_subdirectory(kernel) -add_subdirectory(rollup) -add_subdirectory(recursion) \ No newline at end of file diff --git a/circuits/cpp/src/aztec3/circuits/abis/.test.cpp b/circuits/cpp/src/aztec3/circuits/abis/.test.cpp deleted file mode 100644 index 757531113ef..00000000000 --- a/circuits/cpp/src/aztec3/circuits/abis/.test.cpp +++ /dev/null @@ -1,123 +0,0 @@ -#include "index.hpp" -#include "previous_kernel_data.hpp" - -#include "aztec3/circuits/abis/combined_accumulated_data.hpp" - -#include - -#include - -namespace { -// Builder -using Builder = UltraCircuitBuilder; - -// Types -using CT = aztec3::utils::types::CircuitTypes; -using NT = aztec3::utils::types::NativeTypes; -} // namespace - -namespace aztec3::circuits::abis { - -class abi_tests : public ::testing::Test {}; - -TEST(abi_tests, msgpack_schema_smoke_test) -{ - // Just exercise these to make sure they don't error - // They will test for any bad serialization methods - msgpack_schema_to_string(CombinedAccumulatedData{}); - CombinedAccumulatedData cad; - EXPECT_EQ(msgpack::check_msgpack_method(cad), ""); -} - -TEST(abi_tests, native_read_write_call_context) -{ - CallContext const call_context = { - .msg_sender = 1, - .storage_contract_address = 2, - .portal_contract_address = 3, - .function_selector = { .value = 1 }, - .is_delegate_call = false, - .is_static_call = false, - .is_contract_deployment = false, - }; - - info("call_context: ", call_context); - - auto buffer = to_buffer(call_context); - auto call_context_2 = from_buffer>(buffer.data()); - - EXPECT_EQ(call_context, call_context_2); -} - -TEST(abi_tests, native_read_write_function_data) -{ - FunctionData const function_data = { - .selector = - { - .value = 11, - }, - .is_private = false, - .is_constructor = false, - }; - - info("function data: ", function_data); - - auto buffer = to_buffer(function_data); - auto function_data_2 = from_buffer>(buffer.data()); - - EXPECT_EQ(function_data, function_data_2); -} - -TEST(abi_tests, native_to_circuit_function_data) -{ - FunctionData const native_function_data = { - .selector = - { - .value = 11, - }, - .is_private = false, - .is_constructor = false, - }; - - info("function data: ", native_function_data); - - Builder builder = Builder(); - FunctionData const circuit_function_data = native_function_data.to_circuit_type(builder); - - info("function data: ", circuit_function_data); -} - -TEST(abi_tests, native_call_context) -{ - CallContext const call_context = { - .msg_sender = 10, - .storage_contract_address = 11, - .portal_contract_address = 12, - .function_selector = { .value = 1 }, - .is_delegate_call = false, - .is_static_call = false, - }; - - info("call context: ", call_context); -} - -TEST(abi_tests, native_to_circuit_call_context) -{ - CallContext const native_call_context = { - .msg_sender = 10, - .storage_contract_address = 11, - .portal_contract_address = 12, - .function_selector = { .value = 1 }, - .is_delegate_call = false, - .is_static_call = false, - }; - - info("call context: ", native_call_context); - - Builder builder = Builder(); - CallContext const circuit_call_context = native_call_context.to_circuit_type(builder); - - info("call context: ", circuit_call_context); -} - -} // namespace aztec3::circuits::abis diff --git a/circuits/cpp/src/aztec3/circuits/abis/CMakeLists.txt b/circuits/cpp/src/aztec3/circuits/abis/CMakeLists.txt deleted file mode 100644 index 53fc0c72e0a..00000000000 --- a/circuits/cpp/src/aztec3/circuits/abis/CMakeLists.txt +++ /dev/null @@ -1,4 +0,0 @@ -circuits_cmake_module( - aztec3_circuits_abis - barretenberg -) \ No newline at end of file diff --git a/circuits/cpp/src/aztec3/circuits/abis/README.md b/circuits/cpp/src/aztec3/circuits/abis/README.md deleted file mode 100644 index 1d6b1b23af5..00000000000 --- a/circuits/cpp/src/aztec3/circuits/abis/README.md +++ /dev/null @@ -1,9 +0,0 @@ -# aztec3::circuits::abis - -Contains all ABIs for use by: - -- Test app circuits -- Kernel circuits -- Rollup circuits - -All ABIs are generalized by an `NCT` template parameter (meaning `NativeOrCircuitTypes`). `NCT` can be either `aztec3::utils::types::NativeTypes` or `aztec3::utils::types::CircuitTypes` for some `Builder`. The idea being, there's a single implementation of every struct/class for native and circuit types. NativeType structs can be switched to CircuitType with the `to_circuit_type()` method. diff --git a/circuits/cpp/src/aztec3/circuits/abis/append_only_tree_snapshot.hpp b/circuits/cpp/src/aztec3/circuits/abis/append_only_tree_snapshot.hpp deleted file mode 100644 index 50ec136e492..00000000000 --- a/circuits/cpp/src/aztec3/circuits/abis/append_only_tree_snapshot.hpp +++ /dev/null @@ -1,18 +0,0 @@ -#pragma once - -#include - -namespace aztec3::circuits::abis { - -template struct AppendOnlyTreeSnapshot { - using fr = typename NCT::fr; - using uint32 = typename NCT::uint32; - - fr root = 0; - uint32 next_available_leaf_index; - MSGPACK_FIELDS(root, next_available_leaf_index); - - bool operator==(AppendOnlyTreeSnapshot const&) const = default; -}; - -} // namespace aztec3::circuits::abis diff --git a/circuits/cpp/src/aztec3/circuits/abis/block_header.hpp b/circuits/cpp/src/aztec3/circuits/abis/block_header.hpp deleted file mode 100644 index a843a94ff5b..00000000000 --- a/circuits/cpp/src/aztec3/circuits/abis/block_header.hpp +++ /dev/null @@ -1,138 +0,0 @@ -#pragma once - -#include "aztec3/circuits/abis/append_only_tree_snapshot.hpp" -#include "aztec3/circuits/abis/global_variables.hpp" -#include "aztec3/circuits/hash.hpp" -#include "aztec3/utils/types/circuit_types.hpp" -#include "aztec3/utils/types/convert.hpp" -#include "aztec3/utils/types/native_types.hpp" - -#include - -#include - -namespace aztec3::circuits::abis { - -using aztec3::utils::types::CircuitTypes; -using aztec3::utils::types::NativeTypes; -using std::is_same; - -template struct BlockHeader { - using fr = typename NCT::fr; - using boolean = typename NCT::boolean; - - // Private data - fr note_hash_tree_root = 0; - fr nullifier_tree_root = 0; - fr contract_tree_root = 0; - fr l1_to_l2_message_tree_root = 0; - fr archive_root = 0; - fr private_kernel_vk_tree_root = 0; // TODO: future enhancement - - // Public data - fr public_data_tree_root = 0; - fr global_variables_hash = 0; - - // for serialization, update with new fields - MSGPACK_FIELDS(note_hash_tree_root, - nullifier_tree_root, - contract_tree_root, - l1_to_l2_message_tree_root, - archive_root, - private_kernel_vk_tree_root, - public_data_tree_root, - global_variables_hash); - - boolean operator==(BlockHeader const& other) const - { - return note_hash_tree_root == other.note_hash_tree_root && nullifier_tree_root == other.nullifier_tree_root && - contract_tree_root == other.contract_tree_root && - l1_to_l2_message_tree_root == other.l1_to_l2_message_tree_root && archive_root == other.archive_root && - private_kernel_vk_tree_root == other.private_kernel_vk_tree_root && - public_data_tree_root == other.public_data_tree_root && - global_variables_hash == other.global_variables_hash; - }; - - template void assert_is_zero() - { - static_assert((std::is_same, NCT>::value)); - - note_hash_tree_root.assert_is_zero(); - nullifier_tree_root.assert_is_zero(); - contract_tree_root.assert_is_zero(); - l1_to_l2_message_tree_root.assert_is_zero(); - archive_root.assert_is_zero(); - private_kernel_vk_tree_root.assert_is_zero(); - public_data_tree_root.assert_is_zero(); - global_variables_hash.assert_is_zero(); - } - - template BlockHeader> to_circuit_type(Builder& builder) const - { - static_assert((std::is_same::value)); - - // Capture the circuit builder: - auto to_ct = [&](auto& e) { return aztec3::utils::types::to_ct(builder, e); }; - - BlockHeader> data = { - to_ct(note_hash_tree_root), to_ct(nullifier_tree_root), to_ct(contract_tree_root), - to_ct(l1_to_l2_message_tree_root), to_ct(archive_root), to_ct(private_kernel_vk_tree_root), - to_ct(public_data_tree_root), to_ct(global_variables_hash), - }; - - return data; - }; - - template BlockHeader to_native_type() const - { - static_assert(std::is_same, NCT>::value); - auto to_nt = [&](auto& e) { return aztec3::utils::types::to_nt(e); }; - - BlockHeader data = { - to_nt(note_hash_tree_root), to_nt(nullifier_tree_root), to_nt(contract_tree_root), - to_nt(l1_to_l2_message_tree_root), to_nt(archive_root), to_nt(private_kernel_vk_tree_root), - to_nt(public_data_tree_root), to_nt(global_variables_hash), - }; - - return data; - }; - - void set_public() - { - static_assert(!(std::is_same::value)); - - note_hash_tree_root.set_public(); - nullifier_tree_root.set_public(); - contract_tree_root.set_public(); - l1_to_l2_message_tree_root.set_public(); - archive_root.set_public(); - private_kernel_vk_tree_root.set_public(); - public_data_tree_root.set_public(); - global_variables_hash.set_public(); - } - - std::array to_array() const - { - return { note_hash_tree_root, - nullifier_tree_root, - contract_tree_root, - l1_to_l2_message_tree_root, - archive_root, // TODO(#3441) Note private_kernel_vk_tree_root, is not included yet as - // it is not present in noir, - public_data_tree_root, - global_variables_hash }; - } - - - fr hash() - { - return compute_block_hash(global_variables_hash, - note_hash_tree_root, - nullifier_tree_root, - contract_tree_root, - l1_to_l2_message_tree_root, - public_data_tree_root); - } -}; - -} // namespace aztec3::circuits::abis diff --git a/circuits/cpp/src/aztec3/circuits/abis/c_bind.cpp b/circuits/cpp/src/aztec3/circuits/abis/c_bind.cpp deleted file mode 100644 index 648cde0870d..00000000000 --- a/circuits/cpp/src/aztec3/circuits/abis/c_bind.cpp +++ /dev/null @@ -1,588 +0,0 @@ -#include "c_bind.h" - -#include "call_stack_item.hpp" -#include "function_data.hpp" -#include "function_leaf_preimage.hpp" -#include "kernel_circuit_public_inputs.hpp" -#include "kernel_circuit_public_inputs_final.hpp" -#include "previous_kernel_data.hpp" -#include "private_circuit_public_inputs.hpp" -#include "tx_context.hpp" -#include "tx_request.hpp" -#include "private_kernel/private_kernel_inputs_inner.hpp" -#include "public_kernel/public_kernel_inputs.hpp" -#include "rollup/base/base_or_merge_rollup_public_inputs.hpp" -#include "rollup/base/base_rollup_inputs.hpp" -#include "rollup/root/root_rollup_inputs.hpp" -#include "rollup/root/root_rollup_public_inputs.hpp" - -#include "aztec3/circuits/abis/combined_accumulated_data.hpp" -#include "aztec3/circuits/abis/complete_address.hpp" -#include "aztec3/circuits/abis/final_accumulated_data.hpp" -#include "aztec3/circuits/abis/new_contract_data.hpp" -#include "aztec3/circuits/abis/packers.hpp" -#include "aztec3/circuits/abis/point.hpp" -#include "aztec3/circuits/abis/private_kernel/private_kernel_inputs_init.hpp" -#include "aztec3/circuits/abis/tx_request.hpp" -#include "aztec3/circuits/abis/types.hpp" -#include "aztec3/circuits/hash.hpp" -#include "aztec3/constants.hpp" -#include "aztec3/utils/types/native_types.hpp" - -#include - -namespace { - -using aztec3::circuits::compute_constructor_hash; -using aztec3::circuits::abis::CallStackItem; -using aztec3::circuits::abis::CompleteAddress; -using aztec3::circuits::abis::ConstantsPacker; -using aztec3::circuits::abis::FunctionData; -using aztec3::circuits::abis::FunctionLeafPreimage; -using aztec3::circuits::abis::GeneratorIndexPacker; -using aztec3::circuits::abis::NewContractData; -using aztec3::circuits::abis::Point; -using aztec3::circuits::abis::PrivateStateNoteGeneratorIndexPacker; -using aztec3::circuits::abis::PrivateStateTypePacker; -using aztec3::circuits::abis::StorageSlotGeneratorIndexPacker; -using aztec3::circuits::abis::TxContext; -using aztec3::circuits::abis::TxRequest; -using NT = aztec3::utils::types::NativeTypes; -using aztec3::circuits::abis::PrivateTypes; -using aztec3::circuits::abis::PublicTypes; - -// Cbind helper functions - -/** - * @brief Fill in zero-leaves to get a full tree's bottom layer. - * - * @details Given the a vector of nonzero leaves starting at the left, - * append zero leaves to that list until it represents a FULL set of leaves - * for a tree of the given height. - * **MODIFIES THE INPUT `leaves` REFERENCE!** - * - * @tparam TREE_HEIGHT height of the tree used to determine max leaves - * @param leaves the nonzero leaves of the tree starting at the left - * @param zero_leaf the leaf value to be used for any empty/unset leaves - */ -template void rightfill_with_zeroleaves(std::vector& leaves, NT::fr& zero_leaf) -{ - constexpr size_t max_leaves = 2 << (TREE_HEIGHT - 1); - // input cant exceed max leaves - // FIXME don't think asserts will show in wasm - ASSERT(leaves.size() <= max_leaves); - - // fill in input vector with zero-leaves - // to get a full bottom layer of the tree - leaves.insert(leaves.end(), max_leaves - leaves.size(), zero_leaf); -} - -} // namespace - -/** Copy this string to a bbmalloc'd buffer */ -static const char* bbmalloc_copy_string(const char* data, size_t len) -{ - char* output_copy = static_cast(bbmalloc(len + 1)); - memcpy(output_copy, data, len + 1); - return output_copy; -} - -/** - * For testing only. Take this object, write it to a buffer, then output it. */ -template static const char* as_string_output(uint8_t const* input_buf, uint32_t* size) -{ - using serialize::read; - T obj; - read(input_buf, obj); - std::ostringstream stream; - stream << obj; - std::string const str = stream.str(); - *size = static_cast(str.size()); - return bbmalloc_copy_string(str.c_str(), *size); -} - -/** - * For testing only. Take this object, serialize it to a buffer, then output it. */ -template static const char* as_serialized_output(uint8_t const* input_buf, uint32_t* size) -{ - using serialize::read; - T obj; - serialize::read(input_buf, obj); - std::vector stream; - serialize::write(stream, obj); - *size = static_cast(stream.size()); - return bbmalloc_copy_string(reinterpret_cast(stream.data()), *size); -} - -// WASM Cbinds -/** - * @brief Hashes a TX request. This is a WASM-export that can be called from Typescript. - * - * @details given a `uint8_t*` buffer representing a full TX request, - * read it into a `TxRequest` object, hash it to a `fr`, - * and serialize it to a `uint8_t*` output buffer - * - * @param tx_request_buf buffer of bytes containing all data needed to construct a TX request via `serialize::read()` - * @param output buffer that will contain the output which will be the hashed `TxRequest` - */ -WASM_EXPORT void abis__hash_tx_request(uint8_t const* tx_request_buf, uint8_t* output) -{ - TxRequest tx_request; - serialize::read(tx_request_buf, tx_request); - // TODO(dbanks12) consider using write() and serialize::read() instead of - // serialize to/from everywhere here and in test - NT::fr::serialize_to_buffer(tx_request.hash(), output); -} - -/** - * @brief Generates a function's "selector" from its "signature" using keccak256. - * This is a WASM-export that can be called from Typescript. - * - * @details given a `char const*` c-string representing a "function signature", - * hash using keccak and return its first 4 bytes (the "function selector") - * by copying them into the `output` buffer arg. This is a workalike of - * Ethereum/solidity's function selector computation.... - * Ethereum function selector is computed as follows: - * `uint8_t* hash = keccak256(const char* func_sig);` - * where func_sig does NOT include the trailing null character - * And the resulting cstring for "transfer(address,uint256)" is: - * `0xa9059cbb` - * The 0th to 3rd bytes make up the function selector like: - * where 0xa9 is hash[0], 05 is hash[1], 9c is hash[2], and bb is hash[3] - * - * @param func_sig_cstr c-string representing the function signature string like "transfer(uint256,address)" - * @param output buffer that will contain the output which will be 4-byte function selector - */ -WASM_EXPORT void abis__compute_function_selector(char const* func_sig_cstr, uint8_t* output) -{ - // hash the function signature using keccak256 - auto keccak_hash = ethash_keccak256(reinterpret_cast(func_sig_cstr), strlen(func_sig_cstr)); - // get a pointer to the start of the hash bytes - auto const* hash_bytes = reinterpret_cast(&keccak_hash.word64s[0]); - // get the correct number of bytes from the hash and copy into output buffer - std::copy_n(hash_bytes, aztec3::FUNCTION_SELECTOR_NUM_BYTES, output); -} - -/** - * @brief Hash verification key data. - * This is a WASM-export that can be called from Typescript. - * - * @details Pedersen hash VK to use later when computing function leaf - * or constructor hash. Return the serialized results in the `output` buffer. - * - * @param vk_data_buf buffer of bytes representing serialized verification_key_data - * @param output buffer that will contain the output. The serialized vk_hash. - */ -WASM_EXPORT void abis__hash_vk(uint8_t const* vk_data_buf, uint8_t* output) -{ - NT::VKData vk_data; - serialize::read(vk_data_buf, vk_data); - - NT::fr::serialize_to_buffer(vk_data.hash_native(), output); -} - -/** - * @brief Generates a function tree leaf from its preimage. - * This is a WASM-export that can be called from Typescript. - * - * @details given a `uint8_t const*` buffer representing a function leaf's preimage, - * construct a FunctionLeafPreimage instance, hash, and return the serialized results - * in the `output` buffer. - * - * @param function_leaf_preimage_buf a buffer of bytes representing the function leaf's preimage - * contents (`function_selector`, `is_private`, `vk_hash`, and `acir_hash`) - * @param output buffer that will contain the output. The hashed and serialized function leaf. - */ -WASM_EXPORT void abis__compute_function_leaf(uint8_t const* function_leaf_preimage_buf, uint8_t* output) -{ - FunctionLeafPreimage leaf_preimage; - serialize::read(function_leaf_preimage_buf, leaf_preimage); - leaf_preimage.hash(); - NT::fr::serialize_to_buffer(leaf_preimage.hash(), output); -} - -/** - * @brief Compute a function tree root from its nonzero leaves. - * This is a WASM-export that can be called from Typescript. - * - * @details given a serialized vector of nonzero function leaves, - * compute the corresponding tree's root and return the - * serialized results via `root_out` buffer. - * - * @param function_leaves_in input buffer representing a serialized vector of - * nonzero function leaves where each leaf is an `fr` starting at the left of the tree - * @param root_out buffer that will contain the serialized function tree root `fr`. - */ -WASM_EXPORT void abis__compute_function_tree_root(uint8_t const* function_leaves_in, uint8_t* root_out) -{ - std::vector leaves; - // fill in nonzero leaves to start - read(function_leaves_in, leaves); - // fill in zero leaves to complete tree - NT::fr zero_leaf = FunctionLeafPreimage().hash(); // hash of empty/0 preimage - rightfill_with_zeroleaves(leaves, zero_leaf); - - // compute the root of this complete tree, return - NT::fr const root = plonk::stdlib::merkle_tree::compute_tree_root_native(leaves); - - // serialize and return root - NT::fr::serialize_to_buffer(root, root_out); -} - -/** - * @brief Compute all of a function tree's nodes from its nonzero leaves. - * This is a WASM-export that can be called from Typescript. - * - * @details given a serialized vector of nonzero function leaves, - * compute ALL of the corresponding tree's nodes (including root) and return - * the serialized results via `tree_nodes_out` buffer. - * - * @param function_leaves_in input buffer representing a serialized vector of - * nonzero function leaves where each leaf is an `fr` starting at the left of the tree. - * @param tree_nodes_out buffer that will contain the serialized function tree. - * The 0th node is the bottom leftmost leaf. The last entry is the root. - */ -WASM_EXPORT void abis__compute_function_tree(uint8_t const* function_leaves_in, uint8_t* tree_nodes_out) -{ - std::vector leaves; - // fill in nonzero leaves to start - read(function_leaves_in, leaves); - // fill in zero leaves to complete tree - NT::fr zero_leaf = FunctionLeafPreimage().hash(); // hash of empty/0 preimage - rightfill_with_zeroleaves(leaves, zero_leaf); - - std::vector const tree = plonk::stdlib::merkle_tree::compute_tree_native(leaves); - - // serialize and return tree - write(tree_nodes_out, tree); -} - -/** - * @brief Hash some constructor info. - * This is a WASM-export that can be called from Typescript. - * - * @details Hash constructor info to use later when deriving/generating contract address: - * hash(function_signature_hash, args_hash, constructor_vk_hash) - * Return the serialized results in the `output` buffer. - * - * @param function_data_buf function data struct but as a buffer of bytes - * @param args_buf constructor args (array of fields) but as a buffer of bytes - * @param constructor_vk_hash_buf constructor vk hashed to a field but as a buffer of bytes - * @param output buffer that will contain the output. The serialized constructor_vk_hash. - */ -WASM_EXPORT void abis__hash_constructor(uint8_t const* function_data_buf, - uint8_t const* args_hash_buf, - uint8_t const* constructor_vk_hash_buf, - uint8_t* output) -{ - FunctionData function_data; - NT::fr args_hash; - NT::fr constructor_vk_hash; - - serialize::read(function_data_buf, function_data); - read(args_hash_buf, args_hash); - read(constructor_vk_hash_buf, constructor_vk_hash); - - NT::fr const constructor_hash = compute_constructor_hash(function_data, args_hash, constructor_vk_hash); - - NT::fr::serialize_to_buffer(constructor_hash, output); -} - -/** - * @brief Compute a complete address. - */ -CBIND(abis__compute_complete_address, aztec3::circuits::abis::CompleteAddress::compute); - -/** - * @brief Compute a contract address from deployer public key and partial address. - * This is a WASM-export that can be called from Typescript. - * - * @details Computes a contract address by hashing the deployers public key along with the previously computed partial - * address Return the serialized results in the `output` buffer. - * - * @param point_data_buf point data struct as a buffer of bytes - * @param partial_address_data_buf partial address - * @param output buffer that will contain the output. The serialized contract address. - */ -WASM_EXPORT void abis__compute_contract_address_from_partial(uint8_t const* point_data_buf, - uint8_t const* partial_address_data_buf, - uint8_t* output) -{ - Point deployer_public_key; - NT::fr partial_address; - - serialize::read(point_data_buf, deployer_public_key); - read(partial_address_data_buf, partial_address); - - NT::fr const contract_address = - aztec3::circuits::compute_contract_address_from_partial(deployer_public_key, partial_address); - - NT::fr::serialize_to_buffer(contract_address, output); -} - -/** - * @brief Hash args for a function call. - * - * @param args_buf array of args (fields), with the length on the first position - * @param output buffer that will contain the output - */ -WASM_EXPORT void abis__compute_var_args_hash(uint8_t const* args_buf, uint8_t* output) -{ - std::vector args; - read(args_buf, args); - NT::fr const args_hash = aztec3::circuits::compute_var_args_hash(args); - NT::fr::serialize_to_buffer(args_hash, output); -} - -/** - * @brief Generates a function tree leaf from its preimage. - * This is a WASM-export that can be called from Typescript. - * - * @details given a `uint8_t const*` buffer representing a function leaf's preimage, - * construct a NewContractData instance, hash, and return the serialized results - * in the `output` buffer. - * - * @param contract_leaf_preimage_buf a buffer of bytes representing the contract leaf's preimage - * contents (`contract_address`, `portal_contract_address`, `function_tree_root`) - * @param output buffer that will contain the output. The hashed and serialized contract leaf. - */ -WASM_EXPORT void abis__compute_contract_leaf(uint8_t const* contract_leaf_preimage_buf, uint8_t* output) -{ - NewContractData leaf_preimage; - serialize::read(contract_leaf_preimage_buf, leaf_preimage); - // as per the circuit implementation, if contract address == zero then return a zero leaf - auto to_write = leaf_preimage.hash(); - NT::fr::serialize_to_buffer(to_write, output); -} - -/** - * @brief Generates a commitment nonce, which will be used to create a unique commitment. - */ -CBIND(abis__compute_commitment_nonce, aztec3::circuits::compute_commitment_nonce); - -/** - * @brief Generates a unique commitment using a commitment nonce. - */ -CBIND(abis__compute_unique_commitment, aztec3::circuits::compute_unique_commitment); - -/** - * @brief Generates a siloed commitment tree leaf from the contract and the commitment. - */ -CBIND(abis__silo_commitment, aztec3::circuits::silo_commitment); - -/** - * @brief Generates a siloed nullifier from the contract and the nullifier. - */ -CBIND(abis__silo_nullifier, aztec3::circuits::silo_nullifier); - -/** - * @brief Computes the block hash from the block information. - * Globals is provided as a hash in this instance. - */ -CBIND(abis__compute_block_hash, aztec3::circuits::compute_block_hash); - -/** - * @brief Computes the block hash from the block information. - * The entire globals object is provided in this instance, rather than a hash as in above. - */ -CBIND(abis__compute_block_hash_with_globals, aztec3::circuits::compute_block_hash_with_globals); - -/** - * @brief Computes the hash of the global variables - */ -CBIND(abis__compute_globals_hash, aztec3::circuits::compute_globals_hash); - -/** - * @brief Compute the value to be inserted into the public data tree - */ -CBIND(abis__compute_public_data_tree_value, aztec3::circuits::compute_public_data_tree_value); - -/** - * @brief Compute the index for inserting a value into the public data tree - */ -CBIND(abis__compute_public_data_tree_index, aztec3::circuits::compute_public_data_tree_index); - -/** - * @brief Generates a signed tx request hash from it's pre-image - * This is a WASM-export that can be called from Typescript. - * - * @details given a `uint8_t const*` buffer representing a signed tx request's pre-image, - * construct a TxRequest instance, hash, and return the serialized results - * in the `output` buffer. - * - * @param tx_request_buf a buffer of bytes representing the signed tx request - * @param output buffer that will contain the output. The hashed and serialized signed tx request. - */ -WASM_EXPORT void abis__compute_transaction_hash(uint8_t const* tx_request_buf, uint8_t* output) -{ - TxRequest tx_request_preimage; - serialize::read(tx_request_buf, tx_request_preimage); - auto to_write = tx_request_preimage.hash(); - NT::fr::serialize_to_buffer(to_write, output); -} - -WASM_EXPORT void abis__compute_private_call_stack_item_hash(uint8_t const* call_stack_item_buf, uint8_t* output) -{ - CallStackItem call_stack_item; - serialize::read(call_stack_item_buf, call_stack_item); - NT::fr::serialize_to_buffer(call_stack_item.hash(), output); -} - -WASM_EXPORT void abis__compute_public_call_stack_item_hash(uint8_t const* call_stack_item_buf, uint8_t* output) -{ - CallStackItem call_stack_item; - serialize::read(call_stack_item_buf, call_stack_item); - NT::fr::serialize_to_buffer(get_call_stack_item_hash(call_stack_item), output); -} - -/** - * @brief Computes the hash of a message secret for use in l1 -> l2 messaging - * - * @param secret - * @param output - */ -WASM_EXPORT void abis__compute_message_secret_hash(uint8_t const* secret, uint8_t* output) -{ - NT::fr message_secret; - read(secret, message_secret); - auto secret_hash = NT::hash({ message_secret }, aztec3::GeneratorIndex::L1_TO_L2_MESSAGE_SECRET); - NT::fr::serialize_to_buffer(secret_hash, output); -} - -/* Typescript test helpers that call as_string_output() to stress serialization. - * Each of these take an object buffer, and a string size pointer. - * They return a string pointer (to be bbfree'd) and write to the string size pointer. */ -WASM_EXPORT const char* abis__test_roundtrip_serialize_tx_context(uint8_t const* tx_context_buf, uint32_t* size) -{ - return as_string_output>(tx_context_buf, size); -} - -WASM_EXPORT const char* abis__test_roundtrip_serialize_tx_request(uint8_t const* tx_request_buf, uint32_t* size) -{ - return as_string_output>(tx_request_buf, size); -} - -WASM_EXPORT const char* abis__test_roundtrip_serialize_call_context(uint8_t const* call_context_buf, uint32_t* size) -{ - return as_string_output>(call_context_buf, size); -} - -WASM_EXPORT const char* abis__test_roundtrip_serialize_private_circuit_public_inputs( - uint8_t const* private_circuits_public_inputs_buf, uint32_t* size) -{ - return as_string_output>(private_circuits_public_inputs_buf, - size); -} - -WASM_EXPORT const char* abis__test_roundtrip_serialize_public_circuit_public_inputs( - uint8_t const* public_circuits_public_inputs_buf, uint32_t* size) -{ - return as_string_output>(public_circuits_public_inputs_buf, - size); -} - -WASM_EXPORT const char* abis__test_roundtrip_serialize_function_data(uint8_t const* function_data_buf, uint32_t* size) -{ - return as_string_output>(function_data_buf, size); -} - -WASM_EXPORT const char* abis__test_roundtrip_serialize_base_rollup_inputs(uint8_t const* rollup_inputs_buf, - uint32_t* size) -{ - return as_string_output>(rollup_inputs_buf, size); -} - -WASM_EXPORT const char* abis__test_roundtrip_serialize_previous_kernel_data(uint8_t const* kernel_data_buf, - uint32_t* size) -{ - return as_string_output>(kernel_data_buf, size); -} - -WASM_EXPORT const char* abis__test_roundtrip_serialize_base_or_merge_rollup_public_inputs( - uint8_t const* rollup_inputs_buf, uint32_t* size) -{ - return as_string_output>(rollup_inputs_buf, size); -} - -WASM_EXPORT const char* abis__test_roundtrip_reserialize_base_or_merge_rollup_public_inputs( - uint8_t const* rollup_inputs_buf, uint32_t* size) -{ - return as_serialized_output>(rollup_inputs_buf, size); -} - -WASM_EXPORT const char* abis__test_roundtrip_serialize_root_rollup_inputs(uint8_t const* rollup_inputs_buf, - uint32_t* size) -{ - return as_string_output>(rollup_inputs_buf, size); -} - -WASM_EXPORT const char* abis__test_roundtrip_serialize_root_rollup_public_inputs(uint8_t const* rollup_inputs_buf, - uint32_t* size) -{ - return as_string_output>(rollup_inputs_buf, size); -} - -WASM_EXPORT const char* abis__test_roundtrip_reserialize_root_rollup_public_inputs(uint8_t const* rollup_inputs_buf, - uint32_t* size) -{ - return as_serialized_output>(rollup_inputs_buf, size); -} - -WASM_EXPORT const char* abis__test_roundtrip_serialize_combined_accumulated_data(uint8_t const* input, uint32_t* size) -{ - return as_string_output>(input, size); -} - -WASM_EXPORT const char* abis__test_roundtrip_serialize_final_accumulated_data(uint8_t const* input, uint32_t* size) -{ - return as_string_output>(input, size); -} - -WASM_EXPORT const char* abis__test_roundtrip_serialize_signature(uint8_t const* input, uint32_t* size) -{ - return as_string_output(input, size); -} - -WASM_EXPORT const char* abis__test_roundtrip_serialize_private_kernel_inputs_inner(uint8_t const* input, uint32_t* size) -{ - return as_string_output>(input, size); -} - -WASM_EXPORT const char* abis__test_roundtrip_serialize_private_kernel_inputs_init(uint8_t const* input, uint32_t* size) -{ - return as_string_output>(input, size); -} - -WASM_EXPORT const char* abis__test_roundtrip_serialize_kernel_circuit_public_inputs(uint8_t const* input, - uint32_t* size) -{ - return as_string_output>(input, size); -} - -WASM_EXPORT const char* abis__test_roundtrip_serialize_kernel_circuit_public_inputs_final(uint8_t const* input, - uint32_t* size) -{ - return as_string_output>(input, size); -} - -WASM_EXPORT const char* abis__test_roundtrip_serialize_public_kernel_inputs(uint8_t const* input, uint32_t* size) -{ - return as_string_output>(input, size); -} - -WASM_EXPORT const char* abis__test_roundtrip_serialize_function_leaf_preimage(uint8_t const* function_leaf_preimage_buf, - uint32_t* size) -{ - return as_string_output>(function_leaf_preimage_buf, size); -} - - -// When we return a packer from packers.hpp, we call its msgpack_pack method (as that is what is used -// internally in msgpack) and thus can get a JSON-like object of all our constants in Typescript. We explicitly do not -// want a schema here as our ConstantsPacker is not meant to be used in a Typescript function. (if it were, it would -// need to implement msgpack_schema, but as we handle it specially not much value). -CBIND_NOSCHEMA(get_circuit_constants, [] { return ConstantsPacker{}; }); -CBIND_NOSCHEMA(get_circuit_generator_index, [] { return GeneratorIndexPacker{}; }); -CBIND_NOSCHEMA(get_circuit_private_state_note_generator_index, [] { return PrivateStateNoteGeneratorIndexPacker{}; }); -CBIND_NOSCHEMA(get_circuit_storage_slot_generator_index, [] { return StorageSlotGeneratorIndexPacker{}; }); -CBIND_NOSCHEMA(get_circuit_private_state_type, [] { return PrivateStateTypePacker{}; }); diff --git a/circuits/cpp/src/aztec3/circuits/abis/c_bind.h b/circuits/cpp/src/aztec3/circuits/abis/c_bind.h deleted file mode 100644 index 49ebe515418..00000000000 --- a/circuits/cpp/src/aztec3/circuits/abis/c_bind.h +++ /dev/null @@ -1,38 +0,0 @@ -#include - -#include -#include -#include - -WASM_EXPORT void abis__hash_tx_request(uint8_t const* tx_request_buf, uint8_t* output); - -WASM_EXPORT void abis__compute_function_selector(char const* func_sig_cstr, uint8_t* output); - -WASM_EXPORT void abis__compute_function_leaf(uint8_t const* function_leaf_preimage_buf, uint8_t* output); - -WASM_EXPORT void abis__compute_function_tree_root(uint8_t const* function_leaves_buf, uint8_t* output); - -WASM_EXPORT void abis__compute_function_tree(uint8_t const* function_leaves_buf, uint8_t* output); - -WASM_EXPORT void abis__hash_vk(uint8_t const* vk_data_buf, uint8_t* output); - -WASM_EXPORT void abis__hash_constructor(uint8_t const* func_data_buf, - uint8_t const* args_buf, - uint8_t const* constructor_vk_hash_buf, - uint8_t* output); - -CBIND_DECL(abis__compute_complete_address); - -CBIND_DECL(abis__compute_commitment_nonce); -CBIND_DECL(abis__compute_unique_commitment); -CBIND_DECL(abis__silo_commitment); -CBIND_DECL(abis__silo_nullifier); -CBIND_DECL(abis__compute_block_hash); -CBIND_DECL(abis__compute_block_hash_with_globals); -CBIND_DECL(abis__compute_globals_hash); - -WASM_EXPORT void abis__compute_message_secret_hash(uint8_t const* secret, uint8_t* output); -WASM_EXPORT void abis__compute_contract_leaf(uint8_t const* contract_leaf_preimage_buf, uint8_t* output); -WASM_EXPORT void abis__compute_transaction_hash(uint8_t const* tx_request_buf, uint8_t* output); -WASM_EXPORT void abis__compute_call_stack_item_hash(uint8_t const* call_stack_item_buf, uint8_t* output); -WASM_EXPORT void abis__compute_var_args_hash(uint8_t const* args_buf, uint8_t* output); \ No newline at end of file diff --git a/circuits/cpp/src/aztec3/circuits/abis/c_bind.test.cpp b/circuits/cpp/src/aztec3/circuits/abis/c_bind.test.cpp deleted file mode 100644 index ffd0217a836..00000000000 --- a/circuits/cpp/src/aztec3/circuits/abis/c_bind.test.cpp +++ /dev/null @@ -1,338 +0,0 @@ -#include "c_bind.h" - -#include "function_leaf_preimage.hpp" -#include "tx_request.hpp" - -#include "aztec3/circuits/abis/complete_address.hpp" -#include "aztec3/circuits/abis/new_contract_data.hpp" -#include "aztec3/circuits/abis/tx_request.hpp" -#include "aztec3/circuits/hash.hpp" - -#include - -#include - -#include -#include - -namespace { - -using NT = aztec3::utils::types::NativeTypes; -using aztec3::circuits::abis::NewContractData; -// num_leaves = 2**h = 2<<(h-1) -// root layer does not count in height -constexpr size_t FUNCTION_TREE_NUM_LEAVES = 2 << (aztec3::FUNCTION_TREE_HEIGHT - 1); -// num_nodes = (2**(h+1))-1 = (2< std::string bytes_to_hex_str(std::array bytes) -{ - std::ostringstream stream; - for (const uint8_t& byte : bytes) { - stream << std::setw(2) << std::setfill('0') << std::hex << static_cast(byte); - } - return stream.str(); -} - -} // namespace - -namespace aztec3::circuits::abis { - -TEST(abi_tests, hash_tx_request) -{ - // Construct TxRequest with some randomized fields - TxRequest const tx_request = TxRequest{ - .origin = NT::fr::random_element(), - .function_data = FunctionData(), - .args_hash = NT::fr::random_element(), - .tx_context = TxContext(), - }; - - // Write the tx request to a buffer and - std::vector buf; - serialize::write(buf, tx_request); - - // create an output buffer for cbind hash results - std::array output = { 0 }; - // Make the c_bind call to hash the tx request - abis__hash_tx_request(buf.data(), output.data()); - - // Convert buffer to `fr` for comparison to in-test calculated hash - NT::fr const got_hash = NT::fr::serialize_from_buffer(output.data()); - - // Confirm cbind output == hash of tx request - EXPECT_EQ(got_hash, tx_request.hash()); -} - -TEST(abi_tests, compute_selector_transfer) -{ - const char* function_signature = "transfer(address,uint256)"; - - // create an output buffer for cbind selector results - std::array output = { 0 }; - // Make the c_bind call to compute the function selector via keccak256 - abis__compute_function_selector(function_signature, output.data()); - - // get the selector as a hex string - // compare against known good selector from solidity - // In solidity where selectors are 4 bytes it is a9059cbb - std::string const full_selector = "a9059cbb2ab09eb219583f4a59a5d0623ade346d962bcd4e46b11da047c9049b"; - EXPECT_EQ(bytes_to_hex_str(output), full_selector.substr(0, FUNCTION_SELECTOR_NUM_BYTES * 2)); -} - -TEST(abi_tests, compute_function_selector_transferFrom) -{ - const char* function_signature = "transferFrom(address,address,uint256)"; - - // create an output buffer for cbind selector results - std::array output = { 0 }; - // Make the c_bind call to compute the function selector via keccak256 - abis__compute_function_selector(function_signature, output.data()); - - // get the selector as a hex string - // compare against known good selector from solidity - std::string const full_selector = "23b872dd7302113369cda2901243429419bec145408fa8b352b3dd92b66c680b"; - EXPECT_EQ(bytes_to_hex_str(output), full_selector.substr(0, FUNCTION_SELECTOR_NUM_BYTES * 2)); -} - -TEST(abi_tests, hash_vk) -{ - // Initialize some random VK data - NT::VKData vk_data; - vk_data.circuit_type = static_cast(proof_system::CircuitType::ULTRA); - vk_data.circuit_size = static_cast(1) << (engine.get_random_uint8() >> 3); // must be a power of two - vk_data.num_public_inputs = engine.get_random_uint32(); - vk_data.commitments["test1"] = g1::element::random_element(); - vk_data.commitments["test2"] = g1::element::random_element(); - vk_data.commitments["foo1"] = g1::element::random_element(); - vk_data.commitments["foo2"] = g1::element::random_element(); - // Write the vk data to a bytes vector - std::vector vk_data_vec; - serialize::write(vk_data_vec, vk_data); - - // create an output buffer for cbind hash results - std::array output = { 0 }; - - // Make the c_bind call to hash the vk - abis__hash_vk(vk_data_vec.data(), output.data()); - - // Convert buffer to `fr` for comparison to in-test calculated hash - NT::fr const got_hash = NT::fr::serialize_from_buffer(output.data()); - - // Calculate the expected hash in-test - NT::fr const expected_hash = vk_data.hash_native(); - - // Confirm cbind output == expected hash - EXPECT_EQ(got_hash, expected_hash); -} - -TEST(abi_tests, compute_function_leaf) -{ - // Construct FunctionLeafPreimage with some randomized fields - auto const preimage = FunctionLeafPreimage{ - .selector = - { - .value = engine.get_random_uint32(), - }, - .is_private = static_cast(engine.get_random_uint8() & 1), - .vk_hash = NT::fr::random_element(), - .acir_hash = NT::fr::random_element(), - }; - - // Write the leaf preimage to a buffer - std::vector preimage_buf; - serialize::write(preimage_buf, preimage); - - std::array output = { 0 }; - abis__compute_function_leaf(preimage_buf.data(), output.data()); - - NT::fr const got_leaf = NT::fr::serialize_from_buffer(output.data()); - EXPECT_EQ(got_leaf, preimage.hash()); -} - -TEST(abi_tests, compute_function_tree_root) -{ - // randomize number of non-zero leaves such that `0 < num_nonzero_leaves <= FUNCTION_TREE_NUM_LEAVES` - uint8_t const num_nonzero_leaves = engine.get_random_uint8() % (FUNCTION_TREE_NUM_LEAVES + 1); - - // generate some random leaves - std::vector leaves_frs; - for (size_t l = 0; l < num_nonzero_leaves; l++) { - leaves_frs.push_back(NT::fr::random_element()); - } - // serialize the leaves to a buffer to pass to cbind - std::vector leaves_bytes_vec; - write(leaves_bytes_vec, leaves_frs); - - // call cbind and get output (root) - std::array output = { 0 }; - abis__compute_function_tree_root(leaves_bytes_vec.data(), output.data()); - NT::fr const got_root = NT::fr::serialize_from_buffer(output.data()); - - // compare cbind results with direct computation - - // add the zero leaves to the vector of fields and pass to barretenberg helper - NT::fr const zero_leaf = FunctionLeafPreimage().hash(); // hash of empty/0 preimage - for (size_t l = num_nonzero_leaves; l < FUNCTION_TREE_NUM_LEAVES; l++) { - leaves_frs.push_back(zero_leaf); - } - // compare results - EXPECT_EQ(got_root, plonk::stdlib::merkle_tree::compute_tree_root_native(leaves_frs)); -} - -TEST(abi_tests, compute_function_tree) -{ - // randomize number of non-zero leaves such that `0 < num_nonzero_leaves <= FUNCTION_TREE_NUM_LEAVES` - uint8_t const num_nonzero_leaves = engine.get_random_uint8() % (FUNCTION_TREE_NUM_LEAVES + 1); - - // generate some random leaves - std::vector leaves_frs; - for (size_t l = 0; l < num_nonzero_leaves; l++) { - leaves_frs.push_back(NT::fr::random_element()); - } - // serialize the leaves to a buffer to pass to cbind - std::vector leaves_bytes_vec; - write(leaves_bytes_vec, leaves_frs); - - // setup output buffer - // it must fit a uint32_t (for the vector length) - // plus all of the nodes `frs` in the tree - constexpr auto size_output_buf = sizeof(uint32_t) + (sizeof(NT::fr) * FUNCTION_TREE_NUM_NODES); - std::array output = { 0 }; - - // call cbind and get output (full tree root) - abis__compute_function_tree(leaves_bytes_vec.data(), output.data()); - // deserialize output to vector of frs representing all nodes in tree - std::vector got_tree; - uint8_t const* output_ptr = output.data(); - read(output_ptr, got_tree); - - // compare cbind results with direct computation - - // add the zero leaves to the vector of fields and pass to barretenberg helper - NT::fr const zero_leaf = FunctionLeafPreimage().hash(); // hash of empty/0 preimage - for (size_t l = num_nonzero_leaves; l < FUNCTION_TREE_NUM_LEAVES; l++) { - leaves_frs.push_back(zero_leaf); - } - - EXPECT_EQ(got_tree, plonk::stdlib::merkle_tree::compute_tree_native(leaves_frs)); -} - -TEST(abi_tests, hash_constructor) -{ - // Randomize required values - auto const func_data = FunctionData{ .selector = - { - .value = 10, - }, - .is_private = true, - .is_constructor = false }; - - NT::fr const args_hash = NT::fr::random_element(); - NT::fr const constructor_vk_hash = NT::fr::random_element(); - - // Write the function data and args to a buffer - std::vector func_data_buf; - serialize::write(func_data_buf, func_data); - - std::vector args_hash_buf; - write(args_hash_buf, args_hash); - - std::array constructor_vk_hash_buf = { 0 }; - NT::fr::serialize_to_buffer(constructor_vk_hash, constructor_vk_hash_buf.data()); - - // create an output buffer for cbind hash results - std::array output = { 0 }; - - // Make the c_bind call to hash the constructor values - abis__hash_constructor(func_data_buf.data(), args_hash_buf.data(), constructor_vk_hash_buf.data(), output.data()); - - // Convert buffer to `fr` for comparison to in-test calculated hash - NT::fr const got_hash = NT::fr::serialize_from_buffer(output.data()); - - // Calculate the expected hash in-test - NT::fr const expected_hash = - NT::hash({ func_data.hash(), args_hash, constructor_vk_hash }, aztec3::GeneratorIndex::CONSTRUCTOR); - - // Confirm cbind output == expected hash - EXPECT_EQ(got_hash, expected_hash); -} - -TEST(abi_tests, hash_var_args) -{ - // Initialize test data and write to buffer - std::vector const args(32, NT::fr::random_element()); - std::vector buf; - write(buf, args); - - // Prepare output buffer - std::array output = { 0 }; - - // Make the c_bind call to hash the constructor values - abis__compute_var_args_hash(buf.data(), output.data()); - - // Convert buffer to `fr` for comparison to in-test calculated hash - NT::fr const got_hash = NT::fr::serialize_from_buffer(output.data()); - - // Calculate the expected hash in-test - NT::fr const expected_hash = NT::hash(args, aztec3::GeneratorIndex::FUNCTION_ARGS); - - // Confirm cbind output == expected hash - EXPECT_EQ(got_hash, expected_hash); -} - -TEST(abi_tests, compute_contract_leaf) -{ - // Construct ContractLeafPreimage with some randomized fields - NewContractData const preimage = NewContractData{ - .contract_address = NT::fr::random_element(), - .portal_contract_address = NT::fr::random_element(), - .function_tree_root = NT::fr::random_element(), - }; - - // Write the leaf preimage to a buffer - std::vector preimage_buf; - serialize::write(preimage_buf, preimage); - - std::array output = { 0 }; - abis__compute_contract_leaf(preimage_buf.data(), output.data()); - - NT::fr const got_leaf = NT::fr::serialize_from_buffer(output.data()); - EXPECT_EQ(got_leaf, preimage.hash()); -} - -TEST(abi_tests, compute_transaction_hash) -{ - // Construct TxRequest with some randomized fields - TxRequest const tx_request = TxRequest{ - .origin = NT::fr::random_element(), - .function_data = FunctionData(), - .args_hash = NT::fr::random_element(), - .tx_context = TxContext(), - }; - - // Write the leaf preimage to a buffer - std::vector preimage_buf; - serialize::write(preimage_buf, tx_request); - - std::array output = { 0 }; - abis__compute_transaction_hash(preimage_buf.data(), output.data()); - - NT::fr const got_tx_hash = NT::fr::serialize_from_buffer(output.data()); - EXPECT_EQ(got_tx_hash, tx_request.hash()); -} - -} // namespace aztec3::circuits::abis diff --git a/circuits/cpp/src/aztec3/circuits/abis/call_context.hpp b/circuits/cpp/src/aztec3/circuits/abis/call_context.hpp deleted file mode 100644 index fc50a3a562a..00000000000 --- a/circuits/cpp/src/aztec3/circuits/abis/call_context.hpp +++ /dev/null @@ -1,127 +0,0 @@ -#pragma once - -#include "aztec3/circuits/abis/function_selector.hpp" -#include "aztec3/circuits/hash.hpp" -#include "aztec3/constants.hpp" -#include "aztec3/utils/msgpack_derived_equals.hpp" -#include "aztec3/utils/msgpack_derived_output.hpp" -#include "aztec3/utils/types/circuit_types.hpp" -#include "aztec3/utils/types/convert.hpp" -#include "aztec3/utils/types/native_types.hpp" - -#include - -namespace aztec3::circuits::abis { - -using aztec3::utils::types::CircuitTypes; -using aztec3::utils::types::NativeTypes; - -template struct CallContext { - using address = typename NCT::address; - using fr = typename NCT::fr; - using boolean = typename NCT::boolean; - - address msg_sender = 0; - address storage_contract_address = 0; - fr portal_contract_address = 0; - - FunctionSelector function_selector{}; - - boolean is_delegate_call = false; - boolean is_static_call = false; - boolean is_contract_deployment = false; - - // for serialization, update with new fields - MSGPACK_FIELDS(msg_sender, - storage_contract_address, - portal_contract_address, - function_selector, - is_delegate_call, - is_static_call, - is_contract_deployment); - boolean operator==(CallContext const& other) const - { - // we can't use =default with a custom boolean, but we can use a msgpack-derived utility - return utils::msgpack_derived_equals(*this, other); - }; - - template CallContext> to_circuit_type(Builder& builder) const - { - static_assert((std::is_same::value)); - - // Capture the circuit builder: - auto to_ct = [&](auto& e) { return aztec3::utils::types::to_ct(builder, e); }; - - CallContext> call_context = { - to_ct(msg_sender), - to_ct(storage_contract_address), - to_ct(portal_contract_address), - function_selector.to_circuit_type(builder), - to_ct(is_delegate_call), - to_ct(is_static_call), - to_ct(is_contract_deployment), - - }; - - return call_context; - }; - - template CallContext to_native_type() const - { - static_assert(std::is_same, NCT>::value); - auto to_native_type = [](T& e) { return e.template to_native_type(); }; - auto to_nt = [&](auto& e) { return aztec3::utils::types::to_nt(e); }; - - CallContext call_context = { - to_nt(msg_sender), - to_nt(storage_contract_address), - to_nt(portal_contract_address), - to_native_type(function_selector), - to_nt(is_delegate_call), - to_nt(is_static_call), - to_nt(is_contract_deployment), - }; - - return call_context; - }; - - fr hash() const - { - std::vector const inputs = { - msg_sender.to_field(), storage_contract_address.to_field(), - portal_contract_address, function_selector.to_field(), - fr(is_delegate_call), fr(is_static_call), - fr(is_contract_deployment), - }; - - return NCT::hash(inputs, GeneratorIndex::CALL_CONTEXT); - } - - template void assert_is_zero() - { - static_assert((std::is_same, NCT>::value)); - - msg_sender.to_field().assert_is_zero(); - storage_contract_address.to_field().assert_is_zero(); - portal_contract_address.assert_is_zero(); - function_selector.to_field().assert_is_zero(); - fr(is_delegate_call).assert_is_zero(); - fr(is_static_call).assert_is_zero(); - fr(is_contract_deployment).assert_is_zero(); - } - - void set_public() - { - static_assert(!(std::is_same::value)); - - msg_sender.to_field().set_public(); - storage_contract_address.to_field().set_public(); - portal_contract_address.set_public(); - function_selector.set_public(); - fr(is_delegate_call).set_public(); - fr(is_static_call).set_public(); - fr(is_contract_deployment).set_public(); - } -}; - -} // namespace aztec3::circuits::abis diff --git a/circuits/cpp/src/aztec3/circuits/abis/call_stack_item.hpp b/circuits/cpp/src/aztec3/circuits/abis/call_stack_item.hpp deleted file mode 100644 index f964abe594a..00000000000 --- a/circuits/cpp/src/aztec3/circuits/abis/call_stack_item.hpp +++ /dev/null @@ -1,109 +0,0 @@ -#pragma once -#include "function_data.hpp" -#include "kernel_circuit_public_inputs.hpp" -#include "private_circuit_public_inputs.hpp" -#include "public_circuit_public_inputs.hpp" - -#include "aztec3/circuits/abis/types.hpp" -#include "aztec3/utils/msgpack_derived_equals.hpp" -#include "aztec3/utils/types/circuit_types.hpp" -#include "aztec3/utils/types/convert.hpp" -#include "aztec3/utils/types/native_types.hpp" - -#include - -namespace aztec3::circuits::abis { - -using aztec3::utils::types::CircuitTypes; -using aztec3::utils::types::NativeTypes; -using std::is_same; - -template typename PrivatePublic> struct CallStackItem { - using address = typename NCT::address; - using boolean = typename NCT::boolean; - using fr = typename NCT::fr; - - // This is the _actual_ contract address relating to where this function's code resides in the - // contract tree. Regardless of whether this is a call or delegatecall, this - // `contract_address` _does not change_. Amongst other things, it's used as a lookup for - // getting the correct code from the tree. There is a separate `storage_contract_address` - // within a CallStackItem which varies depending on whether this is a call or delegatecall. - address contract_address = 0; - FunctionData function_data{}; - typename PrivatePublic::AppCircuitPublicInputs public_inputs{}; - // True if this call stack item represents a request to execute a function rather than a - // fulfilled execution. Used when enqueuing calls from private to public functions. - boolean is_execution_request = false; - - // for serialization, update with new fields - MSGPACK_FIELDS(contract_address, function_data, public_inputs, is_execution_request); - // for schema serialization - void msgpack_schema(auto& packer) const - { - packer.pack_with_name(PrivatePublic::schema_name + std::string("CallStackItem"), *this); // NOLINT - } - boolean operator==(CallContext const& other) const - { - // we can't use =default with a custom boolean, but we can use a msgpack-derived utility - return utils::msgpack_derived_equals(*this, other); - }; - - template - CallStackItem, PrivatePublic> to_circuit_type(Builder& builder) const - { - static_assert((std::is_same::value)); - - // Capture the circuit builder: - auto to_ct = [&](auto& e) { return aztec3::utils::types::to_ct(builder, e); }; - - CallStackItem, PrivatePublic> call_stack_item = { - to_ct(contract_address), - function_data.to_circuit_type(builder), - public_inputs.to_circuit_type(builder), - to_ct(is_execution_request), - }; - - return call_stack_item; - }; - - fr hash() const - { - const std::vector inputs = { - contract_address.to_field(), - function_data.hash(), - public_inputs.hash(), - }; - - // NOLINTNEXTLINE(misc-const-correctness) - fr call_stack_item_hash = NCT::hash(inputs, GeneratorIndex::CALL_STACK_ITEM); - - return call_stack_item_hash; - } -}; - -// Returns a copy of this call stack item where all result-related fields are zeroed out. -inline CallStackItem as_execution_request( - CallStackItem const& call_stack_item) -{ - return { - .contract_address = call_stack_item.contract_address, - .function_data = call_stack_item.function_data, - .public_inputs = { - .call_context = call_stack_item.public_inputs.call_context, - .args_hash = call_stack_item.public_inputs.args_hash, - }, - .is_execution_request = call_stack_item.is_execution_request, - }; -}; - -// Returns the hash of a call stack item, or if the call stack item represents an execution request, -// zeroes out all fields but those related to the request (contract, function data, call context, args) -// and then hashes the item. Implemented only for native types for now. -inline fr get_call_stack_item_hash(abis::CallStackItem const& call_stack_item) -{ - auto const& preimage = - call_stack_item.is_execution_request ? as_execution_request(call_stack_item) : call_stack_item; - return preimage.hash(); -} - -} // namespace aztec3::circuits::abis diff --git a/circuits/cpp/src/aztec3/circuits/abis/combined_accumulated_data.hpp b/circuits/cpp/src/aztec3/circuits/abis/combined_accumulated_data.hpp deleted file mode 100644 index e3e2800a69c..00000000000 --- a/circuits/cpp/src/aztec3/circuits/abis/combined_accumulated_data.hpp +++ /dev/null @@ -1,230 +0,0 @@ -#pragma once -#include "new_contract_data.hpp" -#include "optionally_revealed_data.hpp" -#include "public_data_read.hpp" -#include "public_data_update_request.hpp" - -#include "aztec3/circuits/abis/membership_witness.hpp" -#include "aztec3/circuits/abis/read_request_membership_witness.hpp" -#include "aztec3/constants.hpp" -#include "aztec3/utils/types/circuit_types.hpp" -#include "aztec3/utils/types/convert.hpp" -#include "aztec3/utils/types/native_types.hpp" - -#include - -#include -#include - -namespace aztec3::circuits::abis { - -using aztec3::utils::types::CircuitTypes; -using aztec3::utils::types::NativeTypes; -using std::is_same; - -template struct CombinedAccumulatedData { - using fr = typename NCT::fr; - using uint32 = typename NCT::uint32; - using boolean = typename NCT::boolean; - using AggregationObject = typename NCT::AggregationObject; - - AggregationObject aggregation_object{}; - - std::array read_requests{}; - - std::array new_commitments{}; - std::array new_nullifiers{}; - std::array nullified_commitments{}; - // For pending nullifiers, we have: - // nullifiedCommitments[j] != 0 <==> newNullifiers[j] nullifies nullifiedCommitments[j] - - std::array private_call_stack{}; - std::array public_call_stack{}; - std::array new_l2_to_l1_msgs{}; - - std::array encrypted_logs_hash{}; - std::array unencrypted_logs_hash{}; - - // Here so that the gas cost of this request can be measured by circuits, without actually needing to feed in the - // variable-length data. - fr encrypted_log_preimages_length = 0; - fr unencrypted_log_preimages_length = 0; - - std::array, MAX_NEW_CONTRACTS_PER_TX> new_contracts{}; - - std::array, MAX_OPTIONALLY_REVEALED_DATA_LENGTH_PER_TX> optionally_revealed_data{}; - - std::array, MAX_PUBLIC_DATA_UPDATE_REQUESTS_PER_TX> public_data_update_requests{}; - std::array, MAX_PUBLIC_DATA_READS_PER_TX> public_data_reads{}; - - // for serialization, update with new fields - MSGPACK_FIELDS(aggregation_object, - read_requests, - new_commitments, - new_nullifiers, - nullified_commitments, - private_call_stack, - public_call_stack, - new_l2_to_l1_msgs, - encrypted_logs_hash, - unencrypted_logs_hash, - encrypted_log_preimages_length, - unencrypted_log_preimages_length, - new_contracts, - optionally_revealed_data, - public_data_update_requests, - public_data_reads); - boolean operator==(CombinedAccumulatedData const& other) const - { - return msgpack_derived_equals(*this, other); - }; - - template CombinedAccumulatedData> to_circuit_type(Builder& builder) const - { - typedef CircuitTypes CT; - static_assert((std::is_same::value)); - - // Capture the circuit builder: - auto to_ct = [&](auto& e) { return aztec3::utils::types::to_ct(builder, e); }; - auto to_circuit_type = [&](auto& e) { return e.to_circuit_type(builder); }; - - CombinedAccumulatedData acc_data = { - typename CT::AggregationObject{ - to_ct(aggregation_object.P0), - to_ct(aggregation_object.P1), - to_ct(aggregation_object.public_inputs), - aggregation_object.proof_witness_indices, - aggregation_object.has_data, - }, - - to_ct(read_requests), - - to_ct(new_commitments), - to_ct(new_nullifiers), - to_ct(nullified_commitments), - - to_ct(private_call_stack), - to_ct(public_call_stack), - to_ct(new_l2_to_l1_msgs), - - to_ct(encrypted_logs_hash), - to_ct(unencrypted_logs_hash), - - to_ct(encrypted_log_preimages_length), - to_ct(unencrypted_log_preimages_length), - - map(new_contracts, to_circuit_type), - map(optionally_revealed_data, to_circuit_type), - map(public_data_update_requests, to_circuit_type), - map(public_data_reads, to_circuit_type), - }; - - return acc_data; - }; - - template CombinedAccumulatedData to_native_type() const - { - static_assert(std::is_same, NCT>::value); - auto to_nt = [&](auto& e) { return aztec3::utils::types::to_nt(e); }; - auto to_native_type = [](T& e) { return e.template to_native_type(); }; - - CombinedAccumulatedData acc_data = { - typename NativeTypes::AggregationObject{ - to_nt(aggregation_object.P0), - to_nt(aggregation_object.P1), - to_nt(aggregation_object.public_inputs), - aggregation_object.proof_witness_indices, - aggregation_object.has_data, - }, - - to_nt(read_requests), - - to_nt(new_commitments), - to_nt(new_nullifiers), - to_nt(nullified_commitments), - - to_nt(private_call_stack), - to_nt(public_call_stack), - to_nt(new_l2_to_l1_msgs), - - to_nt(encrypted_logs_hash), - to_nt(unencrypted_logs_hash), - - to_nt(encrypted_log_preimages_length), - to_nt(unencrypted_log_preimages_length), - - map(new_contracts, to_native_type), - map(optionally_revealed_data, to_native_type), - map(public_data_update_requests, to_native_type), - map(public_data_reads, to_native_type), - }; - return acc_data; - } - - void set_public() - { - static_assert(!(std::is_same::value)); - - aggregation_object.add_proof_outputs_as_public_inputs(); - - set_array_public(read_requests); - - set_array_public(new_commitments); - set_array_public(new_nullifiers); - set_array_public(nullified_commitments); - - set_array_public(private_call_stack); - set_array_public(public_call_stack); - set_array_public(new_l2_to_l1_msgs); - - set_array_public(encrypted_logs_hash); - set_array_public(unencrypted_logs_hash); - - set_array_public(new_contracts); - set_array_public(optionally_revealed_data); - set_array_public(public_data_update_requests); - set_array_public(public_data_reads); - } - - template void set_array_public(std::array& arr) - { - static_assert(!(std::is_same::value)); - for (T& e : arr) { - fr(e).set_public(); - } - } - - template void set_array_public(std::array, SIZE>& arr) - { - static_assert(!(std::is_same::value)); - for (auto& e : arr) { - e.set_public(); - } - } - - template void set_array_public(std::array, SIZE>& arr) - { - static_assert(!(std::is_same::value)); - for (auto& e : arr) { - e.set_public(); - } - } - - template void set_array_public(std::array, SIZE>& arr) - { - static_assert(!(std::is_same::value)); - for (auto& e : arr) { - e.set_public(); - } - } - - template void set_array_public(std::array, SIZE>& arr) - { - static_assert(!(std::is_same::value)); - for (auto& e : arr) { - e.set_public(); - } - } -}; - -} // namespace aztec3::circuits::abis diff --git a/circuits/cpp/src/aztec3/circuits/abis/combined_constant_data.hpp b/circuits/cpp/src/aztec3/circuits/abis/combined_constant_data.hpp deleted file mode 100644 index 21d2aef29b4..00000000000 --- a/circuits/cpp/src/aztec3/circuits/abis/combined_constant_data.hpp +++ /dev/null @@ -1,67 +0,0 @@ -#pragma once - -#include "tx_context.hpp" - -#include "aztec3/circuits/abis/block_header.hpp" -#include "aztec3/utils/types/circuit_types.hpp" -#include "aztec3/utils/types/native_types.hpp" - -#include - -namespace aztec3::circuits::abis { - -using aztec3::circuits::abis::BlockHeader; -using aztec3::utils::types::CircuitTypes; -using aztec3::utils::types::NativeTypes; -using std::is_same; - -template struct CombinedConstantData { - using fr = typename NCT::fr; - using boolean = typename NCT::boolean; - - BlockHeader block_header{}; - TxContext tx_context{}; - - // for serialization: update up with new fields - MSGPACK_FIELDS(block_header, tx_context); - boolean operator==(CombinedConstantData const& other) const - { - return msgpack_derived_equals(*this, other); - } - - template CombinedConstantData> to_circuit_type(Builder& builder) const - { - static_assert((std::is_same::value)); - - CombinedConstantData> constant_data = { - block_header.to_circuit_type(builder), - tx_context.to_circuit_type(builder), - }; - - return constant_data; - }; - - template CombinedConstantData to_native_type() const - { - static_assert(std::is_same, NCT>::value); - - auto to_native_type = [](T& e) { return e.template to_native_type(); }; - - CombinedConstantData constant_data = { - to_native_type(block_header), - to_native_type(tx_context), - }; - - return constant_data; - }; - - void set_public() - { - static_assert(!(std::is_same::value)); - - block_header.set_public(); - tx_context.set_public(); - } -}; - -} // namespace aztec3::circuits::abis diff --git a/circuits/cpp/src/aztec3/circuits/abis/complete_address.hpp b/circuits/cpp/src/aztec3/circuits/abis/complete_address.hpp deleted file mode 100644 index e8bc16eb76e..00000000000 --- a/circuits/cpp/src/aztec3/circuits/abis/complete_address.hpp +++ /dev/null @@ -1,90 +0,0 @@ -#pragma once -#include "aztec3/circuits/abis/coordinate.hpp" -#include "aztec3/circuits/abis/point.hpp" -#include "aztec3/circuits/hash.hpp" -#include "aztec3/utils/types/circuit_types.hpp" -#include "aztec3/utils/types/convert.hpp" -#include "aztec3/utils/types/native_types.hpp" - -#include - -namespace aztec3::circuits::abis { - -using aztec3::circuits::compute_partial_address; -using aztec3::utils::types::CircuitTypes; -using aztec3::utils::types::NativeTypes; - -template struct CompleteAddress { - using fr = typename NCT::fr; - using boolean = typename NCT::boolean; - - typename NCT::address address; - Point public_key; - fr partial_address; - - // for serialization, update with new fields - MSGPACK_FIELDS(address, public_key, partial_address); - bool operator==(CompleteAddress const&) const = default; - - template CompleteAddress> to_circuit_type(Builder& builder) const - { - static_assert((std::is_same::value)); - - auto to_ct = [&](auto& e) { return aztec3::utils::types::to_ct(builder, e); }; - - CompleteAddress> complete_address = { to_ct(address), - to_ct(public_key), - to_ct(partial_address) }; - - return complete_address; - }; - - template CompleteAddress to_native_type() const - { - static_assert((std::is_same, NCT>::value)); - - auto to_nt = [&](auto& e) { return aztec3::utils::types::to_nt(e); }; - - CompleteAddress complete_address = { to_nt(address), to_nt(public_key), to_nt(partial_address) }; - - return complete_address; - }; - - void set_public() - { - static_assert(!(std::is_same::value)); - - address.set_public(); - public_key.set_public(); - partial_address.set_public(); - } - - void assert_is_zero() - { - static_assert(!(std::is_same::value)); - - address.assert_is_zero(); - public_key.assert_is_zero(); - partial_address.assert_is_zero(); - } - - static CompleteAddress compute(Point const& point, - typename NCT::fr const& contract_address_salt, - typename NCT::fr const& function_tree_root, - typename NCT::fr const& constructor_hash) - { - using fr = typename NCT::fr; - - const fr partial_address = - compute_partial_address(contract_address_salt, function_tree_root, constructor_hash); - - CompleteAddress complete_address; - complete_address.address = compute_contract_address_from_partial(point, partial_address); - complete_address.public_key = point; - complete_address.partial_address = partial_address; - - return complete_address; - } -}; - -} // namespace aztec3::circuits::abis diff --git a/circuits/cpp/src/aztec3/circuits/abis/contract_deployment_data.hpp b/circuits/cpp/src/aztec3/circuits/abis/contract_deployment_data.hpp deleted file mode 100644 index f351d5bfe9b..00000000000 --- a/circuits/cpp/src/aztec3/circuits/abis/contract_deployment_data.hpp +++ /dev/null @@ -1,105 +0,0 @@ -#pragma once -#include "aztec3/circuits/abis/point.hpp" -#include "aztec3/constants.hpp" -#include "aztec3/utils/types/circuit_types.hpp" -#include "aztec3/utils/types/convert.hpp" -#include "aztec3/utils/types/native_types.hpp" - -#include - -#include - -namespace aztec3::circuits::abis { - -using aztec3::utils::types::CircuitTypes; -using aztec3::utils::types::NativeTypes; -using std::is_same; - -template struct ContractDeploymentData { - using address = typename NCT::address; - using fr = typename NCT::fr; - using boolean = typename NCT::boolean; - - Point deployer_public_key; - fr constructor_vk_hash = 0; - fr function_tree_root = 0; - fr contract_address_salt = 0; - address portal_contract_address = 0; - - // for serialization: update up with new fields - MSGPACK_FIELDS( - deployer_public_key, constructor_vk_hash, function_tree_root, contract_address_salt, portal_contract_address); - - boolean operator==(ContractDeploymentData const& other) const - { - return deployer_public_key == other.deployer_public_key && constructor_vk_hash == other.constructor_vk_hash && - function_tree_root == other.function_tree_root && contract_address_salt == other.contract_address_salt && - portal_contract_address == other.portal_contract_address; - }; - - template ContractDeploymentData> to_circuit_type(Builder& builder) const - { - static_assert((std::is_same::value)); - - // Capture the circuit builder: - auto to_ct = [&](auto& e) { return aztec3::utils::types::to_ct(builder, e); }; - - ContractDeploymentData> data = { - deployer_public_key.to_circuit_type(builder), - to_ct(constructor_vk_hash), - to_ct(function_tree_root), - to_ct(contract_address_salt), - to_ct(portal_contract_address), - }; - - return data; - }; - - template ContractDeploymentData to_native_type() const - { - static_assert(std::is_same, NCT>::value); - auto to_nt = [&](auto& e) { return aztec3::utils::types::to_nt(e); }; - auto to_native_type = [](T& e) { return e.template to_native_type(); }; - - ContractDeploymentData call_context = { - to_native_type(deployer_public_key), to_nt(constructor_vk_hash), to_nt(function_tree_root), - to_nt(contract_address_salt), to_nt(portal_contract_address), - }; - - return call_context; - }; - - template void assert_is_zero() - { - static_assert((std::is_same, NCT>::value)); - - deployer_public_key.assert_is_zero(); - constructor_vk_hash.assert_is_zero(); - function_tree_root.assert_is_zero(); - contract_address_salt.assert_is_zero(); - portal_contract_address.to_field().assert_is_zero(); - } - - void set_public() - { - static_assert(!(std::is_same::value)); - - deployer_public_key.set_public(); - constructor_vk_hash.set_public(); - function_tree_root.set_public(); - contract_address_salt.set_public(); - portal_contract_address.to_field().set_public(); - } - - fr hash() const - { - std::vector const inputs = { - deployer_public_key.x, deployer_public_key.y, constructor_vk_hash, - function_tree_root, contract_address_salt, portal_contract_address.to_field(), - }; - - return NCT::hash(inputs, GeneratorIndex::CONTRACT_DEPLOYMENT_DATA); - } -}; - -} // namespace aztec3::circuits::abis diff --git a/circuits/cpp/src/aztec3/circuits/abis/contract_storage_read.hpp b/circuits/cpp/src/aztec3/circuits/abis/contract_storage_read.hpp deleted file mode 100644 index 0748759a494..00000000000 --- a/circuits/cpp/src/aztec3/circuits/abis/contract_storage_read.hpp +++ /dev/null @@ -1,75 +0,0 @@ -#pragma once -#include "aztec3/utils/types/circuit_types.hpp" -#include "aztec3/utils/types/convert.hpp" -#include "aztec3/utils/types/native_types.hpp" - -#include - -namespace aztec3::circuits::abis { - -using aztec3::utils::types::CircuitTypes; -using aztec3::utils::types::NativeTypes; -using plonk::stdlib::witness_t; - -template struct ContractStorageRead { - using fr = typename NCT::fr; - using boolean = typename NCT::boolean; - - fr storage_slot = 0; - fr current_value = 0; - - // for serialization, update with new fields - MSGPACK_FIELDS(storage_slot, current_value); - bool operator==(ContractStorageRead const&) const = default; - - template ContractStorageRead> to_circuit_type(Builder& builder) const - { - static_assert((std::is_same::value)); - - // Capture the circuit builder: - auto to_ct = [&](auto& e) { return aztec3::utils::types::to_ct(builder, e); }; - - ContractStorageRead> contract_storage_read = { - to_ct(storage_slot), - to_ct(current_value), - }; - - return contract_storage_read; - }; - - template ContractStorageRead to_native_type() const - { - static_assert((std::is_same, NCT>::value)); - - auto to_nt = [&](auto& e) { return aztec3::utils::types::to_nt(e); }; - - ContractStorageRead contract_storage_read = { - to_nt(storage_slot), - to_nt(current_value), - }; - - return contract_storage_read; - }; - - fr hash() const - { - std::vector const inputs = { - storage_slot, - current_value, - }; - - return NCT::hash(inputs, GeneratorIndex::PUBLIC_DATA_READ); - } - - void set_public() - { - static_assert(!(std::is_same::value)); - - storage_slot.set_public(); - current_value.set_public(); - } - - boolean is_empty() const { return storage_slot == 0; } -}; - -} // namespace aztec3::circuits::abis diff --git a/circuits/cpp/src/aztec3/circuits/abis/contract_storage_update_request.hpp b/circuits/cpp/src/aztec3/circuits/abis/contract_storage_update_request.hpp deleted file mode 100644 index ea0e66df800..00000000000 --- a/circuits/cpp/src/aztec3/circuits/abis/contract_storage_update_request.hpp +++ /dev/null @@ -1,81 +0,0 @@ -#pragma once -#include "aztec3/utils/msgpack_derived_output.hpp" -#include "aztec3/utils/types/circuit_types.hpp" -#include "aztec3/utils/types/convert.hpp" -#include "aztec3/utils/types/native_types.hpp" - -#include - -namespace aztec3::circuits::abis { - -using aztec3::utils::types::CircuitTypes; -using aztec3::utils::types::NativeTypes; -using plonk::stdlib::witness_t; - -template struct ContractStorageUpdateRequest { - using fr = typename NCT::fr; - using boolean = typename NCT::boolean; - - fr storage_slot = 0; - fr old_value = 0; - fr new_value = 0; - - // for serialization, update with new fields - MSGPACK_FIELDS(storage_slot, old_value, new_value); - bool operator==(ContractStorageUpdateRequest const&) const = default; - template - ContractStorageUpdateRequest> to_circuit_type(Builder& builder) const - { - static_assert((std::is_same::value)); - - // Capture the circuit builder: - auto to_ct = [&](auto& e) { return aztec3::utils::types::to_ct(builder, e); }; - - ContractStorageUpdateRequest> update_request = { - to_ct(storage_slot), - to_ct(old_value), - to_ct(new_value), - }; - - return update_request; - }; - - template ContractStorageUpdateRequest to_native_type() const - { - static_assert((std::is_same, NCT>::value)); - - auto to_nt = [&](auto& e) { return aztec3::utils::types::to_nt(e); }; - - ContractStorageUpdateRequest update_request = { - to_nt(storage_slot), - to_nt(old_value), - to_nt(new_value), - }; - - return update_request; - }; - - fr hash() const - { - std::vector const inputs = { - storage_slot, - old_value, - new_value, - }; - - return NCT::hash(inputs, GeneratorIndex::PUBLIC_DATA_UPDATE_REQUEST); - } - - void set_public() - { - static_assert(!(std::is_same::value)); - - storage_slot.set_public(); - old_value.set_public(); - new_value.set_public(); - } - - boolean is_empty() const { return storage_slot == 0; } -}; - -} // namespace aztec3::circuits::abis diff --git a/circuits/cpp/src/aztec3/circuits/abis/coordinate.hpp b/circuits/cpp/src/aztec3/circuits/abis/coordinate.hpp deleted file mode 100644 index 446ab45628d..00000000000 --- a/circuits/cpp/src/aztec3/circuits/abis/coordinate.hpp +++ /dev/null @@ -1,69 +0,0 @@ -#pragma once -#include "aztec3/constants.hpp" -#include "aztec3/utils/types/circuit_types.hpp" -#include "aztec3/utils/types/convert.hpp" -#include "aztec3/utils/types/native_types.hpp" - -#include - -namespace aztec3::circuits::abis { - -using aztec3::GeneratorIndex; -using aztec3::utils::types::CircuitTypes; -using aztec3::utils::types::NativeTypes; - -template struct Coordinate { - using fr = typename NCT::fr; - using boolean = typename NCT::boolean; - - std::array fields; - - // for serialization, update with new fields - MSGPACK_FIELDS(fields); - bool operator==(Coordinate const&) const = default; - - template Coordinate> to_circuit_type(Builder& builder) const - { - static_assert((std::is_same::value)); - - // Capture the builder: - auto to_ct = [&](auto& e) { return aztec3::utils::types::to_ct(builder, e); }; - - Coordinate> coordinate = { - to_ct(fields), - }; - - return coordinate; - }; - - template Coordinate to_native_type() const - { - static_assert((std::is_same, NCT>::value)); - - auto to_nt = [&](auto& e) { return aztec3::utils::types::to_nt(e); }; - - Coordinate coordinate = { - to_nt(fields), - }; - - return coordinate; - }; - - void set_public() - { - static_assert(!(std::is_same::value)); - - fields[0].set_public(); - fields[1].set_public(); - } - - void assert_is_zero() - { - static_assert(!(std::is_same::value)); - - fields[0].assert_is_zero(); - fields[1].assert_is_zero(); - } -}; - -} // namespace aztec3::circuits::abis diff --git a/circuits/cpp/src/aztec3/circuits/abis/final_accumulated_data.hpp b/circuits/cpp/src/aztec3/circuits/abis/final_accumulated_data.hpp deleted file mode 100644 index 9aba003e806..00000000000 --- a/circuits/cpp/src/aztec3/circuits/abis/final_accumulated_data.hpp +++ /dev/null @@ -1,194 +0,0 @@ -#pragma once -#include "new_contract_data.hpp" -#include "optionally_revealed_data.hpp" -#include "public_data_read.hpp" -#include "public_data_update_request.hpp" - -#include "aztec3/circuits/abis/membership_witness.hpp" -#include "aztec3/circuits/abis/read_request_membership_witness.hpp" -#include "aztec3/constants.hpp" -#include "aztec3/utils/types/circuit_types.hpp" -#include "aztec3/utils/types/convert.hpp" -#include "aztec3/utils/types/native_types.hpp" - -#include - -#include -#include - -namespace aztec3::circuits::abis { - -using aztec3::utils::types::CircuitTypes; -using aztec3::utils::types::NativeTypes; -using std::is_same; - -template struct FinalAccumulatedData { - using fr = typename NCT::fr; - using uint32 = typename NCT::uint32; - using boolean = typename NCT::boolean; - using AggregationObject = typename NCT::AggregationObject; - - AggregationObject aggregation_object{}; - - std::array new_commitments{}; - std::array new_nullifiers{}; - std::array nullified_commitments{}; - // For pending nullifiers, we have: - // nullifiedCommitments[j] != 0 <==> newNullifiers[j] nullifies nullifiedCommitments[j] - - std::array private_call_stack{}; - std::array public_call_stack{}; - std::array new_l2_to_l1_msgs{}; - - std::array encrypted_logs_hash{}; - std::array unencrypted_logs_hash{}; - - // Here so that the gas cost of this request can be measured by circuits, without actually needing to feed in the - // variable-length data. - fr encrypted_log_preimages_length = 0; - fr unencrypted_log_preimages_length = 0; - - std::array, MAX_NEW_CONTRACTS_PER_TX> new_contracts{}; - - std::array, MAX_OPTIONALLY_REVEALED_DATA_LENGTH_PER_TX> optionally_revealed_data{}; - - // for serialization, update with new fields - MSGPACK_FIELDS(aggregation_object, - new_commitments, - new_nullifiers, - nullified_commitments, - private_call_stack, - public_call_stack, - new_l2_to_l1_msgs, - encrypted_logs_hash, - unencrypted_logs_hash, - encrypted_log_preimages_length, - unencrypted_log_preimages_length, - new_contracts, - optionally_revealed_data); - boolean operator==(FinalAccumulatedData const& other) const - { - return msgpack_derived_equals(*this, other); - }; - - template FinalAccumulatedData> to_circuit_type(Builder& builder) const - { - typedef CircuitTypes CT; - static_assert((std::is_same::value)); - - // Capture the circuit builder: - auto to_ct = [&](auto& e) { return aztec3::utils::types::to_ct(builder, e); }; - auto to_circuit_type = [&](auto& e) { return e.to_circuit_type(builder); }; - - FinalAccumulatedData acc_data = { - typename CT::AggregationObject{ - to_ct(aggregation_object.P0), - to_ct(aggregation_object.P1), - to_ct(aggregation_object.public_inputs), - aggregation_object.proof_witness_indices, - aggregation_object.has_data, - }, - - to_ct(new_commitments), - to_ct(new_nullifiers), - to_ct(nullified_commitments), - - to_ct(private_call_stack), - to_ct(public_call_stack), - to_ct(new_l2_to_l1_msgs), - - to_ct(encrypted_logs_hash), - to_ct(unencrypted_logs_hash), - - to_ct(encrypted_log_preimages_length), - to_ct(unencrypted_log_preimages_length), - - map(new_contracts, to_circuit_type), - map(optionally_revealed_data, to_circuit_type), - }; - - return acc_data; - }; - - template FinalAccumulatedData to_native_type() const - { - static_assert(std::is_same, NCT>::value); - auto to_nt = [&](auto& e) { return aztec3::utils::types::to_nt(e); }; - auto to_native_type = [](T& e) { return e.template to_native_type(); }; - - FinalAccumulatedData acc_data = { - typename NativeTypes::AggregationObject{ - to_nt(aggregation_object.P0), - to_nt(aggregation_object.P1), - to_nt(aggregation_object.public_inputs), - aggregation_object.proof_witness_indices, - aggregation_object.has_data, - }, - - to_nt(new_commitments), - to_nt(new_nullifiers), - to_nt(nullified_commitments), - - to_nt(private_call_stack), - to_nt(public_call_stack), - to_nt(new_l2_to_l1_msgs), - - to_nt(encrypted_logs_hash), - to_nt(unencrypted_logs_hash), - - to_nt(encrypted_log_preimages_length), - to_nt(unencrypted_log_preimages_length), - - map(new_contracts, to_native_type), - map(optionally_revealed_data, to_native_type), - }; - return acc_data; - } - - void set_public() - { - static_assert(!(std::is_same::value)); - - aggregation_object.add_proof_outputs_as_public_inputs(); - - set_array_public(new_commitments); - set_array_public(new_nullifiers); - set_array_public(nullified_commitments); - - set_array_public(private_call_stack); - set_array_public(public_call_stack); - set_array_public(new_l2_to_l1_msgs); - - set_array_public(encrypted_logs_hash); - set_array_public(unencrypted_logs_hash); - - set_array_public(new_contracts); - set_array_public(optionally_revealed_data); - } - - template void set_array_public(std::array& arr) - { - static_assert(!(std::is_same::value)); - for (T& e : arr) { - fr(e).set_public(); - } - } - - template void set_array_public(std::array, SIZE>& arr) - { - static_assert(!(std::is_same::value)); - for (auto& e : arr) { - e.set_public(); - } - } - - template void set_array_public(std::array, SIZE>& arr) - { - static_assert(!(std::is_same::value)); - for (auto& e : arr) { - e.set_public(); - } - } -}; - -} // namespace aztec3::circuits::abis diff --git a/circuits/cpp/src/aztec3/circuits/abis/function_data.hpp b/circuits/cpp/src/aztec3/circuits/abis/function_data.hpp deleted file mode 100644 index 784aa4691d0..00000000000 --- a/circuits/cpp/src/aztec3/circuits/abis/function_data.hpp +++ /dev/null @@ -1,91 +0,0 @@ -#pragma once - -#include "aztec3/circuits/abis/function_selector.hpp" -#include "aztec3/constants.hpp" -#include "aztec3/utils/types/circuit_types.hpp" -#include "aztec3/utils/types/convert.hpp" -#include "aztec3/utils/types/native_types.hpp" - -namespace aztec3::circuits::abis { - -// using plonk::stdlib::witness_t; -using aztec3::utils::types::CircuitTypes; -using aztec3::utils::types::NativeTypes; -using std::is_same; - -template struct FunctionData { - using uint32 = typename NCT::uint32; - using boolean = typename NCT::boolean; - using fr = typename NCT::fr; - - FunctionSelector selector; - boolean is_internal = false; - boolean is_private = false; - boolean is_constructor = false; - - MSGPACK_FIELDS(selector, is_internal, is_private, is_constructor); - - boolean operator==(FunctionData const& other) const - { - return selector == other.selector && is_internal == other.is_internal && is_private == other.is_private && - is_constructor == other.is_constructor; - }; - - template FunctionData> to_circuit_type(Builder& builder) const - { - static_assert((std::is_same::value)); - - // Capture the circuit builder: - auto to_ct = [&](auto& e) { return aztec3::utils::types::to_ct(builder, e); }; - - FunctionData> function_data = { - selector.to_circuit_type(builder), - to_ct(is_internal), - to_ct(is_private), - to_ct(is_constructor), - }; - - return function_data; - }; - - template FunctionData to_native_type() const - { - static_assert(std::is_same, NCT>::value); - auto to_native_type = [](T& e) { return e.template to_native_type(); }; - auto to_nt = [&](auto& e) { return aztec3::utils::types::to_nt(e); }; - - FunctionData function_data = { - to_native_type(selector), - to_nt(is_internal), - to_nt(is_private), - to_nt(is_constructor), - }; - - return function_data; - }; - - void set_public() - { - static_assert(!(std::is_same::value)); - - selector.set_public(); - fr(is_internal).set_public(); - fr(is_private).set_public(); - fr(is_constructor).set_public(); - } - - // TODO: this can all be packed into 1 field element, so this `hash` function should just return that field element. - fr hash() const - { - std::vector const inputs = { - fr(selector.value), - fr(is_internal), - fr(is_private), - fr(is_constructor), - }; - - return NCT::hash(inputs, GeneratorIndex::FUNCTION_DATA); - } -}; - -} // namespace aztec3::circuits::abis diff --git a/circuits/cpp/src/aztec3/circuits/abis/function_leaf_preimage.hpp b/circuits/cpp/src/aztec3/circuits/abis/function_leaf_preimage.hpp deleted file mode 100644 index f1461cea4b5..00000000000 --- a/circuits/cpp/src/aztec3/circuits/abis/function_leaf_preimage.hpp +++ /dev/null @@ -1,97 +0,0 @@ -#pragma once - -#include "aztec3/circuits/abis/function_selector.hpp" -#include "aztec3/constants.hpp" -#include "aztec3/utils/types/circuit_types.hpp" -#include "aztec3/utils/types/convert.hpp" -#include "aztec3/utils/types/native_types.hpp" - -namespace aztec3::circuits::abis { - -using aztec3::utils::types::CircuitTypes; -using aztec3::utils::types::NativeTypes; -using std::is_same; - -/** - * @brief A struct representing the "preimage" of a function tree leaf. - * Templated on NativeTypes/CircuitTypes. - * - * @details A FunctionLeafPreimage contains: - * - `selector` keccak hash of function signature truncated to NUM_FUNCTION_SELECTOR_BYTES - * - `is_private` boolean flag - * - `vk_hash` pedersen hash of the function verification key - * - `acir_hash` hash of the function's acir bytecode - * This struct includes a `hash()` function for computing its pedersen compression. - * There are also static functions for: - * - converting preimages between native/circuit types - * - serializing and deserializing preimages - * - writing a preimage to an ostream - */ -template struct FunctionLeafPreimage { - using boolean = typename NCT::boolean; - using fr = typename NCT::fr; - using uint32 = typename NCT::uint32; - - FunctionSelector selector = {}; - boolean is_internal = false; - boolean is_private = false; - fr vk_hash = 0; - fr acir_hash = 0; - - // For serialization, update with new fields - MSGPACK_FIELDS(selector, is_internal, is_private, vk_hash, acir_hash); - - boolean operator==(FunctionLeafPreimage const& other) const - { - return selector == other.selector && is_internal == other.is_internal && is_private == other.is_private && - vk_hash == other.vk_hash && acir_hash == other.acir_hash; - }; - - template FunctionLeafPreimage> to_circuit_type(Builder& builder) const - { - static_assert((std::is_same::value)); - - // Capture the circuit builder: - auto to_ct = [&](auto& e) { return aztec3::utils::types::to_ct(builder, e); }; - - FunctionLeafPreimage> preimage = { - selector.to_circuit_type(builder), to_ct(is_internal), to_ct(is_private), to_ct(vk_hash), to_ct(acir_hash), - }; - - return preimage; - }; - - template FunctionLeafPreimage to_native_type() const - { - static_assert(std::is_same, NCT>::value); - auto to_native_type = [](T& e) { return e.template to_native_type(); }; - auto to_nt = [&](auto& e) { return aztec3::utils::types::to_nt(e); }; - - FunctionLeafPreimage preimage = { - to_native_type(selector), to_nt(is_internal), to_nt(is_private), to_nt(vk_hash), to_nt(acir_hash), - }; - - return preimage; - }; - - void set_public() - { - static_assert(!(std::is_same::value)); - - selector.set_public(); - fr(is_internal).set_public(); - fr(is_private).set_public(); - vk_hash.set_public(); - acir_hash.set_public(); - } - - fr hash() const - { - std::vector const inputs = { - selector.value, fr(is_internal), fr(is_private), vk_hash, acir_hash, - }; - return NCT::hash(inputs, GeneratorIndex::FUNCTION_LEAF); - } -}; - -} // namespace aztec3::circuits::abis diff --git a/circuits/cpp/src/aztec3/circuits/abis/function_selector.hpp b/circuits/cpp/src/aztec3/circuits/abis/function_selector.hpp deleted file mode 100644 index 6506b9a40cd..00000000000 --- a/circuits/cpp/src/aztec3/circuits/abis/function_selector.hpp +++ /dev/null @@ -1,61 +0,0 @@ -#pragma once - -#include "aztec3/utils/types/circuit_types.hpp" -#include "aztec3/utils/types/convert.hpp" -#include "aztec3/utils/types/native_types.hpp" - -#include "barretenberg/serialize/msgpack.hpp" - -namespace aztec3::circuits::abis { - -using aztec3::utils::types::CircuitTypes; -using aztec3::utils::types::NativeTypes; - -template struct FunctionSelector { - using uint32 = typename NCT::uint32; - using boolean = typename NCT::boolean; - using fr = typename NCT::fr; - - uint32 value; // e.g. 1st 4-bytes of abi-encoding of function. - - MSGPACK_FIELDS(value); - - boolean operator==(FunctionSelector const& other) const { return value == other.value; }; - - template FunctionSelector> to_circuit_type(Builder& builder) const - { - static_assert((std::is_same::value)); - - // Capture the circuit builder: - auto to_ct = [&](auto& e) { return aztec3::utils::types::to_ct(builder, e); }; - - FunctionSelector> selector = { - to_ct(value), - }; - - return selector; - }; - - template FunctionSelector to_native_type() const - { - static_assert(std::is_same, NCT>::value); - auto to_nt = [&](auto& e) { return aztec3::utils::types::to_nt(e); }; - - FunctionSelector selector = { - to_nt(value), - }; - - return selector; - }; - - fr to_field() const { return fr(value); } - - void set_public() - { - static_assert(!(std::is_same::value)); - - fr(value).set_public(); - } -}; - -} // namespace aztec3::circuits::abis \ No newline at end of file diff --git a/circuits/cpp/src/aztec3/circuits/abis/global_variables.hpp b/circuits/cpp/src/aztec3/circuits/abis/global_variables.hpp deleted file mode 100644 index 79442dfed58..00000000000 --- a/circuits/cpp/src/aztec3/circuits/abis/global_variables.hpp +++ /dev/null @@ -1,88 +0,0 @@ -#pragma once -#include "function_data.hpp" -#include "tx_context.hpp" - -#include "aztec3/utils/array.hpp" -#include "aztec3/utils/types/circuit_types.hpp" -#include "aztec3/utils/types/convert.hpp" -#include "aztec3/utils/types/native_types.hpp" - -#include - -namespace aztec3::circuits::abis { - -using aztec3::utils::types::CircuitTypes; -using aztec3::utils::types::NativeTypes; - -template struct GlobalVariables { - using address = typename NCT::address; - using fr = typename NCT::fr; - using boolean = typename NCT::boolean; - - fr chain_id = 0; - fr version = 0; - fr block_number = 0; - fr timestamp = 0; - - // For serialization, update with new fields - MSGPACK_FIELDS(chain_id, version, block_number, timestamp); - - boolean operator==(GlobalVariables const& other) const - { - return chain_id == other.chain_id && version == other.version && block_number == other.block_number && - timestamp == other.timestamp; - }; - - /** - * @brief Returns an object containing all global variables set to zero. - * - * @return GlobalVariables - */ - static GlobalVariables empty() - { - GlobalVariables globals = { 0, 0, 0, 0 }; - return globals; - } - - template GlobalVariables> to_circuit_type(Builder& builder) const - { - static_assert((std::is_same::value)); - - // Capture the builder: - auto to_ct = [&](auto& e) { return aztec3::utils::types::to_ct(builder, e); }; - auto to_circuit_type = [&](auto& e) { return e.to_circuit_type(builder); }; - - GlobalVariables> globals = { - to_ct(chain_id), - to_ct(version), - to_ct(block_number), - to_ct(timestamp), - }; - - return globals; - }; - - - fr hash() const - { - std::vector inputs; - inputs.push_back(chain_id); - inputs.push_back(version); - inputs.push_back(block_number); - inputs.push_back(timestamp); - - return NCT::hash(inputs, GeneratorIndex::GLOBAL_VARIABLES); - } - - void set_public() - { - static_assert(!(std::is_same::value)); - - chain_id.set_public(); - version.set_public(); - block_number.set_public(); - timestamp.set_public(); - } -}; // namespace aztec3::circuits::abis - -} // namespace aztec3::circuits::abis diff --git a/circuits/cpp/src/aztec3/circuits/abis/index.hpp b/circuits/cpp/src/aztec3/circuits/abis/index.hpp deleted file mode 100644 index dc500bbe9a7..00000000000 --- a/circuits/cpp/src/aztec3/circuits/abis/index.hpp +++ /dev/null @@ -1,6 +0,0 @@ -#include "call_context.hpp" -#include "call_stack_item.hpp" -#include "contract_storage_read.hpp" -#include "contract_storage_update_request.hpp" -#include "function_data.hpp" -#include "private_circuit_public_inputs.hpp" \ No newline at end of file diff --git a/circuits/cpp/src/aztec3/circuits/abis/kernel_circuit_public_inputs.hpp b/circuits/cpp/src/aztec3/circuits/abis/kernel_circuit_public_inputs.hpp deleted file mode 100644 index d14a11eb796..00000000000 --- a/circuits/cpp/src/aztec3/circuits/abis/kernel_circuit_public_inputs.hpp +++ /dev/null @@ -1,78 +0,0 @@ -#pragma once -#include "combined_accumulated_data.hpp" -#include "combined_constant_data.hpp" - -#include "aztec3/utils/types/circuit_types.hpp" -#include "aztec3/utils/types/convert.hpp" -#include "aztec3/utils/types/native_types.hpp" - -#include - -namespace aztec3::circuits::abis { - -using aztec3::utils::types::CircuitTypes; -using aztec3::utils::types::NativeTypes; -using std::is_same; - -template struct KernelCircuitPublicInputs { - using fr = typename NCT::fr; - using boolean = typename NCT::boolean; - - CombinedAccumulatedData end{}; - CombinedConstantData constants{}; - - boolean is_private = true; // TODO: might need to instantiate from witness! - - // for serialization, update with new fields - MSGPACK_FIELDS(end, constants, is_private); - - boolean operator==(KernelCircuitPublicInputs const& other) const - { - return msgpack_derived_equals(*this, other); - }; - - template KernelCircuitPublicInputs> to_circuit_type(Builder& builder) const - { - static_assert((std::is_same::value)); - - // Capture the circuit builder: - auto to_ct = [&](auto& e) { return aztec3::utils::types::to_ct(builder, e); }; - - KernelCircuitPublicInputs> private_inputs = { - end.to_circuit_type(builder), - constants.to_circuit_type(builder), - - to_ct(is_private), - }; - - return private_inputs; - }; - - template KernelCircuitPublicInputs to_native_type() const - { - static_assert(std::is_same, NCT>::value); - auto to_nt = [&](auto& e) { return aztec3::utils::types::to_nt(e); }; - auto to_native_type = [](T& e) { return e.template to_native_type(); }; - - KernelCircuitPublicInputs pis = { - to_native_type(end), - to_native_type(constants), - - to_nt(is_private), - }; - - return pis; - }; - - void set_public() - { - static_assert(!(std::is_same::value)); - - end.set_public(); - constants.set_public(); - - fr(is_private).set_public(); - } -}; - -} // namespace aztec3::circuits::abis diff --git a/circuits/cpp/src/aztec3/circuits/abis/kernel_circuit_public_inputs_final.hpp b/circuits/cpp/src/aztec3/circuits/abis/kernel_circuit_public_inputs_final.hpp deleted file mode 100644 index 90ce25eedd2..00000000000 --- a/circuits/cpp/src/aztec3/circuits/abis/kernel_circuit_public_inputs_final.hpp +++ /dev/null @@ -1,79 +0,0 @@ -#pragma once -#include "combined_constant_data.hpp" -#include "final_accumulated_data.hpp" - -#include "aztec3/utils/types/circuit_types.hpp" -#include "aztec3/utils/types/convert.hpp" -#include "aztec3/utils/types/native_types.hpp" - -#include - -namespace aztec3::circuits::abis { - -using aztec3::utils::types::CircuitTypes; -using aztec3::utils::types::NativeTypes; -using std::is_same; - -template struct KernelCircuitPublicInputsFinal { - using fr = typename NCT::fr; - using boolean = typename NCT::boolean; - - FinalAccumulatedData end{}; - CombinedConstantData constants{}; - - boolean is_private = true; // TODO: might need to instantiate from witness! - - // for serialization, update with new fields - MSGPACK_FIELDS(end, constants, is_private); - - boolean operator==(KernelCircuitPublicInputsFinal const& other) const - { - return msgpack_derived_equals(*this, other); - } - - template - KernelCircuitPublicInputsFinal> to_circuit_type(Builder& builder) const - { - static_assert((std::is_same::value)); - - // Capture the circuit builder: - auto to_ct = [&](auto& e) { return aztec3::utils::types::to_ct(builder, e); }; - - KernelCircuitPublicInputsFinal> private_inputs = { - end.to_circuit_type(builder), - constants.to_circuit_type(builder), - - to_ct(is_private), - }; - - return private_inputs; - }; - - template KernelCircuitPublicInputsFinal to_native_type() const - { - static_assert(std::is_same, NCT>::value); - auto to_nt = [&](auto& e) { return aztec3::utils::types::to_nt(e); }; - auto to_native_type = [](T& e) { return e.template to_native_type(); }; - - KernelCircuitPublicInputsFinal pis = { - to_native_type(end), - to_native_type(constants), - - to_nt(is_private), - }; - - return pis; - }; - - void set_public() - { - static_assert(!(std::is_same::value)); - - end.set_public(); - constants.set_public(); - - fr(is_private).set_public(); - } -}; - -} // namespace aztec3::circuits::abis diff --git a/circuits/cpp/src/aztec3/circuits/abis/membership_witness.hpp b/circuits/cpp/src/aztec3/circuits/abis/membership_witness.hpp deleted file mode 100644 index 45ad4680b2d..00000000000 --- a/circuits/cpp/src/aztec3/circuits/abis/membership_witness.hpp +++ /dev/null @@ -1,71 +0,0 @@ -#pragma once - -#include "aztec3/utils/array.hpp" -#include "aztec3/utils/types/circuit_types.hpp" -#include "aztec3/utils/types/convert.hpp" - -namespace aztec3::circuits::abis { - -using aztec3::utils::is_array_empty; -using aztec3::utils::types::CircuitTypes; -using aztec3::utils::types::NativeTypes; -using std::is_same; - -template struct MembershipWitness { - using fr = typename NCT::fr; - using boolean = typename NCT::boolean; - - fr leaf_index = 0; - std::array sibling_path{}; - - MSGPACK_FIELDS(leaf_index, sibling_path); - // for schema serialization - void msgpack_schema(auto& packer) const { packer.pack_with_name("MembershipWitness" + std::to_string(N), *this); } - boolean operator==(MembershipWitness const& other) const - { - return leaf_index == other.leaf_index && sibling_path == other.sibling_path; - }; - - template MembershipWitness, N> to_circuit_type(Builder& builder) const - { - static_assert((std::is_same::value)); - - // Capture the circuit builder: - auto to_ct = [&](auto& e) { return aztec3::utils::types::to_ct(builder, e); }; - - MembershipWitness, N> witness = { - to_ct(leaf_index), - map(sibling_path, to_ct), - }; - - return witness; - } - - template MembershipWitness to_native_type() const - { - static_assert((std::is_same, NCT>::value)); - - auto to_nt = [&](auto& e) { return aztec3::utils::types::to_nt(e); }; - - MembershipWitness witness = { - to_nt(leaf_index), - map(sibling_path, to_nt), - }; - - return witness; - } - - void set_public() - { - static_assert(!(std::is_same::value)); - - leaf_index.set_public(); - for (fr const& e : sibling_path) { - e.set_public(); - } - } - - boolean is_empty() const { return aztec3::utils::is_empty(leaf_index) && is_array_empty(sibling_path); } -}; - -} // namespace aztec3::circuits::abis diff --git a/circuits/cpp/src/aztec3/circuits/abis/new_contract_data.hpp b/circuits/cpp/src/aztec3/circuits/abis/new_contract_data.hpp deleted file mode 100644 index 4933e7716ce..00000000000 --- a/circuits/cpp/src/aztec3/circuits/abis/new_contract_data.hpp +++ /dev/null @@ -1,100 +0,0 @@ -#pragma once - -#include "aztec3/constants.hpp" -#include "aztec3/utils/msgpack_derived_equals.hpp" -#include "aztec3/utils/types/circuit_types.hpp" -#include "aztec3/utils/types/convert.hpp" - -#include - -namespace aztec3::circuits::abis { - -using aztec3::utils::types::CircuitTypes; -using aztec3::utils::types::NativeTypes; -using std::is_same; - -template struct NewContractData { - using address = typename NCT::address; - using fr = typename NCT::fr; - using boolean = typename NCT::boolean; - - address contract_address = 0; - address portal_contract_address = 0; - fr function_tree_root = 0; - // for serialization, update with new fields - MSGPACK_FIELDS(contract_address, portal_contract_address, function_tree_root); - - boolean operator==(NewContractData const& other) const - { - // we can't use =default with a custom boolean, but we can use a msgpack-derived utility - return utils::msgpack_derived_equals(*this, other); - }; - - template NewContractData> to_circuit_type(Builder& builder) const - { - static_assert((std::is_same::value)); - - auto to_ct = [&](auto& e) { return aztec3::utils::types::to_ct(builder, e); }; - - NewContractData> new_contract_data = { to_ct(contract_address), - to_ct(portal_contract_address), - to_ct(function_tree_root) }; - - return new_contract_data; - }; - - template NewContractData to_native_type() const - { - static_assert(std::is_same, NCT>::value); - - auto to_nt = [&](auto& e) { return aztec3::utils::types::to_nt(e); }; - - NewContractData new_contract_data = { to_nt(contract_address), - to_nt(portal_contract_address), - to_nt(function_tree_root) }; - - return new_contract_data; - }; - - boolean is_empty() const - { - return ((contract_address.to_field().is_zero()) && (portal_contract_address.to_field().is_zero()) && - (function_tree_root.is_zero())); - } - - void set_public() - { - static_assert(!(std::is_same::value)); - - contract_address.to_field().set_public(); - portal_contract_address.to_field().set_public(); - function_tree_root.set_public(); - } - - fr hash() const - { - // as per the circuit implementation, if contract address == zero then return a zero leaf - if (is_empty()) { - return fr::zero(); - } - std::vector const inputs = { - fr(contract_address), - fr(portal_contract_address), - fr(function_tree_root), - }; - - return NCT::hash(inputs, GeneratorIndex::CONTRACT_LEAF); - } - - void conditional_select(const boolean& condition, const NewContractData& other) - { - contract_address = address::conditional_assign(condition, other.contract_address, contract_address); - portal_contract_address = - address::conditional_assign(condition, other.portal_contract_address, portal_contract_address); - function_tree_root = fr::conditional_assign(condition, other.function_tree_root, function_tree_root); - } -}; - -template using ContractLeafPreimage = NewContractData; - -} // namespace aztec3::circuits::abis diff --git a/circuits/cpp/src/aztec3/circuits/abis/optionally_revealed_data.hpp b/circuits/cpp/src/aztec3/circuits/abis/optionally_revealed_data.hpp deleted file mode 100644 index 41e118d6049..00000000000 --- a/circuits/cpp/src/aztec3/circuits/abis/optionally_revealed_data.hpp +++ /dev/null @@ -1,105 +0,0 @@ -#pragma once -#include "function_data.hpp" - -#include "aztec3/utils/types/circuit_types.hpp" -#include "aztec3/utils/types/convert.hpp" -#include "aztec3/utils/types/native_types.hpp" - -#include - -namespace aztec3::circuits::abis { - -using aztec3::utils::types::CircuitTypes; -using aztec3::utils::types::NativeTypes; - -template struct OptionallyRevealedData { - using address = typename NCT::address; - using boolean = typename NCT::boolean; - using fr = typename NCT::fr; - - fr call_stack_item_hash = 0; - FunctionData function_data{}; - fr vk_hash = 0; - address portal_contract_address = 0; - boolean pay_fee_from_l1 = false; - boolean pay_fee_from_public_l2 = false; - boolean called_from_l1 = false; - boolean called_from_public_l2 = false; - - // for serialization: update up with new fields - MSGPACK_FIELDS(call_stack_item_hash, - function_data, - vk_hash, - portal_contract_address, - pay_fee_from_l1, - pay_fee_from_public_l2, - called_from_l1, - called_from_public_l2); - boolean operator==(OptionallyRevealedData const& other) const - { - return call_stack_item_hash == other.call_stack_item_hash && function_data == other.function_data && - vk_hash == other.vk_hash && portal_contract_address == other.portal_contract_address && - pay_fee_from_l1 == other.pay_fee_from_l1 && pay_fee_from_public_l2 == other.pay_fee_from_public_l2 && - called_from_l1 == other.called_from_l1 && called_from_public_l2 == other.called_from_public_l2; - }; - - template OptionallyRevealedData> to_circuit_type(Builder& builder) const - { - static_assert((std::is_same::value)); - - // Capture the circuit builder: - auto to_ct = [&](auto& e) { return aztec3::utils::types::to_ct(builder, e); }; - - OptionallyRevealedData> data = { - to_ct(call_stack_item_hash), - function_data.to_circuit_type(builder), - to_ct(vk_hash), - to_ct(portal_contract_address), - to_ct(pay_fee_from_l1), - to_ct(pay_fee_from_public_l2), - to_ct(called_from_l1), - to_ct(called_from_public_l2), - }; - - return data; - }; - - template OptionallyRevealedData to_native_type() const - { - static_assert(std::is_same, NCT>::value); - auto to_nt = [&](auto& e) { return aztec3::utils::types::to_nt(e); }; - auto to_native_type = [](T& e) { return e.template to_native_type(); }; - - OptionallyRevealedData data = { - to_nt(call_stack_item_hash), to_native_type(function_data), to_nt(vk_hash), - to_nt(portal_contract_address), to_nt(pay_fee_from_l1), to_nt(pay_fee_from_public_l2), - to_nt(called_from_l1), to_nt(called_from_public_l2), - }; - - return data; - }; - - void set_public() - { - static_assert(!(std::is_same::value)); - - call_stack_item_hash.set_public(); - function_data.set_public(); - vk_hash.set_public(); - portal_contract_address.to_field().set_public(); - fr(pay_fee_from_l1).set_public(); - fr(pay_fee_from_public_l2).set_public(); - fr(called_from_l1).set_public(); - fr(called_from_public_l2).set_public(); - } - - template void set_array_public(std::array& arr) - { - static_assert(!(std::is_same::value)); - for (T& e : arr) { - fr(e).set_public(); - } - } -}; - -} // namespace aztec3::circuits::abis diff --git a/circuits/cpp/src/aztec3/circuits/abis/packers.hpp b/circuits/cpp/src/aztec3/circuits/abis/packers.hpp deleted file mode 100644 index 45291c516ac..00000000000 --- a/circuits/cpp/src/aztec3/circuits/abis/packers.hpp +++ /dev/null @@ -1,230 +0,0 @@ -#pragma once -#include "msgpack/v3/adaptor/detail/cpp11_define_map_decl.hpp" - -#include "aztec3/constants.hpp" - -#include "barretenberg/serialize/msgpack_impl/name_value_pair_macro.hpp" - -namespace aztec3::circuits::abis { - -// Represents constants during serialization (only) -struct ConstantsPacker { - template void msgpack_pack(Packer& packer) const - { - auto pack = [&](auto&... args) { - msgpack::type::define_map{ args... }.msgpack_pack(packer); - }; - - // Note: NVP macro can handle up to 30 arguments so we call it multiple times here. If adding a new constant - // add it to the last call or introduce a new one if the last call is already "full". - pack(NVP(ARGS_LENGTH, - RETURN_VALUES_LENGTH, - MAX_NEW_COMMITMENTS_PER_CALL, - MAX_NEW_NULLIFIERS_PER_CALL, - MAX_PRIVATE_CALL_STACK_LENGTH_PER_CALL, - MAX_PUBLIC_CALL_STACK_LENGTH_PER_CALL, - MAX_NEW_L2_TO_L1_MSGS_PER_CALL, - MAX_PUBLIC_DATA_UPDATE_REQUESTS_PER_CALL, - MAX_PUBLIC_DATA_READS_PER_CALL, - MAX_READ_REQUESTS_PER_CALL, - MAX_NEW_COMMITMENTS_PER_TX, - MAX_NEW_NULLIFIERS_PER_TX, - MAX_PRIVATE_CALL_STACK_LENGTH_PER_TX, - MAX_PUBLIC_CALL_STACK_LENGTH_PER_TX, - MAX_NEW_L2_TO_L1_MSGS_PER_TX, - MAX_PUBLIC_DATA_UPDATE_REQUESTS_PER_TX, - MAX_PUBLIC_DATA_READS_PER_TX, - MAX_NEW_CONTRACTS_PER_TX, - MAX_OPTIONALLY_REVEALED_DATA_LENGTH_PER_TX, - MAX_READ_REQUESTS_PER_TX), - NVP(NUM_ENCRYPTED_LOGS_HASHES_PER_TX, - NUM_UNENCRYPTED_LOGS_HASHES_PER_TX, - NUMBER_OF_L1_L2_MESSAGES_PER_ROLLUP, - KERNELS_PER_BASE_ROLLUP, - MAX_NEW_NULLIFIERS_PER_BASE_ROLLUP, - MAX_PUBLIC_DATA_UPDATE_REQUESTS_PER_BASE_ROLLUP, - MAX_PUBLIC_DATA_READS_PER_BASE_ROLLUP, - VK_TREE_HEIGHT, - FUNCTION_TREE_HEIGHT, - CONTRACT_TREE_HEIGHT, - NOTE_HASH_TREE_HEIGHT, - PUBLIC_DATA_TREE_HEIGHT, - NULLIFIER_TREE_HEIGHT, - L1_TO_L2_MSG_TREE_HEIGHT, - ROLLUP_VK_TREE_HEIGHT, - CONTRACT_SUBTREE_HEIGHT, - CONTRACT_SUBTREE_SIBLING_PATH_LENGTH, - NOTE_HASH_SUBTREE_HEIGHT, - NOTE_HASH_SUBTREE_SIBLING_PATH_LENGTH, - NULLIFIER_SUBTREE_HEIGHT, - ARCHIVE_HEIGHT, - NULLIFIER_SUBTREE_SIBLING_PATH_LENGTH, - L1_TO_L2_MSG_SUBTREE_HEIGHT), - NVP(L1_TO_L2_MSG_SUBTREE_SIBLING_PATH_LENGTH, - FUNCTION_SELECTOR_NUM_BYTES, - MAPPING_SLOT_PEDERSEN_SEPARATOR, - NUM_FIELDS_PER_SHA256, - L1_TO_L2_MESSAGE_LENGTH, - L1_TO_L2_MESSAGE_ORACLE_CALL_LENGTH, - MAX_NOTE_FIELDS_LENGTH, - GET_NOTE_ORACLE_RETURN_LENGTH, - MAX_NOTES_PER_PAGE, - VIEW_NOTE_ORACLE_RETURN_LENGTH, - CALL_CONTEXT_LENGTH, - BLOCK_HEADER_LENGTH, - FUNCTION_DATA_LENGTH, - CONTRACT_DEPLOYMENT_DATA_LENGTH, - PRIVATE_CIRCUIT_PUBLIC_INPUTS_LENGTH, - CONTRACT_STORAGE_UPDATE_REQUEST_LENGTH, - CONTRACT_STORAGE_READ_LENGTH, - PUBLIC_CIRCUIT_PUBLIC_INPUTS_LENGTH, - GET_NOTES_ORACLE_RETURN_LENGTH, - EMPTY_NULLIFIED_COMMITMENT), - NVP(CALL_PRIVATE_FUNCTION_RETURN_SIZE, - PUBLIC_CIRCUIT_PUBLIC_INPUTS_HASH_INPUT_LENGTH, - PRIVATE_CIRCUIT_PUBLIC_INPUTS_HASH_INPUT_LENGTH, - KERNELS_PER_BASE_ROLLUP, - COMMITMENTS_NUM_BYTES_PER_BASE_ROLLUP, - NULLIFIERS_NUM_BYTES_PER_BASE_ROLLUP, - PUBLIC_DATA_WRITES_NUM_BYTES_PER_BASE_ROLLUP, - CONTRACTS_NUM_BYTES_PER_BASE_ROLLUP, - CONTRACT_DATA_NUM_BYTES_PER_BASE_ROLLUP, - CONTRACT_DATA_NUM_BYTES_PER_BASE_ROLLUP_UNPADDED, - L2_TO_L1_MSGS_NUM_BYTES_PER_BASE_ROLLUP, - LOGS_HASHES_NUM_BYTES_PER_BASE_ROLLUP)); // <-- Add names of new constants here - } -}; - -struct GeneratorIndexPacker { - template void msgpack_pack(Packer& packer) const - { - auto pack = [&](auto&... args) { - msgpack::type::define_map{ args... }.msgpack_pack(packer); - }; - - int COMMITMENT = GeneratorIndex::COMMITMENT; - int COMMITMENT_NONCE = GeneratorIndex::COMMITMENT_NONCE; - int UNIQUE_COMMITMENT = GeneratorIndex::UNIQUE_COMMITMENT; - int SILOED_COMMITMENT = GeneratorIndex::SILOED_COMMITMENT; - int NULLIFIER = GeneratorIndex::NULLIFIER; - int INITIALIZATION_NULLIFIER = GeneratorIndex::INITIALIZATION_NULLIFIER; - int OUTER_NULLIFIER = GeneratorIndex::OUTER_NULLIFIER; - int PUBLIC_DATA_READ = GeneratorIndex::PUBLIC_DATA_READ; - int PUBLIC_DATA_UPDATE_REQUEST = GeneratorIndex::PUBLIC_DATA_UPDATE_REQUEST; - int FUNCTION_DATA = GeneratorIndex::FUNCTION_DATA; - int FUNCTION_LEAF = GeneratorIndex::FUNCTION_LEAF; - int CONTRACT_DEPLOYMENT_DATA = GeneratorIndex::CONTRACT_DEPLOYMENT_DATA; - int CONSTRUCTOR = GeneratorIndex::CONSTRUCTOR; - int CONSTRUCTOR_ARGS = GeneratorIndex::CONSTRUCTOR_ARGS; - int CONTRACT_ADDRESS = GeneratorIndex::CONTRACT_ADDRESS; - int CONTRACT_LEAF = GeneratorIndex::CONTRACT_LEAF; - int CALL_CONTEXT = GeneratorIndex::CALL_CONTEXT; - int CALL_STACK_ITEM = GeneratorIndex::CALL_STACK_ITEM; - int CALL_STACK_ITEM_2 = GeneratorIndex::CALL_STACK_ITEM_2; - int L1_TO_L2_MESSAGE_SECRET = GeneratorIndex::L1_TO_L2_MESSAGE_SECRET; - int L2_TO_L1_MSG = GeneratorIndex::L2_TO_L1_MSG; - int TX_CONTEXT = GeneratorIndex::TX_CONTEXT; - int PUBLIC_LEAF_INDEX = GeneratorIndex::PUBLIC_LEAF_INDEX; - int PUBLIC_DATA_LEAF = GeneratorIndex::PUBLIC_DATA_LEAF; - int SIGNED_TX_REQUEST = GeneratorIndex::SIGNED_TX_REQUEST; - int GLOBAL_VARIABLES = GeneratorIndex::GLOBAL_VARIABLES; - int PARTIAL_ADDRESS = GeneratorIndex::PARTIAL_ADDRESS; - int BLOCK_HASH = GeneratorIndex::BLOCK_HASH; - int SIDE_EFFECT = GeneratorIndex::SIDE_EFFECT; - int TX_REQUEST = GeneratorIndex::TX_REQUEST; - int SIGNATURE_PAYLOAD = GeneratorIndex::SIGNATURE_PAYLOAD; - int VK = GeneratorIndex::VK; - int PRIVATE_CIRCUIT_PUBLIC_INPUTS = GeneratorIndex::PRIVATE_CIRCUIT_PUBLIC_INPUTS; - int PUBLIC_CIRCUIT_PUBLIC_INPUTS = GeneratorIndex::PUBLIC_CIRCUIT_PUBLIC_INPUTS; - int FUNCTION_ARGS = GeneratorIndex::FUNCTION_ARGS; - - - // Note: NVP macro can handle up to 20 arguments so we call it multiple times here. If adding a new constant - // add it to the last call or introduce a new one if the last call is already "full". - pack(NVP(COMMITMENT, - COMMITMENT_NONCE, - UNIQUE_COMMITMENT, - SILOED_COMMITMENT, - NULLIFIER, - INITIALIZATION_NULLIFIER, - OUTER_NULLIFIER, - PUBLIC_DATA_READ, - PUBLIC_DATA_UPDATE_REQUEST, - FUNCTION_DATA, - FUNCTION_LEAF, - CONTRACT_DEPLOYMENT_DATA, - CONSTRUCTOR, - CONSTRUCTOR_ARGS, - CONTRACT_ADDRESS, - CONTRACT_LEAF, - CALL_CONTEXT, - CALL_STACK_ITEM, - CALL_STACK_ITEM_2, - L1_TO_L2_MESSAGE_SECRET), - NVP(L2_TO_L1_MSG, - TX_CONTEXT, - PUBLIC_LEAF_INDEX, - PUBLIC_DATA_LEAF, - SIGNED_TX_REQUEST, - GLOBAL_VARIABLES, - PARTIAL_ADDRESS, - BLOCK_HASH, - SIDE_EFFECT, - TX_REQUEST, - SIGNATURE_PAYLOAD, - VK, - PRIVATE_CIRCUIT_PUBLIC_INPUTS, - PUBLIC_CIRCUIT_PUBLIC_INPUTS, - FUNCTION_ARGS)); - } -}; - -struct StorageSlotGeneratorIndexPacker { - template void msgpack_pack(Packer& packer) const - { - auto pack = [&](auto&... args) { - msgpack::type::define_map{ args... }.msgpack_pack(packer); - }; - - int BASE_SLOT = StorageSlotGeneratorIndex::BASE_SLOT; - int MAPPING_SLOT = StorageSlotGeneratorIndex::MAPPING_SLOT; - int MAPPING_SLOT_PLACEHOLDER = StorageSlotGeneratorIndex::MAPPING_SLOT_PLACEHOLDER; - - pack(NVP(BASE_SLOT, MAPPING_SLOT, MAPPING_SLOT_PLACEHOLDER)); - } -}; - -struct PrivateStateNoteGeneratorIndexPacker { - template void msgpack_pack(Packer& packer) const - { - auto pack = [&](auto&... args) { - msgpack::type::define_map{ args... }.msgpack_pack(packer); - }; - - int VALUE = PrivateStateNoteGeneratorIndex::VALUE; - int OWNER = PrivateStateNoteGeneratorIndex::OWNER; - int CREATOR = PrivateStateNoteGeneratorIndex::CREATOR; - int SALT = PrivateStateNoteGeneratorIndex::SALT; - int NONCE = PrivateStateNoteGeneratorIndex::NONCE; - int MEMO = PrivateStateNoteGeneratorIndex::MEMO; - int IS_DUMMY = PrivateStateNoteGeneratorIndex::IS_DUMMY; - - pack(NVP(VALUE, OWNER, CREATOR, SALT, NONCE, MEMO, IS_DUMMY)); - } -}; - -struct PrivateStateTypePacker { - template void msgpack_pack(Packer& packer) const - { - auto pack = [&](auto&... args) { - msgpack::type::define_map{ args... }.msgpack_pack(packer); - }; - - int PARTITIONED = PrivateStateType::PARTITIONED; - int WHOLE = PrivateStateType::WHOLE; - - pack(NVP(PARTITIONED, WHOLE)); - } -}; - -} // namespace aztec3::circuits::abis diff --git a/circuits/cpp/src/aztec3/circuits/abis/point.hpp b/circuits/cpp/src/aztec3/circuits/abis/point.hpp deleted file mode 100644 index ec0bf26b478..00000000000 --- a/circuits/cpp/src/aztec3/circuits/abis/point.hpp +++ /dev/null @@ -1,66 +0,0 @@ -#pragma once -#include "aztec3/circuits/abis/coordinate.hpp" -#include "aztec3/constants.hpp" -#include "aztec3/utils/types/circuit_types.hpp" -#include "aztec3/utils/types/convert.hpp" -#include "aztec3/utils/types/native_types.hpp" - -#include - -namespace aztec3::circuits::abis { - -using aztec3::GeneratorIndex; -using aztec3::utils::types::CircuitTypes; -using aztec3::utils::types::NativeTypes; - -template struct Point { - using fr = typename NCT::fr; - using boolean = typename NCT::boolean; - - fr x; - fr y; - - // for serialization, update with new fields - MSGPACK_FIELDS(x, y); - bool operator==(Point const&) const = default; - - template Point> to_circuit_type(Builder& builder) const - { - static_assert((std::is_same::value)); - - auto to_ct = [&](auto& e) { return aztec3::utils::types::to_ct(builder, e); }; - - Point> point = { to_ct(x), to_ct(y) }; - - return point; - }; - - template Point to_native_type() const - { - static_assert((std::is_same, NCT>::value)); - - auto to_nt = [&](auto& e) { return aztec3::utils::types::to_nt(e); }; - - Point point = { to_nt(x), to_nt(y) }; - - return point; - }; - - void set_public() - { - static_assert(!(std::is_same::value)); - - x.set_public(); - y.set_public(); - } - - void assert_is_zero() - { - static_assert(!(std::is_same::value)); - - x.assert_is_zero(); - y.assert_is_zero(); - } -}; - -} // namespace aztec3::circuits::abis diff --git a/circuits/cpp/src/aztec3/circuits/abis/previous_kernel_data.hpp b/circuits/cpp/src/aztec3/circuits/abis/previous_kernel_data.hpp deleted file mode 100644 index 588738eb076..00000000000 --- a/circuits/cpp/src/aztec3/circuits/abis/previous_kernel_data.hpp +++ /dev/null @@ -1,73 +0,0 @@ -#pragma once -#include "aztec3/circuits/abis/kernel_circuit_public_inputs.hpp" -#include "aztec3/utils/types/circuit_types.hpp" -#include "aztec3/utils/types/convert.hpp" -#include "aztec3/utils/types/native_types.hpp" - -#include - -namespace aztec3::circuits::abis { - -using aztec3::utils::types::CircuitTypes; -using aztec3::utils::types::NativeTypes; -using std::is_same; - -// @todo Naming should not be previous. Annoying. -template struct PreviousKernelData { - using fr = typename NCT::fr; - using boolean = typename NCT::boolean; - using VK = typename NCT::VK; - using uint32 = typename NCT::uint32; - - KernelCircuitPublicInputs public_inputs{}; // TODO: not needed as already contained in proof? - NativeTypes::Proof proof{}; // TODO: how to express proof as native/circuit type when it gets used as a buffer? - std::shared_ptr vk; - - // TODO: this index and path are meant to be those of a leaf within the tree of _kernel circuit_ vks; not the tree - // of functions within the contract tree. - uint32 vk_index = 0; - std::array vk_path{}; - - // for serialization, update with new fields - MSGPACK_FIELDS(public_inputs, proof, vk, vk_index, vk_path); - boolean operator==(PreviousKernelData const& other) const - { - // WARNING: proof not checked! - return public_inputs == other.public_inputs && - // proof == other.proof && - vk == other.vk && vk_index == other.vk_index && vk_path == other.vk_path; - }; - - // WARNING: the `proof` does NOT get converted! - template PreviousKernelData> to_circuit_type(Builder& builder) const - { - typedef CircuitTypes CT; - static_assert((std::is_same::value)); - - // Capture the circuit builder: - auto to_ct = [&](auto& e) { return aztec3::utils::types::to_ct(builder, e); }; - - PreviousKernelData> data = { - public_inputs.to_circuit_type(builder), - proof, // Notice: not converted! Stays as native. - CT::VK::from_witness(&builder, vk), - to_ct(vk_index), - to_ct(vk_path), - }; - - return data; - }; - -}; // namespace aztec3::circuits::abis::private_kernel - -template inline void read(B& buf, verification_key& key) -{ - using serialize::read; - // TODO(AD): We read this as if it were verification_key_data. - // TODO(AD): This seems like it could be rethought. - verification_key_data data; - read(buf, data); - key = verification_key{ std::move(data), barretenberg::srs::get_crs_factory()->get_verifier_crs() }; -} - -} // namespace aztec3::circuits::abis diff --git a/circuits/cpp/src/aztec3/circuits/abis/private_circuit_public_inputs.hpp b/circuits/cpp/src/aztec3/circuits/abis/private_circuit_public_inputs.hpp deleted file mode 100644 index c10e84c6d17..00000000000 --- a/circuits/cpp/src/aztec3/circuits/abis/private_circuit_public_inputs.hpp +++ /dev/null @@ -1,691 +0,0 @@ -#pragma once - -#include "call_context.hpp" -#include "contract_deployment_data.hpp" - -#include "aztec3/circuits/abis/block_header.hpp" -#include "aztec3/constants.hpp" -#include "aztec3/utils/types/circuit_types.hpp" -#include "aztec3/utils/types/convert.hpp" -#include "aztec3/utils/types/native_types.hpp" - -#include - -namespace aztec3::circuits::abis { - -using aztec3::utils::types::CircuitTypes; -using aztec3::utils::types::NativeTypes; -using plonk::stdlib::witness_t; - -template class PrivateCircuitPublicInputs { - using fr = typename NCT::fr; - using boolean = typename NCT::boolean; - - public: - CallContext call_context{}; - - fr args_hash = 0; - std::array return_values{}; - - std::array read_requests{}; - - std::array new_commitments{}; - std::array new_nullifiers{}; - std::array nullified_commitments{}; - - std::array private_call_stack{}; - std::array public_call_stack{}; - std::array new_l2_to_l1_msgs{}; - - std::array encrypted_logs_hash{}; - std::array unencrypted_logs_hash{}; - - // Here so that the gas cost of this request can be measured by circuits, without actually needing to feed in the - // variable-length data. - fr encrypted_log_preimages_length = 0; - fr unencrypted_log_preimages_length = 0; - - BlockHeader block_header{}; - - ContractDeploymentData contract_deployment_data{}; - - fr chain_id = 0; - fr version = 0; - - // For serialization, update with new fields - MSGPACK_FIELDS(call_context, - args_hash, - return_values, - read_requests, - new_commitments, - new_nullifiers, - nullified_commitments, - private_call_stack, - public_call_stack, - new_l2_to_l1_msgs, - encrypted_logs_hash, - unencrypted_logs_hash, - encrypted_log_preimages_length, - unencrypted_log_preimages_length, - block_header, - contract_deployment_data, - chain_id, - version); - - boolean operator==(PrivateCircuitPublicInputs const& other) const - { - return call_context == other.call_context && args_hash == other.args_hash && - return_values == other.return_values && read_requests == other.read_requests && - new_commitments == other.new_commitments && - new_nullifiers == other.new_nullifiers && nullified_commitments == other.nullified_commitments && - private_call_stack == other.private_call_stack && public_call_stack == other.public_call_stack && - new_l2_to_l1_msgs == other.new_l2_to_l1_msgs && encrypted_logs_hash == other.encrypted_logs_hash && - unencrypted_logs_hash == other.unencrypted_logs_hash && - encrypted_log_preimages_length == other.encrypted_log_preimages_length && - unencrypted_log_preimages_length == other.unencrypted_log_preimages_length && - block_header == other.block_header && contract_deployment_data == other.contract_deployment_data && - chain_id == other.chain_id && version == other.version; - }; - - template - PrivateCircuitPublicInputs> to_circuit_type(Builder& builder) const - { - static_assert((std::is_same::value)); - - // Capture the circuit builder: - auto to_ct = [&](auto& e) { return aztec3::utils::types::to_ct(builder, e); }; - auto to_circuit_type = [&](auto& e) { return e.to_circuit_type(builder); }; - - PrivateCircuitPublicInputs> pis = { - to_circuit_type(call_context), - - to_ct(args_hash), - to_ct(return_values), - - to_ct(read_requests), - - to_ct(new_commitments), - to_ct(new_nullifiers), - to_ct(nullified_commitments), - - to_ct(private_call_stack), - to_ct(public_call_stack), - to_ct(new_l2_to_l1_msgs), - - to_ct(encrypted_logs_hash), - to_ct(unencrypted_logs_hash), - - to_ct(encrypted_log_preimages_length), - to_ct(unencrypted_log_preimages_length), - - to_circuit_type(block_header), - - to_circuit_type(contract_deployment_data), - - to_ct(chain_id), - to_ct(version), - }; - - return pis; - }; - - template PrivateCircuitPublicInputs to_native_type() const - { - static_assert(std::is_same, NCT>::value); - auto to_nt = [&](auto& e) { return aztec3::utils::types::to_nt(e); }; - auto to_native_type = [](T& e) { return e.template to_native_type(); }; - - PrivateCircuitPublicInputs pis = { - to_native_type(call_context), - - to_nt(args_hash), - to_nt(return_values), - - to_nt(read_requests), - - to_nt(new_commitments), - to_nt(new_nullifiers), - to_nt(nullified_commitments), - - to_nt(private_call_stack), - to_nt(public_call_stack), - to_nt(new_l2_to_l1_msgs), - - to_nt(encrypted_logs_hash), - to_nt(unencrypted_logs_hash), - - to_nt(encrypted_log_preimages_length), - to_nt(unencrypted_log_preimages_length), - - to_native_type(block_header), - - to_native_type(contract_deployment_data), - - to_nt(chain_id), - to_nt(version), - }; - - return pis; - }; - - fr hash() const - { - // auto to_hashes = [](const T& e) { return e.hash(); }; - - std::vector inputs; - - inputs.push_back(call_context.hash()); - - inputs.push_back(args_hash); - spread_arr_into_vec(return_values, inputs); - - spread_arr_into_vec(read_requests, inputs); - - spread_arr_into_vec(new_commitments, inputs); - spread_arr_into_vec(new_nullifiers, inputs); - spread_arr_into_vec(nullified_commitments, inputs); - - spread_arr_into_vec(private_call_stack, inputs); - spread_arr_into_vec(public_call_stack, inputs); - spread_arr_into_vec(new_l2_to_l1_msgs, inputs); - - spread_arr_into_vec(encrypted_logs_hash, inputs); - spread_arr_into_vec(unencrypted_logs_hash, inputs); - - inputs.push_back(encrypted_log_preimages_length); - inputs.push_back(unencrypted_log_preimages_length); - - spread_arr_into_vec(block_header.to_array(), inputs); - - inputs.push_back(contract_deployment_data.hash()); - - inputs.push_back(chain_id); - inputs.push_back(version); - - if (inputs.size() != PRIVATE_CIRCUIT_PUBLIC_INPUTS_HASH_INPUT_LENGTH) { - throw_or_abort("Incorrect number of input fields when hashing PrivateCircuitPublicInputs"); - } - return NCT::hash(inputs, GeneratorIndex::PRIVATE_CIRCUIT_PUBLIC_INPUTS); - } - - template void spread_arr_into_vec(std::array const& arr, std::vector& vec) const - { - const auto arr_size = sizeof(arr) / sizeof(fr); - vec.insert(vec.end(), arr.data(), arr.data() + arr_size); - } -}; - -// It's been extremely useful for all members here to be std::optional. It allows test app circuits to be very -// quickly drafted without worrying about any of the public inputs which aren't relevant to that circuit. Any values -// which aren't set by the circuit can then be safely set to zero when calling `set_public` (by checking for -// std::nullopt) -template class OptionalPrivateCircuitPublicInputs { - using fr = typename NCT::fr; - using opt_fr = typename std::optional; - - public: - std::optional> call_context; - - opt_fr args_hash; - std::array return_values; - - std::array read_requests; - - std::array new_commitments; - std::array new_nullifiers; - std::array nullified_commitments; - - std::array private_call_stack; - std::array public_call_stack; - std::array new_l2_to_l1_msgs; - - std::array encrypted_logs_hash; - std::array unencrypted_logs_hash; - - opt_fr encrypted_log_preimages_length; - opt_fr unencrypted_log_preimages_length; - - std::optional> block_header; - - std::optional> contract_deployment_data; - - opt_fr chain_id; - opt_fr version; - - // For serialization, update with new fields - MSGPACK_FIELDS(call_context, - args_hash, - return_values, - read_requests, - new_commitments, - new_nullifiers, - nullified_commitments, - private_call_stack, - public_call_stack, - new_l2_to_l1_msgs, - encrypted_logs_hash, - unencrypted_logs_hash, - encrypted_log_preimages_length, - unencrypted_log_preimages_length, - block_header, - contract_deployment_data, - chain_id, - version); - - OptionalPrivateCircuitPublicInputs() = default; - - OptionalPrivateCircuitPublicInputs( - std::optional> const& call_context, - - opt_fr const& args_hash, - std::array const& return_values, - - std::array const& read_requests, - - std::array const& new_commitments, - std::array const& new_nullifiers, - std::array const& nullified_commitments, - - std::array const& private_call_stack, - std::array const& public_call_stack, - std::array const& new_l2_to_l1_msgs, - - std::array const& encrypted_logs_hash, - std::array const& unencrypted_logs_hash, - - opt_fr const& encrypted_log_preimages_length, - opt_fr const& unencrypted_log_preimages_length, - - std::optional> const& block_header, - - std::optional> const& contract_deployment_data, - - opt_fr const& chain_id, - opt_fr const& version) - : call_context(call_context) - , args_hash(args_hash) - , return_values(return_values) - , read_requests(read_requests) - , new_commitments(new_commitments) - , new_nullifiers(new_nullifiers) - , nullified_commitments(nullified_commitments) - , private_call_stack(private_call_stack) - , public_call_stack(public_call_stack) - , new_l2_to_l1_msgs(new_l2_to_l1_msgs) - , encrypted_logs_hash(encrypted_logs_hash) - , unencrypted_logs_hash(unencrypted_logs_hash) - , encrypted_log_preimages_length(encrypted_log_preimages_length) - , unencrypted_log_preimages_length(unencrypted_log_preimages_length) - , block_header(block_header) - , contract_deployment_data(contract_deployment_data) - , chain_id(chain_id) - , version(version){}; - - bool operator==(OptionalPrivateCircuitPublicInputs const&) const = default; - - static OptionalPrivateCircuitPublicInputs create() - { - auto new_inputs = OptionalPrivateCircuitPublicInputs(); - - new_inputs.call_context = std::nullopt; - - new_inputs.args_hash = std::nullopt; - new_inputs.return_values.fill(std::nullopt); - - new_inputs.read_requests.fill(std::nullopt); - - new_inputs.new_commitments.fill(std::nullopt); - new_inputs.new_nullifiers.fill(std::nullopt); - new_inputs.nullified_commitments.fill(std::nullopt); - - new_inputs.private_call_stack.fill(std::nullopt); - new_inputs.public_call_stack.fill(std::nullopt); - new_inputs.new_l2_to_l1_msgs.fill(std::nullopt); - - new_inputs.encrypted_logs_hash.fill(std::nullopt); - new_inputs.unencrypted_logs_hash.fill(std::nullopt); - - new_inputs.encrypted_log_preimages_length = std::nullopt; - new_inputs.unencrypted_log_preimages_length = std::nullopt; - - new_inputs.block_header = std::nullopt; - - new_inputs.contract_deployment_data = std::nullopt; - - new_inputs.chain_id = std::nullopt; - new_inputs.version = std::nullopt; - - return new_inputs; - }; - - void set_commitments(std::vector commitments) - { - if (commitments.size() > new_commitments.size()) { - throw_or_abort("Too many commitments for the number supported by the public inputs ABI."); - } - for (size_t i = 0; i < commitments.size(); ++i) { - new_commitments[i] = commitments[i]; - } - } - - void set_nullifiers(std::vector nullifiers) - { - if (nullifiers.size() > new_nullifiers.size()) { - throw_or_abort("Too many commitments for the number supported by the public inputs ABI."); - } - for (size_t i = 0; i < nullifiers.size(); ++i) { - new_nullifiers[i] = nullifiers[i]; - } - } - - void set_nullified_commitments(std::vector input_nullified_commitments) - { - if (input_nullified_commitments.size() > nullified_commitments.size()) { - throw_or_abort("Too many commitments nullified for the number supported by the public inputs ABI."); - } - for (size_t i = 0; i < input_nullified_commitments.size(); ++i) { - nullified_commitments[i] = input_nullified_commitments[i]; - } - } - - template void make_unused_inputs_zero(Builder& builder) - { - static_assert((std::is_same, NCT>::value)); - - make_unused_element_zero(builder, call_context); - - make_unused_element_zero(builder, args_hash); - make_unused_array_elements_zero(builder, return_values); - - make_unused_array_elements_zero(builder, read_requests); - - make_unused_array_elements_zero(builder, new_commitments); - make_unused_array_elements_zero(builder, new_nullifiers); - make_unused_array_elements_zero(builder, nullified_commitments); - - make_unused_array_elements_zero(builder, private_call_stack); - make_unused_array_elements_zero(builder, public_call_stack); - make_unused_array_elements_zero(builder, new_l2_to_l1_msgs); - - make_unused_array_elements_zero(builder, encrypted_logs_hash); - make_unused_array_elements_zero(builder, unencrypted_logs_hash); - - make_unused_element_zero(builder, encrypted_log_preimages_length); - make_unused_element_zero(builder, unencrypted_log_preimages_length); - - make_unused_element_zero(builder, block_header); - - make_unused_element_zero(builder, contract_deployment_data); - - make_unused_element_zero(builder, chain_id); - make_unused_element_zero(builder, version); - - all_elements_populated = true; - } - - template void set_public(Builder& builder) - { - static_assert(!(std::is_same::value)); - - make_unused_inputs_zero(builder); - - // Optional members are guaranteed to be nonempty from here. - - (*call_context).set_public(); - - (*args_hash).set_public(); - set_array_public(return_values); - - set_array_public(read_requests); - - set_array_public(new_commitments); - set_array_public(new_nullifiers); - set_array_public(nullified_commitments); - - set_array_public(private_call_stack); - set_array_public(public_call_stack); - set_array_public(new_l2_to_l1_msgs); - - set_array_public(encrypted_logs_hash); - set_array_public(unencrypted_logs_hash); - - (*encrypted_log_preimages_length).set_public(); - (*unencrypted_log_preimages_length).set_public(); - - (*block_header).set_public(); - - (*contract_deployment_data).set_public(); - - (*chain_id).set_public(); - (*version).set_public(); - } - - template - OptionalPrivateCircuitPublicInputs> to_circuit_type(Builder& builder) const - { - static_assert((std::is_same::value)); - - // Capture the circuit builder: - auto to_ct = [&](auto& e) { return aztec3::utils::types::to_ct(builder, e); }; - auto to_circuit_type = [&](auto& e) { - return e ? std::make_optional((*e).to_circuit_type(builder)) : std::nullopt; - }; - - OptionalPrivateCircuitPublicInputs> pis = { - to_circuit_type(call_context), - - to_ct(args_hash), - to_ct(return_values), - - to_ct(read_requests), - - to_ct(new_commitments), - to_ct(new_nullifiers), - to_ct(nullified_commitments), - - to_ct(private_call_stack), - to_ct(public_call_stack), - to_ct(new_l2_to_l1_msgs), - - to_ct(encrypted_logs_hash), - to_ct(unencrypted_logs_hash), - - to_ct(encrypted_log_preimages_length), - to_ct(unencrypted_log_preimages_length), - - to_circuit_type(block_header), - - to_circuit_type(contract_deployment_data), - - to_ct(chain_id), - to_ct(version), - }; - - return pis; - }; - - template OptionalPrivateCircuitPublicInputs to_native_type() const - { - static_assert(std::is_same, NCT>::value); - auto to_nt = [&](auto& e) { return aztec3::utils::types::to_nt(e); }; - auto to_native_type = [](const std::optional& e) { - return e ? std::make_optional((*e).template to_native_type()) : std::nullopt; - }; - // auto to_native_type = [&](T& e) { return e.to_native_type(); }; - - OptionalPrivateCircuitPublicInputs pis = { to_native_type(call_context), - - to_nt(args_hash), - to_nt(return_values), - - to_nt(read_requests), - - to_nt(new_commitments), - to_nt(new_nullifiers), - to_nt(nullified_commitments), - - to_nt(private_call_stack), - to_nt(public_call_stack), - to_nt(new_l2_to_l1_msgs), - - to_nt(encrypted_logs_hash), - to_nt(unencrypted_logs_hash), - - to_nt(encrypted_log_preimages_length), - to_nt(unencrypted_log_preimages_length), - - to_native_type(block_header), - - to_native_type(contract_deployment_data), - - to_nt(chain_id), - to_nt(version) }; - - return pis; - }; - - fr hash() const - { - // auto to_hashes = [](const std::optional& e) { - // if (!e) { - // throw_or_abort("Value is nullopt"); - // } - // return (*e).hash(); - // }; - - std::vector inputs; - - inputs.push_back((*call_context).hash()); - - inputs.push_back(*args_hash); - spread_arr_opt_into_vec(return_values, inputs); - - spread_arr_opt_into_vec(read_requests, inputs); - - spread_arr_opt_into_vec(new_commitments, inputs); - spread_arr_opt_into_vec(new_nullifiers, inputs); - spread_arr_opt_into_vec(nullified_commitments, inputs); - - spread_arr_opt_into_vec(private_call_stack, inputs); - spread_arr_opt_into_vec(public_call_stack, inputs); - spread_arr_opt_into_vec(new_l2_to_l1_msgs, inputs); - - spread_arr_into_vec(encrypted_logs_hash, inputs); - spread_arr_into_vec(unencrypted_logs_hash, inputs); - - inputs.push_back(*encrypted_log_preimages_length); - inputs.push_back(*unencrypted_log_preimages_length); - - spread_arr_opt_into_vec((*block_header).to_array(), inputs); - - inputs.push_back((*contract_deployment_data).hash()); - - inputs.push_back(*chain_id); - inputs.push_back(*version); - - return NCT::hash(inputs, GeneratorIndex::PRIVATE_CIRCUIT_PUBLIC_INPUTS); - } - - // We can remove optionality when using the inputs in a kernel or rollup circuit, for ease of use. - PrivateCircuitPublicInputs remove_optionality() const - { - auto get_value = [&](auto& e) { return e.value(); }; - - return PrivateCircuitPublicInputs{ - .call_context = call_context.value(), - - .args_hash = args_hash.value(), - .return_values = map(return_values, get_value), - - .read_requests = map(read_requests, get_value), - - .new_commitments = map(new_commitments, get_value), - .new_nullifiers = map(new_nullifiers, get_value), - .nullified_commitments = map(nullified_commitments, get_value), - - .private_call_stack = map(private_call_stack, get_value), - .public_call_stack = map(public_call_stack, get_value), - .new_l2_to_l1_msgs = map(new_l2_to_l1_msgs, get_value), - - .encrypted_logs_hash = map(encrypted_logs_hash, get_value), - .unencrypted_logs_hash = map(unencrypted_logs_hash, get_value), - - .encrypted_log_preimages_length = encrypted_log_preimages_length.value(), - .unencrypted_log_preimages_length = unencrypted_log_preimages_length.value(), - - .block_header = block_header.value(), - - .contract_deployment_data = contract_deployment_data.value(), - - .chain_id = chain_id.value(), - .version = version.value(), - }; - } - - private: - bool all_elements_populated = false; - - template - void spread_arr_opt_into_vec(std::array, SIZE> const& arr, std::vector& vec) const - { - auto get_opt_value = [](const std::optional& e) { - if (!e) { - throw_or_abort("Value is nullopt"); - } - return *e; - }; - - std::array arr_values = map(arr, get_opt_value); - const auto arr_size = sizeof(arr_values) / sizeof(fr); - vec.insert(vec.end(), arr_values.data(), arr_values.data() + arr_size); - } - - template void spread_arr_into_vec(std::array const& arr, std::vector& vec) const - { - const auto arr_size = sizeof(arr) / sizeof(fr); - vec.insert(vec.end(), arr.data(), arr.data() + arr_size); - } - - template - void make_unused_array_elements_zero(Builder& builder, std::array, SIZE>& arr) - { - static_assert((std::is_same, NCT>::value)); - - for (std::optional& e : arr) { - make_unused_element_zero(builder, e); - } - } - - template void make_unused_element_zero(Builder& builder, std::optional& element) - { - static_assert((std::is_same, NCT>::value)); - - if (!element) { - element = - T(witness_t(&builder, 0)); // convert the nullopt value to a circuit witness value of `0` - fr(*element).assert_is_zero(); - } - } - - // ABIStruct is a template for any of the structs in the abis/ dir. E.g. ExecutedCallback, CallbackStackItem. - template class ABIStruct> - void make_unused_element_zero(Builder& builder, std::optional>>& element) - { - static_assert((std::is_same, NCT>::value)); - - if (!element) { - element = ABIStruct().to_circuit_type( - builder); // convert the nullopt value to a circuit witness value of `0` - (*element).template assert_is_zero(); - } - } - - // Make sure this is only called by functions which have implemented a "CT only" check. - template void set_array_public(std::array, SIZE>& arr) - { - for (std::optional& e : arr) { - fr(*e).set_public(); - } - } -}; -} // namespace aztec3::circuits::abis diff --git a/circuits/cpp/src/aztec3/circuits/abis/private_kernel/call_context_reconciliation_data.hpp b/circuits/cpp/src/aztec3/circuits/abis/private_kernel/call_context_reconciliation_data.hpp deleted file mode 100644 index 7889c49b641..00000000000 --- a/circuits/cpp/src/aztec3/circuits/abis/private_kernel/call_context_reconciliation_data.hpp +++ /dev/null @@ -1,58 +0,0 @@ -// #pragma once - -// #include "../call_context.hpp" - -// #include "aztec3/utils/types/native_types.hpp" -// #include "aztec3/utils/types/circuit_types.hpp" -// #include "aztec3/utils/types/convert.hpp" - -// #include - -// namespace aztec3::circuits::abis::private_kernel { - -// using plonk::stdlib::witness_t; -// using aztec3::utils::types::CircuitTypes; -// using aztec3::utils::types::NativeTypes; -// using std::is_same; - -// template struct CallContextReconciliationData { -// typedef typename NCT::fr fr; - -// /** -// * This class needs an explanation... -// * -// */ -// std::array, MAX_PRIVATE_CALL_STACK_LENGTH_PER_CALL> private_call_contexts; -// std::array private_counterparts; - -// std::array, MAX_PUBLIC_CALL_STACK_LENGTH_PER_CALL> public_call_contexts; -// std::array public_counterparts; - -// // std::array contract_deployment_call_stack; - -// std::array, MAX_NEW_L2_TO_L1_MSGS_PER_CALL> l1_call_contexts; -// std::array l1_counterparts; // TODO: this is probably wrong. - -// template -// CallContextReconciliationData> to_circuit_type(Builder& builder) const -// { -// static_assert((std::is_same::value)); - -// // Capture the circuit builder: -// auto to_ct = [&](auto& e) { return aztec3::utils::types::to_ct(builder, e); }; -// auto to_circuit_type = [&](auto& e) { return e.to_circuit_type(builder); }; - -// CallContextReconciliationData> data = { - -// map(private_call_contexts, to_circuit_type), to_ct(private_counterparts), - -// map(public_call_contexts, to_circuit_type), to_ct(public_counterparts), - -// map(l1_call_contexts, to_circuit_type), to_ct(l1_counterparts), -// }; - -// return data; -// }; -// }; - -// } // namespace aztec3::circuits::abis::private_kernel \ No newline at end of file diff --git a/circuits/cpp/src/aztec3/circuits/abis/private_kernel/globals.hpp b/circuits/cpp/src/aztec3/circuits/abis/private_kernel/globals.hpp deleted file mode 100644 index c0dddfa2d65..00000000000 --- a/circuits/cpp/src/aztec3/circuits/abis/private_kernel/globals.hpp +++ /dev/null @@ -1,42 +0,0 @@ -#pragma once -#include "aztec3/utils/types/circuit_types.hpp" -#include "aztec3/utils/types/convert.hpp" -#include "aztec3/utils/types/native_types.hpp" - -#include - -namespace aztec3::circuits::abis::private_kernel { - -using aztec3::utils::types::CircuitTypes; -using aztec3::utils::types::NativeTypes; -using std::is_same; - -template struct Globals { - using fr = typename NCT::fr; - using boolean = typename NCT::boolean; - - fr min_timestamp = 0; - - boolean operator==(Globals const& other) const { return min_timestamp == other.min_timestamp; }; - - template Globals> to_circuit_type(Builder& builder) const - { - static_assert((std::is_same::value)); - - // Capture the circuit builder: - auto to_ct = [&](auto& e) { return aztec3::utils::types::to_ct(builder, e); }; - - Globals> global_data = { to_ct(min_timestamp) }; - - return global_data; - }; - - void set_public() - { - static_assert(!(std::is_same::value)); - - min_timestamp.set_public(); - } -}; - -} // namespace aztec3::circuits::abis::private_kernel \ No newline at end of file diff --git a/circuits/cpp/src/aztec3/circuits/abis/private_kernel/private_call_data.hpp b/circuits/cpp/src/aztec3/circuits/abis/private_kernel/private_call_data.hpp deleted file mode 100644 index 5ad120dfdf5..00000000000 --- a/circuits/cpp/src/aztec3/circuits/abis/private_kernel/private_call_data.hpp +++ /dev/null @@ -1,102 +0,0 @@ -#pragma once - -#include "call_context_reconciliation_data.hpp" -#include "../call_stack_item.hpp" -#include "../read_request_membership_witness.hpp" -#include "../types.hpp" - -#include "aztec3/constants.hpp" -#include "aztec3/utils/types/circuit_types.hpp" -#include "aztec3/utils/types/convert.hpp" -#include "aztec3/utils/types/native_types.hpp" - -#include - -namespace aztec3::circuits::abis::private_kernel { - -using aztec3::utils::types::CircuitTypes; -using aztec3::utils::types::NativeTypes; -using std::is_same; - -template struct PrivateCallData { - using address = typename NCT::address; - using fr = typename NCT::fr; - using boolean = typename NCT::boolean; - using VK = typename NCT::VK; - - CallStackItem call_stack_item{}; - - std::array, MAX_PRIVATE_CALL_STACK_LENGTH_PER_CALL> private_call_stack_preimages{}; - - // std::array, MAX_PUBLIC_CALL_STACK_LENGTH_PER_CALL> - // public_call_stack_preimages; - - NativeTypes::Proof proof{}; // TODO: how to express proof as native/circuit type when it gets used as a buffer? - std::shared_ptr vk; - - MembershipWitness function_leaf_membership_witness{}; - MembershipWitness contract_leaf_membership_witness{}; - - std::array, MAX_READ_REQUESTS_PER_CALL> - read_request_membership_witnesses{}; - - fr portal_contract_address = 0; // an ETH address - fr acir_hash = 0; - - // For serialization, update with new fields - MSGPACK_FIELDS(call_stack_item, - private_call_stack_preimages, - proof, - vk, - function_leaf_membership_witness, - contract_leaf_membership_witness, - read_request_membership_witnesses, - portal_contract_address, - acir_hash); - - boolean operator==(PrivateCallData const& other) const - { - // WARNING: proof skipped! - return call_stack_item == other.call_stack_item && - private_call_stack_preimages == other.private_call_stack_preimages && vk == other.vk && - function_leaf_membership_witness == other.function_leaf_membership_witness && - contract_leaf_membership_witness == other.contract_leaf_membership_witness && - read_request_membership_witnesses == other.read_request_membership_witnesses && - portal_contract_address == other.portal_contract_address && acir_hash == other.acir_hash; - }; - - // WARNING: the `proof` does NOT get converted! (because the current implementation of `verify_proof` takes a proof - // of native bytes; any conversion to circuit types happens within the `verify_proof` function) - template PrivateCallData> to_circuit_type(Builder& builder) const - { - typedef CircuitTypes CT; - static_assert((std::is_same::value)); - - // Capture the circuit builder: - auto to_ct = [&](auto& e) { return aztec3::utils::types::to_ct(builder, e); }; - auto to_circuit_type = [&](auto& e) { return e.to_circuit_type(builder); }; - - PrivateCallData> data = { - to_circuit_type(call_stack_item), - - map(private_call_stack_preimages, to_circuit_type), - - proof, // Notice: not converted! Stays as native. This is because of how the verify_proof function - // currently works. - CT::VK::from_witness(&builder, vk), - - to_circuit_type(function_leaf_membership_witness), - to_circuit_type(contract_leaf_membership_witness), - - aztec3::utils::types::to_ct>( - builder, read_request_membership_witnesses), - - to_ct(portal_contract_address), - to_ct(acir_hash), - }; - - return data; - }; -}; // namespace aztec3::circuits::abis::private_kernel - -} // namespace aztec3::circuits::abis::private_kernel diff --git a/circuits/cpp/src/aztec3/circuits/abis/private_kernel/private_kernel_inputs_init.hpp b/circuits/cpp/src/aztec3/circuits/abis/private_kernel/private_kernel_inputs_init.hpp deleted file mode 100644 index d21935e3217..00000000000 --- a/circuits/cpp/src/aztec3/circuits/abis/private_kernel/private_kernel_inputs_init.hpp +++ /dev/null @@ -1,43 +0,0 @@ -#pragma once - -#include "private_call_data.hpp" -#include "../tx_request.hpp" - -#include "aztec3/utils/types/circuit_types.hpp" -#include "aztec3/utils/types/native_types.hpp" - -namespace aztec3::circuits::abis::private_kernel { - -using aztec3::utils::types::CircuitTypes; -using aztec3::utils::types::NativeTypes; -using std::is_same; - -template struct PrivateKernelInputsInit { - using fr = typename NCT::fr; - using boolean = typename NCT::boolean; - - TxRequest tx_request{}; - PrivateCallData private_call{}; - - // For serialization, update with new fields - MSGPACK_FIELDS(tx_request, private_call); - boolean operator==(PrivateKernelInputsInit const& other) const - { - return msgpack_derived_equals(*this, other); - }; - - template PrivateKernelInputsInit> to_circuit_type(Builder& builder) const - { - static_assert((std::is_same::value)); - - PrivateKernelInputsInit> private_inputs = { - // TODO to_ct(signature), - tx_request.to_circuit_type(builder), - private_call.to_circuit_type(builder), - }; - - return private_inputs; - }; -}; - -} // namespace aztec3::circuits::abis::private_kernel diff --git a/circuits/cpp/src/aztec3/circuits/abis/private_kernel/private_kernel_inputs_inner.hpp b/circuits/cpp/src/aztec3/circuits/abis/private_kernel/private_kernel_inputs_inner.hpp deleted file mode 100644 index d6b5217bbfa..00000000000 --- a/circuits/cpp/src/aztec3/circuits/abis/private_kernel/private_kernel_inputs_inner.hpp +++ /dev/null @@ -1,44 +0,0 @@ -#pragma once - -#include "private_call_data.hpp" -#include "../previous_kernel_data.hpp" - -#include "aztec3/utils/types/circuit_types.hpp" -#include "aztec3/utils/types/native_types.hpp" - -#include - -namespace aztec3::circuits::abis::private_kernel { - -using aztec3::utils::types::CircuitTypes; -using aztec3::utils::types::NativeTypes; -using std::is_same; - -template struct PrivateKernelInputsInner { - using fr = typename NCT::fr; - using boolean = typename NCT::boolean; - - PreviousKernelData previous_kernel{}; - PrivateCallData private_call{}; - - // For serialization, update with new fields - MSGPACK_FIELDS(previous_kernel, private_call); - boolean operator==(PrivateKernelInputsInner const& other) const - { - return msgpack_derived_equals(*this, other); - }; - - template PrivateKernelInputsInner> to_circuit_type(Builder& builder) const - { - static_assert((std::is_same::value)); - - PrivateKernelInputsInner> private_inputs = { - previous_kernel.to_circuit_type(builder), - private_call.to_circuit_type(builder), - }; - - return private_inputs; - }; -}; - -} // namespace aztec3::circuits::abis::private_kernel diff --git a/circuits/cpp/src/aztec3/circuits/abis/private_kernel/private_kernel_inputs_ordering.hpp b/circuits/cpp/src/aztec3/circuits/abis/private_kernel/private_kernel_inputs_ordering.hpp deleted file mode 100644 index 2cfd6d2b643..00000000000 --- a/circuits/cpp/src/aztec3/circuits/abis/private_kernel/private_kernel_inputs_ordering.hpp +++ /dev/null @@ -1,49 +0,0 @@ -#pragma once - -#include "private_call_data.hpp" -#include "../previous_kernel_data.hpp" - -#include "aztec3/constants.hpp" -#include "aztec3/utils/types/circuit_types.hpp" -#include "aztec3/utils/types/native_types.hpp" - -#include - -namespace aztec3::circuits::abis::private_kernel { - -using aztec3::utils::types::CircuitTypes; -using aztec3::utils::types::NativeTypes; -using std::is_same; - -template struct PrivateKernelInputsOrdering { - using fr = typename NCT::fr; - using boolean = typename NCT::boolean; - - PreviousKernelData previous_kernel{}; - - std::array read_commitment_hints{}; - std::array nullifier_commitment_hints{}; - - // For serialization, update with new fields - MSGPACK_FIELDS(previous_kernel, read_commitment_hints, nullifier_commitment_hints); - boolean operator==(PrivateKernelInputsOrdering const& other) const - { - return msgpack_derived_equals(*this, other); - }; - - template - PrivateKernelInputsOrdering> to_circuit_type(Builder& builder) const - { - static_assert((std::is_same::value)); - - PrivateKernelInputsOrdering> private_inputs = { - previous_kernel.to_circuit_type(builder), - read_commitment_hints.to_circuit_type(builder), - nullifier_commitment_hints.to_circuit_type(builder), - }; - - return private_inputs; - }; -}; - -} // namespace aztec3::circuits::abis::private_kernel diff --git a/circuits/cpp/src/aztec3/circuits/abis/public_circuit_public_inputs.hpp b/circuits/cpp/src/aztec3/circuits/abis/public_circuit_public_inputs.hpp deleted file mode 100644 index 6548b93a28d..00000000000 --- a/circuits/cpp/src/aztec3/circuits/abis/public_circuit_public_inputs.hpp +++ /dev/null @@ -1,140 +0,0 @@ -#pragma once - -#include "call_context.hpp" -#include "contract_storage_read.hpp" -#include "contract_storage_update_request.hpp" -#include "../../constants.hpp" - -#include "aztec3/circuits/abis/block_header.hpp" -#include "aztec3/utils/types/circuit_types.hpp" -#include "aztec3/utils/types/native_types.hpp" - -#include "barretenberg/common/throw_or_abort.hpp" -#include - -namespace aztec3::circuits::abis { - -using aztec3::utils::types::CircuitTypes; -using aztec3::utils::types::NativeTypes; - -template struct PublicCircuitPublicInputs { - using fr = typename NCT::fr; - using boolean = typename NCT::boolean; - using address = typename NCT::address; - - CallContext call_context{}; - - fr args_hash = 0; - std::array return_values{}; - - std::array, MAX_PUBLIC_DATA_UPDATE_REQUESTS_PER_CALL> - contract_storage_update_requests{}; - std::array, MAX_PUBLIC_DATA_READS_PER_CALL> contract_storage_reads{}; - - std::array public_call_stack{}; - std::array new_commitments{}; - std::array new_nullifiers{}; - std::array new_l2_to_l1_msgs{}; - - std::array unencrypted_logs_hash{}; - - // Here so that the gas cost of this request can be measured by circuits, without actually needing to feed in the - // variable-length data. - fr unencrypted_log_preimages_length = 0; - - BlockHeader block_header{}; - - address prover_address{}; - - // for serialization, update with new fields - MSGPACK_FIELDS(call_context, - args_hash, - return_values, - contract_storage_update_requests, - contract_storage_reads, - public_call_stack, - new_commitments, - new_nullifiers, - new_l2_to_l1_msgs, - unencrypted_logs_hash, - unencrypted_log_preimages_length, - block_header, - prover_address); - - boolean operator==(PublicCircuitPublicInputs const& other) const - { - return msgpack_derived_equals(*this, other); - } - - template PublicCircuitPublicInputs> to_circuit_type(Builder& builder) const - { - static_assert((std::is_same::value)); - - // Capture the circuit builder: - auto to_ct = [&](auto& e) { return aztec3::utils::types::to_ct(builder, e); }; - auto to_circuit_type = [&](auto& e) { return e.to_circuit_type(builder); }; - - PublicCircuitPublicInputs> pis = { - .call_context = to_circuit_type(call_context), - - .args_hash = to_ct(args_hash), - .return_values = to_ct(return_values), - - .contract_storage_update_requests = map(contract_storage_update_requests, to_circuit_type), - .contract_storage_reads = map(contract_storage_reads, to_circuit_type), - - .public_call_stack = to_ct(public_call_stack), - .new_commitments = to_ct(new_commitments), - .new_nullifiers = to_ct(new_nullifiers), - .new_l2_to_l1_msgs = to_ct(new_l2_to_l1_msgs), - - .unencrypted_logs_hash = to_ct(unencrypted_logs_hash), - .unencrypted_log_preimages_length = to_ct(unencrypted_log_preimages_length), - - .block_header = to_ct(block_header), - - .prover_address = to_ct(prover_address), - }; - - return pis; - }; - - fr hash() const - { - auto to_hashes = [](const T& e) { return e.hash(); }; - - std::vector inputs; - - inputs.push_back(call_context.hash()); - - inputs.push_back(args_hash); - spread_arr_into_vec(return_values, inputs); - - spread_arr_into_vec(map(contract_storage_update_requests, to_hashes), inputs); - spread_arr_into_vec(map(contract_storage_reads, to_hashes), inputs); - - spread_arr_into_vec(public_call_stack, inputs); - spread_arr_into_vec(new_commitments, inputs); - spread_arr_into_vec(new_nullifiers, inputs); - spread_arr_into_vec(new_l2_to_l1_msgs, inputs); - - spread_arr_into_vec(unencrypted_logs_hash, inputs); - inputs.push_back(unencrypted_log_preimages_length); - - spread_arr_into_vec(block_header.to_array(), inputs); - inputs.push_back(prover_address); - - if (inputs.size() != PUBLIC_CIRCUIT_PUBLIC_INPUTS_HASH_INPUT_LENGTH) { - throw_or_abort("Incorrect number of input fields when hashing PublicCircuitPublicInputs"); - } - return NCT::hash(inputs, GeneratorIndex::PUBLIC_CIRCUIT_PUBLIC_INPUTS); - } - - template void spread_arr_into_vec(std::array const& arr, std::vector& vec) const - { - const auto arr_size = sizeof(arr) / sizeof(fr); - vec.insert(vec.end(), arr.data(), arr.data() + arr_size); - } -}; // namespace aztec3::circuits::abis - -} // namespace aztec3::circuits::abis diff --git a/circuits/cpp/src/aztec3/circuits/abis/public_data_read.hpp b/circuits/cpp/src/aztec3/circuits/abis/public_data_read.hpp deleted file mode 100644 index d95ed30160a..00000000000 --- a/circuits/cpp/src/aztec3/circuits/abis/public_data_read.hpp +++ /dev/null @@ -1,76 +0,0 @@ -#pragma once -#include "aztec3/constants.hpp" -#include "aztec3/utils/types/circuit_types.hpp" -#include "aztec3/utils/types/convert.hpp" -#include "aztec3/utils/types/native_types.hpp" - -#include - -namespace aztec3::circuits::abis { - -using aztec3::GeneratorIndex; -using aztec3::utils::types::CircuitTypes; -using aztec3::utils::types::NativeTypes; - -template struct PublicDataRead { - using fr = typename NCT::fr; - using boolean = typename NCT::boolean; - - fr leaf_index = 0; - fr value = 0; - - // for serialization, update with new fields - MSGPACK_FIELDS(leaf_index, value); - bool operator==(PublicDataRead const&) const = default; - - template PublicDataRead> to_circuit_type(Builder& builder) const - { - static_assert((std::is_same::value)); - - // Capture the circuit builder: - auto to_ct = [&](auto& e) { return aztec3::utils::types::to_ct(builder, e); }; - - PublicDataRead> read = { - to_ct(leaf_index), - to_ct(value), - }; - - return read; - }; - - template PublicDataRead to_native_type() const - { - static_assert((std::is_same, NCT>::value)); - - auto to_nt = [&](auto& e) { return aztec3::utils::types::to_nt(e); }; - - PublicDataRead read = { - to_nt(leaf_index), - to_nt(value), - }; - - return read; - }; - - fr hash() const - { - std::vector inputs = { - leaf_index, - value, - }; - - return NCT::hash(inputs, GeneratorIndex::PUBLIC_DATA_READ); - } - - void set_public() - { - static_assert(!(std::is_same::value)); - - leaf_index.set_public(); - value.set_public(); - } - - boolean is_empty() const { return leaf_index == 0; } -}; - -} // namespace aztec3::circuits::abis diff --git a/circuits/cpp/src/aztec3/circuits/abis/public_data_update_request.hpp b/circuits/cpp/src/aztec3/circuits/abis/public_data_update_request.hpp deleted file mode 100644 index 2d70484d232..00000000000 --- a/circuits/cpp/src/aztec3/circuits/abis/public_data_update_request.hpp +++ /dev/null @@ -1,81 +0,0 @@ -#pragma once -#include "aztec3/constants.hpp" -#include "aztec3/utils/types/circuit_types.hpp" -#include "aztec3/utils/types/convert.hpp" -#include "aztec3/utils/types/native_types.hpp" - -#include - -namespace aztec3::circuits::abis { - -using aztec3::GeneratorIndex; -using aztec3::utils::types::CircuitTypes; -using aztec3::utils::types::NativeTypes; - -template struct PublicDataUpdateRequest { - using fr = typename NCT::fr; - using boolean = typename NCT::boolean; - - fr leaf_index = 0; - fr old_value = 0; - fr new_value = 0; - - // for serialization, update with new fields - MSGPACK_FIELDS(leaf_index, old_value, new_value); - bool operator==(PublicDataUpdateRequest const&) const = default; - - template PublicDataUpdateRequest> to_circuit_type(Builder& builder) const - { - static_assert((std::is_same::value)); - - // Capture the circuit builder: - auto to_ct = [&](auto& e) { return aztec3::utils::types::to_ct(builder, e); }; - - PublicDataUpdateRequest> update_request = { - to_ct(leaf_index), - to_ct(old_value), - to_ct(new_value), - }; - - return update_request; - }; - - template PublicDataUpdateRequest to_native_type() const - { - static_assert((std::is_same, NCT>::value)); - - auto to_nt = [&](auto& e) { return aztec3::utils::types::to_nt(e); }; - - PublicDataUpdateRequest update_request = { - to_nt(leaf_index), - to_nt(old_value), - to_nt(new_value), - }; - - return update_request; - }; - - fr hash() const - { - std::vector inputs = { - leaf_index, - old_value, - new_value, - }; - - return NCT::hash(inputs, GeneratorIndex::PUBLIC_DATA_UPDATE_REQUEST); - } - - void set_public() - { - static_assert(!(std::is_same::value)); - - leaf_index.set_public(); - old_value.set_public(); - new_value.set_public(); - } - - boolean is_empty() const { return leaf_index == 0; } -}; - -} // namespace aztec3::circuits::abis diff --git a/circuits/cpp/src/aztec3/circuits/abis/public_kernel/public_call_data.hpp b/circuits/cpp/src/aztec3/circuits/abis/public_kernel/public_call_data.hpp deleted file mode 100644 index 46ae4286599..00000000000 --- a/circuits/cpp/src/aztec3/circuits/abis/public_kernel/public_call_data.hpp +++ /dev/null @@ -1,75 +0,0 @@ -#pragma once - -#include "../call_stack_item.hpp" -#include "../types.hpp" - -#include "aztec3/constants.hpp" -#include "aztec3/utils/types/circuit_types.hpp" -#include "aztec3/utils/types/convert.hpp" -#include "aztec3/utils/types/native_types.hpp" - -#include - -namespace aztec3::circuits::abis::public_kernel { - -using aztec3::utils::types::CircuitTypes; -using aztec3::utils::types::NativeTypes; -using std::is_same; - -template struct PublicCallData { - using address = typename NCT::address; - using fr = typename NCT::fr; - using boolean = typename NCT::boolean; - using VK = typename NCT::VK; - - CallStackItem call_stack_item{}; - - std::array, MAX_PUBLIC_CALL_STACK_LENGTH_PER_CALL> public_call_stack_preimages{}; - - NativeTypes::Proof proof{}; // TODO: how to express proof as native/circuit type when it gets used as a buffer? - - fr portal_contract_address = 0; // an ETH address - fr bytecode_hash = 0; - - // for serialization, update with new fields - MSGPACK_FIELDS(call_stack_item, public_call_stack_preimages, proof, portal_contract_address, bytecode_hash); - boolean operator==(PublicCallData const& other) const - { - // WARNING: proof skipped! - return call_stack_item == other.call_stack_item && - public_call_stack_preimages == other.public_call_stack_preimages && - portal_contract_address == other.portal_contract_address && bytecode_hash == other.bytecode_hash; - }; - - // WARNING: the `proof` does NOT get converted! (because the current implementation of `verify_proof` takes a proof - // of native bytes; any conversion to circuit types happens within the `verify_proof` function) - template PublicCallData> to_circuit_type(Builder& builder) const - { - // typedef CircuitTypes CT; - static_assert((std::is_same::value)); - - // Capture the circuit builder: - auto to_ct = [&](auto& e) { return aztec3::utils::types::to_ct(builder, e); }; - auto to_circuit_type = [&](auto& e) { return e.to_circuit_type(builder); }; - - PublicCallData> data = { - call_stack_item.to_circuit_type(builder), - - map(public_call_stack_preimages, to_circuit_type), - - proof, // Notice: not converted! Stays as native. This is because of how the verify_proof function - // currently works. - // CT::VK::from_witness(&builder, vk), - - // to_circuit_type(function_leaf_membership_witness), - // to_circuit_type(contract_leaf_membership_witness), - - to_ct(portal_contract_address), - to_ct(bytecode_hash), - }; - - return data; - }; -}; - -} // namespace aztec3::circuits::abis::public_kernel diff --git a/circuits/cpp/src/aztec3/circuits/abis/public_kernel/public_kernel_inputs.hpp b/circuits/cpp/src/aztec3/circuits/abis/public_kernel/public_kernel_inputs.hpp deleted file mode 100644 index d4119747c80..00000000000 --- a/circuits/cpp/src/aztec3/circuits/abis/public_kernel/public_kernel_inputs.hpp +++ /dev/null @@ -1,44 +0,0 @@ -#pragma once - -#include "public_call_data.hpp" -#include "../previous_kernel_data.hpp" - -#include "aztec3/utils/types/circuit_types.hpp" -#include "aztec3/utils/types/native_types.hpp" - -#include - -namespace aztec3::circuits::abis::public_kernel { - -using aztec3::utils::types::CircuitTypes; -using aztec3::utils::types::NativeTypes; -using std::is_same; - -template struct PublicKernelInputs { - using fr = typename NCT::fr; - using boolean = typename NCT::boolean; - - PreviousKernelData previous_kernel{}; - PublicCallData public_call{}; - - // for serialization, update with new fields - MSGPACK_FIELDS(previous_kernel, public_call); - boolean operator==(PublicKernelInputs const& other) const - { - return previous_kernel == other.previous_kernel && public_call == other.public_call; - }; - - template PublicKernelInputs> to_circuit_type(Builder& builder) const - { - static_assert((std::is_same::value)); - - PublicKernelInputs> public_kernel_inputs = { - previous_kernel.to_circuit_type(builder), - public_call.to_circuit_type(builder), - }; - - return public_kernel_inputs; - }; -}; - -} // namespace aztec3::circuits::abis::public_kernel diff --git a/circuits/cpp/src/aztec3/circuits/abis/read_request_membership_witness.hpp b/circuits/cpp/src/aztec3/circuits/abis/read_request_membership_witness.hpp deleted file mode 100644 index e63e60a9837..00000000000 --- a/circuits/cpp/src/aztec3/circuits/abis/read_request_membership_witness.hpp +++ /dev/null @@ -1,87 +0,0 @@ -#pragma once - -#include "aztec3/circuits/abis/membership_witness.hpp" -#include "aztec3/utils/types/circuit_types.hpp" -#include "aztec3/utils/types/convert.hpp" - -namespace aztec3::circuits::abis { - -using aztec3::utils::types::CircuitTypes; -using aztec3::utils::types::NativeTypes; -using std::is_same; - -/** - * A ReadRequestMembershipWitness is similar to a MembershipWitness but includes - * some additional fields used to direct the kernel regarding whether a read is transient - * and if so which commitment it corresponds to. - */ -template struct ReadRequestMembershipWitness { - using fr = typename NCT::fr; - using boolean = typename NCT::boolean; - - fr leaf_index = 0; - std::array sibling_path{}; - boolean is_transient = false; // whether or not the read request corresponds to a pending commitment - // In case we change the default to true, we have to adapt is_empty() method - fr hint_to_commitment = 0; // hint to point kernel to the commitment this rr corresponds to - - // For serialization, update with new fields - MSGPACK_FIELDS(leaf_index, sibling_path, is_transient, hint_to_commitment); - - boolean operator==(ReadRequestMembershipWitness const& other) const - { - return leaf_index == other.leaf_index && sibling_path == other.sibling_path && - is_transient == other.is_transient && hint_to_commitment == other.hint_to_commitment; - }; - - template - ReadRequestMembershipWitness, N> to_circuit_type(Builder& builder) const - { - static_assert((std::is_same::value)); - - // Capture the circuit builder: - auto to_ct = [&](auto& e) { return aztec3::utils::types::to_ct(builder, e); }; - - ReadRequestMembershipWitness, N> witness = { - to_ct(leaf_index), to_ct(sibling_path), to_ct(is_transient), to_ct(hint_to_commitment) - }; - - return witness; - } - - template ReadRequestMembershipWitness to_native_type() const - { - static_assert((std::is_same, NCT>::value)); - - auto to_nt = [&](auto& e) { return aztec3::utils::types::to_nt(e); }; - - ReadRequestMembershipWitness witness = { - to_nt(leaf_index), map(sibling_path, to_nt), to_nt(is_transient), to_nt(hint_to_commitment) - }; - - return witness; - } - - - void set_public() - { - static_assert(!(std::is_same::value)); - - leaf_index.set_public(); - for (fr const& e : sibling_path) { - e.set_public(); - } - - fr(is_transient).set_public(); - hint_to_commitment.set_public(); - } - - // Deliberately consider a transient read request membership witness as non-empty. - boolean is_empty() const - { - return aztec3::utils::is_empty(leaf_index) && is_array_empty(sibling_path) && !is_transient && - aztec3::utils::is_empty(hint_to_commitment); - } -}; - -} // namespace aztec3::circuits::abis diff --git a/circuits/cpp/src/aztec3/circuits/abis/rollup/base/base_or_merge_rollup_public_inputs.hpp b/circuits/cpp/src/aztec3/circuits/abis/rollup/base/base_or_merge_rollup_public_inputs.hpp deleted file mode 100644 index 342acd3808c..00000000000 --- a/circuits/cpp/src/aztec3/circuits/abis/rollup/base/base_or_merge_rollup_public_inputs.hpp +++ /dev/null @@ -1,62 +0,0 @@ -#pragma once - -#include "../../append_only_tree_snapshot.hpp" -#include "../constant_rollup_data.hpp" - -#include "aztec3/constants.hpp" - -namespace aztec3::circuits::abis { - - -const uint32_t BASE_ROLLUP_TYPE = 0; -const uint32_t MERGE_ROLLUP_TYPE = 1; - -template struct BaseOrMergeRollupPublicInputs { - using fr = typename NCT::fr; - using AggregationObject = typename NCT::AggregationObject; - using boolean = typename NCT::boolean; - - uint32_t rollup_type; - // subtree height is always 0 for base. - // so that we always pass-in two base/merge circuits of the same height into the next level of recursion - fr rollup_subtree_height; - - AggregationObject end_aggregation_object; - ConstantRollupData constants; - - AppendOnlyTreeSnapshot start_note_hash_tree_snapshot; - AppendOnlyTreeSnapshot end_note_hash_tree_snapshot; - - AppendOnlyTreeSnapshot start_nullifier_tree_snapshot; - AppendOnlyTreeSnapshot end_nullifier_tree_snapshot; - - AppendOnlyTreeSnapshot start_contract_tree_snapshot; - AppendOnlyTreeSnapshot end_contract_tree_snapshot; - - fr start_public_data_tree_root; - fr end_public_data_tree_root; - - // We hash public inputs to make them constant-sized (to then be unpacked on-chain) - std::array calldata_hash; - - // For serialization, update with new fields - MSGPACK_FIELDS(rollup_type, - rollup_subtree_height, - end_aggregation_object, - constants, - start_note_hash_tree_snapshot, - end_note_hash_tree_snapshot, - start_nullifier_tree_snapshot, - end_nullifier_tree_snapshot, - start_contract_tree_snapshot, - end_contract_tree_snapshot, - start_public_data_tree_root, - end_public_data_tree_root, - calldata_hash); - boolean operator==(BaseOrMergeRollupPublicInputs const& other) const - { - return msgpack_derived_equals(*this, other); - }; -}; - -} // namespace aztec3::circuits::abis diff --git a/circuits/cpp/src/aztec3/circuits/abis/rollup/base/base_rollup_inputs.hpp b/circuits/cpp/src/aztec3/circuits/abis/rollup/base/base_rollup_inputs.hpp deleted file mode 100644 index c50cb0e5572..00000000000 --- a/circuits/cpp/src/aztec3/circuits/abis/rollup/base/base_rollup_inputs.hpp +++ /dev/null @@ -1,67 +0,0 @@ -#pragma once -#include "../../append_only_tree_snapshot.hpp" -#include "../../membership_witness.hpp" -#include "../../previous_kernel_data.hpp" -#include "../constant_rollup_data.hpp" -#include "../nullifier_leaf_preimage.hpp" - -#include "aztec3/constants.hpp" - -#include - -namespace aztec3::circuits::abis { - -template struct BaseRollupInputs { - using fr = typename NCT::fr; - using boolean = typename NCT::boolean; - - std::array, KERNELS_PER_BASE_ROLLUP> kernel_data{}; - - AppendOnlyTreeSnapshot start_note_hash_tree_snapshot{}; - AppendOnlyTreeSnapshot start_nullifier_tree_snapshot{}; - AppendOnlyTreeSnapshot start_contract_tree_snapshot{}; - fr start_public_data_tree_root{}; - AppendOnlyTreeSnapshot start_archive_snapshot{}; - - std::array, MAX_NEW_NULLIFIERS_PER_BASE_ROLLUP> low_nullifier_leaf_preimages{}; - std::array, MAX_NEW_NULLIFIERS_PER_BASE_ROLLUP> - low_nullifier_membership_witness{}; - - // For inserting the new subtrees into their respective trees: - // Note: the insertion leaf index can be derived from the above snapshots' `next_available_leaf_index` values. - std::array new_commitments_subtree_sibling_path{}; - std::array new_nullifiers_subtree_sibling_path{}; - std::array new_contracts_subtree_sibling_path{}; - std::array, MAX_PUBLIC_DATA_UPDATE_REQUESTS_PER_BASE_ROLLUP> - new_public_data_update_requests_sibling_paths{}; - std::array, MAX_PUBLIC_DATA_READS_PER_BASE_ROLLUP> - new_public_data_reads_sibling_paths{}; - - std::array, KERNELS_PER_BASE_ROLLUP> archive_root_membership_witnesses{}; - - ConstantRollupData constants{}; - - // for serialization, update with new fields - MSGPACK_FIELDS(kernel_data, - start_note_hash_tree_snapshot, - start_nullifier_tree_snapshot, - start_contract_tree_snapshot, - start_public_data_tree_root, - start_archive_snapshot, - low_nullifier_leaf_preimages, - low_nullifier_membership_witness, - new_commitments_subtree_sibling_path, - new_nullifiers_subtree_sibling_path, - new_contracts_subtree_sibling_path, - new_public_data_update_requests_sibling_paths, - new_public_data_reads_sibling_paths, - archive_root_membership_witnesses, - constants); - - boolean operator==(BaseRollupInputs const& other) const - { - return msgpack_derived_equals(*this, other); - }; -}; - -} // namespace aztec3::circuits::abis diff --git a/circuits/cpp/src/aztec3/circuits/abis/rollup/constant_rollup_data.hpp b/circuits/cpp/src/aztec3/circuits/abis/rollup/constant_rollup_data.hpp deleted file mode 100644 index e77d45a492f..00000000000 --- a/circuits/cpp/src/aztec3/circuits/abis/rollup/constant_rollup_data.hpp +++ /dev/null @@ -1,34 +0,0 @@ -#pragma once - -#include "../append_only_tree_snapshot.hpp" -#include "../global_variables.hpp" - -#include - -namespace aztec3::circuits::abis { - -template struct ConstantRollupData { - using fr = typename NCT::fr; - - // The very latest roots as at the very beginning of the entire rollup: - AppendOnlyTreeSnapshot start_archive_snapshot{}; - - // Some members of this struct tbd: - fr private_kernel_vk_tree_root = 0; - fr public_kernel_vk_tree_root = 0; - fr base_rollup_vk_hash = 0; - fr merge_rollup_vk_hash = 0; - - GlobalVariables global_variables{}; - - MSGPACK_FIELDS(start_archive_snapshot, - private_kernel_vk_tree_root, - public_kernel_vk_tree_root, - base_rollup_vk_hash, - merge_rollup_vk_hash, - global_variables); - - bool operator==(ConstantRollupData const&) const = default; -}; - -} // namespace aztec3::circuits::abis diff --git a/circuits/cpp/src/aztec3/circuits/abis/rollup/merge/merge_rollup_inputs.hpp b/circuits/cpp/src/aztec3/circuits/abis/rollup/merge/merge_rollup_inputs.hpp deleted file mode 100644 index bf1c30dc291..00000000000 --- a/circuits/cpp/src/aztec3/circuits/abis/rollup/merge/merge_rollup_inputs.hpp +++ /dev/null @@ -1,16 +0,0 @@ -#pragma once -#include "aztec3/circuits/abis/rollup/merge/previous_rollup_data.hpp" - -#include - -namespace aztec3::circuits::abis { - -template struct MergeRollupInputs { - std::array, 2> previous_rollup_data; - - // For serialization, update with new fields - MSGPACK_FIELDS(previous_rollup_data); - bool operator==(MergeRollupInputs const&) const = default; -}; - -} // namespace aztec3::circuits::abis diff --git a/circuits/cpp/src/aztec3/circuits/abis/rollup/merge/previous_rollup_data.hpp b/circuits/cpp/src/aztec3/circuits/abis/rollup/merge/previous_rollup_data.hpp deleted file mode 100644 index 53b2de8ce62..00000000000 --- a/circuits/cpp/src/aztec3/circuits/abis/rollup/merge/previous_rollup_data.hpp +++ /dev/null @@ -1,27 +0,0 @@ -#pragma once -#include "aztec3/circuits/abis/membership_witness.hpp" -#include "aztec3/circuits/abis/rollup/base/base_or_merge_rollup_public_inputs.hpp" -#include "aztec3/constants.hpp" -#include "aztec3/utils/types/native_types.hpp" - -#include - -namespace aztec3::circuits::abis { - -using aztec3::utils::types::NativeTypes; - -template struct PreviousRollupData { - BaseOrMergeRollupPublicInputs base_or_merge_rollup_public_inputs; - - NativeTypes::Proof proof; - std::shared_ptr vk; - NativeTypes::uint32 vk_index = 0; - MembershipWitness vk_sibling_path; - - // For serialization, update with new fields - MSGPACK_FIELDS(base_or_merge_rollup_public_inputs, proof, vk, vk_index, vk_sibling_path); - - bool operator==(PreviousRollupData const&) const = default; -}; - -} // namespace aztec3::circuits::abis diff --git a/circuits/cpp/src/aztec3/circuits/abis/rollup/nullifier_leaf_preimage.hpp b/circuits/cpp/src/aztec3/circuits/abis/rollup/nullifier_leaf_preimage.hpp deleted file mode 100644 index 0f4af4ad921..00000000000 --- a/circuits/cpp/src/aztec3/circuits/abis/rollup/nullifier_leaf_preimage.hpp +++ /dev/null @@ -1,32 +0,0 @@ -#pragma once - -#include "aztec3/utils/types/circuit_types.hpp" - -#include - -namespace aztec3::circuits::abis { - -using aztec3::utils::types::CircuitTypes; -using aztec3::utils::types::NativeTypes; -using std::is_same; - -template struct NullifierLeafPreimage { - using fr = typename NCT::fr; - using uint32 = typename NCT::uint32; - - fr leaf_value = 0; - fr next_value = 0; - uint32 next_index; - - MSGPACK_FIELDS(leaf_value, next_value, next_index); - bool operator==(NullifierLeafPreimage const&) const = default; - - bool is_empty() const { return leaf_value.is_zero() && next_index == 0 && next_value.is_zero(); } - - fr hash() const - { - return is_empty() ? fr::zero() : stdlib::merkle_tree::hash_native({ leaf_value, next_index, next_value }); - } -}; - -} // namespace aztec3::circuits::abis diff --git a/circuits/cpp/src/aztec3/circuits/abis/rollup/root/root_rollup_inputs.hpp b/circuits/cpp/src/aztec3/circuits/abis/rollup/root/root_rollup_inputs.hpp deleted file mode 100644 index ce1993e6194..00000000000 --- a/circuits/cpp/src/aztec3/circuits/abis/rollup/root/root_rollup_inputs.hpp +++ /dev/null @@ -1,41 +0,0 @@ - - -#pragma once -#include "aztec3/circuits/abis/append_only_tree_snapshot.hpp" -#include "aztec3/circuits/abis/rollup/merge/previous_rollup_data.hpp" -#include "aztec3/constants.hpp" - -#include - -namespace aztec3::circuits::abis { - -// TODO: The copy constructor for this struct may throw memory access out of bounds -// Hit when running aztec3-packages/yarn-project/circuits.js/src/rollup/rollup_wasm_wrapper.test.ts."calls -// root_rollup__sim" -template struct RootRollupInputs { - using fr = typename NCT::fr; - - // All below are shared between the base and merge rollups - std::array, 2> previous_rollup_data{}; - - // inputs required to process l1 to l2 messages - std::array new_l1_to_l2_messages{}; - std::array new_l1_to_l2_message_tree_root_sibling_path{}; - - AppendOnlyTreeSnapshot start_l1_to_l2_message_tree_snapshot{}; - - // inputs required to add the block hash - AppendOnlyTreeSnapshot start_archive_snapshot{}; - std::array new_archive_sibling_path{}; - - // For serialization, update with new fields - MSGPACK_FIELDS(previous_rollup_data, - new_l1_to_l2_messages, - new_l1_to_l2_message_tree_root_sibling_path, - start_l1_to_l2_message_tree_snapshot, - start_archive_snapshot, - new_archive_sibling_path); - bool operator==(RootRollupInputs const&) const = default; -}; - -} // namespace aztec3::circuits::abis diff --git a/circuits/cpp/src/aztec3/circuits/abis/rollup/root/root_rollup_public_inputs.hpp b/circuits/cpp/src/aztec3/circuits/abis/rollup/root/root_rollup_public_inputs.hpp deleted file mode 100644 index bf2525c27d8..00000000000 --- a/circuits/cpp/src/aztec3/circuits/abis/rollup/root/root_rollup_public_inputs.hpp +++ /dev/null @@ -1,128 +0,0 @@ -#pragma once - -#include "aztec3/circuits/abis/append_only_tree_snapshot.hpp" -#include "aztec3/circuits/abis/global_variables.hpp" -#include "aztec3/constants.hpp" - -#include - -#include - -namespace aztec3::circuits::abis { - -template struct RootRollupPublicInputs { - using fr = typename NCT::fr; - using AggregationObject = typename NCT::AggregationObject; - - // All below are shared between the base and merge rollups - AggregationObject end_aggregation_object{}; - - GlobalVariables global_variables{}; - - AppendOnlyTreeSnapshot start_note_hash_tree_snapshot{}; - AppendOnlyTreeSnapshot end_note_hash_tree_snapshot{}; - - AppendOnlyTreeSnapshot start_nullifier_tree_snapshot{}; - AppendOnlyTreeSnapshot end_nullifier_tree_snapshot{}; - - AppendOnlyTreeSnapshot start_contract_tree_snapshot{}; - AppendOnlyTreeSnapshot end_contract_tree_snapshot{}; - - fr start_public_data_tree_root{}; - fr end_public_data_tree_root{}; - - AppendOnlyTreeSnapshot start_tree_of_historical_note_hash_tree_roots_snapshot{}; - AppendOnlyTreeSnapshot end_tree_of_historical_note_hash_tree_roots_snapshot{}; - - AppendOnlyTreeSnapshot start_tree_of_historical_contract_tree_roots_snapshot{}; - AppendOnlyTreeSnapshot end_tree_of_historical_contract_tree_roots_snapshot{}; - - AppendOnlyTreeSnapshot start_l1_to_l2_message_tree_snapshot{}; - AppendOnlyTreeSnapshot end_l1_to_l2_message_tree_snapshot{}; - - AppendOnlyTreeSnapshot start_tree_of_historical_l1_to_l2_message_tree_roots_snapshot{}; - AppendOnlyTreeSnapshot end_tree_of_historical_l1_to_l2_message_tree_roots_snapshot{}; - - AppendOnlyTreeSnapshot start_archive_snapshot{}; - AppendOnlyTreeSnapshot end_archive_snapshot{}; - - std::array calldata_hash{}; - std::array l1_to_l2_messages_hash{}; - - // For serialization, update with new fields - MSGPACK_FIELDS(end_aggregation_object, - global_variables, - start_note_hash_tree_snapshot, - end_note_hash_tree_snapshot, - start_nullifier_tree_snapshot, - end_nullifier_tree_snapshot, - start_contract_tree_snapshot, - end_contract_tree_snapshot, - start_public_data_tree_root, - end_public_data_tree_root, - start_tree_of_historical_note_hash_tree_roots_snapshot, - end_tree_of_historical_note_hash_tree_roots_snapshot, - start_tree_of_historical_contract_tree_roots_snapshot, - end_tree_of_historical_contract_tree_roots_snapshot, - start_l1_to_l2_message_tree_snapshot, - end_l1_to_l2_message_tree_snapshot, - start_tree_of_historical_l1_to_l2_message_tree_roots_snapshot, - end_tree_of_historical_l1_to_l2_message_tree_roots_snapshot, - start_archive_snapshot, - end_archive_snapshot, - calldata_hash, - l1_to_l2_messages_hash); - - bool operator==(RootRollupPublicInputs const&) const = default; - - fr hash() const - { - std::vector buf; - - write(&buf, global_variables); - write(buf, start_note_hash_tree_snapshot); - write(buf, start_nullifier_tree_snapshot); - write(buf, start_contract_tree_snapshot); - write(buf, start_tree_of_historical_note_hash_tree_roots_snapshot); - write(buf, start_tree_of_historical_contract_tree_roots_snapshot); - write(buf, start_public_data_tree_root); - write(buf, start_l1_to_l2_message_tree_snapshot); - write(buf, start_tree_of_historical_l1_to_l2_message_tree_roots_snapshot); - write(buf, start_archive_snapshot); - write(buf, end_note_hash_tree_snapshot); - write(buf, end_nullifier_tree_snapshot); - write(buf, end_contract_tree_snapshot); - write(buf, end_tree_of_historical_note_hash_tree_roots_snapshot); - write(buf, end_tree_of_historical_contract_tree_roots_snapshot); - write(buf, end_public_data_tree_root); - write(buf, end_l1_to_l2_message_tree_snapshot); - write(buf, end_tree_of_historical_l1_to_l2_message_tree_roots_snapshot); - write(buf, end_archive_snapshot); - - // Stitching calldata hash together - auto high_buffer = calldata_hash[0].to_buffer(); - auto low_buffer = calldata_hash[1].to_buffer(); - - for (uint8_t i = 0; i < 16; i++) { - buf.push_back(high_buffer[16 + i]); - } - for (uint8_t i = 0; i < 16; i++) { - buf.push_back(low_buffer[16 + i]); - } - - // Stitch l1_to_l2_messages_hash - auto high_buffer_m = l1_to_l2_messages_hash[0].to_buffer(); - auto low_buffer_m = l1_to_l2_messages_hash[1].to_buffer(); - - for (uint8_t i = 0; i < 16; i++) { - buf.push_back(high_buffer_m[16 + i]); - } - for (uint8_t i = 0; i < 16; i++) { - buf.push_back(low_buffer_m[16 + i]); - } - - return sha256::sha256_to_field(buf); - } -}; - -} // namespace aztec3::circuits::abis diff --git a/circuits/cpp/src/aztec3/circuits/abis/tx_context.hpp b/circuits/cpp/src/aztec3/circuits/abis/tx_context.hpp deleted file mode 100644 index 278c8dc2ce0..00000000000 --- a/circuits/cpp/src/aztec3/circuits/abis/tx_context.hpp +++ /dev/null @@ -1,108 +0,0 @@ -#pragma once -#include "contract_deployment_data.hpp" -#include "function_data.hpp" - -#include "aztec3/utils/types/circuit_types.hpp" -#include "aztec3/utils/types/convert.hpp" -#include "aztec3/utils/types/native_types.hpp" - -#include - -namespace aztec3::circuits::abis { - -using aztec3::utils::types::CircuitTypes; -using aztec3::utils::types::NativeTypes; - -template struct TxContext { - using address = typename NCT::address; - using fr = typename NCT::fr; - using boolean = typename NCT::boolean; - - boolean is_fee_payment_tx = false; - boolean is_rebate_payment_tx = false; - boolean is_contract_deployment_tx = false; - - ContractDeploymentData contract_deployment_data{}; - - fr chain_id = 0; - fr version = 0; - - // for serialization: update up with new fields - MSGPACK_FIELDS(is_fee_payment_tx, - is_rebate_payment_tx, - is_contract_deployment_tx, - contract_deployment_data, - chain_id, - version); - boolean operator==(TxContext const& other) const - { - return is_fee_payment_tx == other.is_fee_payment_tx && is_rebate_payment_tx == other.is_rebate_payment_tx && - is_contract_deployment_tx == other.is_contract_deployment_tx && - contract_deployment_data == other.contract_deployment_data && chain_id == other.chain_id && - version == other.version; - }; - - template TxContext> to_circuit_type(Builder& builder) const - { - static_assert((std::is_same::value)); - - // Capture the circuit builder: - auto to_ct = [&](auto& e) { return aztec3::utils::types::to_ct(builder, e); }; - // auto to_circuit_type = [&](auto& e) { return e.to_circuit_type(builder); }; - - TxContext> tx_context = { - to_ct(is_fee_payment_tx), - to_ct(is_rebate_payment_tx), - to_ct(is_contract_deployment_tx), - contract_deployment_data.to_circuit_type(builder), - to_ct(chain_id), - to_ct(version), - }; - - return tx_context; - }; - - template TxContext to_native_type() const - { - static_assert(std::is_same, NCT>::value); - auto to_nt = [&](auto& e) { return aztec3::utils::types::to_nt(e); }; - auto to_native_type = [](T& e) { return e.template to_native_type(); }; - - TxContext tx_context = { to_nt(is_fee_payment_tx), - to_nt(is_rebate_payment_tx), - to_nt(is_contract_deployment_tx), - to_native_type(contract_deployment_data), - to_nt(chain_id), - to_nt(version) }; - - return tx_context; - }; - - void set_public() - { - static_assert(!(std::is_same::value)); - - fr(is_fee_payment_tx).set_public(); - fr(is_rebate_payment_tx).set_public(); - fr(is_contract_deployment_tx).set_public(); - contract_deployment_data.set_public(); - chain_id.set_public(); - version.set_public(); - } - - fr hash() const - { - std::vector const inputs = { - fr(is_fee_payment_tx), - fr(is_rebate_payment_tx), - fr(is_contract_deployment_tx), - contract_deployment_data.hash(), - chain_id, - version, - }; - - return NCT::hash(inputs, GeneratorIndex::TX_CONTEXT); - } -}; - -} // namespace aztec3::circuits::abis diff --git a/circuits/cpp/src/aztec3/circuits/abis/tx_request.hpp b/circuits/cpp/src/aztec3/circuits/abis/tx_request.hpp deleted file mode 100644 index c6682bf59c9..00000000000 --- a/circuits/cpp/src/aztec3/circuits/abis/tx_request.hpp +++ /dev/null @@ -1,64 +0,0 @@ -#pragma once -#include "function_data.hpp" -#include "tx_context.hpp" - -#include "aztec3/utils/types/circuit_types.hpp" -#include "aztec3/utils/types/convert.hpp" -#include "aztec3/utils/types/native_types.hpp" - -#include - -namespace aztec3::circuits::abis { - -using aztec3::utils::types::CircuitTypes; -using aztec3::utils::types::NativeTypes; - -template struct TxRequest { - using address = typename NCT::address; - using fr = typename NCT::fr; - using boolean = typename NCT::boolean; - - address origin = 0; - FunctionData function_data{}; - fr args_hash = 0; - TxContext tx_context{}; - - // For serialization, update with new fields - MSGPACK_FIELDS(origin, function_data, args_hash, tx_context); - boolean operator==(TxContext const& other) const - { - return origin == other.origin && function_data == other.function_data && args_hash == other.args && - tx_context == other.tx_context; - }; - - template TxRequest> to_circuit_type(Builder& builder) const - { - static_assert((std::is_same::value)); - - // Capture the circuit builder: - auto to_ct = [&](auto& e) { return aztec3::utils::types::to_ct(builder, e); }; - auto to_circuit_type = [&](auto& e) { return e.to_circuit_type(builder); }; - - TxRequest> tx_request = { - to_ct(origin), - to_circuit_type(function_data), - to_ct(args_hash), - to_circuit_type(tx_context), - }; - - return tx_request; - }; - - fr hash() const - { - std::vector inputs; - inputs.push_back(fr(origin)); - inputs.push_back(function_data.hash()); - inputs.push_back(args_hash); - inputs.push_back(tx_context.hash()); - - return NCT::hash(inputs, GeneratorIndex::TX_REQUEST); - } -}; - -} // namespace aztec3::circuits::abis diff --git a/circuits/cpp/src/aztec3/circuits/abis/types.hpp b/circuits/cpp/src/aztec3/circuits/abis/types.hpp deleted file mode 100644 index e0601e48fc0..00000000000 --- a/circuits/cpp/src/aztec3/circuits/abis/types.hpp +++ /dev/null @@ -1,20 +0,0 @@ -#pragma once -#include "private_circuit_public_inputs.hpp" -#include "public_circuit_public_inputs.hpp" - -#include - -namespace aztec3::circuits::abis { - -template struct PrivateTypes { - using AppCircuitPublicInputs = PrivateCircuitPublicInputs; - // used in schema serialization - static constexpr char schema_name[] = "Private"; // NOLINT -}; - -template struct PublicTypes { - using AppCircuitPublicInputs = PublicCircuitPublicInputs; - // used in schema serialization - static constexpr char schema_name[] = "Public"; // NOLINT -}; -} // namespace aztec3::circuits::abis diff --git a/circuits/cpp/src/aztec3/circuits/apps/.test.cpp b/circuits/cpp/src/aztec3/circuits/apps/.test.cpp deleted file mode 100644 index de490d5fd4e..00000000000 --- a/circuits/cpp/src/aztec3/circuits/apps/.test.cpp +++ /dev/null @@ -1,383 +0,0 @@ - -#include - -// #include "utxo_state_var.hpp" - -#include "contract.hpp" -#include "function_execution_context.hpp" -#include "oracle_wrapper.hpp" -#include "notes/default_private_note/note.hpp" -#include "notes/default_private_note/note_preimage.hpp" -#include "notes/default_singleton_private_note/note.hpp" -#include "notes/default_singleton_private_note/note_preimage.hpp" -#include "notes/note_interface.hpp" -#include "state_vars/field_state_var.hpp" -#include "state_vars/mapping_state_var.hpp" -#include "state_vars/utxo_set_state_var.hpp" -#include "state_vars/utxo_state_var.hpp" - -#include "aztec3/oracle/oracle.hpp" -#include "aztec3/utils/types/circuit_types.hpp" -#include "aztec3/utils/types/convert.hpp" -#include "aztec3/utils/types/native_types.hpp" - -#include - -namespace { -// Builder -using C = UltraCircuitBuilder; - -// Types -using CT = aztec3::utils::types::CircuitTypes; -using NT = aztec3::utils::types::NativeTypes; -using aztec3::utils::types::to_ct; - -// exec_ctx -// using aztec3::circuits::apps::FunctionExecutionContext; - -// Contract -using Contract = aztec3::circuits::apps::Contract; - -// Oracle -using DB = aztec3::oracle::FakeDB; -using aztec3::oracle::NativeOracle; -using OracleWrapper = aztec3::circuits::apps::OracleWrapperInterface; - -// StateVars -using aztec3::circuits::apps::state_vars::FieldStateVar; -using aztec3::circuits::apps::state_vars::MappingStateVar; -using aztec3::circuits::apps::state_vars::UTXOSetStateVar; -using aztec3::circuits::apps::state_vars::UTXOStateVar; - - -using aztec3::circuits::apps::notes::DefaultPrivateNote; - -using aztec3::circuits::apps::notes::DefaultSingletonPrivateNote; - -// State variables -// Get rid of ugly `Builder` template arg from our state var types: -template using Mapping = MappingStateVar; -template using UTXO = UTXOStateVar; -template using UTXOSet = UTXOSetStateVar; - -using Field = FieldStateVar; -} // namespace - -namespace aztec3::circuits::apps { - -class state_var_tests : public ::testing::Test { - protected: - static NativeOracle get_test_native_oracle(DB& db) - { - const NT::address contract_address = 12345; - const NT::fr msg_sender_private_key = 123456789; - const NT::address msg_sender = NT::fr( - uint256_t(0x01071e9a23e0f7edULL, 0x5d77b35d1830fa3eULL, 0xc6ba3660bb1f0c0bULL, 0x2ef9f7f09867fd6eULL)); - - FunctionData const function_data{ - .selector = - { - .value = 1, // TODO: deduce this from the contract, somehow. - }, - .is_private = true, - .is_constructor = false, - }; - - CallContext const call_context{ .msg_sender = msg_sender, - .storage_contract_address = contract_address, - .portal_contract_address = 0, - .is_delegate_call = false, - .is_static_call = false, - .is_contract_deployment = false }; - - return NativeOracle(db, contract_address, function_data, call_context, msg_sender_private_key); - }; -}; - -TEST_F(state_var_tests, circuit_mapping) -{ - // TODO: currently, we can't hide all of this boilerplate in a test fixture function, because each of these classes - // contains a reference to earlier-declared classes... so we'd end up with classes containing dangling references, - // if all this stuff were to be declared in a setup function's scope. - // We could instead store shared_ptrs in every class...? - C builder = C(); - DB db; - NativeOracle native_oracle = get_test_native_oracle(db); - OracleWrapper oracle_wrapper = OracleWrapper(builder, native_oracle); - FunctionExecutionContext exec_ctx(builder, oracle_wrapper); - - // TODO: - // Interestingly, if I scope the below, the debugger works, but running the test via the command line fails. This is - // because all pointers to the contract which are stored in other classes become dangling pointers, because contract - // would go out of scope immediately... so the declaration of this contract and any pointers probably all need to be - // shared_ptr eventually. - // { - - // I'm not entirely sure why we need to prepend `::` to `Contract`, to get to the unnamed namespace's declaration of - // `Contract` above... - ::Contract contract("TestContract"); - exec_ctx.register_contract(&contract); - - contract.declare_state_var("my_mapping"); - // } - - Mapping my_mapping(&exec_ctx, "my_mapping"); - - my_mapping[5] = to_ct(builder, NT::fr(5)); - - // info("my_mapping[5]: ", my_mapping[5]); - // info("my_mapping[5].start_slot: ", my_mapping[5].start_slot); - // info("my_mapping[5].storage_slot_point: ", my_mapping[5].storage_slot_point); -} - -TEST_F(state_var_tests, circuit_mapping_within_mapping) -{ - C builder = C(); - DB db; - NativeOracle native_oracle = get_test_native_oracle(db); - OracleWrapper oracle_wrapper = OracleWrapper(builder, native_oracle); - FunctionExecutionContext exec_ctx(builder, oracle_wrapper); - - // { - ::Contract contract("TestContract"); - exec_ctx.register_contract(&contract); - - contract.declare_state_var("my_mapping"); - // } - - Mapping> my_mapping(&exec_ctx, "my_mapping"); - - my_mapping[5][6] = 7; - - info(my_mapping[5][6].state_var_name, ": ", my_mapping[5][6]); -} - -TEST_F(state_var_tests, circuit_partial_mapping) -{ - C builder = C(); - DB db; - NativeOracle native_oracle = get_test_native_oracle(db); - OracleWrapper oracle_wrapper = OracleWrapper(builder, native_oracle); - FunctionExecutionContext exec_ctx(builder, oracle_wrapper); - - // { - ::Contract contract("TestContract"); - exec_ctx.register_contract(&contract); - - contract.declare_state_var("my_mapping"); - // } - - Mapping> my_mapping(&exec_ctx, "my_mapping"); - - my_mapping["?"][6] = 7; - - info(my_mapping["?"][6].state_var_name, ": ", my_mapping["?"][6]); -} - -TEST_F(state_var_tests, circuit_utxo_of_default_private_note_fr) -{ - C builder = C(); - DB db; - NativeOracle native_oracle = get_test_native_oracle(db); - OracleWrapper oracle_wrapper = OracleWrapper(builder, native_oracle); - FunctionExecutionContext exec_ctx(builder, oracle_wrapper); - - ::Contract contract("TestContract"); - exec_ctx.register_contract(&contract); - - contract.declare_state_var("my_utxo"); - - // FUNCTION: - - using Note = DefaultPrivateNote; - - UTXO my_utxo(&exec_ctx, "my_utxo"); - - const auto& msg_sender = oracle_wrapper.get_msg_sender(); - - Note old_note = my_utxo.get({ .owner = msg_sender }); - - old_note.remove(); - - CT::fr const old_value = *(old_note.get_preimage().value); - - CT::fr new_value = old_value + 5; - - my_utxo.insert({ .value = new_value, // - .owner = msg_sender, - .creator_address = msg_sender, - .memo = 1234 }); - - exec_ctx.finalize(); - - // Here, we test that the shared_ptr of a note, stored within the exec_ctx, works. TODO: put this in its own little - // test, instead of this ever-growing beast test. - auto new_note_pointers = exec_ctx.get_new_notes(); - std::shared_ptr const debug_note = std::dynamic_pointer_cast(new_note_pointers[0]); - // info("new_note_pointers: ", new_note_pointers); - // info("*(new_note_pointers[0]): ", debug_note->get_preimage()); - - auto new_nullifiers = exec_ctx.get_new_nullifiers(); - // info("new_nullifiers: ", new_nullifiers); -} - -TEST_F(state_var_tests, circuit_utxo_set_of_default_private_notes_fr) -{ - C builder = C(); - DB db; - NativeOracle native_oracle = get_test_native_oracle(db); - OracleWrapper oracle_wrapper = OracleWrapper(builder, native_oracle); - FunctionExecutionContext exec_ctx(builder, oracle_wrapper); - - // bool sort(NT::uint256 i, NT::uint256 j) - // { - // return (i < j); - // }; - - ::Contract contract("TestContract"); - exec_ctx.register_contract(&contract); - - contract.declare_state_var("balances"); - - // FUNCTION: - - using Note = DefaultPrivateNote; - - UTXOSet balances(&exec_ctx, "balances"); - - // Imagine these were passed into the function as args: - CT::fr const amount = 5; - CT::address to_address = 765976; - - const auto& msg_sender = oracle_wrapper.get_msg_sender(); - - std::vector old_balance_notes = balances.get(2, { .owner = msg_sender }); - - CT::fr const old_value_1 = *(old_balance_notes[0].get_preimage().value); - CT::fr const old_value_2 = *(old_balance_notes[1].get_preimage().value); - - old_balance_notes[0].remove(); - old_balance_notes[1].remove(); - - // MISSING: overflow & underflow checks, but I can't be bothered with safe_uint or range checks yet. - - CT::fr new_value = (old_value_1 + old_value_2) - amount; - - balances.insert({ - .value = new_value, - .owner = to_address, - .creator_address = msg_sender, - .memo = 1234, - }); - - exec_ctx.finalize(); - - // Here, we test that the shared_ptr of a note, stored within the exec_ctx, works. TODO: put this in its own little - // test, instead of this ever-growing beast test. - auto new_note_pointers = exec_ctx.get_new_notes(); - std::shared_ptr const debug_note = std::dynamic_pointer_cast(new_note_pointers[0]); - // info("new_note_pointers: ", new_note_pointers); - // info("*(new_note_pointers[0]): ", debug_note->get_preimage()); - - auto new_nullifiers = exec_ctx.get_new_nullifiers(); - // info("new_nullifiers: ", new_nullifiers); -} - -TEST_F(state_var_tests, circuit_initialize_utxo_of_default_singleton_private_note_fr) -{ - C builder = C(); - DB db; - NativeOracle native_oracle = get_test_native_oracle(db); - OracleWrapper oracle_wrapper = OracleWrapper(builder, native_oracle); - FunctionExecutionContext exec_ctx(builder, oracle_wrapper); - - ::Contract contract("TestContract"); - exec_ctx.register_contract(&contract); - - contract.declare_state_var("my_utxo"); - - // FUNCTION: - - // This time we use a slightly different Note type, which is tailored towards singleton UTXO use-cases. In - // particular, it copes with the distinction between initialization of the UTXO, vs future modification of the UTXO. - using Note = DefaultSingletonPrivateNote; - - UTXO my_utxo(&exec_ctx, "my_utxo"); - - // We hard-code the address of the person who may initialize the state in the 'contract's bytecode' (i.e. as a - // selector value). (Number chosen to match msg_sender of tests). - const CT::address unique_person_who_may_initialize = - NT::uint256(0x01071e9a23e0f7edULL, 0x5d77b35d1830fa3eULL, 0xc6ba3660bb1f0c0bULL, 0x2ef9f7f09867fd6eULL); - - unique_person_who_may_initialize.assert_equal(oracle_wrapper.get_msg_sender()); - - // The person who may initialize the note might be different from the person who's actually given the note to own. - // (E.g. the caller of this function might be the deployer of the contract, who is initializing notes on behalf of - // other users) - CT::address owner_of_initialized_note = 888888; - - my_utxo.initialize({ .value = 100, .owner = owner_of_initialized_note }); - - exec_ctx.finalize(); - - // Here, we test that the shared_ptr of a note, stored within the exec_ctx, works. TODO: put this in its own little - // test, instead of this ever-growing beast test. - auto new_note_pointers = exec_ctx.get_new_notes(); - std::shared_ptr const debug_note = std::dynamic_pointer_cast(new_note_pointers[0]); - // info("new_note_pointers: ", new_note_pointers); - // info("*(new_note_pointers[0]): ", debug_note->get_preimage()); - - auto new_nullifiers = exec_ctx.get_new_nullifiers(); - // info("new_nullifiers: ", new_nullifiers); -} - -TEST_F(state_var_tests, circuit_modify_utxo_of_default_singleton_private_note_fr) -{ - C builder = C(); - DB db; - NativeOracle native_oracle = get_test_native_oracle(db); - OracleWrapper oracle_wrapper = OracleWrapper(builder, native_oracle); - FunctionExecutionContext exec_ctx(builder, oracle_wrapper); - - ::Contract contract("TestContract"); - exec_ctx.register_contract(&contract); - - contract.declare_state_var("my_utxo"); - - // FUNCTION: - - // This time we use a slightly different Note type, which is tailored towards singleton UTXO use-cases. In - // particular, it copes with the distinction between initialization of the UTXO, vs future modification of the UTXO. - using Note = DefaultSingletonPrivateNote; - - UTXO my_utxo(&exec_ctx, "my_utxo"); - - const auto& msg_sender = oracle_wrapper.get_msg_sender(); - - Note old_note = my_utxo.get({ .owner = msg_sender }); - - old_note.remove(); - - CT::fr const old_value = *(old_note.get_preimage().value); - - CT::fr new_value = old_value + 5; - - my_utxo.insert({ - .value = new_value, // - .owner = msg_sender, - }); - - exec_ctx.finalize(); - - // Here, we test that the shared_ptr of a note, stored within the exec_ctx, works. TODO: put this in its own little - // test, instead of this ever-growing beast test. - auto new_note_pointers = exec_ctx.get_new_notes(); - std::shared_ptr const debug_note = std::dynamic_pointer_cast(new_note_pointers[0]); - // info("new_note_pointers: ", new_note_pointers); - // info("*(new_note_pointers[0]): ", debug_note->get_preimage()); - - auto new_nullifiers = exec_ctx.get_new_nullifiers(); - // info("new_nullifiers: ", new_nullifiers); -} - -} // namespace aztec3::circuits::apps \ No newline at end of file diff --git a/circuits/cpp/src/aztec3/circuits/apps/CMakeLists.txt b/circuits/cpp/src/aztec3/circuits/apps/CMakeLists.txt deleted file mode 100644 index 59a9a8ad289..00000000000 --- a/circuits/cpp/src/aztec3/circuits/apps/CMakeLists.txt +++ /dev/null @@ -1,4 +0,0 @@ -circuits_cmake_module( - aztec3_circuits_apps - barretenberg -) \ No newline at end of file diff --git a/circuits/cpp/src/aztec3/circuits/apps/README.md b/circuits/cpp/src/aztec3/circuits/apps/README.md deleted file mode 100644 index d323b199f28..00000000000 --- a/circuits/cpp/src/aztec3/circuits/apps/README.md +++ /dev/null @@ -1,59 +0,0 @@ -# aztec3::circuits::apps - -All code in this dir is for: -- Demonstrating how our users' (developers) apps will need to be written, in terms of: - - Public inputs - - Syntax for State Variables & UTXOs - - Function calls -- To help illustrate ideas to the Noir team -- To allow test app circuits to be mocked-up as quickly and easily as possible, for use in the Kernel & Rollup circuits. - - - - -utxo_state_var.insert(note_preimage) - ↓ -Opcodes.UTXO_SSTORE (this=state_var*, note_preimage) - ↓ -exec_ctx got from state_var->exec_ctx -new_note = Note(state_var, new_note_preimage) <- could be done in state_var -exec_ctx->new_notes.push_back(new_note) -NO VISITOR CALLED - - - -utxo_state_var.get(NotePreimage advice) - ↓ -Opcodes.UTXO_LOAD (this=state_var*, advice) - ↓ -oracle got from state_var->exec_ctx->oracle -storage_slot_point got from state_var->storage_slot_point -Grab note_preimages and paths from DB -new_note = Note(state_var, note_preimage) -Compare against advice -- VISITOR CALLED: new_note.constrain_against_advice(advice) -Membership checks (and other checks) -- VISITOR CALLED: new_note.get_commitment() -return new_note - - - -note.remove() - ↓ -Opcodes.UTXO_NULL (state_var*, *this=note) - ↓ -exec_ctx got from state_var->exec_ctx -- VISITOR CALLED: - - nullifier = note.compute_nullifier() <- could be done in state_var -exec_ctx->new_nullifiers.push_back(nullifier) - - - -utxo_state_var.initialize(note_preimage) - ↓ -Opcodes.UTXO_SSTORE (this=state_var*, note_preimage) - ↓ -exec_ctx got from state_var->exec_ctx -new_note = Note(state_var, new_note_preimage) -exec_ctx->new_notes.push_back(new_note) -NO VISITOR CALLED \ No newline at end of file diff --git a/circuits/cpp/src/aztec3/circuits/apps/contract.hpp b/circuits/cpp/src/aztec3/circuits/apps/contract.hpp deleted file mode 100644 index cfb880a96cc..00000000000 --- a/circuits/cpp/src/aztec3/circuits/apps/contract.hpp +++ /dev/null @@ -1,103 +0,0 @@ -#pragma once - -#include "function_declaration.hpp" - -#include "aztec3/circuits/abis/function_data.hpp" - -#include - -namespace aztec3::circuits::apps { - -using aztec3::circuits::abis::FunctionData; - -using NT = aztec3::utils::types::NativeTypes; - -// template class FunctionExecutionContext; - -template class Contract { - using fr = typename NCT::fr; - using uint32 = typename NCT::uint32; - - public: - const std::string contract_name; - - fr state_var_counter = 0; - - std::vector state_var_names; - - std::map start_slots_by_state_var_name; - - std::map> function_datas; - - std::map> imported_contracts; - - explicit Contract(std::string const& contract_name) : contract_name(contract_name) - { - // exec_ctx.register_contract(this); - } - - void set_functions(std::vector> const& functions); - - void import_contracts(std::vector>> import_declarations); - - Contract& get_imported_contract(std::string const& name) - { - if (!imported_contracts.contains(name)) { - throw_or_abort("No contract with that name imported"); - } - return imported_contracts[name]; - } - - // TODO: return some Function class which has a `call` method... - // FunctionData get_function(std::string name) { return function_data[name]; } - - FunctionData get_function_data_by_name(std::string const& name); - - // TODO: maybe also declare a type at this stage, so the correct type can be checked-for when the StateVar type is - // created within the function. - /** - * Note: this simply tracks the 'start' storage slots of each state variable at the 'contract scope level'. - * TODO: maybe we can just keep a vector of names and query the start slot with index_of(), instead. - */ - void declare_state_var(std::string const& state_var_name) - { - push_new_state_var_name(state_var_name); - start_slots_by_state_var_name[state_var_name] = state_var_counter; - // state_var_counter++; - state_var_counter++; - ASSERT(state_var_counter == state_var_names.size()); - }; - - fr& get_start_slot(std::string const& state_var_name) - { - if (!start_slots_by_state_var_name.contains(state_var_name)) { - throw_or_abort("Name '" + state_var_name + "' not found. Use `declare_state_var`."); - } - return start_slots_by_state_var_name.at(state_var_name); - }; - - private: - // Prevents an infinite loop if two contracts import each other. - bool already_imported; - - void push_new_state_var_name(std::string const& state_var_name) - { - if (index_of(state_var_names, state_var_name) == -1) { - state_var_names.push_back(state_var_name); - - return; - } - throw_or_abort("name already exists"); - } -}; - -} // namespace aztec3::circuits::apps - -// Importing in this way (rather than explicit instantiation of a template class at the bottom of a .cpp file) preserves -// the following: -// - We retain implicit instantiation of templates. -// - We don't implement method definitions in this file, to avoid a circular dependency with -// function_execution_context.hpp. -// TODO: things have changed since initially importing this .tpp file - maybe a conventional .cpp file is possible now -// instead... -#include "contract.tpp" \ No newline at end of file diff --git a/circuits/cpp/src/aztec3/circuits/apps/contract.tpp b/circuits/cpp/src/aztec3/circuits/apps/contract.tpp deleted file mode 100644 index 0496fb90dfa..00000000000 --- a/circuits/cpp/src/aztec3/circuits/apps/contract.tpp +++ /dev/null @@ -1,65 +0,0 @@ -#pragma once - -#include "function_declaration.hpp" -#include "function_execution_context.hpp" - -#include "aztec3/circuits/abis/function_data.hpp" -#include "aztec3/constants.hpp" -#include "aztec3/utils/types/convert.hpp" - -#include - -namespace aztec3::circuits::apps { - -using NT = aztec3::utils::types::NativeTypes; -using aztec3::circuits::abis::FunctionData; -using aztec3::circuits::abis::FunctionSelector; - -template void Contract::set_functions(std::vector> const& functions) -{ - for (uint32_t i = 0; i < functions.size(); ++i) { - const auto& function = functions[i]; - if (function_datas.contains(function.name)) { - throw_or_abort("Name already exists"); - } - function_datas[function.name] = FunctionData{ - .selector = - { - .value = static_cast(i), - }, - .is_private = function.is_private, - .is_constructor = function.is_constructor, - }; - } -}; - -template -void Contract::import_contracts(std::vector>> const import_declarations) -{ - // Prevents an infinite loop if two contracts import each-other. - if (already_imported) { - return; - } - - for (uint32_t i = 0; i < import_declarations.size(); ++i) { - const std::pair>& decl = import_declarations[i]; - if (imported_contracts.contains(decl.first)) { - throw_or_abort("Name already exists"); - } - imported_contracts.insert(decl); - } - already_imported = true; -} - -// TODO: return some Function class which has a `call` method... -// FunctionData get_function(std::string name) { return function_data[name]; } - -template FunctionData Contract::get_function_data_by_name(std::string const& name) -{ - if (!function_datas.contains(name)) { - throw_or_abort("function data not found"); - } - return function_datas[name]; -} - -} // namespace aztec3::circuits::apps \ No newline at end of file diff --git a/circuits/cpp/src/aztec3/circuits/apps/function_declaration.hpp b/circuits/cpp/src/aztec3/circuits/apps/function_declaration.hpp deleted file mode 100644 index 20266e2e9f8..00000000000 --- a/circuits/cpp/src/aztec3/circuits/apps/function_declaration.hpp +++ /dev/null @@ -1,23 +0,0 @@ -#pragma once -#include "aztec3/utils/types/circuit_types.hpp" -#include "aztec3/utils/types/convert.hpp" -#include "aztec3/utils/types/native_types.hpp" - -#include - -namespace aztec3::circuits::apps { - -using aztec3::utils::types::CircuitTypes; -using aztec3::utils::types::NativeTypes; -using plonk::stdlib::witness_t; - -// This exists just so that designated initializers can be used when passing this info to a function, for readability. -template struct FunctionDeclaration { - using boolean = typename NCT::boolean; - - std::string name; - boolean is_private = false; - boolean is_constructor = false; -}; - -} // namespace aztec3::circuits::apps \ No newline at end of file diff --git a/circuits/cpp/src/aztec3/circuits/apps/function_execution_context.hpp b/circuits/cpp/src/aztec3/circuits/apps/function_execution_context.hpp deleted file mode 100644 index 172e17d13c1..00000000000 --- a/circuits/cpp/src/aztec3/circuits/apps/function_execution_context.hpp +++ /dev/null @@ -1,336 +0,0 @@ -#pragma once - -#include "contract.hpp" -#include "oracle_wrapper.hpp" -#include "notes/note_interface.hpp" -#include "opcodes/opcodes.hpp" - -#include "aztec3/circuits/abis/call_stack_item.hpp" -#include "aztec3/circuits/abis/function_data.hpp" -#include "aztec3/circuits/abis/private_circuit_public_inputs.hpp" -#include "aztec3/circuits/abis/types.hpp" -#include "aztec3/circuits/hash.hpp" -#include "aztec3/constants.hpp" -#include "aztec3/utils/types/convert.hpp" - -#include - -namespace aztec3::circuits::apps { - -using aztec3::circuits::abis::CallStackItem; -using aztec3::circuits::abis::FunctionData; -using aztec3::circuits::abis::OptionalPrivateCircuitPublicInputs; -using aztec3::circuits::abis::PrivateCircuitPublicInputs; -using aztec3::circuits::abis::PrivateTypes; - -using aztec3::circuits::apps::notes::NoteInterface; -using aztec3::circuits::apps::opcodes::Opcodes; - -using plonk::stdlib::array_push; - -using aztec3::utils::types::CircuitTypes; -using plonk::stdlib::witness_t; -using NT = aztec3::utils::types::NativeTypes; -using aztec3::utils::types::to_ct; -using aztec3::utils::types::to_nt; - -template class FunctionExecutionContext { - using NT = NativeTypes; - using CT = CircuitTypes; - using fr = typename CT::fr; - using address = typename CT::address; - - // We restrict only the opcodes to be able to push to the private members of the exec_ctx. - // This will just help us build better separation of concerns. - friend class Opcodes; - - public: - Builder& builder; - OracleWrapperInterface& oracle; - - Contract* contract = nullptr; - - std::array>, MAX_PRIVATE_CALL_STACK_LENGTH_PER_CALL> - nested_private_call_exec_ctxs; - - // TODO: make this private! - OptionalPrivateCircuitPublicInputs private_circuit_public_inputs{}; - - private: - std::vector>> new_notes; - std::vector new_commitments; - - // Nullifier preimages can be got from the corresponding Note that they nullify. - std::vector>> nullified_notes; - std::vector new_nullifiers; - std::vector nullified_commitments; - - PrivateCircuitPublicInputs final_private_circuit_public_inputs{}; - - bool is_finalized = false; - - public: - FunctionExecutionContext(Builder& builder, OracleWrapperInterface& oracle) - : builder(builder) - , oracle(oracle) - , private_circuit_public_inputs(OptionalPrivateCircuitPublicInputs::create()) - { - private_circuit_public_inputs.call_context = oracle.get_call_context(); - private_circuit_public_inputs.contract_deployment_data = oracle.get_contract_deployment_data(); - } - - void register_contract(Contract* contract) - { - if (this->contract != nullptr) { - throw_or_abort("A contract is already assigned to this FunctionExecutionContext"); - } - this->contract = contract; - } - - // TODO: consider making this a debug-only method. - // Not a reference, because we won't want to allow unsafe access. Hmmm, except it's a vector of pointers, so one can - // still modify the pointers... But at least the original vector isn't being pushed-to or deleted-from. - std::vector>> get_new_notes() { return new_notes; } - - std::vector get_new_nullifiers() { return new_nullifiers; } - - void push_new_note(NoteInterface* const note_ptr) { new_notes.push_back(note_ptr); } - - void push_newly_nullified_note(NoteInterface* note_ptr) { nullified_notes.push_back(note_ptr); } - - PrivateCircuitPublicInputs get_final_private_circuit_public_inputs() - { - // For safety, only return this if the circuit is complete. - if (!is_finalized) { - throw_or_abort("You need to call exec_ctx.finalize() in your circuit first."); - } - return final_private_circuit_public_inputs; - } - - /** - * @brief Get the call_stack_item representing `this` exec_ctx's function call. - */ - CallStackItem get_call_stack_item() - { - const NT::address& actual_contract_address = oracle.native_oracle.get_actual_contract_address(); - const FunctionData& function_data = oracle.native_oracle.get_function_data(); - - return CallStackItem{ - .contract_address = actual_contract_address, - .function_data = function_data, - .public_inputs = get_final_private_circuit_public_inputs(), - }; - } - - /** - * @brief Get the call_stack_items of any nested function calls made by this exec_ctx's function. - */ - std::array, MAX_PRIVATE_CALL_STACK_LENGTH_PER_CALL> get_private_call_stack_items() - { - std::array, MAX_PRIVATE_CALL_STACK_LENGTH_PER_CALL> result; - - for (size_t i = 0; i < result.size(); ++i) { - auto& nested_exec_ctx = nested_private_call_exec_ctxs[i]; - if (nested_exec_ctx != nullptr) { - const NT::address& actual_contract_address = - nested_exec_ctx->oracle.native_oracle.get_actual_contract_address(); - const FunctionData& function_data = nested_exec_ctx->oracle.native_oracle.get_function_data(); - - result[i] = CallStackItem{ - .contract_address = actual_contract_address, - .function_data = function_data, - .public_inputs = nested_exec_ctx->get_final_private_circuit_public_inputs(), - }; - } - } - - // TODO: do we need to instantiate-with-zeros the structs at the unused indices of `result`? - - return result; - } - - /** - * @brief Allows a call to be made to a function of another contract - * - * TODO: maybe we want to move some of the code that's in this function into a method in the Opcodes class. Although - * that class was really shoehorned into existence, and is a bit bleurgh. - */ - std::array call( - address const& f_contract_address, - std::string const& f_name, - std::function&, std::vector)> f, - std::vector const& args) - { - // Convert function name to bytes and use the first 4 bytes as the function encoding, for now: - std::vector f_name_bytes(f_name.begin(), f_name.end()); - std::vector f_encoding_bytes(f_name_bytes.begin(), f_name_bytes.begin() + 4); - uint32_t f_encoding = 0; - memcpy(&f_encoding, f_encoding_bytes.data(), sizeof(f_encoding)); - - fr f_encoding_ct = fr(f_encoding); - // Important Note: we MUST constrain this function_selector value against a fixed selector value. Without the - // below line, an attacker could pass any f_encoding as a witness. - f_encoding_ct.convert_constant_to_fixed_witness(&builder); - - /// @dev The above constraining could alternatively be achieved as follows: - // fr alternative_f_encoding_ct = fr(to_ct(builder, f_encoding)); - // alternative_f_encoding_ct.fix_witness(); - - const FunctionData f_function_data_ct{ - // Note: we MUST - .selector = - { - .value = f_encoding_ct, - }, - .is_private = true, - .is_constructor = false, - }; - - const CallContext f_call_context_ct{ - .msg_sender = oracle.get_this_contract_address(), // the sender is `this` contract! - .storage_contract_address = f_contract_address, - .portal_contract_address = 0, // TODO - .function_selector = f_function_data_ct.selector, - .is_delegate_call = false, - .is_static_call = false, - .is_contract_deployment = false, - }; - - NativeOracle f_oracle(oracle.native_oracle.db, - f_contract_address.get_value(), - f_function_data_ct.template to_native_type(), - f_call_context_ct.template to_native_type(), - oracle.get_msg_sender_private_key() - .get_value() // TODO: consider whether a nested function should even be able to - // access a private key, given that the call is now coming from a - // contract (which cannot own a secret), rather than a human. - ); - - Builder f_builder = Builder(); - - OracleWrapperInterface f_oracle_wrapper(f_builder, f_oracle); - - // We need an exec_ctx reference which won't go out of scope, so we store a shared_ptr to the newly-created - // exec_ctx in `this` exec_ctx. - auto f_exec_ctx = std::make_shared>(f_builder, f_oracle_wrapper); - - array_push(nested_private_call_exec_ctxs, f_exec_ctx); - - auto native_args = to_nt(args); - - // This calls the function `f`, passing the arguments shown. - // The f_exec_ctx will be populated with all the information about that function's execution. - std::apply(f, std::forward_as_tuple(*f_exec_ctx, native_args)); - - // Remember: the data held in the f_exec_ctc was built with a different builder than that - // of `this` exec_ctx. So we only allow ourselves to get the native types, so that we can consciously declare - // circuit types for `this` exec_ctx using `this->builder`. - auto f_public_inputs_nt = f_exec_ctx->get_final_private_circuit_public_inputs(); - - // Since we've made a call to another function, we now need to push a call_stack_item_hash to `this` function's - // private call stack. - // Note: we need to constrain some of `this` circuit's variables against f's public inputs: - // - args - // - return_values - // - call_context (TODO: maybe this only needs to be done in the kernel circuit). - auto f_public_inputs_ct = f_public_inputs_nt.to_circuit_type(builder); - - // Constrain that the arguments of the executed function match those we expect: - auto args_hash_ct = compute_var_args_hash(args); - args_hash_ct.assert_equal(f_public_inputs_ct.args_hash); - - CallStackItem const f_call_stack_item_ct{ - .contract_address = f_contract_address, - .function_data = f_function_data_ct, - .public_inputs = f_public_inputs_ct, - }; - - auto call_stack_item_hash = f_call_stack_item_ct.hash(); - - array_push(private_circuit_public_inputs.private_call_stack, call_stack_item_hash); - - // The return values are implicitly constrained by being returned as circuit types from this method, for - // further use in the circuit. Note: ALL elements of the return_values array MUST be constrained, even if - // they're placeholder zeroes. - return f_public_inputs_ct.return_values; - } - - /** - * @brief This is an important optimization, to save on the number of emitted nullifiers. - * - * A nullifier is ideal to serve as a nonce for a new note commitment, because its uniqueness is enforced by the - * Rollup circuit. But we won't know how many non-dummy nullifiers we have at our disposal (to inject into - * commitments) until the end of the function. - * - * Or to put it another way, at the time we want to create a new commitment (during a function's execution), we - * would need a nonce. We could certainly query the `exec_ctx` for any nullifiers which have already been created - * earlier in this function's execution, and we could use one of those. But there might not-yet have been any - * nullifiers created within the function. Now, at that point, we _could_ generate a dummy nullifier and use that as - * a nonce. But that uses up a precious slot in the circuit's nullifiers array (part of the circuit's public inputs - * abi). And it might be the case that later in the function, a load of non-dummy nullifiers get created. So as an - * optimization, it would be better if we could use _those_ nullifiers, so as to minimize dummy values in the - * circuit's public inputs. - * - * And so, we provide the option here of deferring the injection of nonces into note_preimages (and hence deferring - * the computation of each new note commitment) until the very end of the function's execution, when we know how - * many non-dummy nullifiers we have to play with. If we find this circuit is creating more new commitments than new - * nullifiers, we can generate some dummy nullifiers at this stage to make up the difference. - * - * Note: Using a nullifier as a nonce is a very common and widely-applicable pattern. So much so that it feels - * acceptable to have this function execute regardless of the underlying Note types being used by the circuit. - * - * Note: It's up to the implementer of a custom Note type to decide how a nonce is derived, via the `set_nonce() - * override` method dictated by the NoteInterface. - * - * Note: Not all custom Note types will need a nonce of this kind in their NotePreimage. But they can simply - * implement an empty body in the `set_nonce() override`. - * - * TODO: Might need some refactoring. Roles between: Opcodes modifying exec_ctx members; and the exec_ctx directly - * modifying its members, are somewhat blurred at the moment. - */ - void finalize_utxos() - { - // Copy some vectors, as we can't control whether they'll be pushed-to further, when we call Note methods. - auto new_nullifiers_copy = new_nullifiers; - - size_t used_nullifiers_count = 0; - fr next_nullifier; - std::vector new_nonces; - - // This is almost a visitor pattern. Call methods on each note. The note will choose what to do. - for (size_t i = 0; i < new_notes.size(); ++i) { - NoteInterface& note = *new_notes[i]; - - if (note.needs_nonce()) { - const bool next_nullifier_available = new_nullifiers_copy.size() > used_nullifiers_count; - - if (next_nullifier_available) { - next_nullifier = new_nullifiers_copy[used_nullifiers_count++]; - note.set_nonce(next_nullifier); - } else { - const fr new_nonce = note.generate_nonce(); - new_nonces.push_back(new_nonce); - } - } - - new_commitments.push_back(note.get_commitment()); - } - - // Push new_nonces to the end of new_nullifiers: - std::copy(new_nonces.begin(), new_nonces.end(), std::back_inserter(new_nullifiers)); - } - - void finalize() - { - finalize_utxos(); - private_circuit_public_inputs.set_commitments(new_commitments); - private_circuit_public_inputs.set_nullifiers(new_nullifiers); - private_circuit_public_inputs.set_nullified_commitments(nullified_commitments); - private_circuit_public_inputs.set_public(builder); - final_private_circuit_public_inputs = - private_circuit_public_inputs.remove_optionality().template to_native_type(); - is_finalized = true; - } -}; - -} // namespace aztec3::circuits::apps diff --git a/circuits/cpp/src/aztec3/circuits/apps/notes/default_private_note/note.hpp b/circuits/cpp/src/aztec3/circuits/apps/notes/default_private_note/note.hpp deleted file mode 100644 index 6c2837c5447..00000000000 --- a/circuits/cpp/src/aztec3/circuits/apps/notes/default_private_note/note.hpp +++ /dev/null @@ -1,130 +0,0 @@ -#pragma once - -#include "note_preimage.hpp" -#include "nullifier_preimage.hpp" -#include "../note_interface.hpp" - -#include "aztec3/utils/types/circuit_types.hpp" -#include "aztec3/utils/types/native_types.hpp" - -// Forward-declare from this namespace in particular: -namespace aztec3::circuits::apps::state_vars { -template class StateVar; -} // namespace aztec3::circuits::apps::state_vars - -namespace aztec3::circuits::apps::notes { - -using aztec3::circuits::apps::state_vars::StateVar; // Don't #include it! - -using aztec3::utils::types::CircuitTypes; -using aztec3::utils::types::NativeTypes; - -template class DefaultPrivateNote : public NoteInterface { - public: - using CT = CircuitTypes; - using fr = typename CT::fr; - using grumpkin_point = typename CT::grumpkin_point; - using address = typename CT::address; - using boolean = typename CT::boolean; - - using NotePreimage = DefaultPrivateNotePreimage, ValueType>; - using NullifierPreimage = DefaultPrivateNoteNullifierPreimage>; - - - StateVar* state_var; - - private: - std::optional commitment; - std::optional nullifier; - std::optional partial_commitment; - - NotePreimage note_preimage; - std::optional nullifier_preimage; - - public: - DefaultPrivateNote(StateVar* state_var, NotePreimage note_preimage) - : state_var(state_var), note_preimage(note_preimage){}; - - ~DefaultPrivateNote() override = default; - - // OVERRIDE METHODS: - - void remove() override; - - fr get_commitment() override - { - if (commitment) { - return *commitment; - } - return compute_commitment(); - }; - - fr get_nullifier() override - { - if (nullifier) { - return *nullifier; - } - return compute_nullifier(); - }; - - // grumpkin_point get_partial_commitment() override const - // { - // if (!partial_commitment) { - // throw_or_abort( - // "No partial_commitment exists for this note. Are you sure you haven't accidentally created a " - // "complete commitment?"); - // } - // return *partial_commitment; - // }; - - void constrain_against_advice(NoteInterface const& advice_note) override; - - bool needs_nonce() override; - - void set_nonce(fr const& nonce) override; - - fr generate_nonce() override; - - fr get_initialization_nullifier() override - { - throw_or_abort( - "DefaultPrivateNote does not support initialization. Maybe use DefaultSingletonPrivateNote instead?"); - }; - - fr get_initialization_commitment() override - { - throw_or_abort( - "DefaultPrivateNote does not support initialization. Maybe use DefaultSingletonPrivateNote instead?"); - }; - - // CUSTOM METHODS - - auto& get_oracle(); - - grumpkin_point compute_partial_commitment(); - - fr compute_dummy_nullifier(); - - static fr compute_nullifier(fr const& commitment, - fr const& owner_private_key, - boolean const& is_dummy_commitment = false); - - NotePreimage& get_preimage() { return note_preimage; }; - - private: - fr compute_commitment(); - fr compute_nullifier(); - - bool is_partial_preimage() const; - bool is_partial_storage_slot() const; - bool is_partial() const; -}; - -} // namespace aztec3::circuits::apps::notes - -// Importing in this way (rather than explicit instantiation of a template class at the bottom of a .cpp file) preserves -// the following: -// - We retain implicit instantiation of templates. -// - We don't implement method definitions in this file, to avoid a circular dependency with the state_var files (which -// are forward-declared in this file). -#include "note.tpp" \ No newline at end of file diff --git a/circuits/cpp/src/aztec3/circuits/apps/notes/default_private_note/note.tpp b/circuits/cpp/src/aztec3/circuits/apps/notes/default_private_note/note.tpp deleted file mode 100644 index 67a71a529bf..00000000000 --- a/circuits/cpp/src/aztec3/circuits/apps/notes/default_private_note/note.tpp +++ /dev/null @@ -1,195 +0,0 @@ -#include "note_preimage.hpp" -#include "nullifier_preimage.hpp" -#include "../../opcodes/opcodes.hpp" -#include "../../oracle_wrapper.hpp" -#include "../../state_vars/state_var_base.hpp" -#include "../note_interface.hpp" - -#include "aztec3/utils/types/circuit_types.hpp" -#include "aztec3/utils/types/convert.hpp" -#include "aztec3/utils/types/native_types.hpp" - -#include - -namespace { -using aztec3::circuits::apps::opcodes::Opcodes; -using aztec3::circuits::apps::state_vars::StateVar; -} // namespace - -namespace aztec3::circuits::apps::notes { - -using aztec3::GeneratorIndex; -using aztec3::utils::types::CircuitTypes; -using aztec3::utils::types::NativeTypes; -using plonk::stdlib::witness_t; - -template void DefaultPrivateNote::remove() -{ - Opcodes::UTXO_NULL(state_var, *this); -} - -template auto& DefaultPrivateNote::get_oracle() -{ - return state_var->exec_ctx->oracle; -} - -template bool DefaultPrivateNote::is_partial_preimage() const -{ - const auto& [value, owner, creator_address, memo, salt, nonce, _] = note_preimage; - - return (!value || !owner || !creator_address || !memo || !salt || !nonce); -} - -template bool DefaultPrivateNote::is_partial_storage_slot() const -{ - return state_var->is_partial_slot; -} - -template bool DefaultPrivateNote::is_partial() const -{ - return is_partial_preimage() || is_partial_storage_slot(); -} - -template -typename CircuitTypes::fr DefaultPrivateNote::compute_commitment() -{ - if (commitment.has_value()) { - return *commitment; - } - - grumpkin_point const storage_slot_point = state_var->storage_slot_point; - - if (!note_preimage.salt) { - note_preimage.salt = get_oracle().generate_random_element(); - } - - const auto& [value, owner, creator_address, memo, salt, nonce, is_dummy] = note_preimage; - - const grumpkin_point commitment_point = - storage_slot_point + CT::commit( - { - *value, /*PrivateStateNoteGeneratorIndex::VALUE*/ - (*owner).to_field(), /*PrivateStateNoteGeneratorIndex::OWNER*/ - (*creator_address).to_field(), /*PrivateStateNoteGeneratorIndex::CREATOR*/ - *memo, /*PrivateStateNoteGeneratorIndex::MEMO*/ - *salt, /*PrivateStateNoteGeneratorIndex::SALT*/ - *nonce, /*PrivateStateNoteGeneratorIndex::NONCE*/ - is_dummy, /*PrivateStateNoteGeneratorIndex::IS_DUMMY*/ - }, - GeneratorIndex::COMMITMENT); - - commitment = commitment_point.x; - - return *commitment; -} - -template -typename CircuitTypes::fr DefaultPrivateNote::compute_nullifier() -{ - if (is_partial()) { - throw_or_abort("Can't nullify a partial note."); - } - if (nullifier && nullifier_preimage) { - return *nullifier; - } - if (!commitment) { - compute_commitment(); - } - - fr const& owner_private_key = get_oracle().get_msg_sender_private_key(); - - nullifier = - DefaultPrivateNote::compute_nullifier(*commitment, owner_private_key, note_preimage.is_dummy); - nullifier_preimage = { - *commitment, - owner_private_key, - note_preimage.is_dummy, - }; - return *nullifier; -}; - -template -typename CircuitTypes::fr DefaultPrivateNote::compute_dummy_nullifier() -{ - auto& oracle = get_oracle(); - fr const dummy_commitment = oracle.generate_random_element(); - fr const& owner_private_key = oracle.get_msg_sender_private_key(); - const boolean is_dummy_commitment = true; - - return DefaultPrivateNote::compute_nullifier(dummy_commitment, owner_private_key, is_dummy_commitment); -}; - -template -typename CircuitTypes::fr DefaultPrivateNote::compute_nullifier(fr const& commitment, - fr const& owner_private_key, - boolean const& is_dummy_commitment) -{ - const std::vector hash_inputs{ - commitment, - owner_private_key, - is_dummy_commitment, - }; - - // We compress the hash_inputs with Pedersen, because that's cheaper (constraint-wise) than compressing - // the data directly with Blake2s in the next step. - const fr compressed_inputs = CT::hash(hash_inputs, GeneratorIndex::NULLIFIER); - - // Blake2s hash the compressed result. Without this it's possible to leak info from the pedersen - // compression. - /** E.g. we can extract a representation of the hashed_pk: - * Paraphrasing, if: - * nullifier = note_comm * G1 + hashed_pk * G2 + is_dummy_note * G3 - * Then an observer can derive hashed_pk * G2 = nullifier - note_comm * G1 - is_dummy_note * G3 - * They can derive this for every tx, to link which txs are being sent by the same user. - * Notably, at the point someone withdraws, the observer would be able to connect `hashed_pk * G2` with a - * specific eth address. - */ - auto blake_input = typename CT::byte_array(compressed_inputs); - auto blake_result = CT::blake2s(blake_input); - return fr(blake_result); -}; - -template -void DefaultPrivateNote::constrain_against_advice(NoteInterface const& advice_note) -{ - // Cast from a ref to the base (interface) type to a ref to this derived type: - const auto& advice_note_ref = dynamic_cast&>(advice_note); - - auto assert_equal = [](std::optional& this_member, std::optional const& advice_member) { - if (advice_member) { - (*this_member).assert_equal(*advice_member); - } - }; - - const auto& advice_preimage = advice_note_ref.note_preimage; - auto& this_preimage = note_preimage; - - assert_equal(this_preimage.value, advice_preimage.value); - assert_equal(this_preimage.owner, advice_preimage.owner); - assert_equal(this_preimage.creator_address, advice_preimage.creator_address); - assert_equal(this_preimage.memo, advice_preimage.memo); - assert_equal(this_preimage.salt, advice_preimage.salt); - assert_equal(this_preimage.nonce, advice_preimage.nonce); -} - -template bool DefaultPrivateNote::needs_nonce() -{ - return !note_preimage.nonce; -} - -template -void DefaultPrivateNote::set_nonce(typename CircuitTypes::fr const& nonce) -{ - ASSERT(!note_preimage.nonce); - note_preimage.nonce = nonce; -}; - -template -typename CircuitTypes::fr DefaultPrivateNote::generate_nonce() -{ - ASSERT(!note_preimage.nonce); - note_preimage.nonce = compute_dummy_nullifier(); - return *(note_preimage.nonce); -}; - -} // namespace aztec3::circuits::apps::notes \ No newline at end of file diff --git a/circuits/cpp/src/aztec3/circuits/apps/notes/default_private_note/note_preimage.hpp b/circuits/cpp/src/aztec3/circuits/apps/notes/default_private_note/note_preimage.hpp deleted file mode 100644 index e93eb00e0bb..00000000000 --- a/circuits/cpp/src/aztec3/circuits/apps/notes/default_private_note/note_preimage.hpp +++ /dev/null @@ -1,102 +0,0 @@ -#pragma once - -#include "aztec3/utils/types/circuit_types.hpp" -#include "aztec3/utils/types/convert.hpp" -#include "aztec3/utils/types/native_types.hpp" - -#include - -namespace aztec3::circuits::apps::notes { - -using aztec3::utils::types::CircuitTypes; -using aztec3::utils::types::NativeTypes; - -template struct DefaultPrivateNotePreimage { - using fr = typename NCT::fr; - using address = typename NCT::address; - using boolean = typename NCT::boolean; - - // No custom constructors so that designated initializers can be used (for readability of test circuits). - - std::optional value; - std::optional
owner; - std::optional
creator_address; - std::optional memo; // numerical representation of a string - - std::optional salt; - std::optional nonce; - - boolean is_dummy = false; - // For serialization, update with new fields - MSGPACK_FIELDS(value, owner, creator_address, memo, salt, nonce, is_dummy); - - bool operator==(DefaultPrivateNotePreimage const&) const = default; - - template auto to_circuit_type(Builder& builder) const - { - static_assert((std::is_same::value)); - - // Capture the circuit builder: - auto to_ct = [&](auto& e) { return aztec3::utils::types::to_ct(builder, e); }; - - // Depending on whether the _circuit_ type version of `V` is from the stdlib, or some custom type, the - // conversion method will be different. - const bool has_to_circuit_type = requires(V v) { v.to_circuit_type(); }; - const bool has_to_ct = requires(V v) { to_ct(v); }; - - // To avoid messy template arguments in the calling code, we use a lambda function with `auto` return type to - // avoid explicitly having to state the circuit type for `V`. - auto circuit_value = [&]() -> auto - { - if constexpr (has_to_circuit_type) { - return value.to_circuit_type(); - } else if (has_to_ct) { - return to_ct(value); - } else { - throw_or_abort("Can't convert Value to circuit type"); - } - } - (); - - // When this method is called, this class must be templated over native types. We can avoid templating over the - // circuit types (for the return values) (in order to make the calling syntax cleaner) with the below `decltype` - // deduction of the _circuit_ version of template type `V`. - DefaultPrivateNotePreimage, typename decltype(circuit_value)::value_type> preimage = { - circuit_value, to_ct(owner), to_ct(creator_address), to_ct(memo), - to_ct(salt), to_ct(nonce), to_ct(is_dummy), - }; - - return preimage; - }; - - template auto to_native_type() const - { - static_assert(!std::is_same::value); - - auto to_nt = [&](auto& e) { return aztec3::utils::types::to_nt(e); }; - - // See `to_circuit_type()` for explanation of this code. - const bool has_to_native_type = requires(V v) { v.to_native_type(); }; - const bool has_to_nt = requires(V v) { to_nt(v); }; - - auto native_value = [&]() -> auto - { - if constexpr (has_to_native_type) { - return value.to_native_type(); - } else if (has_to_nt) { - return to_nt(value); - } else { - throw_or_abort("Can't convert Value to native type"); - } - } - (); - - DefaultPrivateNotePreimage preimage = { - native_value, to_nt(owner), to_nt(creator_address), to_nt(memo), to_nt(salt), to_nt(nonce), to_nt(is_dummy), - }; - - return preimage; - }; -}; - -} // namespace aztec3::circuits::apps::notes diff --git a/circuits/cpp/src/aztec3/circuits/apps/notes/default_private_note/nullifier_preimage.hpp b/circuits/cpp/src/aztec3/circuits/apps/notes/default_private_note/nullifier_preimage.hpp deleted file mode 100644 index 9cf6fb9ac3b..00000000000 --- a/circuits/cpp/src/aztec3/circuits/apps/notes/default_private_note/nullifier_preimage.hpp +++ /dev/null @@ -1,44 +0,0 @@ -#pragma once - -#include "aztec3/utils/types/circuit_types.hpp" -#include "aztec3/utils/types/convert.hpp" -#include "aztec3/utils/types/native_types.hpp" - -#include - -namespace aztec3::circuits::apps::notes { - -using aztec3::utils::types::CircuitTypes; -using aztec3::utils::types::NativeTypes; - -template struct DefaultPrivateNoteNullifierPreimage { - using fr = typename NCT::fr; - using boolean = typename NCT::boolean; - - fr commitment; - fr owner_private_key; - boolean is_dummy = false; - // For serialization, update with new fields - MSGPACK_FIELDS(commitment, owner_private_key, is_dummy); - - bool operator==(DefaultPrivateNoteNullifierPreimage const&) const = default; - - template - DefaultPrivateNoteNullifierPreimage> to_circuit_type(Builder& builder) const - { - static_assert((std::is_same::value)); - - // Capture the circuit builder: - auto to_ct = [&](auto& e) { return aztec3::utils::types::to_ct(builder, e); }; - - DefaultPrivateNoteNullifierPreimage> preimage = { - to_ct(commitment), - to_ct(owner_private_key), - to_ct(is_dummy), - }; - - return preimage; - }; -}; - -} // namespace aztec3::circuits::apps::notes diff --git a/circuits/cpp/src/aztec3/circuits/apps/notes/default_singleton_private_note/note.hpp b/circuits/cpp/src/aztec3/circuits/apps/notes/default_singleton_private_note/note.hpp deleted file mode 100644 index df09571661c..00000000000 --- a/circuits/cpp/src/aztec3/circuits/apps/notes/default_singleton_private_note/note.hpp +++ /dev/null @@ -1,111 +0,0 @@ -#pragma once - -#include "note_preimage.hpp" -#include "nullifier_preimage.hpp" -#include "../note_interface.hpp" - -#include "aztec3/utils/types/circuit_types.hpp" -#include "aztec3/utils/types/native_types.hpp" - -// Forward-declare from this namespace in particular: -namespace aztec3::circuits::apps::state_vars { -template class StateVar; -} // namespace aztec3::circuits::apps::state_vars - -namespace aztec3::circuits::apps::notes { - -using aztec3::circuits::apps::state_vars::StateVar; // Don't #include it! - -using aztec3::utils::types::CircuitTypes; -using aztec3::utils::types::NativeTypes; - -template class DefaultSingletonPrivateNote : public NoteInterface { - public: - using CT = CircuitTypes; - using fr = typename CT::fr; - using grumpkin_point = typename CT::grumpkin_point; - using address = typename CT::address; - using boolean = typename CT::boolean; - - using NotePreimage = DefaultSingletonPrivateNotePreimage, ValueType>; - using NullifierPreimage = DefaultSingletonPrivateNoteNullifierPreimage>; - - - StateVar* state_var; - - private: - std::optional commitment; - std::optional nullifier; - - NotePreimage note_preimage; - std::optional nullifier_preimage; - - public: - DefaultSingletonPrivateNote(StateVar* state_var, NotePreimage note_preimage) - : state_var(state_var), note_preimage(note_preimage){}; - - ~DefaultSingletonPrivateNote() override = default; - - // OVERRIDE METHODS: - - void remove() override; - - fr get_commitment() override - { - if (commitment) { - return *commitment; - } - return compute_commitment(); - }; - - fr get_nullifier() override - { - if (nullifier) { - return *nullifier; - } - return compute_nullifier(); - }; - - void constrain_against_advice(NoteInterface const& advice_note) override; - - bool needs_nonce() override; - - void set_nonce(fr const& nonce) override; - - fr generate_nonce() override; - - fr get_initialization_nullifier() override; - - fr get_initialization_commitment() override; - - // CUSTOM METHODS - - auto& get_oracle(); - - grumpkin_point compute_partial_commitment(); - - fr compute_dummy_nullifier(); - - static fr compute_nullifier(fr const& commitment, - fr const& owner_private_key, - boolean const& is_dummy_commitment = false); - - NotePreimage& get_preimage() { return note_preimage; }; - - private: - fr compute_commitment(); - fr compute_nullifier(); - - bool is_partial_preimage() const; - bool is_partial_storage_slot() const; - bool is_partial() const; -}; - -} // namespace aztec3::circuits::apps::notes - -// Importing in this way (rather than explicit instantiation of a template class at the bottom of a .cpp file) preserves -// the following: -// - We retain implicit instantiation of templates. -// - We don't implement method definitions in this file, to avoid a circular dependency with the state_var files (which -// are forward-declared in this file). -#include "note.tpp" \ No newline at end of file diff --git a/circuits/cpp/src/aztec3/circuits/apps/notes/default_singleton_private_note/note.tpp b/circuits/cpp/src/aztec3/circuits/apps/notes/default_singleton_private_note/note.tpp deleted file mode 100644 index 1f9b44822bf..00000000000 --- a/circuits/cpp/src/aztec3/circuits/apps/notes/default_singleton_private_note/note.tpp +++ /dev/null @@ -1,251 +0,0 @@ -#include "note_preimage.hpp" -#include "nullifier_preimage.hpp" -#include "../../opcodes/opcodes.hpp" -#include "../../oracle_wrapper.hpp" -#include "../../state_vars/state_var_base.hpp" -#include "../note_interface.hpp" - -#include "aztec3/constants.hpp" -#include "aztec3/utils/types/circuit_types.hpp" -#include "aztec3/utils/types/convert.hpp" -#include "aztec3/utils/types/native_types.hpp" - -#include - -namespace { -using aztec3::circuits::apps::opcodes::Opcodes; -using aztec3::circuits::apps::state_vars::StateVar; -} // namespace - -namespace aztec3::circuits::apps::notes { - -using aztec3::GeneratorIndex; - -using aztec3::utils::types::CircuitTypes; -using aztec3::utils::types::NativeTypes; -using plonk::stdlib::witness_t; - -template void DefaultSingletonPrivateNote::remove() -{ - Opcodes::UTXO_NULL(state_var, *this); -} - -template auto& DefaultSingletonPrivateNote::get_oracle() -{ - return state_var->exec_ctx->oracle; -} - -template bool DefaultSingletonPrivateNote::is_partial_preimage() const -{ - const auto& [value, owner, salt, nonce] = note_preimage; - - return (!value || !owner || !salt || !nonce); -} - -template bool DefaultSingletonPrivateNote::is_partial_storage_slot() const -{ - return state_var->is_partial_slot; -} - -template bool DefaultSingletonPrivateNote::is_partial() const -{ - return is_partial_preimage() || is_partial_storage_slot(); -} - -template -typename CircuitTypes::fr DefaultSingletonPrivateNote::compute_commitment() -{ - if (commitment.has_value()) { - return *commitment; - } - - grumpkin_point const storage_slot_point = state_var->storage_slot_point; - - if (!note_preimage.salt) { - note_preimage.salt = get_oracle().generate_random_element(); - } - - const auto& [value, owner, salt, nonce] = note_preimage; - - const grumpkin_point commitment_point = - storage_slot_point + CT::commit( - { - *value, /*PrivateStateNoteGeneratorIndex::VALUE*/ - (*owner).to_field(), /*PrivateStateNoteGeneratorIndex::OWNER*/ - *salt, /*PrivateStateNoteGeneratorIndex::SALT*/ - *nonce, /*PrivateStateNoteGeneratorIndex::NONCE*/ - }, - GeneratorIndex::COMMITMENT); - - commitment = commitment_point.x; - - return *commitment; -} - -template -typename CircuitTypes::fr DefaultSingletonPrivateNote::compute_nullifier() -{ - if (is_partial()) { - throw_or_abort("Can't nullify a partial note."); - } - if (nullifier && nullifier_preimage) { - return *nullifier; - } - if (!commitment) { - compute_commitment(); - } - - fr const& owner_private_key = get_oracle().get_msg_sender_private_key(); - - nullifier = DefaultSingletonPrivateNote::compute_nullifier(*commitment, owner_private_key); - nullifier_preimage = { - *commitment, - owner_private_key, - }; - return *nullifier; -}; - -template -typename CircuitTypes::fr DefaultSingletonPrivateNote::compute_dummy_nullifier() -{ - auto& oracle = get_oracle(); - fr const dummy_commitment = oracle.generate_random_element(); - fr const& owner_private_key = oracle.get_msg_sender_private_key(); - const boolean is_dummy_commitment = true; - - return DefaultSingletonPrivateNote::compute_nullifier( - dummy_commitment, owner_private_key, is_dummy_commitment); -}; - -template -typename CircuitTypes::fr DefaultSingletonPrivateNote::compute_nullifier( - fr const& commitment, fr const& owner_private_key, boolean const& is_dummy_commitment) -{ - const std::vector hash_inputs{ - commitment, - owner_private_key, - is_dummy_commitment, - }; - - // We compress the hash_inputs with Pedersen, because that's cheaper (constraint-wise) than compressing - // the data directly with Blake2s in the next step. - const fr compressed_inputs = CT::hash(hash_inputs, GeneratorIndex::NULLIFIER); - - // Blake2s hash the compressed result. Without this it's possible to leak info from the pedersen - // compression. - /** E.g. we can extract a representation of the hashed_pk: - * Paraphrasing, if: - * nullifier = note_comm * G1 + hashed_pk * G2 + is_dummy_note * G3 - * Then an observer can derive hashed_pk * G2 = nullifier - note_comm * G1 - is_dummy_note * G3 - * They can derive this for every tx, to link which txs are being sent by the same user. - * Notably, at the point someone withdraws, the observer would be able to connect `hashed_pk * G2` with a - * specific eth address. - */ - auto blake_input = typename CT::byte_array(compressed_inputs); - auto blake_result = CT::blake2s(blake_input); - return fr(blake_result); -}; - -template -void DefaultSingletonPrivateNote::constrain_against_advice(NoteInterface const& advice_note) -{ - // Cast from a ref to the base (interface) type to a ref to this derived type: - const auto& advice_note_ref = dynamic_cast&>(advice_note); - - auto assert_equal = [](std::optional& this_member, std::optional const& advice_member) { - if (advice_member) { - (*this_member).assert_equal(*advice_member); - } - }; - - const auto& advice_preimage = advice_note_ref.note_preimage; - auto& this_preimage = note_preimage; - - assert_equal(this_preimage.value, advice_preimage.value); - assert_equal(this_preimage.owner, advice_preimage.owner); - assert_equal(this_preimage.salt, advice_preimage.salt); - assert_equal(this_preimage.nonce, advice_preimage.nonce); -} - -template bool DefaultSingletonPrivateNote::needs_nonce() -{ - return !note_preimage.nonce; -} - -template -void DefaultSingletonPrivateNote::set_nonce(typename CircuitTypes::fr const& nonce) -{ - ASSERT(!note_preimage.nonce); - note_preimage.nonce = nonce; -}; - -template -typename CircuitTypes::fr DefaultSingletonPrivateNote::generate_nonce() -{ - ASSERT(!note_preimage.nonce); - note_preimage.nonce = compute_dummy_nullifier(); - return *(note_preimage.nonce); -}; - -template -typename CircuitTypes::fr DefaultSingletonPrivateNote::get_initialization_nullifier() -{ - auto& oracle = get_oracle(); - - const fr& owner_private_key = oracle.get_msg_sender_private_key(); - - // We prevent this storage slot from even being initialized again: - auto& storage_slot_point = state_var->storage_slot_point; - - const std::vector hash_inputs{ - storage_slot_point.x, - storage_slot_point.y, - }; - - const bool is_dummy = false; - - // We compress the hash_inputs with Pedersen, because that's cheap. - const fr compressed_storage_slot_point = CT::hash(hash_inputs, GeneratorIndex::INITIALIZATION_NULLIFIER); - - // For now, we piggy-back on the regular nullifier function. - return DefaultSingletonPrivateNote::compute_nullifier( - compressed_storage_slot_point, owner_private_key, is_dummy); -}; - -template -typename CircuitTypes::fr DefaultSingletonPrivateNote::get_initialization_commitment() -{ - /** - * TODO: Get rid of this temporary fix of including owner_private_key while computing the initialization commitment. - * Details: We need to add the initialization commitment value to the `nullified_commitments`. - * In this case, since the actual note data is not yet available, we compute the initialization nullifier as: - * null = hash(compressed_storage_slot, owner_private_key, false) - * - * Thus, the initialization commitment here is `compressed_storage_slot`. But since the storage slot is not a real - * circuit variable, `compressed_storage_slot` would be a circuit constant. The compiler doesn't allow us - * to make a circuit constant as a public input of the circuit, it just crashes at runtime. - * To avoid this, we compute the initial commitment as: - * comm = hash(storage_slot_point.x, storage_slot_point.y, owner_private_key) - * - * This makes the initial commitment a "real" circuit variable. - */ - auto& oracle = get_oracle(); - - const fr& owner_private_key = oracle.get_msg_sender_private_key(); - - // We prevent this storage slot from even being initialized again: - auto& storage_slot_point = state_var->storage_slot_point; - - const std::vector hash_inputs{ - storage_slot_point.x, - storage_slot_point.y, - owner_private_key, - }; - - // We compress the hash_inputs with Pedersen, because that's cheap. - fr compressed_storage_slot_point = CT::hash(hash_inputs, GeneratorIndex::INITIALIZATION_NULLIFIER); - - return compressed_storage_slot_point; -}; - -} // namespace aztec3::circuits::apps::notes \ No newline at end of file diff --git a/circuits/cpp/src/aztec3/circuits/apps/notes/default_singleton_private_note/note_preimage.hpp b/circuits/cpp/src/aztec3/circuits/apps/notes/default_singleton_private_note/note_preimage.hpp deleted file mode 100644 index 852897c4ac8..00000000000 --- a/circuits/cpp/src/aztec3/circuits/apps/notes/default_singleton_private_note/note_preimage.hpp +++ /dev/null @@ -1,106 +0,0 @@ -#pragma once - -#include "aztec3/utils/types/circuit_types.hpp" -#include "aztec3/utils/types/convert.hpp" -#include "aztec3/utils/types/native_types.hpp" - -#include - -namespace aztec3::circuits::apps::notes { - -using aztec3::utils::types::CircuitTypes; -using aztec3::utils::types::NativeTypes; - -template struct DefaultSingletonPrivateNotePreimage { - using fr = typename NCT::fr; - using grumpkin_point = typename NCT::grumpkin_point; - using address = typename NCT::address; - using boolean = typename NCT::boolean; - - // No custom constructors so that designated initializers can be used (for readability of test circuits). - - std::optional value; - std::optional
owner; - - std::optional salt; - std::optional nonce; - - // For serialization, update with new fields - MSGPACK_FIELDS(value, owner, salt, nonce); - - bool operator==(DefaultSingletonPrivateNotePreimage const&) const = default; - - template auto to_circuit_type(Builder& builder) const - { - static_assert((std::is_same::value)); - - // Capture the circuit builder: - auto to_ct = [&](auto& e) { return aztec3::utils::types::to_ct(builder, e); }; - - // Depending on whether the _circuit_ type version of `V` is from the stdlib, or some custom type, the - // conversion method will be different. - const bool has_to_circuit_type = requires(V v) { v.to_circuit_type(); }; - const bool has_to_ct = requires(V v) { to_ct(v); }; - - // To avoid messy template arguments in the calling code, we use a lambda function with `auto` return type to - // avoid explicitly having to state the circuit type for `V`. - auto circuit_value = [&]() -> auto - { - if constexpr (has_to_circuit_type) { - return value.to_circuit_type(); - } else if (has_to_ct) { - return to_ct(value); - } else { - throw_or_abort("Can't convert Value to circuit type"); - } - } - (); - - // When this method is called, this class must be templated over native types. We can avoid templating over the - // circuit types (for the return values) (in order to make the calling syntax cleaner) with the below `decltype` - // deduction of the _circuit_ version of template type `V`. - DefaultSingletonPrivateNotePreimage, typename decltype(circuit_value)::value_type> - preimage = { - circuit_value, - to_ct(owner), - to_ct(salt), - to_ct(nonce), - }; - - return preimage; - }; - - template auto to_native_type() const - { - static_assert(!std::is_same::value); - - auto to_nt = [&](auto& e) { return aztec3::utils::types::to_nt(e); }; - - // See `to_circuit_type()` for explanation of this code. - const bool has_to_native_type = requires(V v) { v.to_native_type(); }; - const bool has_to_nt = requires(V v) { to_nt(v); }; - - auto native_value = [&]() -> auto - { - if constexpr (has_to_native_type) { - return value.to_native_type(); - } else if (has_to_nt) { - return to_nt(value); - } else { - throw_or_abort("Can't convert Value to native type"); - } - } - (); - - DefaultSingletonPrivateNotePreimage preimage = { - native_value, - to_nt(owner), - to_nt(salt), - to_nt(nonce), - }; - - return preimage; - }; -}; - -} // namespace aztec3::circuits::apps::notes diff --git a/circuits/cpp/src/aztec3/circuits/apps/notes/default_singleton_private_note/nullifier_preimage.hpp b/circuits/cpp/src/aztec3/circuits/apps/notes/default_singleton_private_note/nullifier_preimage.hpp deleted file mode 100644 index f73cd42560d..00000000000 --- a/circuits/cpp/src/aztec3/circuits/apps/notes/default_singleton_private_note/nullifier_preimage.hpp +++ /dev/null @@ -1,45 +0,0 @@ -#pragma once - -#include "aztec3/utils/types/circuit_types.hpp" -#include "aztec3/utils/types/convert.hpp" -#include "aztec3/utils/types/native_types.hpp" - -#include - -namespace aztec3::circuits::apps::notes { - -using aztec3::utils::types::CircuitTypes; -using aztec3::utils::types::NativeTypes; - -template struct DefaultSingletonPrivateNoteNullifierPreimage { - using fr = typename NCT::fr; - using boolean = typename NCT::boolean; - - fr commitment; - fr owner_private_key; - boolean is_dummy = false; - - // For serialization, update with new fields - MSGPACK_FIELDS(commitment, owner_private_key, is_dummy); - - bool operator==(DefaultSingletonPrivateNoteNullifierPreimage const&) const = default; - - template - DefaultSingletonPrivateNoteNullifierPreimage> to_circuit_type(Builder& builder) const - { - static_assert((std::is_same::value)); - - // Capture the circuit builder: - auto to_ct = [&](auto& e) { return aztec3::utils::types::to_ct(builder, e); }; - - DefaultSingletonPrivateNoteNullifierPreimage> preimage = { - to_ct(commitment), - to_ct(owner_private_key), - to_ct(is_dummy), - }; - - return preimage; - }; -}; - -} // namespace aztec3::circuits::apps::notes diff --git a/circuits/cpp/src/aztec3/circuits/apps/notes/note_interface.hpp b/circuits/cpp/src/aztec3/circuits/apps/notes/note_interface.hpp deleted file mode 100644 index f9bfb3c7e94..00000000000 --- a/circuits/cpp/src/aztec3/circuits/apps/notes/note_interface.hpp +++ /dev/null @@ -1,54 +0,0 @@ -#pragma once - -#include "aztec3/utils/types/circuit_types.hpp" - -namespace aztec3::circuits::apps::notes { - -using aztec3::utils::types::CircuitTypes; - -/** - * Note: The methods in this interface must be implemented by the derived Note types, even if such note types don't - * require such functions. - * - * It's essentially a visitor pattern. The Opcodes and UTXOStateVar types can call all of these - * methods on any Note, and the Note must choose whether to compute something, or whether to throw, for each method. - */ -template class NoteInterface { - public: - using CT = CircuitTypes; - using fr = typename CT::fr; - using grumpkin_point = typename CT::grumpkin_point; - using address = typename CT::address; - using boolean = typename CT::boolean; - - // Destructor explicitly made virtual, to ensure that the destructor of the derived class is called if the derived - // object is deleted through a pointer to this base class. (In many places in the code, files handle - // `NoteInterface*` pointers instead of the derived class). - virtual ~NoteInterface() = default; - - // TODO: maybe rather than have this be a pure interface, we should have a constructor and the `state_var*` and - // `note_preimage` members here (although that would require a NotePreimage template param). - // This is all because the Opcodes actually _assume_ a particular constructor layout for each Note, as well as - // _assume_ those two data members are always present. Having said that, there's still no way to actually enforce a - // constructor function data of a derived class. - - virtual void remove() = 0; - - virtual fr get_commitment() = 0; - - virtual fr get_nullifier() = 0; - - virtual fr get_initialization_nullifier() = 0; - - virtual fr get_initialization_commitment() = 0; - - virtual void constrain_against_advice(NoteInterface const& advice_note) = 0; - - virtual bool needs_nonce() = 0; - - virtual void set_nonce(fr const& nonce) = 0; - - virtual fr generate_nonce() = 0; -}; - -} // namespace aztec3::circuits::apps::notes \ No newline at end of file diff --git a/circuits/cpp/src/aztec3/circuits/apps/opcodes/opcodes.hpp b/circuits/cpp/src/aztec3/circuits/apps/opcodes/opcodes.hpp deleted file mode 100644 index ca2bef912fd..00000000000 --- a/circuits/cpp/src/aztec3/circuits/apps/opcodes/opcodes.hpp +++ /dev/null @@ -1,83 +0,0 @@ -#pragma once - -#include "aztec3/utils/types/circuit_types.hpp" -#include "aztec3/utils/types/native_types.hpp" - -#include - -namespace aztec3::circuits::apps::state_vars { -template class StateVar; -template class UTXOStateVar; -template class UTXOSetStateVar; -} // namespace aztec3::circuits::apps::state_vars - -namespace aztec3::circuits::apps::opcodes { - -using aztec3::circuits::apps::state_vars::StateVar; // Don't #include it! -using aztec3::circuits::apps::state_vars::UTXOSetStateVar; // Don't #include it! -using aztec3::circuits::apps::state_vars::UTXOStateVar; // Don't #include it! - -using aztec3::utils::types::CircuitTypes; -using aztec3::utils::types::NativeTypes; -using plonk::stdlib::witness_t; - -/** - * @brief - These static methods are a suggestion for what ACIR++ 'Opcodes' might do. They can get - * data from an oracle. They can apply constraints to that data. And they are the only class allowed to push data to the - * execution context. - * Separating out this functionality into a separate `Opcodes` class, like this, was trickier than - * just writing this stuff directly in the `Note` or `FunctionExecutionContext` classes, but hopefully the separation is - * sensible. - * - * TODO: Any oracle access or exec_ctx access should go through this class? - */ -template class Opcodes { - public: - using CT = CircuitTypes; - using address = typename CT::address; - - /** - * @brief - * - Load a singleton UTXOSLoadDatum from the Private Client's DB - * - Generate constraints to prove its existence in the tree - * - Validate the data - */ - template - static Note UTXO_SLOAD(UTXOStateVar* utxo_state_var, typename Note::NotePreimage const& advice); - - /** - * @brief - * - Load a subset of `UTXOSLoadDatum`s (which belong to a particular UTXOSetStateVar), from the Private Client's DB - * - Generate constraints to prove each datum's existence in the tree - * - Validate the data - */ - template static std::vector UTXO_SLOAD(UTXOSetStateVar* utxo_set_state_var, - size_t const& num_notes, - typename Note::NotePreimage const& advice); - - /** - * @brief Compute and push a new nullifier to the public inputs of this exec_ctx. - */ - template static void UTXO_NULL(StateVar* state_var, Note& note_to_nullify); - - /** - * @brief Compute and push a new commitment to the public inputs of this exec_ctx, BUT ALSO compute and produce an - * initialization nullifier, to prevent this note from being initialized again in the future. - */ - template static void UTXO_INIT(StateVar* state_var, Note& note_to_initialize); - - /** - * @brief Compute and push a new commitment to the public inputs of this exec_ctx. - */ - template - static void UTXO_SSTORE(StateVar* state_var, typename Note::NotePreimage new_note_preimage); -}; - -} // namespace aztec3::circuits::apps::opcodes - -// Importing in this way (rather than explicit instantiation of a template class at the bottom of a .cpp file) preserves -// the following: -// - We retain implicit instantiation of templates. -// - We don't implement method definitions in this file, to avoid a circular dependency with the state_var files (which -// are forward-declared in this file). -#include "opcodes.tpp" diff --git a/circuits/cpp/src/aztec3/circuits/apps/opcodes/opcodes.tpp b/circuits/cpp/src/aztec3/circuits/apps/opcodes/opcodes.tpp deleted file mode 100644 index 25c8c81a860..00000000000 --- a/circuits/cpp/src/aztec3/circuits/apps/opcodes/opcodes.tpp +++ /dev/null @@ -1,153 +0,0 @@ -#pragma once - -#include "../state_vars/state_var_base.hpp" -#include "../state_vars/utxo_set_state_var.hpp" -#include "../state_vars/utxo_state_var.hpp" -#include "../function_execution_context.hpp" -#include "../utxo_datum.hpp" - -#include "aztec3/utils/types/circuit_types.hpp" -#include "aztec3/utils/types/native_types.hpp" - -#include - -namespace { -// Declared here, so that `opcodes.hpp` doesn't see it; thereby preventing circular dependencies. -using aztec3::circuits::apps::state_vars::StateVar; -using aztec3::circuits::apps::state_vars::UTXOSetStateVar; -using aztec3::circuits::apps::state_vars::UTXOStateVar; -} // namespace - -namespace aztec3::circuits::apps::opcodes { - -using aztec3::circuits::apps::FunctionExecutionContext; - -using aztec3::utils::types::CircuitTypes; -using aztec3::utils::types::NativeTypes; -using plonk::stdlib::witness_t; - -template template -Note Opcodes::UTXO_SLOAD(UTXOStateVar* utxo_state_var, - typename Note::NotePreimage const& advice) -{ - auto& oracle = utxo_state_var->exec_ctx->oracle; - - typename CT::grumpkin_point const& storage_slot_point = utxo_state_var->storage_slot_point; - - // Retrieve UTXO witness datum from the DB: - UTXOSLoadDatum const utxo_datum = - oracle.template get_utxo_sload_datum(storage_slot_point, advice); - - Note new_note{ utxo_state_var, utxo_datum.preimage }; - Note advice_note{ utxo_state_var, advice }; - - new_note.constrain_against_advice(advice_note); - - // TODO: hard-code or calculate the correct commitment in the FakeDB stub, so that the returned data passes this - // check. - // Commenting-out this check for now, so the proof verifies. - // info("calculated commitment: ", new_note.get_commitment()); - // info("retrieved commitment: ", utxo_datum.commitment); - // new_note.get_commitment().assert_equal(utxo_datum.commitment, "UTXO_SLOAD: bad commitment"); - - oracle.get_contract_address().assert_equal(utxo_datum.contract_address, "UTXO_SLOAD: bad contract address"); - - // TODO within this function: - // - Merkle Membership Check using the contract_address, utxo_datum.{sibling_path, leaf_index, - // historical_note_hash_tree_root} - - return new_note; -}; - -template template -std::vector Opcodes::UTXO_SLOAD(UTXOSetStateVar* utxo_set_state_var, - size_t const& num_notes, - typename Note::NotePreimage const& advice) -{ - auto& oracle = utxo_set_state_var->exec_ctx->oracle; - - typename CT::grumpkin_point const& storage_slot_point = utxo_set_state_var->storage_slot_point; - - // Retrieve multiple UTXO witness datum's from the DB: - std::vector> utxo_data = - oracle.template get_utxo_sload_data(storage_slot_point, num_notes, advice); - - // Rely on the oracle to pad the data set with dummies, if there aren't enough notes in the DB. - ASSERT(utxo_data.size() == num_notes); - - std::vector new_notes; - - for (size_t i = 0; i < num_notes; i++) { - auto& utxo_datum = utxo_data[i]; - Note new_note{ utxo_set_state_var, utxo_datum.preimage }; - Note advice_note{ utxo_set_state_var, advice }; - - new_note.constrain_against_advice(advice_note); - - // TODO: hard-code or calculate the correct commitment in the FakeDB stub, so that the returned data passes this - // check. - // Commenting-out this check for now, so the proof verifies. - // info("calculated commitment: ", new_note.get_commitment()); - // info("retrieved commitment: ", utxo_datum.commitment); - // new_note.get_commitment().assert_equal(utxo_datum.commitment, "UTXO_SLOAD: bad commitment"); - - oracle.get_contract_address().assert_equal(utxo_datum.contract_address, "UTXO_SLOAD: bad contract address"); - - // TODO within this function: - // - Merkle Membership Check using the contract_address, utxo_datum.{sibling_path, leaf_index, - // historical_note_hash_tree_root} - - new_notes.push_back(new_note); - } - - return new_notes; -}; - -template template -void Opcodes::UTXO_NULL(StateVar* state_var, Note& note_to_nullify) -{ - typename CT::fr const nullifier = note_to_nullify.get_nullifier(); - typename CT::fr const nullified_note_commitment = note_to_nullify.get_commitment(); - - auto& exec_ctx = state_var->exec_ctx; - - exec_ctx->new_nullifiers.push_back(nullifier); - exec_ctx->nullified_commitments.push_back(nullified_note_commitment); - - std::shared_ptr const nullified_note_ptr = std::make_shared(note_to_nullify); - - exec_ctx->nullified_notes.push_back(nullified_note_ptr); -}; - -template template -void Opcodes::UTXO_INIT(StateVar* state_var, Note& note_to_initialize) -{ - typename CT::fr const init_nullifier = note_to_initialize.get_initialization_nullifier(); - typename CT::fr const init_commitment = note_to_initialize.get_initialization_commitment(); - - auto& exec_ctx = state_var->exec_ctx; - - exec_ctx->new_nullifiers.push_back(init_nullifier); - exec_ctx->nullified_commitments.push_back(init_commitment); - - std::shared_ptr const init_note_ptr = std::make_shared(note_to_initialize); - - // TODO: consider whether this should actually be pushed-to... - exec_ctx->nullified_notes.push_back(init_note_ptr); - - exec_ctx->new_notes.push_back(init_note_ptr); -}; - -template template -void Opcodes::UTXO_SSTORE(StateVar* state_var, typename Note::NotePreimage new_note_preimage) -{ - auto& exec_ctx = state_var->exec_ctx; - - // Make a shared pointer, so we don't end up with a dangling pointer in the exec_ctx when this `new_note` - // immediately goes out of scope. - std::shared_ptr const new_note_ptr = std::make_shared(state_var, new_note_preimage); - - exec_ctx->new_notes.push_back(new_note_ptr); -}; - -} // namespace aztec3::circuits::apps::opcodes diff --git a/circuits/cpp/src/aztec3/circuits/apps/oracle_wrapper.hpp b/circuits/cpp/src/aztec3/circuits/apps/oracle_wrapper.hpp deleted file mode 100644 index 5e30857ad2c..00000000000 --- a/circuits/cpp/src/aztec3/circuits/apps/oracle_wrapper.hpp +++ /dev/null @@ -1,133 +0,0 @@ -#pragma once - -#include "aztec3/circuits/abis/call_context.hpp" -#include "aztec3/circuits/abis/contract_deployment_data.hpp" -#include "aztec3/oracle/oracle.hpp" -#include "aztec3/utils/types/circuit_types.hpp" -#include "aztec3/utils/types/convert.hpp" -#include "aztec3/utils/types/native_types.hpp" - -#include - -namespace aztec3::circuits::apps { - -using NT = aztec3::utils::types::NativeTypes; -using aztec3::circuits::abis::CallContext; -using aztec3::circuits::abis::ContractDeploymentData; -using aztec3::oracle::NativeOracle; -using aztec3::utils::types::CircuitTypes; - -/** - * The main purpose of this wrapper is to: - * - cache values which have been already given by the oracle previously during this execution; - * - convert Native types (returned by the oracle) into circuit types, using the builder instance. - * Note: Insecure circuits could be built if the same value is queried twice from the oracle (since a malicious prover - * could provide two different witnesses for a single thing). The Native oracle will throw if you try a double-query of - * certain information. - */ -template class OracleWrapperInterface { - using CT = CircuitTypes; - using fr = typename CT::fr; - using grumpkin_point = typename CT::grumpkin_point; - using address = typename CT::address; - - public: - Builder& builder; - NativeOracle& native_oracle; - - // Initialize from Native. - // Used when initializing for a user's first call. - OracleWrapperInterface(Builder& builder, NativeOracle& native_oracle) - : builder(builder), native_oracle(native_oracle){}; - - fr& get_msg_sender_private_key() - { - if (msg_sender_private_key) { - return *msg_sender_private_key; - } - msg_sender_private_key = aztec3::utils::types::to_ct(builder, native_oracle.get_msg_sender_private_key()); - validate_msg_sender_private_key(); - return *msg_sender_private_key; - }; - - address get_contract_address() { return get_call_context().storage_contract_address; }; - - CallContext& get_call_context() - { - if (call_context) { - return *call_context; - } - call_context = native_oracle.get_call_context().to_circuit_type(builder); - return *call_context; - }; - - ContractDeploymentData& get_contract_deployment_data() - { - if (contract_deployment_data) { - return *contract_deployment_data; - } - contract_deployment_data = native_oracle.get_contract_deployment_data().to_circuit_type(builder); - return *contract_deployment_data; - }; - - address& get_msg_sender() { return get_call_context().msg_sender; }; - - address& get_this_contract_address() { return get_call_context().storage_contract_address; }; - - address& get_tx_origin() { return get_call_context().tx_origin; }; - - fr generate_salt() const { return aztec3::utils::types::to_ct(builder, native_oracle.generate_salt()); } - - fr generate_random_element() const - { - return aztec3::utils::types::to_ct(builder, native_oracle.generate_random_element()); - } - - template - auto get_utxo_sload_datum(grumpkin_point const& storage_slot_point, NotePreimage const& advice) - { - auto native_storage_slot_point = aztec3::utils::types::to_nt(storage_slot_point); - - auto native_advice = advice.template to_native_type(); - - auto native_utxo_sload_datum = native_oracle.get_utxo_sload_datum(native_storage_slot_point, native_advice); - - return native_utxo_sload_datum.to_circuit_type(builder); - } - - template auto get_utxo_sload_data(grumpkin_point const& storage_slot_point, - size_t const& num_notes, - NotePreimage const& advice) - { - auto native_storage_slot_point = aztec3::utils::types::to_nt(storage_slot_point); - - auto native_advice = advice.template to_native_type(); - - auto native_utxo_sload_data = - native_oracle.get_utxo_sload_data(native_storage_slot_point, num_notes, native_advice); - - auto to_circuit_type = [&](auto& e) { return e.to_circuit_type(builder); }; - - return map(native_utxo_sload_data, to_circuit_type); - } - - private: - std::optional> call_context; - std::optional> contract_deployment_data; - std::optional msg_sender_private_key; - - void validate_msg_sender_private_key() - { - const address msg_sender = get_msg_sender(); - const address derived_msg_sender = address::derive_from_private_key(*msg_sender_private_key); - msg_sender.assert_equal(derived_msg_sender, - format("msg_sender validation failed.\nmsg_sender_private_key: ", - msg_sender_private_key, - "\nPurported msg_sender: ", - msg_sender, - "\nDerived msg_sender: ", - derived_msg_sender)); - } -}; - -} // namespace aztec3::circuits::apps \ No newline at end of file diff --git a/circuits/cpp/src/aztec3/circuits/apps/private_state.test.cpp b/circuits/cpp/src/aztec3/circuits/apps/private_state.test.cpp deleted file mode 100644 index 4584d1d145e..00000000000 --- a/circuits/cpp/src/aztec3/circuits/apps/private_state.test.cpp +++ /dev/null @@ -1,119 +0,0 @@ -// #include "index.hpp" - -// // #include "aztec3/constants.hpp" -// -// #include -// #include - -// namespace aztec3::circuits::apps { - -// namespace { -// using Builder = aztec3::utils::types:plonk::stdlib::types::Builder; -// using CT = aztec3::utils::types::CircuitTypes; -// using NT = aztec3::utils::types::NativeTypes; -// // using plonk::stdlib::pedersen; -// } // namespace - -// class private_state_tests : public ::testing::Test {}; - -// // TEST(private_state_tests, test_native_private_state) -// // { -// // StateFactory state_factory("MyContract"); -// // PrivateStateVar x = state_factory.declare_private_state_var("x"); - -// // PrivateStateVar native_private_state = PrivateStateVar(x); - -// // auto buffer = to_buffer(native_private_state); - -// // auto native_private_state_2 = from_buffer>(buffer.data()); - -// // EXPECT_EQ(native_private_state, native_private_state_2); -// // } - -// // TEST(private_state_tests, test_create_private_state) -// // { -// // StateFactory state_factory("MyContract"); - -// // state_factory.declare_private_state_var("balances", { "asset_id", "owner" }); - -// // state_factory.declare_private_state_var("x"); - -// // // info("state_factory: ", state_factory); -// // } - -// // TEST(private_state_tests, test_native_private_state_note_preimage) -// // { -// // StateFactory state_factory("MyContract"); -// // PrivateStateVar x = state_factory.declare_private_state_var("x"); - -// // PrivateStateNotePreimage native_preimage = { -// // .value = 2, -// // .owner = 3, -// // .creator_address = NT::address(4), -// // .salt = 5, -// // .nonce = 6, -// // .memo = 7, -// // }; - -// // auto buffer = to_buffer(native_preimage); - -// // auto native_preimage_2 = from_buffer>(buffer.data()); - -// // EXPECT_EQ(native_preimage, native_preimage_2); -// // } - -// // TEST(private_state_tests, test_native_private_state_note_preimage_mapping) -// // { -// // StateFactory state_factory("MyContract"); -// // PrivateStateVar x = state_factory.declare_private_state_var("x", { "mapping_key_name_1", -// "mapping_key_name_2" -// // }); - -// // PrivateStateNotePreimage native_preimage = { -// // .mapping_key_values_by_key_name = std::map>({ { "mapping_key_name_2", 5 } -// }), -// // .value = 2, -// // .owner = 3, -// // .creator_address = NT::address(4), -// // .salt = 5, -// // .nonce = 6, -// // .memo = 7, -// // }; - -// // auto buffer = to_buffer(native_preimage); - -// // auto native_preimage_2 = from_buffer>(buffer.data()); - -// // EXPECT_EQ(native_preimage, native_preimage_2); -// // } - -// // TEST(private_state_tests, test_native_private_state_note_mapping) -// // { -// // StateFactory state_factory("MyContract"); -// // PrivateStateVar x = state_factory.declare_private_state_var("x", { "mapping_key_name_1", -// "mapping_key_name_2" -// // }); - -// // PrivateStateNotePreimage private_state_preimage = { -// // .mapping_key_values_by_key_name = std::map>({ { "mapping_key_name_2", 5 } -// }), -// // .value = 2, -// // .owner = 3, -// // .creator_address = NT::address(4), -// // .salt = 5, -// // .nonce = 6, -// // .memo = 7, -// // }; - -// // PrivateStateNote private_state_note = PrivateStateNote(x, private_state_preimage); - -// // auto buffer = to_buffer(private_state_note); - -// // auto private_state_note_2 = from_buffer>(buffer.data()); - -// // EXPECT_EQ(private_state_note, private_state_note_2); -// // } - -// /// TODO: figure out how to catch and test errors in gtest. - -// } // namespace aztec3::circuits::apps \ No newline at end of file diff --git a/circuits/cpp/src/aztec3/circuits/apps/state_vars/field_state_var.hpp b/circuits/cpp/src/aztec3/circuits/apps/state_vars/field_state_var.hpp deleted file mode 100644 index 6345f4cb8e5..00000000000 --- a/circuits/cpp/src/aztec3/circuits/apps/state_vars/field_state_var.hpp +++ /dev/null @@ -1,52 +0,0 @@ -#pragma once - -#include "state_var_base.hpp" - -#include "aztec3/utils/types/circuit_types.hpp" -#include "aztec3/utils/types/native_types.hpp" - -namespace aztec3::circuits::apps::state_vars { - -using aztec3::utils::types::CircuitTypes; -using aztec3::utils::types::NativeTypes; - -// TODO: we can probably generalize this to be a PrimitiveStateVar for any stdlib primitive. -template class FieldStateVar : public StateVar { - public: - using CT = CircuitTypes; - using NT = NativeTypes; - using fr = typename CT::fr; - using grumpkin_point = typename CT::grumpkin_point; - - fr value = 0; - - FieldStateVar& operator=(fr&& other) - { - value = other; - return *this; - } - - FieldStateVar() = default; - - // Instantiate a top-level var: - FieldStateVar(FunctionExecutionContext* exec_ctx, std::string const& state_var_name, fr const& start_slot) - : StateVar(exec_ctx, state_var_name, start_slot){}; - - // Instantiate a var nested within a container: - FieldStateVar(FunctionExecutionContext* exec_ctx, - std::string const& state_var_name, - grumpkin_point const& storage_slot_point, - size_t level_of_container_nesting, - bool is_partial_slot) - : StateVar( - exec_ctx, state_var_name, storage_slot_point, level_of_container_nesting, is_partial_slot){}; - - bool operator==(FieldStateVar const&) const = default; -}; - -template inline std::ostream& operator<<(std::ostream& os, FieldStateVar const& v) -{ - return os << v.value; -} - -} // namespace aztec3::circuits::apps::state_vars diff --git a/circuits/cpp/src/aztec3/circuits/apps/state_vars/mapping_state_var.hpp b/circuits/cpp/src/aztec3/circuits/apps/state_vars/mapping_state_var.hpp deleted file mode 100644 index 1add53261f8..00000000000 --- a/circuits/cpp/src/aztec3/circuits/apps/state_vars/mapping_state_var.hpp +++ /dev/null @@ -1,79 +0,0 @@ -#pragma once - -#include "state_var_base.hpp" - -#include "aztec3/utils/types/circuit_types.hpp" -#include "aztec3/utils/types/native_types.hpp" - -// Forward-declare from this namespace in particular: -namespace aztec3::circuits::apps { -template class FunctionExecutionContext; -} - -namespace aztec3::circuits::apps::state_vars { - -using aztec3::circuits::apps::FunctionExecutionContext; // Don't #include it! - -using aztec3::utils::types::CircuitTypes; -using aztec3::utils::types::NativeTypes; - -/** - * @tparam V - the value type being mapped-to by this mapping. - * - * Note: we restrict mapping _keys_ to always be a `field` type. This is to allow storage_slot_points to be computed - * more easily (it was difficult enough to get working). You'll notice, therefore, that there's no Key template type; - * only a value template type (`V`). Adding a Key template type could be a future enhancement. - */ -template class MappingStateVar : public StateVar { - public: - using CT = CircuitTypes; - using NT = NativeTypes; - using fr = typename CT::fr; - using grumpkin_point = typename CT::grumpkin_point; - - // native_storage_slot.x => value cache, to prevent creating constraints with each `at()` call. - std::map value_cache; - - MappingStateVar() = default; - - // Instantiate a top-level mapping: - MappingStateVar(FunctionExecutionContext* exec_ctx, std::string const& state_var_name) - : StateVar(exec_ctx, state_var_name){}; - - // Instantiate a nested mapping (within some other container). - // Note: we assume this is called by some other StateVar, and the params have been computed correctly. - // TODO: we could specify a set of `friend` classes which may access this method, to make this assumption more - // explicit. - MappingStateVar(FunctionExecutionContext* exec_ctx, - std::string const& state_var_name, - grumpkin_point const& storage_slot_point, - size_t level_of_container_nesting, - bool is_partial_slot) - : StateVar( - exec_ctx, state_var_name, storage_slot_point, level_of_container_nesting, is_partial_slot){}; - - bool operator==(MappingStateVar const&) const = default; - - V& operator[](std::optional const& key) { return this->at(key); }; - V& operator[](std::string const& question_mark) - { - ASSERT(question_mark == "?"); - return this->at(std::nullopt); - }; - - V& at(std::optional const& key); - - static std::tuple compute_slot_point_at_mapping_key(NT::fr const& start_slot, - std::optional const& key); - - std::tuple compute_slot_point_at_mapping_key(std::optional const& key); -}; - -} // namespace aztec3::circuits::apps::state_vars - -// Importing in this way (rather than explicit instantiation of a template class at the bottom of a .cpp file) preserves -// the following: -// - We retain implicit instantiation of templates. -// - We don't implement method definitions in this file, to avoid a circular dependency with -// function_execution_context.hpp. -#include "mapping_state_var.tpp" diff --git a/circuits/cpp/src/aztec3/circuits/apps/state_vars/mapping_state_var.tpp b/circuits/cpp/src/aztec3/circuits/apps/state_vars/mapping_state_var.tpp deleted file mode 100644 index 43b3c22a382..00000000000 --- a/circuits/cpp/src/aztec3/circuits/apps/state_vars/mapping_state_var.tpp +++ /dev/null @@ -1,129 +0,0 @@ -#pragma once - -// #include "oracle_wrapper.hpp" -// #include "private_state_note.hpp" -// #include "private_state_note_preimage.hpp" -// #include "private_state_operand.hpp" - -#include "../function_execution_context.hpp" - -#include "aztec3/utils/types/circuit_types.hpp" -#include "aztec3/utils/types/convert.hpp" -#include "aztec3/utils/types/native_types.hpp" - -#include - -namespace { -using aztec3::circuits::apps::FunctionExecutionContext; -} // namespace - -namespace aztec3::circuits::apps::state_vars { - - -using aztec3::utils::types::CircuitTypes; -using aztec3::utils::types::NativeTypes; - -// Fr: start_slot -// -// mapping(fr => V): -// level of nesting = 1 -// start_slot_point = start_slot * A -// at(k1).slot = start_slot_point + k1 * B -// -// mapping(fr => mapping(fr => T)): -// level_of_nesting = 2 -// start_slot_point becomes: prev_start_slot_point + k1 * B -// at(k2).slot = new_start_slot_point + k2 * C - -template -std::tuple MappingStateVar::compute_slot_point_at_mapping_key( - NT::fr const& start_slot, std::optional const& key) -{ - bool const is_partial_slot = false; - - std::vector inputs; - - // TODO: compare (in a test) this little calc against calling `compute_start_slot_point`. - inputs.emplace_back(start_slot); - - if (key) { - inputs.emplace_back(*key); - } else { - // If this mapping key has no mapping_key_value (std::nullopt), then we must be partially committing and - // omitting this mapping key from that partial commitment. - // So use a placeholder generator for this mapping key, to signify "this mapping key is missing". - // Note: we can't just commit to a value of `0` for this mapping key, since `0` is a valid value to - // commit to, and so "missing" is distinguished as follows. - inputs.emplace_back(NativeTypes::fr(1)); - } - - return std::make_tuple(NativeTypes::commit(inputs, StorageSlotGeneratorIndex::MAPPING_SLOT), is_partial_slot); -} - -template -std::tuple::grumpkin_point, bool> MappingStateVar:: - compute_slot_point_at_mapping_key(std::optional const& key) -{ - bool is_partial_slot = false; - - std::vector inputs; - - inputs.push_back(this->start_slot); - - if (key) { - inputs.push_back(*key); - } else { - // If this mapping key has no mapping_key_value (std::nullopt), then we must be partially committing and - // omitting this mapping key from that partial commitment. - // So use a placeholder generator for this mapping key, to signify "this mapping key is missing". - // Note: we can't just commit to a value of `0` for this mapping key, since `0` is a valid value to - // commit to, and so "missing" is distinguished as follows. - inputs.push_back(fr(1)); - is_partial_slot = true; - } - - return std::make_tuple(CT::commit(inputs, StorageSlotGeneratorIndex::MAPPING_SLOT), is_partial_slot); -} - -template V& MappingStateVar::at(std::optional const& key) -{ - // First calculate natively and check to see if we've already calculated this state's slot and stored it in the - // cache, so we don't create unnecessary circuit gates: - - std::optional native_key; - if (!key) { - native_key = std::nullopt; - } else { - native_key = static_cast((*key).get_value()); - } - - bool is_partial_slot = false; - NativeTypes::grumpkin_point native_new_slot_point; - std::tie(native_new_slot_point, is_partial_slot) = - MappingStateVar::compute_slot_point_at_mapping_key(this->start_slot.get_value(), native_key); - NativeTypes::fr const native_lookup = native_new_slot_point.x; - - // Check cache - if (this->value_cache.contains(native_lookup)) { - return this->value_cache[native_lookup]; - } - - // Create gates: - grumpkin_point new_slot_point; - std::tie(new_slot_point, is_partial_slot) = compute_slot_point_at_mapping_key(key); - NativeTypes::fr const lookup = new_slot_point.x.get_value(); - - if (lookup != native_lookup) { - throw_or_abort("Expected lookup calcs to be equal!"); - } - - std::string const value_name = this->state_var_name + (key ? format("[", *key, "]").c_str() : "[?]"); - - V value = V(this->exec_ctx, value_name, new_slot_point, this->level_of_container_nesting + 1, is_partial_slot); - - this->value_cache[lookup] = value; - - return this->value_cache[lookup]; -} - -}; // namespace aztec3::circuits::apps::state_vars diff --git a/circuits/cpp/src/aztec3/circuits/apps/state_vars/state_var_base.hpp b/circuits/cpp/src/aztec3/circuits/apps/state_vars/state_var_base.hpp deleted file mode 100644 index 0bead2a69c3..00000000000 --- a/circuits/cpp/src/aztec3/circuits/apps/state_vars/state_var_base.hpp +++ /dev/null @@ -1,98 +0,0 @@ -#pragma once - -#include "aztec3/utils/types/circuit_types.hpp" -#include "aztec3/utils/types/native_types.hpp" - -// Forward-declare from this namespace in particular: -namespace aztec3::circuits::apps { -template class FunctionExecutionContext; -} - -namespace aztec3::circuits::apps::state_vars { - -using aztec3::circuits::apps::FunctionExecutionContext; // Don't #include it! - -using aztec3::utils::types::CircuitTypes; -using aztec3::utils::types::NativeTypes; - -/** - * @brief StateVar is a base class from which contract state variables are derived. Its main purpose is deriving storage - * slots, and generating constraints for those slot derivations, in a protocol-consistent way, regardless of the app - * being written. - */ -template class StateVar { - public: - using CT = CircuitTypes; - using NT = NativeTypes; - using fr = typename CT::fr; - using grumpkin_point = typename CT::grumpkin_point; - - // The execution context of the function currently being executed. - FunctionExecutionContext* exec_ctx; - - // Must match the name of a state which has been declared to the `Contract`. - std::string state_var_name; - - // The `start slot` of the state variable is the slot which is assigned to this particular state by the `Contract`, - // based on the ordering of declarations of the _names_ of states. For container types (mappings/arrays/structs), - // the state variable might be able to access multiple storage slots. The start slot is the 'starting point' for - // deriving such slots. - fr start_slot = 0; - - // The 'storage slot point' of the state variable. Having a _point_ for every storage slot allows for - // partial-commitment functionality. - // I.e. we can generate placeholder storage slots, which can be partially-committed to in one function, and then - // completed in some future function, once the mapping keys or array indices at which we'd like to store the data - // are known in future. Aztec Connect does something similar (the `asset_id` of the output value note isn't known - // until later, so is partially committed-to). - grumpkin_point storage_slot_point; - - // In order to calculate the correct storage_slot_point, we need to know how many containers - // we're nested inside, so that we can find the correct Pedersen generator. - size_t level_of_container_nesting = 0; - - // Optionally informs custom notes whether they should commit or partially-commit to this state. - bool is_partial_slot = false; - - StateVar() = default; - - // Instantiate a top-level state: - StateVar(FunctionExecutionContext* exec_ctx, std::string const& state_var_name); - - // Instantiate a state nested within a container: - StateVar( - FunctionExecutionContext* exec_ctx, - std::string const& state_var_name, - grumpkin_point const& storage_slot_point, // the parent always calculates the storage_slot_point of its child. - size_t level_of_container_nesting, // the parent always calculates the level of nesting of its child. - bool is_partial_slot = false) - : exec_ctx(exec_ctx) - , state_var_name(state_var_name) - , storage_slot_point(storage_slot_point) - , level_of_container_nesting(level_of_container_nesting) - , is_partial_slot(is_partial_slot){}; - - bool operator==(StateVar const&) const = default; - - StateVar operator=(StateVar const& other) - { - this->exec_ctx = other.exec_ctx; - this->state_var_name = other.state_var_name; - this->start_slot = other.start_slot; - this->storage_slot_point = other.storage_slot_point; - this->level_of_container_nesting = other.level_of_container_nesting; - this->is_partial_slot = other.is_partial_slot; - return *this; - } - - private: - grumpkin_point compute_slot_point(); -}; - -} // namespace aztec3::circuits::apps::state_vars - -// Importing in this way (rather than explicit instantiation of a template class at the bottom of a .cpp file) preserves -// the following: -// - We retain implicit instantiation of templates. -// - We avoid circular dependencies with function_execution_context.hpp -#include "state_var_base.tpp" diff --git a/circuits/cpp/src/aztec3/circuits/apps/state_vars/state_var_base.tpp b/circuits/cpp/src/aztec3/circuits/apps/state_vars/state_var_base.tpp deleted file mode 100644 index f1448192827..00000000000 --- a/circuits/cpp/src/aztec3/circuits/apps/state_vars/state_var_base.tpp +++ /dev/null @@ -1,36 +0,0 @@ -#pragma once - -#include "../function_execution_context.hpp" - -#include "aztec3/utils/types/circuit_types.hpp" - -#include - -namespace { -using aztec3::circuits::apps::FunctionExecutionContext; -} - -namespace aztec3::circuits::apps::state_vars { - -using aztec3::utils::types::CircuitTypes; - -template -StateVar::StateVar(FunctionExecutionContext* exec_ctx, std::string const& state_var_name) - : exec_ctx(exec_ctx), state_var_name(state_var_name) -{ - // NOLINTBEGIN(cppcoreguidelines-prefer-member-initializer) - // this ^ linter rule breaks things here here - start_slot = exec_ctx->contract->get_start_slot(state_var_name); - storage_slot_point = compute_slot_point(); - // NOLINTEND(cppcoreguidelines-prefer-member-initializer) -} - -template typename CircuitTypes::grumpkin_point StateVar::compute_slot_point() -{ - ASSERT(level_of_container_nesting == 0); - return CT::commit({ start_slot }, { StorageSlotGeneratorIndex::BASE_SLOT }); -} - -// template class PrivateStateVar; - -}; // namespace aztec3::circuits::apps::state_vars diff --git a/circuits/cpp/src/aztec3/circuits/apps/state_vars/utxo_set_state_var.hpp b/circuits/cpp/src/aztec3/circuits/apps/state_vars/utxo_set_state_var.hpp deleted file mode 100644 index 69121dba555..00000000000 --- a/circuits/cpp/src/aztec3/circuits/apps/state_vars/utxo_set_state_var.hpp +++ /dev/null @@ -1,64 +0,0 @@ -#pragma once - -#include "state_var_base.hpp" - -#include "aztec3/utils/types/circuit_types.hpp" -#include "aztec3/utils/types/native_types.hpp" - -// Forward-declare from this namespace in particular: -namespace aztec3::circuits::apps { -template class FunctionExecutionContext; -} - -namespace aztec3::circuits::apps::state_vars { - -using aztec3::circuits::apps::FunctionExecutionContext; // Don't #include it! - -using aztec3::utils::types::CircuitTypes; -using aztec3::utils::types::NativeTypes; - -/** - * @brief - A derived StateVar which represents an unordered set of UTXOs which live in the UTXO tree. - * Notice the `get` and `insert` methods for this StateVar, which interact with the UTXO tree opcodes. - * - * @tparam Note - A UTXO state variable always acts on notes and note preimages. We allow for custom Note types to be - * designed. The Note type must implement the NoteInterface. TODO: maybe explicitly have this class act on the - * NoteInterface type, rather than a template type. - */ -template class UTXOSetStateVar : public StateVar { - public: - using CT = CircuitTypes; - using NT = NativeTypes; - using fr = typename CT::fr; - using grumpkin_point = typename CT::grumpkin_point; - - using NotePreimage = typename Note::NotePreimage; - - UTXOSetStateVar() = default; - - // Instantiate a top-level var: - UTXOSetStateVar(FunctionExecutionContext* exec_ctx, std::string const& state_var_name) - : StateVar(exec_ctx, state_var_name){}; - - // Instantiate a var nested within a container: - UTXOSetStateVar(FunctionExecutionContext* exec_ctx, - std::string const& state_var_name, - grumpkin_point const& storage_slot_point, - size_t level_of_container_nesting, - bool is_partial_slot) - : StateVar( - exec_ctx, state_var_name, storage_slot_point, level_of_container_nesting, is_partial_slot){}; - - /** - * @param advice - For NotePreimages, we allow 'advice' to be given, so that the correct DB entry is - * chosen. - * E.g. so that the `owner` can be specified. - */ - std::vector get(size_t const& num_notes, NotePreimage const& advice); - - void insert(NotePreimage new_note_preimage); -}; - -} // namespace aztec3::circuits::apps::state_vars - -#include "utxo_set_state_var.tpp" \ No newline at end of file diff --git a/circuits/cpp/src/aztec3/circuits/apps/state_vars/utxo_set_state_var.tpp b/circuits/cpp/src/aztec3/circuits/apps/state_vars/utxo_set_state_var.tpp deleted file mode 100644 index 4f289777f2e..00000000000 --- a/circuits/cpp/src/aztec3/circuits/apps/state_vars/utxo_set_state_var.tpp +++ /dev/null @@ -1,27 +0,0 @@ -#pragma once - -#include "../opcodes/opcodes.hpp" - -#include "aztec3/utils/types/circuit_types.hpp" -#include "aztec3/utils/types/native_types.hpp" - -namespace { -using aztec3::circuits::apps::opcodes::Opcodes; -} // namespace - -namespace aztec3::circuits::apps::state_vars { - -template -std::vector UTXOSetStateVar::get(size_t const& num_notes, - typename Note::NotePreimage const& advice) -{ - return Opcodes::template UTXO_SLOAD(this, num_notes, advice); -}; - -template -void UTXOSetStateVar::insert(typename Note::NotePreimage new_note_preimage) -{ - return Opcodes::template UTXO_SSTORE(this, new_note_preimage); -}; - -} // namespace aztec3::circuits::apps::state_vars \ No newline at end of file diff --git a/circuits/cpp/src/aztec3/circuits/apps/state_vars/utxo_state_var.hpp b/circuits/cpp/src/aztec3/circuits/apps/state_vars/utxo_state_var.hpp deleted file mode 100644 index 48f5bba3f46..00000000000 --- a/circuits/cpp/src/aztec3/circuits/apps/state_vars/utxo_state_var.hpp +++ /dev/null @@ -1,73 +0,0 @@ -#pragma once - -#include "state_var_base.hpp" - -#include "aztec3/utils/types/circuit_types.hpp" -#include "aztec3/utils/types/native_types.hpp" - -// Forward-declare from this namespace in particular: -namespace aztec3::circuits::apps { -template class FunctionExecutionContext; -} - -namespace aztec3::circuits::apps::state_vars { - -using aztec3::circuits::apps::FunctionExecutionContext; // Don't #include it! - -using aztec3::utils::types::CircuitTypes; -using aztec3::utils::types::NativeTypes; - -/** - * @brief - A derived StateVar which represents a singleton UTXO type. I.e. a state which can only ever have at-most ONE - * non-nullified UTXO in the tree at any time. Notice the `get` and `insert` methods for this StateVar, which interact - * with the UTXO tree opcodes. - * - * @tparam Note - A UTXO state variable always acts on notes and note preimages. We allow for custom Note types to be - * designed. The Note type must implement the NoteInterface. TODO: maybe explicitly have this class act on the - * NoteInterface type, rather than a template type. - */ -template class UTXOStateVar : public StateVar { - public: - using CT = CircuitTypes; - using NT = NativeTypes; - using fr = typename CT::fr; - using grumpkin_point = typename CT::grumpkin_point; - using address = typename CT::address; - - using NotePreimage = typename Note::NotePreimage; - - UTXOStateVar() = default; - - // Instantiate a top-level var: - UTXOStateVar(FunctionExecutionContext* exec_ctx, std::string const& state_var_name) - : StateVar(exec_ctx, state_var_name){}; - - // Instantiate a var nested within a container: - UTXOStateVar(FunctionExecutionContext* exec_ctx, - std::string const& state_var_name, - grumpkin_point const& storage_slot_point, - size_t level_of_container_nesting, - bool is_partial_slot) - : StateVar( - exec_ctx, state_var_name, storage_slot_point, level_of_container_nesting, is_partial_slot){}; - - /** - * @param advice - For NotePreimages, we allow 'advice' to be given, so that the correct DB entry is - * chosen. - * E.g. so that the `owner` can be specified. - */ - Note get(NotePreimage const& advice); - - /** - * @brief For singleton UTXOs, there's a distinction between initializing and modifying in future. See here: - * https://discourse.aztec.network/t/utxo-syntax-2-initializing-singleton-utxos/47. So we include this method for - * singleton UTXO types. - */ - void initialize(NotePreimage new_note_preimage); - - void insert(NotePreimage new_note_preimage); -}; - -} // namespace aztec3::circuits::apps::state_vars - -#include "utxo_state_var.tpp" \ No newline at end of file diff --git a/circuits/cpp/src/aztec3/circuits/apps/state_vars/utxo_state_var.tpp b/circuits/cpp/src/aztec3/circuits/apps/state_vars/utxo_state_var.tpp deleted file mode 100644 index 68842792702..00000000000 --- a/circuits/cpp/src/aztec3/circuits/apps/state_vars/utxo_state_var.tpp +++ /dev/null @@ -1,33 +0,0 @@ -#pragma once - -#include "../opcodes/opcodes.hpp" - -#include "aztec3/utils/types/circuit_types.hpp" -#include "aztec3/utils/types/native_types.hpp" - -namespace { -using aztec3::circuits::apps::opcodes::Opcodes; -} // namespace - -namespace aztec3::circuits::apps::state_vars { - -template -Note UTXOStateVar::get(typename Note::NotePreimage const& advice) -{ - return Opcodes::template UTXO_SLOAD(this, advice); -}; - -template -void UTXOStateVar::initialize(typename Note::NotePreimage new_note_preimage) -{ - Note new_note{ this, new_note_preimage }; - Opcodes::template UTXO_INIT(this, new_note); -}; - -template -void UTXOStateVar::insert(typename Note::NotePreimage new_note_preimage) -{ - Opcodes::template UTXO_SSTORE(this, new_note_preimage); -}; - -} // namespace aztec3::circuits::apps::state_vars \ No newline at end of file diff --git a/circuits/cpp/src/aztec3/circuits/apps/test_apps/basic_contract_deployment/basic_contract_deployment.cpp b/circuits/cpp/src/aztec3/circuits/apps/test_apps/basic_contract_deployment/basic_contract_deployment.cpp deleted file mode 100644 index 5a9e39fe27f..00000000000 --- a/circuits/cpp/src/aztec3/circuits/apps/test_apps/basic_contract_deployment/basic_contract_deployment.cpp +++ /dev/null @@ -1,53 +0,0 @@ -#include "basic_contract_deployment.hpp" - -#include "contract.hpp" - -#include "aztec3/circuits/abis/private_circuit_public_inputs.hpp" -#include "aztec3/circuits/hash.hpp" - -namespace aztec3::circuits::apps::test_apps::basic_contract_deployment { - -using aztec3::circuits::abis::OptionalPrivateCircuitPublicInputs; - -OptionalPrivateCircuitPublicInputs constructor(FunctionExecutionContext& exec_ctx, std::vector const& args) -{ - /**************************************************************** - * PREAMBLE - ****************************************************************/ - - // Make the exec_ctx aware of the contract's layout. - Contract contract = init_contract(); - exec_ctx.register_contract(&contract); - - // Convert params into circuit types: - auto& builder = exec_ctx.builder; - - CT::fr const arg0 = to_ct(builder, args[0]); - CT::fr const arg1 = to_ct(builder, args[1]); - CT::fr const arg2 = to_ct(builder, args[2]); - - auto& oracle = exec_ctx.oracle; - const CT::address msg_sender = oracle.get_msg_sender(); - - /**************************************************************** - * BODY - ****************************************************************/ - // SKIPPED - - /**************************************************************** - * CLEANUP - ****************************************************************/ - - // Push args to the public inputs. - // TODO: don't give function direct access to the exec_ctx? - auto& public_inputs = exec_ctx.private_circuit_public_inputs; - public_inputs.args_hash = compute_var_args_hash({ arg0, arg1, arg2 }); - - exec_ctx.finalize(); - - // info("public inputs: ", public_inputs); - - return public_inputs.to_native_type(); -} - -} // namespace aztec3::circuits::apps::test_apps::basic_contract_deployment diff --git a/circuits/cpp/src/aztec3/circuits/apps/test_apps/basic_contract_deployment/basic_contract_deployment.hpp b/circuits/cpp/src/aztec3/circuits/apps/test_apps/basic_contract_deployment/basic_contract_deployment.hpp deleted file mode 100644 index e478e4d997f..00000000000 --- a/circuits/cpp/src/aztec3/circuits/apps/test_apps/basic_contract_deployment/basic_contract_deployment.hpp +++ /dev/null @@ -1,14 +0,0 @@ -#pragma once - -#include "init.hpp" - -#include "aztec3/circuits/abis/private_circuit_public_inputs.hpp" -#include "aztec3/circuits/apps/function_execution_context.hpp" - -namespace aztec3::circuits::apps::test_apps::basic_contract_deployment { - -using aztec3::circuits::abis::OptionalPrivateCircuitPublicInputs; - -OptionalPrivateCircuitPublicInputs constructor(FunctionExecutionContext& exec_ctx, std::vector const& args); - -} // namespace aztec3::circuits::apps::test_apps::basic_contract_deployment \ No newline at end of file diff --git a/circuits/cpp/src/aztec3/circuits/apps/test_apps/basic_contract_deployment/contract.hpp b/circuits/cpp/src/aztec3/circuits/apps/test_apps/basic_contract_deployment/contract.hpp deleted file mode 100644 index 11e110731ea..00000000000 --- a/circuits/cpp/src/aztec3/circuits/apps/test_apps/basic_contract_deployment/contract.hpp +++ /dev/null @@ -1,23 +0,0 @@ -#pragma once - -#include "contract.hpp" -#include "init.hpp" - -#include "aztec3/circuits/apps/contract.hpp" -#include "aztec3/circuits/apps/function_declaration.hpp" - -namespace aztec3::circuits::apps::test_apps::basic_contract_deployment { - -inline Contract init_contract() -{ - Contract contract("BasicContractDeployment"); - - // Solely used for assigning vk indices. - contract.set_functions({ - { .name = "constructor", .is_private = true, .is_constructor = true }, - }); - - return contract; -} - -} // namespace aztec3::circuits::apps::test_apps::basic_contract_deployment \ No newline at end of file diff --git a/circuits/cpp/src/aztec3/circuits/apps/test_apps/basic_contract_deployment/init.hpp b/circuits/cpp/src/aztec3/circuits/apps/test_apps/basic_contract_deployment/init.hpp deleted file mode 100644 index 4b193f601d7..00000000000 --- a/circuits/cpp/src/aztec3/circuits/apps/test_apps/basic_contract_deployment/init.hpp +++ /dev/null @@ -1,43 +0,0 @@ -#pragma once - -#include "aztec3/circuits/apps/contract.hpp" -#include "aztec3/circuits/apps/function_execution_context.hpp" -#include "aztec3/circuits/apps/notes/default_private_note/note.hpp" -#include "aztec3/circuits/apps/oracle_wrapper.hpp" -#include "aztec3/circuits/apps/state_vars/mapping_state_var.hpp" -#include "aztec3/circuits/apps/state_vars/utxo_set_state_var.hpp" -#include "aztec3/oracle/oracle.hpp" -#include "aztec3/utils/types/circuit_types.hpp" -#include "aztec3/utils/types/convert.hpp" -#include "aztec3/utils/types/native_types.hpp" - -namespace aztec3::circuits::apps::test_apps::basic_contract_deployment { - -// Builder -using C = UltraCircuitBuilder; - -// Native and circuit types -using CT = aztec3::utils::types::CircuitTypes; -using NT = aztec3::utils::types::NativeTypes; - -// Database types -using DB = oracle::FakeDB; -using oracle::NativeOracle; -using OracleWrapper = apps::OracleWrapperInterface; - -using Contract = apps::Contract; -using FunctionExecutionContext = apps::FunctionExecutionContext; - -using aztec3::utils::types::to_ct; - -// StateVars -using apps::state_vars::MappingStateVar; -using apps::state_vars::UTXOSetStateVar; - -// Get rid of ugly `Builder` template arg from our state var types: -template using Mapping = MappingStateVar; -template using UTXOSet = UTXOSetStateVar; - -using DefaultNote = apps::notes::DefaultPrivateNote; - -} // namespace aztec3::circuits::apps::test_apps::basic_contract_deployment \ No newline at end of file diff --git a/circuits/cpp/src/aztec3/circuits/apps/test_apps/escrow/.test.cpp b/circuits/cpp/src/aztec3/circuits/apps/test_apps/escrow/.test.cpp deleted file mode 100644 index 0bae9fc9f21..00000000000 --- a/circuits/cpp/src/aztec3/circuits/apps/test_apps/escrow/.test.cpp +++ /dev/null @@ -1,117 +0,0 @@ -#include "index.hpp" - -#include - -#include - -namespace aztec3::circuits::apps::test_apps::escrow { - -class escrow_tests : public ::testing::Test { - protected: - static NativeOracle get_test_native_oracle(DB& db) - { - const NT::address contract_address = 12345; - const NT::fr msg_sender_private_key = 123456789; - const NT::address msg_sender = NT::fr( - uint256_t(0x01071e9a23e0f7edULL, 0x5d77b35d1830fa3eULL, 0xc6ba3660bb1f0c0bULL, 0x2ef9f7f09867fd6eULL)); - - FunctionData const function_data{ - .selector = - { - .value = 1, // TODO: deduce this from the contract, somehow. - }, - .is_private = true, - .is_constructor = false, - }; - - CallContext const call_context{ - .msg_sender = msg_sender, - .storage_contract_address = contract_address, - .portal_contract_address = 0, - .function_selector = function_data.selector, - .is_delegate_call = false, - .is_static_call = false, - .is_contract_deployment = false, - }; - - return NativeOracle(db, contract_address, function_data, call_context, msg_sender_private_key); - }; -}; - -TEST_F(escrow_tests, circuit_deposit) -{ - // TODO: currently, we can't hide all of this boilerplate in a test fixture function, because each of these classes - // contains a reference to earlier-declared classes... so we'd end up with classes containing dangling references, - // if all this stuff were to be declared in a setup function's scope. - // We could instead store shared_ptrs in every class...? - C builder = C(); - DB db; - NativeOracle native_oracle = get_test_native_oracle(db); - OracleWrapper oracle_wrapper = OracleWrapper(builder, native_oracle); - FunctionExecutionContext exec_ctx(builder, oracle_wrapper); - - auto amount = NT::fr(5); - auto asset_id = NT::fr(1); - auto memo = NT::fr(999); - - auto result = deposit(exec_ctx, { amount, asset_id, memo }); - info("result: ", result); - - // info("witness: ", builder.witness); - // info("constant variables: ", builder.constant_variables); - // info("variables: ", builder.variables); - info("failed?: ", builder.failed()); - info("err: ", builder.err()); - info("n: ", builder.num_gates); -} - -TEST_F(escrow_tests, circuit_transfer) -{ - C builder = C(); - DB db; - NativeOracle native_oracle = get_test_native_oracle(db); - OracleWrapper oracle_wrapper = OracleWrapper(builder, native_oracle); - FunctionExecutionContext exec_ctx(builder, oracle_wrapper); - - auto amount = NT::fr(5); - auto to = NT::address(657756); - auto asset_id = NT::fr(1); - auto memo = NT::fr(999); - auto reveal_msg_sender_to_recipient = true; - auto fee = NT::fr(2); - - transfer(exec_ctx, amount, to, asset_id, memo, reveal_msg_sender_to_recipient, fee); - - // info("witness: ", builder.witness); - // info("constant variables: ", builder.constant_variables); - // info("variables: ", builder.variables); - info("failed?: ", builder.failed()); - info("err: ", builder.err()); - info("n: ", builder.num_gates); -} - -TEST_F(escrow_tests, circuit_withdraw) -{ - C builder = C(); - DB db; - NativeOracle native_oracle = get_test_native_oracle(db); - OracleWrapper oracle_wrapper = OracleWrapper(builder, native_oracle); - FunctionExecutionContext exec_ctx(builder, oracle_wrapper); - - auto amount = NT::fr(5); - auto asset_id = NT::fr(1); - auto memo = NT::fr(999); - auto l1_withdrawal_address = NT::fr(657756); - auto fee = NT::fr(2); - - withdraw(exec_ctx, amount, asset_id, memo, l1_withdrawal_address, fee); - - // info("witness: ", builder.witness); - // info("constant variables: ", builder.constant_variables); - // info("variables: ", builder.variables); - info("failed?: ", builder.failed()); - info("err: ", builder.err()); - info("n: ", builder.num_gates); -} - -} // namespace aztec3::circuits::apps::test_apps::escrow \ No newline at end of file diff --git a/circuits/cpp/src/aztec3/circuits/apps/test_apps/escrow/README.md b/circuits/cpp/src/aztec3/circuits/apps/test_apps/escrow/README.md deleted file mode 100644 index 85bb977feed..00000000000 --- a/circuits/cpp/src/aztec3/circuits/apps/test_apps/escrow/README.md +++ /dev/null @@ -1,3 +0,0 @@ -# test_apps::escrow - -zk-money functionality in an Aztec3 setting. \ No newline at end of file diff --git a/circuits/cpp/src/aztec3/circuits/apps/test_apps/escrow/contract.hpp b/circuits/cpp/src/aztec3/circuits/apps/test_apps/escrow/contract.hpp deleted file mode 100644 index 2aa577eafbb..00000000000 --- a/circuits/cpp/src/aztec3/circuits/apps/test_apps/escrow/contract.hpp +++ /dev/null @@ -1,26 +0,0 @@ -#pragma once - -#include "init.hpp" - -#include "aztec3/circuits/apps/contract.hpp" -#include "aztec3/circuits/apps/function_declaration.hpp" - -namespace aztec3::circuits::apps::test_apps::escrow { - -inline Contract init_contract() -{ - Contract contract("Escrow"); - - contract.declare_state_var("balances"); - - // Solely used for assigning vk indices. - contract.set_functions({ - { .name = "deposit", .is_private = true }, - { .name = "transfer", .is_private = true }, - { .name = "withdraw", .is_private = true }, - }); - - return contract; -} - -} // namespace aztec3::circuits::apps::test_apps::escrow \ No newline at end of file diff --git a/circuits/cpp/src/aztec3/circuits/apps/test_apps/escrow/deposit.cpp b/circuits/cpp/src/aztec3/circuits/apps/test_apps/escrow/deposit.cpp deleted file mode 100644 index 61703be8a8c..00000000000 --- a/circuits/cpp/src/aztec3/circuits/apps/test_apps/escrow/deposit.cpp +++ /dev/null @@ -1,67 +0,0 @@ -#include "deposit.hpp" - -#include "contract.hpp" - -#include "aztec3/circuits/abis/private_circuit_public_inputs.hpp" -#include "aztec3/circuits/apps/function_execution_context.hpp" -#include "aztec3/circuits/hash.hpp" - -namespace aztec3::circuits::apps::test_apps::escrow { - -using aztec3::circuits::abis::OptionalPrivateCircuitPublicInputs; - -OptionalPrivateCircuitPublicInputs deposit(FunctionExecutionContext& exec_ctx, std::vector const& args) -{ - /**************************************************************** - * PREAMBLE - ****************************************************************/ - - // Make the exec_ctx aware of the contract's layout. - Contract contract = init_contract(); - exec_ctx.register_contract(&contract); - - // Convert params into circuit types: - auto& builder = exec_ctx.builder; - - CT::fr amount = to_ct(builder, args[0]); - CT::fr asset_id = to_ct(builder, args[1]); - CT::fr memo = to_ct(builder, args[2]); - - auto& oracle = exec_ctx.oracle; - const CT::address msg_sender = oracle.get_msg_sender(); - - /**************************************************************** - * BODY - ****************************************************************/ - - // Syntactic sugar for a state variable: - // Note: these Mappings always map-from a field type (because it was complicated enough!!!) - // mapping(asset_id => mapping(owner => UTXOSet< >)) balances; - Mapping>> balances(&exec_ctx, "balances"); - - balances[asset_id][msg_sender.to_field()].insert({ - .value = amount, - .owner = msg_sender, - .creator_address = msg_sender, - .memo = memo, - }); - - /**************************************************************** - * CLEANUP - ****************************************************************/ - - // Push args to the public inputs. - // TODO: don't give function direct access to the exec_ctx? - auto& public_inputs = exec_ctx.private_circuit_public_inputs; - public_inputs.args_hash = compute_var_args_hash({ amount, asset_id, memo }); - - exec_ctx.finalize(); - - // info("public inputs: ", public_inputs); - - return public_inputs.to_native_type(); - // TODO: also return note preimages and nullifier preimages. - // TODO: or, we'll be collecting this data in the exec_ctx. -}; - -} // namespace aztec3::circuits::apps::test_apps::escrow \ No newline at end of file diff --git a/circuits/cpp/src/aztec3/circuits/apps/test_apps/escrow/deposit.hpp b/circuits/cpp/src/aztec3/circuits/apps/test_apps/escrow/deposit.hpp deleted file mode 100644 index 326cef2c0dd..00000000000 --- a/circuits/cpp/src/aztec3/circuits/apps/test_apps/escrow/deposit.hpp +++ /dev/null @@ -1,14 +0,0 @@ -#pragma once - -#include "init.hpp" - -#include "aztec3/circuits/abis/private_circuit_public_inputs.hpp" -#include "aztec3/circuits/apps/function_execution_context.hpp" - -namespace aztec3::circuits::apps::test_apps::escrow { - -using aztec3::circuits::abis::OptionalPrivateCircuitPublicInputs; - -OptionalPrivateCircuitPublicInputs deposit(FunctionExecutionContext& exec_ctx, std::vector const& args); - -} // namespace aztec3::circuits::apps::test_apps::escrow \ No newline at end of file diff --git a/circuits/cpp/src/aztec3/circuits/apps/test_apps/escrow/index.hpp b/circuits/cpp/src/aztec3/circuits/apps/test_apps/escrow/index.hpp deleted file mode 100644 index ff3c61046c1..00000000000 --- a/circuits/cpp/src/aztec3/circuits/apps/test_apps/escrow/index.hpp +++ /dev/null @@ -1,5 +0,0 @@ -#include "contract.hpp" -#include "deposit.hpp" -#include "init.hpp" -#include "transfer.hpp" -#include "withdraw.hpp" \ No newline at end of file diff --git a/circuits/cpp/src/aztec3/circuits/apps/test_apps/escrow/init.hpp b/circuits/cpp/src/aztec3/circuits/apps/test_apps/escrow/init.hpp deleted file mode 100644 index 30d5da1dbbc..00000000000 --- a/circuits/cpp/src/aztec3/circuits/apps/test_apps/escrow/init.hpp +++ /dev/null @@ -1,43 +0,0 @@ -#pragma once - -#include "aztec3/circuits/apps/contract.hpp" -#include "aztec3/circuits/apps/function_execution_context.hpp" -#include "aztec3/circuits/apps/notes/default_private_note/note.hpp" -#include "aztec3/circuits/apps/oracle_wrapper.hpp" -#include "aztec3/circuits/apps/state_vars/mapping_state_var.hpp" -#include "aztec3/circuits/apps/state_vars/utxo_set_state_var.hpp" -#include "aztec3/oracle/oracle.hpp" -#include "aztec3/utils/types/circuit_types.hpp" -#include "aztec3/utils/types/convert.hpp" -#include "aztec3/utils/types/native_types.hpp" - -namespace aztec3::circuits::apps::test_apps::escrow { - -// Builder -using C = UltraCircuitBuilder; - -// Native and circuit types -using CT = aztec3::utils::types::CircuitTypes; -using NT = aztec3::utils::types::NativeTypes; - -// Database types -using DB = oracle::FakeDB; -using oracle::NativeOracle; -using OracleWrapper = apps::OracleWrapperInterface; - -using Contract = apps::Contract; -using FunctionExecutionContext = apps::FunctionExecutionContext; - -using aztec3::utils::types::to_ct; - -// StateVars -using apps::state_vars::MappingStateVar; -using apps::state_vars::UTXOSetStateVar; - -// Get rid of ugly `Builder` template arg from our state var types: -template using Mapping = MappingStateVar; -template using UTXOSet = UTXOSetStateVar; - -using DefaultNote = apps::notes::DefaultPrivateNote; - -} // namespace aztec3::circuits::apps::test_apps::escrow \ No newline at end of file diff --git a/circuits/cpp/src/aztec3/circuits/apps/test_apps/escrow/transfer.cpp b/circuits/cpp/src/aztec3/circuits/apps/test_apps/escrow/transfer.cpp deleted file mode 100644 index 08809a8e4e2..00000000000 --- a/circuits/cpp/src/aztec3/circuits/apps/test_apps/escrow/transfer.cpp +++ /dev/null @@ -1,105 +0,0 @@ -#include "transfer.hpp" - -#include "contract.hpp" - -#include "aztec3/circuits/abis/private_circuit_public_inputs.hpp" -#include "aztec3/circuits/hash.hpp" - -namespace aztec3::circuits::apps::test_apps::escrow { - -using aztec3::circuits::abis::OptionalPrivateCircuitPublicInputs; - -OptionalPrivateCircuitPublicInputs transfer(FunctionExecutionContext& exec_ctx, - NT::fr const& _amount, - NT::address const& _to, - NT::fr const& _asset_id, - NT::fr const& _memo, - NT::boolean const& _reveal_msg_sender_to_recipient, - NT::fr const& _fee) -{ - /**************************************************************** - * Initialization - ****************************************************************/ - - // Make the exec_ctx aware of the contract's layout. - Contract contract = init_contract(); - exec_ctx.register_contract(&contract); - - // Convert arguments into circuit types: - auto& builder = exec_ctx.builder; - - CT::fr amount = to_ct(builder, _amount); - CT::address to = to_ct(builder, _to); - CT::fr asset_id = to_ct(builder, _asset_id); - CT::fr memo = to_ct(builder, _memo); - CT::boolean const reveal_msg_sender_to_recipient = to_ct(builder, _reveal_msg_sender_to_recipient); - CT::fr const fee = to_ct(builder, _fee); - - /**************************************************************** - * Get States & Globals used by the function - ****************************************************************/ - - auto& oracle = exec_ctx.oracle; - CT::address msg_sender = oracle.get_msg_sender(); - - // Syntactic sugar for a state variable: - // Note: these Mappings always map-from a field type (because it was complicated enough!!!) - // mapping(asset_id => mapping(owner => UTXOSet< >)) balances; - Mapping>> balances(&exec_ctx, "balances"); - - /**************************************************************** - * BODY - ****************************************************************/ - - CT::address creator_address = - CT::address::conditional_assign(reveal_msg_sender_to_recipient, msg_sender, CT::address(0)); - - // TODO: sort & filter functions! - std::vector old_balance_notes = - balances[asset_id][msg_sender.to_field()].get(2, { .owner = msg_sender }); - - CT::fr const old_value_1 = *(old_balance_notes[0].get_preimage().value); - CT::fr const old_value_2 = *(old_balance_notes[1].get_preimage().value); - - // MISSING: overflow & underflow checks, but I can't be bothered with safe_uint or range checks yet. - CT::fr change = (old_value_1 + old_value_2) - (amount + fee); - - old_balance_notes[0].remove(); - old_balance_notes[1].remove(); - - // Send amount to `to` address. - balances[asset_id][to.to_field()].insert({ - .value = amount, - .owner = to, - .creator_address = creator_address, - .memo = memo, - }); - - // Return change to sender: - balances[asset_id][msg_sender.to_field()].insert({ - .value = change, - .owner = msg_sender, - .creator_address = msg_sender, - .memo = memo, - }); - - /**************************************************************** - * CLEANUP - ****************************************************************/ - - // Push args to the public inputs. - auto& public_inputs = exec_ctx.private_circuit_public_inputs; - public_inputs.args_hash = compute_var_args_hash( - { amount, to.to_field(), asset_id, memo, CT::fr(reveal_msg_sender_to_recipient), fee }); - - /// TODO: merkle membership check - // public_inputs.historical_note_hash_tree_root - - exec_ctx.finalize(); - - // info("public inputs: ", public_inputs); - - return public_inputs.to_native_type(); -}; - -} // namespace aztec3::circuits::apps::test_apps::escrow \ No newline at end of file diff --git a/circuits/cpp/src/aztec3/circuits/apps/test_apps/escrow/transfer.hpp b/circuits/cpp/src/aztec3/circuits/apps/test_apps/escrow/transfer.hpp deleted file mode 100644 index 444f5052ea4..00000000000 --- a/circuits/cpp/src/aztec3/circuits/apps/test_apps/escrow/transfer.hpp +++ /dev/null @@ -1,20 +0,0 @@ -#pragma once - -#include "init.hpp" - -#include "aztec3/circuits/abis/private_circuit_public_inputs.hpp" -#include "aztec3/circuits/apps/function_execution_context.hpp" - -namespace aztec3::circuits::apps::test_apps::escrow { - -using aztec3::circuits::abis::OptionalPrivateCircuitPublicInputs; - -OptionalPrivateCircuitPublicInputs transfer(FunctionExecutionContext& exec_ctx, - NT::fr const& _amount, - NT::address const& _to, - NT::fr const& _asset_id, - NT::fr const& _memo, - NT::boolean const& _reveal_msg_sender_to_recipient, - NT::fr const& _fee); - -} // namespace aztec3::circuits::apps::test_apps::escrow \ No newline at end of file diff --git a/circuits/cpp/src/aztec3/circuits/apps/test_apps/escrow/withdraw.cpp b/circuits/cpp/src/aztec3/circuits/apps/test_apps/escrow/withdraw.cpp deleted file mode 100644 index d61fcceed27..00000000000 --- a/circuits/cpp/src/aztec3/circuits/apps/test_apps/escrow/withdraw.cpp +++ /dev/null @@ -1,96 +0,0 @@ -#include "withdraw.hpp" - -#include "contract.hpp" - -#include "aztec3/circuits/abis/private_circuit_public_inputs.hpp" -#include "aztec3/circuits/hash.hpp" - -namespace aztec3::circuits::apps::test_apps::escrow { - -using aztec3::circuits::abis::OptionalPrivateCircuitPublicInputs; - -OptionalPrivateCircuitPublicInputs withdraw(FunctionExecutionContext& exec_ctx, - NT::fr const& _amount, - NT::fr const& _asset_id, - NT::fr const& _memo, - NT::fr const& _l1_withdrawal_address, - NT::fr const& _fee) -{ - /**************************************************************** - * Initialization - ****************************************************************/ - - // Make the exec_ctx aware of the contract's layout. - Contract contract = init_contract(); - exec_ctx.register_contract(&contract); - - // Convert arguments into circuit types: - auto& builder = exec_ctx.builder; - - CT::fr const amount = to_ct(builder, _amount); - CT::fr asset_id = to_ct(builder, _asset_id); - CT::fr memo = to_ct(builder, _memo); - CT::fr const l1_withdrawal_address = to_ct(builder, _l1_withdrawal_address); - CT::fr const fee = to_ct(builder, _fee); - - /**************************************************************** - * Get States & Globals used by the function - ****************************************************************/ - - auto& oracle = exec_ctx.oracle; - CT::address msg_sender = oracle.get_msg_sender(); - - // Syntactic sugar for a state variable: - // Note: these Mappings always map-from a field type (because it was complicated enough!!!) - // mapping(asset_id => mapping(owner => UTXOSet< >)) balances; - Mapping>> balances(&exec_ctx, "balances"); - - /**************************************************************** - * BODY - ****************************************************************/ - - // TODO: sort & filter functions! - std::vector old_balance_notes = - balances[asset_id][msg_sender.to_field()].get(2, { .owner = msg_sender }); - - CT::fr const old_value_1 = *(old_balance_notes[0].get_preimage().value); - CT::fr const old_value_2 = *(old_balance_notes[1].get_preimage().value); - - // MISSING: overflow & underflow checks, but I can't be bothered with safe_uint or range checks yet. - CT::fr change = (old_value_1 + old_value_2) - (amount + fee); - - old_balance_notes[0].remove(); - old_balance_notes[1].remove(); - - // Return change to self: - balances[asset_id][msg_sender.to_field()].insert({ - .value = change, - .owner = msg_sender, - .creator_address = msg_sender, - .memo = memo, - }); - - // auto& l1_withdraw_function = contract.get_l1_function("withdraw"); - - // TODO: this doesn't do anything at the moment: - // l1_withdraw_function.call({ asset_id, amount, msg_sender.to_field() }); - - /**************************************************************** - * CLEANUP - ****************************************************************/ - - // Push args to the public inputs. - auto& public_inputs = exec_ctx.private_circuit_public_inputs; - public_inputs.args_hash = compute_var_args_hash({ amount, asset_id, memo, l1_withdrawal_address, fee }); - - exec_ctx.finalize(); - - /// TODO: merkle membership check - // public_inputs.historical_note_hash_tree_root - - // info("public inputs: ", public_inputs); - - return public_inputs.to_native_type(); -}; - -} // namespace aztec3::circuits::apps::test_apps::escrow \ No newline at end of file diff --git a/circuits/cpp/src/aztec3/circuits/apps/test_apps/escrow/withdraw.hpp b/circuits/cpp/src/aztec3/circuits/apps/test_apps/escrow/withdraw.hpp deleted file mode 100644 index 666e9db417e..00000000000 --- a/circuits/cpp/src/aztec3/circuits/apps/test_apps/escrow/withdraw.hpp +++ /dev/null @@ -1,19 +0,0 @@ -#pragma once - -#include "init.hpp" - -#include "aztec3/circuits/abis/private_circuit_public_inputs.hpp" -#include "aztec3/circuits/apps/function_execution_context.hpp" - -namespace aztec3::circuits::apps::test_apps::escrow { - -using aztec3::circuits::abis::OptionalPrivateCircuitPublicInputs; - -OptionalPrivateCircuitPublicInputs withdraw(FunctionExecutionContext& exec_ctx, - NT::fr const& _amount, - NT::fr const& _asset_id, - NT::fr const& _memo, - NT::fr const& _l1_withdrawal_address, - NT::fr const& _fee); - -} // namespace aztec3::circuits::apps::test_apps::escrow \ No newline at end of file diff --git a/circuits/cpp/src/aztec3/circuits/apps/test_apps/private_to_private_function_call/.test.cpp b/circuits/cpp/src/aztec3/circuits/apps/test_apps/private_to_private_function_call/.test.cpp deleted file mode 100644 index df1d4e060ea..00000000000 --- a/circuits/cpp/src/aztec3/circuits/apps/test_apps/private_to_private_function_call/.test.cpp +++ /dev/null @@ -1,66 +0,0 @@ -#include "index.hpp" - -#include "aztec3/circuits/abis/call_context.hpp" -#include "aztec3/circuits/abis/function_data.hpp" - -#include - -#include - -namespace aztec3::circuits::apps::test_apps::private_to_private_function_call { - -class private_to_private_function_call_tests : public ::testing::Test {}; - -TEST(private_to_private_function_call_tests, circuit_private_to_private_function_call) -{ - C fn1_builder = C(); - DB db; - - const NT::address contract_address = 12345; - const NT::fr msg_sender_private_key = 123456789; - const NT::address msg_sender = - uint256_t(0x01071e9a23e0f7edULL, 0x5d77b35d1830fa3eULL, 0xc6ba3660bb1f0c0bULL, 0x2ef9f7f09867fd6eULL); - - const FunctionData function_data{ - .selector = - { - .value = 1, // TODO: deduce this from the contract, somehow. - }, - .is_private = true, - .is_constructor = false, - }; - - const CallContext call_context{ - .msg_sender = msg_sender, - .storage_contract_address = contract_address, - .portal_contract_address = 0, - .function_selector = function_data.selector, - .is_delegate_call = false, - .is_static_call = false, - .is_contract_deployment = false, - }; - - NativeOracle fn1_oracle = NativeOracle(db, contract_address, function_data, call_context, msg_sender_private_key); - OracleWrapper fn1_oracle_wrapper = OracleWrapper(fn1_builder, fn1_oracle); - - FunctionExecutionContext fn1_exec_ctx(fn1_builder, fn1_oracle_wrapper); - - auto a = NT::fr(111); - auto b = NT::fr(222); - auto c = NT::fr(333); - - function_1_1(fn1_exec_ctx, { a, b, c, 0, 0, 0, 0, 0 }); - - const auto& function_1_1_public_inputs = fn1_exec_ctx.get_final_private_circuit_public_inputs(); - - info("function_1_1_public_inputs: ", function_1_1_public_inputs); - - // info("witness: ", fn1_builder.witness); - // info("constant variables: ", fn1_builder.constant_variables); - // info("variables: ", fn1_builder.variables); - info("failed?: ", fn1_builder.failed()); - info("err: ", fn1_builder.err()); - info("n: ", fn1_builder.num_gates); -} - -} // namespace aztec3::circuits::apps::test_apps::private_to_private_function_call \ No newline at end of file diff --git a/circuits/cpp/src/aztec3/circuits/apps/test_apps/private_to_private_function_call/README.md b/circuits/cpp/src/aztec3/circuits/apps/test_apps/private_to_private_function_call/README.md deleted file mode 100644 index 34550188adf..00000000000 --- a/circuits/cpp/src/aztec3/circuits/apps/test_apps/private_to_private_function_call/README.md +++ /dev/null @@ -1,2 +0,0 @@ -Unfinished example directory. -Not worth reading yet. \ No newline at end of file diff --git a/circuits/cpp/src/aztec3/circuits/apps/test_apps/private_to_private_function_call/contract.hpp b/circuits/cpp/src/aztec3/circuits/apps/test_apps/private_to_private_function_call/contract.hpp deleted file mode 100644 index 4d62ff38fda..00000000000 --- a/circuits/cpp/src/aztec3/circuits/apps/test_apps/private_to_private_function_call/contract.hpp +++ /dev/null @@ -1,43 +0,0 @@ -#pragma once - -#include "init.hpp" - -#include "aztec3/circuits/apps/contract.hpp" -#include "aztec3/circuits/apps/function_declaration.hpp" -#include "aztec3/circuits/apps/function_execution_context.hpp" - -namespace aztec3::circuits::apps::test_apps::private_to_private_function_call { - -Contract init_contract_2(); - -inline Contract init_contract_1() -{ - Contract contract_1("contract_1"); - - contract_1.declare_state_var("x"); - - // Solely used for assigning vk indices. - contract_1.set_functions({ - { .name = "function_1_1", .is_private = true }, - }); - - contract_1.import_contracts({ std::make_pair("contract_2", init_contract_2()) }); - - return contract_1; -} - -inline Contract init_contract_2() -{ - Contract contract_2("contract_2"); - - contract_2.declare_state_var("y"); - - // Solely used for assigning vk indices. - contract_2.set_functions({ - { .name = "function_2_1", .is_private = true }, - }); - - return contract_2; -} - -} // namespace aztec3::circuits::apps::test_apps::private_to_private_function_call \ No newline at end of file diff --git a/circuits/cpp/src/aztec3/circuits/apps/test_apps/private_to_private_function_call/function_1_1.cpp b/circuits/cpp/src/aztec3/circuits/apps/test_apps/private_to_private_function_call/function_1_1.cpp deleted file mode 100644 index 53c8c0db1e7..00000000000 --- a/circuits/cpp/src/aztec3/circuits/apps/test_apps/private_to_private_function_call/function_1_1.cpp +++ /dev/null @@ -1,86 +0,0 @@ -#include "function_1_1.hpp" - -#include "contract.hpp" -#include "function_2_1.hpp" - -#include "aztec3/circuits/apps/function_execution_context.hpp" -#include "aztec3/circuits/hash.hpp" - -namespace aztec3::circuits::apps::test_apps::private_to_private_function_call { - -void function_1_1(FunctionExecutionContext& exec_ctx, std::vector const& _args) -{ - /**************************************************************** - * Initialization - ****************************************************************/ - - // Make the exec_ctx aware of the contract's layout. - Contract contract_1 = init_contract_1(); - exec_ctx.register_contract(&contract_1); - - // Convert arguments into circuit types: - auto& builder = exec_ctx.builder; - const auto a = to_ct(builder, _args[0]); - const auto b = to_ct(builder, _args[1]); - const auto c = to_ct(builder, _args[2]); - - /**************************************************************** - * Get States & Globals used by the function - ****************************************************************/ - - auto& oracle = exec_ctx.oracle; - - CT::address msg_sender = oracle.get_msg_sender(); - - // Syntactic sugar for declaring a state variable: - UTXO x(&exec_ctx, "x"); - - /**************************************************************** - * BODY - ****************************************************************/ - - // Hard-coded to match tests. - const CT::address unique_person_who_may_initialize = - NT::uint256(0x01071e9a23e0f7edULL, 0x5d77b35d1830fa3eULL, 0xc6ba3660bb1f0c0bULL, 0x2ef9f7f09867fd6eULL); - - unique_person_who_may_initialize.assert_equal(msg_sender); - - /** - * Now we want to call an external function of another smart contract. - * What I've written below is a bit of a hack. - * In reality what we'll need from Noir++ is syntax which hides all of the boilerplate I write below. - * Also, I _know_ where all the code for `function_2_1` is, so I've taken a big shortcut and #included - * `function_2_1.hpp`. This won't be the way we'll fetch bytecode in practice. In practice, we might only learn the - * contract address at runtime, and hence we'll have to fetch some acir bytecode at runtime from a DB and execute - * that in a simulator (e.g. the ACVM). This is where all this noddy C++ example code that I'm writing falls short. - * But hopefully this code still serves as a useful example of how the public inputs of a private function should be - * computed. - */ - // auto function_2_1 = contract_1.get_function("function_2_1"); - const CT::address fn_2_1_contract_address = 23456; - - // TODO: this can probably be tidied up. - auto return_values = - exec_ctx.call(fn_2_1_contract_address, - "function_2_1", - std::function)>(function_2_1), - { a, b, c, 0, 0, 0, 0, 0 }); - - // Use the return value in some way, just for fun: - x.initialize({ - .value = return_values[0], - .owner = msg_sender, - }); - - /**************************************************************** - * CLEANUP - ****************************************************************/ - - // Push args to the public inputs. - auto& public_inputs = exec_ctx.private_circuit_public_inputs; - public_inputs.args_hash = compute_var_args_hash({ a, b, c }); - - exec_ctx.finalize(); -}; - -} // namespace aztec3::circuits::apps::test_apps::private_to_private_function_call \ No newline at end of file diff --git a/circuits/cpp/src/aztec3/circuits/apps/test_apps/private_to_private_function_call/function_1_1.hpp b/circuits/cpp/src/aztec3/circuits/apps/test_apps/private_to_private_function_call/function_1_1.hpp deleted file mode 100644 index 3119a4263b9..00000000000 --- a/circuits/cpp/src/aztec3/circuits/apps/test_apps/private_to_private_function_call/function_1_1.hpp +++ /dev/null @@ -1,11 +0,0 @@ -#pragma once - -#include "init.hpp" - -#include "aztec3/circuits/apps/function_execution_context.hpp" - -namespace aztec3::circuits::apps::test_apps::private_to_private_function_call { - -void function_1_1(FunctionExecutionContext& exec_ctx, std::vector const& _args); - -} // namespace aztec3::circuits::apps::test_apps::private_to_private_function_call \ No newline at end of file diff --git a/circuits/cpp/src/aztec3/circuits/apps/test_apps/private_to_private_function_call/function_2_1.cpp b/circuits/cpp/src/aztec3/circuits/apps/test_apps/private_to_private_function_call/function_2_1.cpp deleted file mode 100644 index ba26b494b80..00000000000 --- a/circuits/cpp/src/aztec3/circuits/apps/test_apps/private_to_private_function_call/function_2_1.cpp +++ /dev/null @@ -1,73 +0,0 @@ -#include "function_2_1.hpp" - -#include "contract.hpp" - -#include "aztec3/circuits/abis/private_circuit_public_inputs.hpp" -#include "aztec3/circuits/hash.hpp" - -namespace aztec3::circuits::apps::test_apps::private_to_private_function_call { - - -void function_2_1(FunctionExecutionContext& exec_ctx, std::vector const& _args) -{ - /**************************************************************** - * Initialization - ****************************************************************/ - - // Make the exec_ctx aware of the contract's layout. - Contract contract_2 = init_contract_2(); - exec_ctx.register_contract(&contract_2); - - // Convert arguments into circuit types: - auto& builder = exec_ctx.builder; - const auto a = to_ct(builder, _args[0]); - const auto b = to_ct(builder, _args[1]); - const auto c = to_ct(builder, _args[2]); - - /**************************************************************** - * Get States & Globals used by the function - ****************************************************************/ - - auto& oracle = exec_ctx.oracle; - - CT::address msg_sender = oracle.get_msg_sender(); - - // Syntactic sugar for declaring a state variable: - UTXO y(&exec_ctx, "y"); - - /**************************************************************** - * BODY - ****************************************************************/ - - auto product = a * b * c; - - CT::address const unique_person_who_may_initialize = 999999; - - unique_person_who_may_initialize.assert_equal(msg_sender); - - y.initialize({ - .value = product, - .owner = msg_sender, - }); - // TODO: how to initialize a UTXO if it's part of a nested function call, because the msg_sender will be a contract - // address (currently the unique_initializer_address is asserted to be the msg_sender). - - /**************************************************************** - * CLEANUP - ****************************************************************/ - - // Push args to the public inputs. - - auto& public_inputs = exec_ctx.private_circuit_public_inputs; - public_inputs.args_hash = compute_var_args_hash({ a, b, c }); - - public_inputs.return_values[0] = product; - - exec_ctx.finalize(); - - // info("public inputs: ", public_inputs); - - // TODO: also return note preimages and nullifier preimages. -}; - -} // namespace aztec3::circuits::apps::test_apps::private_to_private_function_call \ No newline at end of file diff --git a/circuits/cpp/src/aztec3/circuits/apps/test_apps/private_to_private_function_call/function_2_1.hpp b/circuits/cpp/src/aztec3/circuits/apps/test_apps/private_to_private_function_call/function_2_1.hpp deleted file mode 100644 index e363e100b62..00000000000 --- a/circuits/cpp/src/aztec3/circuits/apps/test_apps/private_to_private_function_call/function_2_1.hpp +++ /dev/null @@ -1,13 +0,0 @@ -#pragma once - -#include "init.hpp" - -#include "aztec3/circuits/abis/private_circuit_public_inputs.hpp" - -namespace aztec3::circuits::apps::test_apps::private_to_private_function_call { - -using aztec3::circuits::abis::OptionalPrivateCircuitPublicInputs; - -void function_2_1(FunctionExecutionContext& exec_ctx, std::vector const& _args); - -} // namespace aztec3::circuits::apps::test_apps::private_to_private_function_call \ No newline at end of file diff --git a/circuits/cpp/src/aztec3/circuits/apps/test_apps/private_to_private_function_call/index.hpp b/circuits/cpp/src/aztec3/circuits/apps/test_apps/private_to_private_function_call/index.hpp deleted file mode 100644 index 80035d4a71c..00000000000 --- a/circuits/cpp/src/aztec3/circuits/apps/test_apps/private_to_private_function_call/index.hpp +++ /dev/null @@ -1,4 +0,0 @@ -#include "contract.hpp" -#include "function_1_1.hpp" -#include "function_2_1.hpp" -#include "init.hpp" \ No newline at end of file diff --git a/circuits/cpp/src/aztec3/circuits/apps/test_apps/private_to_private_function_call/init.hpp b/circuits/cpp/src/aztec3/circuits/apps/test_apps/private_to_private_function_call/init.hpp deleted file mode 100644 index 7f2e42a70a4..00000000000 --- a/circuits/cpp/src/aztec3/circuits/apps/test_apps/private_to_private_function_call/init.hpp +++ /dev/null @@ -1,40 +0,0 @@ -#pragma once - -#include "aztec3/circuits/apps/contract.hpp" -#include "aztec3/circuits/apps/function_execution_context.hpp" -#include "aztec3/circuits/apps/notes/default_singleton_private_note/note.hpp" -#include "aztec3/circuits/apps/oracle_wrapper.hpp" -#include "aztec3/circuits/apps/state_vars/utxo_state_var.hpp" -#include "aztec3/oracle/oracle.hpp" -#include "aztec3/utils/types/circuit_types.hpp" -#include "aztec3/utils/types/convert.hpp" -#include "aztec3/utils/types/native_types.hpp" - -namespace aztec3::circuits::apps::test_apps::private_to_private_function_call { - -// Builder -using C = UltraCircuitBuilder; - -// Native and circuit types -using CT = aztec3::utils::types::CircuitTypes; -using NT = aztec3::utils::types::NativeTypes; - -// Database types -using DB = oracle::FakeDB; -using oracle::NativeOracle; -using OracleWrapper = OracleWrapperInterface; - -using Contract = apps::Contract; -using FunctionExecutionContext = apps::FunctionExecutionContext; - -using aztec3::utils::types::to_ct; - -// StateVars -using apps::state_vars::UTXOStateVar; - -// Get rid of ugly `Builder` template arg from our state var types: -template using UTXO = UTXOStateVar; - -using Note = apps::notes::DefaultSingletonPrivateNote; - -} // namespace aztec3::circuits::apps::test_apps::private_to_private_function_call \ No newline at end of file diff --git a/circuits/cpp/src/aztec3/circuits/apps/utxo_datum.hpp b/circuits/cpp/src/aztec3/circuits/apps/utxo_datum.hpp deleted file mode 100644 index c13bd18527b..00000000000 --- a/circuits/cpp/src/aztec3/circuits/apps/utxo_datum.hpp +++ /dev/null @@ -1,49 +0,0 @@ -#pragma once - -#include "aztec3/utils/types/circuit_types.hpp" -#include "aztec3/utils/types/native_types.hpp" - -#include - -namespace aztec3::circuits::apps { - -using aztec3::utils::types::CircuitTypes; -using aztec3::utils::types::NativeTypes; -using plonk::stdlib::witness_t; - -/** - * @tparam NCT - NativeTypes or CircuitTypes - * @tparam NotePreimage - */ -template struct UTXOSLoadDatum { - using fr = typename NCT::fr; - using address = typename NCT::address; - using uint32 = typename NCT::uint32; - - fr commitment = 0; - address contract_address = 0; - NotePreimage preimage{}; - - std::vector sibling_path; - uint32 leaf_index; - fr historical_note_hash_tree_root = 0; - - template auto to_circuit_type(Builder& builder) const - { - static_assert(std::is_same::value); - - // Capture the circuit builder: - auto to_ct = [&](auto& e) { return aztec3::utils::types::to_ct(builder, e); }; - - auto preimage_ct = preimage.to_circuit_type(builder); - - UTXOSLoadDatum, decltype(preimage_ct)> datum = { - to_ct(commitment), to_ct(contract_address), preimage_ct, - to_ct(sibling_path), to_ct(leaf_index), to_ct(historical_note_hash_tree_root), - }; - - return datum; - }; -}; - -} // namespace aztec3::circuits::apps diff --git a/circuits/cpp/src/aztec3/circuits/hash.hpp b/circuits/cpp/src/aztec3/circuits/hash.hpp deleted file mode 100644 index 241271f1925..00000000000 --- a/circuits/cpp/src/aztec3/circuits/hash.hpp +++ /dev/null @@ -1,458 +0,0 @@ -#pragma once - -#include "aztec3/circuits/abis/function_data.hpp" -#include "aztec3/circuits/abis/function_leaf_preimage.hpp" -#include "aztec3/circuits/abis/function_selector.hpp" -#include "aztec3/circuits/abis/global_variables.hpp" -#include "aztec3/circuits/abis/new_contract_data.hpp" -#include "aztec3/circuits/abis/point.hpp" -#include "aztec3/constants.hpp" -#include "aztec3/utils/circuit_errors.hpp" - -#include - -#include -#include - -namespace aztec3::circuits { - -using abis::FunctionData; -using abis::FunctionSelector; -using abis::Point; -using aztec3::circuits::abis::ContractLeafPreimage; -using aztec3::circuits::abis::FunctionLeafPreimage; -using MemoryStore = proof_system::plonk::stdlib::merkle_tree::MemoryStore; -using MerkleTree = proof_system::plonk::stdlib::merkle_tree::MerkleTree; - -template typename NCT::fr compute_var_args_hash(std::vector const& args) -{ - auto const MAX_ARGS = 32; - if (args.size() > MAX_ARGS) { - throw_or_abort("Too many arguments in call to compute_var_args_hash"); - } - return NCT::hash(args, FUNCTION_ARGS); -} - -template typename NCT::fr compute_constructor_hash(FunctionData const& function_data, - typename NCT::fr const& args_hash, - typename NCT::fr const& constructor_vk_hash) -{ - using fr = typename NCT::fr; - - fr const function_data_hash = function_data.hash(); - - std::vector const inputs = { - function_data_hash, - args_hash, - constructor_vk_hash, - }; - - return NCT::hash(inputs, aztec3::GeneratorIndex::CONSTRUCTOR); -} - -template typename NCT::fr compute_partial_address(typename NCT::fr const& contract_address_salt, - typename NCT::fr const& function_tree_root, - typename NCT::fr const& constructor_hash) -{ - std::vector const inputs = { - typename NCT::fr(0), typename NCT::fr(0), contract_address_salt, function_tree_root, constructor_hash, - }; - return NCT::hash(inputs, aztec3::GeneratorIndex::PARTIAL_ADDRESS); -} - -template -typename NCT::address compute_contract_address_from_partial(Point const& point, - typename NCT::fr const& partial_address) -{ - std::vector const inputs = { - point.x, - point.y, - partial_address, - }; - return { NCT::hash(inputs, aztec3::GeneratorIndex::CONTRACT_ADDRESS) }; -} - -template typename NCT::fr compute_commitment_nonce(typename NCT::fr const& first_nullifier, - typename NCT::fr const& commitment_index) -{ - using fr = typename NCT::fr; - - std::vector const inputs = { - first_nullifier, - commitment_index, - }; - - return NCT::hash(inputs, aztec3::GeneratorIndex::COMMITMENT_NONCE); -} - -template typename NCT::fr silo_commitment(typename NCT::address const& contract_address, - typename NCT::fr const& inner_commitment) -{ - using fr = typename NCT::fr; - - std::vector const inputs = { - contract_address.to_field(), - inner_commitment, - }; - - return NCT::hash(inputs, aztec3::GeneratorIndex::SILOED_COMMITMENT); -} - -template -typename NCT::fr compute_unique_commitment(typename NCT::fr nonce, typename NCT::fr siloed_commitment) -{ - using fr = typename NCT::fr; - - std::vector const inputs = { - nonce, - siloed_commitment, - }; - - return NCT::hash(inputs, aztec3::GeneratorIndex::UNIQUE_COMMITMENT); -} - -template -typename NCT::fr silo_nullifier(typename NCT::address const& contract_address, typename NCT::fr nullifier) -{ - using fr = typename NCT::fr; - - std::vector const inputs = { - contract_address.to_field(), - nullifier, - }; - - // TODO(https://github.com/AztecProtocol/aztec-packages/issues/1475): use hash here (everywhere?) - return NCT::hash(inputs, aztec3::GeneratorIndex::OUTER_NULLIFIER); -} - - -template typename NCT::fr compute_block_hash(typename NCT::fr const& globals_hash, - typename NCT::fr const& note_hash_tree_root, - typename NCT::fr const& nullifier_tree_root, - typename NCT::fr const& contract_tree_root, - typename NCT::fr const& l1_to_l2_message_tree_root, - typename NCT::fr const& public_data_tree_root) -{ - using fr = typename NCT::fr; - - std::vector const inputs = { - globals_hash, note_hash_tree_root, nullifier_tree_root, - contract_tree_root, l1_to_l2_message_tree_root, public_data_tree_root, - }; - - return NCT::hash(inputs, aztec3::GeneratorIndex::BLOCK_HASH); -} - -template -typename NCT::fr compute_block_hash_with_globals(abis::GlobalVariables const& globals, - typename NCT::fr const& note_hash_tree_root, - typename NCT::fr const& nullifier_tree_root, - typename NCT::fr const& contract_tree_root, - typename NCT::fr const& l1_to_l2_message_tree_root, - typename NCT::fr const& public_data_tree_root) -{ - using fr = typename NCT::fr; - - std::vector const inputs = { - globals.hash(), note_hash_tree_root, nullifier_tree_root, - contract_tree_root, l1_to_l2_message_tree_root, public_data_tree_root, - }; - - return NCT::hash(inputs, aztec3::GeneratorIndex::BLOCK_HASH); -} - -template typename NCT::fr compute_globals_hash(abis::GlobalVariables const& globals) -{ - return globals.hash(); -} - -/** - * @brief Calculate the Merkle tree root from the sibling path and leaf. - * - * @details The leaf is hashed with its sibling, and then the result is hashed - * with the next sibling etc in the path. The last hash is the root. - * - * @tparam NCT Operate on NativeTypes or CircuitTypes - * @tparam N The number of elements in the sibling path - * @param leaf The leaf element of the Merkle tree - * @param leaf_index The index of the leaf element in the Merkle tree - * @param sibling_path The nodes representing the merkle siblings of the leaf, its parent, - * the next parent, etc up to the sibling below the root - * @return The computed Merkle tree root. - * - * TODO need to use conditional assigns instead of `ifs` for circuit version. - * see membership.hpp:check_subtree_membership (left/right/conditional_assign, etc) - */ -template -typename NCT::fr root_from_sibling_path(typename NCT::fr const& leaf, - typename NCT::uint32 const& leaf_index, - std::array const& sibling_path) -{ - auto node = leaf; - for (size_t i = 0; i < N; i++) { - if (leaf_index & (1 << i)) { - node = NCT::merkle_hash(sibling_path[i], node); - } else { - node = NCT::merkle_hash(node, sibling_path[i]); - } - } - return node; // root -} - -/** - * @brief Calculate the Merkle tree root from the sibling path and leaf. - * - * @details The leaf is hashed with its sibling, and then the result is hashed - * with the next sibling etc in the path. The last hash is the root. - * - * @tparam NCT Operate on NativeTypes or CircuitTypes - * @tparam N The number of elements in the sibling path - * @param leaf The leaf element of the Merkle tree - * @param leaf_index The index of the leaf element in the Merkle tree - * @param sibling_path The nodes representing the merkle siblings of the leaf, its parent, - * the next parent, etc up to the sibling below the root - * @return The computed Merkle tree root. - * - * TODO need to use conditional assigns instead of `ifs` for circuit version. - * see membership.hpp:check_subtree_membership (left/right/conditional_assign, etc) - */ -template -typename NCT::fr root_from_sibling_path(typename NCT::fr const& leaf, - typename NCT::fr const& leaf_index, - std::array const& sibling_path) -{ - auto node = leaf; - uint256_t index = leaf_index; - for (size_t i = 0; i < N; i++) { - if (index & 1) { - node = NCT::merkle_hash(sibling_path[i], node); - } else { - node = NCT::merkle_hash(node, sibling_path[i]); - } - index >>= uint256_t(1); - } - return node; // root -} - -/** - * @brief Get the sibling path of an item in a given merkle tree - * - * WARNING: this function is for testing purposes only! leaf_index is an fr - * in `MembershipWitness` but is a `size_t` here. This could lead to overflows - * on `1 << i` if the tree is large enough. - * - * @tparam N height of tree (not including root) - * @param tree merkle tree to operate on - * @param leaf_index index of the leaf to get path for - * @param subtree_depth_to_skip skip some number of bottom layers - * @return std::array sibling path - */ -template -std::array get_sibling_path(MerkleTree& tree, size_t leaf_index, size_t const& subtree_depth_to_skip) -{ - std::array sibling_path; - auto path = tree.get_hash_path(leaf_index); - // slice out the skip - leaf_index = leaf_index >> (subtree_depth_to_skip); - for (size_t i = 0; i < N; i++) { - if (leaf_index & (1 << i)) { - sibling_path[i] = path[subtree_depth_to_skip + i].first; - } else { - sibling_path[i] = path[subtree_depth_to_skip + i].second; - } - } - return sibling_path; -} - -template -void check_membership(Builder& builder, - typename NCT::fr const& value, - typename NCT::fr const& index, - std::array const& sibling_path, - typename NCT::fr const& root, - std::string const& msg) -{ - const auto calculated_root = root_from_sibling_path(value, index, sibling_path); - builder.do_assert(calculated_root == root, - std::string("Membership check failed: ") + msg, - aztec3::utils::CircuitErrorCode::MEMBERSHIP_CHECK_FAILED); -} - -/** - * @brief Calculate the function tree root from the sibling path and leaf preimage. - * - * @tparam NCT (native or circuit) - * @param selector in leaf preimage - * @param is_internal in leaf preimage - * @param is_private in leaf preimage - * @param vk_hash in leaf preimage - * @param acir_hash in leaf preimage - * @param function_leaf_index leaf index in the function tree - * @param function_leaf_sibling_path - * @return NCT::fr - */ -template typename NCT::fr function_tree_root_from_siblings( - FunctionSelector const& selector, - typename NCT::boolean const& is_internal, - typename NCT::boolean const& is_private, - typename NCT::fr const& vk_hash, - typename NCT::fr const& acir_hash, - typename NCT::fr const& function_leaf_index, - std::array const& function_leaf_sibling_path) -{ - const auto function_leaf_preimage = FunctionLeafPreimage{ - .selector = selector, - .is_internal = is_internal, - .is_private = is_private, - .vk_hash = vk_hash, - .acir_hash = acir_hash, - }; - - const auto function_leaf = function_leaf_preimage.hash(); - - auto function_tree_root = - root_from_sibling_path(function_leaf, function_leaf_index, function_leaf_sibling_path); - return function_tree_root; -} - -/** - * @brief Calculate the contract tree root from the sibling path and leaf preimage. - * - * @tparam NCT (native or circuit) - * @param function_tree_root in leaf preimage - * @param storage_contract_address in leaf preimage - * @param portal_contract_address in leaf preimage - * @param contract_leaf_index leaf index in the function tree - * @param contract_leaf_sibling_path - * @return NCT::fr - */ -template typename NCT::fr contract_tree_root_from_siblings( - typename NCT::fr const& function_tree_root, - typename NCT::address const& storage_contract_address, - typename NCT::address const& portal_contract_address, - typename NCT::fr const& contract_leaf_index, - std::array const& contract_leaf_sibling_path) -{ - const ContractLeafPreimage contract_leaf_preimage{ storage_contract_address, - portal_contract_address, - function_tree_root }; - - const auto contract_leaf = contract_leaf_preimage.hash(); - - const auto computed_contract_tree_root = - root_from_sibling_path(contract_leaf, contract_leaf_index, contract_leaf_sibling_path); - return computed_contract_tree_root; -} - -/** - * @brief Compute sibling path for an empty tree. - * - * @tparam NCT (native or circuit) - * @tparam TREE_HEIGHT - * @param zero_leaf the leaf value that corresponds to a zero preimage - * @return std::array - */ -template -std::array compute_empty_sibling_path(typename NCT::fr const& zero_leaf) -{ - std::array sibling_path = { zero_leaf }; - for (size_t i = 1; i < TREE_HEIGHT; i++) { - // hash previous sibling with itself to get node above - sibling_path[i] = NCT::merkle_hash(sibling_path[i - 1], sibling_path[i - 1]); - } - return sibling_path; -} - -/** - * @brief Compute the value to be inserted into the public data tree - * @param value The value to be inserted into the public data tree - * @return The hash value required for insertion into the public data tree - */ -template typename NCT::fr compute_public_data_tree_value(typename NCT::fr const& value) -{ - // as it's a public value, it doesn't require hashing. - // leaving this function here in case we decide to change this. - return value; -} - -/** - * @brief Compute the index for inserting a value into the public data tree - * @param contract_address The address of the contract to which the inserted element belongs - * @param storage_slot The storage slot to which the inserted element belongs - * @return The index for insertion into the public data tree - */ -template typename NCT::fr compute_public_data_tree_index(typename NCT::address const& contract_address, - typename NCT::fr const& storage_slot) -{ - return NCT::hash({ contract_address.to_field(), storage_slot }, GeneratorIndex::PUBLIC_LEAF_INDEX); -} - -template typename NCT::fr compute_l2_to_l1_hash(typename NCT::address const& contract_address, - typename NCT::fr const& rollup_version_id, - typename NCT::fr const& portal_contract_address, - typename NCT::fr const& chain_id, - typename NCT::fr const& content) -{ - using fr = typename NCT::fr; - - std::vector const inputs = { - contract_address.to_field(), rollup_version_id, portal_contract_address, chain_id, content, - }; - - constexpr auto const num_bytes = 5 * 32; - std::array calldata_hash_inputs_bytes; - // Convert all into a buffer, then copy into the array, then hash - for (size_t i = 0; i < inputs.size(); i++) { - auto as_bytes = inputs[i].to_buffer(); - - auto offset = i * 32; - std::copy(as_bytes.begin(), as_bytes.end(), calldata_hash_inputs_bytes.begin() + offset); - } - - std::vector const calldata_hash_inputs_bytes_vec(calldata_hash_inputs_bytes.begin(), - calldata_hash_inputs_bytes.end()); - - // @todo @LHerskind NOTE sha to field! - return sha256::sha256_to_field(calldata_hash_inputs_bytes_vec); -} - -/** - * @brief Computes sha256 hash of 2 input hashes stored in 4 fields. - * @param hashes 4 fields containing 2 hashes [high, low, high, low]. - * @return Resulting sha256 hash stored in 2 fields. - */ -template std::array accumulate_sha256( - std::array const& hashes) -{ - using fr = typename NCT::fr; - - // Generate a 512 bit input from right and left 256 bit hashes - constexpr auto num_bytes = 2 * 32; - std::array hash_input_bytes; - for (size_t i = 0; i < 4; i++) { - auto half = hashes[i].to_buffer(); - for (size_t j = 0; j < 16; j++) { - hash_input_bytes[i * 16 + j] = half[16 + j]; - } - } - - // Compute the sha256 - std::vector const hash_input_bytes_vec(hash_input_bytes.begin(), hash_input_bytes.end()); - auto h = sha256::sha256(hash_input_bytes_vec); - - // Split the hash into two fields, a high and a low - std::array buf_1; - std::array buf_2; - for (uint8_t i = 0; i < 16; i++) { - buf_1[i] = 0; - buf_1[16 + i] = h[i]; - buf_2[i] = 0; - buf_2[16 + i] = h[i + 16]; - } - auto high = fr::serialize_from_buffer(buf_1.data()); - auto low = fr::serialize_from_buffer(buf_2.data()); - - return { high, low }; -} - -} // namespace aztec3::circuits diff --git a/circuits/cpp/src/aztec3/circuits/kernel/CMakeLists.txt b/circuits/cpp/src/aztec3/circuits/kernel/CMakeLists.txt deleted file mode 100644 index 88e86fd9163..00000000000 --- a/circuits/cpp/src/aztec3/circuits/kernel/CMakeLists.txt +++ /dev/null @@ -1,9 +0,0 @@ -circuits_cmake_module( - aztec3_circuits_kernel - - # Question: why can't I link to these barretenberg modules? - # aztec3_circuits_abis - aztec3_circuits_apps - - barretenberg -) \ No newline at end of file diff --git a/circuits/cpp/src/aztec3/circuits/kernel/private/.test.cpp b/circuits/cpp/src/aztec3/circuits/kernel/private/.test.cpp deleted file mode 100644 index c682ed1cbd0..00000000000 --- a/circuits/cpp/src/aztec3/circuits/kernel/private/.test.cpp +++ /dev/null @@ -1,104 +0,0 @@ -#include "index.hpp" -#include "init.hpp" -#include "testing_harness.hpp" - -#include "aztec3/circuits/apps/test_apps/basic_contract_deployment/basic_contract_deployment.hpp" -#include "aztec3/circuits/apps/test_apps/escrow/deposit.hpp" - -#include - -#include - -namespace { - -using aztec3::circuits::apps::test_apps::basic_contract_deployment::constructor; -using aztec3::circuits::apps::test_apps::escrow::deposit; - -using aztec3::circuits::kernel::private_kernel::testing_harness::do_private_call_get_kernel_inputs_init; -using aztec3::circuits::kernel::private_kernel::testing_harness::do_private_call_get_kernel_inputs_inner; - -} // namespace - -namespace aztec3::circuits::kernel::private_kernel { - -class private_kernel_tests : public ::testing::Test { - protected: - static void SetUpTestSuite() { barretenberg::srs::init_crs_factory("../barretenberg/cpp/srs_db/ignition"); } -}; - -/** - * @brief Check private kernel circuit for arbitrary valid app proof and previous kernel proof - * @details The purpose of this test is to check the private kernel circuit given a valid app proof and a valid previous - * private kernel proof. To avoid doing actual proof construction, we simply read in an arbitrary but valid proof and a - * corresponding valid verification key from file. The same proof and vkey data is used for both the app and the - * previous kernel. - * @note The choice of app circuit (currently 'deposit') is entirely arbitrary and can be replaced with any other valid - * app circuit. - */ -TEST_F(private_kernel_tests, circuit_basic) -{ - NT::fr const& amount = 5; - NT::fr const& asset_id = 1; - NT::fr const& memo = 999; - std::array const& empty_logs_hash = { NT::fr(16), NT::fr(69) }; - NT::fr const& empty_log_preimages_length = NT::fr(100); - - // Generate private inputs including proofs and vkeys for app circuit and previous kernel - auto const& private_inputs = do_private_call_get_kernel_inputs_inner(false, - deposit, - { amount, asset_id, memo }, - empty_logs_hash, - empty_logs_hash, - empty_log_preimages_length, - empty_log_preimages_length, - empty_logs_hash, - empty_logs_hash, - empty_log_preimages_length, - empty_log_preimages_length, - true); - - // Execute and prove the first kernel iteration - Builder private_kernel_builder; - private_kernel_circuit(private_kernel_builder, private_inputs, true); - - // Check the private kernel circuit - EXPECT_TRUE(private_kernel_builder.check_circuit()); -} - -// TODO(1998): Lack of support for msgpack deserialization for Circuitresult type for this test -// to be able to call private_kernel__sim_init. -/** - * @brief Some private circuit simulation checked against its results via cbinds - */ -/* -TEST_F(private_kernel_tests, circuit_cbinds) -{ - NT::fr const& arg0 = 5; - NT::fr const& arg1 = 1; - NT::fr const& arg2 = 999; - std::array const& encrypted_logs_hash = { NT::fr(16), NT::fr(69) }; - NT::fr const& encrypted_log_preimages_length = NT::fr(100); - std::array const& unencrypted_logs_hash = { NT::fr(26), NT::fr(47) }; - NT::fr const& unencrypted_log_preimages_length = NT::fr(50); - - // first run actual simulation to get public inputs - auto const& private_inputs = do_private_call_get_kernel_inputs_init(true, - constructor, - { arg0, arg1, arg2 }, - encrypted_logs_hash, - unencrypted_logs_hash, - encrypted_log_preimages_length, - unencrypted_log_preimages_length, - true); - DummyBuilder builder = DummyBuilder("private_kernel_tests__circuit_create_proof_cbinds"); - auto const& public_inputs = native_private_kernel_circuit_initial(builder, private_inputs); - - auto exp_result = builder.result_or_error(public_inputs); - // Does not compile. See https://github.com/AztecProtocol/aztec-packages/issues/1998 - auto res = call_msgpack_cbind(private_kernel__sim_init, private_inputs); - - ASSERT_TRUE(exp_result.result == res.result); -} -*/ - -} // namespace aztec3::circuits::kernel::private_kernel diff --git a/circuits/cpp/src/aztec3/circuits/kernel/private/c_bind.cpp b/circuits/cpp/src/aztec3/circuits/kernel/private/c_bind.cpp deleted file mode 100644 index 0044f2e179e..00000000000 --- a/circuits/cpp/src/aztec3/circuits/kernel/private/c_bind.cpp +++ /dev/null @@ -1,49 +0,0 @@ -#include "c_bind.h" - -#include "index.hpp" -#include "utils.hpp" - -#include "aztec3/circuits/abis/private_kernel/private_kernel_inputs_init.hpp" -#include "aztec3/circuits/abis/private_kernel/private_kernel_inputs_inner.hpp" -#include "aztec3/circuits/abis/private_kernel/private_kernel_inputs_ordering.hpp" -#include "aztec3/constants.hpp" - -#include - -#include - -namespace { -using Builder = UltraCircuitBuilder; -using NT = aztec3::utils::types::NativeTypes; -using DummyCircuitBuilder = aztec3::utils::DummyCircuitBuilder; -using aztec3::circuits::abis::private_kernel::PrivateKernelInputsInit; -using aztec3::circuits::abis::private_kernel::PrivateKernelInputsInner; -using aztec3::circuits::abis::private_kernel::PrivateKernelInputsOrdering; -using aztec3::circuits::kernel::private_kernel::native_private_kernel_circuit_initial; -using aztec3::circuits::kernel::private_kernel::native_private_kernel_circuit_inner; -using aztec3::circuits::kernel::private_kernel::native_private_kernel_circuit_ordering; -using aztec3::circuits::kernel::private_kernel::utils::dummy_previous_kernel; - -} // namespace - -// WASM Cbinds - -CBIND(private_kernel__dummy_previous_kernel, []() { return dummy_previous_kernel(); }); - -CBIND(private_kernel__sim_init, [](PrivateKernelInputsInit private_inputs) { - DummyCircuitBuilder builder = DummyCircuitBuilder("private_kernel__sim_init"); - auto const& public_inputs = native_private_kernel_circuit_initial(builder, private_inputs); - return builder.result_or_error(public_inputs); -}); - -CBIND(private_kernel__sim_inner, [](PrivateKernelInputsInner private_inputs) { - DummyCircuitBuilder builder = DummyCircuitBuilder("private_kernel__sim_inner"); - auto const& public_inputs = native_private_kernel_circuit_inner(builder, private_inputs); - return builder.result_or_error(public_inputs); -}); - -CBIND(private_kernel__sim_ordering, [](PrivateKernelInputsOrdering private_inputs) { - DummyCircuitBuilder builder = DummyCircuitBuilder("private_kernel__sim_ordering"); - auto const& public_inputs = native_private_kernel_circuit_ordering(builder, private_inputs); - return builder.result_or_error(public_inputs); -}); diff --git a/circuits/cpp/src/aztec3/circuits/kernel/private/c_bind.h b/circuits/cpp/src/aztec3/circuits/kernel/private/c_bind.h deleted file mode 100644 index b88b051c326..00000000000 --- a/circuits/cpp/src/aztec3/circuits/kernel/private/c_bind.h +++ /dev/null @@ -1,10 +0,0 @@ -#pragma once -#include - -#include -#include - -CBIND_DECL(private_kernel__dummy_previous_kernel); -CBIND_DECL(private_kernel__sim_init); -CBIND_DECL(private_kernel__sim_inner); -CBIND_DECL(private_kernel__sim_ordering); diff --git a/circuits/cpp/src/aztec3/circuits/kernel/private/common.cpp b/circuits/cpp/src/aztec3/circuits/kernel/private/common.cpp deleted file mode 100644 index 101ead9ec52..00000000000 --- a/circuits/cpp/src/aztec3/circuits/kernel/private/common.cpp +++ /dev/null @@ -1,438 +0,0 @@ -#include "common.hpp" - -#include "init.hpp" - -#include "aztec3/circuits/abis/complete_address.hpp" -#include "aztec3/circuits/abis/contract_deployment_data.hpp" -#include "aztec3/circuits/abis/function_data.hpp" -#include "aztec3/circuits/abis/kernel_circuit_public_inputs.hpp" -#include "aztec3/circuits/abis/new_contract_data.hpp" -#include "aztec3/circuits/abis/private_circuit_public_inputs.hpp" -#include "aztec3/circuits/abis/private_kernel/private_call_data.hpp" -#include "aztec3/circuits/abis/read_request_membership_witness.hpp" -#include "aztec3/circuits/hash.hpp" -#include "aztec3/constants.hpp" -#include "aztec3/utils/array.hpp" -#include "aztec3/utils/dummy_circuit_builder.hpp" - -using DummyBuilder = aztec3::utils::DummyCircuitBuilder; - -using aztec3::circuits::abis::CompleteAddress; -using aztec3::circuits::abis::ContractDeploymentData; -using aztec3::circuits::abis::ContractLeafPreimage; -using aztec3::circuits::abis::FunctionData; -using aztec3::circuits::abis::KernelCircuitPublicInputs; -using aztec3::circuits::abis::NewContractData; -using aztec3::circuits::abis::ReadRequestMembershipWitness; - -using aztec3::utils::array_push; -using aztec3::utils::is_array_empty; -using aztec3::utils::push_array_to_array; -using aztec3::utils::validate_array; -using DummyBuilder = aztec3::utils::DummyCircuitBuilder; -using CircuitErrorCode = aztec3::utils::CircuitErrorCode; -using aztec3::circuits::abis::private_kernel::PrivateCallData; - -namespace aztec3::circuits::kernel::private_kernel { - -void common_validate_call_stack(DummyBuilder& builder, PrivateCallData const& private_call) -{ - const auto& stack = private_call.call_stack_item.public_inputs.private_call_stack; - const auto& preimages = private_call.private_call_stack_preimages; - for (size_t i = 0; i < stack.size(); ++i) { - const auto& hash = stack[i]; - const auto& preimage = preimages[i]; - - // Note: this assumes it's computationally infeasible to have `0` as a valid call_stack_item_hash. - // Assumes `hash == 0` means "this stack item is empty". - const auto calculated_hash = hash == 0 ? 0 : preimage.hash(); - builder.do_assert(hash == calculated_hash, - format("private_call_stack[", i, "] = ", hash, "; does not reconcile"), - CircuitErrorCode::PRIVATE_KERNEL__PRIVATE_CALL_STACK_ITEM_HASH_MISMATCH); - } -} - -/** - * @brief Validate all read requests against the historical note hash tree root. - * Use their membership witnesses to do so. If the historical root is not yet - * initialized, initialize it using the first read request here (if present). - * - * @details More info here: - * - https://discourse.aztec.network/t/to-read-or-not-to-read/178 - * - https://discourse.aztec.network/t/spending-notes-which-havent-yet-been-inserted/180 - * - * @param builder - * @param historical_note_hash_tree_root This is a reference to the historical root which all - * read requests are checked against here. - * @param read_requests the commitments being read by this private call - 'transient note reads' here are - * `inner_note_hashes` (not yet siloed, not unique), but 'pre-existing note reads' are `unique_siloed_note_hashes` - * @param read_request_membership_witnesses used to compute the note hash tree root - * for a given request which is essentially a membership check - */ -void common_validate_read_requests(DummyBuilder& builder, - NT::fr const& historical_note_hash_tree_root, - std::array const& read_requests, - std::array, - MAX_READ_REQUESTS_PER_CALL> const& read_request_membership_witnesses) -{ - // membership witnesses must resolve to the same note hash tree root - // for every request in all kernel iterations - for (size_t rr_idx = 0; rr_idx < aztec3::MAX_READ_REQUESTS_PER_CALL; rr_idx++) { - const auto& read_request = read_requests[rr_idx]; - const auto& witness = read_request_membership_witnesses[rr_idx]; - - // A pending commitment is the one that is not yet added to note hash tree - // A "transient read" is when we try to "read" a pending commitment within a transaction - // between function calls, as opposed to reading the outputs of a previous transaction - // which is a "pending read". - // We determine if it is a transient read depending on the leaf index from the membership witness - // Note that the Merkle membership proof would be null and void in case of an transient read - // but we use the leaf index as a placeholder to detect a 'pending note read'. - if (read_request != 0 && !witness.is_transient) { - const auto& root_for_read_request = - root_from_sibling_path(read_request, witness.leaf_index, witness.sibling_path); - builder.do_assert( - root_for_read_request == historical_note_hash_tree_root, - format("note hash tree root mismatch at read_request[", - rr_idx, - "]", - "\n\texpected root: ", - historical_note_hash_tree_root, - "\n\tbut got root*: ", - root_for_read_request, - "\n\tread_request**: ", - read_request, - "\n\tleaf_index: ", - witness.leaf_index, - "\n\tis_transient: ", - witness.is_transient, - "\n\thint_to_commitment: ", - witness.hint_to_commitment, - "\n\t* got root by treating the read_request as a leaf in the note hash tree " - "and merkle-hashing to a root using the membership witness" - "\n\t** for 'pre-existing note reads', the read_request is the unique_siloed_note_hash " - "(it has been hashed with contract address and then a nonce)"), - CircuitErrorCode::PRIVATE_KERNEL__READ_REQUEST_NOTE_HASH_TREE_ROOT_MISMATCH); - // TODO(https://github.com/AztecProtocol/aztec-packages/issues/1354): do we need to enforce - // that a non-transient read_request was derived from the proper/current contract address? - } - } -} - -/** - * @brief We validate that relevant arrays assumed to be zero-padded on the right comply to this format. - * - * @param builder - * @param app_public_inputs Reference to the private_circuit_public_inputs of the current kernel iteration. - */ -void common_validate_arrays(DummyBuilder& builder, PrivateCircuitPublicInputs const& app_public_inputs) -{ - // Each of the following arrays is expected to be zero-padded. - // In addition, some of the following arrays (new_commitments, etc...) are passed - // to push_array_to_array() routines which rely on the passed arrays to be well-formed. - validate_array(builder, app_public_inputs.return_values, "App public inputs - Return values"); - validate_array(builder, app_public_inputs.read_requests, "App public inputs - Read requests"); - validate_array(builder, app_public_inputs.new_commitments, "App public inputs - New commitments"); - validate_array(builder, app_public_inputs.new_nullifiers, "App public inputs - New nullifiers"); - validate_array(builder, app_public_inputs.nullified_commitments, "App public inputs - Nullified commitments"); - validate_array(builder, app_public_inputs.private_call_stack, "App public inputs - Private call stack"); - validate_array(builder, app_public_inputs.public_call_stack, "App public inputs - Public call stack"); - validate_array(builder, app_public_inputs.new_l2_to_l1_msgs, "App public inputs - New L2 to L1 messages"); - // encrypted_logs_hash and unencrypted_logs_hash have their own integrity checks. -} - -/** - * @brief We validate that relevant arrays assumed to be zero-padded on the right comply to this format. - * - * @param builder - * @param end Reference to previous_kernel.public_inputs.end. - */ -void common_validate_previous_kernel_arrays(DummyBuilder& builder, CombinedAccumulatedData const& end) -{ - // Each of the following arrays is expected to be zero-padded. - validate_array(builder, end.read_requests, "Accumulated data - Read Requests"); - validate_array(builder, end.new_commitments, "Accumulated data - New commitments"); - validate_array(builder, end.new_nullifiers, "Accumulated data - New nullifiers"); - validate_array(builder, end.nullified_commitments, "Accumulated data - Nullified commitments"); - validate_array(builder, end.private_call_stack, "Accumulated data - Private call stack"); - validate_array(builder, end.public_call_stack, "Accumulated data - Public call stack"); - validate_array(builder, end.new_l2_to_l1_msgs, "Accumulated data - New L2 to L1 messages"); -} - -void common_validate_previous_kernel_0th_nullifier(DummyBuilder& builder, CombinedAccumulatedData const& end) -{ - builder.do_assert(end.new_nullifiers[0] != 0, - "The 0th nullifier in the accumulated nullifier array is zero", - CircuitErrorCode::PRIVATE_KERNEL__0TH_NULLIFIER_IS_ZERO); -} - -void common_validate_previous_kernel_values(DummyBuilder& builder, CombinedAccumulatedData const& end) -{ - common_validate_previous_kernel_arrays(builder, end); - common_validate_previous_kernel_0th_nullifier(builder, end); -} - -void common_update_end_values(DummyBuilder& builder, - PrivateCallData const& private_call, - KernelCircuitPublicInputs& public_inputs) -{ - const auto private_call_public_inputs = private_call.call_stack_item.public_inputs; - - const auto& read_requests = private_call_public_inputs.read_requests; - const auto& read_request_membership_witnesses = private_call.read_request_membership_witnesses; - - - const auto& new_commitments = private_call_public_inputs.new_commitments; - const auto& new_nullifiers = private_call_public_inputs.new_nullifiers; - const auto& nullified_commitments = private_call_public_inputs.nullified_commitments; - - const auto& is_static_call = private_call_public_inputs.call_context.is_static_call; - - if (is_static_call) { - // No state changes are allowed for static calls: - builder.do_assert(is_array_empty(new_commitments) == true, - "new_commitments must be empty for static calls", - CircuitErrorCode::PRIVATE_KERNEL__NEW_COMMITMENTS_PROHIBITED_IN_STATIC_CALL); - builder.do_assert(is_array_empty(new_nullifiers) == true, - "new_nullifiers must be empty for static calls", - CircuitErrorCode::PRIVATE_KERNEL__NEW_NULLIFIERS_PROHIBITED_IN_STATIC_CALL); - } - - const auto& storage_contract_address = private_call_public_inputs.call_context.storage_contract_address; - - // Transient read requests and witnesses are accumulated in public_inputs.end - // We silo the read requests (domain separation per contract address) - { - for (size_t i = 0; i < read_requests.size(); ++i) { - const auto& read_request = read_requests[i]; - const auto& witness = read_request_membership_witnesses[i]; - if (witness.is_transient) { // only forward transient to public inputs - // TODO (David): This is pushing zeroed read requests for public inputs if they are transient. Is that - // correct? - const auto siloed_read_request = - read_request == 0 ? 0 : silo_commitment(storage_contract_address, read_request); - array_push(builder, - public_inputs.end.read_requests, - siloed_read_request, - format(PRIVATE_KERNEL_CIRCUIT_ERROR_MESSAGE_BEGINNING, - "too many transient read requests in one tx")); - } - } - } - - // Enhance commitments and nullifiers with domain separation whereby domain is the contract. - { - // nullifiers - std::array siloed_new_nullifiers{}; - for (size_t i = 0; i < MAX_NEW_NULLIFIERS_PER_CALL; ++i) { - siloed_new_nullifiers[i] = - new_nullifiers[i] == 0 ? 0 : silo_nullifier(storage_contract_address, new_nullifiers[i]); - } - push_array_to_array( - builder, - siloed_new_nullifiers, - public_inputs.end.new_nullifiers, - format(PRIVATE_KERNEL_CIRCUIT_ERROR_MESSAGE_BEGINNING, "too many new nullifiers in one tx")); - - // commitments - std::array siloed_new_commitments{}; - for (size_t i = 0; i < new_commitments.size(); ++i) { - siloed_new_commitments[i] = - new_commitments[i] == 0 ? 0 : silo_commitment(storage_contract_address, new_commitments[i]); - } - push_array_to_array( - builder, - siloed_new_commitments, - public_inputs.end.new_commitments, - format(PRIVATE_KERNEL_CIRCUIT_ERROR_MESSAGE_BEGINNING, "too many new commitments in one tx")); - - // nullified commitments (for matching transient nullifiers to transient commitments) - // Since every new_nullifiers entry is paired with a nullified_commitment, EMPTY - // is used here for nullified_commitments of persistable nullifiers. EMPTY will still - // take up a slot in the nullified_commitments array so that the array lines up properly - // with new_nullifiers. This is necessary since the constant-size circuit-array functions - // we use assume that the first 0-valued array entry designates the end of the array. - std::array siloed_nullified_commitments{}; - for (size_t i = 0; i < MAX_NEW_NULLIFIERS_PER_CALL; ++i) { - siloed_nullified_commitments[i] = - nullified_commitments[i] == fr(0) ? fr(0) // don't silo when empty - : nullified_commitments[i] == fr(EMPTY_NULLIFIED_COMMITMENT) - ? fr(EMPTY_NULLIFIED_COMMITMENT) // don't silo when empty - : silo_commitment(storage_contract_address, nullified_commitments[i]); - } - - push_array_to_array( - builder, - siloed_nullified_commitments, - public_inputs.end.nullified_commitments, - format(PRIVATE_KERNEL_CIRCUIT_ERROR_MESSAGE_BEGINNING, "too many new nullified commitments in one tx")); - } - - { // call stacks - const auto& this_private_call_stack = private_call_public_inputs.private_call_stack; - push_array_to_array( - builder, - this_private_call_stack, - public_inputs.end.private_call_stack, - format(PRIVATE_KERNEL_CIRCUIT_ERROR_MESSAGE_BEGINNING, "too many private call stacks in one tx")); - - const auto& this_public_call_stack = private_call_public_inputs.public_call_stack; - push_array_to_array( - builder, - this_public_call_stack, - public_inputs.end.public_call_stack, - format(PRIVATE_KERNEL_CIRCUIT_ERROR_MESSAGE_BEGINNING, "too many public call stacks in one tx")); - } - - { // new l2 to l1 messages - const auto& portal_contract_address = private_call.portal_contract_address; - const auto& new_l2_to_l1_msgs = private_call_public_inputs.new_l2_to_l1_msgs; - std::array new_l2_to_l1_msgs_to_insert{}; - for (size_t i = 0; i < new_l2_to_l1_msgs.size(); ++i) { - if (!new_l2_to_l1_msgs[i].is_zero()) { - new_l2_to_l1_msgs_to_insert[i] = compute_l2_to_l1_hash(storage_contract_address, - private_call_public_inputs.version, - portal_contract_address, - private_call_public_inputs.chain_id, - new_l2_to_l1_msgs[i]); - } - } - push_array_to_array( - builder, - new_l2_to_l1_msgs_to_insert, - public_inputs.end.new_l2_to_l1_msgs, - format(PRIVATE_KERNEL_CIRCUIT_ERROR_MESSAGE_BEGINNING, "too many new l2 to l1 messages in one tx")); - } - - { // logs hashes - // See the following thread if not clear: - // https://discourse.aztec.network/t/proposal-forcing-the-sequencer-to-actually-submit-data-to-l1/426 - const auto& previous_encrypted_logs_hash = public_inputs.end.encrypted_logs_hash; - const auto& current_encrypted_logs_hash = private_call_public_inputs.encrypted_logs_hash; - public_inputs.end.encrypted_logs_hash = accumulate_sha256({ previous_encrypted_logs_hash[0], - previous_encrypted_logs_hash[1], - current_encrypted_logs_hash[0], - current_encrypted_logs_hash[1] }); - - const auto& previous_unencrypted_logs_hash = public_inputs.end.unencrypted_logs_hash; - const auto& current_unencrypted_logs_hash = private_call_public_inputs.unencrypted_logs_hash; - public_inputs.end.unencrypted_logs_hash = accumulate_sha256({ previous_unencrypted_logs_hash[0], - previous_unencrypted_logs_hash[1], - current_unencrypted_logs_hash[0], - current_unencrypted_logs_hash[1] }); - - // Add log preimages lengths from current iteration to accumulated lengths - public_inputs.end.encrypted_log_preimages_length = public_inputs.end.encrypted_log_preimages_length + - private_call_public_inputs.encrypted_log_preimages_length; - public_inputs.end.unencrypted_log_preimages_length = - public_inputs.end.unencrypted_log_preimages_length + - private_call_public_inputs.unencrypted_log_preimages_length; - } -} - -void common_contract_logic(DummyBuilder& builder, - PrivateCallData const& private_call, - KernelCircuitPublicInputs& public_inputs, - ContractDeploymentData const& contract_dep_data, - FunctionData const& function_data) -{ - const auto private_call_public_inputs = private_call.call_stack_item.public_inputs; - const auto& storage_contract_address = private_call_public_inputs.call_context.storage_contract_address; - const auto& portal_contract_address = private_call.portal_contract_address; - - // TODO(#3062) VKs are mocked out for now - // const auto private_call_vk_hash = stdlib::recursion::verification_key::hash_native(private_call.vk); - const auto private_call_vk_hash = 0; - - const auto is_contract_deployment = public_inputs.constants.tx_context.is_contract_deployment_tx; - - if (is_contract_deployment) { - auto constructor_hash = - compute_constructor_hash(function_data, private_call_public_inputs.args_hash, private_call_vk_hash); - - auto const new_contract_address = CompleteAddress::compute(contract_dep_data.deployer_public_key, - contract_dep_data.contract_address_salt, - contract_dep_data.function_tree_root, - constructor_hash) - .address; - - // Add new contract data if its a contract deployment function - NewContractData const native_new_contract_data{ new_contract_address, - portal_contract_address, - contract_dep_data.function_tree_root }; - - array_push(builder, - public_inputs.end.new_contracts, - native_new_contract_data, - format(PRIVATE_KERNEL_CIRCUIT_ERROR_MESSAGE_BEGINNING, "too many contracts created in one tx")); - - // TODO(#3062) VKs are mocked out for now - // builder.do_assert(contract_dep_data.constructor_vk_hash == private_call_vk_hash, - // "constructor_vk_hash doesn't match private_call_vk_hash", - // CircuitErrorCode::PRIVATE_KERNEL__INVALID_CONSTRUCTOR_VK_HASH); - - // must imply == derived address - builder.do_assert(storage_contract_address == new_contract_address, - "contract address supplied doesn't match derived address", - CircuitErrorCode::PRIVATE_KERNEL__INVALID_CONTRACT_ADDRESS); - - // compute contract address nullifier - auto const blake_input = new_contract_address.to_field().to_buffer(); - auto const new_contract_address_nullifier = NT::fr::serialize_from_buffer(NT::blake2s(blake_input).data()); - - // push the contract address nullifier to nullifier vector - array_push(builder, - public_inputs.end.new_nullifiers, - new_contract_address_nullifier, - format(PRIVATE_KERNEL_CIRCUIT_ERROR_MESSAGE_BEGINNING, - "too many nullifiers in one tx to add the new contract address")); - } else { - // non-contract deployments must specify contract address being interacted with - builder.do_assert(storage_contract_address != 0, - "contract address can't be 0 for non-contract deployment related transactions", - CircuitErrorCode::PRIVATE_KERNEL__INVALID_CONTRACT_ADDRESS); - - /* We need to compute the root of the contract tree, starting from the function's VK: - * - Compute the vk_hash (done above) - * - Compute the function_leaf: hash(function_selector, is_internal, is_private, vk_hash, acir_hash) - * - Hash the function_leaf with the function_leaf's sibling_path to get the function_tree_root - * - Compute the contract_leaf: hash(contract_address, portal_contract_address, function_tree_root) - * - Hash the contract_leaf with the contract_leaf's sibling_path to get the contract_tree_root - */ - - // Ensures that if the function is internal, only the contract itself can call it - if (private_call.call_stack_item.function_data.is_internal) { - builder.do_assert( - storage_contract_address == private_call.call_stack_item.public_inputs.call_context.msg_sender, - "call is internal, but msg_sender is not self", - CircuitErrorCode::PRIVATE_KERNEL__IS_INTERNAL_BUT_NOT_SELF_CALL); - } - - // The logic below ensures that the contract exists in the contracts tree - auto const& computed_function_tree_root = - function_tree_root_from_siblings(private_call.call_stack_item.function_data.selector, - private_call.call_stack_item.function_data.is_internal, - true, // is_private - private_call_vk_hash, - private_call.acir_hash, - private_call.function_leaf_membership_witness.leaf_index, - private_call.function_leaf_membership_witness.sibling_path); - - auto const& computed_contract_tree_root = - contract_tree_root_from_siblings(computed_function_tree_root, - storage_contract_address, - portal_contract_address, - private_call.contract_leaf_membership_witness.leaf_index, - private_call.contract_leaf_membership_witness.sibling_path); - - auto const& purported_contract_tree_root = - private_call.call_stack_item.public_inputs.block_header.contract_tree_root; - - builder.do_assert( - computed_contract_tree_root == purported_contract_tree_root, - "computed_contract_tree_root doesn't match purported_contract_tree_root", - CircuitErrorCode::PRIVATE_KERNEL__COMPUTED_CONTRACT_TREE_ROOT_AND_PURPORTED_CONTRACT_TREE_ROOT_MISMATCH); - } -} - -} // namespace aztec3::circuits::kernel::private_kernel diff --git a/circuits/cpp/src/aztec3/circuits/kernel/private/common.hpp b/circuits/cpp/src/aztec3/circuits/kernel/private/common.hpp deleted file mode 100644 index 2e1191a252f..00000000000 --- a/circuits/cpp/src/aztec3/circuits/kernel/private/common.hpp +++ /dev/null @@ -1,81 +0,0 @@ -#pragma once - -#include "init.hpp" - -#include "aztec3/circuits/abis/combined_accumulated_data.hpp" -#include "aztec3/circuits/abis/contract_deployment_data.hpp" -#include "aztec3/circuits/abis/function_data.hpp" -#include "aztec3/circuits/abis/kernel_circuit_public_inputs.hpp" -#include "aztec3/circuits/abis/previous_kernel_data.hpp" -#include "aztec3/circuits/abis/private_circuit_public_inputs.hpp" -#include "aztec3/circuits/abis/private_kernel/private_call_data.hpp" -#include "aztec3/circuits/abis/read_request_membership_witness.hpp" -#include "aztec3/utils/dummy_circuit_builder.hpp" - - -namespace aztec3::circuits::kernel::private_kernel { - -using aztec3::circuits::abis::CombinedAccumulatedData; -using aztec3::circuits::abis::ContractDeploymentData; -using aztec3::circuits::abis::FunctionData; -using aztec3::circuits::abis::KernelCircuitPublicInputs; -using aztec3::circuits::abis::PreviousKernelData; -using aztec3::circuits::abis::PrivateCircuitPublicInputs; -using aztec3::circuits::abis::ReadRequestMembershipWitness; -using aztec3::circuits::abis::private_kernel::PrivateCallData; - -using DummyBuilder = aztec3::utils::DummyCircuitBuilder; - - -// TODO(suyash): Add comments to these as well as other functions in PKC-init. -void common_validate_call_stack(DummyBuilder& builder, PrivateCallData const& private_call); - -void common_validate_read_requests(DummyBuilder& builder, - NT::fr const& historical_note_hash_tree_root, - std::array const& read_requests, - std::array, - MAX_READ_REQUESTS_PER_CALL> const& read_request_membership_witnesses); - -void common_validate_arrays(DummyBuilder& builder, PrivateCircuitPublicInputs const& app_public_inputs); -void common_validate_previous_kernel_arrays(DummyBuilder& builder, CombinedAccumulatedData const& end); -void common_validate_previous_kernel_values(DummyBuilder& builder, CombinedAccumulatedData const& end); -void common_validate_previous_kernel_0th_nullifier(DummyBuilder& builder, CombinedAccumulatedData const& end); - -void common_update_end_values(DummyBuilder& builder, - PrivateCallData const& private_call, - KernelCircuitPublicInputs& public_inputs); - -void common_contract_logic(DummyBuilder& builder, - PrivateCallData const& private_call, - KernelCircuitPublicInputs& public_inputs, - ContractDeploymentData const& contract_dep_data, - FunctionData const& function_data); - -template -void common_initialize_end_values(PreviousKernelData const& previous_kernel, KernelPublicInputs& public_inputs) -{ - public_inputs.constants = previous_kernel.public_inputs.constants; - - // Ensure the arrays are the same as previously, before we start pushing more data onto them in other - // functions within this circuit: - auto& end = public_inputs.end; - const auto& start = previous_kernel.public_inputs.end; - - end.new_commitments = start.new_commitments; - end.new_nullifiers = start.new_nullifiers; - end.nullified_commitments = start.nullified_commitments; - - end.private_call_stack = start.private_call_stack; - end.public_call_stack = start.public_call_stack; - end.new_l2_to_l1_msgs = start.new_l2_to_l1_msgs; - - end.encrypted_logs_hash = start.encrypted_logs_hash; - end.unencrypted_logs_hash = start.unencrypted_logs_hash; - - end.encrypted_log_preimages_length = start.encrypted_log_preimages_length; - end.unencrypted_log_preimages_length = start.unencrypted_log_preimages_length; - - end.optionally_revealed_data = start.optionally_revealed_data; -} - -} // namespace aztec3::circuits::kernel::private_kernel \ No newline at end of file diff --git a/circuits/cpp/src/aztec3/circuits/kernel/private/fixtures/ultra_plonk_proof.dat b/circuits/cpp/src/aztec3/circuits/kernel/private/fixtures/ultra_plonk_proof.dat deleted file mode 100644 index e602b755deb85c1ba4dfa93c564448e0a172cbbc..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 3520 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 QMfSB_jXc_%Tmu092Z4nOP5=M^ diff --git a/circuits/cpp/src/aztec3/circuits/kernel/private/index.hpp b/circuits/cpp/src/aztec3/circuits/kernel/private/index.hpp deleted file mode 100644 index 2e351895bba..00000000000 --- a/circuits/cpp/src/aztec3/circuits/kernel/private/index.hpp +++ /dev/null @@ -1,5 +0,0 @@ -#include "init.hpp" -#include "native_private_kernel_circuit_init.hpp" -#include "native_private_kernel_circuit_inner.hpp" -#include "native_private_kernel_circuit_ordering.hpp" -#include "private_kernel_circuit.hpp" \ No newline at end of file diff --git a/circuits/cpp/src/aztec3/circuits/kernel/private/init.hpp b/circuits/cpp/src/aztec3/circuits/kernel/private/init.hpp deleted file mode 100644 index 62131e524f8..00000000000 --- a/circuits/cpp/src/aztec3/circuits/kernel/private/init.hpp +++ /dev/null @@ -1,31 +0,0 @@ -#pragma once -#include "aztec3/circuits/apps/function_execution_context.hpp" -#include "aztec3/circuits/apps/oracle_wrapper.hpp" -#include "aztec3/circuits/recursion/aggregator.hpp" -#include "aztec3/oracle/oracle.hpp" -#include "aztec3/utils/types/circuit_types.hpp" -#include "aztec3/utils/types/convert.hpp" -#include "aztec3/utils/types/native_types.hpp" - -namespace aztec3::circuits::kernel::private_kernel { - -using Builder = UltraCircuitBuilder; - -using Aggregator = aztec3::circuits::recursion::Aggregator; - -// Generic: -using CT = aztec3::utils::types::CircuitTypes; -using NT = aztec3::utils::types::NativeTypes; -using aztec3::utils::types::to_ct; - -using DB = oracle::FakeDB; -using oracle::NativeOracle; -using OracleWrapper = aztec3::circuits::apps::OracleWrapperInterface; - -using FunctionExecutionContext = aztec3::circuits::apps::FunctionExecutionContext; - -// Used when calling library functions like `push_array` which have their own generic error code. -// So we pad this in front of the error message to identify where the error originally came from. -const std::string PRIVATE_KERNEL_CIRCUIT_ERROR_MESSAGE_BEGINNING = "private_kernel_circuit: "; - -} // namespace aztec3::circuits::kernel::private_kernel \ No newline at end of file diff --git a/circuits/cpp/src/aztec3/circuits/kernel/private/native_private_kernel_circuit.test.cpp b/circuits/cpp/src/aztec3/circuits/kernel/private/native_private_kernel_circuit.test.cpp deleted file mode 100644 index c2e31a04af1..00000000000 --- a/circuits/cpp/src/aztec3/circuits/kernel/private/native_private_kernel_circuit.test.cpp +++ /dev/null @@ -1,221 +0,0 @@ -#include "testing_harness.hpp" - -#include "aztec3/circuits/abis/private_kernel/private_kernel_inputs_init.hpp" -#include "aztec3/circuits/abis/private_kernel/private_kernel_inputs_ordering.hpp" -#include "aztec3/circuits/abis/read_request_membership_witness.hpp" -#include "aztec3/circuits/apps/test_apps/escrow/deposit.hpp" -#include "aztec3/circuits/kernel/private/common.hpp" -#include "aztec3/circuits/kernel/private/init.hpp" -#include "aztec3/constants.hpp" -#include "aztec3/utils/circuit_errors.hpp" - -#include "barretenberg/ecc/curves/bn254/fr.hpp" -#include - -#include - -#include -#include - -namespace aztec3::circuits::kernel::private_kernel { - -using aztec3::circuits::apps::test_apps::escrow::deposit; - -using abis::private_kernel::PrivateKernelInputsOrdering; -using aztec3::circuits::kernel::private_kernel::testing_harness::do_private_call_get_kernel_inputs_init; -using aztec3::circuits::kernel::private_kernel::testing_harness::do_private_call_get_kernel_inputs_inner; -using aztec3::utils::array_length; -using aztec3::utils::CircuitErrorCode; - - -// TODO(https://github.com/AztecProtocol/aztec-packages/issues/892): test expected kernel failures if transient -// reads (or their hints) don't match -// TODO(https://github.com/AztecProtocol/aztec-packages/issues/836): test expected kernel failures if nullifiers (or -// their hints) don't match - -/************************************************************** - * MULTI ITERATION UNIT TESTS FOR NATIVE PRIVATE KERNEL CIRCUIT - **************************************************************/ - - -// NOTE: *DO NOT* call fr constructors in static initializers and assign them to constants. This will fail. Instead, -// use lazy initialization or functions. Lambdas were introduced here. amount = 5, asset_id = 1, memo = 999 -const auto standard_test_args = [] { return std::vector{ NT::fr(5), NT::fr(1), NT::fr(999) }; }; -class native_private_kernel_tests : public ::testing::Test { - protected: - static void SetUpTestSuite() { barretenberg::srs::init_crs_factory("../barretenberg/cpp/srs_db/ignition"); } -}; - - -// 1. We send transient read request on value 23 and pending commitment 12 -// 2. We send transient read request on value 12 and pending commitment 23 -// We expect both read requests and commitments to be successfully matched in ordering circuit. -TEST_F(native_private_kernel_tests, native_accumulate_transient_read_requests) -{ - auto private_inputs_init = do_private_call_get_kernel_inputs_init(false, deposit, standard_test_args()); - - private_inputs_init.private_call.call_stack_item.public_inputs.new_commitments[0] = fr(12); - private_inputs_init.private_call.call_stack_item.public_inputs.read_requests[0] = fr(23); - private_inputs_init.private_call.read_request_membership_witnesses[0].is_transient = true; - - std::array read_commitment_hints{}; - read_commitment_hints[0] = fr(1); - - DummyBuilder builder = DummyBuilder("native_private_kernel_tests__native_accumulate_transient_read_requests"); - auto public_inputs = native_private_kernel_circuit_initial(builder, private_inputs_init); - - ASSERT_FALSE(builder.failed()) << "failure: " << builder.get_first_failure() - << " with code: " << builder.get_first_failure().code; - ASSERT_TRUE(array_length(public_inputs.end.new_commitments) == 1); - ASSERT_TRUE(array_length(public_inputs.end.read_requests) == 1); - - auto private_inputs_inner = do_private_call_get_kernel_inputs_inner(false, deposit, standard_test_args()); - - private_inputs_inner.private_call.call_stack_item.public_inputs.new_commitments[0] = fr(23); - private_inputs_inner.private_call.call_stack_item.public_inputs.read_requests[0] = fr(12); - private_inputs_inner.private_call.read_request_membership_witnesses[0].is_transient = true; - - read_commitment_hints[1] = fr(0); - - // We need to update the previous_kernel's private_call_stack because the current_call_stack_item has changed - // i.e. we changed the new_commitments and read_requests of the current_call_stack_item's public_inputs - private_inputs_inner.previous_kernel.public_inputs.end.private_call_stack[0] = - private_inputs_inner.private_call.call_stack_item.hash(); - - // The original call is not multi-iterative (call stack depth == 1) and we re-feed the same private call stack - public_inputs.end.private_call_stack = private_inputs_inner.previous_kernel.public_inputs.end.private_call_stack; - private_inputs_inner.previous_kernel.public_inputs = public_inputs; - - public_inputs = native_private_kernel_circuit_inner(builder, private_inputs_inner); - - ASSERT_FALSE(builder.failed()) << "failure: " << builder.get_first_failure() - << " with code: " << builder.get_first_failure().code; - ASSERT_TRUE(array_length(public_inputs.end.new_commitments) == 2); - ASSERT_TRUE(array_length(public_inputs.end.read_requests) == 2); - - auto& previous_kernel = private_inputs_inner.previous_kernel; - previous_kernel.public_inputs = public_inputs; - - PrivateKernelInputsOrdering private_inputs{ previous_kernel, read_commitment_hints }; - auto final_public_inputs = native_private_kernel_circuit_ordering(builder, private_inputs); - - ASSERT_FALSE(builder.failed()) << "failure: " << builder.get_first_failure() - << " with code: " << builder.get_first_failure().code; - ASSERT_TRUE(array_length(final_public_inputs.end.new_commitments) == 2); // no commitments squashed -} - -// 1. We send transient read request on value 23 and pending commitment 10 -// 2. We send transient read request on value 12 and pending commitment 23 -// We expect the read request on value 12 to fail as there is no corresponding pending commitment. -TEST_F(native_private_kernel_tests, native_transient_read_requests_no_match) -{ - auto private_inputs_init = do_private_call_get_kernel_inputs_init(false, deposit, standard_test_args()); - - private_inputs_init.private_call.call_stack_item.public_inputs.new_commitments[0] = fr(10); - private_inputs_init.private_call.call_stack_item.public_inputs.read_requests[0] = fr(23); - private_inputs_init.private_call.read_request_membership_witnesses[0].is_transient = true; - - std::array read_commitment_hints{}; - read_commitment_hints[0] = fr(1); - - DummyBuilder builder = DummyBuilder("native_private_kernel_tests__native_transient_read_requests_no_match"); - auto public_inputs = native_private_kernel_circuit_initial(builder, private_inputs_init); - - ASSERT_FALSE(builder.failed()) << "failure: " << builder.get_first_failure() - << " with code: " << builder.get_first_failure().code; - ASSERT_TRUE(array_length(public_inputs.end.new_commitments) == 1); - ASSERT_TRUE(array_length(public_inputs.end.read_requests) == 1); - - auto private_inputs_inner = do_private_call_get_kernel_inputs_inner(false, deposit, standard_test_args()); - - private_inputs_inner.private_call.call_stack_item.public_inputs.new_commitments[0] = fr(23); - private_inputs_inner.private_call.call_stack_item.public_inputs.read_requests[0] = fr(12); - private_inputs_inner.private_call.read_request_membership_witnesses[0].is_transient = true; - - read_commitment_hints[1] = fr(0); // There is not correct possible value. - - // We need to update the previous_kernel's private_call_stack because the current_call_stack_item has changed - // i.e. we changed the new_commitments and read_requests of the current_call_stack_item's public_inputs - private_inputs_inner.previous_kernel.public_inputs.end.private_call_stack[0] = - private_inputs_inner.private_call.call_stack_item.hash(); - - // The original call is not multi-iterative (call stack depth == 1) and we re-feed the same private call stack - public_inputs.end.private_call_stack = private_inputs_inner.previous_kernel.public_inputs.end.private_call_stack; - private_inputs_inner.previous_kernel.public_inputs = public_inputs; - - public_inputs = native_private_kernel_circuit_inner(builder, private_inputs_inner); - - ASSERT_FALSE(builder.failed()) << "failure: " << builder.get_first_failure() - << " with code: " << builder.get_first_failure().code; - ASSERT_TRUE(array_length(public_inputs.end.new_commitments) == 2); - ASSERT_TRUE(array_length(public_inputs.end.read_requests) == 2); - - auto& previous_kernel = private_inputs_inner.previous_kernel; - previous_kernel.public_inputs = public_inputs; - - PrivateKernelInputsOrdering private_inputs{ previous_kernel, read_commitment_hints }; - auto final_public_inputs = native_private_kernel_circuit_ordering(builder, private_inputs); - - ASSERT_TRUE(builder.failed()); - ASSERT_TRUE(builder.get_first_failure().code == CircuitErrorCode::PRIVATE_KERNEL__TRANSIENT_READ_REQUEST_NO_MATCH); - - ASSERT_TRUE(array_length(final_public_inputs.end.new_commitments) == 2); // no commitments squashed -} - -// Testing that the special value EMPTY_NULLIFIED_COMMITMENT keeps new_nullifiers aligned with nullified_commitments. -TEST_F(native_private_kernel_tests, native_empty_nullified_commitment_respected) -{ - // The 0th nullifier (and corresponding nullified_commitment EMPTY_NULLIFIED_COMMITMENT) which is - // added by the init iteration is set into private_inputs_inner. - auto private_inputs_inner = do_private_call_get_kernel_inputs_inner(false, deposit, standard_test_args()); - - private_inputs_inner.private_call.call_stack_item.public_inputs.new_commitments[0] = fr(23); - private_inputs_inner.private_call.call_stack_item.public_inputs.new_commitments[1] = fr(33); - - private_inputs_inner.private_call.call_stack_item.public_inputs.new_nullifiers[0] = fr(11); - private_inputs_inner.private_call.call_stack_item.public_inputs.new_nullifiers[1] = fr(18); - - private_inputs_inner.private_call.call_stack_item.public_inputs.nullified_commitments[0] = - fr(EMPTY_NULLIFIED_COMMITMENT); - private_inputs_inner.private_call.call_stack_item.public_inputs.nullified_commitments[1] = fr(33); - - // update the private call stack contents to reflect the above changes which affect the item hash - private_inputs_inner.previous_kernel.public_inputs.end.private_call_stack[0] = - private_inputs_inner.private_call.call_stack_item.hash(); - - DummyBuilder builder = DummyBuilder("native_private_kernel_tests__native_empty_nullified_commitment_respected"); - - auto public_inputs = native_private_kernel_circuit_inner(builder, private_inputs_inner); - - ASSERT_FALSE(builder.failed()) << "failure: " << builder.get_first_failure() - << " with code: " << builder.get_first_failure().code; - - // EMPTY nullified commitment should keep new_nullifiers aligned with nullified_commitments - // We have to shift the offset by 1 due to the 0th nullifier/nullified_commitment pair. - ASSERT_TRUE(public_inputs.end.nullified_commitments[1] == fr(EMPTY_NULLIFIED_COMMITMENT)); - ASSERT_TRUE(public_inputs.end.nullified_commitments[2] != fr(0) && - public_inputs.end.nullified_commitments[2] != fr(EMPTY_NULLIFIED_COMMITMENT)); - - // Nothing squashed yet (until ordering circuit) - ASSERT_TRUE(array_length(public_inputs.end.new_nullifiers) == 3); // 0th nullifier to be taking into account - ASSERT_TRUE(array_length(public_inputs.end.new_commitments) == 2); - // explicitly EMPTY commitment respected in array - ASSERT_TRUE(array_length(public_inputs.end.nullified_commitments) == 3); - - auto& previous_kernel = private_inputs_inner.previous_kernel; - previous_kernel.public_inputs = public_inputs; - - PrivateKernelInputsOrdering private_inputs{ .previous_kernel = previous_kernel, - .nullifier_commitment_hints = - std::array{ 0, 0, 1 } }; - - auto final_public_inputs = native_private_kernel_circuit_ordering(builder, private_inputs); - - ASSERT_FALSE(builder.failed()) << "failure: " << builder.get_first_failure() - << " with code: " << builder.get_first_failure().code; - - ASSERT_TRUE(array_length(final_public_inputs.end.new_commitments) == 1); // 1/2 commitment squashed - ASSERT_TRUE(array_length(final_public_inputs.end.new_nullifiers) == 2); // 1/2 nullifier squashed (+ 0th nullifier) -} - -} // namespace aztec3::circuits::kernel::private_kernel diff --git a/circuits/cpp/src/aztec3/circuits/kernel/private/native_private_kernel_circuit_init.cpp b/circuits/cpp/src/aztec3/circuits/kernel/private/native_private_kernel_circuit_init.cpp deleted file mode 100644 index 9c9c0fa1037..00000000000 --- a/circuits/cpp/src/aztec3/circuits/kernel/private/native_private_kernel_circuit_init.cpp +++ /dev/null @@ -1,192 +0,0 @@ -#include "common.hpp" -#include "init.hpp" - -#include "aztec3/circuits/abis/block_header.hpp" -#include "aztec3/circuits/abis/combined_constant_data.hpp" -#include "aztec3/circuits/abis/private_kernel/private_kernel_inputs_init.hpp" -#include "aztec3/constants.hpp" -#include "aztec3/utils/array.hpp" - - -namespace { -using NT = aztec3::utils::types::NativeTypes; - -using aztec3::circuits::abis::CombinedConstantData; -using aztec3::circuits::abis::KernelCircuitPublicInputs; -using aztec3::circuits::abis::private_kernel::PrivateKernelInputsInit; -using aztec3::utils::array_push; -using aztec3::utils::CircuitErrorCode; -using aztec3::utils::DummyCircuitBuilder; -using aztec3::utils::is_array_empty; - - -void initialize_end_values(PrivateKernelInputsInit const& private_inputs, - KernelCircuitPublicInputs& public_inputs) -{ - // Define the constants data. - auto const& private_call_public_inputs = private_inputs.private_call.call_stack_item.public_inputs; - auto const constants = CombinedConstantData{ - .block_header = private_call_public_inputs.block_header, - .tx_context = private_inputs.tx_request.tx_context, - }; - - // Set the constants in public_inputs. - public_inputs.constants = constants; -} -} // namespace - -namespace aztec3::circuits::kernel::private_kernel { - -void validate_this_private_call_against_tx_request(DummyCircuitBuilder& builder, - PrivateKernelInputsInit const& private_inputs) -{ - // TODO(mike): this logic might need to change to accommodate the weird edge 3 initial txs (the 'main' tx, the 'fee' - // tx, and the 'gas rebate' tx). - - // Confirm that the TxRequest (user's intent) matches the private call being executed - const auto& tx_request = private_inputs.tx_request; - const auto& call_stack_item = private_inputs.private_call.call_stack_item; - - builder.do_assert(tx_request.origin == call_stack_item.contract_address, - "user's intent does not match initial private call (origin address of tx_request must match " - "call_stack_item's contract_address)", - CircuitErrorCode::PRIVATE_KERNEL__USER_INTENT_MISMATCH_BETWEEN_TX_REQUEST_AND_CALL_STACK_ITEM); - - builder.do_assert(tx_request.function_data.hash() == call_stack_item.function_data.hash(), - "user's intent does not match initial private call (tx_request.function_data must match " - "call_stack_item.function_data)", - CircuitErrorCode::PRIVATE_KERNEL__USER_INTENT_MISMATCH_BETWEEN_TX_REQUEST_AND_CALL_STACK_ITEM); - - builder.do_assert( - tx_request.args_hash == call_stack_item.public_inputs.args_hash, - "user's intent does not match initial private call (noir function args passed to tx_request must match " - "args in the call_stack_item)", - CircuitErrorCode::PRIVATE_KERNEL__USER_INTENT_MISMATCH_BETWEEN_TX_REQUEST_AND_CALL_STACK_ITEM); -}; - -void validate_inputs(DummyCircuitBuilder& builder, PrivateKernelInputsInit const& private_inputs) -{ - const auto& this_call_stack_item = private_inputs.private_call.call_stack_item; - - builder.do_assert(this_call_stack_item.function_data.is_private == true, - "Cannot execute a non-private function with the private kernel circuit", - CircuitErrorCode::PRIVATE_KERNEL__NON_PRIVATE_FUNCTION_EXECUTED_WITH_PRIVATE_KERNEL); - - // TODO(mike): change to allow 3 initial calls on the private call stack, so a fee can be paid and a gas - // rebate can be paid. - - /* If we are going to have 3 initial calls on the private call stack, - * then do we still need the `private_call_stack` - * despite no longer needing a full `previous_kernel` - */ - - builder.do_assert(this_call_stack_item.public_inputs.call_context.is_delegate_call == false, - "Users cannot make a delegatecall", - CircuitErrorCode::PRIVATE_KERNEL__UNSUPPORTED_OP); - builder.do_assert(this_call_stack_item.public_inputs.call_context.is_static_call == false, - "Users cannot make a static call", - CircuitErrorCode::PRIVATE_KERNEL__UNSUPPORTED_OP); - - // The below also prevents delegatecall/staticcall in the base case - builder.do_assert(this_call_stack_item.public_inputs.call_context.storage_contract_address == - this_call_stack_item.contract_address, - "Storage contract address must be that of the called contract", - CircuitErrorCode::PRIVATE_KERNEL__CONTRACT_ADDRESS_MISMATCH); -} - -void update_end_values(DummyCircuitBuilder& builder, - PrivateKernelInputsInit const& private_inputs, - KernelCircuitPublicInputs& public_inputs) -{ - // We only initialized constants member of public_inputs so far. Therefore, there must not be any - // new nullifiers or logs as part of public_inputs. - builder.do_assert(is_array_empty(public_inputs.end.new_commitments), - "public_inputs.end.new_commitments must start as empty in initial kernel iteration", - CircuitErrorCode::PRIVATE_KERNEL__UNSUPPORTED_OP); - builder.do_assert(is_array_empty(public_inputs.end.new_nullifiers), - "public_inputs.end.new_nullifiers must start as empty in initial kernel iteration", - CircuitErrorCode::PRIVATE_KERNEL__UNSUPPORTED_OP); - builder.do_assert(is_array_empty(public_inputs.end.nullified_commitments), - "public_inputs.end.nullified_commitments must start as empty in initial kernel iteration", - CircuitErrorCode::PRIVATE_KERNEL__UNSUPPORTED_OP); - builder.do_assert(is_array_empty(public_inputs.end.encrypted_logs_hash), - "public_inputs.end.encrypted_logs_hash must start as empty in initial kernel iteration", - CircuitErrorCode::PRIVATE_KERNEL__UNSUPPORTED_OP); - builder.do_assert(is_array_empty(public_inputs.end.unencrypted_logs_hash), - "public_inputs.end.unencrypted_logs_hash must start as empty in initial kernel iteration", - CircuitErrorCode::PRIVATE_KERNEL__UNSUPPORTED_OP); - builder.do_assert(is_array_empty(public_inputs.end.read_requests), - "public_inputs.end.read_requests must start as empty in initial kernel iteration", - CircuitErrorCode::PRIVATE_KERNEL__UNSUPPORTED_OP); - builder.do_assert(public_inputs.end.encrypted_log_preimages_length == NT::fr(0), - "public_inputs.end.encrypted_log_preimages_length must start as 0 in initial kernel iteration", - CircuitErrorCode::PRIVATE_KERNEL__UNSUPPORTED_OP); - builder.do_assert(public_inputs.end.unencrypted_log_preimages_length == NT::fr(0), - "public_inputs.end.unencrypted_log_preimages_length must start as 0 in initial kernel iteration", - CircuitErrorCode::PRIVATE_KERNEL__UNSUPPORTED_OP); - - // Since it's the first iteration, we need to push the the tx hash nullifier into the `new_nullifiers` array - array_push(builder, - public_inputs.end.new_nullifiers, - private_inputs.tx_request.hash(), - format(PRIVATE_KERNEL_CIRCUIT_ERROR_MESSAGE_BEGINNING, - "could not push tx hash nullifier into new_nullifiers array. Too many new nullifiers in one tx")); - // Push an empty nullified commitment too since each nullifier must - // be paired with a nonzero (real or "empty") nullified commitment - array_push(builder, - public_inputs.end.nullified_commitments, - NT::fr(EMPTY_NULLIFIED_COMMITMENT), - format(PRIVATE_KERNEL_CIRCUIT_ERROR_MESSAGE_BEGINNING, - "could not push tx hash nullifier into new_nullifiers array. Too many new nullifiers in one tx")); - - // Note that we do not need to nullify the transaction request nonce anymore. - // Should an account want to additionally use nonces for replay protection or handling cancellations, - // they will be able to do so in the account contract logic: - // https://github.com/AztecProtocol/aztec-packages/issues/660 -} - -KernelCircuitPublicInputs native_private_kernel_circuit_initial(DummyCircuitBuilder& builder, - PrivateKernelInputsInit const& private_inputs) -{ - // We'll be pushing data to this during execution of this circuit. - KernelCircuitPublicInputs public_inputs{}; - - // Do this before any functions can modify the inputs. - initialize_end_values(private_inputs, public_inputs); - - validate_inputs(builder, private_inputs); - - common_validate_arrays(builder, private_inputs.private_call.call_stack_item.public_inputs); - - validate_this_private_call_against_tx_request(builder, private_inputs); - - common_validate_call_stack(builder, private_inputs.private_call); - - common_validate_read_requests( - builder, - public_inputs.constants.block_header.note_hash_tree_root, - private_inputs.private_call.call_stack_item.public_inputs.read_requests, // read requests from private call - private_inputs.private_call.read_request_membership_witnesses); - - // TODO(dbanks12): feels like update_end_values should happen after contract logic - update_end_values(builder, private_inputs, public_inputs); - common_update_end_values(builder, private_inputs.private_call, public_inputs); - - common_contract_logic(builder, - private_inputs.private_call, - public_inputs, - private_inputs.tx_request.tx_context.contract_deployment_data, - private_inputs.tx_request.function_data); - - // This is where a real circuit would perform recursive verification of the previous kernel proof and private call - // proof. - - // In the native version, as there is no verify_proofs call, we can initialize aggregation object with the default - // constructor. - NT::AggregationObject const empty_aggregation_object{}; - public_inputs.end.aggregation_object = empty_aggregation_object; - - return public_inputs; -}; - -} // namespace aztec3::circuits::kernel::private_kernel \ No newline at end of file diff --git a/circuits/cpp/src/aztec3/circuits/kernel/private/native_private_kernel_circuit_init.hpp b/circuits/cpp/src/aztec3/circuits/kernel/private/native_private_kernel_circuit_init.hpp deleted file mode 100644 index 8baabb77f1f..00000000000 --- a/circuits/cpp/src/aztec3/circuits/kernel/private/native_private_kernel_circuit_init.hpp +++ /dev/null @@ -1,18 +0,0 @@ -#pragma once - -#include "init.hpp" - -#include "aztec3/circuits/abis/kernel_circuit_public_inputs.hpp" -#include "aztec3/circuits/abis/private_kernel/private_kernel_inputs_init.hpp" -#include "aztec3/utils/dummy_circuit_builder.hpp" - -namespace aztec3::circuits::kernel::private_kernel { - -using aztec3::circuits::abis::KernelCircuitPublicInputs; -using aztec3::circuits::abis::private_kernel::PrivateKernelInputsInit; -using DummyBuilder = aztec3::utils::DummyCircuitBuilder; - -KernelCircuitPublicInputs native_private_kernel_circuit_initial(DummyBuilder& builder, - PrivateKernelInputsInit const& private_inputs); - -} // namespace aztec3::circuits::kernel::private_kernel \ No newline at end of file diff --git a/circuits/cpp/src/aztec3/circuits/kernel/private/native_private_kernel_circuit_init.test.cpp b/circuits/cpp/src/aztec3/circuits/kernel/private/native_private_kernel_circuit_init.test.cpp deleted file mode 100644 index f502436d603..00000000000 --- a/circuits/cpp/src/aztec3/circuits/kernel/private/native_private_kernel_circuit_init.test.cpp +++ /dev/null @@ -1,827 +0,0 @@ -#include "testing_harness.hpp" - -#include "aztec3/circuits/abis/read_request_membership_witness.hpp" -#include "aztec3/circuits/apps/test_apps/basic_contract_deployment/basic_contract_deployment.hpp" -#include "aztec3/circuits/apps/test_apps/escrow/deposit.hpp" -#include "aztec3/circuits/hash.hpp" -#include "aztec3/circuits/kernel/private/init.hpp" -#include "aztec3/constants.hpp" -#include "aztec3/utils/circuit_errors.hpp" - -#include - -#include - -#include -#include - -namespace aztec3::circuits::kernel::private_kernel { - -using aztec3::circuits::silo_nullifier; -using aztec3::circuits::apps::test_apps::basic_contract_deployment::constructor; -using aztec3::circuits::apps::test_apps::escrow::deposit; -using aztec3::circuits::kernel::private_kernel::testing_harness::do_private_call_get_kernel_inputs_init; -using aztec3::circuits::kernel::private_kernel::testing_harness::get_random_reads; -using aztec3::circuits::kernel::private_kernel::testing_harness::validate_deployed_contract_address; -using aztec3::circuits::kernel::private_kernel::testing_harness::validate_no_new_deployed_contract; - -using aztec3::utils::array_length; -using aztec3::utils::CircuitErrorCode; - - -// NOTE: *DO NOT* call fr constructors in static initializers and assign them to constants. This will fail. Instead, use -// lazy initialization or functions. Lambdas were introduced here. -// amount = 5, asset_id = 1, memo = 999 -const auto standard_test_args = [] { return std::vector{ NT::fr(5), NT::fr(1), NT::fr(999) }; }; - -class native_private_kernel_init_tests : public ::testing::Test { - protected: - static void SetUpTestSuite() { barretenberg::srs::init_crs_factory("../barretenberg/cpp/srs_db/ignition"); } -}; - - -/** - ************************************************************** - * Native initial private kernel circuit tests. - ************************************************************** - */ - -/** - * @brief Some private circuit simulation (`deposit`, in this case) - */ -TEST_F(native_private_kernel_init_tests, deposit) -{ - std::array const& encrypted_logs_hash = { NT::fr(16), NT::fr(69) }; - NT::fr const& encrypted_log_preimages_length = NT::fr(100); - std::array const& unencrypted_logs_hash = { NT::fr(26), NT::fr(47) }; - NT::fr const& unencrypted_log_preimages_length = NT::fr(50); - - auto const& private_inputs = do_private_call_get_kernel_inputs_init(false, - deposit, - standard_test_args(), - encrypted_logs_hash, - unencrypted_logs_hash, - encrypted_log_preimages_length, - unencrypted_log_preimages_length); - DummyBuilder builder = DummyBuilder("private_kernel_tests__native_deposit"); - auto const& public_inputs = native_private_kernel_circuit_initial(builder, private_inputs); - - EXPECT_TRUE(validate_no_new_deployed_contract(public_inputs)); - - // Check the first nullifier is hash of the signed tx request - ASSERT_EQ(public_inputs.end.new_nullifiers[0], private_inputs.tx_request.hash()); - - // Log preimages length should increase by `(un)encrypted_log_preimages_length` from private input - ASSERT_EQ(public_inputs.end.encrypted_log_preimages_length, encrypted_log_preimages_length); - ASSERT_EQ(public_inputs.end.unencrypted_log_preimages_length, unencrypted_log_preimages_length); - - // Logs hashes should be a sha256 hash of a 0 value and the `(un)encrypted_logs_hash` from private input - auto const& expected_encrypted_logs_hash = - accumulate_sha256({ fr(0), fr(0), encrypted_logs_hash[0], encrypted_logs_hash[1] }); - ASSERT_EQ(public_inputs.end.encrypted_logs_hash, expected_encrypted_logs_hash); - - auto const& expected_unencrypted_logs_hash = - accumulate_sha256({ fr(0), fr(0), unencrypted_logs_hash[0], unencrypted_logs_hash[1] }); - ASSERT_EQ(public_inputs.end.unencrypted_logs_hash, expected_unencrypted_logs_hash); - - // Assert that builder doesn't give any errors - ASSERT_FALSE(builder.failed()); - EXPECT_EQ(builder.get_first_failure().message, ""); - EXPECT_EQ(builder.get_first_failure().code, CircuitErrorCode::NO_ERROR); -} - -/** - * @brief Some private circuit simulation (`constructor`, in this case) - */ -TEST_F(native_private_kernel_init_tests, basic_contract_deployment) -{ - auto const& private_inputs = do_private_call_get_kernel_inputs_init(true, constructor, standard_test_args()); - DummyBuilder builder = DummyBuilder("private_kernel_tests__native_basic_contract_deployment"); - auto const& public_inputs = native_private_kernel_circuit_initial(builder, private_inputs); - - EXPECT_TRUE(validate_deployed_contract_address(private_inputs, public_inputs)); - - // Since there are no logs, log preimages length should be 0 and both logs hashes should be a sha256 hash of 2 zero - // values - ASSERT_EQ(public_inputs.end.encrypted_log_preimages_length, fr(0)); - ASSERT_EQ(public_inputs.end.unencrypted_log_preimages_length, fr(0)); - - auto const& expected_logs_hash = accumulate_sha256({ fr(0), fr(0), fr(0), fr(0) }); - - ASSERT_EQ(public_inputs.end.encrypted_logs_hash, expected_logs_hash); - ASSERT_EQ(public_inputs.end.unencrypted_logs_hash, expected_logs_hash); - - // Assert that builder doesn't give any errors - ASSERT_FALSE(builder.failed()); - EXPECT_EQ(builder.get_first_failure().message, ""); - EXPECT_EQ(builder.get_first_failure().code, CircuitErrorCode::NO_ERROR); -} - -TEST_F(native_private_kernel_init_tests, input_validation_malformed_arrays_return_values) -{ - auto private_inputs = do_private_call_get_kernel_inputs_init(true, constructor, standard_test_args()); - - std::array malformed_return_values{ fr(0), fr(0), fr(553) }; - private_inputs.private_call.call_stack_item.public_inputs.return_values = malformed_return_values; - - DummyBuilder builder = DummyBuilder("private_kernel_tests__input_validation_malformed_arrays_return_values"); - native_private_kernel_circuit_initial(builder, private_inputs); - - EXPECT_EQ(builder.failed(), true); - EXPECT_EQ(builder.get_first_failure().code, CircuitErrorCode::ARRAY_NOT_ZERO_RIGHT_PADDED); -} - -TEST_F(native_private_kernel_init_tests, input_validation_malformed_arrays_read_requests) -{ - auto private_inputs = do_private_call_get_kernel_inputs_init(true, constructor, standard_test_args()); - - std::array malformed_read_requests{ fr(0), fr(9123), fr(0), fr(12) }; - private_inputs.private_call.call_stack_item.public_inputs.read_requests = malformed_read_requests; - - DummyBuilder builder = DummyBuilder("private_kernel_tests__input_validation_malformed_arrays_read_requests"); - native_private_kernel_circuit_initial(builder, private_inputs); - - EXPECT_EQ(builder.failed(), true); - EXPECT_EQ(builder.get_first_failure().code, CircuitErrorCode::ARRAY_NOT_ZERO_RIGHT_PADDED); -} - -TEST_F(native_private_kernel_init_tests, input_validation_malformed_arrays_commitments) -{ - auto private_inputs = do_private_call_get_kernel_inputs_init(true, constructor, standard_test_args()); - - std::array malformed_commitments{ fr(0), fr(9123) }; - private_inputs.private_call.call_stack_item.public_inputs.new_commitments = malformed_commitments; - - DummyBuilder builder = DummyBuilder("private_kernel_tests__input_validation_malformed_arrays_commitments"); - native_private_kernel_circuit_initial(builder, private_inputs); - - EXPECT_EQ(builder.failed(), true); - EXPECT_EQ(builder.get_first_failure().code, CircuitErrorCode::ARRAY_NOT_ZERO_RIGHT_PADDED); -} - -TEST_F(native_private_kernel_init_tests, input_validation_malformed_arrays_nullifiers) -{ - auto private_inputs = do_private_call_get_kernel_inputs_init(true, constructor, standard_test_args()); - - std::array malformed_nullifiers{}; - malformed_nullifiers[MAX_NEW_NULLIFIERS_PER_CALL - 1] = fr(12); - private_inputs.private_call.call_stack_item.public_inputs.new_nullifiers = malformed_nullifiers; - - DummyBuilder builder = DummyBuilder("private_kernel_tests__input_validation_malformed_arrays_nullifiers"); - native_private_kernel_circuit_initial(builder, private_inputs); - - EXPECT_EQ(builder.failed(), true); - EXPECT_EQ(builder.get_first_failure().code, CircuitErrorCode::ARRAY_NOT_ZERO_RIGHT_PADDED); -} - -TEST_F(native_private_kernel_init_tests, input_validation_malformed_arrays_nullified_commitments) -{ - auto private_inputs = do_private_call_get_kernel_inputs_init(true, constructor, standard_test_args()); - - std::array malformed_nullified_commitments{ fr(0), - fr(0), - EMPTY_NULLIFIED_COMMITMENT }; - private_inputs.private_call.call_stack_item.public_inputs.nullified_commitments = malformed_nullified_commitments; - - DummyBuilder builder = - DummyBuilder("private_kernel_tests__input_validation_malformed_arrays_nullified_commitments"); - native_private_kernel_circuit_initial(builder, private_inputs); - - EXPECT_EQ(builder.failed(), true); - EXPECT_EQ(builder.get_first_failure().code, CircuitErrorCode::ARRAY_NOT_ZERO_RIGHT_PADDED); -} - -TEST_F(native_private_kernel_init_tests, input_validation_malformed_arrays_private_call_stack) -{ - auto private_inputs = do_private_call_get_kernel_inputs_init(true, constructor, standard_test_args()); - - std::array malformed_private_call_stack{ fr(0), fr(888) }; - private_inputs.private_call.call_stack_item.public_inputs.private_call_stack = malformed_private_call_stack; - - DummyBuilder builder = DummyBuilder("private_kernel_tests__input_validation_malformed_arrays_private_call_stack"); - native_private_kernel_circuit_initial(builder, private_inputs); - - EXPECT_EQ(builder.failed(), true); - EXPECT_EQ(builder.get_first_failure().code, CircuitErrorCode::ARRAY_NOT_ZERO_RIGHT_PADDED); -} - -TEST_F(native_private_kernel_init_tests, input_validation_malformed_arrays_public_call_stack) -{ - auto private_inputs = do_private_call_get_kernel_inputs_init(true, constructor, standard_test_args()); - - std::array malformed_public_call_stack{ fr(0), fr(888) }; - private_inputs.private_call.call_stack_item.public_inputs.public_call_stack = malformed_public_call_stack; - - DummyBuilder builder = DummyBuilder("private_kernel_tests__input_validation_malformed_arrays_public_call_stack"); - native_private_kernel_circuit_initial(builder, private_inputs); - - EXPECT_EQ(builder.failed(), true); - EXPECT_EQ(builder.get_first_failure().code, CircuitErrorCode::ARRAY_NOT_ZERO_RIGHT_PADDED); -} - -TEST_F(native_private_kernel_init_tests, input_validation_malformed_arrays_new_l2_to_l1_msgs) -{ - auto private_inputs = do_private_call_get_kernel_inputs_init(true, constructor, standard_test_args()); - - std::array malformed_new_l2_to_l1_msgs{}; - malformed_new_l2_to_l1_msgs[MAX_NEW_L2_TO_L1_MSGS_PER_CALL - 1] = fr(1); - private_inputs.private_call.call_stack_item.public_inputs.new_l2_to_l1_msgs = malformed_new_l2_to_l1_msgs; - - DummyBuilder builder = DummyBuilder("private_kernel_tests__input_validation_malformed_arrays_new_l2_to_l1_msgs"); - native_private_kernel_circuit_initial(builder, private_inputs); - - EXPECT_EQ(builder.failed(), true); - EXPECT_EQ(builder.get_first_failure().code, CircuitErrorCode::ARRAY_NOT_ZERO_RIGHT_PADDED); -} - -TEST_F(native_private_kernel_init_tests, contract_deployment_call_stack_item_hash_mismatch_fails) -{ - auto private_inputs = do_private_call_get_kernel_inputs_init(true, constructor, standard_test_args()); - - // Randomize the first item in the private call stack (i.e. hash of the private call item). - private_inputs.private_call.call_stack_item.public_inputs.private_call_stack[0] = NT::fr::random_element(); - - DummyBuilder builder = - DummyBuilder("private_kernel_tests__contract_deployment_call_stack_item_hash_mismatch_fails"); - native_private_kernel_circuit_initial(builder, private_inputs); - - EXPECT_EQ(builder.failed(), true); - EXPECT_EQ(builder.get_first_failure().code, - CircuitErrorCode::PRIVATE_KERNEL__PRIVATE_CALL_STACK_ITEM_HASH_MISMATCH); -} - -// TODO(#3062) VKs are mocked out for now -// TEST_F(native_private_kernel_init_tests, contract_deployment_incorrect_constructor_vk_hash_fails) -// { -// auto private_inputs = do_private_call_get_kernel_inputs_init(true, constructor, standard_test_args()); - -// // Pollute the constructor vk hash in the tx_request. -// private_inputs.tx_request.tx_context.contract_deployment_data.constructor_vk_hash = NT::fr::random_element(); - -// DummyBuilder builder = -// DummyBuilder("private_kernel_tests__contract_deployment_incorrect_constructor_vk_hash_fails"); -// native_private_kernel_circuit_initial(builder, private_inputs); - -// EXPECT_EQ(builder.failed(), true); -// EXPECT_EQ(builder.get_first_failure().code, CircuitErrorCode::PRIVATE_KERNEL__INVALID_CONSTRUCTOR_VK_HASH); -// EXPECT_EQ(builder.get_first_failure().message, "constructor_vk_hash doesn't match private_call_vk_hash"); -// } - -TEST_F(native_private_kernel_init_tests, contract_deployment_incorrect_contract_address_fails) -{ - auto private_inputs = do_private_call_get_kernel_inputs_init(true, constructor, standard_test_args()); - - // Modify the contract address in appropriate places. - const fr random_address = NT::fr::random_element(); - private_inputs.private_call.call_stack_item.public_inputs.call_context.storage_contract_address = random_address; - private_inputs.tx_request.origin = random_address; - private_inputs.private_call.call_stack_item.contract_address = random_address; - - DummyBuilder builder = DummyBuilder("private_kernel_tests__contract_deployment_incorrect_contract_address_fails"); - native_private_kernel_circuit_initial(builder, private_inputs); - - EXPECT_EQ(builder.failed(), true); - EXPECT_EQ(builder.get_first_failure().code, CircuitErrorCode::PRIVATE_KERNEL__INVALID_CONTRACT_ADDRESS); - EXPECT_EQ(builder.get_first_failure().message, "contract address supplied doesn't match derived address"); -} - -TEST_F(native_private_kernel_init_tests, contract_deployment_contract_address_mismatch_fails) -{ - auto private_inputs = do_private_call_get_kernel_inputs_init(true, constructor, standard_test_args()); - - // Modify the storage_contract_address. - const auto random_contract_address = NT::fr::random_element(); - private_inputs.private_call.call_stack_item.public_inputs.call_context.storage_contract_address = - random_contract_address; - private_inputs.private_call.call_stack_item.contract_address = random_contract_address; - - // Invoke the native private kernel circuit - DummyBuilder builder = DummyBuilder("private_kernel_tests__contract_deployment_contract_address_mismatch_fails"); - native_private_kernel_circuit_initial(builder, private_inputs); - - // Assertion checks - EXPECT_TRUE(builder.failed()); - EXPECT_EQ(builder.get_first_failure().code, - CircuitErrorCode::PRIVATE_KERNEL__USER_INTENT_MISMATCH_BETWEEN_TX_REQUEST_AND_CALL_STACK_ITEM); -} - -TEST_F(native_private_kernel_init_tests, contract_deployment_function_data_mismatch_fails) -{ - auto private_inputs = do_private_call_get_kernel_inputs_init(true, constructor, standard_test_args()); - - // Modify the function selector in function data. - private_inputs.tx_request.function_data.selector = { - .value = numeric::random::get_engine().get_random_uint32(), - }; - - // Invoke the native private kernel circuit - DummyBuilder builder = DummyBuilder("private_kernel_tests__contract_deployment_function_data_mismatch_fails"); - native_private_kernel_circuit_initial(builder, private_inputs); - - // Assertion checks - EXPECT_TRUE(builder.failed()); - EXPECT_EQ(builder.get_first_failure().code, - CircuitErrorCode::PRIVATE_KERNEL__USER_INTENT_MISMATCH_BETWEEN_TX_REQUEST_AND_CALL_STACK_ITEM); - EXPECT_EQ(builder.get_first_failure().message, - "user's intent does not match initial private call (tx_request.function_data must match " - "call_stack_item.function_data)"); -} - -TEST_F(native_private_kernel_init_tests, contract_deployment_args_hash_mismatch_fails) -{ - auto private_inputs = do_private_call_get_kernel_inputs_init(true, constructor, standard_test_args()); - - // Modify the args hash in tx request. - private_inputs.tx_request.args_hash = NT::fr::random_element(); - - // Invoke the native private kernel circuit - DummyBuilder builder = DummyBuilder("private_kernel_tests__contract_deployment_args_hash_mismatch_fails"); - native_private_kernel_circuit_initial(builder, private_inputs); - - // Assertion checks - EXPECT_TRUE(builder.failed()); - EXPECT_EQ(builder.get_first_failure().code, - CircuitErrorCode::PRIVATE_KERNEL__USER_INTENT_MISMATCH_BETWEEN_TX_REQUEST_AND_CALL_STACK_ITEM); - EXPECT_EQ(builder.get_first_failure().message, - "user's intent does not match initial private call (noir function args passed to tx_request must match " - "args in the call_stack_item)"); -} - -TEST_F(native_private_kernel_init_tests, private_function_is_private_false_fails) -{ - auto private_inputs = do_private_call_get_kernel_inputs_init(false, deposit, standard_test_args()); - - // Set is_private in function data to false. - private_inputs.private_call.call_stack_item.function_data.is_private = false; - - // Invoke the native private kernel circuit - DummyBuilder builder = DummyBuilder("private_kernel_tests__private_function_is_private_false_fails"); - native_private_kernel_circuit_initial(builder, private_inputs); - - // Assertion checks - EXPECT_TRUE(builder.failed()); - EXPECT_EQ(builder.get_first_failure().code, - CircuitErrorCode::PRIVATE_KERNEL__NON_PRIVATE_FUNCTION_EXECUTED_WITH_PRIVATE_KERNEL); - EXPECT_EQ(builder.get_first_failure().message, - "Cannot execute a non-private function with the private kernel circuit"); -} - - -TEST_F(native_private_kernel_init_tests, private_function_static_call_fails) -{ - auto private_inputs = do_private_call_get_kernel_inputs_init(false, deposit, standard_test_args()); - - // Set is_static_call to true. - private_inputs.private_call.call_stack_item.public_inputs.call_context.is_static_call = true; - - // Invoke the native private kernel circuit - DummyBuilder builder = DummyBuilder("private_kernel_tests__private_function_static_call_fails"); - native_private_kernel_circuit_initial(builder, private_inputs); - - // Assertion checks - EXPECT_TRUE(builder.failed()); - EXPECT_EQ(builder.get_first_failure().code, CircuitErrorCode::PRIVATE_KERNEL__UNSUPPORTED_OP); - EXPECT_EQ(builder.get_first_failure().message, "Users cannot make a static call"); -} - -TEST_F(native_private_kernel_init_tests, private_function_delegate_call_fails) -{ - auto private_inputs = do_private_call_get_kernel_inputs_init(false, deposit, standard_test_args()); - - // Set is_delegate_call to true. - private_inputs.private_call.call_stack_item.public_inputs.call_context.is_delegate_call = true; - - // Invoke the native private kernel circuit - DummyBuilder builder = DummyBuilder("private_kernel_tests__private_function_delegate_call_fails"); - native_private_kernel_circuit_initial(builder, private_inputs); - - // Assertion checks - EXPECT_TRUE(builder.failed()); - EXPECT_EQ(builder.get_first_failure().code, CircuitErrorCode::PRIVATE_KERNEL__UNSUPPORTED_OP); - EXPECT_EQ(builder.get_first_failure().message, "Users cannot make a delegatecall"); -} - -TEST_F(native_private_kernel_init_tests, private_function_incorrect_storage_contract_address_fails) -{ - auto private_inputs = do_private_call_get_kernel_inputs_init(false, deposit, standard_test_args()); - - // Set the storage_contract_address to a random scalar. - private_inputs.private_call.call_stack_item.public_inputs.call_context.storage_contract_address = - NT::fr::random_element(); - - // Invoke the native private kernel circuit - DummyBuilder builder = - DummyBuilder("private_kernel_tests__private_function_incorrect_storage_contract_address_fails"); - native_private_kernel_circuit_initial(builder, private_inputs); - - // Assertion checks - EXPECT_TRUE(builder.failed()); - EXPECT_EQ(builder.get_first_failure().code, CircuitErrorCode::PRIVATE_KERNEL__CONTRACT_ADDRESS_MISMATCH); - EXPECT_EQ(builder.get_first_failure().message, "Storage contract address must be that of the called contract"); -} - -TEST_F(native_private_kernel_init_tests, native_read_request_bad_request) -{ - auto private_inputs = do_private_call_get_kernel_inputs_init(false, deposit, standard_test_args()); - - auto const& contract_address = - private_inputs.private_call.call_stack_item.public_inputs.call_context.storage_contract_address; - - auto const first_nullifier = silo_nullifier(contract_address, private_inputs.tx_request.hash()); - auto [read_requests, - read_request_membership_witnesses, - _transient_read_requests, - _transient_read_request_membership_witnesses, - root] = get_random_reads(first_nullifier, contract_address, 2); - private_inputs.private_call.call_stack_item.public_inputs.block_header.note_hash_tree_root = root; - - // tweak read_request so it gives wrong root when paired with its sibling path - read_requests[1] += 1; - - private_inputs.private_call.call_stack_item.public_inputs.read_requests = read_requests; - private_inputs.private_call.read_request_membership_witnesses = read_request_membership_witnesses; - - DummyBuilder builder = DummyBuilder("native_private_kernel_init_tests__native_read_request_bad_request"); - auto const& public_inputs = native_private_kernel_circuit_initial(builder, private_inputs); - - validate_no_new_deployed_contract(public_inputs); - - ASSERT_TRUE(builder.failed()); - ASSERT_EQ(builder.get_first_failure().code, - CircuitErrorCode::PRIVATE_KERNEL__READ_REQUEST_NOTE_HASH_TREE_ROOT_MISMATCH); - - // Check the first nullifier is hash of the signed tx request - ASSERT_EQ(public_inputs.end.new_nullifiers[0], private_inputs.tx_request.hash()); -} - -TEST_F(native_private_kernel_init_tests, native_read_request_bad_leaf_index) -{ - auto private_inputs = do_private_call_get_kernel_inputs_init(false, deposit, standard_test_args()); - - auto const& contract_address = - private_inputs.private_call.call_stack_item.public_inputs.call_context.storage_contract_address; - - auto const first_nullifier = silo_nullifier(contract_address, private_inputs.tx_request.hash()); - auto [read_requests, - read_request_membership_witnesses, - _transient_read_requests, - _transient_read_request_membership_witnesses, - root] = get_random_reads(first_nullifier, contract_address, 2); - private_inputs.private_call.call_stack_item.public_inputs.block_header.note_hash_tree_root = root; - - // tweak leaf index so it gives wrong root when paired with its request and sibling path - read_request_membership_witnesses[1].leaf_index += 1; - private_inputs.private_call.call_stack_item.public_inputs.read_requests = read_requests; - private_inputs.private_call.read_request_membership_witnesses = read_request_membership_witnesses; - - DummyBuilder builder = DummyBuilder("native_private_kernel_init_tests__native_read_request_bad_leaf_index"); - auto const& public_inputs = native_private_kernel_circuit_initial(builder, private_inputs); - - validate_no_new_deployed_contract(public_inputs); - - ASSERT_TRUE(builder.failed()); - ASSERT_EQ(builder.get_first_failure().code, - CircuitErrorCode::PRIVATE_KERNEL__READ_REQUEST_NOTE_HASH_TREE_ROOT_MISMATCH); - - // Check the first nullifier is hash of the signed tx request - ASSERT_EQ(public_inputs.end.new_nullifiers[0], private_inputs.tx_request.hash()); -} - -TEST_F(native_private_kernel_init_tests, native_read_request_bad_sibling_path) -{ - auto private_inputs = do_private_call_get_kernel_inputs_init(false, deposit, standard_test_args()); - - auto const& contract_address = - private_inputs.private_call.call_stack_item.public_inputs.call_context.storage_contract_address; - - auto const first_nullifier = silo_nullifier(contract_address, private_inputs.tx_request.hash()); - auto [read_requests, - read_request_membership_witnesses, - _transient_read_requests, - _transient_read_request_membership_witnesses, - root] = get_random_reads(first_nullifier, contract_address, 2); - private_inputs.private_call.call_stack_item.public_inputs.block_header.note_hash_tree_root = root; - - // tweak sibling path so it gives wrong root when paired with its request - read_request_membership_witnesses[1].sibling_path[1] += 1; - private_inputs.private_call.call_stack_item.public_inputs.read_requests = read_requests; - private_inputs.private_call.read_request_membership_witnesses = read_request_membership_witnesses; - - DummyBuilder builder = DummyBuilder("native_private_kernel_init_tests__native_read_request_bad_sibling_path"); - auto const& public_inputs = native_private_kernel_circuit_initial(builder, private_inputs); - - validate_no_new_deployed_contract(public_inputs); - - ASSERT_TRUE(builder.failed()); - ASSERT_EQ(builder.get_first_failure().code, - CircuitErrorCode::PRIVATE_KERNEL__READ_REQUEST_NOTE_HASH_TREE_ROOT_MISMATCH); - - // Check the first nullifier is hash of the signed tx request - ASSERT_EQ(public_inputs.end.new_nullifiers[0], private_inputs.tx_request.hash()); -} - -TEST_F(native_private_kernel_init_tests, native_read_request_root_mismatch) -{ - auto private_inputs = do_private_call_get_kernel_inputs_init(false, deposit, standard_test_args()); - - auto const& contract_address = - private_inputs.private_call.call_stack_item.public_inputs.call_context.storage_contract_address; - - // generate two random sets of read requests and mix them so their roots don't match - auto const first_nullifier = silo_nullifier(contract_address, private_inputs.tx_request.hash()); - auto [read_requests0, - read_request_membership_witnesses0, - _transient_read_requests0, - _transient_read_request_membership_witnesses0, - root] = get_random_reads(first_nullifier, contract_address, 2); - private_inputs.private_call.call_stack_item.public_inputs.block_header.note_hash_tree_root = root; - auto [read_requests1, - read_request_membership_witnesses1, - _transient_read_requests1, - _transient_read_request_membership_witnesses1, - _root] = get_random_reads(first_nullifier, contract_address, 2); - std::array bad_requests{}; - std::array, MAX_READ_REQUESTS_PER_CALL> bad_witnesses; - // note we are using read_requests0 for some and read_requests1 for others - bad_requests[0] = read_requests0[0]; - bad_requests[1] = read_requests0[1]; - bad_requests[2] = read_requests1[0]; - bad_requests[3] = read_requests1[1]; - bad_witnesses[0] = read_request_membership_witnesses0[0]; - bad_witnesses[1] = read_request_membership_witnesses0[1]; - bad_witnesses[2] = read_request_membership_witnesses1[0]; - bad_witnesses[3] = read_request_membership_witnesses1[1]; - private_inputs.private_call.call_stack_item.public_inputs.read_requests = bad_requests; - private_inputs.private_call.read_request_membership_witnesses = bad_witnesses; - - DummyBuilder builder = DummyBuilder("native_private_kernel_init_tests__native_read_request_root_mismatch"); - auto const& public_inputs = native_private_kernel_circuit_initial(builder, private_inputs); - - validate_no_new_deployed_contract(public_inputs); - - ASSERT_TRUE(builder.failed()); - ASSERT_EQ(builder.get_first_failure().code, - CircuitErrorCode::PRIVATE_KERNEL__READ_REQUEST_NOTE_HASH_TREE_ROOT_MISMATCH); - - // Check the first nullifier is hash of the signed tx request - ASSERT_EQ(public_inputs.end.new_nullifiers[0], private_inputs.tx_request.hash()); -} - -TEST_F(native_private_kernel_init_tests, native_no_read_requests_works) -{ - // no read requests should work - - auto private_inputs = do_private_call_get_kernel_inputs_init(false, deposit, standard_test_args()); - - // empty requests - std::array const read_requests{}; - std::array, MAX_READ_REQUESTS_PER_CALL> const - read_request_membership_witnesses{}; - private_inputs.private_call.call_stack_item.public_inputs.read_requests = read_requests; - private_inputs.private_call.read_request_membership_witnesses = read_request_membership_witnesses; - - DummyBuilder builder = DummyBuilder("native_private_kernel_init_tests__native_no_read_requests_works"); - auto const& public_inputs = native_private_kernel_circuit_initial(builder, private_inputs); - - validate_no_new_deployed_contract(public_inputs); - - auto failure = builder.get_first_failure(); - if (failure.code != CircuitErrorCode::NO_ERROR) { - info("failure: ", failure); - } - ASSERT_FALSE(builder.failed()); - - // Check the first nullifier is hash of the signed tx request - ASSERT_EQ(public_inputs.end.new_nullifiers[0], private_inputs.tx_request.hash()); - - // non-transient read requests are NOT forwarded - ASSERT_EQ(array_length(public_inputs.end.read_requests), 0); -} - -TEST_F(native_private_kernel_init_tests, native_one_read_requests_works) -{ - // one read request should work - - auto private_inputs = do_private_call_get_kernel_inputs_init(false, deposit, standard_test_args()); - - auto const& contract_address = - private_inputs.private_call.call_stack_item.public_inputs.call_context.storage_contract_address; - - auto const first_nullifier = silo_nullifier(contract_address, private_inputs.tx_request.hash()); - auto [read_requests, - read_request_membership_witnesses, - _transient_read_requests, - _transient_read_request_membership_witnesses, - root] = get_random_reads(first_nullifier, contract_address, 1); - private_inputs.private_call.call_stack_item.public_inputs.block_header.note_hash_tree_root = root; - private_inputs.private_call.call_stack_item.public_inputs.read_requests = read_requests; - private_inputs.private_call.read_request_membership_witnesses = read_request_membership_witnesses; - - DummyBuilder builder = DummyBuilder("native_private_kernel_init_tests__native_one_read_requests_works"); - auto const& public_inputs = native_private_kernel_circuit_initial(builder, private_inputs); - - validate_no_new_deployed_contract(public_inputs); - - auto failure = builder.get_first_failure(); - if (failure.code != CircuitErrorCode::NO_ERROR) { - info("failure: ", failure); - } - ASSERT_FALSE(builder.failed()); - - // Check the first nullifier is hash of the signed tx request - ASSERT_EQ(public_inputs.end.new_nullifiers[0], private_inputs.tx_request.hash()); - - // non-transient read requests are NOT forwarded - ASSERT_EQ(array_length(public_inputs.end.read_requests), 0); -} - -TEST_F(native_private_kernel_init_tests, native_two_read_requests_works) -{ - // two read requests should work - - auto private_inputs = do_private_call_get_kernel_inputs_init(false, deposit, standard_test_args()); - - auto const& contract_address = - private_inputs.private_call.call_stack_item.public_inputs.call_context.storage_contract_address; - - auto const first_nullifier = silo_nullifier(contract_address, private_inputs.tx_request.hash()); - auto [read_requests, - read_request_membership_witnesses, - _transient_read_requests, - _transient_read_request_membership_witnesses, - root] = get_random_reads(first_nullifier, contract_address, 2); - private_inputs.private_call.call_stack_item.public_inputs.block_header.note_hash_tree_root = root; - private_inputs.private_call.call_stack_item.public_inputs.read_requests = read_requests; - private_inputs.private_call.read_request_membership_witnesses = read_request_membership_witnesses; - - DummyBuilder builder = DummyBuilder("native_private_kernel_init_tests__native_two_read_requests_works"); - auto const& public_inputs = native_private_kernel_circuit_initial(builder, private_inputs); - - validate_no_new_deployed_contract(public_inputs); - - auto failure = builder.get_first_failure(); - if (failure.code != CircuitErrorCode::NO_ERROR) { - info("failure: ", failure); - } - ASSERT_FALSE(builder.failed()); - - // Check the first nullifier is hash of the signed tx request - ASSERT_EQ(public_inputs.end.new_nullifiers[0], private_inputs.tx_request.hash()); - - // non-transient read requests are NOT forwarded - ASSERT_EQ(array_length(public_inputs.end.read_requests), 0); -} - -TEST_F(native_private_kernel_init_tests, native_max_read_requests_works) -{ - // max read requests should work - - auto private_inputs = do_private_call_get_kernel_inputs_init(false, deposit, standard_test_args()); - - auto const& contract_address = - private_inputs.private_call.call_stack_item.public_inputs.call_context.storage_contract_address; - - auto const first_nullifier = silo_nullifier(contract_address, private_inputs.tx_request.hash()); - auto [read_requests, - read_request_membership_witnesses, - _transient_read_requests, - _transient_read_request_membership_witnesses, - root] = get_random_reads(first_nullifier, contract_address, MAX_READ_REQUESTS_PER_CALL); - private_inputs.private_call.call_stack_item.public_inputs.block_header.note_hash_tree_root = root; - private_inputs.private_call.call_stack_item.public_inputs.read_requests = read_requests; - private_inputs.private_call.read_request_membership_witnesses = read_request_membership_witnesses; - - DummyBuilder builder = DummyBuilder("native_private_kernel_init_tests__native_max_read_requests_works"); - auto const& public_inputs = native_private_kernel_circuit_initial(builder, private_inputs); - - validate_no_new_deployed_contract(public_inputs); - - auto failure = builder.get_first_failure(); - if (failure.code != CircuitErrorCode::NO_ERROR) { - info("failure: ", failure); - } - ASSERT_FALSE(builder.failed()); - - // Check the first nullifier is hash of the signed tx request - ASSERT_EQ(public_inputs.end.new_nullifiers[0], private_inputs.tx_request.hash()); - - // non-transient read requests are NOT forwarded - ASSERT_EQ(array_length(public_inputs.end.read_requests), 0); -} - -// TODO(dbanks12): more tests of read_requests for multiple iterations. -// Check enforcement that inner iterations' read_requests match root in constants -// https://github.com/AztecProtocol/aztec-packages/issues/786 - -TEST_F(native_private_kernel_init_tests, native_one_transient_read_requests_works) -{ - // one transient read request should work - - auto private_inputs = do_private_call_get_kernel_inputs_init(false, deposit, standard_test_args()); - - auto const& contract_address = - private_inputs.private_call.call_stack_item.public_inputs.call_context.storage_contract_address; - - auto const first_nullifier = silo_nullifier(contract_address, private_inputs.tx_request.hash()); - auto [read_requests, - read_request_membership_witnesses, - transient_read_requests, - transient_read_request_membership_witnesses, - root] = get_random_reads(first_nullifier, contract_address, 1); - private_inputs.private_call.call_stack_item.public_inputs.block_header.note_hash_tree_root = root; - - // Make the read request transient - read_requests[0] = transient_read_requests[0]; - read_request_membership_witnesses[0] = transient_read_request_membership_witnesses[0]; - private_inputs.private_call.call_stack_item.public_inputs.read_requests = read_requests; - private_inputs.private_call.read_request_membership_witnesses = read_request_membership_witnesses; - - DummyBuilder builder = DummyBuilder("native_private_kernel_init_tests__native_one_transient_read_requests_works"); - auto const& public_inputs = native_private_kernel_circuit_initial(builder, private_inputs); - - validate_no_new_deployed_contract(public_inputs); - - auto failure = builder.get_first_failure(); - if (failure.code != CircuitErrorCode::NO_ERROR) { - info("failure: ", failure); - } - ASSERT_FALSE(builder.failed()); - - ASSERT_EQ(array_length(public_inputs.end.read_requests), 1); // transient read request gets forwarded -} - -// TODO(https://github.com/AztecProtocol/aztec-packages/issues/906): re-enable once kernel supports forwarding/matching -// of transient reads. -TEST_F(native_private_kernel_init_tests, native_max_read_requests_one_transient_works) -{ - // max read requests with one transient should work - auto private_inputs = do_private_call_get_kernel_inputs_init(false, deposit, standard_test_args()); - - auto const& contract_address = - private_inputs.private_call.call_stack_item.public_inputs.call_context.storage_contract_address; - - auto const first_nullifier = silo_nullifier(contract_address, private_inputs.tx_request.hash()); - auto [read_requests, - read_request_membership_witnesses, - transient_read_requests, - transient_read_request_membership_witnesses, - root] = get_random_reads(first_nullifier, contract_address, MAX_READ_REQUESTS_PER_CALL); - private_inputs.private_call.call_stack_item.public_inputs.block_header.note_hash_tree_root = root; - private_inputs.private_call.call_stack_item.public_inputs.read_requests = read_requests; - - // Make the read request at position 1 transient - read_requests[1] = transient_read_requests[1]; - read_request_membership_witnesses[1] = transient_read_request_membership_witnesses[1]; - private_inputs.private_call.read_request_membership_witnesses = read_request_membership_witnesses; - - DummyBuilder builder = - DummyBuilder("native_private_kernel_init_tests__native_max_read_requests_one_transient_works"); - auto const& public_inputs = native_private_kernel_circuit_initial(builder, private_inputs); - - validate_no_new_deployed_contract(public_inputs); - - auto failure = builder.get_first_failure(); - if (failure.code != CircuitErrorCode::NO_ERROR) { - info("failure: ", failure); - } - ASSERT_FALSE(builder.failed()); - - // transient read request gets forwarded - ASSERT_EQ(array_length(public_inputs.end.read_requests), 1); -} - -TEST_F(native_private_kernel_init_tests, native_max_read_requests_all_transient_works) -{ - // max read requests with all transient should work - auto private_inputs = do_private_call_get_kernel_inputs_init(false, deposit, standard_test_args()); - - auto const& contract_address = - private_inputs.private_call.call_stack_item.public_inputs.call_context.storage_contract_address; - - auto const first_nullifier = silo_nullifier(contract_address, private_inputs.tx_request.hash()); - auto [read_requests, - read_request_membership_witnesses, - transient_read_requests, - transient_read_request_membership_witnesses, - root] = get_random_reads(first_nullifier, contract_address, MAX_READ_REQUESTS_PER_CALL); - private_inputs.private_call.call_stack_item.public_inputs.block_header.note_hash_tree_root = root; - private_inputs.private_call.call_stack_item.public_inputs.read_requests = transient_read_requests; - private_inputs.private_call.read_request_membership_witnesses = transient_read_request_membership_witnesses; - - DummyBuilder builder = - DummyBuilder("native_private_kernel_init_tests__native_max_read_requests_all_transient_works"); - auto const& public_inputs = native_private_kernel_circuit_initial(builder, private_inputs); - - validate_no_new_deployed_contract(public_inputs); - - auto failure = builder.get_first_failure(); - if (failure.code != CircuitErrorCode::NO_ERROR) { - info("failure: ", failure); - } - ASSERT_FALSE(builder.failed()); - - // transient read request all get forwarded - ASSERT_EQ(array_length(public_inputs.end.read_requests), MAX_READ_REQUESTS_PER_CALL); -} - -} // namespace aztec3::circuits::kernel::private_kernel diff --git a/circuits/cpp/src/aztec3/circuits/kernel/private/native_private_kernel_circuit_inner.cpp b/circuits/cpp/src/aztec3/circuits/kernel/private/native_private_kernel_circuit_inner.cpp deleted file mode 100644 index d98423e8322..00000000000 --- a/circuits/cpp/src/aztec3/circuits/kernel/private/native_private_kernel_circuit_inner.cpp +++ /dev/null @@ -1,145 +0,0 @@ -#include "common.hpp" -#include "init.hpp" - -#include "aztec3/circuits/abis/kernel_circuit_public_inputs.hpp" -#include "aztec3/circuits/abis/new_contract_data.hpp" -#include "aztec3/circuits/abis/previous_kernel_data.hpp" -#include "aztec3/circuits/abis/private_kernel/private_kernel_inputs_inner.hpp" -#include "aztec3/utils/array.hpp" -#include "aztec3/utils/dummy_circuit_builder.hpp" - -namespace { -using NT = aztec3::utils::types::NativeTypes; - -using aztec3::circuits::abis::ContractLeafPreimage; -using aztec3::circuits::abis::KernelCircuitPublicInputs; -using aztec3::circuits::abis::PreviousKernelData; -using aztec3::circuits::abis::private_kernel::PrivateKernelInputsInner; -using aztec3::circuits::kernel::private_kernel::common_initialize_end_values; -using aztec3::utils::array_length; -using aztec3::utils::array_pop; -using aztec3::utils::CircuitErrorCode; -using aztec3::utils::DummyCircuitBuilder; - -void initialize_end_values(PreviousKernelData const& previous_kernel, KernelCircuitPublicInputs& public_inputs) -{ - common_initialize_end_values(previous_kernel, public_inputs); - - // Ensure the arrays are the same as previously, before we start pushing more data onto them in other - // functions within this circuit: - auto& end = public_inputs.end; - const auto& start = previous_kernel.public_inputs.end; - end.read_requests = start.read_requests; -} -} // namespace - -namespace aztec3::circuits::kernel::private_kernel { - -void pop_and_validate_this_private_call_hash( - DummyCircuitBuilder& builder, - PrivateCallData const& private_call, - std::array& private_call_stack) -{ - // TODO(mike): this logic might need to change to accommodate the weird edge 3 initial txs (the 'main' tx, the - // 'fee' tx, and the 'gas rebate' tx). - const auto popped_private_call_hash = array_pop(private_call_stack); - const auto calculated_this_private_call_hash = private_call.call_stack_item.hash(); - - builder.do_assert( - popped_private_call_hash == calculated_this_private_call_hash, - format("calculated private_call_hash (", - calculated_this_private_call_hash, - ") does not match provided private_call_hash (", - popped_private_call_hash, - ") at the top of the call stack"), - CircuitErrorCode::PRIVATE_KERNEL__CALCULATED_PRIVATE_CALL_HASH_AND_PROVIDED_PRIVATE_CALL_HASH_MISMATCH); -}; - -void validate_contract_tree_root(DummyCircuitBuilder& builder, PrivateKernelInputsInner const& private_inputs) -{ - auto const& purported_contract_tree_root = - private_inputs.private_call.call_stack_item.public_inputs.block_header.contract_tree_root; - auto const& previous_kernel_contract_tree_root = - private_inputs.previous_kernel.public_inputs.constants.block_header.contract_tree_root; - builder.do_assert( - purported_contract_tree_root == previous_kernel_contract_tree_root, - "purported_contract_tree_root doesn't match previous_kernel_contract_tree_root", - CircuitErrorCode::PRIVATE_KERNEL__PURPORTED_CONTRACT_TREE_ROOT_AND_PREVIOUS_KERNEL_CONTRACT_TREE_ROOT_MISMATCH); -} - -void validate_inputs(DummyCircuitBuilder& builder, PrivateKernelInputsInner const& private_inputs) -{ - const auto& this_call_stack_item = private_inputs.private_call.call_stack_item; - - builder.do_assert(this_call_stack_item.function_data.is_private == true, - "Cannot execute a non-private function with the private kernel circuit", - CircuitErrorCode::PRIVATE_KERNEL__NON_PRIVATE_FUNCTION_EXECUTED_WITH_PRIVATE_KERNEL); - - const auto& start = private_inputs.previous_kernel.public_inputs.end; - - // TODO(mike): we might want to range-constrain the call_count to prevent some kind of overflow errors. Having - // said that, iterating 2^254 times isn't feasible. - - NT::fr const start_private_call_stack_length = array_length(start.private_call_stack); - - builder.do_assert(private_inputs.previous_kernel.public_inputs.is_private == true, - "Cannot verify a non-private kernel snark in the private kernel circuit", - CircuitErrorCode::PRIVATE_KERNEL__NON_PRIVATE_KERNEL_VERIFIED_WITH_PRIVATE_KERNEL); - builder.do_assert(this_call_stack_item.function_data.is_constructor == false, - "A constructor must be executed as the first tx in the recursion", - CircuitErrorCode::PRIVATE_KERNEL__CONSTRUCTOR_EXECUTED_IN_RECURSION); - builder.do_assert(start_private_call_stack_length != 0, - "Cannot execute private kernel circuit with an empty private call stack", - CircuitErrorCode::PRIVATE_KERNEL__PRIVATE_CALL_STACK_EMPTY); -} - -KernelCircuitPublicInputs native_private_kernel_circuit_inner(DummyCircuitBuilder& builder, - PrivateKernelInputsInner const& private_inputs) -{ - // We'll be pushing data to this during execution of this circuit. - KernelCircuitPublicInputs public_inputs{}; - - common_validate_previous_kernel_values(builder, private_inputs.previous_kernel.public_inputs.end); - - // Do this before any functions can modify the inputs. - initialize_end_values(private_inputs.previous_kernel, public_inputs); - - validate_inputs(builder, private_inputs); - - common_validate_arrays(builder, private_inputs.private_call.call_stack_item.public_inputs); - - pop_and_validate_this_private_call_hash(builder, private_inputs.private_call, public_inputs.end.private_call_stack); - - common_validate_call_stack(builder, private_inputs.private_call); - - common_validate_read_requests( - builder, - public_inputs.constants.block_header.note_hash_tree_root, - private_inputs.private_call.call_stack_item.public_inputs.read_requests, // read requests from private call - private_inputs.private_call.read_request_membership_witnesses); - - - // TODO(dbanks12): feels like update_end_values should happen later - common_update_end_values(builder, private_inputs.private_call, public_inputs); - - // ensure that historical/purported contract tree root matches the one in previous kernel - validate_contract_tree_root(builder, private_inputs); - - const auto private_call_stack_item = private_inputs.private_call.call_stack_item; - common_contract_logic(builder, - private_inputs.private_call, - public_inputs, - private_call_stack_item.public_inputs.contract_deployment_data, - private_call_stack_item.function_data); - - // This is where a real circuit would perform recursive verification of the previous kernel proof and private call - // proof. - - // Note: given that we skipped the verify_proof function, the aggregation object we get at the end will just be - // the same as we had at the start. public_inputs.end.aggregation_object = aggregation_object; - public_inputs.end.aggregation_object = private_inputs.previous_kernel.public_inputs.end.aggregation_object; - - return public_inputs; -}; - -} // namespace aztec3::circuits::kernel::private_kernel \ No newline at end of file diff --git a/circuits/cpp/src/aztec3/circuits/kernel/private/native_private_kernel_circuit_inner.hpp b/circuits/cpp/src/aztec3/circuits/kernel/private/native_private_kernel_circuit_inner.hpp deleted file mode 100644 index 52ff291a27d..00000000000 --- a/circuits/cpp/src/aztec3/circuits/kernel/private/native_private_kernel_circuit_inner.hpp +++ /dev/null @@ -1,18 +0,0 @@ -#pragma once - -#include "init.hpp" - -#include "aztec3/circuits/abis/kernel_circuit_public_inputs.hpp" -#include "aztec3/circuits/abis/private_kernel/private_kernel_inputs_inner.hpp" -#include "aztec3/utils/dummy_circuit_builder.hpp" - -namespace aztec3::circuits::kernel::private_kernel { - -using aztec3::circuits::abis::KernelCircuitPublicInputs; -using aztec3::circuits::abis::private_kernel::PrivateKernelInputsInner; -using DummyBuilder = aztec3::utils::DummyCircuitBuilder; - -KernelCircuitPublicInputs native_private_kernel_circuit_inner(DummyBuilder& builder, - PrivateKernelInputsInner const& private_inputs); - -} // namespace aztec3::circuits::kernel::private_kernel \ No newline at end of file diff --git a/circuits/cpp/src/aztec3/circuits/kernel/private/native_private_kernel_circuit_inner.test.cpp b/circuits/cpp/src/aztec3/circuits/kernel/private/native_private_kernel_circuit_inner.test.cpp deleted file mode 100644 index d29c08eb590..00000000000 --- a/circuits/cpp/src/aztec3/circuits/kernel/private/native_private_kernel_circuit_inner.test.cpp +++ /dev/null @@ -1,986 +0,0 @@ -#include "c_bind.h" -#include "testing_harness.hpp" - -#include "aztec3/circuits/abis/membership_witness.hpp" -#include "aztec3/circuits/apps/test_apps/escrow/deposit.hpp" -#include "aztec3/circuits/kernel/private/common.hpp" -#include "aztec3/constants.hpp" -#include "aztec3/utils/array.hpp" -#include "aztec3/utils/circuit_errors.hpp" - -#include - -#include - -#include -#include - -namespace aztec3::circuits::kernel::private_kernel { - -using aztec3::circuits::apps::test_apps::escrow::deposit; - -using aztec3::circuits::kernel::private_kernel::testing_harness::do_private_call_get_kernel_inputs_inner; -using aztec3::circuits::kernel::private_kernel::testing_harness::get_random_reads; -using aztec3::circuits::kernel::private_kernel::testing_harness::validate_no_new_deployed_contract; - -using aztec3::utils::array_length; -using aztec3::utils::CircuitErrorCode; - -// NOTE: *DO NOT* call fr constructors in static initializers and assign them to constants. This will fail. Instead, use -// lazy initialization or functions. Lambdas were introduced here. -// amount = 5, asset_id = 1, memo = 999 -const auto standard_test_args = [] { return std::vector{ NT::fr(5), NT::fr(1), NT::fr(999) }; }; - -class native_private_kernel_inner_tests : public ::testing::Test { - protected: - static void SetUpTestSuite() { barretenberg::srs::init_crs_factory("../barretenberg/cpp/srs_db/ignition"); } -}; - -/** - ************************************************************** - * Native inner private kernel circuit tests. - ************************************************************** - */ -TEST_F(native_private_kernel_inner_tests, private_function_zero_storage_contract_address_fails) -{ - auto private_inputs = do_private_call_get_kernel_inputs_inner(false, deposit, standard_test_args()); - - // Set storage_contract_address to 0 - private_inputs.private_call.call_stack_item.public_inputs.call_context.storage_contract_address = 0; - private_inputs.private_call.call_stack_item.contract_address = 0; - - // Modify the call stack item's hash with the newly added contract address. - private_inputs.previous_kernel.public_inputs.end.private_call_stack[0] = - private_inputs.private_call.call_stack_item.hash(); - - // Invoke the native private kernel circuit - DummyBuilder builder = DummyBuilder("private_kernel_tests__private_function_zero_storage_contract_address_fails"); - native_private_kernel_circuit_inner(builder, private_inputs); - - // Assertion checks - EXPECT_TRUE(builder.failed()); - EXPECT_EQ(builder.get_first_failure().code, CircuitErrorCode::PRIVATE_KERNEL__INVALID_CONTRACT_ADDRESS); - EXPECT_EQ(builder.get_first_failure().message, - "contract address can't be 0 for non-contract deployment related transactions"); -} - - -TEST_F(native_private_kernel_inner_tests, private_function_incorrect_is_internal) -{ - auto private_inputs = do_private_call_get_kernel_inputs_inner(false, deposit, standard_test_args()); - - // Make the call internal but msg_sender != storage_contract_address. - private_inputs.private_call.call_stack_item.function_data.is_internal = true; - private_inputs.private_call.call_stack_item.public_inputs.call_context.msg_sender = 1; - private_inputs.private_call.call_stack_item.public_inputs.call_context.storage_contract_address = 2; - - // We need to update the previous_kernel's private_call_stack because the current_call_stack_item has changed - // i.e. we changed the function_data and public_inputs->call_context of the current_call_stack_item - private_inputs.previous_kernel.public_inputs.end.private_call_stack[0] = - private_inputs.private_call.call_stack_item.hash(); - - // Invoke the native private kernel circuit - DummyBuilder builder = DummyBuilder("private_kernel_tests__private_function_incorrect_contract_tree_root_fails"); - native_private_kernel_circuit_inner(builder, private_inputs); - - // Assertion checks - EXPECT_TRUE(builder.failed()); - EXPECT_EQ(builder.get_first_failure().code, CircuitErrorCode::PRIVATE_KERNEL__IS_INTERNAL_BUT_NOT_SELF_CALL); - EXPECT_EQ(builder.get_first_failure().message, "call is internal, but msg_sender is not self"); -} - -TEST_F(native_private_kernel_inner_tests, private_function_incorrect_contract_tree_root_fails) -{ - auto private_inputs = do_private_call_get_kernel_inputs_inner(false, deposit, standard_test_args()); - - // Set historical_tree_root to a random scalar. - private_inputs.previous_kernel.public_inputs.constants.block_header.contract_tree_root = NT::fr::random_element(); - - // Invoke the native private kernel circuit - DummyBuilder builder = DummyBuilder("private_kernel_tests__private_function_incorrect_contract_tree_root_fails"); - native_private_kernel_circuit_inner(builder, private_inputs); - - // Assertion checks - EXPECT_TRUE(builder.failed()); - EXPECT_EQ( - builder.get_first_failure().code, - CircuitErrorCode::PRIVATE_KERNEL__PURPORTED_CONTRACT_TREE_ROOT_AND_PREVIOUS_KERNEL_CONTRACT_TREE_ROOT_MISMATCH); - EXPECT_EQ(builder.get_first_failure().message, - "purported_contract_tree_root doesn't match previous_kernel_contract_tree_root"); -} - -TEST_F(native_private_kernel_inner_tests, private_function_incorrect_contract_leaf_index_fails) -{ - auto private_inputs = do_private_call_get_kernel_inputs_inner(false, deposit, standard_test_args()); - - // Set the leaf index of the contract leaf to 20 (the correct value is 1). - NT::fr const wrong_idx = 20; - private_inputs.private_call.contract_leaf_membership_witness.leaf_index = wrong_idx; - - // Invoke the native private kernel circuit - DummyBuilder builder = DummyBuilder("private_kernel_tests__private_function_incorrect_contract_leaf_index_fails"); - native_private_kernel_circuit_inner(builder, private_inputs); - - // Assertion checks - EXPECT_TRUE(builder.failed()); - EXPECT_EQ(builder.get_first_failure().code, - CircuitErrorCode::PRIVATE_KERNEL__COMPUTED_CONTRACT_TREE_ROOT_AND_PURPORTED_CONTRACT_TREE_ROOT_MISMATCH); - EXPECT_EQ(builder.get_first_failure().message, - "computed_contract_tree_root doesn't match purported_contract_tree_root"); -} - -TEST_F(native_private_kernel_inner_tests, private_function_incorrect_contract_leaf_sibling_path_fails) -{ - auto private_inputs = do_private_call_get_kernel_inputs_inner(false, deposit, standard_test_args()); - - // Change the contract leaf's membership proof. - private_inputs.private_call.contract_leaf_membership_witness.sibling_path[0] = fr::random_element(); - - // Invoke the native private kernel circuit - DummyBuilder builder = - DummyBuilder("private_kernel_tests__private_function_incorrect_contract_leaf_sibling_path_fails"); - native_private_kernel_circuit_inner(builder, private_inputs); - - // Assertion checks - EXPECT_TRUE(builder.failed()); - EXPECT_EQ(builder.get_first_failure().code, - CircuitErrorCode::PRIVATE_KERNEL__COMPUTED_CONTRACT_TREE_ROOT_AND_PURPORTED_CONTRACT_TREE_ROOT_MISMATCH); - EXPECT_EQ(builder.get_first_failure().message, - "computed_contract_tree_root doesn't match purported_contract_tree_root"); -} - -TEST_F(native_private_kernel_inner_tests, private_function_incorrect_function_leaf_index_fails) -{ - auto private_inputs = do_private_call_get_kernel_inputs_inner(false, deposit, standard_test_args()); - - // Set the leaf index of the function leaf to 10 (the correct value is 1). - NT::fr const wrong_idx = 10; - private_inputs.private_call.function_leaf_membership_witness.leaf_index = wrong_idx; - - // Invoke the native private kernel circuit - DummyBuilder builder = DummyBuilder("private_kernel_tests__private_function_incorrect_contract_leaf_index_fails"); - native_private_kernel_circuit_inner(builder, private_inputs); - - // Assertion checks - EXPECT_TRUE(builder.failed()); - EXPECT_EQ(builder.get_first_failure().code, - CircuitErrorCode::PRIVATE_KERNEL__COMPUTED_CONTRACT_TREE_ROOT_AND_PURPORTED_CONTRACT_TREE_ROOT_MISMATCH); - EXPECT_EQ(builder.get_first_failure().message, - "computed_contract_tree_root doesn't match purported_contract_tree_root"); -} - -TEST_F(native_private_kernel_inner_tests, private_function_incorrect_function_leaf_sibling_path_fails) -{ - auto private_inputs = do_private_call_get_kernel_inputs_inner(false, deposit, standard_test_args()); - - // Change the function leaf's membership proof. - private_inputs.private_call.function_leaf_membership_witness.sibling_path[0] = fr::random_element(); - - // Invoke the native private kernel circuit - DummyBuilder builder = - DummyBuilder("private_kernel_tests__private_function_incorrect_contract_leaf_sibling_path_fails"); - native_private_kernel_circuit_inner(builder, private_inputs); - - // Assertion checks - EXPECT_TRUE(builder.failed()); - EXPECT_EQ(builder.get_first_failure().code, - CircuitErrorCode::PRIVATE_KERNEL__COMPUTED_CONTRACT_TREE_ROOT_AND_PURPORTED_CONTRACT_TREE_ROOT_MISMATCH); - EXPECT_EQ(builder.get_first_failure().message, - "computed_contract_tree_root doesn't match purported_contract_tree_root"); -} - -TEST_F(native_private_kernel_inner_tests, private_function_incorrect_call_stack_item_hash_fails) -{ - auto private_inputs = do_private_call_get_kernel_inputs_inner(false, deposit, standard_test_args()); - - // Set the first call stack member corresponding to the `deposit` function to random scalar. - private_inputs.private_call.call_stack_item.public_inputs.private_call_stack[0] = NT::fr::random_element(); - - // Invoke the native private kernel circuit - DummyBuilder builder = DummyBuilder("private_kernel_tests__private_function_incorrect_call_stack_item_hash_fails"); - native_private_kernel_circuit_inner(builder, private_inputs); - - // Assertion checks - EXPECT_TRUE(builder.failed()); - EXPECT_EQ(builder.get_first_failure().code, - CircuitErrorCode::PRIVATE_KERNEL__CALCULATED_PRIVATE_CALL_HASH_AND_PROVIDED_PRIVATE_CALL_HASH_MISMATCH); -} - -TEST_F(native_private_kernel_inner_tests, input_validation_malformed_arrays_return_values) -{ - auto private_inputs = do_private_call_get_kernel_inputs_inner(false, deposit, standard_test_args()); - - std::array malformed_return_values{ fr(0), fr(0), fr(553) }; - private_inputs.private_call.call_stack_item.public_inputs.return_values = malformed_return_values; - - DummyBuilder builder = DummyBuilder("private_kernel_tests__input_validation_malformed_arrays_return_values"); - native_private_kernel_circuit_inner(builder, private_inputs); - - EXPECT_EQ(builder.failed(), true); - EXPECT_EQ(builder.get_first_failure().code, CircuitErrorCode::ARRAY_NOT_ZERO_RIGHT_PADDED); -} - -TEST_F(native_private_kernel_inner_tests, input_validation_malformed_arrays_read_requests) -{ - auto private_inputs = do_private_call_get_kernel_inputs_inner(false, deposit, standard_test_args()); - - std::array malformed_read_requests{ fr(0), fr(9123), fr(0), fr(12) }; - private_inputs.private_call.call_stack_item.public_inputs.read_requests = malformed_read_requests; - - DummyBuilder builder = DummyBuilder("private_kernel_tests__input_validation_malformed_arrays_read_requests"); - native_private_kernel_circuit_inner(builder, private_inputs); - - EXPECT_EQ(builder.failed(), true); - EXPECT_EQ(builder.get_first_failure().code, CircuitErrorCode::ARRAY_NOT_ZERO_RIGHT_PADDED); -} - -TEST_F(native_private_kernel_inner_tests, input_validation_malformed_arrays_commitments) -{ - auto private_inputs = do_private_call_get_kernel_inputs_inner(false, deposit, standard_test_args()); - - std::array malformed_commitments{ fr(0), fr(9123) }; - private_inputs.private_call.call_stack_item.public_inputs.new_commitments = malformed_commitments; - - DummyBuilder builder = DummyBuilder("private_kernel_tests__input_validation_malformed_arrays_commitments"); - native_private_kernel_circuit_inner(builder, private_inputs); - - EXPECT_EQ(builder.failed(), true); - EXPECT_EQ(builder.get_first_failure().code, CircuitErrorCode::ARRAY_NOT_ZERO_RIGHT_PADDED); -} - -TEST_F(native_private_kernel_inner_tests, input_validation_malformed_arrays_nullifiers) -{ - auto private_inputs = do_private_call_get_kernel_inputs_inner(false, deposit, standard_test_args()); - - std::array malformed_nullifiers{}; - malformed_nullifiers[MAX_NEW_NULLIFIERS_PER_CALL - 1] = fr(12); - private_inputs.private_call.call_stack_item.public_inputs.new_nullifiers = malformed_nullifiers; - - DummyBuilder builder = DummyBuilder("private_kernel_tests__input_validation_malformed_arrays_nullifiers"); - native_private_kernel_circuit_inner(builder, private_inputs); - - EXPECT_EQ(builder.failed(), true); - EXPECT_EQ(builder.get_first_failure().code, CircuitErrorCode::ARRAY_NOT_ZERO_RIGHT_PADDED); -} - -TEST_F(native_private_kernel_inner_tests, input_validation_malformed_arrays_nullified_commitments) -{ - auto private_inputs = do_private_call_get_kernel_inputs_inner(false, deposit, standard_test_args()); - - std::array malformed_nullified_commitments{ fr(0), - fr(0), - EMPTY_NULLIFIED_COMMITMENT }; - private_inputs.private_call.call_stack_item.public_inputs.nullified_commitments = malformed_nullified_commitments; - - DummyBuilder builder = - DummyBuilder("private_kernel_tests__input_validation_malformed_arrays_nullified_commitments"); - native_private_kernel_circuit_inner(builder, private_inputs); - - EXPECT_EQ(builder.failed(), true); - EXPECT_EQ(builder.get_first_failure().code, CircuitErrorCode::ARRAY_NOT_ZERO_RIGHT_PADDED); -} - -TEST_F(native_private_kernel_inner_tests, input_validation_malformed_arrays_private_call_stack) -{ - auto private_inputs = do_private_call_get_kernel_inputs_inner(false, deposit, standard_test_args()); - - std::array malformed_private_call_stack{ fr(0), fr(888) }; - private_inputs.private_call.call_stack_item.public_inputs.private_call_stack = malformed_private_call_stack; - - DummyBuilder builder = DummyBuilder("private_kernel_tests__input_validation_malformed_arrays_private_call_stack"); - native_private_kernel_circuit_inner(builder, private_inputs); - - EXPECT_EQ(builder.failed(), true); - EXPECT_EQ(builder.get_first_failure().code, CircuitErrorCode::ARRAY_NOT_ZERO_RIGHT_PADDED); -} - -TEST_F(native_private_kernel_inner_tests, input_validation_malformed_arrays_public_call_stack) -{ - auto private_inputs = do_private_call_get_kernel_inputs_inner(false, deposit, standard_test_args()); - - std::array malformed_public_call_stack{ fr(0), fr(888) }; - private_inputs.private_call.call_stack_item.public_inputs.public_call_stack = malformed_public_call_stack; - - DummyBuilder builder = DummyBuilder("private_kernel_tests__input_validation_malformed_arrays_public_call_stack"); - native_private_kernel_circuit_inner(builder, private_inputs); - - EXPECT_EQ(builder.failed(), true); - EXPECT_EQ(builder.get_first_failure().code, CircuitErrorCode::ARRAY_NOT_ZERO_RIGHT_PADDED); -} - -TEST_F(native_private_kernel_inner_tests, input_validation_malformed_arrays_new_l2_to_l1_msgs) -{ - auto private_inputs = do_private_call_get_kernel_inputs_inner(false, deposit, standard_test_args()); - - std::array malformed_new_l2_to_l1_msgs{}; - malformed_new_l2_to_l1_msgs[MAX_NEW_L2_TO_L1_MSGS_PER_CALL - 1] = fr(1); - private_inputs.private_call.call_stack_item.public_inputs.new_l2_to_l1_msgs = malformed_new_l2_to_l1_msgs; - - DummyBuilder builder = DummyBuilder("private_kernel_tests__input_validation_malformed_arrays_new_l2_to_l1_msgs"); - native_private_kernel_circuit_inner(builder, private_inputs); - - EXPECT_EQ(builder.failed(), true); - EXPECT_EQ(builder.get_first_failure().code, CircuitErrorCode::ARRAY_NOT_ZERO_RIGHT_PADDED); -} - -TEST_F(native_private_kernel_inner_tests, input_validation_malformed_end_arrays_read_requests) -{ - auto private_inputs = do_private_call_get_kernel_inputs_inner(false, deposit, standard_test_args()); - - std::array malformed_read_requests{ fr(0), fr(9123), fr(0), fr(12) }; - private_inputs.previous_kernel.public_inputs.end.read_requests = malformed_read_requests; - - DummyBuilder builder = DummyBuilder("private_kernel_tests__input_validation_malformed_end_arrays_read_requests"); - native_private_kernel_circuit_inner(builder, private_inputs); - - EXPECT_EQ(builder.failed(), true); - EXPECT_EQ(builder.get_first_failure().code, CircuitErrorCode::ARRAY_NOT_ZERO_RIGHT_PADDED); -} - -TEST_F(native_private_kernel_inner_tests, input_validation_malformed_end_arrays_commitments) -{ - auto private_inputs = do_private_call_get_kernel_inputs_inner(false, deposit, standard_test_args()); - - std::array malformed_commitments{ fr(0), fr(9123) }; - private_inputs.previous_kernel.public_inputs.end.new_commitments = malformed_commitments; - - DummyBuilder builder = DummyBuilder("private_kernel_tests__input_validation_malformed_end_arrays_commitments"); - native_private_kernel_circuit_inner(builder, private_inputs); - - EXPECT_EQ(builder.failed(), true); - EXPECT_EQ(builder.get_first_failure().code, CircuitErrorCode::ARRAY_NOT_ZERO_RIGHT_PADDED); -} - -TEST_F(native_private_kernel_inner_tests, input_validation_malformed_end_arrays_nullifiers) -{ - auto private_inputs = do_private_call_get_kernel_inputs_inner(false, deposit, standard_test_args()); - - std::array malformed_nullifiers{}; - malformed_nullifiers[MAX_NEW_NULLIFIERS_PER_TX - 1] = fr(12); - private_inputs.previous_kernel.public_inputs.end.new_nullifiers = malformed_nullifiers; - - DummyBuilder builder = DummyBuilder("private_kernel_tests__input_validation_malformed_end_arrays_nullifiers"); - native_private_kernel_circuit_inner(builder, private_inputs); - - EXPECT_EQ(builder.failed(), true); - EXPECT_EQ(builder.get_first_failure().code, CircuitErrorCode::ARRAY_NOT_ZERO_RIGHT_PADDED); -} - -TEST_F(native_private_kernel_inner_tests, input_validation_malformed_end_arrays_nullified_commitments) -{ - auto private_inputs = do_private_call_get_kernel_inputs_inner(false, deposit, standard_test_args()); - - std::array malformed_nullified_commitments{ fr(0), - fr(0), - EMPTY_NULLIFIED_COMMITMENT }; - private_inputs.previous_kernel.public_inputs.end.nullified_commitments = malformed_nullified_commitments; - - DummyBuilder builder = - DummyBuilder("private_kernel_tests__input_validation_malformed_arrays_end_nullified_commitments"); - native_private_kernel_circuit_inner(builder, private_inputs); - - EXPECT_EQ(builder.failed(), true); - EXPECT_EQ(builder.get_first_failure().code, CircuitErrorCode::ARRAY_NOT_ZERO_RIGHT_PADDED); -} - -TEST_F(native_private_kernel_inner_tests, input_validation_malformed_end_arrays_private_call_stack) -{ - auto private_inputs = do_private_call_get_kernel_inputs_inner(false, deposit, standard_test_args()); - - std::array malformed_private_call_stack{ fr(0), fr(888) }; - private_inputs.previous_kernel.public_inputs.end.private_call_stack = malformed_private_call_stack; - - DummyBuilder builder = - DummyBuilder("private_kernel_tests__input_validation_malformed_end_arrays_private_call_stack"); - native_private_kernel_circuit_inner(builder, private_inputs); - - EXPECT_EQ(builder.failed(), true); - EXPECT_EQ(builder.get_first_failure().code, CircuitErrorCode::ARRAY_NOT_ZERO_RIGHT_PADDED); -} - -TEST_F(native_private_kernel_inner_tests, input_validation_malformed_end_arrays_public_call_stack) -{ - auto private_inputs = do_private_call_get_kernel_inputs_inner(false, deposit, standard_test_args()); - - std::array malformed_public_call_stack{ fr(0), fr(888) }; - private_inputs.previous_kernel.public_inputs.end.public_call_stack = malformed_public_call_stack; - - DummyBuilder builder = - DummyBuilder("private_kernel_tests__input_validation_malformed_end_arrays_public_call_stack"); - native_private_kernel_circuit_inner(builder, private_inputs); - - EXPECT_EQ(builder.failed(), true); - EXPECT_EQ(builder.get_first_failure().code, CircuitErrorCode::ARRAY_NOT_ZERO_RIGHT_PADDED); -} - -TEST_F(native_private_kernel_inner_tests, input_validation_malformed_end_arrays_l2_to_l1_msgs) -{ - auto private_inputs = do_private_call_get_kernel_inputs_inner(false, deposit, standard_test_args()); - - std::array malformed_l2_to_l1_msgs{}; - malformed_l2_to_l1_msgs[MAX_NEW_L2_TO_L1_MSGS_PER_TX - 1] = fr(1); - private_inputs.previous_kernel.public_inputs.end.new_l2_to_l1_msgs = malformed_l2_to_l1_msgs; - - DummyBuilder builder = DummyBuilder("private_kernel_tests__input_validation_malformed_end_arrays_l2_to_l1_msgs"); - native_private_kernel_circuit_inner(builder, private_inputs); - - EXPECT_EQ(builder.failed(), true); - EXPECT_EQ(builder.get_first_failure().code, CircuitErrorCode::ARRAY_NOT_ZERO_RIGHT_PADDED); -} - -TEST_F(native_private_kernel_inner_tests, private_kernel_should_fail_if_aggregating_too_many_commitments) -{ - // Negative test to check if push_array_to_array fails if two many commitments are merged together - DummyBuilder builder = DummyBuilder("should_fail_if_aggregating_too_many_commitments"); - - PrivateKernelInputsInner private_inputs = - do_private_call_get_kernel_inputs_inner(false, deposit, standard_test_args()); - - // Mock the previous new commitments to be full, therefore no need commitments can be added - std::array full_new_commitments{}; - for (size_t i = 0; i < MAX_NEW_COMMITMENTS_PER_TX; ++i) { - full_new_commitments[i] = i + 1; - } - private_inputs.previous_kernel.public_inputs.end.new_commitments = full_new_commitments; - native_private_kernel_circuit_inner(builder, private_inputs); - - ASSERT_TRUE(builder.failed()); - ASSERT_EQ(builder.get_first_failure().code, CircuitErrorCode::ARRAY_OVERFLOW); -} - -/** - * @brief Test this dummy cbind - */ -TEST_F(native_private_kernel_inner_tests, cbind_private_kernel__dummy_previous_kernel) -{ - auto func = [] { return aztec3::circuits::kernel::private_kernel::utils::dummy_previous_kernel(); }; - auto [actual, expected] = call_func_and_wrapper(func, private_kernel__dummy_previous_kernel); - // TODO(AD): investigate why direct operator== didn't work - std::stringstream actual_ss; - std::stringstream expected_ss; - actual_ss << actual; - expected_ss << expected; - EXPECT_EQ(actual_ss.str(), expected_ss.str()); -} - -TEST_F(native_private_kernel_inner_tests, native_read_request_bad_request) -{ - auto private_inputs = do_private_call_get_kernel_inputs_inner(false, deposit, standard_test_args()); - - auto const& contract_address = - private_inputs.private_call.call_stack_item.public_inputs.call_context.storage_contract_address; - - auto const first_nullifier = - silo_nullifier(contract_address, private_inputs.previous_kernel.public_inputs.end.new_nullifiers[0]); - auto [read_requests, - read_request_membership_witnesses, - _transient_read_requests, - _transient_read_request_membership_witnesses, - root] = get_random_reads(first_nullifier, contract_address, 2); - private_inputs.previous_kernel.public_inputs.constants.block_header.note_hash_tree_root = root; - private_inputs.private_call.call_stack_item.public_inputs.block_header.note_hash_tree_root = root; - - // tweak read_request so it gives wrong root when paired with its sibling path - read_requests[1] += 1; - - private_inputs.private_call.call_stack_item.public_inputs.read_requests = read_requests; - private_inputs.private_call.read_request_membership_witnesses = read_request_membership_witnesses; - - // We need to update the previous_kernel's private_call_stack because the current_call_stack_item has changed - // i.e. we changed the public_inputs->read_requests and public_inputs->historical_note_hash_tree_root of the - // current_call_stack_item - private_inputs.previous_kernel.public_inputs.end.private_call_stack[0] = - private_inputs.private_call.call_stack_item.hash(); - - DummyBuilder builder = DummyBuilder("native_private_kernel_inner_tests__native_read_request_bad_request"); - auto const& public_inputs = native_private_kernel_circuit_inner(builder, private_inputs); - - validate_no_new_deployed_contract(public_inputs); - - ASSERT_TRUE(builder.failed()); - ASSERT_EQ(builder.get_first_failure().code, - CircuitErrorCode::PRIVATE_KERNEL__READ_REQUEST_NOTE_HASH_TREE_ROOT_MISMATCH); -} - -TEST_F(native_private_kernel_inner_tests, native_read_request_bad_leaf_index) -{ - auto private_inputs = do_private_call_get_kernel_inputs_inner(false, deposit, standard_test_args()); - - auto const& contract_address = - private_inputs.private_call.call_stack_item.public_inputs.call_context.storage_contract_address; - - auto const first_nullifier = - silo_nullifier(contract_address, private_inputs.previous_kernel.public_inputs.end.new_nullifiers[0]); - auto [read_requests, - read_request_membership_witnesses, - _transient_read_requests, - _transient_read_request_membership_witnesses, - root] = get_random_reads(first_nullifier, contract_address, 2); - private_inputs.previous_kernel.public_inputs.constants.block_header.note_hash_tree_root = root; - private_inputs.private_call.call_stack_item.public_inputs.block_header.note_hash_tree_root = root; - - // tweak leaf index so it gives wrong root when paired with its request and sibling path - read_request_membership_witnesses[1].leaf_index += 1; - private_inputs.private_call.call_stack_item.public_inputs.read_requests = read_requests; - private_inputs.private_call.read_request_membership_witnesses = read_request_membership_witnesses; - - // We need to update the previous_kernel's private_call_stack because the current_call_stack_item has changed - // i.e. we changed the public_inputs->read_requests and public_inputs->historical_note_hash_tree_root of the - // current_call_stack_item - private_inputs.previous_kernel.public_inputs.end.private_call_stack[0] = - private_inputs.private_call.call_stack_item.hash(); - - DummyBuilder builder = DummyBuilder("native_private_kernel_inner_tests__native_read_request_bad_leaf_index"); - auto const& public_inputs = native_private_kernel_circuit_inner(builder, private_inputs); - - validate_no_new_deployed_contract(public_inputs); - - ASSERT_TRUE(builder.failed()); - ASSERT_EQ(builder.get_first_failure().code, - CircuitErrorCode::PRIVATE_KERNEL__READ_REQUEST_NOTE_HASH_TREE_ROOT_MISMATCH); -} - -TEST_F(native_private_kernel_inner_tests, native_read_request_bad_sibling_path) -{ - auto private_inputs = do_private_call_get_kernel_inputs_inner(false, deposit, standard_test_args()); - - auto const& contract_address = - private_inputs.private_call.call_stack_item.public_inputs.call_context.storage_contract_address; - - auto const first_nullifier = - silo_nullifier(contract_address, private_inputs.previous_kernel.public_inputs.end.new_nullifiers[0]); - auto [read_requests, - read_request_membership_witnesses, - _transient_read_requests, - _transient_read_request_membership_witnesses, - root] = get_random_reads(first_nullifier, contract_address, 2); - private_inputs.previous_kernel.public_inputs.constants.block_header.note_hash_tree_root = root; - private_inputs.private_call.call_stack_item.public_inputs.block_header.note_hash_tree_root = root; - - // tweak sibling path so it gives wrong root when paired with its request - read_request_membership_witnesses[1].sibling_path[1] += 1; - private_inputs.private_call.call_stack_item.public_inputs.read_requests = read_requests; - private_inputs.private_call.read_request_membership_witnesses = read_request_membership_witnesses; - - // We need to update the previous_kernel's private_call_stack because the current_call_stack_item has changed - // i.e. we changed the public_inputs->read_requests and public_inputs->historical_note_hash_tree_root of the - // current_call_stack_item - private_inputs.previous_kernel.public_inputs.end.private_call_stack[0] = - private_inputs.private_call.call_stack_item.hash(); - - DummyBuilder builder = DummyBuilder("native_private_kernel_inner_tests__native_read_request_bad_sibling_path"); - auto const& public_inputs = native_private_kernel_circuit_inner(builder, private_inputs); - - validate_no_new_deployed_contract(public_inputs); - - ASSERT_TRUE(builder.failed()); - ASSERT_EQ(builder.get_first_failure().code, - CircuitErrorCode::PRIVATE_KERNEL__READ_REQUEST_NOTE_HASH_TREE_ROOT_MISMATCH); -} - -TEST_F(native_private_kernel_inner_tests, native_read_request_root_mismatch) -{ - auto private_inputs = do_private_call_get_kernel_inputs_inner(false, deposit, standard_test_args()); - - auto const& contract_address = - private_inputs.private_call.call_stack_item.public_inputs.call_context.storage_contract_address; - - // generate two random sets of read requests and mix them so their roots don't match - auto const first_nullifier = - silo_nullifier(contract_address, private_inputs.previous_kernel.public_inputs.end.new_nullifiers[0]); - auto [read_requests0, - read_request_membership_witnesses0, - _transient_read_requests0, - _transient_read_request_membership_witnesses0, - root] = get_random_reads(first_nullifier, contract_address, 2); - private_inputs.previous_kernel.public_inputs.constants.block_header.note_hash_tree_root = root; - private_inputs.private_call.call_stack_item.public_inputs.block_header.note_hash_tree_root = root; - auto [read_requests1, - read_request_membership_witnesses1, - _transient_read_requests1, - _transient_read_request_membership_witnesses1, - _root] = get_random_reads(first_nullifier, contract_address, 2); - std::array bad_requests{}; - std::array, MAX_READ_REQUESTS_PER_CALL> bad_witnesses; - // note we are using read_requests0 for some and read_requests1 for others - bad_requests[0] = read_requests0[0]; - bad_requests[1] = read_requests0[1]; - bad_requests[2] = read_requests1[0]; - bad_requests[3] = read_requests1[1]; - bad_witnesses[0] = read_request_membership_witnesses0[0]; - bad_witnesses[1] = read_request_membership_witnesses0[1]; - bad_witnesses[2] = read_request_membership_witnesses1[0]; - bad_witnesses[3] = read_request_membership_witnesses1[1]; - private_inputs.private_call.call_stack_item.public_inputs.read_requests = bad_requests; - private_inputs.private_call.read_request_membership_witnesses = bad_witnesses; - - // We need to update the previous_kernel's private_call_stack because the current_call_stack_item has changed - // i.e. we changed the public_inputs->read_requests and public_inputs->historical_note_hash_tree_root of the - // current_call_stack_item - private_inputs.previous_kernel.public_inputs.end.private_call_stack[0] = - private_inputs.private_call.call_stack_item.hash(); - - DummyBuilder builder = DummyBuilder("native_private_kernel_inner_tests__native_read_request_root_mismatch"); - auto const& public_inputs = native_private_kernel_circuit_inner(builder, private_inputs); - - validate_no_new_deployed_contract(public_inputs); - - ASSERT_TRUE(builder.failed()); - ASSERT_EQ(builder.get_first_failure().code, - CircuitErrorCode::PRIVATE_KERNEL__READ_REQUEST_NOTE_HASH_TREE_ROOT_MISMATCH); -} - -TEST_F(native_private_kernel_inner_tests, native_no_read_requests_works) -{ - // no read requests should work - - auto private_inputs = do_private_call_get_kernel_inputs_inner(false, deposit, standard_test_args()); - - // empty requests - std::array const read_requests{}; - std::array, MAX_READ_REQUESTS_PER_CALL> const - read_request_membership_witnesses{}; - private_inputs.private_call.call_stack_item.public_inputs.read_requests = read_requests; - private_inputs.private_call.read_request_membership_witnesses = read_request_membership_witnesses; - - // We need to update the previous_kernel's private_call_stack because the current_call_stack_item has changed - // i.e. we changed the public_inputs->read_requests of the current_call_stack_item - private_inputs.previous_kernel.public_inputs.end.private_call_stack[0] = - private_inputs.private_call.call_stack_item.hash(); - - DummyBuilder builder = DummyBuilder("native_private_kernel_inner_tests__native_no_read_requests_works"); - auto const& public_inputs = native_private_kernel_circuit_inner(builder, private_inputs); - - validate_no_new_deployed_contract(public_inputs); - - auto failure = builder.get_first_failure(); - if (failure.code != CircuitErrorCode::NO_ERROR) { - info("failure: ", failure); - } - ASSERT_FALSE(builder.failed()); - - // non-transient read requests are NOT forwarded - ASSERT_EQ(array_length(public_inputs.end.read_requests), 0); -} - -TEST_F(native_private_kernel_inner_tests, native_one_read_requests_works) -{ - // one read request should work - - auto private_inputs = do_private_call_get_kernel_inputs_inner(false, deposit, standard_test_args()); - - auto const& contract_address = - private_inputs.private_call.call_stack_item.public_inputs.call_context.storage_contract_address; - - auto const first_nullifier = - silo_nullifier(contract_address, private_inputs.previous_kernel.public_inputs.end.new_nullifiers[0]); - auto [read_requests, - read_request_membership_witnesses, - _transient_read_requests, - _transient_read_request_membership_witnesses, - root] = get_random_reads(first_nullifier, contract_address, 1); - private_inputs.previous_kernel.public_inputs.constants.block_header.note_hash_tree_root = root; - private_inputs.private_call.call_stack_item.public_inputs.block_header.note_hash_tree_root = root; - private_inputs.private_call.call_stack_item.public_inputs.read_requests = read_requests; - private_inputs.private_call.read_request_membership_witnesses = read_request_membership_witnesses; - - // We need to update the previous_kernel's private_call_stack because the current_call_stack_item has changed - // i.e. we changed the public_inputs->read_requests of the current_call_stack_item - private_inputs.previous_kernel.public_inputs.end.private_call_stack[0] = - private_inputs.private_call.call_stack_item.hash(); - - DummyBuilder builder = DummyBuilder("native_private_kernel_inner_tests__native_one_read_requests_works"); - auto const& public_inputs = native_private_kernel_circuit_inner(builder, private_inputs); - - validate_no_new_deployed_contract(public_inputs); - - auto failure = builder.get_first_failure(); - if (failure.code != CircuitErrorCode::NO_ERROR) { - info("failure: ", failure); - } - ASSERT_FALSE(builder.failed()); - - // non-transient read requests are NOT forwarded - ASSERT_EQ(array_length(public_inputs.end.read_requests), 0); -} - -TEST_F(native_private_kernel_inner_tests, native_two_read_requests_works) -{ - // two read requests should work - - auto private_inputs = do_private_call_get_kernel_inputs_inner(false, deposit, standard_test_args()); - - auto const& contract_address = - private_inputs.private_call.call_stack_item.public_inputs.call_context.storage_contract_address; - - auto const first_nullifier = - silo_nullifier(contract_address, private_inputs.previous_kernel.public_inputs.end.new_nullifiers[0]); - auto [read_requests, - read_request_membership_witnesses, - _transient_read_requests, - _transient_read_request_membership_witnesses, - root] = get_random_reads(first_nullifier, contract_address, 2); - private_inputs.previous_kernel.public_inputs.constants.block_header.note_hash_tree_root = root; - private_inputs.private_call.call_stack_item.public_inputs.block_header.note_hash_tree_root = root; - private_inputs.private_call.call_stack_item.public_inputs.read_requests = read_requests; - private_inputs.private_call.read_request_membership_witnesses = read_request_membership_witnesses; - - // We need to update the previous_kernel's private_call_stack because the current_call_stack_item has changed - // i.e. we changed the public_inputs->read_requests of the current_call_stack_item - private_inputs.previous_kernel.public_inputs.end.private_call_stack[0] = - private_inputs.private_call.call_stack_item.hash(); - - DummyBuilder builder = DummyBuilder("native_private_kernel_inner_tests__native_two_read_requests_works"); - auto const& public_inputs = native_private_kernel_circuit_inner(builder, private_inputs); - - validate_no_new_deployed_contract(public_inputs); - - auto failure = builder.get_first_failure(); - if (failure.code != CircuitErrorCode::NO_ERROR) { - info("failure: ", failure); - } - ASSERT_FALSE(builder.failed()); - - // non-transient read requests are NOT forwarded - ASSERT_EQ(array_length(public_inputs.end.read_requests), 0); -} - -TEST_F(native_private_kernel_inner_tests, native_max_read_requests_works) -{ - // max read requests should work - - auto private_inputs = do_private_call_get_kernel_inputs_inner(false, deposit, standard_test_args()); - - auto const& contract_address = - private_inputs.private_call.call_stack_item.public_inputs.call_context.storage_contract_address; - - auto const first_nullifier = - silo_nullifier(contract_address, private_inputs.previous_kernel.public_inputs.end.new_nullifiers[0]); - auto [read_requests, - read_request_membership_witnesses, - _transient_read_requests, - _transient_read_request_membership_witnesses, - root] = get_random_reads(first_nullifier, contract_address, MAX_READ_REQUESTS_PER_CALL); - private_inputs.previous_kernel.public_inputs.constants.block_header.note_hash_tree_root = root; - private_inputs.private_call.call_stack_item.public_inputs.block_header.note_hash_tree_root = root; - private_inputs.private_call.call_stack_item.public_inputs.read_requests = read_requests; - private_inputs.private_call.read_request_membership_witnesses = read_request_membership_witnesses; - - // We need to update the previous_kernel's private_call_stack because the current_call_stack_item has changed - // i.e. we changed the public_inputs->read_requests and public_inputs->historical_note_hash_tree_root of the - // current_call_stack_item - private_inputs.previous_kernel.public_inputs.end.private_call_stack[0] = - private_inputs.private_call.call_stack_item.hash(); - - DummyBuilder builder = DummyBuilder("native_private_kernel_inner_tests__native_max_read_requests_works"); - auto const& public_inputs = native_private_kernel_circuit_inner(builder, private_inputs); - - validate_no_new_deployed_contract(public_inputs); - - auto failure = builder.get_first_failure(); - if (failure.code != CircuitErrorCode::NO_ERROR) { - info("failure: ", failure); - } - ASSERT_FALSE(builder.failed()); - - // non-transient read requests are NOT forwarded - ASSERT_EQ(array_length(public_inputs.end.read_requests), 0); -} - -TEST_F(native_private_kernel_inner_tests, native_one_transient_read_requests_works) -{ - // one transient read request should work - - auto private_inputs = do_private_call_get_kernel_inputs_inner(false, deposit, standard_test_args()); - - auto const& contract_address = - private_inputs.private_call.call_stack_item.public_inputs.call_context.storage_contract_address; - - auto const first_nullifier = - silo_nullifier(contract_address, private_inputs.previous_kernel.public_inputs.end.new_nullifiers[0]); - auto [read_requests, - read_request_membership_witnesses, - transient_read_requests, - transient_read_request_membership_witnesses, - root] = get_random_reads(first_nullifier, contract_address, 1); - private_inputs.private_call.call_stack_item.public_inputs.block_header.note_hash_tree_root = root; - - // Make the read request transient - read_requests[0] = transient_read_requests[0]; - read_request_membership_witnesses[0] = transient_read_request_membership_witnesses[0]; - private_inputs.private_call.call_stack_item.public_inputs.read_requests = read_requests; - private_inputs.private_call.read_request_membership_witnesses = read_request_membership_witnesses; - - // We need to update the previous_kernel's private_call_stack because the current_call_stack_item has changed - // i.e. we changed the public_inputs->read_requests and public_inputs->historical_note_hash_tree_root of the - // current_call_stack_item - private_inputs.previous_kernel.public_inputs.end.private_call_stack[0] = - private_inputs.private_call.call_stack_item.hash(); - - DummyBuilder builder = DummyBuilder("native_private_kernel_inner_tests__native_one_transient_read_requests_works"); - auto const& public_inputs = native_private_kernel_circuit_inner(builder, private_inputs); - - validate_no_new_deployed_contract(public_inputs); - - auto failure = builder.get_first_failure(); - if (failure.code != CircuitErrorCode::NO_ERROR) { - info("failure: ", failure); - } - ASSERT_FALSE(builder.failed()); - - ASSERT_EQ(array_length(public_inputs.end.read_requests), 1); // transient read request gets forwarded -} - -TEST_F(native_private_kernel_inner_tests, native_max_read_requests_one_transient_works) -{ - // max read requests with one transient should work - - auto private_inputs = do_private_call_get_kernel_inputs_inner(false, deposit, standard_test_args()); - - auto const& contract_address = - private_inputs.private_call.call_stack_item.public_inputs.call_context.storage_contract_address; - - auto const first_nullifier = - silo_nullifier(contract_address, private_inputs.previous_kernel.public_inputs.end.new_nullifiers[0]); - auto [read_requests, - read_request_membership_witnesses, - transient_read_requests, - transient_read_request_membership_witnesses, - root] = get_random_reads(first_nullifier, contract_address, MAX_READ_REQUESTS_PER_CALL); - private_inputs.previous_kernel.public_inputs.constants.block_header.note_hash_tree_root = root; - private_inputs.private_call.call_stack_item.public_inputs.block_header.note_hash_tree_root = root; - - // Make the read request at position 1 transient - read_requests[1] = transient_read_requests[1]; - read_request_membership_witnesses[1] = transient_read_request_membership_witnesses[1]; - private_inputs.private_call.call_stack_item.public_inputs.read_requests = read_requests; - private_inputs.private_call.read_request_membership_witnesses = read_request_membership_witnesses; - - // We need to update the previous_kernel's private_call_stack because the current_call_stack_item has changed - // i.e. we changed the public_inputs->read_requests and public_inputs->historical_note_hash_tree_root of the - // current_call_stack_item - private_inputs.previous_kernel.public_inputs.end.private_call_stack[0] = - private_inputs.private_call.call_stack_item.hash(); - - DummyBuilder builder = - DummyBuilder("native_private_kernel_inner_tests__native_max_read_requests_one_transient_works"); - auto const& public_inputs = native_private_kernel_circuit_inner(builder, private_inputs); - - validate_no_new_deployed_contract(public_inputs); - - auto failure = builder.get_first_failure(); - if (failure.code != CircuitErrorCode::NO_ERROR) { - info("failure: ", failure); - } - ASSERT_FALSE(builder.failed()); - - // transient read request gets forwarded - ASSERT_EQ(array_length(public_inputs.end.read_requests), 1); -} - -TEST_F(native_private_kernel_inner_tests, native_max_read_requests_all_transient_works) -{ - // max read requests with all transient should work - - auto private_inputs = do_private_call_get_kernel_inputs_inner(false, deposit, standard_test_args()); - - auto const& contract_address = - private_inputs.private_call.call_stack_item.public_inputs.call_context.storage_contract_address; - - auto const first_nullifier = - silo_nullifier(contract_address, private_inputs.previous_kernel.public_inputs.end.new_nullifiers[0]); - auto [read_requests, - read_request_membership_witnesses, - transient_read_requests, - transient_read_request_membership_witnesses, - root] = get_random_reads(first_nullifier, contract_address, MAX_READ_REQUESTS_PER_CALL); - private_inputs.previous_kernel.public_inputs.constants.block_header.note_hash_tree_root = root; - private_inputs.private_call.call_stack_item.public_inputs.block_header.note_hash_tree_root = root; - private_inputs.private_call.call_stack_item.public_inputs.read_requests = transient_read_requests; - private_inputs.private_call.read_request_membership_witnesses = transient_read_request_membership_witnesses; - - // We need to update the previous_kernel's private_call_stack because the current_call_stack_item has changed - // i.e. we changed the public_inputs->read_requests and public_inputs->historical_note_hash_tree_root of the - // current_call_stack_item - private_inputs.previous_kernel.public_inputs.end.private_call_stack[0] = - private_inputs.private_call.call_stack_item.hash(); - - DummyBuilder builder = - DummyBuilder("native_private_kernel_inner_tests__native_max_read_requests_one_transient_works"); - auto const& public_inputs = native_private_kernel_circuit_inner(builder, private_inputs); - - validate_no_new_deployed_contract(public_inputs); - - auto failure = builder.get_first_failure(); - if (failure.code != CircuitErrorCode::NO_ERROR) { - info("failure: ", failure); - } - ASSERT_FALSE(builder.failed()); - - // transient read request all get forwarded - ASSERT_EQ(array_length(public_inputs.end.read_requests), MAX_READ_REQUESTS_PER_CALL); -} - -TEST_F(native_private_kernel_inner_tests, native_logs_are_hashed_as_expected) -{ - std::array const& encrypted_logs_hash = { NT::fr(16), NT::fr(69) }; - NT::fr const& encrypted_log_preimages_length = NT::fr(100); - std::array const& unencrypted_logs_hash = { NT::fr(26), NT::fr(47) }; - NT::fr const& unencrypted_log_preimages_length = NT::fr(50); - std::array const& public_inputs_encrypted_logs_hash = { NT::fr(80), NT::fr(429) }; - NT::fr const& public_inputs_encrypted_log_preimages_length = NT::fr(13); - std::array const& public_inputs_unencrypted_logs_hash = { NT::fr(956), NT::fr(112) }; - NT::fr const& public_inputs_unencrypted_log_preimages_length = NT::fr(24); - - auto private_inputs = do_private_call_get_kernel_inputs_inner(false, - deposit, - standard_test_args(), - encrypted_logs_hash, - unencrypted_logs_hash, - encrypted_log_preimages_length, - unencrypted_log_preimages_length, - public_inputs_encrypted_logs_hash, - public_inputs_unencrypted_logs_hash, - public_inputs_encrypted_log_preimages_length, - public_inputs_unencrypted_log_preimages_length); - - DummyBuilder builder = DummyBuilder("native_private_kernel_inner_tests__native_logs_are_hashed_as_expected"); - auto const& public_inputs = native_private_kernel_circuit_inner(builder, private_inputs); - - ASSERT_EQ(public_inputs.end.encrypted_log_preimages_length, - encrypted_log_preimages_length + public_inputs_encrypted_log_preimages_length); - ASSERT_EQ(public_inputs.end.unencrypted_log_preimages_length, - unencrypted_log_preimages_length + public_inputs_unencrypted_log_preimages_length); - - auto const& expected_encrypted_logs_hash = accumulate_sha256({ public_inputs_encrypted_logs_hash[0], - public_inputs_encrypted_logs_hash[1], - encrypted_logs_hash[0], - encrypted_logs_hash[1] }); - - ASSERT_EQ(public_inputs.end.encrypted_logs_hash, expected_encrypted_logs_hash); - - auto const& expected_unencrypted_logs_hash = accumulate_sha256({ public_inputs_unencrypted_logs_hash[0], - public_inputs_unencrypted_logs_hash[1], - unencrypted_logs_hash[0], - unencrypted_logs_hash[1] }); - - ASSERT_EQ(public_inputs.end.unencrypted_logs_hash, expected_unencrypted_logs_hash); -} - -TEST_F(native_private_kernel_inner_tests, 0th_nullifier_zero_fails) -{ - auto private_inputs = do_private_call_get_kernel_inputs_inner(false, deposit, standard_test_args()); - - // Change the 0th nullifier to be zero. - private_inputs.previous_kernel.public_inputs.end.new_nullifiers[0] = 0; - - // Invoke the native private kernel circuit - DummyBuilder builder = DummyBuilder("private_kernel_tests__0th_nullifier_zero_fails"); - native_private_kernel_circuit_inner(builder, private_inputs); - - // Assertion checks - EXPECT_TRUE(builder.failed()); - EXPECT_EQ(builder.get_first_failure().code, CircuitErrorCode::PRIVATE_KERNEL__0TH_NULLIFIER_IS_ZERO); -} - -} // namespace aztec3::circuits::kernel::private_kernel diff --git a/circuits/cpp/src/aztec3/circuits/kernel/private/native_private_kernel_circuit_ordering.cpp b/circuits/cpp/src/aztec3/circuits/kernel/private/native_private_kernel_circuit_ordering.cpp deleted file mode 100644 index 19c21eecfb5..00000000000 --- a/circuits/cpp/src/aztec3/circuits/kernel/private/native_private_kernel_circuit_ordering.cpp +++ /dev/null @@ -1,192 +0,0 @@ -#include "common.hpp" -#include "init.hpp" - -#include "aztec3/circuits/abis/kernel_circuit_public_inputs_final.hpp" -#include "aztec3/circuits/abis/previous_kernel_data.hpp" -#include "aztec3/circuits/abis/private_kernel/private_kernel_inputs_ordering.hpp" -#include "aztec3/circuits/hash.hpp" -#include "aztec3/constants.hpp" -#include "aztec3/utils/array.hpp" -#include "aztec3/utils/circuit_errors.hpp" -#include "aztec3/utils/dummy_circuit_builder.hpp" - -#include - -namespace { -using NT = aztec3::utils::types::NativeTypes; - -using aztec3::circuits::abis::KernelCircuitPublicInputsFinal; -using aztec3::circuits::abis::PreviousKernelData; -using aztec3::circuits::abis::private_kernel::PrivateKernelInputsOrdering; -using aztec3::circuits::kernel::private_kernel::common_initialize_end_values; -using aztec3::utils::array_rearrange; -using aztec3::utils::CircuitErrorCode; -using aztec3::utils::DummyCircuitBuilder; - -void initialize_end_values(PreviousKernelData const& previous_kernel, - KernelCircuitPublicInputsFinal& public_inputs) -{ - common_initialize_end_values(previous_kernel, public_inputs); - public_inputs.end.new_contracts = previous_kernel.public_inputs.end.new_contracts; -} -} // namespace - - -namespace aztec3::circuits::kernel::private_kernel { - -void match_reads_to_commitments(DummyCircuitBuilder& builder, - std::array const& read_requests, - std::array const& read_commitment_hints, - std::array const& new_commitments) -{ - // match reads to commitments from the previous call(s) - for (size_t rr_idx = 0; rr_idx < MAX_READ_REQUESTS_PER_TX; rr_idx++) { - const auto& read_request = read_requests[rr_idx]; - const auto& read_commitment_hint = read_commitment_hints[rr_idx]; - const auto hint_pos = static_cast(uint64_t(read_commitment_hint)); - - if (read_request != 0) { - size_t match_pos = MAX_NEW_COMMITMENTS_PER_TX; - if (hint_pos < MAX_NEW_COMMITMENTS_PER_TX) { - match_pos = read_request == new_commitments[hint_pos] ? hint_pos : match_pos; - } - - builder.do_assert( - match_pos != MAX_NEW_COMMITMENTS_PER_TX, - format("read_request at position [", - rr_idx, - "]* is transient but does not match any new commitment.", - "\n\tread_request: ", - read_request, - "\n\thint_to_commitment: ", - read_commitment_hint, - "\n\t* the read_request position/index is not expected to match position in app-circuit " - "outputs because kernel iterations gradually remove non-transient read_requests as " - "membership checks are resolved."), - CircuitErrorCode::PRIVATE_KERNEL__TRANSIENT_READ_REQUEST_NO_MATCH); - } - } -} - -/** - * @brief This function matches transient nullifiers to commitments and squashes (deletes) them both. - * - * @details A non-zero entry in nullified_commitments at position i implies that - * 1) new_commitments array contains at least an occurrence of nullified_commitments[i] - * 2) this commitment is nullified by new_nullifiers[i] (according to app circuit, the kernel cannot check this on its - * own.) - * Remark: We do not check that new_nullifiers[i] is non-empty. (app circuit responsibility) - * - * @param builder - * @param new_nullifiers public_input's nullifiers that should be squashed when matching a transient commitment - * @param nullified_commitments commitments that each new_nullifier nullifies. 0 here implies non-transient nullifier, - * and a non-zero `nullified_commitment` implies a transient nullifier that MUST be matched to a new_commitment. - * @param new_commitments public_input's commitments to be matched against and squashed when matched to a transient - * nullifier. - */ -void match_nullifiers_to_commitments_and_squash( - DummyCircuitBuilder& builder, - std::array& new_nullifiers, - std::array const& nullified_commitments, - std::array const& nullifier_commitment_hints, - std::array& new_commitments) -{ - // match nullifiers/nullified_commitments to commitments from the previous call(s) - for (size_t n_idx = 0; n_idx < MAX_NEW_NULLIFIERS_PER_TX; n_idx++) { - const auto& nullified_commitment = nullified_commitments[n_idx]; - const auto& nullifier_commitment_hint = nullifier_commitment_hints[n_idx]; - const auto hint_pos = static_cast(uint64_t(nullifier_commitment_hint)); - // Nullified_commitment of value `EMPTY_NULLIFIED_COMMITMENT` implies non-transient (persistable) - // nullifier in which case no attempt will be made to match it to a commitment. - // Non-empty nullified_commitment implies transient nullifier which MUST be matched to a commitment below! - // 0-valued nullified_commitment is empty and will be ignored - if (nullified_commitments[n_idx] != NT::fr(0) && - nullified_commitments[n_idx] != NT::fr(EMPTY_NULLIFIED_COMMITMENT)) { - size_t match_pos = MAX_NEW_COMMITMENTS_PER_TX; - - if (hint_pos < MAX_NEW_COMMITMENTS_PER_TX) { - match_pos = nullified_commitment == new_commitments[hint_pos] ? hint_pos : match_pos; - } - - if (match_pos != MAX_NEW_COMMITMENTS_PER_TX) { - // match found! - // squash both the nullifier and the commitment - // (set to 0 here and then rearrange array after loop) - important("chopped commitment for siloed inner hash note \n", new_commitments[match_pos]); - new_commitments[match_pos] = NT::fr(0); - new_nullifiers[n_idx] = NT::fr(0); - } else { - // Transient nullifiers MUST match a pending commitment - builder.do_assert(false, - format("new_nullifier at position [", - n_idx, - "]* is transient but does not match any new commitment.", - "\n\tnullifier: ", - new_nullifiers[n_idx], - "\n\tnullified_commitment: ", - nullified_commitments[n_idx]), - CircuitErrorCode::PRIVATE_KERNEL__TRANSIENT_NEW_NULLIFIER_NO_MATCH); - } - } - // non-transient (persistable) nullifiers are just kept in new_nullifiers array and forwarded - // to public inputs (used later by base rollup circuit) - } - // Move all zero-ed (removed) entries of these arrays to the end and preserve ordering of other entries - array_rearrange(new_commitments); - array_rearrange(new_nullifiers); -} - -void apply_commitment_nonces(NT::fr const& first_nullifier, - std::array& new_commitments) -{ - for (size_t c_idx = 0; c_idx < MAX_NEW_COMMITMENTS_PER_TX; c_idx++) { - // Apply nonce to all non-zero/non-empty commitments - // Nonce is the hash of the first (0th) nullifier and the commitment's index into new_commitments array - const auto nonce = compute_commitment_nonce(first_nullifier, c_idx); - new_commitments[c_idx] = - new_commitments[c_idx] == 0 ? 0 : compute_unique_commitment(nonce, new_commitments[c_idx]); - } -} - -KernelCircuitPublicInputsFinal native_private_kernel_circuit_ordering( - DummyCircuitBuilder& builder, PrivateKernelInputsOrdering const& private_inputs) -{ - // We'll be pushing data to this during execution of this circuit. - KernelCircuitPublicInputsFinal public_inputs{}; - - common_validate_previous_kernel_values(builder, private_inputs.previous_kernel.public_inputs.end); - - // Do this before any functions can modify the inputs. - initialize_end_values(private_inputs.previous_kernel, public_inputs); - - // TODO(https://github.com/AztecProtocol/aztec-packages/issues/1486): validate that `len(new_nullifiers) == - // len(nullified_commitments)` - - // Matching read requests to pending commitments requires the full list of new commitments accumulated over - // all iterations of the private kernel. Therefore, we match reads against new_commitments in - // previous_kernel.public_inputs.end, where "previous kernel" is the last "inner" kernel iteration. - // Remark: The commitments in public_inputs.end have already been siloed by contract address! - match_reads_to_commitments(builder, - private_inputs.previous_kernel.public_inputs.end.read_requests, - private_inputs.read_commitment_hints, - private_inputs.previous_kernel.public_inputs.end.new_commitments); - - // Matching nullifiers to pending commitments requires the full list of new commitments accumulated over - // all iterations of the private kernel. Therefore, we match nullifiers (their nullified_commitments) - // against new_commitments in public_inputs.end which has been initialized to - // previous_kernel.public_inputs.end in common_initialize_*() above. - // Remark: The commitments in public_inputs.end have already been siloed by contract address! - match_nullifiers_to_commitments_and_squash(builder, - public_inputs.end.new_nullifiers, - public_inputs.end.nullified_commitments, - private_inputs.nullifier_commitment_hints, - public_inputs.end.new_commitments); - - // tx hash - const auto& first_nullifier = private_inputs.previous_kernel.public_inputs.end.new_nullifiers[0]; - apply_commitment_nonces(first_nullifier, public_inputs.end.new_commitments); - - return public_inputs; -}; - -} // namespace aztec3::circuits::kernel::private_kernel \ No newline at end of file diff --git a/circuits/cpp/src/aztec3/circuits/kernel/private/native_private_kernel_circuit_ordering.hpp b/circuits/cpp/src/aztec3/circuits/kernel/private/native_private_kernel_circuit_ordering.hpp deleted file mode 100644 index 872396c6178..00000000000 --- a/circuits/cpp/src/aztec3/circuits/kernel/private/native_private_kernel_circuit_ordering.hpp +++ /dev/null @@ -1,20 +0,0 @@ -#pragma once - -#include "init.hpp" - -#include "aztec3/circuits/abis/kernel_circuit_public_inputs_final.hpp" -#include "aztec3/circuits/abis/private_kernel/private_kernel_inputs_ordering.hpp" -#include "aztec3/utils/dummy_circuit_builder.hpp" - -#include - -namespace aztec3::circuits::kernel::private_kernel { - -using aztec3::circuits::abis::KernelCircuitPublicInputsFinal; -using aztec3::circuits::abis::private_kernel::PrivateKernelInputsOrdering; -using DummyBuilder = aztec3::utils::DummyCircuitBuilder; - -KernelCircuitPublicInputsFinal native_private_kernel_circuit_ordering( - DummyBuilder& builder, PrivateKernelInputsOrdering const& private_inputs); - -} // namespace aztec3::circuits::kernel::private_kernel \ No newline at end of file diff --git a/circuits/cpp/src/aztec3/circuits/kernel/private/native_private_kernel_circuit_ordering.test.cpp b/circuits/cpp/src/aztec3/circuits/kernel/private/native_private_kernel_circuit_ordering.test.cpp deleted file mode 100644 index 76e87056ea8..00000000000 --- a/circuits/cpp/src/aztec3/circuits/kernel/private/native_private_kernel_circuit_ordering.test.cpp +++ /dev/null @@ -1,510 +0,0 @@ -#include "testing_harness.hpp" - -#include "aztec3/circuits/apps/test_apps/escrow/deposit.hpp" -#include "aztec3/circuits/kernel/private/common.hpp" -#include "aztec3/constants.hpp" -#include "aztec3/utils/array.hpp" -#include "aztec3/utils/circuit_errors.hpp" - -#include "barretenberg/ecc/curves/bn254/fr.hpp" -#include - -#include - -#include -#include - -namespace aztec3::circuits::kernel::private_kernel { - -using aztec3::circuits::apps::test_apps::escrow::deposit; - -using aztec3::circuits::kernel::private_kernel::testing_harness::do_private_call_get_kernel_inputs_inner; -using aztec3::utils::array_length; -using aztec3::utils::CircuitErrorCode; - -// NOTE: *DO NOT* call fr constructors in static initializers and assign them to constants. This will fail. Instead, use -// lazy initialization or functions. Lambdas were introduced here. -// amount = 5, asset_id = 1, memo = 999 -const auto standard_test_args = [] { return std::vector{ NT::fr(5), NT::fr(1), NT::fr(999) }; }; -class native_private_kernel_ordering_tests : public ::testing::Test { - protected: - static void SetUpTestSuite() { barretenberg::srs::init_crs_factory("../barretenberg/cpp/srs_db/ignition"); } -}; - -// TODO(1998): testing cbind calls private_kernel__sim_ordering, private_kernel__sim_init, private_kernel__sim_inner -// in their respective test suites once msgpack capabilities allow it. One current limitation is due to -// the lack of support to deserialize std::variant in particular CircuitResult type. -// See https://github.com/AztecProtocol/aztec-packages/issues/1998 -/** - * @brief Test cbind - */ -// TEST_F(native_private_kernel_ordering_tests, cbind_private_kernel__sim_ordering) -// { -// auto func = [](PrivateKernelInputsOrdering private_inputs) { -// DummyCircuitBuilder builder = DummyCircuitBuilder("private_kernel__sim_ordering"); -// auto const& public_inputs = native_private_kernel_circuit_ordering(builder, private_inputs); -// return builder.result_or_error(public_inputs); -// }; - -// NT::fr const& amount = 5; -// NT::fr const& asset_id = 1; -// NT::fr const& memo = 999; -// std::array const& empty_logs_hash = { NT::fr(16), NT::fr(69) }; -// NT::fr const& empty_log_preimages_length = NT::fr(100); - -// // Generate private inputs including proofs and vkeys for app circuit and previous kernel -// auto const& private_inputs_inner = do_private_call_get_kernel_inputs_inner(false, -// deposit, -// { amount, asset_id, memo }, -// empty_logs_hash, -// empty_logs_hash, -// empty_log_preimages_length, -// empty_log_preimages_length, -// empty_logs_hash, -// empty_logs_hash, -// empty_log_preimages_length, -// empty_log_preimages_length, -// true); -// PrivateKernelInputsOrdering private_inputs{ private_inputs_inner.previous_kernel, -// std::array{ fr(123), fr(89) } }; - -// auto [actual, expected] = call_func_and_wrapper(func, private_kernel__sim_ordering, private_inputs); - -// std::stringstream actual_ss; -// std::stringstream expected_ss; -// actual_ss << actual; -// expected_ss << expected; -// EXPECT_EQ(actual_ss.str(), expected_ss.str()); -// } - -TEST_F(native_private_kernel_ordering_tests, native_matching_one_read_request_to_commitment_works) -{ - auto private_inputs_inner = do_private_call_get_kernel_inputs_inner(false, deposit, standard_test_args()); - - std::array new_nullifiers{}; - std::array siloed_commitments{}; - std::array unique_siloed_commitments{}; - std::array read_requests{}; - std::array hints{}; - - std::array, MAX_READ_REQUESTS_PER_TX> - read_request_membership_witnesses{}; - - new_nullifiers[0] = NT::fr::random_element(); - siloed_commitments[0] = NT::fr::random_element(); // create random commitment - // ordering circuit applies nonces to commitments - const auto nonce = compute_commitment_nonce(new_nullifiers[0], 0); - unique_siloed_commitments[0] = - siloed_commitments[0] == 0 ? 0 : compute_unique_commitment(nonce, siloed_commitments[0]); - - read_requests[0] = siloed_commitments[0]; - // hints[0] == fr(0) due to the default initialization of hints - read_request_membership_witnesses[0].is_transient = true; - - auto& previous_kernel = private_inputs_inner.previous_kernel; - - previous_kernel.public_inputs.end.new_nullifiers = new_nullifiers; - previous_kernel.public_inputs.end.new_commitments = siloed_commitments; - previous_kernel.public_inputs.end.read_requests = read_requests; - - PrivateKernelInputsOrdering private_inputs{ previous_kernel, hints }; - - DummyBuilder builder = - DummyBuilder("native_private_kernel_ordering_tests__native_matching_one_read_request_to_commitment_works"); - auto const& public_inputs = native_private_kernel_circuit_ordering(builder, private_inputs); - - ASSERT_FALSE(builder.failed()) << "failure: " << builder.get_first_failure(); - ASSERT_TRUE(array_length(public_inputs.end.new_commitments) == 1); - ASSERT_TRUE(public_inputs.end.new_commitments[0] == unique_siloed_commitments[0]); -} - -TEST_F(native_private_kernel_ordering_tests, native_matching_some_read_requests_to_commitments_works) -{ - auto private_inputs_inner = do_private_call_get_kernel_inputs_inner(false, deposit, standard_test_args()); - - std::array new_nullifiers{}; - std::array siloed_commitments{}; - std::array unique_siloed_commitments{}; - std::array read_requests{}; - std::array hints{}; - - std::array, MAX_READ_REQUESTS_PER_TX> - read_request_membership_witnesses{}; - - new_nullifiers[0] = NT::fr::random_element(); - const auto& first_nullifier = new_nullifiers[0]; - // create random commitments to input to ordering circuit, and compute their "unique" versions - // to be expected at the output - for (size_t c_idx = 0; c_idx < MAX_NEW_COMMITMENTS_PER_TX; c_idx++) { - siloed_commitments[c_idx] = NT::fr::random_element(); // create random commitment - // ordering circuit applies nonces to commitments - const auto nonce = compute_commitment_nonce(first_nullifier, c_idx); - unique_siloed_commitments[c_idx] = - siloed_commitments[c_idx] == 0 ? 0 : compute_unique_commitment(nonce, siloed_commitments[c_idx]); - } - - read_requests[0] = siloed_commitments[1]; - read_requests[1] = siloed_commitments[3]; - read_request_membership_witnesses[0].is_transient = true; - read_request_membership_witnesses[1].is_transient = true; - hints[0] = fr(1); - hints[1] = fr(3); - - auto& previous_kernel = private_inputs_inner.previous_kernel; - - previous_kernel.public_inputs.end.new_nullifiers = new_nullifiers; - previous_kernel.public_inputs.end.new_commitments = siloed_commitments; - previous_kernel.public_inputs.end.read_requests = read_requests; - - PrivateKernelInputsOrdering private_inputs{ previous_kernel, hints }; - - DummyBuilder builder = - DummyBuilder("native_private_kernel_ordering_tests__native_matching_some_read_requests_to_commitments_works"); - auto const& public_inputs = native_private_kernel_circuit_ordering(builder, private_inputs); - - ASSERT_FALSE(builder.failed()) << "failure: " << builder.get_first_failure(); - ASSERT_TRUE(array_length(public_inputs.end.new_commitments) == MAX_NEW_COMMITMENTS_PER_TX); - // ensure that commitments had nonce applied properly and all appear at output - for (size_t c_idx = 0; c_idx < MAX_NEW_COMMITMENTS_PER_TX; c_idx++) { - ASSERT_TRUE(public_inputs.end.new_commitments[c_idx] == unique_siloed_commitments[c_idx]); - } -} - -TEST_F(native_private_kernel_ordering_tests, native_read_request_unknown_fails) -{ - auto private_inputs_inner = do_private_call_get_kernel_inputs_inner(false, deposit, standard_test_args()); - - std::array siloed_commitments{}; - std::array read_requests{}; - std::array hints{}; - std::array, MAX_READ_REQUESTS_PER_TX> - read_request_membership_witnesses{}; - - for (size_t c_idx = 0; c_idx < MAX_NEW_COMMITMENTS_PER_TX; c_idx++) { - siloed_commitments[c_idx] = NT::fr::random_element(); // create random commitment - read_requests[c_idx] = siloed_commitments[c_idx]; // create random read requests - hints[c_idx] = fr(c_idx); // ^ will match each other! - read_request_membership_witnesses[c_idx].is_transient = true; // ordering circuit only allows transient reads - } - read_requests[3] = NT::fr::random_element(); // force one read request not to match - - auto& previous_kernel = private_inputs_inner.previous_kernel; - - previous_kernel.public_inputs.end.new_commitments = siloed_commitments; - previous_kernel.public_inputs.end.read_requests = read_requests; - - PrivateKernelInputsOrdering private_inputs{ previous_kernel, hints }; - - DummyBuilder builder = DummyBuilder("native_private_kernel_ordering_tests__native_read_request_unknown_fails"); - native_private_kernel_circuit_ordering(builder, private_inputs); - - auto failure = builder.get_first_failure(); - ASSERT_EQ(failure.code, CircuitErrorCode::PRIVATE_KERNEL__TRANSIENT_READ_REQUEST_NO_MATCH); -} - -TEST_F(native_private_kernel_ordering_tests, native_squash_one_of_one_transient_matches_works) -{ - auto private_inputs_inner = do_private_call_get_kernel_inputs_inner(false, deposit, standard_test_args()); - - std::array new_commitments{}; - std::array new_nullifiers{}; - std::array nullifier_commitments{}; - - const auto commitment0 = fr(213); - new_commitments[0] = commitment0; - - new_nullifiers[0] = fr(32); - nullifier_commitments[0] = commitment0; - - auto& previous_kernel = private_inputs_inner.previous_kernel; - - previous_kernel.public_inputs.end.new_commitments = new_commitments; - previous_kernel.public_inputs.end.new_nullifiers = new_nullifiers; - previous_kernel.public_inputs.end.nullified_commitments = nullifier_commitments; - - // Correct nullifier_commitment hint for new_nullifiers[0] == 0 is correct due to the default - // initialization of the array. - PrivateKernelInputsOrdering private_inputs{ .previous_kernel = previous_kernel }; - - DummyBuilder builder = - DummyBuilder("native_private_kernel_ordering_tests__native_squash_one_of_one_transient_matches_works"); - auto public_inputs = native_private_kernel_circuit_ordering(builder, private_inputs); - - ASSERT_FALSE(builder.failed()) << "failure: " << builder.get_first_failure(); - ASSERT_TRUE(array_length(public_inputs.end.new_commitments) == 0); // 1/1 squashed - ASSERT_TRUE(array_length(public_inputs.end.new_nullifiers) == 0); // 1/1 squashed -} - -TEST_F(native_private_kernel_ordering_tests, native_squash_one_of_two_transient_matches_works) -{ - auto private_inputs_inner = do_private_call_get_kernel_inputs_inner(false, deposit, standard_test_args()); - - std::array new_commitments{}; - std::array new_nullifiers{}; - std::array nullifier_commitments{}; - - const auto commitment1 = fr(213); - new_commitments[0] = fr(763); - new_commitments[1] = commitment1; - - new_nullifiers[0] = fr(32); - nullifier_commitments[0] = commitment1; - - auto& previous_kernel = private_inputs_inner.previous_kernel; - - previous_kernel.public_inputs.end.new_commitments = new_commitments; - previous_kernel.public_inputs.end.new_nullifiers = new_nullifiers; - previous_kernel.public_inputs.end.nullified_commitments = nullifier_commitments; - - PrivateKernelInputsOrdering private_inputs{ .previous_kernel = previous_kernel, - .nullifier_commitment_hints = - std::array{ 1 } }; - - DummyBuilder builder = - DummyBuilder("native_private_kernel_ordering_tests__native_squash_one_of_two_transient_matches_works"); - auto public_inputs = native_private_kernel_circuit_ordering(builder, private_inputs); - - ASSERT_FALSE(builder.failed()) << "failure: " << builder.get_first_failure(); - ASSERT_TRUE(array_length(public_inputs.end.new_commitments) == 1); // 1/2 squashed - ASSERT_TRUE(array_length(public_inputs.end.new_nullifiers) == 0); // 1/1 squashed -} - -TEST_F(native_private_kernel_ordering_tests, native_squash_two_of_two_transient_matches_works) -{ - auto private_inputs_inner = do_private_call_get_kernel_inputs_inner(false, deposit, standard_test_args()); - - std::array new_commitments{}; - std::array new_nullifiers{}; - std::array nullifier_commitments{}; - - const auto commitment0 = fr(763); - const auto commitment1 = fr(213); - new_commitments[0] = commitment0; - new_commitments[1] = commitment1; - - new_nullifiers[0] = fr(32); - new_nullifiers[1] = fr(43); - nullifier_commitments[0] = commitment1; - nullifier_commitments[1] = commitment0; - - auto& previous_kernel = private_inputs_inner.previous_kernel; - - previous_kernel.public_inputs.end.new_commitments = new_commitments; - previous_kernel.public_inputs.end.new_nullifiers = new_nullifiers; - previous_kernel.public_inputs.end.nullified_commitments = nullifier_commitments; - - PrivateKernelInputsOrdering private_inputs{ .previous_kernel = previous_kernel, - .nullifier_commitment_hints = - std::array{ 1 } }; - - DummyBuilder builder = - DummyBuilder("native_private_kernel_ordering_tests__native_squash_two_of_two_transient_matches_works"); - auto public_inputs = native_private_kernel_circuit_ordering(builder, private_inputs); - - ASSERT_FALSE(builder.failed()) << "failure: " << builder.get_first_failure(); - ASSERT_TRUE(array_length(public_inputs.end.new_commitments) == 0); // 2/2 squashed - ASSERT_TRUE(array_length(public_inputs.end.new_nullifiers) == 0); // 2/2 squashed -} - -TEST_F(native_private_kernel_ordering_tests, native_empty_nullified_commitment_means_persistent_nullifier_0) -{ - auto private_inputs_inner = do_private_call_get_kernel_inputs_inner(false, deposit, standard_test_args()); - - std::array new_commitments{}; - std::array new_nullifiers{}; - std::array nullifier_commitments{}; - - new_commitments[0] = fr(213); - - new_nullifiers[0] = fr(32); - nullifier_commitments[0] = fr(EMPTY_NULLIFIED_COMMITMENT); - - auto& previous_kernel = private_inputs_inner.previous_kernel; - - previous_kernel.public_inputs.end.new_commitments = new_commitments; - previous_kernel.public_inputs.end.new_nullifiers = new_nullifiers; - previous_kernel.public_inputs.end.nullified_commitments = nullifier_commitments; - - PrivateKernelInputsOrdering private_inputs{ .previous_kernel = previous_kernel }; - - DummyBuilder builder = DummyBuilder( - "native_private_kernel_ordering_tests__native_empty_nullified_commitment_means_persistent_nullifier_0"); - auto public_inputs = native_private_kernel_circuit_ordering(builder, private_inputs); - - ASSERT_FALSE(builder.failed()) << "failure: " << builder.get_first_failure(); - // nullifier and commitment present at output (will become persistent) - ASSERT_TRUE(array_length(public_inputs.end.new_commitments) == 1); - ASSERT_TRUE(array_length(public_inputs.end.new_nullifiers) == 1); -} - -// same as previous test, but this time there are 0 commitments! -TEST_F(native_private_kernel_ordering_tests, native_empty_nullified_commitment_means_persistent_nullifier_1) -{ - auto private_inputs_inner = do_private_call_get_kernel_inputs_inner(false, deposit, standard_test_args()); - - std::array new_commitments{}; - std::array new_nullifiers{}; - std::array nullifier_commitments{}; - - new_nullifiers[0] = fr(32); - nullifier_commitments[0] = fr(EMPTY_NULLIFIED_COMMITMENT); - - auto& previous_kernel = private_inputs_inner.previous_kernel; - - previous_kernel.public_inputs.end.new_commitments = new_commitments; - previous_kernel.public_inputs.end.new_nullifiers = new_nullifiers; - previous_kernel.public_inputs.end.nullified_commitments = nullifier_commitments; - - PrivateKernelInputsOrdering private_inputs{ .previous_kernel = previous_kernel }; - - DummyBuilder builder = DummyBuilder( - "native_private_kernel_ordering_tests__native_empty_nullified_commitment_means_persistent_nullifier_1"); - auto public_inputs = native_private_kernel_circuit_ordering(builder, private_inputs); - - ASSERT_FALSE(builder.failed()) << "failure: " << builder.get_first_failure(); - ASSERT_TRUE(array_length(public_inputs.end.new_commitments) == 0); - // nullifier present at output (will become persistent) - ASSERT_TRUE(array_length(public_inputs.end.new_nullifiers) == 1); -} - -TEST_F(native_private_kernel_ordering_tests, 0th_nullifier_zero_fails) -{ - auto private_inputs_inner = do_private_call_get_kernel_inputs_inner(false, deposit, standard_test_args()); - - std::array new_nullifiers{}; - auto& previous_kernel = private_inputs_inner.previous_kernel; - previous_kernel.public_inputs.end.new_nullifiers = new_nullifiers; - PrivateKernelInputsOrdering private_inputs{ .previous_kernel = previous_kernel }; - - DummyBuilder builder = DummyBuilder("native_private_kernel_ordering_tests__0th_nullifier_zero_fails"); - auto public_inputs = native_private_kernel_circuit_ordering(builder, private_inputs); - - ASSERT_TRUE(builder.failed()); - auto failure = builder.get_first_failure(); - ASSERT_EQ(failure.code, CircuitErrorCode::PRIVATE_KERNEL__0TH_NULLIFIER_IS_ZERO); -} - -TEST_F(native_private_kernel_ordering_tests, input_validation_malformed_end_arrays_read_requests) -{ - auto private_inputs_inner = do_private_call_get_kernel_inputs_inner(false, deposit, standard_test_args()); - - std::array malformed_read_requests{ fr(0), fr(9123), fr(0), fr(12) }; - auto& previous_kernel = private_inputs_inner.previous_kernel; - previous_kernel.public_inputs.end.read_requests = malformed_read_requests; - PrivateKernelInputsOrdering private_inputs{ .previous_kernel = previous_kernel }; - - DummyBuilder builder = - DummyBuilder("native_private_kernel_ordering_tests__input_validation_malformed_end_arrays_read_requests"); - native_private_kernel_circuit_ordering(builder, private_inputs); - - EXPECT_EQ(builder.failed(), true); - EXPECT_EQ(builder.get_first_failure().code, CircuitErrorCode::ARRAY_NOT_ZERO_RIGHT_PADDED); -} - -TEST_F(native_private_kernel_ordering_tests, input_validation_malformed_end_arrays_commitments) -{ - auto private_inputs_inner = do_private_call_get_kernel_inputs_inner(false, deposit, standard_test_args()); - - std::array malformed_commitments{ fr(0), fr(9123) }; - auto& previous_kernel = private_inputs_inner.previous_kernel; - previous_kernel.public_inputs.end.new_commitments = malformed_commitments; - PrivateKernelInputsOrdering private_inputs{ .previous_kernel = previous_kernel }; - - DummyBuilder builder = - DummyBuilder("native_private_kernel_ordering_tests__input_validation_malformed_end_arrays_commitments"); - native_private_kernel_circuit_ordering(builder, private_inputs); - - EXPECT_EQ(builder.failed(), true); - EXPECT_EQ(builder.get_first_failure().code, CircuitErrorCode::ARRAY_NOT_ZERO_RIGHT_PADDED); -} - -TEST_F(native_private_kernel_ordering_tests, input_validation_malformed_end_arrays_nullifiers) -{ - auto private_inputs_inner = do_private_call_get_kernel_inputs_inner(false, deposit, standard_test_args()); - - std::array malformed_nullifiers{}; - malformed_nullifiers[MAX_NEW_NULLIFIERS_PER_TX - 1] = fr(12); - auto& previous_kernel = private_inputs_inner.previous_kernel; - previous_kernel.public_inputs.end.new_nullifiers = malformed_nullifiers; - PrivateKernelInputsOrdering private_inputs{ .previous_kernel = previous_kernel }; - - DummyBuilder builder = - DummyBuilder("native_private_kernel_ordering_tests__input_validation_malformed_end_arrays_nullifiers"); - native_private_kernel_circuit_ordering(builder, private_inputs); - - EXPECT_EQ(builder.failed(), true); - EXPECT_EQ(builder.get_first_failure().code, CircuitErrorCode::ARRAY_NOT_ZERO_RIGHT_PADDED); -} - -TEST_F(native_private_kernel_ordering_tests, input_validation_malformed_end_arrays_nullified_commitments) -{ - auto private_inputs_inner = do_private_call_get_kernel_inputs_inner(false, deposit, standard_test_args()); - - std::array malformed_nullified_commitments{ fr(0), - fr(0), - EMPTY_NULLIFIED_COMMITMENT }; - auto& previous_kernel = private_inputs_inner.previous_kernel; - previous_kernel.public_inputs.end.nullified_commitments = malformed_nullified_commitments; - PrivateKernelInputsOrdering private_inputs{ .previous_kernel = previous_kernel }; - - DummyBuilder builder = DummyBuilder( - "native_private_kernel_ordering_tests__input_validation_malformed_end_arrays_nullified_commitments"); - native_private_kernel_circuit_ordering(builder, private_inputs); - - EXPECT_EQ(builder.failed(), true); - EXPECT_EQ(builder.get_first_failure().code, CircuitErrorCode::ARRAY_NOT_ZERO_RIGHT_PADDED); -} - -TEST_F(native_private_kernel_ordering_tests, input_validation_malformed_end_arrays_private_call_stack) -{ - auto private_inputs_inner = do_private_call_get_kernel_inputs_inner(false, deposit, standard_test_args()); - - std::array malformed_private_call_stack{ fr(0), fr(888) }; - auto& previous_kernel = private_inputs_inner.previous_kernel; - previous_kernel.public_inputs.end.private_call_stack = malformed_private_call_stack; - PrivateKernelInputsOrdering private_inputs{ .previous_kernel = previous_kernel }; - - DummyBuilder builder = - DummyBuilder("native_private_kernel_ordering_tests__input_validation_malformed_end_arrays_private_call_stack"); - native_private_kernel_circuit_ordering(builder, private_inputs); - - EXPECT_EQ(builder.failed(), true); - EXPECT_EQ(builder.get_first_failure().code, CircuitErrorCode::ARRAY_NOT_ZERO_RIGHT_PADDED); -} - -TEST_F(native_private_kernel_ordering_tests, input_validation_malformed_end_arrays_public_call_stack) -{ - auto private_inputs_inner = do_private_call_get_kernel_inputs_inner(false, deposit, standard_test_args()); - - std::array malformed_public_call_stack{ fr(0), fr(888) }; - auto& previous_kernel = private_inputs_inner.previous_kernel; - previous_kernel.public_inputs.end.public_call_stack = malformed_public_call_stack; - PrivateKernelInputsOrdering private_inputs{ .previous_kernel = previous_kernel }; - - DummyBuilder builder = - DummyBuilder("native_private_kernel_ordering_tests__input_validation_malformed_end_arrays_public_call_stack"); - native_private_kernel_circuit_ordering(builder, private_inputs); - - EXPECT_EQ(builder.failed(), true); - EXPECT_EQ(builder.get_first_failure().code, CircuitErrorCode::ARRAY_NOT_ZERO_RIGHT_PADDED); -} - -TEST_F(native_private_kernel_ordering_tests, input_validation_malformed_end_arrays_l2_to_l1_msgs) -{ - auto private_inputs_inner = do_private_call_get_kernel_inputs_inner(false, deposit, standard_test_args()); - - std::array malformed_l2_to_l1_msgs{}; - malformed_l2_to_l1_msgs[MAX_NEW_L2_TO_L1_MSGS_PER_TX - 1] = fr(1); - auto& previous_kernel = private_inputs_inner.previous_kernel; - previous_kernel.public_inputs.end.new_l2_to_l1_msgs = malformed_l2_to_l1_msgs; - PrivateKernelInputsOrdering private_inputs{ .previous_kernel = previous_kernel }; - - DummyBuilder builder = - DummyBuilder("native_private_kernel_ordering_tests__input_validation_malformed_end_arrays_l2_to_l1_msgs"); - native_private_kernel_circuit_ordering(builder, private_inputs); - - EXPECT_EQ(builder.failed(), true); - EXPECT_EQ(builder.get_first_failure().code, CircuitErrorCode::ARRAY_NOT_ZERO_RIGHT_PADDED); -} - -} // namespace aztec3::circuits::kernel::private_kernel diff --git a/circuits/cpp/src/aztec3/circuits/kernel/private/private_kernel_circuit.cpp b/circuits/cpp/src/aztec3/circuits/kernel/private/private_kernel_circuit.cpp deleted file mode 100644 index c5586701a2b..00000000000 --- a/circuits/cpp/src/aztec3/circuits/kernel/private/private_kernel_circuit.cpp +++ /dev/null @@ -1,343 +0,0 @@ -#include "init.hpp" - -#include "aztec3/circuits/abis/complete_address.hpp" -#include "aztec3/circuits/abis/kernel_circuit_public_inputs.hpp" -#include "aztec3/circuits/abis/new_contract_data.hpp" -#include "aztec3/circuits/abis/private_kernel/private_kernel_inputs_inner.hpp" -#include "aztec3/circuits/hash.hpp" -#include "aztec3/constants.hpp" - -#include - -namespace aztec3::circuits::kernel::private_kernel { - -using aztec3::circuits::abis::KernelCircuitPublicInputs; -using aztec3::circuits::abis::NewContractData; -using aztec3::circuits::abis::private_kernel::PrivateKernelInputsInner; - -using plonk::stdlib::array_length; -using plonk::stdlib::array_pop; -using plonk::stdlib::array_push; -using plonk::stdlib::is_array_empty; -using plonk::stdlib::push_array_to_array; - -using aztec3::circuits::compute_constructor_hash; -using aztec3::circuits::silo_commitment; -using aztec3::circuits::silo_nullifier; - -// TODO: NEED TO RECONCILE THE `proof`'s public inputs (which are uint8's) with the -// private_call.call_stack_item.public_inputs! -CT::AggregationObject verify_proofs(Builder& builder, PrivateKernelInputsInner const& private_inputs) -{ - // compute P0, P1 for private function proof - CT::AggregationObject aggregation_object = - Aggregator::aggregate(&builder, private_inputs.private_call.vk, private_inputs.private_call.proof); - - // computes P0, P1 for previous kernel proof - // AND accumulates all of it in P0_agg, P1_agg - Aggregator::aggregate( - &builder, private_inputs.previous_kernel.vk, private_inputs.previous_kernel.proof, aggregation_object); - - return aggregation_object; -} - -/** - * @brief fill in the initial `end` (AccumulatedData) values by copying - * contents from the previous iteration of this kernel. - * - * @param private_inputs contains the information from the previous kernel iteration - * as well as signed TX request and the private call information - * @param public_inputs should be empty here since it is being initialized in this call - */ -void initialize_end_values(PrivateKernelInputsInner const& private_inputs, - KernelCircuitPublicInputs& public_inputs) -{ - // TODO: Ensure public inputs is empty here - public_inputs.constants = private_inputs.previous_kernel.public_inputs.constants; - - // Ensure the arrays are the same as previously, before we start pushing more data onto them in other functions - // within this circuit: - auto& end = public_inputs.end; - const auto& start = private_inputs.previous_kernel.public_inputs.end; - - // TODO - // end.aggregation_object = start.aggregation_object; - - end.new_commitments = start.new_commitments; - end.new_nullifiers = start.new_nullifiers; - - end.private_call_stack = start.private_call_stack; - end.public_call_stack = start.public_call_stack; - end.new_l2_to_l1_msgs = start.new_l2_to_l1_msgs; - - // TODO - end.new_contracts = start.new_contracts; - - end.optionally_revealed_data = start.optionally_revealed_data; -} - -/** - * @brief Update the AccumulatedData with new commitments, nullifiers, contracts, etc - * and update its running callstack with all items in the current private-circuit/function's - * callstack. - */ -void update_end_values(PrivateKernelInputsInner const& private_inputs, KernelCircuitPublicInputs& public_inputs) -{ - const auto private_call_public_inputs = private_inputs.private_call.call_stack_item.public_inputs; - - // TODO: private call count - const auto& new_commitments = private_call_public_inputs.new_commitments; - const auto& new_nullifiers = private_call_public_inputs.new_nullifiers; - - const auto& is_static_call = private_call_public_inputs.call_context.is_static_call; - - // No state changes are allowed for static calls: - is_static_call.must_imply(is_array_empty(new_commitments) == true); - is_static_call.must_imply(is_array_empty(new_nullifiers) == true); - - // TODO: name change (just contract_address) - const auto& storage_contract_address = private_call_public_inputs.call_context.storage_contract_address; - const auto& portal_contract_address = private_inputs.private_call.portal_contract_address; - const auto& contract_deployment_data = private_call_public_inputs.contract_deployment_data; - - { // contract deployment - // input storage contract address must be 0 if its a constructor call and non-zero otherwise - auto is_contract_deployment = public_inputs.constants.tx_context.is_contract_deployment_tx; - - auto private_call_vk_hash = private_inputs.private_call.vk->hash(); - auto constructor_hash = compute_constructor_hash(private_inputs.private_call.call_stack_item.function_data, - private_call_public_inputs.args_hash, - private_call_vk_hash); - - is_contract_deployment.must_imply(contract_deployment_data.constructor_vk_hash == private_call_vk_hash, - "constructor_vk_hash does not match private call vk hash"); - - // compute the contract address (only valid if this is a contract deployment) - auto const contract_address = abis::CompleteAddress::compute(contract_deployment_data.deployer_public_key, - contract_deployment_data.contract_address_salt, - contract_deployment_data.function_tree_root, - constructor_hash) - .address; - - // must imply == derived address - is_contract_deployment.must_imply( - storage_contract_address == contract_address, - "storage_contract_address must match derived address for contract deployment"); - - // non-contract deployments must specify contract address being interacted with - (!is_contract_deployment) - .must_imply(storage_contract_address != CT::fr(0), - "storage_contract_address must be nonzero for a private function"); - - // compute contract address nullifier - auto blake_input = CT::byte_array(contract_address.to_field()); - auto contract_address_nullifier = CT::fr(CT::blake2s(blake_input)); - - // push the contract address nullifier to nullifier vector - CT::fr const conditional_contract_address_nullifier = - CT::fr::conditional_assign(is_contract_deployment, contract_address_nullifier, CT::fr(0)); - array_push(public_inputs.end.new_nullifiers, conditional_contract_address_nullifier); - - // Add new contract data if its a contract deployment function - auto const new_contract_data = NewContractData{ - .contract_address = contract_address, - .portal_contract_address = portal_contract_address, - .function_tree_root = contract_deployment_data.function_tree_root, - }; - - array_push, MAX_NEW_CONTRACTS_PER_TX>(public_inputs.end.new_contracts, - new_contract_data); - } - - { // commitments, nullifiers, and contracts - std::array siloed_new_commitments; - for (size_t i = 0; i < new_commitments.size(); ++i) { - siloed_new_commitments[i] = CT::fr::conditional_assign( - new_commitments[i] == 0, 0, silo_commitment(storage_contract_address, new_commitments[i])); - } - std::array siloed_new_nullifiers; - for (size_t i = 0; i < new_nullifiers.size(); ++i) { - siloed_new_nullifiers[i] = CT::fr::conditional_assign( - new_nullifiers[i] == 0, 0, silo_nullifier(storage_contract_address, new_nullifiers[i])); - } - - // Add new commitments/etc to AggregatedData - push_array_to_array(siloed_new_commitments, public_inputs.end.new_commitments); - push_array_to_array(siloed_new_nullifiers, public_inputs.end.new_nullifiers); - } - - { // call stacks - // copy the private function circuit's callstack into the AggregatedData - const auto& this_private_call_stack = private_call_public_inputs.private_call_stack; - push_array_to_array(this_private_call_stack, public_inputs.end.private_call_stack); - } - - // { - // const auto& new_l2_to_l1_msgs = private_call_public_inputs.new_l2_to_l1_msgs; - // std::array l1_call_stack; - - // for (size_t i = 0; i < new_l2_to_l1_msgs.size(); ++i) { - // l1_call_stack[i] = CT::fr::conditional_assign( - // new_l2_to_l1_msgs[i] == 0, - // 0, - // CT::hash({ portal_contract_address, new_l2_to_l1_msgs[i] }, GeneratorIndex::L2_TO_L1_MSG)); - // } - // } -} - -/** - * @brief Ensure that the function/call-stack-item currently being processed by the kernel - * matches the one that the previous kernel iteration said should come next. - */ -void validate_this_private_call_hash(PrivateKernelInputsInner const& private_inputs) -{ - const auto& start = private_inputs.previous_kernel.public_inputs.end; - // TODO: this logic might need to change to accommodate the weird edge 3 initial txs (the 'main' tx, the 'fee' tx, - // and the 'gas rebate' tx). - const auto this_private_call_hash = array_pop(start.private_call_stack); - const auto calculated_this_private_call_hash = private_inputs.private_call.call_stack_item.hash(); - - this_private_call_hash.assert_equal(calculated_this_private_call_hash, "this private_call_hash does not reconcile"); -}; - -/** - * @brief Ensure that the callstack inputs are consistent. - * - * @details The private function circuit will output a callstack containing just hashes - * of CallStackItems, but the kernel circuit also needs the actual item preimages. - * So here we just ensure that the callstack preimages in the kernel's private inputs - * matches the function's CallStackItem hashes. - */ -void validate_this_private_call_stack(PrivateKernelInputsInner const& private_inputs) -{ - const auto& stack = private_inputs.private_call.call_stack_item.public_inputs.private_call_stack; - const auto& preimages = private_inputs.private_call.private_call_stack_preimages; - for (size_t i = 0; i < stack.size(); ++i) { - const auto& hash = stack[i]; - const auto& preimage = preimages[i]; - - // Note: this assumes it's computationally infeasible to have `0` as a valid call_stack_item_hash. - // Assumes `hash == 0` means "this stack item is empty". - const auto calculated_hash = CT::fr::conditional_assign(hash == 0, 0, preimage.hash()); - - hash.assert_equal(calculated_hash, format("private_call_stack[", i, "] = ", hash, "; does not reconcile")); - } -}; - -void validate_inputs(PrivateKernelInputsInner const& private_inputs, bool first_iteration) -{ - // this callstack represents the function currently being processed - const auto& this_call_stack_item = private_inputs.private_call.call_stack_item; - - this_call_stack_item.function_data.is_private.assert_equal( - true, "Cannot execute a non-private function with the private kernel circuit"); - - const auto& start = private_inputs.previous_kernel.public_inputs.end; - - // base case: have not processed any functions yet - const CT::boolean is_base_case(first_iteration); - - // TODO: we might want to range-constrain the call_count to prevent some kind of overflow errors - const CT::boolean is_recursive_case = !is_base_case; - - // Grab stack lengths as output from the previous kernel iteration - // These lengths are calculated by counting entries until a non-zero one is encountered - // True array length is constant which is a property we need for circuit inputs, - // but we want to know "length" in terms of how many nonzero entries have been inserted - CT::fr const start_private_call_stack_length = array_length(start.private_call_stack); - CT::fr const start_public_call_stack_length = array_length(start.public_call_stack); - CT::fr const start_new_l2_to_l1_msgs_length = array_length(start.new_l2_to_l1_msgs); - - // Recall: we can't do traditional `if` statements in a circuit; all code paths are always executed. The below is - // some syntactic sugar, which seeks readability similar to an `if` statement. - - // Base Case - std::vector> const base_case_conditions{ - // TODO: change to allow 3 initial calls on the private call stack, so a fee can be paid and a gas - // rebate can be paid. - { start_private_call_stack_length == 1, "Private call stack must be length 1" }, - { start_public_call_stack_length == 0, "Public call stack must be empty" }, - { start_new_l2_to_l1_msgs_length == 0, "L2 to L1 msgs must be empty" }, - - { this_call_stack_item.public_inputs.call_context.is_delegate_call == false, - "Users cannot make a delegatecall" }, - { this_call_stack_item.public_inputs.call_context.is_static_call == false, "Users cannot make a static call" }, - - // The below also prevents delegatecall/staticcall in the base case - { this_call_stack_item.public_inputs.call_context.storage_contract_address == - this_call_stack_item.contract_address, - "Storage contract address must be that of the called contract" }, - - { private_inputs.previous_kernel.vk->contains_recursive_proof == false, - "Mock kernel proof must not contain a recursive proof" } - - // TODO: Assert that the previous kernel data is empty. (Or rather, the verify_proof() function needs a valid - // dummy proof and vk to complete execution, so actually what we want is for that mockvk to be - // hard-coded into the circuit and assert that that is the one which has been used in the base case). - // kernel VK tree contains 16 VKs that would be used to verify kernel proofs depending on the - // number of public inputs each of them spits out. - // TODO (later): merkle membership check that the vk from the previous data is present at leaf 0. - - // TODO: verify signed tx request against current function being called - }; - is_base_case.must_imply(base_case_conditions); - - // Recursive Case - std::vector> const recursive_case_conditions{ - { private_inputs.previous_kernel.public_inputs.is_private == true, - "Cannot verify a non-private kernel snark in the private kernel circuit" }, - { this_call_stack_item.function_data.is_constructor == false, - "A constructor must be executed as the first tx in the recursion" }, - { start_private_call_stack_length != 0, - "Cannot execute private kernel circuit with an empty private call stack" } - // TODO (later): assert that previous kernel VK matches VK for this input size - // TODO (later): membership proof of VK - }; - is_recursive_case.must_imply(recursive_case_conditions); - - // validate constructor hash - // generate contract address - // generate contract address nullifier and add to list - // create new contract data and add to list - // MAYBE: check other contract deployment data: - // function tree, contracts root -} - -// NOTE: THIS IS A VERY UNFINISHED WORK IN PROGRESS. -// TODO: decide what to return. -// TODO: is there a way to identify whether an input has not been used by ths circuit? This would help us more-safely -// ensure we're constraining everything. -KernelCircuitPublicInputs private_kernel_circuit(Builder& builder, - PrivateKernelInputsInner const& _private_inputs, - bool first_iteration) -{ - const PrivateKernelInputsInner private_inputs = _private_inputs.to_circuit_type(builder); - - // We'll be pushing data to this during execution of this circuit. - KernelCircuitPublicInputs public_inputs = KernelCircuitPublicInputs{}.to_circuit_type(builder); - - // Do this before any functions can modify the inputs. - initialize_end_values(private_inputs, public_inputs); - - validate_inputs(private_inputs, first_iteration); - - validate_this_private_call_hash(private_inputs); - - validate_this_private_call_stack(private_inputs); - - // TODO (later): do we need to validate this private_call_stack against end.private_call_stack? - - update_end_values(private_inputs, public_inputs); - - auto aggregation_object = verify_proofs(builder, private_inputs); - - // TODO: kernel vk membership check! - - public_inputs.end.aggregation_object = aggregation_object; - - public_inputs.set_public(); - - return public_inputs.to_native_type(); -}; - -} // namespace aztec3::circuits::kernel::private_kernel diff --git a/circuits/cpp/src/aztec3/circuits/kernel/private/private_kernel_circuit.hpp b/circuits/cpp/src/aztec3/circuits/kernel/private/private_kernel_circuit.hpp deleted file mode 100644 index 17d10d4561a..00000000000 --- a/circuits/cpp/src/aztec3/circuits/kernel/private/private_kernel_circuit.hpp +++ /dev/null @@ -1,17 +0,0 @@ -#pragma once - -#include "init.hpp" - -#include "aztec3/circuits/abis/kernel_circuit_public_inputs.hpp" -#include "aztec3/circuits/abis/private_kernel/private_kernel_inputs_inner.hpp" - -namespace aztec3::circuits::kernel::private_kernel { - -using aztec3::circuits::abis::KernelCircuitPublicInputs; -using aztec3::circuits::abis::private_kernel::PrivateKernelInputsInner; - -KernelCircuitPublicInputs private_kernel_circuit(Builder& builder, - PrivateKernelInputsInner const& private_inputs, - bool first_iteration); - -} // namespace aztec3::circuits::kernel::private_kernel \ No newline at end of file diff --git a/circuits/cpp/src/aztec3/circuits/kernel/private/testing_harness.cpp b/circuits/cpp/src/aztec3/circuits/kernel/private/testing_harness.cpp deleted file mode 100644 index f068662a3e5..00000000000 --- a/circuits/cpp/src/aztec3/circuits/kernel/private/testing_harness.cpp +++ /dev/null @@ -1,555 +0,0 @@ -#include "testing_harness.hpp" - -#include "index.hpp" -#include "init.hpp" - -#include "aztec3/circuits/abis/block_header.hpp" -#include "aztec3/circuits/abis/call_context.hpp" -#include "aztec3/circuits/abis/call_stack_item.hpp" -#include "aztec3/circuits/abis/combined_accumulated_data.hpp" -#include "aztec3/circuits/abis/combined_constant_data.hpp" -#include "aztec3/circuits/abis/complete_address.hpp" -#include "aztec3/circuits/abis/contract_deployment_data.hpp" -#include "aztec3/circuits/abis/function_data.hpp" -#include "aztec3/circuits/abis/private_circuit_public_inputs.hpp" -#include "aztec3/circuits/abis/private_kernel/private_call_data.hpp" -#include "aztec3/circuits/abis/tx_context.hpp" -#include "aztec3/circuits/abis/tx_request.hpp" -#include "aztec3/circuits/abis/types.hpp" -#include "aztec3/circuits/hash.hpp" -#include "aztec3/circuits/kernel/private/utils.hpp" -#include "aztec3/constants.hpp" - -#include - -#include -#include -#include - -namespace aztec3::circuits::kernel::private_kernel::testing_harness { - -using aztec3::circuits::abis::BlockHeader; -using aztec3::circuits::abis::CallContext; -using aztec3::circuits::abis::CallStackItem; -using aztec3::circuits::abis::CombinedAccumulatedData; -using aztec3::circuits::abis::CombinedConstantData; -using aztec3::circuits::abis::ContractDeploymentData; -using aztec3::circuits::abis::FunctionData; -using aztec3::circuits::abis::PrivateCircuitPublicInputs; -using aztec3::circuits::abis::PrivateTypes; -using aztec3::circuits::abis::TxContext; -using aztec3::circuits::abis::TxRequest; -using aztec3::circuits::abis::private_kernel::PrivateCallData; - -using aztec3::utils::array_length; - -/** - * @brief Get the random read requests and their membership requests - * - * @details read requests are siloed by contract address and nonce before being - * inserted into mock note hash tree - * - * @param first_nullifier used when computing nonce for unique_siloed_commitments (note hash tree leaves) - * @param contract_address address to use when siloing read requests - * @param num_read_requests if negative, use random num. Must be < MAX_READ_REQUESTS_PER_CALL - * @return std::tuple - */ -std::tuple, - std::array, MAX_READ_REQUESTS_PER_CALL>, - std::array, - std::array, MAX_READ_REQUESTS_PER_CALL>, - NT::fr> -get_random_reads(NT::fr const& first_nullifier, NT::fr const& contract_address, int const num_read_requests) -{ - std::array transient_read_requests{}; - std::array read_requests{}; - std::array leaves{}; - - // randomize the number of read requests with a configurable minimum - const auto final_num_rr = num_read_requests >= 0 - ? std::min(static_cast(num_read_requests), MAX_READ_REQUESTS_PER_CALL) - : numeric::random::get_engine().get_random_uint8() % (MAX_READ_REQUESTS_PER_CALL + 1); - // randomize private app circuit's read requests - for (size_t rr = 0; rr < final_num_rr; rr++) { - // randomize commitment and its leaf index - // transient read requests are raw (not siloed and not unique at input to kernel circuit) - transient_read_requests[rr] = NT::fr::random_element(); - - const auto siloed_commitment = silo_commitment(contract_address, read_requests[rr]); - const auto nonce = compute_commitment_nonce(first_nullifier, rr); - const auto unique_siloed_commitment = - siloed_commitment == 0 ? 0 : compute_unique_commitment(nonce, siloed_commitment); - - leaves[rr] = unique_siloed_commitment; - read_requests[rr] = unique_siloed_commitment; - } - - // this set and the following loop lets us generate totally random leaf indices - // for read requests while avoiding collisions - std::unordered_set rr_leaf_indices_set; - while (rr_leaf_indices_set.size() < final_num_rr) { - rr_leaf_indices_set.insert(numeric::random::get_engine().get_random_uint32() % NOTE_HASH_TREE_NUM_LEAVES); - } - // set -> vector without collisions - std::vector rr_leaf_indices(rr_leaf_indices_set.begin(), rr_leaf_indices_set.end()); - - MemoryStore note_hash_tree_store; - MerkleTree note_hash_tree = MerkleTree(note_hash_tree_store, NOTE_HASH_TREE_HEIGHT); - - // add the commitments to the note hash tree for each read request - // add them at their corresponding index in the tree - // (in practice the the tree is left-to-right append-only, but here - // we treat it as sparse just to get these commitments in their correct spot) - for (size_t i = 0; i < array_length(leaves); i++) { - note_hash_tree.update_element(rr_leaf_indices[i], leaves[i]); - } - - // compute the merkle sibling paths for each request - std::array, MAX_READ_REQUESTS_PER_CALL> - transient_read_request_membership_witnesses{}; - std::array, MAX_READ_REQUESTS_PER_CALL> - read_request_membership_witnesses{}; - for (size_t i = 0; i < array_length(read_requests); i++) { - read_request_membership_witnesses[i] = { .leaf_index = NT::fr(rr_leaf_indices[i]), - .sibling_path = get_sibling_path( - note_hash_tree, rr_leaf_indices[i], 0), - .is_transient = false, - .hint_to_commitment = 0 }; - transient_read_request_membership_witnesses[i] = { .leaf_index = NT::fr(0), - .sibling_path = - compute_empty_sibling_path(0), - .is_transient = true, - .hint_to_commitment = 0 }; - } - - - return { read_requests, - read_request_membership_witnesses, - transient_read_requests, - transient_read_request_membership_witnesses, - note_hash_tree.root() }; -} // namespace aztec3::circuits::kernel::private_kernel::testing_harness - -std::pair, ContractDeploymentData> create_private_call_deploy_data( - bool const is_constructor, - private_function const& func, - std::vector const& args_vec, - NT::address const& msg_sender, - std::array const& encrypted_logs_hash, - std::array const& unencrypted_logs_hash, - NT::fr const& encrypted_log_preimages_length, - NT::fr const& unencrypted_log_preimages_length, - bool is_circuit) -{ - //*************************************************************************** - // Initialize some inputs to private call and kernel circuits - //*************************************************************************** - // TODO(suyash) randomize inputs - NT::address contract_address = is_constructor ? 0 : 12345; // updated later if in constructor - const NT::uint32 contract_leaf_index = 1; - const NT::uint32 function_leaf_index = 1; - const NT::fr portal_contract_address = 23456; - const NT::fr contract_address_salt = 34567; - const NT::fr acir_hash = 12341234; - - const NT::fr msg_sender_private_key = 123456789; - const Point msg_sender_pub_key = { .x = 123456789, .y = 123456789 }; - - FunctionData const function_data{ - .selector = - { - .value = 1, // TODO: deduce this from the contract, somehow. - }, - .is_private = true, - .is_constructor = is_constructor, - }; - - CallContext call_context{ - .msg_sender = msg_sender, - .storage_contract_address = contract_address, - .portal_contract_address = portal_contract_address, - .function_selector = function_data.selector, - .is_delegate_call = false, - .is_static_call = false, - .is_contract_deployment = is_constructor, - }; - - // sometimes need private call args as array - std::array args{}; - for (size_t i = 0; i < args_vec.size(); ++i) { - args[i] = args_vec[i]; - } - const NT::fr args_hash = compute_var_args_hash(args_vec); - - //*************************************************************************** - // Initialize contract related information like private call VK (and its hash), - // function tree, contract tree, contract address for newly deployed contract, - // etc... - //*************************************************************************** - - // generate private circuit VK and its hash using circuit with dummy inputs - // it is needed below: - // for constructors - to generate the contract address, function leaf, etc - // for private calls - to generate the function leaf, etc - auto const private_circuit_vk = is_circuit ? utils::get_verification_key_from_file() : utils::fake_vk(); - - // TODO(#3062) VKs are mocked out for now - // const NT::fr private_circuit_vk_hash = - // stdlib::recursion::verification_key::hash_native(private_circuit_vk); - const NT::fr private_circuit_vk_hash = 0; - - ContractDeploymentData contract_deployment_data{}; - NT::fr contract_tree_root = 0; // TODO(david) set properly for constructor? - if (is_constructor) { - // TODO(david) compute function tree root from leaves - // create leaf preimage for each function and hash all into tree - // push to array/vector - // use variation of `compute_root_partial_left_tree` to compute the root from leaves - // const auto& function_leaf_preimage = FunctionLeafPreimage{ - // .selector = function_data.selector, - // .is_private = function_data.is_private, - // .vk_hash = private_circuit_vk_hash, - // .acir_hash = acir_hash, - //}; - std::vector const function_leaves(MAX_FUNCTION_LEAVES, EMPTY_FUNCTION_LEAF()); - // const NT::fr& function_tree_root = plonk::stdlib::merkle_tree::compute_tree_root_native(function_leaves); - - // TODO(david) use actual function tree root computed from leaves - // update cdd with actual info - contract_deployment_data = { - .deployer_public_key = msg_sender_pub_key, - .constructor_vk_hash = private_circuit_vk_hash, - .function_tree_root = plonk::stdlib::merkle_tree::compute_tree_root_native(function_leaves), - .contract_address_salt = contract_address_salt, - .portal_contract_address = portal_contract_address, - }; - - // Get constructor hash for use when deriving contract address - auto constructor_hash = compute_constructor_hash(function_data, args_hash, private_circuit_vk_hash); - - // Derive contract address so that it can be used inside the constructor itself - contract_address = abis::CompleteAddress::compute(msg_sender_pub_key, - contract_address_salt, - contract_deployment_data.function_tree_root, - constructor_hash) - .address; - // update the contract address in the call context now that it is known - call_context.storage_contract_address = contract_address; - } else { - const NT::fr& function_tree_root = function_tree_root_from_siblings(function_data.selector, - function_data.is_internal, - function_data.is_private, - private_circuit_vk_hash, - acir_hash, - function_leaf_index, - get_empty_function_siblings()); - - // update contract_tree_root with real value - contract_tree_root = contract_tree_root_from_siblings(function_tree_root, - contract_address, - portal_contract_address, - contract_leaf_index, - get_empty_contract_siblings()); - } - - /** - * If `is_circuit` is true, we are running a real circuit test and therefore we need to generate a real proof using - * a private function builder. For the native tests, we are using a random data as public inputs of the private - * function. As the native private kernel circuit doesn't validate any proofs and we don't currently test - * multi-iterative kernel circuit, this should be fine. - */ - PrivateCircuitPublicInputs private_circuit_public_inputs; - const NT::Proof private_circuit_proof = utils::get_proof_from_file(); - if (is_circuit) { - //*************************************************************************** - // Create a private circuit/call using builder, oracles, execution context - // Generate its proof and public inputs for submission with a TX request - //*************************************************************************** - Builder private_circuit_builder = Builder(); - - DB dummy_db; - NativeOracle oracle = - is_constructor - ? NativeOracle(dummy_db, - contract_address, - function_data, - call_context, - contract_deployment_data, - msg_sender_private_key) - : NativeOracle(dummy_db, contract_address, function_data, call_context, msg_sender_private_key); - - OracleWrapper oracle_wrapper = OracleWrapper(private_circuit_builder, oracle); - - FunctionExecutionContext ctx(private_circuit_builder, oracle_wrapper); - - OptionalPrivateCircuitPublicInputs const opt_private_circuit_public_inputs = func(ctx, args_vec); - private_circuit_public_inputs = opt_private_circuit_public_inputs.remove_optionality(); - // TODO(suyash): this should likely be handled as part of the DB/Oracle/Context infrastructure - private_circuit_public_inputs.block_header.contract_tree_root = contract_tree_root; - - private_circuit_public_inputs.encrypted_logs_hash = encrypted_logs_hash; - private_circuit_public_inputs.unencrypted_logs_hash = unencrypted_logs_hash; - private_circuit_public_inputs.encrypted_log_preimages_length = encrypted_log_preimages_length; - private_circuit_public_inputs.unencrypted_log_preimages_length = unencrypted_log_preimages_length; - } else { - private_circuit_public_inputs = PrivateCircuitPublicInputs{ - .call_context = call_context, - .args_hash = args_hash, - .return_values = {}, - .new_commitments = { NT::fr::random_element() }, // One random commitment - .new_nullifiers = { NT::fr::random_element() }, // One random nullifier - .nullified_commitments = {}, - .private_call_stack = {}, - .new_l2_to_l1_msgs = {}, - .encrypted_logs_hash = encrypted_logs_hash, - .unencrypted_logs_hash = unencrypted_logs_hash, - .encrypted_log_preimages_length = encrypted_log_preimages_length, - .unencrypted_log_preimages_length = unencrypted_log_preimages_length, - .block_header = BlockHeader{ .contract_tree_root = contract_tree_root }, - .contract_deployment_data = contract_deployment_data, - }; - } - - const CallStackItem call_stack_item{ - .contract_address = contract_address, - .function_data = function_data, - .public_inputs = private_circuit_public_inputs, - }; - - //*************************************************************************** - // Now we can construct the full private inputs to the kernel circuit - //*************************************************************************** - - return std::pair, ContractDeploymentData>( - PrivateCallData{ - .call_stack_item = call_stack_item, - // TODO(dbanks12): these tests do not test multiple kernel iterations - // and do not test non-empty callstacks. They should! To have such tests - // we will need to populate these callstackitem preimages - // and ensure they match the hashed callstackitems themselves. - //.private_call_stack_preimages = , - - .proof = private_circuit_proof, - .vk = private_circuit_vk, - - .function_leaf_membership_witness = { - .leaf_index = function_leaf_index, - .sibling_path = get_empty_function_siblings(), - }, - - .contract_leaf_membership_witness = { - .leaf_index = contract_leaf_index, - .sibling_path = get_empty_contract_siblings(), - }, - - .portal_contract_address = portal_contract_address, - - .acir_hash = acir_hash - }, - contract_deployment_data); -} - -/** - * @brief Perform an initial private circuit call and generate the inputs to private kernel - * - * @param is_constructor whether this private circuit call is a constructor - * @param func the private circuit call being validated by this kernel iteration - * @param args_vec the private call's args - * @param encrypted_logs_hash The encrypted logs hash emitted from app circuit. - * @param unencrypted_logs_hash The unencrypted logs hash emitted from app circuit. - * @param encrypted_log_preimages_length The length of encrypted logs emitted from app circuit. - * @param unencrypted_log_preimages_length The length of unencrypted logs emitted from app circuit. - * @param is_circuit boolean to switch to circuit or native (fake vk and no proof) - * @return PrivateInputsInit - the inputs to the private call circuit of an init iteration - */ -PrivateKernelInputsInit do_private_call_get_kernel_inputs_init( - bool const is_constructor, - private_function const& func, - std::vector const& args_vec, - std::array const& encrypted_logs_hash, - std::array const& unencrypted_logs_hash, - NT::fr const& encrypted_log_preimages_length, - NT::fr const& unencrypted_log_preimages_length, - bool is_circuit) -{ - //*************************************************************************** - // Initialize some inputs to private call and kernel circuits - //*************************************************************************** - // TODO(suyash) randomize inputs - - const NT::address msg_sender = - NT::fr(uint256_t(0x01071e9a23e0f7edULL, 0x5d77b35d1830fa3eULL, 0xc6ba3660bb1f0c0bULL, 0x2ef9f7f09867fd6eULL)); - - auto const& [private_call_data, contract_deployment_data] = - create_private_call_deploy_data(is_constructor, - func, - args_vec, - msg_sender, - encrypted_logs_hash, - unencrypted_logs_hash, - encrypted_log_preimages_length, - unencrypted_log_preimages_length, - is_circuit); - - //*************************************************************************** - // We can create a TxRequest from some of the above data. - //*************************************************************************** - auto const tx_request = TxRequest{ .origin = private_call_data.call_stack_item.contract_address, - .function_data = private_call_data.call_stack_item.function_data, - .args_hash = compute_var_args_hash(args_vec), - .tx_context = TxContext{ - .is_fee_payment_tx = false, - .is_rebate_payment_tx = false, - .is_contract_deployment_tx = is_constructor, - .contract_deployment_data = contract_deployment_data, - .chain_id = 1, - } }; - - //*************************************************************************** - // Now we can construct the full private inputs to the kernel circuit - //*************************************************************************** - PrivateKernelInputsInit kernel_private_inputs = PrivateKernelInputsInit{ - .tx_request = tx_request, - .private_call = private_call_data, - }; - - return kernel_private_inputs; -} - - -/** - * @brief Perform an inner private circuit call and generate the inputs to private kernel - * - * @param is_constructor whether this private circuit call is a constructor - * @param func the private circuit call being validated by this kernel iteration - * @param args_vec the private call's args - * @param encrypted_logs_hash The encrypted logs hash emitted from app circuit. - * @param unencrypted_logs_hash The unencrypted logs hash emitted from app circuit. - * @param encrypted_log_preimages_length The length of encrypted logs emitted from app circuit. - * @param unencrypted_log_preimages_length The length of unencrypted logs emitted from app circuit. - * @param public_inputs_encrypted_logs_hash The encrypted logs hash on the output of the previous kernel. - * @param public_inputs_unencrypted_logs_hash The unencrypted logs hash on the output of the previous kernel. - * @param public_inputs_encrypted_log_preimages_length The length of encrypted logs on the output of the previous - * kernel. - * @param public_inputs_unencrypted_log_preimages_length The length of unencrypted logs on the output of the previous - * kernel. - * @param is_circuit boolean to switch to circuit or native (fake vk and no proof) - * @return PrivateInputsInner - the inputs to the private call circuit of an inner iteration - */ -PrivateKernelInputsInner do_private_call_get_kernel_inputs_inner( - bool const is_constructor, - private_function const& func, - std::vector const& args_vec, - std::array const& encrypted_logs_hash, - std::array const& unencrypted_logs_hash, - NT::fr const& encrypted_log_preimages_length, - NT::fr const& unencrypted_log_preimages_length, - std::array const& public_inputs_encrypted_logs_hash, - std::array const& public_inputs_unencrypted_logs_hash, - NT::fr const& public_inputs_encrypted_log_preimages_length, - NT::fr const& public_inputs_unencrypted_log_preimages_length, - bool is_circuit) -{ - //*************************************************************************** - // Initialize some inputs to private call and kernel circuits - //*************************************************************************** - // TODO(suyash) randomize inputs - - const NT::address msg_sender = - NT::fr(uint256_t(0x01071e9a23e0f7edULL, 0x5d77b35d1830fa3eULL, 0xc6ba3660bb1f0c0bULL, 0x2ef9f7f09867fd6eULL)); - - auto const& [private_call_data, contract_deployment_data] = - create_private_call_deploy_data(is_constructor, - func, - args_vec, - msg_sender, - encrypted_logs_hash, - unencrypted_logs_hash, - encrypted_log_preimages_length, - unencrypted_log_preimages_length, - is_circuit); - - const TxContext tx_context = TxContext{ - .is_fee_payment_tx = false, - .is_rebate_payment_tx = false, - .is_contract_deployment_tx = is_constructor, - .contract_deployment_data = contract_deployment_data, - }; - - //*************************************************************************** - // We mock a kernel circuit proof to initialize input required by an inner call - //*************************************************************************** - - std::array initial_kernel_private_call_stack{}; - initial_kernel_private_call_stack[0] = private_call_data.call_stack_item.hash(); - - auto const& private_circuit_public_inputs = private_call_data.call_stack_item.public_inputs; - // Get dummy previous kernel - auto mock_previous_kernel = utils::dummy_previous_kernel(is_circuit); - // Fill in some important fields in public inputs - mock_previous_kernel.public_inputs.end.private_call_stack = initial_kernel_private_call_stack; - mock_previous_kernel.public_inputs.constants = CombinedConstantData{ - .block_header = - BlockHeader{ - .note_hash_tree_root = private_circuit_public_inputs.block_header.note_hash_tree_root, - .contract_tree_root = private_circuit_public_inputs.block_header.contract_tree_root, - }, - .tx_context = tx_context, - }; - mock_previous_kernel.public_inputs.is_private = true; - mock_previous_kernel.public_inputs.end.encrypted_logs_hash = public_inputs_encrypted_logs_hash; - mock_previous_kernel.public_inputs.end.unencrypted_logs_hash = public_inputs_unencrypted_logs_hash; - mock_previous_kernel.public_inputs.end.encrypted_log_preimages_length = - public_inputs_encrypted_log_preimages_length; - mock_previous_kernel.public_inputs.end.unencrypted_log_preimages_length = - public_inputs_unencrypted_log_preimages_length; - mock_previous_kernel.public_inputs.end.new_nullifiers[0] = 321; // 0th nullifier must be non-zero. - mock_previous_kernel.public_inputs.end.nullified_commitments[0] = EMPTY_NULLIFIED_COMMITMENT; - - //*************************************************************************** - // Now we can construct the full private inputs to the kernel circuit - //*************************************************************************** - PrivateKernelInputsInner kernel_private_inputs = PrivateKernelInputsInner{ - .previous_kernel = mock_previous_kernel, - .private_call = private_call_data, - }; - - return kernel_private_inputs; -} - -/** - * @brief Validate that the deployed contract address is correct. - * - * @details Compare the public inputs new contract address - * with one manually computed from private inputs. - * @param private_inputs to be used in manual computation - * @param public_inputs that contain the expected new contract address - */ -bool validate_deployed_contract_address(PrivateKernelInputsInit const& private_inputs, - KernelCircuitPublicInputs const& public_inputs) -{ - auto tx_request = private_inputs.tx_request; - auto cdd = private_inputs.tx_request.tx_context.contract_deployment_data; - - // TODO(#3062) VKs are mocked out for now - // auto private_circuit_vk_hash = - // stdlib::recursion::verification_key::hash_native(private_inputs.private_call.vk); - auto private_circuit_vk_hash = 0; - - auto expected_constructor_hash = compute_constructor_hash( - private_inputs.private_call.call_stack_item.function_data, tx_request.args_hash, private_circuit_vk_hash); - - NT::fr const expected_contract_address = - abis::CompleteAddress::compute( - cdd.deployer_public_key, cdd.contract_address_salt, cdd.function_tree_root, expected_constructor_hash) - .address; - - return (public_inputs.end.new_contracts[0].contract_address.to_field() == expected_contract_address); -} - -bool validate_no_new_deployed_contract(KernelCircuitPublicInputs const& public_inputs) -{ - return (public_inputs.end.new_contracts == CombinedAccumulatedData{}.new_contracts); -} - -} // namespace aztec3::circuits::kernel::private_kernel::testing_harness diff --git a/circuits/cpp/src/aztec3/circuits/kernel/private/testing_harness.hpp b/circuits/cpp/src/aztec3/circuits/kernel/private/testing_harness.hpp deleted file mode 100644 index 8ed96456998..00000000000 --- a/circuits/cpp/src/aztec3/circuits/kernel/private/testing_harness.hpp +++ /dev/null @@ -1,200 +0,0 @@ -#include "index.hpp" -#include "init.hpp" - -#include "aztec3/circuits/abis/contract_deployment_data.hpp" -#include "aztec3/circuits/abis/kernel_circuit_public_inputs.hpp" -#include "aztec3/circuits/abis/membership_witness.hpp" -#include "aztec3/circuits/abis/private_circuit_public_inputs.hpp" -#include "aztec3/circuits/abis/private_kernel/private_call_data.hpp" -#include "aztec3/circuits/abis/private_kernel/private_kernel_inputs_init.hpp" -#include "aztec3/circuits/abis/private_kernel/private_kernel_inputs_inner.hpp" -#include "aztec3/circuits/abis/read_request_membership_witness.hpp" -#include "aztec3/circuits/abis/tx_request.hpp" -#include "aztec3/circuits/hash.hpp" -#include "aztec3/circuits/kernel/private/common.hpp" -#include "aztec3/circuits/kernel/private/utils.hpp" - -#include - -#include -#include - -namespace { - -using aztec3::circuits::compute_empty_sibling_path; -using aztec3::circuits::abis::ContractDeploymentData; -using aztec3::circuits::abis::FunctionLeafPreimage; -using aztec3::circuits::abis::KernelCircuitPublicInputs; -using aztec3::circuits::abis::NewContractData; -using aztec3::circuits::abis::OptionalPrivateCircuitPublicInputs; -using aztec3::circuits::abis::ReadRequestMembershipWitness; -using aztec3::circuits::abis::TxRequest; -using aztec3::circuits::abis::private_kernel::PrivateCallData; -using aztec3::circuits::abis::private_kernel::PrivateKernelInputsInit; -using aztec3::circuits::abis::private_kernel::PrivateKernelInputsInner; - - -using DummyBuilder = aztec3::utils::DummyCircuitBuilder; - -// A type representing any private circuit function -// (for now it works for deposit and constructor) -using private_function = std::function( - FunctionExecutionContext&, std::vector const&)>; - -} // namespace - -namespace aztec3::circuits::kernel::private_kernel::testing_harness { - -using aztec3::circuits::compute_empty_sibling_path; - -// Some helper constants for trees -constexpr size_t MAX_FUNCTION_LEAVES = 1 << aztec3::FUNCTION_TREE_HEIGHT; // 2^(height-1) -// NOTE: *DO NOT* call hashes in static initializers and assign them to constants. This will fail. Instead, use -// lazy initialization or functions. Lambdas were introduced here. -const auto EMPTY_FUNCTION_LEAF = [] { return FunctionLeafPreimage{}.hash(); }; // hash of empty/0 preimage -const auto EMPTY_CONTRACT_LEAF = [] { return NewContractData{}.hash(); }; // hash of empty/0 preimage -constexpr uint64_t NOTE_HASH_TREE_NUM_LEAVES = 1ULL << aztec3::NOTE_HASH_TREE_HEIGHT; // 2^(height-1) - -inline const auto& get_empty_function_siblings() -{ - static auto EMPTY_FUNCTION_SIBLINGS = []() { - const auto result = compute_empty_sibling_path(EMPTY_FUNCTION_LEAF()); - return result; - }(); - return EMPTY_FUNCTION_SIBLINGS; -} - -inline const auto& get_empty_contract_siblings() -{ - static auto EMPTY_CONTRACT_SIBLINGS = []() { - const auto result = compute_empty_sibling_path(EMPTY_CONTRACT_LEAF()); - return result; - }(); - return EMPTY_CONTRACT_SIBLINGS; -} - -/** - * @brief Get the random read requests and their membership requests - * - * @details read requests are siloed by contract address and nonce before being - * inserted into mock note hash tree - * - * @param first_nullifier used when computing nonce for unique_siloed_commitments (note hash tree leaves) - * @param contract_address address to use when siloing read requests - * @param num_read_requests if negative, use random num - * @return tuple including read requests, their membership witnesses, their transient versions, and the - * note hash tree root that contains all of these randomly created commitments at random leaf indices - * std::tuple< - * read_requests, - * read_request_memberships_witnesses, - * transient_read_requests, - * transient_read_request_memberships_witnesses, - * historical_note_hash_tree_root> - */ -std::tuple, - std::array, MAX_READ_REQUESTS_PER_CALL>, - std::array, - std::array, MAX_READ_REQUESTS_PER_CALL>, - NT::fr> -get_random_reads(NT::fr const& first_nullifier, NT::fr const& contract_address, int num_read_requests); - -/** - * @brief Create a private call deploy data object - * - * @param is_constructor Whether this private call is a constructor call - * @param func The private circuit (i.e. constructor in case of contract deployment) call - * @param args_vec Number of args to that private circuit call - * @param msg_sender The sender of the transaction request - * @return std::pair, ContractDeploymentData> - the generated private call data with the - * contract deployment data - */ -std::pair, ContractDeploymentData> create_private_call_deploy_data( - bool is_constructor, - private_function const& func, - std::vector const& args_vec, - NT::address const& msg_sender, - std::array const& encrypted_logs_hash, - NT::fr const& encrypted_log_preimages_length, - bool is_circuit = false); - -/** - * @brief Perform an inner private circuit call and generate the inputs to private kernel - * - * @param is_constructor whether this private circuit call is a constructor - * @param func the private circuit call being validated by this kernel iteration - * @param args_vec the private call's args - * @param encrypted_logs_hash The encrypted logs hash emitted from app circuit. - * @param unencrypted_logs_hash The unencrypted logs hash emitted from app circuit. - * @param encrypted_log_preimages_length The length of encrypted logs emitted from app circuit. - * @param unencrypted_log_preimages_length The length of unencrypted logs emitted from app circuit. - * @param public_inputs_encrypted_logs_hash The encrypted logs hash on the output of the previous kernel. - * @param public_inputs_unencrypted_logs_hash The unencrypted logs hash on the output of the previous kernel. - * @param public_inputs_encrypted_log_preimages_length The length of encrypted logs on the output of the previous - * kernel. - * @param public_inputs_unencrypted_log_preimages_length The length of unencrypted logs on the output of the previous - * kernel. - * @param is_circuit boolean to switch to circuit or native (fake vk and no proof) - * @return PrivateInputsInner - the inputs to the private call circuit of an inner iteration - */ -PrivateKernelInputsInner do_private_call_get_kernel_inputs_inner( - bool is_constructor, - private_function const& func, - std::vector const& args_vec, - std::array const& encrypted_logs_hash = std::array{}, - std::array const& unencrypted_logs_hash = - std::array{}, - NT::fr const& encrypted_log_preimages_length = NT::fr(0), - NT::fr const& unencrypted_log_preimages_length = NT::fr(0), - std::array const& public_inputs_encrypted_logs_hash = - std::array{}, - std::array const& public_inputs_unencrypted_logs_hash = - std::array{}, - NT::fr const& public_inputs_encrypted_log_preimages_length = NT::fr(0), - NT::fr const& public_inputs_unencrypted_log_preimages_length = NT::fr(0), - bool is_circuit = false); - -/** - * @brief Perform an initial private circuit call and generate the inputs to private kernel - * - * @param is_constructor whether this private circuit call is a constructor - * @param func the private circuit call being validated by this kernel iteration - * @param args_vec the private call's args - * @param encrypted_logs_hash The encrypted logs hash emitted from app circuit. - * @param unencrypted_logs_hash The unencrypted logs hash emitted from app circuit. - * @param encrypted_log_preimages_length The length of encrypted logs emitted from app circuit. - * @param unencrypted_log_preimages_length The length of unencrypted logs emitted from app circuit. - * @param is_circuit boolean to switch to circuit or native (fake vk and no proof) - * @return PrivateInputsInit - the inputs to the private call circuit of an init iteration - */ -PrivateKernelInputsInit do_private_call_get_kernel_inputs_init( - bool is_constructor, - private_function const& func, - std::vector const& args_vec, - std::array const& encrypted_logs_hash = std::array{}, - std::array const& unencrypted_logs_hash = - std::array{}, - NT::fr const& encrypted_log_preimages_length = NT::fr(0), - NT::fr const& unencrypted_log_preimages_length = NT::fr(0), - bool is_circuit = false); - -/** - * @brief Validate that the deployed contract address is correct. - * - * @details Compare the public inputs new contract address - * with one manually computed from private inputs. - * @param private_inputs to be used in manual computation - * @param public_inputs that contain the expected new contract address - * @return true or false - */ -bool validate_deployed_contract_address(PrivateKernelInputsInit const& private_inputs, - KernelCircuitPublicInputs const& public_inputs); - -/** - * @brief Checks if there is no newly deployed contract - * - * @param public_inputs that contain the expected new contract deployment data - * @return true or false - */ -bool validate_no_new_deployed_contract(KernelCircuitPublicInputs const& public_inputs); - -} // namespace aztec3::circuits::kernel::private_kernel::testing_harness diff --git a/circuits/cpp/src/aztec3/circuits/kernel/private/utils.cpp b/circuits/cpp/src/aztec3/circuits/kernel/private/utils.cpp deleted file mode 100644 index de9a52c1f42..00000000000 --- a/circuits/cpp/src/aztec3/circuits/kernel/private/utils.cpp +++ /dev/null @@ -1,137 +0,0 @@ -#include "index.hpp" -#include "init.hpp" - -#include "aztec3/circuits/mock/mock_kernel_circuit.hpp" - -#include - -#include - -#include - -namespace { -using NT = aztec3::utils::types::NativeTypes; -using AggregationObject = aztec3::utils::types::NativeTypes::AggregationObject; -using aztec3::circuits::abis::PreviousKernelData; -using aztec3::circuits::mock::mock_kernel_circuit; - -} // namespace - -namespace aztec3::circuits::kernel::private_kernel::utils { - -/** - * @brief Utility for reading into a vector from file - * - * @param filename - * @return std::vector - */ -std::vector read_buffer_from_file(const std::string& filename) -{ - std::ifstream file(filename, std::ios::binary); - std::vector buf; - - if (file.is_open()) { - // Get the file size by seeking to the end of the file - file.seekg(0, std::ios::end); - const std::streampos fileSize = file.tellg(); - file.seekg(0, std::ios::beg); - - // Resize the vector to hold the file contents - buf.resize(static_cast(fileSize)); - - // Read the file contents into the vector - file.read(reinterpret_cast(buf.data()), static_cast(fileSize)); - - file.close(); - } else { - std::cout << "Unable to open the file: " << filename << std::endl; - } - - return buf; -} - -/** - * @brief Utility for constructing a proof from proof_data read from a file - * @details Currently hard coded to read an UltraPlonk proof - * - * @return NT::Proof - */ -NT::Proof get_proof_from_file() -{ - NT::Proof proof; - const std::string proof_data_file = "../src/aztec3/circuits/kernel/private/fixtures/ultra_plonk_proof.dat"; - proof.proof_data = read_buffer_from_file(proof_data_file); - return proof; -} - -/** - * @brief Utility for constructing a verification key from verification_key_data stored in file - * @details This verification key corresponds to the UP proof stored in ultra_plonk_proof.dat - * @return std::shared_ptr - */ -std::shared_ptr get_verification_key_from_file() -{ - const std::string vk_data_file = "../src/aztec3/circuits/kernel/private/fixtures/ultra_plonk_verification_key.dat"; - auto vk_buf = utils::read_buffer_from_file(vk_data_file); - NT::VK new_vk; - const uint8_t* vk_iter = vk_buf.data(); - read(vk_iter, new_vk); - - return std::make_shared(new_vk); -} - -/** - * @brief Create a fake verification key - * - * @details will not work with real circuits - * - * @return std::shared_ptr fake verification key - */ -std::shared_ptr fake_vk() -{ - std::map commitments; - commitments["FAKE"] = *new NT::bn254_point(NT::fq(0), NT::fq(0)); - NT::VKData vk_data = { .circuit_type = static_cast(CircuitType::ULTRA), - .circuit_size = 2048, - .num_public_inputs = 116, - .commitments = commitments, - .contains_recursive_proof = false, - .recursive_proof_public_input_indices = {} }; - return std::make_shared(std::move(vk_data), barretenberg::srs::get_crs_factory()->get_verifier_crs()); -} - -/** - * @brief Create a dummy "previous kernel" - * - * @details For use in the first iteration of the kernel circuit - * - * @param real_vk_proof should the vk and proof included be real and usable by real circuits? - * @return PreviousKernelData the previous kernel data for use in the kernel circuit - */ -PreviousKernelData dummy_previous_kernel(bool real_vk_proof = false) -{ - PreviousKernelData const init_previous_kernel{}; - - auto crs_factory = barretenberg::srs::get_crs_factory(); - Builder mock_kernel_builder; - auto mock_kernel_public_inputs = mock_kernel_circuit(mock_kernel_builder, init_previous_kernel.public_inputs); - - NT::Proof const mock_kernel_proof = - real_vk_proof ? get_proof_from_file() : NT::Proof{ .proof_data = std::vector(64, 0) }; - - std::shared_ptr const mock_kernel_vk = real_vk_proof ? get_verification_key_from_file() : fake_vk(); - - PreviousKernelData previous_kernel = { - .public_inputs = mock_kernel_public_inputs, - .proof = mock_kernel_proof, - .vk = mock_kernel_vk, - }; - - // TODO(rahul) assertions don't work in wasm and it isn't worth updating barretenberg to handle our error code - // mechanism. Apparently we are getting rid of this function (dummy_previous_kernel()) soon anyway. - assert(!mock_kernel_builder.failed()); - - return previous_kernel; -} - -} // namespace aztec3::circuits::kernel::private_kernel::utils \ No newline at end of file diff --git a/circuits/cpp/src/aztec3/circuits/kernel/private/utils.hpp b/circuits/cpp/src/aztec3/circuits/kernel/private/utils.hpp deleted file mode 100644 index 0e81a13061a..00000000000 --- a/circuits/cpp/src/aztec3/circuits/kernel/private/utils.hpp +++ /dev/null @@ -1,18 +0,0 @@ -#pragma once -#include "index.hpp" -#include "init.hpp" - -namespace { -using NT = aztec3::utils::types::NativeTypes; -using aztec3::circuits::abis::PreviousKernelData; -} // namespace - -namespace aztec3::circuits::kernel::private_kernel::utils { - -std::vector read_buffer_from_file(const std::string&); -NT::Proof get_proof_from_file(); -std::shared_ptr get_verification_key_from_file(); -std::shared_ptr fake_vk(); -PreviousKernelData dummy_previous_kernel(bool real_vk_proof = false); - -} // namespace aztec3::circuits::kernel::private_kernel::utils \ No newline at end of file diff --git a/circuits/cpp/src/aztec3/circuits/kernel/public/.test.cpp b/circuits/cpp/src/aztec3/circuits/kernel/public/.test.cpp deleted file mode 100644 index c87630c2191..00000000000 --- a/circuits/cpp/src/aztec3/circuits/kernel/public/.test.cpp +++ /dev/null @@ -1,1292 +0,0 @@ -#include "init.hpp" -#include "native_public_kernel_circuit_private_previous_kernel.hpp" -#include "native_public_kernel_circuit_public_previous_kernel.hpp" - -#include "aztec3/circuits/abis/call_context.hpp" -#include "aztec3/circuits/abis/call_stack_item.hpp" -#include "aztec3/circuits/abis/combined_accumulated_data.hpp" -#include "aztec3/circuits/abis/combined_constant_data.hpp" -#include "aztec3/circuits/abis/contract_storage_update_request.hpp" -#include "aztec3/circuits/abis/previous_kernel_data.hpp" -#include "aztec3/circuits/abis/public_kernel/public_call_data.hpp" -#include "aztec3/circuits/abis/public_kernel/public_kernel_inputs.hpp" -#include "aztec3/circuits/abis/tx_context.hpp" -#include "aztec3/circuits/abis/tx_request.hpp" -#include "aztec3/circuits/abis/types.hpp" -#include "aztec3/circuits/hash.hpp" -#include "aztec3/constants.hpp" -#include "aztec3/utils/array.hpp" -#include "aztec3/utils/circuit_errors.hpp" - -#include - -#include - -namespace aztec3::circuits::kernel::public_kernel { -using DummyCircuitBuilder = aztec3::utils::DummyCircuitBuilder; -using aztec3::circuits::abis::public_kernel::PublicKernelInputs; -using NT = aztec3::utils::types::NativeTypes; -using aztec3::circuits::abis::BlockHeader; -using aztec3::circuits::abis::CallContext; -using aztec3::circuits::abis::CallStackItem; -using aztec3::circuits::abis::CombinedAccumulatedData; -using aztec3::circuits::abis::CombinedConstantData; -using aztec3::circuits::abis::ContractStorageRead; -using aztec3::circuits::abis::ContractStorageUpdateRequest; -using aztec3::circuits::abis::NewContractData; -using aztec3::circuits::abis::OptionallyRevealedData; -using aztec3::circuits::abis::PreviousKernelData; -using aztec3::circuits::abis::PublicCircuitPublicInputs; -using aztec3::circuits::abis::PublicDataRead; -using aztec3::circuits::abis::PublicTypes; -using aztec3::circuits::abis::TxContext; -using aztec3::circuits::abis::TxRequest; -using aztec3::circuits::abis::public_kernel::PublicCallData; -using aztec3::utils::source_arrays_are_in_target; -using PublicCallStackItem = CallStackItem; - -template -std::array array_of_values(NT::uint32& count, NT::uint32 num_values_required = SIZE) -{ - ASSERT(num_values_required <= SIZE); - std::array values{}; - for (size_t i = 0; i < num_values_required; i++) { - values[i] = ++count; - } - return values; -} - -template std::array empty_array_of_values() -{ - std::array values{}; - return values; -} - -std::array, MAX_PUBLIC_DATA_UPDATE_REQUESTS_PER_CALL> -generate_contract_storage_update_requests(NT::uint32& count, - NT::uint32 num_values_required = MAX_PUBLIC_DATA_UPDATE_REQUESTS_PER_CALL) -{ - std::array, MAX_PUBLIC_DATA_UPDATE_REQUESTS_PER_CALL> values; - for (size_t i = 0; i < num_values_required; i++) { - const auto prev = count++; - values[i] = ContractStorageUpdateRequest{ - .storage_slot = prev, - .old_value = prev, - .new_value = count, - }; - }; - return values; -} - -std::array, MAX_PUBLIC_DATA_READS_PER_CALL> generate_contract_storage_reads( - NT::uint32& count, NT::uint32 num_values_required = MAX_PUBLIC_DATA_READS_PER_CALL) -{ - std::array, MAX_PUBLIC_DATA_READS_PER_CALL> values; - for (size_t i = 0; i < num_values_required; i++) { - const auto prev = count++; - values[i] = ContractStorageRead{ - .storage_slot = prev, - .current_value = prev, - }; - }; - return values; -} - -PublicCallStackItem generate_call_stack_item(NT::fr contract_address, - NT::fr msg_sender, - NT::fr storage_contract_address, - NT::fr portal_contract_address, - NT::boolean is_delegate_call, - NT::uint32 seed = 0) -{ - NT::uint32 count = seed + 1; - FunctionData const function_data{ - .selector = - { - .value = count, - }, - .is_private = false, - .is_constructor = false, - }; - CallContext const call_context{ - .msg_sender = msg_sender, - .storage_contract_address = storage_contract_address, - .portal_contract_address = portal_contract_address, - .function_selector = function_data.selector, - .is_delegate_call = is_delegate_call, - .is_static_call = false, - .is_contract_deployment = false, - }; - fr const args_hash = count; - std::array const return_values = array_of_values(count); - std::array const public_call_stack = - array_of_values(count); - std::array const new_commitments = - array_of_values(count); - std::array const new_nullifiers = - array_of_values(count); - std::array const new_l2_to_l1_msgs = - array_of_values(count); - std::array, MAX_PUBLIC_DATA_READS_PER_CALL> const reads = - generate_contract_storage_reads(count); - std::array, MAX_PUBLIC_DATA_UPDATE_REQUESTS_PER_CALL> const update_requests = - generate_contract_storage_update_requests(count); - - // create the public circuit public inputs - auto const public_circuit_public_inputs = PublicCircuitPublicInputs{ - .call_context = call_context, - .args_hash = args_hash, - .return_values = return_values, - .contract_storage_update_requests = update_requests, - .contract_storage_reads = reads, - .public_call_stack = public_call_stack, - .new_commitments = new_commitments, - .new_nullifiers = new_nullifiers, - .new_l2_to_l1_msgs = new_l2_to_l1_msgs, - - }; - auto call_stack_item = PublicCallStackItem{ - .contract_address = contract_address, - .function_data = function_data, - .public_inputs = public_circuit_public_inputs, - }; - return call_stack_item; -} - -PublicDataRead public_data_read_from_contract_storage_read(ContractStorageRead const& contract_storage_read, - NT::fr const& contract_address) -{ - return PublicDataRead{ - .leaf_index = compute_public_data_tree_index(contract_address, contract_storage_read.storage_slot), - .value = compute_public_data_tree_value(contract_storage_read.current_value), - }; -} - -PublicDataUpdateRequest public_data_update_request_from_contract_storage_update_request( - ContractStorageUpdateRequest const& contract_storage_update_request, NT::fr const& contract_address) -{ - return PublicDataUpdateRequest{ - .leaf_index = - compute_public_data_tree_index(contract_address, contract_storage_update_request.storage_slot), - .old_value = compute_public_data_tree_value(contract_storage_update_request.old_value), - .new_value = compute_public_data_tree_value(contract_storage_update_request.new_value), - }; -} - -std::array, MAX_PUBLIC_DATA_READS_PER_CALL> public_data_reads_from_contract_storage_reads( - std::array, MAX_PUBLIC_DATA_READS_PER_CALL> const& public_data_reads, - NT::fr const& contract_address) -{ - std::array, MAX_PUBLIC_DATA_READS_PER_CALL> values; - for (size_t i = 0; i < MAX_PUBLIC_DATA_READS_PER_CALL; i++) { - const auto& read = public_data_reads[i]; - if (read.is_empty()) { - continue; - } - values[i] = public_data_read_from_contract_storage_read(read, contract_address); - } - return values; -} - -std::array, MAX_PUBLIC_DATA_UPDATE_REQUESTS_PER_CALL> -public_data_update_requests_from_contract_storage_update_requests( - std::array, MAX_PUBLIC_DATA_UPDATE_REQUESTS_PER_CALL> const& update_requests, - NT::fr const& contract_address) -{ - std::array, MAX_PUBLIC_DATA_UPDATE_REQUESTS_PER_CALL> values; - for (size_t i = 0; i < MAX_PUBLIC_DATA_UPDATE_REQUESTS_PER_TX; i++) { - const auto& update_request = update_requests[i]; - if (update_request.is_empty()) { - continue; - } - values[i] = public_data_update_request_from_contract_storage_update_request(update_request, contract_address); - } - return values; -} - -std::array new_commitments_as_siloed_commitments( - std::array const& new_commitments, NT::fr const& contract_address) -{ - std::array siloed_commitments{}; - for (size_t i = 0; i < MAX_NEW_COMMITMENTS_PER_CALL; ++i) { - if (!new_commitments[i].is_zero()) { - siloed_commitments[i] = silo_commitment(contract_address, new_commitments[i]); - } - } - return siloed_commitments; -} - -std::array new_nullifiers_as_siloed_nullifiers( - std::array const& new_nullifiers, NT::fr const& contract_address) -{ - std::array siloed_nullifiers{}; - for (size_t i = 0; i < MAX_NEW_NULLIFIERS_PER_CALL; ++i) { - if (!new_nullifiers[i].is_zero()) { - siloed_nullifiers[i] = silo_nullifier(contract_address, new_nullifiers[i]); - } - } - return siloed_nullifiers; -} - -std::array new_l2_messages_from_message( - std::array const& new_messages, - NT::fr const& contract_address, - fr const& portal_contract_address, - fr const& chain_id, - fr const& version) -{ - std::array formatted_msgs{}; - for (size_t i = 0; i < MAX_NEW_L2_TO_L1_MSGS_PER_CALL; ++i) { - if (!new_messages[i].is_zero()) { - formatted_msgs[i] = compute_l2_to_l1_hash( - contract_address, version, portal_contract_address, chain_id, new_messages[i]); - } - } - return formatted_msgs; -} - -/** - * @brief Generates the inputs to the public kernel circuit - * - * @param is_constructor whether this public circuit call is a constructor - * @param args_vec the private call's args - * @return PrivateInputs - the inputs to the private call circuit - */ -PublicKernelInputs get_kernel_inputs_with_previous_kernel(NT::boolean private_previous) -{ - NT::uint32 seed = 1000; - - NT::address contract_address = 12345; - const NT::fr portal_contract_address = 23456; - - const NT::address msg_sender = NT::fr(1); - - FunctionData const function_data{ - .selector = - { - .value = 1, - }, - .is_private = false, - .is_constructor = false, - }; - - CallContext const call_context{ - .msg_sender = msg_sender, - .storage_contract_address = contract_address, - .portal_contract_address = portal_contract_address, - .function_selector = function_data.selector, - .is_delegate_call = false, - .is_static_call = false, - .is_contract_deployment = false, - }; - - std::vector const args = { 1, 2, 3 }; - - //*************************************************************************** - // We can create a TxRequest from some of the above data. - //*************************************************************************** - auto const tx_request = TxRequest{ .origin = contract_address, - .function_data = function_data, - .args_hash = compute_var_args_hash(args), - .tx_context = TxContext{ - .is_fee_payment_tx = false, - .is_rebate_payment_tx = false, - .is_contract_deployment_tx = false, - .contract_deployment_data = {}, - .chain_id = 1, - } }; - - std::array child_call_stacks; - NT::fr child_contract_address = 100000; - NT::fr child_portal_contract_address = 200000; - std::array call_stack_hashes{}; - for (size_t i = 0; i < MAX_PUBLIC_CALL_STACK_LENGTH_PER_CALL; i++) { - // NOLINTNEXTLINE(readability-suspicious-call-argument) - child_call_stacks[i] = generate_call_stack_item(child_contract_address, - contract_address, - child_contract_address, - child_portal_contract_address, - false, - seed); - call_stack_hashes[i] = child_call_stacks[i].hash(); - child_contract_address++; - child_portal_contract_address++; - } - - std::array const return_values = - array_of_values(seed, RETURN_VALUES_LENGTH / 2); - std::array, MAX_PUBLIC_DATA_UPDATE_REQUESTS_PER_CALL> const update_requests = - generate_contract_storage_update_requests(seed, MAX_PUBLIC_DATA_UPDATE_REQUESTS_PER_CALL / 2); - std::array, MAX_PUBLIC_DATA_READS_PER_CALL> const reads = - generate_contract_storage_reads(seed, MAX_PUBLIC_DATA_READS_PER_CALL / 2); - std::array const new_commitments = - array_of_values(seed, MAX_NEW_COMMITMENTS_PER_CALL / 2); - std::array const new_nullifiers = - array_of_values(seed, MAX_NEW_NULLIFIERS_PER_CALL / 2); - std::array const new_l2_to_l1_msgs = - array_of_values(seed, MAX_NEW_L2_TO_L1_MSGS_PER_CALL / 2); - std::array const unencrypted_logs_hash = - array_of_values(seed, NUM_FIELDS_PER_SHA256); - fr const unencrypted_log_preimages_length = ++seed; - BlockHeader block_header = { - .note_hash_tree_root = ++seed, - .nullifier_tree_root = ++seed, - .contract_tree_root = ++seed, - .l1_to_l2_message_tree_root = ++seed, - .archive_root = ++seed, - .private_kernel_vk_tree_root = ++seed, - .public_data_tree_root = ++seed, - .global_variables_hash = ++seed, - }; - - // create the public circuit public inputs - auto const public_circuit_public_inputs = PublicCircuitPublicInputs{ - .call_context = call_context, - .args_hash = compute_var_args_hash(args), - .return_values = return_values, - .contract_storage_update_requests = update_requests, - .contract_storage_reads = reads, - .public_call_stack = call_stack_hashes, - .new_commitments = new_commitments, - .new_nullifiers = new_nullifiers, - .new_l2_to_l1_msgs = new_l2_to_l1_msgs, - .unencrypted_logs_hash = unencrypted_logs_hash, - .unencrypted_log_preimages_length = unencrypted_log_preimages_length, - .block_header = block_header, - }; - - const PublicCallStackItem call_stack_item{ - .contract_address = contract_address, - .function_data = tx_request.function_data, - .public_inputs = public_circuit_public_inputs, - }; - - const PublicCallData public_call_data = { - .call_stack_item = call_stack_item, - .public_call_stack_preimages = child_call_stacks, - .portal_contract_address = portal_contract_address, - .bytecode_hash = 1234567, - }; - - // TODO(914) Should this be unused? - [[maybe_unused]] BlockHeader const historical_tree_roots = { - .note_hash_tree_root = 1000, - .contract_tree_root = 2000, - .l1_to_l2_message_tree_root = 3000, - .private_kernel_vk_tree_root = 4000, - }; - - CombinedConstantData const end_constants = { .block_header = - BlockHeader{ .note_hash_tree_root = ++seed, - .nullifier_tree_root = ++seed, - .contract_tree_root = ++seed, - .private_kernel_vk_tree_root = ++seed }, - .tx_context = TxContext{ - .is_fee_payment_tx = false, - .is_rebate_payment_tx = false, - .is_contract_deployment_tx = false, - .contract_deployment_data = {}, - } }; - - std::array public_call_stack{}; - public_call_stack[0] = public_call_data.call_stack_item.hash(); - - CombinedAccumulatedData const end_accumulated_data = { - .new_commitments = - array_of_values(seed, private_previous ? MAX_NEW_COMMITMENTS_PER_TX / 2 : 0), - .new_nullifiers = - array_of_values(seed, private_previous ? MAX_NEW_NULLIFIERS_PER_TX / 2 : 0), - .private_call_stack = array_of_values(seed, 0), - .public_call_stack = public_call_stack, - .new_l2_to_l1_msgs = array_of_values( - seed, private_previous ? MAX_NEW_L2_TO_L1_MSGS_PER_TX / 2 : 0), - .encrypted_logs_hash = array_of_values( - seed, private_previous ? 2 : 0), // only private kernel is producing encrypted logs - .unencrypted_logs_hash = array_of_values(seed, NUM_FIELDS_PER_SHA256), - .encrypted_log_preimages_length = private_previous ? ++seed : 0, - .unencrypted_log_preimages_length = ++seed, - .new_contracts = std::array, MAX_NEW_CONTRACTS_PER_TX>(), - .optionally_revealed_data = - std::array, MAX_OPTIONALLY_REVEALED_DATA_LENGTH_PER_TX>(), - .public_data_update_requests = - std::array, MAX_PUBLIC_DATA_UPDATE_REQUESTS_PER_TX>(), - .public_data_reads = std::array, MAX_PUBLIC_DATA_READS_PER_TX>() - }; - - const KernelCircuitPublicInputs public_inputs = { - .end = end_accumulated_data, - .constants = end_constants, - .is_private = private_previous, - }; - - const PreviousKernelData previous_kernel = { - .public_inputs = public_inputs, - }; - - // NOLINTNEXTLINE(misc-const-correctness) - PublicKernelInputs kernel_inputs = { - .previous_kernel = previous_kernel, - .public_call = public_call_data, - }; - return kernel_inputs; -} // namespace aztec3::circuits::kernel::public_kernel - -template -void validate_public_kernel_outputs_correctly_propagated(const KernelInput& inputs, - const KernelCircuitPublicInputs& public_inputs) -{ - for (size_t i = 0; i < MAX_PUBLIC_CALL_STACK_LENGTH_PER_CALL; i++) { - ASSERT_EQ(public_inputs.end.public_call_stack[i], - inputs.public_call.call_stack_item.public_inputs.public_call_stack[i]); - } - - const auto contract_address = inputs.public_call.call_stack_item.contract_address; - size_t st_index = 0; - for (size_t i = 0; i < MAX_PUBLIC_DATA_UPDATE_REQUESTS_PER_TX; i++) { - const auto& contract_storage_update_request = - inputs.public_call.call_stack_item.public_inputs.contract_storage_update_requests[i]; - if (contract_storage_update_request.is_empty()) { - continue; - } - const auto public_data_update_request = public_data_update_request_from_contract_storage_update_request( - contract_storage_update_request, contract_address); - ASSERT_EQ(public_inputs.end.public_data_update_requests[st_index++], public_data_update_request); - } - - size_t sr_index = 0; - for (size_t i = 0; i < MAX_PUBLIC_DATA_READS_PER_TX; i++) { - const auto& read = inputs.public_call.call_stack_item.public_inputs.contract_storage_reads[i]; - if (read.is_empty()) { - continue; - } - const auto public_read = public_data_read_from_contract_storage_read(read, contract_address); - ASSERT_EQ(public_inputs.end.public_data_reads[sr_index++], public_read); - } -} - -void validate_private_data_propagation(DummyBuilder& builder, - const PublicKernelInputs& inputs, - const KernelCircuitPublicInputs& public_inputs) -{ - ASSERT_TRUE(source_arrays_are_in_target(builder, - inputs.previous_kernel.public_inputs.end.private_call_stack, - std::array{}, - public_inputs.end.private_call_stack)); - - ASSERT_TRUE(source_arrays_are_in_target(builder, - inputs.previous_kernel.public_inputs.end.new_contracts, - std::array, MAX_NEW_CONTRACTS_PER_TX>(), - public_inputs.end.new_contracts)); - - ASSERT_EQ(inputs.previous_kernel.public_inputs.end.encrypted_logs_hash, - inputs.previous_kernel.public_inputs.end.encrypted_logs_hash); - ASSERT_EQ(inputs.previous_kernel.public_inputs.end.encrypted_log_preimages_length, - inputs.previous_kernel.public_inputs.end.encrypted_log_preimages_length); - - ASSERT_EQ(inputs.previous_kernel.public_inputs.end.optionally_revealed_data, - public_inputs.end.optionally_revealed_data); -} - -TEST(public_kernel_tests, only_valid_public_data_reads_should_be_propagated) -{ - DummyBuilder dummyBuilder = DummyBuilder("public_kernel_tests__only_valid_public_data_reads_should_be_propagated"); - PublicKernelInputs inputs = get_kernel_inputs_with_previous_kernel(true); - - // modify the contract storage reads so only 2 are valid and only those should be propagated - const auto first_valid = ContractStorageRead{ - .storage_slot = 123456789, - .current_value = 76543, - }; - const auto second_valid = ContractStorageRead{ - .storage_slot = 123456789, - .current_value = 76543, - }; - std::array, MAX_PUBLIC_DATA_READS_PER_CALL> reads = - std::array, MAX_PUBLIC_DATA_READS_PER_CALL>(); - reads[1] = first_valid; - reads[3] = second_valid; - inputs.public_call.call_stack_item.public_inputs.contract_storage_reads = reads; - - // adjust the call stack item hash for the current call in the previous iteration - inputs.previous_kernel.public_inputs.end.public_call_stack[0] = inputs.public_call.call_stack_item.hash(); - - auto public_inputs = native_public_kernel_circuit_private_previous_kernel(dummyBuilder, inputs); - ASSERT_EQ(dummyBuilder.get_first_failure(), utils::CircuitError::no_error()); - ASSERT_FALSE(dummyBuilder.failed()); - - ASSERT_FALSE(public_inputs.is_private); - ASSERT_EQ(public_inputs.constants.tx_context, inputs.previous_kernel.public_inputs.constants.tx_context); - - for (size_t i = 0; i < MAX_PUBLIC_CALL_STACK_LENGTH_PER_CALL; i++) { - ASSERT_EQ(public_inputs.end.public_call_stack[i], - inputs.public_call.call_stack_item.public_inputs.public_call_stack[i]); - } - - // only the 2 valid reads should have been propagated - const auto contract_address = inputs.public_call.call_stack_item.contract_address; - const auto public_read_1 = public_data_read_from_contract_storage_read(first_valid, contract_address); - const auto public_read_2 = public_data_read_from_contract_storage_read(second_valid, contract_address); - ASSERT_EQ(public_inputs.end.public_data_reads[0], public_read_1); - ASSERT_EQ(public_inputs.end.public_data_reads[1], public_read_2); -} - -TEST(public_kernel_tests, only_valid_update_requests_should_be_propagated) -{ - DummyBuilder dummyBuilder = DummyBuilder("public_kernel_tests__only_valid_update_requests_should_be_propagated"); - PublicKernelInputs inputs = get_kernel_inputs_with_previous_kernel(true); - - // modify the contract storage update requests so only 2 are valid and only those should be propagated - const auto first_valid = ContractStorageUpdateRequest{ - .storage_slot = 123456789, - .old_value = 76543, - .new_value = 76544, - }; - const auto second_valid = ContractStorageUpdateRequest{ - .storage_slot = 987654321, - .old_value = 86543, - .new_value = 86544, - }; - std::array, MAX_PUBLIC_DATA_UPDATE_REQUESTS_PER_CALL> update_requests = - std::array, MAX_PUBLIC_DATA_UPDATE_REQUESTS_PER_CALL>(); - update_requests[1] = first_valid; - update_requests[3] = second_valid; - inputs.public_call.call_stack_item.public_inputs.contract_storage_update_requests = update_requests; - - // adjust the call stack item hash for the current call in the previous iteration - inputs.previous_kernel.public_inputs.end.public_call_stack[0] = inputs.public_call.call_stack_item.hash(); - - auto public_inputs = native_public_kernel_circuit_private_previous_kernel(dummyBuilder, inputs); - ASSERT_EQ(dummyBuilder.get_first_failure(), utils::CircuitError::no_error()); - ASSERT_FALSE(dummyBuilder.failed()); - - ASSERT_FALSE(public_inputs.is_private); - ASSERT_EQ(public_inputs.constants.tx_context, inputs.previous_kernel.public_inputs.constants.tx_context); - - for (size_t i = 0; i < MAX_PUBLIC_CALL_STACK_LENGTH_PER_CALL; i++) { - ASSERT_EQ(public_inputs.end.public_call_stack[i], - inputs.public_call.call_stack_item.public_inputs.public_call_stack[i]); - } - - // only the 2 valid update requests should have been propagated - const auto contract_address = inputs.public_call.call_stack_item.contract_address; - const auto public_write_1 = - public_data_update_request_from_contract_storage_update_request(first_valid, contract_address); - const auto public_write_2 = - public_data_update_request_from_contract_storage_update_request(second_valid, contract_address); - ASSERT_EQ(public_inputs.end.public_data_update_requests[0], public_write_1); - ASSERT_EQ(public_inputs.end.public_data_update_requests[1], public_write_2); -} - -TEST(public_kernel_tests, constructor_should_fail) -{ - DummyBuilder dummyBuilder = DummyBuilder("public_kernel_tests__constructor_should_fail"); - PublicKernelInputs inputs = get_kernel_inputs_with_previous_kernel(true); - - inputs.public_call.call_stack_item.function_data.is_constructor = true; - auto public_inputs = native_public_kernel_circuit_private_previous_kernel(dummyBuilder, inputs); - ASSERT_TRUE(dummyBuilder.failed()); - ASSERT_EQ(dummyBuilder.get_first_failure().code, CircuitErrorCode::PUBLIC_KERNEL__CONSTRUCTOR_NOT_ALLOWED); -} - -TEST(public_kernel_tests, constructor_should_fail_2) -{ - DummyBuilder dummyBuilder = DummyBuilder("public_kernel_tests__constructor_should_fail_2"); - PublicKernelInputs inputs = get_kernel_inputs_with_previous_kernel(true); - - inputs.public_call.call_stack_item.public_inputs.call_context.is_contract_deployment = true; - auto public_inputs = native_public_kernel_circuit_private_previous_kernel(dummyBuilder, inputs); - ASSERT_TRUE(dummyBuilder.failed()); - ASSERT_EQ(dummyBuilder.get_first_failure().code, CircuitErrorCode::PUBLIC_KERNEL__CONTRACT_DEPLOYMENT_NOT_ALLOWED); -} - -TEST(public_kernel_tests, no_bytecode_hash_should_fail) -{ - DummyBuilder dummyBuilder = DummyBuilder("public_kernel_tests__no_bytecode_hash_should_fail"); - PublicKernelInputs inputs = get_kernel_inputs_with_previous_kernel(true); - - inputs.public_call.bytecode_hash = 0; - auto public_inputs = native_public_kernel_circuit_private_previous_kernel(dummyBuilder, inputs); - ASSERT_TRUE(dummyBuilder.failed()); - ASSERT_EQ(dummyBuilder.get_first_failure().code, CircuitErrorCode::PUBLIC_KERNEL__BYTECODE_HASH_INVALID); -} - - -TEST(public_kernel_tests, invalid_is_internal) -{ - DummyBuilder dummyBuilder = DummyBuilder("public_kernel_tests__no_bytecode_hash_should_fail"); - PublicKernelInputs inputs = get_kernel_inputs_with_previous_kernel(true); - - // Make the call internal but msg_sender != storage_contract_address. - inputs.public_call.call_stack_item.function_data.is_internal = true; - inputs.public_call.call_stack_item.public_inputs.call_context.msg_sender = 1; - inputs.public_call.call_stack_item.public_inputs.call_context.storage_contract_address = 2; - - auto public_inputs = native_public_kernel_circuit_private_previous_kernel(dummyBuilder, inputs); - ASSERT_TRUE(dummyBuilder.failed()); - ASSERT_EQ(dummyBuilder.get_first_failure().code, CircuitErrorCode::PUBLIC_KERNEL__IS_INTERNAL_BUT_NOT_SELF_CALL); -} - -TEST(public_kernel_tests, contract_address_must_be_valid) -{ - DummyBuilder dummyBuilder = DummyBuilder("public_kernel_tests__contract_address_must_be_valid"); - PublicKernelInputs inputs = get_kernel_inputs_with_previous_kernel(true); - - inputs.public_call.call_stack_item.contract_address = 0; - auto public_inputs = native_public_kernel_circuit_private_previous_kernel(dummyBuilder, inputs); - ASSERT_TRUE(dummyBuilder.failed()); - ASSERT_EQ(dummyBuilder.get_first_failure().code, CircuitErrorCode::PUBLIC_KERNEL__CONTRACT_ADDRESS_INVALID); -} - -TEST(public_kernel_tests, function_selector_must_be_valid) -{ - DummyBuilder dummyBuilder = DummyBuilder("public_kernel_tests__function_selector_must_be_valid"); - PublicKernelInputs inputs = get_kernel_inputs_with_previous_kernel(true); - - inputs.public_call.call_stack_item.function_data.selector = { - .value = 0, - }; - auto public_inputs = native_public_kernel_circuit_private_previous_kernel(dummyBuilder, inputs); - ASSERT_TRUE(dummyBuilder.failed()); - ASSERT_EQ(dummyBuilder.get_first_failure().code, CircuitErrorCode::PUBLIC_KERNEL__FUNCTION_SIGNATURE_INVALID); -} - -TEST(public_kernel_tests, private_call_should_fail) -{ - DummyBuilder dummyBuilder = DummyBuilder("public_kernel_tests__private_call_should_fail"); - PublicKernelInputs inputs = get_kernel_inputs_with_previous_kernel(true); - - inputs.public_call.call_stack_item.function_data.is_private = true; - auto public_inputs = native_public_kernel_circuit_private_previous_kernel(dummyBuilder, inputs); - ASSERT_TRUE(dummyBuilder.failed()); - ASSERT_EQ(dummyBuilder.get_first_failure().code, CircuitErrorCode::PUBLIC_KERNEL__PRIVATE_FUNCTION_NOT_ALLOWED); -} - -TEST(public_kernel_tests, inconsistent_call_hash_should_fail) -{ - for (size_t i = 0; i < MAX_PUBLIC_CALL_STACK_LENGTH_PER_CALL; i++) { - DummyBuilder dummyBuilder = DummyBuilder(format("public_kernel_tests__inconsistent_call_hash_should_fail-", i)); - PublicKernelInputs inputs = get_kernel_inputs_with_previous_kernel(true); - - // change a value of something in the call stack pre-image - inputs.public_call.public_call_stack_preimages[i].public_inputs.args_hash++; - auto public_inputs = native_public_kernel_circuit_private_previous_kernel(dummyBuilder, inputs); - ASSERT_TRUE(dummyBuilder.failed()); - ASSERT_EQ(dummyBuilder.get_first_failure().code, CircuitErrorCode::PUBLIC_KERNEL__PUBLIC_CALL_STACK_MISMATCH); - } -} - -TEST(public_kernel_tests, incorrect_storage_contract_address_fails_for_regular_calls) -{ - for (size_t i = 0; i < MAX_PUBLIC_CALL_STACK_LENGTH_PER_CALL; i++) { - DummyBuilder dummyBuilder = - DummyBuilder(format("public_kernel_tests__incorrect_storage_contract_address_fails_for_regular_calls-", i)); - PublicKernelInputs inputs = get_kernel_inputs_with_previous_kernel(true); - - // change the storage contract address so it does not equal the contract address - const NT::fr new_contract_address = - NT::fr(inputs.public_call.public_call_stack_preimages[i].contract_address) + 1; - inputs.public_call.public_call_stack_preimages[i].public_inputs.call_context.storage_contract_address = - new_contract_address; - // update the call stack item hash after the change in the preimage - inputs.public_call.call_stack_item.public_inputs.public_call_stack[i] = - inputs.public_call.public_call_stack_preimages[i].hash(); - auto public_inputs = native_public_kernel_circuit_private_previous_kernel(dummyBuilder, inputs); - ASSERT_TRUE(dummyBuilder.failed()); - ASSERT_EQ(dummyBuilder.get_first_failure().code, - CircuitErrorCode::PUBLIC_KERNEL__PUBLIC_CALL_STACK_INVALID_STORAGE_ADDRESS); - } -} - -TEST(public_kernel_tests, incorrect_msg_sender_fails_for_regular_calls) -{ - for (size_t i = 0; i < MAX_PUBLIC_CALL_STACK_LENGTH_PER_CALL; i++) { - DummyBuilder dummyBuilder = - DummyBuilder(format("public_kernel_tests__incorrect_msg_sender_fails_for_regular_calls-", i)); - PublicKernelInputs inputs = get_kernel_inputs_with_previous_kernel(true); - // set the msg sender to be the address of the called contract, which is wrong - const auto new_msg_sender = inputs.public_call.public_call_stack_preimages[i].contract_address; - // change the storage contract address so it does not equal the contract address - inputs.public_call.public_call_stack_preimages[i].public_inputs.call_context.msg_sender = new_msg_sender; - // update the call stack item hash after the change in the preimage - inputs.public_call.call_stack_item.public_inputs.public_call_stack[i] = - inputs.public_call.public_call_stack_preimages[i].hash(); - auto public_inputs = native_public_kernel_circuit_private_previous_kernel(dummyBuilder, inputs); - ASSERT_TRUE(dummyBuilder.failed()); - ASSERT_EQ(dummyBuilder.get_first_failure().code, - CircuitErrorCode::PUBLIC_KERNEL__PUBLIC_CALL_STACK_INVALID_MSG_SENDER); - } -} - -TEST(public_kernel_tests, public_kernel_circuit_succeeds_for_mixture_of_regular_and_delegate_calls) -{ - DummyBuilder dummyBuilder = - DummyBuilder("public_kernel_tests__public_kernel_circuit_succeeds_for_mixture_of_regular_and_delegate_calls"); - PublicKernelInputs inputs = get_kernel_inputs_with_previous_kernel(true); - - const auto contract_address = NT::fr(inputs.public_call.call_stack_item.contract_address); - const auto origin_msg_sender = NT::fr(inputs.public_call.call_stack_item.public_inputs.call_context.msg_sender); - const auto contract_portal_address = NT::fr(inputs.public_call.portal_contract_address); - - // redefine the child calls/stacks to use some delegate calls - NT::uint32 const seed = 1000; - NT::fr child_contract_address = 100000; - NT::fr child_portal_contract_address = 200000; - NT::boolean is_delegate_call = false; - for (size_t i = 0; i < MAX_PUBLIC_CALL_STACK_LENGTH_PER_CALL; i++) { - inputs.public_call.public_call_stack_preimages[i] = - // NOLINTNEXTLINE(readability-suspicious-call-argument) - generate_call_stack_item(child_contract_address, - is_delegate_call ? origin_msg_sender : contract_address, - is_delegate_call ? contract_address : child_contract_address, - is_delegate_call ? contract_portal_address : child_portal_contract_address, - is_delegate_call, - seed); - inputs.public_call.call_stack_item.public_inputs.public_call_stack[i] = - inputs.public_call.public_call_stack_preimages[i].hash(); - - // change the next call type - is_delegate_call = !is_delegate_call; - child_contract_address++; - child_portal_contract_address++; - } - - // we update the hash of the current call stack item in the previous kernel, - // since we modified the hash of the nested calls, which changes the hash of the parent item - inputs.previous_kernel.public_inputs.end.public_call_stack[0] = inputs.public_call.call_stack_item.hash(); - - auto public_inputs = native_public_kernel_circuit_private_previous_kernel(dummyBuilder, inputs); - ASSERT_EQ(dummyBuilder.get_first_failure(), utils::CircuitError::no_error()); - ASSERT_FALSE(dummyBuilder.failed()); -} - -TEST(public_kernel_tests, public_kernel_circuit_fails_on_incorrect_msg_sender_in_delegate_call) -{ - DummyBuilder dummyBuilder = - DummyBuilder("public_kernel_tests__public_kernel_circuit_fails_on_incorrect_msg_sender_in_delegate_call"); - PublicKernelInputs inputs = get_kernel_inputs_with_previous_kernel(true); - - const auto contract_address = NT::fr(inputs.public_call.call_stack_item.contract_address); - // const auto origin_msg_sender = NT::fr(inputs.public_call.call_stack_item.public_inputs.call_context.msg_sender); - const auto contract_portal_address = NT::fr(inputs.public_call.portal_contract_address); - - // set the first call stack item to be a delegate call - std::array child_call_stacks; - NT::uint32 const seed = 1000; - NT::fr const child_contract_address = 100000; - std::array call_stack_hashes{}; - child_call_stacks[0] = - // NOLINTNEXTLINE(readability-suspicious-call-argument) - generate_call_stack_item(child_contract_address, - contract_address, // this should be the origin_msg_sender, not the contract address - contract_address, - contract_portal_address, - true, - seed); - call_stack_hashes[0] = child_call_stacks[0].hash(); - - inputs.public_call.call_stack_item.public_inputs.public_call_stack = call_stack_hashes; - inputs.public_call.public_call_stack_preimages = child_call_stacks; - auto public_inputs = native_public_kernel_circuit_private_previous_kernel(dummyBuilder, inputs); - ASSERT_TRUE(dummyBuilder.failed()); - ASSERT_EQ(dummyBuilder.get_first_failure().code, - CircuitErrorCode::PUBLIC_KERNEL__PUBLIC_CALL_STACK_INVALID_MSG_SENDER); -} - -TEST(public_kernel_tests, public_kernel_circuit_fails_on_incorrect_storage_contract_in_delegate_call) -{ - DummyBuilder dummyBuilder = - DummyBuilder("public_kernel_tests__public_kernel_circuit_fails_on_incorrect_storage_contract_in_delegate_call"); - PublicKernelInputs inputs = get_kernel_inputs_with_previous_kernel(true); - - // const auto contract_address = NT::fr(inputs.public_call.call_stack_item.contract_address); - const auto origin_msg_sender = NT::fr(inputs.public_call.call_stack_item.public_inputs.call_context.msg_sender); - const auto contract_portal_address = NT::fr(inputs.public_call.portal_contract_address); - - // set the first call stack item to be a delegate call - std::array child_call_stacks; - NT::uint32 const seed = 1000; - NT::fr const child_contract_address = 100000; - std::array call_stack_hashes{}; - child_call_stacks[0] = generate_call_stack_item(child_contract_address, - origin_msg_sender, - child_contract_address, // this should be contract_address - contract_portal_address, - true, - seed); - call_stack_hashes[0] = child_call_stacks[0].hash(); - - inputs.public_call.call_stack_item.public_inputs.public_call_stack = call_stack_hashes; - inputs.public_call.public_call_stack_preimages = child_call_stacks; - auto public_inputs = native_public_kernel_circuit_private_previous_kernel(dummyBuilder, inputs); - ASSERT_TRUE(dummyBuilder.failed()); - ASSERT_EQ(dummyBuilder.get_first_failure().code, - CircuitErrorCode::PUBLIC_KERNEL__PUBLIC_CALL_STACK_INVALID_STORAGE_ADDRESS); -} - -TEST(public_kernel_tests, public_kernel_circuit_fails_on_incorrect_portal_contract_in_delegate_call) -{ - DummyBuilder dummyBuilder = - DummyBuilder("public_kernel_tests__public_kernel_circuit_fails_on_incorrect_portal_contract_in_delegate_call"); - PublicKernelInputs inputs = get_kernel_inputs_with_previous_kernel(true); - - const auto contract_address = NT::fr(inputs.public_call.call_stack_item.contract_address); - const auto origin_msg_sender = NT::fr(inputs.public_call.call_stack_item.public_inputs.call_context.msg_sender); - // const auto contract_portal_address = NT::fr(inputs.public_call.portal_contract_address); - - // set the first call stack item to be a delegate call - std::array child_call_stacks; - NT::uint32 const seed = 1000; - NT::fr const child_contract_address = 100000; - NT::fr const child_portal_contract = 200000; - std::array call_stack_hashes{}; - // NOLINTNEXTLINE(readability-suspicious-call-argument) - child_call_stacks[0] = generate_call_stack_item(child_contract_address, - origin_msg_sender, - contract_address, - child_portal_contract, // this should be contract_portal_address - true, - seed); - call_stack_hashes[0] = child_call_stacks[0].hash(); - - inputs.public_call.call_stack_item.public_inputs.public_call_stack = call_stack_hashes; - inputs.public_call.public_call_stack_preimages = child_call_stacks; - auto public_inputs = native_public_kernel_circuit_private_previous_kernel(dummyBuilder, inputs); - ASSERT_TRUE(dummyBuilder.failed()); - ASSERT_EQ(dummyBuilder.get_first_failure().code, - CircuitErrorCode::PUBLIC_KERNEL__PUBLIC_CALL_STACK_INVALID_PORTAL_ADDRESS); -} - -TEST(public_kernel_tests, public_kernel_circuit_only_checks_non_empty_call_stacks) -{ - DummyBuilder dummyBuilder = - DummyBuilder("public_kernel_tests__public_kernel_circuit_only_checks_non_empty_call_stacks"); - PublicKernelInputs inputs = get_kernel_inputs_with_previous_kernel(true); - - const auto contract_address = NT::fr(inputs.public_call.call_stack_item.contract_address); - const auto origin_msg_sender = NT::fr(inputs.public_call.call_stack_item.public_inputs.call_context.msg_sender); - // const auto contract_portal_address = NT::fr(inputs.public_call.portal_contract_address); - - // set all but the first call stack item to have a zero call stack hash - // these call stack items will have an contract portal address but will be ignored as the call stack will be ignored - std::array& child_call_stacks = - inputs.public_call.public_call_stack_preimages; - std::array& call_stack_hashes = - inputs.public_call.call_stack_item.public_inputs.public_call_stack; - NT::uint32 const seed = 1000; - NT::fr const child_contract_address = 100000; - NT::fr const child_portal_contract = 200000; - for (size_t i = 1; i < MAX_PUBLIC_CALL_STACK_LENGTH_PER_CALL; i++) { - // NOLINTNEXTLINE(readability-suspicious-call-argument) - child_call_stacks[i] = generate_call_stack_item(child_contract_address, - origin_msg_sender, - contract_address, - child_portal_contract, // shuold be contract_portal_address - false, - seed); - // setting this to zero makes the call stack item be ignored so it won't fail - call_stack_hashes[i] = 0; - } - // adjust the call stack item hash for the current call in the previous iteration - inputs.previous_kernel.public_inputs.end.public_call_stack[0] = inputs.public_call.call_stack_item.hash(); - - auto public_inputs = native_public_kernel_circuit_private_previous_kernel(dummyBuilder, inputs); - ASSERT_EQ(dummyBuilder.get_first_failure(), utils::CircuitError::no_error()); - ASSERT_FALSE(dummyBuilder.failed()); -} - -TEST(public_kernel_tests, public_kernel_circuit_with_private_previous_kernel_should_succeed) -{ - DummyBuilder dummyBuilder = - DummyBuilder("public_kernel_tests__public_kernel_circuit_with_private_previous_kernel_should_succeed"); - PublicKernelInputs const inputs = get_kernel_inputs_with_previous_kernel(true); - auto public_inputs = native_public_kernel_circuit_private_previous_kernel(dummyBuilder, inputs); - ASSERT_FALSE(dummyBuilder.failed()); -} - -TEST(public_kernel_tests, circuit_outputs_should_be_correctly_populated_with_previous_private_kernel) -{ - DummyBuilder dummyBuilder = - DummyBuilder("public_kernel_tests__circuit_outputs_should_be_correctly_populated_with_previous_private_kernel"); - PublicKernelInputs const inputs = get_kernel_inputs_with_previous_kernel(true); - auto public_inputs = native_public_kernel_circuit_private_previous_kernel(dummyBuilder, inputs); - - // test that the prior set of private kernel public inputs were copied to the outputs - validate_private_data_propagation(dummyBuilder, inputs, public_inputs); - - validate_public_kernel_outputs_correctly_propagated(inputs, public_inputs); - ASSERT_FALSE(dummyBuilder.failed()); -} - -TEST(public_kernel_tests, private_previous_kernel_non_empty_private_call_stack_should_fail) -{ - DummyBuilder dummyBuilder = - DummyBuilder("public_kernel_tests__private_previous_kernel_non_empty_private_call_stack_should_fail"); - PublicKernelInputs inputs = get_kernel_inputs_with_previous_kernel(true); - inputs.previous_kernel.public_inputs.end.private_call_stack[0] = 1; - auto public_inputs = native_public_kernel_circuit_private_previous_kernel(dummyBuilder, inputs); - ASSERT_TRUE(dummyBuilder.failed()); - ASSERT_EQ(dummyBuilder.get_first_failure().code, CircuitErrorCode::PUBLIC_KERNEL__NON_EMPTY_PRIVATE_CALL_STACK); -} - -TEST(public_kernel_tests, private_previous_kernel_empty_public_call_stack_should_fail) -{ - DummyBuilder dummyBuilder = - DummyBuilder("public_kernel_tests__private_previous_kernel_empty_public_call_stack_should_fail"); - PublicKernelInputs inputs = get_kernel_inputs_with_previous_kernel(true); - inputs.previous_kernel.public_inputs.end.public_call_stack = - std::array{}; - auto public_inputs = native_public_kernel_circuit_private_previous_kernel(dummyBuilder, inputs); - ASSERT_TRUE(dummyBuilder.failed()); - ASSERT_EQ(dummyBuilder.get_first_failure().code, CircuitErrorCode::PUBLIC_KERNEL__EMPTY_PUBLIC_CALL_STACK); -} - -TEST(public_kernel_tests, private_previous_kernel_non_private_previous_kernel_should_fail) -{ - DummyBuilder dummyBuilder = - DummyBuilder("public_kernel_tests__private_previous_kernel_non_private_previous_kernel_should_fail"); - PublicKernelInputs inputs = get_kernel_inputs_with_previous_kernel(true); - inputs.previous_kernel.public_inputs.is_private = false; - auto public_inputs = native_public_kernel_circuit_private_previous_kernel(dummyBuilder, inputs); - ASSERT_TRUE(dummyBuilder.failed()); - ASSERT_EQ(dummyBuilder.get_first_failure().code, CircuitErrorCode::PUBLIC_KERNEL__PREVIOUS_KERNEL_NOT_PRIVATE); -} - -TEST(public_kernel_tests, previous_private_kernel_fails_if_contract_storage_update_requests_on_static_call) -{ - DummyBuilder dummyBuilder = DummyBuilder( - "public_kernel_tests__previous_private_kernel_fails_if_contract_storage_update_requests_on_static_call"); - PublicKernelInputs inputs = get_kernel_inputs_with_previous_kernel(true); - - // the function call has contract storage update requests so setting it to static should fail - inputs.public_call.call_stack_item.public_inputs.call_context.is_static_call = true; - - auto public_inputs = native_public_kernel_circuit_private_previous_kernel(dummyBuilder, inputs); - ASSERT_TRUE(dummyBuilder.failed()); - ASSERT_EQ( - dummyBuilder.get_first_failure().code, - CircuitErrorCode::PUBLIC_KERNEL__CALL_CONTEXT_CONTRACT_STORAGE_UPDATE_REQUESTS_PROHIBITED_FOR_STATIC_CALL); -} - -TEST(public_kernel_tests, previous_private_kernel_fails_if_incorrect_storage_contract_on_delegate_call) -{ - DummyBuilder dummyBuilder = DummyBuilder( - "public_kernel_tests__previous_private_kernel_fails_if_incorrect_storage_contract_on_delegate_call"); - PublicKernelInputs inputs = get_kernel_inputs_with_previous_kernel(true); - - // the function call has the contract address and storage contract address equal and so it should fail for a - // delegate call - inputs.public_call.call_stack_item.public_inputs.call_context.is_delegate_call = true; - - auto public_inputs = native_public_kernel_circuit_private_previous_kernel(dummyBuilder, inputs); - ASSERT_TRUE(dummyBuilder.failed()); - ASSERT_EQ(dummyBuilder.get_first_failure().code, - CircuitErrorCode::PUBLIC_KERNEL__CALL_CONTEXT_INVALID_STORAGE_ADDRESS_FOR_DELEGATE_CALL); -} - -TEST(public_kernel_tests, public_kernel_circuit_with_public_previous_kernel_should_succeed) -{ - DummyBuilder dummyBuilder = - DummyBuilder("public_kernel_tests__public_kernel_circuit_with_public_previous_kernel_should_succeed"); - PublicKernelInputs const inputs = get_kernel_inputs_with_previous_kernel(false); - auto public_inputs = native_public_kernel_circuit_public_previous_kernel(dummyBuilder, inputs); - ASSERT_FALSE(dummyBuilder.failed()); -} - -TEST(public_kernel_tests, public_previous_kernel_empty_public_call_stack_should_fail) -{ - DummyBuilder dummyBuilder = - DummyBuilder("public_kernel_tests__public_previous_kernel_empty_public_call_stack_should_fail"); - PublicKernelInputs inputs = get_kernel_inputs_with_previous_kernel(false); - inputs.previous_kernel.public_inputs.end.public_call_stack = - std::array{}; - auto public_inputs = native_public_kernel_circuit_public_previous_kernel(dummyBuilder, inputs); - ASSERT_TRUE(dummyBuilder.failed()); - ASSERT_EQ(dummyBuilder.get_first_failure().code, CircuitErrorCode::PUBLIC_KERNEL__EMPTY_PUBLIC_CALL_STACK); -} - -TEST(public_kernel_tests, public_previous_kernel_private_previous_kernel_should_fail) -{ - DummyBuilder dummyBuilder = - DummyBuilder("public_kernel_tests__public_previous_kernel_private_previous_kernel_should_fail"); - PublicKernelInputs inputs = get_kernel_inputs_with_previous_kernel(false); - inputs.previous_kernel.public_inputs.is_private = true; - auto public_inputs = native_public_kernel_circuit_public_previous_kernel(dummyBuilder, inputs); - ASSERT_TRUE(dummyBuilder.failed()); - ASSERT_EQ(dummyBuilder.get_first_failure().code, CircuitErrorCode::PUBLIC_KERNEL__PREVIOUS_KERNEL_NOT_PUBLIC); -} - -TEST(public_kernel_tests, circuit_outputs_should_be_correctly_populated_with_previous_public_kernel) -{ - DummyBuilder dummyBuilder = - DummyBuilder("public_kernel_tests__circuit_outputs_should_be_correctly_populated_with_previous_public_kernel"); - PublicKernelInputs inputs = get_kernel_inputs_with_previous_kernel(false); - - // setup 2 previous data writes on the public inputs - const auto first_write = PublicDataUpdateRequest{ - .leaf_index = 123456789, - .old_value = 76543, - .new_value = 76544, - }; - const auto second_write = PublicDataUpdateRequest{ - .leaf_index = 987654321, - .old_value = 86543, - .new_value = 86544, - }; - std::array, MAX_PUBLIC_DATA_UPDATE_REQUESTS_PER_TX> initial_writes = - std::array, MAX_PUBLIC_DATA_UPDATE_REQUESTS_PER_TX>(); - initial_writes[0] = first_write; - initial_writes[1] = second_write; - inputs.previous_kernel.public_inputs.end.public_data_update_requests = initial_writes; - - // setup 2 previous data reads on the public inputs - const auto first_read = PublicDataRead{ - .leaf_index = 123456789, - .value = 96543, - }; - const auto second_read = PublicDataRead{ - .leaf_index = 987654321, - .value = 96544, - }; - std::array, MAX_PUBLIC_DATA_READS_PER_TX> initial_reads = - std::array, MAX_PUBLIC_DATA_READS_PER_TX>(); - initial_reads[0] = first_read; - initial_reads[1] = second_read; - inputs.previous_kernel.public_inputs.end.public_data_reads = initial_reads; - - // setup 2 previous new commitments - std::array initial_commitments{}; - initial_commitments[0] = fr(1); - initial_commitments[1] = fr(2); - inputs.previous_kernel.public_inputs.end.new_commitments = initial_commitments; - - // setup 2 previous new nullifiers - std::array initial_nullifiers{}; - initial_nullifiers[0] = fr(12345); - initial_nullifiers[1] = fr(67890); - inputs.previous_kernel.public_inputs.end.new_nullifiers = initial_nullifiers; - - // setup 1 new l2 to l1 messages - std::array initial_l2_to_l1_messages{}; - initial_l2_to_l1_messages[0] = fr(1); - inputs.previous_kernel.public_inputs.end.new_l2_to_l1_msgs = initial_l2_to_l1_messages; - - auto public_inputs = native_public_kernel_circuit_public_previous_kernel(dummyBuilder, inputs); - - // test that the prior set of private kernel public inputs were copied to the outputs - validate_private_data_propagation(dummyBuilder, inputs, public_inputs); - - // this call should have been popped from the public call stack and the stack of call pre images pushed on - for (size_t i = 0; i < MAX_PUBLIC_CALL_STACK_LENGTH_PER_CALL; i++) { - ASSERT_EQ(public_inputs.end.public_call_stack[i], - inputs.public_call.call_stack_item.public_inputs.public_call_stack[i]); - } - - // we should now see the public data reads and writes, new commitments, new_nullifiers, - // l2_to_l1_messages from this iteration appended to the combined output - ASSERT_EQ(array_length(public_inputs.end.public_data_reads), - array_length(inputs.previous_kernel.public_inputs.end.public_data_reads) + - array_length(inputs.public_call.call_stack_item.public_inputs.contract_storage_reads)); - ASSERT_EQ(array_length(public_inputs.end.public_data_update_requests), - array_length(inputs.previous_kernel.public_inputs.end.public_data_update_requests) + - array_length(inputs.public_call.call_stack_item.public_inputs.contract_storage_update_requests)); - ASSERT_EQ(array_length(public_inputs.end.new_commitments), - array_length(inputs.previous_kernel.public_inputs.end.new_commitments) + - array_length(inputs.public_call.call_stack_item.public_inputs.new_commitments)); - ASSERT_EQ(array_length(public_inputs.end.new_nullifiers), - array_length(inputs.previous_kernel.public_inputs.end.new_nullifiers) + - array_length(inputs.public_call.call_stack_item.public_inputs.new_nullifiers)); - ASSERT_EQ(array_length(public_inputs.end.new_l2_to_l1_msgs), - array_length(inputs.previous_kernel.public_inputs.end.new_l2_to_l1_msgs) + - array_length(inputs.public_call.call_stack_item.public_inputs.new_l2_to_l1_msgs)); - - - const auto contract_address = inputs.public_call.call_stack_item.contract_address; - const auto portal_contract_address = inputs.public_call.portal_contract_address; - std::array, MAX_PUBLIC_DATA_UPDATE_REQUESTS_PER_CALL> const expected_new_writes = - public_data_update_requests_from_contract_storage_update_requests( - inputs.public_call.call_stack_item.public_inputs.contract_storage_update_requests, contract_address); - - // Unencrypted logs hash and preimage lengths should now be correctly accumulated - auto const& public_inputs_unencrypted_logs_hash = inputs.previous_kernel.public_inputs.end.unencrypted_logs_hash; - auto const& unencrypted_logs_hash = inputs.public_call.call_stack_item.public_inputs.unencrypted_logs_hash; - // TODO(914) Should this be unused? - [[maybe_unused]] auto const& expected_unencrypted_logs_hash = - accumulate_sha256({ public_inputs_unencrypted_logs_hash[0], - public_inputs_unencrypted_logs_hash[1], - unencrypted_logs_hash[0], - unencrypted_logs_hash[1] }); - - auto const& public_inputs_unencrypted_log_preimages_length = - inputs.previous_kernel.public_inputs.end.unencrypted_log_preimages_length; - auto const& unencrypted_log_preimages_length = - inputs.public_call.call_stack_item.public_inputs.unencrypted_log_preimages_length; - ASSERT_EQ(public_inputs.end.unencrypted_log_preimages_length, - unencrypted_log_preimages_length + public_inputs_unencrypted_log_preimages_length); - - ASSERT_TRUE(source_arrays_are_in_target(dummyBuilder, - inputs.previous_kernel.public_inputs.end.public_data_update_requests, - expected_new_writes, - public_inputs.end.public_data_update_requests)); - - std::array, MAX_PUBLIC_DATA_READS_PER_CALL> const expected_new_reads = - public_data_reads_from_contract_storage_reads( - inputs.public_call.call_stack_item.public_inputs.contract_storage_reads, contract_address); - - ASSERT_TRUE(source_arrays_are_in_target(dummyBuilder, - inputs.previous_kernel.public_inputs.end.public_data_reads, - expected_new_reads, - public_inputs.end.public_data_reads)); - - std::array const expected_new_commitments = - new_commitments_as_siloed_commitments(inputs.public_call.call_stack_item.public_inputs.new_commitments, - contract_address); - - ASSERT_TRUE(source_arrays_are_in_target(dummyBuilder, - inputs.previous_kernel.public_inputs.end.new_commitments, - expected_new_commitments, - public_inputs.end.new_commitments)); - - std::array const expected_new_nullifiers = new_nullifiers_as_siloed_nullifiers( - inputs.public_call.call_stack_item.public_inputs.new_nullifiers, contract_address); - - ASSERT_TRUE(source_arrays_are_in_target(dummyBuilder, - inputs.previous_kernel.public_inputs.end.new_nullifiers, - expected_new_nullifiers, - public_inputs.end.new_nullifiers)); - - // Reading the chain id and version from the tx context - fr const chain_id = inputs.previous_kernel.public_inputs.constants.tx_context.chain_id; - fr const version = inputs.previous_kernel.public_inputs.constants.tx_context.version; - - std::array const expected_new_messages = - new_l2_messages_from_message(inputs.public_call.call_stack_item.public_inputs.new_l2_to_l1_msgs, - contract_address, - portal_contract_address, - chain_id, - version); - - ASSERT_TRUE(source_arrays_are_in_target(dummyBuilder, - inputs.previous_kernel.public_inputs.end.new_l2_to_l1_msgs, - expected_new_messages, - public_inputs.end.new_l2_to_l1_msgs)); - - ASSERT_FALSE(dummyBuilder.failed()); -} - -TEST(public_kernel_tests, previous_public_kernel_fails_if_contract_storage_update_requests_on_static_call) -{ - DummyBuilder dummyBuilder = DummyBuilder( - "public_kernel_tests__previous_public_kernel_fails_if_contract_storage_update_requests_on_static_call"); - PublicKernelInputs inputs = get_kernel_inputs_with_previous_kernel(false); - - // the function call has contract storage update requests so setting it to static should fail - inputs.public_call.call_stack_item.public_inputs.call_context.is_static_call = true; - - auto public_inputs = native_public_kernel_circuit_public_previous_kernel(dummyBuilder, inputs); - ASSERT_TRUE(dummyBuilder.failed()); - ASSERT_EQ( - dummyBuilder.get_first_failure().code, - CircuitErrorCode::PUBLIC_KERNEL__CALL_CONTEXT_CONTRACT_STORAGE_UPDATE_REQUESTS_PROHIBITED_FOR_STATIC_CALL); -} - -TEST(public_kernel_tests, previous_public_kernel_fails_if_incorrect_storage_contract_on_delegate_call) -{ - DummyBuilder dummyBuilder = DummyBuilder( - "public_kernel_tests__previous_public_kernel_fails_if_incorrect_storage_contract_on_delegate_call"); - PublicKernelInputs inputs = get_kernel_inputs_with_previous_kernel(false); - - // the function call has the contract address and storage contract address equal and so it should fail for a - // delegate call - inputs.public_call.call_stack_item.public_inputs.call_context.is_delegate_call = true; - - auto public_inputs = native_public_kernel_circuit_public_previous_kernel(dummyBuilder, inputs); - ASSERT_TRUE(dummyBuilder.failed()); - ASSERT_EQ(dummyBuilder.get_first_failure().code, - CircuitErrorCode::PUBLIC_KERNEL__CALL_CONTEXT_INVALID_STORAGE_ADDRESS_FOR_DELEGATE_CALL); -} - -TEST(public_kernel_tests, public_kernel_fails_creating_new_commitments_on_static_call) -{ - DummyBuilder dummyBuilder = DummyBuilder("public_kernel_fails_creating_new_commitments_on_static_call"); - PublicKernelInputs inputs = get_kernel_inputs_with_previous_kernel(false); - - // the function call has the contract address and storage contract address equal and so it should fail for a - // delegate call - inputs.public_call.call_stack_item.public_inputs.call_context.is_static_call = true; - - // set previously set items to 0 - inputs.public_call.call_stack_item.public_inputs.contract_storage_update_requests = - empty_array_of_values, MAX_PUBLIC_DATA_UPDATE_REQUESTS_PER_CALL>(); - - // regenerate call data hash - inputs.previous_kernel.public_inputs.end.public_call_stack[0] = - get_call_stack_item_hash(inputs.public_call.call_stack_item); - - // Update call stack hash - auto public_inputs = native_public_kernel_circuit_public_previous_kernel(dummyBuilder, inputs); - ASSERT_TRUE(dummyBuilder.failed()); - ASSERT_EQ(dummyBuilder.get_first_failure().code, - CircuitErrorCode::PUBLIC_KERNEL__NEW_COMMITMENTS_PROHIBITED_IN_STATIC_CALL); -} - -TEST(public_kernel_tests, public_kernel_fails_creating_new_nullifiers_on_static_call) -{ - DummyBuilder dummyBuilder = DummyBuilder("public_kernel_fails_creating_new_nullifiers_on_static_call"); - PublicKernelInputs inputs = get_kernel_inputs_with_previous_kernel(false); - - // the function call has the contract address and storage contract address equal and so it should fail for a - // delegate call - inputs.public_call.call_stack_item.public_inputs.call_context.is_static_call = true; - - // set previously set items to 0 - inputs.public_call.call_stack_item.public_inputs.contract_storage_update_requests = - empty_array_of_values, MAX_PUBLIC_DATA_UPDATE_REQUESTS_PER_CALL>(); - inputs.public_call.call_stack_item.public_inputs.new_commitments = - empty_array_of_values(); - - // regenerate call data hash - inputs.previous_kernel.public_inputs.end.public_call_stack[0] = - get_call_stack_item_hash(inputs.public_call.call_stack_item); - - // Update call stack hash - auto public_inputs = native_public_kernel_circuit_public_previous_kernel(dummyBuilder, inputs); - ASSERT_TRUE(dummyBuilder.failed()); - ASSERT_EQ(dummyBuilder.get_first_failure().code, - CircuitErrorCode::PUBLIC_KERNEL__NEW_NULLIFIERS_PROHIBITED_IN_STATIC_CALL); -} - -TEST(public_kernel_tests, logs_are_handled_as_expected) -{ - DummyBuilder dummyBuilder = DummyBuilder("public_kernel_tests__logs_are_handled_as_expected"); - PublicKernelInputs const& inputs = get_kernel_inputs_with_previous_kernel(true); - std::array const& zero_hash{}; - - // Ensure encrypted logs hash values are non-zero - ASSERT_NE(inputs.previous_kernel.public_inputs.end.encrypted_logs_hash, zero_hash); - - auto const& public_inputs = native_public_kernel_circuit_private_previous_kernel(dummyBuilder, inputs); - - // Encrypted logs hash values are propagated form input to output without change - ASSERT_EQ(inputs.previous_kernel.public_inputs.end.encrypted_logs_hash, public_inputs.end.encrypted_logs_hash); - - // Unencrypted logs hash and preimage lengths should now be correctly accumulated - auto const& public_inputs_unencrypted_logs_hash = inputs.previous_kernel.public_inputs.end.unencrypted_logs_hash; - auto const& unencrypted_logs_hash = inputs.public_call.call_stack_item.public_inputs.unencrypted_logs_hash; - // TODO(914) Should this be unused? - [[maybe_unused]] auto const& expected_unencrypted_logs_hash = - accumulate_sha256({ public_inputs_unencrypted_logs_hash[0], - public_inputs_unencrypted_logs_hash[1], - unencrypted_logs_hash[0], - unencrypted_logs_hash[1] }); - - auto const& public_inputs_unencrypted_log_preimages_length = - inputs.previous_kernel.public_inputs.end.unencrypted_log_preimages_length; - auto const& unencrypted_log_preimages_length = - inputs.public_call.call_stack_item.public_inputs.unencrypted_log_preimages_length; - ASSERT_EQ(public_inputs.end.unencrypted_log_preimages_length, - unencrypted_log_preimages_length + public_inputs_unencrypted_log_preimages_length); - - ASSERT_FALSE(dummyBuilder.failed()); -} - -} // namespace aztec3::circuits::kernel::public_kernel \ No newline at end of file diff --git a/circuits/cpp/src/aztec3/circuits/kernel/public/c_bind.cpp b/circuits/cpp/src/aztec3/circuits/kernel/public/c_bind.cpp deleted file mode 100644 index 33fa83c4f3c..00000000000 --- a/circuits/cpp/src/aztec3/circuits/kernel/public/c_bind.cpp +++ /dev/null @@ -1,33 +0,0 @@ -#include "c_bind.h" - -#include "index.hpp" -#include "init.hpp" - -#include "aztec3/circuits/abis/kernel_circuit_public_inputs.hpp" -#include "aztec3/circuits/abis/public_kernel/public_kernel_inputs.hpp" -#include "aztec3/constants.hpp" -#include "aztec3/utils/dummy_circuit_builder.hpp" -#include "aztec3/utils/types/native_types.hpp" - -#include - -namespace { -using Builder = UltraCircuitBuilder; -using NT = aztec3::utils::types::NativeTypes; -using DummyBuilder = aztec3::utils::DummyCircuitBuilder; -using aztec3::circuits::abis::KernelCircuitPublicInputs; -using aztec3::circuits::abis::public_kernel::PublicKernelInputs; -using aztec3::circuits::kernel::public_kernel::native_public_kernel_circuit_private_previous_kernel; -using aztec3::circuits::kernel::public_kernel::native_public_kernel_circuit_public_previous_kernel; - -// WASM Cbinds -CBIND(public_kernel__sim, [](PublicKernelInputs const& public_kernel_inputs) { - DummyBuilder builder = DummyBuilder("public_kernel__sim"); - KernelCircuitPublicInputs const result = - public_kernel_inputs.previous_kernel.public_inputs.is_private - ? native_public_kernel_circuit_private_previous_kernel(builder, public_kernel_inputs) - : native_public_kernel_circuit_public_previous_kernel(builder, public_kernel_inputs); - return builder.result_or_error(result); -}); - -} // namespace diff --git a/circuits/cpp/src/aztec3/circuits/kernel/public/c_bind.h b/circuits/cpp/src/aztec3/circuits/kernel/public/c_bind.h deleted file mode 100644 index 85086a48cff..00000000000 --- a/circuits/cpp/src/aztec3/circuits/kernel/public/c_bind.h +++ /dev/null @@ -1,6 +0,0 @@ -#include - -#include -#include - -CBIND_DECL(public_kernel__sim); diff --git a/circuits/cpp/src/aztec3/circuits/kernel/public/common.cpp b/circuits/cpp/src/aztec3/circuits/kernel/public/common.cpp deleted file mode 100644 index 16023f9fe42..00000000000 --- a/circuits/cpp/src/aztec3/circuits/kernel/public/common.cpp +++ /dev/null @@ -1,75 +0,0 @@ -#include "common.hpp" - -#include "init.hpp" - -#include "aztec3/circuits/abis/call_stack_item.hpp" -#include "aztec3/circuits/abis/types.hpp" - -namespace aztec3::circuits::kernel::public_kernel { - -using aztec3::utils::array_pop; - -void common_initialize_end_values(PublicKernelInputs const& public_kernel_inputs, - KernelCircuitPublicInputs& circuit_outputs) -{ - // Initializes the circuit outputs with the end state of the previous iteration - circuit_outputs.constants = public_kernel_inputs.previous_kernel.public_inputs.constants; - - // Ensure the arrays are the same as previously, before we start pushing more data onto them in other functions - // within this circuit: - auto& end = circuit_outputs.end; - const auto& start = public_kernel_inputs.previous_kernel.public_inputs.end; - - end.new_commitments = start.new_commitments; - end.new_nullifiers = start.new_nullifiers; - - end.private_call_stack = start.private_call_stack; - end.public_call_stack = start.public_call_stack; - end.new_l2_to_l1_msgs = start.new_l2_to_l1_msgs; - - end.optionally_revealed_data = start.optionally_revealed_data; - - end.public_data_update_requests = start.public_data_update_requests; - end.public_data_reads = start.public_data_reads; - - // Public kernel does not modify encrypted logs values --> we just copy them to output - end.encrypted_logs_hash = start.encrypted_logs_hash; - end.encrypted_log_preimages_length = start.encrypted_log_preimages_length; - - end.new_contracts = start.new_contracts; -} - -/** - * @brief Validates that the call stack item for this circuit iteration is at the top of the call stack - * @param builder The circuit builder - * @param public_kernel_inputs The inputs to this iteration of the kernel circuit - */ -void validate_this_public_call_hash(DummyBuilder& builder, - PublicKernelInputs const& public_kernel_inputs, - KernelCircuitPublicInputs& public_inputs) -{ - // If public call stack is empty, we bail so array_pop doesn't throw_or_abort - if (array_length(public_inputs.end.public_call_stack) == 0) { - builder.do_assert( - false, "Public call stack can't be empty", CircuitErrorCode::PUBLIC_KERNEL__EMPTY_PUBLIC_CALL_STACK); - return; - } - - // Pops the current function execution from the stack and validates it against the call stack item - - // TODO: this logic might need to change to accommodate the weird edge 3 initial txs (the 'main' tx, the 'fee' tx, - // and the 'gas rebate' tx). - const auto popped_public_call_hash = array_pop(public_inputs.end.public_call_stack); - const auto calculated_this_public_call_hash = - get_call_stack_item_hash(public_kernel_inputs.public_call.call_stack_item); - - builder.do_assert( - popped_public_call_hash == calculated_this_public_call_hash, - format("calculated public_call_hash (", - calculated_this_public_call_hash, - ") does not match provided public_call_hash (", - popped_public_call_hash, - ") at the top of the call stack"), - CircuitErrorCode::PUBLIC_KERNEL__CALCULATED_PUBLIC_CALL_HASH_AND_PROVIDED_PUBLIC_CALL_HASH_MISMATCH); -}; -} // namespace aztec3::circuits::kernel::public_kernel diff --git a/circuits/cpp/src/aztec3/circuits/kernel/public/common.hpp b/circuits/cpp/src/aztec3/circuits/kernel/public/common.hpp deleted file mode 100644 index 052c3152416..00000000000 --- a/circuits/cpp/src/aztec3/circuits/kernel/public/common.hpp +++ /dev/null @@ -1,459 +0,0 @@ -#pragma once - -#include "init.hpp" - -#include "aztec3/circuits/abis/kernel_circuit_public_inputs.hpp" -#include "aztec3/circuits/abis/public_data_update_request.hpp" -#include "aztec3/circuits/abis/public_kernel/public_kernel_inputs.hpp" -#include "aztec3/circuits/hash.hpp" -#include "aztec3/utils/array.hpp" -#include "aztec3/utils/dummy_circuit_builder.hpp" - -namespace aztec3::circuits::kernel::public_kernel { - -using NT = aztec3::utils::types::NativeTypes; -using aztec3::circuits::abis::KernelCircuitPublicInputs; -using aztec3::circuits::abis::PublicDataRead; -using aztec3::circuits::abis::PublicDataUpdateRequest; -using aztec3::circuits::abis::public_kernel::PublicKernelInputs; -using DummyBuilder = aztec3::utils::DummyCircuitBuilder; -using aztec3::utils::array_length; -using aztec3::utils::array_push; -using aztec3::utils::push_array_to_array; - -/** - * @brief Validate that all pre-images on the call stack hash to equal the accumulated data - * @tparam The type of kernel input - * @param builder The circuit builder - * @param public_kernel_inputs The inputs to this iteration of the kernel circuit - */ -template -void common_validate_call_stack(DummyBuilder& builder, KernelInput const& public_kernel_inputs) -{ - // Ensures that the stack of pre-images corresponds to the call stack - auto& stack = public_kernel_inputs.public_call.call_stack_item.public_inputs.public_call_stack; - auto& preimages = public_kernel_inputs.public_call.public_call_stack_preimages; - - // grab our contract address, our storage contract address and our portal contract address to verify - // child executions in the case of delegate call types - auto our_contract_address = public_kernel_inputs.public_call.call_stack_item.contract_address; - auto our_storage_address = - public_kernel_inputs.public_call.call_stack_item.public_inputs.call_context.storage_contract_address; - auto our_msg_sender = public_kernel_inputs.public_call.call_stack_item.public_inputs.call_context.msg_sender; - auto our_portal_contract_address = - public_kernel_inputs.public_call.call_stack_item.public_inputs.call_context.portal_contract_address; - - for (size_t i = 0; i < stack.size(); ++i) { - const auto& hash = stack[i]; - const auto& preimage = preimages[i]; - - // Note: this assumes it's computationally infeasible to have `0` as a valid call_stack_item_hash. - // Assumes `hash == 0` means "this stack item is empty". - if (hash == 0) { - continue; - } - - const auto is_delegate_call = preimage.public_inputs.call_context.is_delegate_call; - const auto is_static_call = preimage.public_inputs.call_context.is_static_call; - const auto contract_being_called = preimage.contract_address; - - const auto calculated_hash = preimage.hash(); - builder.do_assert( - hash == calculated_hash, - format( - "public_call_stack[", i, "] = ", hash, "; does not reconcile with calculatedHash = ", calculated_hash), - CircuitErrorCode::PUBLIC_KERNEL__PUBLIC_CALL_STACK_MISMATCH); - - // here we validate the msg sender for each call on the stack - // we need to consider regular vs delegate calls - const auto preimage_msg_sender = preimage.public_inputs.call_context.msg_sender; - const auto expected_msg_sender = is_delegate_call ? our_msg_sender : our_contract_address; - builder.do_assert(expected_msg_sender == preimage_msg_sender, - format("call_stack_msg_sender[", - i, - "] = ", - preimage_msg_sender, - " expected ", - expected_msg_sender, - "; does not reconcile"), - CircuitErrorCode::PUBLIC_KERNEL__PUBLIC_CALL_STACK_INVALID_MSG_SENDER); - - // here we validate the storage address for each call on the stack - // we need to consider regular vs delegate calls - const auto preimage_storage_address = preimage.public_inputs.call_context.storage_contract_address; - const auto expected_storage_address = is_delegate_call ? our_storage_address : contract_being_called; - builder.do_assert(expected_storage_address == preimage_storage_address, - format("call_stack_storage_address[", - i, - "] = ", - preimage_storage_address, - " expected ", - expected_storage_address, - "; does not reconcile"), - CircuitErrorCode::PUBLIC_KERNEL__PUBLIC_CALL_STACK_INVALID_STORAGE_ADDRESS); - - // if it is a delegate call then we check that the portal contract in the pre image is our portal contract - const auto preimage_portal_address = preimage.public_inputs.call_context.portal_contract_address; - const auto expected_portal_address = our_portal_contract_address; - builder.do_assert(!is_delegate_call || expected_portal_address == preimage_portal_address, - format("call_stack_portal_address[", - i, - "] = ", - preimage_portal_address, - " expected ", - expected_portal_address, - "; does not reconcile for a delegate call"), - CircuitErrorCode::PUBLIC_KERNEL__PUBLIC_CALL_STACK_INVALID_PORTAL_ADDRESS); - - const auto num_contract_storage_update_requests = - array_length(preimage.public_inputs.contract_storage_update_requests); - builder.do_assert( - !is_static_call || num_contract_storage_update_requests == 0, - format("contract_storage_update_requests[", i, "] should be empty for a static call"), - CircuitErrorCode::PUBLIC_KERNEL__PUBLIC_CALL_STACK_CONTRACT_STORAGE_UPDATES_PROHIBITED_FOR_STATIC_CALL); - } -}; - -/** - * @brief Validates the call context of the current iteration - * @tparam The type of kernel input - * @param builder The circuit builder - * @param public_kernel_inputs The inputs to this iteration of the kernel circuit - */ -template -void common_validate_call_context(DummyBuilder& builder, KernelInput const& public_kernel_inputs) -{ - const auto& call_stack_item = public_kernel_inputs.public_call.call_stack_item; - const auto is_delegate_call = call_stack_item.public_inputs.call_context.is_delegate_call; - const auto is_static_call = call_stack_item.public_inputs.call_context.is_static_call; - const auto contract_address = call_stack_item.contract_address; - const auto storage_contract_address = call_stack_item.public_inputs.call_context.storage_contract_address; - const auto contract_storage_update_requests_length = - array_length(call_stack_item.public_inputs.contract_storage_update_requests); - - builder.do_assert(!is_delegate_call || contract_address != storage_contract_address, - std::string("curent contract address must not match storage contract address for delegate calls"), - CircuitErrorCode::PUBLIC_KERNEL__CALL_CONTEXT_INVALID_STORAGE_ADDRESS_FOR_DELEGATE_CALL); - - builder.do_assert( - !is_static_call || contract_storage_update_requests_length == 0, - std::string("No contract storage update requests are allowed for static calls"), - CircuitErrorCode::PUBLIC_KERNEL__CALL_CONTEXT_CONTRACT_STORAGE_UPDATE_REQUESTS_PROHIBITED_FOR_STATIC_CALL); -}; - -/** - * @brief Validates the kernel execution of the current iteration - * @tparam The type of kernel input - * @param builder The circuit builder - * @param public_kernel_inputs The inputs to this iteration of the kernel circuit - */ -template -void common_validate_kernel_execution(DummyBuilder& builder, KernelInput const& public_kernel_inputs) -{ - common_validate_call_context(builder, public_kernel_inputs); - common_validate_call_stack(builder, public_kernel_inputs); -}; - -/** - * @brief Validates inputs to the kernel circuit that are common to all invocation scenarios - * @tparam The type of kernel input - * @param builder The circuit builder - * @param public_kernel_inputs The inputs to this iteration of the kernel circuit - */ -template -void common_validate_inputs(DummyBuilder& builder, KernelInput const& public_kernel_inputs) -{ - // Validates commons inputs for all type of kernel inputs - const auto& this_call_stack_item = public_kernel_inputs.public_call.call_stack_item; - builder.do_assert(this_call_stack_item.public_inputs.call_context.is_contract_deployment == false, - "Contract deployment can't be a public function", - CircuitErrorCode::PUBLIC_KERNEL__CONTRACT_DEPLOYMENT_NOT_ALLOWED); - builder.do_assert(this_call_stack_item.contract_address != 0, - "Contract address must be non-zero", - CircuitErrorCode::PUBLIC_KERNEL__CONTRACT_ADDRESS_INVALID); - builder.do_assert(this_call_stack_item.function_data.selector.value != 0, - "Function signature must be non-zero", - CircuitErrorCode::PUBLIC_KERNEL__FUNCTION_SIGNATURE_INVALID); - builder.do_assert(this_call_stack_item.function_data.is_constructor == false, - "Constructors can't be public functions", - CircuitErrorCode::PUBLIC_KERNEL__CONSTRUCTOR_NOT_ALLOWED); - builder.do_assert(this_call_stack_item.function_data.is_private == false, - "Cannot execute a private function with the public kernel circuit", - CircuitErrorCode::PUBLIC_KERNEL__PRIVATE_FUNCTION_NOT_ALLOWED); - builder.do_assert(public_kernel_inputs.public_call.bytecode_hash != 0, - "Bytecode hash must be non-zero", - CircuitErrorCode::PUBLIC_KERNEL__BYTECODE_HASH_INVALID); - - if (this_call_stack_item.function_data.is_internal) { - auto const target = this_call_stack_item.contract_address; - auto const sender = this_call_stack_item.public_inputs.call_context.msg_sender; - - builder.do_assert(target == sender, - "call is internal, but msg_sender is not self", - CircuitErrorCode::PUBLIC_KERNEL__IS_INTERNAL_BUT_NOT_SELF_CALL); - } -} - -template -void perform_static_call_checks(Builder& builder, KernelInput const& public_kernel_inputs) -{ - // If the call is a static call, there should be no new commitments or nullifiers. - const auto& public_call_public_inputs = public_kernel_inputs.public_call.call_stack_item.public_inputs; - - const auto& is_static_call = public_call_public_inputs.call_context.is_static_call; - const auto& new_commitments = public_call_public_inputs.new_commitments; - const auto& new_nullifiers = public_call_public_inputs.new_nullifiers; - - if (is_static_call) { - builder.do_assert(utils::is_array_empty(new_commitments) == true, - "no new commitments must be created for static calls", - CircuitErrorCode::PUBLIC_KERNEL__NEW_COMMITMENTS_PROHIBITED_IN_STATIC_CALL); - builder.do_assert(utils::is_array_empty(new_nullifiers) == true, - "no new nullifiers must be created for static calls", - CircuitErrorCode::PUBLIC_KERNEL__NEW_NULLIFIERS_PROHIBITED_IN_STATIC_CALL); - } -} - -/** - * @brief Propagates valid (i.e. non-empty) update requests from this iteration to the circuit output - * @tparam The type of kernel input - * @param public_kernel_inputs The inputs to this iteration of the kernel circuit - * @param circuit_outputs The circuit outputs to be populated - */ -template -void propagate_valid_public_data_update_requests(Builder& builder, - KernelInput const& public_kernel_inputs, - KernelCircuitPublicInputs& circuit_outputs) -{ - const auto& contract_address = public_kernel_inputs.public_call.call_stack_item.contract_address; - const auto& update_requests = - public_kernel_inputs.public_call.call_stack_item.public_inputs.contract_storage_update_requests; - for (size_t i = 0; i < MAX_PUBLIC_DATA_UPDATE_REQUESTS_PER_TX; ++i) { - const auto& update_request = update_requests[i]; - if (update_request.is_empty()) { - continue; - } - const auto new_write = PublicDataUpdateRequest{ - .leaf_index = compute_public_data_tree_index(contract_address, update_request.storage_slot), - .old_value = compute_public_data_tree_value(update_request.old_value), - .new_value = compute_public_data_tree_value(update_request.new_value), - }; - array_push( - builder, - circuit_outputs.end.public_data_update_requests, - new_write, - format(PUBLIC_KERNEL_CIRCUIT_ERROR_MESSAGE_BEGINNING, "too many public data update requests in one tx")); - } -} - -/** - * @brief Propagates valid (i.e. non-empty) public data reads from this iteration to the circuit output - * @tparam The type of kernel input - * @param public_kernel_inputs The inputs to this iteration of the kernel circuit - * @param circuit_outputs The circuit outputs to be populated - */ -template -void propagate_valid_public_data_reads(Builder& builder, - KernelInput const& public_kernel_inputs, - KernelCircuitPublicInputs& circuit_outputs) -{ - const auto& contract_address = public_kernel_inputs.public_call.call_stack_item.contract_address; - const auto& reads = public_kernel_inputs.public_call.call_stack_item.public_inputs.contract_storage_reads; - for (size_t i = 0; i < MAX_PUBLIC_DATA_READS_PER_TX; ++i) { - const auto& contract_storage_read = reads[i]; - if (contract_storage_read.is_empty()) { - continue; - } - const auto new_read = PublicDataRead{ - .leaf_index = compute_public_data_tree_index(contract_address, contract_storage_read.storage_slot), - .value = compute_public_data_tree_value(contract_storage_read.current_value), - }; - array_push(builder, - circuit_outputs.end.public_data_reads, - new_read, - format(PUBLIC_KERNEL_CIRCUIT_ERROR_MESSAGE_BEGINNING, "too many public data reads in one tx")); - } -} - -/** - * @brief Propagates new commitments from this iteration to the circuit output. - * - * @tparam The type of the kernel input - * @tparam The builder type - * @param public_kernel_inputs The inputs to this iteration to the kernel circuit. - * @param circuit_outputs The circuit outputs to be populated - */ -template -void propagate_new_commitments(Builder& builder, - KernelInput const& public_kernel_inputs, - KernelCircuitPublicInputs& circuit_outputs) -{ - // Get the new commitments - const auto& public_call_public_inputs = public_kernel_inputs.public_call.call_stack_item.public_inputs; - - const auto& new_commitments = public_call_public_inputs.new_commitments; - const auto& storage_contract_address = public_call_public_inputs.call_context.storage_contract_address; - - std::array siloed_new_commitments{}; - for (size_t i = 0; i < new_commitments.size(); ++i) { - if (!new_commitments[i].is_zero()) { - siloed_new_commitments[i] = silo_commitment(storage_contract_address, new_commitments[i]); - } - } - - push_array_to_array(builder, - siloed_new_commitments, - circuit_outputs.end.new_commitments, - format(PUBLIC_KERNEL_CIRCUIT_ERROR_MESSAGE_BEGINNING, "too many new commitments in one tx")); -} - -/** - * @brief Propagates new nullifiers from this iteration to the circuit output. - * - * @tparam The type of the kernel input - * @tparam The builder type - * @param public_kernel_inputs The inputs to this iteration to the kernel circuit. - * @param circuit_outputs The circuit outputs to be populated - */ -template -void propagate_new_nullifiers(Builder& builder, - KernelInput const& public_kernel_inputs, - KernelCircuitPublicInputs& circuit_outputs) -{ - // Get the new commitments - const auto& public_call_public_inputs = public_kernel_inputs.public_call.call_stack_item.public_inputs; - - const auto& new_nullifiers = public_call_public_inputs.new_nullifiers; - const auto& storage_contract_address = public_call_public_inputs.call_context.storage_contract_address; - - std::array siloed_new_nullifiers{}; - for (size_t i = 0; i < new_nullifiers.size(); ++i) { - if (!new_nullifiers[i].is_zero()) { - siloed_new_nullifiers[i] = silo_nullifier(storage_contract_address, new_nullifiers[i]); - } - } - - push_array_to_array(builder, - siloed_new_nullifiers, - circuit_outputs.end.new_nullifiers, - format(PUBLIC_KERNEL_CIRCUIT_ERROR_MESSAGE_BEGINNING, "too many new nullifiers in one tx")); -} - -/** - * @brief Propagates new l2 to l1 messages from this iteration to the circuit output. - * - * @tparam The type of the kernel input - * @param public_kernel_inputs The inputs to this iteration to the kernel circuit. - * @param circuit_outputs The circuit outputs to be populated - */ -template -void propagate_new_l2_to_l1_messages(Builder& builder, - KernelInput const& public_kernel_inputs, - KernelCircuitPublicInputs& circuit_outputs) -{ - // Get the new l2 messages - const auto& public_call_public_inputs = public_kernel_inputs.public_call.call_stack_item.public_inputs; - - const auto& portal_contract_address = public_kernel_inputs.public_call.portal_contract_address; - const auto& storage_contract_address = public_call_public_inputs.call_context.storage_contract_address; - const auto& new_l2_to_l1_msgs = public_call_public_inputs.new_l2_to_l1_msgs; - - std::array new_l2_to_l1_msgs_to_insert{}; - for (size_t i = 0; i < new_l2_to_l1_msgs.size(); ++i) { - if (!new_l2_to_l1_msgs[i].is_zero()) { - const auto chain_id = public_kernel_inputs.previous_kernel.public_inputs.constants.tx_context.chain_id; - const auto version = public_kernel_inputs.previous_kernel.public_inputs.constants.tx_context.version; - - new_l2_to_l1_msgs_to_insert[i] = compute_l2_to_l1_hash( - storage_contract_address, version, portal_contract_address, chain_id, new_l2_to_l1_msgs[i]); - } - } - push_array_to_array( - builder, - new_l2_to_l1_msgs_to_insert, - circuit_outputs.end.new_l2_to_l1_msgs, - format(PUBLIC_KERNEL_CIRCUIT_ERROR_MESSAGE_BEGINNING, "too many new l2 to l1 messages in one tx")); -} - -/** - * @brief Accumulates unencrypted logs hashes and lengths. - * @tparam The type of kernel input - * @param public_kernel_inputs The inputs to this iteration of the kernel circuit - * @param circuit_outputs The circuit outputs to be populated - * @note See the following thread if not clear: - * https://discourse.aztec.network/t/proposal-forcing-the-sequencer-to-actually-submit-data-to-l1/426 - * @note Used by public kernels which had previous iterations. - */ -template void accumulate_unencrypted_logs(PublicKernelInputs const& public_kernel_inputs, - KernelCircuitPublicInputs& circuit_outputs) -{ - const auto public_call_public_inputs = public_kernel_inputs.public_call.call_stack_item.public_inputs; - - const auto& previous_kernel_end = public_kernel_inputs.previous_kernel.public_inputs.end; - const auto& previous_unencrypted_logs_hash = previous_kernel_end.unencrypted_logs_hash; - - const auto& current_unencrypted_logs_hash = public_call_public_inputs.unencrypted_logs_hash; - circuit_outputs.end.unencrypted_logs_hash = accumulate_sha256({ previous_unencrypted_logs_hash[0], - previous_unencrypted_logs_hash[1], - current_unencrypted_logs_hash[0], - current_unencrypted_logs_hash[1] }); - - // Add log preimages lengths from current iteration to accumulated lengths - const auto& current_unencrypted_log_preimages_length = public_call_public_inputs.unencrypted_log_preimages_length; - circuit_outputs.end.unencrypted_log_preimages_length = - previous_kernel_end.unencrypted_log_preimages_length + current_unencrypted_log_preimages_length; -} - -/** - * @brief Propagates valid (i.e. non-empty) public data reads from this iteration to the circuit output - * @tparam The type of kernel input - * @tparam The current builder - * @param public_kernel_inputs The inputs to this iteration of the kernel circuit - * @param circuit_outputs The circuit outputs to be populated - */ -template -void common_update_public_end_values(Builder& builder, - KernelInput const& public_kernel_inputs, - KernelCircuitPublicInputs& circuit_outputs) -{ - // Updates the circuit outputs with new state changes, call stack etc - circuit_outputs.is_private = false; - - // If this call is a static call, certain operations are disallowed, such as creating new state. - perform_static_call_checks(builder, public_kernel_inputs); - - const auto& stack = public_kernel_inputs.public_call.call_stack_item.public_inputs.public_call_stack; - push_array_to_array( - builder, - stack, - circuit_outputs.end.public_call_stack, - format(PUBLIC_KERNEL_CIRCUIT_ERROR_MESSAGE_BEGINNING, "too many public call stack items in one tx")); - - propagate_new_commitments(builder, public_kernel_inputs, circuit_outputs); - propagate_new_nullifiers(builder, public_kernel_inputs, circuit_outputs); - - propagate_new_l2_to_l1_messages(builder, public_kernel_inputs, circuit_outputs); - - propagate_valid_public_data_update_requests(builder, public_kernel_inputs, circuit_outputs); - - propagate_valid_public_data_reads(builder, public_kernel_inputs, circuit_outputs); -} - -/** - * @brief Initializes the circuit output end state from provided inputs - * @param public_kernel_inputs The inputs to this iteration of the kernel circuit - * @param circuit_outputs The circuit outputs to be initialized - */ -void common_initialize_end_values(PublicKernelInputs const& public_kernel_inputs, - KernelCircuitPublicInputs& circuit_outputs); - -/** - * @brief Validates that the call stack item for this circuit iteration is at the top of the call stack - * @param builder The circuit builder - * @param public_kernel_inputs The inputs to this iteration of the kernel circuit - * @param public_inputs The circuit outputs - */ -void validate_this_public_call_hash(DummyBuilder& builder, - PublicKernelInputs const& public_kernel_inputs, - KernelCircuitPublicInputs& public_inputs); -} // namespace aztec3::circuits::kernel::public_kernel \ No newline at end of file diff --git a/circuits/cpp/src/aztec3/circuits/kernel/public/index.hpp b/circuits/cpp/src/aztec3/circuits/kernel/public/index.hpp deleted file mode 100644 index db8f855b55c..00000000000 --- a/circuits/cpp/src/aztec3/circuits/kernel/public/index.hpp +++ /dev/null @@ -1,3 +0,0 @@ -#include "init.hpp" -#include "native_public_kernel_circuit_private_previous_kernel.hpp" -#include "native_public_kernel_circuit_public_previous_kernel.hpp" \ No newline at end of file diff --git a/circuits/cpp/src/aztec3/circuits/kernel/public/init.hpp b/circuits/cpp/src/aztec3/circuits/kernel/public/init.hpp deleted file mode 100644 index d146108face..00000000000 --- a/circuits/cpp/src/aztec3/circuits/kernel/public/init.hpp +++ /dev/null @@ -1,30 +0,0 @@ -#pragma once -#include "aztec3/circuits/apps/oracle_wrapper.hpp" -#include "aztec3/circuits/recursion/aggregator.hpp" -#include "aztec3/oracle/oracle.hpp" -#include "aztec3/utils/dummy_circuit_builder.hpp" -#include "aztec3/utils/types/circuit_types.hpp" -#include "aztec3/utils/types/convert.hpp" -#include "aztec3/utils/types/native_types.hpp" - -namespace aztec3::circuits::kernel::public_kernel { - -using Builder = proof_system::UltraCircuitBuilder; - -using Aggregator = aztec3::circuits::recursion::Aggregator; - -// Generic: -using CT = aztec3::utils::types::CircuitTypes; -using NT = aztec3::utils::types::NativeTypes; -using aztec3::utils::types::to_ct; -using CircuitErrorCode = aztec3::utils::CircuitErrorCode; - -using DB = oracle::FakeDB; -using oracle::NativeOracle; -using OracleWrapper = aztec3::circuits::apps::OracleWrapperInterface; - -// Used when calling library functions like `push_array` which have their own generic error code. -// So we pad this in front of the error message to identify where the error originally came from. -const std::string PUBLIC_KERNEL_CIRCUIT_ERROR_MESSAGE_BEGINNING = "public_kernel_circuit: "; - -} // namespace aztec3::circuits::kernel::public_kernel \ No newline at end of file diff --git a/circuits/cpp/src/aztec3/circuits/kernel/public/native_public_kernel_circuit_private_previous_kernel.cpp b/circuits/cpp/src/aztec3/circuits/kernel/public/native_public_kernel_circuit_private_previous_kernel.cpp deleted file mode 100644 index bbb9eda5871..00000000000 --- a/circuits/cpp/src/aztec3/circuits/kernel/public/native_public_kernel_circuit_private_previous_kernel.cpp +++ /dev/null @@ -1,79 +0,0 @@ -#include "common.hpp" -#include "init.hpp" -#include "native_public_kernel_circuit_public_previous_kernel.hpp" - -#include "aztec3/circuits/abis/kernel_circuit_public_inputs.hpp" -#include "aztec3/circuits/abis/public_kernel/public_kernel_inputs.hpp" -#include "aztec3/utils/array.hpp" -#include "aztec3/utils/circuit_errors.hpp" -#include "aztec3/utils/dummy_circuit_builder.hpp" - -// Purpose of this anonymous namespace is to avoid to clash with the validate_inputs() -// counterpart defined in native_public_kernel_circuit_public_previous_kernel.cpp -namespace { -using CircuitErrorCode = aztec3::utils::CircuitErrorCode; -using aztec3::circuits::kernel::public_kernel::NT; -using DummyBuilder = aztec3::utils::DummyCircuitBuilder; -using aztec3::circuits::abis::public_kernel::PublicKernelInputs; -using aztec3::utils::array_length; - -/** - * @brief Validates the kernel circuit inputs specific to having a private previous kernel - * @param builder The circuit builder - * @param public_kernel_inputs The inputs to this iteration of the kernel circuit - */ -void validate_inputs(DummyBuilder& builder, PublicKernelInputs const& public_kernel_inputs) -{ - builder.do_assert(array_length(public_kernel_inputs.previous_kernel.public_inputs.end.private_call_stack) == 0, - "Private call stack must be empty when executing in the public kernel (i.e. all private calls " - "must have been completed)", - CircuitErrorCode::PUBLIC_KERNEL__NON_EMPTY_PRIVATE_CALL_STACK); - builder.do_assert(public_kernel_inputs.previous_kernel.public_inputs.is_private == true, - "Previous kernel must be private when in this public kernel version", - CircuitErrorCode::PUBLIC_KERNEL__PREVIOUS_KERNEL_NOT_PRIVATE); -} -} // namespace - -namespace aztec3::circuits::kernel::public_kernel { - -using aztec3::circuits::abis::KernelCircuitPublicInputs; -using aztec3::circuits::kernel::public_kernel::common_initialize_end_values; -using aztec3::circuits::kernel::public_kernel::common_validate_kernel_execution; - - -/** - * @brief Entry point for the native public kernel circuit with a private previous kernel - * @param builder The circuit builder - * @param public_kernel_inputs The inputs to this iteration of the kernel circuit - * @return The circuit public inputs - */ -KernelCircuitPublicInputs native_public_kernel_circuit_private_previous_kernel( - DummyBuilder& builder, PublicKernelInputs const& public_kernel_inputs) -{ - // construct the circuit outputs - KernelCircuitPublicInputs public_inputs{}; - - // initialize the end state with our provided previous kernel state - common_initialize_end_values(public_kernel_inputs, public_inputs); - - // validate the inputs common to all invocation circumstances - common_validate_inputs(builder, public_kernel_inputs); - - // validate the inputs unique to having a previous private kernel - validate_inputs(builder, public_kernel_inputs); - - // validate the kernel execution common to all invocation circumstances - common_validate_kernel_execution(builder, public_kernel_inputs); - - // validate our public call hash - validate_this_public_call_hash(builder, public_kernel_inputs, public_inputs); - - // update the public end state of the circuit - common_update_public_end_values(builder, public_kernel_inputs, public_inputs); - - accumulate_unencrypted_logs(public_kernel_inputs, public_inputs); - - return public_inputs; -}; - -} // namespace aztec3::circuits::kernel::public_kernel \ No newline at end of file diff --git a/circuits/cpp/src/aztec3/circuits/kernel/public/native_public_kernel_circuit_private_previous_kernel.hpp b/circuits/cpp/src/aztec3/circuits/kernel/public/native_public_kernel_circuit_private_previous_kernel.hpp deleted file mode 100644 index 46eab401e78..00000000000 --- a/circuits/cpp/src/aztec3/circuits/kernel/public/native_public_kernel_circuit_private_previous_kernel.hpp +++ /dev/null @@ -1,18 +0,0 @@ -#pragma once - -#include "common.hpp" -#include "init.hpp" - -#include "aztec3/circuits/abis/kernel_circuit_public_inputs.hpp" -#include "aztec3/circuits/abis/public_kernel/public_kernel_inputs.hpp" -#include "aztec3/utils/dummy_circuit_builder.hpp" - -namespace aztec3::circuits::kernel::public_kernel { - -using aztec3::circuits::abis::KernelCircuitPublicInputs; -using aztec3::circuits::abis::public_kernel::PublicKernelInputs; -using DummyBuilder = aztec3::utils::DummyCircuitBuilder; - -KernelCircuitPublicInputs native_public_kernel_circuit_private_previous_kernel( - DummyBuilder& builder, PublicKernelInputs const& public_kernel_inputs); -} // namespace aztec3::circuits::kernel::public_kernel \ No newline at end of file diff --git a/circuits/cpp/src/aztec3/circuits/kernel/public/native_public_kernel_circuit_public_previous_kernel.cpp b/circuits/cpp/src/aztec3/circuits/kernel/public/native_public_kernel_circuit_public_previous_kernel.cpp deleted file mode 100644 index 27aa8dc6580..00000000000 --- a/circuits/cpp/src/aztec3/circuits/kernel/public/native_public_kernel_circuit_public_previous_kernel.cpp +++ /dev/null @@ -1,73 +0,0 @@ -#include "native_public_kernel_circuit_public_previous_kernel.hpp" - -#include "common.hpp" -#include "init.hpp" - -#include "aztec3/circuits/abis/kernel_circuit_public_inputs.hpp" -#include "aztec3/circuits/abis/public_kernel/public_kernel_inputs.hpp" -#include "aztec3/utils/dummy_circuit_builder.hpp" - -// Purpose of this anonymous namespace is to avoid to clash with the validate_inputs() -// counterpart defined in native_public_kernel_circuit_private_previous_kernel.cpp -namespace { -using CircuitErrorCode = aztec3::utils::CircuitErrorCode; -using aztec3::circuits::kernel::public_kernel::NT; -using DummyBuilder = aztec3::utils::DummyCircuitBuilder; -using aztec3::circuits::abis::public_kernel::PublicKernelInputs; - -/** - * @brief Validates the kernel circuit inputs specific to having a public previous kernel - * @param builder The circuit builder - * @param public_kernel_inputs The inputs to this iteration of the kernel circuit - */ -void validate_inputs(DummyBuilder& builder, PublicKernelInputs const& public_kernel_inputs) -{ - const auto& previous_kernel = public_kernel_inputs.previous_kernel.public_inputs; - builder.do_assert(previous_kernel.is_private == false, - "Previous kernel must be public when in this public kernel version", - CircuitErrorCode::PUBLIC_KERNEL__PREVIOUS_KERNEL_NOT_PUBLIC); -} -} // namespace - -namespace aztec3::circuits::kernel::public_kernel { - -using aztec3::circuits::abis::KernelCircuitPublicInputs; -using aztec3::circuits::kernel::public_kernel::common_validate_kernel_execution; - - -/** - * @brief Entry point for the native public kernel circuit with a public previous kernel - * @param builder The circuit builder - * @param public_kernel_inputs The inputs to this iteration of the kernel circuit - * @return The circuit public inputs - */ -KernelCircuitPublicInputs native_public_kernel_circuit_public_previous_kernel( - DummyBuilder& builder, PublicKernelInputs const& public_kernel_inputs) -{ - // construct the circuit outputs - KernelCircuitPublicInputs public_inputs{}; - - // initialize the end state with our provided previous kernel state - common_initialize_end_values(public_kernel_inputs, public_inputs); - - // validate the inputs common to all invocation circumstances - common_validate_inputs(builder, public_kernel_inputs); - - // validate the inputs unique to having a previous public kernel - validate_inputs(builder, public_kernel_inputs); - - // validate the kernel execution common to all invocation circumstances - common_validate_kernel_execution(builder, public_kernel_inputs); - - // validate our public call hash - validate_this_public_call_hash(builder, public_kernel_inputs, public_inputs); - - // update the public end state of the circuit - common_update_public_end_values(builder, public_kernel_inputs, public_inputs); - - accumulate_unencrypted_logs(public_kernel_inputs, public_inputs); - - return public_inputs; -}; - -} // namespace aztec3::circuits::kernel::public_kernel \ No newline at end of file diff --git a/circuits/cpp/src/aztec3/circuits/kernel/public/native_public_kernel_circuit_public_previous_kernel.hpp b/circuits/cpp/src/aztec3/circuits/kernel/public/native_public_kernel_circuit_public_previous_kernel.hpp deleted file mode 100644 index 8d21acd913c..00000000000 --- a/circuits/cpp/src/aztec3/circuits/kernel/public/native_public_kernel_circuit_public_previous_kernel.hpp +++ /dev/null @@ -1,18 +0,0 @@ -#pragma once - -#include "common.hpp" -#include "init.hpp" - -#include "aztec3/circuits/abis/kernel_circuit_public_inputs.hpp" -#include "aztec3/circuits/abis/public_kernel/public_kernel_inputs.hpp" -#include "aztec3/utils/dummy_circuit_builder.hpp" - -namespace aztec3::circuits::kernel::public_kernel { - -using aztec3::circuits::abis::KernelCircuitPublicInputs; -using aztec3::circuits::abis::public_kernel::PublicKernelInputs; -using DummyBuilder = aztec3::utils::DummyCircuitBuilder; - -KernelCircuitPublicInputs native_public_kernel_circuit_public_previous_kernel( - DummyBuilder& builder, PublicKernelInputs const& public_kernel_inputs); -} // namespace aztec3::circuits::kernel::public_kernel \ No newline at end of file diff --git a/circuits/cpp/src/aztec3/circuits/mock/mock_circuit.hpp b/circuits/cpp/src/aztec3/circuits/mock/mock_circuit.hpp deleted file mode 100644 index 4e5c2744757..00000000000 --- a/circuits/cpp/src/aztec3/circuits/mock/mock_circuit.hpp +++ /dev/null @@ -1,18 +0,0 @@ -#pragma once - -#include - -namespace aztec3::circuits::mock { - -using namespace plonk::stdlib; - -template void mock_circuit(Builder& builder, std::vector const& public_inputs_) -{ - const auto public_inputs = map(public_inputs_, [&](auto& i) { return field_t(witness_t(&builder, i)); }); - for (auto& p : public_inputs) { - p.set_public(); - } - plonk::stdlib::pedersen::hash({ field_t(witness_t(&builder, 1)), field_t(witness_t(&builder, 1)) }); -} - -} // namespace aztec3::circuits::mock diff --git a/circuits/cpp/src/aztec3/circuits/mock/mock_kernel_circuit.hpp b/circuits/cpp/src/aztec3/circuits/mock/mock_kernel_circuit.hpp deleted file mode 100644 index 53148fd979b..00000000000 --- a/circuits/cpp/src/aztec3/circuits/mock/mock_kernel_circuit.hpp +++ /dev/null @@ -1,50 +0,0 @@ -#pragma once -#include "aztec3/circuits/abis/kernel_circuit_public_inputs.hpp" -#include "aztec3/utils/types/circuit_types.hpp" -#include "aztec3/utils/types/native_types.hpp" - -#include - -namespace { -auto& engine = numeric::random::get_debug_engine(); -} - -namespace aztec3::circuits::mock { - -using aztec3::circuits::abis::KernelCircuitPublicInputs; -using NT = aztec3::utils::types::NativeTypes; -using aztec3::utils::types::CircuitTypes; -using plonk::stdlib::witness_t; - -template -KernelCircuitPublicInputs mock_kernel_circuit(Builder& builder, KernelCircuitPublicInputs const& _public_inputs) -{ - typedef CircuitTypes CT; - typedef typename CT::fr fr; - - auto public_inputs = _public_inputs.to_circuit_type(builder); - - { - std::vector dummy_witness_indices; - // 16 is the number of values added to `proof_witness_indices` at the end of `verify_proof`. - for (size_t i = 0; i < 16; ++i) { - fr const witness = fr(witness_t(&builder, i)); - uint32_t const witness_index = witness.get_witness_index(); - dummy_witness_indices.push_back(witness_index); - } - public_inputs.end.aggregation_object.proof_witness_indices = dummy_witness_indices; - } - - public_inputs.set_public(); - - // NOTE: We don't want a recursive proof in the mock kernel proof. - // We still add dummy witness indices in the recursive proof indices just so that we don't trigger an assertion in - // while setting recursion elements as public inputs. These dummy indices would not be used as we're setting - // contains_recursive_proof to be false. - builder.contains_recursive_proof = false; - - plonk::stdlib::pedersen_hash::hash({ fr(witness_t(&builder, 1)), fr(witness_t(&builder, 1)) }); - return public_inputs.template to_native_type(); -} - -} // namespace aztec3::circuits::mock diff --git a/circuits/cpp/src/aztec3/circuits/recursion/CMakeLists.txt b/circuits/cpp/src/aztec3/circuits/recursion/CMakeLists.txt deleted file mode 100644 index 3386bf2e151..00000000000 --- a/circuits/cpp/src/aztec3/circuits/recursion/CMakeLists.txt +++ /dev/null @@ -1,4 +0,0 @@ -circuits_cmake_module( - aztec3_circuits_recursion - barretenberg -) \ No newline at end of file diff --git a/circuits/cpp/src/aztec3/circuits/recursion/aggregator.hpp b/circuits/cpp/src/aztec3/circuits/recursion/aggregator.hpp deleted file mode 100644 index 343ba12e79e..00000000000 --- a/circuits/cpp/src/aztec3/circuits/recursion/aggregator.hpp +++ /dev/null @@ -1,22 +0,0 @@ -#pragma once -#include "init.hpp" - -#include - -namespace aztec3::circuits::recursion { - -class Aggregator { - public: - static CT::AggregationObject aggregate( - Builder* builder, - const std::shared_ptr& vk, - const NT::Proof& proof, - const CT::AggregationObject& previous_aggregation_output = CT::AggregationObject()) - { - CT::AggregationObject result = - verify_proof(builder, vk, proof, previous_aggregation_output); - - return result; - } -}; -} // namespace aztec3::circuits::recursion \ No newline at end of file diff --git a/circuits/cpp/src/aztec3/circuits/recursion/init.hpp b/circuits/cpp/src/aztec3/circuits/recursion/init.hpp deleted file mode 100644 index 427d25f502d..00000000000 --- a/circuits/cpp/src/aztec3/circuits/recursion/init.hpp +++ /dev/null @@ -1,21 +0,0 @@ -#pragma once -#include "aztec3/utils/types/circuit_types.hpp" -#include "aztec3/utils/types/convert.hpp" -#include "aztec3/utils/types/native_types.hpp" - -#include - -namespace aztec3::circuits::recursion { -// Builder -using Builder = UltraCircuitBuilder; - -// Generic types: -using CT = aztec3::utils::types::CircuitTypes; -using NT = aztec3::utils::types::NativeTypes; -using aztec3::utils::types::to_ct; - -// Recursion types and methods -using plonk::stdlib::recursion::verify_proof; -using transcript::Manifest; - -} // namespace aztec3::circuits::recursion \ No newline at end of file diff --git a/circuits/cpp/src/aztec3/circuits/rollup/CMakeLists.txt b/circuits/cpp/src/aztec3/circuits/rollup/CMakeLists.txt deleted file mode 100644 index d2994796d2c..00000000000 --- a/circuits/cpp/src/aztec3/circuits/rollup/CMakeLists.txt +++ /dev/null @@ -1,5 +0,0 @@ -circuits_cmake_module( - aztec3_circuits_rollup - aztec3_circuits_kernel - barretenberg -) \ No newline at end of file diff --git a/circuits/cpp/src/aztec3/circuits/rollup/base/.test.cpp b/circuits/cpp/src/aztec3/circuits/rollup/base/.test.cpp deleted file mode 100644 index fab2b6c9a08..00000000000 --- a/circuits/cpp/src/aztec3/circuits/rollup/base/.test.cpp +++ /dev/null @@ -1,899 +0,0 @@ -#include "c_bind.h" -#include "index.hpp" -#include "init.hpp" - -#include "aztec3/circuits/abis/append_only_tree_snapshot.hpp" -#include "aztec3/circuits/abis/combined_accumulated_data.hpp" -#include "aztec3/circuits/abis/global_variables.hpp" -#include "aztec3/circuits/abis/kernel_circuit_public_inputs.hpp" -#include "aztec3/circuits/abis/membership_witness.hpp" -#include "aztec3/circuits/abis/new_contract_data.hpp" -#include "aztec3/circuits/abis/previous_kernel_data.hpp" -#include "aztec3/circuits/abis/public_data_read.hpp" -#include "aztec3/circuits/hash.hpp" -#include "aztec3/circuits/kernel/private/utils.hpp" -#include "aztec3/circuits/rollup/components/components.hpp" -#include "aztec3/circuits/rollup/test_utils/utils.hpp" -#include "aztec3/constants.hpp" -#include "aztec3/utils/circuit_errors.hpp" - -#include - -#include - -#include -#include -#include -#include -#include - - -namespace { - - -using aztec3::circuits::abis::PreviousKernelData; - - -// using aztec3::circuits::mock::mock_circuit; -using aztec3::circuits::rollup::test_utils::utils::base_rollup_inputs_from_kernels; -using aztec3::circuits::rollup::test_utils::utils::compare_field_hash_to_expected; -using aztec3::circuits::rollup::test_utils::utils::get_empty_kernel; -using aztec3::circuits::rollup::test_utils::utils::get_initial_nullifier_tree; -// using aztec3::circuits::mock::mock_kernel_inputs; - -using aztec3::circuits::abis::AppendOnlyTreeSnapshot; - -using aztec3::circuits::rollup::native_base_rollup::BaseOrMergeRollupPublicInputs; -using aztec3::circuits::rollup::native_base_rollup::BaseRollupInputs; -using aztec3::circuits::rollup::native_base_rollup::ConstantRollupData; -using aztec3::circuits::rollup::native_base_rollup::NT; - -using aztec3::circuits::abis::NewContractData; - -using aztec3::circuits::rollup::test_utils::utils::make_public_data_update_request; -using aztec3::circuits::rollup::test_utils::utils::make_public_read; - -using DummyCircuitBuilder = aztec3::utils::DummyCircuitBuilder; - -using aztec3::utils::CircuitErrorCode; -} // namespace - -namespace aztec3::circuits::rollup::base::native_base_rollup_circuit { - -class base_rollup_tests : public ::testing::Test { - protected: - static void SetUpTestSuite() { barretenberg::srs::init_crs_factory("../barretenberg/cpp/srs_db/ignition"); } - - // TODO(1998): uncomment once https://github.com/AztecProtocol/aztec-packages/issues/1998 is solved and - // use new pattern such as call_func_and_wrapper from test_helper.hpp - - // static void run_cbind(BaseRollupInputs& base_rollup_inputs, - // BaseOrMergeRollupPublicInputs& expected_public_inputs, - // bool compare_pubins = true, - // bool assert_no_circuit_failure = true) - // { - // info("Retesting via cbinds...."); - - // std::vector base_rollup_inputs_vec; - // serialize::write(base_rollup_inputs_vec, base_rollup_inputs); - - // // uint8_t const* proof_data; - // // size_t proof_data_size; - // uint8_t const* public_inputs_buf = nullptr; - // size_t public_inputs_size = 0; - // // info("simulating circuit via cbind"); - // uint8_t* const circuit_failure_ptr = - // base_rollup__sim(base_rollup_inputs_vec.data(), &public_inputs_size, &public_inputs_buf); - - // ASSERT_TRUE(assert_no_circuit_failure ? circuit_failure_ptr == nullptr : circuit_failure_ptr != nullptr); - // // info("Proof size: ", proof_data_size); - // // info("PublicInputs size: ", public_inputs_size); - - // if (compare_pubins) { - // BaseOrMergeRollupPublicInputs public_inputs; - // uint8_t const* public_inputs_buf_tmp = public_inputs_buf; - // serialize::read(public_inputs_buf_tmp, public_inputs); - // ASSERT_EQ(public_inputs.calldata_hash.size(), expected_public_inputs.calldata_hash.size()); - // for (size_t i = 0; i < public_inputs.calldata_hash.size(); i++) { - // ASSERT_EQ(public_inputs.calldata_hash[i], expected_public_inputs.calldata_hash[i]); - // } - - // std::vector expected_public_inputs_vec; - // serialize::write(expected_public_inputs_vec, expected_public_inputs); - - // ASSERT_EQ(public_inputs_size, expected_public_inputs_vec.size()); - // // Just compare the first 10 bytes of the serialized public outputs - // if (public_inputs_size > 10) { - // // for (size_t 0; i < public_inputs_size; i++) { - // for (size_t i = 0; i < 10; i++) { - // ASSERT_EQ(public_inputs_buf[i], expected_public_inputs_vec[i]); - // } - // } - // } - - // // free((void*)proof_data); - // free((void*)public_inputs_buf); - // // info("finished retesting via cbinds..."); - // } -}; - -TEST_F(base_rollup_tests, native_no_new_contract_leafs) -{ - DummyCircuitBuilder builder = DummyCircuitBuilder("base_rollup_tests__native_no_new_contract_leafs"); - // When there are no contract deployments. The contract tree should be inserting 0 leafs, (not empty leafs); - // Initially, the start_contract_tree_snapshot is empty (leaf is 0. hash it up). - // Get sibling path of index 0 leaf (for circuit to check membership via sibling path) - // No contract leaves -> will insert empty tree -> i.e. end_contract_tree_root = start_contract_tree_root - - BaseRollupInputs emptyInputs = base_rollup_inputs_from_kernels({ get_empty_kernel(), get_empty_kernel() }); - MemoryStore contract_tree_store; - auto empty_contract_tree = MerkleTree(contract_tree_store, CONTRACT_TREE_HEIGHT); - - BaseOrMergeRollupPublicInputs outputs = - aztec3::circuits::rollup::native_base_rollup::base_rollup_circuit(builder, emptyInputs); - - AppendOnlyTreeSnapshot const expectedStartContractTreeSnapshot = { - .root = empty_contract_tree.root(), - .next_available_leaf_index = 0, - }; - AppendOnlyTreeSnapshot const expectedEndContractTreeSnapshot = { - .root = empty_contract_tree.root(), - .next_available_leaf_index = 2, - }; - ASSERT_EQ(outputs.start_contract_tree_snapshot, expectedStartContractTreeSnapshot); - ASSERT_EQ(outputs.end_contract_tree_snapshot, expectedEndContractTreeSnapshot); - ASSERT_EQ(outputs.start_contract_tree_snapshot, emptyInputs.start_contract_tree_snapshot); - ASSERT_FALSE(builder.failed()) << builder.failure_msgs; - // TODO(1998): see above - // run_cbind(emptyInputs, outputs); -} - -TEST_F(base_rollup_tests, native_contract_leaf_inserted) -{ - DummyCircuitBuilder builder = DummyCircuitBuilder("base_rollup_tests__native_contract_leaf_inserted"); - // When there is a contract deployment, the contract tree should be inserting 1 leaf. - // The remaining leafs should be 0 leafs, (not empty leafs); - - // Create a "mock" contract deployment - NewContractData const new_contract = { - .contract_address = fr(1), - .portal_contract_address = fr(3), - .function_tree_root = fr(2), - }; - - MemoryStore empty_contract_tree_store; - auto empty_contract_tree = MerkleTree(empty_contract_tree_store, CONTRACT_TREE_HEIGHT); - AppendOnlyTreeSnapshot const expected_start_contracts_snapshot = { - .root = empty_contract_tree.root(), - .next_available_leaf_index = 0, - }; - - // create expected end contract tree snapshot - MemoryStore contract_tree_store; - auto expected_end_contracts_snapshot_tree = - stdlib::merkle_tree::MerkleTree(contract_tree_store, CONTRACT_TREE_HEIGHT); - expected_end_contracts_snapshot_tree.update_element(0, new_contract.hash()); - - AppendOnlyTreeSnapshot const expected_end_contracts_snapshot = { - .root = expected_end_contracts_snapshot_tree.root(), - .next_available_leaf_index = 2, - }; - - std::array, 2> kernel_data = { get_empty_kernel(), get_empty_kernel() }; - kernel_data[0].public_inputs.end.new_contracts[0] = new_contract; - BaseRollupInputs inputs = base_rollup_inputs_from_kernels(kernel_data); - - BaseOrMergeRollupPublicInputs outputs = - aztec3::circuits::rollup::native_base_rollup::base_rollup_circuit(builder, inputs); - - ASSERT_EQ(outputs.start_contract_tree_snapshot, expected_start_contracts_snapshot); - ASSERT_EQ(outputs.start_contract_tree_snapshot, inputs.start_contract_tree_snapshot); - ASSERT_EQ(outputs.end_contract_tree_snapshot, expected_end_contracts_snapshot); - ASSERT_FALSE(builder.failed()) << builder.failure_msgs; - // TODO(1998): see above - // run_cbind(inputs, outputs); -} - -TEST_F(base_rollup_tests, native_contract_leaf_inserted_in_non_empty_snapshot_tree) -{ - DummyCircuitBuilder builder = - DummyCircuitBuilder("base_rollup_tests__native_contract_leaf_inserted_in_non_empty_snapshot_tree"); - // Same as before except our start_contract_snapshot_tree is not empty - std::array, 2> kernel_data = { get_empty_kernel(), get_empty_kernel() }; - - // Create a "mock" contract deployment - NewContractData new_contract = { - .contract_address = fr(1), - .portal_contract_address = fr(3), - .function_tree_root = fr(2), - }; - kernel_data[0].public_inputs.end.new_contracts[0] = new_contract; - BaseRollupInputs inputs = base_rollup_inputs_from_kernels(kernel_data); - - MemoryStore start_contract_tree_snapshot_store; - auto start_contract_tree_snapshot = MerkleTree(start_contract_tree_snapshot_store, CONTRACT_TREE_HEIGHT); - // insert 12 leaves to the tree (next available leaf index is 12) - for (size_t i = 0; i < 12; ++i) { - start_contract_tree_snapshot.update_element(i, fr(i)); - } - // set the start_contract_tree_snapshot - inputs.start_contract_tree_snapshot = { - .root = start_contract_tree_snapshot.root(), - .next_available_leaf_index = 12, - }; - - // Set the new_contracts_subtree_sibling_path - auto sibling_path = get_sibling_path( - start_contract_tree_snapshot, 12, CONTRACT_SUBTREE_HEIGHT); - inputs.new_contracts_subtree_sibling_path = sibling_path; - - // create expected end contract tree snapshot - auto expected_contract_leaf = crypto::pedersen_hash::hash( - { new_contract.contract_address, new_contract.portal_contract_address, new_contract.function_tree_root }, - NativeTypes::get_generator_context(GeneratorIndex::CONTRACT_LEAF)); - - auto expected_end_contract_tree_snapshot_store = start_contract_tree_snapshot_store; - auto expected_end_contracts_snapshot_tree = - MerkleTree(expected_end_contract_tree_snapshot_store, CONTRACT_TREE_HEIGHT); - expected_end_contracts_snapshot_tree.update_element(12, expected_contract_leaf); - - AppendOnlyTreeSnapshot const expected_end_contracts_snapshot = { - .root = expected_end_contracts_snapshot_tree.root(), - .next_available_leaf_index = 14, - }; - BaseOrMergeRollupPublicInputs outputs = - aztec3::circuits::rollup::native_base_rollup::base_rollup_circuit(builder, inputs); - - ASSERT_EQ(outputs.start_contract_tree_snapshot, inputs.start_contract_tree_snapshot); - ASSERT_EQ(outputs.end_contract_tree_snapshot, expected_end_contracts_snapshot); - ASSERT_FALSE(builder.failed()) << builder.failure_msgs; - // TODO(1998): see above - // run_cbind(inputs, outputs); -} - -TEST_F(base_rollup_tests, native_new_commitments_tree) -{ - DummyCircuitBuilder builder = DummyCircuitBuilder("base_rollup_tests__native_new_commitments_tree"); - // Create 4 new mock commitments. Add them to kernel data. - // Then get sibling path so we can verify insert them into the tree. - - std::array, 2> kernel_data = { get_empty_kernel(), get_empty_kernel() }; - std::array new_commitments = { 0, 1, 2, 3, 4, 5, 6, 7 }; - for (uint8_t i = 0; i < 2; i++) { - std::array kernel_commitments; - for (uint8_t j = 0; j < MAX_NEW_COMMITMENTS_PER_TX; j++) { - kernel_commitments[j] = new_commitments[i * MAX_NEW_COMMITMENTS_PER_TX + j]; - } - kernel_data[i].public_inputs.end.new_commitments = kernel_commitments; - } - - // get sibling path - MemoryStore note_hash_tree_store; - auto note_hash_tree = MerkleTree(note_hash_tree_store, NOTE_HASH_TREE_HEIGHT); - AppendOnlyTreeSnapshot const expected_start_commitments_snapshot = { - .root = note_hash_tree.root(), - .next_available_leaf_index = 0, - }; - for (size_t i = 0; i < new_commitments.size(); ++i) { - note_hash_tree.update_element(i, new_commitments[i]); - } - AppendOnlyTreeSnapshot const expected_end_commitments_snapshot = { - .root = note_hash_tree.root(), - .next_available_leaf_index = 2 * MAX_NEW_COMMITMENTS_PER_TX, - }; - - auto inputs = base_rollup_inputs_from_kernels(kernel_data); - BaseOrMergeRollupPublicInputs outputs = - aztec3::circuits::rollup::native_base_rollup::base_rollup_circuit(builder, inputs); - - ASSERT_EQ(outputs.start_note_hash_tree_snapshot, expected_start_commitments_snapshot); - ASSERT_EQ(outputs.start_note_hash_tree_snapshot, inputs.start_note_hash_tree_snapshot); - ASSERT_EQ(outputs.end_note_hash_tree_snapshot, expected_end_commitments_snapshot); - ASSERT_FALSE(builder.failed()) << builder.failure_msgs; - // TODO(1998): see above - // run_cbind(inputs, outputs); -} - -template NT::fr calc_root(NT::fr leaf, NT::uint32 leafIndex, std::array siblingPath) -{ - for (size_t i = 0; i < siblingPath.size(); i++) { - if (leafIndex & (1 << i)) { - leaf = proof_system::plonk::stdlib::merkle_tree::hash_pair_native(siblingPath[i], leaf); - } else { - leaf = proof_system::plonk::stdlib::merkle_tree::hash_pair_native(leaf, siblingPath[i]); - } - } - return leaf; -} - -TEST_F(base_rollup_tests, native_new_nullifier_tree_empty) -{ - /** - * DESCRIPTION - */ - - // This test checks for insertions of all 0 values - // In this special case we will not need to provide sibling paths to check insertion of the nullifier values - // This is because 0 values are not actually inserted into the tree, rather the inserted subtree is left - // empty to begin with. - - std::array const new_nullifiers{}; - std::vector initial_values(2 * MAX_NEW_NULLIFIERS_PER_TX - 1); - - for (size_t i = 0; i < initial_values.size(); i++) { - initial_values[i] = i + 1; - } - - auto nullifier_tree = get_initial_nullifier_tree(initial_values); - auto start_nullifier_tree_snapshot = nullifier_tree.get_snapshot(); - for (auto v : new_nullifiers) { - nullifier_tree.update_element(v); - } - auto end_nullifier_tree_snapshot = nullifier_tree.get_snapshot(); - - /** - * RUN - */ - DummyCircuitBuilder builder = DummyCircuitBuilder("base_rollup_tests__native_new_nullifier_tree_empty"); - std::array, 2> const kernel_data = { get_empty_kernel(), get_empty_kernel() }; - BaseRollupInputs const empty_inputs = base_rollup_inputs_from_kernels(kernel_data); - - BaseOrMergeRollupPublicInputs const outputs = - aztec3::circuits::rollup::native_base_rollup::base_rollup_circuit(builder, empty_inputs); - - /** - * ASSERT - */ - // Start state - ASSERT_EQ(outputs.start_nullifier_tree_snapshot, start_nullifier_tree_snapshot); - - // End state - ASSERT_EQ(outputs.end_nullifier_tree_snapshot, end_nullifier_tree_snapshot); - ASSERT_EQ(outputs.end_nullifier_tree_snapshot.root, outputs.start_nullifier_tree_snapshot.root); - ASSERT_EQ(outputs.end_nullifier_tree_snapshot.next_available_leaf_index, - outputs.start_nullifier_tree_snapshot.next_available_leaf_index + 2 * MAX_NEW_NULLIFIERS_PER_TX); - ASSERT_FALSE(builder.failed()) << builder.failure_msgs; -} - -void nullifier_insertion_test(std::array new_nullifiers) -{ - // @todo We can probably reuse this more than we are already doing. - // Regression test caught when testing the typescript nullifier tree implementation - - std::vector initial_values(2 * MAX_NEW_NULLIFIERS_PER_TX - 1); - for (size_t i = 0; i < initial_values.size(); i++) { - initial_values[i] = i + 1; - } - - auto nullifier_tree = get_initial_nullifier_tree(initial_values); - auto start_nullifier_tree_snapshot = nullifier_tree.get_snapshot(); - for (auto v : new_nullifiers) { - nullifier_tree.update_element(v); - } - auto end_nullifier_tree_snapshot = nullifier_tree.get_snapshot(); - - DummyCircuitBuilder builder = DummyCircuitBuilder("base_rollup_tests__nullifier_insertion_test"); - std::array, 2> kernel_data = { get_empty_kernel(), get_empty_kernel() }; - for (uint8_t i = 0; i < 2; i++) { - std::array kernel_nullifiers; - for (uint8_t j = 0; j < MAX_NEW_NULLIFIERS_PER_TX; j++) { - kernel_nullifiers[j] = new_nullifiers[i * MAX_NEW_NULLIFIERS_PER_TX + j]; - } - kernel_data[i].public_inputs.end.new_nullifiers = kernel_nullifiers; - } - BaseRollupInputs const inputs = base_rollup_inputs_from_kernels(kernel_data); - - BaseOrMergeRollupPublicInputs const outputs = - aztec3::circuits::rollup::native_base_rollup::base_rollup_circuit(builder, inputs); - /** - * ASSERT - */ - ASSERT_EQ(outputs.start_nullifier_tree_snapshot, start_nullifier_tree_snapshot); - ASSERT_EQ(outputs.end_nullifier_tree_snapshot, end_nullifier_tree_snapshot); - ASSERT_EQ(outputs.end_nullifier_tree_snapshot.next_available_leaf_index, - outputs.start_nullifier_tree_snapshot.next_available_leaf_index + MAX_NEW_NULLIFIERS_PER_TX * 2); - ASSERT_FALSE(builder.failed()) << builder.failure_msgs; -} - -TEST_F(base_rollup_tests, native_new_nullifier_tree_all_larger) -{ - std::array initial_values; - - for (size_t i = 0; i < initial_values.size(); i++) { - initial_values[i] = 2 * MAX_NEW_NULLIFIERS_PER_TX + i; - } - - nullifier_insertion_test(initial_values); -} - -TEST_F(base_rollup_tests, native_new_nullifier_tree_sparse_insertions) -{ - std::array initial_values; - - for (size_t i = 0; i < initial_values.size(); i++) { - initial_values[i] = 2 * MAX_NEW_NULLIFIERS_PER_TX + 5 * i + 1; - } - nullifier_insertion_test(initial_values); -} - -TEST_F(base_rollup_tests, native_new_nullifier_tree_sparse) -{ - std::array nullifiers; - - for (size_t i = 0; i < nullifiers.size(); i++) { - nullifiers[i] = 2 * MAX_NEW_NULLIFIERS_PER_TX + 5 * i + 1; - } - - std::vector initial_values(2 * MAX_NEW_NULLIFIERS_PER_TX - 1); - - for (size_t i = 0; i < initial_values.size(); i++) { - initial_values[i] = 5 * (i + 1); - } - - auto nullifier_tree = get_initial_nullifier_tree(initial_values); - auto expected_start_nullifier_tree_snapshot = nullifier_tree.get_snapshot(); - for (auto v : nullifiers) { - nullifier_tree.update_element(v); - } - auto expected_end_nullifier_tree_snapshot = nullifier_tree.get_snapshot(); - - DummyCircuitBuilder builder = DummyCircuitBuilder("base_rollup_tests__native_new_nullifier_tree_sparse"); - BaseRollupInputs const empty_inputs = base_rollup_inputs_from_kernels({ get_empty_kernel(), get_empty_kernel() }); - std::tuple, AppendOnlyTreeSnapshot> inputs_and_snapshots = - test_utils::utils::generate_nullifier_tree_testing_values_explicit(empty_inputs, nullifiers, initial_values); - - BaseRollupInputs const testing_inputs = std::get<0>(inputs_and_snapshots); - - /** - * RUN - */ - - // Run the circuit - BaseOrMergeRollupPublicInputs const outputs = - aztec3::circuits::rollup::native_base_rollup::base_rollup_circuit(builder, testing_inputs); - - /** - * ASSERT - */ - // Start state - ASSERT_EQ(outputs.start_nullifier_tree_snapshot, expected_start_nullifier_tree_snapshot); - - // End state - ASSERT_EQ(outputs.end_nullifier_tree_snapshot, expected_end_nullifier_tree_snapshot); - ASSERT_FALSE(builder.failed()) << builder.failure_msgs; -} - -TEST_F(base_rollup_tests, native_nullifier_tree_regression) -{ - // Regression test caught when testing the typescript nullifier tree implementation - DummyCircuitBuilder builder = DummyCircuitBuilder("base_rollup_tests__native_nullifier_tree_regression"); - - // This test runs after some data has already been inserted into the tree - // This test will pre-populate the tree with 6 * KERNEL_NEW_NULLIFIERS_LENGTH values (0 item + 6 * - // KERNEL_NEW_NULLIFIERS_LENGTH -1 more) simulating that a rollup inserting two random values has already - // succeeded. Note that this corresponds to 3 (1 already initialized and 2 new ones) base rollups. This rollup - // then adds two further random values that will end up having their low nullifiers point at each other - std::vector initial_values(6 * MAX_NEW_NULLIFIERS_PER_TX - 1, 0); - for (size_t i = 0; i < 2 * MAX_NEW_NULLIFIERS_PER_TX - 1; i++) { - initial_values[i] = i + 1; - } - // Note these are hex representations - initial_values[7] = uint256_t("2bb9aa4a22a6ae7204f2c67abaab59cead6558cde4ee25ce3464704cb2e38136"); - initial_values[8] = uint256_t("16a732095298ccca828c4d747813f8bd46e188079ed17904e2c9de50760833c8"); - - std::array new_nullifiers = { 0 }; - new_nullifiers[0] = uint256_t("16da4f27fb78de7e0db4c5a04b569bc46382c5f471da2f7d670beff1614e0118"), - new_nullifiers[1] = uint256_t("26ab07ce103a55e29f11478eaa36cebd10c4834b143a7debcc7ef53bfdb547dd"); - - auto nullifier_tree = get_initial_nullifier_tree(initial_values); - auto expected_start_nullifier_tree_snapshot = nullifier_tree.get_snapshot(); - for (auto v : new_nullifiers) { - nullifier_tree.update_element(v); - } - auto expected_end_nullifier_tree_snapshot = nullifier_tree.get_snapshot(); - - /** - * RUN - */ - BaseRollupInputs const empty_inputs = base_rollup_inputs_from_kernels({ get_empty_kernel(), get_empty_kernel() }); - std::tuple, AppendOnlyTreeSnapshot> inputs_and_snapshots = - test_utils::utils::generate_nullifier_tree_testing_values_explicit( - empty_inputs, new_nullifiers, initial_values); - BaseRollupInputs const testing_inputs = std::get<0>(inputs_and_snapshots); - // Run the circuit - BaseOrMergeRollupPublicInputs const outputs = - aztec3::circuits::rollup::native_base_rollup::base_rollup_circuit(builder, testing_inputs); - - /** - * ASSERT - */ - // Start state - ASSERT_EQ(outputs.start_nullifier_tree_snapshot, expected_start_nullifier_tree_snapshot); - - // End state - ASSERT_EQ(outputs.end_nullifier_tree_snapshot, expected_end_nullifier_tree_snapshot); - ASSERT_FALSE(builder.failed()) << builder.failure_msgs; -} - -// Another regression test with values from a failing packages test -TEST_F(base_rollup_tests, nullifier_tree_regression_2) -{ - // Regression test caught when testing the typescript nullifier tree implementation - std::array new_nullifiers = { 0 }; - new_nullifiers[0] = uint256_t("2a7d956c1365d259646d2d85babe1abb793bb8789e98df7e2336a29a0c91fd01"); - new_nullifiers[1] = uint256_t("236bf2d113f9ffee89df1a7a04890c9ad3583c6773eb9cdec484184f66abd4c6"); - new_nullifiers[4] = uint256_t("2f5c8a1ee33c7104b244e22a3e481637cd501c9eae868cfab6b16e3b4ef3d635"); - new_nullifiers[5] = uint256_t("0c484a20780e31747cf9f4f6803986525ed98ef587f5155a1c50689c2cad10ae"); - - nullifier_insertion_test(new_nullifiers); -} - -TEST_F(base_rollup_tests, nullifier_tree_regression_3) -{ - std::array new_nullifiers = { 0 }; - new_nullifiers[0] = uint256_t("0740a17aa6437e71836d2adcdcb3f52879bb869cdd9c8fb8dc39a12846cd17f2"); - new_nullifiers[1] = uint256_t("282e0e2f38310a7c7c98b636830b66f3276294560e26ef2499da10892f00af8f"); - new_nullifiers[4] = uint256_t("0f117936e888bd3befb4435f4d65300d25609e95a3d1563f62ef7e58c294f578"); - new_nullifiers[5] = uint256_t("0fcb3908cb15ebf8bab276f5df17524d3b676c8655234e4350953c387fffcdd7"); - - nullifier_insertion_test(new_nullifiers); -} - -TEST_F(base_rollup_tests, native_new_nullifier_tree_double_spend) -{ - /** - * DESCRIPTION - */ - - DummyCircuitBuilder builder = DummyCircuitBuilder("base_rollup_tests__native_new_nullifier_tree_double_spend"); - BaseRollupInputs const empty_inputs = base_rollup_inputs_from_kernels({ get_empty_kernel(), get_empty_kernel() }); - - fr const nullifier_to_insert = - 2 * MAX_NEW_NULLIFIERS_PER_TX + 4; // arbitrary value greater than 2 * MAX_NEW_NULLIFIERS_PER_TX - std::array new_nullifiers{}; - - new_nullifiers[0] = nullifier_to_insert; - new_nullifiers[2] = nullifier_to_insert; - - std::tuple, AppendOnlyTreeSnapshot> inputs_and_snapshots = - test_utils::utils::generate_nullifier_tree_testing_values(empty_inputs, new_nullifiers, 1); - BaseRollupInputs const testing_inputs = std::get<0>(inputs_and_snapshots); - - BaseOrMergeRollupPublicInputs const outputs = - aztec3::circuits::rollup::native_base_rollup::base_rollup_circuit(builder, testing_inputs); - - ASSERT_TRUE(builder.failed()); - ASSERT_EQ(builder.get_first_failure().code, CircuitErrorCode::BASE__INVALID_NULLIFIER_RANGE); -} - -TEST_F(base_rollup_tests, native_empty_block_calldata_hash) -{ - DummyCircuitBuilder builder = DummyCircuitBuilder("base_rollup_tests__native_empty_block_calldata_hash"); - std::vector const zero_bytes_vec = test_utils::utils::get_empty_calldata_leaf(); - auto expected_calldata_hash = sha256::sha256(zero_bytes_vec); - BaseRollupInputs inputs = base_rollup_inputs_from_kernels({ get_empty_kernel(), get_empty_kernel() }); - BaseOrMergeRollupPublicInputs outputs = - aztec3::circuits::rollup::native_base_rollup::base_rollup_circuit(builder, inputs); - std::array const output_calldata_hash = outputs.calldata_hash; - - ASSERT_TRUE(compare_field_hash_to_expected(output_calldata_hash, expected_calldata_hash) == true); - - ASSERT_FALSE(builder.failed()) << builder.failure_msgs; - // TODO(1998): see above - // run_cbind(inputs, outputs); -} - -TEST_F(base_rollup_tests, native_calldata_hash) -{ - // Execute the base rollup circuit with nullifiers, commitments and a contract deployment. Then check the - // calldata hash against the expected value. - std::array, 2> kernel_data = { get_empty_kernel(), get_empty_kernel() }; - - // Commitments inserted are [1,2,3,4,5,6,7,8 ...]. Nullifiers inserted are [8,9,10,11,12,13,14,15 ...] - for (size_t i = 0; i < 2; ++i) { - for (size_t j = 0; j < MAX_NEW_NULLIFIERS_PER_TX; j++) { - kernel_data[i].public_inputs.end.new_commitments[j] = fr(i * MAX_NEW_NULLIFIERS_PER_TX + j + 1); - kernel_data[i].public_inputs.end.new_nullifiers[j] = fr((2 + i) * MAX_NEW_NULLIFIERS_PER_TX + j); - } - } - - // Add logs hashes - kernel_data[0].public_inputs.end.encrypted_logs_hash = { NT::fr(16), NT::fr(69) }; - kernel_data[1].public_inputs.end.encrypted_logs_hash = { NT::fr(812), NT::fr(234) }; - kernel_data[0].public_inputs.end.unencrypted_logs_hash = { NT::fr(163), NT::fr(212) }; - kernel_data[1].public_inputs.end.unencrypted_logs_hash = { NT::fr(4352), NT::fr(1632) }; - - // Add a contract deployment - NewContractData const new_contract = { - .contract_address = fr(1), - .portal_contract_address = fr(3), - .function_tree_root = fr(2), - }; - kernel_data[0].public_inputs.end.new_contracts[0] = new_contract; - - std::array const expected_calldata_hash = - components::compute_kernels_calldata_hash(kernel_data); - - DummyCircuitBuilder builder = DummyCircuitBuilder("base_rollup_tests__native_calldata_hash"); - BaseRollupInputs inputs = base_rollup_inputs_from_kernels(kernel_data); - BaseOrMergeRollupPublicInputs outputs = - aztec3::circuits::rollup::native_base_rollup::base_rollup_circuit(builder, inputs); - - std::array const output_calldata_hash = outputs.calldata_hash; - - ASSERT_EQ(expected_calldata_hash, output_calldata_hash); - - ASSERT_FALSE(builder.failed()) << builder.failure_msgs; - // TODO(1998): see above - // run_cbind(inputs, outputs); -} - -TEST_F(base_rollup_tests, native_compute_membership_archive_negative) -{ - // WRITE a negative test that will fail the inclusion proof - - // Test membership works for empty trees - DummyCircuitBuilder builder = - DummyCircuitBuilder("base_rollup_tests__native_compute_membership_historical_private_data_negative"); - std::array, 2> const kernel_data = { get_empty_kernel(), get_empty_kernel() }; - BaseRollupInputs inputs = base_rollup_inputs_from_kernels(kernel_data); - - MemoryStore blocks_store; - auto archive = MerkleTree(blocks_store, ARCHIVE_HEIGHT); - - // Create an INCORRECT sibling path for the note hash tree root in the historical tree roots. - auto hash_path = archive.get_sibling_path(0); - std::array sibling_path{}; - for (size_t i = 0; i < ARCHIVE_HEIGHT; ++i) { - sibling_path[i] = hash_path[i] + 1; - } - inputs.archive_root_membership_witnesses[0] = { - .leaf_index = 0, - .sibling_path = sibling_path, - }; - - BaseOrMergeRollupPublicInputs const outputs = - aztec3::circuits::rollup::native_base_rollup::base_rollup_circuit(builder, inputs); - - ASSERT_TRUE(builder.failed()); - ASSERT_EQ(builder.get_first_failure().message, - "Membership check failed: base_rollup_circuit: historical root is in rollup constants but not in " - "blocks tree at kernel input 0 to this base rollup circuit"); -} - - -TEST_F(base_rollup_tests, native_constants_dont_change) -{ - DummyCircuitBuilder builder = DummyCircuitBuilder("base_rollup_tests__native_constants_dont_change"); - BaseRollupInputs inputs = base_rollup_inputs_from_kernels({ get_empty_kernel(), get_empty_kernel() }); - BaseOrMergeRollupPublicInputs outputs = - aztec3::circuits::rollup::native_base_rollup::base_rollup_circuit(builder, inputs); - ASSERT_EQ(inputs.constants, outputs.constants); - EXPECT_FALSE(builder.failed()); - // TODO(1998): see above - // run_cbind(inputs, outputs); -} - -TEST_F(base_rollup_tests, native_constants_dont_match_kernels_chain_id) -{ - DummyCircuitBuilder builder = DummyCircuitBuilder("base_rollup_tests__native_constants_dont_change"); - BaseRollupInputs inputs = base_rollup_inputs_from_kernels({ get_empty_kernel(), get_empty_kernel() }); - inputs.constants.global_variables.chain_id = 3; - BaseOrMergeRollupPublicInputs const outputs = - aztec3::circuits::rollup::native_base_rollup::base_rollup_circuit(builder, inputs); - ASSERT_EQ(inputs.constants, outputs.constants); - EXPECT_TRUE(builder.failed()); - ASSERT_EQ(builder.get_first_failure().message, "kernel chain_id does not match the rollup chain_id"); -} - -TEST_F(base_rollup_tests, native_constants_dont_match_kernels_version) -{ - DummyCircuitBuilder builder = DummyCircuitBuilder("base_rollup_tests__native_constants_dont_change"); - BaseRollupInputs inputs = base_rollup_inputs_from_kernels({ get_empty_kernel(), get_empty_kernel() }); - inputs.constants.global_variables.version = 3; - BaseOrMergeRollupPublicInputs const outputs = - aztec3::circuits::rollup::native_base_rollup::base_rollup_circuit(builder, inputs); - ASSERT_EQ(inputs.constants, outputs.constants); - EXPECT_TRUE(builder.failed()); - ASSERT_EQ(builder.get_first_failure().message, "kernel version does not match the rollup version"); -} - -TEST_F(base_rollup_tests, native_aggregate) -{ - // TODO(rahul): Fix this when aggregation works - DummyCircuitBuilder builder = DummyCircuitBuilder("base_rollup_tests__native_aggregate"); - BaseRollupInputs inputs = base_rollup_inputs_from_kernels({ get_empty_kernel(), get_empty_kernel() }); - BaseOrMergeRollupPublicInputs const outputs = - aztec3::circuits::rollup::native_base_rollup::base_rollup_circuit(builder, inputs); - ASSERT_EQ(inputs.kernel_data[0].public_inputs.end.aggregation_object.public_inputs, - outputs.end_aggregation_object.public_inputs); - ASSERT_FALSE(builder.failed()) << builder.failure_msgs; -} - -TEST_F(base_rollup_tests, native_subtree_height_is_0) -{ - DummyCircuitBuilder builder = DummyCircuitBuilder("base_rollup_tests__native_subtree_height_is_0"); - BaseRollupInputs const inputs = base_rollup_inputs_from_kernels({ get_empty_kernel(), get_empty_kernel() }); - BaseOrMergeRollupPublicInputs const outputs = - aztec3::circuits::rollup::native_base_rollup::base_rollup_circuit(builder, inputs); - ASSERT_EQ(outputs.rollup_subtree_height, fr(0)); - ASSERT_FALSE(builder.failed()) << builder.failure_msgs; -} - -TEST_F(base_rollup_tests, native_cbind_0) -{ - // @todo Error handling? - BaseRollupInputs inputs = base_rollup_inputs_from_kernels({ get_empty_kernel(), get_empty_kernel() }); - BaseOrMergeRollupPublicInputs ignored_public_inputs; - // TODO(1998): see above - // run_cbind(inputs, ignored_public_inputs, false); -} - -TEST_F(base_rollup_tests, native_single_public_state_read) -{ - DummyCircuitBuilder builder = DummyCircuitBuilder("base_rollup_tests__native_single_public_state_read"); - MemoryStore note_hash_tree_store; - MerkleTree note_hash_tree(note_hash_tree_store, NOTE_HASH_TREE_HEIGHT); - - MemoryStore contract_tree_store; - MerkleTree contract_tree(contract_tree_store, CONTRACT_TREE_HEIGHT); - - MemoryStore public_data_tree_store; - MerkleTree public_data_tree(public_data_tree_store, PUBLIC_DATA_TREE_HEIGHT); - - MemoryStore l1_to_l2_message_tree_store; - MerkleTree l1_to_l2_message_tree(l1_to_l2_message_tree_store, L1_TO_L2_MSG_TREE_HEIGHT); - - auto data_read = abis::PublicDataRead{ - .leaf_index = fr(1), - .value = fr(42), - }; - - std::array, 2> kernel_data = { get_empty_kernel(), get_empty_kernel() }; - kernel_data[0].public_inputs.end.public_data_reads[0] = data_read; - auto inputs = test_utils::utils::base_rollup_inputs_from_kernels( - kernel_data, note_hash_tree, contract_tree, public_data_tree, l1_to_l2_message_tree); - - BaseOrMergeRollupPublicInputs outputs = - aztec3::circuits::rollup::native_base_rollup::base_rollup_circuit(builder, inputs); - - ASSERT_EQ(outputs.start_public_data_tree_root, inputs.start_public_data_tree_root); - ASSERT_EQ(outputs.end_public_data_tree_root, public_data_tree.root()); - ASSERT_EQ(outputs.end_public_data_tree_root, outputs.start_public_data_tree_root); - ASSERT_FALSE(builder.failed()) << builder.failure_msgs; - // TODO(1998): see above - // run_cbind(inputs, outputs); -} - -TEST_F(base_rollup_tests, native_single_public_state_write) -{ - DummyCircuitBuilder builder = DummyCircuitBuilder("base_rollup_tests__native_single_public_state_write"); - MemoryStore note_hash_tree_store; - MerkleTree note_hash_tree(note_hash_tree_store, NOTE_HASH_TREE_HEIGHT); - - MemoryStore contract_tree_store; - MerkleTree contract_tree(contract_tree_store, CONTRACT_TREE_HEIGHT); - - MemoryStore public_data_tree_store; - MerkleTree public_data_tree(public_data_tree_store, PUBLIC_DATA_TREE_HEIGHT); - - MemoryStore l1_to_l2_message_tree_store; - MerkleTree l1_to_l2_message_tree(l1_to_l2_message_tree_store, L1_TO_L2_MSG_TREE_HEIGHT); - - - auto data_write = abis::PublicDataUpdateRequest{ - .leaf_index = fr(1), - .old_value = fr(2), - .new_value = fr(42), - }; - - std::array, 2> kernel_data = { get_empty_kernel(), get_empty_kernel() }; - kernel_data[0].public_inputs.end.public_data_update_requests[0] = data_write; - - auto inputs = test_utils::utils::base_rollup_inputs_from_kernels( - kernel_data, note_hash_tree, contract_tree, public_data_tree, l1_to_l2_message_tree); - - BaseOrMergeRollupPublicInputs outputs = - aztec3::circuits::rollup::native_base_rollup::base_rollup_circuit(builder, inputs); - - ASSERT_EQ(outputs.start_public_data_tree_root, inputs.start_public_data_tree_root); - ASSERT_EQ(outputs.end_public_data_tree_root, public_data_tree.root()); - ASSERT_NE(outputs.end_public_data_tree_root, outputs.start_public_data_tree_root); - ASSERT_FALSE(builder.failed()) << builder.failure_msgs; - // TODO(1998): see above - // run_cbind(inputs, outputs); -} - -TEST_F(base_rollup_tests, native_multiple_public_state_read_writes) -{ - DummyCircuitBuilder builder = DummyCircuitBuilder("base_rollup_tests__native_multiple_public_state_read_writes"); - MemoryStore note_hash_tree_store; - MerkleTree note_hash_tree(note_hash_tree_store, NOTE_HASH_TREE_HEIGHT); - - MemoryStore contract_tree_store; - MerkleTree contract_tree(contract_tree_store, CONTRACT_TREE_HEIGHT); - - MemoryStore public_data_tree_store; - MerkleTree public_data_tree(public_data_tree_store, PUBLIC_DATA_TREE_HEIGHT); - - MemoryStore l1_to_l2_message_tree_store; - MerkleTree l1_to_l2_message_tree(l1_to_l2_message_tree_store, L1_TO_L2_MSG_TREE_HEIGHT); - - std::array, 2> kernel_data = { get_empty_kernel(), get_empty_kernel() }; - - // We set up reads and writes such that the right tx will read or write to indices already modified by the left - // tx - kernel_data[0].public_inputs.end.public_data_reads[0] = make_public_read(fr(1), fr(101)); - kernel_data[0].public_inputs.end.public_data_reads[1] = make_public_read(fr(2), fr(102)); - kernel_data[0].public_inputs.end.public_data_update_requests[0] = - make_public_data_update_request(fr(3), fr(103), fr(203)); - kernel_data[0].public_inputs.end.public_data_update_requests[1] = - make_public_data_update_request(fr(4), fr(104), fr(204)); - kernel_data[0].public_inputs.end.public_data_update_requests[2] = - make_public_data_update_request(fr(5), fr(105), fr(205)); - - kernel_data[1].public_inputs.end.public_data_reads[0] = make_public_read(fr(3), fr(203)); - kernel_data[1].public_inputs.end.public_data_reads[1] = make_public_read(fr(11), fr(211)); - kernel_data[1].public_inputs.end.public_data_update_requests[0] = - make_public_data_update_request(fr(12), fr(212), fr(312)); - kernel_data[1].public_inputs.end.public_data_update_requests[1] = - make_public_data_update_request(fr(4), fr(204), fr(304)); - - auto inputs = test_utils::utils::base_rollup_inputs_from_kernels( - kernel_data, note_hash_tree, contract_tree, public_data_tree, l1_to_l2_message_tree); - - BaseOrMergeRollupPublicInputs outputs = - aztec3::circuits::rollup::native_base_rollup::base_rollup_circuit(builder, inputs); - - ASSERT_EQ(outputs.start_public_data_tree_root, inputs.start_public_data_tree_root); - ASSERT_EQ(outputs.end_public_data_tree_root, public_data_tree.root()); - ASSERT_NE(outputs.end_public_data_tree_root, outputs.start_public_data_tree_root); - ASSERT_FALSE(builder.failed()) << builder.failure_msgs; - // TODO(1998): see above - // run_cbind(inputs, outputs); -} - -// TODO(#2521) - data read validation should happen against the current state of the tx and not the start state. -// https://aztecprotocol.slack.com/archives/C02M7VC7TN0/p1695809629015719?thread_ts=1695653252.007339&cid=C02M7VC7TN0 - -// TEST_F(base_rollup_tests, native_invalid_public_state_read) -// { -// DummyCircuitBuilder builder = DummyCircuitBuilder("base_rollup_tests__native_invalid_public_state_read"); -// MemoryStore note_hash_tree_store; -// MerkleTree note_hash_tree(note_hash_tree_store, NOTE_HASH_TREE_HEIGHT); - -// MemoryStore contract_tree_store; -// MerkleTree contract_tree(contract_tree_store, CONTRACT_TREE_HEIGHT); - -// MemoryStore public_data_tree_store; -// MerkleTree public_data_tree(public_data_tree_store, PUBLIC_DATA_TREE_HEIGHT); - -// MemoryStore l1_to_l2_message_tree_store; -// MerkleTree l1_to_l2_message_tree(l1_to_l2_message_tree_store, L1_TO_L2_MSG_TREE_HEIGHT); - -// auto data_read = abis::PublicDataRead{ -// .leaf_index = fr(1), -// .value = fr(42), -// }; - -// std::array, 2> kernel_data = { get_empty_kernel(), get_empty_kernel() }; -// kernel_data[0].public_inputs.end.public_data_reads[0] = data_read; -// auto inputs = test_utils::utils::base_rollup_inputs_from_kernels( -// kernel_data, note_hash_tree, contract_tree, public_data_tree, l1_to_l2_message_tree); - -// // We change the initial tree root so the read value does not match -// public_data_tree.update_element(1, fr(43)); -// inputs.start_public_data_tree_root = public_data_tree.root(); - -// BaseOrMergeRollupPublicInputs outputs = -// aztec3::circuits::rollup::native_base_rollup::base_rollup_circuit(builder, inputs); - -// ASSERT_EQ(outputs.start_public_data_tree_root, inputs.start_public_data_tree_root); -// ASSERT_EQ(outputs.end_public_data_tree_root, public_data_tree.root()); -// ASSERT_EQ(outputs.end_public_data_tree_root, outputs.start_public_data_tree_root); -// ASSERT_TRUE(builder.failed()); -// // TODO(1998): see above -// // run_cbind(inputs, outputs, true, false); -// } - -} // namespace aztec3::circuits::rollup::base::native_base_rollup_circuit diff --git a/circuits/cpp/src/aztec3/circuits/rollup/base/CMakeLists.txt b/circuits/cpp/src/aztec3/circuits/rollup/base/CMakeLists.txt deleted file mode 100644 index d2994796d2c..00000000000 --- a/circuits/cpp/src/aztec3/circuits/rollup/base/CMakeLists.txt +++ /dev/null @@ -1,5 +0,0 @@ -circuits_cmake_module( - aztec3_circuits_rollup - aztec3_circuits_kernel - barretenberg -) \ No newline at end of file diff --git a/circuits/cpp/src/aztec3/circuits/rollup/base/c_bind.cpp b/circuits/cpp/src/aztec3/circuits/rollup/base/c_bind.cpp deleted file mode 100644 index 0be1cd67ae3..00000000000 --- a/circuits/cpp/src/aztec3/circuits/rollup/base/c_bind.cpp +++ /dev/null @@ -1,26 +0,0 @@ -#include "c_bind.h" - -#include "index.hpp" -#include "init.hpp" - -#include "aztec3/circuits/abis/rollup/base/base_or_merge_rollup_public_inputs.hpp" -#include "aztec3/constants.hpp" -#include "aztec3/utils/dummy_circuit_builder.hpp" -#include "aztec3/utils/types/native_types.hpp" - -#include - -namespace { -using Builder = UltraCircuitBuilder; -using NT = aztec3::utils::types::NativeTypes; -using DummyCircuitBuilder = aztec3::utils::DummyCircuitBuilder; -using aztec3::circuits::abis::BaseRollupInputs; -using aztec3::circuits::rollup::native_base_rollup::base_rollup_circuit; -} // namespace - -// WASM Cbinds -CBIND(base_rollup__sim, [](BaseRollupInputs const& base_rollup_inputs) { - DummyCircuitBuilder builder = DummyCircuitBuilder("base_rollup__sim"); - auto const& public_inputs = base_rollup_circuit(builder, base_rollup_inputs); - return builder.result_or_error(public_inputs); -}); \ No newline at end of file diff --git a/circuits/cpp/src/aztec3/circuits/rollup/base/c_bind.h b/circuits/cpp/src/aztec3/circuits/rollup/base/c_bind.h deleted file mode 100644 index dc833dbda13..00000000000 --- a/circuits/cpp/src/aztec3/circuits/rollup/base/c_bind.h +++ /dev/null @@ -1,8 +0,0 @@ -#pragma once - -#include - -#include -#include - -CBIND_DECL(base_rollup__sim); \ No newline at end of file diff --git a/circuits/cpp/src/aztec3/circuits/rollup/base/index.hpp b/circuits/cpp/src/aztec3/circuits/rollup/base/index.hpp deleted file mode 100644 index 82e34f051f1..00000000000 --- a/circuits/cpp/src/aztec3/circuits/rollup/base/index.hpp +++ /dev/null @@ -1,2 +0,0 @@ -#include "init.hpp" -#include "native_base_rollup_circuit.hpp" \ No newline at end of file diff --git a/circuits/cpp/src/aztec3/circuits/rollup/base/init.hpp b/circuits/cpp/src/aztec3/circuits/rollup/base/init.hpp deleted file mode 100644 index ad75c10258b..00000000000 --- a/circuits/cpp/src/aztec3/circuits/rollup/base/init.hpp +++ /dev/null @@ -1,41 +0,0 @@ - -#pragma once - -#include "aztec3/circuits/abis/append_only_tree_snapshot.hpp" -#include "aztec3/circuits/abis/previous_kernel_data.hpp" -#include "aztec3/circuits/abis/private_circuit_public_inputs.hpp" -#include "aztec3/circuits/abis/rollup/base/base_or_merge_rollup_public_inputs.hpp" -#include "aztec3/circuits/abis/rollup/base/base_rollup_inputs.hpp" -#include "aztec3/circuits/abis/rollup/constant_rollup_data.hpp" -#include "aztec3/circuits/hash.hpp" -#include "aztec3/circuits/recursion/aggregator.hpp" -#include "aztec3/utils/circuit_errors.hpp" -#include "aztec3/utils/dummy_circuit_builder.hpp" -#include "aztec3/utils/types/circuit_types.hpp" -#include "aztec3/utils/types/convert.hpp" -#include "aztec3/utils/types/native_types.hpp" - -#include - -namespace aztec3::circuits::rollup::native_base_rollup { - -using NT = aztec3::utils::types::NativeTypes; - -// Params -using ConstantRollupData = abis::ConstantRollupData; -using BaseRollupInputs = abis::BaseRollupInputs; -using BaseOrMergeRollupPublicInputs = abis::BaseOrMergeRollupPublicInputs; -using DummyBuilder = aztec3::utils::DummyCircuitBuilder; -using CircuitErrorCode = aztec3::utils::CircuitErrorCode; - -using Aggregator = aztec3::circuits::recursion::Aggregator; -using AggregationObject = utils::types::NativeTypes::AggregationObject; -using AppendOnlySnapshot = abis::AppendOnlyTreeSnapshot; - -// Tree Aliases -using MemoryStore = stdlib::merkle_tree::MemoryStore; -using MerkleTree = stdlib::merkle_tree::MerkleTree; -using NullifierTree = stdlib::merkle_tree::NullifierMemoryTree; -using NullifierLeafPreimage = abis::NullifierLeafPreimage; - -} // namespace aztec3::circuits::rollup::native_base_rollup \ No newline at end of file diff --git a/circuits/cpp/src/aztec3/circuits/rollup/base/native_base_rollup_circuit.cpp b/circuits/cpp/src/aztec3/circuits/rollup/base/native_base_rollup_circuit.cpp deleted file mode 100644 index 47f3da86036..00000000000 --- a/circuits/cpp/src/aztec3/circuits/rollup/base/native_base_rollup_circuit.cpp +++ /dev/null @@ -1,549 +0,0 @@ -#include "init.hpp" - -#include "aztec3/circuits/abis/membership_witness.hpp" -#include "aztec3/circuits/abis/public_data_read.hpp" -#include "aztec3/circuits/abis/public_data_update_request.hpp" -#include "aztec3/circuits/abis/rollup/base/base_or_merge_rollup_public_inputs.hpp" -#include "aztec3/circuits/abis/rollup/base/base_rollup_inputs.hpp" -#include "aztec3/circuits/hash.hpp" -#include "aztec3/circuits/rollup/components/components.hpp" -#include "aztec3/constants.hpp" -#include "aztec3/utils/circuit_errors.hpp" - -#include - -#include -#include -#include -#include -#include -#include - - -namespace aztec3::circuits::rollup::native_base_rollup { - -// Used when calling library functions like `check_membership` which have their own generic error code. -// So we pad this in front of the error message to identify where the error originally came from. -const std::string BASE_CIRCUIT_ERROR_MESSAGE_BEGINNING = "base_rollup_circuit: "; - -NT::fr calculate_empty_tree_root(const size_t depth) -{ - MemoryStore empty_tree_store; - MerkleTree const empty_tree = MerkleTree(empty_tree_store, depth); - return empty_tree.root(); -} - -// TODO: can we aggregate proofs if we do not have a working circuit impl - -bool verify_kernel_proof(NT::Proof const& kernel_proof) -{ - (void)kernel_proof; - return true; -} - -/** - * @brief Create an aggregation object for the proofs that are provided - * - We add points P0 for each of our proofs - * - We add points P1 for each of our proofs - * - We concat our public inputs - * - * @param baseRollupInputs - * @return AggregationObject - */ -AggregationObject aggregate_proofs(BaseRollupInputs const& baseRollupInputs) -{ - // TODO: NOTE: for now we simply return the aggregation object from the first proof - return baseRollupInputs.kernel_data[0].public_inputs.end.aggregation_object; -} - -/** TODO: implement - * This is not being used - * @brief Get the prover contribution hash object - * - * @return NT::fr - */ -NT::fr get_prover_contribution_hash() -{ - return NT::fr(0); -} - -std::vector calculate_contract_leaves(BaseRollupInputs const& baseRollupInputs) -{ - std::vector contract_leaves; - - for (size_t i = 0; i < 2; i++) { - auto new_contacts = baseRollupInputs.kernel_data[i].public_inputs.end.new_contracts; - - // loop over the new contracts - // TODO: NOTE: we are currently assuming that there is only going to be one - for (auto& leaf_preimage : new_contacts) { - // When there is no contract deployment, we should insert a zero leaf into the tree and ignore the - // member-ship check. This is to ensure that we don't hit "already deployed" errors when we are not - // deploying contracts. e.g., when we are only calling functions on existing contracts. - auto to_push = leaf_preimage.contract_address == NT::address(0) ? NT::fr(0) : leaf_preimage.hash(); - contract_leaves.push_back(to_push); - } - } - - return contract_leaves; -} - -NT::fr calculate_contract_subtree(std::vector contract_leaves) -{ - MemoryStore contracts_tree_store; - MerkleTree contracts_tree(contracts_tree_store, CONTRACT_SUBTREE_HEIGHT); - - - // Compute the merkle root of a contract subtree - // Contracts subtree - for (size_t i = 0; i < contract_leaves.size(); i++) { - contracts_tree.update_element(i, contract_leaves[i]); - } - return contracts_tree.root(); -} - -NT::fr calculate_commitments_subtree(DummyBuilder& builder, BaseRollupInputs const& baseRollupInputs) -{ - MemoryStore commitments_tree_store; - MerkleTree commitments_tree(commitments_tree_store, NOTE_HASH_SUBTREE_HEIGHT); - - - for (size_t i = 0; i < 2; i++) { - auto new_commitments = baseRollupInputs.kernel_data[i].public_inputs.end.new_commitments; - - // Our commitments size MUST be 4 to calculate our subtrees correctly - builder.do_assert(new_commitments.size() == MAX_NEW_COMMITMENTS_PER_TX, - "New commitments in kernel data must be MAX_NEW_COMMITMENTS_PER_TX (see constants.hpp)", - CircuitErrorCode::BASE__INCORRECT_NUM_OF_NEW_COMMITMENTS); - - for (size_t j = 0; j < new_commitments.size(); j++) { - // todo: batch insert - commitments_tree.update_element(i * MAX_NEW_COMMITMENTS_PER_TX + j, new_commitments[j]); - } - } - - // Commitments subtree - return commitments_tree.root(); -} - -/** - * @brief Check all of the provided commitments against the historical tree roots - * - * @param constantBaseRollupData - * @param baseRollupInputs - */ -void perform_archive_membership_checks(DummyBuilder& builder, BaseRollupInputs const& baseRollupInputs) -{ - // For each of the historical_note_hash_tree_membership_checks, we need to do an inclusion proof - // against the historical root provided in the rollup constants - auto historical_root = baseRollupInputs.constants.start_archive_snapshot.root; - - for (size_t i = 0; i < 2; i++) { - // Rebuild the block hash - auto historical_block = baseRollupInputs.kernel_data[i].public_inputs.constants.block_header; - - auto note_hash_tree_root = historical_block.note_hash_tree_root; - auto nullifier_tree_root = historical_block.nullifier_tree_root; - auto contract_tree_root = historical_block.contract_tree_root; - auto l1_to_l2_message_tree_root = historical_block.l1_to_l2_message_tree_root; - auto public_data_tree_root = historical_block.public_data_tree_root; - - auto previous_block_hash = compute_block_hash(historical_block.global_variables_hash, - note_hash_tree_root, - nullifier_tree_root, - contract_tree_root, - l1_to_l2_message_tree_root, - public_data_tree_root); - - abis::MembershipWitness const historical_root_witness = - baseRollupInputs.archive_root_membership_witnesses[i]; - - check_membership(builder, - previous_block_hash, - historical_root_witness.leaf_index, - historical_root_witness.sibling_path, - historical_root, - format(BASE_CIRCUIT_ERROR_MESSAGE_BEGINNING, - "historical root is in rollup constants but not in blocks tree at kernel input ", - i, - " to this base rollup circuit")); - } -} - -NT::fr create_nullifier_subtree( - std::array const& nullifier_leaves) -{ - // Build a merkle tree of the nullifiers - MemoryStore nullifier_subtree_store; - MerkleTree nullifier_subtree(nullifier_subtree_store, NULLIFIER_SUBTREE_HEIGHT); - for (size_t i = 0; i < nullifier_leaves.size(); i++) { - // hash() checks if nullifier is empty (and if so returns 0) - nullifier_subtree.update_element(i, nullifier_leaves[i].hash()); - } - - return nullifier_subtree.root(); -} - -/** - * @brief Check non membership of each of the generated nullifiers in the current tree - * - * @returns The end nullifier tree root - */ -AppendOnlySnapshot check_nullifier_tree_non_membership_and_insert_to_tree(DummyBuilder& builder, - BaseRollupInputs const& baseRollupInputs) -{ - // LADIES AND GENTLEMEN The P L A N ( is simple ) - // 1. Get the previous nullifier set setup - // 2. Check for the first added nullifier that it doesnt exist - // 3. Update the nullifier set - // 4. Calculate a new root with the sibling path - // 5. Use that for the next nullifier check. - // 6. Iterate for all of em - - // BOYS AND GIRLS THE P L A N ( once the first plan is complete ) - // GENERATE OUR NEW NULLIFIER SUBTREE - // 1. We need to point the new nullifiers to point to the index that the previous nullifier replaced - // 2. If we receive the 0 nullifier leaf (where all values are 0, we skip insertion and leave a sparse subtree) - - // New nullifier subtree - std::array nullifier_insertion_subtree; - - // This will update on each iteration - auto current_nullifier_tree_root = baseRollupInputs.start_nullifier_tree_snapshot.root; - - // This will increase with every insertion - auto start_insertion_index = baseRollupInputs.start_nullifier_tree_snapshot.next_available_leaf_index; - auto new_index = start_insertion_index; - - // For each kernel circuit - for (size_t i = 0; i < 2; i++) { - auto new_nullifiers = baseRollupInputs.kernel_data[i].public_inputs.end.new_nullifiers; - // For each of our nullifiers - for (size_t j = 0; j < MAX_NEW_NULLIFIERS_PER_TX; j++) { - // Witness containing index and path - auto nullifier_index = i * MAX_NEW_NULLIFIERS_PER_TX + j; - - auto witness = baseRollupInputs.low_nullifier_membership_witness[nullifier_index]; - // Preimage of the lo-index required for a non-membership proof - auto low_nullifier_preimage = baseRollupInputs.low_nullifier_leaf_preimages[nullifier_index]; - // Newly created nullifier - auto nullifier = new_nullifiers[j]; - - // TODO(maddiaa): reason about this more strongly, can this cause issues? - if (nullifier != 0) { - // Create the nullifier leaf of the new nullifier to be inserted - NullifierLeafPreimage new_nullifier_leaf = { - .leaf_value = nullifier, - .next_value = low_nullifier_preimage.next_value, - .next_index = low_nullifier_preimage.next_index, - }; - - // Assuming populated premier subtree - if (low_nullifier_preimage.is_empty()) { - // check previous nullifier leaves - bool matched = false; - - for (size_t k = 0; k < nullifier_index && !matched; k++) { - if (nullifier_insertion_subtree[k].is_empty()) { - continue; - } - - if ((uint256_t(nullifier_insertion_subtree[k].leaf_value) < uint256_t(nullifier)) && - (uint256_t(nullifier_insertion_subtree[k].next_value) > uint256_t(nullifier) || - nullifier_insertion_subtree[k].next_value == 0)) { - matched = true; - // Update pointers - new_nullifier_leaf.next_index = nullifier_insertion_subtree[k].next_index; - new_nullifier_leaf.next_value = nullifier_insertion_subtree[k].next_value; - - // Update child - nullifier_insertion_subtree[k].next_index = new_index; - nullifier_insertion_subtree[k].next_value = nullifier; - } - } - - // if not matched, our subtree will misformed - we must reject - builder.do_assert( - matched, "Nullifier subtree is malformed", CircuitErrorCode::BASE__INVALID_NULLIFIER_SUBTREE); - - } else { - auto is_less_than_nullifier = uint256_t(low_nullifier_preimage.leaf_value) < uint256_t(nullifier); - auto is_next_greater_than = uint256_t(low_nullifier_preimage.next_value) > uint256_t(nullifier); - - if (!(is_less_than_nullifier && is_next_greater_than)) { - if (low_nullifier_preimage.next_index != 0 && low_nullifier_preimage.next_value != 0) { - builder.do_assert(false, - format("Nullifier (", - nullifier, - ") is not in the correct range. \n ", - "is_less_than_nullifier ", - is_less_than_nullifier, - "\n is_next_greater_than ", - is_next_greater_than, - "\n low_nullifier_preimage.leaf_value ", - low_nullifier_preimage.leaf_value, - "\n low_nullifier_preimage.next_index ", - low_nullifier_preimage.next_index, - "\n low_nullifier_preimage.next_value ", - low_nullifier_preimage.next_value), - CircuitErrorCode::BASE__INVALID_NULLIFIER_RANGE); - } - } - - // Recreate the original low nullifier from the preimage - auto const original_low_nullifier = NullifierLeafPreimage{ - .leaf_value = low_nullifier_preimage.leaf_value, - .next_value = low_nullifier_preimage.next_value, - .next_index = low_nullifier_preimage.next_index, - }; - - // perform membership check for the low nullifier against the original root - check_membership( - builder, - original_low_nullifier.hash(), - witness.leaf_index, - witness.sibling_path, - current_nullifier_tree_root, - format(BASE_CIRCUIT_ERROR_MESSAGE_BEGINNING, "low nullifier not in nullifier tree")); - - // Calculate the new value of the low_nullifier_leaf - auto const updated_low_nullifier = - NullifierLeafPreimage{ .leaf_value = low_nullifier_preimage.leaf_value, - .next_value = nullifier, - .next_index = new_index }; - - // We need another set of witness values for this - current_nullifier_tree_root = root_from_sibling_path( - updated_low_nullifier.hash(), witness.leaf_index, witness.sibling_path); - } - - nullifier_insertion_subtree[nullifier_index] = new_nullifier_leaf; - } else { - // 0 case - NullifierLeafPreimage const new_nullifier_leaf = { .leaf_value = 0, .next_value = 0, .next_index = 0 }; - nullifier_insertion_subtree[nullifier_index] = new_nullifier_leaf; - } - - // increment insertion index - new_index = new_index + 1; - } - } - - // Check that the new subtree is to be inserted at the next location, and is empty currently - const auto empty_nullifier_subtree_root = components::calculate_empty_tree_root(NULLIFIER_SUBTREE_HEIGHT); - auto leafIndexNullifierSubtreeDepth = - baseRollupInputs.start_nullifier_tree_snapshot.next_available_leaf_index >> NULLIFIER_SUBTREE_HEIGHT; - check_membership( - builder, - empty_nullifier_subtree_root, - leafIndexNullifierSubtreeDepth, - baseRollupInputs.new_nullifiers_subtree_sibling_path, - current_nullifier_tree_root, - format(BASE_CIRCUIT_ERROR_MESSAGE_BEGINNING, - "nullifier tree not empty at location where the new nullifier subtree would be inserted")); - - // Create new nullifier subtree to insert into the whole nullifier tree - auto nullifier_sibling_path = baseRollupInputs.new_nullifiers_subtree_sibling_path; - auto nullifier_subtree_root = create_nullifier_subtree(nullifier_insertion_subtree); - - // Calculate the new root - // We are inserting a subtree rather than a full tree here - auto subtree_index = start_insertion_index >> (NULLIFIER_SUBTREE_HEIGHT); - auto new_root = root_from_sibling_path(nullifier_subtree_root, subtree_index, nullifier_sibling_path); - - // Return the new state of the nullifier tree - return { - .root = new_root, - .next_available_leaf_index = new_index, - }; -} - -fr insert_public_data_update_requests( - DummyBuilder& builder, - fr tree_root, - std::array, MAX_PUBLIC_DATA_UPDATE_REQUESTS_PER_TX> const& - public_data_update_requests, - size_t witnesses_offset, - std::array, 2 * MAX_PUBLIC_DATA_UPDATE_REQUESTS_PER_TX> const& witnesses) -{ - auto root = tree_root; - - for (size_t i = 0; i < MAX_PUBLIC_DATA_UPDATE_REQUESTS_PER_TX; ++i) { - const auto& state_write = public_data_update_requests[i]; - const auto& witness = witnesses[i + witnesses_offset]; - - if (state_write.is_empty()) { - continue; - } - - check_membership( - builder, - state_write.old_value, - state_write.leaf_index, - witness, - root, - format(BASE_CIRCUIT_ERROR_MESSAGE_BEGINNING, "validate_public_data_update_requests index ", i)); - - root = root_from_sibling_path(state_write.new_value, state_write.leaf_index, witness); - } - - return root; -} - -void validate_public_data_reads( - DummyBuilder& builder, - fr tree_root, - std::array, MAX_PUBLIC_DATA_READS_PER_TX> const& public_data_reads, - size_t witnesses_offset, - std::array, 2 * MAX_PUBLIC_DATA_READS_PER_TX> const& witnesses) -{ - for (size_t i = 0; i < MAX_PUBLIC_DATA_READS_PER_TX; ++i) { - const auto& public_data_read = public_data_reads[i]; - const auto& witness = witnesses[i + witnesses_offset]; - - if (public_data_read.is_empty()) { - continue; - } - - check_membership( - builder, - public_data_read.value, - public_data_read.leaf_index, - witness, - tree_root, - format(BASE_CIRCUIT_ERROR_MESSAGE_BEGINNING, "validate_public_data_reads index ", i + witnesses_offset)); - } -}; - -fr validate_and_process_public_state(DummyBuilder& builder, BaseRollupInputs const& baseRollupInputs) -{ - // TODO(#2521) - data read validation should happen against the current state of the tx and not the start state. - // Blocks all interesting usecases that read and write to the same public state in the same tx. - // https://aztecprotocol.slack.com/archives/C02M7VC7TN0/p1695809629015719?thread_ts=1695653252.007339&cid=C02M7VC7TN0 - - - // Process public data reads and public data update requests for left input - // validate_public_data_reads(builder, - // baseRollupInputs.start_public_data_tree_root, - // baseRollupInputs.kernel_data[0].public_inputs.end.public_data_reads, - // 0, - // baseRollupInputs.new_public_data_reads_sibling_paths); - - auto mid_public_data_tree_root = insert_public_data_update_requests( - builder, - baseRollupInputs.start_public_data_tree_root, - baseRollupInputs.kernel_data[0].public_inputs.end.public_data_update_requests, - 0, - baseRollupInputs.new_public_data_update_requests_sibling_paths); - - - // TODO(#2521) - data read validation should happen against the current state of the tx and not the start state. - // Blocks all interesting usecases that read and write to the same public state in the same tx. - // https://aztecprotocol.slack.com/archives/C02M7VC7TN0/p1695809629015719?thread_ts=1695653252.007339&cid=C02M7VC7TN0 - - - // Process public data reads and public data update requests for right input using the resulting tree root from the - // left one - // validate_public_data_reads(builder, - // mid_public_data_tree_root, - // baseRollupInputs.kernel_data[1].public_inputs.end.public_data_reads, - // MAX_PUBLIC_DATA_READS_PER_TX, - // baseRollupInputs.new_public_data_reads_sibling_paths); - - auto end_public_data_tree_root = insert_public_data_update_requests( - builder, - mid_public_data_tree_root, - baseRollupInputs.kernel_data[1].public_inputs.end.public_data_update_requests, - MAX_PUBLIC_DATA_UPDATE_REQUESTS_PER_TX, - baseRollupInputs.new_public_data_update_requests_sibling_paths); - - return end_public_data_tree_root; -} - -BaseOrMergeRollupPublicInputs base_rollup_circuit(DummyBuilder& builder, BaseRollupInputs const& baseRollupInputs) -{ - // Verify the previous kernel proofs - for (size_t i = 0; i < 2; i++) { - NT::Proof const proof = baseRollupInputs.kernel_data[i].proof; - builder.do_assert(verify_kernel_proof(proof), - "kernel proof verification failed", - CircuitErrorCode::BASE__KERNEL_PROOF_VERIFICATION_FAILED); - } - - // Verify the kernel chain_id and versions - for (size_t i = 0; i < 2; i++) { - builder.do_assert(baseRollupInputs.kernel_data[i].public_inputs.constants.tx_context.chain_id == - baseRollupInputs.constants.global_variables.chain_id, - "kernel chain_id does not match the rollup chain_id", - CircuitErrorCode::BASE__INVALID_CHAIN_ID); - builder.do_assert(baseRollupInputs.kernel_data[i].public_inputs.constants.tx_context.version == - baseRollupInputs.constants.global_variables.version, - "kernel version does not match the rollup version", - CircuitErrorCode::BASE__INVALID_VERSION); - } - - // First we compute the contract tree leaves - std::vector const contract_leaves = calculate_contract_leaves(baseRollupInputs); - - // Check contracts and commitments subtrees - NT::fr const contracts_tree_subroot = calculate_contract_subtree(contract_leaves); - NT::fr const commitments_tree_subroot = calculate_commitments_subtree(builder, baseRollupInputs); - - // Insert commitment subtrees: - const auto empty_commitments_subtree_root = components::calculate_empty_tree_root(NOTE_HASH_SUBTREE_HEIGHT); - auto end_note_hash_tree_snapshot = components::insert_subtree_to_snapshot_tree( - builder, - baseRollupInputs.start_note_hash_tree_snapshot, - baseRollupInputs.new_commitments_subtree_sibling_path, - empty_commitments_subtree_root, - commitments_tree_subroot, - NOTE_HASH_SUBTREE_HEIGHT, - format(BASE_CIRCUIT_ERROR_MESSAGE_BEGINNING, - "note hash tree not empty at location where the new commitment subtree would be inserted")); - // Insert contract subtrees: - const auto empty_contracts_subtree_root = components::calculate_empty_tree_root(CONTRACT_SUBTREE_HEIGHT); - auto end_contract_tree_snapshot = components::insert_subtree_to_snapshot_tree( - builder, - baseRollupInputs.start_contract_tree_snapshot, - baseRollupInputs.new_contracts_subtree_sibling_path, - empty_contracts_subtree_root, - contracts_tree_subroot, - CONTRACT_SUBTREE_HEIGHT, - format(BASE_CIRCUIT_ERROR_MESSAGE_BEGINNING, - "contract tree not empty at location where the new contract subtree would be inserted")); - - // Insert nullifiers: - AppendOnlySnapshot const end_nullifier_tree_snapshot = - check_nullifier_tree_non_membership_and_insert_to_tree(builder, baseRollupInputs); - - // Validate public public data reads and public data update requests, and update public data tree - fr const end_public_data_tree_root = validate_and_process_public_state(builder, baseRollupInputs); - - // Calculate the overall calldata hash - std::array const calldata_hash = - components::compute_kernels_calldata_hash(baseRollupInputs.kernel_data); - - // Perform membership checks that the notes provided exist within the historical trees data - perform_archive_membership_checks(builder, baseRollupInputs); - - AggregationObject const aggregation_object = aggregate_proofs(baseRollupInputs); - - BaseOrMergeRollupPublicInputs public_inputs = { - .rollup_type = abis::BASE_ROLLUP_TYPE, - .rollup_subtree_height = fr(0), - .end_aggregation_object = aggregation_object, - .constants = baseRollupInputs.constants, - .start_note_hash_tree_snapshot = baseRollupInputs.start_note_hash_tree_snapshot, - .end_note_hash_tree_snapshot = end_note_hash_tree_snapshot, - .start_nullifier_tree_snapshot = baseRollupInputs.start_nullifier_tree_snapshot, - .end_nullifier_tree_snapshot = end_nullifier_tree_snapshot, - .start_contract_tree_snapshot = baseRollupInputs.start_contract_tree_snapshot, - .end_contract_tree_snapshot = end_contract_tree_snapshot, - .start_public_data_tree_root = baseRollupInputs.start_public_data_tree_root, - .end_public_data_tree_root = end_public_data_tree_root, - .calldata_hash = calldata_hash, - }; - return public_inputs; -} - -} // namespace aztec3::circuits::rollup::native_base_rollup diff --git a/circuits/cpp/src/aztec3/circuits/rollup/base/native_base_rollup_circuit.hpp b/circuits/cpp/src/aztec3/circuits/rollup/base/native_base_rollup_circuit.hpp deleted file mode 100644 index 9ea9dbb9a18..00000000000 --- a/circuits/cpp/src/aztec3/circuits/rollup/base/native_base_rollup_circuit.hpp +++ /dev/null @@ -1,17 +0,0 @@ -#pragma once - -#include "init.hpp" - -// TODO: not needed right at this moment for native impl -#include "aztec3/circuits/abis/rollup/base/base_or_merge_rollup_public_inputs.hpp" -#include "aztec3/circuits/abis/rollup/base/base_rollup_inputs.hpp" -#include "aztec3/circuits/abis/rollup/constant_rollup_data.hpp" -#include "aztec3/utils/types/circuit_types.hpp" -#include "aztec3/utils/types/convert.hpp" -#include "aztec3/utils/types/native_types.hpp" - -namespace aztec3::circuits::rollup::native_base_rollup { - -BaseOrMergeRollupPublicInputs base_rollup_circuit(DummyBuilder& builder, BaseRollupInputs const& baseRollupInputs); - -} // namespace aztec3::circuits::rollup::native_base_rollup \ No newline at end of file diff --git a/circuits/cpp/src/aztec3/circuits/rollup/components/components.cpp b/circuits/cpp/src/aztec3/circuits/rollup/components/components.cpp deleted file mode 100644 index fe3b1463910..00000000000 --- a/circuits/cpp/src/aztec3/circuits/rollup/components/components.cpp +++ /dev/null @@ -1,277 +0,0 @@ -#include "init.hpp" - -#include "aztec3/circuits/abis/rollup/base/base_or_merge_rollup_public_inputs.hpp" -#include "aztec3/constants.hpp" -#include "aztec3/utils/circuit_errors.hpp" - -#include - -#include -#include -#include -#include -#include -#include -#include - -namespace aztec3::circuits::rollup::components { - -/** - * @brief Get the root of an empty tree of a given depth - * - * @param depth - * @return NT::fr - */ -NT::fr calculate_empty_tree_root(const size_t depth) -{ - MemoryStore empty_tree_store; - MerkleTree const empty_tree = MerkleTree(empty_tree_store, depth); - return empty_tree.root(); -} - -/** - * @brief Create an aggregation object for the proofs that are provided - * - We add points P0 for each of our proofs - * - We add points P1 for each of our proofs - * - We concat our public inputs - * - * @param mergeRollupInputs - * @return AggregationObject - */ -AggregationObject aggregate_proofs(BaseOrMergeRollupPublicInputs const& left, - BaseOrMergeRollupPublicInputs const& right) -{ - // TODO: NOTE: for now we simply return the aggregation object from the first proof - (void)right; - return left.end_aggregation_object; -} - -/** - * @brief Asserts that the rollup types are the same - * - * @param left - The public inputs of the left rollup (base or merge) - * @param right - The public inputs of the right rollup (base or merge) - */ -void assert_both_input_proofs_of_same_rollup_type(DummyBuilder& builder, - BaseOrMergeRollupPublicInputs const& left, - BaseOrMergeRollupPublicInputs const& right) -{ - builder.do_assert(left.rollup_type == right.rollup_type, - "input proofs are of different rollup types", - utils::CircuitErrorCode::ROLLUP_TYPE_MISMATCH); -} - -/** - * @brief Asserts that the rollup subtree heights are the same and returns the height - * - * @param left - The public inputs of the left rollup (base or merge) - * @param right - The public inputs of the right rollup (base or merge) - * @return NT::fr - The height of the rollup subtrees - */ -NT::fr assert_both_input_proofs_of_same_height_and_return(DummyBuilder& builder, - BaseOrMergeRollupPublicInputs const& left, - BaseOrMergeRollupPublicInputs const& right) -{ - builder.do_assert(left.rollup_subtree_height == right.rollup_subtree_height, - "input proofs are of different rollup heights", - utils::CircuitErrorCode::ROLLUP_HEIGHT_MISMATCH); - return left.rollup_subtree_height; -} - -/** - * @brief Asserts that the constants used in the left and right child are identical - * - * @param left - The public inputs of the left rollup (base or merge) - * @param right - The public inputs of the right rollup (base or merge) - */ -void assert_equal_constants(DummyBuilder& builder, - BaseOrMergeRollupPublicInputs const& left, - BaseOrMergeRollupPublicInputs const& right) -{ - builder.do_assert(left.constants == right.constants, - "input proofs have different constants", - utils::CircuitErrorCode::CONSTANTS_MISMATCH); -} - -/** - * @brief Computes the calldata hash for a base rollup - * - * @param kernel_data - 2 kernels - * @return calldata hash stored in 2 fields - */ -std::array compute_kernels_calldata_hash( - std::array, 2> kernel_data) -{ - // Compute calldata hashes - // Consist of 2 kernels - // 2 * MAX_NEW_COMMITMENTS_PER_TX fields for commitments - // 2 * MAX_NEW_NULLIFIERS_PER_TX fields for nullifiers - // 8 public data update requests (4 per kernel) -> 16 fields - // 4 l2 -> l1 messages (2 per kernel) -> 4 fields - // 2 contract deployments (1 per kernel) -> 6 fields - // 2 encrypted logs hashes (1 per kernel) -> 4 fields --> 2 sha256 hashes --> 64 bytes - // 2 unencrypted logs hashes (1 per kernel) -> 4 fields --> 2 sha256 hashes --> 64 bytes - auto const number_of_inputs = - (MAX_NEW_COMMITMENTS_PER_TX + MAX_NEW_NULLIFIERS_PER_TX + MAX_PUBLIC_DATA_UPDATE_REQUESTS_PER_TX * 2 + - MAX_NEW_L2_TO_L1_MSGS_PER_TX + MAX_NEW_CONTRACTS_PER_TX * 3 + - NUM_ENCRYPTED_LOGS_HASHES_PER_TX * NUM_FIELDS_PER_SHA256 + - NUM_UNENCRYPTED_LOGS_HASHES_PER_TX * NUM_FIELDS_PER_SHA256) * - 2; - std::array calldata_hash_inputs{}; - - for (size_t i = 0; i < 2; i++) { - auto new_commitments = kernel_data[i].public_inputs.end.new_commitments; - auto new_nullifiers = kernel_data[i].public_inputs.end.new_nullifiers; - auto public_data_update_requests = kernel_data[i].public_inputs.end.public_data_update_requests; - auto newL2ToL1msgs = kernel_data[i].public_inputs.end.new_l2_to_l1_msgs; - auto encryptedLogsHash = kernel_data[i].public_inputs.end.encrypted_logs_hash; - auto unencryptedLogsHash = kernel_data[i].public_inputs.end.unencrypted_logs_hash; - - size_t offset = 0; - - for (size_t j = 0; j < MAX_NEW_COMMITMENTS_PER_TX; j++) { - calldata_hash_inputs[offset + i * MAX_NEW_COMMITMENTS_PER_TX + j] = new_commitments[j]; - } - offset += MAX_NEW_COMMITMENTS_PER_TX * 2; - - for (size_t j = 0; j < MAX_NEW_NULLIFIERS_PER_TX; j++) { - calldata_hash_inputs[offset + i * MAX_NEW_NULLIFIERS_PER_TX + j] = new_nullifiers[j]; - } - offset += MAX_NEW_NULLIFIERS_PER_TX * 2; - - for (size_t j = 0; j < MAX_PUBLIC_DATA_UPDATE_REQUESTS_PER_TX; j++) { - calldata_hash_inputs[offset + i * MAX_PUBLIC_DATA_UPDATE_REQUESTS_PER_TX * 2 + j * 2] = - public_data_update_requests[j].leaf_index; - calldata_hash_inputs[offset + i * MAX_PUBLIC_DATA_UPDATE_REQUESTS_PER_TX * 2 + j * 2 + 1] = - public_data_update_requests[j].new_value; - } - offset += MAX_PUBLIC_DATA_UPDATE_REQUESTS_PER_TX * 2 * 2; - - for (size_t j = 0; j < MAX_NEW_L2_TO_L1_MSGS_PER_TX; j++) { - calldata_hash_inputs[offset + i * MAX_NEW_L2_TO_L1_MSGS_PER_TX + j] = newL2ToL1msgs[j]; - } - offset += MAX_NEW_L2_TO_L1_MSGS_PER_TX * 2; - - auto const contract_leaf = kernel_data[i].public_inputs.end.new_contracts[0]; - calldata_hash_inputs[offset + i] = contract_leaf.hash(); - - offset += MAX_NEW_CONTRACTS_PER_TX * 2; - - auto new_contracts = kernel_data[i].public_inputs.end.new_contracts; - calldata_hash_inputs[offset + i * 2] = new_contracts[0].contract_address; - calldata_hash_inputs[offset + i * 2 + 1] = new_contracts[0].portal_contract_address; - - offset += MAX_NEW_CONTRACTS_PER_TX * 2 * 2; - - for (size_t j = 0; j < NUM_FIELDS_PER_SHA256; j++) { - calldata_hash_inputs[offset + i * 2 + j] = encryptedLogsHash[j]; - } - - offset += NUM_ENCRYPTED_LOGS_HASHES_PER_TX * NUM_FIELDS_PER_SHA256 * 2; - - for (size_t j = 0; j < NUM_FIELDS_PER_SHA256; j++) { - calldata_hash_inputs[offset + i * 2 + j] = unencryptedLogsHash[j]; - } - } - - // OPTIMIZE DATA OVERHEAD - // The data structure calldata_hash_inputs contains 2 * 2 * NUM_FIELDS_PER_SHA256 fr entries for logsHashes. (2 - // kernels and 2 types of log per kernel). One sha-256 hash value represented in fr array is actually splitted into - // 2 fields because fr is 254 bits (smaller than 256 bits). By serializing such a (fr-based) hash value back into - // bytes, we only need 32 bytes (256 bits), while the standard fr-array to bytes serialization would return 64 - // bytes. For the unencryptedlogs and encryptedlogs, we therefore need 2 * NUM_FIELDS_PER_SHA256 * 32 bytes instead - // of 2 * 2 * NUM_FIELDS_PER_SHA256 * 32 bytes (half of this is saved). - - // We subtract 2 * NUM_FIELDS_PER_SHA256 * 32 bytes as explained above. - constexpr auto num_bytes = (calldata_hash_inputs.size() - NUM_FIELDS_PER_SHA256 * 2) * 32; - std::array calldata_hash_inputs_bytes{}; - - // Serialize everything from calldata_hash_inputs except the logs at this stage and copy into - // calldata_hash_inputs_bytes; - for (size_t i = 0; i < calldata_hash_inputs.size() - NUM_FIELDS_PER_SHA256 * 2 * 2; i++) { - auto as_bytes = calldata_hash_inputs[i].to_buffer(); - auto offset = i * 32; - std::copy(as_bytes.begin(), as_bytes.end(), calldata_hash_inputs_bytes.begin() + offset); - } - - // Copy the 4 fields of 2 encrypted logs to 64 bytes - // Modified version of: - // https://github.com/AztecProtocol/aztec-packages/blob/01080c7f1d2956512b6a9cff0582b43be25b3cc2/circuits/cpp/src/aztec3/circuits/hash.hpp#L350 - const uint32_t encrypted_logs_start_index = calldata_hash_inputs.size() - NUM_FIELDS_PER_SHA256 * 2 * 2; - const uint32_t first_modified_byte_encrypted = - num_bytes - NUM_FIELDS_PER_SHA256 * 2 * - 32; // offsetting by number of bytes occupied by all the logs hashes (in the optimized form) - for (uint8_t i = 0; i < 4; i++) { - auto half = calldata_hash_inputs[encrypted_logs_start_index + i].to_buffer(); - for (uint8_t j = 0; j < 16; j++) { - calldata_hash_inputs_bytes[first_modified_byte_encrypted + i * 16 + j] = half[16 + j]; - } - } - - // Do the same for the unencrypted logs - const uint32_t unencrypted_logs_start_index = calldata_hash_inputs.size() - NUM_FIELDS_PER_SHA256 * 2; - const uint32_t first_modified_byte_unencrypted = - num_bytes - NUM_FIELDS_PER_SHA256 * 32; // offsetting num bytes occupied by unencrypted logs hashes - for (uint8_t i = 0; i < 4; i++) { - auto half = calldata_hash_inputs[unencrypted_logs_start_index + i].to_buffer(); - for (uint8_t j = 0; j < 16; j++) { - calldata_hash_inputs_bytes[first_modified_byte_unencrypted + i * 16 + j] = half[16 + j]; - } - } - - std::vector const calldata_hash_inputs_bytes_vec(calldata_hash_inputs_bytes.begin(), - calldata_hash_inputs_bytes.end()); - - auto h = sha256::sha256(calldata_hash_inputs_bytes_vec); - - // Split the hash into two fields, a high and a low - std::array buf_1; - std::array buf_2; - for (uint8_t i = 0; i < 16; i++) { - buf_1[i] = 0; - buf_1[16 + i] = h[i]; - buf_2[i] = 0; - buf_2[16 + i] = h[i + 16]; - } - auto high = fr::serialize_from_buffer(buf_1.data()); - auto low = fr::serialize_from_buffer(buf_2.data()); - - return std::array{ high, low }; -} - -/** - * @brief From two previous rollup data, compute a single calldata hash - * - * @param previous_rollup_data - * @return calldata hash stored in 2 fields - */ -std::array compute_calldata_hash( - std::array, 2> previous_rollup_data) -{ - return accumulate_sha256({ previous_rollup_data[0].base_or_merge_rollup_public_inputs.calldata_hash[0], - previous_rollup_data[0].base_or_merge_rollup_public_inputs.calldata_hash[1], - previous_rollup_data[1].base_or_merge_rollup_public_inputs.calldata_hash[0], - previous_rollup_data[1].base_or_merge_rollup_public_inputs.calldata_hash[1] }); -} - -// asserts that the end snapshot of previous_rollup 0 equals the start snapshot of previous_rollup 1 (i.e. ensure they -// follow on from one-another). Ensures that right uses the tres that was updated by left. -void assert_prev_rollups_follow_on_from_each_other(DummyBuilder& builder, - BaseOrMergeRollupPublicInputs const& left, - BaseOrMergeRollupPublicInputs const& right) -{ - builder.do_assert(left.end_note_hash_tree_snapshot == right.start_note_hash_tree_snapshot, - "input proofs have different note hash tree snapshots", - utils::CircuitErrorCode::NOTE_HASH_TREE_SNAPSHOT_MISMATCH); - builder.do_assert(left.end_nullifier_tree_snapshot == right.start_nullifier_tree_snapshot, - "input proofs have different nullifier tree snapshots", - utils::CircuitErrorCode::NULLIFIER_TREE_SNAPSHOT_MISMATCH); - builder.do_assert(left.end_contract_tree_snapshot == right.start_contract_tree_snapshot, - "input proofs have different contract tree snapshots", - utils::CircuitErrorCode::CONTRACT_TREE_SNAPSHOT_MISMATCH); - builder.do_assert(left.end_public_data_tree_root == right.start_public_data_tree_root, - "input proofs have different public data tree snapshots", - utils::CircuitErrorCode::CONTRACT_TREE_SNAPSHOT_MISMATCH); -} - -} // namespace aztec3::circuits::rollup::components \ No newline at end of file diff --git a/circuits/cpp/src/aztec3/circuits/rollup/components/components.hpp b/circuits/cpp/src/aztec3/circuits/rollup/components/components.hpp deleted file mode 100644 index 2e64593dc65..00000000000 --- a/circuits/cpp/src/aztec3/circuits/rollup/components/components.hpp +++ /dev/null @@ -1,58 +0,0 @@ -#pragma once - -#include "init.hpp" - -#include "aztec3/utils/circuit_errors.hpp" - -using aztec3::circuits::check_membership; -using aztec3::circuits::root_from_sibling_path; - -namespace aztec3::circuits::rollup::components { -NT::fr calculate_empty_tree_root(size_t depth); -std::array compute_kernels_calldata_hash( - std::array, 2> kernel_data); -std::array compute_calldata_hash( - std::array, 2> previous_rollup_data); -void assert_prev_rollups_follow_on_from_each_other(DummyBuilder& builder, - BaseOrMergeRollupPublicInputs const& left, - BaseOrMergeRollupPublicInputs const& right); -void assert_both_input_proofs_of_same_rollup_type(DummyBuilder& builder, - BaseOrMergeRollupPublicInputs const& left, - BaseOrMergeRollupPublicInputs const& right); -NT::fr assert_both_input_proofs_of_same_height_and_return(DummyBuilder& builder, - BaseOrMergeRollupPublicInputs const& left, - BaseOrMergeRollupPublicInputs const& right); -void assert_equal_constants(DummyBuilder& builder, - BaseOrMergeRollupPublicInputs const& left, - BaseOrMergeRollupPublicInputs const& right); - -AggregationObject aggregate_proofs(BaseOrMergeRollupPublicInputs const& left, - BaseOrMergeRollupPublicInputs const& right); - -template AppendOnlySnapshot insert_subtree_to_snapshot_tree(DummyBuilder& builder, - AppendOnlySnapshot snapshot, - std::array siblingPath, - NT::fr emptySubtreeRoot, - NT::fr subtreeRootToInsert, - uint8_t subtreeDepth, - std::string const& emptySubtreeCheckErrorMessage) -{ - // TODO: Sanity check len of siblingPath > height of subtree - // TODO: Ensure height of subtree is correct (eg 3 for commitments, 1 for contracts) - auto leafIndexAtDepth = snapshot.next_available_leaf_index >> subtreeDepth; - - // Check that the current root is correct and that there is an empty subtree at the insertion location - check_membership( - builder, emptySubtreeRoot, leafIndexAtDepth, siblingPath, snapshot.root, emptySubtreeCheckErrorMessage); - - // if index of leaf is x, index of its parent is x/2 or x >> 1. We need to find the parent `subtreeDepth` levels up. - auto new_root = root_from_sibling_path(subtreeRootToInsert, leafIndexAtDepth, siblingPath); - - // 2^subtreeDepth is the number of leaves added. 2^x = 1 << x - auto new_next_available_leaf_index = snapshot.next_available_leaf_index + (static_cast(1) << subtreeDepth); - - AppendOnlySnapshot newTreeSnapshot = { .root = new_root, - .next_available_leaf_index = new_next_available_leaf_index }; - return newTreeSnapshot; -} -} // namespace aztec3::circuits::rollup::components \ No newline at end of file diff --git a/circuits/cpp/src/aztec3/circuits/rollup/components/init.hpp b/circuits/cpp/src/aztec3/circuits/rollup/components/init.hpp deleted file mode 100644 index 895cdee4c41..00000000000 --- a/circuits/cpp/src/aztec3/circuits/rollup/components/init.hpp +++ /dev/null @@ -1,30 +0,0 @@ - -#pragma once - -#include "aztec3/circuits/abis/append_only_tree_snapshot.hpp" -#include "aztec3/circuits/abis/previous_kernel_data.hpp" -#include "aztec3/circuits/abis/private_circuit_public_inputs.hpp" -#include "aztec3/circuits/abis/rollup/base/base_or_merge_rollup_public_inputs.hpp" -#include "aztec3/circuits/abis/rollup/constant_rollup_data.hpp" -#include "aztec3/circuits/abis/rollup/merge/merge_rollup_inputs.hpp" -#include "aztec3/circuits/hash.hpp" -#include "aztec3/circuits/recursion/aggregator.hpp" -#include "aztec3/utils/dummy_circuit_builder.hpp" -#include "aztec3/utils/types/circuit_types.hpp" -#include "aztec3/utils/types/convert.hpp" -#include "aztec3/utils/types/native_types.hpp" - -#include - -namespace aztec3::circuits::rollup::components { - -using NT = aztec3::utils::types::NativeTypes; - -// Params -using NT = aztec3::utils::types::NativeTypes; -using AggregationObject = aztec3::utils::types::NativeTypes::AggregationObject; -using BaseOrMergeRollupPublicInputs = aztec3::circuits::abis::BaseOrMergeRollupPublicInputs; -using AppendOnlySnapshot = abis::AppendOnlyTreeSnapshot; -using DummyBuilder = aztec3::utils::DummyCircuitBuilder; - -} // namespace aztec3::circuits::rollup::components \ No newline at end of file diff --git a/circuits/cpp/src/aztec3/circuits/rollup/merge/.test.cpp b/circuits/cpp/src/aztec3/circuits/rollup/merge/.test.cpp deleted file mode 100644 index 85d436a8319..00000000000 --- a/circuits/cpp/src/aztec3/circuits/rollup/merge/.test.cpp +++ /dev/null @@ -1,309 +0,0 @@ -#include "c_bind.h" -#include "index.hpp" -#include "init.hpp" - -#include "aztec3/circuits/rollup/merge/init.hpp" -#include "aztec3/circuits/rollup/test_utils/utils.hpp" - -#include - -#include - -namespace { -using aztec3::circuits::rollup::merge::MergeRollupInputs; -using DummyCircuitBuilder = aztec3::utils::DummyCircuitBuilder; - -using aztec3::circuits::rollup::test_utils::utils::compare_field_hash_to_expected; -using aztec3::circuits::rollup::test_utils::utils::get_empty_kernel; -using aztec3::circuits::rollup::test_utils::utils::get_merge_rollup_inputs; - -using NT = aztec3::utils::types::NativeTypes; - -using KernelData = aztec3::circuits::abis::PreviousKernelData; - -} // namespace -namespace aztec3::circuits::rollup::merge::native_merge_rollup_circuit { - -class merge_rollup_tests : public ::testing::Test { - protected: - static void SetUpTestSuite() { barretenberg::srs::init_crs_factory("../barretenberg/cpp/srs_db/ignition"); } - - // TODO(1998): uncomment once https://github.com/AztecProtocol/aztec-packages/issues/1998 is solved and - // use new pattern such as call_func_and_wrapper from test_helper.hpp - - // static void run_cbind(MergeRollupInputs& merge_rollup_inputs, - // BaseOrMergeRollupPublicInputs& expected_public_inputs, - // bool compare_pubins = true) - // { - // info("Retesting via cbinds...."); - // std::vector merge_rollup_inputs_vec; - // serialize::write(merge_rollup_inputs_vec, merge_rollup_inputs); - - // uint8_t const* public_inputs_buf = nullptr; - // // info("simulating circuit via cbind"); - // size_t public_inputs_size = 0; - // info("creating proof"); - // auto* circuit_failure_ptr = - // merge_rollup__sim(merge_rollup_inputs_vec.data(), &public_inputs_size, &public_inputs_buf); - // ASSERT_TRUE(circuit_failure_ptr == nullptr); - // // info("PublicInputs size: ", public_inputs_size); - - // if (compare_pubins) { - // BaseOrMergeRollupPublicInputs public_inputs; - // uint8_t const* public_inputs_buf_tmp = public_inputs_buf; - // serialize::read(public_inputs_buf_tmp, public_inputs); - // ASSERT_EQ(public_inputs.calldata_hash.size(), expected_public_inputs.calldata_hash.size()); - // for (size_t i = 0; i < public_inputs.calldata_hash.size(); i++) { - // ASSERT_EQ(public_inputs.calldata_hash[i], expected_public_inputs.calldata_hash[i]); - // } - - // std::vector expected_public_inputs_vec; - // serialize::write(expected_public_inputs_vec, expected_public_inputs); - - // ASSERT_EQ(public_inputs_size, expected_public_inputs_vec.size()); - // // Just compare the first 10 bytes of the serialized public outputs - // if (public_inputs_size > 10) { - // // for (size_t 0; i < public_inputs_size; i++) { - // for (size_t i = 0; i < 10; i++) { - // ASSERT_EQ(public_inputs_buf[i], expected_public_inputs_vec[i]); - // } - // } - // } - // free((void*)public_inputs_buf); - // } -}; - -TEST_F(merge_rollup_tests, native_different_rollup_type_fails) -{ - DummyBuilder builder = DummyBuilder("merge_rollup_tests__native_different_rollup_type_fails"); - std::array const kernels = { - get_empty_kernel(), get_empty_kernel(), get_empty_kernel(), get_empty_kernel() - }; - MergeRollupInputs mergeInput = get_merge_rollup_inputs(builder, kernels); - mergeInput.previous_rollup_data[0].base_or_merge_rollup_public_inputs.rollup_type = 0; - mergeInput.previous_rollup_data[1].base_or_merge_rollup_public_inputs.rollup_type = 1; - merge_rollup_circuit(builder, mergeInput); - ASSERT_TRUE(builder.failed()); - ASSERT_EQ(builder.get_first_failure().message, "input proofs are of different rollup types"); -} - -TEST_F(merge_rollup_tests, native_different_rollup_height_fails) -{ - DummyBuilder builder = DummyBuilder("merge_rollup_tests__native_different_rollup_height_fails"); - std::array const kernels = { - get_empty_kernel(), get_empty_kernel(), get_empty_kernel(), get_empty_kernel() - }; - MergeRollupInputs mergeInput = get_merge_rollup_inputs(builder, kernels); - mergeInput.previous_rollup_data[0].base_or_merge_rollup_public_inputs.rollup_subtree_height = 0; - mergeInput.previous_rollup_data[1].base_or_merge_rollup_public_inputs.rollup_subtree_height = 1; - merge_rollup_circuit(builder, mergeInput); - ASSERT_TRUE(builder.failed()); - ASSERT_EQ(builder.get_first_failure().message, "input proofs are of different rollup heights"); -} - -TEST_F(merge_rollup_tests, native_constants_different_failure) -{ - DummyBuilder builder = DummyBuilder("merge_rollup_tests__native_constants_different_failure"); - std::array const kernels = { - get_empty_kernel(), get_empty_kernel(), get_empty_kernel(), get_empty_kernel() - }; - MergeRollupInputs inputs = get_merge_rollup_inputs(builder, kernels); - inputs.previous_rollup_data[0].base_or_merge_rollup_public_inputs.constants.public_kernel_vk_tree_root = fr(1); - inputs.previous_rollup_data[1].base_or_merge_rollup_public_inputs.constants.public_kernel_vk_tree_root = fr(0); - merge_rollup_circuit(builder, inputs); - ASSERT_TRUE(builder.failed()); - ASSERT_EQ(builder.get_first_failure().message, "input proofs have different constants"); -} - -TEST_F(merge_rollup_tests, native_constants_different_chain_id_failure) -{ - DummyBuilder builder = DummyBuilder("merge_rollup_tests__native_constants_different_failure"); - std::array const kernels = { - get_empty_kernel(), get_empty_kernel(), get_empty_kernel(), get_empty_kernel() - }; - MergeRollupInputs inputs = get_merge_rollup_inputs(builder, kernels); - inputs.previous_rollup_data[0].base_or_merge_rollup_public_inputs.constants.global_variables.chain_id = fr(1); - inputs.previous_rollup_data[1].base_or_merge_rollup_public_inputs.constants.global_variables.chain_id = fr(0); - merge_rollup_circuit(builder, inputs); - ASSERT_TRUE(builder.failed()); - ASSERT_EQ(builder.get_first_failure().message, "input proofs have different constants"); -} - -TEST_F(merge_rollup_tests, native_fail_if_previous_rollups_dont_follow_on) -{ - DummyBuilder builderA = DummyBuilder("merge_rollup_tests__native_fail_if_previous_rollups_dont_follow_on_A"); - std::array const kernels = { - get_empty_kernel(), get_empty_kernel(), get_empty_kernel(), get_empty_kernel() - }; - MergeRollupInputs const inputs = get_merge_rollup_inputs(builderA, kernels); - auto inputA = inputs; - inputA.previous_rollup_data[0].base_or_merge_rollup_public_inputs.end_note_hash_tree_snapshot = { - .root = fr(0), .next_available_leaf_index = 0 - }; - inputA.previous_rollup_data[1].base_or_merge_rollup_public_inputs.start_note_hash_tree_snapshot = { - .root = fr(1), .next_available_leaf_index = 0 - }; - - merge_rollup_circuit(builderA, inputA); - ASSERT_TRUE(builderA.failed()); - ASSERT_EQ(builderA.get_first_failure().message, "input proofs have different note hash tree snapshots"); - - // do the same for nullifier tree - DummyBuilder builderB = DummyBuilder("merge_rollup_tests__native_fail_if_previous_rollups_dont_follow_on_B"); - auto inputB = inputs; - - inputB.previous_rollup_data[0].base_or_merge_rollup_public_inputs.end_nullifier_tree_snapshot = { - .root = fr(0), .next_available_leaf_index = 0 - }; - inputB.previous_rollup_data[1].base_or_merge_rollup_public_inputs.start_nullifier_tree_snapshot = { - .root = fr(1), .next_available_leaf_index = 0 - }; - merge_rollup_circuit(builderB, inputB); - ASSERT_TRUE(builderB.failed()); - ASSERT_EQ(builderB.get_first_failure().message, "input proofs have different nullifier tree snapshots"); - - // do the same for contract tree - DummyBuilder builderC = DummyBuilder("merge_rollup_tests__native_fail_if_previous_rollups_dont_follow_on_C"); - auto inputC = inputs; - inputC.previous_rollup_data[0].base_or_merge_rollup_public_inputs.end_contract_tree_snapshot = { - .root = fr(0), .next_available_leaf_index = 0 - }; - inputC.previous_rollup_data[1].base_or_merge_rollup_public_inputs.start_contract_tree_snapshot = { - .root = fr(1), .next_available_leaf_index = 0 - }; - merge_rollup_circuit(builderC, inputC); - ASSERT_TRUE(builderC.failed()); - ASSERT_EQ(builderC.get_first_failure().message, "input proofs have different contract tree snapshots"); -} - -TEST_F(merge_rollup_tests, native_rollup_fields_are_set_correctly) -{ - DummyBuilder builder = DummyBuilder("merge_rollup_tests__native_rollup_fields_are_set_correctly"); - std::array const kernels = { - get_empty_kernel(), get_empty_kernel(), get_empty_kernel(), get_empty_kernel() - }; - MergeRollupInputs inputs = get_merge_rollup_inputs(builder, kernels); - BaseOrMergeRollupPublicInputs outputs = merge_rollup_circuit(builder, inputs); - // check that rollup type is set to merge - ASSERT_EQ(outputs.rollup_type, 1); - // check that rollup height is incremented - ASSERT_EQ(outputs.rollup_subtree_height, - inputs.previous_rollup_data[0].base_or_merge_rollup_public_inputs.rollup_subtree_height + 1); - - // set inputs to have a merge rollup type and set the rollup height and test again. - inputs.previous_rollup_data[0].base_or_merge_rollup_public_inputs.rollup_type = 1; - inputs.previous_rollup_data[0].base_or_merge_rollup_public_inputs.rollup_subtree_height = 1; - - inputs.previous_rollup_data[1].base_or_merge_rollup_public_inputs.rollup_type = 1; - inputs.previous_rollup_data[1].base_or_merge_rollup_public_inputs.rollup_subtree_height = 1; - - outputs = merge_rollup_circuit(builder, inputs); - ASSERT_EQ(outputs.rollup_type, 1); - ASSERT_EQ(outputs.rollup_subtree_height, 2); - ASSERT_FALSE(builder.failed()); -} - -TEST_F(merge_rollup_tests, native_start_and_end_snapshots) -{ - DummyBuilder builder = DummyBuilder("merge_rollup_tests__native_start_and_end_snapshots"); - std::array const kernels = { - get_empty_kernel(), get_empty_kernel(), get_empty_kernel(), get_empty_kernel() - }; - MergeRollupInputs inputs = get_merge_rollup_inputs(builder, kernels); - BaseOrMergeRollupPublicInputs const outputs = merge_rollup_circuit(builder, inputs); - // check that start and end snapshots are set correctly - ASSERT_EQ(outputs.start_note_hash_tree_snapshot, - inputs.previous_rollup_data[0].base_or_merge_rollup_public_inputs.start_note_hash_tree_snapshot); - ASSERT_EQ(outputs.end_note_hash_tree_snapshot, - inputs.previous_rollup_data[1].base_or_merge_rollup_public_inputs.end_note_hash_tree_snapshot); - - ASSERT_EQ(outputs.start_nullifier_tree_snapshot, - inputs.previous_rollup_data[0].base_or_merge_rollup_public_inputs.start_nullifier_tree_snapshot); - ASSERT_EQ(outputs.end_nullifier_tree_snapshot, - inputs.previous_rollup_data[1].base_or_merge_rollup_public_inputs.end_nullifier_tree_snapshot); - - ASSERT_EQ(outputs.start_contract_tree_snapshot, - inputs.previous_rollup_data[0].base_or_merge_rollup_public_inputs.start_contract_tree_snapshot); - ASSERT_EQ(outputs.end_contract_tree_snapshot, - inputs.previous_rollup_data[1].base_or_merge_rollup_public_inputs.end_contract_tree_snapshot); - - ASSERT_EQ(outputs.start_public_data_tree_root, - inputs.previous_rollup_data[0].base_or_merge_rollup_public_inputs.start_public_data_tree_root); - ASSERT_EQ(outputs.end_public_data_tree_root, - inputs.previous_rollup_data[1].base_or_merge_rollup_public_inputs.end_public_data_tree_root); - - ASSERT_FALSE(builder.failed()); -} - -TEST_F(merge_rollup_tests, native_calldata_hash) -{ - DummyBuilder builder = DummyBuilder("merge_rollup_tests__native_calldata_hash"); - std::vector const zero_bytes_vec = test_utils::utils::get_empty_calldata_leaf(); - auto call_data_hash_inner = sha256::sha256(zero_bytes_vec); - - std::array hash_input; - for (uint8_t i = 0; i < 32; ++i) { - hash_input[i] = call_data_hash_inner[i]; - hash_input[32 + i] = call_data_hash_inner[i]; - } - - std::vector const calldata_hash_input_bytes_vec(hash_input.begin(), hash_input.end()); - - auto expected_calldata_hash = sha256::sha256(calldata_hash_input_bytes_vec); - - std::array const kernels = { - get_empty_kernel(), get_empty_kernel(), get_empty_kernel(), get_empty_kernel() - }; - MergeRollupInputs const inputs = get_merge_rollup_inputs(builder, kernels); - - BaseOrMergeRollupPublicInputs const outputs = merge_rollup_circuit(builder, inputs); - - std::array const output_calldata_hash = outputs.calldata_hash; - - ASSERT_TRUE(compare_field_hash_to_expected(output_calldata_hash, expected_calldata_hash)); - ASSERT_FALSE(builder.failed()); -} - -TEST_F(merge_rollup_tests, native_constants_dont_change) -{ - DummyBuilder builder = DummyBuilder("merge_rollup_tests__native_constants_dont_change"); - std::array const kernels = { - get_empty_kernel(), get_empty_kernel(), get_empty_kernel(), get_empty_kernel() - }; - MergeRollupInputs inputs = get_merge_rollup_inputs(builder, kernels); - - BaseOrMergeRollupPublicInputs const outputs = merge_rollup_circuit(builder, inputs); - ASSERT_EQ(inputs.previous_rollup_data[0].base_or_merge_rollup_public_inputs.constants, outputs.constants); - ASSERT_EQ(inputs.previous_rollup_data[1].base_or_merge_rollup_public_inputs.constants, outputs.constants); -} - -TEST_F(merge_rollup_tests, native_aggregate) -{ - // TODO: Fix this when aggregation works - DummyBuilder builder = DummyBuilder("merge_rollup_tests__native_aggregate"); - std::array const kernels = { - get_empty_kernel(), get_empty_kernel(), get_empty_kernel(), get_empty_kernel() - }; - MergeRollupInputs inputs = get_merge_rollup_inputs(builder, kernels); - - BaseOrMergeRollupPublicInputs const outputs = merge_rollup_circuit(builder, inputs); - ASSERT_EQ(inputs.previous_rollup_data[0].base_or_merge_rollup_public_inputs.end_aggregation_object.public_inputs, - outputs.end_aggregation_object.public_inputs); - ASSERT_FALSE(builder.failed()); -} - -TEST_F(merge_rollup_tests, native_merge_cbind) -{ - DummyBuilder builder = DummyBuilder("merge_rollup_tests__native_merge_cbind"); - std::array const kernels = { - get_empty_kernel(), get_empty_kernel(), get_empty_kernel(), get_empty_kernel() - }; - MergeRollupInputs inputs = get_merge_rollup_inputs(builder, kernels); - - ASSERT_FALSE(builder.failed()); - BaseOrMergeRollupPublicInputs ignored_public_inputs; - - // TODO(1998): see above - // run_cbind(inputs, ignored_public_inputs, false); -} -} // namespace aztec3::circuits::rollup::merge::native_merge_rollup_circuit diff --git a/circuits/cpp/src/aztec3/circuits/rollup/merge/CMakeLists.txt b/circuits/cpp/src/aztec3/circuits/rollup/merge/CMakeLists.txt deleted file mode 100644 index ba98f5c140d..00000000000 --- a/circuits/cpp/src/aztec3/circuits/rollup/merge/CMakeLists.txt +++ /dev/null @@ -1,4 +0,0 @@ -circuits_cmake_module( - aztec3_circuits_rollup - barretenberg -) \ No newline at end of file diff --git a/circuits/cpp/src/aztec3/circuits/rollup/merge/c_bind.cpp b/circuits/cpp/src/aztec3/circuits/rollup/merge/c_bind.cpp deleted file mode 100644 index cd52451cc6c..00000000000 --- a/circuits/cpp/src/aztec3/circuits/rollup/merge/c_bind.cpp +++ /dev/null @@ -1,22 +0,0 @@ -#include "c_bind.h" - -#include "index.hpp" - -#include "aztec3/utils/dummy_circuit_builder.hpp" - -#include - -namespace { -using NT = aztec3::utils::types::NativeTypes; -using DummyCircuitBuilder = aztec3::utils::DummyCircuitBuilder; -using aztec3::circuits::abis::MergeRollupInputs; -using aztec3::circuits::rollup::merge::merge_rollup_circuit; -} // namespace - -// WASM Cbinds - -CBIND(merge_rollup__sim, [](MergeRollupInputs const& merge_rollup_inputs) { - DummyCircuitBuilder builder = DummyCircuitBuilder("merge_rollup__sim"); - auto const& public_inputs = merge_rollup_circuit(builder, merge_rollup_inputs); - return builder.result_or_error(public_inputs); -}); \ No newline at end of file diff --git a/circuits/cpp/src/aztec3/circuits/rollup/merge/c_bind.h b/circuits/cpp/src/aztec3/circuits/rollup/merge/c_bind.h deleted file mode 100644 index 5063715b631..00000000000 --- a/circuits/cpp/src/aztec3/circuits/rollup/merge/c_bind.h +++ /dev/null @@ -1,8 +0,0 @@ -#pragma once - -#include - -#include -#include - -CBIND_DECL(merge_rollup__sim); \ No newline at end of file diff --git a/circuits/cpp/src/aztec3/circuits/rollup/merge/index.hpp b/circuits/cpp/src/aztec3/circuits/rollup/merge/index.hpp deleted file mode 100644 index 38c7f98999e..00000000000 --- a/circuits/cpp/src/aztec3/circuits/rollup/merge/index.hpp +++ /dev/null @@ -1,2 +0,0 @@ -#include "init.hpp" -#include "native_merge_rollup_circuit.hpp" \ No newline at end of file diff --git a/circuits/cpp/src/aztec3/circuits/rollup/merge/init.hpp b/circuits/cpp/src/aztec3/circuits/rollup/merge/init.hpp deleted file mode 100644 index 34ad4c1f94e..00000000000 --- a/circuits/cpp/src/aztec3/circuits/rollup/merge/init.hpp +++ /dev/null @@ -1,32 +0,0 @@ - -#pragma once - -#include "aztec3/circuits/abis/append_only_tree_snapshot.hpp" -#include "aztec3/circuits/abis/private_circuit_public_inputs.hpp" -#include "aztec3/circuits/abis/rollup/base/base_or_merge_rollup_public_inputs.hpp" -#include "aztec3/circuits/abis/rollup/constant_rollup_data.hpp" -#include "aztec3/circuits/abis/rollup/merge/merge_rollup_inputs.hpp" -#include "aztec3/circuits/recursion/aggregator.hpp" -#include "aztec3/utils/dummy_circuit_builder.hpp" -#include "aztec3/utils/types/circuit_types.hpp" -#include "aztec3/utils/types/convert.hpp" -#include "aztec3/utils/types/native_types.hpp" - -#include - -namespace aztec3::circuits::rollup::merge { - -using NT = aztec3::utils::types::NativeTypes; -using DummyBuilder = aztec3::utils::DummyCircuitBuilder; - -// Params -using ConstantRollupData = abis::ConstantRollupData; -using MergeRollupInputs = abis::MergeRollupInputs; -using PreviousRollupData = abis::PreviousRollupData; -using BaseOrMergeRollupPublicInputs = abis::BaseOrMergeRollupPublicInputs; - -using Aggregator = aztec3::circuits::recursion::Aggregator; -using AggregationObject = utils::types::NativeTypes::AggregationObject; -using AppendOnlySnapshot = abis::AppendOnlyTreeSnapshot; - -} // namespace aztec3::circuits::rollup::merge \ No newline at end of file diff --git a/circuits/cpp/src/aztec3/circuits/rollup/merge/native_merge_rollup_circuit.cpp b/circuits/cpp/src/aztec3/circuits/rollup/merge/native_merge_rollup_circuit.cpp deleted file mode 100644 index 82e6d56f49d..00000000000 --- a/circuits/cpp/src/aztec3/circuits/rollup/merge/native_merge_rollup_circuit.cpp +++ /dev/null @@ -1,55 +0,0 @@ -#include "init.hpp" - -#include "aztec3/circuits/rollup/components/components.hpp" - -#include - -#include -#include -#include -#include -#include -#include - -namespace aztec3::circuits::rollup::merge { - -BaseOrMergeRollupPublicInputs merge_rollup_circuit(DummyBuilder& builder, MergeRollupInputs const& mergeRollupInputs) -{ - // TODO: Verify the previous rollup proofs - // TODO: Check both previous rollup vks (in previous_rollup_data) against the permitted set of kernel vks. - // we don't have a set of permitted kernel vks yet. - - auto left = mergeRollupInputs.previous_rollup_data[0].base_or_merge_rollup_public_inputs; - auto right = mergeRollupInputs.previous_rollup_data[1].base_or_merge_rollup_public_inputs; - - // check that both input proofs are either both "BASE" or "MERGE" and not a mix! - // this prevents having wonky commitment, nullifier and contract subtrees. - AggregationObject const aggregation_object = components::aggregate_proofs(left, right); - components::assert_both_input_proofs_of_same_rollup_type(builder, left, right); - auto current_height = components::assert_both_input_proofs_of_same_height_and_return(builder, left, right); - components::assert_equal_constants(builder, left, right); - components::assert_prev_rollups_follow_on_from_each_other(builder, left, right); - - // compute calldata hash: - auto new_calldata_hash = components::compute_calldata_hash(mergeRollupInputs.previous_rollup_data); - - BaseOrMergeRollupPublicInputs public_inputs = { - .rollup_type = abis::MERGE_ROLLUP_TYPE, - .rollup_subtree_height = current_height + 1, - .end_aggregation_object = aggregation_object, - .constants = left.constants, - .start_note_hash_tree_snapshot = left.start_note_hash_tree_snapshot, - .end_note_hash_tree_snapshot = right.end_note_hash_tree_snapshot, - .start_nullifier_tree_snapshot = left.start_nullifier_tree_snapshot, - .end_nullifier_tree_snapshot = right.end_nullifier_tree_snapshot, - .start_contract_tree_snapshot = left.start_contract_tree_snapshot, - .end_contract_tree_snapshot = right.end_contract_tree_snapshot, - .start_public_data_tree_root = left.start_public_data_tree_root, - .end_public_data_tree_root = right.end_public_data_tree_root, - .calldata_hash = new_calldata_hash, - }; - - return public_inputs; -} - -} // namespace aztec3::circuits::rollup::merge \ No newline at end of file diff --git a/circuits/cpp/src/aztec3/circuits/rollup/merge/native_merge_rollup_circuit.hpp b/circuits/cpp/src/aztec3/circuits/rollup/merge/native_merge_rollup_circuit.hpp deleted file mode 100644 index 9d1e4b24772..00000000000 --- a/circuits/cpp/src/aztec3/circuits/rollup/merge/native_merge_rollup_circuit.hpp +++ /dev/null @@ -1,7 +0,0 @@ -#pragma once - -#include "init.hpp" - -namespace aztec3::circuits::rollup::merge { -BaseOrMergeRollupPublicInputs merge_rollup_circuit(DummyBuilder& builder, MergeRollupInputs const& mergeRollupInputs); -} // namespace aztec3::circuits::rollup::merge \ No newline at end of file diff --git a/circuits/cpp/src/aztec3/circuits/rollup/root/.test.cpp b/circuits/cpp/src/aztec3/circuits/rollup/root/.test.cpp deleted file mode 100644 index 6be0e29b6c5..00000000000 --- a/circuits/cpp/src/aztec3/circuits/rollup/root/.test.cpp +++ /dev/null @@ -1,337 +0,0 @@ -#include "c_bind.h" -#include "index.hpp" -#include "init.hpp" - -#include "aztec3/circuits/abis/append_only_tree_snapshot.hpp" -#include "aztec3/circuits/abis/combined_accumulated_data.hpp" -#include "aztec3/circuits/abis/global_variables.hpp" -#include "aztec3/circuits/abis/kernel_circuit_public_inputs.hpp" -#include "aztec3/circuits/abis/new_contract_data.hpp" -#include "aztec3/circuits/abis/previous_kernel_data.hpp" -#include "aztec3/circuits/abis/rollup/merge/previous_rollup_data.hpp" -#include "aztec3/circuits/hash.hpp" -#include "aztec3/circuits/kernel/private/utils.hpp" -#include "aztec3/circuits/rollup/base/init.hpp" -#include "aztec3/circuits/rollup/components/components.hpp" -#include "aztec3/circuits/rollup/test_utils/init.hpp" -#include "aztec3/circuits/rollup/test_utils/utils.hpp" -#include "aztec3/constants.hpp" -#include "aztec3/utils/dummy_circuit_builder.hpp" - -#include - -#include - -#include -#include -#include -#include - -namespace { - - -using aztec3::circuits::abis::PreviousKernelData; - - -// using aztec3::circuits::mock::mock_circuit; -using aztec3::circuits::rollup::test_utils::utils::compare_field_hash_to_expected; -using aztec3::circuits::rollup::test_utils::utils::get_empty_kernel; -using aztec3::circuits::rollup::test_utils::utils::get_empty_l1_to_l2_messages; -using aztec3::circuits::rollup::test_utils::utils::get_initial_nullifier_tree_empty; -using aztec3::circuits::rollup::test_utils::utils::get_root_rollup_inputs; - -using aztec3::circuits::abis::AppendOnlyTreeSnapshot; - -using aztec3::circuits::rollup::native_base_rollup::BaseOrMergeRollupPublicInputs; -using aztec3::circuits::rollup::native_base_rollup::BaseRollupInputs; -using aztec3::circuits::rollup::native_base_rollup::ConstantRollupData; -using aztec3::circuits::rollup::native_base_rollup::NT; - -using aztec3::circuits::rollup::native_root_rollup::RootRollupInputs; -using aztec3::circuits::rollup::native_root_rollup::RootRollupPublicInputs; - -using aztec3::circuits::abis::NewContractData; - -using MemoryStore = stdlib::merkle_tree::MemoryStore; -using MerkleTree = stdlib::merkle_tree::MerkleTree; - -using KernelData = aztec3::circuits::abis::PreviousKernelData; -} // namespace - -namespace aztec3::circuits::rollup::root::native_root_rollup_circuit { - -class root_rollup_tests : public ::testing::Test { - protected: - static void SetUpTestSuite() { barretenberg::srs::init_crs_factory("../barretenberg/cpp/srs_db/ignition"); } - - // TODO(1998): uncomment once https://github.com/AztecProtocol/aztec-packages/issues/1998 is solved and - // use new pattern such as call_func_and_wrapper from test_helper.hpp - - // static void run_cbind(RootRollupInputs& root_rollup_inputs, - // RootRollupPublicInputs& expected_public_inputs, - // bool compare_pubins = true) - // { - // info("Retesting via cbinds...."); - // // info("Verification key size: ", vk_size); - - // std::vector root_rollup_inputs_vec; - // serialize::write(root_rollup_inputs_vec, root_rollup_inputs); - - // // uint8_t const* proof_data; - // // size_t proof_data_size; - // uint8_t const* public_inputs_buf = nullptr; - // size_t public_inputs_size = 0; - // // info("simulating circuit via cbind"); - // uint8_t* const circuit_failure_ptr = - // root_rollup__sim(root_rollup_inputs_vec.data(), &public_inputs_size, &public_inputs_buf); - // ASSERT_TRUE(circuit_failure_ptr == nullptr); - // // info("Proof size: ", proof_data_size); - // // info("PublicInputs size: ", public_inputs_size); - - // if (compare_pubins) { - // RootRollupPublicInputs public_inputs; - // uint8_t const* public_inputs_buf_tmp = public_inputs_buf; - // serialize::read(public_inputs_buf_tmp, public_inputs); - // ASSERT_EQ(public_inputs.calldata_hash.size(), expected_public_inputs.calldata_hash.size()); - // for (size_t i = 0; i < public_inputs.calldata_hash.size(); i++) { - // ASSERT_EQ(public_inputs.calldata_hash[i], expected_public_inputs.calldata_hash[i]); - // } - - // std::vector expected_public_inputs_vec; - // serialize::write(expected_public_inputs_vec, expected_public_inputs); - - // ASSERT_EQ(public_inputs_size, expected_public_inputs_vec.size()); - // // Just compare the first 10 bytes of the serialized public outputs - // if (public_inputs_size > 10) { - // // for (size_t 0; i < public_inputs_size; i++) { - // for (size_t i = 0; i < 10; i++) { - // ASSERT_EQ(public_inputs_buf[i], expected_public_inputs_vec[i]); - // } - // } - // } - - // // free((void*)proof_data); - // free((void*)public_inputs_buf); - // } -}; - -TEST_F(root_rollup_tests, native_check_block_hashes_empty_blocks) -{ - std::vector const zero_bytes_vec = test_utils::utils::get_empty_calldata_leaf(); - auto call_data_hash_inner = sha256::sha256(zero_bytes_vec); - - // Compute a new calldata hash based on TWO of the above rollups - std::array hash_input; - for (uint8_t i = 0; i < 32; ++i) { - hash_input[i] = call_data_hash_inner[i]; - hash_input[32 + i] = call_data_hash_inner[i]; - } - std::vector const calldata_hash_input_bytes_vec(hash_input.begin(), hash_input.end()); - auto calldata_hash = sha256::sha256(calldata_hash_input_bytes_vec); - - // get messages - std::array const l1_to_l2_messages = get_empty_l1_to_l2_messages(); - - // hash messages - std::vector const messages_hash_input_bytes_vec(NUMBER_OF_L1_L2_MESSAGES_PER_ROLLUP * 32, 0); - auto messages_hash = sha256::sha256(messages_hash_input_bytes_vec); - - utils::DummyCircuitBuilder builder = - utils::DummyCircuitBuilder("root_rollup_tests__native_check_block_hashes_empty_blocks"); - std::array const kernels = { - get_empty_kernel(), get_empty_kernel(), get_empty_kernel(), get_empty_kernel() - }; - - RootRollupInputs inputs = get_root_rollup_inputs(builder, kernels, l1_to_l2_messages); - RootRollupPublicInputs outputs = aztec3::circuits::rollup::native_root_rollup::root_rollup_circuit(builder, inputs); - - // check calldata hash - ASSERT_TRUE(compare_field_hash_to_expected(outputs.calldata_hash, calldata_hash)); - // Check messages hash - ASSERT_TRUE(compare_field_hash_to_expected(outputs.l1_to_l2_messages_hash, messages_hash)); - - EXPECT_FALSE(builder.failed()); - - // TODO(1998): see above - // run_cbind(inputs, outputs, true); -} - -TEST_F(root_rollup_tests, native_root_missing_nullifier_logic) -{ - utils::DummyCircuitBuilder builder = - utils::DummyCircuitBuilder("root_rollup_tests__native_root_missing_nullifier_logic"); - - MemoryStore note_hash_tree_store; - MerkleTree note_hash_tree(note_hash_tree_store, NOTE_HASH_TREE_HEIGHT); - - MemoryStore contract_tree_store; - MerkleTree contract_tree(contract_tree_store, CONTRACT_TREE_HEIGHT); - - MemoryStore l1_to_l2_message_tree_store; - MerkleTree l1_to_l2_message_tree(l1_to_l2_message_tree_store, L1_TO_L2_MSG_TREE_HEIGHT); - - MemoryStore public_store; - MerkleTree public_data_tree(public_store, PUBLIC_DATA_TREE_HEIGHT); - - // Create initial nullifier tree with 32 initial nullifiers - auto nullifier_tree = get_initial_nullifier_tree_empty(); - - MemoryStore archive_store; - MerkleTree archive(archive_store, ARCHIVE_HEIGHT); - - std::array kernels = { - get_empty_kernel(), get_empty_kernel(), get_empty_kernel(), get_empty_kernel() - }; - std::array l1_to_l2_messages = get_empty_l1_to_l2_messages(); - - // Calculate the start block hash - abis::GlobalVariables globals = abis::GlobalVariables::empty(); - auto start_block_hash = compute_block_hash_with_globals(globals, - note_hash_tree.root(), - nullifier_tree.root(), - contract_tree.root(), - l1_to_l2_message_tree.root(), - public_data_tree.root()); - archive.update_element(0, start_block_hash); - AppendOnlyTreeSnapshot start_archive_snapshot = { .root = archive.root(), .next_available_leaf_index = 1 }; - - // Create commitments - for (size_t kernel_j = 0; kernel_j < 4; kernel_j++) { - std::array new_commitments; - for (uint8_t commitment_k = 0; commitment_k < MAX_NEW_COMMITMENTS_PER_TX; commitment_k++) { - auto val = fr(kernel_j * MAX_NEW_COMMITMENTS_PER_TX + commitment_k + 1); - new_commitments[commitment_k] = val; - note_hash_tree.update_element(kernel_j * MAX_NEW_COMMITMENTS_PER_TX + commitment_k, val); - } - kernels[kernel_j].public_inputs.end.new_commitments = new_commitments; - - std::array new_l2_to_l1_messages; - for (uint8_t i = 0; i < MAX_NEW_L2_TO_L1_MSGS_PER_TX; i++) { - auto val = fr(kernel_j * MAX_NEW_L2_TO_L1_MSGS_PER_TX + i + 1); - new_l2_to_l1_messages[i] = val; - } - kernels[kernel_j].public_inputs.end.new_l2_to_l1_msgs = new_l2_to_l1_messages; - } - - // @todo @LHerskind: Add nullifiers - // @todo @LHerskind: Add public data writes - - // Contract tree - NewContractData const new_contract = { - .contract_address = fr(1), - .portal_contract_address = fr(3), - .function_tree_root = fr(2), - }; - // Update contract tree - contract_tree.update_element(2, new_contract.hash()); - kernels[2].public_inputs.end.new_contracts[0] = new_contract; - - // l1 to l2 messages snapshot - AppendOnlyTreeSnapshot const start_l1_to_l2_message_tree_snapshot = { .root = l1_to_l2_message_tree.root(), - .next_available_leaf_index = 0 }; - - // Create 16 empty l1 to l2 messages, and update the l1_to_l2 message tree - for (size_t i = 0; i < l1_to_l2_messages.size(); i++) { - l1_to_l2_message_tree.update_element(i, l1_to_l2_messages[i]); - } - - // Get the block hash after. - auto end_block_hash = compute_block_hash_with_globals(globals, - note_hash_tree.root(), - nullifier_tree.root(), - contract_tree.root(), - l1_to_l2_message_tree.root(), - public_data_tree.root()); - archive.update_element(1, end_block_hash); - AppendOnlyTreeSnapshot end_archive_snapshot = { .root = archive.root(), .next_available_leaf_index = 2 }; - - // Compute the end snapshot - AppendOnlyTreeSnapshot const end_l1_to_l2_message_tree_snapshot = { .root = l1_to_l2_message_tree.root(), - .next_available_leaf_index = - NUMBER_OF_L1_L2_MESSAGES_PER_ROLLUP }; - - RootRollupInputs rootRollupInputs = get_root_rollup_inputs(builder, kernels, l1_to_l2_messages); - RootRollupPublicInputs outputs = - aztec3::circuits::rollup::native_root_rollup::root_rollup_circuit(builder, rootRollupInputs); - - // Check note hash trees - ASSERT_EQ( - outputs.start_note_hash_tree_snapshot, - rootRollupInputs.previous_rollup_data[0].base_or_merge_rollup_public_inputs.start_note_hash_tree_snapshot); - ASSERT_EQ(outputs.end_note_hash_tree_snapshot, - rootRollupInputs.previous_rollup_data[1].base_or_merge_rollup_public_inputs.end_note_hash_tree_snapshot); - AppendOnlyTreeSnapshot const expected_note_hash_tree_snapshot = { .root = note_hash_tree.root(), - .next_available_leaf_index = - 4 * MAX_NEW_COMMITMENTS_PER_TX }; - ASSERT_EQ(outputs.end_note_hash_tree_snapshot, expected_note_hash_tree_snapshot); - - // Check public data trees - ASSERT_EQ(outputs.start_public_data_tree_root, - rootRollupInputs.previous_rollup_data[0].base_or_merge_rollup_public_inputs.start_public_data_tree_root); - ASSERT_EQ(outputs.end_public_data_tree_root, - rootRollupInputs.previous_rollup_data[1].base_or_merge_rollup_public_inputs.end_public_data_tree_root); - - // check contract trees - ASSERT_EQ(outputs.start_contract_tree_snapshot, - rootRollupInputs.previous_rollup_data[0].base_or_merge_rollup_public_inputs.start_contract_tree_snapshot); - ASSERT_EQ(outputs.end_contract_tree_snapshot, - rootRollupInputs.previous_rollup_data[1].base_or_merge_rollup_public_inputs.end_contract_tree_snapshot); - AppendOnlyTreeSnapshot const expected_contract_tree_snapshot{ .root = contract_tree.root(), - .next_available_leaf_index = 4 }; - ASSERT_EQ(outputs.end_contract_tree_snapshot, expected_contract_tree_snapshot); - - // @todo @LHerskind: Check nullifier trees - - // Check l1 to l2 message trees - ASSERT_EQ(outputs.start_l1_to_l2_message_tree_snapshot, start_l1_to_l2_message_tree_snapshot); - ASSERT_EQ(outputs.start_contract_tree_snapshot, - rootRollupInputs.previous_rollup_data[0].base_or_merge_rollup_public_inputs.start_contract_tree_snapshot); - ASSERT_EQ(outputs.end_contract_tree_snapshot, - rootRollupInputs.previous_rollup_data[1].base_or_merge_rollup_public_inputs.end_contract_tree_snapshot); - ASSERT_EQ(outputs.end_l1_to_l2_message_tree_snapshot, end_l1_to_l2_message_tree_snapshot); - - ASSERT_EQ(outputs.start_archive_snapshot, start_archive_snapshot); - ASSERT_EQ(outputs.end_archive_snapshot, end_archive_snapshot); - - // Compute the expected calldata hash for the root rollup (including the l2 -> l1 messages) - auto left = components::compute_kernels_calldata_hash({ kernels[0], kernels[1] }); - auto right = components::compute_kernels_calldata_hash({ kernels[2], kernels[3] }); - auto root = accumulate_sha256({ left[0], left[1], right[0], right[1] }); - ASSERT_EQ(outputs.calldata_hash, root); - - EXPECT_FALSE(builder.failed()); - - // TODO(1998): see above - // run_cbind(rootRollupInputs, outputs, true); -} - -TEST_F(root_rollup_tests, noir_interop_test) -{ - // This is an annoying hack to convert the field into a hex string - // We should add a to_hex and from_hex method to field class - auto to_hex = [](const NT::fr& value) -> std::string { - std::stringstream field_as_hex_stream; - field_as_hex_stream << value; - return field_as_hex_stream.str(); - }; - - MemoryStore merkle_tree_store; - MerkleTree merkle_tree(merkle_tree_store, L1_TO_L2_MSG_SUBTREE_HEIGHT); - - std::array leaves = { 1, 2, 3, 4, 1, 2, 3, 4, 1, 2, 3, 4, 1, 2, 3, 4 }; - for (size_t i = 0; i < NUMBER_OF_L1_L2_MESSAGES_PER_ROLLUP; i++) { - merkle_tree.update_element(i, leaves[i]); - } - auto root = merkle_tree.root(); - auto expected = "0x17e8bb70a11d0c946345950879484d2f4f9fef397ff6adbfdec3baab2d41faab"; - ASSERT_EQ(to_hex(root), expected); - - // Empty subtree is the same as zeroes - MemoryStore empty_tree_store; - MerkleTree const empty_tree = MerkleTree(empty_tree_store, L1_TO_L2_MSG_SUBTREE_HEIGHT); - auto empty_root = empty_tree.root(); - auto expected_empty_root = "0x06e62084ee7b602fe9abc15632dda3269f56fb0c6e12519a2eb2ec897091919d"; - ASSERT_EQ(to_hex(empty_root), expected_empty_root); -} - -} // namespace aztec3::circuits::rollup::root::native_root_rollup_circuit \ No newline at end of file diff --git a/circuits/cpp/src/aztec3/circuits/rollup/root/CMakeLists.txt b/circuits/cpp/src/aztec3/circuits/rollup/root/CMakeLists.txt deleted file mode 100644 index d2994796d2c..00000000000 --- a/circuits/cpp/src/aztec3/circuits/rollup/root/CMakeLists.txt +++ /dev/null @@ -1,5 +0,0 @@ -circuits_cmake_module( - aztec3_circuits_rollup - aztec3_circuits_kernel - barretenberg -) \ No newline at end of file diff --git a/circuits/cpp/src/aztec3/circuits/rollup/root/c_bind.cpp b/circuits/cpp/src/aztec3/circuits/rollup/root/c_bind.cpp deleted file mode 100644 index 197560a64fb..00000000000 --- a/circuits/cpp/src/aztec3/circuits/rollup/root/c_bind.cpp +++ /dev/null @@ -1,26 +0,0 @@ -#include "c_bind.h" - -#include "index.hpp" -#include "init.hpp" - -#include "aztec3/constants.hpp" -#include "aztec3/utils/types/native_types.hpp" - -#include - -namespace { -using Builder = UltraCircuitBuilder; -using NT = aztec3::utils::types::NativeTypes; -using DummyCircuitBuilder = aztec3::utils::DummyCircuitBuilder; -using aztec3::circuits::rollup::native_root_rollup::root_rollup_circuit; -using aztec3::circuits::rollup::native_root_rollup::RootRollupInputs; -using aztec3::circuits::rollup::native_root_rollup::RootRollupPublicInputs; - -} // namespace - -// WASM Cbinds -CBIND(root_rollup__sim, [](RootRollupInputs const& root_rollup_inputs) { - DummyCircuitBuilder builder = DummyCircuitBuilder("root_rollup__sim"); - auto const& public_inputs = root_rollup_circuit(builder, root_rollup_inputs); - return builder.result_or_error(public_inputs); -}); \ No newline at end of file diff --git a/circuits/cpp/src/aztec3/circuits/rollup/root/c_bind.h b/circuits/cpp/src/aztec3/circuits/rollup/root/c_bind.h deleted file mode 100644 index 5105580e922..00000000000 --- a/circuits/cpp/src/aztec3/circuits/rollup/root/c_bind.h +++ /dev/null @@ -1,8 +0,0 @@ -#pragma once - -#include - -#include -#include - -CBIND_DECL(root_rollup__sim); \ No newline at end of file diff --git a/circuits/cpp/src/aztec3/circuits/rollup/root/index.hpp b/circuits/cpp/src/aztec3/circuits/rollup/root/index.hpp deleted file mode 100644 index 091987013ee..00000000000 --- a/circuits/cpp/src/aztec3/circuits/rollup/root/index.hpp +++ /dev/null @@ -1,2 +0,0 @@ -#include "init.hpp" -#include "native_root_rollup_circuit.hpp" \ No newline at end of file diff --git a/circuits/cpp/src/aztec3/circuits/rollup/root/init.hpp b/circuits/cpp/src/aztec3/circuits/rollup/root/init.hpp deleted file mode 100644 index f24e7c719f3..00000000000 --- a/circuits/cpp/src/aztec3/circuits/rollup/root/init.hpp +++ /dev/null @@ -1,31 +0,0 @@ - -#pragma once - -#include "aztec3/circuits/abis/append_only_tree_snapshot.hpp" -#include "aztec3/circuits/abis/rollup/constant_rollup_data.hpp" -#include "aztec3/circuits/abis/rollup/root/root_rollup_inputs.hpp" -#include "aztec3/circuits/abis/rollup/root/root_rollup_public_inputs.hpp" -#include "aztec3/circuits/recursion/aggregator.hpp" -#include "aztec3/utils/dummy_circuit_builder.hpp" -#include "aztec3/utils/types/circuit_types.hpp" -#include "aztec3/utils/types/convert.hpp" -#include "aztec3/utils/types/native_types.hpp" - -#include - -namespace aztec3::circuits::rollup::native_root_rollup { - -using NT = aztec3::utils::types::NativeTypes; -using DummyBuilder = aztec3::utils::DummyCircuitBuilder; - -// Params -using ConstantRollupData = abis::ConstantRollupData; -using RootRollupInputs = abis::RootRollupInputs; -using RootRollupPublicInputs = abis::RootRollupPublicInputs; - -using Aggregator = aztec3::circuits::recursion::Aggregator; - -using MemoryStore = stdlib::merkle_tree::MemoryStore; -using MerkleTree = stdlib::merkle_tree::MerkleTree; - -} // namespace aztec3::circuits::rollup::native_root_rollup diff --git a/circuits/cpp/src/aztec3/circuits/rollup/root/native_root_rollup_circuit.cpp b/circuits/cpp/src/aztec3/circuits/rollup/root/native_root_rollup_circuit.cpp deleted file mode 100644 index da9a256b812..00000000000 --- a/circuits/cpp/src/aztec3/circuits/rollup/root/native_root_rollup_circuit.cpp +++ /dev/null @@ -1,152 +0,0 @@ -#include "init.hpp" - -#include "aztec3/circuits/abis/rollup/root/root_rollup_inputs.hpp" -#include "aztec3/circuits/abis/rollup/root/root_rollup_public_inputs.hpp" -#include "aztec3/circuits/hash.hpp" -#include "aztec3/circuits/rollup/components/components.hpp" -#include "aztec3/constants.hpp" - -#include -#include -#include -#include -#include -#include - -namespace aztec3::circuits::rollup::native_root_rollup { - -// Used when calling library functions like `check_membership` which have their own generic error code. -// So we pad this in front of the error message to identify where the error originally came from. -const std::string ROOT_CIRCUIT_ERROR_MESSAGE_BEGINNING = "root_rollup_circuit: "; - -// TODO: can we aggregate proofs if we do not have a working circuit impl -// TODO: change the public inputs array - we wont be using this? - -// Access Native types through NT namespace - -/** - * @brief Calculates the messages subtree from the leaves array - * @param leaves - * @return root - */ -NT::fr calculate_subtree(std::array leaves) -{ - MemoryStore merkle_tree_store; - MerkleTree merkle_tree(merkle_tree_store, L1_TO_L2_MSG_SUBTREE_HEIGHT); - - for (size_t i = 0; i < NUMBER_OF_L1_L2_MESSAGES_PER_ROLLUP; i++) { - merkle_tree.update_element(i, leaves[i]); - } - return merkle_tree.root(); -} - -/** - * @brief Computes the messages hash from the leaves array - * @param leaves - * @param return - hash split into two field elements - */ -std::array compute_messages_hash( - std::array leaves) -{ - // convert vector of field elements into uint_8 - std::array messages_hash_input_bytes; - for (size_t i = 0; i < NUMBER_OF_L1_L2_MESSAGES_PER_ROLLUP; i++) { - auto bytes = leaves[i].to_buffer(); - for (size_t j = 0; j < 32; j++) { - messages_hash_input_bytes[i * 32 + j] = bytes[j]; - } - } - - std::vector const messages_hash_input_bytes_vec(messages_hash_input_bytes.begin(), - messages_hash_input_bytes.end()); - auto h = sha256::sha256(messages_hash_input_bytes_vec); - - std::array buf_1; - std::array buf_2; - for (uint8_t i = 0; i < 16; i++) { - buf_1[i] = 0; - buf_1[16 + i] = h[i]; - buf_2[i] = 0; - buf_2[16 + i] = h[i + 16]; - } - auto high = fr::serialize_from_buffer(buf_1.data()); - auto low = fr::serialize_from_buffer(buf_2.data()); - - return { high, low }; -} - -RootRollupPublicInputs root_rollup_circuit(DummyBuilder& builder, RootRollupInputs const& rootRollupInputs) -{ - // TODO: Verify the previous rollup proofs - // TODO: Check both previous rollup vks (in previous_rollup_data) against the permitted set of kernel vks. - // we don't have a set of permitted kernel vks yet. - - auto left = rootRollupInputs.previous_rollup_data[0].base_or_merge_rollup_public_inputs; - auto right = rootRollupInputs.previous_rollup_data[1].base_or_merge_rollup_public_inputs; - - auto aggregation_object = components::aggregate_proofs(left, right); - components::assert_both_input_proofs_of_same_rollup_type(builder, left, right); - components::assert_both_input_proofs_of_same_height_and_return(builder, left, right); - components::assert_equal_constants(builder, left, right); - components::assert_prev_rollups_follow_on_from_each_other(builder, left, right); - - // Check correct l1 to l2 tree given - // Compute subtree inserting l1 to l2 messages - auto l1_to_l2_subtree_root = calculate_subtree(rootRollupInputs.new_l1_to_l2_messages); - - // Insert subtree into the l1 to l2 data tree - const auto empty_l1_to_l2_subtree_root = components::calculate_empty_tree_root(L1_TO_L2_MSG_SUBTREE_HEIGHT); - auto new_l1_to_l2_message_tree_snapshot = components::insert_subtree_to_snapshot_tree( - builder, - rootRollupInputs.start_l1_to_l2_message_tree_snapshot, - rootRollupInputs.new_l1_to_l2_message_tree_root_sibling_path, - empty_l1_to_l2_subtree_root, - l1_to_l2_subtree_root, - L1_TO_L2_MSG_SUBTREE_HEIGHT, - format(ROOT_CIRCUIT_ERROR_MESSAGE_BEGINNING, - "l1 to l2 message tree not empty at location where subtree would be inserted")); - - // Build the block hash for this iteration from the tree roots and global variables - // Then insert the block into the blocks tree - auto block_hash = compute_block_hash_with_globals(left.constants.global_variables, - right.end_note_hash_tree_snapshot.root, - right.end_nullifier_tree_snapshot.root, - right.end_contract_tree_snapshot.root, - new_l1_to_l2_message_tree_snapshot.root, - right.end_public_data_tree_root); - - // Update the blocks tree - auto end_archive_snapshot = components::insert_subtree_to_snapshot_tree( - builder, - rootRollupInputs.start_archive_snapshot, - rootRollupInputs.new_archive_sibling_path, - fr::zero(), - block_hash, - 0, - format(ROOT_CIRCUIT_ERROR_MESSAGE_BEGINNING, - "blocks tree roots not empty at location where subtree would be inserted")); - - - RootRollupPublicInputs public_inputs = { - .end_aggregation_object = aggregation_object, - .global_variables = left.constants.global_variables, - .start_note_hash_tree_snapshot = left.start_note_hash_tree_snapshot, - .end_note_hash_tree_snapshot = right.end_note_hash_tree_snapshot, - .start_nullifier_tree_snapshot = left.start_nullifier_tree_snapshot, - .end_nullifier_tree_snapshot = right.end_nullifier_tree_snapshot, - .start_contract_tree_snapshot = left.start_contract_tree_snapshot, - .end_contract_tree_snapshot = right.end_contract_tree_snapshot, - .start_public_data_tree_root = left.start_public_data_tree_root, - .end_public_data_tree_root = right.end_public_data_tree_root, - .start_l1_to_l2_message_tree_snapshot = rootRollupInputs.start_l1_to_l2_message_tree_snapshot, - .end_l1_to_l2_message_tree_snapshot = new_l1_to_l2_message_tree_snapshot, - .start_archive_snapshot = rootRollupInputs.start_archive_snapshot, - .end_archive_snapshot = end_archive_snapshot, - .calldata_hash = components::compute_calldata_hash(rootRollupInputs.previous_rollup_data), - .l1_to_l2_messages_hash = compute_messages_hash(rootRollupInputs.new_l1_to_l2_messages) - }; - - return public_inputs; -} - -} // namespace aztec3::circuits::rollup::native_root_rollup diff --git a/circuits/cpp/src/aztec3/circuits/rollup/root/native_root_rollup_circuit.hpp b/circuits/cpp/src/aztec3/circuits/rollup/root/native_root_rollup_circuit.hpp deleted file mode 100644 index d611a114dfd..00000000000 --- a/circuits/cpp/src/aztec3/circuits/rollup/root/native_root_rollup_circuit.hpp +++ /dev/null @@ -1,16 +0,0 @@ -#pragma once - -#include "init.hpp" - -// TODO: not needed right at this moment for native impl -#include "aztec3/circuits/abis/rollup/root/root_rollup_inputs.hpp" -#include "aztec3/circuits/abis/rollup/root/root_rollup_public_inputs.hpp" -#include "aztec3/utils/types/circuit_types.hpp" -#include "aztec3/utils/types/convert.hpp" -#include "aztec3/utils/types/native_types.hpp" - -namespace aztec3::circuits::rollup::native_root_rollup { - -RootRollupPublicInputs root_rollup_circuit(DummyBuilder& builder, RootRollupInputs const& rootRollupInputs); - -} // namespace aztec3::circuits::rollup::native_root_rollup \ No newline at end of file diff --git a/circuits/cpp/src/aztec3/circuits/rollup/test_utils/init.hpp b/circuits/cpp/src/aztec3/circuits/rollup/test_utils/init.hpp deleted file mode 100644 index 4d95d8b7c5d..00000000000 --- a/circuits/cpp/src/aztec3/circuits/rollup/test_utils/init.hpp +++ /dev/null @@ -1,46 +0,0 @@ - -#pragma once - -#include "aztec3/circuits/abis/append_only_tree_snapshot.hpp" -#include "aztec3/circuits/abis/private_circuit_public_inputs.hpp" -#include "aztec3/circuits/abis/rollup/base/base_or_merge_rollup_public_inputs.hpp" -#include "aztec3/circuits/abis/rollup/base/base_rollup_inputs.hpp" -#include "aztec3/circuits/abis/rollup/constant_rollup_data.hpp" -#include "aztec3/circuits/abis/rollup/merge/merge_rollup_inputs.hpp" -#include "aztec3/circuits/abis/rollup/merge/previous_rollup_data.hpp" -#include "aztec3/circuits/abis/rollup/root/root_rollup_inputs.hpp" -#include "aztec3/circuits/recursion/aggregator.hpp" -#include "aztec3/circuits/rollup/base/native_base_rollup_circuit.hpp" -#include "aztec3/utils/dummy_circuit_builder.hpp" -#include "aztec3/utils/types/circuit_types.hpp" -#include "aztec3/utils/types/convert.hpp" -#include "aztec3/utils/types/native_types.hpp" - -// TODO(dbanks12) should we force files to explicitly include barretenberg when using it? -#include - -namespace aztec3::circuits::rollup::test_utils { - -using NT = aztec3::utils::types::NativeTypes; - -// Types -using ConstantRollupData = abis::ConstantRollupData; -using BaseRollupInputs = abis::BaseRollupInputs; -using BaseOrMergeRollupPublicInputs = abis::BaseOrMergeRollupPublicInputs; -using DummyBuilder = aztec3::utils::DummyCircuitBuilder; - -using Aggregator = aztec3::circuits::recursion::Aggregator; -using AggregationObject = aztec3::utils::types::NativeTypes::AggregationObject; -using AppendOnlySnapshot = abis::AppendOnlyTreeSnapshot; - -using NullifierLeafPreimage = aztec3::circuits::abis::NullifierLeafPreimage; - -// Tree Aliases -using MemoryStore = stdlib::merkle_tree::MemoryStore; -using MerkleTree = stdlib::merkle_tree::MerkleTree; -using NullifierTree = stdlib::merkle_tree::NullifierMemoryTree; -using NullifierLeaf = stdlib::merkle_tree::nullifier_leaf; - -using aztec3::circuits::abis::MembershipWitness; - -} // namespace aztec3::circuits::rollup::test_utils \ No newline at end of file diff --git a/circuits/cpp/src/aztec3/circuits/rollup/test_utils/nullifier_tree_testing_harness.cpp b/circuits/cpp/src/aztec3/circuits/rollup/test_utils/nullifier_tree_testing_harness.cpp deleted file mode 100644 index 8ddc564c86c..00000000000 --- a/circuits/cpp/src/aztec3/circuits/rollup/test_utils/nullifier_tree_testing_harness.cpp +++ /dev/null @@ -1,155 +0,0 @@ -#include "nullifier_tree_testing_harness.hpp" - -#include - -#include -#include - -using NullifierMemoryTree = proof_system::plonk::stdlib::merkle_tree::NullifierMemoryTree; -using nullifier_leaf = proof_system::plonk::stdlib::merkle_tree::nullifier_leaf; - -NullifierMemoryTreeTestingHarness::NullifierMemoryTreeTestingHarness(size_t depth) : NullifierMemoryTree(depth) {} - -// Check for a larger value in an array -bool check_has_less_than(std::vector const& values, fr const& value) -{ - // Must perform comparisons on integers - auto const value_as_uint = uint256_t(value); - for (auto const& v : values) { - // info(v, " ", uint256_t(v) < value_as_uint); - if (uint256_t(v) < value_as_uint) { - return true; - } - } - return false; -} - -// handle synthetic membership assertions -std::tuple, std::vector>, std::vector> -NullifierMemoryTreeTestingHarness::circuit_prep_batch_insert(std::vector const& values) -{ - // Start insertion index - fr const start_insertion_index = this->size(); - - // Low nullifiers - std::vector low_nullifiers; - std::vector pending_insertion_tree; - - // Low nullifier sibling paths - std::vector> sibling_paths; - - // Low nullifier indexes - std::vector low_nullifier_indexes; - - // Keep track of the currently touched nodes while updating - std::map> touched_nodes; - - // Keep track of 0 values - std::vector const empty_sp(depth_, 0); - nullifier_leaf const empty_leaf = { 0, 0, 0 }; - uint32_t const empty_index = 0; - - // Find the leaf with the value closest and less than `value` for each value - for (size_t i = 0; i < values.size(); ++i) { - auto new_value = values[i]; - auto insertion_index = start_insertion_index + i; - - size_t current = 0; - bool is_already_present = false; - std::tie(current, is_already_present) = find_closest_leaf(leaves_, new_value); - - // If the inserted value is 0, then we ignore and provide a dummy low nullifier - if (new_value == 0) { - sibling_paths.push_back(empty_sp); - low_nullifier_indexes.push_back(empty_index); - low_nullifiers.push_back(empty_leaf); - continue; - } - - // If the low_nullifier node has been touched this sub tree insertion, we provide a dummy sibling path - // It will be up to the circuit to check if the included node is valid vs the other nodes that have been - // inserted before it If it has not been touched, we provide a sibling path then update the nodes pointers - auto prev_nodes = touched_nodes.find(current); - - bool has_less_than = false; - if (prev_nodes != touched_nodes.end()) { - has_less_than = check_has_less_than(prev_nodes->second, new_value); - } - // If there is a lower value in the tree, we need to check the current low nullifiers for one that can be used - if (has_less_than) { - for (size_t j = 0; j < pending_insertion_tree.size(); ++j) { - // Skip checking empty values - if (pending_insertion_tree[j].value == 0) { - continue; - } - - if (pending_insertion_tree[j].value < new_value && - (pending_insertion_tree[j].nextValue > new_value || pending_insertion_tree[j].nextValue == 0)) { - // Add a new pending low nullifier for this value - nullifier_leaf const new_leaf = { .value = new_value, - .nextIndex = pending_insertion_tree[j].nextIndex, - .nextValue = pending_insertion_tree[j].nextValue }; - pending_insertion_tree.push_back(new_leaf); - - // Update the pending low nullifier to point at the new value - pending_insertion_tree[j].nextIndex = insertion_index; - pending_insertion_tree[j].nextValue = new_value; - - break; - } - } - - // add empty low nullifier - sibling_paths.push_back(empty_sp); - low_nullifier_indexes.push_back(empty_index); - low_nullifiers.push_back(empty_leaf); - } else { - // Update the touched mapping - if (prev_nodes == touched_nodes.end()) { - std::vector const new_touched_values = { new_value }; - touched_nodes[current] = new_touched_values; - } else { - prev_nodes->second.push_back(new_value); - } - - nullifier_leaf const low_nullifier = leaves_[current].unwrap(); - std::vector const sibling_path = this->get_sibling_path(current); - - sibling_paths.push_back(sibling_path); - low_nullifier_indexes.push_back(static_cast(current)); - low_nullifiers.push_back(low_nullifier); - - // Update the current low nullifier - nullifier_leaf const new_leaf = { .value = low_nullifier.value, - .nextIndex = insertion_index, - .nextValue = new_value }; - - // Update the old leaf in the tree - // update old value in tree - update_element_in_place(current, new_leaf); - } - } - - // Return tuple of low nullifiers and sibling paths - return std::make_tuple(low_nullifiers, sibling_paths, low_nullifier_indexes); -} - -void NullifierMemoryTreeTestingHarness::update_element_in_place(size_t index, const nullifier_leaf& leaf) -{ - // Find the leaf with the value closest and less than `value` - this->leaves_[index].set(leaf); - update_element(index, leaf.hash()); -} - -std::pair NullifierMemoryTreeTestingHarness::find_lower(fr const& value) -{ - size_t current = 0; - bool is_already_present = false; - std::tie(current, is_already_present) = find_closest_leaf(leaves_, value); - - // TODO: handle is already present case - if (!is_already_present) { - return std::make_pair(leaves_[current].unwrap(), current); - } - return std::make_pair(leaves_[current].unwrap(), current); -} \ No newline at end of file diff --git a/circuits/cpp/src/aztec3/circuits/rollup/test_utils/nullifier_tree_testing_harness.hpp b/circuits/cpp/src/aztec3/circuits/rollup/test_utils/nullifier_tree_testing_harness.hpp deleted file mode 100644 index fc8384a3622..00000000000 --- a/circuits/cpp/src/aztec3/circuits/rollup/test_utils/nullifier_tree_testing_harness.hpp +++ /dev/null @@ -1,58 +0,0 @@ -#pragma once -#include "aztec3/circuits/abis/append_only_tree_snapshot.hpp" -#include "aztec3/utils/types/native_types.hpp" - -#include - -#include - -namespace { -using aztec3::utils::types::NativeTypes; -} // namespace -/** - * A version of the nullifier memory tree with extra methods specific to testing our rollup circuits. - */ -class NullifierMemoryTreeTestingHarness : public proof_system::plonk::stdlib::merkle_tree::NullifierMemoryTree { - using nullifier_leaf = proof_system::plonk::stdlib::merkle_tree::nullifier_leaf; - - public: - explicit NullifierMemoryTreeTestingHarness(size_t depth); - - using MemoryTree::get_hash_path; - using MemoryTree::root; - using MemoryTree::update_element; - - using NullifierMemoryTree::update_element; - - using NullifierMemoryTree::get_hashes; - using NullifierMemoryTree::get_leaf; - using NullifierMemoryTree::get_leaves; - - // Get the value immediately lower than the given value - std::pair find_lower(fr const& value); - - // Utilities to inspect tree - fr total_size() const { return total_size_; } - fr depth() const { return depth_; } - - // Current size of the tree - fr size() { return leaves_.size(); } - - aztec3::circuits::abis::AppendOnlyTreeSnapshot get_snapshot() - { - return { .root = root(), .next_available_leaf_index = static_cast(leaves_.size()) }; - } - - void update_element_in_place(size_t index, const nullifier_leaf& leaf); - - // Get all of the sibling paths and low nullifier values required to craft an non membership / inclusion proofs - std::tuple, std::vector>, std::vector> - circuit_prep_batch_insert(std::vector const& values); - - protected: - using MemoryTree::depth_; - using MemoryTree::hashes_; - using MemoryTree::root_; - using MemoryTree::total_size_; - using NullifierMemoryTree::leaves_; -}; \ No newline at end of file diff --git a/circuits/cpp/src/aztec3/circuits/rollup/test_utils/utils.cpp b/circuits/cpp/src/aztec3/circuits/rollup/test_utils/utils.cpp deleted file mode 100644 index 7fa9db9203a..00000000000 --- a/circuits/cpp/src/aztec3/circuits/rollup/test_utils/utils.cpp +++ /dev/null @@ -1,599 +0,0 @@ -#include "utils.hpp" - -#include "nullifier_tree_testing_harness.hpp" - -#include "aztec3/circuits/abis/global_variables.hpp" -#include "aztec3/circuits/abis/membership_witness.hpp" -#include "aztec3/circuits/abis/new_contract_data.hpp" -#include "aztec3/circuits/abis/rollup/root/root_rollup_public_inputs.hpp" -#include "aztec3/circuits/hash.hpp" -#include "aztec3/circuits/kernel/private/utils.hpp" -#include "aztec3/circuits/rollup/base/init.hpp" -#include "aztec3/constants.hpp" - -#include - -#include -#include -#include -#include -namespace { -using NT = aztec3::utils::types::NativeTypes; - -using ConstantRollupData = aztec3::circuits::abis::ConstantRollupData; -using BaseRollupInputs = aztec3::circuits::abis::BaseRollupInputs; -using RootRollupInputs = aztec3::circuits::abis::RootRollupInputs; -using RootRollupPublicInputs = aztec3::circuits::abis::RootRollupPublicInputs; -using DummyCircuitBuilder = aztec3::utils::DummyCircuitBuilder; - -using Aggregator = aztec3::circuits::recursion::Aggregator; -using AppendOnlyTreeSnapshot = aztec3::circuits::abis::AppendOnlyTreeSnapshot; -using KernelData = aztec3::circuits::abis::PreviousKernelData; - -using NullifierLeafPreimage = aztec3::circuits::abis::NullifierLeafPreimage; - -using MemoryStore = stdlib::merkle_tree::MemoryStore; -using MerkleTree = stdlib::merkle_tree::MerkleTree; -using NullifierTree = stdlib::merkle_tree::NullifierMemoryTree; -using NullifierLeaf = stdlib::merkle_tree::nullifier_leaf; - -using aztec3::circuits::abis::MembershipWitness; -using MergeRollupInputs = aztec3::circuits::abis::MergeRollupInputs; -using aztec3::circuits::abis::PreviousRollupData; - -using nullifier_tree_testing_values = std::tuple; - -using aztec3::circuits::kernel::private_kernel::utils::dummy_previous_kernel; -} // namespace - -namespace aztec3::circuits::rollup::test_utils::utils { - -// Want some helper functions for generating kernels with some commitments, nullifiers and contracts - -std::vector get_empty_calldata_leaf() -{ - auto const number_of_inputs = - (MAX_NEW_COMMITMENTS_PER_TX + MAX_NEW_NULLIFIERS_PER_TX + MAX_PUBLIC_DATA_UPDATE_REQUESTS_PER_TX * 2 + - MAX_NEW_L2_TO_L1_MSGS_PER_TX + MAX_NEW_CONTRACTS_PER_TX * 3 + NUM_ENCRYPTED_LOGS_HASHES_PER_TX * 2 + - NUM_UNENCRYPTED_LOGS_HASHES_PER_TX * 2) * - 2; - - // We subtract 4 from inputs size because 1 logs hash is stored in 2 fields and those 2 fields get converted only - // to 256 bits and there are 4 logs hashes in total. - auto const size = (number_of_inputs - 4) * 32; - std::vector input_data(size, 0); - return input_data; -} - -KernelData get_empty_kernel() -{ - return dummy_previous_kernel(); -} - -std::array get_empty_l1_to_l2_messages() -{ - std::array l1_to_l2_messages = { 0 }; - return l1_to_l2_messages; -} - -BaseRollupInputs base_rollup_inputs_from_kernels(std::array kernel_data, - fr prev_global_variables_hash, - MerkleTree& note_hash_tree, - MerkleTree& nullifier_tree, - MerkleTree& contract_tree, - MerkleTree& public_data_tree, - MerkleTree& l1_to_l2_msg_tree) -{ - // @todo Look at the starting points for all of these. - // By supporting as inputs we can make very generic tests, where it is trivial to try new setups. - MemoryStore archive_store; - MerkleTree archive = MerkleTree(archive_store, ARCHIVE_HEIGHT); - - - BaseRollupInputs baseRollupInputs = { .kernel_data = kernel_data, - .start_note_hash_tree_snapshot = { - .root = note_hash_tree.root(), - .next_available_leaf_index = 0, - }, - .start_contract_tree_snapshot = { - .root = contract_tree.root(), - .next_available_leaf_index = 0, - } - }; - - - std::vector initial_values(2 * MAX_NEW_NULLIFIERS_PER_TX - 1); - - for (size_t i = 0; i < initial_values.size(); i++) { - initial_values[i] = i + 1; - } - - std::array nullifiers; - for (size_t i = 0; i < 2; i++) { - for (size_t j = 0; j < MAX_NEW_NULLIFIERS_PER_TX; j++) { - nullifiers[i * MAX_NEW_NULLIFIERS_PER_TX + j] = kernel_data[i].public_inputs.end.new_nullifiers[j]; - } - } - - // TODO(lasse): It is a bit hacky here that it is always the same location we are inserting it. - - auto temp = generate_nullifier_tree_testing_values_explicit(baseRollupInputs, nullifiers, initial_values); - baseRollupInputs = std::get<0>(temp); - - baseRollupInputs.new_contracts_subtree_sibling_path = - get_sibling_path(contract_tree, 0, CONTRACT_SUBTREE_HEIGHT); - - baseRollupInputs.new_commitments_subtree_sibling_path = - get_sibling_path(note_hash_tree, 0, NOTE_HASH_SUBTREE_HEIGHT); - - - // Update public data tree to generate sibling paths: we first set the initial public data tree to the result of all - // public data reads and old_values from public data update requests. Note that, if the right tx reads or writes an - // index that was already processed by the left one, we don't want to reflect that as part of the initial state, so - // we skip those. - std::set visited_indices; - for (size_t i = 0; i < 2; i++) { - for (auto public_data_read : kernel_data[i].public_inputs.end.public_data_reads) { - auto leaf_index = uint256_t(public_data_read.leaf_index); - if (public_data_read.is_empty() || visited_indices.contains(leaf_index)) { - continue; - } - visited_indices.insert(leaf_index); - public_data_tree.update_element(leaf_index, public_data_read.value); - } - - for (auto public_data_update_request : kernel_data[i].public_inputs.end.public_data_update_requests) { - auto leaf_index = uint256_t(public_data_update_request.leaf_index); - if (public_data_update_request.is_empty() || visited_indices.contains(leaf_index)) { - continue; - } - visited_indices.insert(leaf_index); - public_data_tree.update_element(leaf_index, public_data_update_request.old_value); - } - } - - baseRollupInputs.start_public_data_tree_root = public_data_tree.root(); - - // create the original blocks tree leaf - auto block_hash = compute_block_hash(prev_global_variables_hash, - note_hash_tree.root(), - nullifier_tree.root(), - contract_tree.root(), - l1_to_l2_msg_tree.root(), - public_data_tree.root()); - archive.update_element(0, block_hash); - - ConstantRollupData const constantRollupData = { .start_archive_snapshot = { - .root = archive.root(), - .next_available_leaf_index = 1, - } }; - baseRollupInputs.constants = constantRollupData; - - // Set historical tree roots data in the public inputs. - for (size_t i = 0; i < 2; i++) { - kernel_data[i].public_inputs.constants.block_header.note_hash_tree_root = note_hash_tree.root(); - kernel_data[i].public_inputs.constants.block_header.nullifier_tree_root = nullifier_tree.root(); - kernel_data[i].public_inputs.constants.block_header.nullifier_tree_root = nullifier_tree.root(); - kernel_data[i].public_inputs.constants.block_header.contract_tree_root = contract_tree.root(); - kernel_data[i].public_inputs.constants.block_header.l1_to_l2_message_tree_root = l1_to_l2_msg_tree.root(); - kernel_data[i].public_inputs.constants.block_header.archive_root = archive.root(); - kernel_data[i].public_inputs.constants.block_header.public_data_tree_root = public_data_tree.root(); - kernel_data[i].public_inputs.constants.block_header.global_variables_hash = prev_global_variables_hash; - } - - // Then we collect all sibling paths for the reads in the left tx, and then apply the update requests while - // collecting their paths. And then repeat for the right tx. - for (size_t i = 0; i < 2; i++) { - for (size_t j = 0; j < MAX_PUBLIC_DATA_READS_PER_TX; j++) { - auto public_data_read = kernel_data[i].public_inputs.end.public_data_reads[j]; - if (public_data_read.is_empty()) { - continue; - } - auto leaf_index = uint256_t(public_data_read.leaf_index); - baseRollupInputs.new_public_data_reads_sibling_paths[i * MAX_PUBLIC_DATA_READS_PER_TX + j] = - get_sibling_path(public_data_tree, leaf_index); - } - - for (size_t j = 0; j < MAX_PUBLIC_DATA_UPDATE_REQUESTS_PER_TX; j++) { - auto public_data_update_request = kernel_data[i].public_inputs.end.public_data_update_requests[j]; - if (public_data_update_request.is_empty()) { - continue; - } - auto leaf_index = uint256_t(public_data_update_request.leaf_index); - public_data_tree.update_element(leaf_index, public_data_update_request.new_value); - baseRollupInputs - .new_public_data_update_requests_sibling_paths[i * MAX_PUBLIC_DATA_UPDATE_REQUESTS_PER_TX + j] = - get_sibling_path(public_data_tree, leaf_index); - } - } - - // Get historical_root sibling paths - baseRollupInputs.archive_root_membership_witnesses[0] = { - .leaf_index = 0, - .sibling_path = get_sibling_path(archive, 0, 0), - }; - baseRollupInputs.archive_root_membership_witnesses[1] = baseRollupInputs.archive_root_membership_witnesses[0]; - - baseRollupInputs.kernel_data = kernel_data; - - return baseRollupInputs; -} - -BaseRollupInputs base_rollup_inputs_from_kernels(std::array kernel_data) -{ - MemoryStore note_hash_store; - MerkleTree note_hash_tree = MerkleTree(note_hash_store, NOTE_HASH_TREE_HEIGHT); - MemoryStore contract_tree_store; - MerkleTree contract_tree = MerkleTree(contract_tree_store, CONTRACT_TREE_HEIGHT); - MemoryStore l1_to_l2_messages_store; - MerkleTree l1_to_l2_message_tree = MerkleTree(l1_to_l2_messages_store, L1_TO_L2_MSG_TREE_HEIGHT); - - MemoryStore public_data_tree_store; - MerkleTree public_data_tree(public_data_tree_store, PUBLIC_DATA_TREE_HEIGHT); - - - return base_rollup_inputs_from_kernels( - std::move(kernel_data), note_hash_tree, contract_tree, public_data_tree, l1_to_l2_message_tree); -} - -BaseRollupInputs base_rollup_inputs_from_kernels(std::array kernel_data, - abis::GlobalVariables global_variables) -{ - MemoryStore note_hash_store; - MerkleTree note_hash_tree = MerkleTree(note_hash_store, NOTE_HASH_TREE_HEIGHT); - MemoryStore nullifier_data_store; - MerkleTree nullifier_tree = MerkleTree(nullifier_data_store, NOTE_HASH_TREE_HEIGHT); - MemoryStore contract_tree_store; - MerkleTree contract_tree = MerkleTree(contract_tree_store, CONTRACT_TREE_HEIGHT); - MemoryStore l1_to_l2_messages_store; - MerkleTree l1_to_l2_message_tree = MerkleTree(l1_to_l2_messages_store, L1_TO_L2_MSG_TREE_HEIGHT); - - MemoryStore public_data_tree_store; - MerkleTree public_data_tree(public_data_tree_store, PUBLIC_DATA_TREE_HEIGHT); - - - return base_rollup_inputs_from_kernels(std::move(kernel_data), - global_variables.hash(), - note_hash_tree, - nullifier_tree, - contract_tree, - public_data_tree, - l1_to_l2_message_tree); -} - -BaseRollupInputs base_rollup_inputs_from_kernels(std::array kernel_data, - MerkleTree& note_hash_tree, - MerkleTree& contract_tree, - MerkleTree& public_data_tree, - MerkleTree& l1_to_l2_msg_tree) -{ - MemoryStore nullifier_tree_store; - MerkleTree nullifier_tree = MerkleTree(nullifier_tree_store, NULLIFIER_TREE_HEIGHT); - - abis::GlobalVariables prev_globals; - - return base_rollup_inputs_from_kernels(std::move(kernel_data), - prev_globals.hash(), - note_hash_tree, - nullifier_tree, - contract_tree, - public_data_tree, - l1_to_l2_msg_tree); -} - -std::array, 2> get_previous_rollup_data(DummyBuilder& builder, - std::array kernel_data) -{ - // NOTE: Still assuming that this is first and second. Don't handle more rollups atm - auto base_rollup_input_1 = base_rollup_inputs_from_kernels({ kernel_data[0], kernel_data[1] }); - auto base_public_input_1 = - aztec3::circuits::rollup::native_base_rollup::base_rollup_circuit(builder, base_rollup_input_1); - - // Build the trees based on inputs in base_rollup_input_1. - MemoryStore note_hash_store; - MerkleTree note_hash_tree = MerkleTree(note_hash_store, NOTE_HASH_TREE_HEIGHT); - MemoryStore contract_tree_store; - MerkleTree contract_tree = MerkleTree(contract_tree_store, CONTRACT_TREE_HEIGHT); - std::vector initial_values(2 * MAX_NEW_NULLIFIERS_PER_TX - 1); - - for (size_t i = 0; i < initial_values.size(); i++) { - initial_values[i] = i + 1; - } - std::array nullifiers; - - for (size_t i = 0; i < 2; i++) { - for (size_t j = 0; j < MAX_NEW_COMMITMENTS_PER_TX; j++) { - note_hash_tree.update_element(i * MAX_NEW_COMMITMENTS_PER_TX + j, - kernel_data[i].public_inputs.end.new_commitments[j]); - } - auto contract_data = kernel_data[i].public_inputs.end.new_contracts[0]; - if (!contract_data.is_empty()) { - contract_tree.update_element(i, contract_data.hash()); - } - for (size_t j = 0; j < MAX_NEW_NULLIFIERS_PER_TX; j++) { - initial_values.push_back(kernel_data[i].public_inputs.end.new_nullifiers[j]); - nullifiers[i * MAX_NEW_NULLIFIERS_PER_TX + j] = kernel_data[2 + i].public_inputs.end.new_nullifiers[j]; - } - } - - auto base_rollup_input_2 = base_rollup_inputs_from_kernels({ kernel_data[2], kernel_data[3] }); - auto temp = generate_nullifier_tree_testing_values_explicit(base_rollup_input_2, nullifiers, initial_values); - base_rollup_input_2 = std::get<0>(temp); - - base_rollup_input_2.start_note_hash_tree_snapshot = base_public_input_1.end_note_hash_tree_snapshot; - base_rollup_input_2.start_nullifier_tree_snapshot = base_public_input_1.end_nullifier_tree_snapshot; - base_rollup_input_2.start_contract_tree_snapshot = base_public_input_1.end_contract_tree_snapshot; - - base_rollup_input_2.new_contracts_subtree_sibling_path = - get_sibling_path(contract_tree, 2, CONTRACT_SUBTREE_HEIGHT); - base_rollup_input_2.new_commitments_subtree_sibling_path = get_sibling_path( - note_hash_tree, 2 * MAX_NEW_COMMITMENTS_PER_TX, NOTE_HASH_SUBTREE_HEIGHT); - - auto base_public_input_2 = - aztec3::circuits::rollup::native_base_rollup::base_rollup_circuit(builder, base_rollup_input_2); - - PreviousRollupData const previous_rollup1 = { - .base_or_merge_rollup_public_inputs = base_public_input_1, - .proof = kernel_data[0].proof, - .vk = kernel_data[0].vk, - .vk_index = 0, - .vk_sibling_path = MembershipWitness(), - }; - PreviousRollupData const previous_rollup2 = { - .base_or_merge_rollup_public_inputs = base_public_input_2, - .proof = kernel_data[2].proof, - .vk = kernel_data[2].vk, - .vk_index = 0, - .vk_sibling_path = MembershipWitness(), - }; - - return { previous_rollup1, previous_rollup2 }; -} - -MergeRollupInputs get_merge_rollup_inputs(utils::DummyBuilder& builder, std::array kernel_data) -{ - MergeRollupInputs inputs = { .previous_rollup_data = get_previous_rollup_data(builder, std::move(kernel_data)) }; - return inputs; -} - - -RootRollupInputs get_root_rollup_inputs(utils::DummyBuilder& builder, - std::array kernel_data, - std::array l1_to_l2_messages) -{ - abis::GlobalVariables globals = { 0, 0, 0, 0 }; - - MemoryStore note_hash_store; - const MerkleTree note_hash_tree(note_hash_store, NOTE_HASH_TREE_HEIGHT); - - auto nullifier_tree = get_initial_nullifier_tree_empty(); - - MemoryStore contract_tree_store; - const MerkleTree contract_tree(contract_tree_store, CONTRACT_TREE_HEIGHT); - - MemoryStore l1_to_l2_msg_tree_store; - MerkleTree l1_to_l2_msg_tree(l1_to_l2_msg_tree_store, L1_TO_L2_MSG_TREE_HEIGHT); - - MemoryStore public_data_tree_store; - MerkleTree public_data_tree(public_data_tree_store, PUBLIC_DATA_TREE_HEIGHT); - - MemoryStore archive_store; - MerkleTree archive(archive_store, ARCHIVE_HEIGHT); - - // Start blocks tree - auto block_hash = compute_block_hash_with_globals(globals, - note_hash_tree.root(), - nullifier_tree.root(), - contract_tree.root(), - l1_to_l2_msg_tree.root(), - public_data_tree.root()); - archive.update_element(0, block_hash); - - // Blocks tree snapshots - AppendOnlyTreeSnapshot const start_archive_snapshot = { - .root = archive.root(), - .next_available_leaf_index = 1, - }; - - // Blocks tree - auto archive_sibling_path = get_sibling_path(archive, 1, 0); - - // l1 to l2 tree - auto l1_to_l2_tree_sibling_path = - get_sibling_path(l1_to_l2_msg_tree, 0, L1_TO_L2_MSG_SUBTREE_HEIGHT); - - // l1_to_l2_message tree snapshots - AppendOnlyTreeSnapshot const start_l1_to_l2_msg_tree_snapshot = { - .root = l1_to_l2_msg_tree.root(), - .next_available_leaf_index = 0, - }; - - RootRollupInputs rootRollupInputs = { - .previous_rollup_data = get_previous_rollup_data(builder, std::move(kernel_data)), - .new_l1_to_l2_messages = l1_to_l2_messages, - .new_l1_to_l2_message_tree_root_sibling_path = l1_to_l2_tree_sibling_path, - .start_l1_to_l2_message_tree_snapshot = start_l1_to_l2_msg_tree_snapshot, - .start_archive_snapshot = start_archive_snapshot, - .new_archive_sibling_path = archive_sibling_path, - }; - return rootRollupInputs; -} - -////////////////////////// -// NULLIFIER TREE BELOW // -////////////////////////// - -/** - * @brief Get initial nullifier tree object - * - * @return NullifierMemoryTreeTestingHarness - */ -NullifierMemoryTreeTestingHarness get_initial_nullifier_tree_empty() -{ - NullifierMemoryTreeTestingHarness nullifier_tree = NullifierMemoryTreeTestingHarness(NULLIFIER_TREE_HEIGHT); - for (size_t i = 0; i < (MAX_NEW_NULLIFIERS_PER_TX * 2 - 1); i++) { - nullifier_tree.update_element(i + 1); - } - return nullifier_tree; -} - -/** - * @brief Get initial nullifier tree object - * - * @param initial_values values to pre-populate the tree - * @return NullifierMemoryTreeTestingHarness - */ -NullifierMemoryTreeTestingHarness get_initial_nullifier_tree(const std::vector& initial_values) -{ - NullifierMemoryTreeTestingHarness nullifier_tree = NullifierMemoryTreeTestingHarness(NULLIFIER_TREE_HEIGHT); - for (const auto& initial_value : initial_values) { - nullifier_tree.update_element(initial_value); - } - return nullifier_tree; -} - -nullifier_tree_testing_values generate_nullifier_tree_testing_values(BaseRollupInputs inputs, - size_t starting_insertion_value = 0, - size_t spacing = 5) -{ - const size_t NUMBER_OF_NULLIFIERS = MAX_NEW_NULLIFIERS_PER_TX * 2; - std::array nullifiers; - for (size_t i = 0; i < NUMBER_OF_NULLIFIERS; ++i) { - auto insertion_val = (starting_insertion_value + i * spacing); - nullifiers[i] = fr(insertion_val); - } - - // Generate initial values lin spaced - std::vector initial_values; - for (size_t i = 1; i < NUMBER_OF_NULLIFIERS; ++i) { - initial_values.emplace_back(i * spacing); - } - - return utils::generate_nullifier_tree_testing_values_explicit(std::move(inputs), nullifiers, initial_values); -} - -nullifier_tree_testing_values generate_nullifier_tree_testing_values( - BaseRollupInputs inputs, std::array new_nullifiers, size_t spacing = 5) -{ - // Generate initial values lin spaced - std::vector initial_values; - for (size_t i = 1; i < 2 * MAX_NEW_NULLIFIERS_PER_TX; ++i) { - initial_values.emplace_back(i * spacing); - } - - return utils::generate_nullifier_tree_testing_values_explicit(std::move(inputs), new_nullifiers, initial_values); -} - -nullifier_tree_testing_values generate_nullifier_tree_testing_values_explicit( - BaseRollupInputs rollupInputs, - std::array new_nullifiers, - const std::vector& initial_values) -{ - size_t const start_tree_size = initial_values.size() + 1; - // Generate nullifier tree testing values - NullifierMemoryTreeTestingHarness nullifier_tree = get_initial_nullifier_tree(initial_values); - NullifierMemoryTreeTestingHarness reference_tree = get_initial_nullifier_tree(initial_values); - - AppendOnlyTreeSnapshot const nullifier_tree_start_snapshot = nullifier_tree.get_snapshot(); - - const size_t NUMBER_OF_NULLIFIERS = MAX_NEW_NULLIFIERS_PER_TX * 2; - std::array new_nullifier_leaves{}; - - // Calculate the predecessor nullifier pre-images - // Get insertion values - std::vector insertion_values; - std::array new_nullifiers_kernel_1{}; - std::array new_nullifiers_kernel_2{}; - - for (size_t i = 0; i < NUMBER_OF_NULLIFIERS; ++i) { - auto insertion_val = new_nullifiers[i]; - if (i < MAX_NEW_NULLIFIERS_PER_TX) { - new_nullifiers_kernel_1[i] = insertion_val; - } else { - new_nullifiers_kernel_2[i - MAX_NEW_NULLIFIERS_PER_TX] = insertion_val; - } - insertion_values.push_back(insertion_val); - reference_tree.update_element(insertion_val); - } - - // Get the hash paths etc from the insertion values - auto witnesses_and_preimages = nullifier_tree.circuit_prep_batch_insert(insertion_values); - - auto new_nullifier_leaves_preimages = std::get<0>(witnesses_and_preimages); - auto new_nullifier_leaves_sibling_paths = std::get<1>(witnesses_and_preimages); - auto new_nullifier_leave_indexes = std::get<2>(witnesses_and_preimages); - - // Create witness values from this - std::array, NUMBER_OF_NULLIFIERS> new_membership_witnesses{}; - for (size_t i = 0; i < NUMBER_OF_NULLIFIERS; i++) { - // create an array of the witness from the depth - std::array witness_array{}; - std::copy(new_nullifier_leaves_sibling_paths[i].begin(), - new_nullifier_leaves_sibling_paths[i].end(), - witness_array.begin()); - - MembershipWitness const witness = { - .leaf_index = static_cast(new_nullifier_leave_indexes[i]), - .sibling_path = witness_array, - }; - new_membership_witnesses[i] = witness; - - // Create circuit compatible preimages - issue created to remove this step - NullifierLeafPreimage const preimage = { - .leaf_value = new_nullifier_leaves_preimages[i].value, - .next_value = new_nullifier_leaves_preimages[i].nextValue, - .next_index = NT::uint32(new_nullifier_leaves_preimages[i].nextIndex), - }; - new_nullifier_leaves[i] = preimage; - } - - // Get expected root with subtrees inserted correctly - // Expected end state - AppendOnlyTreeSnapshot const nullifier_tree_end_snapshot = reference_tree.get_snapshot(); - - std::vector sibling_path = reference_tree.get_sibling_path(start_tree_size); - std::array sibling_path_array; - - // Chop the first NULLIFIER-SUBTREE-DEPTH levels from the sibling_path - sibling_path.erase(sibling_path.begin(), sibling_path.begin() + NULLIFIER_SUBTREE_HEIGHT); - std::copy(sibling_path.begin(), sibling_path.end(), sibling_path_array.begin()); - - // Update our start state - // Nullifier trees - rollupInputs.start_nullifier_tree_snapshot = nullifier_tree_start_snapshot; - rollupInputs.new_nullifiers_subtree_sibling_path = sibling_path_array; - - rollupInputs.kernel_data[0].public_inputs.end.new_nullifiers = new_nullifiers_kernel_1; - rollupInputs.kernel_data[1].public_inputs.end.new_nullifiers = new_nullifiers_kernel_2; - - rollupInputs.low_nullifier_leaf_preimages = new_nullifier_leaves; - rollupInputs.low_nullifier_membership_witness = new_membership_witnesses; - - return std::make_tuple(rollupInputs, nullifier_tree_start_snapshot, nullifier_tree_end_snapshot); -} - -/** - * @brief Compares a hash calculated within a circuit (made up of two field elements) against - * one generated natively, (32 bytes) and checks if they match - * - * @param field_hash - * @param expected_hash - * @return true - * @return false - */ -bool compare_field_hash_to_expected(std::array field_hash, - std::array expected_hash) -{ - auto high_buffer = field_hash[0].to_buffer(); - auto low_buffer = field_hash[1].to_buffer(); - - std::array field_expanded_hash; - for (uint8_t i = 0; i < 16; ++i) { - field_expanded_hash[i] = high_buffer[16 + i]; - field_expanded_hash[16 + i] = low_buffer[16 + i]; - } - - return expected_hash == field_expanded_hash; -} - -} // namespace aztec3::circuits::rollup::test_utils::utils diff --git a/circuits/cpp/src/aztec3/circuits/rollup/test_utils/utils.hpp b/circuits/cpp/src/aztec3/circuits/rollup/test_utils/utils.hpp deleted file mode 100644 index 0bc7e8a069f..00000000000 --- a/circuits/cpp/src/aztec3/circuits/rollup/test_utils/utils.hpp +++ /dev/null @@ -1,130 +0,0 @@ -#pragma once -#include "init.hpp" -#include "nullifier_tree_testing_harness.hpp" - -#include "aztec3/circuits/abis/public_data_update_request.hpp" -#include "aztec3/circuits/hash.hpp" -#include "aztec3/constants.hpp" - -#include - -namespace aztec3::circuits::rollup::test_utils::utils { - -namespace { - -using NT = aztec3::utils::types::NativeTypes; - -// Helpers -using aztec3::circuits::get_sibling_path; - -// Types -using ConstantRollupData = aztec3::circuits::abis::ConstantRollupData; -using BaseRollupInputs = aztec3::circuits::abis::BaseRollupInputs; -using MergeRollupInputs = aztec3::circuits::abis::MergeRollupInputs; -using BaseOrMergeRollupPublicInputs = aztec3::circuits::abis::BaseOrMergeRollupPublicInputs; -using RootRollupInputs = aztec3::circuits::abis::RootRollupInputs; -using DummyBuilder = aztec3::utils::DummyCircuitBuilder; - -using Aggregator = aztec3::circuits::recursion::Aggregator; -using AppendOnlyTreeSnapshot = aztec3::circuits::abis::AppendOnlyTreeSnapshot; - -using NullifierLeafPreimage = aztec3::circuits::abis::NullifierLeafPreimage; - -// Tree Aliases -using MemoryStore = stdlib::merkle_tree::MemoryStore; -using MerkleTree = stdlib::merkle_tree::MerkleTree; -using NullifierTree = stdlib::merkle_tree::NullifierMemoryTree; -using NullifierLeaf = stdlib::merkle_tree::nullifier_leaf; - -using KernelData = aztec3::circuits::abis::PreviousKernelData; - -using aztec3::circuits::abis::MembershipWitness; -using aztec3::circuits::abis::PreviousRollupData; - -using nullifier_tree_testing_values = std::tuple; -} // namespace - -BaseRollupInputs base_rollup_inputs_from_kernels(std::array kernel_data); - -BaseRollupInputs base_rollup_inputs_from_kernels(std::array kernel_data, - abis::GlobalVariables global_variables); - -BaseRollupInputs base_rollup_inputs_from_kernels(std::array kernel_data, - MerkleTree& note_hash_tree, - MerkleTree& contract_tree, - MerkleTree& public_data_tree, - MerkleTree& l1_to_l2_msg_tree); - -BaseRollupInputs base_rollup_inputs_from_kernels(std::array kernel_data, - fr prev_global_variables_hash, - MerkleTree& note_hash_tree, - MerkleTree& nullifier_tree, - MerkleTree& contract_tree, - MerkleTree& public_data_tree, - MerkleTree& l1_to_l2_msg_tree); - - -template std::array get_sibling_path(MerkleTree& tree, uint256_t leafIndex) -{ - std::array siblingPath; - auto path = tree.get_hash_path(leafIndex); - for (size_t i = 0; i < N; i++) { - if (leafIndex & (uint256_t(1) << i)) { - siblingPath[i] = path[i].first; - } else { - siblingPath[i] = path[i].second; - } - } - return siblingPath; -} - -abis::AppendOnlyTreeSnapshot get_snapshot_of_tree_state(NullifierMemoryTreeTestingHarness nullifier_tree); - -nullifier_tree_testing_values generate_nullifier_tree_testing_values_explicit( - BaseRollupInputs inputs, - std::array new_nullifiers, - const std::vector& initial_values); - -nullifier_tree_testing_values generate_nullifier_tree_testing_values(BaseRollupInputs inputs, - size_t starting_insertion_value, - size_t spacing); - -std::array get_empty_l1_to_l2_messages(); - -nullifier_tree_testing_values generate_nullifier_tree_testing_values( - BaseRollupInputs inputs, std::array new_nullifiers, size_t spacing); - -NullifierMemoryTreeTestingHarness get_initial_nullifier_tree_empty(); -NullifierMemoryTreeTestingHarness get_initial_nullifier_tree(const std::vector& initial_values); - -KernelData get_empty_kernel(); - -RootRollupInputs get_root_rollup_inputs(utils::DummyBuilder& builder, - std::array kernel_data, - std::array l1_to_l2_messages); - -MergeRollupInputs get_merge_rollup_inputs(utils::DummyBuilder& builder, std::array kernel_data); - -inline abis::PublicDataUpdateRequest make_public_data_update_request(fr leaf_index, fr old_value, fr new_value) -{ - return abis::PublicDataUpdateRequest{ - .leaf_index = leaf_index, - .old_value = old_value, - .new_value = new_value, - }; -}; - -inline abis::PublicDataRead make_public_read(fr leaf_index, fr value) -{ - return abis::PublicDataRead{ - .leaf_index = leaf_index, - .value = value, - }; -} - -bool compare_field_hash_to_expected(std::array field_hash, - std::array expected_hash); - -std::vector get_empty_calldata_leaf(); - -} // namespace aztec3::circuits::rollup::test_utils::utils diff --git a/circuits/cpp/src/aztec3/constants.hpp b/circuits/cpp/src/aztec3/constants.hpp deleted file mode 100644 index d2c444ac1b3..00000000000 --- a/circuits/cpp/src/aztec3/constants.hpp +++ /dev/null @@ -1,364 +0,0 @@ -#pragma once -#include -#include - -#include -#include -// NOTE: When modifying names of constants or enums do the changes in `src/aztec3/circuits/abis/packers.hpp` as well - -namespace aztec3 { - -/** - * @brief Computes log2 at compile-time for inputs of the form 2^n. - * - * @param input - * @return ⌈ log₂(input) ⌉ - */ -constexpr size_t log2(size_t input) -{ - return static_cast(numeric::get_msb64(static_cast(input))); -} - -constexpr size_t ARGS_LENGTH = 16; -constexpr size_t RETURN_VALUES_LENGTH = 4; - -/** - * Convention for constant array lengths are mainly divided in 2 classes: - * - FUNCTION CALL - * - TRANSACTION - * - * Agreed convention is to use MAX_XXX_PER_CALL resp. MAX_XXX_PER_TX, where XXX denotes a type of element such as - * commitment, or nullifier, e.g.,: - * - MAX_NEW_NULLIFIERS_PER_CALL - * - MAX_NEW_COMMITMENTS_PER_TX - * - * In the kernel circuits, we accumulate elements such as commitments and the nullifiers from all functions calls in a - * transaction. Therefore, we always must have: - * MAX_XXX_PER_TX ≥ MAX_XXX_PER_CALL - * - * For instance: - * MAX_NEW_COMMITMENTS_PER_TX ≥ MAX_NEW_COMMITMENTS_PER_CALL - * MAX_NEW_NULLIFIERS_PER_TX ≥ MAX_NEW_NULLIFIERS_PER_CALL - * - */ - -// docs:start:constants -// "PER CALL" CONSTANTS -constexpr size_t MAX_NEW_COMMITMENTS_PER_CALL = 16; -constexpr size_t MAX_NEW_NULLIFIERS_PER_CALL = 16; -constexpr size_t MAX_PRIVATE_CALL_STACK_LENGTH_PER_CALL = 4; -constexpr size_t MAX_PUBLIC_CALL_STACK_LENGTH_PER_CALL = 4; -constexpr size_t MAX_NEW_L2_TO_L1_MSGS_PER_CALL = 2; -constexpr size_t MAX_PUBLIC_DATA_UPDATE_REQUESTS_PER_CALL = 16; -constexpr size_t MAX_PUBLIC_DATA_READS_PER_CALL = 16; -constexpr size_t MAX_READ_REQUESTS_PER_CALL = 32; - - -// "PER TRANSACTION" CONSTANTS -constexpr size_t MAX_NEW_COMMITMENTS_PER_TX = MAX_PRIVATE_CALL_STACK_LENGTH_PER_CALL * MAX_NEW_COMMITMENTS_PER_CALL; -constexpr size_t MAX_NEW_NULLIFIERS_PER_TX = MAX_PRIVATE_CALL_STACK_LENGTH_PER_CALL * MAX_NEW_NULLIFIERS_PER_CALL; -constexpr size_t MAX_PRIVATE_CALL_STACK_LENGTH_PER_TX = 8; -constexpr size_t MAX_PUBLIC_CALL_STACK_LENGTH_PER_TX = 8; -constexpr size_t MAX_NEW_L2_TO_L1_MSGS_PER_TX = 2; -constexpr size_t MAX_PUBLIC_DATA_UPDATE_REQUESTS_PER_TX = 16; -constexpr size_t MAX_PUBLIC_DATA_READS_PER_TX = 16; -constexpr size_t MAX_NEW_CONTRACTS_PER_TX = 1; -constexpr size_t MAX_OPTIONALLY_REVEALED_DATA_LENGTH_PER_TX = 4; -constexpr size_t MAX_READ_REQUESTS_PER_TX = MAX_PRIVATE_CALL_STACK_LENGTH_PER_CALL * MAX_READ_REQUESTS_PER_CALL; -constexpr size_t NUM_ENCRYPTED_LOGS_HASHES_PER_TX = 1; -constexpr size_t NUM_UNENCRYPTED_LOGS_HASHES_PER_TX = 1; -// docs:end:constants - -//////////////////////////////////////////////////////////////////////////////// -// ROLLUP CONTRACT CONSTANTS - constants used only in l1-contracts -//////////////////////////////////////////////////////////////////////////////// -constexpr size_t NUMBER_OF_L1_L2_MESSAGES_PER_ROLLUP = 16; -// TODO(961): Use this constant everywhere instead of hard-coded "2". -constexpr size_t KERNELS_PER_BASE_ROLLUP = 2; -constexpr size_t COMMITMENTS_NUM_BYTES_PER_BASE_ROLLUP = KERNELS_PER_BASE_ROLLUP * MAX_NEW_COMMITMENTS_PER_TX * 32; -constexpr size_t MAX_NEW_NULLIFIERS_PER_BASE_ROLLUP = KERNELS_PER_BASE_ROLLUP * MAX_NEW_NULLIFIERS_PER_TX; -constexpr size_t NULLIFIERS_NUM_BYTES_PER_BASE_ROLLUP = MAX_NEW_NULLIFIERS_PER_BASE_ROLLUP * 32; -constexpr size_t MAX_PUBLIC_DATA_UPDATE_REQUESTS_PER_BASE_ROLLUP = - KERNELS_PER_BASE_ROLLUP * MAX_PUBLIC_DATA_UPDATE_REQUESTS_PER_TX; -constexpr size_t PUBLIC_DATA_WRITES_NUM_BYTES_PER_BASE_ROLLUP = - MAX_PUBLIC_DATA_UPDATE_REQUESTS_PER_BASE_ROLLUP * 64; // old value, new value -constexpr size_t MAX_PUBLIC_DATA_READS_PER_BASE_ROLLUP = KERNELS_PER_BASE_ROLLUP * MAX_PUBLIC_DATA_READS_PER_TX; -constexpr size_t CONTRACTS_NUM_BYTES_PER_BASE_ROLLUP = KERNELS_PER_BASE_ROLLUP * MAX_NEW_CONTRACTS_PER_TX * 32; -constexpr size_t CONTRACT_DATA_NUM_BYTES_PER_BASE_ROLLUP = - KERNELS_PER_BASE_ROLLUP * MAX_NEW_CONTRACTS_PER_TX * 64; // aztec address + eth address (padded to 0x20) -constexpr size_t CONTRACT_DATA_NUM_BYTES_PER_BASE_ROLLUP_UNPADDED = - KERNELS_PER_BASE_ROLLUP * MAX_NEW_CONTRACTS_PER_TX * - 52; // same as prev except doesn't pad eth address. So 0x20 (aztec address) + 0x14 (eth address) -constexpr size_t L2_TO_L1_MSGS_NUM_BYTES_PER_BASE_ROLLUP = KERNELS_PER_BASE_ROLLUP * MAX_NEW_L2_TO_L1_MSGS_PER_TX * 32; -constexpr size_t LOGS_HASHES_NUM_BYTES_PER_BASE_ROLLUP = - KERNELS_PER_BASE_ROLLUP * 2 * 32; // 1 for encrypted + 1 for unencrypted - -// TREES RELATED CONSTANTS -constexpr size_t VK_TREE_HEIGHT = 3; -constexpr size_t FUNCTION_TREE_HEIGHT = 5; -constexpr size_t CONTRACT_TREE_HEIGHT = 16; -constexpr size_t NOTE_HASH_TREE_HEIGHT = 32; -constexpr size_t PUBLIC_DATA_TREE_HEIGHT = 254; -constexpr size_t NULLIFIER_TREE_HEIGHT = 20; -constexpr size_t L1_TO_L2_MSG_TREE_HEIGHT = 16; -constexpr size_t ARCHIVE_HEIGHT = 16; -constexpr size_t ROLLUP_VK_TREE_HEIGHT = 8; // TODO: update - - -// SUB-TREES RELATED CONSTANTS -constexpr size_t CONTRACT_SUBTREE_HEIGHT = - static_cast(log2(MAX_NEW_CONTRACTS_PER_TX * KERNELS_PER_BASE_ROLLUP)); -constexpr size_t CONTRACT_SUBTREE_SIBLING_PATH_LENGTH = CONTRACT_TREE_HEIGHT - CONTRACT_SUBTREE_HEIGHT; -constexpr size_t NOTE_HASH_SUBTREE_HEIGHT = - static_cast(log2(KERNELS_PER_BASE_ROLLUP * MAX_NEW_COMMITMENTS_PER_TX)); -constexpr size_t NOTE_HASH_SUBTREE_SIBLING_PATH_LENGTH = NOTE_HASH_TREE_HEIGHT - NOTE_HASH_SUBTREE_HEIGHT; -constexpr size_t NULLIFIER_SUBTREE_HEIGHT = - static_cast(log2(KERNELS_PER_BASE_ROLLUP * MAX_NEW_NULLIFIERS_PER_TX)); -constexpr size_t NULLIFIER_SUBTREE_SIBLING_PATH_LENGTH = NULLIFIER_TREE_HEIGHT - NULLIFIER_SUBTREE_HEIGHT; -constexpr size_t L1_TO_L2_MSG_SUBTREE_HEIGHT = static_cast(log2(NUMBER_OF_L1_L2_MESSAGES_PER_ROLLUP)); -constexpr size_t L1_TO_L2_MSG_SUBTREE_SIBLING_PATH_LENGTH = L1_TO_L2_MSG_TREE_HEIGHT - L1_TO_L2_MSG_SUBTREE_HEIGHT; - - -// MISC CONSTANTS -constexpr size_t FUNCTION_SELECTOR_NUM_BYTES = 4; // must be <= 31 -constexpr size_t MAPPING_SLOT_PEDERSEN_SEPARATOR = 4; -// sha256 hash is stored in two fields to accommodate all 256-bits of the hash -constexpr size_t NUM_FIELDS_PER_SHA256 = 2; - -/** - * Enumerate the hash_indices which are used for pedersen hashing. - * We start from 1 to avoid the default generators. The generator indices are listed - * based on the number of elements each index hashes. The following conditions must be met: - * - * +-----------+-------------------------------+----------------------+ - * | Hash size | Number of elements hashed (n) | Condition to use | - * |-----------+-------------------------------+----------------------| - * | LOW | n ≤ 8 | 0 < hash_index ≤ 32 | - * | MID | 8 < n ≤ 16 | 32 < hash_index ≤ 40 | - * | HIGH | 16 < n ≤ 48 | 40 < hash_index ≤ 48 | - * +-----------+-------------------------------+----------------------+ - * - * Note: When modifying, modify `GeneratorIndexPacker` in packer.hpp accordingly. - */ -enum GeneratorIndex { - /** - * Indices with size ≤ 8 - */ - DEFAULT = 0, - COMMITMENT = 1, // Size = 7 (unused) - COMMITMENT_NONCE, // Size = 2 - UNIQUE_COMMITMENT, // Size = 2 - SILOED_COMMITMENT, // Size = 2 - NULLIFIER, // Size = 4 (unused) - INITIALIZATION_NULLIFIER, // Size = 2 (unused) - OUTER_NULLIFIER, // Size = 2 - PUBLIC_DATA_READ, // Size = 2 - PUBLIC_DATA_UPDATE_REQUEST, // Size = 3 - FUNCTION_DATA, // Size = 4 - FUNCTION_LEAF, // Size = 5 - CONTRACT_DEPLOYMENT_DATA, // Size = 4 - CONSTRUCTOR, // Size = 3 - CONSTRUCTOR_ARGS, // Size = 8 - CONTRACT_ADDRESS, // Size = 4 - CONTRACT_LEAF, // Size = 3 - CALL_CONTEXT, // Size = 6 - CALL_STACK_ITEM, // Size = 3 - CALL_STACK_ITEM_2, // Size = ? (unused), // TODO see function where it's used for explanation - L1_TO_L2_MESSAGE_SECRET, // Size = 1 - L2_TO_L1_MSG, // Size = 2 (unused) - TX_CONTEXT, // Size = 4 - PUBLIC_LEAF_INDEX, // Size = 2 (unused) - PUBLIC_DATA_LEAF, // Size = ? (unused) // TODO what's the expected size? Assuming ≤ 8 - SIGNED_TX_REQUEST, // Size = 7 - GLOBAL_VARIABLES, // Size = 4 - PARTIAL_ADDRESS, // Size = 7 - BLOCK_HASH, // Size = 6 - /** - * Indices with size ≤ 16 - */ - TX_REQUEST = 33, // Size = 14 - SIGNATURE_PAYLOAD, // Size = 13 - /** - * Indices with size ≤ 44 - */ - VK = 41, // Size = 35 - PRIVATE_CIRCUIT_PUBLIC_INPUTS, // Size = 45 - PUBLIC_CIRCUIT_PUBLIC_INPUTS, // Size = 32 (unused) - FUNCTION_ARGS, // Size ≤ 40 - NUMBER_OF_INDICES, -}; - -static constexpr std::string_view AZTEC_DOMAIN = "__AZTEC_"; -static constexpr std::string generatorIndexDomain(GeneratorIndex idx) -{ - switch (idx) { - case DEFAULT: - return "DEFAULT"; - case COMMITMENT: - return "COMMITMENT"; - case COMMITMENT_NONCE: - return "COMMITMENT_NONCE"; - case UNIQUE_COMMITMENT: - return "UNIQUE_COMMITMENT"; - case SILOED_COMMITMENT: - return "SILOED_COMMITMENT"; - case NULLIFIER: - return "NULLIFIER "; - case INITIALIZATION_NULLIFIER: - return "INITIALIZATION_NULLIFIER"; - case OUTER_NULLIFIER: - return "OUTER_NULLIFIER "; - case PUBLIC_DATA_READ: - return "PUBLIC_DATA_READ"; - case PUBLIC_DATA_UPDATE_REQUEST: - return "PUBLIC_DATA_UPDATE_REQUEST"; - case FUNCTION_DATA: - return "FUNCTION_DATA"; - case FUNCTION_LEAF: - return "FUNCTION_LEAF"; - case CONTRACT_DEPLOYMENT_DATA: - return "CONTRACT_DEPLOYMENT_DATA"; - case CONSTRUCTOR: - return "CONSTRUCTOR"; - case CONSTRUCTOR_ARGS: - return "CONSTRUCTOR_ARGS"; - case CONTRACT_ADDRESS: - return "CONTRACT_ADDRESS"; - case CONTRACT_LEAF: - return "CONTRACT_LEAF"; - case CALL_CONTEXT: - return "CALL_CONTEXT"; - case CALL_STACK_ITEM: - return "CALL_STACK_ITEM"; - case CALL_STACK_ITEM_2: - return "CALL_STACK_ITEM_2"; - case L1_TO_L2_MESSAGE_SECRET: - return "L1_TO_L2_MESSAGE_SECRET "; - case L2_TO_L1_MSG: - return "L2_TO_L1_MSG"; - case TX_CONTEXT: - return "TX_CONTEXT"; - case PUBLIC_LEAF_INDEX: - return "PUBLIC_LEAF_INDEX"; - case PUBLIC_DATA_LEAF: - return "PUBLIC_DATA_LEAF"; - case SIGNED_TX_REQUEST: - return "SIGNED_TX_REQUEST"; - case GLOBAL_VARIABLES: - return "GLOBAL_VARIABLES"; - case PARTIAL_ADDRESS: - return "PARTIAL_ADDRESS"; - case BLOCK_HASH: - return "BLOCK_HASH"; - case TX_REQUEST: - return "TX_REQUEST"; - case SIGNATURE_PAYLOAD: - return "SIGNATURE_PAYLOAD"; - case VK: - return "VK"; - case PRIVATE_CIRCUIT_PUBLIC_INPUTS: - return "PRIVATE_CIRCUIT_PUBLIC_INPUTS"; - case PUBLIC_CIRCUIT_PUBLIC_INPUTS: - return "PUBLIC_CIRCUIT_PUBLIC_INPUTS"; - case FUNCTION_ARGS: - return "FUNCTION_ARGS"; - default: { - throw_or_abort("could not convert GeneratorIndex enum to string_view"); - } - } -} - -// Note: When modifying, modify `StorageSlotGeneratorIndexPacker` in packer.hpp accordingly. -enum StorageSlotGeneratorIndex { - BASE_SLOT, - MAPPING_SLOT, - MAPPING_SLOT_PLACEHOLDER, -}; - -// Enumerate the hash_sub_indices which are used for committing to private state note preimages. -// Start from 1. -// Note: When modifying, modify `PrivateStateNoteGeneratorIndexPacker` in packer.hpp accordingly. -enum PrivateStateNoteGeneratorIndex { - VALUE = 1, - OWNER, - CREATOR, - SALT, - NONCE, - MEMO, - IS_DUMMY, -}; - -// Note: When modifying, modify `PrivateStateTypePacker` in packer.hpp accordingly. -enum PrivateStateType { PARTITIONED = 1, WHOLE }; - -//////////////////////////////////////////////////////////////////////////////// -// NOIR CONSTANTS - constants used only in yarn-packages/noir-contracts -// --> Here because Noir doesn't yet support globals referencing other globals yet and doing so in C++ seems to be the -// best thing to do for now. Move these constants to a noir file once the issue bellow is resolved: -// https://github.com/noir-lang/noir/issues/1734 -constexpr size_t L1_TO_L2_MESSAGE_LENGTH = 8; -// message length + sibling path (same size as tree height) + 1 field for root + 1 field for index -constexpr size_t L1_TO_L2_MESSAGE_ORACLE_CALL_LENGTH = L1_TO_L2_MESSAGE_LENGTH + L1_TO_L2_MSG_TREE_HEIGHT + 1 + 1; - -// TODO: Remove these when nested array is supported. -constexpr size_t MAX_NOTE_FIELDS_LENGTH = 20; -// MAX_NOTE_FIELDS_LENGTH + 1: the plus 1 is 1 extra field for nonce. -// + 2 for EXTRA_DATA: [number_of_return_notes, contract_address] -constexpr size_t GET_NOTE_ORACLE_RETURN_LENGTH = MAX_NOTE_FIELDS_LENGTH + 1 + 2; -constexpr size_t GET_NOTES_ORACLE_RETURN_LENGTH = MAX_READ_REQUESTS_PER_CALL * (MAX_NOTE_FIELDS_LENGTH + 1) + 2; -constexpr size_t MAX_NOTES_PER_PAGE = 10; -// + 2 for EXTRA_DATA: [number_of_return_notes, contract_address] -constexpr size_t VIEW_NOTE_ORACLE_RETURN_LENGTH = MAX_NOTES_PER_PAGE * (MAX_NOTE_FIELDS_LENGTH + 1) + 2; - -constexpr size_t CALL_CONTEXT_LENGTH = 7; -// Must be updated if any data is added into the block hash calculation. -constexpr size_t BLOCK_HEADER_LENGTH = 7; -constexpr size_t FUNCTION_DATA_LENGTH = 4; -constexpr size_t CONTRACT_DEPLOYMENT_DATA_LENGTH = 6; - -// Change this ONLY if you have changed the PrivateCircuitPublicInputs structure in C++. -// In other words, if the structure/size of the public inputs of a function call changes then we -// should change this constant as well as the offsets in private_call_stack_item.nr -constexpr size_t PRIVATE_CIRCUIT_PUBLIC_INPUTS_LENGTH = - CALL_CONTEXT_LENGTH + 1 // +1 for args_hash - + RETURN_VALUES_LENGTH + MAX_READ_REQUESTS_PER_CALL + - MAX_NEW_COMMITMENTS_PER_CALL + 2 * MAX_NEW_NULLIFIERS_PER_CALL + MAX_PRIVATE_CALL_STACK_LENGTH_PER_CALL + - MAX_PUBLIC_CALL_STACK_LENGTH_PER_CALL + MAX_NEW_L2_TO_L1_MSGS_PER_CALL + NUM_FIELDS_PER_SHA256 + - NUM_FIELDS_PER_SHA256 + 2 // + 2 for logs preimage lengths - + BLOCK_HEADER_LENGTH + CONTRACT_DEPLOYMENT_DATA_LENGTH + 2; // + 2 for chain_id and version - -constexpr size_t PRIVATE_CIRCUIT_PUBLIC_INPUTS_HASH_INPUT_LENGTH = - 1 + 1 // call_context_hash + args_hash - + RETURN_VALUES_LENGTH + MAX_READ_REQUESTS_PER_CALL + - MAX_NEW_COMMITMENTS_PER_CALL + 2 * MAX_NEW_NULLIFIERS_PER_CALL + MAX_PRIVATE_CALL_STACK_LENGTH_PER_CALL + - MAX_PUBLIC_CALL_STACK_LENGTH_PER_CALL + MAX_NEW_L2_TO_L1_MSGS_PER_CALL + NUM_FIELDS_PER_SHA256 + - NUM_FIELDS_PER_SHA256 + 2 // + 2 for logs preimage lengths - + BLOCK_HEADER_LENGTH + 3; // + 3 for contract_deployment_data.hash(), chain_id, version - -constexpr size_t CONTRACT_STORAGE_UPDATE_REQUEST_LENGTH = 3; -constexpr size_t CONTRACT_STORAGE_READ_LENGTH = 2; - -// Change this ONLY if you have changed the PublicCircuitPublicInputs structure in C++. -constexpr size_t PUBLIC_CIRCUIT_PUBLIC_INPUTS_LENGTH = - CALL_CONTEXT_LENGTH + 1 + RETURN_VALUES_LENGTH + // + 1 for args_hash - MAX_PUBLIC_DATA_UPDATE_REQUESTS_PER_CALL * CONTRACT_STORAGE_UPDATE_REQUEST_LENGTH + - MAX_PUBLIC_DATA_READS_PER_CALL * CONTRACT_STORAGE_READ_LENGTH + MAX_PUBLIC_CALL_STACK_LENGTH_PER_CALL + - MAX_NEW_COMMITMENTS_PER_CALL + MAX_NEW_NULLIFIERS_PER_CALL + MAX_NEW_L2_TO_L1_MSGS_PER_CALL + - NUM_FIELDS_PER_SHA256 + 1 + // + 1 for unencrypted logs preimage length - BLOCK_HEADER_LENGTH + 2; // + 2 for chain_id and version - -constexpr size_t PUBLIC_CIRCUIT_PUBLIC_INPUTS_HASH_INPUT_LENGTH = - 2 + RETURN_VALUES_LENGTH + // + 1 for args_hash + 1 call_context.hash - MAX_PUBLIC_DATA_UPDATE_REQUESTS_PER_CALL + MAX_PUBLIC_DATA_READS_PER_CALL + MAX_PUBLIC_CALL_STACK_LENGTH_PER_CALL + - MAX_NEW_COMMITMENTS_PER_CALL + MAX_NEW_NULLIFIERS_PER_CALL + MAX_NEW_L2_TO_L1_MSGS_PER_CALL + - NUM_FIELDS_PER_SHA256 + // unencrypted_logs_hash (being represented by NUM_FIELDS_PER_SHA256) - BLOCK_HEADER_LENGTH + 2; // unencrypted_log_preimages_length + prover_address - - -// Size of the return value of a private function call, -constexpr size_t CALL_PRIVATE_FUNCTION_RETURN_SIZE = - 1 + FUNCTION_DATA_LENGTH + PRIVATE_CIRCUIT_PUBLIC_INPUTS_LENGTH + 1; - -constexpr size_t EMPTY_NULLIFIED_COMMITMENT = 1000000; - -} // namespace aztec3 \ No newline at end of file diff --git a/circuits/cpp/src/aztec3/dbs/CMakeLists.txt b/circuits/cpp/src/aztec3/dbs/CMakeLists.txt deleted file mode 100644 index 49c80213432..00000000000 --- a/circuits/cpp/src/aztec3/dbs/CMakeLists.txt +++ /dev/null @@ -1,33 +0,0 @@ -if(NOT WASM) - include(FetchContent) - FetchContent_Declare( - leveldb - GIT_REPOSITORY https://github.com/google/leveldb.git - GIT_TAG 1.22 - ) - - FetchContent_GetProperties(leveldb) - if(NOT leveldb_POPULATED) - FetchContent_Populate(leveldb) - set(LEVELDB_BUILD_TESTS OFF CACHE BOOL "LevelDB tests off") - add_subdirectory(${leveldb_SOURCE_DIR} ${leveldb_BINARY_DIR} EXCLUDE_FROM_ALL) - endif() - - target_compile_options( - leveldb - PRIVATE - -Wno-sign-conversion - -Wno-unused-parameter - -Wno-shorten-64-to-32 - -Wno-implicit-int-conversion - -Wno-conversion - -Wno-implicit-fallthrough - ) - - link_libraries(leveldb) -endif() - -circuits_cmake_module( - aztec3_dbs - barretenberg -) \ No newline at end of file diff --git a/circuits/cpp/src/aztec3/dbs/private_state_db.hpp b/circuits/cpp/src/aztec3/dbs/private_state_db.hpp deleted file mode 100644 index 60d4ecaedf8..00000000000 --- a/circuits/cpp/src/aztec3/dbs/private_state_db.hpp +++ /dev/null @@ -1,151 +0,0 @@ -// // TODO: move the leveldb_store to a more neutral module. -// #include "../constants.hpp" - -// #include "aztec3/utils/types/native_types.hpp" - -// #include - -// namespace aztec3::db { - -// using aztec3::GeneratorIndex; -// using crypto::pedersen::hash; - -// char const* PRIVATE_STATE_DB_PATH = "./private_state_var.db"; - -// struct PrivateStateVar { -// bool is_partitioned = false; -// std::vector active_private_state_preimages; -// }; - -// // contract->storageSlots->[current->historical] - -// /** -// * Hmmm... There are multiple active leaves for partitioned states. -// * -// * [contract_Address, storage_slot] -> { -// * is_partitioned, -// * earliest_active_commitment, -// * earliest_commitment, -// * } -// * -// * commitment -> { -// * PrivateStateVar, -// * next_active_commitment, -// * next_commitment, -// * } -// */ - -// template class PrivateStateDb { -// public: -// PrivateStateDb(Store& store, size_t max_state_var_id) -// : store_(store) -// , max_state_var_id_(max_state_var_id) -// {} - -// // we need a linked list through all active commitments, and another linked list through all (active & inactive) -// // commitments. - -// PrivateStateCommitment get_earliest_active_commitment(fr const& contract_address, fr const& state_var_id) -// { -// const fr& storage_slot = state_var_id; -// const fr db_key = -// commit_native(std::vector{ contract_address, storage_slot }, GeneratorIndex::UNIVERSAL_STORAGE_SLOT); -// } - -// PrivateStateCommitment get_earliest_active_commitment(fr const& contract_address, -// fr const& state_var_id, -// fr const& mapping_key) -// { -// const fr storage_slot = -// commit_native(std::vector{ state_var_id, mapping_key }, GeneratorIndex::MAPPING_STORAGE_SLOT); -// const fr db_key = -// commit_native(std::vector{ contract_address, storage_slot }, GeneratorIndex::UNIVERSAL_STORAGE_SLOT); - -// std::vector data; - -// bool success = store_.get(db_key, data); - -// return data; -// } - -// fr get_current_private_state_value(fr const& contract_address, fr const& state_var_id) -// { -// PrivateStateCommitment earliest_active_commitment = -// get_earliest_active_commitment(contract_address, state_var_id); -// } - -// fr get_current_private_state_value(fr const& contract_address, fr const& state_var_id, fr const& mapping_key) {} - -// void write_metadata(std::ostream& os) -// { -// write(os, data_tree_.root()); -// write(os, nullifier_tree_.root()); -// write(os, root_tree_.root()); -// write(os, defi_tree_.root()); -// write(os, data_tree_.size()); -// write(os, nullifier_tree_.size()); -// write(os, root_tree_.size()); -// write(os, defi_tree_.size()); -// } - -// void get(std::istream& is, std::ostream& os) -// { -// GetRequest get_request; -// read(is, get_request); -// // std::cerr << get_request << std::endl; -// auto tree = trees_[get_request.tree_id]; -// auto path = tree->get_hash_path(get_request.index); -// auto leaf = get_request.index & 0x1 ? path[0].second : path[0].first; -// write(os, leaf == fr::neg_one() ? fr(0) : leaf); -// } - -// void get_path(std::istream& is, std::ostream& os) -// { -// GetRequest get_request; -// read(is, get_request); -// // std::cerr << get_request << std::endl; -// auto tree = trees_[get_request.tree_id]; -// auto path = tree->get_hash_path(get_request.index); -// write(os, path); -// } - -// void put(std::istream& is, std::ostream& os) -// { -// PutRequest put_request; -// read(is, put_request); -// // std::cerr << put_request << std::endl; -// PutResponse put_response; -// put_response.root = trees_[put_request.tree_id]->update_element(put_request.index, put_request.value); -// write(os, put_response); -// } - -// void batch_put(std::istream& is, std::ostream& os) -// { -// std::vector put_requests; -// read(is, put_requests); -// for (auto& put_request : put_requests) { -// trees_[put_request.tree_id]->update_element(put_request.index, put_request.value); -// } -// write_metadata(os); -// } - -// void commit(std::ostream& os) -// { -// // std::cerr << "COMMIT" << std::endl; -// store_.commit(); -// write_metadata(os); -// } - -// void rollback(std::ostream& os) -// { -// // std::cerr << "ROLLBACK" << std::endl; -// store_.rollback(); -// write_metadata(os); -// } - -// private: -// Store& store_; -// size_t max_state_var_id_; -// }; - -// } // namespace aztec3::db \ No newline at end of file diff --git a/circuits/cpp/src/aztec3/oracle/CMakeLists.txt b/circuits/cpp/src/aztec3/oracle/CMakeLists.txt deleted file mode 100644 index 1e9734a1d34..00000000000 --- a/circuits/cpp/src/aztec3/oracle/CMakeLists.txt +++ /dev/null @@ -1,5 +0,0 @@ -circuits_cmake_module( - aztec3_oracle - aztec3_circuits_apps - barretenberg -) \ No newline at end of file diff --git a/circuits/cpp/src/aztec3/oracle/README.md b/circuits/cpp/src/aztec3/oracle/README.md deleted file mode 100644 index 43cc17cfa67..00000000000 --- a/circuits/cpp/src/aztec3/oracle/README.md +++ /dev/null @@ -1,13 +0,0 @@ -# aztec3::circuits::oracle - -When Aztec3 makes a call to a circuit executor (such as Noir or the test apps in aztec3/circuits/apps/test_apps), we don't pass _all_ public inputs when calling the circuit; only the `args` inputs are sent initially. - -Why? - -This has two main benefits: - -- We only need one implementation of the circuit's logic: the circuit itself. We don't need to implement a separate native version of the circuit to 'predict' what commitments/nullifiers and other values will need to ultimately become inputs to the circuit. -- There are occasions where it's difficult to predict the inputs required in advance. E.g. if a circuit_1 makes a call to another circuit, circuit_2, at some other contract_address, but that address is calculated within the body of circuit_1. We'd need to independently deduce the address which would be called so that we could grab the correct contract_address, VK, vkIndex, proving key (etc) from the DB. - -Instead, we provide an oracle to the circuit executor when we call it. As the circuit is executed, it'll occasionally reach points where it needs more input data. The circuit executor can query the oracle (the Aztec3 node) for more data. The oracle can grab data from its DB and return it the circuit executor. The circuit executor can then convert the data into witnesses and continue with some more of the circuit's execution. - diff --git a/circuits/cpp/src/aztec3/oracle/fake_db.hpp b/circuits/cpp/src/aztec3/oracle/fake_db.hpp deleted file mode 100644 index a14ffa6f57e..00000000000 --- a/circuits/cpp/src/aztec3/oracle/fake_db.hpp +++ /dev/null @@ -1,161 +0,0 @@ -#pragma once - -#include "aztec3/circuits/abis/call_context.hpp" -#include "aztec3/circuits/apps/notes/default_private_note/note_preimage.hpp" -#include "aztec3/circuits/apps/notes/default_singleton_private_note/note_preimage.hpp" -#include "aztec3/circuits/apps/utxo_datum.hpp" -#include "aztec3/utils/types/native_types.hpp" - -namespace aztec3::oracle { - -using aztec3::circuits::apps::UTXOSLoadDatum; - -using aztec3::circuits::apps::notes::DefaultPrivateNotePreimage; - -using aztec3::circuits::apps::notes::DefaultSingletonPrivateNotePreimage; - -using NT = aztec3::utils::types::NativeTypes; - -// A temporary stub, whilst building other things first. -class FakeDB { - public: - FakeDB() = default; - - /** - * For getting a singleton UTXO (not a set). - * - * NOTICE: this fake db stub is hard-coded to a DefaultPrivateNotePreimage which _itself_ is hard-coded to the value - * type being a field. - * So if you want to test other note types against this stub DB, you'll need to write your own stub DB entry. - */ - static UTXOSLoadDatum> get_utxo_sload_datum( - NT::address const& contract_address, - NT::grumpkin_point const& storage_slot_point, - DefaultPrivateNotePreimage const& advice) - // NT::address const& owner, - // NT::fr required_utxo_tree_root, - // size_t utxo_tree_depth) - { - (void)storage_slot_point; // Not used in this 'fake' implementation. - - DefaultPrivateNotePreimage const preimage{ - .value = 100, - .owner = advice.owner, - .creator_address = 0, - .memo = 3456, - .salt = 1234, - .nonce = 2345, - .is_dummy = false, - }; - - const size_t utxo_tree_depth = 32; - const NT::fr required_utxo_tree_root = 2468; - - std::vector sibling_path(utxo_tree_depth); - std::fill(sibling_path.begin(), sibling_path.end(), 1); // Fill with 1's to be lazy. TODO: return a valid path. - - return { - .commitment = 1, - .contract_address = contract_address, - .preimage = preimage, - - .sibling_path = sibling_path, - .leaf_index = 2, - .historical_note_hash_tree_root = required_utxo_tree_root, - }; - }; - - /** - * For getting a set of UTXOs. - * - * * NOTICE: this fake db stub is hard-coded to a DefaultPrivateNotePreimage which _itself_ is hard-coded to the - * value type being a field. - * So if you want to test other note types against this stub DB, you'll need to write your own stub DB entry. - */ - static std::vector>> get_utxo_sload_data( - NT::address const& contract_address, - NT::grumpkin_point const& storage_slot_point, - size_t const& num_notes, - DefaultPrivateNotePreimage const& advice) - // NT::address const& owner, - // NT::fr required_utxo_tree_root, - // size_t utxo_tree_depth) - { - (void)storage_slot_point; // Not used in this 'fake' implementation. - - std::vector>> data; - - const size_t utxo_tree_depth = 32; - const NT::fr required_utxo_tree_root = 2468; - - std::vector sibling_path(utxo_tree_depth); - std::fill(sibling_path.begin(), sibling_path.end(), 1); // Fill with 1's to be lazy. TODO: return a valid path. - - for (size_t i = 0; i < num_notes; i++) { - DefaultPrivateNotePreimage const preimage{ - .value = 100 + i, - .owner = advice.owner, - .creator_address = 0, - .memo = 3456, - .salt = 1234, - .nonce = 2345, - .is_dummy = false, - }; - - data.push_back({ - .commitment = 1, - .contract_address = contract_address, - .preimage = preimage, - - .sibling_path = sibling_path, - .leaf_index = 2, - .historical_note_hash_tree_root = required_utxo_tree_root, - }); - } - - return data; - }; - - /** - * For getting a singleton UTXO (not a set). - * - * NOTICE: this fake db stub is hard-coded to a DefaultSingletonPrivateNotePreimage which _itself_ is hard-coded to - * the value type being a field. So if you want to test other note types against this stub DB, you'll need to write - * your own stub DB entry. - */ - static UTXOSLoadDatum> get_utxo_sload_datum( - NT::address const& contract_address, - NT::grumpkin_point const& storage_slot_point, - DefaultSingletonPrivateNotePreimage const& advice) - // NT::address const& owner, - // NT::fr required_utxo_tree_root, - // size_t utxo_tree_depth) - { - (void)storage_slot_point; // Not used in this 'fake' implementation. - - DefaultSingletonPrivateNotePreimage const preimage{ - .value = 100, - .owner = advice.owner, - .salt = 1234, - .nonce = 2345, - }; - - const size_t utxo_tree_depth = 32; - const NT::fr required_utxo_tree_root = 2468; - - std::vector sibling_path(utxo_tree_depth); - std::fill(sibling_path.begin(), sibling_path.end(), 1); // Fill with 1's to be lazy. TODO: return a valid path. - - return { - .commitment = 1, - .contract_address = contract_address, - .preimage = preimage, - - .sibling_path = sibling_path, - .leaf_index = 2, - .historical_note_hash_tree_root = required_utxo_tree_root, - }; - }; -}; - -} // namespace aztec3::oracle \ No newline at end of file diff --git a/circuits/cpp/src/aztec3/oracle/oracle.hpp b/circuits/cpp/src/aztec3/oracle/oracle.hpp deleted file mode 100644 index 8744f30f0ec..00000000000 --- a/circuits/cpp/src/aztec3/oracle/oracle.hpp +++ /dev/null @@ -1,150 +0,0 @@ -#pragma once - -#include "fake_db.hpp" - -#include "aztec3/circuits/abis/call_context.hpp" -#include "aztec3/circuits/abis/contract_deployment_data.hpp" -#include "aztec3/circuits/abis/function_data.hpp" -#include "aztec3/circuits/apps/notes/default_singleton_private_note/note_preimage.hpp" -#include "aztec3/circuits/apps/utxo_datum.hpp" -#include "aztec3/utils/types/native_types.hpp" - -namespace aztec3::oracle { - -using aztec3::circuits::abis::CallContext; -using aztec3::circuits::abis::ContractDeploymentData; -using aztec3::circuits::abis::FunctionData; - -using aztec3::circuits::apps::UTXOSLoadDatum; - -using NT = aztec3::utils::types::NativeTypes; - -/// Note: the server will always serve NATIVE types to the circuit, since eventually we'll be passing data to Noir (so -/// won't be handling circuit types at all from the Aztec3 end). -template class NativeOracleInterface { - public: - DB& db; - - NativeOracleInterface(DB& db, - NT::address const& actual_contract_address, - FunctionData const& function_data, - CallContext const& call_context, - std::optional const& msg_sender_private_key = std::nullopt) - : db(db) - , actual_contract_address(actual_contract_address) - , function_data(function_data) - , call_context(call_context) - // , portal_contract_address(portal_contract_address) - , msg_sender_private_key(msg_sender_private_key){}; - - NativeOracleInterface(DB& db, - NT::address const& actual_contract_address, - FunctionData const& function_data, - CallContext const& call_context, - ContractDeploymentData const& contract_deployment_data, - std::optional const& msg_sender_private_key = std::nullopt) - : db(db) - , actual_contract_address(actual_contract_address) - , function_data(function_data) - , call_context(call_context) - , contract_deployment_data(contract_deployment_data) - // , portal_contract_address(portal_contract_address) - , msg_sender_private_key(msg_sender_private_key){}; - - NT::fr get_msg_sender_private_key() - { - if (!msg_sender_private_key) { - throw_or_abort("no private key stored in memory"); - } - if (msg_sender_private_key_already_got) { - throw_or_abort("msg_sender_private_key: " + already_got_error); - } - msg_sender_private_key_already_got = true; - return *msg_sender_private_key; - }; - - // NT::fr get_portal_contract_address() - // { - // if (portal_contract_address_already_got) { - // throw_or_abort(already_got_error); - // } - // portal_contract_address_already_got = true; - // return portal_contract_address; - // }; - - NT::address get_actual_contract_address() { return actual_contract_address; }; - - FunctionData get_function_data() { return function_data; }; - - CallContext get_call_context() - { - if (call_context_already_got) { - throw_or_abort("call_context: " + already_got_error); - } - call_context_already_got = true; - return call_context; - }; - - ContractDeploymentData get_contract_deployment_data() - { - if (contract_deployment_data_already_got) { - throw_or_abort("contract_deployment_data: " + already_got_error); - } - contract_deployment_data_already_got = true; - return contract_deployment_data; - }; - - template - UTXOSLoadDatum get_utxo_sload_datum(NT::grumpkin_point const storage_slot_point, - NotePreimage const advice) - { - // TODO: consider whether it's actually safe to bypass get_call_context() here... - const auto& contract_address = call_context.storage_contract_address; - return db.get_utxo_sload_datum(contract_address, storage_slot_point, advice); - } - - template - std::vector> get_utxo_sload_data(NT::grumpkin_point const storage_slot_point, - size_t const& num_notes, - NotePreimage const advice) - { - // TODO: consider whether it's actually safe to bypass get_call_context() here... - const auto& contract_address = call_context.storage_contract_address; - return db.get_utxo_sload_data(contract_address, storage_slot_point, num_notes, advice); - } - - NT::fr generate_salt() const { return NT::fr::random_element(); } - - NT::fr generate_random_element() const { return NT::fr::random_element(); } - - private: - // We MUST make these values private, so the circuit isn't able to `get` these values more than once (the getter - // functions can check this). This will help us write secure circuits. If we were to query the same thing twice, an - // untrustworthy oracle could give two different pieces of information. As long as this (trusted) oracle catches - // double-queries, we can ensure the circuit we build doesn't query twice. - - // Note: actual_contract_address and function_data are NOT to be provided to the circuit, so don't include - // getter methods for these in the OracleWrapper. - NT::address actual_contract_address; // not to be confused with call_context.storage_contract_address; - FunctionData function_data; - - CallContext call_context; - ContractDeploymentData contract_deployment_data; - // NT::fr portal_contract_address; - std::optional msg_sender_private_key; - - // Ensure functions called only once: - bool actual_contract_address_already_got = false; - bool function_data_already_got = false; - bool call_context_already_got = false; - bool contract_deployment_data_already_got = false; - // bool portal_contract_address_already_got = false; - bool msg_sender_private_key_already_got = false; - std::string already_got_error = - "Your circuit has already accessed this value. Don't ask the oracle twice, since " - "it shouldn't be trusted, and could lead to circuit bugs"; -}; - -using NativeOracle = NativeOracleInterface; - -} // namespace aztec3::oracle \ No newline at end of file diff --git a/circuits/cpp/src/aztec3/utils/CMakeLists.txt b/circuits/cpp/src/aztec3/utils/CMakeLists.txt deleted file mode 100644 index 5bda22d8e8a..00000000000 --- a/circuits/cpp/src/aztec3/utils/CMakeLists.txt +++ /dev/null @@ -1,6 +0,0 @@ -add_subdirectory(types) - -circuits_cmake_module( - aztec3_utils - barretenberg -) \ No newline at end of file diff --git a/circuits/cpp/src/aztec3/utils/array.hpp b/circuits/cpp/src/aztec3/utils/array.hpp deleted file mode 100644 index 310e89cdbab..00000000000 --- a/circuits/cpp/src/aztec3/utils/array.hpp +++ /dev/null @@ -1,290 +0,0 @@ -#pragma once -#include "./types/native_types.hpp" - -#include "aztec3/utils/circuit_errors.hpp" - -#include - -#include - -/** - * NOTE: see bberg's stdlib/primitives/field/array.hpp for the corresponding circuit implementations of these functions. - */ -namespace aztec3::utils { - -using NT = types::NativeTypes; - -/** - * @brief Helper method to determine if a value is 'empty' based on what empty means for it's type - * @tparam The type of the input value - * @param The value being queried for 'emptiness' - * @return Whether the value is 'empty' - */ -template bool is_empty(T const& value) -{ - if constexpr (std::is_same::value) { - return value == NT::fr(0); - } else { - return value.is_empty(); - } -} - -/** - * @brief Helper method to generate an 'empty' value of a given type - * @tparam The type of the value to return - * @return The empty value - */ -template T empty_value() -{ - if constexpr (std::is_same::value) { - return NT::fr(0); - } else { - return T(); - } -} - -/** - * @brief Helper method to determine the number of non 'empty' items in an array - * @tparam The type of the value stored in the array - * @tparam The size of the array - * @param The array being evaluated for it's length - * @return The number of non-empty items in the array - */ -template size_t array_length(std::array const& arr) -{ - size_t length = 0; - for (const auto& e : arr) { - if (is_empty(e)) { - break; - } - length++; - } - return length; -}; - -/** - * @brief Routine which validates that all zero values of an array form a contiguous region at the end, i.e., - of the form: [*,*,*...,0,0,0,0] where any * is non-zero. Note that a full array of non-zero values is - valid. - * @tparam The type of the values stored in the array. - * @tparam The builder type. - * @tparam The size of the array. - * @param builder The builder which will assert on an array being malformed. - * @param arr The array to validate. - * @param error_message The prefix to the error message set by the caller - */ -template -void validate_array(Builder& builder, std::array const& arr, std::string const& error_message) -{ - size_t first_zero_pos = SIZE; - size_t last_non_zero_pos = 0; - for (size_t i = 0; i < SIZE; i++) { - if (!is_empty(arr[i])) { - last_non_zero_pos = i; - } else if (is_empty(arr[i]) && first_zero_pos == SIZE) { - first_zero_pos = i; - } - } - - builder.do_assert( - last_non_zero_pos <= first_zero_pos, // Only case when equality holds is for a full zeros array. - format( - error_message, - " - array is not well-formed. A non-zero value occurred after a zero. Last position of a non-zero value: ", - last_non_zero_pos, - "; First position of a zero-value: ", - first_zero_pos), - CircuitErrorCode::ARRAY_NOT_ZERO_RIGHT_PADDED); -}; - -/** - * @brief Helper method to return the last non-empty item in an array - * @tparam The type of the value stored in the array - * @tparam The size of the array - * @param The array from which we are to return a value - * @return The returned item - */ -template T array_pop(std::array& arr) -{ - for (size_t i = arr.max_size() - 1; i != static_cast(-1); i--) { - if (!is_empty(arr[i])) { - const auto temp = arr[i]; - arr[i] = empty_value(); - return temp; - } - } - throw_or_abort("array_pop cannot pop from an empty array"); -}; - -/** - * @brief Helper method to push an item into the first empty slot in an array - * @tparam The type of the value stored in the array - * @tparam The builder type - * @tparam The size of the array - * @param The array into which we want to store the value - * @param The value to store - * @param The error message to display if the array is full - */ -template -void array_push(Builder& builder, std::array& arr, T const& value, std::string const& error_message) -{ - for (size_t i = 0; i < arr.size(); ++i) { - if (is_empty(arr[i])) { - arr[i] = value; - return; - } - } - builder.do_assert(false, - format(error_message, " - array_push: capacity exceeded. Limit: ", arr.size()), - CircuitErrorCode::ARRAY_OVERFLOW); -}; - -/** - * @brief Helper method to move all non-zero elements to the left of the array. E.g., [0,7,4,0,5] --> [7,4,5,0,0] - * Remark: The ordering of the non-zero values is preserved. - * - * @tparam The type of the value stored in the array - * @tparam The size of the array - * @param The array into which we want to store the value - */ -template void array_rearrange(std::array& arr) -{ - size_t target_pos = 0; - for (size_t i = 0; i < SIZE; i++) { - if constexpr (std::is_same::value) { - if (arr[i] != NT::fr(0)) { - arr[target_pos] = arr[i]; - target_pos++; - } - } else { - if (!arr[i].is_empty()) { - arr[target_pos] = arr[i]; - target_pos++; - } - } - } - - // Cleaning needed to avoid duplicate values, e.g., [1,0,3,0] --> [1,3,3,0] otherwise. - for (size_t i = target_pos; i < SIZE; i++) { - if constexpr (std::is_same::value) { - arr[i] = NT::fr(0); - } else { - arr[i] = T{}; - } - } -} - -/** - * @brief Helper method to determine if an array contains all 'empty' items - * @tparam The type of the value stored in the array - * @tparam The size of the array - * @param The array to evaluate for non-empty items - */ -template NT::boolean is_array_empty(std::array const& arr) -{ - for (size_t i = 0; i < arr.size(); ++i) { - if (!is_empty(arr[i])) { - return false; - } - } - return true; -}; - -/** - * @brief Inserts the `source` array at the first 'empty' index of the `target` array. - * Ensures that all values after the first 'empty' index are 'empty' too. - * Fails if the `source` array is too large vs the remaining capacity of the `target` array. - * @tparam The size of the `source` array - * @tparam The size of the `target` array - * @tparam The type of the value stored in the arrays - * @param The `source` array - * @param The `target` array - * @param The error message to display if the `source` array is too large - */ -template -void push_array_to_array(Builder& builder, - std::array const& source, - std::array& target, - std::string const& error_message) -{ - // Check if the `source` array is too large vs the remaining capacity of the `target` array - size_t const source_size = array_length(source); - size_t const target_size = array_length(target); - - builder.do_assert(source_size <= size_2 - target_size, - format(error_message, - " - push_array_to_array exceeded capacity. Limit: ", - size_2 - target_size, - " but required size: ", - source_size), - CircuitErrorCode::ARRAY_OVERFLOW); - - // Ensure that there are no non-zero values in the `target` array after the first zero-valued index - for (size_t i = target_size; i < size_2; i++) { - builder.do_assert( - is_empty(target[i]), - format(error_message, " - push_array_to_array inserting into a non empty space at index, ", i), - CircuitErrorCode::ARRAY_OVERFLOW); - } - // Copy the non-zero elements of the `source` array to the `target` array at the first zero-valued index - auto zero_index = target_size; - for (size_t i = 0; i < size_1; i++) { - if (!is_empty(source[i])) { - target[zero_index] = source[i]; - zero_index++; - } - } -} - -/** - * @brief Verifies that the contents of 2 arrays are included within a third - * Ensures that all values after the concatenated values are zero. - * Fails if the `source` arrays combined are too large vs the size of the `target` array. - * @tparam The size of the `source` 1 array - * @tparam The size of the `source` 2 array - * @tparam The size of the `target` array - * @tparam The type of the value stored in the arrays - * @param The first `source` array - * @param The second `source` array - * @param The `target` array - * @return Whether the source arrays are indeed in the target - */ -template -bool source_arrays_are_in_target(Builder& builder, - std::array const& source1, - std::array const& source2, - std::array const& target) -{ - // Check if the `source` arrays are too large vs the size of the `target` array - size_t const source1_size = array_length(source1); - size_t const source2_size = array_length(source2); - builder.do_assert(source1_size + source2_size <= size_3, - "source_arrays_are_in_target: source arrays are too large vs the size of the target", - CircuitErrorCode::ARRAY_OVERFLOW); - - // first ensure that all non-empty items in the first source are in the target - size_t target_index = 0; - for (size_t i = 0; i < source1_size; ++i) { - if (source1[i] != target[target_index]) { - return false; - } - ++target_index; - } - - // now ensure that all non-empty items in the second source are in the target - for (size_t i = 0; i < source2_size; ++i) { - if (source2[i] != target[target_index]) { - return false; - } - ++target_index; - } - - for (; target_index < size_3; ++target_index) { - if (!is_empty(target[target_index])) { - return false; - } - } - return true; -} - -} // namespace aztec3::utils diff --git a/circuits/cpp/src/aztec3/utils/array.test.cpp b/circuits/cpp/src/aztec3/utils/array.test.cpp deleted file mode 100644 index a371e4ce44f..00000000000 --- a/circuits/cpp/src/aztec3/utils/array.test.cpp +++ /dev/null @@ -1,298 +0,0 @@ -#include "array.hpp" - -#include "aztec3/circuits/hash.hpp" -#include "aztec3/utils/dummy_circuit_builder.hpp" - -#include - -#include -#include -#include -#include -#include - -namespace aztec3::utils { - -using fr = NT::fr; - -template -void rearrange_and_check(std::array& input, std::array const& expected, std::string name) -{ - array_rearrange(input); - - for (size_t i = 0; i < N; i++) { - ASSERT_EQ(input[i], expected[i]) << "Mismatch for test vector " << name << " at position " << i; - } -}; - -TEST(hash_tests, noir_l2_l1_interop_hashing) -{ - // This is an annoying hack to convert the field into a hex string - // We should add a to_hex and from_hex method to field class - auto to_hex = [](const NT::fr& value) -> std::string { - std::stringstream field_as_hex_stream; - field_as_hex_stream << value; - return field_as_hex_stream.str(); - }; - - // All zeroes case - auto address = NT::address(0); - auto res = circuits::compute_l2_to_l1_hash(address, fr(0), fr(0), fr(0), fr(0)); - auto res_as_hex = to_hex(res); - ASSERT_EQ(res_as_hex, "0x2266ac2f9f0c19c015239ef5ea85862fc6fac00db73779b220a4d49c4856c2e1"); - - // Non-zero case - address = NT::address(1); - res = circuits::compute_l2_to_l1_hash(address, fr(2), fr(3), fr(4), fr(5)); - res_as_hex = to_hex(res); - ASSERT_EQ(res_as_hex, "0x0f24729168d4450a5681beafa5e3a899ac28bd17bf5a4877dab37bcd834e1634"); - - // Smoke test that sha256_to_field is also the same in Noir - // For an l2_to_l1 hash the maximum size of the buffer that will be sha256'd is 160 bytes - size_t max_buffer_size = 160; - std::vector buffer(max_buffer_size); // Creating a vector of size 160 - for (size_t i = 0; i < 160; ++i) { - buffer[i] = static_cast(i); - } - - res = sha256::sha256_to_field(buffer); - res_as_hex = to_hex(res); - ASSERT_EQ(res_as_hex, "0x142a6d57007171f6eaa33d55976d9dbe739c889c8e920f115f7808dea184c718"); -} - -TEST(utils_array_tests, rearrange_test_vector1) -{ - std::array test_vec{ fr(2), fr(4), fr(0), fr(12), fr(0) }; - std::array const test_vec_rearranged{ fr(2), fr(4), fr(12), fr(0), fr(0) }; - - rearrange_and_check(test_vec, test_vec_rearranged, "1"); -} - -TEST(utils_array_tests, rearrange_test_vector2) -{ - std::array test_vec{ fr(0), fr(99), fr(0), fr(103), fr(0), fr(17) }; - std::array const test_vec_rearranged{ fr(99), fr(103), fr(17), fr(0), fr(0), fr(0) }; - - rearrange_and_check(test_vec, test_vec_rearranged, "2"); -} - -TEST(utils_array_tests, rearrange_test_vector3) -{ - std::array test_vec{ fr(0), fr(0), fr(12), fr(0) }; - std::array const test_vec_rearranged{ fr(12), fr(0), fr(0), fr(0) }; - - rearrange_and_check(test_vec, test_vec_rearranged, "3"); -} - -TEST(utils_array_tests, rearrange_test_vector_identical) -{ - std::array test_vec{ fr(2), fr(4), fr(7), fr(12), fr(9) }; - std::array const test_vec_rearranged{ fr(2), fr(4), fr(7), fr(12), fr(9) }; - - rearrange_and_check(test_vec, test_vec_rearranged, "identical"); -} - -TEST(utils_array_tests, rearrange_test_vector_empty) -{ - std::array test_vec{}; - std::array const test_vec_rearranged{}; - - rearrange_and_check(test_vec, test_vec_rearranged, "empty"); -} - -TEST(utils_array_tests, rearrange_test_vector_all_zeros) -{ - std::array test_vec{ fr(0), fr(0), fr(0), fr(0), fr(0), fr(0), fr(0) }; - std::array const test_vec_rearranged{ fr(0), fr(0), fr(0), fr(0), fr(0), fr(0), fr(0) }; - - rearrange_and_check(test_vec, test_vec_rearranged, "all zeros"); -} - -TEST(utils_array_tests, rearrange_test_vector_long_alternated) -{ - const size_t SIZE = 10000; - std::array test_vec{}; - std::array test_vec_rearranged{}; - - for (size_t i = 0; i < SIZE; i++) { - test_vec[i] = (i % 2 == 0) ? fr(0) : fr(i); - } - - for (size_t i = 0; i < SIZE / 2; i++) { - test_vec_rearranged[i] = fr(2 * i + 1); - } - - for (size_t i = SIZE / 2; i < SIZE; i++) { - test_vec_rearranged[i] = fr(0); - } - - rearrange_and_check(test_vec, test_vec_rearranged, "long alternated"); -} - -TEST(utils_array_tests, rearrange_test_vector_long_zeros_right) -{ - const size_t SIZE = 10000; - std::array test_vec{}; - std::array test_vec_rearranged{}; - - for (size_t i = 0; i < SIZE / 2; i++) { - test_vec[i] = fr(i + 1); - test_vec_rearranged[i] = fr(i + 1); - } - - for (size_t i = SIZE / 2; i < SIZE; i++) { - test_vec[i] = fr(0); - test_vec_rearranged[i] = fr(0); - } - - rearrange_and_check(test_vec, test_vec_rearranged, "long zeros right"); -} - -TEST(utils_array_tests, rearrange_test_vector_long_zeros_left) -{ - const size_t SIZE = 10000; - std::array test_vec{}; - std::array test_vec_rearranged{}; - - for (size_t i = 0; i < SIZE / 2; i++) { - test_vec[i] = fr(0); - test_vec_rearranged[i] = fr(i + 1); - } - - for (size_t i = SIZE / 2; i < SIZE; i++) { - test_vec[i] = fr(i - SIZE / 2 + 1); - test_vec_rearranged[i] = fr(0); - } - - rearrange_and_check(test_vec, test_vec_rearranged, "long zeros left"); -} - -TEST(utils_array_validation, test_vector_all_zeros) -{ - const size_t SIZE = 64; - std::array test_vec{}; - DummyCircuitBuilder dummyBuilder("Builder for array validation test vectors"); - validate_array(dummyBuilder, test_vec, "Test vector with all zeros"); - - EXPECT_FALSE(dummyBuilder.failed()) << dummyBuilder.get_first_failure(); -} - -TEST(utils_array_validation, test_vector_all_non_zeros) -{ - const size_t SIZE = 64; - std::array test_vec; - unsigned int gen = 4127; - for (size_t i = 0; i < SIZE; i++) { - test_vec[i] = fr(gen); - gen = 761 * gen % 5619; - } - - DummyCircuitBuilder dummyBuilder("Builder for array validation test vectors"); - validate_array(dummyBuilder, test_vec, "Test vector with all non zeros"); - - EXPECT_FALSE(dummyBuilder.failed()) << dummyBuilder.get_first_failure(); -} - -TEST(utils_array_validation, test_vector_valid_one_zero) -{ - const size_t SIZE = 110; - std::array test_vec{}; - unsigned int gen = 4159; - for (size_t i = 0; i < SIZE - 1; i++) { - test_vec[i] = fr(gen); - gen = 71 * gen % 2613; - } - - DummyCircuitBuilder dummyBuilder("Builder for array validation test vectors"); - validate_array(dummyBuilder, test_vec, "Test vector with a single zero at the end"); - - EXPECT_FALSE(dummyBuilder.failed()) << dummyBuilder.get_first_failure(); -} - -TEST(utils_array_validation, test_vector_valid_one_non_zero) -{ - const size_t SIZE = 110; - std::array test_vec{}; - test_vec[0] = fr(124); - DummyCircuitBuilder dummyBuilder("Builder for array validation test vectors"); - validate_array(dummyBuilder, test_vec, "Test vector with a single non-zero at the beginning"); - - EXPECT_FALSE(dummyBuilder.failed()) << dummyBuilder.get_first_failure(); -} - -TEST(utils_array_validation, test_vector_invalid_one_zero_middle) -{ - const size_t SIZE = 128; - std::array test_vec{}; - unsigned int gen = 354; - for (size_t i = 0; i < SIZE; i++) { - test_vec[i] = fr(gen); - gen = 319 * gen % 2213; - } - test_vec[67] = fr(0); - DummyCircuitBuilder dummyBuilder("Builder for array validation test vectors"); - validate_array(dummyBuilder, test_vec, "Test vector with a single zero in the middle"); - - EXPECT_TRUE(dummyBuilder.failed()); -} - -TEST(utils_array_validation, test_vector_invalid_one_zero_beginning) -{ - const size_t SIZE = 128; - std::array test_vec{}; - unsigned int gen = 447; - for (size_t i = 0; i < SIZE; i++) { - test_vec[i] = fr(gen); - gen = 39 * gen % 12313; - } - test_vec[0] = fr(0); - DummyCircuitBuilder dummyBuilder("Builder for array validation test vectors"); - validate_array(dummyBuilder, test_vec, "Test vector with a single zero at the beginning"); - - EXPECT_TRUE(dummyBuilder.failed()); -} - -TEST(utils_array_validation, test_vector_invalid_zero_both_ends) -{ - const size_t SIZE = 128; - std::array test_vec{}; - unsigned int gen = 47; - for (size_t i = 0; i < SIZE; i++) { - test_vec[i] = fr(gen); - gen = 6439 * gen % 82313; - } - test_vec[0] = fr(0); - test_vec[SIZE - 1] = fr(0); - DummyCircuitBuilder dummyBuilder("Builder for array validation test vectors"); - validate_array(dummyBuilder, test_vec, "Test vector with a zero at each end"); - - EXPECT_TRUE(dummyBuilder.failed()); -} - -TEST(utils_array_validation, test_vector_invalid_non_zero_last) -{ - const size_t SIZE = 203; - std::array test_vec{}; - test_vec[SIZE - 1] = fr(785); - DummyCircuitBuilder dummyBuilder("Builder for array validation test vectors"); - validate_array(dummyBuilder, test_vec, "Test vector with a non-zero at the end"); - - EXPECT_TRUE(dummyBuilder.failed()); -} - -TEST(utils_array_validation, test_vector_invalid_alternate) -{ - const size_t SIZE = 203; - std::array test_vec{}; - unsigned int gen = 83; - for (size_t i = 0; i < SIZE; i += 2) { - test_vec[i] = fr(gen); - gen = 2437 * gen % 2314; - } - DummyCircuitBuilder dummyBuilder("Builder for array validation test vectors"); - validate_array(dummyBuilder, test_vec, "Test vector with alternating zero and non-zero values."); - - EXPECT_TRUE(dummyBuilder.failed()); -} - -} // namespace aztec3::utils \ No newline at end of file diff --git a/circuits/cpp/src/aztec3/utils/circuit_errors.hpp b/circuits/cpp/src/aztec3/utils/circuit_errors.hpp deleted file mode 100644 index 5fa0213d178..00000000000 --- a/circuits/cpp/src/aztec3/utils/circuit_errors.hpp +++ /dev/null @@ -1,125 +0,0 @@ -#pragma once - -#include - -#include - -namespace aztec3::utils { - -enum CircuitErrorCode : uint16_t { - NO_ERROR = 0, - UNINITIALIZED_RESULT = 1, // Default for CircuitResult - // Private kernel related errors - PRIVATE_KERNEL_CIRCUIT_FAILED = 2000, - PRIVATE_KERNEL__INVALID_CONSTRUCTOR_VK_HASH = 2001, - PRIVATE_KERNEL__INVALID_CONTRACT_ADDRESS = 2002, - PRIVATE_KERNEL__PURPORTED_CONTRACT_TREE_ROOT_AND_PREVIOUS_KERNEL_CONTRACT_TREE_ROOT_MISMATCH = 2003, - PRIVATE_KERNEL__COMPUTED_CONTRACT_TREE_ROOT_AND_PURPORTED_CONTRACT_TREE_ROOT_MISMATCH = 2004, - PRIVATE_KERNEL__NEW_COMMITMENTS_PROHIBITED_IN_STATIC_CALL = 2005, - PRIVATE_KERNEL__NEW_NULLIFIERS_PROHIBITED_IN_STATIC_CALL = 2006, - PRIVATE_KERNEL__CALCULATED_PRIVATE_CALL_HASH_AND_PROVIDED_PRIVATE_CALL_HASH_MISMATCH = 2007, - PRIVATE_KERNEL__PRIVATE_CALL_STACK_ITEM_HASH_MISMATCH = 2008, - PRIVATE_KERNEL__NON_PRIVATE_FUNCTION_EXECUTED_WITH_PRIVATE_KERNEL = 2009, - PRIVATE_KERNEL__MAX_PRIVATE_CALL_STACK_LENGTH_PER_CALL_MISMATCH = 2010, - PRIVATE_KERNEL__UNSUPPORTED_OP = 2011, - PRIVATE_KERNEL__CONTRACT_ADDRESS_MISMATCH = 2012, - PRIVATE_KERNEL__NON_PRIVATE_KERNEL_VERIFIED_WITH_PRIVATE_KERNEL = 2013, - PRIVATE_KERNEL__CONSTRUCTOR_EXECUTED_IN_RECURSION = 2014, - PRIVATE_KERNEL__PRIVATE_CALL_STACK_EMPTY = 2015, - PRIVATE_KERNEL__KERNEL_PROOF_CONTAINS_RECURSIVE_PROOF = 2016, - PRIVATE_KERNEL__USER_INTENT_MISMATCH_BETWEEN_TX_REQUEST_AND_CALL_STACK_ITEM = 2017, - PRIVATE_KERNEL__READ_REQUEST_NOTE_HASH_TREE_ROOT_MISMATCH = 2018, - PRIVATE_KERNEL__TRANSIENT_READ_REQUEST_NO_MATCH = 2019, - PRIVATE_KERNEL__READ_REQUEST_WITNESSES_ARRAY_LENGTH_MISMATCH = 2020, - PRIVATE_KERNEL__UNRESOLVED_NON_TRANSIENT_READ_REQUEST = 2021, - PRIVATE_KERNEL__IS_INTERNAL_BUT_NOT_SELF_CALL = 2022, - PRIVATE_KERNEL__TRANSIENT_NEW_NULLIFIER_NO_MATCH = 2023, - PRIVATE_KERNEL__0TH_NULLIFIER_IS_ZERO = 2024, - - // Public kernel related errors - PUBLIC_KERNEL_CIRCUIT_FAILED = 3000, - PUBLIC_KERNEL__UNSUPPORTED_OP = 3001, - PUBLIC_KERNEL__PRIVATE_FUNCTION_NOT_ALLOWED = 3002, - PUBLIC_KERNEL__CONTRACT_ADDRESS_MISMATCH = 3003, - PUBLIC_KERNEL__EMPTY_PUBLIC_CALL_STACK = 3004, - PUBLIC_KERNEL__NON_EMPTY_PRIVATE_CALL_STACK = 3005, - PUBLIC_KERNEL__PREVIOUS_KERNEL_NOT_PRIVATE = 3009, - PUBLIC_KERNEL__PREVIOUS_KERNEL_NOT_PUBLIC = 3010, - PUBLIC_KERNEL__CALCULATED_PUBLIC_CALL_HASH_AND_PROVIDED_PUBLIC_CALL_HASH_MISMATCH = 3011, - PUBLIC_KERNEL__PUBLIC_CALL_STACK_MISMATCH = 3012, - PUBLIC_KERNEL__CONTRACT_DEPLOYMENT_NOT_ALLOWED = 3013, - PUBLIC_KERNEL__CONSTRUCTOR_NOT_ALLOWED = 3014, - PUBLIC_KERNEL__CONTRACT_ADDRESS_INVALID = 3015, - PUBLIC_KERNEL__FUNCTION_SIGNATURE_INVALID = 3016, - PUBLIC_KERNEL__BYTECODE_HASH_INVALID = 3017, - PUBLIC_KERNEL__CALL_CONTEXT_INVALID = 3018, - PUBLIC_KERNEL__DELEGATE_CALL_PROHIBITED_BY_USER = 3019, - PUBLIC_KERNEL__STATIC_CALL_PROHIBITED_BY_USER = 3019, - PUBLIC_KERNEL__PUBLIC_CALL_STACK_INVALID_MSG_SENDER = 3020, - PUBLIC_KERNEL__PUBLIC_CALL_STACK_INVALID_STORAGE_ADDRESS = 3021, - PUBLIC_KERNEL__PUBLIC_CALL_STACK_INVALID_PORTAL_ADDRESS = 3022, - PUBLIC_KERNEL__PUBLIC_CALL_STACK_CONTRACT_STORAGE_UPDATES_PROHIBITED_FOR_STATIC_CALL = 3022, - PUBLIC_KERNEL__CALL_CONTEXT_INVALID_STORAGE_ADDRESS_FOR_DELEGATE_CALL = 3023, - PUBLIC_KERNEL__CALL_CONTEXT_CONTRACT_STORAGE_UPDATE_REQUESTS_PROHIBITED_FOR_STATIC_CALL = 3024, - PUBLIC_KERNEL__NEW_NULLIFIERS_NOT_EMPTY_IN_FIRST_ITERATION = 3025, - PUBLIC_KERNEL__NEW_COMMITMENTS_PROHIBITED_IN_STATIC_CALL = 3026, - PUBLIC_KERNEL__NEW_NULLIFIERS_PROHIBITED_IN_STATIC_CALL = 3027, - PUBLIC_KERNEL__IS_INTERNAL_BUT_NOT_SELF_CALL = 3028, - - BASE__CIRCUIT_FAILED = 4000, - BASE__KERNEL_PROOF_VERIFICATION_FAILED = 4001, - BASE__INCORRECT_NUM_OF_NEW_COMMITMENTS = 4002, - BASE__INVALID_NULLIFIER_SUBTREE = 4003, - BASE__INVALID_NULLIFIER_RANGE = 4004, - BASE__INVALID_PUBLIC_DATA_READS = 4005, - BASE__INVALID_PUBLIC_DATA_UPDATE_REQUESTS = 4006, - BASE__INVALID_CHAIN_ID = 4007, - BASE__INVALID_VERSION = 4008, - BASE__INVALID_BLOCK_HASH = 4009, - - MERGE_CIRCUIT_FAILED = 6000, - - // Component errors (used by all circuits) - ROLLUP_TYPE_MISMATCH = 7001, - ROLLUP_HEIGHT_MISMATCH = 7002, - CONSTANTS_MISMATCH = 7003, - NOTE_HASH_TREE_SNAPSHOT_MISMATCH = 7004, - NULLIFIER_TREE_SNAPSHOT_MISMATCH = 7005, - CONTRACT_TREE_SNAPSHOT_MISMATCH = 7006, - PUBLIC_DATA_TREE_ROOT_MISMATCH = 7007, - MEMBERSHIP_CHECK_FAILED = 7008, - ARRAY_OVERFLOW = 7009, - ARRAY_NOT_ZERO_RIGHT_PADDED = 7010, - - ROOT_CIRCUIT_FAILED = 8000, - -}; - -struct CircuitError { - // using int for easy serialization - uint16_t code = CircuitErrorCode::NO_ERROR; - std::string message; - - // for serialization, update with new fields - MSGPACK_FIELDS(code, message); - static CircuitError no_error() { return { CircuitErrorCode::NO_ERROR, "" }; } - - bool operator==(CircuitError const& other) const { return code == other.code && message == other.message; } -}; - -// Define CircuitResult as a std::variant T + error union type -// We do not use std::variant directly as we need default-constructible types for msgpack -template struct CircuitResult { - CircuitResult() : result(CircuitError{ UNINITIALIZED_RESULT, "" }) {} - explicit CircuitResult(const T& value) : result(value) {} - explicit CircuitResult(const CircuitError& value) : result(value) {} - std::variant result; - - // for serialization: delegate to msgpack std::variant support - void msgpack_pack(auto& packer) const { packer.pack(result); } - void msgpack_unpack(auto obj) { result = obj; } - // for schema serialization: delegate to msgpack std::variant support - void msgpack_schema(auto& packer) const { packer.pack_schema(result); } -}; - -} // namespace aztec3::utils diff --git a/circuits/cpp/src/aztec3/utils/dummy_circuit_builder.hpp b/circuits/cpp/src/aztec3/utils/dummy_circuit_builder.hpp deleted file mode 100644 index 95d637f1b04..00000000000 --- a/circuits/cpp/src/aztec3/utils/dummy_circuit_builder.hpp +++ /dev/null @@ -1,76 +0,0 @@ -#pragma once -#include "aztec3/utils/circuit_errors.hpp" - -#include -#include -#include -#include -#include - -namespace aztec3::utils { - -class DummyCircuitBuilder { - public: - std::vector failure_msgs; - // method that created this builder instance. Useful for logging. - std::string method_name; - - explicit DummyCircuitBuilder(std::string method_name) : method_name(std::move(method_name)) {} - - void do_assert(bool const& assertion, std::string const& msg, CircuitErrorCode error_code) - { - if (!assertion) { -#ifdef __wasm__ - info("Error(", error_code, "): ", msg); -#endif - failure_msgs.push_back(CircuitError{ error_code, msg }); - } - } - - [[nodiscard]] bool failed() const { return !failure_msgs.empty(); } - - CircuitError get_first_failure() - { - if (failed()) { - return failure_msgs[0]; - } - return CircuitError::no_error(); - } - - /** - * Returns 'value' as a CircuitResult, unless there was an error. - * If there was an error, return it instead. - * @tparam T the value type. - * @param value the value. - * @return the value, or last error if it exists. - */ - template CircuitResult result_or_error(const T& value) - { - CircuitError const failure = get_first_failure(); - if (failure.code != CircuitErrorCode::NO_ERROR) { - return CircuitResult{ failure }; - } - return CircuitResult{ value }; - } - - uint8_t* alloc_and_serialize_first_failure() - { - CircuitError const failure = get_first_failure(); - if (failure.code == CircuitErrorCode::NO_ERROR) { - return nullptr; - } - info(this->method_name, ": builder.get_first_failure() = ", failure_msgs[0]); - - - // serialize circuit failure to bytes vec - std::vector circuit_failure_vec; - serialize::write(circuit_failure_vec, failure); - - // copy to output buffer - auto* raw_failure_buf = static_cast(malloc(circuit_failure_vec.size())); - memcpy(raw_failure_buf, (void*)circuit_failure_vec.data(), circuit_failure_vec.size()); - return raw_failure_buf; - } -}; - -} // namespace aztec3::utils diff --git a/circuits/cpp/src/aztec3/utils/msgpack_derived_equals.hpp b/circuits/cpp/src/aztec3/utils/msgpack_derived_equals.hpp deleted file mode 100644 index 9d1fda1f6a2..00000000000 --- a/circuits/cpp/src/aztec3/utils/msgpack_derived_equals.hpp +++ /dev/null @@ -1,29 +0,0 @@ -#pragma once -#include - -namespace aztec3::utils { -// Auxiliary function for comparison of elements from tuples -template -auto _compare_msgpack_tuples(const Tuple1& t1, const Tuple2& t2, std::index_sequence) // NOLINT -{ - // Compare every 2nd value from our key1, value1, key2, value2 list - return ((std::get(t1) == std::get(t2)) && ...); -} - -// Function to check equality of msgpack objects based on their values. -// Normally, you should instead use operator==() = default; -// BoolLike represents a type like the boolean DSL type -template BoolLike msgpack_derived_equals(const T& obj1, const T& obj2) -{ - BoolLike are_equal; - // De-serialize objects to alternating key-value tuples and compare the values - const_cast(obj1).msgpack([&](auto&... args1) { // NOLINT - const_cast(obj2).msgpack([&](auto&... args2) { // NOLINT - auto tuple1 = std::tie(args1...); - auto tuple2 = std::tie(args2...); - are_equal = _compare_msgpack_tuples(tuple1, tuple2, std::make_index_sequence{}); - }); - }); - return are_equal; -} -} // namespace aztec3::utils diff --git a/circuits/cpp/src/aztec3/utils/msgpack_derived_output.hpp b/circuits/cpp/src/aztec3/utils/msgpack_derived_output.hpp deleted file mode 100644 index b134651e923..00000000000 --- a/circuits/cpp/src/aztec3/utils/msgpack_derived_output.hpp +++ /dev/null @@ -1,38 +0,0 @@ -#pragma once - -#include - -#include -#include - -namespace aztec3::utils { - -template std::ostream& msgpack_derived_output(std::ostream& os, const T& value); - -inline void _msgpack_derived_output_helper(std::ostream& os) -{ - // base case - (void)os; // unused -} -inline void _msgpack_derived_output_helper(std::ostream& os, - const std::string& key, - const auto& value, - const auto&... rest) -{ - os << key << ": "; - msgpack_derived_output(os, value); - os << '\n'; - _msgpack_derived_output_helper(os, rest...); // NOLINT -} -// Specialization if we have msgpack -template void msgpack_derived_output(std::ostream& os, const T& value) -{ - const_cast(value).msgpack([&](auto&... args) { _msgpack_derived_output_helper(os, args...); }); // NOLINT -} - -// Otherwise -template std::ostream& msgpack_derived_output(std::ostream& os, const T& value) -{ - return os << value; -} -} // namespace aztec3::utils diff --git a/circuits/cpp/src/aztec3/utils/types/CMakeLists.txt b/circuits/cpp/src/aztec3/utils/types/CMakeLists.txt deleted file mode 100644 index 0e089a172fe..00000000000 --- a/circuits/cpp/src/aztec3/utils/types/CMakeLists.txt +++ /dev/null @@ -1,4 +0,0 @@ -circuits_cmake_module( - aztec3_types - barretenberg -) \ No newline at end of file diff --git a/circuits/cpp/src/aztec3/utils/types/circuit_types.hpp b/circuits/cpp/src/aztec3/utils/types/circuit_types.hpp deleted file mode 100644 index e9bc1fc610e..00000000000 --- a/circuits/cpp/src/aztec3/utils/types/circuit_types.hpp +++ /dev/null @@ -1,97 +0,0 @@ -#pragma once - -// TODO(dbanks12) consider removing this include which is used by consumers of circuit_types.hpp - -#include "aztec3/constants.hpp" - -#include - -using namespace proof_system::plonk; - -// Note: Inner proving system type for recursion is inflexibly set to UltraPlonk. -namespace aztec3::utils::types { - -template struct CircuitTypes { - // Basic uint types - using boolean = plonk::stdlib::bool_t; - using uint8 = plonk::stdlib::uint8; - using uint16 = plonk::stdlib::uint16; - using uint32 = plonk::stdlib::uint32; - using uint64 = plonk::stdlib::uint64; - - // Types related to the bn254 curve - using fr = plonk::stdlib::field_t; - using safe_fr = plonk::stdlib::safe_uint_t; - using address = plonk::stdlib::address_t; - using fq = plonk::stdlib::bigfield; - - using witness = plonk::stdlib::witness_t; - - // typedef fq grumpkin_fr; - // typedef fr grumpkin_fq; - using grumpkin_point = plonk::stdlib::cycle_group; // affine - - using bn254 = plonk::stdlib::bn254; - // typedef bn254::g1_ct bn254_point; - using bn254_point = plonk::stdlib::element; // affine - - using bit_array = plonk::stdlib::bit_array; - using byte_array = plonk::stdlib::byte_array; - using packed_byte_array = plonk::stdlib::packed_byte_array; - - using schnorr_signature = plonk::stdlib::schnorr::signature_bits; - using ecdsa_signature = plonk::stdlib::ecdsa::signature; - - using AggregationObject = plonk::stdlib::recursion::aggregation_state; - using VK = plonk::stdlib::recursion::verification_key; - // Notice: no CircuitType for a Proof: we only ever handle native; the verify_proof() function swallows the - // 'circuit-type-ness' of the proof. - - static crypto::GeneratorContext get_generator_context(const size_t hash_index) - { - crypto::GeneratorContext result; - result.offset = hash_index; - return result; - } - - /// TODO: lots of these compress / commit functions aren't actually used: remove them. - - // Define the 'circuit' version of the function `hash`, with the name `hash`: - static fr hash(std::vector const& inputs, const size_t hash_index = 0) - { - return plonk::stdlib::pedersen_hash::hash(inputs, get_generator_context(hash_index)); - } - - - static fr hash(std::vector const& inputs, - std::vector const& hash_sub_indices, - const size_t hash_index = 0) - { - return plonk::stdlib::pedersen_hash::hash(inputs, hash_sub_indices, get_generator_context(hash_index)); - } - - /** - * @brief Compute the hash for a pair of left and right nodes in a merkle tree. - * - * @details Compress the two nodes using the default/0-generator which is reserved - * for internal merkle hashing. - * - * @param left The left child node - * @param right The right child node - * @return The computed Merkle tree hash for the given pair of nodes - */ - static fr merkle_hash(fr left, fr right) - { - // use 0-generator for internal merkle hashing - return plonk::stdlib::pedersen_hash::hash({ left, right }, 0); - }; - - static grumpkin_point commit(const std::vector& inputs, const size_t hash_index = 0) - { - return plonk::stdlib::pedersen_commitment::commit(inputs, get_generator_context(hash_index)); - }; - - static byte_array blake2s(const byte_array& input) { return plonk::stdlib::blake2s(input); } -}; - -} // namespace aztec3::utils::types diff --git a/circuits/cpp/src/aztec3/utils/types/convert.hpp b/circuits/cpp/src/aztec3/utils/types/convert.hpp deleted file mode 100644 index cf127a78125..00000000000 --- a/circuits/cpp/src/aztec3/utils/types/convert.hpp +++ /dev/null @@ -1,278 +0,0 @@ -#pragma once - -#include "circuit_types.hpp" -#include "native_types.hpp" - -#include - -#include - -namespace aztec3::utils::types { - -using plonk::stdlib::witness_t; - -namespace { - -template using CT = aztec3::utils::types::CircuitTypes; -using NT = aztec3::utils::types::NativeTypes; - -} // namespace - -/// TODO: Lots of identical functions here (but for their in/out types). Can we use templates? I couldn't figure out how -/// to keep the NT:: or CT:: prefixes with templates. -template typename CT::boolean to_ct(Builder& builder, typename NT::boolean const& e) -{ - return typename CT::boolean(witness_t(&builder, e)); -}; - -template typename CT::fr to_ct(Builder& builder, typename NT::fr const& e) -{ - return typename CT::fr(witness_t(&builder, e)); -}; - -template typename CT::fq to_ct(Builder& builder, typename NT::fq const& e) -{ - return typename CT::fq(witness_t(&builder, e)); -}; - -template typename CT::address to_ct(Builder& builder, typename NT::address const& e) -{ - return typename CT::address(witness_t(&builder, e)); -}; - -template typename CT::uint32 to_ct(Builder& builder, typename NT::uint32 const& e) -{ - return typename CT::uint32(witness_t(&builder, e)); -}; - -template -typename CT::grumpkin_point to_ct(Builder& builder, typename NT::grumpkin_point const& e) -{ - return plonk::stdlib::cycle_group::from_witness(builder, e); -}; - -template typename CT::bn254_point to_ct(Builder& builder, typename NT::bn254_point const& e) -{ - return CT::bn254_point::from_witness(&builder, e); -}; - -template -typename CT::ecdsa_signature to_ct(Builder& builder, typename NT::ecdsa_signature const& e) -{ - return CT::ecdsa_signature::template from_witness(&builder, e); -}; - -template -std::optional::boolean> to_ct(Builder& builder, std::optional const& e) -{ - return e ? std::make_optional::boolean>(to_ct(builder, *e)) : std::nullopt; -}; - -template -std::optional::fr> to_ct(Builder& builder, std::optional const& e) -{ - return e ? std::make_optional::fr>(to_ct(builder, *e)) : std::nullopt; -}; - -template -std::optional::address> to_ct(Builder& builder, std::optional const& e) -{ - return e ? std::make_optional::address>(to_ct(builder, *e)) : std::nullopt; -}; - -template -std::optional::grumpkin_point> to_ct(Builder& builder, - std::optional const& e) -{ - return e ? std::make_optional::grumpkin_point>(to_ct(builder, *e)) : std::nullopt; -}; - -template -std::optional::ecdsa_signature> to_ct(Builder& builder, - std::optional const& e) -{ - return e ? std::make_optional::ecdsa_signature>(to_ct(&builder, e)) : std::nullopt; -}; - -template -std::vector::fr> to_ct(Builder& builder, std::vector const& vec) -{ - auto ref_to_ct = [&](typename NT::fr const& e) { return to_ct(builder, e); }; - - return map(vec, ref_to_ct); -}; - -template -std::optional::fr>> to_ct(Builder& builder, - std::optional> const& vec) -{ - auto ref_to_ct = [&](typename NT::fr const& e) { return to_ct(builder, e); }; - - return vec ? std::make_optional::fr>>(map(*vec, ref_to_ct)) : std::nullopt; -}; - -template -std::array::fr, SIZE> to_ct(Builder& builder, std::array const& arr) -{ - auto ref_to_ct = [&](typename NT::fr const& e) { return to_ct(builder, e); }; - - return map(arr, ref_to_ct); -}; - - -template std::array::fr>, SIZE> to_ct( - Builder& builder, std::array, SIZE> const& arr) -{ - auto ref_to_ct = [&](std::optional const& e) { return to_ct(builder, e); }; - - return map(arr, ref_to_ct); -}; - -/** - * @brief Convert from an array of any native types (NT_TYPE) to array of circuit types (CT_TYPE) - */ -template -std::array to_ct(Builder& builder, std::array const& arr) -{ - auto ref_to_ct = [&](NT_TYPE const& e) { return e.to_circuit_type(builder); }; - - return map(arr, ref_to_ct); -}; - -/** - * @brief Convert from an array of any native types (NT_TYPE) to array of circuit types (CT_TYPE). - * Allow array entries to be optional. - */ -template -std::array, SIZE> to_ct(Builder& builder, std::array, SIZE> const& arr) -{ - auto ref_to_ct = [&](std::optional const& e) { return e.to_circuit_type(builder); }; - - return map(arr, ref_to_ct); -}; - -// to_nt() below ******************************** - -template typename NT::boolean to_nt(typename CT::boolean const& e) -{ - return e.get_value(); -}; - -template typename NT::fr to_nt(typename CT::fr const& e) -{ - return e.get_value(); -}; - -template typename NT::fq to_nt(typename CT::fq const& e) -{ - return e.get_value(); -}; - -template typename NT::address to_nt(typename CT::address const& e) -{ - return NT::address(e.address_.get_value()); // TODO: add get_value() method to address types. -}; - -template typename NT::uint32 to_nt(typename CT::uint32 const& e) -{ - NT::uint256 const e_256 = e.get_value(); - NT::uint64 const e_64 = e_256.data[0]; // TODO: check that this endianness is correct! - auto const e_32 = static_cast(e_64); - return e_32; -}; - -template typename NT::grumpkin_point to_nt(typename CT::grumpkin_point const& e) -{ - return NT::grumpkin_point{ e.x.get_value(), e.y.get_value() }; -}; - -template typename NT::bn254_point to_nt(typename CT::bn254_point const& e) -{ - return e.get_value(); -}; - -template typename NT::ecdsa_signature to_nt(typename CT::ecdsa_signature const& e) -{ - std::vector r_bytes = e.r.get_value(); - std::vector s_bytes = e.s.get_value(); - const uint8_t v_byte = e.v.get_value(); - - std::array r_array; - std::array s_array; - std::copy(r_bytes.begin(), r_bytes.end(), r_array.begin()); - std::copy(s_bytes.begin(), s_bytes.end(), s_array.begin()); - - return NT::ecdsa_signature{ r_array, s_array, v_byte }; -}; - -template -std::optional to_nt(std::optional::boolean> const& e) -{ - return e ? std::make_optional(to_nt(*e)) : std::nullopt; -}; - -template std::optional to_nt(std::optional::fr> const& e) -{ - return e ? std::make_optional(to_nt(*e)) : std::nullopt; -}; - -template -std::optional to_nt(std::optional::address> const& e) -{ - return e ? std::make_optional(to_nt(*e)) : std::nullopt; -}; - -template -std::optional to_nt(std::optional::grumpkin_point> const& e) -{ - return e ? std::make_optional(to_nt(*e)) : std::nullopt; -}; - -template -std::optional to_nt(std::optional::ecdsa_signature> const& e) -{ - return e ? std::make_optional(to_nt(*e)) : std::nullopt; -}; - -template std::vector to_nt(std::vector::fr> const& vec) -{ - auto ref_to_nt = [&](typename CT::fr const& e) { return to_nt(e); }; - - return map(vec, ref_to_nt); -}; - -template -std::optional> to_nt(std::optional::fr>> const& vec) -{ - auto ref_to_nt = [&](typename CT::fr const& e) { return to_nt(e); }; - - return vec ? std::make_optional>(map(*vec, ref_to_nt)) : std::nullopt; -}; - -template -std::array to_nt(std::array::fr, SIZE> const& arr) -{ - auto ref_to_nt = [&](typename CT::fr const& e) { return to_nt(e); }; - - return map(arr, ref_to_nt); -}; - -// template -// std::optional> to_nt( -// std::optional::fr, SIZE>> const& arr) -// { -// auto ref_to_nt = [&](typename CT::fr const& e) { return to_nt(e); }; - -// return arr ? std::make_optional>(map(arr, ref_to_nt)) : std::nullopt; -// }; - -template std::array, SIZE> to_nt( - std::array::fr>, SIZE> const& arr) -{ - auto ref_to_nt = [&](std::optional::fr> const& e) { return to_nt(e); }; - - return map(arr, ref_to_nt); -}; - - -} // namespace aztec3::utils::types diff --git a/circuits/cpp/src/aztec3/utils/types/native_types.hpp b/circuits/cpp/src/aztec3/utils/types/native_types.hpp deleted file mode 100644 index fb2ba5788bd..00000000000 --- a/circuits/cpp/src/aztec3/utils/types/native_types.hpp +++ /dev/null @@ -1,86 +0,0 @@ -#pragma once -#include "aztec3/constants.hpp" - -#include -namespace aztec3::utils::types { - -struct NativeTypes { - using boolean = bool; - - using uint8 = uint8_t; - using uint16 = uint16_t; - using uint32 = uint32_t; - using uint64 = uint64_t; - using uint256 = uint256_t; - - using fr = barretenberg::fr; - using address = proof_system::plonk::stdlib::address; - - using fq = barretenberg::fq; - - // typedef fq grumpkin_fr; - // typedef fr grumpkin_fq; - using grumpkin_point = grumpkin::g1::affine_element; - // typedef grumpkin::g1::element grumpkin_jac_point; - - using bn254_point = barretenberg::g1::affine_element; - // typedef barretenberg::g1::element bn254_jac_point; - // typedef barretenberg::g1 bn254_group; - - using bit_array = std::vector; - using byte_array = std::vector; - using packed_byte_array = std::string; - - using schnorr_signature = crypto::schnorr::signature; - using ecdsa_signature = crypto::ecdsa::signature; - - using AggregationObject = proof_system::plonk::stdlib::recursion::native_aggregation_state; - using VKData = plonk::verification_key_data; - using VK = plonk::verification_key; - using Proof = plonk::proof; - - static crypto::GeneratorContext get_generator_context(const size_t hash_index) - { - crypto::GeneratorContext result; - result.offset = hash_index; - return result; - } - - // Define the 'native' version of the function `hash`, with the name `hash`: - static fr hash(const std::vector& inputs, const size_t hash_index = 0) - { - return crypto::pedersen_hash::hash(inputs, get_generator_context(hash_index)); - } - - /** - * @brief Compute the hash for a pair of left and right nodes in a merkle tree. - * - * @details Compress the two nodes using the default/0-generator which is reserved - * for internal merkle hashing. - * - * @param left The left child node - * @param right The right child node - * @return The computed Merkle tree hash for the given pair of nodes - */ - static fr merkle_hash(fr left, fr right) - { - // use 0-generator for internal merkle hashing - // use lookup namespace since we now use ultraplonk - return crypto::pedersen_hash::hash({ left, right }, 0); - } - - static grumpkin_point commit(const std::vector& inputs, const size_t hash_index = 0) - { - return crypto::pedersen_commitment::commit_native(inputs, get_generator_context(hash_index)); - } - - static byte_array blake2s(const byte_array& input) - { - auto res = blake2::blake2s(input); - return byte_array(res.begin(), res.end()); - } - - static byte_array blake3s(const byte_array& input) { return blake3::blake3s(input); } -}; - -} // namespace aztec3::utils::types \ No newline at end of file diff --git a/circuits/cpp/srs_db b/circuits/cpp/srs_db deleted file mode 120000 index 003731dd615..00000000000 --- a/circuits/cpp/srs_db +++ /dev/null @@ -1 +0,0 @@ -barretenberg/cpp/srs_db \ No newline at end of file diff --git a/cspell.json b/cspell.json index fe0e7cd0497..c9298ca6640 100644 --- a/cspell.json +++ b/cspell.json @@ -118,6 +118,9 @@ "memdown", "memfs", "Merkle", + "merkleization", + "merkleized", + "merkleizing", "messagebox", "mimc", "mktemp", @@ -140,6 +143,7 @@ "noirup", "nullifer", "offchain", + "onchain", "otterscan", "outdir", "overlayfs", @@ -148,6 +152,7 @@ "parallelizable", "Pedersen", "permissionless", + "permissionlessly", "persistable", "pids", "pkgs", @@ -156,6 +161,7 @@ "pnat", "Pokeable", "preauthenticated", + "precompute", "preimage", "preimages", "prestat", @@ -216,6 +222,7 @@ "typecheck", "typegen", "typeparam", + "undeployed", "unexclude", "unexcluded", "unprefixed", diff --git a/docs/.dockerignore b/docs/.dockerignore index e9573dbf7ff..b5f7883caaf 100644 --- a/docs/.dockerignore +++ b/docs/.dockerignore @@ -10,9 +10,6 @@ build/ Debug/ Release/ -circuits/cpp/build/ -circuits/cpp/build-wasm/ -circuits/cpp/build-coverage/ barretenberg/ # Ignore Node.js build artifacts diff --git a/docs/Dockerfile.dockerignore b/docs/Dockerfile.dockerignore index ab13fbd2925..cedb373cab3 100644 --- a/docs/Dockerfile.dockerignore +++ b/docs/Dockerfile.dockerignore @@ -7,7 +7,6 @@ docs/node_modules !l1-contracts/src !l1-contracts/test !barretenberg/cpp/src/barretenberg -!circuits/cpp/src !.release-please-manifest.json !boxes diff --git a/docs/docs/about_aztec/history/differences_to_aztec_connect.md b/docs/docs/about_aztec/history/differences_to_aztec_connect.md deleted file mode 100644 index e94c8d1f551..00000000000 --- a/docs/docs/about_aztec/history/differences_to_aztec_connect.md +++ /dev/null @@ -1,15 +0,0 @@ -## Heuristics that differ from 'Aztec Connect' (a.k.a. 'Connect') - -**A contract cannot manipulate another contract's state** - -In Connect, we had multiple circuits that could each create/destroy a unified set of value notes. This was acceptable because all Connect circuits had a single author (us!). - -In Aztec our architecture must process arbitrary circuits written by potentially dishonest actors. Contract state must therefore be siloed at the architecture level similar to Ethereum. Fortunately, this does not require splitting up the privacy set. - -**Privacy set must be shared across all contracts** - -In Connect observers knew when different note types were being created (value note, account note etc). This cannot be the case in Aztec, as we want to provide strong privacy guarantees to all private contracts even if they have few transactions interacting with their contract. - -**Support for call semantics** - -If a contract can only modify its own state, we need a way for a contract to "call" another contract to request state modifications. diff --git a/docs/docs/concepts/advanced/circuits/kernels/main.md b/docs/docs/concepts/advanced/circuits/kernels/main.md deleted file mode 100644 index 66491cc446c..00000000000 --- a/docs/docs/concepts/advanced/circuits/kernels/main.md +++ /dev/null @@ -1,5 +0,0 @@ -import DocCardList from '@theme/DocCardList'; - -# Kernel Circuits - - diff --git a/docs/docs/concepts/advanced/data_structures/main.md b/docs/docs/concepts/advanced/data_structures/main.md deleted file mode 100644 index 43c378b2ec7..00000000000 --- a/docs/docs/concepts/advanced/data_structures/main.md +++ /dev/null @@ -1,5 +0,0 @@ -import DocCardList from '@theme/DocCardList'; - -# Data Structures - - diff --git a/docs/docs/concepts/advanced/main.md b/docs/docs/concepts/advanced/main.md deleted file mode 100644 index a1234235739..00000000000 --- a/docs/docs/concepts/advanced/main.md +++ /dev/null @@ -1,5 +0,0 @@ -import DocCardList from '@theme/DocCardList'; - -# Advanced Concepts - - diff --git a/docs/docs/dev_docs/contracts/syntax/functions.md b/docs/docs/dev_docs/contracts/syntax/functions.md deleted file mode 100644 index aa1edecb35f..00000000000 --- a/docs/docs/dev_docs/contracts/syntax/functions.md +++ /dev/null @@ -1,323 +0,0 @@ ---- -title: Functions -description: This page covers functions, private and public functions composability, as well as their differences. ---- - -Functions serve as the building blocks of smart contracts. Functions can be either public, ie they can interact with other contracts and the blockchain, or private for internal contract use. Every smart contract also has a private `constructor` function which is called when the contract is deployed. There are also special oracle functions, which can get data from outside of the smart contract. In the context of Aztec, oracles are often used to get user-provided inputs. - -On this page, you’ll learn more about: - -- How function visibility works in Aztec -- A detailed understanding of public, private, and unconstrained functions, and how to write them -- How constructors work and remain private -- The process of calling functions from within the same smart contract and from different contracts, including calling private functions from private functions, public from public, and even private from public -- What oracles and how Aztec smart contracts might use them -- Built-in oracles - -## Visibility - -In Aztec there are multiple different types of visibility that can be applied to functions. Namely we have `data visibility` and `function visibility`. - -### Data Visibility - -Data visibility is used to describe whether the data (or state) used in a function is generally accessible (public) or on a need to know basis (private). Functions with public data visibility are executed by the sequencer, and functions with private data visibility are executed by the user. For more information on why this is the case, see [communication](../../../concepts/foundation/communication/public_private_calls/main.md). - -In the following sections, we are going to see how these two "types" co-exists and interact. - -### Function visibility - -This is the kind of visibility you are more used to seeing in Solidity and more traditional programming languages. It is used to describe whether a function is callable from other contracts, or only from within the same contract. - -By default, all functions are callable from other contracts, similarly to the Solidity `public` visibility. To make them only callable from the contract itself, you can mark them as `internal`. Contrary to solidity, we don't have the `external` nor `private` keywords. `external` since it is limited usage when we don't support inheritance, and `private` since we don't support inheritance and it would also be confusing with multiple types of `private`. - -A good place to use `internal` is when you want a private function to be able to alter public state. As mentioned above, private functions cannot do this directly. They are able to call public functions and by making these internal we can ensure that this state manipulating function is only callable from our private function. - -:::danger -Note that non-internal functions could be used directly as an entry-point, which currently means that the `msg_sender` would be `0`, so for now, using address `0` as a burn address is not recommended. -::: - -## Mutability - -Currently, any function is "mutable" in the sense that it might alter state. In the future, we will support static calls, similarly to EVM. A static call is essentially a call that does not alter state (it keeps state static). This is useful for when you want to call a function in a separate contract, but ensure that it cannot alter state, or call other functions that might alter state (such as re-entering). - -Similarly, a special case of a mutating call is the `delegatecall` where the function executed might not be in the same contract as the state being altered. It is at this moment, not certain if `delegatecall`s should become a fully fledged feature. - -:::danger No `staticcall` or `delegatecall` support -While `staticcall` and `delegatecall` both have flags in the call context, they are currently not supported and will not behave as one would expect if usage is attempted. -::: - -## `constructor` - -- A special `constructor` function MUST be declared within a contract's scope. -- A constructor doesn't have a name, because its purpose is clear: to initialize contract state. -- In Aztec terminology, a constructor is always a '`private` function' (i.e. it cannot be a `public` function). -- A constructor behaves almost identically to any other function. It's just important for Aztec to be able to identify this function as special: it may only be called once, and will not be deployed as part of the contract. - -An example of a somewhat boring constructor is as follows: - -#include_code empty-constructor /yarn-project/noir-contracts/contracts/test_contract/src/main.nr rust - -Although you can have a constructor that does nothing, you might want to do something with it, such as setting the deployer as an owner. - -#include_code constructor /yarn-project/noir-contracts/contracts/escrow_contract/src/main.nr rust - -:::danger -It is not possible to call public functions from within a constructor. Beware that the compiler will not throw an error if you do, but the execution will fail! -::: - -## `Public` Functions - -A public function is executed by the sequencer and has access to a state model that is very similar to that of the EVM and Ethereum. Even though they work in an EVM-like model for public transactions, they are able to write data into private storage that can be consumed later by a private function. - -To create a public function you can annotate it with the `#[aztec(public)]` attribute. This will make the [public context](./context.mdx#public-context) available within your current function's execution scope. - -#include_code set_minter /yarn-project/noir-contracts/contracts/token_contract/src/main.nr rust - -## `Private` Functions - -As alluded to earlier, a private function operates on private information, and is executed by the user. To tell the compiler that this is the kind of function we are creating annotate it with the `#[aztec(private)]` attribute. This will make the [private context](./context.mdx#private-context-broken-down) available within your current function's execution scope. - -#include_code redeem_shield /yarn-project/noir-contracts/contracts/token_contract/src/main.nr rust - -## `unconstrained` functions - -Unconstrained functions are an underlying part of Noir - a deeper explanation can be found [here](https://noir-lang.org/docs/noir/syntax/unconstrained). But in short, they are functions which are not directly constrained and therefore should be seen as untrusted! That they are untrusted means that, for security, the developer must make sure to constrain them when used! - -Beyond using them inside your other functions, they are convenient for providing an interface that reads storage, applies logic and returns values to a UI or test. Below is a snippet from exposing the `balance_of_private` function from a token implementation, which allows a user to easily read their balance, similar to the `balanceOf` function in the ERC20 standard. - -#include_code balance_of_private /yarn-project/noir-contracts/contracts/token_contract/src/main.nr rust - -:::info -Note, that unconstrained functions can have access to both public and private data when executed on the user's device. This is possible since it is not actually part of the circuits that are executed in contract execution. -::: - -## Oracle functions - -An oracle is something that allows us to get data from the outside world into our contracts. The most widely-known types of oracles in blockchain systems are probably Chainlink price feeds, which allow us to get the price of an asset in USD taking non-blockchain data into account. - -While this is one type of oracle, the more general oracle, allows us to get "some" data into the contract. In the context of oracle functions or oracle calls in Aztec, it can essentially be seen as user-provided arguments, that can be embedded at any point in the circuit, and thus don't need to be an input parameter. - -**Why is this useful? Why don't just pass them as input parameters?** -In the world of EVM, you would just read the values directly from storage and call it a day. However, when we are working with circuits for private execution, this becomes more tricky as you cannot just read the storage directly from your state tree, only commitments sit in there 😱. The pre-images (content) of your notes need to be provided to the function to prove that you actually allowed to spend them. - -You could of course provide them to your function as inputs, but then functions that have different underlying notes would end up with different function signatures and thus selectors. This means that integrating with many different tokens (with different underlying notes) would become a pain for the developers, see some of the motivation behind [EIP-4626](https://eips.ethereum.org/EIPS/eip-4626) for similar case in EVM. - -If we are instead fetching the notes using an oracle call, we can keep the function signature independent of the underlying notes and thus make it much easier to integrate with! A similar idea, but applied to the authentication mechanism is used for the Authentication Witnesses that allow us to have a single function signature for any wallet implementation making integrations a breeze, see [AuthWit](../../wallets/main#authorizing-actions) for more information on this. - -Oracles introduce **non-determinism** into a circuit, and thus are `unconstrained`. It is important that any information that is injected into a circuit through an oracle is later constrained for correctness. Otherwise, the circuit will be **under-constrained** and potentially insecure! - -`Aztec.nr` has a module dedicated to its oracles. If you are interested, you can view them by following the link below: -#include_code oracles-module /yarn-project/aztec-nr/aztec/src/oracle.nr rust - -### A few useful inbuilt oracles - -- [`debug_log`](https://github.com/AztecProtocol/aztec-packages/blob/master/yarn-project/aztec-nr/aztec/src/oracle/debug_log.nr) - Provides a couple of debug functions that can be used to log information to the console. -- [`auth_witness`](https://github.com/AztecProtocol/aztec-packages/blob/master/yarn-project/aztec-nr/authwit/src/auth_witness.nr) - Provides a way to fetch the authentication witness for a given address. This is useful when building account contracts to support approve-like functionality. -- [`get_l1_to_l2_message`](https://github.com/AztecProtocol/aztec-packages/blob/master/yarn-project/aztec-nr/aztec/src/oracle/get_l1_to_l2_message.nr) - Useful for application that receive messages from L1 to be consumed on L2, such as token bridges or other cross-chain applications. -- [`notes`](https://github.com/AztecProtocol/aztec-packages/blob/master/yarn-project/aztec-nr/aztec/src/oracle/notes.nr) - Provides a lot of functions related to notes, such as fetches notes from storage etc, used behind the scenes for value notes and other pre-build note implementations. -- [`logs`](https://github.com/AztecProtocol/aztec-packages/blob/master/yarn-project/aztec-nr/aztec/src/oracle/logs.nr) - Provides the to log encrypted and unencrypted data. - ---- - -## Calling functions from other functions - -### Private -> Private - -In Aztec Private to Private function calls are handled by the [private kernel circuit](../../../concepts/advanced/circuits/kernels/private_kernel.md), and take place on the user's device. -Behind the scenes, the `Private Execution Environment (PXE)` (the beating heart of Aztec that runs in your wallet) will execute all of the functions in the desired order "simulating" them in sequence. For example, a very common use-case of Private to Private interaction is calling another private function from an `account contract` (Account contracts are a general concept, more information about them can be found [here](../../wallets/writing_an_account_contract.md)). - -Take, for example, the following call stack: - -``` -AccountContract::entrypoint - |-> Foo::example_call - | -> Bar::nested_call - |-> Baz::example_call -``` - -In the example above the Account Contract has been instructed to call two external functions. In the first function all, to `Foo::example_call` a further nested call is performed to `Bar::nested_call`. Finally the account contract makes one last call to `Baz::example_call`. - -Lets further illustrate what these examples could look like - - - -```rust -// Foo contains a singular function that returns the result of Bar::nested_call -contract Foo { - #[aztec(private)] - fn example_call(input: Field) -> pub Field { - Bar::at().nested_call(input) - } -} - -// Bar contains a singular function that returns a `input + 1` -contract Bar { - #[aztec(private)] - fn nested_call(input: Field) -> pub Field { - input + 1 - } -} - -// Baz contains a singular function that simply returns `10` -contract Baz { - #[aztec(private)] - fn example_call() -> pub Field { - 10 - } -} -``` - -When simulating the following call stack, we can expect execution flow to continue procedurally. The simulator will begin at the account contract's entry point, find a call to `Foo::example_call`, then begin to execute the code there. When the simulator executes the code in contract `Foo`, it will find the further nested call to contract `Bar::nested_call`. It will execute the code in `Bar`, bringing the return value back to contract `Foo`. -The same process will be followed for contract `Baz`. - -So far the provided example is identical to other executions. Ethereum execution occurs in a similar way, during execution the EVM will execute instructions until it reaches an external call, where it will hop into a new context and execute code there, returning back when it is complete, bringing with it return values from the foreign execution. - -Those of you who have written circuits before may see an issue here. The account contract, contract `Foo`, `Bar` and `Baz` are all distinct circuits, which do not know anything about each other. How is it possible to use a value from contract `Bar` in contract `Foo`? This value will not be constrained. - -This is where the `kernel` circuit comes in. Once the execution of all of our functions has completed, we can just prove the execution of each of them independently. It is the job of the `kernel` circuit to constrain that the input parameters in a cross function call are correct, as well as the return values. The kernel will constrain that the value returned from `Foo::example_call` is the same value that is returned from `Bar::nested_call`, it will also be able to constrain the value returned by `Bar::nested_call` is the inputs to `Foo::example_call` + 1. - -The orchestration of these calls has an added benefit. All of the nested calls are **recursively proven**. This means that the kernel circuit essentially gobbles up each of our function's execution proofs. Condensing the size of the final proof to just be one. - - - -With this intuition in place, lets see how we actually perform the call. To make things easier, we can make a small struct that wraps the calls to something as seen in the `token_interface`s burn function below. This struct is just providing us a clean way to call function, but we could also just call the function directly as it is done in this function. - -:::info -Note that the function selector is computed using one of the oracles from earlier, and that the first `Field` is wrapped in parenthesis. Structs are outlined in tuple-form for selector computation, so they are wrapped in parenthesis--`AztecAddress` becomes `(Field)`. -::: - -#include_code private_burn_interface /yarn-project/noir-contracts/contracts/token_bridge_contract/src/token_interface.nr rust - -Using this interface, we can then call it as seen below. All the way down at the bottom we can see that we are calling the `burn` function from the `token_interface` struct. - -The following snippet is from a token bridge that is burning the underlying token and creating a message for L1 to mint some assets to the `recipient` on Ethereum. - -#include_code exit_to_l1_private /yarn-project/noir-contracts/contracts/token_bridge_contract/src/main.nr rust - -### Public -> Public - -The public execution environment in Aztec takes place on the sequencer through a [Public VM](../../../concepts/advanced/public_vm.md). This execution model is conceptually much simpler than the private transaction model as code is executed and proven on the sequencer. - -Using the same example code and call stack from the section [above](#private----private-function-calls), we will walk through how it gets executed in public. - -The first key difference is that public functions are not compiled to circuits, rather they are compiled to `Aztec Bytecode` (might also be referred to as brillig). - -This bytecode is run by the sequencer in the `Aztec VM`, which is in turn proven by the [`Aztec VM circuit`](../../../concepts/advanced/public_vm.md). -The mental model for public execution carries many of the same idea as are carried by Ethereum. Programs are compiled into a series of opcodes (known as bytecode). This bytecode is then executed. The extra step for the Aztec VM is that each opcode is then proven for correctness. - -Calling a public function from another public function is quite similar to what we saw for private to private, with the keyword private swapped for public. - -#include_code public_burn_interface /yarn-project/noir-contracts/contracts/token_bridge_contract/src/token_interface.nr rust -#include_code exit_to_l1_public /yarn-project/noir-contracts/contracts/token_bridge_contract/src/main.nr rust - -### Private -> Public - -As discussed above, private function execution and calls take place on the user's device, while public function execution and calls take place on a sequencer, in two different places at two different times, it is natural to question how we can achieve composability between the two. The solution is asynchronicity. Further reading can be found in the foundational concepts [here](../../../concepts/foundation/communication/public_private_calls/main.md). - -Private function execution takes place on the users device, where it keeps track of any public function calls that have been made. Whenever private execution completes, and a kernel proof is produced, the transaction sent to the network will include all of the public calls that were dispatched. -When the sequencer receives the messages, it will take over and execute the public parts of the transaction. - -As a consequence a private function _CANNOT_ accept a return value from a public function. It can only dispatch it. - -The code required to dispatch a public function call from a private function is actually quite similar to private to private calls. As an example, we will look at the token contract, where users can unshield assets from private to public domain, essentially a transfer from a private account to a public one (often used for depositing privately into DeFi etc). - -#include_code unshield /yarn-project/noir-contracts/contracts/token_contract/src/main.nr rust -#include_code increase_public_balance /yarn-project/noir-contracts/contracts/token_contract/src/main.nr rust - -As we can see above, the private to public transaction flow looks very similar to the others in snippets, with the practicality being a bit different behind the scenes. - - - -### Public -> Private - -Wait, I thought you said we could not do this? Well, you are right, we cannot do this directly. However, we can do this indirectly if we are a little cheeky. - -While we cannot directly call a private function, we can indirectly call it by adding a commitment to the note hash tree. This commitment can then be consumed by a private function later, to "finish" the execution. So while it is not practically a call, we can ensure that it could only happen as an effect of a public function call, which is still pretty useful. - -In the snippet below, we insert a custom note, the transparent note, into the commitments tree from public such that it can later be consumed in private. - -#include_code shield /yarn-project/noir-contracts/contracts/token_contract/src/main.nr rust - -If you recall the `redeem_shield` from back in the [private function section](#private-functions), you might remember it removing a `TransparentNote` from `pending_shields`. This is the note that we just inserted from public! - -#include_code redeem_shield /yarn-project/noir-contracts/contracts/token_contract/src/main.nr rust - -When the note is removed, it emits a nullifier so that it cannot be used again. This nullifier is then added to the note hash tree, and can be used to prove that the note was removed from the pending shields. Interestingly, we can generate the nullifier such that no-one who saw the public execution will know that it have been consumed. When sending messages between L1 and L2 in [portals](../portals/main.md) we are going to see this pattern again. - -:::danger -Something to be mindful of when inserting from public. Everyone can see the insertion and what happens in public, so if you are including a secret directly anyone would be able to see it. This is why the hash of the secret is used in the snippet above (`secret_hash`). -::: - ---- - -## Deep dive - -Below, we go more into depth of what is happening under the hood when you create a function in Aztec.nr and what the attributes are really doing. - -### Function type attributes explained. - -Aztec.nr uses an attribute system to annotate a function's type. Annotating a function with the `#[aztec(private)]` attribute tells the framework that this will be a private function that will be executed on a users device. Thus the compiler will create a circuit to define this function. - -However; `#aztec(private)` is just syntactic sugar. At compile time, the framework inserts code that allows the function to interact with the [kernel](../../../concepts/advanced/circuits/kernels/private_kernel.md). - -To help illustrate how this interacts with the internals of Aztec and its kernel circuits, we can take an example private function, and explore what it looks like after Aztec.nr's macro expansion. - -#### Before expansion - -#include_code simple_macro_example /yarn-project/noir-contracts/contracts/docs_example_contract/src/main.nr rust - -#### After expansion - -#include_code simple_macro_example_expanded /yarn-project/noir-contracts/contracts/docs_example_contract/src/main.nr rust - -#### The expansion broken down? - -Viewing the expanded noir contract uncovers a lot about how noir contracts interact with the [kernel](../../../concepts/advanced/circuits/kernels/private_kernel.md). To aid with developing intuition, we will break down each inserted line. - -**Receiving context from the kernel.** -#include_code context-example-inputs /yarn-project/noir-contracts/contracts/docs_example_contract/src/main.nr rust - -Private function calls are able to interact with each other through orchestration from within the [kernel circuit](../../../concepts/advanced/circuits/kernels/private_kernel.md). The kernel circuit forwards information to each app circuit. This information then becomes part of the private context. -For example, within each circuit we can access some global variables. To access them we can call `context.chain_id()`. The value of this chain ID comes from the values passed into the circuit from the kernel. - -The kernel can then check that all of the values passed to each circuit in a function call are the same. - -**Returning the context to the kernel.** -#include_code context-example-return /yarn-project/noir-contracts/contracts/docs_example_contract/src/main.nr rust - -Just as the kernel passes information into the app circuits, the application must return information about the executed app back to the kernel. This is done through a rigid structure we call the `PrivateCircuitPublicInputs`. - -> _Why is it called the `PrivateCircuitPublicInputs`_ -> It is commonly asked why the return values of a function in a circuit are labeled as the `Public Inputs`. Common intuition from other programming paradigms suggests that the return values and public inputs should be distinct. -> However; In the eyes of the circuit, anything that is publicly viewable (or checkable) is a public input. Hence in this case, the return values are also public inputs. - -This structure contains a host of information about the executed program. It will contain any newly created nullifiers, any messages to be sent to l2 and most importantly it will contain the actual return values of the function! - -**Hashing the function inputs.** -#include_code context-example-hasher /yarn-project/noir-contracts/contracts/docs_example_contract/src/main.nr rust - -_What is the hasher and why is it needed?_ - -Inside the kernel circuits, the inputs to functions are reduced to a single value; the inputs hash. This prevents the need for multiple different kernel circuits; each supporting differing numbers of inputs. The hasher abstraction that allows us to create an array of all of the inputs that can be reduced to a single value. - -**Creating the function's context.** -#include_code context-example-context /yarn-project/noir-contracts/contracts/docs_example_contract/src/main.nr rust - -Each Aztec function has access to a [context](./context.mdx) object. This object although ergonomically a global variable, is local. It is initialized from the inputs provided by the kernel, and a hash of the function's inputs. - -#include_code context-example-context-return /yarn-project/noir-contracts/contracts/docs_example_contract/src/main.nr rust - -As previously mentioned we use the kernel to pass information between circuits. This means that the return values of functions must also be passed to the kernel (where they can be later passed on to another function). -We achieve this by pushing return values to the execution context, which we then pass to the kernel. - -**Making the contract's storage available** -#include_code storage-example-context /yarn-project/noir-contracts/contracts/docs_example_contract/src/main.nr rust - -When a [`Storage` struct](./storage/main.md) is declared within a contract, the `storage` keyword is made available. As shown in the macro expansion above, this calls the init function on the storage struct with the current function's context. - -Any state variables declared in the `Storage` struct can now be accessed as normal struct members. - -**Returning the function context to the kernel.** -#include_code context-example-finish /yarn-project/noir-contracts/contracts/docs_example_contract/src/main.nr rust - -This function takes the application context, and converts it into the `PrivateCircuitPublicInputs` structure. This structure is then passed to the kernel circuit. diff --git a/docs/docs/dev_docs/contracts/syntax/storage/main.md b/docs/docs/dev_docs/contracts/syntax/storage/main.md deleted file mode 100644 index b774ee0c682..00000000000 --- a/docs/docs/dev_docs/contracts/syntax/storage/main.md +++ /dev/null @@ -1,531 +0,0 @@ ---- -title: Storage ---- - -Smart contracts rely on storage, acting as the persistent memory on the blockchain. In Aztec, because of its hybrid, privacy-first architecture, the management of this storage is more complex than other blockchains like Ethereum. - -You control this storage in Aztec using the `Storage` struct. This struct serves as the housing unit for all your smart contract's state variables - the data it needs to keep track of and maintain. - -These state variables come in two forms: public and private. Public variables are visible to anyone, and private variables remain hidden within the contract. - -Aztec.nr has a few abstractions to help define the type of data your contract holds. These include Singletons, ImmutableSingletons, Set, and Map. - -On this page, you’ll learn: - -- How to manage a smart contract's storage structure -- The distinctions and applications of public and private state variables -- How to use Singleton, ImmutableSingleton, Set, and Map -- An overview of 'notes' and the UTXO model -- Practical implications of Storage in real smart contracts - In an Aztec.nr contract, storage is to be defined as a single struct, that contains both public and private state variables. - -## Public and private state variables - -Public state variables can be read by anyone, while private state variables can only be read by their owner (or people whom the owner has shared the decrypted data or note viewing key with). - -Public state follows the Ethereum style account model, where each contract has its own key-value datastore. Private state follows a UTXO model, where note contents (pre-images) are only known by the sender and those able to decrypt them - see ([state model](../../../../concepts/foundation/state_model/main.md) and [private/public execution](../../../../concepts/foundation/communication/public_private_calls/main.md)) for more background. - -## Storage struct - -:::info -The struct **must** be called `Storage` for the Aztec.nr library to properly handle it (this will be relaxed in the future). -::: - -```rust -struct Storage { - // public state variables - // private state variables -} -``` - -:::danger -If your contract works with storage (has Storage struct defined), you **MUST** include a `compute_note_hash_and_nullifier` function to allow PXE to process encrypted events. See [encrypted events](../events.md#processing-encrypted-events) for more. - -If you don't yet have any private state variables defined put there a placeholder function: - -#include_code compute_note_hash_and_nullifier_placeholder /yarn-project/noir-contracts/contracts/token_bridge_contract/src/main.nr rust -::: - -Since Aztec.nr is written in Noir, which on its own is state-less, we need to specify how the storage struct should be initialized to read and write data correctly. This is done by specifying an `init` function that is run in functions that rely on reading or altering the state variables. This `init` function must declare the storage struct with an instantiation defining how variables are accessed and manipulated. The function MUST be called `init` for the Aztec.nr library to properly handle it (this will be relaxed in the future). - -```rust -impl Storage { - fn init(context: Context) -> Self { - Storage { - // (public state variables)::new() - // (private state variables)::new() - } - } -} -``` - -If you have defined a `Storage` struct following this naming scheme, then it will be made available to you through the reserved `storage` keyword within your contract functions. - -:::warning Using slot `0` is not supported! -No storage values should be initialized at slot `0` - storage slots begin at `1`. This is a known issue that will be fixed in the future. -::: - -## Map - -A `map` is a state variable that "maps" a key to a value. It can be used with private or public storage variables. - -:::info -In Aztec.nr, keys are always `Field`s (or types that can be serialized as Fields) and values can be any type - even other maps. `Field`s are finite field elements, but you can think of them as integers for now. -::: - -It includes a [`Context`](../context.mdx) to specify the private or public domain, a `storage_slot` to specify where in storage the map is stored, and a `start_var_constructor` which tells the map how it should operate on the underlying type. This includes how to serialize and deserialize the type, as well as how commitments and nullifiers are computed for the type if it's private. - -You can view the implementation in the Aztec.nr library [here](https://github.com/AztecProtocol/aztec-packages/blob/master/yarn-project/aztec-nr/aztec/src/state_vars/map.nr). - -### `new` - -When declaring the storage for a map, we use the `Map::new()` constructor. As seen below, this takes the `storage_slot` and the `start_var_constructor` along with the [`Context`](../context.mdx). - -We will see examples of map constructors for public and private variables in later sections. - -#### As private storage - -When declaring a mapping in private storage, we have to specify which type of Note to use. In the example below, we are specifying that we want to use the `Singleton` note type. See the [Singleton](#singletonnotetype) section for more information. - -In the Storage struct: - -#include_code storage-map-singleton-declaration /yarn-project/noir-contracts/contracts/docs_example_contract/src/main.nr rust - -In the `Storage::init` function: - -#include_code state_vars-MapSingleton /yarn-project/noir-contracts/contracts/docs_example_contract/src/main.nr rust - -#### Public Example - -When declaring a public mapping in Storage, we have to specify that the type is public by declaring it as `PublicState` instead of specifying a note type like with private storage above. - -In the Storage struct: - -#include_code storage_minters /yarn-project/noir-contracts/contracts/token_contract/src/main.nr rust - -In the `Storage::init` function: - -#include_code storage_minters_init /yarn-project/noir-contracts/contracts/token_contract/src/main.nr rust - -### `at` - -When dealing with a map, we can access the value at a given key using the `::at` method. This takes the key as an argument and returns the value at that key. - -This function behaves similarly for both private and public maps. An example could be if we have a map with `minters`, which is mapping addresses to a flag for whether they are allowed to mint tokens or not. - -#include_code read_minter /yarn-project/noir-contracts/contracts/token_contract/src/main.nr rust - -Above, we are specifying that we want to get the storage in the Map `at` the `msg_sender()`, read the value stored and check that `msg_sender()` is indeed a minter. Doing a similar operation in Solidity code would look like: - -```solidity -require(minters[msg.sender], "caller is not minter"); -``` - -## Public State Variables - -To define that a variable is public, it is wrapped in the `PublicState` struct. - -The `PublicState` struct is generic over the variable type `T` and its serialized size `T_SERIALIZED_LEN`. -:::info -Currently, the length of the types must be specified when declaring the storage struct but the intention is that this will be inferred in the future. -::: - -The struct contains a `storage_slot` which, similar to Ethereum, is used to figure out _where_ in storage the variable is located. Notice that while we don't have the exact same [state model](../../../../concepts/foundation/state_model/main.md) as EVM chains it will look similar from the contract developers point of view. - -Beyond the struct, the `PublicState` also contains `serialization_methods`, which is a struct with methods that instruct the `PublicState` how to serialize and deserialize the variable. You can find the details of `PublicState` in the implementation [here](https://github.com/AztecProtocol/aztec-packages/blob/master/yarn-project/aztec-nr/aztec/src/state_vars/public_state.nr). - -:::info -The British haven't surrendered fully to US spelling, so serialization is currently inconsistent. -::: - -The Aztec.nr library provides serialization methods for various common types. - -:::info -An example using a larger struct can be found in the [lending example](https://github.com/AztecProtocol/aztec-packages/tree/master/yarn-project/noir-contracts/contracts/lending_contract)'s use of an [`Asset`](https://github.com/AztecProtocol/aztec-packages/tree/master/yarn-project/noir-contracts/contracts/lending_contract/src/asset.nr). -::: - -### `new` - -When declaring the storage for `T` as a persistent public storage variable, we use the `PublicState::new()` constructor. As seen below, this takes the `storage_slot` and the `serialization_methods` as arguments along with the [`Context`](../context.mdx), which in this case is used to share interface with other structures. - -#include_code public_state_struct_new /yarn-project/aztec-nr/aztec/src/state_vars/public_state.nr rust - -#### Single value example - -Say that we wish to add `admin` public state variable into our storage struct. In the struct we can define it as: - -#include_code storage_admin /yarn-project/noir-contracts/contracts/token_contract/src/main.nr rust - -And then when initializing it in the `Storage::init` function we can do: - -#include_code storage_admin_init /yarn-project/noir-contracts/contracts/token_contract/src/main.nr rust - -We have specified that we are storing a `Field` that should be placed in storage slot `1`. This is just a single value, and is similar to the following in solidity: - -```solidity -address internal admin; -``` - -:::info -We know its verbose, and are working on making it less so. -::: - -#### Mapping example - -Say we want to have a group of `minters` that are able to mint assets in our contract, and we want them in public storage, because [access control in private is quite cumbersome](../../../../concepts/foundation/communication/public_private_calls/main.md#a-note-on-l2-access-control). In the `Storage` struct we can add it as follows: - -#include_code storage_minters /yarn-project/noir-contracts/contracts/token_contract/src/main.nr rust - -And then when initializing it in the `Storage::init` function we can do it as follows: - -#include_code storage_minters_init /yarn-project/noir-contracts/contracts/token_contract/src/main.nr rust - -In this case, specifying that we are dealing with a map of Fields, and that it should be put at slot 2. - -This would be similar to the following in solidity: - -```solidity -mapping(address => bool) internal minters; -``` - -### `read` - -On the `PublicState` structs we have a `read` method to read the value at the location in storage and using the specified deserialization method to deserialize it. - -#### Reading from our `admin` example - -For our `admin` example from earlier, this could be used as follows to check that the stored value matches the `msg_sender()`. - -#include_code read_admin /yarn-project/noir-contracts/contracts/token_contract/src/main.nr rust - -#### Reading from our `minters` example - -As we saw in the Map earlier, a very similar operation can be done to perform a lookup in a map. - -#include_code read_minter /yarn-project/noir-contracts/contracts/token_contract/src/main.nr rust - -### `write` - -We have a `write` method on the `PublicState` struct that takes the value to write as an input and saves this in storage. It uses the serialization method to serialize the value which inserts (possibly multiple) values into storage. - -#### Writing to our `admin` example - -#include_code write_admin /yarn-project/noir-contracts/contracts/token_contract/src/main.nr rust - -#### Writing to our `minters` example - -#include_code write_minter /yarn-project/noir-contracts/contracts/token_contract/src/main.nr rust - -## Private State Variables - -In contrast to public state, private state is persistent state that is **not** visible to the whole world. Depending on the logic of the smart contract, a private state variable's current value will only be known to one entity, or a closed group of entities. - -The value of a private state variable can either be shared via an [encrypted log](../events.md#encrypted-events), or offchain via web2, or completely offline: it's up to the app developer. - -Aztec private state follows a utxo-based model. That is, a private state's current value is represented as one or many [notes](#notes). Each note is stored as an individual leaf in a utxo-based merkle tree: the [private state tree](../../../../concepts/foundation/state_model/main.md). - -To greatly simplify the experience of writing private state, Aztec.nr provides three different types of private state variable: - -- [Singleton](#singletonnotetype) -- [ImmutableSingleton](#immutablesingletonnotetype) -- [Set](#setnotetype) - -These three structs abstract-away many of Aztec's protocol complexities, by providing intuitive methods to modify notes in the utxo tree in a privacy-preserving way. - -:::info -An app can also choose to emit data via unencrypted log, or to define a note whose data is easy to figure out, then the information is technically not private and could be visible to anyone. -::: - -### Notes - -Unlike public state variables, which can be arbitrary types, private state variables operate on `NoteType`. - -Notes are the fundamental elements in the private world. - -A note should conform to the following interface: - -#include_code NoteInterface /yarn-project/aztec-nr/aztec/src/note/note_interface.nr rust - -The interplay between a private state variable and its notes can be confusing. Here's a summary to aid intuition: - -A private state variable (of type `Singleton`, `ImmutableSingleton` or `Set`) may be declared in storage. - -Every note contains (as a 'header') the contract address and storage slot of the state variable to which it "belongs". A note is said to "belong" to a private state if the storage slot of the private state matches the storage slot contained in the note's header. The header provides information that helps the user interpret the note's data. Without it the user would have to figure out where it belongs by brute-forcing the address and contract space. - -Management of this 'header' is abstracted-away from developers who use the `ImmutableSingleton`, `Singleton` and `Set` types. - -A private state variable is colloquially said to "point" to one or many notes (depending on the type), if those note(s) all "belong" to that private state, and those note(s) haven't-yet been nullified. - -An `ImmutableSingleton` will point to _one_ note over the lifetime of the contract. ("One", hence "Singleton"). This note is a struct of information that is persisted forever. - -A `Singleton` may point to _one_ note at a time. ("One", hence "Singleton"). But since it's not "immutable", the note that it points to may be [replaced](#replace) by functions of the contract, over time. The "current value" of a `Singleton` is interpreted as the one note which has not-yet been nullified. The act of 'replacing' a Singleton's note is how a `Singleton` state may be modified by functions. - -`Singleton` is a useful type when declaring a private state which may only ever be modified by those who are privy to the current value of that state. - -A `Set` may point to _multiple_ notes at a time. The "current value" of a private state variable of type `Set` is some 'accumulation' of all not-yet nullified notes which "belong" to the `Set`. The term "some accumulation" is intentionally vague. The interpretation of the "current value" of a `Set` must be expressed by the smart contract developer. A common use case for a `Set` is to represent the sum of a collection of values (in which case 'accumulation' is 'summation'). - -Think of a ZCash balance (or even a Bitcoin balance). The "current value" of a user's ZCash balance is the sum of all unspent (not-yet nullified) notes belonging to that user. To modify the "current value" of a `Set` state variable, is to [`insert`](#insert) new notes into the `Set`, or [`remove`](#remove) notes from that set. - -Interestingly, if a developer requires a private state to be modifiable by users who _aren't_ privy to the value of that state, a `Set` is a very useful type. The `insert` method allows new notes to be added to the `Set` without knowing any of the other notes in the set! (Like posting an envelope into a post box, you don't know what else is in there!). - -## `Singleton` - -Singleton is a private state variable that is unique in a way. When a Singleton is initialized, a note is created to represent its value. And the way to update the value is to destroy the current note, and create a new one with the updated value. - -Like for public state, we define the struct to have context, a storage slot and `note_interface` specifying how the note should be constructed and manipulated. You can view the implementation [here](https://github.com/AztecProtocol/aztec-packages/blob/master/yarn-project/aztec-nr/aztec/src/state_vars/singleton.nr). - -An example of singleton usage in the account contracts is keeping track of public keys. The `Singleton` is added to the `Storage` struct as follows: - -#include_code storage-singleton-declaration /yarn-project/noir-contracts/contracts/docs_example_contract/src/main.nr rust - -### `new` - -As part of the initialization of the `Storage` struct, the `Singleton` is created as follows, here at the specified storage slot and with the `NoteInterface` for `CardNote`. - -#include_code start_vars_singleton /yarn-project/noir-contracts/contracts/docs_example_contract/src/main.nr rust - -### `initialize` - -As mentioned, the Singleton is initialized to create the first note and value. - -When this function is called, a nullifier of the storage slot is created, preventing this Singleton from being initialized again. If an `owner` is specified, the nullifier will be hashed with the owner's secret key. It's crucial to provide an owner if the Singleton is associated with an account. Initializing it without an owner may inadvertently reveal important information about the owner's intention. - -Unlike public states, which have a default initial value of `0` (or many zeros, in the case of a struct, array or map), a private state (of type `Singleton`, `ImmutableSingleton` or `Set`) does not have a default initial value. The `initialize` method (or `insert`, in the case of a `Set`) must be called. - -:::info -Extend on what happens if you try to use non-initialized state. -::: - -### `is_initialized` - -An unconstrained method to check whether the Singleton has been initialized or not. It takes an optional owner and returns a boolean. You can view the implementation [here](https://github.com/AztecProtocol/aztec-packages/blob/master/yarn-project/aztec-nr/aztec/src/state_vars/singleton.nr). - -### `replace` - -To update the value of a `Singleton`, we can use the `replace` method. The method takes a new note as input, and replaces the current note with the new one. It emits a nullifier for the old value, and inserts the new note into the data tree. - -An example of this is seen in a example card game, where we create a new note (a `CardNote`) containing some new data, and replace the current note with it: - -#include_code state_vars-SingletonReplace /yarn-project/noir-contracts/contracts/docs_example_contract/src/main.nr rust - -If two people are trying to modify the Singleton at the same time, only one will succeed as we don't allow duplicate nullifiers! Developers should put in place appropriate access controls to avoid race conditions (unless a race is intended!). - -### `get_note` - -This function allows us to get the note of a Singleton, essentially reading the value. - -#include_code state_vars-SingletonGet /yarn-project/noir-contracts/contracts/docs_example_contract/src/main.nr rust - -#### Nullifying Note reads - -It's possible that at the time this function is called, the system hasn't synced to the block where the latest note was created. Or a malicious user might feed an old state to this function, tricking the proving system into thinking that the value hasn't changed. To avoid an attack around it, this function will destroy the current note, and replace it with a duplicated note that has the same fields. Because the nullifier of the latest note will be emitted, if two people are trying to use this function against the same note, only one will succeed (no duplicate nullifiers allowed). - -### `view_note` - -Functionally similar to [`get_note`](#get_note), but executed in unconstrained functions and can be used by the wallet to fetch notes for use by front-ends etc. - -## `ImmutableSingleton` - -ImmutableSingleton represents a unique private state variable that, as the name suggests, is immutable. Once initialized, its value cannot be altered. - -#include_code struct /yarn-project/aztec-nr/aztec/src/state_vars/immutable_singleton.nr rust - -### `new` - -As part of the initialization of the `Storage` struct, the `Singleton` is created as follows, here at storage slot 1 and with the `NoteInterface` for `PublicKeyNote`. - -#include_code storage_init /yarn-project/noir-contracts/contracts/schnorr_account_contract/src/main.nr rust - -### `initialize` - -When this function is invoked, it creates a nullifier for the storage slot, ensuring that the ImmutableSingleton cannot be initialized again. If an owner is specified, the nullifier will be hashed with the owner's secret key. It is crucial to provide an owner if the ImmutableSingleton is linked to an account; initializing it without one may inadvertently disclose sensitive information about the owner's intent. - -Set the value of an ImmutableSingleton by calling the `initialize` method: - -#include_code initialize /yarn-project/noir-contracts/contracts/schnorr_account_contract/src/main.nr rust - -Once initialized, an ImmutableSingleton's value remains unchangeable. This method can only be called once. - -### `is_initialized` - -An unconstrained method to check if the ImmutableSingleton has been initialized. Takes an optional owner and returns a boolean. You can find the implementation [here](https://github.com/AztecProtocol/aztec-packages/blob/master//yarn-project/aztec-nr/aztec/src/state_vars/immutable_singleton.nr). - -### `get_note` - -Similar to the `Singleton`, we can use the `get_note` method to read the value of an ImmutableSingleton. - -Use this method to retrieve the value of an initialized ImmutableSingleton. - -#include_code get_note /yarn-project/noir-contracts/contracts/schnorr_account_contract/src/main.nr rust - -Unlike a `Singleton`, the `get_note` function for an ImmutableSingleton doesn't destroy the current note in the background. This means that multiple accounts can concurrently call this function to read the value. - -This function will throw if the ImmutableSingleton hasn't been initialized. - -### `view_note` - -Functionally similar to `get_note`, but executed unconstrained and can be used by the wallet to fetch notes for use by front-ends etc. - -## `Set` - -Set is used for managing a collection of notes. All notes in a set are of the same `NoteType`. But whether these notes all belong to one entity, or are accessible and editable by different entities, is totally up to the developer. Due to our state model, the set is a collection of notes inserted into the data-tree, but notes are never removed from the tree itself, they are only nullified. - -#include_code struct /yarn-project/aztec-nr/aztec/src/state_vars/set.nr rust - -And can be added to the `Storage` struct as follows. Here adding a set for a custom note, the TransparentNote (useful for [public -> private communication](../functions.md#public---private)). - -#include_code storage_pending_shields /yarn-project/noir-contracts/contracts/token_contract/src/main.nr rust - -### `new` - -The `new` method tells the contract how to operate on the underlying storage. - -We can initialize the set as follows: - -#include_code storage_pending_shields_init /yarn-project/noir-contracts/contracts/token_contract/src/main.nr rust - -### `insert` - -Allows us to modify the storage by inserting a note into the set. - -A commitment from the note will be generated, and inserted into data-tree, allowing us to later use in contract interactions. - -A commitment from the note will be generated, and inserted into data-tree, allowing us to later use in contract interactions. Recall that the content of the note should be shared with the owner to allow them to use it, as mentioned this can be done via an [encrypted log](../events.md#encrypted-events), or offchain via web2, or completely offline. - -#include_code insert /yarn-project/aztec-nr/easy-private-state/src/easy_private_state.nr rust - -### `insert_from_public` - -While we don't support private functions to directly alter public storage, the opposite direction is possible. The `insert_from_public` allow public function to insert notes into private storage. This is very useful when we want to support private function calls that must have been initiated in public, such as shielding or the like. - -The usage is rather straight-forward and very similar to using the `insert` method with the difference that this one is called in public functions. - -#include_code insert_from_public /yarn-project/noir-contracts/contracts/token_contract/src/main.nr rust - -### `remove` - -Will remove a note from the set if it previously has been read from storage, e.g. you have fetched it through a `get_notes` call. This is useful when you want to remove a note that you have previously read from storage and do not have to read it again. - -Nullifiers are emitted when reading values to make sure that they are up to date. - -An example of how to use this operation is visible in the `easy_private_state`: - -#include_code remove /yarn-project/aztec-nr/easy-private-state/src/easy_private_state.nr rust - -### `get_notes` - -This function returns the notes the account has access to. - -The kernel circuits are constrained to a maximum number of notes this function can return at a time. Check [here](https://github.com/AztecProtocol/aztec-packages/blob/master/yarn-project/noir-protocol-circuits/src/crates/types/src/constants.nr) and look for `MAX_READ_REQUESTS_PER_CALL` for the up-to-date number. - -Because of this limit, we should always consider using the second argument `NoteGetterOptions` to limit the number of notes we need to read and constrain in our programs. This is quite important as every extra call increases the time used to prove the program and we don't want to spend more time than necessary. - -An example of such options is using the [filter_notes_min_sum](https://github.com/AztecProtocol/aztec-packages/blob/master/yarn-project/aztec-nr/value-note/src/filter.nr) to get "enough" notes to cover a given value. Essentially, this function will return just enough notes to cover the amount specified such that we don't need to read all our notes. For users with a lot of notes, this becomes increasingly important. - -#include_code get_notes /yarn-project/aztec-nr/easy-private-state/src/easy_private_state.nr rust - -### `view_notes` - -Functionally similar to [`get_notes`](#get_notes), but executed unconstrained and can be used by the wallet to fetch notes for use by front-ends etc. - -#include_code view_notes /yarn-project/aztec-nr/value-note/src/balance_utils.nr rust - -There's also a limit on the maximum number of notes that can be returned in one go. To find the current limit, refer to [this file](https://github.com/AztecProtocol/aztec-packages/blob/master/yarn-project/noir-protocol-circuits/src/crates/types/src/constants.nr) and look for `MAX_NOTES_PER_PAGE`. - -The key distinction is that this method is unconstrained. It does not perform a check to verify if the notes actually exist, which is something the [`get_notes`](#get_notes) method does under the hood. Therefore, it should only be used in an unconstrained contract function. - -This function requires a `NoteViewerOptions`. The `NoteViewerOptions` is essentially similar to the [`NoteGetterOptions`](#notegetteroptions), except that it doesn't take a custom filter. - -### NoteGetterOptions - -`NoteGetterOptions` encapsulates a set of configurable options for filtering and retrieving a selection of notes from a [data oracle](../functions.md#oracle-functions). Developers can design instances of `NoteGetterOptions`, to determine how notes should be filtered and returned to the functions of their smart contracts. - -#include_code NoteGetterOptions /yarn-project/aztec-nr/aztec/src/note/note_getter_options.nr rust - -#### `selects: BoundedVec, N>` - -`selects` is a collection of filtering criteria, specified by `Select { field_index: u8, value: Field }` structs. It instructs the data oracle to find notes whose (`field_index`)th field matches the provided `value`. - -#### `sorts: BoundedVec, N>` - -`sorts` is a set of sorting instructions defined by `Sort { field_index: u8, order: u2 }` structs. This directs the data oracle to sort the matching notes based on the value of the specified field index and in the indicated order. The value of order is **1** for _DESCENDING_ and **2** for _ASCENDING_. - -#### `limit: u32` - -When the `limit` is set to a non-zero value, the data oracle will return a maximum of `limit` notes. - -#### `offset: u32` - -This setting enables us to skip the first `offset` notes. It's particularly useful for pagination. - -#### `filter: fn ([Option; MAX_READ_REQUESTS_PER_CALL], FILTER_ARGS) -> [Option; MAX_READ_REQUESTS_PER_CALL]` - -Developers have the option to provide a custom filter. This allows specific logic to be applied to notes that meet the criteria outlined above. The filter takes the notes returned from the oracle and `filter_args` as its parameters. - -It's important to note that the process of applying the custom filter to get the final notes is not constrained. It's crucial to verify the returned notes even if certain assumptions are made in the custom filter. - -#### `filter_args: FILTER_ARGS` - -`filter_args` provides a means to furnish additional data or context to the custom filter. - -#### Methods - -Several methods are available on `NoteGetterOptions` to construct the options in a more readable manner: - -#### `fn new() -> NoteGetterOptions` - -This function initializes a `NoteGetterOptions` that simply returns the maximum number of notes allowed in a call. - -#### `fn with_filter(filter, filter_args) -> NoteGetterOptions` - -This function initializes a `NoteGetterOptions` with a [`filter`](#filter-fn-optionnote-max_read_requests_per_call-filter_args---optionnote-max_read_requests_per_call) and [`filter_args`](#filter_args-filter_args). - -#### `.select` - -This method adds a [`Select`](#selects-boundedvecoptionselect-n) criterion to the options. - -#### `.sort` - -This method adds a [`Sort`](#sorts-boundedvecoptionsort-n) criterion to the options. - -#### `.set_limit` - -This method lets you set a limit for the maximum number of notes to be retrieved. - -#### `.set_offset` - -This method sets the offset value, which determines where to start retrieving notes. - -#### Examples - -The following code snippet creates an instance of `NoteGetterOptions`, which has been configured to find the cards that belong to `account_address`. The returned cards are sorted by their points in descending order, and the first `offset` cards with the highest points are skipped. - -#include_code state_vars-NoteGetterOptionsSelectSortOffset /yarn-project/noir-contracts/contracts/docs_example_contract/src/options.nr rust - -The first value of `.select` and `.sort` is the index of a field in a note type. For the note type `CardNote` that has the following fields: - -#include_code state_vars-CardNote /yarn-project/noir-contracts/contracts/docs_example_contract/src/types/card_note.nr rust - -The indices are: 0 for `points`, 1 for `secret`, and 2 for `owner`. - -In the example, `.select(2, account_address)` matches the 2nd field of `CardNote`, which is `owner`, and returns the cards whose `owner` field equals `account_address`. - -`.sort(0, SortOrder.DESC)` sorts the 0th field of `CardNote`, which is `points`, in descending order. - -There can be as many conditions as the number of fields a note type has. The following example finds cards whose fields match the three given values: - -#include_code state_vars-NoteGetterOptionsMultiSelects /yarn-project/noir-contracts/contracts/docs_example_contract/src/options.nr rust - -While `selects` lets us find notes with specific values, `filter` lets us find notes in a more dynamic way. The function below picks the cards whose points are at least `min_points`: - -#include_code state_vars-OptionFilter /yarn-project/noir-contracts/contracts/docs_example_contract/src/options.nr rust - -We can use it as a filter to further reduce the number of the final notes: - -#include_code state_vars-NoteGetterOptionsFilter /yarn-project/noir-contracts/contracts/docs_example_contract/src/options.nr rust - -One thing to remember is, `filter` will be applied on the notes after they are picked from the database. Therefore, it's possible that the actual notes we end up getting are fewer than the limit. - -The limit is `MAX_READ_REQUESTS_PER_CALL` by default. But we can set it to any value **smaller** than that: - -#include_code state_vars-NoteGetterOptionsPickOne /yarn-project/noir-contracts/contracts/docs_example_contract/src/options.nr rust diff --git a/docs/docs/dev_docs/getting_started/core-concepts.md b/docs/docs/dev_docs/getting_started/core-concepts.md deleted file mode 100644 index e4549fb9731..00000000000 --- a/docs/docs/dev_docs/getting_started/core-concepts.md +++ /dev/null @@ -1,88 +0,0 @@ ---- -title: Core Concepts ---- - -import Image from '@theme/IdealImage'; - -This page outlines Aztec concepts that are essential for developers to understand. Understanding these concepts will help you as you start to dive deeper into smart contracts. - -A little bit of time here can save a lot down the road. - -## Aztec Overview - - - -To sum this up: - -1. A user interacts with Aztec through Aztec.js (like web3js or ethersjs) or Aztec CLI -2. Private functions are executed in the PXE, which is client-side -3. They are rolled up and sent to the Public VM (running on an Aztec node) -4. Public functions are executed in the Public VM -5. The Public VM rolls up the private & public transaction rollups -6. These rollups are submitted to Ethereum - -## Composability between private and public state - -The PXE is unaware of the Public VM. And the Public VM is unaware of the PXE. They are completely separate execution environments. This means: - -- The PXE and the Public VM cannot directly communicate with each other -- Private transactions in the PXE are executed first, followed by public transactions - -You can call a public function from a private function by using `context.call_public_function`, like this: - -#include_code call_public_function yarn-project/noir-contracts/contracts/card_game_contract/src/main.nr rust - -You cannot call a private function from a public function, but you can use a [slow updates tree](../contracts/syntax/slow_updates_tree.md) to read historical public state and stage writes to public state from a private function. - -### Data types - -Private state works with UTXOs, or what we call notes. To keep things private, everything is stored in an [append-only UTXO tree](../../concepts/advanced/data_structures/trees.md#note-hash-tree), and a nullifier is created when notes are invalidated. - -Public state works similarly to other chains like Ethereum, behaving like a public ledger. - -Working with private state is like creating commitments and nullifiers to state, whereas working with public state is like directly updating state. - -We have abstractions for working with private state so you don't have to worry about these commitments and nullifiers. However, it is important to understand that the types and libraries you use will be different when working with private state and public state. - -For example, let's say you're trying to work with an integer. We have a library called `EasyPrivateUint` that acts like an integer but in the background is actually updating notes in private state. For the public side, we instead have something called `SafeU120`. You cannot use EasyPrivateUint in a public environment, and you cannot use SafeU120 in a private environment. - -## Storage - -Currently, when writing Aztec.nr smart contracts, you will need to define two things when initiating storage: - -1. The storage struct, ie what you are storing and their types -2. A storage `impl` block with `init` function that will be called when you use the storage in a function - -The `init` function must declare the storage struct with an instantiation defining how variables are accessed and manipulated. Each variable must be given a storage slot, which can be anything except 0. - -The impl block is likely to be abstracted away at a later date. - -Learn more about how to use storage [here](../contracts/syntax/storage/main.md). - -## Portals - -Aztec allows you to interact with Ethereum privately - ie no-one knows where the transaction is coming from, just that it is coming from somewhere on Aztec. - -This is achieved through portals - these are smart contracts written in Solidity that are related to the Ethereum smart contract you want to interact with. - -A portal can be associated with multiple Aztec contracts, but an Aztec contract can only be associated with one portal. - -Learn more about how to work with portals [here](../contracts/portals/main.md). - -## Accounts - -Every account in Aztec is a smart contract (account abstraction). This allows implementing different schemes for transaction signing, nonce management, and fee payments. - -You can write your own account contract to define the rules by which user transactions are authorized and paid for, as well as how user keys are managed. - -Learn more about account contracts [here](../../concepts/foundation/accounts/main.md). - -## Noir Language - -Aztec smart contracts are written in a framework on top of Noir, the zero-knowledge domain-specific language developed specifically for Aztec. Its syntax is similar to Rust. Outside of Aztec, Noir is used for writing circuits that can be verified in Solidity. - -A cursory understanding of Noir is sufficient for writing Aztec contracts. The [Noir docs](https://noir-lang.org) will be a helpful reference when you start writing more advanced contracts. - -## Next steps - -Continue through the getting started section by reviewing how to write a smart contract contract in [Getting started with Aztec.nr](./aztecnr-getting-started.md). diff --git a/docs/docs/dev_docs/aztecjs/main.md b/docs/docs/developers/aztecjs/main.md similarity index 100% rename from docs/docs/dev_docs/aztecjs/main.md rename to docs/docs/developers/aztecjs/main.md diff --git a/docs/docs/dev_docs/cli/blank_box.md b/docs/docs/developers/cli/blank_box.md similarity index 100% rename from docs/docs/dev_docs/cli/blank_box.md rename to docs/docs/developers/cli/blank_box.md diff --git a/docs/docs/dev_docs/cli/cli-commands.md b/docs/docs/developers/cli/cli-commands.md similarity index 97% rename from docs/docs/dev_docs/cli/cli-commands.md rename to docs/docs/developers/cli/cli-commands.md index 080dd071ab5..83c107365d8 100644 --- a/docs/docs/dev_docs/cli/cli-commands.md +++ b/docs/docs/developers/cli/cli-commands.md @@ -41,11 +41,11 @@ You can find more information about compiling contracts [on this page](../contra ## Creating Accounts -The first thing we want to do is create a couple of accounts. We will use the `create-account` command which will generate a new private key for us, register the account on the sandbox, and deploy a simple account contract which [uses a single key for privacy and authentication](../../concepts/foundation/accounts/keys.md): +The first thing we want to do is create a couple of accounts. We will use the `create-account` command which will generate a new private key for us, register the account on the sandbox, and deploy a simple account contract which [uses a single key for privacy and authentication](../../learn/concepts/accounts/keys.md): #include_code create-account yarn-project/end-to-end/src/cli_docs_sandbox.test.ts bash -Once the account is set up, the CLI returns the resulting address, its privacy key, and partial address. You can read more about these [here](../../concepts/foundation/accounts/keys.md#addresses-partial-addresses-and-public-keys). +Once the account is set up, the CLI returns the resulting address, its privacy key, and partial address. You can read more about these [here](../../learn/concepts/accounts/keys.md#addresses-partial-addresses-and-public-keys). Save the Address and Private key as environment variables. We will be using them later. diff --git a/docs/docs/dev_docs/cli/main.md b/docs/docs/developers/cli/main.md similarity index 100% rename from docs/docs/dev_docs/cli/main.md rename to docs/docs/developers/cli/main.md diff --git a/docs/docs/developers/cli/run_more_than_one_pxe_sandbox.md b/docs/docs/developers/cli/run_more_than_one_pxe_sandbox.md new file mode 100644 index 00000000000..c53a082b3e1 --- /dev/null +++ b/docs/docs/developers/cli/run_more_than_one_pxe_sandbox.md @@ -0,0 +1,35 @@ +--- +title: How to run more than one PXE in the sandbox +--- + +When you run the sandbox, the Aztec node and PXE have their own http server. This makes it possible to run two PXEs on your local machine, which can be useful for testing that notes are accurately stored and remaining private in their respective PXEs. + +We are working on a better solution for this so expect an update soon, but currently you can follow this guide. + +## Run the sandbox in one terminal + +Rather than use the usual command, run: +```bash +cd ~/.aztec && docker-compose up +``` +This removes any other arguments, allowing you to ensure an isolated environment for the sandbox so it doesn't interfere with another PXE. + +## Run PXE mode in another terminal + +In another terminal, run: + +```bash +docker-compose run -e MODE=pxe -e PXE_PORT=8085 -e AZTEC_NODE_URL='http://aztec-aztec-1:8079' -e TEST_ACCOUNTS='false' -p 8085:8085 aztec +``` +This does a few things: +* Starts in PXE mode +* Passes the current Aztec node URL +* Does not load new test accounts +* Sets a port to listen on +* Only runs Aztec PXE, not Ethereum + +This command uses the default ports, so they might need to be changed depending on yuor configuration. + +You can learn more about custom commands in the [sandbox reference](./sandbox-reference.md). + + diff --git a/docs/docs/dev_docs/cli/sandbox-reference.md b/docs/docs/developers/cli/sandbox-reference.md similarity index 93% rename from docs/docs/dev_docs/cli/sandbox-reference.md rename to docs/docs/developers/cli/sandbox-reference.md index 97dfc98998a..8fb0866d331 100644 --- a/docs/docs/dev_docs/cli/sandbox-reference.md +++ b/docs/docs/developers/cli/sandbox-reference.md @@ -56,13 +56,24 @@ cd ~/.aztec && docker-compose up ## Running Aztec PXE / Node / P2P-Bootstrap node -If you wish to run components of the Aztec network stack separately, you can still use the Sandbox by including a `MODE` variable. -The values for `MODE` can be: +If you wish to run components of the Aztec network stack separately, you can use the `aztec start` command with various options for enabling components. -- sandbox (default) -- node -- pxe -- p2p-bootstrap +```bash +aztec start --node [nodeOptions] --pxe [pxeOptions] --archiver [archiverOptions] --sequencer [sequencerOptions] ----p2p-bootstrap [p2pOptions] +``` + +Starting the aztec node alongside a PXE, sequencer or archiver, will attach the components to the node. If you want to e.g. run a PXE separately to a node, you can: +Start a node: + +```bash +aztec start --node [node] --archiver [archiverOptions] +``` + +Then start a PXE on a separate terminal that connects to that node: + +```bash +aztec start --pxe nodeUrl=http://localhost:8080 +``` ## Environment Variables diff --git a/docs/docs/dev_docs/contracts/abi.md b/docs/docs/developers/contracts/abi.md similarity index 100% rename from docs/docs/dev_docs/contracts/abi.md rename to docs/docs/developers/contracts/abi.md diff --git a/docs/docs/dev_docs/contracts/artifacts.md b/docs/docs/developers/contracts/artifacts.md similarity index 100% rename from docs/docs/dev_docs/contracts/artifacts.md rename to docs/docs/developers/contracts/artifacts.md diff --git a/docs/docs/dev_docs/contracts/compiling.md b/docs/docs/developers/contracts/compiling.md similarity index 94% rename from docs/docs/dev_docs/contracts/compiling.md rename to docs/docs/developers/contracts/compiling.md index 8d19f7811a8..58c805006ec 100644 --- a/docs/docs/dev_docs/contracts/compiling.md +++ b/docs/docs/developers/contracts/compiling.md @@ -115,7 +115,7 @@ Read more about interacting with contracts using `aztec.js` [here](../getting_st ### Aztec.nr interfaces -An Aztec.nr contract can [call a function](./syntax/functions.md) in another contract via `context.call_private_function` or `context.call_public_function`. However, this requires manually assembling the function selector and manually serializing the arguments, which is not type-safe. +An Aztec.nr contract can [call a function](./syntax/functions/main.md) in another contract via `context.call_private_function` or `context.call_public_function`. However, this requires manually assembling the function selector and manually serializing the arguments, which is not type-safe. To make this easier, the compiler can generate contract interface structs that expose a convenience method for each function listed in a given contract artifact. These structs are intended to be used from another contract project that calls into the current one. For each contract, two interface structs are generated: one to be used from private functions with a `PrivateContext`, and one to be used from open functions with a `PublicContext`. @@ -209,7 +209,7 @@ impl TokenPublicContextInterface { } ``` -Read more about how to use the Aztec.nr interfaces [here](./syntax/functions.md#contract-interface). +Read more about how to use the Aztec.nr interfaces [here](./syntax/functions/main.md). :::info At the moment, the compiler generates these interfaces from already compiled ABIs, and not from source code. This means that you should not import a generated interface from within the same project as its source contract, or you risk circular references. @@ -217,7 +217,7 @@ At the moment, the compiler generates these interfaces from already compiled ABI ## Next steps -Once you have compiled your contracts, you can use the generated artifacts via the `Contract` class in the `aztec.js` package to deploy and interact with them, or rely on the type-safe typescript classes directly. Alternatively, use the CLI [to deploy](../../dev_docs/cli/main.md#deploying-a-token-contract) and [interact](../../dev_docs/cli/main.md#sending-a-transaction) with them. +Once you have compiled your contracts, you can use the generated artifacts via the `Contract` class in the `aztec.js` package to deploy and interact with them, or rely on the type-safe typescript classes directly. Alternatively, use the CLI [to deploy](../../developers/cli/main.md#deploying-a-token-contract) and [interact](../../developers/cli/main.md#sending-a-transaction) with them. import Disclaimer from "../../misc/common/\_disclaimer.mdx"; diff --git a/docs/docs/dev_docs/contracts/deploying.md b/docs/docs/developers/contracts/deploying.md similarity index 98% rename from docs/docs/dev_docs/contracts/deploying.md rename to docs/docs/developers/contracts/deploying.md index c2db9fc764a..ec9d88ef1f4 100644 --- a/docs/docs/dev_docs/contracts/deploying.md +++ b/docs/docs/developers/contracts/deploying.md @@ -6,7 +6,7 @@ Once you have [compiled](./compiling.md) your contracts you can proceed to deplo - `aztec-cli` and `aztec-nargo` installed (go to [CLI main section](../cli/main.md) for installation instructions) - contract artifacts ready (go to [Compiling contracts section](./compiling.md) for instructions on how to compile contracts) -- aztec-sandbox running (go to [Sandbox section](../getting_started/quickstart.md) for instructions on how to install and run the sandbox) +- Aztec Sandbox running (go to [Sandbox section](../getting_started/quickstart.md) for instructions on how to install and run the sandbox) ## Deploy diff --git a/docs/docs/dev_docs/contracts/example-contract.md b/docs/docs/developers/contracts/example-contract.md similarity index 100% rename from docs/docs/dev_docs/contracts/example-contract.md rename to docs/docs/developers/contracts/example-contract.md diff --git a/docs/docs/dev_docs/contracts/layout.md b/docs/docs/developers/contracts/layout.md similarity index 71% rename from docs/docs/dev_docs/contracts/layout.md rename to docs/docs/developers/contracts/layout.md index 084a3a9c1fc..48fbc9a7030 100644 --- a/docs/docs/dev_docs/contracts/layout.md +++ b/docs/docs/developers/contracts/layout.md @@ -2,7 +2,7 @@ title: Structure --- -A contract is a collection of persistent [state variables](./syntax/storage/main.md), and [functions](./syntax/functions) which may manipulate these variables. Functions and state variables within a contract's scope are said to belong to that contract. A contract can only access and modify its own state. If a contract wishes to access or modify another contract's state, it must make a call to an external function of the other contract. For anything to happen on the Aztec network, an external function of a contract needs to be called. +A contract is a collection of persistent [state variables](./syntax/storage/main.md), and [functions](./syntax/functions/main.md) which may manipulate these variables. Functions and state variables within a contract's scope are said to belong to that contract. A contract can only access and modify its own state. If a contract wishes to access or modify another contract's state, it must make a call to an external function of the other contract. For anything to happen on the Aztec network, an external function of a contract needs to be called. # Contract diff --git a/docs/docs/dev_docs/contracts/main.md b/docs/docs/developers/contracts/main.md similarity index 100% rename from docs/docs/dev_docs/contracts/main.md rename to docs/docs/developers/contracts/main.md diff --git a/docs/docs/dev_docs/contracts/portals/data_structures.md b/docs/docs/developers/contracts/portals/data_structures.md similarity index 100% rename from docs/docs/dev_docs/contracts/portals/data_structures.md rename to docs/docs/developers/contracts/portals/data_structures.md diff --git a/docs/docs/dev_docs/contracts/portals/inbox.md b/docs/docs/developers/contracts/portals/inbox.md similarity index 100% rename from docs/docs/dev_docs/contracts/portals/inbox.md rename to docs/docs/developers/contracts/portals/inbox.md diff --git a/docs/docs/dev_docs/contracts/portals/main.md b/docs/docs/developers/contracts/portals/main.md similarity index 97% rename from docs/docs/dev_docs/contracts/portals/main.md rename to docs/docs/developers/contracts/portals/main.md index ab0654dba98..11fc1c0b9de 100644 --- a/docs/docs/dev_docs/contracts/portals/main.md +++ b/docs/docs/developers/contracts/portals/main.md @@ -7,7 +7,7 @@ description: Documentation of Aztec's Portals and Cross-chain communication. A portal is the point of contact between L1 and a specific contract on Aztec. For applications such as token bridges, this is the point where the tokens are held on L1 while used in L2. -As outlined in the [foundational concepts](../../../concepts/foundation/communication/cross_chain_calls.md), an Aztec L2 contract is linked to _ONE_ L1 address at time of deployment (specified by the developer). This L1 address is the only address that can send messages to that specific L2 contract, and the only address that can receive messages sent from the L2 contract to L1. Note, that a portal don't actually need to be a contract, it could be any address on L1. We say that an Aztec contract is attached to a portal. +As outlined in [Communication](../../../learn/concepts/communication/cross_chain_calls.md), an Aztec L2 contract is linked to _ONE_ L1 address at time of deployment (specified by the developer). This L1 address is the only address that can send messages to that specific L2 contract, and the only address that can receive messages sent from the L2 contract to L1. Note, that a portal don't actually need to be a contract, it could be any address on L1. We say that an Aztec contract is attached to a portal. ## Passing data to the rollup diff --git a/docs/docs/dev_docs/contracts/portals/outbox.md b/docs/docs/developers/contracts/portals/outbox.md similarity index 100% rename from docs/docs/dev_docs/contracts/portals/outbox.md rename to docs/docs/developers/contracts/portals/outbox.md diff --git a/docs/docs/dev_docs/contracts/portals/registry.md b/docs/docs/developers/contracts/portals/registry.md similarity index 100% rename from docs/docs/dev_docs/contracts/portals/registry.md rename to docs/docs/developers/contracts/portals/registry.md diff --git a/docs/docs/dev_docs/contracts/resources/common_patterns/authwit.md b/docs/docs/developers/contracts/resources/common_patterns/authwit.md similarity index 96% rename from docs/docs/dev_docs/contracts/resources/common_patterns/authwit.md rename to docs/docs/developers/contracts/resources/common_patterns/authwit.md index 54e5c3c8dd3..0858b4cbebb 100644 --- a/docs/docs/dev_docs/contracts/resources/common_patterns/authwit.md +++ b/docs/docs/developers/contracts/resources/common_patterns/authwit.md @@ -5,13 +5,13 @@ description: Developer Documentation to use Authentication Witness for authentic ## Prerequisite reading -- [Authwit from Foundational Concepts](./../../../../concepts/foundation/accounts/authwit.md) +- [Authwit](./../../../../learn/concepts/accounts/authwit.md) ## Introduction Authentication Witness is a scheme for authentication actions on Aztec, so users can allow third-parties (eg protocols or other users) to execute an action on their behalf. -How it works logically is explained in the [foundational concepts](./../../../../concepts/foundation/accounts/authwit.md) but we will do a short recap here. +How it works logically is explained in the [concepts](./../../../../learn/concepts/accounts/authwit.md) but we will do a short recap here. An authentication witness is defined for a specific action, such as allowing a Defi protocol to transfer funds on behalf of the user. An action is here something that could be explained as `A is allowed to perform X operation on behalf of B` and we define it as a hash computed as such: @@ -168,7 +168,7 @@ With private functions covered, how can we use this in a public function? Well, Authenticating an action in the public domain is quite similar to the private domain, with the difference that we are executing a function on the account contract to add the witness, if you recall, this is because we don't have access to the oracle in the public domain. -In the snippet below, this is done as a separate contract call, but can also be done as part of a batch as mentioned in the [foundational concepts](./../../../../concepts/foundation/accounts/authwit.md#what-about-public). +In the snippet below, this is done as a separate contract call, but can also be done as part of a batch as mentioned in the [Accounts concepts](./../../../../learn/concepts/accounts/authwit.md#what-about-public). #include_code authwit_public_transfer_example /yarn-project/end-to-end/src/e2e_token_contract.test.ts typescript diff --git a/docs/docs/dev_docs/contracts/resources/common_patterns/main.md b/docs/docs/developers/contracts/resources/common_patterns/main.md similarity index 96% rename from docs/docs/dev_docs/contracts/resources/common_patterns/main.md rename to docs/docs/developers/contracts/resources/common_patterns/main.md index 18592c18902..5d54f7b6e35 100644 --- a/docs/docs/dev_docs/contracts/resources/common_patterns/main.md +++ b/docs/docs/developers/contracts/resources/common_patterns/main.md @@ -48,7 +48,7 @@ Note - you could also create a note and send it to the user. The problem is ther You can't read public storage in private domain. But nevertheless reading public storage is desirable. There are two ways: -1. For public storage that changes infrequently, use the slow updates tree! Learn more about it [here](../../../../concepts/foundation/communication/public_private_calls/slow_updates_tree.md). +1. For public storage that changes infrequently, use the slow updates tree! Learn more about it [here](../../../../learn/concepts/communication/public_private_calls/slow_updates_tree.md). 2. You pass the data as a parameter to your private method and later assert in public that the data is correct. E.g.: @@ -93,7 +93,7 @@ This pattern is discussed in detail in [writing a token contract section in the ### Discovering my notes -When you send someone a note, the note hash gets added to the [note hash tree](../../../../concepts/advanced/data_structures/trees#note-hash-tree). To spend the note, the receiver needs to get the note itself (the note hash preimage). There are two ways you can get a hold of your notes: +When you send someone a note, the note hash gets added to the [note hash tree](../../../../learn/concepts/storage/trees/main.md#note-hash-tree). To spend the note, the receiver needs to get the note itself (the note hash preimage). There are two ways you can get a hold of your notes: 1. When sending someone a note, use `emit_encrypted_log` (the function encrypts the log in such a way that only a recipient can decrypt it). PXE then tries to decrypt all the encrypted logs, and stores the successfully decrypted one. [More info here](../../syntax/events.md) 2. Manually using `pxe.addNote()` - If you choose to not emit logs to save gas or when creating a note in the public domain and want to consume it in private domain (`emit_encrypted_log` shouldn't be called in the public domain because everything is public), like in the previous section where we created a TransparentNote in public. @@ -169,6 +169,6 @@ PS: when calling from private to public, `msg_sender` is the contract address wh In the [Prevent the same user flow from happening twice using nullifier](#prevent-the-same-user-flow-from-happening-twice-using-nullifiers), we recommended using nullifiers. But what you put in the nullifier is also as important. -E.g. for a voting contract, if your nullifier simply emits just the `user_address`, then privacy can easily be leaked as nullifiers are deterministic (have no randomness), especially if there are few users of the contract. So you need some kind of randomness. You can add the user's secret key into the nullifier to add randomness. We call this "nullifier secrets" as explained [here](../../../../concepts/foundation/accounts/keys.md#nullifier-secrets). E.g.: +E.g. for a voting contract, if your nullifier simply emits just the `user_address`, then privacy can easily be leaked as nullifiers are deterministic (have no randomness), especially if there are few users of the contract. So you need some kind of randomness. You can add the user's secret key into the nullifier to add randomness. We call this "nullifier secrets" as explained [here](../../../../learn/concepts/accounts/keys.md#nullifier-secrets). E.g.: #include_code nullifier /yarn-project/aztec-nr/value-note/src/value_note.nr rust diff --git a/docs/docs/dev_docs/contracts/resources/dependencies.md b/docs/docs/developers/contracts/resources/dependencies.md similarity index 100% rename from docs/docs/dev_docs/contracts/resources/dependencies.md rename to docs/docs/developers/contracts/resources/dependencies.md diff --git a/docs/docs/dev_docs/contracts/resources/main.md b/docs/docs/developers/contracts/resources/main.md similarity index 100% rename from docs/docs/dev_docs/contracts/resources/main.md rename to docs/docs/developers/contracts/resources/main.md diff --git a/docs/docs/dev_docs/contracts/resources/style_guide.md b/docs/docs/developers/contracts/resources/style_guide.md similarity index 100% rename from docs/docs/dev_docs/contracts/resources/style_guide.md rename to docs/docs/developers/contracts/resources/style_guide.md diff --git a/docs/docs/dev_docs/contracts/security/breaking_changes/main.md b/docs/docs/developers/contracts/security/breaking_changes/main.md similarity index 100% rename from docs/docs/dev_docs/contracts/security/breaking_changes/main.md rename to docs/docs/developers/contracts/security/breaking_changes/main.md diff --git a/docs/docs/dev_docs/contracts/security/breaking_changes/v0.md b/docs/docs/developers/contracts/security/breaking_changes/v0.md similarity index 100% rename from docs/docs/dev_docs/contracts/security/breaking_changes/v0.md rename to docs/docs/developers/contracts/security/breaking_changes/v0.md diff --git a/docs/docs/dev_docs/contracts/security/main.md b/docs/docs/developers/contracts/security/main.md similarity index 100% rename from docs/docs/dev_docs/contracts/security/main.md rename to docs/docs/developers/contracts/security/main.md diff --git a/docs/docs/dev_docs/contracts/setup.md b/docs/docs/developers/contracts/setup.md similarity index 100% rename from docs/docs/dev_docs/contracts/setup.md rename to docs/docs/developers/contracts/setup.md diff --git a/docs/docs/dev_docs/contracts/syntax/constrain.md b/docs/docs/developers/contracts/syntax/constrain.md similarity index 100% rename from docs/docs/dev_docs/contracts/syntax/constrain.md rename to docs/docs/developers/contracts/syntax/constrain.md diff --git a/docs/docs/dev_docs/contracts/syntax/context.mdx b/docs/docs/developers/contracts/syntax/context.mdx similarity index 87% rename from docs/docs/dev_docs/contracts/syntax/context.mdx rename to docs/docs/developers/contracts/syntax/context.mdx index 3321cd91c4a..5f6a028dfed 100644 --- a/docs/docs/dev_docs/contracts/syntax/context.mdx +++ b/docs/docs/developers/contracts/syntax/context.mdx @@ -10,7 +10,7 @@ import Image from "@theme/IdealImage"; ## What is the context -The context is an object that is made available within every function in `Aztec.nr`. As mentioned in the [kernel circuit documentation](../../../concepts/advanced/circuits/kernels/private_kernel.md). At the beginning of a function's execution, the context contains all of the kernel information that application needs to execute. During the lifecycle of a transaction, the function will update the context with each of it's side effects (created notes, nullifiers etc.). At the end of a function's execution the mutated context is returned to the kernel to be checked for validity. +The context is an object that is made available within every function in `Aztec.nr`. As mentioned in the [kernel circuit documentation](../../../learn/concepts/circuits/kernels/private_kernel.md). At the beginning of a function's execution, the context contains all of the kernel information that application needs to execute. During the lifecycle of a transaction, the function will update the context with each of it's side effects (created notes, nullifiers etc.). At the end of a function's execution the mutated context is returned to the kernel to be checked for validity. Behind the scenes, Aztec.nr will pass data the kernel needs to and from a circuit, this is abstracted away from the developer. In an developer's eyes; the context is a useful structure that allows access and mutate the state of the `Aztec` blockchain. @@ -23,7 +23,7 @@ On this page, you'll learn - Differences between the private and public contexts, especially the unique features and variables in the public context ## Two context's one API -The `Aztec` blockchain contains two environments [public and private](../../../concepts/foundation/state_model/main.md). +The `Aztec` blockchain contains two environments [public and private](../../../learn/concepts/hybrid_state/main.md). - Private, for private transactions taking place on user's devices. - Public, for public transactions taking place on the network's sequencers. @@ -71,11 +71,13 @@ The call context contains information about the current call being made: - is_static_call: This will be set if and only if the current call is a static call. In a static call, state changing altering operations are not allowed. - is_contract_deployment: This will be set if and only if the current call is the contract's constructor. -### Block Header +### Header -Another structure that is contained within the context is the Block Header object. This object is a special one as it contains all of the roots of Aztec's data trees. +Another structure that is contained within the context is the Header object. +In the private context this is a header of a block which used to generate proofs against. +In the public context this header is set by sequencer (sequencer executes public calls) and it is set to 1 block before the block in which the transaction is included. -#include_code block-header /yarn-project/noir-protocol-circuits/src/crates/types/src/abis/block_header.nr rust +#include_code header /yarn-project/noir-protocol-circuits/src/crates/types/src/header.nr rust ### Contract Deployment Data @@ -97,7 +99,7 @@ The `args_hash` is the result of pedersen hashing all of a function's inputs. ### Return Values -The return values are a set of values that are returned from an applications execution to be passed to other functions through the kernel. Developers do not need to worry about passing their function return values to the `context` directly as `Aztec.nr` takes care of it for you. See the documentation surrounding `Aztec.nr` [macro expansion](./functions.md#after-expansion) for more details. +The return values are a set of values that are returned from an applications execution to be passed to other functions through the kernel. Developers do not need to worry about passing their function return values to the `context` directly as `Aztec.nr` takes care of it for you. See the documentation surrounding `Aztec.nr` [macro expansion](./functions/inner_workings.md#after-expansion) for more details. return_values : BoundedVec, @@ -129,7 +131,7 @@ The public call stack contains all of the external function calls that are creat ### New L2 to L1 msgs -New L2 to L1 messages contains messages that are delivered to the [l1 outbox](../../../concepts/foundation/communication/cross_chain_calls.md) on the execution of each rollup. +New L2 to L1 messages contains messages that are delivered to the [l1 outbox](../../../learn/concepts/communication/cross_chain_calls.md) on the execution of each rollup. ## Public Context diff --git a/docs/docs/dev_docs/contracts/syntax/control_structure.md b/docs/docs/developers/contracts/syntax/control_structure.md similarity index 100% rename from docs/docs/dev_docs/contracts/syntax/control_structure.md rename to docs/docs/developers/contracts/syntax/control_structure.md diff --git a/docs/docs/dev_docs/contracts/syntax/events.md b/docs/docs/developers/contracts/syntax/events.md similarity index 98% rename from docs/docs/dev_docs/contracts/syntax/events.md rename to docs/docs/developers/contracts/syntax/events.md index 2ccc3c0be38..234e978544f 100644 --- a/docs/docs/dev_docs/contracts/syntax/events.md +++ b/docs/docs/developers/contracts/syntax/events.md @@ -61,7 +61,7 @@ In the future we will allow emitting arbitrary information. (If you currently emit arbitrary information, PXE will fail to decrypt, process and store this data, so it will not be queryable). ::: -To emit encrypted logs first import the `emit_encrypted_log` utility function which wraps an [oracle](./functions.md#oracle-functions): +To emit encrypted logs first import the `emit_encrypted_log` utility function which wraps an [oracle](./functions/oracles.md): #include_code encrypted_import /yarn-project/aztec-nr/address-note/src/address_note.nr rust @@ -95,7 +95,7 @@ They can be emitted by both public and private functions. :::danger - Emitting unencrypted events from private function is a significant privacy leak and it should be considered by the developer whether it is acceptable. -- Unencrypted events are currently **NOT** linked to the contract emitting them, so it is practically a [`debug_log`](./functions.md#a-few-useful-inbuilt-oracles). +- Unencrypted events are currently **NOT** linked to the contract emitting them, so it is practically a [`debug_log`](./functions/oracles.md#a-few-useful-inbuilt-oracles). ::: To emit unencrypted logs first import the `emit_unencrypted_log` utility function inside your contract: diff --git a/docs/docs/developers/contracts/syntax/functions/calling_functions.md b/docs/docs/developers/contracts/syntax/functions/calling_functions.md new file mode 100644 index 00000000000..4b24e32a9c2 --- /dev/null +++ b/docs/docs/developers/contracts/syntax/functions/calling_functions.md @@ -0,0 +1,132 @@ +--- +title: Calling Functions from Other Functions +--- + +This page talks about how functions call other functions. For a more hands-on guide into calling functions from other functions, follow the [token tutorial](../../../tutorials/writing_token_contract.md). + +### Private -> Private + +In Aztec Private to Private function calls are handled by the [private kernel circuit](../../../../learn/concepts/circuits/kernels/private_kernel.md), and take place on the user's device. +Behind the scenes, the `Private Execution Environment (PXE)` (the beating heart of Aztec that runs in your wallet) will execute all of the functions in the desired order "simulating" them in sequence. For example, a very common use-case of private-to-private interaction is calling a private function on another contract from an `account contract` (Account contracts are a general concept, more information about them can be found [here](../../../wallets/writing_an_account_contract.md)). + +Take, for example, the following call stack: + +``` +AccountContract::entrypoint + |-> Foo::example_call + | -> Bar::nested_call + |-> Baz::example_call +``` + +In the example above the Account Contract has been instructed to call two external functions. In the first function all, to `Foo::example_call` a further nested call is performed to `Bar::nested_call`. Finally the Account Contract makes one last call to `Baz::example_call`. + +Lets further illustrate what these examples could look like + + + +```rust +// Foo contains a singular function that returns the result of Bar::nested_call +contract Foo { + #[aztec(private)] + fn example_call(input: Field) -> pub Field { + Bar::at().nested_call(input) + } +} + +// Bar contains a singular function that returns a `input + 1` +contract Bar { + #[aztec(private)] + fn nested_call(input: Field) -> pub Field { + input + 1 + } +} + +// Baz contains a singular function that simply returns `10` +contract Baz { + #[aztec(private)] + fn example_call() -> pub Field { + 10 + } +} +``` + +When simulating the following call stack, we can expect execution flow to continue procedurally. The simulator will begin at the account contract's entry point, find a call to `Foo::example_call`, then begin to execute the code there. When the simulator executes the code in contract `Foo`, it will find the further nested call to contract `Bar::nested_call`. It will execute the code in `Bar`, bringing the return value back to contract `Foo`. +The same process will be followed for contract `Baz`. + +So far the provided example is identical to other executions. Ethereum execution occurs in a similar way, during execution the EVM will execute instructions until it reaches an external call, where it will hop into a new context and execute code there, returning back when it is complete, bringing with it return values from the foreign execution. + +Aztec differs from Ethereum in that these function calls are really executing zk programs (or circuits). The account contract, contract `Foo`, `Bar` and `Baz` are all distinct circuits, which are not directly aware of one another in the way that Ethereum contracts are. How is it possible to use a value from contract `Bar` in contract `Foo`? `Foo` cannot guarantee claims about the execution of `Bar`. + +This is where the `kernel` circuit comes in. Once the execution of all of the contract functions has completed, it can prove the execution of each of them independently. It is the job of the `kernel` circuit to constrain that the input parameters in a cross function call are correct, as well as the return values. The kernel will constrain that the value returned from `Foo::example_call` is the same value that is returned from `Bar::nested_call`, it will also be able to guarantee the value returned by `Bar::nested_call` is the inputs to `Foo::example_call` + 1. + +The orchestration of these calls has an added benefit. All of the nested calls are **recursively proven**. This means that the kernel circuit essentially aggregates each of our function's execution proofs, resulting in one proof that proves all function execution. + + + +With this intuition in place, lets see how we actually perform the call. To make things easier, we can make a small struct that wraps the calls to something as seen in the token interface `burn` function below. This struct is providing us a clean way to call function, but we could also just call the function directly as it is done in this function. + +:::info +Note that the function selector is computed using Oracles, and that the first `Field` is wrapped in parenthesis. Structs are outlined in tuple-form for selector computation, so they are wrapped in parenthesis--e.g. `AztecAddress` becomes `(Field)`. +::: + +#include_code private_burn_interface /yarn-project/noir-contracts/contracts/token_bridge_contract/src/token_interface.nr rust + +Using this interface, we can then call it as seen below. All the way down at the bottom we can see that we are calling the `burn` function from the `token_interface` struct. + +The following snippet is from a token bridge that is burning the underlying token and creating a message for L1 to mint some assets to the `recipient` on Ethereum. + +#include_code exit_to_l1_private /yarn-project/noir-contracts/contracts/token_bridge_contract/src/main.nr rust + +### Public -> Public + +The public execution environment in Aztec takes place on the sequencer through a [Public VM](../../../../learn/concepts/hybrid_state/public_vm.md). This execution model is conceptually much simpler than the private transaction model as code is executed and proven on the sequencer. + +Using the same example code and call stack from the section [above](#private---private-function-calls), we will walk through how it gets executed in public. + +The first key difference is that public functions are not compiled to circuits, rather they are compiled to `Aztec Bytecode`. + +This bytecode is run by the sequencer in the `Aztec VM`, which is in turn proven by the [`Aztec VM circuit`](../../../../learn/concepts/hybrid_state/public_vm.md). +The mental model for public execution carries many of the same idea as are carried by Ethereum. Programs are compiled into a series of opcodes (known as bytecode). This bytecode is then executed. The extra step for the Aztec VM is that each opcode is then proven for correctness. + +Calling a public function from another public function is quite similar to what we saw for private to private, with the keyword private swapped for public. + +#include_code public_burn_interface /yarn-project/noir-contracts/contracts/token_bridge_contract/src/token_interface.nr rust +#include_code exit_to_l1_public /yarn-project/noir-contracts/contracts/token_bridge_contract/src/main.nr rust + +### Private -> Public + +As discussed above, private function execution and calls take place on the user's device, while public function execution and calls take place on a sequencer, in two different places at two different times. We can achieve composability between the two contexts via asynchronicity. Further reading can be found in the concepts [here](../../../../learn/concepts/communication/public_private_calls/main.md). + +Private function execution takes place on the users device, where it keeps track of any public function calls that have been made. Whenever private execution completes, and a kernel proof is produced, the transaction sent to the network will include all of the public calls that were dispatched. +When the sequencer receives the messages, it will execute the public parts of the transaction. + +As a consequence a private function _CANNOT_ accept a return value from a public function. It can only dispatch it. + +The code required to dispatch a public function call from a private function is similar to private-to-private calls. As an example, we will look at the token contract, where users can unshield assets from private to public domain. This is essentially a transfer from a private account to a public one (often used for depositing privately into DeFi etc). + +#include_code unshield /yarn-project/noir-contracts/contracts/token_contract/src/main.nr rust +#include_code increase_public_balance /yarn-project/noir-contracts/contracts/token_contract/src/main.nr rust + +As we can see above, in the code the private to public transaction flow looks very similar to the others in snippets, with the transaction data flow being a bit different behind the scenes. + + + +### Public -> Private + + +While we cannot directly call a private function from a public function, we can indirectly call it by adding a commitment to the note hash tree. This commitment can then be consumed by a private function later, to "finish" the execution. So while it is not practically a call, we can ensure that it could only happen as an effect of a public function call, which is still useful. + +In the snippet below, we insert a custom note, the transparent note, into the commitments tree from public such that it can later be consumed in private. + +#include_code shield /yarn-project/noir-contracts/contracts/token_contract/src/main.nr rust + +If you recall the `redeem_shield` from back in the [private function section](./public_private_unconstrained.md#private-functions), you might remember it removing a `TransparentNote` from `pending_shields`. This is the note that we just inserted from public! + +#include_code redeem_shield /yarn-project/noir-contracts/contracts/token_contract/src/main.nr rust + +When the note is removed, it emits a nullifier so that it cannot be used again. This nullifier is then added to the nullifier tree, and can be used to prove that the note was removed from the pending shields. Interestingly, we can generate the nullifier such that no-one who saw the public execution will know that it have been consumed. When sending messages between L1 and L2 in [portals](./../../../../learn/concepts/communication/cross_chain_calls.md) we are going to see this pattern again. + +:::danger +Something to be mindful of when inserting from public. Everyone can see the insertion and what happens in public, so if you are including a secret directly anyone would be able to see it. This is why the hash of the secret is used in the snippet above (`secret_hash`). +::: + diff --git a/docs/docs/developers/contracts/syntax/functions/constructor.md b/docs/docs/developers/contracts/syntax/functions/constructor.md new file mode 100644 index 00000000000..d0a9c7d0c3b --- /dev/null +++ b/docs/docs/developers/contracts/syntax/functions/constructor.md @@ -0,0 +1,15 @@ +--- +title: Constructor +--- +This page talks about declaring constructors in functions. + +For a more hands-on guide into declaring a constructor, follow the [private voting tutorial](../../../tutorials/writing_private_voting_contract.md). + +A special `constructor` function **must** be declared within a contract's scope. +- A constructor doesn't have a name, because its purpose is clear: to initialize contract state. +- In Aztec terminology, a constructor is always a '`private` function' (i.e. it cannot be a `public` function). +- A constructor behaves almost identically to any other function. It is just important for Aztec to be able to identify this function as special: it may only be called once, and will not be deployed as part of the contract. + +Although you can have a constructor that does nothing, you might want to do something with it, such as setting the deployer as an owner. + +#include_code constructor /yarn-project/noir-contracts/contracts/escrow_contract/src/main.nr rust diff --git a/docs/docs/developers/contracts/syntax/functions/inner_workings.md b/docs/docs/developers/contracts/syntax/functions/inner_workings.md new file mode 100644 index 00000000000..3b1a093e00a --- /dev/null +++ b/docs/docs/developers/contracts/syntax/functions/inner_workings.md @@ -0,0 +1,100 @@ +--- +title: Inner Workings of Functions +--- + +Below, we go more into depth of what is happening under the hood when you create a function in an Aztec contract and what the attributes are really doing. + +To get a more practical understanding about functions, read [the rest of this section](./main.md). + +## Private functions + +Aztec.nr uses an attribute system to annotate a function's type. Annotating a function with the `#[aztec(private)]` attribute tells the framework that this is a private function that will be executed on a users device. The compiler will create a circuit to define this function. + +`#aztec(private)` is just syntactic sugar. At compile time, the Aztec.nr framework inserts code that allows the function to interact with the [kernel](../../../../learn/concepts/circuits/kernels/private_kernel.md). + +To help illustrate how this interacts with the internals of Aztec and its kernel circuits, we can take an example private function, and explore what it looks like after Aztec.nr's macro expansion. + +#### Before expansion + +#include_code simple_macro_example /yarn-project/noir-contracts/contracts/docs_example_contract/src/main.nr rust + +#### After expansion + +#include_code simple_macro_example_expanded /yarn-project/noir-contracts/contracts/docs_example_contract/src/main.nr rust + +#### The expansion broken down? + +Viewing the expanded Aztec contract uncovers a lot about how Aztec contracts interact with the [kernel](../../../../learn/concepts/circuits/kernels/private_kernel.md). To aid with developing intuition, we will break down each inserted line. + +**Receiving context from the kernel.** +#include_code context-example-inputs /yarn-project/noir-contracts/contracts/docs_example_contract/src/main.nr rust + +Private function calls are able to interact with each other through orchestration from within the [kernel circuit](../../../../learn/concepts/circuits/kernels/private_kernel.md). The kernel circuit forwards information to each contract function (recall each contract function is a circuit). This information then becomes part of the private context. +For example, within each private function we can access some global variables. To access them we can call on the `context`, e.g. `context.chain_id()`. The value of the chain ID comes from the values passed into the circuit from the kernel. + +The kernel checks that all of the values passed to each circuit in a function call are the same. + +**Returning the context to the kernel.** +#include_code context-example-return /yarn-project/noir-contracts/contracts/docs_example_contract/src/main.nr rust + +The contract function must return information about the execution back to the kernel. This is done through a rigid structure we call the `PrivateCircuitPublicInputs`. + +> _Why is it called the `PrivateCircuitPublicInputs`?_ +> When verifying zk programs, return values are not computed at verification runtime, rather expected return values are provided as inputs and checked for correctness. Hence, the return values are considered public inputs. + +This structure contains a host of information about the executed program. It will contain any newly created nullifiers, any messages to be sent to l2 and most importantly it will contain the return values of the function. + +**Hashing the function inputs.** +#include_code context-example-hasher /yarn-project/noir-contracts/contracts/docs_example_contract/src/main.nr rust + +_What is the hasher and why is it needed?_ + +Inside the kernel circuits, the inputs to functions are reduced to a single value; the inputs hash. This prevents the need for multiple different kernel circuits; each supporting differing numbers of inputs. The hasher abstraction that allows us to create an array of all of the inputs that can be reduced to a single value. + +**Creating the function's context.** +#include_code context-example-context /yarn-project/noir-contracts/contracts/docs_example_contract/src/main.nr rust + +Each Aztec function has access to a [context](../context.mdx) object. This object, although labelled a global variable, is created locally on a users' device. It is initialized from the inputs provided by the kernel, and a hash of the function's inputs. + +#include_code context-example-context-return /yarn-project/noir-contracts/contracts/docs_example_contract/src/main.nr rust + +We use the kernel to pass information between circuits. This means that the return values of functions must also be passed to the kernel (where they can be later passed on to another function). +We achieve this by pushing return values to the execution context, which we then pass to the kernel. + +**Making the contract's storage available** +#include_code storage-example-context /yarn-project/noir-contracts/contracts/docs_example_contract/src/main.nr rust + +When a [`Storage` struct](../storage/main.md) is declared within a contract, the `storage` keyword is made available. As shown in the macro expansion above, this calls the init function on the storage struct with the current function's context. + +Any state variables declared in the `Storage` struct can now be accessed as normal struct members. + +**Returning the function context to the kernel.** +#include_code context-example-finish /yarn-project/noir-contracts/contracts/docs_example_contract/src/main.nr rust + +This function takes the application context, and converts it into the `PrivateCircuitPublicInputs` structure. This structure is then passed to the kernel circuit. + +## Unconstrained functions + +Defining a function as `unconstrained` tells Aztec to simulate it completely client-side in the [ACIR simulator](../../../../learn/concepts/pxe/acir_simulator.md) without generating proofs. They are useful for extracting information from a user through an [oracle](./oracles.md). + +When an unconstrained function is called, it prompts the ACIR simulator to + +1. generate the execution environment +2. execute the function within this environment + +To generate the environment, the simulator gets the blockheader from the [PXE database](../../../../learn/concepts/pxe/main.md#database) and passes it along with the contract address to `ViewDataOracle`. This creates a context that simulates the state of the blockchain at a specific block, allowing the unconstrained function to access and interact with blockchain data as it would appear in that block, but without affecting the actual blockchain state. + +Once the execution environment is created, `execute_unconstrained_function` is invoked: + +#include_code execute_unconstrained_function yarn-project/acir-simulator/src/client/unconstrained_execution.ts typescript + +This: + +1. Prepares the ACIR for execution +2. Converts `args` into a format suitable for the ACVM (Abstract Circuit Virtual Machine), creating an initial witness (witness = set of inputs required to compute the function). `args` might be an oracle to request a user's balance +3. Executes the function in the ACVM, which involves running the ACIR with the initial witness and the context. If requesting a user's balance, this would query the balance from the PXE database +4. Extracts the return values from the `partialWitness` and decodes them based on the artifact to get the final function output. The [artifact](../../artifacts.md) is the compiled output of the contract, and has information like the function signature, parameter types, and return types + + + + diff --git a/docs/docs/developers/contracts/syntax/functions/main.md b/docs/docs/developers/contracts/syntax/functions/main.md new file mode 100644 index 00000000000..17de123e35b --- /dev/null +++ b/docs/docs/developers/contracts/syntax/functions/main.md @@ -0,0 +1,20 @@ +--- +title: Functions +--- + +Functions serve as the building blocks of smart contracts. Functions can be either **public**, ie they are publicly available for anyone to see and can directly interact with public state, or **private**, meaning they are executed completely client-side in the [PXE](../../../../learn/concepts/pxe/main.md). Read more about how private functions work [here](./inner_workings.md#private-functions). + +For a more practical guide of using multiple types of functions, follow the [token tutorial](../../../tutorials/writing_token_contract.md). + +Every smart contract has a private `constructor` function which is called when the contract is deployed. There are also special oracle functions, which can get data from outside of the smart contract. In the context of Aztec, oracles are often used to get user-provided inputs. + +Currently, any function is "mutable" in the sense that it might alter state. In the future, we will support static calls, similarly to EVM. A static call is essentially a call that does not alter state (it keeps state static). + +Explore this section to learn: + +- [How function visibility works in Aztec](./visibility.md) +- [Public, private, and unconstrained functions](./public_private_unconstrained.md), and how to write them +- How [constructors](./constructor.md) work and remain private +- [Calling functions from within the same smart contract and from different contracts](./calling_functions.md), including calling private functions from private functions, public from public, and even private from public +- [Oracles](./oracles) and how Aztec smart contracts might use them +- [How functions work under the hood](./inner_workings.md) \ No newline at end of file diff --git a/docs/docs/developers/contracts/syntax/functions/oracles.md b/docs/docs/developers/contracts/syntax/functions/oracles.md new file mode 100644 index 00000000000..28a57bed1d6 --- /dev/null +++ b/docs/docs/developers/contracts/syntax/functions/oracles.md @@ -0,0 +1,23 @@ +--- +title: Oracle Functions +--- + +This page goes over what oracles are in Aztec and how they work. + +Looking for a hands-on guide? You can learn how to use oracles in a smart contract [here](../oracles.md). + +An oracle is something that allows us to get data from the outside world into our contracts. The most widely-known types of oracles in blockchain systems are probably Chainlink price feeds, which allow us to get the price of an asset in USD taking non-blockchain data into account. + +While this is one type of oracle, the more general oracle, allows us to get any data into the contract. In the context of oracle functions or oracle calls in Aztec, it can essentially be seen as user-provided arguments, that can be fetched at any point in the circuit, and don't need to be an input parameter. + +**Why is this useful? Why don't just pass them as input parameters?** +In the world of EVM, you would just read the values directly from storage and call it a day. However, when we are working with circuits for private execution, this becomes more tricky as you cannot just read the storage directly from your state tree, because there are only commitments (e.g. hashes) there. The pre-images (content) of your commitments need to be provided to the function to prove that you actually allowed to modify them. + + +If we fetch the notes using an oracle call, we can keep the function signature independent of the underlying data and make it easier to use. A similar idea, applied to the authentication mechanism is used for the Authentication Witnesses that allow us to have a single function signature for any wallet implementation, see [AuthWit](../../../wallets/main#authorizing-actions) for more information on this. + +Oracles introduce **non-determinism** into a circuit, and thus are `unconstrained`. It is important that any information that is injected into a circuit through an oracle is later constrained for correctness. Otherwise, the circuit will be **under-constrained** and potentially insecure! + +`Aztec.nr` has a module dedicated to its oracles. If you are interested, you can view them by following the link below: +#include_code oracles-module /yarn-project/aztec-nr/aztec/src/oracle.nr rust + diff --git a/docs/docs/developers/contracts/syntax/functions/public_private_unconstrained.md b/docs/docs/developers/contracts/syntax/functions/public_private_unconstrained.md new file mode 100644 index 00000000000..678fc7ad9d3 --- /dev/null +++ b/docs/docs/developers/contracts/syntax/functions/public_private_unconstrained.md @@ -0,0 +1,35 @@ +--- +title: Public, Private, and Unconstrained Functions +--- + +This page explains the three types of functions that exist on Aztec - public, private, and unconstrained. For a deeper dive into how these functions work under the hood, check out the [Inner Workings](./inner_workings.md) page. + +## `Public` Functions + +A public function is executed by the sequencer and has access to a state model that is very similar to that of the EVM and Ethereum. Even though they work in an EVM-like model for public transactions, they are able to write data into private storage that can be consumed later by a private function. + +:::note +All data inserted into private storage from a public function will be publicly viewable (not private). +::: + +To create a public function you can annotate it with the `#[aztec(public)]` attribute. This will make the [public context](../context.mdx) available within the function's execution scope. + +#include_code set_minter /yarn-project/noir-contracts/contracts/token_contract/src/main.nr rust + +## `Private` Functions + +A private function operates on private information, and is executed by the user. Annotate the function with the `#[aztec(private)]` attribute to tell the compiler it's a private function. This will make the [private context](../context.mdx#private-context-broken-down) available within the function's execution scope. + +#include_code redeem_shield /yarn-project/noir-contracts/contracts/token_contract/src/main.nr rust + +## `unconstrained` functions + +Unconstrained functions are an underlying part of Noir - a deeper explanation can be found [here](https://noir-lang.org/docs/noir/syntax/unconstrained). In short, they are functions which are not directly constrained and therefore should be seen as un-trusted. That they are un-trusted means that the developer must make sure to constrain their return values when used. Note: Calling an unconstrained function from a private function means that you are injecting unconstrained values. + +Beyond using them inside your other functions, they are convenient for providing an interface that reads storage, applies logic and returns values to a UI or test. Below is a snippet from exposing the `balance_of_private` function from a token implementation, which allows a user to easily read their balance, similar to the `balanceOf` function in the ERC20 standard. + +#include_code balance_of_private /yarn-project/noir-contracts/contracts/token_contract/src/main.nr rust + +:::info +Note, that unconstrained functions can have access to both public and private data when executed on the user's device. This is possible since it is not actually part of the circuits that are executed in contract execution. +::: \ No newline at end of file diff --git a/docs/docs/developers/contracts/syntax/functions/visibility.md b/docs/docs/developers/contracts/syntax/functions/visibility.md new file mode 100644 index 00000000000..61756bff01e --- /dev/null +++ b/docs/docs/developers/contracts/syntax/functions/visibility.md @@ -0,0 +1,25 @@ +--- +title: Visibility +--- + +In Aztec there are multiple different types of visibility that can be applied to functions. Namely we have `data visibility` and `function visibility`. This page explains these types of visibility. + +For a practical guide of using multiple types of data and function visibility,follow the [token tutorial](../../../tutorials/writing_token_contract.md). + +### Data Visibility + +Data visibility is used to describe whether the data (or state) used in a function is generally accessible (public) or on a need to know basis (private). + +### Function visibility + +This is the kind of visibility you are more used to seeing in Solidity and more traditional programming languages. It is used to describe whether a function is callable from other contracts, or only from within the same contract. + +By default, all functions are callable from other contracts, similarly to the Solidity `public` visibility. To make them only callable from the contract itself, you can mark them as `internal`. Contrary to solidity, we don't have the `external` nor `private` keywords. `external` since it is limited usage when we don't support inheritance, and `private` since we don't support inheritance and it would also be confusing with multiple types of `private`. + +A good place to use `internal` is when you want a private function to be able to alter public state. As mentioned above, private functions cannot do this directly. They are able to call public functions and by making these internal we can ensure that this state manipulating function is only callable from our private function. + +:::danger +Note that non-internal functions could be used directly as an entry-point, which currently means that the `msg_sender` would be `0`, so for now, using address `0` as a burn address is not recommended. You can learn more about this in the [Accounts concept page](../../../../learn/concepts/accounts/main.md#entrypoint-restrictions). +::: + +To understand how visibility works under the hood, check out the [Inner Workings page](./inner_workings.md). \ No newline at end of file diff --git a/docs/docs/dev_docs/contracts/syntax/globals.md b/docs/docs/developers/contracts/syntax/globals.md similarity index 100% rename from docs/docs/dev_docs/contracts/syntax/globals.md rename to docs/docs/developers/contracts/syntax/globals.md diff --git a/docs/docs/developers/contracts/syntax/historical_access/history_lib_reference.md b/docs/docs/developers/contracts/syntax/historical_access/history_lib_reference.md new file mode 100644 index 00000000000..d7688909262 --- /dev/null +++ b/docs/docs/developers/contracts/syntax/historical_access/history_lib_reference.md @@ -0,0 +1,119 @@ +--- +title: History Reference +--- + + + +## Note inclusion + +Note inclusion proves that a note existed (its hash was included in a note hash tree) at a specific block number. + +## prove_note_inclusion + +`prove_note_inclusion` takes 4 parameters: + +| Name | Type | Description | +|-----------------|------------------------|-----------------------------------------------------| +| note_interface | NoteInterface | Interface for the note with necessary functionality| +| note_with_header| Note | The note you are proving inclusion for | +| block_number | u32 | Block number for proving note's existence | +| context | PrivateContext | Private context | + +## prove_note_commitment_inclusion + +A **commitment**, also referred to as a **note hash** is a public acknowledgment of the existence of a note without revealing the content of the note. You can learn more about how to compress a note to a note hash [here](../../../../learn/concepts/storage/trees/main.md#example-note). + +`prove_note_commitment_inclusion` takes 3 parameters: + +| Name | Type | Description | +|-----------------|------------------------|-----------------------------------------------------| +| commitment | Field | Note commitment we are checking inclusion of | +| block_number | u32 | Block number for proving note's existence | +| context| PrivateContext | Private Context | + +## Note validity + +This proves that a note exists and has not been nullified at a specified block. + +### prove_note_validity + +`prove_note_validity` takes 4 parameters: + +| Name | Type | Description | +|-----------------|------------------------|-----------------------------------------------------| +| note_interface | NoteInterface | Interface for the note with necessary functionality| +| note_with_header| Note | The note you are proving inclusion for | +| block_number | u32 | Block number for proving note's existence | +| context | PrivateContext | Private context | + +## Nullifier inclusion + +This proves that a nullifier was included in a certain block (can be used to prove that a note had been nullified). + +### prove_nullifier_inclusion + +`prove_nullifier_inclusion` takes 3 parameters: + +| Name | Type | Description | +|-----------------|------------------------|-----------------------------------------------------| +| nullifier | Field | The nullifier you are proving inclusion for | +| block_number | u32 | Block number for proving note's existence | +| context | PrivateContext | Private context | + +## Nullifier non inclusion + +This proves that a nullifier was not included in a certain block (can be used to prove that a note had not yet been nullified in a given block). + +### prove_nullifier_non_inclusion + +`prove_nullifier_non_inclusion` takes 3 parameters: + +| Name | Type | Description | +|-----------------|------------------------|-----------------------------------------------------| +| nullifier | Field | The nullifier you are proving inclusion for | +| block_number | u32 | Block number for proving note's existence | +| context | PrivateContext | Private context | + + +### note_not_nullified + +Instead of passing the nullifier, you can check that a note has not been nullified by passing the note. + +## Public value inclusion + +This proves that a public value exists at a certain block. + +### prove_public_value_inclusion + +`prove_public_value_inclusion` takes 4 parameters: + +| Name | Type | Description | +|-----------------|------------------------|-----------------------------------------------------| +| value | Field | The public value you are proving inclusion for | +| storage_slot | Field | Storage slot the value exists in | +| block_number | u32 | Block number for proving value's existence | +| context | PrivateContext | Private context | + +## Contract inclusion + +This proves that a contract exists in, ie had been deployed before or in, a certain block. + +### prove_contract_inclusion + +`prove_contract_inclusion` takes 7 parameters: + +| Name | Type | Description | +|---------------------------|-----------------|-------------------------------------------------------| +| deployer_public_key | GrumpkinPoint | Public key of the contract deployer | +| contract_address_salt | Field | Unique identifier for the contract's address | +| function_tree_root | Field | Root of the contract's function tree | +| constructor_hash | Field | Hash of the contract's constructor | +| portal_contract_address | EthAddress | Ethereum address of the associated portal contract | +| block_number | u32 | Block number for proof verification | +| context | PrivateContext | Private context | + +If there is no associated portal contract, you can use a zero Ethereum address: + +```ts +new EthAddress(Buffer.alloc(EthAddress.SIZE_IN_BYTES)); +``` diff --git a/docs/docs/developers/contracts/syntax/historical_access/how_to_prove_history.md b/docs/docs/developers/contracts/syntax/historical_access/how_to_prove_history.md new file mode 100644 index 00000000000..8441611dbf2 --- /dev/null +++ b/docs/docs/developers/contracts/syntax/historical_access/how_to_prove_history.md @@ -0,0 +1,102 @@ +--- +title: How to prove existence of historical notes and nullifiers +--- + +The Aztec Protocol uses an append-only Merkle tree to store hashes of the headers of all previous blocks in the chain as its leaves. This is known as an archive tree. You can learn more about how it works in the [concepts section](../../../../learn/concepts/storage/trees/main.md#archive-tree). + +# History library + +The history library allows you to prove any of the following at a given block height before the current height: +* [Note inclusion](./history_lib_reference.md#note-inclusion) +* [Nullifier inclusion](./history_lib_reference.md#nullifier-inclusion) +* [Note validity](./history_lib_reference.md#note-validity) +* [Existence of public value](./history_lib_reference.md#public-value-inclusion) +* [Contract inclusion](./history_lib_reference.md#contract-inclusion) + +Using this library, you can check that specific notes or nullifiers were part of Aztec network state at specific blocks. This can be useful for things such as: + +* Verifying a minimum timestamp from a private context +* Checking eligibility based on historical events (e.g. for an airdrop by proving that you owned a note) +* Verifying historic ownership / relinquishing of assets +* Proving existence of a value in public data tree at a given contract slot +* Proving that a contract was deployed in a given block with some parameters + +**In this guide you will learn how to** +* Prove a note was included in a specified block +* Create a nullifier and prove it was not included in a specified block + +For a more extensive reference, go to [the reference page](./history_lib_reference.md). + +## 1. Import the `history` library from `aztec` + +```rust +aztec::{ + #include_code imports yarn-project/noir-contracts/contracts/inclusion_proofs_contract/src/main.nr raw +} +``` + +This imports all functions from the `history` library. You should only import the functions you will use in your contract. + +## 2. Create a note to prove inclusion of + +In general you will likely have the note you want to prove inclusion of. But if you are just experimenting you can create a note with a function like below: + +#include_code create_note yarn-project/noir-contracts/contracts/inclusion_proofs_contract/src/main.nr rust + +## 3. Get the note from the PXE + +Retrieve the note from the user's PXE. + +#include_code get_note_from_pxe yarn-project/noir-contracts/contracts/inclusion_proofs_contract/src/main.nr rust + +In this example, the user's notes are stored in a map called `private_values`. We retrieve this map, then select 1 note from it with the value of `1`. + +## 4. Prove that a note was included in a specified block + +To prove that a note existed in a specified block, call `prove_note_inclusion` as shown in this example: + +#include_code prove_note_inclusion yarn-project/noir-contracts/contracts/inclusion_proofs_contract/src/main.nr rust + +This function takes in 4 arguments: + +1. The note interface (`ValueNoteMethods`) +2. The note (`maybe_note.unwrap_unchecked()`). Here, `unwrap_unchecked()` returns the inner value without asserting `self.is_some()` +3. The block number +4. Private context + +Note: for this to work, you will need to import `ValueNoteMethods` at the beginning of the contract: + +#include_code value_note_imports yarn-project/noir-contracts/contracts/inclusion_proofs_contract/src/main.nr rust + +This will only prove the note existed, not whether or not the note has been nullified. You can prove that a note existed and had not been nullified in a specified block by using `prove_note_validity` which takes the same arguments: + +#include_code prove_note_validity yarn-project/noir-contracts/contracts/inclusion_proofs_contract/src/main.nr rust + +## 5. Create a nullifier to prove inclusion of + +You can easily nullify a note like so: + +#include_code nullify_note yarn-project/noir-contracts/contracts/inclusion_proofs_contract/src/main.nr rust + +This function gets a note from the PXE like we did in [step 3](#3-get-the-note-from-the-pxe) and nullifies it with `remove()`. + +You can then compute this nullifier with `note.compute_nullifier(&mut context)`. + +## 6. Prove that a nullifier was included in a specified block + +Call `prove_nullifier_inclusion` like so: + +#include_code prove_nullifier_inclusion yarn-project/noir-contracts/contracts/inclusion_proofs_contract/src/main.nr rust + +This takes three arguments: +1. The nullifier +2. Block number +3. Private context + +You can also prove that a nullifier was not included in a specified block by using `prove_nullifier_non_inclusion` which takes the same arguments: + +#include_code prove_nullifier_non_inclusion yarn-project/noir-contracts/contracts/inclusion_proofs_contract/src/main.nr rust + +## Prove contract inclusion, public value inclusion, and note commitment inclusion + +To see what else you can do with the `history` library, check out the [reference](./history_lib_reference.md). diff --git a/docs/docs/dev_docs/contracts/syntax/main.md b/docs/docs/developers/contracts/syntax/main.md similarity index 94% rename from docs/docs/dev_docs/contracts/syntax/main.md rename to docs/docs/developers/contracts/syntax/main.md index 7157b46764c..73a6c7ffddc 100644 --- a/docs/docs/dev_docs/contracts/syntax/main.md +++ b/docs/docs/developers/contracts/syntax/main.md @@ -12,7 +12,7 @@ Aztec.nr contains abstractions which remove the need to understand the low-level - Public and private [state variable types](./storage/main.md) - Some pre-designed notes - Functions for [emitting](./events.md) encrypted and unencrypted logs -- [Oracle functions](./functions.md#oracle-functions) for accessing: +- [Oracle functions](./functions/oracles.md) for accessing: - private state - secrets - Functions for communicating with [Ethereum L1](../portals/main.md) diff --git a/docs/docs/developers/contracts/syntax/oracles.md b/docs/docs/developers/contracts/syntax/oracles.md new file mode 100644 index 00000000000..fa524a02bc8 --- /dev/null +++ b/docs/docs/developers/contracts/syntax/oracles.md @@ -0,0 +1,49 @@ +--- +title: Oracles +--- + +On this page you will learn: + +1. [A list of inbuilt oracles](#inbuilt-oracles) +3. [How to use the debug_log oracle](#how-to-use-the-debug-oracle) +3. [How to use the auth_witness oracle](#how-to-use-the-auth_witness-oracle) +4. [How to use the pop_capsule oracle for arbitrary data](#how-to-use-the-popCapsule-oracle) +4. [Reference](#oracles-reference) + +## Inbuilt oracles + +- [`debug_log`](https://github.com/AztecProtocol/aztec-packages/blob/master/yarn-project/aztec-nr/aztec/src/oracle/debug_log.nr) - Provides a couple of debug functions that can be used to log information to the console. Read more about debugging [here](../../debugging/main.md). +- [`auth_witness`](https://github.com/AztecProtocol/aztec-packages/blob/master/yarn-project/aztec-nr/authwit/src/auth_witness.nr) - Provides a way to fetch the authentication witness for a given address. This is useful when building account contracts to support approve-like functionality. +- [`get_l1_to_l2_message`](https://github.com/AztecProtocol/aztec-packages/blob/master/yarn-project/aztec-nr/aztec/src/oracle/get_l1_to_l2_message.nr) - Useful for application that receive messages from L1 to be consumed on L2, such as token bridges or other cross-chain applications. +- [`notes`](https://github.com/AztecProtocol/aztec-packages/blob/master/yarn-project/aztec-nr/aztec/src/oracle/notes.nr) - Provides a lot of functions related to notes, such as fetches notes from storage etc, used behind the scenes for value notes and other pre-build note implementations. +- [`logs`](https://github.com/AztecProtocol/aztec-packages/blob/master/yarn-project/aztec-nr/aztec/src/oracle/logs.nr) - Provides the to log encrypted and unencrypted data. + +Find a full list [on GitHub](https://github.com/AztecProtocol/aztec-packages/tree/master/yarn-project/aztec-nr/aztec/src/oracle). + +:::note +Please note that it is **not** possible to write a custom oracle for your dapp. Oracles are implemented in the PXE, so all users of your dapp would have to use a PXE service with your custom oracle included. If you want to inject some arbitrary data that does not have a dedicated oracle, you can use [popCapsule](#how-to-use-the-pop_capsule-oracle). +::: + +## How to use the popCapsule oracle + +`popCapsule` is used for passing artbitrary data. We have not yet included this in Aztec.nr, so it is a bit more complex than the other oracles. You can follow this how-to: + +### 1. Define the pop_capsule function + +In a new file on the same level as your `main.nr`, implement an unconstrained function that calls the pop_capsule oracle: + +#include_code pop_capsule yarn-project/noir-contracts/contracts/slow_tree_contract/src/capsule.nr rust + +### 2. Import this into your smart contract + +If it lies in the same directory as your smart contract, you can import it like this: + +#include_code import_pop_capsule yarn-project/noir-contracts/contracts/slow_tree_contract/src/main.nr rust + +### 3. Use it as any other oracle + +Now it becomes a regular oracle you can call like this: + +#include_code pop_capsule yarn-project/noir-contracts/contracts/slow_tree_contract/src/main.nr rust + + diff --git a/docs/docs/dev_docs/contracts/syntax/slow_updates_tree.md b/docs/docs/developers/contracts/syntax/slow_updates_tree.md similarity index 98% rename from docs/docs/dev_docs/contracts/syntax/slow_updates_tree.md rename to docs/docs/developers/contracts/syntax/slow_updates_tree.md index cfe7d5905f9..3f8538baeb9 100644 --- a/docs/docs/dev_docs/contracts/syntax/slow_updates_tree.md +++ b/docs/docs/developers/contracts/syntax/slow_updates_tree.md @@ -2,7 +2,7 @@ title: Slow Updates Tree --- -Slow Updates Tree is a data structure that allows for historical public data to be accessed in both private and public domains. Read the high level overview in the [concepts section](../../../concepts/foundation/communication/public_private_calls/slow_updates_tree.md). +Slow Updates Tree is a data structure that allows for historical public data to be accessed in both private and public domains. Read the high level overview in the [Communication section](../../../learn/concepts/communication/public_private_calls/slow_updates_tree.md). The slow updates tree works by having a current tree and a pending tree, and replacing the current tree with the pending tree after an epoch has passed. Public functions can read directly from the current tree, and private functions can perform a membership proof that values are part of a commitment to the current state of the tree. diff --git a/docs/docs/developers/contracts/syntax/storage/main.md b/docs/docs/developers/contracts/syntax/storage/main.md new file mode 100644 index 00000000000..7e9b382f82d --- /dev/null +++ b/docs/docs/developers/contracts/syntax/storage/main.md @@ -0,0 +1,133 @@ +--- +title: Storage +--- + +Smart contracts rely on storage, acting as the persistent memory on the blockchain. In Aztec, because of its hybrid, privacy-first architecture, the management of this storage is more complex than other blockchains like Ethereum. + +You control this storage in Aztec using the `Storage` struct. This struct serves as the housing unit for all your smart contract's state variables - the data it needs to keep track of and maintain. + +These state variables come in two forms: public and private. Public variables are visible to anyone, and private variables remain hidden within the contract. + +Aztec.nr has a few abstractions to help define the type of data your contract holds. These include Singletons, ImmutableSingletons, Set, and Map. + +On this and the following pages in this section, you’ll learn: + +- How to manage a smart contract's storage structure +- The distinctions and applications of public and private state variables +- How to use Singleton, ImmutableSingleton, Set, and Map +- An overview of 'notes' and the UTXO model +- Practical implications of Storage in real smart contracts + In an Aztec.nr contract, storage is to be defined as a single struct, that contains both public and private state variables. + +## Public and private state variables + +Public state variables can be read by anyone, while private state variables can only be read by their owner (or people whom the owner has shared the decrypted data or note viewing key with). + +Public state follows the Ethereum style account model, where each contract has its own key-value datastore. Private state follows a UTXO model, where note contents (pre-images) are only known by the sender and those able to decrypt them - see ([state model](../../../../learn/concepts/hybrid_state/main.md) and [private/public execution](../../../../learn/concepts/communication/public_private_calls/main.md)) for more background. + +## Storage struct + +:::info +The struct **must** be called `Storage` for the Aztec.nr library to properly handle it (this will be relaxed in the future). +::: + +```rust +struct Storage { + // public state variables + // private state variables +} +``` + +:::danger +If your contract uses storage (has Storage struct defined), you **MUST** include a `compute_note_hash_and_nullifier` function to allow PXE to process encrypted events. See [encrypted events](../events.md#processing-encrypted-events) for more. + +If you don't yet have any private state variables defined you can use this placeholder function: + +#include_code compute_note_hash_and_nullifier_placeholder /yarn-project/noir-contracts/contracts/token_bridge_contract/src/main.nr rust +::: + +Since Aztec.nr is written in Noir, which is state-less, we need to specify how the storage struct should be initialized to read and write data correctly. This is done by specifying an `init` function that is run in functions that rely on reading or altering the state variables. This `init` function must declare the Storage struct with an instantiation defining how variables are accessed and manipulated. The function MUST be called `init` for the Aztec.nr library to properly handle it (this will be relaxed in the future). + +```rust +impl Storage { + fn init(context: Context) -> Self { + Storage { + // (public state variables)::new() + // (private state variables)::new() + } + } +} +``` + +If you have defined a `Storage` struct following this naming scheme, then it will be made available to you through the reserved `storage` keyword within your contract functions. + +:::warning Using slot `0` is not supported! +No storage values should be initialized at slot `0` - storage slots begin at `1`. This is a known issue that will be fixed in the future. +::: + +## Map + +A `map` is a state variable that "maps" a key to a value. It can be used with private or public storage variables. + +:::info +In Aztec.nr, keys are always `Field`s, or types that can be serialized as Fields, and values can be any type - even other maps. `Field`s are finite field elements, but you can think of them as integers. +::: + +It includes a [`Context`](../context.mdx) to specify the private or public domain, a `storage_slot` to specify where in storage the map is stored, and a `start_var_constructor` which tells the map how it should operate on the underlying type. This includes how to serialize and deserialize the type, as well as how commitments and nullifiers are computed for the type if it's private. + +You can view the implementation in the Aztec.nr library [here](https://github.com/AztecProtocol/aztec-packages/blob/master/yarn-project/aztec-nr/aztec/src/state_vars/map.nr). + +### `new` + +When declaring the storage for a map, we use the `Map::new()` constructor. As seen below, this takes the `storage_slot` and the `start_var_constructor` along with the [`Context`](../context.mdx). + +We will see examples of map constructors for public and private variables in later sections. + +#### As private storage + +When declaring a mapping in private storage, we have to specify which type of Note to use. In the example below, we are specifying that we want to use the `Singleton` note type. + +In the Storage struct: + +#include_code storage-map-singleton-declaration /yarn-project/noir-contracts/contracts/docs_example_contract/src/main.nr rust + +In the `Storage::init` function: + +#include_code state_vars-MapSingleton /yarn-project/noir-contracts/contracts/docs_example_contract/src/main.nr rust + +#### Public Example + +When declaring a public mapping in Storage, we have to specify that the type is public by declaring it as `PublicState` instead of specifying a note type like with private storage above. + +In the Storage struct: + +#include_code storage_minters /yarn-project/noir-contracts/contracts/token_contract/src/main.nr rust + +In the `Storage::init` function: + +#include_code storage_minters_init /yarn-project/noir-contracts/contracts/token_contract/src/main.nr rust + +### `at` + +When dealing with a Map, we can access the value at a given key using the `::at` method. This takes the key as an argument and returns the value at that key. + +This function behaves similarly for both private and public maps. An example could be if we have a map with `minters`, which is mapping addresses to a flag for whether they are allowed to mint tokens or not. + +#include_code read_minter /yarn-project/noir-contracts/contracts/token_contract/src/main.nr rust + +Above, we are specifying that we want to get the storage in the Map `at` the `msg_sender()`, read the value stored and check that `msg_sender()` is indeed a minter. Doing a similar operation in Solidity code would look like: + +```solidity +require(minters[msg.sender], "caller is not minter"); +``` + +## Further Reading + +- Managing [Public State](./public_state.md) +- Jump to the page on [Private State](./private_state.md) + +## Concepts mentioned + +- [Hybrid State Model](../../../../learn/concepts/hybrid_state/main.md) +- [Public-private execution](../../../../learn/concepts/communication/public_private_calls/main.md) +- [Function Contexts](../context.mdx) diff --git a/docs/docs/developers/contracts/syntax/storage/private_state.md b/docs/docs/developers/contracts/syntax/storage/private_state.md new file mode 100644 index 00000000000..d636459720a --- /dev/null +++ b/docs/docs/developers/contracts/syntax/storage/private_state.md @@ -0,0 +1,361 @@ +--- +title: Private State +--- + +On this page we will look at how to manage private state in Aztec contracts. We will look at how to declare private state, how to read and write to it, and how to use it in your contracts. + +For a higher level overview of the state model in Aztec, see the [hybrid state model](../../../../learn/concepts/hybrid_state/main.md) page, or jump back to the previous page on [Storage](./main.md). + +## Overview + +In contrast to public state, private state is persistent state that is **not** visible to the whole world. Depending on the logic of the smart contract, a private state variable's current value will only be known to one entity, or a closed group of entities. + +The value of a private state variable can either be shared via an [encrypted log](../events.md#encrypted-events), or offchain via web2, or completely offline: it's up to the app developer. + +Aztec private state follows a [utxo](https://en.wikipedia.org/wiki/Unspent_transaction_output)-based model. That is, a private state's current value is represented as one or many [notes](#notes). Each note is stored as an individual leaf in a utxo-based merkle tree: the [private state tree](../../../../learn/concepts/storage/trees/main.md). + +To greatly simplify the experience of writing private state, Aztec.nr provides three different types of private state variable: + +- [Singleton](#singletonnotetype) +- [ImmutableSingleton](#immutablesingletonnotetype) +- [Set](#setnotetype) + +These three structs abstract-away many of Aztec's protocol complexities, by providing intuitive methods to modify notes in the utxo tree in a privacy-preserving way. + +:::info +An app can also choose to emit data via unencrypted log, or to define a note whose data is easy to figure out, then the information is technically not private and could be visible to anyone. +::: + +### Notes + +Unlike public state variables, which can be arbitrary types, private state variables operate on `NoteType`. + +Notes are the fundamental elements in the private world. + +A note should implement the following traits: + +#include_code note_interface /yarn-project/aztec-nr/aztec/src/note/note_interface.nr rust + +#include_code serialize /yarn-project/noir-protocol-circuits/src/crates/types/src/traits.nr rust + +#include_code deserialize /yarn-project/noir-protocol-circuits/src/crates/types/src/traits.nr rust + +The interplay between a private state variable and its notes can be confusing. Here's a summary to aid intuition: + +A private state variable (of type `Singleton`, `ImmutableSingleton` or `Set`) may be declared in storage. + +Every note contains a header, which contains the contract address and storage slot of the state variable to which it is associated. A note is associated with a private state variable if the storage slot of the private state variable matches the storage slot contained in the note's header. The header provides information that helps the user interpret the note's data. + +Management of the header is abstracted-away from developers who use the `ImmutableSingleton`, `Singleton` and `Set` types. + +A private state variable points to one or many notes (depending on the type). The note(s) are all valid private state if the note(s) haven't yet been nullified. + +An `ImmutableSingleton` will point to _one_ note over the lifetime of the contract. This note is a struct of information that is persisted forever. + +A `Singleton` may point to _one_ note at a time. But since it's not "immutable", the note that it points to may be [replaced](#replace) by functions of the contract. The current value of a `Singleton` is interpreted as the one note which has not-yet been nullified. The act of replacing a Singleton's note is how a `Singleton` state may be modified by functions. + +`Singleton` is a useful type when declaring a private state variable which may only ever be modified by those who are privy to the current value of that state. + +A `Set` may point to _multiple_ notes at a time. The "current value" of a private state variable of type `Set` is some accumulation of all not-yet nullified notes which belong to the `Set`. + +:::note +The term "some accumulation" is intentionally vague. The interpretation of the "current value" of a `Set` must be expressed by the smart contract developer. A common use case for a `Set` is to represent the sum of a collection of values (in which case 'accumulation' is 'summation'). + +Think of a ZCash balance (or even a Bitcoin balance). The "current value" of a user's ZCash balance is the sum of all unspent (not-yet nullified) notes belonging to that user. To modify the "current value" of a `Set` state variable, is to [`insert`](#insert) new notes into the `Set`, or [`remove`](#remove) notes from that set. +::: + +Interestingly, if a developer requires a private state to be modifiable by users who _aren't_ privy to the value of that state, a `Set` is a very useful type. The `insert` method allows new notes to be added to the `Set` without knowing any of the other notes in the set! (Like posting an envelope into a post box, you don't know what else is in there!). + +## `Singleton` + +Singleton is a private state variable that is unique in a way. When a Singleton is initialized, a note is created to represent its value. And the way to update the value is to destroy the current note, and create a new one with the updated value. + +Like for public state, we define the struct to have context and a storage slot. You can view the implementation [here](https://github.com/AztecProtocol/aztec-packages/blob/master/yarn-project/aztec-nr/aztec/src/state_vars/singleton.nr). + +An example of singleton usage in the account contracts is keeping track of public keys. The `Singleton` is added to the `Storage` struct as follows: + +#include_code storage-singleton-declaration /yarn-project/noir-contracts/contracts/docs_example_contract/src/main.nr rust + +### `new` + +As part of the initialization of the `Storage` struct, the `Singleton` is created as follows at the specified storage slot. + +#include_code start_vars_singleton /yarn-project/noir-contracts/contracts/docs_example_contract/src/main.nr rust + +### `initialize` + +As mentioned, the Singleton is initialized to create the first note and value. + +When this function is called, a nullifier of the storage slot is created, preventing this Singleton from being initialized again. + +:::danger Privacy-Leak +Beware that because this nullifier is created only from the storage slot without randomness it leaks privacy. This means that it is possible for an external observer to determine when the note is nullified. + +For example, if the storage slot depends on the an address then it is possible to link the nullifier to the address. If the singleton is part of a `map` with an `AztecAddress` as the key then the nullifier will be linked to the address. +::: + +Unlike public states, which have a default initial value of `0` (or many zeros, in the case of a struct, array or map), a private state (of type `Singleton`, `ImmutableSingleton` or `Set`) does not have a default initial value. The `initialize` method (or `insert`, in the case of a `Set`) must be called. + +:::info +Extend on what happens if you try to use non-initialized state. +::: + +### `is_initialized` + +An unconstrained method to check whether the Singleton has been initialized or not. It takes an optional owner and returns a boolean. You can view the implementation [here](https://github.com/AztecProtocol/aztec-packages/blob/#include_aztec_version/yarn-project/aztec-nr/aztec/src/state_vars/singleton.nr). + +#include_code singleton_is_initialized /yarn-project/noir-contracts/contracts/docs_example_contract/src/main.nr rust + +### `replace` + +To update the value of a `Singleton`, we can use the `replace` method. The method takes a new note as input, and replaces the current note with the new one. It emits a nullifier for the old value, and inserts the new note into the data tree. + +An example of this is seen in a example card game, where we create a new note (a `CardNote`) containing some new data, and replace the current note with it: + +#include_code state_vars-SingletonReplace /yarn-project/noir-contracts/contracts/docs_example_contract/src/main.nr rust + +If two people are trying to modify the Singleton at the same time, only one will succeed as we don't allow duplicate nullifiers! Developers should put in place appropriate access controls to avoid race conditions (unless a race is intended!). + +### `get_note` + +This function allows us to get the note of a Singleton, essentially reading the value. + +#include_code state_vars-SingletonGet /yarn-project/noir-contracts/contracts/docs_example_contract/src/main.nr rust + +#### Nullifying Note reads + +To ensure that a user's private execution always uses the latest value of a Singleton, the `get_note` function will nullify the note that it is reading. This means that if two people are trying to use this function with the same note, only one will succeed (no duplicate nullifiers allowed). + +This also makes read operations indistinguishable from write operations and allows the sequencer to verifying correct execution without learning anything about the value of the note. + +### `view_note` + +Functionally similar to [`get_note`](#get_note), but executed in unconstrained functions and can be used by the wallet to fetch notes for use by front-ends etc. + +## `ImmutableSingleton` + +`ImmutableSingleton` represents a unique private state variable that, as the name suggests, is immutable. Once initialized, its value cannot be altered. You can view the implementation [here](https://github.com/AztecProtocol/aztec-packages/blob/#include_aztec_version/yarn-project/aztec-nr/aztec/src/state_vars/immutable_singleton.nr). + +### `new` + +As part of the initialization of the `Storage` struct, the `Singleton` is created as follows, here at storage slot 1 and with the `NoteInterface` for `PublicKeyNote`. + +#include_code storage_init /yarn-project/noir-contracts/contracts/schnorr_account_contract/src/main.nr rust + +### `initialize` + +When this function is invoked, it creates a nullifier for the storage slot, ensuring that the ImmutableSingleton cannot be initialized again. + +:::danger Privacy-Leak +Beware that because this nullifier is created only from the storage slot without randomness it leaks privacy. This means that it is possible for an external observer to determine when the note is nullified. + +For example, if the storage slot depends on the an address then it is possible to link the nullifier to the address. If the singleton is part of a `map` with an `AztecAddress` as the key then the nullifier will be linked to the address. +::: + +Set the value of an ImmutableSingleton by calling the `initialize` method: + +#include_code initialize /yarn-project/noir-contracts/contracts/schnorr_account_contract/src/main.nr rust + +Once initialized, an ImmutableSingleton's value remains unchangeable. This method can only be called once. + +### `is_initialized` + +An unconstrained method to check if the ImmutableSingleton has been initialized. Takes an optional owner and returns a boolean. You can find the implementation [here](https://github.com/AztecProtocol/aztec-packages/blob/#include_aztec_version/yarn-project/aztec-nr/aztec/src/state_vars/immutable_singleton.nr). + +### `get_note` + +Similar to the `Singleton`, we can use the `get_note` method to read the value of an ImmutableSingleton. + +Use this method to retrieve the value of an initialized ImmutableSingleton. + +#include_code get_note /yarn-project/noir-contracts/contracts/schnorr_account_contract/src/main.nr rust + +Unlike a `Singleton`, the `get_note` function for an ImmutableSingleton doesn't nullify the current note in the background. This means that multiple accounts can concurrently call this function to read the value. + +This function will throw if the `ImmutableSingleton` hasn't been initialized. + +### `view_note` + +Functionally similar to `get_note`, but executed unconstrained and can be used by the wallet to fetch notes for use by front-ends etc. + +## `Set` + +Set is used for managing a collection of notes. All notes in a Set are of the same `NoteType`. But whether these notes all belong to one entity, or are accessible and editable by different entities, is up to the developer. The set is a collection of notes inserted into the data-tree, but notes are never removed from the tree itself, they are only nullified. + +You can view the implementation [here](https://github.com/AztecProtocol/aztec-packages/blob/#include_aztec_version/yarn-project/aztec-nr/aztec/src/state_vars/set.nr). + +And can be added to the `Storage` struct as follows. Here adding a set for a custom note, the TransparentNote (useful for [public -> private communication](../functions/calling_functions.md#public---private)). + +#include_code storage_pending_shields /yarn-project/noir-contracts/contracts/token_contract/src/main.nr rust + +### `new` + +The `new` method tells the contract how to operate on the underlying storage. + +We can initialize the set as follows: + +#include_code storage_pending_shields_init /yarn-project/noir-contracts/contracts/token_contract/src/main.nr rust + +### `insert` + +Allows us to modify the storage by inserting a note into the set. + +A hash of the note will be generated, and inserted into the note hash tree, allowing us to later use in contract interactions. Recall that the content of the note should be shared with the owner to allow them to use it, as mentioned this can be done via an [encrypted log](../events.md#encrypted-events), or offchain via web2, or completely offline. + +#include_code insert /yarn-project/aztec-nr/easy-private-state/src/easy_private_state.nr rust + +### `insert_from_public` + +The `insert_from_public` allow public function to insert notes into private storage. This is very useful when we want to support private function calls that have been initiated in public, such as shielding in the [example token contract](../../../tutorials/writing_token_contract.md#shield). + +The usage is similar to using the `insert` method with the difference that this one is called in public functions. + +#include_code insert_from_public /yarn-project/noir-contracts/contracts/token_contract/src/main.nr rust + +### `remove` + +Will remove a note from the set if it previously has been read from storage, e.g. you have fetched it through a `get_notes` call. This is useful when you want to remove a note that you have previously read from storage and do not have to read it again. + +Nullifiers are emitted when reading values to make sure that they are up to date. + +An example of how to use this operation is visible in the `easy_private_state`: + +#include_code remove /yarn-project/aztec-nr/easy-private-state/src/easy_private_state.nr rust + +### `get_notes` + +This function returns the notes the account has access to. + +The kernel circuits are constrained to a maximum number of notes this function can return at a time. Check [here](https://github.com/AztecProtocol/aztec-packages/blob/#include_aztec_version/yarn-project/noir-protocol-circuits/src/crates/types/src/constants.nr) and look for `MAX_READ_REQUESTS_PER_CALL` for the up-to-date number. + +Because of this limit, we should always consider using the second argument `NoteGetterOptions` to limit the number of notes we need to read and constrain in our programs. This is quite important as every extra call increases the time used to prove the program and we don't want to spend more time than necessary. + +An example of such options is using the [filter_notes_min_sum](https://github.com/AztecProtocol/aztec-packages/blob/#include_aztec_version/yarn-project/aztec-nr/value-note/src/filter.nr) to get "enough" notes to cover a given value. Essentially, this function will return just enough notes to cover the amount specified such that we don't need to read all our notes. For users with a lot of notes, this becomes increasingly important. + +#include_code get_notes /yarn-project/aztec-nr/easy-private-state/src/easy_private_state.nr rust + +### `view_notes` + +Functionally similar to [`get_notes`](#get_notes), but executed unconstrained and can be used by the wallet to fetch notes for use by front-ends etc. + +#include_code view_notes /yarn-project/aztec-nr/value-note/src/balance_utils.nr rust + +There's also a limit on the maximum number of notes that can be returned in one go. To find the current limit, refer to [this file](https://github.com/AztecProtocol/aztec-packages/blob/#include_aztec_version/yarn-project/noir-protocol-circuits/src/crates/types/src/constants.nr) and look for `MAX_NOTES_PER_PAGE`. + +The key distinction is that this method is unconstrained. It does not perform a check to verify if the notes actually exist, which is something the [`get_notes`](#get_notes) method does under the hood. Therefore, it should only be used in an unconstrained contract function. + +This function requires a `NoteViewerOptions`. The `NoteViewerOptions` is essentially similar to the [`NoteGetterOptions`](#notegetteroptions), except that it doesn't take a custom filter. + +## `NoteGetterOptions` + +`NoteGetterOptions` encapsulates a set of configurable options for filtering and retrieving a selection of notes from a [data oracle](../functions/oracles.md). Developers can design instances of `NoteGetterOptions`, to determine how notes should be filtered and returned to the functions of their smart contracts. + +You can view the implementation [here](https://github.com/AztecProtocol/aztec-packages/blob/#include_aztec_version/yarn-project/aztec-nr/aztec/src/note/note_getter_options.nr). + +### `selects: BoundedVec, N>` + +`selects` is a collection of filtering criteria, specified by `Select { field_index: u8, value: Field, comparator: u3 }` structs. It instructs the data oracle to find notes whose (`field_index`)th field matches the provided `value`, according to the `comparator`. + +### `sorts: BoundedVec, N>` + +`sorts` is a set of sorting instructions defined by `Sort { field_index: u8, order: u2 }` structs. This directs the data oracle to sort the matching notes based on the value of the specified field index and in the indicated order. The value of order is **1** for _DESCENDING_ and **2** for _ASCENDING_. + +### `limit: u32` + +When the `limit` is set to a non-zero value, the data oracle will return a maximum of `limit` notes. + +### `offset: u32` + +This setting enables us to skip the first `offset` notes. It's particularly useful for pagination. + +### `filter: fn ([Option; MAX_READ_REQUESTS_PER_CALL], FILTER_ARGS) -> [Option; MAX_READ_REQUESTS_PER_CALL]` + +Developers have the option to provide a custom filter. This allows specific logic to be applied to notes that meet the criteria outlined above. The filter takes the notes returned from the oracle and `filter_args` as its parameters. + +It's important to note that the process of applying the custom filter to get the final notes is not constrained. It's crucial to verify the returned notes even if certain assumptions are made in the custom filter. + +### `filter_args: FILTER_ARGS` + +`filter_args` provides a means to furnish additional data or context to the custom filter. + +### `status: u2` + +`status` allows the caller to retrieve notes that have been nullified, which can be useful to prove historical data. Note that when querying for both active and nullified notes the caller cannot know if each note retrieved has or has not been nullified. + +### Methods + +Several methods are available on `NoteGetterOptions` to construct the options in a more readable manner: + +### `fn new() -> NoteGetterOptions` + +This function initializes a `NoteGetterOptions` that simply returns the maximum number of notes allowed in a call. + +### `fn with_filter(filter, filter_args) -> NoteGetterOptions` + +This function initializes a `NoteGetterOptions` with a [`filter`](#filter-fn-optionnote-max_read_requests_per_call-filter_args---optionnote-max_read_requests_per_call) and [`filter_args`](#filter_args-filter_args). + +### `.select` + +This method adds a [`Select`](#selects-boundedvecoptionselect-n) criterion to the options. + +### `.sort` + +This method adds a [`Sort`](#sorts-boundedvecoptionsort-n) criterion to the options. + +### `.set_limit` + +This method lets you set a limit for the maximum number of notes to be retrieved. + +### `.set_offset` + +This method sets the offset value, which determines where to start retrieving notes. + +### `.set_status` + +This method sets the status of notes to retrieve (active or nullified). + +### Examples + +#### Example 1 + +The following code snippet creates an instance of `NoteGetterOptions`, which has been configured to find the cards that belong to `account_address`. The returned cards are sorted by their points in descending order, and the first `offset` cards with the highest points are skipped. + +#include_code state_vars-NoteGetterOptionsSelectSortOffset /yarn-project/noir-contracts/contracts/docs_example_contract/src/options.nr rust + +The first value of `.select` and `.sort` is the index of a field in a note type. For the note type `CardNote` that has the following fields: + +#include_code state_vars-CardNote /yarn-project/noir-contracts/contracts/docs_example_contract/src/types/card_note.nr rust + +The indices are: 0 for `points`, 1 for `secret`, and 2 for `owner`. + +In the example, `.select(2, account_address, Option::none())` matches the 2nd field of `CardNote`, which is `owner`, and returns the cards whose `owner` field equals `account_address`, equality is the comparator used because because including no comparator (the third argument) defaults to using the equality comparator. The current possible values of Comparator are specified in the Note Getter Options implementation linked above. + +`.sort(0, SortOrder.DESC)` sorts the 0th field of `CardNote`, which is `points`, in descending order. + +There can be as many conditions as the number of fields a note type has. The following example finds cards whose fields match the three given values: + +#include_code state_vars-NoteGetterOptionsMultiSelects /yarn-project/noir-contracts/contracts/docs_example_contract/src/options.nr rust + +While `selects` lets us find notes with specific values, `filter` lets us find notes in a more dynamic way. The function below picks the cards whose points are at least `min_points`, although this now can be done by using the select function with a GTE comparator: + +#include_code state_vars-OptionFilter /yarn-project/noir-contracts/contracts/docs_example_contract/src/options.nr rust + +We can use it as a filter to further reduce the number of the final notes: + +#include_code state_vars-NoteGetterOptionsFilter /yarn-project/noir-contracts/contracts/docs_example_contract/src/options.nr rust + +One thing to remember is, `filter` will be applied on the notes after they are picked from the database, so it is more efficient to use select with comparators where possible. Another side effect of this is that it's possible that the actual notes we end up getting are fewer than the limit. + +The limit is `MAX_READ_REQUESTS_PER_CALL` by default. But we can set it to any value **smaller** than that: + +#include_code state_vars-NoteGetterOptionsPickOne /yarn-project/noir-contracts/contracts/docs_example_contract/src/options.nr rust + +#### Example 2 + +An example of how we can use a Comparator to select notes when calling a Noir contract from aztec.js is below. + +#include_code state_vars-NoteGetterOptionsComparatorExampleTs /yarn-project/end-to-end/src/e2e_note_getter.test.ts typescript + +In this example, we use the above typescript code to invoke a call to our Noir contract below. This Noir contract function takes an input to match with, and a comparator to use when fetching and selecting notes from storage. + +#include_code state_vars-NoteGetterOptionsComparatorExampleNoir /yarn-project/noir-contracts/contracts/docs_example_contract/src/main.nr rust diff --git a/docs/docs/developers/contracts/syntax/storage/public_state.md b/docs/docs/developers/contracts/syntax/storage/public_state.md new file mode 100644 index 00000000000..1b53e9dbaf9 --- /dev/null +++ b/docs/docs/developers/contracts/syntax/storage/public_state.md @@ -0,0 +1,134 @@ +--- +title: Public State +--- + +On this page we will look at how to manage public state in Aztec contracts. We will look at how to declare public state, how to read and write to it, and how to use it in your contracts. + +For a higher level overview of the state model in Aztec, see the [state model](../../../../learn/concepts/hybrid_state/main.md) page, or jump back to the previous page on [Storage](./main.md). + +## Overview + +The `PublicState` struct is generic over the variable type `T`. The type *must* implement Serialize and Deserialize traits, as specified here: + +#include_code serialize /yarn-project/noir-protocol-circuits/src/crates/types/src/traits.nr rust +#include_code deserialize /yarn-project/noir-protocol-circuits/src/crates/types/src/traits.nr rust + +The struct contains a `storage_slot` which, similar to Ethereum, is used to figure out _where_ in storage the variable is located. Notice that while we don't have the exact same [state model](../../../../learn/concepts/hybrid_state/main.md) as EVM chains it will look similar from the contract developers point of view. + +You can find the details of `PublicState` in the implementation [here](https://github.com/AztecProtocol/aztec-packages/blob/#include_aztec_version/yarn-project/aztec-nr/aztec/src/state_vars/public_state.nr). + +:::info +An example using a larger struct can be found in the [lending example](https://github.com/AztecProtocol/aztec-packages/tree/master/yarn-project/noir-contracts/contracts/lending_contract)'s use of an [`Asset`](https://github.com/AztecProtocol/aztec-packages/tree/#include_aztec_version/yarn-project/noir-contracts/contracts/lending_contract/src/asset.nr). +::: + +### `new` + +When declaring the storage for `T` as a persistent public storage variable, we use the `PublicState::new()` constructor. As seen below, this takes the `storage_slot` and the [`Context`](../context.mdx), which in this case is used to share interface with other structures. You can view the implementation [here](https://github.com/AztecProtocol/aztec-packages/blob/#include_aztec_version/yarn-project/aztec-nr/aztec/src/state_vars/public_state.nr). + +#### Single value example + +Say that we wish to add `admin` public state variable into our storage struct. In the struct we can define it as: + +#include_code storage_admin /yarn-project/noir-contracts/contracts/token_contract/src/main.nr rust + +And then when initializing it in the `Storage::init` function we can do: + +#include_code storage_admin_init /yarn-project/noir-contracts/contracts/token_contract/src/main.nr rust + +We have specified that we are storing a `Field` that should be placed in storage slot `1`. This is just a single value, and is similar to the following in solidity: + +```solidity +address internal admin; +``` + +:::info +We know its verbose, and are working on making it less so. +::: + +#### Mapping example + +Say we want to have a group of `minters` that are able to mint assets in our contract, and we want them in public storage, because [access control in private is quite cumbersome](../../../../learn/concepts/communication/cross_chain_calls.md#a-note-on-l2-access-control). In the `Storage` struct we can add it as follows: + +#include_code storage_minters /yarn-project/noir-contracts/contracts/token_contract/src/main.nr rust + +And then when initializing it in the `Storage::init` function we can do it as follows: + +#include_code storage_minters_init /yarn-project/noir-contracts/contracts/token_contract/src/main.nr rust + +In this case, specifying that we are dealing with a map of Fields, and that it should be put at slot 2. + +This would be similar to the following in solidity: + +```solidity +mapping(address => bool) internal minters; +``` + +### `read` + +On the `PublicState` structs we have a `read` method to read the value at the location in storage. + +#### Reading from our `admin` example + +For our `admin` example from earlier, this could be used as follows to check that the stored value matches the `msg_sender()`. + +#include_code read_admin /yarn-project/noir-contracts/contracts/token_contract/src/main.nr rust + +#### Reading from our `minters` example + +As we saw in the Map earlier, a very similar operation can be done to perform a lookup in a map. + +#include_code read_minter /yarn-project/noir-contracts/contracts/token_contract/src/main.nr rust + +### `write` + +We have a `write` method on the `PublicState` struct that takes the value to write as an input and saves this in storage. It uses the serialization method to serialize the value which inserts (possibly multiple) values into storage. + +#### Writing to our `admin` example + +#include_code write_admin /yarn-project/noir-contracts/contracts/token_contract/src/main.nr rust + +#### Writing to our `minters` example + +#include_code write_minter /yarn-project/noir-contracts/contracts/token_contract/src/main.nr rust + +--- + +## Stable Public State + +`StablePublicState` is a special type of `PublicState` that can be read from both public and private! + +Since private execution is based on historical data, the user can pick ANY of its prior values to read from. This is why it `MUST` not be updated after the contract is deployed. The variable should be initialized at the constructor and then never changed. + +This makes the stable public variables useful for stuff that you would usually have in `immutable` values in solidity. For example this can be the name of a token or its number of decimals. + +Just like the `PublicState` it is generic over the variable type `T`. The type `MUST` implement Serialize and Deserialize traits. + +You can find the details of `StablePublicState` in the implementation [here](https://github.com/AztecProtocol/aztec-packages/blob/#include_aztec_version/yarn-project/aztec-nr/aztec/src/state_vars/stable_public_state.nr). + +### `new` +Is done exactly like the `PublicState` struct, but with the `StablePublicState` struct. + +#include_code storage_decimals /yarn-project/noir-contracts/contracts/token_contract/src/main.nr rust + +#include_code storage_decimals_init /yarn-project/noir-contracts/contracts/token_contract/src/main.nr rust + +### `initialize` + +#include_code initialize_decimals /yarn-project/noir-contracts/contracts/token_contract/src/main.nr rust + +:::warning Should only be called as part of the deployment. +If this is called outside the deployment transaction multiple values could be used down the line, potentially breaking the contract. + +Currently this is not constrained as we are in the middle of changing deployments. +::: + +### `read_public` + +Reading the value is like `PublicState`, simply with `read_public` instead of `read`. +#include_code read_decimals_public /yarn-project/noir-contracts/contracts/token_contract/src/main.nr rust + + +### `read_private` +Reading the value is like `PublicState`, simply with `read_private` instead of `read`. This part can only be executed in private. + +#include_code read_decimals_private /yarn-project/noir-contracts/contracts/token_contract/src/main.nr rust diff --git a/docs/docs/dev_docs/contracts/syntax/storage/storage_slots.md b/docs/docs/developers/contracts/syntax/storage/storage_slots.md similarity index 87% rename from docs/docs/dev_docs/contracts/syntax/storage/storage_slots.md rename to docs/docs/developers/contracts/syntax/storage/storage_slots.md index dd8d1b01d18..62386330e7f 100644 --- a/docs/docs/dev_docs/contracts/syntax/storage/storage_slots.md +++ b/docs/docs/developers/contracts/syntax/storage/storage_slots.md @@ -2,7 +2,7 @@ title: Storage slots --- -From the description of [storage slot in concepts](./../../../../concepts/foundation/state_model/storage_slots.md) you will get an idea around the logic of storage slots. In this section we will go into more detail and walk through an entire example of how storage slots are computed for private state to improve our storage slot intuition. Recall, that storage slots in the private domain is just a logical construct, and are not "actually" used for lookups, but rather just as a value to constrain against. +From the description of storage slots [in the Concepts](./../../../../learn/concepts/storage/storage_slots.md) you will get an idea around the logic of storage slots. In this section we will go into more detail and walk through an entire example of how storage slots are computed for private state to improve our storage slot intuition. Recall, that storage slots in the private domain is just a logical construct, and are not "actually" used for lookups, but rather just as a value to constrain against. For the case of the example, we will look at what is inserted into the note hashes tree when adding a note in the Token contract. Specifically, we are looking at the last part of the `transfer` function: @@ -31,7 +31,7 @@ sequenceDiagram Set->>LifeCycle: create_note(derived_slot, note) LifeCycle->>LifeCycle: note.header = NoteHeader { contract_address,
storage_slot: derived_slot, nonce: 0, is_transient: true } LifeCycle->>Utils: compute_inner_note_hash(note) - Utils->>TokenNote: compute_note_hash(note) + Utils->>TokenNote: note.compute_note_content_hash() TokenNote->>Utils: note_hash = H(amount, to, randomness) Utils->>NoteHash: compute_inner_hash(derived_slot, note_hash) NoteHash->>LifeCycle: inner_note_hash = H(derived_slot, note_hash) diff --git a/docs/docs/dev_docs/contracts/workflow.md b/docs/docs/developers/contracts/workflow.md similarity index 100% rename from docs/docs/dev_docs/contracts/workflow.md rename to docs/docs/developers/contracts/workflow.md diff --git a/docs/docs/dev_docs/debugging/aztecnr-errors.md b/docs/docs/developers/debugging/aztecnr-errors.md similarity index 98% rename from docs/docs/dev_docs/debugging/aztecnr-errors.md rename to docs/docs/developers/debugging/aztecnr-errors.md index 5d7e14aea2a..9e2e8236702 100644 --- a/docs/docs/dev_docs/debugging/aztecnr-errors.md +++ b/docs/docs/developers/debugging/aztecnr-errors.md @@ -57,7 +57,7 @@ This error occurs when you are trying to interact with a smart contract via an P To execute a transaction, the PXE needs to know the complete address of a contract, portal address (if portal is used) and contract artifacts. -To address the error, add the contract to the PXE by calling [`pxe.addContracts(...)`](../../apis/pxe/interfaces/PXE#addcontracts). +To address the error, add the contract to the PXE by calling [`pxe.addContracts(...)`](../../apis/pxe/interfaces/PXE.md#addcontracts). #### `Simulation error: No public key registered for address 0x0. Register it by calling pxe.registerRecipient(...) or pxe.registerAccount(...)` diff --git a/docs/docs/dev_docs/debugging/main.md b/docs/docs/developers/debugging/main.md similarity index 100% rename from docs/docs/dev_docs/debugging/main.md rename to docs/docs/developers/debugging/main.md diff --git a/docs/docs/dev_docs/debugging/sandbox-errors.md b/docs/docs/developers/debugging/sandbox-errors.md similarity index 89% rename from docs/docs/dev_docs/debugging/sandbox-errors.md rename to docs/docs/developers/debugging/sandbox-errors.md index f95925a4da2..6e1a92f154e 100644 --- a/docs/docs/dev_docs/debugging/sandbox-errors.md +++ b/docs/docs/developers/debugging/sandbox-errors.md @@ -13,7 +13,7 @@ This section contains a list of errors you may encounter when using Aztec Sandbo ### Kernel Circuits -We have several versions of public and private kernels as explained in [our circuits section](../../concepts/advanced/circuits/kernels/main.md). Certain things are only possible in certain versions of the circuits. So always ensure that the right version is being used for proof generation. For example, there is a specific version of the public kernel that only works if the previous kernel iteration was a private kernel. Similarly there is one that only works if the previous kernel was public. +We have several versions of public and private kernels as explained in [our circuits section](../../learn/concepts/circuits/main.md). Certain things are only possible in certain versions of the circuits. So always ensure that the right version is being used for proof generation. For example, there is a specific version of the public kernel that only works if the previous kernel iteration was a private kernel. Similarly there is one that only works if the previous kernel was public. Remember that for each function call (i.e. each item in the call stack), there is a new kernel iteration that gets run. @@ -86,7 +86,7 @@ Calling a private Aztec.nr function in a public kernel is not allowed. #### 3005 - PUBLIC_KERNEL\_\_NON_EMPTY_PRIVATE_CALL_STACK -Public functions are executed after all the private functions are (see [private-public execution](../../concepts/foundation/communication/public_private_calls/main.md)). As such, private call stack must be empty when executing in the public kernel. +Public functions are executed after all the private functions are (see [private-public execution](../../learn/concepts/communication/public_private_calls/main.md)). As such, private call stack must be empty when executing in the public kernel. #### 3011 - PUBLIC_KERNEL\_\_CALCULATED_PRIVATE_CALL_HASH_AND_PROVIDED_PRIVATE_CALL_HASH_MISMATCH @@ -130,7 +130,7 @@ For static calls, no new commitments or nullifiers can be added to the state. ### Rollup circuit errors -These are errors that occur when kernel proofs (transaction proofs) are sent to the rollup circuits to create an L2 block. See [rollup circuits](../../concepts/advanced/circuits/rollup_circuits/main.md) for more information. +These are errors that occur when kernel proofs (transaction proofs) are sent to the rollup circuits to create an L2 block. See [rollup circuits](../../learn/concepts/circuits/rollup_circuits/main.md) for more information. #### 4007 - BASE\_\_INVALID_CHAIN_ID @@ -140,7 +140,7 @@ The L1 chain ID you used in your proof generation (for your private transaction) Same as [section 4007](#4007---base__invalid_chain_id) except the `version` refers to the version of the Aztec L2 instance. -Some scary bugs like `4003 - BASE__INVALID_NULLIFIER_SUBTREE` and `4004 - BASE__INVALID_NULLIFIER_RANGE` which are to do malformed nullifier trees (see [Indexed Merkle Trees](../../concepts/advanced/data_structures/indexed_merkle_tree.md)) etc may seem unrelated at a glance, but at a closer look may be because of some bug in an application's Aztec.nr code. Same is true for certain instances of `7008 - MEMBERSHIP_CHECK_FAILED`. +Some scary bugs like `4003 - BASE__INVALID_NULLIFIER_SUBTREE` and `4004 - BASE__INVALID_NULLIFIER_RANGE` which are to do malformed nullifier trees (see [Indexed Merkle Trees](../../learn/concepts/storage/trees/indexed_merkle_tree.md)) etc may seem unrelated at a glance, but at a closer look may be because of some bug in an application's Aztec.nr code. Same is true for certain instances of `7008 - MEMBERSHIP_CHECK_FAILED`. ### Generic circuit errors @@ -171,7 +171,7 @@ Users may create a proof against a historical state in Aztec. The rollup circuit - using invalid historical L1 to L2 message data tree state - inserting a subtree into the greater tree - we make a smaller merkle tree of all the new commitments/nullifiers etc that were created in a transaction or in a rollup and add it to the bigger state tree. Before inserting, we do a merkle membership check to ensure that the index to insert at is indeed an empty subtree (otherwise we would be overwriting state). This can happen when `next_available_leaf_index` in the state tree's snapshot is wrong (it is fetched by the sequencer from the archiver). The error message should reveal which tree is causing this issue - - nullifier tree related errors - The nullifier tree uses an [Indexed Merkle Tree](../../concepts/advanced/data_structures/indexed_merkle_tree.md). It requires additional data from the archiver to know which is the nullifier in the tree that is just below the current nullifier before it can perform batch insertion. If the low nullifier is wrong, or the nullifier is in incorrect range, you may receive this error. + - nullifier tree related errors - The nullifier tree uses an [Indexed Merkle Tree](../../learn/concepts/storage/trees/indexed_merkle_tree.md). It requires additional data from the archiver to know which is the nullifier in the tree that is just below the current nullifier before it can perform batch insertion. If the low nullifier is wrong, or the nullifier is in incorrect range, you may receive this error. --- @@ -191,7 +191,7 @@ Users may create a proof against a historical state in Aztec. The rollup circuit - "${treeName} tree next available leaf index mismatch" - validating a tree's root is not enough. It also checks that the `next_available_leaf_index` is as expected. This is the next index we can insert new values into. Note that for the public data tree, this test is skipped since as it is a sparse tree unlike the others. -- "Public call stack size exceeded" - In Aztec, the sequencer executes all enqueued public functions in a transaction (to prevent race conditions - see [private-public execution](../../concepts/foundation/communication/public_private_calls/main.md)). This error says there are too many public functions requested. +- "Public call stack size exceeded" - In Aztec, the sequencer executes all enqueued public functions in a transaction (to prevent race conditions - see [private-public execution](../../learn/concepts/communication/public_private_calls/main.md)). This error says there are too many public functions requested. - "Array size exceeds target length" - happens if you add more items than allowed by the constants set due to our circuit limitations (eg sending too many L2 to L1 messages or creating a function that exceeds the call stack length or returns more values than what Aztec.nr functions allow) diff --git a/docs/docs/dev_docs/getting_started/aztecjs-getting-started.md b/docs/docs/developers/getting_started/aztecjs-getting-started.md similarity index 97% rename from docs/docs/dev_docs/getting_started/aztecjs-getting-started.md rename to docs/docs/developers/getting_started/aztecjs-getting-started.md index 637da709e60..d2f1fd6c2e6 100644 --- a/docs/docs/dev_docs/getting_started/aztecjs-getting-started.md +++ b/docs/docs/developers/getting_started/aztecjs-getting-started.md @@ -152,7 +152,7 @@ The sandbox is preloaded with multiple accounts so you don't have to sit and cre #include_code load_accounts /yarn-project/end-to-end/src/e2e_sandbox_example.test.ts typescript -An explanation on accounts on Aztec can be found [here](../../concepts/foundation/accounts/main.md). +An explanation on accounts on Aztec can be found [here](../../learn/concepts/accounts/main.md). If you want more accounts, you can find instructions in the [Account creation section](../wallets/creating_schnorr_accounts.md). @@ -268,7 +268,7 @@ Now lets transfer some funds from Alice to Bob by calling the `transfer` functio 1. The sender. 2. The recipient. 3. The quantity of tokens to be transferred. -4. The nonce for the [authentication witness](../../concepts//foundation/accounts/main.md#authorizing-actions), or 0 if msg.sender equal sender. +4. The nonce for the [authentication witness](../../learn//concepts/accounts/main.md#authorizing-actions), or 0 if msg.sender equal sender. Here is the Typescript code to call the `transfer` function, add this to your `index.ts` at the bottom of the `main` function: @@ -371,7 +371,7 @@ Our complete output should now be something like: token Bob's balance 10543 +43ms ``` -That's it! We have successfully deployed a token contract to an instance of the Aztec network and mined private state-transitioning transactions. We have also queried the resulting state all via the interfaces provided by the contract. To see exactly what has happened here, you can learn about the transaction flow [here](../../concepts/foundation/transactions.md). +That's it! We have successfully deployed a token contract to an instance of the Aztec network and mined private state-transitioning transactions. We have also queried the resulting state all via the interfaces provided by the contract. To see exactly what has happened here, you can learn about the transaction flow [here](../../learn/concepts/transactions.md). ## Next Steps diff --git a/docs/docs/dev_docs/getting_started/aztecnr-getting-started.md b/docs/docs/developers/getting_started/aztecnr-getting-started.md similarity index 96% rename from docs/docs/dev_docs/getting_started/aztecnr-getting-started.md rename to docs/docs/developers/getting_started/aztecnr-getting-started.md index 27357e5640e..32be3a1e2fa 100644 --- a/docs/docs/dev_docs/getting_started/aztecnr-getting-started.md +++ b/docs/docs/developers/getting_started/aztecnr-getting-started.md @@ -114,7 +114,7 @@ Let’s create a `constructor` method to run on deployment that assigns an initi This function accesses the counts from storage. Then it assigns the passed initial counter to the `owner`'s counter privately using `at().add()`. -We have annotated this and other functions with `#[aztec(private)]` which are ABI macros so the compiler understands it will handle private inputs. Learn more about functions and annotations [here](../contracts/syntax/functions.md). +We have annotated this and other functions with `#[aztec(private)]` which are ABI macros so the compiler understands it will handle private inputs. Learn more about functions and annotations [here](../contracts/syntax/functions/main.md). ## Incrementing our counter @@ -223,7 +223,7 @@ Now you can explore. **Interested in learning more about how Aztec works under the hood?** -Understand the high level architecture [here](../../concepts/foundation/main.md). +Understand the high level architecture [here](../../learn/about_aztec/technical_overview.md). **Want to write more advanced smart contracts?** @@ -231,4 +231,4 @@ Follow the token contract tutorial [here](../tutorials/writing_token_contract.md **Ready to dive into Aztec and Ethereum cross-chain communication?** -Read the [Portals page](../../concepts/foundation/communication/cross_chain_calls.md) and learn how to practically implement portals in the [token bridge tutorial](../tutorials/token_portal/main.md). +Read the [Portals page](../../learn/concepts/communication/cross_chain_calls.md) and learn how to practically implement portals in the [token bridge tutorial](../tutorials/token_portal/main.md). diff --git a/docs/docs/dev_docs/getting_started/main.md b/docs/docs/developers/getting_started/main.md similarity index 83% rename from docs/docs/dev_docs/getting_started/main.md rename to docs/docs/developers/getting_started/main.md index f2f343b0c2e..0e6fa1844ed 100644 --- a/docs/docs/dev_docs/getting_started/main.md +++ b/docs/docs/developers/getting_started/main.md @@ -12,7 +12,7 @@ If this is your first time using Aztec, and you want to get started by learning ## Learn -If you want to read more about the high level concepts of Aztec before writing some code, head to the [Foundational Concepts section](../../concepts/foundation/main.md). +If you want to read more about the high level concepts of Aztec before writing some code, head to the [Concepts section](../../learn/about_aztec/technical_overview.md). ## In this section diff --git a/docs/docs/dev_docs/getting_started/quickstart.md b/docs/docs/developers/getting_started/quickstart.md similarity index 90% rename from docs/docs/dev_docs/getting_started/quickstart.md rename to docs/docs/developers/getting_started/quickstart.md index 62b56e10afe..a7f1f894916 100644 --- a/docs/docs/dev_docs/getting_started/quickstart.md +++ b/docs/docs/developers/getting_started/quickstart.md @@ -53,7 +53,7 @@ Once these have been installed, to start the sandbox, run: aztec-sandbox ``` -This will attempt to run the Sandbox on ` localhost:8080`, so you will have to make sure nothing else is running on that port or change the port defined in `./.aztec/docker-compose.yml`. Running the command again will overwrite any changes made to the `docker-compose.yml`. +This will attempt to run the Sandbox on ` localhost:8080`, so you will have to make sure nothing else is running on that port or change the port defined in `./.aztec/docker-compose.yml`. Running the installation again will overwrite any changes made to the `docker-compose.yml`. This command will also install the CLI if a node package version of the CLI isn't found locally. @@ -71,13 +71,17 @@ Start by deploying a token contract. After it is deployed, we check that the dep #include_code deploy yarn-project/end-to-end/src/guides/up_quick_start.sh bash +:::note +If you're not using the default port for the Sandbox, make sure to pass the `--rpc-url` parameter, e.g.: `--rpc-url http://localhost:8000`. +::: + Note that the deployed contract address is exported, so we can use it as `$CONTRACT` later on. ## Call a contract with the CLI Alice is set up as the contract admin and token minter in the `_initialize` function. Let's get Alice some private tokens. -We need to export the `SECRET` and `SECRET_HASH` values in order to privately mint tokens. Private tokens are claimable by anyone with the pre-image to a provided hash, see more about how the token contract works in the [token contract tutorial](../tutorials/writing_token_contract.md). After the tokens have been minted, the notes will have to added to the [Private Execution Environment](../../apis/pxe/interfaces/PXE) (PXE) to be consumed by private functions. Once added, Alice can claim them with the `redeem_shield` function. After this, Alice should have 1000 tokens in their private balance. +We need to export the `SECRET` and `SECRET_HASH` values in order to privately mint tokens. Private tokens are claimable by anyone with the pre-image to a provided hash, see more about how the token contract works in the [token contract tutorial](../tutorials/writing_token_contract.md). After the tokens have been minted, the notes will have to added to the [Private Execution Environment](../../apis/pxe/interfaces/PXE.md) (PXE) to be consumed by private functions. Once added, Alice can claim them with the `redeem_shield` function. After this, Alice should have 1000 tokens in their private balance. #include_code mint-private yarn-project/end-to-end/src/guides/up_quick_start.sh bash diff --git a/docs/docs/dev_docs/limitations/main.md b/docs/docs/developers/limitations/main.md similarity index 92% rename from docs/docs/dev_docs/limitations/main.md rename to docs/docs/developers/limitations/main.md index bc28d3fd480..4101aeeb6d9 100644 --- a/docs/docs/dev_docs/limitations/main.md +++ b/docs/docs/developers/limitations/main.md @@ -29,13 +29,13 @@ Help shape and define: - It is a testing environment, it is insecure, unaudited and does not generate any proofs, its only for testing purposes; - Constructors can not call nor alter public state - - The constructor is executed exclusively in private domain, WITHOUT the ability to call public functions or alter public state. This means to set initial storage values, you need to follow a pattern similar to [proxies in Ethereum](https://blog.openzeppelin.com/proxy-patterns), where you `initialize` the contract with values after it have been deployed, see [constructor](../contracts/syntax/functions.md#constructor). -- No static nor delegate calls (see [mutability](../contracts/syntax/functions.md#mutability)). + - The constructor is executed exclusively in private domain, WITHOUT the ability to call public functions or alter public state. This means to set initial storage values, you need to follow a pattern similar to [proxies in Ethereum](https://blog.openzeppelin.com/proxy-patterns), where you `initialize` the contract with values after it have been deployed, see [constructor](../contracts/syntax/functions/constructor.md). +- No static nor delegate calls (see [mutability](../contracts/syntax/functions/main.md)). - These values are unused in the call-context. - Beware that what you think of as a `view` could alter state ATM! Notably the account could alter state or re-enter whenever the account contract's `is_valid` function is called. - `msg_sender` is currently leaking when doing private -> public calls - The `msg_sender` will always be set, if you call a public function from the private world, the `msg_sender` will be set to the private caller's address. See [function context](../contracts/syntax/context.mdx). -- The initial `msg_sender` is 0, which can be problematic for some contracts, see [function visibility](../contracts/syntax/functions.md#function-visibility). +- The initial `msg_sender` is 0, which can be problematic for some contracts, see [function visibility](../contracts/syntax/functions/visibility.md). - Unencrypted logs don't link to the contract that emitted it, so essentially just a `debug_log`` that you can match values against. - A note that is created and nullified in the same transaction will still emit an encrypted log. - A limited amount of new commitments, nullifiers and calls that are supported by a transaction, see [circuit limitations](#circuit-limitations). @@ -54,7 +54,7 @@ That's right, the Sandbox doesn't actually generate or verify any zk-SNARKs yet! The main goal of the Sandbox is to enable developers to experiment with building apps, and hopefully to provide feedback. We want the developer experience to be as fast as possible, much like how Ethereum developers use Ganache or Anvil to get super-fast block times, instead of the slow-but-realistic 12-second block times that they'll encounter in production. A fast Sandbox enables fast testing, which enables developers to iterate quickly. -That's not to say a super-fast proving system isn't being worked on [as we speak](../../about_aztec/roadmap/cryptography_roadmap.md). +That's not to say a super-fast proving system isn't being worked on [as we speak](../../misc/roadmap/cryptography_roadmap.md). #### What are the consequences? @@ -193,7 +193,7 @@ Here are the current constants: #### What are the consequences? -When you write an [Aztec.nr](../contracts/main.md) [function](../contracts/syntax/functions.md), there will be upper bounds on the following: +When you write an [Aztec.nr](../contracts/main.md) [function](../contracts/syntax/functions/main.md), there will be upper bounds on the following: - The number of public state reads and writes; - The number of note reads and nullifications; @@ -209,13 +209,13 @@ Not only are there limits on a _per function_ basis, there are also limits on a **In particular, these _per-transaction_ limits will limit transaction call stack depths** in the Sandbox. That means if a function call results in a cascade of nested function calls, and each of those function calls outputs lots of state reads and writes, or logs (etc.), then all of that accumulated output data might exceed the per-transaction limits that we currently have. This would cause such transactions to fail. -There are plans to relax all of this rigidity, by providing many 'sizes' of [kernel circuit](../../concepts/advanced/circuits/kernels/main.md), and introducing a 'bus' to ferry varying lengths of data between kernel iterations. But that'll all take some time. +There are plans to relax all of this rigidity, by providing many 'sizes' of [kernel circuit](../../learn/concepts/circuits/main.md), and introducing a 'bus' to ferry varying lengths of data between kernel iterations. But that'll all take some time. > **In the mean time**, if you encounter a per-transaction limit when testing, and you're feeling adventurous, you could 'hack' the Sandbox to increase the limits. See here (TODO: link) for a guide. **However**, the limits cannot be increased indefinitely. So although we do anticipate that we'll be able to increase them a little bit, don't go mad and provide yourself with 1 million state transitions per transaction. That would be as unrealistic as artificially increasing Ethereum gas limits to 1 trillion. ### Circuits Processing Order Differs from Execution Order -Each function call is represented by a circuit with a dedicated zero-knowledge proof of its execution. The [private kernel circuit](../../concepts/advanced/circuits/kernels/private_kernel.md) is in charge of stitching all these proofs together to produce a zero-knowledge proof that the whole execution of all function calls within a transaction is correct. In doing so, the processing order differs from the execution order. Firstly, the private kernel has to handle one function call in its entirety at a time because a zk proof cannot be verified partially. This property alone makes it impossible for the ordering of kernel circuit validation to match the order in which the functions of the transaction were executed. Secondly, the private kernel processes function calls in a stack-based order, i.e., after having processed a function call, it processes all direct child function calls in an order which is the reverse of the execution order. +Each function call is represented by a circuit with a dedicated zero-knowledge proof of its execution. The [private kernel circuit](../../learn/concepts/circuits/kernels/private_kernel.md) is in charge of stitching all these proofs together to produce a zero-knowledge proof that the whole execution of all function calls within a transaction is correct. In doing so, the processing order differs from the execution order. Firstly, the private kernel has to handle one function call in its entirety at a time because a zk proof cannot be verified partially. This property alone makes it impossible for the ordering of kernel circuit validation to match the order in which the functions of the transaction were executed. Secondly, the private kernel processes function calls in a stack-based order, i.e., after having processed a function call, it processes all direct child function calls in an order which is the reverse of the execution order. Note that there is no plan to change this in the future. @@ -243,7 +243,7 @@ Transaction output elements such as notes in encrypted logs, note hashes (commit ### Chopped Transient Notes are still Emitted in Logs -A note which is created and nullified during the very same transaction is called transient. Such a note is chopped by the [private kernel circuit](../../concepts/advanced/circuits/kernels/private_kernel.md) and is never stored in any persistent data tree. +A note which is created and nullified during the very same transaction is called transient. Such a note is chopped by the [private kernel circuit](../../learn/concepts/circuits/kernels/private_kernel.md) and is never stored in any persistent data tree. For the time being, such chopped notes are still emitted through encrypted logs (which is the communication channel to transmit notes). When a log containing a chopped note is processed, a warning will be logged about a decrypted note which does not exist in data tree. We [improved](https://github.com/AztecProtocol/aztec-packages/issues/1603) error logging to help identify such an occurrence. However, this might be a source of confusion. This issue is tracked in ticket [#1641](https://github.com/AztecProtocol/aztec-packages/issues/1641). diff --git a/docs/docs/dev_docs/privacy/main.md b/docs/docs/developers/privacy/main.md similarity index 100% rename from docs/docs/dev_docs/privacy/main.md rename to docs/docs/developers/privacy/main.md diff --git a/docs/docs/dev_docs/testing/cheat_codes.md b/docs/docs/developers/testing/cheat_codes.md similarity index 97% rename from docs/docs/dev_docs/testing/cheat_codes.md rename to docs/docs/developers/testing/cheat_codes.md index d789872625e..982020d4ae1 100644 --- a/docs/docs/dev_docs/testing/cheat_codes.md +++ b/docs/docs/developers/testing/cheat_codes.md @@ -468,7 +468,7 @@ struct Storage { impl Storage { fn init() -> Self { Storage { - balances: Map::new(1, |slot| PublicState::new(slot, FieldSerializationMethods)), + balances: Map::new(1, |slot| PublicState::new(slot)), } } } @@ -500,13 +500,13 @@ Note: One Field element occupies a storage slot. Hence, structs with multiple fi ```rust struct Storage { - balances: Map>, + balances: Map>, } impl Storage { - fn init() -> Self { + fn init(context: Context) -> Self { Storage { - balances: Map::new(1, |slot| PublicState::new(slot, FieldSerializationMethods)), + balances: Map::new(context, 1, |context, slot| PublicState::new(context, slot)), } } } diff --git a/docs/docs/dev_docs/testing/main.md b/docs/docs/developers/testing/main.md similarity index 100% rename from docs/docs/dev_docs/testing/main.md rename to docs/docs/developers/testing/main.md diff --git a/docs/docs/dev_docs/tutorials/main.md b/docs/docs/developers/tutorials/main.md similarity index 100% rename from docs/docs/dev_docs/tutorials/main.md rename to docs/docs/developers/tutorials/main.md diff --git a/docs/docs/dev_docs/tutorials/testing.md b/docs/docs/developers/tutorials/testing.md similarity index 95% rename from docs/docs/dev_docs/tutorials/testing.md rename to docs/docs/developers/tutorials/testing.md index ca2d015bd90..3b19d4c3bb7 100644 --- a/docs/docs/dev_docs/tutorials/testing.md +++ b/docs/docs/developers/tutorials/testing.md @@ -154,7 +154,7 @@ To query storage directly, you'll need to know the slot you want to access. This #### Querying private state -Private state in the Aztec Network is represented via sets of [private notes](../../concepts/foundation/state_model/main.md#private-state). In our token contract example, the balance of a user is represented as a set of unspent value notes, each with their own corresponding numeric value. +Private state in the Aztec Network is represented via sets of [private notes](../../learn/concepts/hybrid_state/main.md#private-state). In our token contract example, the balance of a user is represented as a set of unspent value notes, each with their own corresponding numeric value. #include_code value-note-def yarn-project/aztec-nr/value-note/src/value_note.nr rust @@ -164,7 +164,7 @@ We can query the Private eXecution Environment (PXE) for all notes encrypted for #### Querying public state -[Public state](../../concepts/foundation/state_model/main.md#public-state) behaves as a key-value store, much like in the EVM. This scenario is much more straightforward, in that we can directly query the target slot and get the result back as a buffer. Note that we use the [`TokenContract`](https://github.com/AztecProtocol/aztec-packages/blob/master/yarn-project/noir-contracts/contracts/token_contract/src/main.nr) in this example, which defines a mapping of public balances on slot 6. +[Public state](../../learn/concepts/hybrid_state/main.md#public-state) behaves as a key-value store, much like in the EVM. This scenario is much more straightforward, in that we can directly query the target slot and get the result back as a buffer. Note that we use the [`TokenContract`](https://github.com/AztecProtocol/aztec-packages/blob/master/yarn-project/noir-contracts/contracts/token_contract/src/main.nr) in this example, which defines a mapping of public balances on slot 6. #include_code public-storage /yarn-project/end-to-end/src/guides/dapp_testing.test.ts typescript diff --git a/docs/docs/dev_docs/tutorials/token_portal/cancelling_deposits.md b/docs/docs/developers/tutorials/token_portal/cancelling_deposits.md similarity index 100% rename from docs/docs/dev_docs/tutorials/token_portal/cancelling_deposits.md rename to docs/docs/developers/tutorials/token_portal/cancelling_deposits.md diff --git a/docs/docs/dev_docs/tutorials/token_portal/depositing_to_aztec.md b/docs/docs/developers/tutorials/token_portal/depositing_to_aztec.md similarity index 91% rename from docs/docs/dev_docs/tutorials/token_portal/depositing_to_aztec.md rename to docs/docs/developers/tutorials/token_portal/depositing_to_aztec.md index bbb5915ff12..9452657be1f 100644 --- a/docs/docs/dev_docs/tutorials/token_portal/depositing_to_aztec.md +++ b/docs/docs/developers/tutorials/token_portal/depositing_to_aztec.md @@ -8,7 +8,20 @@ In this step, we will write our token portal contract on L1. In `l1-contracts/contracts` in your file called `TokenPortal.sol` paste this: -#include_code init /l1-contracts/test/portals/TokenPortal.sol solidity +```solidity +pragma solidity >=0.8.18; + +import {IERC20} from "@openzeppelin/contracts/token/ERC20/IERC20.sol"; +import {SafeERC20} from "@openzeppelin/contracts/token/ERC20/utils/SafeERC20.sol"; + +// Messaging +import {IRegistry} from "@aztec/l1-contracts/src/core/interfaces/messagebridge/IRegistry.sol"; +import {IInbox} from "@aztec/l1-contracts/src/core/interfaces/messagebridge/IInbox.sol"; +import {DataStructures} from "@aztec/l1-contracts/src/core/libraries/DataStructures.sol"; +import {Hash} from "@aztec/l1-contracts/src/core/libraries/Hash.sol"; + +#include_code init /l1-contracts/test/portals/TokenPortal.sol raw +``` This imports relevant files including the interfaces used by the Aztec rollup. And initializes the contract with the following parameters: diff --git a/docs/docs/dev_docs/tutorials/token_portal/main.md b/docs/docs/developers/tutorials/token_portal/main.md similarity index 97% rename from docs/docs/dev_docs/tutorials/token_portal/main.md rename to docs/docs/developers/tutorials/token_portal/main.md index 5e9caf8ce54..5739d5b81aa 100644 --- a/docs/docs/dev_docs/tutorials/token_portal/main.md +++ b/docs/docs/developers/tutorials/token_portal/main.md @@ -35,7 +35,7 @@ Aztec has the following core smart contracts on L1 that we need to know about: - `Outbox.sol` - a mailbox to the rollup for L2 to L1 messages (e.g. withdrawing tokens). Aztec contracts emit these messages and the sequencer adds these to the outbox. Portals then consume these messages. - `Registry.sol` - just like L1, we assume there will be various versions of Aztec (due to upgrades, forks etc). In such a case messages must not be replayable in other Aztec “domains”. A portal must decide which version/ID of Aztec the message is for. The registry stores the rollup, inbox and outbox address for each version of Aztec deployments, so the portal can find out the address of the mailbox it wants to talk to -For more information, read [cross-chain calls](../../../concepts/foundation/communication/cross_chain_calls). +For more information, read [cross-chain calls](../../../learn/concepts/communication/cross_chain_calls). ## Building a Token Bridge with Portals diff --git a/docs/docs/dev_docs/tutorials/token_portal/minting_on_aztec.md b/docs/docs/developers/tutorials/token_portal/minting_on_aztec.md similarity index 100% rename from docs/docs/dev_docs/tutorials/token_portal/minting_on_aztec.md rename to docs/docs/developers/tutorials/token_portal/minting_on_aztec.md diff --git a/docs/docs/dev_docs/tutorials/token_portal/setup.md b/docs/docs/developers/tutorials/token_portal/setup.md similarity index 87% rename from docs/docs/dev_docs/tutorials/token_portal/setup.md rename to docs/docs/developers/tutorials/token_portal/setup.md index 2c1e4472f48..6737c6d3171 100644 --- a/docs/docs/dev_docs/tutorials/token_portal/setup.md +++ b/docs/docs/developers/tutorials/token_portal/setup.md @@ -19,7 +19,7 @@ However if you’d rather skip this part, our dev-rels repo contains the starter - [node v18+](https://github.com/tj/n) - [docker](https://docs.docker.com/) -- [Aztec sandbox](https://docs.aztec.network/dev_docs/getting_started/sandbox) - you should have this running before starting the tutorial +- [Aztec sandbox](https://docs.aztec.network/developers/getting_started/sandbox) - you should have this running before starting the tutorial - [Aztec CLI](../../getting_started/quickstart.md) ```bash @@ -62,7 +62,7 @@ type = "contract" [dependencies] aztec = { git="https://github.com/AztecProtocol/aztec-packages/", tag="#include_aztec_version", directory="yarn-project/aztec-nr/aztec" } -token_portal_content_hash_lib = { git="https://github.com/AztecProtocol/aztec-packages/", tag="aztec-packages-v0.16.9", directory="yarn-project/noir-contracts/contracts/token_portal_content_hash_lib" } +token_portal_content_hash_lib = { git="https://github.com/AztecProtocol/aztec-packages/", tag="#include_aztec_version", directory="yarn-project/noir-contracts/contracts/token_portal_content_hash_lib" } ``` We will also be writing some helper functions that should exist elsewhere so we don't overcomplicated our contract. In `src` create two more files - one called `util.nr` and one called `token_interface` - so your dir structure should now look like this: @@ -74,6 +74,7 @@ aztec-contracts ├── src ├── main.nr ├── token_interface.nr + ├── util.nr ``` # Create a JS hardhat project @@ -99,7 +100,7 @@ touch TokenPortal.sol Now add dependencies that are required. These include interfaces to Aztec Inbox, Outbox and Registry smart contracts, OpenZeppelin contracts, and NomicFoundation. ```bash -yarn add @aztec/foundation @aztec/l1-contracts @openzeppelin/contracts && yarn add --dev @nomicfoundation/hardhat-network-helpers @nomicfoundation/hardhat-chai-matchers @nomiclabs/hardhat-ethers @nomiclabs/hardhat-etherscan @types/chai @types/mocha @typechain/ethers-v5 @typechain/hardhat chai hardhat-gas-reporter solidity-coverage ts-node typechain typescript +yarn add @aztec/foundation @aztec/l1-contracts @openzeppelin/contracts && yarn add --dev @nomicfoundation/hardhat-network-helpers @nomicfoundation/hardhat-chai-matchers @nomiclabs/hardhat-ethers @nomiclabs/hardhat-etherscan @types/chai @types/mocha @typechain/ethers-v5 @typechain/hardhat chai@4.0.0 hardhat-gas-reporter solidity-coverage ts-node typechain typescript ``` @@ -129,7 +130,7 @@ Inside the `packages` directory, run ```bash mkdir src && cd src && yarn init -yp -yarn add @aztec/aztec.js @aztec/accounts @aztec/noir-contracts @aztec/circuit-types @aztec/foundation @aztec/l1-artifacts viem "@types/node@^20.8.2" +yarn add typescript @aztec/aztec.js @aztec/accounts @aztec/noir-contracts @aztec/types @aztec/foundation @aztec/l1-artifacts viem@1.21.4 "@types/node@^20.8.2" yarn add -D jest @jest/globals ts-jest ``` @@ -144,7 +145,7 @@ In `package.json`, add: } ``` -Your `package.json` should look something like this: +Your `package.json` should look something like this (do not copy and paste): ```json { @@ -155,17 +156,10 @@ Your `package.json` should look something like this: "private": true, "type": "module", "dependencies": { - "@aztec/aztec.js": "^0.8.7", - "@aztec/foundation": "^0.8.7", - "@aztec/noir-contracts": "^0.8.7", - "@aztec/circuit-types": "^0.8.7", - "@types/node": "^20.8.2", - "ethers": "^5.7" + "dep": "version" }, "devDependencies": { - "@jest/globals": "^29.7.0", - "jest": "^29.7.0", - "ts-jest": "^29.1.1" + "dep": "version" }, "scripts": { "test": "NODE_NO_WARNINGS=1 node --experimental-vm-modules $(yarn bin jest)" @@ -227,12 +221,6 @@ Then create a jest config file: `jest.config.json` } ``` -You will also need to install some dependencies: - -```bash -yarn add --dev typescript @types/jest ts-jest -``` - Finally, we will create a test file. Run this in the `src` directory.: ```bash @@ -249,6 +237,7 @@ src └── cross_chain_messaging.test.ts ├── jest.config.json ├── package.json +├── tsconfig.json ``` In the next step, we’ll start writing our L1 smart contract with some logic to deposit tokens to Aztec from L1. diff --git a/docs/docs/dev_docs/tutorials/token_portal/typescript_glue_code.md b/docs/docs/developers/tutorials/token_portal/typescript_glue_code.md similarity index 90% rename from docs/docs/dev_docs/tutorials/token_portal/typescript_glue_code.md rename to docs/docs/developers/tutorials/token_portal/typescript_glue_code.md index 7adc80a0154..ca4e63c1994 100644 --- a/docs/docs/dev_docs/tutorials/token_portal/typescript_glue_code.md +++ b/docs/docs/developers/tutorials/token_portal/typescript_glue_code.md @@ -4,19 +4,12 @@ title: Deploy & Call Contracts with Typescript In this step we will write a Typescript test to interact with the sandbox and call our contracts! -Go to the `src/test` directory in your `packages` dir and create a new file called `cross_chain_messaging.test.ts`: - -```bash -cd src/test -touch cross_chain_messaging.test.ts -``` - ## Test imports and setup We need some helper files that can keep our code clean. Inside your `src/test` directory: ```bash -mkdir fixtures && cd fixtures +cd fixtures touch utils.ts cd .. && mkdir shared && cd shared touch cross_chain_test_harness.ts @@ -47,8 +40,8 @@ Open `cross_chain_messaging.test.ts` and paste the initial description of the te ```typescript import { expect, jest} from '@jest/globals' -import { AccountWallet, AztecAddress, DebugLogger, EthAddress, Fr, computeAuthWitMessageHash, createDebugLogger, createPXEClient, waitForPXE } from '@aztec/aztec.js'; -import { getInitialTestAccountsWallets } from '@aztec/accounts/testing'; +import { AccountWallet, AztecAddress, DebugLogger, EthAddress, Fr, computeAuthWitMessageHash, createDebugLogger, createPXEClient, waitForSandbox } from '@aztec/aztec.js'; +import { getSandboxAccountsWallets } from '@aztec/accounts/testing'; import { TokenContract } from '@aztec/noir-contracts/Token'; import { TokenBridgeContract } from '@aztec/noir-contracts/TokenBridge'; @@ -80,8 +73,8 @@ describe('e2e_cross_chain_messaging', () => { beforeEach(async () => { logger = createDebugLogger('aztec:e2e_uniswap'); const pxe = createPXEClient(PXE_URL); - await waitForPXE(pxe); - const wallets = await getInitialTestAccountsWallets(pxe); + await waitForSandbox(pxe); + const wallets = await getSandboxAccountsWallets(pxe); const walletClient = createWalletClient({ account: hdAccount, diff --git a/docs/docs/dev_docs/tutorials/token_portal/withdrawing_to_l1.md b/docs/docs/developers/tutorials/token_portal/withdrawing_to_l1.md similarity index 90% rename from docs/docs/dev_docs/tutorials/token_portal/withdrawing_to_l1.md rename to docs/docs/developers/tutorials/token_portal/withdrawing_to_l1.md index fd2a2ea1721..ff0be84bb85 100644 --- a/docs/docs/dev_docs/tutorials/token_portal/withdrawing_to_l1.md +++ b/docs/docs/developers/tutorials/token_portal/withdrawing_to_l1.md @@ -17,7 +17,7 @@ For this to work we import the `get_withdraw_content_hash` helper function from The `exit_to_l1_public` function enables anyone to withdraw their L2 tokens back to L1 publicly. This is done by burning tokens on L2 and then creating an L2->L1 message. 1. Like with our deposit function, we need to create the L2 to L1 message. The content is the _amount_ to burn, the recipient address, and who can execute the withdraw on the L1 portal on behalf of the user. It can be `0x0` for anyone, or a specified address. -2. `context.message_portal()` passes this content to the [kernel circuit](../../../concepts/advanced/circuits/kernels/public_kernel.md) which creates the proof for the transaction. The kernel circuit then adds the sender (the L2 address of the bridge + version of aztec) and the recipient (the portal to the L2 address + the chain ID of L1) under the hood, to create the message which gets added as rollup calldata by the sequencer and is stored in the outbox for consumption. +2. `context.message_portal()` passes this content to the [kernel circuit](../../../learn/concepts/circuits/kernels/public_kernel.md) which creates the proof for the transaction. The kernel circuit then adds the sender (the L2 address of the bridge + version of aztec) and the recipient (the portal to the L2 address + the chain ID of L1) under the hood, to create the message which gets added as rollup calldata by the sequencer and is stored in the outbox for consumption. 3. Finally, you also burn the tokens on L2! Note that it burning is done at the end to follow the check effects interaction pattern. Note that the caller has to first approve the bridge contract to burn tokens on its behalf. Refer to [burn_public function on the token contract](../writing_token_contract.md#burn_public). The nonce parameter refers to the approval message that the user creates - also refer to [authorizing token spends here](../writing_token_contract.md#authorizing-token-spends). - We burn the tokens from the `msg_sender()`. Otherwise, a malicious user could burn someone else’s tokens and mint tokens on L1 to themselves. One could add another approval flow on the bridge but that might make it complex for other applications to call the bridge. @@ -42,8 +42,10 @@ For both the public and private flow, we use the same mechanism to determine the After the transaction is completed on L2, the portal must call the outbox to successfully transfer funds to the user on L1. Like with deposits, things can be complex here. For example, what happens if the transaction was done on L2 to burn tokens but can’t be withdrawn to L1? Then the funds are lost forever! How do we prevent this? Paste this in your `TokenPortal.sol`: - -#include_code token_portal_withdraw /l1-contracts/test/portals/TokenPortal.sol solidity +```solidity +#include_code token_portal_withdraw /l1-contracts/test/portals/TokenPortal.sol raw +} +``` Here we reconstruct the L2 to L1 message and check that this message exists on the outbox. If so, we consume it and transfer the funds to the recipient. As part of the reconstruction, the content hash looks similar to what we did in our bridge contract on aztec where we pass the amount and recipient to the the hash. This way a malicious actor can’t change the recipient parameter to the address and withdraw funds to themselves. @@ -53,13 +55,17 @@ We call this pattern _designed caller_ which enables a new paradigm **where we c Before we can compile and use the contract, we need to add two additional functions. -We need a function that let's us read the token value. +We need a function that lets us read the token value. Paste this into `main.nr`: #include_code read_token /yarn-project/noir-contracts/contracts/token_bridge_contract/src/main.nr rust -And the `compute_note_hash_and_nullifier` required on every contract. +And the `compute_note_hash_and_nullifier` required on every contract: + +```rust +#include_code compute_note_hash_and_nullifier_placeholder /yarn-project/noir-contracts/contracts/token_bridge_contract/src/main.nr raw +} +``` -#include_code compute_note_hash_and_nullifier_placeholder /yarn-project/noir-contracts/contracts/token_bridge_contract/src/main.nr rust ## Compile code @@ -75,14 +81,14 @@ npx hardhat compile And compile your Aztec.nr contracts like this: ```bash -cd aztec-contracts/token-bridge +cd aztec-contracts/token_bridge aztec-nargo compile ``` And generate the TypeScript interface for the contract and add it to the test dir: ```bash -aztec-cli codegen target -o ../../../src/test/fixtures --ts +aztec-cli codegen target -o ../../src/test/fixtures --ts ``` This will create a TS interface in our `src/test` folder! diff --git a/docs/docs/dev_docs/tutorials/uniswap/execute_private_swap_on_l1.md b/docs/docs/developers/tutorials/uniswap/execute_private_swap_on_l1.md similarity index 100% rename from docs/docs/dev_docs/tutorials/uniswap/execute_private_swap_on_l1.md rename to docs/docs/developers/tutorials/uniswap/execute_private_swap_on_l1.md diff --git a/docs/docs/dev_docs/tutorials/uniswap/execute_public_swap_on_l1.md b/docs/docs/developers/tutorials/uniswap/execute_public_swap_on_l1.md similarity index 100% rename from docs/docs/dev_docs/tutorials/uniswap/execute_public_swap_on_l1.md rename to docs/docs/developers/tutorials/uniswap/execute_public_swap_on_l1.md diff --git a/docs/docs/dev_docs/tutorials/uniswap/l1_portal.md b/docs/docs/developers/tutorials/uniswap/l1_portal.md similarity index 100% rename from docs/docs/dev_docs/tutorials/uniswap/l1_portal.md rename to docs/docs/developers/tutorials/uniswap/l1_portal.md diff --git a/docs/docs/dev_docs/tutorials/uniswap/l2_contract_setup.md b/docs/docs/developers/tutorials/uniswap/l2_contract_setup.md similarity index 100% rename from docs/docs/dev_docs/tutorials/uniswap/l2_contract_setup.md rename to docs/docs/developers/tutorials/uniswap/l2_contract_setup.md diff --git a/docs/docs/dev_docs/tutorials/uniswap/main.md b/docs/docs/developers/tutorials/uniswap/main.md similarity index 100% rename from docs/docs/dev_docs/tutorials/uniswap/main.md rename to docs/docs/developers/tutorials/uniswap/main.md diff --git a/docs/docs/dev_docs/tutorials/uniswap/redeeming_swapped_assets_on_l2.md b/docs/docs/developers/tutorials/uniswap/redeeming_swapped_assets_on_l2.md similarity index 100% rename from docs/docs/dev_docs/tutorials/uniswap/redeeming_swapped_assets_on_l2.md rename to docs/docs/developers/tutorials/uniswap/redeeming_swapped_assets_on_l2.md diff --git a/docs/docs/dev_docs/tutorials/uniswap/setup.md b/docs/docs/developers/tutorials/uniswap/setup.md similarity index 100% rename from docs/docs/dev_docs/tutorials/uniswap/setup.md rename to docs/docs/developers/tutorials/uniswap/setup.md diff --git a/docs/docs/dev_docs/tutorials/uniswap/swap_privately.md b/docs/docs/developers/tutorials/uniswap/swap_privately.md similarity index 100% rename from docs/docs/dev_docs/tutorials/uniswap/swap_privately.md rename to docs/docs/developers/tutorials/uniswap/swap_privately.md diff --git a/docs/docs/dev_docs/tutorials/uniswap/swap_publicly.md b/docs/docs/developers/tutorials/uniswap/swap_publicly.md similarity index 100% rename from docs/docs/dev_docs/tutorials/uniswap/swap_publicly.md rename to docs/docs/developers/tutorials/uniswap/swap_publicly.md diff --git a/docs/docs/dev_docs/tutorials/uniswap/typescript_glue_code.md b/docs/docs/developers/tutorials/uniswap/typescript_glue_code.md similarity index 100% rename from docs/docs/dev_docs/tutorials/uniswap/typescript_glue_code.md rename to docs/docs/developers/tutorials/uniswap/typescript_glue_code.md diff --git a/docs/docs/dev_docs/tutorials/writing_dapp/contract_deployment.md b/docs/docs/developers/tutorials/writing_dapp/contract_deployment.md similarity index 94% rename from docs/docs/dev_docs/tutorials/writing_dapp/contract_deployment.md rename to docs/docs/developers/tutorials/writing_dapp/contract_deployment.md index 10906728034..b55ebcac2c2 100644 --- a/docs/docs/dev_docs/tutorials/writing_dapp/contract_deployment.md +++ b/docs/docs/developers/tutorials/writing_dapp/contract_deployment.md @@ -22,6 +22,7 @@ Then, open the `contracts/token/Nargo.toml` configuration file, and add the `azt aztec = { git="https://github.com/AztecProtocol/aztec-packages/", tag="#include_aztec_version", directory="yarn-project/aztec-nr/aztec" } authwit = { git="https://github.com/AztecProtocol/aztec-packages/", tag="#include_aztec_version", directory="yarn-project/aztec-nr/authwit"} safe_math = { git="https://github.com/AztecProtocol/aztec-packages/", tag="#include_aztec_version", directory="yarn-project/aztec-nr/safe-math"} +compressed_string = {git="https://github.com/AztecProtocol/aztec-packages/", tag="#include_aztec_version", directory="yarn-project/aztec-nr/compressed-string"} ``` Last, copy-paste the code from the `Token` contract into `contracts/token/main.nr`: @@ -51,7 +52,7 @@ Create a new file `src/deploy.mjs`: ```js // src/deploy.mjs import { writeFileSync } from 'fs'; -import { Contract, ContractDeployer, createPXEClient } from '@aztec/aztec.js'; +import { Contract, loadContractArtifact, createPXEClient } from '@aztec/aztec.js'; import { getInitialTestAccountsWallets } from '@aztec/accounts/testing'; import TokenContractJson from "../contracts/token/target/token_contract-Token.json" assert { type: "json" }; diff --git a/docs/docs/dev_docs/tutorials/writing_dapp/contract_interaction.md b/docs/docs/developers/tutorials/writing_dapp/contract_interaction.md similarity index 93% rename from docs/docs/dev_docs/tutorials/writing_dapp/contract_interaction.md rename to docs/docs/developers/tutorials/writing_dapp/contract_interaction.md index 73c0eef546c..058fa3483f5 100644 --- a/docs/docs/dev_docs/tutorials/writing_dapp/contract_interaction.md +++ b/docs/docs/developers/tutorials/writing_dapp/contract_interaction.md @@ -93,12 +93,12 @@ At the time of this writing, there are no events emitted when new private notes ## Working with public state -While [private and public state](../../../concepts/foundation/state_model/main.md) are fundamentally different, the API for working with private and public functions and state from `aztec.js` is equivalent. To query the balance in public tokens for our user accounts, we can just call the `balance_of_public` view function in the contract: +While [private and public state](../../../learn/concepts/hybrid_state/main.md) are fundamentally different, the API for working with private and public functions and state from `aztec.js` is equivalent. To query the balance in public tokens for our user accounts, we can just call the `balance_of_public` view function in the contract: #include_code showPublicBalances yarn-project/end-to-end/src/sample-dapp/index.mjs javascript :::info -Since this we are working with pubic balances, we can now query the balance for any address, not just those registered in our local PXE. We can also send funds to addresses for which we don't know their [public encryption key](../../../concepts/foundation/accounts/keys.md#encryption-keys). +Since this we are working with pubic balances, we can now query the balance for any address, not just those registered in our local PXE. We can also send funds to addresses for which we don't know their [public encryption key](../../../learn/concepts/accounts/keys.md#encryption-keys). ::: Here, since the token contract does not mint any initial funds upon deployment, the balances for all of our user's accounts will be zero. diff --git a/docs/docs/dev_docs/tutorials/writing_dapp/main.md b/docs/docs/developers/tutorials/writing_dapp/main.md similarity index 100% rename from docs/docs/dev_docs/tutorials/writing_dapp/main.md rename to docs/docs/developers/tutorials/writing_dapp/main.md diff --git a/docs/docs/dev_docs/tutorials/writing_dapp/project_setup.md b/docs/docs/developers/tutorials/writing_dapp/project_setup.md similarity index 100% rename from docs/docs/dev_docs/tutorials/writing_dapp/project_setup.md rename to docs/docs/developers/tutorials/writing_dapp/project_setup.md diff --git a/docs/docs/dev_docs/tutorials/writing_dapp/pxe_service.md b/docs/docs/developers/tutorials/writing_dapp/pxe_service.md similarity index 100% rename from docs/docs/dev_docs/tutorials/writing_dapp/pxe_service.md rename to docs/docs/developers/tutorials/writing_dapp/pxe_service.md diff --git a/docs/docs/dev_docs/tutorials/writing_dapp/testing.md b/docs/docs/developers/tutorials/writing_dapp/testing.md similarity index 98% rename from docs/docs/dev_docs/tutorials/writing_dapp/testing.md rename to docs/docs/developers/tutorials/writing_dapp/testing.md index 7178e895b6e..6e835783d3d 100644 --- a/docs/docs/dev_docs/tutorials/writing_dapp/testing.md +++ b/docs/docs/developers/tutorials/writing_dapp/testing.md @@ -67,4 +67,4 @@ yarn node --experimental-vm-modules $(yarn bin jest) --testRegex '.*\.test\.mjs$ ## Next steps -Now that you have finished the tutorial, you can learn more about [writing contracts with Noir](../../contracts/main.md) or read about the [fundamental concepts behind Aztec Network](../../../concepts/foundation/main.md). +Now that you have finished the tutorial, you can learn more about [writing contracts with Noir](../../contracts/main.md) or read about the [fundamental concepts behind Aztec Network](../../../learn/about_aztec/technical_overview.md). diff --git a/docs/docs/dev_docs/tutorials/writing_private_voting_contract.md b/docs/docs/developers/tutorials/writing_private_voting_contract.md similarity index 93% rename from docs/docs/dev_docs/tutorials/writing_private_voting_contract.md rename to docs/docs/developers/tutorials/writing_private_voting_contract.md index c8ab1e70f64..e24eecfa367 100644 --- a/docs/docs/dev_docs/tutorials/writing_private_voting_contract.md +++ b/docs/docs/developers/tutorials/writing_private_voting_contract.md @@ -107,7 +107,7 @@ This `init` function will be called every time we access `storage` in our functi The next step is to initialize the contract with a constructor. The constructor will take an address as a parameter and set the admin. -All constructors must be private, and because the admin is in public storage, we cannot directly update it from the constructor. You can find more information about this [here](../../concepts/foundation/communication/public_private_calls/main.md). +All constructors must be private, and because the admin is in public storage, we cannot directly update it from the constructor. You can find more information about this [here](../../learn/concepts/communication/public_private_calls/main.md). Therefore our constructor must call a public function by using `context.call_public_function()`. Paste this under the `impl` storage block: @@ -141,9 +141,9 @@ Create a private function called `cast_vote`: #include_code cast_vote yarn-project/noir-contracts/contracts/easy_private_voting_contract/src/main.nr rust -In this function, we do not create a nullifier with the address directly. This would leak privacy as it would be easy to reverse-engineer. We must add some randomness or some form of secret, like [nullifier secrets](../../concepts/foundation/accounts/keys.md#nullifier-secrets). +In this function, we do not create a nullifier with the address directly. This would leak privacy as it would be easy to reverse-engineer. We must add some randomness or some form of secret, like [nullifier secrets](../../learn/concepts/accounts/keys.md#nullifier-secrets). -To do this, we make an [oracle call](../contracts/syntax/functions.md#oracle-functions) to fetch the caller's secret key, hash it to create a nullifier, and push the nullifier to Aztec. The `secret.high` and `secret.low` values here refer to how we divide a large [Grumpkin scalar](https://github.com/AztecProtocol/aztec-packages/blob/7fb35874eae3f2cad5cb922282a619206573592c/noir/noir_stdlib/src/grumpkin_scalar.nr) value into its higher and lower parts. This allows for faster cryptographic computations so our hash can still be secure but is calculated faster. +To do this, we make an [oracle call](../contracts/syntax/functions/oracles.md) to fetch the caller's secret key, hash it to create a nullifier, and push the nullifier to Aztec. The `secret.high` and `secret.low` values here refer to how we divide a large [Grumpkin scalar](https://github.com/AztecProtocol/aztec-packages/blob/7fb35874eae3f2cad5cb922282a619206573592c/noir/noir_stdlib/src/grumpkin_scalar.nr) value into its higher and lower parts. This allows for faster cryptographic computations so our hash can still be secure but is calculated faster. After pushing the nullifier, we update the `tally` to reflect this vote. As we know from before, a private function cannot update public state directly, so we are calling a public function. @@ -163,7 +163,7 @@ We will create a function that anyone can call that will return the number of vo #include_code get_vote yarn-project/noir-contracts/contracts/easy_private_voting_contract/src/main.nr rust -We set it as `unconstrained` and do not annotate it because it is only reading from state. You can read more about unconstrained functions [here](../../concepts/advanced/acir_simulator.md#unconstrained-functions). +We set it as `unconstrained` and do not annotate it because it is only reading from state. You can read more about unconstrained functions [here](../../learn/concepts/pxe/acir_simulator.md#unconstrained-functions). ## Allowing an admin to end a voting period diff --git a/docs/docs/dev_docs/tutorials/writing_token_contract.md b/docs/docs/developers/tutorials/writing_token_contract.md similarity index 96% rename from docs/docs/dev_docs/tutorials/writing_token_contract.md rename to docs/docs/developers/tutorials/writing_token_contract.md index f4fda400456..3a867ab7a70 100644 --- a/docs/docs/dev_docs/tutorials/writing_token_contract.md +++ b/docs/docs/developers/tutorials/writing_token_contract.md @@ -61,6 +61,7 @@ type = "contract" aztec = { git="https://github.com/AztecProtocol/aztec-packages/", tag="#include_aztec_version", directory="yarn-project/aztec-nr/aztec" } safe_math = { git="https://github.com/AztecProtocol/aztec-packages/", tag="#include_aztec_version", directory="yarn-project/aztec-nr/safe-math"} authwit={ git="https://github.com/AztecProtocol/aztec-packages/", tag="#include_aztec_version", directory="yarn-project/aztec-nr/authwit"} +compressed_string = {git="https://github.com/AztecProtocol/aztec-packages/", tag="#include_aztec_version", directory="yarn-project/aztec-nr/compressed-string"} ``` ## Contract Interface @@ -155,7 +156,7 @@ These are functions that have transparent logic, will execute in a publicly veri ### Private functions -These are functions that have private logic and will be executed on user devices to maintain privacy. The only data that is submitted to the network is a proof of correct execution, new data [commitments](https://en.wikipedia.org/wiki/Commitment_scheme) and [nullifiers](../../concepts/advanced/data_structures/trees#nullifier-tree), so users will not reveal which contract they are interacting with or which function they are executing. The only information that will be revealed publicly is that someone executed a private transaction on Aztec. +These are functions that have private logic and will be executed on user devices to maintain privacy. The only data that is submitted to the network is a proof of correct execution, new data [commitments](https://en.wikipedia.org/wiki/Commitment_scheme) and [nullifiers](../../learn/concepts/storage/trees/main.md#nullifier-tree), so users will not reveal which contract they are interacting with or which function they are executing. The only information that will be revealed publicly is that someone executed a private transaction on Aztec. - `redeem_shield` enables accounts to claim tokens that have been made private via `mint_private` or `shield` by providing the secret - `unshield` enables an account to send tokens from their private balance to any other account's public balance @@ -208,11 +209,11 @@ Just below the contract definition, add the following imports: #include_code imports /yarn-project/noir-contracts/contracts/token_contract/src/main.nr rust -We are importing the Option type, items from the `value_note` library to help manage private value storage, note utilities, context (for managing private and public execution contexts), `state_vars` for helping manage state, `types` for data manipulation and `oracle` for help passing data from the private to public execution context. We also import the `auth` [library](https://github.com/AztecProtocol/aztec-packages/blob/#include_aztec_version/yarn-project/aztec-nr/aztec/src/auth.nr) to handle token authorizations from [Account Contracts](../../concepts/foundation/accounts/main). Check out the Account Contract with AuthWitness [here](https://github.com/AztecProtocol/aztec-packages/blob/#include_aztec_version/yarn-project/noir-contracts/contracts/schnorr_single_key_account_contract/src/main.nr). +We are importing the Option type, items from the `value_note` library to help manage private value storage, note utilities, context (for managing private and public execution contexts), `state_vars` for helping manage state, `types` for data manipulation and `oracle` for help passing data from the private to public execution context. We also import the `auth` [library](https://github.com/AztecProtocol/aztec-packages/blob/#include_aztec_version/yarn-project/aztec-nr/aztec/src/auth.nr) to handle token authorizations from [Account Contracts](../../learn/concepts/accounts/main). Check out the Account Contract with AuthWitness [here](https://github.com/AztecProtocol/aztec-packages/blob/#include_aztec_version/yarn-project/noir-contracts/contracts/schnorr_single_key_account_contract/src/main.nr). [SafeU120](https://github.com/AztecProtocol/aztec-packages/blob/#include_aztec_version/yarn-project/aztec-nr/safe-math/src/safe_u120.nr) is a library to do safe math operations on unsigned integers that protects against overflows and underflows. -For more detail on execution contexts, see [Contract Communication](../../concepts/foundation/communication/main). +For more detail on execution contexts, see [Contract Communication](../../learn/concepts/communication/main). ### Types files @@ -234,7 +235,7 @@ Below the dependencies, paste the following Storage struct: Reading through the storage variables: -- `admin` a single Field value stored in public state. `FIELD_SERIALIZED_LEN` indicates the length of the variable, which is 1 in this case because it's a single Field element. A `Field` is basically an unsigned integer with a maximum value determined by the underlying cryptographic curve. +- `admin` a single Field value stored in public state. A `Field` is basically an unsigned integer with a maximum value determined by the underlying cryptographic curve. - `minters` is a mapping of Fields in public state. This will store whether an account is an approved minter on the contract. - `balances` is a mapping of private balances. Private balances are stored in a `Set` of `ValueNote`s. The balance is the sum of all of an account's `ValueNote`s. - `total_supply` is a Field value stored in public state and represents the total number of tokens minted. @@ -247,8 +248,6 @@ You can read more about it [here](../contracts/syntax/storage/main.md). Once we have Storage defined, we need to specify how to initialize it. The `init` method creates and initializes an instance of `Storage`. We define an initialization method for each of the storage variables defined above. Storage initialization is generic and can largely be reused for similar types, across different contracts, but it is important to note that each storage variable specifies it's storage slot, starting at 1. -Also, the public storage variables define the type that they store by passing the methods by which they are serialized. Because all `PublicState` in this contract is storing Field elements, each storage variable takes `FieldSerializationMethods`. - #include_code storage_init /yarn-project/noir-contracts/contracts/token_contract/src/main.nr rust ## Functions diff --git a/docs/docs/dev_docs/updating.md b/docs/docs/developers/updating.md similarity index 96% rename from docs/docs/dev_docs/updating.md rename to docs/docs/developers/updating.md index e9f0b54abcc..6e9a3b47d57 100644 --- a/docs/docs/dev_docs/updating.md +++ b/docs/docs/developers/updating.md @@ -19,7 +19,7 @@ cd your/aztec/project aztec-cli update . --contract src/contract1 --contract src/contract2 ``` -The sandbox must be running for the update command to work. Make sure it is [installed and running](../dev_docs/cli/sandbox-reference.md). +The sandbox must be running for the update command to work. Make sure it is [installed and running](../developers/cli/sandbox-reference.md). 3. Refer [Migration Notes](../misc/migration_notes.md) on any breaking changes that might affect your dapp diff --git a/docs/docs/dev_docs/wallets/architecture.md b/docs/docs/developers/wallets/architecture.md similarity index 80% rename from docs/docs/dev_docs/wallets/architecture.md rename to docs/docs/developers/wallets/architecture.md index 7785d43e41b..3d010fd3267 100644 --- a/docs/docs/dev_docs/wallets/architecture.md +++ b/docs/docs/developers/wallets/architecture.md @@ -8,7 +8,7 @@ Architecture-wise, a wallet is an instance of an **Private Execution Environment The PXE also communicates with an **Aztec Node** for retrieving public information or broadcasting transactions. Note that the PXE requires a local database for keeping private state, and is also expected to be continuously syncing new blocks for trial-decryption of user notes. -Additionally, a wallet must be able to handle one or more [account contract implementations](../../concepts/foundation/accounts/main.md#account-contracts-and-wallets). When a user creates a new account, the account is represented on-chain by an account contract. The wallet is responsible for deploying and interacting with this contract. A wallet may support multiple flavours of accounts, such as an account that uses ECDSA signatures, or one that relies on WebAuthn, or one that requires multi-factor authentication. For a user, the choice of what account implementation to use is then determined by the wallet they interact with. +Additionally, a wallet must be able to handle one or more [account contract implementations](../../learn/concepts/accounts/main.md#account-contracts-and-wallets). When a user creates a new account, the account is represented on-chain by an account contract. The wallet is responsible for deploying and interacting with this contract. A wallet may support multiple flavours of accounts, such as an account that uses ECDSA signatures, or one that relies on WebAuthn, or one that requires multi-factor authentication. For a user, the choice of what account implementation to use is then determined by the wallet they interact with. In code, this translates to a wallet implementing an **AccountInterface** interface that defines [how to create an _execution request_ out of an array of _function calls_](./main.md#transaction-lifecycle) for the specific implementation of an account contract and [how to generate an _auth witness_](./main.md#authorizing-actions) for authorizing actions on behalf of the user. Think of this interface as the Javascript counterpart of an account contract, or the piece of code that knows how to format a transaction and authenticate an action based on the rules defined by the user's account contract implementation. diff --git a/docs/docs/dev_docs/wallets/creating_schnorr_accounts.md b/docs/docs/developers/wallets/creating_schnorr_accounts.md similarity index 96% rename from docs/docs/dev_docs/wallets/creating_schnorr_accounts.md rename to docs/docs/developers/wallets/creating_schnorr_accounts.md index d4dfa04e2c3..636bf8df4af 100644 --- a/docs/docs/dev_docs/wallets/creating_schnorr_accounts.md +++ b/docs/docs/developers/wallets/creating_schnorr_accounts.md @@ -6,7 +6,7 @@ title: Creating Schnorr Accounts This section shows how to create schnorr account wallets on the Aztec Sandbox. -An in-depth explaining about accounts on aztec can be found [here](../../concepts/foundation/accounts/main.md). But creating an account on the Sandbox does 2 things: +An in-depth explaining about accounts on aztec can be found [here](../../learn/concepts/accounts/main.md). But creating an account on the Sandbox does 2 things: 1. Deploys an account contract -- representing you -- allowing you to perform actions on the network (deploy contracts, call functions etc). 2. Adds your encryption keys to the Private eXecution Environment (PXE) allowing it to decrypt and manage your private state. diff --git a/docs/docs/dev_docs/wallets/main.md b/docs/docs/developers/wallets/main.md similarity index 75% rename from docs/docs/dev_docs/wallets/main.md rename to docs/docs/developers/wallets/main.md index 898acb41c55..6e458d8241d 100644 --- a/docs/docs/dev_docs/wallets/main.md +++ b/docs/docs/developers/wallets/main.md @@ -10,17 +10,17 @@ In this page we will cover the main responsibilities of a wallet in the Aztec ne ## Account setup -The first step for any wallet is to let the user set up their [accounts](../../concepts/foundation/accounts/main.md). An account in Aztec is represented on-chain by its corresponding account contract that the user must deploy to begin interacting with the network. This account contract dictates how transactions are authenticated and executed. +The first step for any wallet is to let the user set up their [accounts](../../learn/concepts/accounts/main.md). An account in Aztec is represented on-chain by its corresponding account contract that the user must deploy to begin interacting with the network. This account contract dictates how transactions are authenticated and executed. A wallet must support at least one specific [account contract implementation](./writing_an_account_contract.md), which means being able to deploy such a contract, as well as interacting with it when sending transactions. Code-wise, this requires [implementing the `AccountContract` interface](https://github.com/AztecProtocol/aztec-packages/blob/master/yarn-project/aztec.js/src/account_contract/index.ts). -Note that users must be able to receive funds in Aztec before deploying their account. A wallet should let a user generate a [deterministic complete address](../../concepts/foundation/accounts/keys.md#addresses-partial-addresses-and-public-keys) without having to interact with the network, so they can share it with others to receive funds. This requires that the wallet pins a specific contract implementation, its initialization arguments, a deployment salt, and a privacy key. These values yield a deterministic address, so when the account contract is actually deployed, it is available at the precalculated address. Once the account contract is deployed, the user can start sending transactions using it as the transaction origin. +Note that users must be able to receive funds in Aztec before deploying their account. A wallet should let a user generate a [deterministic complete address](../../learn/concepts/accounts/keys.md#addresses-partial-addresses-and-public-keys) without having to interact with the network, so they can share it with others to receive funds. This requires that the wallet pins a specific contract implementation, its initialization arguments, a deployment salt, and a privacy key. These values yield a deterministic address, so when the account contract is actually deployed, it is available at the precalculated address. Once the account contract is deployed, the user can start sending transactions using it as the transaction origin. ## Transaction lifecycle Every transaction in Aztec is broadcast to the network as a zero-knowledge proof of correct execution, in order to preserve privacy. This means that transaction proofs are generated on the wallet and not on a remote node. This is one of the biggest differences with regard to EVM chain wallets. -A wallet is responsible for **creating** an [_execution request_](https://github.com/AztecProtocol/aztec-packages/blob/master/yarn-project/types/src/tx_execution_request.ts) out of one or more [_function calls_](https://github.com/AztecProtocol/aztec-packages/blob/master/yarn-project/types/src/function_call.ts) requested by a dapp. For example, a dapp may request a wallet to "invoke the `transfer` function on the contract at `0x1234` with the following arguments", in response to a user action. The wallet [turns that into an execution request](../../concepts/foundation/accounts/main.md#execution-requests) with the signed instructions to execute that function call from the user's account contract. In an [ECDSA-based account](https://github.com/AztecProtocol/aztec-packages/blob/master/yarn-project/noir-contracts/contracts/ecdsa_account_contract/src/main.nr), for instance, this is an execution request that encodes the function call in the _entrypoint payload_, and includes its ECDSA signature with the account's signing private key. +A wallet is responsible for **creating** an [_execution request_](https://github.com/AztecProtocol/aztec-packages/blob/master/yarn-project/types/src/tx_execution_request.ts) out of one or more [_function calls_](https://github.com/AztecProtocol/aztec-packages/blob/master/yarn-project/types/src/function_call.ts) requested by a dapp. For example, a dapp may request a wallet to "invoke the `transfer` function on the contract at `0x1234` with the following arguments", in response to a user action. The wallet [turns that into an execution request](../../learn/concepts/accounts/main.md#execution-requests) with the signed instructions to execute that function call from the user's account contract. In an [ECDSA-based account](https://github.com/AztecProtocol/aztec-packages/blob/master/yarn-project/noir-contracts/contracts/ecdsa_account_contract/src/main.nr), for instance, this is an execution request that encodes the function call in the _entrypoint payload_, and includes its ECDSA signature with the account's signing private key. Once the _execution request_ is created, the wallet is responsible for **simulating** and **proving** the execution of its private functions. The simulation yields an execution trace, which can be used to provide the user with a list of side effects of the private execution of the transaction. During this simulation, the wallet is responsible of providing data to the virtual machine, such as private notes, encryption keys, or nullifier secrets. This execution trace is fed into the prover, which returns a zero-knowledge proof that guarantees correct execution and hides all private information. The output of this process is a [_transaction_](https://github.com/AztecProtocol/aztec-packages/blob/master/yarn-project/types/src/tx/tx.ts) object. @@ -36,20 +36,20 @@ There are no proofs generated as of the Sandbox release. This will be included i ## Authorizing actions -Account contracts in Aztec expose an interface for other contracts to validate [whether an action is authorized by the account or not](../../concepts/foundation/accounts/main.md#authorizing-actions). For example, an application contract may want to transfer tokens on behalf of a user, in which case the token contract will check with the account contract whether the application is authorized to do so. These actions may be carried out in private or in public functions, and in transactions originated by the user or by someone else. +Account contracts in Aztec expose an interface for other contracts to validate [whether an action is authorized by the account or not](../../learn/concepts/accounts/main.md#authorizing-actions). For example, an application contract may want to transfer tokens on behalf of a user, in which case the token contract will check with the account contract whether the application is authorized to do so. These actions may be carried out in private or in public functions, and in transactions originated by the user or by someone else. Wallets should manage these authorizations, prompting the user when they are requested by an application. Authorizations in private executions come in the form of _auth witnesses_, which are usually signatures over an identifier for an action. Applications can request the wallet to produce an auth witness via the `createAuthWitness` call. In public functions, authorizations are pre-stored in the account contract storage, which is handled by a call to an internal function in the account contract implementation. ## Key management -As in EVM-based chains, wallets are expected to manage user keys, or provide an interface to hardware wallets or alternative key stores. Keep in mind that in Aztec each account requires [two sets of keys](../../concepts/foundation/accounts/keys.md): privacy keys and authentication keys. Privacy keys are mandated by the protocol and used for encryption and nullification, whereas authentication keys are dependent on the account contract implementation rolled out by the wallet. Should the account contract support it, wallets must provide the user with the means to rotate or recover their authentication keys. +As in EVM-based chains, wallets are expected to manage user keys, or provide an interface to hardware wallets or alternative key stores. Keep in mind that in Aztec each account requires [two sets of keys](../../learn/concepts/accounts/keys.md): privacy keys and authentication keys. Privacy keys are mandated by the protocol and used for encryption and nullification, whereas authentication keys are dependent on the account contract implementation rolled out by the wallet. Should the account contract support it, wallets must provide the user with the means to rotate or recover their authentication keys. :::info Due to limitations in the current architecture, privacy keys need to be available in the wallet software itself and cannot be punted to an external keystore. This restriction may be lifted in a future release. ::: ## Recipient encryption keys -Wallets are also expected to manage the public encryption keys of any recipients of local transactions. When creating an encrypted note for a recipient given their address, the wallet needs to provide their [complete address](../../concepts/foundation/accounts/keys.md#addresses-partial-addresses-and-public-keys). Recipients broadcast their complete addresses when deploying their account contracts, and wallets collect this information and save it in a local registry for easy access when needed. +Wallets are also expected to manage the public encryption keys of any recipients of local transactions. When creating an encrypted note for a recipient given their address, the wallet needs to provide their [complete address](../../learn/concepts/accounts/keys.md#addresses-partial-addresses-and-public-keys). Recipients broadcast their complete addresses when deploying their account contracts, and wallets collect this information and save it in a local registry for easy access when needed. Note that, in order to interact with a recipient who has not yet deployed their account contract (and thus not broadcasted their complete address), it must also be possible to manually add an entry to a wallet's local registry of complete addresses. diff --git a/docs/docs/dev_docs/wallets/writing_an_account_contract.md b/docs/docs/developers/wallets/writing_an_account_contract.md similarity index 88% rename from docs/docs/dev_docs/wallets/writing_an_account_contract.md rename to docs/docs/developers/wallets/writing_an_account_contract.md index 058a02fb349..fdc46499755 100644 --- a/docs/docs/dev_docs/wallets/writing_an_account_contract.md +++ b/docs/docs/developers/wallets/writing_an_account_contract.md @@ -11,15 +11,15 @@ You will learn: - Typescript glue code to format and authenticate transactions - Deploying and testing the account contract -Writing your own account contract allows you to define the rules by which user transactions are authorized and paid for, as well as how user keys are managed (including key rotation and recovery). In other words, writing an account contract lets you make the most out of [account abstraction](../../concepts/foundation/accounts/main.md#what-is-account-abstraction) in the Aztec network. +Writing your own account contract allows you to define the rules by which user transactions are authorized and paid for, as well as how user keys are managed (including key rotation and recovery). In other words, writing an account contract lets you make the most out of [account abstraction](../../learn/concepts/accounts/main.md#what-is-account-abstraction) in the Aztec network. -It is highly recommended that you understand how an [account](../../concepts/foundation/accounts/main.md) is defined in Aztec, as well as the differences between privacy and authentication [keys](../../concepts/foundation/accounts/keys.md). You will also need to know how to write a [contract in Noir](../contracts/main.md), as well as some basic [Typescript](https://www.typescriptlang.org/). +It is highly recommended that you understand how an [account](../../learn/concepts/accounts/main.md) is defined in Aztec, as well as the differences between privacy and authentication [keys](../../learn/concepts/accounts/keys.md). You will also need to know how to write a [contract in Noir](../contracts/main.md), as well as some basic [Typescript](https://www.typescriptlang.org/). For this tutorial, we will write an account contract that uses Schnorr signatures for authenticating transaction requests. > That is, every time a transaction payload is passed to this account contract's 'entrypoint' function, the account contract will demand a valid Schnorr signature, whose signed message matches the transaction payload, and whose signer matches the account contract owner's public key. If the signature fails, the transaction will fail. -For the sake of simplicity, we will hardcode the signing public key into the contract, but you could store it [in a private note](../../concepts/foundation/accounts/keys.md#using-a-private-note), [in an immutable note](../../concepts/foundation/accounts/keys.md#using-an-immutable-private-note), or [on a separate keystore](../../concepts/foundation/accounts/keys.md#using-a-separate-keystore), to mention a few examples. +For the sake of simplicity, we will hardcode the signing public key into the contract, but you could store it [in a private note](../../learn/concepts/accounts/keys.md#using-a-private-note), [in an immutable note](../../learn/concepts/accounts/keys.md#using-an-immutable-private-note), or [on a separate keystore](../../learn/concepts/accounts/keys.md#using-a-separate-keystore), to mention a few examples. ## The account contract @@ -83,7 +83,7 @@ More signing schemes are available in case you want to experiment with other typ Let's try creating a new account backed by our account contract, and interact with a simple token contract to test it works. -To create and deploy the account, we will use the `AccountManager` class, which takes an instance of an Private Execution Environment (PXE), a [privacy private key](../../concepts/foundation/accounts/keys.md#privacy-keys), and an instance of our `AccountContract` class: +To create and deploy the account, we will use the `AccountManager` class, which takes an instance of an Private Execution Environment (PXE), a [privacy private key](../../learn/concepts/accounts/keys.md#privacy-keys), and an instance of our `AccountContract` class: #include_code account-contract-deploy yarn-project/end-to-end/src/guides/writing_an_account_contract.test.ts typescript diff --git a/docs/docs/concepts/foundation/main.md b/docs/docs/learn/about_aztec/technical_overview.md similarity index 74% rename from docs/docs/concepts/foundation/main.md rename to docs/docs/learn/about_aztec/technical_overview.md index 6c861dfe12d..7192e34d316 100644 --- a/docs/docs/concepts/foundation/main.md +++ b/docs/docs/learn/about_aztec/technical_overview.md @@ -1,5 +1,5 @@ --- -title: Foundational Concepts +title: Core Components --- Aztec Labs is building a layer 2 rollup on Ethereum focused on 3 things: @@ -36,13 +36,13 @@ An overview of the Aztec network architecture will help contextualize the concep ### Aztec.js -A user of the Aztec network will interact with the network through Aztec.js. Aztec.js is a library that provides APIs for managing accounts and interacting with smart contracts (including account contracts) on the Aztec network. It communicates with the [Private eXecution Environment (PXE)](../../apis/pxe/interfaces/PXE) through a `PXE` implementation, allowing developers to easily register new accounts, deploy contracts, view functions, and send transactions. +A user of the Aztec network will interact with the network through Aztec.js. Aztec.js is a library that provides APIs for managing accounts and interacting with smart contracts (including account contracts) on the Aztec network. It communicates with the [Private eXecution Environment (PXE)](../../apis/pxe/index.md) through a `PXE` implementation, allowing developers to easily register new accounts, deploy contracts, view functions, and send transactions. ### Private Execution Environment -The PXE provides a secure environment for the execution of sensitive operations, ensuring private information and decrypted data are not accessible to unauthorized applications. It hides the details of the [state model](./state_model/main.md) from end users, but the state model is important for Aztec developers to understand as it has implications for [private/public execution](./communication/public_private_calls/main.md) and [L1/L2 communication](./communication/cross_chain_calls.md). The PXE also includes the [ACIR Simulator](../advanced/acir_simulator.md) for private executions and the KeyStore for secure key management. +The PXE provides a secure environment for the execution of sensitive operations, ensuring private information and decrypted data are not accessible to unauthorized applications. It hides the details of the [state model](../concepts/hybrid_state/main.md) from end users, but the state model is important for Aztec developers to understand as it has implications for [private/public execution](../concepts/communication/public_private_calls/main.md) and [L1/L2 communication](../concepts/communication/cross_chain_calls.md). The PXE also includes the [ACIR Simulator](../concepts/pxe/acir_simulator.md) for private executions and the KeyStore for secure key management. -Procedurally, the PXE sends results of private function execution and requests for public function executions to the [sequencer](./nodes_clients/sequencer.md), which will update the state of the rollup. +Procedurally, the PXE sends results of private function execution and requests for public function executions to the [sequencer](../concepts/nodes_clients/sequencer/main.md), which will update the state of the rollup. ### Sequencer @@ -50,8 +50,8 @@ The sequencer aggregates transactions into a block, generates proofs of the stat ## Further Reading -- [The state model](./state_model/main.md) -- [Accounts](./accounts/main.md) -- [Aztec Smart Contracts](./contracts.md) -- [Transactions](./transactions.md) -- [Communication between network components](./communication/main.md) +- [The state model](../concepts/hybrid_state/main.md) +- [Accounts](../concepts/accounts/main.md) +- [Aztec Smart Contracts](../concepts/smart_contracts/main.md) +- [Transactions](../concepts/transactions.md) +- [Communication between network components](../concepts/communication/main.md) diff --git a/docs/docs/about_aztec/vision.md b/docs/docs/learn/about_aztec/vision.md similarity index 96% rename from docs/docs/about_aztec/vision.md rename to docs/docs/learn/about_aztec/vision.md index 505c29794f5..f29b308f8d3 100644 --- a/docs/docs/about_aztec/vision.md +++ b/docs/docs/learn/about_aztec/vision.md @@ -22,5 +22,5 @@ We are building the [Aztec Network](https://aztec.network/), a fully programmabl To achieve these goals, we are pioneering the cryptography and research needed to bring our next generation, privacy-preserving zk-roll-up to mainnet. -import Disclaimer from "../misc/common/\_disclaimer.mdx"; +import Disclaimer from "../../misc/common/\_disclaimer.mdx"; diff --git a/docs/docs/about_aztec/overview.mdx b/docs/docs/learn/about_aztec/what_is_aztec.mdx similarity index 95% rename from docs/docs/about_aztec/overview.mdx rename to docs/docs/learn/about_aztec/what_is_aztec.mdx index c2e3d1b97a3..76080a98c4a 100644 --- a/docs/docs/about_aztec/overview.mdx +++ b/docs/docs/learn/about_aztec/what_is_aztec.mdx @@ -1,5 +1,5 @@ --- -title: Overview +title: What is Aztec? --- import ReactPlayer from "react-player/youtube"; @@ -50,7 +50,7 @@ Contributors to Aztec uphold many of the values of the Ethereum community -- bui Noir is a domain specific programming language for writing zero-knowledge circuits. On Aztec a smart contract is a collection of circuits that developers write using Noir. -You can find more information and resources for learning about Noir smart contracts on [this page](../dev_docs/contracts/main.md). +You can find more information and resources for learning about Noir smart contracts on [this page](../../developers/contracts/main.md). ## Cryptography @@ -60,5 +60,5 @@ To support Aztec's rollup, our cryptography team is building [Honk](https://gith Keep up with the latest discussion and join the conversation in the [Aztec forum](https://discourse.aztec.network) or [Discord server](https://discord.gg/DgWG2DBMyB). -import Disclaimer from "../misc/common/_disclaimer.mdx"; +import Disclaimer from "../../misc/common/_disclaimer.mdx"; ; diff --git a/docs/docs/concepts/foundation/accounts/authwit.md b/docs/docs/learn/concepts/accounts/authwit.md similarity index 95% rename from docs/docs/concepts/foundation/accounts/authwit.md rename to docs/docs/learn/concepts/accounts/authwit.md index ac26bf13ba1..31fa51e1efa 100644 --- a/docs/docs/concepts/foundation/accounts/authwit.md +++ b/docs/docs/learn/concepts/accounts/authwit.md @@ -54,7 +54,7 @@ All of these issues have been discussed in the community for a while, and there Adopting ERC20 for Aztec is not as simple as it might seem because of private state. -If you recall from [State model](./../state_model/main.md), private state is generally only known by its owner and those they have shared it with. Because it relies on secrets, private state might be "owned" by a contract, but it needs someone with knowledge of these secrets to actually spend it. You might see where this is going. +If you recall from the [Hybrid State model](../hybrid_state/main.md), private state is generally only known by its owner and those they have shared it with. Because it relies on secrets, private state might be "owned" by a contract, but it needs someone with knowledge of these secrets to actually spend it. You might see where this is going. If we were to implement the `approve` with an allowance in private, you might know the allowance, but unless you also know about the individual notes that make up the user's balances, it would be of no use to you! It is private after all. To spend the user's funds you would need to know the decryption key, see [keys for more](../accounts/keys.md). @@ -182,7 +182,7 @@ For the transfer, this could be done simply by appending a nonce to the argument action = H(defi, token, transfer_selector, H(alice_account, defi, 1000, nonce)); ``` -Beware that the account contract will be unable to emit the nullifier since it is checked with a static call, so the calling contract must do it. This is similar to nonces in ERC20 tokens today. We provide a small library that handles this which we will see in the [developer documentation](./../../../dev_docs/contracts/resources/common_patterns/authwit.md). +Beware that the account contract will be unable to emit the nullifier since it is checked with a static call, so the calling contract must do it. This is similar to nonces in ERC20 tokens today. We provide a small library that handles this which we will see in the [developer documentation](./../../../developers/contracts/resources/common_patterns/authwit.md). ### Differences to approval @@ -196,4 +196,4 @@ We don't need to limit ourselves to the `transfer` function, we can use the same ### Next Steps -Check out the [developer documentation](./../../../dev_docs/contracts/resources/common_patterns/authwit.md) to see how to implement this in your own contracts. +Check out the [developer documentation](./../../../developers/contracts/resources/common_patterns/authwit.md) to see how to implement this in your own contracts. diff --git a/docs/docs/concepts/foundation/accounts/keys.md b/docs/docs/learn/concepts/accounts/keys.md similarity index 98% rename from docs/docs/concepts/foundation/accounts/keys.md rename to docs/docs/learn/concepts/accounts/keys.md index e5d897d0ffc..5802996cf5e 100644 --- a/docs/docs/concepts/foundation/accounts/keys.md +++ b/docs/docs/learn/concepts/accounts/keys.md @@ -14,7 +14,7 @@ This is a snippet of our Schnorr Account contract implementation, which uses Sch #include_code entrypoint /yarn-project/noir-contracts/contracts/schnorr_account_contract/src/main.nr rust -Still, different accounts may use different signing schemes, may require multi-factor authentication, or _may not even use signing keys_ and instead rely on other authentication mechanisms. Read [how to write an account contract](../../../dev_docs/wallets/writing_an_account_contract.md) for a full example of how to manage authentication. +Still, different accounts may use different signing schemes, may require multi-factor authentication, or _may not even use signing keys_ and instead rely on other authentication mechanisms. Read [how to write an account contract](../../../developers/wallets/writing_an_account_contract.md) for a full example of how to manage authentication. Furthermore, and since signatures are fully abstracted, how the key is stored in the contract is abstracted as well and left to the developer of the account contract. Here are a few ideas on how to store them, each with their pros and cons. diff --git a/docs/docs/concepts/foundation/accounts/main.md b/docs/docs/learn/concepts/accounts/main.md similarity index 99% rename from docs/docs/concepts/foundation/accounts/main.md rename to docs/docs/learn/concepts/accounts/main.md index c96238632be..3ad58265637 100644 --- a/docs/docs/concepts/foundation/accounts/main.md +++ b/docs/docs/learn/concepts/accounts/main.md @@ -68,7 +68,7 @@ def entryPoint(payload): enqueueCall(to, data, value, gasLimit); ``` -Read more about how to write an account contract [here](../../../dev_docs/wallets/writing_an_account_contract.md). +Read more about how to write an account contract [here](../../../developers/wallets/writing_an_account_contract.md). ### Account contracts and wallets diff --git a/docs/docs/concepts/foundation/block_production.md b/docs/docs/learn/concepts/block_production.md similarity index 100% rename from docs/docs/concepts/foundation/block_production.md rename to docs/docs/learn/concepts/block_production.md diff --git a/docs/docs/concepts/foundation/blocks.md b/docs/docs/learn/concepts/blocks.md similarity index 100% rename from docs/docs/concepts/foundation/blocks.md rename to docs/docs/learn/concepts/blocks.md diff --git a/docs/docs/concepts/advanced/circuits/kernels/private_kernel.md b/docs/docs/learn/concepts/circuits/kernels/private_kernel.md similarity index 93% rename from docs/docs/concepts/advanced/circuits/kernels/private_kernel.md rename to docs/docs/learn/concepts/circuits/kernels/private_kernel.md index cc0632c986f..1fccebbfa83 100644 --- a/docs/docs/concepts/advanced/circuits/kernels/private_kernel.md +++ b/docs/docs/learn/concepts/circuits/kernels/private_kernel.md @@ -16,10 +16,10 @@ This circuit is executed by the user, on their own device. This is to ensure pri - public call stacks: hashes representing calls to other public functions; - events; - all data accumulated by all previous private kernel circuit recursions of this tx; -- Hides which private function has been executed, by performing a zero-knowledge proof of membership against the [contract tree](../../data_structures/trees). +- Hides which private function has been executed, by performing a zero-knowledge proof of membership against the [contract tree](../../storage/trees/main.md#contract-tree). - Ensures the entire stack trace of private functions (for a particular tx) adheres to function execution rules. - Verifies a previous 'Private Kernel Proof', recursively, when verifying transactions which are composed of many private function calls. -- Optionally can [deploy](../../contract_creation) a new private contract. +- Optionally can [deploy](../../smart_contracts/contract_creation.md) a new private contract. > Note: **This is the only core protocol circuit which actually needs to be "zk" (zero-knowledge)!!!** That's because this is the only core protocol circuit which handles private data, and hence the only circuit for which proofs must not leak any information about witnesses! (The private data being handled includes: details of the Aztec.nr Contract function which has been executed; the address of the user who executed the function; the intelligible inputs and outputs of that function). > This is a really interesting point. Most so-called "zk-Rollups" do not make use of this "zero-knowledge" property. Their snarks are "snarks"; with no need for zero-knowledge, because they don't seek privacy; they only seek the 'succinct' computation-compression properties of snarks. Aztec's "zk-Rollup" actually makes use of "zero-knowledge" snarks. That's why we sometimes call it a "zk-zk-Rollup", or "_actual_ zk-Rollup". diff --git a/docs/docs/concepts/advanced/circuits/kernels/public_kernel.md b/docs/docs/learn/concepts/circuits/kernels/public_kernel.md similarity index 82% rename from docs/docs/concepts/advanced/circuits/kernels/public_kernel.md rename to docs/docs/learn/concepts/circuits/kernels/public_kernel.md index 0bd2c0b7870..e9454ceb545 100644 --- a/docs/docs/concepts/advanced/circuits/kernels/public_kernel.md +++ b/docs/docs/learn/concepts/circuits/kernels/public_kernel.md @@ -2,7 +2,7 @@ title: Public Kernel Circuit --- -This circuit is executed by a Sequencer, since only a Sequencer knows the current state of the [public data tree](../../data_structures/trees) at any time. A Sequencer might choose to delegate proof generation to the Prover pool. +This circuit is executed by a Sequencer, since only a Sequencer knows the current state of the [public data tree](../../storage/trees/main.md#public-state-tree) at any time. A Sequencer might choose to delegate proof generation to the Prover pool. - Exposes (forwards) the following data to the next recursive circuit: - all data accumulated by all previous private kernel circuit recursions of this tx; diff --git a/docs/docs/concepts/advanced/circuits/main.md b/docs/docs/learn/concepts/circuits/main.md similarity index 98% rename from docs/docs/concepts/advanced/circuits/main.md rename to docs/docs/learn/concepts/circuits/main.md index 9a84f9cf60b..8db42f4e153 100644 --- a/docs/docs/concepts/advanced/circuits/main.md +++ b/docs/docs/learn/concepts/circuits/main.md @@ -51,7 +51,7 @@ In other words, since neither the EVM nor other rollups have rules for how to pr What kind of extra rules / checks does a rollup need, to enforce notions of private states and private functions? Stuff like: -- "Perform state reads and writes using new tree structures which prevent tx linkability" (see [trees](../data_structures/trees)). +- "Perform state reads and writes using new tree structures which prevent tx linkability" (see [trees](../storage/trees/main.md)). - "Hide which function was just executed, by wrapping it in a zk-snark" - "Hide all functions which were executed as part of this tx's stack trace, by wrapping the whole tx in a zk-snark" diff --git a/docs/docs/concepts/advanced/circuits/rollup_circuits/main.md b/docs/docs/learn/concepts/circuits/rollup_circuits/main.md similarity index 96% rename from docs/docs/concepts/advanced/circuits/rollup_circuits/main.md rename to docs/docs/learn/concepts/circuits/rollup_circuits/main.md index 6fee1408755..d5ac8be56c8 100644 --- a/docs/docs/concepts/advanced/circuits/rollup_circuits/main.md +++ b/docs/docs/learn/concepts/circuits/rollup_circuits/main.md @@ -23,7 +23,7 @@ For both transactions, it: - Performs public state read membership checks. - Updates the public data tree in line with the requested state transitions. -- Checks that the nullifiers haven't previously been inserted into the [indexed nullifier tree](../../data_structures/indexed_merkle_tree). +- Checks that the nullifiers haven't previously been inserted into the [indexed nullifier tree](../../storage/trees/indexed_merkle_tree.md#primer-on-nullifier-trees). - Batch-inserts new nullifiers into the nullifier tree. - Batch-inserts new commitments into the note hash tree - Batch-inserts any new contract deployments into the contract tree. diff --git a/docs/docs/concepts/foundation/communication/cross_chain_calls.md b/docs/docs/learn/concepts/communication/cross_chain_calls.md similarity index 100% rename from docs/docs/concepts/foundation/communication/cross_chain_calls.md rename to docs/docs/learn/concepts/communication/cross_chain_calls.md diff --git a/docs/docs/concepts/foundation/communication/main.md b/docs/docs/learn/concepts/communication/main.md similarity index 100% rename from docs/docs/concepts/foundation/communication/main.md rename to docs/docs/learn/concepts/communication/main.md diff --git a/docs/docs/concepts/foundation/communication/public_private_calls/main.md b/docs/docs/learn/concepts/communication/public_private_calls/main.md similarity index 99% rename from docs/docs/concepts/foundation/communication/public_private_calls/main.md rename to docs/docs/learn/concepts/communication/public_private_calls/main.md index 34a98047f4f..15ae6df1c8b 100644 --- a/docs/docs/concepts/foundation/communication/public_private_calls/main.md +++ b/docs/docs/learn/concepts/communication/public_private_calls/main.md @@ -1,5 +1,5 @@ --- -title: Private <--> Public execution +title: Private - Public execution --- import Image from "@theme/IdealImage"; diff --git a/docs/docs/concepts/foundation/communication/public_private_calls/slow_updates_tree.md b/docs/docs/learn/concepts/communication/public_private_calls/slow_updates_tree.md similarity index 97% rename from docs/docs/concepts/foundation/communication/public_private_calls/slow_updates_tree.md rename to docs/docs/learn/concepts/communication/public_private_calls/slow_updates_tree.md index c6ceb070e26..a7459932f07 100644 --- a/docs/docs/concepts/foundation/communication/public_private_calls/slow_updates_tree.md +++ b/docs/docs/learn/concepts/communication/public_private_calls/slow_updates_tree.md @@ -70,4 +70,4 @@ Developers are used to instant state updates, so the Slow Updates Tree might tak ## Dive into the code -For a code walkthrough of how a token blacklist contract can use a slow updates tree, read [this](../../../../dev_docs/contracts/syntax/slow_updates_tree.md). \ No newline at end of file +For a code walkthrough of how a token blacklist contract can use a slow updates tree, read [this](../../../../developers/contracts/syntax/slow_updates_tree.md). \ No newline at end of file diff --git a/docs/docs/concepts/foundation/globals.md b/docs/docs/learn/concepts/globals.md similarity index 100% rename from docs/docs/concepts/foundation/globals.md rename to docs/docs/learn/concepts/globals.md diff --git a/docs/docs/concepts/foundation/state_model/main.md b/docs/docs/learn/concepts/hybrid_state/main.md similarity index 74% rename from docs/docs/concepts/foundation/state_model/main.md rename to docs/docs/learn/concepts/hybrid_state/main.md index 36ca4515c3f..2c540f2a077 100644 --- a/docs/docs/concepts/foundation/state_model/main.md +++ b/docs/docs/learn/concepts/hybrid_state/main.md @@ -12,18 +12,6 @@ Internal to the Aztec network, public state is stored and updated by the sequenc ## Private State -Every smart contract needs a way to track information over time - that's what state is. In order to have both private and public transactions and storage in Aztec, we have to have public and private state. - -On this page, you’ll learn - -- Aztec's unique interpretation of private state -- Representation of private state in an append-only database -- Concept of 'deleting' private state variables using nullifiers -- How to modify private state -- How Aztec abstracts the UTXO model from developers - -## Private State on Aztec - Private state must be treated differently from public state and this must be expressed in the semantics of Aztec.nr. Private state is encrypted and therefore is "owned" by a user or a set of users (via shared secrets) that are able to decrypt the state. @@ -32,7 +20,7 @@ Private state is represented in an append-only database since updating a record The act of "deleting" a private state variable can be represented by adding an associated nullifier to a nullifier set. The nullifier is generated such that, without knowing the decryption key of the owner, an observer cannot link a state record with a nullifier. -Modification of state variables can be emulated by nullifying the a state record and creating a new record to represent the variable. Private state has an intrinsic UTXO structure and this must be represented in the language semantics of manipulating private state. +Modification of state variables can be emulated by nullifying the state record and creating a new record to represent the variable. Private state has an intrinsic UTXO structure and this must be represented in the language semantics of manipulating private state. ### Abstracting UTXO's from App's / Users @@ -55,4 +43,4 @@ This is achieved with two main features: ## Further reading -Read more about how to leverage the Aztec state model in Aztec contracts [here](../../../dev_docs/contracts/syntax/storage/main.md). +Read more about how to leverage the Aztec state model in Aztec contracts [here](../../../developers/contracts/syntax/storage/main.md). diff --git a/docs/docs/concepts/advanced/public_vm.md b/docs/docs/learn/concepts/hybrid_state/public_vm.md similarity index 100% rename from docs/docs/concepts/advanced/public_vm.md rename to docs/docs/learn/concepts/hybrid_state/public_vm.md diff --git a/docs/docs/learn/concepts/main.md b/docs/docs/learn/concepts/main.md new file mode 100644 index 00000000000..0a8c74b2bc0 --- /dev/null +++ b/docs/docs/learn/concepts/main.md @@ -0,0 +1,75 @@ +--- +title: Concepts +--- + +import Image from '@theme/IdealImage'; +import DocCardList from '@theme/DocCardList'; + +This page outlines Aztec's fundamental technical concepts. + +## Aztec Overview + + + +1. A user interacts with Aztec through Aztec.js (like web3js or ethersjs) or Aztec CLI +2. Private functions are executed in the PXE, which is client-side +3. They are rolled up and sent to the Public VM (running on an Aztec node) +4. Public functions are executed in the Public VM +5. The Public VM rolls up the private & public transaction rollups +6. These rollups are submitted to Ethereum + +The PXE is unaware of the Public VM. And the Public VM is unaware of the PXE. They are completely separate execution environments. This means: + +- The PXE and the Public VM cannot directly communicate with each other +- Private transactions in the PXE are executed first, followed by public transactions + +### Private and public state + +Private state works with UTXOs, or what we call notes. To keep things private, everything is stored in an [append-only UTXO tree](../../learn/concepts/storage/trees/main.md#note-hash-tree), and a nullifier is created when notes are invalidated. Nullifiers are then stored in their own [nullifier tree](./storage/trees/indexed_merkle_tree.md#primer-on-nullifier-trees). + +Public state works similarly to other chains like Ethereum, behaving like a public ledger. Public data is stored in a [public data tree](./storage/trees/main.md#public-state-tree). + +Aztec [smart contract](../concepts/smart_contracts/main.md) developers should keep in mind that different types are used when manipulating private or public state. Working with private state is creating commitments and nullifiers to state, whereas working with public state is directly updating state. + +## Accounts + +Every account in Aztec is a smart contract (account abstraction). This allows implementing different schemes for transaction signing, nonce management, and fee payments. + +Developers can write their own account contract to define the rules by which user transactions are authorized and paid for, as well as how user keys are managed. + +Learn more about account contracts [here](../../learn/concepts/accounts/main.md). + +## Smart contracts + +Developers can write [smart contracts](./smart_contracts/main.md) that manipulate both public and private state. They are written in a framework on top of Noir, the zero-knowledge domain-specific language developed specifically for Aztec. Outside of Aztec, Noir is used for writing circuits that can be verified on EVM chains. + +Noir has its own doc site that you can find [here](https://noir-lang.org). + +## Communication with Ethereum + +Aztec allows private communications with Ethereum - ie no-one knows where the transaction is coming from, just that it is coming from somewhere on Aztec. + +This is achieved through portals - these are smart contracts deployed on an EVM that are related to the Ethereum smart contract you want to interact with. + +Learn more about portals [here](./communication/cross_chain_calls.md). + +## Circuits + +Aztec operates on three types of circuits: + +- [Private kernel circuits](./circuits/kernels/private_kernel.md), which are executed by the user on their own device and prove correct execution of a function +- [Public kernel circuits](./circuits/kernels/public_kernel.md), which are executed by the [sequencer](./nodes_clients/sequencer/main.md) and ensure the stack trace of transactions adheres to function execution rules +- [Rollup circuits](./circuits/rollup_circuits/main.md), which bundle all of the Aztec transactions into a proof that can be efficiently verified on Ethereum + +## What's next? + +### Dive deeper into how Aztec works + +Explore the Concepts for a deeper understanding into the components that make up Aztec: + + + +### Start coding + +Follow the [developer getting started guide](../../developers/getting_started/main.md). + diff --git a/docs/docs/concepts/foundation/nodes_clients/execution_client.md b/docs/docs/learn/concepts/nodes_clients/execution_client.md similarity index 100% rename from docs/docs/concepts/foundation/nodes_clients/execution_client.md rename to docs/docs/learn/concepts/nodes_clients/execution_client.md diff --git a/docs/docs/concepts/foundation/nodes_clients/prover_client.md b/docs/docs/learn/concepts/nodes_clients/prover_client.md similarity index 100% rename from docs/docs/concepts/foundation/nodes_clients/prover_client.md rename to docs/docs/learn/concepts/nodes_clients/prover_client.md diff --git a/docs/docs/concepts/foundation/nodes_clients/sequencer.md b/docs/docs/learn/concepts/nodes_clients/sequencer/main.md similarity index 97% rename from docs/docs/concepts/foundation/nodes_clients/sequencer.md rename to docs/docs/learn/concepts/nodes_clients/sequencer/main.md index e9a20081223..4173dd87e2f 100644 --- a/docs/docs/concepts/foundation/nodes_clients/sequencer.md +++ b/docs/docs/learn/concepts/nodes_clients/sequencer/main.md @@ -40,4 +40,4 @@ You can view the current implementation on Github [here](https://github.com/Azte ## Further Reading -- [Sequencer Selection](../../advanced/sequencer_selection.md) +- [Sequencer Selection](../../../concepts/nodes_clients/sequencer/sequencer_selection.md) diff --git a/docs/docs/concepts/advanced/sequencer_selection.md b/docs/docs/learn/concepts/nodes_clients/sequencer/sequencer_selection.md similarity index 100% rename from docs/docs/concepts/advanced/sequencer_selection.md rename to docs/docs/learn/concepts/nodes_clients/sequencer/sequencer_selection.md diff --git a/docs/docs/concepts/advanced/acir_simulator.md b/docs/docs/learn/concepts/pxe/acir_simulator.md similarity index 88% rename from docs/docs/concepts/advanced/acir_simulator.md rename to docs/docs/learn/concepts/pxe/acir_simulator.md index bae95f54ff1..0b794773205 100644 --- a/docs/docs/concepts/advanced/acir_simulator.md +++ b/docs/docs/learn/concepts/pxe/acir_simulator.md @@ -14,13 +14,13 @@ It simulates three types of functions: Private functions are simulated and proved client-side, and verified client-side in the private kernel circuit. -They are run with the assistance of a DB oracle that provides any private data requested by the function. You can read more about oracle functions in the smart contract section [here](../../dev_docs/contracts/syntax/functions.md#oracle-functions). +They are run with the assistance of a DB oracle that provides any private data requested by the function. You can read more about oracle functions in the smart contract section [here](../../../developers/contracts/syntax/functions/oracles.md). Private functions can call other private functions and can request to call a public function. The public function execution will be performed by the sequencer asynchronously, so private functions don't have direct access to the return values of public functions. ### Public Functions -Public functions are simulated and proved on the [sequencer](../foundation/nodes_clients/sequencer.md) side, and verified by the [public kernel circuit](./circuits/kernels/public_kernel.md). +Public functions are simulated and proved on the [sequencer](../nodes_clients/sequencer/main.md) side, and verified by the [public kernel circuit](../circuits/kernels/public_kernel.md). They are run with the assistance of an oracle that provides any value read from the public state tree. diff --git a/docs/docs/learn/concepts/pxe/main.md b/docs/docs/learn/concepts/pxe/main.md new file mode 100644 index 00000000000..028aaf12081 --- /dev/null +++ b/docs/docs/learn/concepts/pxe/main.md @@ -0,0 +1,70 @@ +--- +title: Private Execution Environment (PXE) +--- + +The Private Execution Environment (or PXE, pronounced 'pixie') is a client-side library for the execution of private operations. It is a TypeScript library and can be run within Node, such as when you run the sandbox, within the browser, or any other environment in which TypeScript can run. For example, in future it could be run inside wallet software. + +The PXE generates proofs of private function execution, and sends these proofs along with public function requests to the sequencer. Private inputs never leave the client-side PXE. + +```mermaid +graph TD; + + subgraph client[Client] + subgraph pxe [PXE] + acirSim[ACIR Simulator] + db[Database] + keyStore[KeyStore] + end + end + + subgraph server[Application Server] + subgraph pxeService [PXE Service] + acctMgmt[Account Management] + contractTxInteract[Contract & Transaction Interactions] + noteMgmt[Note Management] + end + end + + pxe -->|interfaces| server + +``` + +## PXE Service + +The PXE is a client-side interface of the PXE Service, which is a set of server-side APIs for interacting with the network. It provides functions for account management, contract and transaction interactions, note management, and more. For a more extensive list of operations, refer to the [PXE reference](../../../apis/pxe/index.md). + +## Components + +### ACIR simulator + +The ACIR (Abstract Circuit Intermediate Representation) simulator handles the accurate execution of smart contract functions by simulating transactions. It generates the required data and inputs for these functions. You can find more details about how it works [here](./acir_simulator.md). + +### Database + +The database stores transactional data and notes within the user's PXE. In the Aztec protocol, the database is implemented as a key-value database backed by LMDB. There is an interface ([GitHub](https://github.com/AztecProtocol/aztec-packages/blob/ca8b5d9dbff8d8062dbf1cb1bd39d93a4a636e86/yarn-project/pxe/src/database/pxe_database.ts)) for this PXE database that can be implemented in other ways, such as an in-memory database that can be used for testing. + +The database stores various types of data, including: + +- **Notes**: Encrypted representations of assets. +- **Deferred Notes**: Notes that are intended for a user but cannot yet be decoded due to the associated contract not being present in the database. When new contracts are deployed, there may be some time before it is accessible from the PXE database. When the PXE database is updated, deferred note are decoded. +- **Authentication Witnesses**: Data used to approve others from executing transactions on your behalf +- **Capsules**: External data or data injected into the system via [oracles](#oracles). + +### Note discovery + +There is an open RFP for how note discovery will work on Aztec. You can find more information in the [forum](https://forum.aztec.network/t/request-for-proposals-note-discovery-protocol/2584). + +Currently in the Aztec sandbox, users download every note, compute a secret, and generate the symmetric decryption key from that secret. If the note belongs to them, then the user will have derived the same secret and ultimately the required decryption key. + +### Keystore + +The keystore is a secure storage for private and public keys. + +## Oracles + +Oracles are pieces of data that are injected into a smart contract function from the client side. You can read more about why and how they work in the [functions section](../../../developers/contracts/syntax/functions/oracles.md). + +## For developers +To learn how to develop on top of the PXE, refer to these guides: +* [Run more than one PXE on your local machine](../../../developers/cli/run_more_than_one_pxe_sandbox.md) +* [Use in-built oracles including oracles for arbitrary data](../../../developers/contracts/syntax/oracles.md) \ No newline at end of file diff --git a/docs/docs/concepts/advanced/contract_creation.md b/docs/docs/learn/concepts/smart_contracts/contract_creation.md similarity index 99% rename from docs/docs/concepts/advanced/contract_creation.md rename to docs/docs/learn/concepts/smart_contracts/contract_creation.md index 3eb3380b6c9..b93f5a7d54e 100644 --- a/docs/docs/concepts/advanced/contract_creation.md +++ b/docs/docs/learn/concepts/smart_contracts/contract_creation.md @@ -4,7 +4,7 @@ title: Contract Creation -import Disclaimer from '../../misc/common/\_disclaimer.mdx'; +import Disclaimer from '../../../misc/common/\_disclaimer.mdx'; @@ -327,4 +327,4 @@ Adds initial public state variables to the public data tree. ## Further reading -To see how to deploy a contract in practice, check out the [dapp development tutorial](../../dev_docs/tutorials/writing_dapp/main.md). +To see how to deploy a contract in practice, check out the [dapp development tutorial](../../../developers/tutorials/writing_dapp/main.md). diff --git a/docs/docs/concepts/foundation/contracts.md b/docs/docs/learn/concepts/smart_contracts/main.md similarity index 95% rename from docs/docs/concepts/foundation/contracts.md rename to docs/docs/learn/concepts/smart_contracts/main.md index 98cbd438992..822c7811ac3 100644 --- a/docs/docs/concepts/foundation/contracts.md +++ b/docs/docs/learn/concepts/smart_contracts/main.md @@ -23,4 +23,4 @@ There are no plans for EVM compatibility or to support Solidity in Aztec. The pr ## Further reading -Read more about writing Aztec contracts [here](../../dev_docs/contracts/main.md). +Read more about writing Aztec contracts [here](../../../developers/contracts/main.md). diff --git a/docs/docs/concepts/foundation/state_model/storage_slots.md b/docs/docs/learn/concepts/storage/storage_slots.md similarity index 77% rename from docs/docs/concepts/foundation/state_model/storage_slots.md rename to docs/docs/learn/concepts/storage/storage_slots.md index 0f67af9ec11..80013ecd156 100644 --- a/docs/docs/concepts/foundation/state_model/storage_slots.md +++ b/docs/docs/learn/concepts/storage/storage_slots.md @@ -11,7 +11,7 @@ It also means that we need to be careful about how we allocate storage to ensure ## Public State Slots -As mentioned in [State Model](./main.md), Aztec public state behaves similarly to public state on Ethereum from the point of view of the developer. Behind the scenes however, the storage is managed differently. As mentioned, public state has just one large sparse tree in Aztec - so we silo slots of public data by hashing it together with its contract address. +As mentioned in [State Model](./../hybrid_state/main.md), Aztec public state behaves similarly to public state on Ethereum from the point of view of the developer. Behind the scenes however, the storage is managed differently. As mentioned, public state has just one large sparse tree in Aztec - so we silo slots of public data by hashing it together with its contract address. The mental model is that we have a key-value store, where the siloed slot is the key, and the value is the data stored in that slot. You can think of the `real_storage_slot` identifying its position in the tree, and the `logical_storage_slot` identifying the position in the contract storage. @@ -19,14 +19,14 @@ The mental model is that we have a key-value store, where the siloed slot is the real_storage_slot = H(contract_address, logical_storage_slot) ``` -The siloing is performed by the [Kernel circuits](../../advanced/circuits/kernels/main.md). +The siloing is performed by the [Kernel circuits](../../concepts/circuits/main.md). For structs and arrays, we are logically using a similar storage slot computation to ethereum, e.g., as a struct with 3 fields would be stored in 3 consecutive slots. However, because the "actual" storage slot is computed as a hash of the contract address and the logical storage slot, the actual storage slot is not consecutive. ## Private State Slots - Slots aren't real -Private storage is a different beast. As you might remember from [State Model](./main.md), private state is stored in encrypted logs and the corresponding private state commitments in append-only tree where each leaf is a commitment. Being append-only, means that leaves are never updated or deleted; instead a nullifier is emitted to signify that some note is no longer valid. A major reason we used this tree, is that lookups at a specific storage slot would leak information in the context of private state. If you could look up a specific address balance just by looking at the storage slot, even if encrypted you would be able to see it changing! That is not good privacy. +Private storage is a different beast. As you might remember from [Hybrid State Model](../hybrid_state/main.md), private state is stored in encrypted logs and the corresponding private state commitments in append-only tree where each leaf is a commitment. Being append-only, means that leaves are never updated or deleted; instead a nullifier is emitted to signify that some note is no longer valid. A major reason we used this tree, is that lookups at a specific storage slot would leak information in the context of private state. If you could look up a specific address balance just by looking at the storage slot, even if encrypted you would be able to see it changing! That is not good privacy. Following this, the storage slot as we know it doesn't really exist. The leaves of the note hashes tree are just commitments to content (think of it as a hash of its content). @@ -62,4 +62,4 @@ By doing this address-siloing at the kernel circuit we *force* the inserted comm To ensure that nullifiers don't collide across contracts we also force this contract siloing at the kernel level. ::: -For an example of this see [developer documentation storage slots](./../../../dev_docs/contracts/syntax/storage/storage_slots.md). +For an example of this see [developer documentation storage slots](./../../../developers/contracts/syntax/storage/storage_slots.md). diff --git a/docs/docs/concepts/advanced/data_structures/indexed_merkle_tree.md b/docs/docs/learn/concepts/storage/trees/indexed_merkle_tree.md similarity index 100% rename from docs/docs/concepts/advanced/data_structures/indexed_merkle_tree.md rename to docs/docs/learn/concepts/storage/trees/indexed_merkle_tree.md diff --git a/docs/docs/concepts/advanced/data_structures/trees.md b/docs/docs/learn/concepts/storage/trees/main.md similarity index 81% rename from docs/docs/concepts/advanced/data_structures/trees.md rename to docs/docs/learn/concepts/storage/trees/main.md index 658782ad010..af58086e10f 100644 --- a/docs/docs/concepts/advanced/data_structures/trees.md +++ b/docs/docs/learn/concepts/storage/trees/main.md @@ -14,7 +14,7 @@ Data includes: - Nullifiers to invalidate old private state ([Nullifier tree](#nullifier-tree)) - Public state ([Public State tree](#public-state-tree)) - Contracts ([Contract tree](#contract-tree)) -- Previous block headers ([Archive tree](#archive-tree)) +- Archive tree for Historical access ([Archive tree](#archive-tree)) - Global Variables ```mermaid @@ -132,7 +132,7 @@ nullifier = hash(note_hash, owner_secret_key); This has the property that it's inextricably linked to the Note it is nullifying, and it can only be derived by the owner of the `owner_public_key` contained within the Note. Ensuring that the secret key corresponds to the public key would be implemented in the Aztec contract. -A smart contract that generates this nullifier and submits it to the network will only be allowed to submit it once; a second submission will be rejected by the base [Rollup Circuit](../circuits/rollup_circuits/main.md#base-rollup-circuit) (which performs Merkle non-membership checks against the Nullifier Tree). This prevents a Note from being 'deleted' twice. +A smart contract that generates this nullifier and submits it to the network will only be allowed to submit it once; a second submission will be rejected by the base [Rollup Circuit](../../circuits/rollup_circuits/main.md) (which performs Merkle non-membership checks against the Nullifier Tree). This prevents a Note from being 'deleted' twice. :::note @@ -171,7 +171,7 @@ This tree's data can only be read/written by the Sequencer, since only they can ## Contract Tree -The contract tree contains information about every function of every contract deployed to the Aztec network. This allows the [Kernel Circuits](../circuits/kernels/main.md) to validate that a function belongs to a specific contract. +The contract tree contains information about every function of every contract deployed to the Aztec network. This allows the [Kernel Circuits](../../circuits/main.md) to validate that a function belongs to a specific contract. @@ -181,9 +181,37 @@ Aztec supports the ability to keep the logic of private functions of a smart con ## Archive Tree -A block includes the root to the Archive Tree (sometimes called the blocks tree). +The archive tree is an append-only Merkle tree that stores hashes of headers of all previous blocks in the chain as its leaves. -The leaves of the tree are hashes of previous block headers. This tree can be used to verify data of any of the trees above at some previous point in time by doing a membership check of the corresponding tree root in the block header and the block header hash in the blocks tree. +As private execution relies on proofs generated by the user, the current block header is not known. We can instead base the proofs on historical state. By including all prior block headers (which include commitments to the state), the historical access tree allows us to easily prove that the historical state that a transaction is using for a proof is valid. + +```mermaid +graph TD; + +PartialStateReference[Partial State
snapshot of note hash tree, nullifier tree, contract tree, public data tree] +StateReference[State Reference
snapshot of L1<>L2 message tree] +GlobalVariables[Global Variables
block number, timestamp, version, chain_id] +Header[Block Header
last snapshot of tree, hash of body] +Logs[Transaction Logs
encrypted & non-encrypted logs] +PublicDataWrite[Public Data Writes
] +ContractData[Contract Data
leaf, address] +TxEffect[Transaction Effects
note hashes, nullifiers, contracts, public writes] +Body[ Body
L1<>L2 messages, transaction effects] +ArchiveTree[Archive Tree
used to validate user-generated proofs and access historical data] + +StateReference --- PartialStateReference +Header --- Body +Header --- StateReference +Header --- GlobalVariables +TxEffect --- ContractData +TxEffect --- PublicDataWrite +TxEffect --- Logs +Body --- TxEffect +HistoricalAccessTree --- Header + +``` + +It can also be used to find information about notes, public state, and contracts that were included in a certain block using [inclusion and non-inclusion proofs](../../../../developers/contracts/syntax/historical_access/how_to_prove_history.md). ## Trees of valid Kernel/Rollup circuit Verification Keys diff --git a/docs/docs/concepts/foundation/transactions.md b/docs/docs/learn/concepts/transactions.md similarity index 93% rename from docs/docs/concepts/foundation/transactions.md rename to docs/docs/learn/concepts/transactions.md index ff1dce31933..5cad5bda8e8 100644 --- a/docs/docs/concepts/foundation/transactions.md +++ b/docs/docs/learn/concepts/transactions.md @@ -29,9 +29,9 @@ _The transaction has not been broadcasted to the sequencer network yet._ _The transaction has still not been broadcasted to the sequencer network yet._ -3. **The PXE proves correct execution** – the PXE proves correct execution (via zero-knowledge proofs) of the authorization and the private transfer method. Once the proofs have been generated, the PXE sends the proofs and required inputs (inputs are new note commitments, stored in the [note hash tree](../advanced/data_structures/trees.md#note-hash-tree) and nullifiers stored in the [nullifiers tree](../advanced/data_structures/trees.md#nullifier-tree)) to the sequencer. Nullifiers are data that invalidate old commitments, ensuring that commitments can only be used once. +3. **The PXE proves correct execution** – the PXE proves correct execution (via zero-knowledge proofs) of the authorization and the private transfer method. Once the proofs have been generated, the PXE sends the proofs and required inputs (inputs are new note commitments, stored in the [note hash tree](../concepts/storage/trees/main.md#note-hash-tree) and nullifiers stored in the [nullifiers tree](../concepts/storage/trees/main.md#nullifier-tree)) to the sequencer. Nullifiers are data that invalidate old commitments, ensuring that commitments can only be used once. -The sequencer has received transaction proof and can begin to process the transaction (verify proofs and apply updates to the relevant [data trees](../advanced/data_structures/trees.md)) alongside other public and private transactions. +The sequencer has received transaction proof and can begin to process the transaction (verify proofs and apply updates to the relevant [data trees](../concepts/storage/trees/main.md)) alongside other public and private transactions. 4. **The sequencer has the necessary information to act** – the randomly-selected sequencer (based on the Fernet sequencer selection protocol) validates the transaction proofs along with required inputs (e.g. the note commitments and nullifiers) for this private transfer. The sequencer also executes public functions and requests proofs of public execution from a prover network. The sequencer updates the corresponding data trees and does the same for other private transactions. The sequencer requests proofs from a prover network that will be bundled into a final rollup proof. diff --git a/docs/docs/concepts/foundation/upgrade_mechanism.md b/docs/docs/learn/concepts/upgrade_mechanism.md similarity index 100% rename from docs/docs/concepts/foundation/upgrade_mechanism.md rename to docs/docs/learn/concepts/upgrade_mechanism.md diff --git a/docs/docs/misc/common/_disclaimer.mdx b/docs/docs/misc/common/_disclaimer.mdx index 2bd4a3a0cfc..b6d393fef45 100644 --- a/docs/docs/misc/common/_disclaimer.mdx +++ b/docs/docs/misc/common/_disclaimer.mdx @@ -3,7 +3,7 @@ :::caution Disclaimer We are building Aztec as transparently as we can. The documents published here are living documents. The protocol, sandbox, language, and tools are all subject to change over time. -Please see [here](../../dev_docs/limitations/main.md) for details of known Aztec protocol and Aztec Sandbox limitations. +Please see [here](../../developers/limitations/main.md) for details of known Aztec protocol and Aztec Sandbox limitations. If you would like to help us build Aztec: - Contribute code on [GitHub](https://github.com/AztecProtocol); or diff --git a/docs/docs/about_aztec/how_to_contribute.md b/docs/docs/misc/how_to_contribute.md similarity index 100% rename from docs/docs/about_aztec/how_to_contribute.md rename to docs/docs/misc/how_to_contribute.md diff --git a/docs/docs/misc/migration_notes.md b/docs/docs/misc/migration_notes.md index 54a95d598f9..62168759c5b 100644 --- a/docs/docs/misc/migration_notes.md +++ b/docs/docs/misc/migration_notes.md @@ -6,6 +6,337 @@ keywords: [sandbox, cli, aztec, notes, migration, updating, upgrading] Aztec is in full-speed development. Literally every version breaks compatibility with the previous ones. This page attempts to target errors and difficulties you might encounter when upgrading, and how to resolve them. +## TBD + +### `Note::compute_note_hash` renamed to `Note::compute_note_content_hash` +The `compute_note_hash` function in of the `Note` trait has been renamed to `compute_note_content_hash` to avoid being confused with the actual note hash. + +Before: +```rust +impl NoteInterface for CardNote { + fn compute_note_hash(self) -> Field { + pedersen_hash([ + self.owner.to_field(), + ], 0) + } +``` + +Now: +```rust +impl NoteInterface for CardNote { + fn compute_note_content_hash(self) -> Field { + pedersen_hash([ + self.owner.to_field(), + ], 0) + } +``` + +### Introduce `compute_note_hash_for_consumption` and `compute_note_hash_for_insertion` + +Makes a split in logic for note hash computation for consumption and insertion. This is to avoid confusion between the two, and to make it clear that the note hash for consumption is different from the note hash for insertion (sometimes). + +`compute_note_hash_for_consumption` replaces `compute_note_hash_for_read_or_nullify`. +`compute_note_hash_for_insertion` is new, and mainly used in `lifecycle.nr`` + + +## 0.22.0 + +### [Aztec.nr] `Serialize`, `Deserialize`, `NoteInterface` as Traits, removal of SerializationMethods and SERIALIZED_LEN + +Storage definition and initialization has been simplified. Previously: + +```rust +struct Storage { + leader: PublicState, + legendary_card: Singleton, + profiles: Map>, + test: Set, + imm_singleton: ImmutableSingleton, +} + +impl Storage { + fn init(context: Context) -> Self { + Storage { + leader: PublicState::new( + context, + 1, + LeaderSerializationMethods, + ), + legendary_card: Singleton::new(context, 2, CardNoteMethods), + profiles: Map::new( + context, + 3, + |context, slot| { + Singleton::new(context, slot, CardNoteMethods) + }, + ), + test: Set::new(context, 4, CardNoteMethods), + imm_singleton: ImmutableSingleton::new(context, 4, CardNoteMethods), + } + } + } +``` + +Now: + +```rust +struct Storage { + leader: PublicState, + legendary_card: Singleton, + profiles: Map>, + test: Set, + imm_singleton: ImmutableSingleton, +} + +impl Storage { + fn init(context: Context) -> Self { + Storage { + leader: PublicState::new( + context, + 1 + ), + legendary_card: Singleton::new(context, 2), + profiles: Map::new( + context, + 3, + |context, slot| { + Singleton::new(context, slot) + }, + ), + test: Set::new(context, 4), + imm_singleton: ImmutableSingleton::new(context, 4), + } + } +} +``` + +For this to work, Notes must implement Serialize, Deserialize and NoteInterface Traits. Previously: + +```rust +use dep::aztec::protocol_types::address::AztecAddress; +use dep::aztec::{ + note::{ + note_header::NoteHeader, + note_interface::NoteInterface, + utils::compute_note_hash_for_read_or_nullify, + }, + oracle::{ + nullifier_key::get_nullifier_secret_key, + get_public_key::get_public_key, + }, + log::emit_encrypted_log, + hash::pedersen_hash, + context::PrivateContext, +}; + +// Shows how to create a custom note + +global CARD_NOTE_LEN: Field = 1; + +impl CardNote { + pub fn new(owner: AztecAddress) -> Self { + CardNote { + owner, + } + } + + pub fn serialize(self) -> [Field; CARD_NOTE_LEN] { + [self.owner.to_field()] + } + + pub fn deserialize(serialized_note: [Field; CARD_NOTE_LEN]) -> Self { + CardNote { + owner: AztecAddress::from_field(serialized_note[1]), + } + } + + pub fn compute_note_hash(self) -> Field { + pedersen_hash([ + self.owner.to_field(), + ],0) + } + + pub fn compute_nullifier(self, context: &mut PrivateContext) -> Field { + let note_hash_for_nullify = compute_note_hash_for_read_or_nullify(CardNoteMethods, self); + let secret = context.request_nullifier_secret_key(self.owner); + pedersen_hash([ + note_hash_for_nullify, + secret.high, + secret.low, + ],0) + } + + pub fn compute_nullifier_without_context(self) -> Field { + let note_hash_for_nullify = compute_note_hash_for_read_or_nullify(CardNoteMethods, self); + let secret = get_nullifier_secret_key(self.owner); + pedersen_hash([ + note_hash_for_nullify, + secret.high, + secret.low, + ],0) + } + + pub fn set_header(&mut self, header: NoteHeader) { + self.header = header; + } + + // Broadcasts the note as an encrypted log on L1. + pub fn broadcast(self, context: &mut PrivateContext, slot: Field) { + let encryption_pub_key = get_public_key(self.owner); + emit_encrypted_log( + context, + (*context).this_address(), + slot, + encryption_pub_key, + self.serialize(), + ); + } +} + +fn deserialize(serialized_note: [Field; CARD_NOTE_LEN]) -> CardNote { + CardNote::deserialize(serialized_note) +} + +fn serialize(note: CardNote) -> [Field; CARD_NOTE_LEN] { + note.serialize() +} + +fn compute_note_hash(note: CardNote) -> Field { + note.compute_note_hash() +} + +fn compute_nullifier(note: CardNote, context: &mut PrivateContext) -> Field { + note.compute_nullifier(context) +} + +fn compute_nullifier_without_context(note: CardNote) -> Field { + note.compute_nullifier_without_context() +} + +fn get_header(note: CardNote) -> NoteHeader { + note.header +} + +fn set_header(note: &mut CardNote, header: NoteHeader) { + note.set_header(header) +} + +// Broadcasts the note as an encrypted log on L1. +fn broadcast(context: &mut PrivateContext, slot: Field, note: CardNote) { + note.broadcast(context, slot); +} + +global CardNoteMethods = NoteInterface { + deserialize, + serialize, + compute_note_hash, + compute_nullifier, + compute_nullifier_without_context, + get_header, + set_header, + broadcast, +}; +``` + +Now: + +```rust +use dep::aztec::{ + note::{ + note_header::NoteHeader, + note_interface::NoteInterface, + utils::compute_note_hash_for_read_or_nullify, + }, + oracle::{ + nullifier_key::get_nullifier_secret_key, + get_public_key::get_public_key, + }, + log::emit_encrypted_log, + hash::pedersen_hash, + context::PrivateContext, + protocol_types::{ + address::AztecAddress, + traits::{Serialize, Deserialize, Empty} + } +}; + +// Shows how to create a custom note + +global CARD_NOTE_LEN: Field = 1; + +impl CardNote { + pub fn new(owner: AztecAddress) -> Self { + CardNote { + owner, + } + } +} + +impl Serialize for CardNote { + fn serialize(self) -> [Field; CARD_NOTE_LEN] { + [self.owner.to_field()] + } +} + +impl Deserialize for CardNote { + fn deserialize(serialized_note: [Field; CARD_NOTE_LEN]) -> Self { + CardNote { + owner: AztecAddress::from_field(serialized_note[2]), + } + } +} + +impl NoteInterface for CardNote { + fn compute_note_hash(self) -> Field { + pedersen_hash([ + self.owner.to_field(), + ],0) + } + + fn compute_nullifier(self, context: &mut PrivateContext) -> Field { + let note_hash_for_nullify = compute_note_hash_for_read_or_nullify(self); + let secret = context.request_nullifier_secret_key(self.owner); + pedersen_hash([ + note_hash_for_nullify, + secret.high, + secret.low, + ],0) + } + + fn compute_nullifier_without_context(self) -> Field { + let note_hash_for_nullify = compute_note_hash_for_read_or_nullify(self); + let secret = get_nullifier_secret_key(self.owner); + pedersen_hash([ + note_hash_for_nullify, + secret.high, + secret.low, + ],0) + } + + fn set_header(&mut self, header: NoteHeader) { + self.header = header; + } + + fn get_header(note: CardNote) -> NoteHeader { + note.header + } + + // Broadcasts the note as an encrypted log on L1. + fn broadcast(self, context: &mut PrivateContext, slot: Field) { + let encryption_pub_key = get_public_key(self.owner); + emit_encrypted_log( + context, + (*context).this_address(), + slot, + encryption_pub_key, + self.serialize(), + ); + } +} +``` + +Public state must implement Serialize and Deserialize traits. + ## 0.20.0 ### [Aztec.nr] Changes to `NoteInterface` diff --git a/docs/docs/about_aztec/roadmap/cryptography_roadmap.md b/docs/docs/misc/roadmap/cryptography_roadmap.md similarity index 100% rename from docs/docs/about_aztec/roadmap/cryptography_roadmap.md rename to docs/docs/misc/roadmap/cryptography_roadmap.md diff --git a/docs/docs/about_aztec/roadmap/engineering_roadmap.md b/docs/docs/misc/roadmap/engineering_roadmap.md similarity index 98% rename from docs/docs/about_aztec/roadmap/engineering_roadmap.md rename to docs/docs/misc/roadmap/engineering_roadmap.md index 890d0f1e14a..4b5163f90ad 100644 --- a/docs/docs/about_aztec/roadmap/engineering_roadmap.md +++ b/docs/docs/misc/roadmap/engineering_roadmap.md @@ -121,7 +121,7 @@ CI takes up a significant amount of time. It gets its own section here, so we re We _need_ a way to read mutable public data from a private function. -Note: we just published the [Slow Updates Tree](../../concepts/foundation/communication/public_private_calls/slow_updates_tree.md). +Note: we just published the [Slow Updates Tree](../../learn/concepts/communication/public_private_calls/slow_updates_tree.md). ## Contract classes and instances? diff --git a/docs/docs/about_aztec/roadmap/features_initial_ldt.md b/docs/docs/misc/roadmap/features_initial_ldt.md similarity index 100% rename from docs/docs/about_aztec/roadmap/features_initial_ldt.md rename to docs/docs/misc/roadmap/features_initial_ldt.md diff --git a/docs/docs/about_aztec/roadmap/main.md b/docs/docs/misc/roadmap/main.md similarity index 100% rename from docs/docs/about_aztec/roadmap/main.md rename to docs/docs/misc/roadmap/main.md diff --git a/docs/docs/intro.md b/docs/docs/welcome.md similarity index 62% rename from docs/docs/intro.md rename to docs/docs/welcome.md index ca9ff902fe9..6be66998211 100644 --- a/docs/docs/intro.md +++ b/docs/docs/welcome.md @@ -1,11 +1,11 @@ --- slug: "/" -id: "intro" -title: What is Aztec? +id: "welcome" +title: Welcome description: "Aztec introduces a privacy-centric zkRollup solution for Ethereum, enhancing confidentiality and scalability within the Ethereum ecosystem." --- -# Aztec: Ethereum, encrypted +# Aztec: A Privacy-First L2 on Ethereum On Ethereum today, everything is publicly visible, by everyone. In the real world, people enjoy privacy. Aztec brings privacy to Ethereum. @@ -13,11 +13,11 @@ On Ethereum today, everything is publicly visible, by everyone. In the real worl ### Learn :book: -Start on the [Foundational Concepts page](./concepts/foundation/main.md) to read about how Aztec works. +Start on the [Technical Overview page](./learn/about_aztec/technical_overview.md) to read about how Aztec works. ### Build :technologist: -Go to the [Getting Started section](./dev_docs/getting_started/main.md) of the developer docs to get your hands dirty and start developing on Aztec. +Go to the [Getting Started section](./developers/getting_started/main.md) of the developer docs to get your hands dirty and start developing on Aztec. #### Go deeper 🔬 @@ -25,4 +25,4 @@ Check out the [Awesome Aztec repo](https://github.com/AztecProtocol/awesome-azte Clone the [Aztec Starter repo](https://github.com/AztecProtocol/aztec-starter) to get a minimal project set up with Sandbox (local developer network), a simple contract and a test suite. -Jump into one of the [tutorials](./dev_docs/tutorials/main.md) to learn how to build more complex applications on Aztec. +Jump into one of the [tutorials](./developers/tutorials/main.md) to learn how to build more complex applications on Aztec. diff --git a/docs/docusaurus.config.js b/docs/docusaurus.config.js index efe1271187e..d2ffd760db2 100644 --- a/docs/docusaurus.config.js +++ b/docs/docusaurus.config.js @@ -195,7 +195,7 @@ const config = { items: [ { type: "doc", - docId: "intro", + docId: "welcome", position: "left", label: "Aztec Protocol", }, @@ -213,7 +213,7 @@ const config = { }, { label: "Developer Quickstart", - to: "/dev_docs/getting_started/quickstart", + to: "/developers/getting_started/quickstart", }, { label: "Aztec.nr", diff --git a/docs/internal_notes/building_dapps.md b/docs/internal_notes/building_dapps.md index b6d4f3a1be1..4cc8792a3bd 100644 --- a/docs/internal_notes/building_dapps.md +++ b/docs/internal_notes/building_dapps.md @@ -36,7 +36,7 @@ FOR INSTRUCTIONS FOR BUILDING A WALLET, WE SHOULD WRITE DOCS HERE ERRORS: -- Add any error explanations to [errors.md](../docs/dev_docs/contracts/common_errors.md) (and break that file into multiple files if it's too unwieldy). +- Add any error explanations to [errors.md](../docs/developers/contracts/common_errors.md) (and break that file into multiple files if it's too unwieldy). ## Testing a dapp diff --git a/docs/netlify.toml b/docs/netlify.toml index a26ea50351b..029b6ad77e3 100644 --- a/docs/netlify.toml +++ b/docs/netlify.toml @@ -20,87 +20,87 @@ [[redirects]] from = "/dev_docs/aztec3/*" - to = "/dev_docs/aztec/overview" + to = "/learn/about_aztec/what_is_aztec" [[redirects]] from = "/dev_docs/contracts/types" - to = "/dev_docs/contracts/syntax/types" + to = "/developers/contracts/syntax/types" [[redirects]] from = "/dev_docs/contracts/storage" - to = "/dev_docs/contracts/syntax/storage" + to = "/developers/contracts/syntax/storage" [[redirects]] from = "/dev_docs/contracts/state_variables" - to = "/dev_docs/contracts/syntax/storage" + to = "/developers/contracts/syntax/storage" [[redirects]] from = "/dev_docs/contracts/functions" - to = "/dev_docs/contracts/syntax/functions" + to = "/developers/contracts/syntax/functions" [[redirects]] from = "/dev_docs/contracts/visibility" - to = "/dev_docs/contracts/syntax/visibility" + to = "/developers/contracts/syntax/visibility" [[redirects]] from = "/dev_docs/contracts/globals" - to = "/dev_docs/contracts/syntax/globals" + to = "/developers/contracts/syntax/globals" [[redirects]] from = "/dev_docs/contracts/syntax" - to = "/dev_docs/contracts/syntax/main" + to = "/developers/contracts/syntax/main" [[redirects]] from = "/dev_docs/contracts/contract" - to = "/dev_docs/contracts/syntax/contract" + to = "/developers/contracts/syntax/contract" [[redirects]] from = "/dev_docs/getting_started/cli" - to = "/dev_docs/cli/main" + to = "/developers/cli/main" [[redirects]] from = "/dev_docs/getting_started/noir_contracts" - to = "/dev_docs/contracts/main" + to = "/developers/contracts/main" [[redirects]] from = "/dev_docs/getting_started/token_contract_tutorial" - to = "/dev_docs/tutorials/writing_token_contract" + to = "/developers/tutorials/writing_token_contract" [[redirects]] from = "/dev_docs/getting_started/updating" - to = "/dev_docs/updating" + to = "/developers/updating" [[redirects]] from = "/dev_docs/sandbox/main" - to = "/dev_docs/getting_started/sandbox" + to = "/developers/getting_started/sandbox" [[redirects]] from = "/dev_docs/cli/updating" - to = "/dev_docs/updating" + to = "/developers/updating" [[redirects]] from = "/dev_docs/dapps/tutorials/main" - to = "/dev_docs/tutorials/writing_dapp/main" + to = "/developers/tutorials/writing_dapp/main" [[redirects]] from = "/dev_docs/dapps/tutorials/project_setup" - to = "/dev_docs/tutorials/writing_dapp/project_setup" + to = "/developers/tutorials/writing_dapp/project_setup" [[redirects]] from = "/dev_docs/dapps/tutorials/contract_deployment" - to = "/dev_docs/tutorials/writing_dapp/contract_deployment" + to = "/developers/tutorials/writing_dapp/contract_deployment" [[redirects]] from = "/dev_docs/dapps/tutorials/contract_interaction" - to = "/dev_docs/tutorials/writing_dapp/contract_interaction" + to = "/developers/tutorials/writing_dapp/contract_interaction" [[redirects]] from = "/dev_docs/dapps/tutorials/rpc_server" - to = "/dev_docs/tutorials/writing_dapp/rpc_server" + to = "/developers/tutorials/writing_dapp/rpc_server" [[redirects]] from = "/dev_docs/dapps/tutorials/testing" - to = "/dev_docs/tutorials/writing_dapp/testing" + to = "/developers/tutorials/writing_dapp/testing" [[redirects]] from = "/aztec/cryptography/cryptography-roadmap" @@ -112,55 +112,55 @@ [[redirects]] from = "/aztec/protocol/trees" - to = "/concepts/advanced/data_structures/trees" + to = "/learn/concepts/storage/trees/main" [[redirects]] from = "/aztec/protocol/trees/indexed-merkle-tree" - to = "/concepts/advanced/data_structures/indexed_merkle_tree" + to = "/learn/concepts/storage/trees/indexed_merkle_tree" [[redirects]] from = "/aztec/protocol/circuits/private-kernel" - to = "/concepts/advanced/circuits/kernels/private_kernel" + to = "/learn/concepts/circuits/kernels/private_kernel" [[redirects]] from = "/aztec/protocol/circuits/public-kernel" - to = "/concepts/advanced/circuits/kernels/public_kernel" + to = "/learn/concepts/circuits/kernels/public_kernel" [[redirects]] from = "/aztec/protocol/circuits/rollup" - to = "/concepts/advanced/circuits/rollup_circuits/main" + to = "/learn/concepts/circuits/rollup_circuits/main" [[redirects]] from = "/aztec/protocol/public-functions-vm-architectures" - to = "/concepts/advanced/public_vm" + to = "/learn/concepts/hybrid_state/public_vm" [[redirects]] from = "/aztec/how-it-works/private-smart-contracts" - to = "/concepts/foundation/contracts" + to = "/learn/concepts/contracts/main" [[redirects]] from = "/aztec/how-it-works/private-state" - to = "/concepts/foundation/state_model/main" + to = "/learn/concepts/hybrid_state/main" [[redirects]] from = "/concepts/foundation/state_model" - to = "/concepts/foundation/state_model/main" + to = "/learn/concepts/hybrid_state/main" [[redirects]] from = "/aztec/how-it-works/private-public-execution" - to = "/concepts/foundation/communication/public_private_calls/main" + to = "/learn/concepts/communication/public_private_calls/main" [[redirects]] from="/concepts/foundation/communication/public_private_calls" - to="/concepts/foundation/communication/public_private_calls/main" + to="/learn/concepts/communication/public_private_calls/main" [[redirects]] from = "/aztec/how-it-works/l1-l2-messaging" - to = "/concepts/foundation/communication/cross_chain_calls" + to = "/learn/concepts/communication/cross_chain_calls" [[redirects]] from = "/aztec/protocol/contract-creation" - to = "/concepts/advanced/contract_creation" + to = "/learn/concepts/contracts/contract_creation" [[redirects]] from = "/noir" @@ -180,27 +180,27 @@ [[redirects]] from = "/dev_docs/contracts/syntax/state_variables" - to = "/dev_docs/contracts/syntax/storage" + to = "/developers/contracts/syntax/storage" [[redirects]] from = "/dev_docs/getting_started/sandbox" - to = "/dev_docs/getting_started/quickstart" + to = "/developers/getting_started/quickstart" [[redirects]] from = "/dev_docs/getting_started/blank_box" - to = "/dev_docs/cli/blank_box" + to = "/developers/cli/blank_box" [[redirects]] from = "/dev_docs/getting_started/updating" - to = "/dev_docs/cli/updating" + to = "/developers/cli/updating" [[redirects]] from = "/dev_docs/sandbox_errors/main" - to = "dev_docs/debugging/sandbox-errors" + to = "developers/debugging/sandbox-errors" [[redirects]] from = "/dev_docs/contracts/common_errors" - to = "dev_docs/debugging/aztecnr-errors" + to = "developers/debugging/aztecnr-errors" [[redirects]] from = "/misc/aztec-connect-sunset" diff --git a/docs/sidebars.js b/docs/sidebars.js index ae468539d33..88dc7f7bd73 100644 --- a/docs/sidebars.js +++ b/docs/sidebars.js @@ -24,33 +24,14 @@ const sidebars = { { type: "html", className: "sidebar-title", - value: "About Aztec", + value: "LEARN", defaultStyle: true, }, - { - label: "What is Aztec?", - type: "category", - link: { type: "doc", id: "intro" }, - items: ["about_aztec/overview"], - }, - - "about_aztec/vision", - - { - label: "Roadmap", - type: "category", - link: { - type: "doc", - id: "about_aztec/roadmap/main", - }, - items: [ - "about_aztec/roadmap/features_initial_ldt", - "about_aztec/roadmap/cryptography_roadmap", - ], - }, - - "about_aztec/how_to_contribute", + "welcome", + "learn/about_aztec/what_is_aztec", + "learn/about_aztec/vision", + "learn/about_aztec/technical_overview", { type: "html", @@ -60,150 +41,132 @@ const sidebars = { // SPECIFICATION { - type: "html", - className: "sidebar-title", - value: "Specification", - defaultStyle: true, - }, - - { - label: "Foundational Concepts", + label: "Concepts", type: "category", link: { type: "doc", - id: "concepts/foundation/main", + id: "learn/concepts/main", }, items: [ { - label: "State Model", + label: "Hybrid State Model", type: "category", link: { type: "doc", - id: "concepts/foundation/state_model/main", + id: "learn/concepts/hybrid_state/main", }, - items: ["concepts/foundation/state_model/storage_slots"], + items: ["learn/concepts/hybrid_state/public_vm"], + }, + { + label: "Storage", + type: "category", + items: [ + { + label: "Trees", + type: "category", + link: { + type: "doc", + id: "learn/concepts/storage/trees/main", + }, + items: ["learn/concepts/storage/trees/indexed_merkle_tree"], + }, + "learn/concepts/storage/storage_slots", + ], }, { label: "Accounts", type: "category", - link: { type: "doc", id: "concepts/foundation/accounts/main" }, + link: { + type: "doc", + id: "learn/concepts/accounts/main", + }, items: [ - "concepts/foundation/accounts/keys", - "concepts/foundation/accounts/authwit", + "learn/concepts/accounts/keys", + "learn/concepts/accounts/authwit", ], }, - "concepts/foundation/contracts", - "concepts/foundation/transactions", - // "concepts/foundation/globals", + "learn/concepts/transactions", { - label: "Communication", + label: "Smart Contracts", type: "category", link: { type: "doc", - id: "concepts/foundation/communication/main", + id: "learn/concepts/smart_contracts/main", }, + items: ["learn/concepts/smart_contracts/contract_creation"], + }, + { + label: "Communication", + type: "category", items: [ { label: "Public <> Private Communication", type: "category", link: { type: "doc", - id: "concepts/foundation/communication/public_private_calls/main", + id: "learn/concepts/communication/public_private_calls/main", }, items: [ - "concepts/foundation/communication/public_private_calls/slow_updates_tree", + "learn/concepts/communication/public_private_calls/slow_updates_tree", ], }, - "concepts/foundation/communication/cross_chain_calls", - ], - }, - { - label: "Nodes and Clients", - type: "category", - // link: { - // type: "doc", - // id: "concepts/foundation/nodes_clients/main", - // }, - items: [ - // "concepts/foundation/nodes_clients/execution_client", - // "concepts/foundation/nodes_clients/prover_client", - "concepts/foundation/nodes_clients/sequencer", + "learn/concepts/communication/cross_chain_calls", ], }, - // "concepts/foundation/block_production", - // "concepts/foundation/upgrade_mechanism", - ], - }, - - { - label: "Advanced Concepts", - type: "category", - link: { - type: "doc", - id: "concepts/advanced/main", - }, - items: [ { - label: "Data Structures", + label: "Private Execution Environment (PXE)", type: "category", link: { type: "doc", - id: "concepts/advanced/data_structures/main", + id: "learn/concepts/pxe/main", }, - items: [ - "concepts/advanced/data_structures/trees", - "concepts/advanced/data_structures/indexed_merkle_tree", - ], + items: ["learn/concepts/pxe/acir_simulator"], }, { label: "Circuits", type: "category", link: { type: "doc", - id: "concepts/advanced/circuits/main", + id: "learn/concepts/circuits/main", }, items: [ { - label: "Kernels", + label: "Kernel Circuits", type: "category", - link: { - type: "doc", - id: "concepts/advanced/circuits/kernels/main", - }, items: [ - "concepts/advanced/circuits/kernels/private_kernel", - "concepts/advanced/circuits/kernels/public_kernel", + "learn/concepts/circuits/kernels/private_kernel", + "learn/concepts/circuits/kernels/public_kernel", ], }, + "learn/concepts/circuits/rollup_circuits/main", + ], + }, + { + label: "Nodes and Clients", + type: "category", + items: [ { - label: "Rollup Circuits", - type: "category", + label: "Sequencer", link: { type: "doc", - id: "concepts/advanced/circuits/rollup_circuits/main", + id: "learn/concepts/nodes_clients/sequencer/main", }, - items: [], + type: "category", + items: [ + "learn/concepts/nodes_clients/sequencer/sequencer_selection", + ], }, ], }, - "concepts/advanced/public_vm", - "concepts/advanced/contract_creation", - "concepts/advanced/sequencer_selection", - "concepts/advanced/acir_simulator", ], }, - { - type: "html", - value: '', - }, - // DEVELOPER DOCUMENTATION { type: "html", className: "sidebar-title", - value: "Developer Documentation", + value: "BUILD", defaultStyle: true, }, @@ -212,13 +175,12 @@ const sidebars = { type: "category", link: { type: "doc", - id: "dev_docs/getting_started/main", + id: "developers/getting_started/main", }, items: [ - "dev_docs/getting_started/quickstart", - "dev_docs/getting_started/core-concepts", - "dev_docs/getting_started/aztecnr-getting-started", - "dev_docs/getting_started/aztecjs-getting-started", + "developers/getting_started/quickstart", + "developers/getting_started/aztecnr-getting-started", + "developers/getting_started/aztecjs-getting-started", ], }, @@ -227,25 +189,25 @@ const sidebars = { type: "category", link: { type: "doc", - id: "dev_docs/tutorials/main", + id: "developers/tutorials/main", }, items: [ - "dev_docs/tutorials/writing_token_contract", - "dev_docs/tutorials/writing_private_voting_contract", + "developers/tutorials/writing_token_contract", + "developers/tutorials/writing_private_voting_contract", { label: "Writing a DApp", type: "category", link: { type: "doc", - id: "dev_docs/tutorials/writing_dapp/main", + id: "developers/tutorials/writing_dapp/main", }, items: [ - "dev_docs/tutorials/writing_dapp/project_setup", - "dev_docs/tutorials/writing_dapp/pxe_service", - "dev_docs/tutorials/writing_dapp/contract_deployment", - "dev_docs/tutorials/writing_dapp/contract_interaction", - "dev_docs/tutorials/writing_dapp/testing", + "developers/tutorials/writing_dapp/project_setup", + "developers/tutorials/writing_dapp/pxe_service", + "developers/tutorials/writing_dapp/contract_deployment", + "developers/tutorials/writing_dapp/contract_interaction", + "developers/tutorials/writing_dapp/testing", ], }, { @@ -253,15 +215,15 @@ const sidebars = { type: "category", link: { type: "doc", - id: "dev_docs/tutorials/token_portal/main", + id: "developers/tutorials/token_portal/main", }, items: [ - "dev_docs/tutorials/token_portal/setup", - "dev_docs/tutorials/token_portal/depositing_to_aztec", - "dev_docs/tutorials/token_portal/minting_on_aztec", - "dev_docs/tutorials/token_portal/cancelling_deposits", - "dev_docs/tutorials/token_portal/withdrawing_to_l1", - "dev_docs/tutorials/token_portal/typescript_glue_code", + "developers/tutorials/token_portal/setup", + "developers/tutorials/token_portal/depositing_to_aztec", + "developers/tutorials/token_portal/minting_on_aztec", + "developers/tutorials/token_portal/cancelling_deposits", + "developers/tutorials/token_portal/withdrawing_to_l1", + "developers/tutorials/token_portal/typescript_glue_code", ], }, { @@ -269,21 +231,21 @@ const sidebars = { type: "category", link: { type: "doc", - id: "dev_docs/tutorials/uniswap/main", + id: "developers/tutorials/uniswap/main", }, items: [ - "dev_docs/tutorials/uniswap/setup", - "dev_docs/tutorials/uniswap/l1_portal", - "dev_docs/tutorials/uniswap/l2_contract_setup", - "dev_docs/tutorials/uniswap/swap_publicly", - "dev_docs/tutorials/uniswap/execute_public_swap_on_l1", - "dev_docs/tutorials/uniswap/swap_privately", - "dev_docs/tutorials/uniswap/execute_private_swap_on_l1", - "dev_docs/tutorials/uniswap/redeeming_swapped_assets_on_l2", - "dev_docs/tutorials/uniswap/typescript_glue_code", + "developers/tutorials/uniswap/setup", + "developers/tutorials/uniswap/l1_portal", + "developers/tutorials/uniswap/l2_contract_setup", + "developers/tutorials/uniswap/swap_publicly", + "developers/tutorials/uniswap/execute_public_swap_on_l1", + "developers/tutorials/uniswap/swap_privately", + "developers/tutorials/uniswap/execute_private_swap_on_l1", + "developers/tutorials/uniswap/redeeming_swapped_assets_on_l2", + "developers/tutorials/uniswap/typescript_glue_code", ], }, - "dev_docs/tutorials/testing", + "developers/tutorials/testing", ], }, @@ -292,27 +254,31 @@ const sidebars = { type: "category", link: { type: "doc", - id: "dev_docs/cli/main", + id: "developers/cli/main", }, - items: ["dev_docs/cli/cli-commands", "dev_docs/cli/sandbox-reference"], + items: [ + "developers/cli/cli-commands", + "developers/cli/sandbox-reference", + "developers/cli/run_more_than_one_pxe_sandbox", + ], }, { label: "Aztec.nr Contracts", type: "category", link: { type: "doc", - id: "dev_docs/contracts/main", + id: "developers/contracts/main", }, items: [ - "dev_docs/contracts/workflow", - "dev_docs/contracts/setup", - "dev_docs/contracts/layout", + "developers/contracts/workflow", + "developers/contracts/setup", + "developers/contracts/layout", { label: "Syntax", type: "category", link: { type: "doc", - id: "dev_docs/contracts/syntax/main", + id: "developers/contracts/syntax/main", }, items: [ { @@ -320,53 +286,82 @@ const sidebars = { type: "category", link: { type: "doc", - id: "dev_docs/contracts/syntax/storage/main", + id: "developers/contracts/syntax/storage/main", }, - items: ["dev_docs/contracts/syntax/storage/storage_slots"], + items: [ + "developers/contracts/syntax/storage/private_state", + "developers/contracts/syntax/storage/public_state", + "developers/contracts/syntax/storage/storage_slots", + ], + }, + "developers/contracts/syntax/events", + { + label: "Functions", + type: "category", + link: { + type: "doc", + id: "developers/contracts/syntax/functions/main", + }, + items: [ + "developers/contracts/syntax/functions/public_private_unconstrained", + "developers/contracts/syntax/functions/visibility", + "developers/contracts/syntax/functions/constructor", + "developers/contracts/syntax/functions/calling_functions", + "developers/contracts/syntax/functions/oracles", + "developers/contracts/syntax/functions/inner_workings", + ], + }, + "developers/contracts/syntax/oracles", + { + label: "Proving Historical Blockchain Data", + type: "category", + items: [ + "developers/contracts/syntax/historical_access/how_to_prove_history", + "developers/contracts/syntax/historical_access/history_lib_reference", + ], }, - "dev_docs/contracts/syntax/events", - "dev_docs/contracts/syntax/functions", - "dev_docs/contracts/syntax/slow_updates_tree", - "dev_docs/contracts/syntax/context", - "dev_docs/contracts/syntax/globals", + "developers/contracts/syntax/slow_updates_tree", + + "developers/contracts/syntax/context", + "developers/contracts/syntax/globals", ], }, - "dev_docs/contracts/compiling", - "dev_docs/contracts/deploying", - "dev_docs/contracts/artifacts", + "developers/contracts/compiling", + "developers/contracts/deploying", + "developers/contracts/artifacts", { label: "Portals", type: "category", link: { type: "doc", - id: "dev_docs/contracts/portals/main", + id: "developers/contracts/portals/main", }, items: [ - "dev_docs/contracts/portals/data_structures", - "dev_docs/contracts/portals/registry", - "dev_docs/contracts/portals/inbox", - "dev_docs/contracts/portals/outbox", + "developers/contracts/portals/data_structures", + "developers/contracts/portals/registry", + "developers/contracts/portals/inbox", + "developers/contracts/portals/outbox", ], }, { label: "Resources", type: "category", items: [ - "dev_docs/contracts/resources/dependencies", - //"dev_docs/contracts/resources/style_guide", + "developers/contracts/resources/dependencies", + //"developers/contracts/resources/style_guide", { label: "Common Patterns", type: "category", link: { type: "doc", - id: "dev_docs/contracts/resources/common_patterns/main", + id: "developers/contracts/resources/common_patterns/main", }, items: [ - "dev_docs/contracts/resources/common_patterns/authwit", - // "dev_docs/contracts/resources/common_patterns/sending_tokens_to_user", - // "dev_docs/contracts/resources/common_patterns/sending_tokens_to_contract", - // "dev_docs/contracts/resources/common_patterns/access_control", - // "dev_docs/contracts/resources/common_patterns/interacting_with_l1", + "developers/contracts/resources/common_patterns/authwit", + // "developers/contracts/resources/common_patterns/sending_tokens_to_user", + // "developers/contracts/resources/common_patterns/sending_tokens_to_contract", + // "developers/contracts/resources/common_patterns/access_control", + // "developers/contracts/resources/common_patterns/interacting_with_l1", ], }, ], @@ -380,9 +375,9 @@ const sidebars = { // type: "category", // link: { // type: "doc", - // id: "dev_docs/contracts/security/breaking_changes/main", + // id: "developers/contracts/security/breaking_changes/main", // }, - // items: ["dev_docs/contracts/security/breaking_changes/v0"], + // items: ["developers/contracts/security/breaking_changes/v0"], // }, // ], // }, @@ -392,24 +387,24 @@ const sidebars = { { label: "Aztec.js", type: "doc", - id: "dev_docs/aztecjs/main", + id: "developers/aztecjs/main", }, { label: "Debugging", type: "category", link: { type: "doc", - id: "dev_docs/debugging/main", + id: "developers/debugging/main", }, items: [ - "dev_docs/debugging/aztecnr-errors", - "dev_docs/debugging/sandbox-errors", + "developers/debugging/aztecnr-errors", + "developers/debugging/sandbox-errors", ], }, { label: "Updating", type: "doc", - id: "dev_docs/updating", + id: "developers/updating", }, { @@ -417,22 +412,21 @@ const sidebars = { type: "category", link: { type: "doc", - id: "dev_docs/testing/main", + id: "developers/testing/main", }, - items: ["dev_docs/testing/cheat_codes"], + items: ["developers/testing/cheat_codes"], }, - { label: "Wallets", type: "category", link: { type: "doc", - id: "dev_docs/wallets/main", + id: "developers/wallets/main", }, items: [ - "dev_docs/wallets/architecture", - "dev_docs/wallets/writing_an_account_contract", - "dev_docs/wallets/creating_schnorr_accounts", + "developers/wallets/architecture", + "developers/wallets/writing_an_account_contract", + "developers/wallets/creating_schnorr_accounts", ], }, @@ -441,8 +435,8 @@ const sidebars = { type: "category", items: [], },*/ - "dev_docs/privacy/main", - "dev_docs/limitations/main", + "developers/privacy/main", + "developers/limitations/main", { label: "API Reference", @@ -476,11 +470,24 @@ const sidebars = { { type: "html", className: "sidebar-title", - value: "Miscellaneous", + value: "MISCELLANEOUS", defaultStyle: true, }, "misc/migration_notes", "misc/glossary", + { + label: "Roadmap", + type: "category", + link: { + type: "doc", + id: "misc/roadmap/main", + }, + items: [ + "misc/roadmap/features_initial_ldt", + "misc/roadmap/cryptography_roadmap", + ], + }, + "misc/how_to_contribute", { type: "html", diff --git a/l1-contracts/Dockerfile b/l1-contracts/Dockerfile index 5544b0eaa7b..f5e51731f9f 100644 --- a/l1-contracts/Dockerfile +++ b/l1-contracts/Dockerfile @@ -1,14 +1,6 @@ -# Linting requires node. -FROM node:18.19.0-alpine -RUN apk update && apk add --no-cache build-base git python3 curl bash jq -WORKDIR /usr/src/l1-contracts -COPY . . -RUN yarn && yarn lint - # Building requires foundry. -FROM ghcr.io/foundry-rs/foundry:nightly-c331b5eeee1b4151ef7354a081667e2d770b37f5 -# Required for foundry -RUN apk update && apk add git jq bash +FROM ghcr.io/foundry-rs/foundry:nightly-4a643801d0b3855934cdec778e33e79c79971783 +RUN apk update && apk add git jq bash nodejs npm yarn python3 py3-pip && pip3 install slither-analyzer slitherin WORKDIR /usr/src/l1-contracts COPY . . RUN git init @@ -17,4 +9,7 @@ RUN forge install --no-commit \ https://github.com/foundry-rs/forge-std \ https://github.com/openzeppelin/openzeppelin-contracts # Run build and tests -RUN forge clean && forge fmt --check && forge build && forge test \ No newline at end of file +RUN forge clean && forge fmt --check && forge build && forge test +RUN yarn && yarn lint +RUN git add . && yarn slither && yarn slither-has-diff +RUN forge build \ No newline at end of file diff --git a/l1-contracts/README.md b/l1-contracts/README.md index cacd6087454..258fb4cdedd 100644 --- a/l1-contracts/README.md +++ b/l1-contracts/README.md @@ -66,10 +66,25 @@ For now, this include bytecode for contract deployment, but over time this will We use an extended version of solhint (https://github.com/LHerskind/solhint) to include custom rules. These custom rules relate to how errors should be named, using custom errors instead of reverts etc, see `.solhint.json` for more specifics about the rules. -The linter is the only node module we need which is the reason behind not going full yarn-project on it. It is not part of the docker image, but can be run once in a while to make sure we are on track. +The linter is the only node module we need which is the reason behind not going full yarn-project on it. To run the linter, simply run: ```bash yarn lint ``` + +--- + +# Slither & Slitherin + +We use slither as an automatic way to find blunders and common vulnerabilities in our contracts. It is not part of the docker image due to its slowness, but it can be run using the following command to generate a markdown file with the results: +```bash +yarn slither +``` + +When this command is run in CI, it will fail if the markdown file generated in docker don't match the one in the repository. + +We assume that you already have slither installed. You can install it with `pip3 install slither-analyzer slitherin`. It is kept out of the bootstrap script as it is not a requirement for people who just want to run tests or are uninterested in the contracts. + +> We are not running the `naming-convention` detector because we have our own rules for naming which is enforced by the linter. \ No newline at end of file diff --git a/l1-contracts/package.json b/l1-contracts/package.json index 1969ca98520..006ddcd0228 100644 --- a/l1-contracts/package.json +++ b/l1-contracts/package.json @@ -7,6 +7,8 @@ "solhint": "https://github.com/LHerskind/solhint#master" }, "scripts": { - "lint": "solhint --config ./.solhint.json --fix \"src/**/*.sol\"" + "lint": "solhint --config ./.solhint.json --fix \"src/**/*.sol\"", + "slither": "forge clean && forge build --build-info --skip '*/test/**' --force && slither . --checklist --ignore-compile --show-ignored-findings --config-file ./slither.config.json | tee slither_output.md", + "slither-has-diff": "./slither_has_diff.sh" } } diff --git a/l1-contracts/slither.config.json b/l1-contracts/slither.config.json new file mode 100644 index 00000000000..c77991cc91c --- /dev/null +++ b/l1-contracts/slither.config.json @@ -0,0 +1,3 @@ +{ + "detectors_to_exclude": "naming-convention" +} diff --git a/l1-contracts/slither_has_diff.sh b/l1-contracts/slither_has_diff.sh new file mode 100755 index 00000000000..2489723a946 --- /dev/null +++ b/l1-contracts/slither_has_diff.sh @@ -0,0 +1,12 @@ +#!/bin/bash + +FILE="slither_output.md" + +DIFF_OUTPUT=$(git diff -- "$FILE") + +if [ -z "$DIFF_OUTPUT" ]; then + echo "No difference found." +else + echo "Difference found!" + exit 1 +fi diff --git a/l1-contracts/slither_output.md b/l1-contracts/slither_output.md new file mode 100644 index 00000000000..db4b5b391fd --- /dev/null +++ b/l1-contracts/slither_output.md @@ -0,0 +1,383 @@ +Summary + - [pess-unprotected-setter](#pess-unprotected-setter) (1 results) (High) + - [uninitialized-local](#uninitialized-local) (1 results) (Medium) + - [unused-return](#unused-return) (1 results) (Medium) + - [pess-dubious-typecast](#pess-dubious-typecast) (8 results) (Medium) + - [reentrancy-events](#reentrancy-events) (1 results) (Low) + - [timestamp](#timestamp) (4 results) (Low) + - [pess-public-vs-external](#pess-public-vs-external) (4 results) (Low) + - [assembly](#assembly) (5 results) (Informational) + - [dead-code](#dead-code) (13 results) (Informational) + - [solc-version](#solc-version) (1 results) (Informational) + - [low-level-calls](#low-level-calls) (1 results) (Informational) + - [similar-names](#similar-names) (1 results) (Informational) + - [unused-state](#unused-state) (2 results) (Informational) + - [pess-magic-number](#pess-magic-number) (1 results) (Informational) + - [constable-states](#constable-states) (1 results) (Optimization) +## pess-unprotected-setter +Impact: High +Confidence: Medium + - [ ] ID-0 +Function [Rollup.process(bytes,bytes32,bytes32,bytes,bytes)](src/core/Rollup.sol#L54-L94) is a non-protected setter archive is written + +src/core/Rollup.sol#L54-L94 + + +## uninitialized-local +Impact: Medium +Confidence: Medium + - [ ] ID-1 +[HeaderLib.decode(bytes).header](src/core/libraries/HeaderLib.sol#L133) is a local variable never initialized + +src/core/libraries/HeaderLib.sol#L133 + + +## unused-return +Impact: Medium +Confidence: Medium + - [ ] ID-2 +[Rollup.process(bytes,bytes32,bytes32,bytes,bytes)](src/core/Rollup.sol#L54-L94) ignores return value by [(inHash,l1ToL2Msgs,l2ToL1Msgs) = MessagesDecoder.decode(_body)](src/core/Rollup.sol#L71-L72) + +src/core/Rollup.sol#L54-L94 + + +## pess-dubious-typecast +Impact: Medium +Confidence: High + - [ ] ID-3 +Dubious typecast in [TxsDecoder.read4(bytes,uint256)](src/core/libraries/decoders/TxsDecoder.sol#L298-L300): + bytes => bytes4 casting occurs in [uint256(uint32(bytes4(slice(_data,_offset,4))))](src/core/libraries/decoders/TxsDecoder.sol#L299) + +src/core/libraries/decoders/TxsDecoder.sol#L298-L300 + + + - [ ] ID-4 +Dubious typecast in [Decoder.read4(bytes,uint256)](src/core/libraries/decoders/Decoder.sol#L415-L417): + bytes => bytes4 casting occurs in [uint256(uint32(bytes4(slice(_data,_offset,4))))](src/core/libraries/decoders/Decoder.sol#L416) + +src/core/libraries/decoders/Decoder.sol#L415-L417 + + + - [ ] ID-5 +Dubious typecast in [Outbox.sendL1Messages(bytes32[])](src/core/messagebridge/Outbox.sol#L38-L46): + uint256 => uint32 casting occurs in [version = uint32(REGISTRY.getVersionFor(msg.sender))](src/core/messagebridge/Outbox.sol#L40) + +src/core/messagebridge/Outbox.sol#L38-L46 + + + - [ ] ID-6 +Dubious typecast in [Inbox.sendL2Message(DataStructures.L2Actor,uint32,bytes32,bytes32)](src/core/messagebridge/Inbox.sol#L45-L91): + uint256 => uint64 casting occurs in [fee = uint64(msg.value)](src/core/messagebridge/Inbox.sol#L64) + uint256 => uint32 casting occurs in [entries.insert(key,fee,uint32(_recipient.version),_deadline,_errIncompatibleEntryArguments)](src/core/messagebridge/Inbox.sol#L76) + +src/core/messagebridge/Inbox.sol#L45-L91 + + + - [ ] ID-7 +Dubious typecast in [HeaderLib.decode(bytes)](src/core/libraries/HeaderLib.sol#L128-L167): + bytes => bytes32 casting occurs in [header.lastArchive = AppendOnlyTreeSnapshot(bytes32(_header),uint32(bytes4(_header)))](src/core/libraries/HeaderLib.sol#L136-L138) + bytes => bytes4 casting occurs in [header.lastArchive = AppendOnlyTreeSnapshot(bytes32(_header),uint32(bytes4(_header)))](src/core/libraries/HeaderLib.sol#L136-L138) + bytes => bytes32 casting occurs in [header.bodyHash = bytes32(_header)](src/core/libraries/HeaderLib.sol#L141) + bytes => bytes32 casting occurs in [header.stateReference.l1ToL2MessageTree = AppendOnlyTreeSnapshot(bytes32(_header),uint32(bytes4(_header)))](src/core/libraries/HeaderLib.sol#L144-L146) + bytes => bytes4 casting occurs in [header.stateReference.l1ToL2MessageTree = AppendOnlyTreeSnapshot(bytes32(_header),uint32(bytes4(_header)))](src/core/libraries/HeaderLib.sol#L144-L146) + bytes => bytes32 casting occurs in [header.stateReference.partialStateReference.noteHashTree = AppendOnlyTreeSnapshot(bytes32(_header),uint32(bytes4(_header)))](src/core/libraries/HeaderLib.sol#L147-L149) + bytes => bytes4 casting occurs in [header.stateReference.partialStateReference.noteHashTree = AppendOnlyTreeSnapshot(bytes32(_header),uint32(bytes4(_header)))](src/core/libraries/HeaderLib.sol#L147-L149) + bytes => bytes32 casting occurs in [header.stateReference.partialStateReference.nullifierTree = AppendOnlyTreeSnapshot(bytes32(_header),uint32(bytes4(_header)))](src/core/libraries/HeaderLib.sol#L150-L152) + bytes => bytes4 casting occurs in [header.stateReference.partialStateReference.nullifierTree = AppendOnlyTreeSnapshot(bytes32(_header),uint32(bytes4(_header)))](src/core/libraries/HeaderLib.sol#L150-L152) + bytes => bytes32 casting occurs in [header.stateReference.partialStateReference.contractTree = AppendOnlyTreeSnapshot(bytes32(_header),uint32(bytes4(_header)))](src/core/libraries/HeaderLib.sol#L153-L155) + bytes => bytes4 casting occurs in [header.stateReference.partialStateReference.contractTree = AppendOnlyTreeSnapshot(bytes32(_header),uint32(bytes4(_header)))](src/core/libraries/HeaderLib.sol#L153-L155) + bytes => bytes32 casting occurs in [header.stateReference.partialStateReference.publicDataTree = AppendOnlyTreeSnapshot(bytes32(_header),uint32(bytes4(_header)))](src/core/libraries/HeaderLib.sol#L156-L158) + bytes => bytes4 casting occurs in [header.stateReference.partialStateReference.publicDataTree = AppendOnlyTreeSnapshot(bytes32(_header),uint32(bytes4(_header)))](src/core/libraries/HeaderLib.sol#L156-L158) + bytes => bytes32 casting occurs in [header.globalVariables.chainId = uint256(bytes32(_header))](src/core/libraries/HeaderLib.sol#L161) + bytes => bytes32 casting occurs in [header.globalVariables.version = uint256(bytes32(_header))](src/core/libraries/HeaderLib.sol#L162) + bytes => bytes32 casting occurs in [header.globalVariables.blockNumber = uint256(bytes32(_header))](src/core/libraries/HeaderLib.sol#L163) + bytes => bytes32 casting occurs in [header.globalVariables.timestamp = uint256(bytes32(_header))](src/core/libraries/HeaderLib.sol#L164) + +src/core/libraries/HeaderLib.sol#L128-L167 + + + - [ ] ID-8 +Dubious typecast in [MessagesDecoder.read4(bytes,uint256)](src/core/libraries/decoders/MessagesDecoder.sol#L110-L112): + bytes => bytes4 casting occurs in [uint256(uint32(bytes4(_data)))](src/core/libraries/decoders/MessagesDecoder.sol#L111) + +src/core/libraries/decoders/MessagesDecoder.sol#L110-L112 + + + - [ ] ID-9 +Dubious typecast in [Inbox.batchConsume(bytes32[],address)](src/core/messagebridge/Inbox.sol#L122-L143): + uint256 => uint32 casting occurs in [expectedVersion = uint32(REGISTRY.getVersionFor(msg.sender))](src/core/messagebridge/Inbox.sol#L128) + +src/core/messagebridge/Inbox.sol#L122-L143 + + + - [ ] ID-10 +Dubious typecast in [Decoder.getL2BlockNumber(bytes)](src/core/libraries/decoders/Decoder.sol#L132-L134): + bytes => bytes32 casting occurs in [uint256(bytes32(_l2Block))](src/core/libraries/decoders/Decoder.sol#L133) + +src/core/libraries/decoders/Decoder.sol#L132-L134 + + +## reentrancy-events +Impact: Low +Confidence: Medium + - [ ] ID-11 +Reentrancy in [Rollup.process(bytes,bytes32,bytes32,bytes,bytes)](src/core/Rollup.sol#L54-L94): + External calls: + - [inbox.batchConsume(l1ToL2Msgs,msg.sender)](src/core/Rollup.sol#L88) + - [outbox.sendL1Messages(l2ToL1Msgs)](src/core/Rollup.sol#L91) + Event emitted after the call(s): + - [L2BlockProcessed(header.globalVariables.blockNumber)](src/core/Rollup.sol#L93) + +src/core/Rollup.sol#L54-L94 + + +## timestamp +Impact: Low +Confidence: Medium + - [ ] ID-12 +[Inbox.batchConsume(bytes32[],address)](src/core/messagebridge/Inbox.sol#L122-L143) uses timestamp for comparisons + Dangerous comparisons: + - [block.timestamp > entry.deadline](src/core/messagebridge/Inbox.sol#L136) + +src/core/messagebridge/Inbox.sol#L122-L143 + + + - [ ] ID-13 +[Inbox.sendL2Message(DataStructures.L2Actor,uint32,bytes32,bytes32)](src/core/messagebridge/Inbox.sol#L45-L91) uses timestamp for comparisons + Dangerous comparisons: + - [_deadline <= block.timestamp](src/core/messagebridge/Inbox.sol#L54) + +src/core/messagebridge/Inbox.sol#L45-L91 + + + - [ ] ID-14 +[HeaderLib.validate(HeaderLib.Header,uint256,uint256,bytes32)](src/core/libraries/HeaderLib.sol#L91-L121) uses timestamp for comparisons + Dangerous comparisons: + - [_header.globalVariables.timestamp > block.timestamp](src/core/libraries/HeaderLib.sol#L105) + +src/core/libraries/HeaderLib.sol#L91-L121 + + + - [ ] ID-15 +[Inbox.cancelL2Message(DataStructures.L1ToL2Msg,address)](src/core/messagebridge/Inbox.sol#L102-L113) uses timestamp for comparisons + Dangerous comparisons: + - [block.timestamp <= _message.deadline](src/core/messagebridge/Inbox.sol#L108) + +src/core/messagebridge/Inbox.sol#L102-L113 + + +## pess-public-vs-external +Impact: Low +Confidence: Medium + - [ ] ID-16 +The following public functions could be turned into external in [Registry](src/core/messagebridge/Registry.sol#L22-L129) contract: + [Registry.constructor()](src/core/messagebridge/Registry.sol#L29-L33) + +src/core/messagebridge/Registry.sol#L22-L129 + + + - [ ] ID-17 +The following public functions could be turned into external in [Rollup](src/core/Rollup.sol#L27-L103) contract: + [Rollup.constructor(IRegistry,IAvailabilityOracle)](src/core/Rollup.sol#L39-L44) + +src/core/Rollup.sol#L27-L103 + + + - [ ] ID-18 +The following public functions could be turned into external in [Outbox](src/core/messagebridge/Outbox.sol#L21-L149) contract: + [Outbox.constructor(address)](src/core/messagebridge/Outbox.sol#L29-L31) + [Outbox.get(bytes32)](src/core/messagebridge/Outbox.sol#L78-L85) + [Outbox.contains(bytes32)](src/core/messagebridge/Outbox.sol#L92-L94) + +src/core/messagebridge/Outbox.sol#L21-L149 + + + - [ ] ID-19 +The following public functions could be turned into external in [Inbox](src/core/messagebridge/Inbox.sol#L21-L231) contract: + [Inbox.constructor(address)](src/core/messagebridge/Inbox.sol#L30-L32) + [Inbox.contains(bytes32)](src/core/messagebridge/Inbox.sol#L174-L176) + +src/core/messagebridge/Inbox.sol#L21-L231 + + +## assembly +Impact: Informational +Confidence: High + - [ ] ID-20 +[Decoder.computeRoot(bytes32[])](src/core/libraries/decoders/Decoder.sol#L373-L392) uses assembly + - [INLINE ASM](src/core/libraries/decoders/Decoder.sol#L380-L382) + +src/core/libraries/decoders/Decoder.sol#L373-L392 + + + - [ ] ID-21 +[TxsDecoder.decode(bytes)](src/core/libraries/decoders/TxsDecoder.sol#L71-L184) uses assembly + - [INLINE ASM](src/core/libraries/decoders/TxsDecoder.sol#L98-L104) + +src/core/libraries/decoders/TxsDecoder.sol#L71-L184 + + + - [ ] ID-22 +[Decoder.computeConsumables(bytes)](src/core/libraries/decoders/Decoder.sol#L164-L301) uses assembly + - [INLINE ASM](src/core/libraries/decoders/Decoder.sol#L196-L202) + - [INLINE ASM](src/core/libraries/decoders/Decoder.sol#L289-L295) + +src/core/libraries/decoders/Decoder.sol#L164-L301 + + + - [ ] ID-23 +[TxsDecoder.computeRoot(bytes32[])](src/core/libraries/decoders/TxsDecoder.sol#L256-L275) uses assembly + - [INLINE ASM](src/core/libraries/decoders/TxsDecoder.sol#L263-L265) + +src/core/libraries/decoders/TxsDecoder.sol#L256-L275 + + + - [ ] ID-24 +[MessagesDecoder.decode(bytes)](src/core/libraries/decoders/MessagesDecoder.sol#L52-L102) uses assembly + - [INLINE ASM](src/core/libraries/decoders/MessagesDecoder.sol#L81-L83) + - [INLINE ASM](src/core/libraries/decoders/MessagesDecoder.sol#L94-L96) + +src/core/libraries/decoders/MessagesDecoder.sol#L52-L102 + + +## dead-code +Impact: Informational +Confidence: Medium + - [ ] ID-25 +[Decoder.computeConsumables(bytes)](src/core/libraries/decoders/Decoder.sol#L164-L301) is never used and should be removed + +src/core/libraries/decoders/Decoder.sol#L164-L301 + + + - [ ] ID-26 +[Inbox._errIncompatibleEntryArguments(bytes32,uint64,uint64,uint32,uint32,uint32,uint32)](src/core/messagebridge/Inbox.sol#L212-L230) is never used and should be removed + +src/core/messagebridge/Inbox.sol#L212-L230 + + + - [ ] ID-27 +[Decoder.slice(bytes,uint256,uint256)](src/core/libraries/decoders/Decoder.sol#L401-L407) is never used and should be removed + +src/core/libraries/decoders/Decoder.sol#L401-L407 + + + - [ ] ID-28 +[Outbox._errNothingToConsume(bytes32)](src/core/messagebridge/Outbox.sol#L115-L117) is never used and should be removed + +src/core/messagebridge/Outbox.sol#L115-L117 + + + - [ ] ID-29 +[Decoder.computeRoot(bytes32[])](src/core/libraries/decoders/Decoder.sol#L373-L392) is never used and should be removed + +src/core/libraries/decoders/Decoder.sol#L373-L392 + + + - [ ] ID-30 +[Hash.sha256ToField(bytes32)](src/core/libraries/Hash.sol#L59-L61) is never used and should be removed + +src/core/libraries/Hash.sol#L59-L61 + + + - [ ] ID-31 +[Decoder.computeKernelLogsHash(uint256,bytes)](src/core/libraries/decoders/Decoder.sol#L335-L365) is never used and should be removed + +src/core/libraries/decoders/Decoder.sol#L335-L365 + + + - [ ] ID-32 +[Decoder.read4(bytes,uint256)](src/core/libraries/decoders/Decoder.sol#L415-L417) is never used and should be removed + +src/core/libraries/decoders/Decoder.sol#L415-L417 + + + - [ ] ID-33 +[Decoder.computeStateHash(uint256,uint256,bytes)](src/core/libraries/decoders/Decoder.sol#L146-L154) is never used and should be removed + +src/core/libraries/decoders/Decoder.sol#L146-L154 + + + - [ ] ID-34 +[Decoder.computePublicInputHash(bytes,bytes32,bytes32)](src/core/libraries/decoders/Decoder.sol#L118-L125) is never used and should be removed + +src/core/libraries/decoders/Decoder.sol#L118-L125 + + + - [ ] ID-35 +[Inbox._errNothingToConsume(bytes32)](src/core/messagebridge/Inbox.sol#L197-L199) is never used and should be removed + +src/core/messagebridge/Inbox.sol#L197-L199 + + + - [ ] ID-36 +[Decoder.getL2BlockNumber(bytes)](src/core/libraries/decoders/Decoder.sol#L132-L134) is never used and should be removed + +src/core/libraries/decoders/Decoder.sol#L132-L134 + + + - [ ] ID-37 +[Outbox._errIncompatibleEntryArguments(bytes32,uint64,uint64,uint32,uint32,uint32,uint32)](src/core/messagebridge/Outbox.sol#L130-L148) is never used and should be removed + +src/core/messagebridge/Outbox.sol#L130-L148 + + +## solc-version +Impact: Informational +Confidence: High + - [ ] ID-38 +solc-0.8.21 is not recommended for deployment + +## low-level-calls +Impact: Informational +Confidence: High + - [ ] ID-39 +Low level call in [Inbox.withdrawFees()](src/core/messagebridge/Inbox.sol#L148-L153): + - [(success) = msg.sender.call{value: balance}()](src/core/messagebridge/Inbox.sol#L151) + +src/core/messagebridge/Inbox.sol#L148-L153 + + +## similar-names +Impact: Informational +Confidence: Medium + - [ ] ID-40 +Variable [Rollup.AVAILABILITY_ORACLE](src/core/Rollup.sol#L30) is too similar to [Rollup.constructor(IRegistry,IAvailabilityOracle)._availabilityOracle](src/core/Rollup.sol#L39) + +src/core/Rollup.sol#L30 + + +## unused-state +Impact: Informational +Confidence: High + - [ ] ID-41 +[Decoder.END_TREES_BLOCK_HEADER_OFFSET](src/core/libraries/decoders/Decoder.sol#L103-L104) is never used in [Decoder](src/core/libraries/decoders/Decoder.sol#L72-L418) + +src/core/libraries/decoders/Decoder.sol#L103-L104 + + + - [ ] ID-42 +[Decoder.BLOCK_HEADER_OFFSET](src/core/libraries/decoders/Decoder.sol#L107-L108) is never used in [Decoder](src/core/libraries/decoders/Decoder.sol#L72-L418) + +src/core/libraries/decoders/Decoder.sol#L107-L108 + + +## pess-magic-number +Impact: Informational +Confidence: High + - [ ] ID-43 +Magic number 376 is used multiple times in: + [_header.length != 376](src/core/libraries/HeaderLib.sol#L129) + [Errors.HeaderLib__InvalidHeaderSize(376,_header.length)](src/core/libraries/HeaderLib.sol#L130) + +src/core/libraries/HeaderLib.sol#L129 + + +## constable-states +Impact: Optimization +Confidence: High + - [ ] ID-44 +[Rollup.lastWarpedBlockTs](src/core/Rollup.sol#L37) should be constant + +src/core/Rollup.sol#L37 + + diff --git a/l1-contracts/src/core/libraries/ConstantsGen.sol b/l1-contracts/src/core/libraries/ConstantsGen.sol index 4b0aecbc3ce..6bf33898baa 100644 --- a/l1-contracts/src/core/libraries/ConstantsGen.sol +++ b/l1-contracts/src/core/libraries/ConstantsGen.sol @@ -24,6 +24,7 @@ library Constants { uint256 internal constant MAX_PUBLIC_DATA_UPDATE_REQUESTS_PER_CALL = 16; uint256 internal constant MAX_PUBLIC_DATA_READS_PER_CALL = 16; uint256 internal constant MAX_READ_REQUESTS_PER_CALL = 32; + uint256 internal constant MAX_NULLIFIER_KEY_VALIDATION_REQUESTS_PER_CALL = 1; uint256 internal constant MAX_NEW_COMMITMENTS_PER_TX = 64; uint256 internal constant MAX_NEW_NULLIFIERS_PER_TX = 64; uint256 internal constant MAX_PRIVATE_CALL_STACK_LENGTH_PER_TX = 8; @@ -34,6 +35,7 @@ library Constants { uint256 internal constant MAX_NEW_CONTRACTS_PER_TX = 1; uint256 internal constant MAX_OPTIONALLY_REVEALED_DATA_LENGTH_PER_TX = 4; uint256 internal constant MAX_READ_REQUESTS_PER_TX = 128; + uint256 internal constant MAX_NULLIFIER_KEY_VALIDATION_REQUESTS_PER_TX = 4; uint256 internal constant NUM_ENCRYPTED_LOGS_HASHES_PER_TX = 1; uint256 internal constant NUM_UNENCRYPTED_LOGS_HASHES_PER_TX = 1; uint256 internal constant NUMBER_OF_L1_L2_MESSAGES_PER_ROLLUP = 16; @@ -62,23 +64,23 @@ library Constants { uint256 internal constant ARGS_HASH_CHUNK_LENGTH = 32; uint256 internal constant ARGS_HASH_CHUNK_COUNT = 16; uint256 internal constant L1_TO_L2_MESSAGE_LENGTH = 8; - uint256 internal constant L1_TO_L2_MESSAGE_ORACLE_CALL_LENGTH = 26; + uint256 internal constant L1_TO_L2_MESSAGE_ORACLE_CALL_LENGTH = 25; uint256 internal constant MAX_NOTE_FIELDS_LENGTH = 20; uint256 internal constant GET_NOTE_ORACLE_RETURN_LENGTH = 23; uint256 internal constant MAX_NOTES_PER_PAGE = 10; uint256 internal constant VIEW_NOTE_ORACLE_RETURN_LENGTH = 212; uint256 internal constant CALL_CONTEXT_LENGTH = 8; - uint256 internal constant BLOCK_HEADER_LENGTH = 7; + uint256 internal constant HEADER_LENGTH = 18; uint256 internal constant FUNCTION_DATA_LENGTH = 4; uint256 internal constant CONTRACT_DEPLOYMENT_DATA_LENGTH = 6; - uint256 internal constant PRIVATE_CIRCUIT_PUBLIC_INPUTS_LENGTH = 189; + uint256 internal constant PRIVATE_CIRCUIT_PUBLIC_INPUTS_LENGTH = 200; uint256 internal constant CONTRACT_STORAGE_UPDATE_REQUEST_LENGTH = 3; uint256 internal constant CONTRACT_STORAGE_READ_LENGTH = 2; uint256 internal constant PUBLIC_CIRCUIT_PUBLIC_INPUTS_LENGTH = 190; uint256 internal constant GET_NOTES_ORACLE_RETURN_LENGTH = 674; - uint256 internal constant CALL_PRIVATE_FUNCTION_RETURN_SIZE = 195; - uint256 internal constant PUBLIC_CIRCUIT_PUBLIC_INPUTS_HASH_INPUT_LENGTH = 87; - uint256 internal constant PRIVATE_CIRCUIT_PUBLIC_INPUTS_HASH_INPUT_LENGTH = 177; + uint256 internal constant CALL_PRIVATE_FUNCTION_RETURN_SIZE = 210; + uint256 internal constant PUBLIC_CIRCUIT_PUBLIC_INPUTS_HASH_INPUT_LENGTH = 98; + uint256 internal constant PRIVATE_CIRCUIT_PUBLIC_INPUTS_HASH_INPUT_LENGTH = 188; uint256 internal constant COMMITMENTS_NUM_BYTES_PER_BASE_ROLLUP = 2048; uint256 internal constant NULLIFIERS_NUM_BYTES_PER_BASE_ROLLUP = 2048; uint256 internal constant PUBLIC_DATA_WRITES_NUM_BYTES_PER_BASE_ROLLUP = 1024; diff --git a/l1-contracts/src/core/libraries/Errors.sol b/l1-contracts/src/core/libraries/Errors.sol index 39ec6a692bc..568bbda37c6 100644 --- a/l1-contracts/src/core/libraries/Errors.sol +++ b/l1-contracts/src/core/libraries/Errors.sol @@ -59,4 +59,7 @@ library Errors { // Registry error Registry__RollupNotRegistered(address rollup); // 0xa1fee4cf error Registry__RollupAlreadyRegistered(address rollup); // 0x3c34eabf + + // HeaderLib + error HeaderLib__InvalidHeaderSize(uint256 expected, uint256 actual); // 0xf3ccb247 } diff --git a/l1-contracts/src/core/libraries/HeaderLib.sol b/l1-contracts/src/core/libraries/HeaderLib.sol index 894cd91ac6a..5ea03ee283c 100644 --- a/l1-contracts/src/core/libraries/HeaderLib.sol +++ b/l1-contracts/src/core/libraries/HeaderLib.sol @@ -22,29 +22,29 @@ import {Hash} from "./Hash.sol"; * | byte start | num bytes | name * | --- | --- | --- * | | | Header { - * | | | GlobalVariables { - * | 0x0000 | 0x20 | chainId - * | 0x0020 | 0x20 | version - * | 0x0040 | 0x20 | blockNumber - * | 0x0060 | 0x20 | timestamp - * | | | } + * | 0x0000 | 0x20 | lastArchive.root + * | 0x0020 | 0x04 | lastArchive.nextAvailableLeafIndex + * | 0x0024 | 0x20 | bodyHash * | | | StateReference { - * | 0x0080 | 0x20 | l1ToL2MessageTree.root - * | 0x00a0 | 0x04 | l1ToL2MessageTree.nextAvailableLeafIndex + * | 0x0044 | 0x20 | l1ToL2MessageTree.root + * | 0x0064 | 0x04 | l1ToL2MessageTree.nextAvailableLeafIndex * | | | PartialStateReference { - * | 0x00a4 | 0x20 | noteHashTree.root - * | 0x00c4 | 0x04 | noteHashTree.nextAvailableLeafIndex - * | 0x00c8 | 0x20 | nullifierTree.root - * | 0x00e8 | 0x04 | nullifierTree.nextAvailableLeafIndex - * | 0x00ec | 0x20 | contractTree.root - * | 0x010c | 0x04 | contractTree.nextAvailableLeafIndex - * | 0x0110 | 0x20 | publicDataTree.root - * | 0x0130 | 0x04 | publicDataTree.nextAvailableLeafIndex + * | 0x0068 | 0x20 | noteHashTree.root + * | 0x0088 | 0x04 | noteHashTree.nextAvailableLeafIndex + * | 0x008c | 0x20 | nullifierTree.root + * | 0x00ac | 0x04 | nullifierTree.nextAvailableLeafIndex + * | 0x00b0 | 0x20 | contractTree.root + * | 0x00d0 | 0x04 | contractTree.nextAvailableLeafIndex + * | 0x00d4 | 0x20 | publicDataTree.root + * | 0x00f4 | 0x04 | publicDataTree.nextAvailableLeafIndex * | | | } * | | | } - * | 0x0134 | 0x20 | lastArchive.root - * | 0x0154 | 0x04 | lastArchive.nextAvailableLeafIndex - * | 0x0158 | 0x20 | bodyHash + * | | | GlobalVariables { + * | 0x00f8 | 0x20 | chainId + * | 0x0118 | 0x20 | version + * | 0x0138 | 0x20 | blockNumber + * | 0x0158 | 0x20 | timestamp + * | | | } * | | | } * | --- | --- | --- */ @@ -54,13 +54,6 @@ library HeaderLib { uint32 nextAvailableLeafIndex; } - struct GlobalVariables { - uint256 chainId; - uint256 version; - uint256 blockNumber; - uint256 timestamp; - } - struct PartialStateReference { AppendOnlyTreeSnapshot noteHashTree; AppendOnlyTreeSnapshot nullifierTree; @@ -74,43 +67,18 @@ library HeaderLib { PartialStateReference partialStateReference; } + struct GlobalVariables { + uint256 chainId; + uint256 version; + uint256 blockNumber; + uint256 timestamp; + } + struct Header { - GlobalVariables globalVariables; - StateReference stateReference; AppendOnlyTreeSnapshot lastArchive; bytes32 bodyHash; - } - - /** - * @notice Decodes the header - * @param _header - The header calldata - * @return The decoded header - */ - function decode(bytes calldata _header) internal pure returns (Header memory) { - require(_header.length == 376, "Invalid header length"); - - Header memory header; - - header.globalVariables.chainId = uint256(bytes32(_header[:0x20])); - header.globalVariables.version = uint256(bytes32(_header[0x20:0x40])); - header.globalVariables.blockNumber = uint256(bytes32(_header[0x40:0x60])); - header.globalVariables.timestamp = uint256(bytes32(_header[0x60:0x80])); - header.stateReference.l1ToL2MessageTree = - AppendOnlyTreeSnapshot(bytes32(_header[0x80:0xa0]), uint32(bytes4(_header[0xa0:0xa4]))); - header.stateReference.partialStateReference.noteHashTree = - AppendOnlyTreeSnapshot(bytes32(_header[0xa4:0xc4]), uint32(bytes4(_header[0xc4:0xc8]))); - header.stateReference.partialStateReference.nullifierTree = - AppendOnlyTreeSnapshot(bytes32(_header[0xc8:0xe8]), uint32(bytes4(_header[0xe8:0xec]))); - header.stateReference.partialStateReference.contractTree = - AppendOnlyTreeSnapshot(bytes32(_header[0xec:0x10c]), uint32(bytes4(_header[0x10c:0x110]))); - header.stateReference.partialStateReference.publicDataTree = - AppendOnlyTreeSnapshot(bytes32(_header[0x110:0x130]), uint32(bytes4(_header[0x130:0x134]))); - header.lastArchive = - AppendOnlyTreeSnapshot(bytes32(_header[0x134:0x154]), uint32(bytes4(_header[0x154:0x158]))); - - header.bodyHash = bytes32(_header[0x158:0x178]); - - return header; + StateReference stateReference; + GlobalVariables globalVariables; } /** @@ -151,4 +119,50 @@ library HeaderLib { revert Errors.Rollup__InvalidArchive(_archive, _header.lastArchive.root); } } + + /** + * @notice Decodes the header + * @param _header - The header calldata + * @return The decoded header + */ + function decode(bytes calldata _header) internal pure returns (Header memory) { + if (_header.length != 376) { + revert Errors.HeaderLib__InvalidHeaderSize(376, _header.length); + } + + Header memory header; + + // Reading lastArchive + header.lastArchive = AppendOnlyTreeSnapshot( + bytes32(_header[0x0000:0x0020]), uint32(bytes4(_header[0x0020:0x0024])) + ); + + // Reading bodyHash + header.bodyHash = bytes32(_header[0x0024:0x0044]); + + // Reading StateReference + header.stateReference.l1ToL2MessageTree = AppendOnlyTreeSnapshot( + bytes32(_header[0x0044:0x0064]), uint32(bytes4(_header[0x0064:0x0068])) + ); + header.stateReference.partialStateReference.noteHashTree = AppendOnlyTreeSnapshot( + bytes32(_header[0x0068:0x0088]), uint32(bytes4(_header[0x0088:0x008c])) + ); + header.stateReference.partialStateReference.nullifierTree = AppendOnlyTreeSnapshot( + bytes32(_header[0x008c:0x00ac]), uint32(bytes4(_header[0x00ac:0x00b0])) + ); + header.stateReference.partialStateReference.contractTree = AppendOnlyTreeSnapshot( + bytes32(_header[0x00b0:0x00d0]), uint32(bytes4(_header[0x00d0:0x00d4])) + ); + header.stateReference.partialStateReference.publicDataTree = AppendOnlyTreeSnapshot( + bytes32(_header[0x00d4:0x00f4]), uint32(bytes4(_header[0x00f4:0x00f8])) + ); + + // Reading GlobalVariables + header.globalVariables.chainId = uint256(bytes32(_header[0x00f8:0x0118])); + header.globalVariables.version = uint256(bytes32(_header[0x0118:0x0138])); + header.globalVariables.blockNumber = uint256(bytes32(_header[0x0138:0x0158])); + header.globalVariables.timestamp = uint256(bytes32(_header[0x0158:0x0178])); + + return header; + } } diff --git a/l1-contracts/src/periphery/ContractDeploymentEmitter.sol b/l1-contracts/src/periphery/ContractDeploymentEmitter.sol index f2f791d7b45..efa55fc0f35 100644 --- a/l1-contracts/src/periphery/ContractDeploymentEmitter.sol +++ b/l1-contracts/src/periphery/ContractDeploymentEmitter.sol @@ -19,11 +19,11 @@ contract ContractDeploymentEmitter is IContractDeploymentEmitter { * @param _aztecAddress - The address of the L2 counterparty * @param _portalAddress - The address of the L1 counterparty * @param _l2BlockHash - The hash of the L2 block that this is related to - * @param _partialAddress - The partial address of the deployed contract - * @param _pubKeyX - The x coordinate of the contract's public key - * @param _pubKeyY - The y coordinate of the contract's public key + * @param _contractClassId - The contract class id + * @param _saltedInitializationHash - Salted init hash + * @param _publicKeyHash - Public key hash * @param _acir - The acir bytecode of the L2 contract - * @dev See the link bellow for more info on partial address and public key: + * @dev See the link below for more info on partial address and public key: * https://github.com/AztecProtocol/aztec-packages/blob/master/docs/docs/concepts/foundation/accounts/keys.md#addresses-partial-addresses-and-public-keys * TODO: replace the link above with the link to deployed docs */ @@ -32,9 +32,9 @@ contract ContractDeploymentEmitter is IContractDeploymentEmitter { bytes32 _aztecAddress, address _portalAddress, bytes32 _l2BlockHash, - bytes32 _partialAddress, - bytes32 _pubKeyX, - bytes32 _pubKeyY, + bytes32 _contractClassId, + bytes32 _saltedInitializationHash, + bytes32 _publicKeyHash, bytes calldata _acir ) external override(IContractDeploymentEmitter) { emit ContractDeployment( @@ -42,9 +42,9 @@ contract ContractDeploymentEmitter is IContractDeploymentEmitter { _aztecAddress, _portalAddress, _l2BlockHash, - _partialAddress, - _pubKeyX, - _pubKeyY, + _contractClassId, + _saltedInitializationHash, + _publicKeyHash, _acir ); } diff --git a/l1-contracts/src/periphery/interfaces/IContractDeploymentEmitter.sol b/l1-contracts/src/periphery/interfaces/IContractDeploymentEmitter.sol index f89142e58e9..9debce8240f 100644 --- a/l1-contracts/src/periphery/interfaces/IContractDeploymentEmitter.sol +++ b/l1-contracts/src/periphery/interfaces/IContractDeploymentEmitter.sol @@ -15,11 +15,11 @@ interface IContractDeploymentEmitter { * @param aztecAddress - The address of the L2 counterparty * @param portalAddress - The address of the L1 counterparty * @param l2BlockHash - The hash of the L2 block that this is related to - * @param partialAddress - The partial address of the deployed contract - * @param pubKeyX - The x coordinate of the contract's public key - * @param pubKeyY - The y coordinate of the contract's public key + * @param contractClassId - The contract class id + * @param saltedInitializationHash - Salted init hash + * @param publicKeyHash - Public key hash * @param acir - The acir bytecode of the L2 contract - * @dev See the link bellow for more info on partial address and public key: + * @dev See the link below for more info on partial address and public key: * https://github.com/AztecProtocol/aztec-packages/blob/master/docs/docs/concepts/foundation/accounts/keys.md#addresses-partial-addresses-and-public-keys * TODO: replace the link above with the link to deployed docs */ @@ -28,9 +28,9 @@ interface IContractDeploymentEmitter { bytes32 indexed aztecAddress, address indexed portalAddress, bytes32 l2BlockHash, - bytes32 partialAddress, - bytes32 pubKeyX, - bytes32 pubKeyY, + bytes32 contractClassId, + bytes32 saltedInitializationHash, + bytes32 publicKeyHash, bytes acir ); @@ -39,9 +39,9 @@ interface IContractDeploymentEmitter { bytes32 _aztecAddress, address _portalAddress, bytes32 _l2BlockHash, - bytes32 _partialAddress, - bytes32 _pubKeyX, - bytes32 _pubKeyY, + bytes32 _contractClassId, + bytes32 _saltedInitializationHash, + bytes32 _publicKeyHash, bytes calldata _acir ) external; } diff --git a/l1-contracts/test/Rollup.t.sol b/l1-contracts/test/Rollup.t.sol index 387dcbac6a8..d3a7dcb7ad2 100644 --- a/l1-contracts/test/Rollup.t.sol +++ b/l1-contracts/test/Rollup.t.sol @@ -67,7 +67,7 @@ contract RollupTest is DecoderBase { bytes memory body = data.body; assembly { - mstore(add(header, 0x20), 0x420) + mstore(add(header, add(0x20, 0x00f8)), 0x420) } bytes32 txsHash = availabilityOracle.publish(body); @@ -83,7 +83,7 @@ contract RollupTest is DecoderBase { bytes memory body = data.body; assembly { - mstore(add(header, 0x40), 0x420) + mstore(add(header, add(0x20, 0x0118)), 0x420) } bytes32 txsHash = availabilityOracle.publish(body); @@ -100,7 +100,7 @@ contract RollupTest is DecoderBase { uint256 ts = block.timestamp + 1; assembly { - mstore(add(header, 0x80), ts) + mstore(add(header, add(0x20, 0x0158)), ts) } bytes32 txsHash = availabilityOracle.publish(body); diff --git a/l1-contracts/test/decoders/Decoder.t.sol b/l1-contracts/test/decoders/Decoder.t.sol index e11586d30b4..cde3eee14b3 100644 --- a/l1-contracts/test/decoders/Decoder.t.sol +++ b/l1-contracts/test/decoders/Decoder.t.sol @@ -8,7 +8,7 @@ import {Hash} from "../../src/core/libraries/Hash.sol"; import {DataStructures} from "../../src/core/libraries/DataStructures.sol"; import {DecoderHelper} from "./helpers/DecoderHelper.sol"; -import {HeaderDecoderHelper} from "./helpers/HeaderDecoderHelper.sol"; +import {HeaderLibHelper} from "./helpers/HeaderLibHelper.sol"; import {MessagesDecoderHelper} from "./helpers/MessagesDecoderHelper.sol"; import {TxsDecoderHelper} from "./helpers/TxsDecoderHelper.sol"; import {HeaderLib} from "../../src/core/libraries/HeaderLib.sol"; @@ -27,13 +27,13 @@ import {AvailabilityOracle} from "../../src/core/availability_oracle/Availabilit */ contract DecoderTest is DecoderBase { DecoderHelper internal helper; - HeaderDecoderHelper internal headerHelper; + HeaderLibHelper internal headerHelper; MessagesDecoderHelper internal messagesHelper; TxsDecoderHelper internal txsHelper; function setUp() public virtual { helper = new DecoderHelper(); - headerHelper = new HeaderDecoderHelper(); + headerHelper = new HeaderLibHelper(); messagesHelper = new MessagesDecoderHelper(); txsHelper = new TxsDecoderHelper(); } diff --git a/l1-contracts/test/decoders/helpers/HeaderDecoderHelper.sol b/l1-contracts/test/decoders/helpers/HeaderLibHelper.sol similarity index 92% rename from l1-contracts/test/decoders/helpers/HeaderDecoderHelper.sol rename to l1-contracts/test/decoders/helpers/HeaderLibHelper.sol index 5d78b620b2a..7d839535a74 100644 --- a/l1-contracts/test/decoders/helpers/HeaderDecoderHelper.sol +++ b/l1-contracts/test/decoders/helpers/HeaderLibHelper.sol @@ -4,7 +4,7 @@ pragma solidity >=0.8.18; import {HeaderLib} from "../../../src/core/libraries/HeaderLib.sol"; -contract HeaderDecoderHelper { +contract HeaderLibHelper { // A wrapper used such that we get "calldata" and not memory function decode(bytes calldata _header) public pure returns (HeaderLib.Header memory) { return HeaderLib.decode(_header); diff --git a/l1-contracts/test/fixtures/empty_block_0.json b/l1-contracts/test/fixtures/empty_block_0.json index 69a1059fe90..bed87d58ed3 100644 --- a/l1-contracts/test/fixtures/empty_block_0.json +++ b/l1-contracts/test/fixtures/empty_block_0.json @@ -75,7 +75,7 @@ } } }, - "header": "0x0000000000000000000000000000000000000000000000000000000000007a690000000000000000000000000000000000000000000000000000000000000001000000000000000000000000000000000000000000000000000000000000000100000000000000000000000000000000000000000000000000000000000000001864fcdaa80ff2719154fa7c8a9050662972707168d69eac9db6fd3110829f800000001016642d9ccd8346c403aa4c3fa451178b22534a27035cdaa6ec34ae53b29c50cb000001000bcfa3e9f1a8922ee92c6dc964d6595907c1804a86753774322b468f69d4f278000001801864fcdaa80ff2719154fa7c8a9050662972707168d69eac9db6fd3110829f80000000041ed250ed73db6e70805c4efcf0056e8695b79cd3ba418e827c184dee6c6fb0e0000000601a005071a487e4891787073a91504fe6ea55280bc6f65a021fd6f7ca1f10aa02000000015b89af3c0d0bf66ac691697d317108e8f51bb8e3217d56f152b80867ad25d4a7", + "header": "0x1a005071a487e4891787073a91504fe6ea55280bc6f65a021fd6f7ca1f10aa02000000015b89af3c0d0bf66ac691697d317108e8f51bb8e3217d56f152b80867ad25d4a71864fcdaa80ff2719154fa7c8a9050662972707168d69eac9db6fd3110829f800000001016642d9ccd8346c403aa4c3fa451178b22534a27035cdaa6ec34ae53b29c50cb000001000bcfa3e9f1a8922ee92c6dc964d6595907c1804a86753774322b468f69d4f278000001801864fcdaa80ff2719154fa7c8a9050662972707168d69eac9db6fd3110829f80000000041ed250ed73db6e70805c4efcf0056e8695b79cd3ba418e827c184dee6c6fb0e0000000600000000000000000000000000000000000000000000000000000000000007a69000000000000000000000000000000000000000000000000000000000000000100000000000000000000000000000000000000000000000000000000000000010000000000000000000000000000000000000000000000000000000000000000", "l1ToL2MessagesHash": "0x076a27c79e5ace2a3d47f9dd2e83e4ff6ea8872b3c2218f66c92b89b55f36560", "publicInputsHash": "0x1476d6b0483d0d5fc4c9d3b04f50105d97a06bb01054ec7acccac3ff6c85f960" } diff --git a/l1-contracts/test/fixtures/empty_block_1.json b/l1-contracts/test/fixtures/empty_block_1.json index 006b03407f5..2d4639fc25f 100644 --- a/l1-contracts/test/fixtures/empty_block_1.json +++ b/l1-contracts/test/fixtures/empty_block_1.json @@ -35,7 +35,7 @@ ] }, "block": { - "archive": "0x01a7192428c21e1c55be4d35e253b2f30a48c4d7fdb3f4048d208986707b7906", + "archive": "0x2068f1adad2617364389ebe8a1eb54b969af5f5145be43080dce735a0edb0e3e", "body": "0x000001000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000001000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000400000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000008000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000400000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000010000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000010000000000000000000000000000000000000001000000000000000000000000000000000", "calldataHash": "0x5b89af3c0d0bf66ac691697d317108e8f51bb8e3217d56f152b80867ad25d4a7", "decodedHeader": { @@ -43,7 +43,7 @@ "globalVariables": { "blockNumber": 2, "chainId": 31337, - "timestamp": 1705501940, + "timestamp": 1706272901, "version": 1 }, "lastArchive": { @@ -75,8 +75,8 @@ } } }, - "header": "0x0000000000000000000000000000000000000000000000000000000000007a69000000000000000000000000000000000000000000000000000000000000000100000000000000000000000000000000000000000000000000000000000000020000000000000000000000000000000000000000000000000000000065a7e4f41864fcdaa80ff2719154fa7c8a9050662972707168d69eac9db6fd3110829f800000002016642d9ccd8346c403aa4c3fa451178b22534a27035cdaa6ec34ae53b29c50cb000002000bcfa3e9f1a8922ee92c6dc964d6595907c1804a86753774322b468f69d4f278000002801864fcdaa80ff2719154fa7c8a9050662972707168d69eac9db6fd3110829f80000000081ed250ed73db6e70805c4efcf0056e8695b79cd3ba418e827c184dee6c6fb0e0000000a01a11e8b1d440a44deef8aa8e4eec8f476e780ee6eac2d7d64b09c66b8063ee11000000025b89af3c0d0bf66ac691697d317108e8f51bb8e3217d56f152b80867ad25d4a7", + "header": "0x1a11e8b1d440a44deef8aa8e4eec8f476e780ee6eac2d7d64b09c66b8063ee11000000025b89af3c0d0bf66ac691697d317108e8f51bb8e3217d56f152b80867ad25d4a71864fcdaa80ff2719154fa7c8a9050662972707168d69eac9db6fd3110829f800000002016642d9ccd8346c403aa4c3fa451178b22534a27035cdaa6ec34ae53b29c50cb000002000bcfa3e9f1a8922ee92c6dc964d6595907c1804a86753774322b468f69d4f278000002801864fcdaa80ff2719154fa7c8a9050662972707168d69eac9db6fd3110829f80000000081ed250ed73db6e70805c4efcf0056e8695b79cd3ba418e827c184dee6c6fb0e0000000a00000000000000000000000000000000000000000000000000000000000007a69000000000000000000000000000000000000000000000000000000000000000100000000000000000000000000000000000000000000000000000000000000020000000000000000000000000000000000000000000000000000000065b3a885", "l1ToL2MessagesHash": "0x076a27c79e5ace2a3d47f9dd2e83e4ff6ea8872b3c2218f66c92b89b55f36560", - "publicInputsHash": "0x0730fec86325dbb2c6cb108f50628cf32f22f526411f8ef8e48fd8be5f43f536" + "publicInputsHash": "0x2c3372274a0c11108d634c3f133ae26232b662ca28c127f1710d92645d206b72" } } \ No newline at end of file diff --git a/l1-contracts/test/fixtures/mixed_block_0.json b/l1-contracts/test/fixtures/mixed_block_0.json index b9e7aea554e..11e14b9d7a4 100644 --- a/l1-contracts/test/fixtures/mixed_block_0.json +++ b/l1-contracts/test/fixtures/mixed_block_0.json @@ -53,10 +53,10 @@ }, "block": { "archive": "0x1f1de772c009f5b1660876343eb57b7a676a84c695b0c526de2f238c41810907", - "body": "0x000001000000000000000000000000000000000000000000000000000000000000000140000000000000000000000000000000000000000000000000000000000000014100000000000000000000000000000000000000000000000000000000000001420000000000000000000000000000000000000000000000000000000000000143000000000000000000000000000000000000000000000000000000000000014400000000000000000000000000000000000000000000000000000000000001450000000000000000000000000000000000000000000000000000000000000146000000000000000000000000000000000000000000000000000000000000014700000000000000000000000000000000000000000000000000000000000001480000000000000000000000000000000000000000000000000000000000000149000000000000000000000000000000000000000000000000000000000000014a000000000000000000000000000000000000000000000000000000000000014b000000000000000000000000000000000000000000000000000000000000014c000000000000000000000000000000000000000000000000000000000000014d000000000000000000000000000000000000000000000000000000000000014e000000000000000000000000000000000000000000000000000000000000014f0000000000000000000000000000000000000000000000000000000000000150000000000000000000000000000000000000000000000000000000000000015100000000000000000000000000000000000000000000000000000000000001520000000000000000000000000000000000000000000000000000000000000153000000000000000000000000000000000000000000000000000000000000015400000000000000000000000000000000000000000000000000000000000001550000000000000000000000000000000000000000000000000000000000000156000000000000000000000000000000000000000000000000000000000000015700000000000000000000000000000000000000000000000000000000000001580000000000000000000000000000000000000000000000000000000000000159000000000000000000000000000000000000000000000000000000000000015a000000000000000000000000000000000000000000000000000000000000015b000000000000000000000000000000000000000000000000000000000000015c000000000000000000000000000000000000000000000000000000000000015d000000000000000000000000000000000000000000000000000000000000015e000000000000000000000000000000000000000000000000000000000000015f0000000000000000000000000000000000000000000000000000000000000160000000000000000000000000000000000000000000000000000000000000016100000000000000000000000000000000000000000000000000000000000001620000000000000000000000000000000000000000000000000000000000000163000000000000000000000000000000000000000000000000000000000000016400000000000000000000000000000000000000000000000000000000000001650000000000000000000000000000000000000000000000000000000000000166000000000000000000000000000000000000000000000000000000000000016700000000000000000000000000000000000000000000000000000000000001680000000000000000000000000000000000000000000000000000000000000169000000000000000000000000000000000000000000000000000000000000016a000000000000000000000000000000000000000000000000000000000000016b000000000000000000000000000000000000000000000000000000000000016c000000000000000000000000000000000000000000000000000000000000016d000000000000000000000000000000000000000000000000000000000000016e000000000000000000000000000000000000000000000000000000000000016f0000000000000000000000000000000000000000000000000000000000000170000000000000000000000000000000000000000000000000000000000000017100000000000000000000000000000000000000000000000000000000000001720000000000000000000000000000000000000000000000000000000000000173000000000000000000000000000000000000000000000000000000000000017400000000000000000000000000000000000000000000000000000000000001750000000000000000000000000000000000000000000000000000000000000176000000000000000000000000000000000000000000000000000000000000017700000000000000000000000000000000000000000000000000000000000001780000000000000000000000000000000000000000000000000000000000000179000000000000000000000000000000000000000000000000000000000000017a000000000000000000000000000000000000000000000000000000000000017b000000000000000000000000000000000000000000000000000000000000017c000000000000000000000000000000000000000000000000000000000000017d000000000000000000000000000000000000000000000000000000000000017e000000000000000000000000000000000000000000000000000000000000017f0000000000000000000000000000000000000000000000000000000000000180000000000000000000000000000000000000000000000000000000000000018100000000000000000000000000000000000000000000000000000000000001820000000000000000000000000000000000000000000000000000000000000183000000000000000000000000000000000000000000000000000000000000018400000000000000000000000000000000000000000000000000000000000001850000000000000000000000000000000000000000000000000000000000000186000000000000000000000000000000000000000000000000000000000000018700000000000000000000000000000000000000000000000000000000000001880000000000000000000000000000000000000000000000000000000000000189000000000000000000000000000000000000000000000000000000000000018a000000000000000000000000000000000000000000000000000000000000018b000000000000000000000000000000000000000000000000000000000000018c000000000000000000000000000000000000000000000000000000000000018d000000000000000000000000000000000000000000000000000000000000018e000000000000000000000000000000000000000000000000000000000000018f0000000000000000000000000000000000000000000000000000000000000190000000000000000000000000000000000000000000000000000000000000019100000000000000000000000000000000000000000000000000000000000001920000000000000000000000000000000000000000000000000000000000000193000000000000000000000000000000000000000000000000000000000000019400000000000000000000000000000000000000000000000000000000000001950000000000000000000000000000000000000000000000000000000000000196000000000000000000000000000000000000000000000000000000000000019700000000000000000000000000000000000000000000000000000000000001980000000000000000000000000000000000000000000000000000000000000199000000000000000000000000000000000000000000000000000000000000019a000000000000000000000000000000000000000000000000000000000000019b000000000000000000000000000000000000000000000000000000000000019c000000000000000000000000000000000000000000000000000000000000019d000000000000000000000000000000000000000000000000000000000000019e000000000000000000000000000000000000000000000000000000000000019f00000000000000000000000000000000000000000000000000000000000001a000000000000000000000000000000000000000000000000000000000000001a100000000000000000000000000000000000000000000000000000000000001a200000000000000000000000000000000000000000000000000000000000001a300000000000000000000000000000000000000000000000000000000000001a400000000000000000000000000000000000000000000000000000000000001a500000000000000000000000000000000000000000000000000000000000001a600000000000000000000000000000000000000000000000000000000000001a700000000000000000000000000000000000000000000000000000000000001a800000000000000000000000000000000000000000000000000000000000001a900000000000000000000000000000000000000000000000000000000000001aa00000000000000000000000000000000000000000000000000000000000001ab00000000000000000000000000000000000000000000000000000000000001ac00000000000000000000000000000000000000000000000000000000000001ad00000000000000000000000000000000000000000000000000000000000001ae00000000000000000000000000000000000000000000000000000000000001af00000000000000000000000000000000000000000000000000000000000001b000000000000000000000000000000000000000000000000000000000000001b100000000000000000000000000000000000000000000000000000000000001b200000000000000000000000000000000000000000000000000000000000001b300000000000000000000000000000000000000000000000000000000000001b400000000000000000000000000000000000000000000000000000000000001b500000000000000000000000000000000000000000000000000000000000001b600000000000000000000000000000000000000000000000000000000000001b700000000000000000000000000000000000000000000000000000000000001b800000000000000000000000000000000000000000000000000000000000001b900000000000000000000000000000000000000000000000000000000000001ba00000000000000000000000000000000000000000000000000000000000001bb00000000000000000000000000000000000000000000000000000000000001bc00000000000000000000000000000000000000000000000000000000000001bd00000000000000000000000000000000000000000000000000000000000001be00000000000000000000000000000000000000000000000000000000000001bf00000000000000000000000000000000000000000000000000000000000001c000000000000000000000000000000000000000000000000000000000000001c100000000000000000000000000000000000000000000000000000000000001c200000000000000000000000000000000000000000000000000000000000001c300000000000000000000000000000000000000000000000000000000000001c400000000000000000000000000000000000000000000000000000000000001c500000000000000000000000000000000000000000000000000000000000001c600000000000000000000000000000000000000000000000000000000000001c700000000000000000000000000000000000000000000000000000000000001c800000000000000000000000000000000000000000000000000000000000001c900000000000000000000000000000000000000000000000000000000000001ca00000000000000000000000000000000000000000000000000000000000001cb00000000000000000000000000000000000000000000000000000000000001cc00000000000000000000000000000000000000000000000000000000000001cd00000000000000000000000000000000000000000000000000000000000001ce00000000000000000000000000000000000000000000000000000000000001cf00000000000000000000000000000000000000000000000000000000000001d000000000000000000000000000000000000000000000000000000000000001d100000000000000000000000000000000000000000000000000000000000001d200000000000000000000000000000000000000000000000000000000000001d300000000000000000000000000000000000000000000000000000000000001d400000000000000000000000000000000000000000000000000000000000001d500000000000000000000000000000000000000000000000000000000000001d600000000000000000000000000000000000000000000000000000000000001d700000000000000000000000000000000000000000000000000000000000001d800000000000000000000000000000000000000000000000000000000000001d900000000000000000000000000000000000000000000000000000000000001da00000000000000000000000000000000000000000000000000000000000001db00000000000000000000000000000000000000000000000000000000000001dc00000000000000000000000000000000000000000000000000000000000001dd00000000000000000000000000000000000000000000000000000000000001de00000000000000000000000000000000000000000000000000000000000001df00000000000000000000000000000000000000000000000000000000000001e000000000000000000000000000000000000000000000000000000000000001e100000000000000000000000000000000000000000000000000000000000001e200000000000000000000000000000000000000000000000000000000000001e300000000000000000000000000000000000000000000000000000000000001e400000000000000000000000000000000000000000000000000000000000001e500000000000000000000000000000000000000000000000000000000000001e600000000000000000000000000000000000000000000000000000000000001e700000000000000000000000000000000000000000000000000000000000001e800000000000000000000000000000000000000000000000000000000000001e900000000000000000000000000000000000000000000000000000000000001ea00000000000000000000000000000000000000000000000000000000000001eb00000000000000000000000000000000000000000000000000000000000001ec00000000000000000000000000000000000000000000000000000000000001ed00000000000000000000000000000000000000000000000000000000000001ee00000000000000000000000000000000000000000000000000000000000001ef00000000000000000000000000000000000000000000000000000000000001f000000000000000000000000000000000000000000000000000000000000001f100000000000000000000000000000000000000000000000000000000000001f200000000000000000000000000000000000000000000000000000000000001f300000000000000000000000000000000000000000000000000000000000001f400000000000000000000000000000000000000000000000000000000000001f500000000000000000000000000000000000000000000000000000000000001f600000000000000000000000000000000000000000000000000000000000001f700000000000000000000000000000000000000000000000000000000000001f800000000000000000000000000000000000000000000000000000000000001f900000000000000000000000000000000000000000000000000000000000001fa00000000000000000000000000000000000000000000000000000000000001fb00000000000000000000000000000000000000000000000000000000000001fc00000000000000000000000000000000000000000000000000000000000001fd00000000000000000000000000000000000000000000000000000000000001fe00000000000000000000000000000000000000000000000000000000000001ff0000000000000000000000000000000000000000000000000000000000000200000000000000000000000000000000000000000000000000000000000000020100000000000000000000000000000000000000000000000000000000000002020000000000000000000000000000000000000000000000000000000000000203000000000000000000000000000000000000000000000000000000000000020400000000000000000000000000000000000000000000000000000000000002050000000000000000000000000000000000000000000000000000000000000206000000000000000000000000000000000000000000000000000000000000020700000000000000000000000000000000000000000000000000000000000002080000000000000000000000000000000000000000000000000000000000000209000000000000000000000000000000000000000000000000000000000000020a000000000000000000000000000000000000000000000000000000000000020b000000000000000000000000000000000000000000000000000000000000020c000000000000000000000000000000000000000000000000000000000000020d000000000000000000000000000000000000000000000000000000000000020e000000000000000000000000000000000000000000000000000000000000020f0000000000000000000000000000000000000000000000000000000000000210000000000000000000000000000000000000000000000000000000000000021100000000000000000000000000000000000000000000000000000000000002120000000000000000000000000000000000000000000000000000000000000213000000000000000000000000000000000000000000000000000000000000021400000000000000000000000000000000000000000000000000000000000002150000000000000000000000000000000000000000000000000000000000000216000000000000000000000000000000000000000000000000000000000000021700000000000000000000000000000000000000000000000000000000000002180000000000000000000000000000000000000000000000000000000000000219000000000000000000000000000000000000000000000000000000000000021a000000000000000000000000000000000000000000000000000000000000021b000000000000000000000000000000000000000000000000000000000000021c000000000000000000000000000000000000000000000000000000000000021d000000000000000000000000000000000000000000000000000000000000021e000000000000000000000000000000000000000000000000000000000000021f0000000000000000000000000000000000000000000000000000000000000220000000000000000000000000000000000000000000000000000000000000022100000000000000000000000000000000000000000000000000000000000002220000000000000000000000000000000000000000000000000000000000000223000000000000000000000000000000000000000000000000000000000000022400000000000000000000000000000000000000000000000000000000000002250000000000000000000000000000000000000000000000000000000000000226000000000000000000000000000000000000000000000000000000000000022700000000000000000000000000000000000000000000000000000000000002280000000000000000000000000000000000000000000000000000000000000229000000000000000000000000000000000000000000000000000000000000022a000000000000000000000000000000000000000000000000000000000000022b000000000000000000000000000000000000000000000000000000000000022c000000000000000000000000000000000000000000000000000000000000022d000000000000000000000000000000000000000000000000000000000000022e000000000000000000000000000000000000000000000000000000000000022f0000000000000000000000000000000000000000000000000000000000000230000000000000000000000000000000000000000000000000000000000000023100000000000000000000000000000000000000000000000000000000000002320000000000000000000000000000000000000000000000000000000000000233000000000000000000000000000000000000000000000000000000000000023400000000000000000000000000000000000000000000000000000000000002350000000000000000000000000000000000000000000000000000000000000236000000000000000000000000000000000000000000000000000000000000023700000000000000000000000000000000000000000000000000000000000002380000000000000000000000000000000000000000000000000000000000000239000000000000000000000000000000000000000000000000000000000000023a000000000000000000000000000000000000000000000000000000000000023b000000000000000000000000000000000000000000000000000000000000023c000000000000000000000000000000000000000000000000000000000000023d000000000000000000000000000000000000000000000000000000000000023e000000000000000000000000000000000000000000000000000000000000023f000001000000000000000000000000000000000000000000000000000000000000000240000000000000000000000000000000000000000000000000000000000000024100000000000000000000000000000000000000000000000000000000000002420000000000000000000000000000000000000000000000000000000000000243000000000000000000000000000000000000000000000000000000000000024400000000000000000000000000000000000000000000000000000000000002450000000000000000000000000000000000000000000000000000000000000246000000000000000000000000000000000000000000000000000000000000024700000000000000000000000000000000000000000000000000000000000002480000000000000000000000000000000000000000000000000000000000000249000000000000000000000000000000000000000000000000000000000000024a000000000000000000000000000000000000000000000000000000000000024b000000000000000000000000000000000000000000000000000000000000024c000000000000000000000000000000000000000000000000000000000000024d000000000000000000000000000000000000000000000000000000000000024e000000000000000000000000000000000000000000000000000000000000024f0000000000000000000000000000000000000000000000000000000000000250000000000000000000000000000000000000000000000000000000000000025100000000000000000000000000000000000000000000000000000000000002520000000000000000000000000000000000000000000000000000000000000253000000000000000000000000000000000000000000000000000000000000025400000000000000000000000000000000000000000000000000000000000002550000000000000000000000000000000000000000000000000000000000000256000000000000000000000000000000000000000000000000000000000000025700000000000000000000000000000000000000000000000000000000000002580000000000000000000000000000000000000000000000000000000000000259000000000000000000000000000000000000000000000000000000000000025a000000000000000000000000000000000000000000000000000000000000025b000000000000000000000000000000000000000000000000000000000000025c000000000000000000000000000000000000000000000000000000000000025d000000000000000000000000000000000000000000000000000000000000025e000000000000000000000000000000000000000000000000000000000000025f0000000000000000000000000000000000000000000000000000000000000260000000000000000000000000000000000000000000000000000000000000026100000000000000000000000000000000000000000000000000000000000002620000000000000000000000000000000000000000000000000000000000000263000000000000000000000000000000000000000000000000000000000000026400000000000000000000000000000000000000000000000000000000000002650000000000000000000000000000000000000000000000000000000000000266000000000000000000000000000000000000000000000000000000000000026700000000000000000000000000000000000000000000000000000000000002680000000000000000000000000000000000000000000000000000000000000269000000000000000000000000000000000000000000000000000000000000026a000000000000000000000000000000000000000000000000000000000000026b000000000000000000000000000000000000000000000000000000000000026c000000000000000000000000000000000000000000000000000000000000026d000000000000000000000000000000000000000000000000000000000000026e000000000000000000000000000000000000000000000000000000000000026f0000000000000000000000000000000000000000000000000000000000000270000000000000000000000000000000000000000000000000000000000000027100000000000000000000000000000000000000000000000000000000000002720000000000000000000000000000000000000000000000000000000000000273000000000000000000000000000000000000000000000000000000000000027400000000000000000000000000000000000000000000000000000000000002750000000000000000000000000000000000000000000000000000000000000276000000000000000000000000000000000000000000000000000000000000027700000000000000000000000000000000000000000000000000000000000002780000000000000000000000000000000000000000000000000000000000000279000000000000000000000000000000000000000000000000000000000000027a000000000000000000000000000000000000000000000000000000000000027b000000000000000000000000000000000000000000000000000000000000027c000000000000000000000000000000000000000000000000000000000000027d000000000000000000000000000000000000000000000000000000000000027e00000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000280000000000000000000000000000000000000000000000000000000000000028100000000000000000000000000000000000000000000000000000000000002820000000000000000000000000000000000000000000000000000000000000283000000000000000000000000000000000000000000000000000000000000028400000000000000000000000000000000000000000000000000000000000002850000000000000000000000000000000000000000000000000000000000000286000000000000000000000000000000000000000000000000000000000000028700000000000000000000000000000000000000000000000000000000000002880000000000000000000000000000000000000000000000000000000000000289000000000000000000000000000000000000000000000000000000000000028a000000000000000000000000000000000000000000000000000000000000028b000000000000000000000000000000000000000000000000000000000000028c000000000000000000000000000000000000000000000000000000000000028d000000000000000000000000000000000000000000000000000000000000028e000000000000000000000000000000000000000000000000000000000000028f0000000000000000000000000000000000000000000000000000000000000290000000000000000000000000000000000000000000000000000000000000029100000000000000000000000000000000000000000000000000000000000002920000000000000000000000000000000000000000000000000000000000000293000000000000000000000000000000000000000000000000000000000000029400000000000000000000000000000000000000000000000000000000000002950000000000000000000000000000000000000000000000000000000000000296000000000000000000000000000000000000000000000000000000000000029700000000000000000000000000000000000000000000000000000000000002980000000000000000000000000000000000000000000000000000000000000299000000000000000000000000000000000000000000000000000000000000029a000000000000000000000000000000000000000000000000000000000000029b000000000000000000000000000000000000000000000000000000000000029c000000000000000000000000000000000000000000000000000000000000029d000000000000000000000000000000000000000000000000000000000000029e000000000000000000000000000000000000000000000000000000000000029f00000000000000000000000000000000000000000000000000000000000002a000000000000000000000000000000000000000000000000000000000000002a100000000000000000000000000000000000000000000000000000000000002a200000000000000000000000000000000000000000000000000000000000002a300000000000000000000000000000000000000000000000000000000000002a400000000000000000000000000000000000000000000000000000000000002a500000000000000000000000000000000000000000000000000000000000002a600000000000000000000000000000000000000000000000000000000000002a700000000000000000000000000000000000000000000000000000000000002a800000000000000000000000000000000000000000000000000000000000002a900000000000000000000000000000000000000000000000000000000000002aa00000000000000000000000000000000000000000000000000000000000002ab00000000000000000000000000000000000000000000000000000000000002ac00000000000000000000000000000000000000000000000000000000000002ad00000000000000000000000000000000000000000000000000000000000002ae00000000000000000000000000000000000000000000000000000000000002af00000000000000000000000000000000000000000000000000000000000002b000000000000000000000000000000000000000000000000000000000000002b100000000000000000000000000000000000000000000000000000000000002b200000000000000000000000000000000000000000000000000000000000002b300000000000000000000000000000000000000000000000000000000000002b400000000000000000000000000000000000000000000000000000000000002b500000000000000000000000000000000000000000000000000000000000002b600000000000000000000000000000000000000000000000000000000000002b700000000000000000000000000000000000000000000000000000000000002b800000000000000000000000000000000000000000000000000000000000002b900000000000000000000000000000000000000000000000000000000000002ba00000000000000000000000000000000000000000000000000000000000002bb00000000000000000000000000000000000000000000000000000000000002bc00000000000000000000000000000000000000000000000000000000000002bd00000000000000000000000000000000000000000000000000000000000002be000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000002c000000000000000000000000000000000000000000000000000000000000002c100000000000000000000000000000000000000000000000000000000000002c200000000000000000000000000000000000000000000000000000000000002c300000000000000000000000000000000000000000000000000000000000002c400000000000000000000000000000000000000000000000000000000000002c500000000000000000000000000000000000000000000000000000000000002c600000000000000000000000000000000000000000000000000000000000002c700000000000000000000000000000000000000000000000000000000000002c800000000000000000000000000000000000000000000000000000000000002c900000000000000000000000000000000000000000000000000000000000002ca00000000000000000000000000000000000000000000000000000000000002cb00000000000000000000000000000000000000000000000000000000000002cc00000000000000000000000000000000000000000000000000000000000002cd00000000000000000000000000000000000000000000000000000000000002ce00000000000000000000000000000000000000000000000000000000000002cf00000000000000000000000000000000000000000000000000000000000002d000000000000000000000000000000000000000000000000000000000000002d100000000000000000000000000000000000000000000000000000000000002d200000000000000000000000000000000000000000000000000000000000002d300000000000000000000000000000000000000000000000000000000000002d400000000000000000000000000000000000000000000000000000000000002d500000000000000000000000000000000000000000000000000000000000002d600000000000000000000000000000000000000000000000000000000000002d700000000000000000000000000000000000000000000000000000000000002d800000000000000000000000000000000000000000000000000000000000002d900000000000000000000000000000000000000000000000000000000000002da00000000000000000000000000000000000000000000000000000000000002db00000000000000000000000000000000000000000000000000000000000002dc00000000000000000000000000000000000000000000000000000000000002dd00000000000000000000000000000000000000000000000000000000000002de00000000000000000000000000000000000000000000000000000000000002df00000000000000000000000000000000000000000000000000000000000002e000000000000000000000000000000000000000000000000000000000000002e100000000000000000000000000000000000000000000000000000000000002e200000000000000000000000000000000000000000000000000000000000002e300000000000000000000000000000000000000000000000000000000000002e400000000000000000000000000000000000000000000000000000000000002e500000000000000000000000000000000000000000000000000000000000002e600000000000000000000000000000000000000000000000000000000000002e700000000000000000000000000000000000000000000000000000000000002e800000000000000000000000000000000000000000000000000000000000002e900000000000000000000000000000000000000000000000000000000000002ea00000000000000000000000000000000000000000000000000000000000002eb00000000000000000000000000000000000000000000000000000000000002ec00000000000000000000000000000000000000000000000000000000000002ed00000000000000000000000000000000000000000000000000000000000002ee00000000000000000000000000000000000000000000000000000000000002ef00000000000000000000000000000000000000000000000000000000000002f000000000000000000000000000000000000000000000000000000000000002f100000000000000000000000000000000000000000000000000000000000002f200000000000000000000000000000000000000000000000000000000000002f300000000000000000000000000000000000000000000000000000000000002f400000000000000000000000000000000000000000000000000000000000002f500000000000000000000000000000000000000000000000000000000000002f600000000000000000000000000000000000000000000000000000000000002f700000000000000000000000000000000000000000000000000000000000002f800000000000000000000000000000000000000000000000000000000000002f900000000000000000000000000000000000000000000000000000000000002fa00000000000000000000000000000000000000000000000000000000000002fb00000000000000000000000000000000000000000000000000000000000002fc00000000000000000000000000000000000000000000000000000000000002fd00000000000000000000000000000000000000000000000000000000000002fe00000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000300000000000000000000000000000000000000000000000000000000000000030100000000000000000000000000000000000000000000000000000000000003020000000000000000000000000000000000000000000000000000000000000303000000000000000000000000000000000000000000000000000000000000030400000000000000000000000000000000000000000000000000000000000003050000000000000000000000000000000000000000000000000000000000000306000000000000000000000000000000000000000000000000000000000000030700000000000000000000000000000000000000000000000000000000000003080000000000000000000000000000000000000000000000000000000000000309000000000000000000000000000000000000000000000000000000000000030a000000000000000000000000000000000000000000000000000000000000030b000000000000000000000000000000000000000000000000000000000000030c000000000000000000000000000000000000000000000000000000000000030d000000000000000000000000000000000000000000000000000000000000030e000000000000000000000000000000000000000000000000000000000000030f0000000000000000000000000000000000000000000000000000000000000310000000000000000000000000000000000000000000000000000000000000031100000000000000000000000000000000000000000000000000000000000003120000000000000000000000000000000000000000000000000000000000000313000000000000000000000000000000000000000000000000000000000000031400000000000000000000000000000000000000000000000000000000000003150000000000000000000000000000000000000000000000000000000000000316000000000000000000000000000000000000000000000000000000000000031700000000000000000000000000000000000000000000000000000000000003180000000000000000000000000000000000000000000000000000000000000319000000000000000000000000000000000000000000000000000000000000031a000000000000000000000000000000000000000000000000000000000000031b000000000000000000000000000000000000000000000000000000000000031c000000000000000000000000000000000000000000000000000000000000031d000000000000000000000000000000000000000000000000000000000000031e000000000000000000000000000000000000000000000000000000000000031f0000000000000000000000000000000000000000000000000000000000000320000000000000000000000000000000000000000000000000000000000000032100000000000000000000000000000000000000000000000000000000000003220000000000000000000000000000000000000000000000000000000000000323000000000000000000000000000000000000000000000000000000000000032400000000000000000000000000000000000000000000000000000000000003250000000000000000000000000000000000000000000000000000000000000326000000000000000000000000000000000000000000000000000000000000032700000000000000000000000000000000000000000000000000000000000003280000000000000000000000000000000000000000000000000000000000000329000000000000000000000000000000000000000000000000000000000000032a000000000000000000000000000000000000000000000000000000000000032b000000000000000000000000000000000000000000000000000000000000032c000000000000000000000000000000000000000000000000000000000000032d000000000000000000000000000000000000000000000000000000000000032e000000000000000000000000000000000000000000000000000000000000032f0000000000000000000000000000000000000000000000000000000000000330000000000000000000000000000000000000000000000000000000000000033100000000000000000000000000000000000000000000000000000000000003320000000000000000000000000000000000000000000000000000000000000333000000000000000000000000000000000000000000000000000000000000033400000000000000000000000000000000000000000000000000000000000003350000000000000000000000000000000000000000000000000000000000000336000000000000000000000000000000000000000000000000000000000000033700000000000000000000000000000000000000000000000000000000000003380000000000000000000000000000000000000000000000000000000000000339000000000000000000000000000000000000000000000000000000000000033a000000000000000000000000000000000000000000000000000000000000033b000000000000000000000000000000000000000000000000000000000000033c000000000000000000000000000000000000000000000000000000000000033d000000000000000000000000000000000000000000000000000000000000033e0000000000000000000000000000000000000000000000000000000000000000000000400000000000000000000000000000000000000000000000000000000000000540000000000000000000000000000000000000000000000000000000000000054a0000000000000000000000000000000000000000000000000000000000000541000000000000000000000000000000000000000000000000000000000000054b0000000000000000000000000000000000000000000000000000000000000542000000000000000000000000000000000000000000000000000000000000054c0000000000000000000000000000000000000000000000000000000000000543000000000000000000000000000000000000000000000000000000000000054d0000000000000000000000000000000000000000000000000000000000000544000000000000000000000000000000000000000000000000000000000000054e0000000000000000000000000000000000000000000000000000000000000545000000000000000000000000000000000000000000000000000000000000054f00000000000000000000000000000000000000000000000000000000000005460000000000000000000000000000000000000000000000000000000000000550000000000000000000000000000000000000000000000000000000000000054700000000000000000000000000000000000000000000000000000000000005510000000000000000000000000000000000000000000000000000000000000548000000000000000000000000000000000000000000000000000000000000055200000000000000000000000000000000000000000000000000000000000005490000000000000000000000000000000000000000000000000000000000000553000000000000000000000000000000000000000000000000000000000000054a0000000000000000000000000000000000000000000000000000000000000554000000000000000000000000000000000000000000000000000000000000054b0000000000000000000000000000000000000000000000000000000000000555000000000000000000000000000000000000000000000000000000000000054c0000000000000000000000000000000000000000000000000000000000000556000000000000000000000000000000000000000000000000000000000000054d0000000000000000000000000000000000000000000000000000000000000557000000000000000000000000000000000000000000000000000000000000054e0000000000000000000000000000000000000000000000000000000000000558000000000000000000000000000000000000000000000000000000000000054f00000000000000000000000000000000000000000000000000000000000005590000000000000000000000000000000000000000000000000000000000000580000000000000000000000000000000000000000000000000000000000000058a0000000000000000000000000000000000000000000000000000000000000581000000000000000000000000000000000000000000000000000000000000058b0000000000000000000000000000000000000000000000000000000000000582000000000000000000000000000000000000000000000000000000000000058c0000000000000000000000000000000000000000000000000000000000000583000000000000000000000000000000000000000000000000000000000000058d0000000000000000000000000000000000000000000000000000000000000584000000000000000000000000000000000000000000000000000000000000058e0000000000000000000000000000000000000000000000000000000000000585000000000000000000000000000000000000000000000000000000000000058f00000000000000000000000000000000000000000000000000000000000005860000000000000000000000000000000000000000000000000000000000000590000000000000000000000000000000000000000000000000000000000000058700000000000000000000000000000000000000000000000000000000000005910000000000000000000000000000000000000000000000000000000000000588000000000000000000000000000000000000000000000000000000000000059200000000000000000000000000000000000000000000000000000000000005890000000000000000000000000000000000000000000000000000000000000593000000000000000000000000000000000000000000000000000000000000058a0000000000000000000000000000000000000000000000000000000000000594000000000000000000000000000000000000000000000000000000000000058b0000000000000000000000000000000000000000000000000000000000000595000000000000000000000000000000000000000000000000000000000000058c0000000000000000000000000000000000000000000000000000000000000596000000000000000000000000000000000000000000000000000000000000058d0000000000000000000000000000000000000000000000000000000000000597000000000000000000000000000000000000000000000000000000000000058e0000000000000000000000000000000000000000000000000000000000000598000000000000000000000000000000000000000000000000000000000000058f000000000000000000000000000000000000000000000000000000000000059900000000000000000000000000000000000000000000000000000000000005c000000000000000000000000000000000000000000000000000000000000005ca00000000000000000000000000000000000000000000000000000000000005c100000000000000000000000000000000000000000000000000000000000005cb00000000000000000000000000000000000000000000000000000000000005c200000000000000000000000000000000000000000000000000000000000005cc00000000000000000000000000000000000000000000000000000000000005c300000000000000000000000000000000000000000000000000000000000005cd00000000000000000000000000000000000000000000000000000000000005c400000000000000000000000000000000000000000000000000000000000005ce00000000000000000000000000000000000000000000000000000000000005c500000000000000000000000000000000000000000000000000000000000005cf00000000000000000000000000000000000000000000000000000000000005c600000000000000000000000000000000000000000000000000000000000005d000000000000000000000000000000000000000000000000000000000000005c700000000000000000000000000000000000000000000000000000000000005d100000000000000000000000000000000000000000000000000000000000005c800000000000000000000000000000000000000000000000000000000000005d200000000000000000000000000000000000000000000000000000000000005c900000000000000000000000000000000000000000000000000000000000005d300000000000000000000000000000000000000000000000000000000000005ca00000000000000000000000000000000000000000000000000000000000005d400000000000000000000000000000000000000000000000000000000000005cb00000000000000000000000000000000000000000000000000000000000005d500000000000000000000000000000000000000000000000000000000000005cc00000000000000000000000000000000000000000000000000000000000005d600000000000000000000000000000000000000000000000000000000000005cd00000000000000000000000000000000000000000000000000000000000005d700000000000000000000000000000000000000000000000000000000000005ce00000000000000000000000000000000000000000000000000000000000005d800000000000000000000000000000000000000000000000000000000000005cf00000000000000000000000000000000000000000000000000000000000005d90000000000000000000000000000000000000000000000000000000000000600000000000000000000000000000000000000000000000000000000000000060a0000000000000000000000000000000000000000000000000000000000000601000000000000000000000000000000000000000000000000000000000000060b0000000000000000000000000000000000000000000000000000000000000602000000000000000000000000000000000000000000000000000000000000060c0000000000000000000000000000000000000000000000000000000000000603000000000000000000000000000000000000000000000000000000000000060d0000000000000000000000000000000000000000000000000000000000000604000000000000000000000000000000000000000000000000000000000000060e0000000000000000000000000000000000000000000000000000000000000605000000000000000000000000000000000000000000000000000000000000060f00000000000000000000000000000000000000000000000000000000000006060000000000000000000000000000000000000000000000000000000000000610000000000000000000000000000000000000000000000000000000000000060700000000000000000000000000000000000000000000000000000000000006110000000000000000000000000000000000000000000000000000000000000608000000000000000000000000000000000000000000000000000000000000061200000000000000000000000000000000000000000000000000000000000006090000000000000000000000000000000000000000000000000000000000000613000000000000000000000000000000000000000000000000000000000000060a0000000000000000000000000000000000000000000000000000000000000614000000000000000000000000000000000000000000000000000000000000060b0000000000000000000000000000000000000000000000000000000000000615000000000000000000000000000000000000000000000000000000000000060c0000000000000000000000000000000000000000000000000000000000000616000000000000000000000000000000000000000000000000000000000000060d0000000000000000000000000000000000000000000000000000000000000617000000000000000000000000000000000000000000000000000000000000060e0000000000000000000000000000000000000000000000000000000000000618000000000000000000000000000000000000000000000000000000000000060f000000000000000000000000000000000000000000000000000000000000061900000008000000000000000000000000000000000000000000000000000000000000034000000000000000000000000000000000000000000000000000000000000003410000000000000000000000000000000000000000000000000000000000000380000000000000000000000000000000000000000000000000000000000000038100000000000000000000000000000000000000000000000000000000000003c000000000000000000000000000000000000000000000000000000000000003c100000000000000000000000000000000000000000000000000000000000004000000000000000000000000000000000000000000000000000000000000000401000000041c72e330b60e0052863df88b19ebb482d412b0a7bbb2fabb9347be5e59453d10078d263242fac5a2ad544b5005bcea367ebc04e01ff7ca279650bc63d9488ac304ad4d395e21ea570da47bf574569c6526312c5c7ad63a6d248f694e5c81c2b60e12693b411d9eb8f564aa43f4aa14e3e30f8574425a6d6491416332b77d2709000000000000000000000000000000000000000000000000000000000000104041414141414141414141414141414141414141410000000000000000000000000000000000000000000000000000000000001080818181818181818181818181818181818181818100000000000000000000000000000000000000000000000000000000000010c0c1c1c1c1c1c1c1c1c1c1c1c1c1c1c1c1c1c1c1c10000000000000000000000000000000000000000000000000000000000001100010101010101010101010101010101010101010100000010151de48ca3efbae39f180fe00b8f472ec9f25be10b4f283a87c6d7839353703914c2ea9dedf77698d4afe23bc663263eed0bf9aa3a8b17d9b74812f185610f9e1570cc6641699e3ae87fa258d80a6d853f7b8ccb211dc244d017e2ca6530f8a12806c860af67e9cd50000378411b8c4c4db172ceb2daa862b259b689ccbdc1e005f140c7c95624c8006774279a01ec1ea88617999e4fe6997b6576c4e1c7395a22048b96b586596bd740d0402e15f5577f7ceb5496b65aafc6d89d7c3b34924b0c3f2d50d16279970d682cada30bfa6b29bc0bac0ee2389f6a0444853eccaa932b2a60561da46a58569d71044a84c639e7f88429826e5622581536eb906d9cdd25a2c0a76f7da6924e10751c755227d2535f4ad258b984e78f9f452a853c52300e212d8e2069e4254d81af07744bcbb81121a38f0e2dbed69a523d3fbf85b75c287ca6f33aadbac2e4f058e05924c140d7895a6ed167caf804b710d2ae3ba62b1b51297b3ea37637af6bd56cf33425d95cc5c96e9c2ee3077322fbec86a0c7f32c15d2a888c6cc122e99478c92470a1311635142d82ad7ae67410beeef4ae31f0902ba2fb964922a4610bb18901f7b923885c1d034da5769a48203ae6f0206a92855e2c01ddb3d6553386b5580d681b8230fa4062948668f834f23e0636eaff70aaa64519aafdf4b040bd2f9836e76b9dc13cfec8065dcdf2834d786e06260d10000381000000e00000001bc00000090083367367613e6307621401e0b943e65d00d94f7edba399d403da5d93b1a260922e76adec1b929ab590c851320486c68244084a7f9ea21e5ece1e5dfe99ed6069d12051b95f1210b468eb7a029d2b437107a5a246cc1ba5c8f590f4db407e035a353dc7913b80b52296e05af286de4e002586c55a0c4914e946268a214e0caa1f040bd17a9a93471faffd36dc9835a0e00000090ead4b0c12eaa3254338bbcb40631f1f09b696da71b404cbf7415a1ee3b767d9943920b1dc8deeafa855a44f74bc38c4a74537b01cbb09f8612e4256575c2931a98ec9fcc5892ec9da59eb4807ad5fc0613222acb0c141fe55ec8bca4afdef1de719ac262d7ffea40a4d8d5bfe4ca69c10f624cd3f2f922fd8f5a10a7f212bec0c876327251624bfd7d777ac8884b62ee000000908cd0590d2d18f1214ff5b03b6a1d4cfebfd3e463e1b8e98b81fef46a5ba1b0204686f7c57a65f0bc5ce8bbdfc658c1f6bf4e52b5494ea72f193706883d6f1e5949ecd47315b3e2d1e75208e1c1ac3ba2079c257743ca4a9a4fc96389cd96ec0116a35814f15543843d383818637698392b5d5c5e3851162f2f53df7777c1c45c48b8899ac511d2a97c4faa87b25d98c8000001bc000000903ef97e0c57a6f5834dd84e80e8140c989a97a42ceee35be251dbf29ca3b9f1d4ec53f628312fb024fb1fabf2fdae3c222cc6f52957adaf9197fc22aa824e9afd66b58f53765e2edfcaa1e09bea5d09822c7d1ab1c0cdcbc2839c0ddd4eec2435989bf25e9f105380068b77674cd555da0d171b7673337a53fc13bd94d037cb898889a1690494f98997d6ad6f7cc9395000000090a66537d78ef99d64dcbbc8ac804c985b57d0a15f66dc4e0718252c967ba90161684073a50b2de36c358c3b2d7695af8128ae6fa3b5de2ba491f0f33c4b79256ae780a6b2fa2b8985fcaf46f51b0fe08c1f0be001e5faf07671f50d5d3f3cce0a2b14d8e600356c2190c919f7d5e95826223e80c90c4a715ab25286ec0a8d7fa958a0921ea9df290fb3bd39d9bd779abd000000903b40d81778940a1fbf4b631c6ca7f3f36db907262f5b7c9dbf7ed4677be6b459fd5aa18aa47d58a769fbc70cefd0a14d95d33dc980cff26b234154eaa53ba2e3a301af3d81881091d9c478670d47d98e2b6d37e7a5f4c5fe019363217b64d95782a5619e4eb81582b1798faa5764b58405bed270e597925e8e6b29101e97594231cd55747be6725f19e7faa830aa719b000001bc000000903cabd8fd2d8cc1b863fd474708974e9a086456a8c87bb553bc3b2249a6e6d35cea1bd6cefe1469e8d0505d496da0ba43fa23d5f95c1c9ceacee8d86bc37408bf788a77cd803aee3e8e9ca6be0d5c94f228565b5fe9635fdaf5cb935db1c32ba2eb22e34967322d150bbbd7418be5b47d0214392be8237a660118ab42a43436e5ee08e251d72da69b1e79b1682ec7dc110000009073b2ff420af305f8d6e8877c931a27e634e0e02ad27bd07a221ad29898bd955d5110432c2d520e3e608d73f71db60a877a0cdac7023fe16e14894242fc69a6630ad88e96ed3d9d9076850ea7f2da60302a5103978eb03a24aeaa3ea100b61250b98b8430a42fa3b40d9835841dd01a160597bfd46679cb40dc21fe48f5bfd0d457b4d857ffa793eb3931b23b5a5575e000000090cae03a08acbe94f7daa500858bac74ee45668f7d8b8699c05d15b6c8e3a7cb8e68caa3b92fca659e8b2ff5e0a459d10de2d9faf8d23e5e7e52e7bb3ce821a8d1b23eb3e68b93f2792385210f1c7900e910f6892d878b639348529c87faa00be2522c1084fb1f660ade21c84b7193003e0742f05a45a89b327fad6d6d8c8c8d6d77a0721e688149b63867f7930eba8342000001bc000000909a9712eb2ca308f32883b79151fca031a8e418d19df5dd397ca5ea709cefe8a36855686415423dc2afa0115c7987cce0d69612d7b485e7350a63b4d83f9e2f227e0422dcc23e1f2322b8df45ec7a8853147f0a8ea06fea5cdab7b07659ee18c036b4decfcb5be193e05fda85b194f10f1725eb65e156b30c5a39806822af3fe584cf9d541723131a22a23d1400ec9df9000000901cc8c637db22248cb9ed2e7ed8b7818f01b2af67c16ec9d9808bfcdf8b3bd4e8e750f54a84f646cad8f17eabc164de05f97fd66eee2495a419762ce946f40feb5f703bf8a5f1df0c2f4ba03f641b79160b234176a75d19d6e8a48b4ddd7e03c1289b3ec09a79220ee291370d2113bdc504924683165fc5d275305e79c7cd7a99a36537b9cc94befe1a2373b55e3460f600000090b8bd36129be0f622298f580af1ce6cd694052bda2e07761d1260b253b9b58206071b8329586dee5c5acbeeb94eb02a3753ea4c9614f7f9b9a6345451df3e7fae190b6c494514aed8c7fb7af6145072ae192a9f383c5d48e6cdcad12db87adbace3e736819ce046fda81bbad9b08c0946070798e6b179878937fcd611638e83637ee32c0260e48e372d621c3424b0c66f000001bc00000090c1e5c80e7841d56f2fdc2e22252561986ac0bde8583467b7b7cee85533dd4e82db0023a4aa1280e62e82f1208c465d378c09fe6f571a5d6f540ce1575db5a430eea826342e1b3eb17edaeaba692b3b3c1a9b142508381f48759935787a260b1360cbeb6a3d6f84faf868faaabe86f9831cb84a0def9afcae01d13019e2f20fb0d7fd57755bf8cf337ebd61c5d8e2e2d1000000908e7f1fd37c67d43076831de6f05532cff7fdfb2e2f57e1a3a51bcd9192a40bed6bfaa130ce8cc5d174a14e78f3f269baf7afc9a54b07e71f87d9fade5b0b2c8bd1973b0660b2c0ea5c1323fce5b40eba1fffb76ec613395d3a59e1588c5ebd8fb0f6ab9758df61d991c68b94ea2374a120a570858b479f0603c36e2b9d71bf2e85275fb3bdfb8919ad81f34d106fff6500000090110542aebfe9f65c67402024cb573c20c15d4835724350ba9c4389777e540ed6046da159569ee140da387a7628e7bd71359b470625cfd22a2ef99b2bc9e3dbb60f92b901a1bd8cc7dd325010cd83dd2d2704a4a0e660466faac7a416bce77d91c55b9d2ecb1f8d9d9c627832be30f4312bf94f8a074a7f94a363430d73571d5436dd9ea9bb13d907965b62686edbc8c0000001bc00000090b1b087d7329b0e3a4eb671fde2368893708b601576f3229943a86385ce556707b1f3838287448c58240ce1053b0fc102928723780612640d14a06993d3cef135803ca465102ba0ce7b0180c1a2a5f86e167c4a9c29f4a1783f93e36e5da3246f2117272dd6ff9a2388c6e78bc68e79951b3b844bd1c9a08ee529a5d851df7571c8698b37674d4bdbebf416422b5b1b5d00000090adf29bb0ad8c624adc804c04eea5fcb04d0d9e1b1eb3dc13f09adf0af446ee7d63f60f79423311a40926cf93f4652f306ff72b0e1fdfb0fd2f9d40a278eb076fd4dcbdc2455eff29f08388609213113e17e430c2a54f0d542b2ed1e8bb4b3a66d1bd8dec420e19feba9c40a94d2fabec22e9e452ff8e1e062f4f6b3d71a71798edef1aabd9bc34cdfc86d357bfbc03300000009046114b8c401cb7a062546f00d9506462e8c59a34cbadbedee505f9964f0c00f91e76f232fb6d1fd6b23ac668095ffa7cbbe69eec9a1683483d117a18bd478c50d95cf7db543b69b47398a9d882cc043a2ce5032191bcb02027b0e1a588c0a78d89a09b56337027f328a9f37dac01b66d1d4f45d56e66a2d8909cd8907d5f538ea8be3e635885bd5cc5e018bead3c16f4000001bc00000090174d1d04ca93c9ca135a32a723bb8967a20200bbb988465a446ff902b77675377c405ef506bb6a52daa0caa3b92333d2d9ccf16c130455e0499af88f64ffa855fe93d68d285f3aa916a96e92f33dda572b67c99a667585f74f8cbed06a7baf5aa1891caaee8594d5ac241331a4a12db61d59c1418591a4b8e553d37f38ea41dd4f3af725857dd679a9ce2540a608b17600000090d51e3b5514ee11b26f48094e98382d9552ec78b1eb2a9ac3bf24c87050f31350bf027db86146b05b78fa04582e8e0f943cb18005e9bef42f5a26b27ac45ec5c186170367d36d64a1a15ae4ac30f960112934d6f501914dc3f8fe2081ac4a8be04649a8a23b1ce48a2e1e80d58ce1b67114fcc3214860532b0873915328c140ad663ea2a4c69225afe369c736ef6731df000000901a0614eebea2300dcd77bff3171ac89dbf10245ab35b0f66a6fbea6af09024d465895f491da812ce7b116ba1be3590d1b6ebb2650444187876eb87f9acae4344a12c9f26fef7b619512a82cc5e87392627172f181e622b76ad3c6eac337fbdaade012e6ce8ab235e9d7c457503f216961890422725c67cd84b0319edfefd35dd60d9d88a0c52cd76508cc077bf65c425000001bc0000009020daeae685b4a7d525fa9e7322887a1da77d1379c01593d06367e695e044247a30bc981149f091016a4fd7b65016c2d4dfb8d9356cd33cd741c1df7f139acddbdc94573f89a76f4f2884436d22eb69361a5f09304639dcb02feb179b2f1128f7336824922f4639d03a1227a7a60f55512d03ec183fa21321d4fa61963ba2c2e191d37f25e88eed06291961c155656641000000907745a1f472feba9a29b326cb944fe0478c26c54b6423025ebf12a03925bb30d949f46c0c6fa5ee8c58d86ee0d210f0c397bbd41fc5b1cf46147fbed121631e3dee48473eb6ea01ff20cbeac67c5489f10ce126c6c4e735d5c40e16f674941015e0ae8d94f896ae0d4cd703ec8d2a9b0f105422e72324726e8277939f7f59d1b4a77f17de58303017608e60747567da6e0000009019216981b066c88ed0b3b6f2df84132b0d272d72597ef14bb385b81685909118c95d3d92a73f4ba6af2dcec7c94ccd6ab630209146343d808060fed0df2efd837f54cb06c78bf253fa1b8b8a5d4d7a902218c7234feec00a4e7b214b06a598b4caa2649745402606220865f338d0db2408404192c99dcc6a7c2c06a4e10ac8c625e008aac57d9426770dc24a2c1d20cb00000e00000001bc0000009082d76487e6d930f670dd8294a3ff896e675b59871ddad13511d9452de5b45ffb84f14b9b13c30aa0a0e7a2e6c9104db67fabfefeb577d70b31b3c815903784d22857c61d6fdfb68b8ffb698d19b8b2573005f3cc1d8fc31d65e44e50ab9e5215e3afaac2f83077deea42491ceeb354be10fb78ba40256771f5bc72402ac5d4b95714b5cf2648c85e3e10ae89b7b9f90000000090bb455f351a243a89123ce2b0a85fc2b0d711f7cc0f2292d0d2a1244429b2c3a982fb23fcd8deddfca3feb7ff27f394eb53b99309a1c510314edfdc9df4c2fc3db95ea67739c2ebee9989354c7252b6271b6ca0c3f3fd42e89c43684e31a94631dd0c57d049f8447cea033c05ac25cd021cab485baf707fcbcabb7210161ed846e3ba40efa3fb4cbe746a1d5b446cc7be000000904a9c85c97e0d4c935ebc0fda0fd70e4fe7ba903bfe904e6b71be94b512c7d61c05945d369e0a354935013d1840c07e272f4f967e9da05693169c04a01d9c677b30e2c399e57415cd33f4703be42ce44d093d5355d2f3cd6545583eb4b716865564986ec13253b5d93bc537a0d8aef806031481898708631063bfbd047cc701e354bff464bbd7d936bb53e73f234f8a00000001bc0000009014068b8ada80fa69e69589488779f7c44a6996e8ac9b6d6a317eb1693f1d4e094e11b24717028489585be7ab499453c89138fadaf6ab6f0f571dfaff11355fb748cf3d829b36c5b972c698d2bca1d7c02b9acc9c15ff9e5666d7b37e627e590a4d8f1bb24109c7b44936ef647a72b4921b2fa2bfaf9714a3e51999d1e590dfc46ead80c91cebe32784ab654874f9ccb200000090139f1dd54b92b5547d2c96e6018cfc3db4cd7859058e4d041123221c165a25ee3bc757ebc93503777b5d42f01eae0869fca95ee005728e2613bc7c94d734bb49a1c349a8815b3f4161ade33090ac86b12431f06f0dde369efdbda3de50c7ba09a345e652984fe5bc561a96e4a9f53997163bd7f642a409dad474891b58100829aa3663d048dc8cac3598e9525044266c00000090cf0c8c78576cd3295e717b4e5f05a419b3b005ec6731ee5c9d2bf97aac15c9ad3e3d2a56da176bf638374539c02ab70fd4db47cf3d89747cc938490aca465a78421b773c7459e71210c785c9a5811cde2ad310b81aba16885cb3b5e0d976536f92dca42cd55128833f6488e8094141150ba86c4bd92381553af54a040a82e41ea12c9549f51975d49db10fdbed1e6b92000001bc00000090869ad8cac952854ed14124129718d6e67d0aef1434bc70a6f523c781db164fdeaf9ef954b4b994923752445d704b459614eb332b76cb1f4401ad3d9a92773640236b9c96bd671fd81e3ac76cddc49e5727524cc35190286d82784dafa03e4f5690d6ddb5bcd7de9cd0429b924959c327049ff8df11b688432454877b5300f2ccb1d2c4166845b4bfdbb1e8015088985c00000090764b4562eda203759b8b174aebe4238efa1cf2a3ec1768ef3ef7d929a31eda380ae130c452455f036b34ecffab440be6e961241c12d24fcf15ba47756302ae12270ca1cb3c6e2a23d21c280fba5bb5c20a89a3deb175ddc55662a85f0941034841b81e9573af1355bfb08f161c4a886209dde8bfc2be93463163c57f7fbf92d7817304992ec94aedfcd43c744db0f66700000090435820bd150fc3779fccdb6cacac156f434bc441728b17ae0eb9fb9bbadb141cd24f050f1a5311375c82114434ee817c375195e929d4411ac94b0e5f7022e2a09e37b8dade0019025709e19cc035a3100266e6e0900c661e8db4e2e70ab508242eb478fc07f5f0e9983d1c3aedcb5431055c9543450fe8915345472673b7afc4f61e856770030f0a00c3587d3400e4c5000001bc000000905cd2b1954b0b81b07042b62e868d1f803fa824c21c24f9ff64b38a74c6e023aab51893ed2f1919cdfdfef53c741bd7a3d97d4cfaff2bf9a4776a18ecf5c94972aff3b1373ba6fc9ab6855ac00a49584f15c065b44f611fcfe69a1a06e1b2e833e8a8ab6f2f3febde83a3f02e263c266b00062b01f9222887fce55e9c2f5e35f024e566da0f0edeebf252f5a6aa30e28300000090077def0c3f50b0b47c9eb48a6b9708044029c535f4b742f75df046315c8b526751863017e899cad6696ecbe1fb3fefb7490af911029372b3a5dd8e04ecc073264d1ae633b48619177d4b1db17ddb7ebc016982aceb8e234406b2609f32c96088b591224ecd84ac0b49eb013bff1d8d5b291f671a1d584d9a5fc1fc86c27358cc1e63a52bf6e2e3087ba362b90a8a092a000000907b5ad8c10d405a4b6d0dc2bceefc087363be47cc13fe5b70d847ece64c9b8fa6c9d2e2d87f0d3452f6be6e1b5195088a732c38f1c127b18ef1c3888e6dda60fe73d4701f6763e764b0fc9bc984c9ec3a1f30df8ce2d7da272b7a20478311e5cdb3d3e2cf782ccfc8fe24c004decb2a7426e6d5cf19e5721f943de5c6f775c7e3a3257ce201bad3a1e279c18e56a0c23c000001bc00000090758e63051bbb20bfa45a1d6f0cba49e3d77c527acc4c969b17681b95330155439f1461314934ed0a76e838cbe30c38ff040dd1b5c786649b5a517ee3fd2cf0526aaa05c171d6fcdd75625d816836abd50f9d14c4b76b00a4e4ec57f3d934d31ed81ef8796c250614fafef5e5110d4b3210bbd4ca38ef2d2a82d835c158e7b419282a6139393e4bd502799986a28f216600000090f0822b26f5f8593d654f9377df02f4d66fc3109e08c6972cd2d0b9a4502361768fc07cd12c3f7d9e01237a31e7eae18227f1330fbff7f21ab933d81787818fe2bb8c1c79aeed32572534bad3a38aa22e12b85f9e9561af15d644b48f3ba832886868a01ccbc1298bd746adc9f213bd0d29ca03ea706ebd3cd5265fcc4d06491974320235bbc43d71208378f376b4fec400000090ab54fe26c2aea2182626d92a98a27670005516c8043202d1802da90008c2cbd61b9d0196b091be8d8605189bbbc5539dcbc4624f4058cb1404091a0437c1870eb160b02a3e57857e850a1c28e34555e21bd4f7fe7cdecb230dd935647c8c3d446b57e65d7f67b2b208953ef2367734380a15526b29102a6aa922a3525d471403a51ac6eb08e1fdfd8679549128879a69000001bc000000905cc031b130a4e6e45e8ce9d099c29f465014f2bc45a18f4dcf79f0ddcfc715db888f9975780c5a716611e8f25b66fb2f9a7a312a4953b9c1f2d3b63f38c5d5c8353de7033cf40b691310617c130142542f59730adba1d58ba4a553df2a009de247673bbbdbd72182e094ae020b503cac044fca8434a01a10648174a643a28b7105c34b2e7e55115b1c63960cb0a79800000000901de3bfad3720f2c15f008e4a346d27ac6812c0af159d93142eef63722a1451bd0bc02c635ccb07d4fdaa341922ebb11628a53fac21606763dfbb7f42448a042973009752036499cf913554f3e7ff82bb0bc082e28ef31fb2ff685c7a6bfeff67e5863ab2aeaf0a5e62f95e36c42a50b70817798e1089cd140dc0a6c817a9c4294925e8cf6af4977fef8a27bfe3774e74000000909d3eb8dda8a7ca6c61442bb4314d7bdd4be33ff477a9cee2db9373648e3d45a3f19ecf8e875caa25358b24db34795ebbd20481ee4eba712572ee7b88d8b345b0c9b6f7795ebcc246c601aad9eea60ceb131b7cbd50c6abccf73ce8dc958b52230df1916828896785b5f9f2a4b994fc0b20b752bc028fd96f880ff96d0239dca3f27c2790a1efb3748db4beecbe9ecf40000001bc00000090abb3bd5e78230fbbdef5e44ff963d2ee5bb5481807d8ab24468db6c0b34afe6191a513d9fd3ce22327464d7d8f1df5d5f1d28a8c8dea8b121aceb6bb693b4b7c92f626038d83f7c850f8e24ca8f2baa30f728e519ff0afcfe0142b8b6c0a26853fbb3ed9a852b5ab5ba7c5ffc0fe2d210c595d34d74ca38dd455f4264e64c9a5bf10fd8fb6d04ea46f7c0fafa6fb726600000090c6058b3d921006949f8ea3925de7a5272a5d0f8b9623ff4d648896e377da63ce69d0107a9c9d5e6cf286859699e32997120d4365e8cad02ce48de0b7a6e1036bf7488f54a2e7ff4cb1c8f904725f03342891db41bb8bee35200212f942c1e2d891b112c9e356328e68101c24c255ab4a1ddfe14afa6679960eda155b3f4e748fba771b4cd4cfec51b13ab41a01441be800000090407a6f231eebc95b1fb372695287319677ad9c009c273b06c3bf083d312b38a478d64ba7502b00644d1300362d7b102b6361f4e79b42e100ca70226c16da7a3d8651cb4c6a5810b137e439496c6b88d00963eaba8e28200569e81317825b7680479af46afc14cc4835dcfd5f1bd1f1ce095018666da971e6824e9c161135939f8260f4869cfcb42321b5952a97845894000001bc000000902a2093271e5d9e426ca48258777ea1c9d9f4d529e8f66f017ab2f7606fe17873efaab862e6cdfe6bdb06b568546371bd19cc9deda883327fc3bb47b5051c564924b2324633d7d354922fc46b8d88fa9711d7d02d5026e036a46a8e15842b0000cf357fe04e93407514976c6e24fc77381537bb76946a6f83207d426683daf202a0dc88370208c44c039b9223a27ed54d00000090f3af40d79ef111be5c885f6e4ce6a5f39aab3dba6721b387f7c0e7ec51f04e0c52219bafe83d29ef4977936827f17ea5c7db377184f3c07f0964e02bea83b1bd4b5aed48e755b48c46aabb0e802e431f0dd313d1c7f55d62d6996e3865770291774110087ef643ad13ee2b0fc80ee30f29ba6c2563a69f4c62e092346f03b20dac17b845455065432cb0f86f4e8fed4d00000090e2feb74514cbc15cbbd4960c3c31c2c8b89f658742235547af39388a2dd4d1b8383cc3a59bfec0ae7416275726dbc85967b1bd89eb45167b0a983558f057c5b6cd12d793661c9763c4aee5f086523d951413571b25824c9dc659ee1dd0390a717306a72fd4372d07bc9bf00fe6a092fb2175106fb6df9581444b794fd86dcffe7b79b82256bf3ced5f46ed9391431f7b00000e00000001bc00000090d25e818b83b533e096a3defeb73fadfba836517df1b920df53e966d82e384cd71fcba7799a3eb1b3e89eee9d99983b3eee97a525e8ed6722ab1113178d97cbef9b71de73a4db75571bb8a0750d1379ab2b674cd571fa1cffdba43f4c3c087dccf894f758be2877070da819381290f7dc0b3fb34c35fe2dc4ac82a9b758d764bfd171578ff07f263de4bf0668fb99faa000000090c910ad0b7cb33399478c81403d65f247bd70bc2de4cd1a400cb789a080a5814050ca36bcda892be9191fe589893aa3cf769c45d17dc9bb2ae20c6c6e8ad4410be0b47c87a4ae9a4d254d6c9454bd39f80acc01d59dcb13bade04106012e4364de822e00028f194bc9445e678b5051d02001ea661773999576ecf326e520e9490f79cafadd121c7de3486ce095923b21300000090ee0f1163ee24e3082804800407605a7ff98a7e3fff5eb530931ca1c29bb90a2127e46c103ed2ba423f55819d97e3b8b7ae7c98b9246c3fa0edcee4f7f4dc752c7861629d7bfaed003b609edfa779b8c80dffadec2e3a8e1fb967d32760a160bec97c15c640303316887a3f4fc927cdc90129f6f2658826ab3281a930ab23726a41699ef2a9b9ada3616f9a09e330e43f000001bc00000090c162bc6a922d0a9677d94e12ef9d0fe3f23f904c40a302ae96691c70abda3f4bad695623640fee23a35e536ddeca6a424c4a910ab4f06566ab833e65b2a56567b118f2dba34dc1639d242349e1e26ebb008681d4393efc63b2fc03a5c9c349740248057f2e7e2954e143baf97d7fc28d2414463f0ccda16435554fc0b96ddec693f64631f8de020a860e823c03e4a95f000000901a4c06bc51205497c73afc915e848da896ccb3e5ba6d0a01e22ab7e90d7ec9e359b99944e6cef34eb776a36f8bfc89a937c00dda7aa3292e877bd2b0949055f224c995a4e3f36c21da9d5336d43b43d512e2fe707d0597dcb3f61dcbe5ace1856adb5735905df6290a1e97e7b3f56f68160afa94767c419399635df90ac7630be9ca9f2be7a08d47a796433e2fe69d0500000090e971760c54c672a7b46b18e36bb1f53e4458e05f8e8e831775a41da37fdc11b57ffe7a5b1cca585f1af6ee11ae31927aaa30305976134acc7686b180bc94f0669ac4536c2f1bd42e988fe2ab15206e411ab8eaf7bd290eedd0aa07d3a96c31f6f59a43e73a17ec768a58765db5cfb4e21a0b1c6ccbc23701ac6fe4e5bade56d8d185c19055e290f091bb8a83d7a103d0000001bc0000009064d04bc457031d3da99682e91f14951b019cee11f426459c1e0bd3a6cb760ad8f6990029ddc57c255ebf32f0b66a9b4861702c4d62af67177a3ce64185a938d80a000ac15fcf090ac0d353635b3b69700e88eae07b1cf387db29491ea0b26bdfddfe728e5742b16707cb9c3cecd374230ade489c5f5714a04fb6077eb065322cba1029c6cba5d519931cc6869b2a613a00000090607f54712a73cf264d806c1db0440bc36dfcdf33a637efbfa41ce7a184e1837cccb43a5ac09872684f960448c6176eb614f0b9332e71d654edcb400dfd823e026abecdd096e445294510c7ffa77e52b21063632c55f7b907fd2dad33e1008f18c18fea3240507d4438e52a59f873a50e0e6840de615959b9dfe9228457bf09db81b97714d1b9e378e23b16179a7aaffb000000905713c015fab8d31dfb3cce41c788026931fe246f12b4c6c52c039006225596edf9ae9e3f9ecb3ae1462c12b431c7451437de202ceaf85cd58749077fed1d2a1dd41aa4a8072e304084c64dd8f04033f7173882fbcf5dd1ed380f3a0b6cc2ae99c7e1ed3d3bc625e9f881678a77cef7762cf6029df8ac6d0ad629c871e7b2a5cbae474c1e3ec458f61a982a3ed305b4c0000001bc00000090c74e13893e14c051d1c4b0e63d962d6790b8d5b3221254396ce134865a88d2a78ab9462e03635c6f12246dda72a5daffd7e47302b4da183fffb6c6e416edf2d45759e90bea5d320dbf3848a2ddb921c20109c2c82f61d2d57d00d9e6b63d5d6f6191a71492d121ce32fabb01f1892e061403e7c92390f2f321d582fb543b6252fbd6bcd4c81fe99f6970a6bd2219b11e00000090f465624b9784a4a376a2bd668f0654e08538e17bcdd05dfd97f4a1e20541dcb5bcdc377794d0fa79d6a2e1734f1271dd6294473ac6396c21f93be03196931cf71fd9c6e2910465b909c91f14437e9bc92586d6a20aa42e5b0e2bfdb3a9dc831ed60f0901977a7a572bf9719164bf265616a1ef37492fa6624392a171f816c33cf8a6516f966f3efe7cb1b343dd4828f3000000908f18852bed214009ed57daf66d3d85ea2eadb39cb1fb37ce940841774fe966a3ea108b0d39c66bb821186fcb321582e69bdfe18e4a36fb684635060a0992dde083c2b0848a99af0c12421c13bb23a8c401092f5a8571708d7096255112c28c544f33e9dac7234da2cd1e84d6f632134e101487ba4bbfe2556902c61e2a569409e749997aba4b89e8fd7af8285e556bf9000001bc0000009011239b4be51760bd69b2219fb45593d6fc0fcb5559b9fe6ec804297b700a677591419ea725d3f521630ad34909b40eb7774927b6b7a5be63c95cac83c04100f07355ac23aa1dd18add27e82fb9abc8412f8b8117f97f579fd48fe738ffb520ab784e721a28b16acd13d9899f9fe9140c29b379f0b1e8f4f632f3b8ed59adea573af784e12d894cd8343d8a527547d744000000905ad7963a5a5863511f5bc0734bf79941f50f2c71cd0082bd957649599719396cb7ed0f6e63ed1b18f541ddcac71ca4e6d4fd1a802acadea6772e76fadc323f7a74c710bcfbd26fa6ef55f6b17d8a187f034e8b77a4ef4467264ae76ee333bd8d3c2de0de95b7a837dd5efde225c3892e2cfdb16f38a7b6430f047a1a3d14a1b25345dcb3b5470676a8eb6b719b555cb000000090f8e203401a6080ec384e8eda1d3f804aeca07981eba58134ed2cff57729e418586df1fa8efa3096d090a219ae521491c878c4247b0ad36c6c11b3d424b2aede8b97b27bbf497c86b2b93239793aac9d61a9c91fdd02a53fdf50cb8a6352f54fa2cf7024b6cc68d57fee4b95fd8e8d15c276a66b4c0111673dc1bb105ab2ca5813cfc9ead215177cdc3d78731b101a39a000001bc000000907427eefc8c32594e8c7a42ab96bb1fe860ea78fb6146bc1d2a73a1e8ff9c6991acc4d9a463ef73ff781c61368fb7aae00631940143d0d53203189392d82b26a576728336a42fe55bafc656d729212b9d07efaaf20bad1e66200b7b80b29c094c858128ea68f3ab9d3885a207417ac1700a223c722d62b480ad3d1793f05579691edabd5f0d873104b257245ee9083c870000009036f06594a53af8ffd4c69460aa230c8f42cfc808e661b74954492215d41af00a7904d4b103347162c8e73762f27bc659fbcdfb35dafd2ecb3d1adae51091de4f455b086b8239ee229c60fdff690df1620d2aaf0f30c9975c519119112bbefadc8123c10f6b1a9c49c4debb0bd7df203114490acdf70dd8dd6f8583dfd250e869a3035549607fc847b663de3b0aeabd8400000090bd0c1f7e5dab2d0cfa161cc54823cfd5f67bae6dc39ad643a094f7172768f234c760fa9d1610c894d255eb0c773abfc023a46b2a8c8eb1f889bf14303c184e065430426e862b1cc1147d2d12cca898f9100dc0c65470adccc7810f657ee6958c952c9a9df0630d717d0281e7a94d4a4b156fe400fbb4fb617ad65dea278490af66f573cb1f720c1ad6f131dc61db2c03000001bc00000090e668492f7d870f241143adc81620eb1174ea8cce9e28ae6e14f1b26864eb39fc4bb4870a0e53b9d244a0743c8ba9d7c8b7399293c41d3b4b6f5ccf0500b2d328ea2e95147f22c1e2477b547d4fb60951140f90f1987a319217467ab516b7b125b4a8cdcc414527263686e37fda2c2dd92c9eb14fe690ab3846f47f6c5558d4ef7ff71e65a1fca7d93b14868c7ef7decb000000909602d909cb30ba815ad08cedb8e5384092dcdacc76dffd3913f78076539cb24c63bd541d0365c94d523a3b999a8a22719719537dd7b5db9e2f9fc4f0a0eec725be2d0e3fbf03a27c9843eb7d43b376bc03f00892cd118a31db431634c3c88bf2af5d33021a2ae9f4f22350398900bbd30841f4f269178d18bf284f8400da53abffa6ef966954397133b84a7e4c4f60900000009044c029f4795c4eee24224ad795237e3e09e2a8ea64e27f5bdbcdbf32883355a5956a96a7f72bc092991dc90d1edbbeee20aee7a1eafeb032627dfee5cdfd9cd0443bb21ea956927600ee6f7e3451c962013baa93ea18c786700354bec36b38343c9bb35dd239dc06466f515b0470c5811d3616246a801b6f2963b965625d955fc87a3d9fb4ed8e1552b38e1aee41b8db000001bc000000900b3bc15271008bd69ba48695c6e6a808a21cd66b6eb861453158751d9eab45b78a1b8f11ab3c5d989ee68b8f3e1f51e1da4f19e3546341ba88d19665da99d06c618d0ef988f558e281a3c97c6c04bde50479198ec6735b58f738a2cab5f45950b770cb5f9d34ea4099872464ff342fea2865375e7e7bbbe5acd490670b759f74d4c66304543a5d0621bd345dd6b9a09b000000906450d9162ac8175e4061c9266bba9ab6593b92b04b863aa0a08c08be535ddf2993e2d2b541701b62f79954e26e9b832aa246566e55e3ee6b1af856484d659c4ea486f0ccf6cf564260fe2bcd2a6a804d2e49472e9871afe13083a141b541dfd434904556033db3806322f7bd5aa3cc5b1a4dc9311ea57e048418d1c36a8fb231bd9821303d29d30a29cc0389d5474ea60000009022988ca796fc0b91f78b4f4535869997b9017a5d6bb9bab63ec708ec270ec6afae9799c358f478dca2b537239a35e9f78aa260e764beb9d361592a5df720dac67916fd799e37bb36d93e0699df5b07b808dccab7c57bceb538a337bcc68cc8b536f2f8b50566acada0b67ea7eb632d54165bf73805460e1b5bb24200bea8ed8dc15f308b233678f053180ca8bc9996bd00000e00000001bc000000904fc89fc6be0e30f05d6f9f1f86ced7feaf57415a3edd0e487c271d504e0a6d10eaea3c02c5733ba7556e33dc3adc695899a576ae2aa5579d41bfd701f91a479cd72e191d26b55bf6fbb032e3b33bc4842a0aa44cebdefaa8579aff992dc220a3a0a32f84f1ef0f3cbe62b37f413ff8441b4e857d847d8560416e3c8394f9969ae23d8b82e3f86ad33359f5f425f5c7ac00000090a37467afea6c4cc97ee51691ef457d7492c7ed5a24502da13c29ef84ec446b88a3c7bd5e6fc66804208adb01299dbe84a9a1b6cbd17bef87fbd0fe0e1808f7e919dd9703e551951b52ca192d9cffff3317a33dc570b5e04f5aba1d43b2e1997ac5f000e28213956be344a50c4c2990d81197a51d8df67a8c9014672c8efaa8507e8d790953cc222a530daf446fd8c94d00000090ed2da66258960faee567fca3f48339c58fc1849f9ed6084a338ad10c69a03b3d5c8528b56dcb54ad2ff53b9e8de77c73d5278ba02a607d8687b94b439b705f798a8f5af5826be2768623bfbaa53fc330046cd30f364e74e2e1afca74a76e4958172cd3f48806044001939affd2cf387f2d21abaa6b077c4818bfe38178995c90021b3861963ce32c34441ab0c2711d53000001bc000000905778f813c2556620961d546a7fdf739f69a15c0f3d27e12a640b7b8e2450ba0c27bffa2ec747b18a73dec69e8fcdf2847b9536fbf843e13a3f2bc495b3f4e036a1f6e52d7d5de6ed92990db981383d792cd6ec169d75d02d70e427aded56ac741223ff81866f4cc71ff0dbe79189d42c108e7e77f8c6845752e5f92e3dfe9b0142c1bc367b7fc03849e74b4169034bfc000000902053096a3ac009b9625b26d351821fe95f33c92111c691e9f526b5441e63a51a7a56bc43a07d6a8d411968ce7f8cbd7071b4392cb5b8a578172dce794ba5022d0eb31706de647545e373f6ee847c0c1c1c542de88666b01e90f664188ca9aadb0d26958d6071b96488ee11ef7c4756b3223d713a0c0e006f4f7c4c5c90c648666f962e245b7c6bc249193cb6af0bb95f00000090d70e906e51410fa1baa9d2b96e43f8a3b88e3571595cd86413376e1b70eab33d3b6b4a2b37842c653e6a7f40d811eee11ee842a0417f0d1f98e2dfee81d9b0fb9ccd0d94c96c09c419e9485012d8e20c0c6a123eb6e1664a2fe3357bdd09b53bf71102b0014877ec5290018d9b3291ab14fe9217b28dad3f0a5ba42aba7001225de17a38be745d4dfee261d3fd70be02000001bc000000901a8a8b3a10b4ef755a584850b202ae137405e8b021d75f806e660d9f747f2ff22cf6e45472b475d431829838c2b28e59361b4c98efb3180cfb7bb9fe638fbef23531160fa1946aef0d243ddfdf8fd66105e05bc9bdccd6fa6e5abba6fbb161859a073bc4eb89dd1f89b7a9879f1bf60a08a4d3be53e4bab4a27bf94e67ef11eea454177537748f0dc2d0029a09cfce9a00000090458256074deb2432ebccca8774904b3d1f2ee4624ae1eb16b0650324c6e055247a0b7630c353507cd8778c19a74981019d8f07f700aa03cff73ffb9bbe2579ecfaf28a8b457c8ddd0dcb7cb821d2bc6b1f8c99301801c3cca6cce6c27d9b451fef4f66221f6fa7c97e53d41e09e2680228643905b2ef882a09f0b77ce6e15bb898ad015b4f7ca5798b11ecb5be57d81a000000901bd4801913d9124efe31cad632fa74842d90821b2fe9f8fec40586e634d8bfdd3b8bac35675795d5adba61418295439168e7df438eb9ef80cc0f394b49e3297c8b4763fb0e7fc473fbf48a8a58e580021c8857152729f39dcf184443da12567a8ebc9b08030d1b9bf19275d6dec49ad71e75202c608f21a387d18f20c5f97572ac8905d1cb373088ce1175b144fbbff7000001bc00000090cc857f9a85db1c472f109527d0abdf1eff9bcf0e020763f0f6dfcb45dfd089ee1a588e1625c0938210a0947cdf78e983dc0c76f58dbba18245df17d4139802530fbd43cdff064d771c411328ed27f6b314b5f0df2516820e61532d5752fee09a6a46d2dc6d851eebacb495e7522de8b2203583f680d03c44ec9b37ea8ce5f8562466e0d19d222240dd728561f7d5c64e000000903babe76f53fdf6bd620f7ce82713adc6390d32939d113075daf95374cf07964c3a6fb30975853f14cc02fb654326c397e560b3d93c56f286572a9221257030b55369b406d752c06901aa937b8a43ffc90fc03fe346168e874dfb5c5b871bac5b2f6ee285e71c7be70d666e630a5f8fc605f87b550eb6b634d98821aa218263eac28c1aae4bff00bee6fac0ba87b3a5fe0000009044ef97d041bb817cf165c54bdfceafe5b3e375456be5a60a20ebf4c0ebc775d19e3b6a05602fe0702205ac3bdad9fee5733dec31985a830d52d934d80f4c26de543ebbc35ce43f58d7effad1a5e2f1632fbdd7f09de729fbcd9c1e420234edc3e11a9fe07bacd69c9bbfbeb95b2926eb109265132a50e27802b43af6ea5007e60c18f2b9f196982942336596486d7b08000001bc0000009078caf7c571dbed7201223e0edf3a9df03b5a286408591b32aaa538b5183e2811ca60a01da9625a016598952794377029c3577b9c5150612a92fc18c8e278e9c3ff7e831c66027ba31121f404c6cc34680f705e4cc64f09e1b5677d8239852b252d5beb89f9b831ab52164f268f5563de26a79d31cf827cc8d665e00f9136de728218f6c52a27ffafc4230b18dd42ccc30000009000811d22230c9c813d548215837b76afeb757179f8353d6907a6651c52d93a20deb6397944d199ffd1f2fd09002f8ee736c8823b748147db1a3182eebe82b840efb49cd9dcf389883e7adb67130065a209457e1a541ae4169fc17c1da29999e55f62143ccb007e6d34dd718321b61d28280ba790996b51f19e7a9697e6032f0c999e9777f6fb1ece70904f4a5f2b776200000090fe245eda1c2973b1f88513a37daacb8d5f35f35893dd5eb250d1442ffd0ab34d9c41dd3cfe6a7926ff1ce27988b8609ffa0c8de6dc9baebd017b7319fbe9abd7f4227dc04aba4d161f31274bcd20193f20fc7cfe85e4b4bb5a724aef57f265c62d737844a517823404a13148abcbec4f08dbb0bfca4e69efd460fec8558024078cdfbc05a42bcd9bf5e971fdefd08d54000001bc000000900bd4a31bbaf363cf3fa57aca5f04adae8f5213a7694b99c466d3fd41fa6ff4935fd74b4ea26ddaa049ce4361475eb92ada4ea3a828da1e80c29eb7ac684239088ead4a38e2271ab5e719612e80f3a25426580088f7d3501ad3876a82b672f5908cc84d06322e5dc6b244bd5bba1b95fd16dd281ae9e19ea36ce6c401f1575cc588b83e498386a711db916626da44415700000090f0dcb0f94e77f9d78ac41eeb40a50a955b4109bbe410ff70224b742f3fe1254447aa7386f653bc98610a67a95ce8919f78514c4a4bf00097084f0cf3d7c1d2d33122b5722bcca75484ab7952fd55e5ba2a20b78242c1e4d2af3333d8cf40e73151e0946821ce53efa736b9a3c22c6a2e21ae0d781bb4d13cde7018868a29285e9fd659bfdb9149e6296addcb396d407700000090ef341d88ea4e7fd9fe6e4a22e4d5dc505d065110899400bcf7cc54766a7127d2c48e47d156d6cb12d20cd0314d9544be558999261a88f269ab19e5ca7270380658bd36a443c55f097783c769c53c6f4016515b5ab04e4bcdb7f6c4cf920e722ae83ae986a67eb316f3114db6b8af057c28659cdff4c0f7597c43c1a80b453d6dd57ce6970ff28a5277998451724f3f7e000001bc00000090a865c2a5ef808bf3a41075b24637dedb1b5e14a0175b6524bf7e6dea4a73bac95984aad6571e030ade342508e58288746e74ccef9a41db44feb83e74c6bc920a2cef3a46eb8e4699019f6b006b5715bd0fb7711ad7933cbcfea5081657135e6faa64852810ade19b319163c1f7bc5a52120cd34e23191db2caf0e3147e4620adbd9c65bb19f60850cbe2bb27b3226fa00000009040a53797ae7569bde81f43f32b91e826343bc0f14316c46c3a767c9a76f3c6f18a5f43883aab511608ed0def297a253745ebc8efcde6f17aad8b894bb8f2c56bb7a0311b86f7a77acf1d5905f1391aef189601ada92d92ee4831fd2f3f6587ab231751c127d25a790e71caadc150f9fe2da2329b5993f4557c85702e1d3f76350008198b0a53e5659b385013256521bc000000908359cabe7b56b8b199554d07c08ac7780ad87048b1c8517a32fc474dc2d77203ec99a1eba9c192a238479e651e0b5b822f750b92523e7f3cb0265a0ddc5adc0c52839067eae0ee372bd1077bb528a5d81e3728b0d47ecd7e359e89144b1ce2e11da0363cbec6d502ce996791d87876311714c6383edc8595bf0fb0ab115f8265dd47c0f13b2d277237fca8c303c25cd1000001bc0000009014d50b10091b00692c84ca9d89b378281586b784b97e31c635ee2f240f7fbfda52caa18d2ed45b30d8c57b9bdf0e90c17cfbf7cfd766fb6ef05cc6b0e9536010dc1f3c570a1e805fe295077a49074d2629f8e3aa398ad0017f483cb595eac860c94524a59cd99f8d6100eaa2880c37af10d95ecff8e7662fd1675a6b699e368b2becb6381b60245955daaef513eda493000000902fe053e6aebc719b33dc8f0484d6ae3b36da92f490fac91bc44457b10a10f05880ccdd4a02ad43595b08769bfd36d6c8ca6b56ddd3639fee1891606c98ef93aa4174ea438c200f742b7b9cdf0eab75f22086c2b7e9c56a469f8fe4778281ae5dcacfcff7c5e928d12b7286346e50994d1a0f76ef0d2ffd1b281aad6cd3dcbbe8a0ff597104cefc4861e35a8dbabe9ed100000090b9e43908601821a6c6af1e8f68f955a0222223622c949747ca6258e843759a02031bfe8181c7354bb6e4cffc40feb6b0ae8ac4c930727f95c3dd1255f72aecfc7f0145cbda6958e8943722c88cdc172d2ab4e685c0f94fe29a92d8eca001d07945fa654f115d67a6ad1bd9a9d11bc8e02b6764d5427b15702eba437a7379d05a34ba6353af0b3c9eda58949a5d744c75000033a000000ce40000012800000090d1267be4f9d271fef7ef72206cd0e9ea08c2a867ea83347a247a3db18c95a3de4d5d19b44a8847af1994aec028371d91817ea93be421bc88de0f7b9ae628440b5a401cd7fef4bdf68ec9cefdf5a61c43305a51e1aa4cc53cecaefad9c8627ef285ab0a648361206ad649249a976fae7814f39800949bef3ed19e7d9b21eb2c673ea392624f1d670d20f8d9065f201217000000902645822c672907e4d8c0829357baac264aefb6b4308489fe113b1ff1ab965e34ef829446287b3002bab3bafaa32ef13e2eafabcc5801bb482d5bd79d5df0ccb9df3ebd126dcfff9d99499f3b22ce9003016efe2dd628279f15a73a50ca7734617b45328cff2ed111388be15bb328b15a012c4e23c527c16343d5f1aa009ce9ebeed9d4d9953a56a5bed7e5d50e9ecac600000128000000903ec8e194dc39e13100ae37743d4261517845da492d394d40d0cc71520111f86e5a1e2f7c1873ba53a8ad917adcf7bd893d82168c00e6317d2aaffe02edb038e3a91cf667cacc0356411f24eebf401e11148eef7900cd61c234127c9938bd1368f2b794fc12e58707e1882cc3f4d21a0c1df957e9bc5b63c528b707f685dddbfdf84643d155b06eafadf2ac436e5fe997000000902605a31b9297ce4f4e026cd62b1f99e8ebee58d4275eb401f67d9d109b2b8b925a942f34d80a0844c74542fad7d28a0c01483f9a26311961ee99e574f7a03781d749d68e46d2634ee3f2f86cda4b9d5c080a3c309d16833d104081e3186f25e8c36fbff25cea36baec6d85359ba8c5ff282403cb327daf376cd0a757807ef376475b9aa8c5b4afd5ee35af3908066a530000012800000090f01ed56f5ce1b851ea8242e8f2b54b3a470b33bef4a3f98e90e57a1efa1d066af51ed76a646cb0569643f75e371ddde0e4bac9f097f48ebeee3408a9e1524cbdaf08400cb29cf8baf0f1c495c040075526e507f551aa7247610b4374115a9cc36d55d17cc2513f34b294b6b57ebb63f811df0b8c164061b559447fea1bbbee0cde3c15837b4866984ae8e1cf7e91848000000090f48beedcc6a7a73c5dbe0f65a08901e8912e17b53430708ec55f8ebd94ef63cdd8d1ac2ff75c07849e92fa721d62c429769b2f9735c357a275929093d05211c63b36b72c38139554d705aa77ba77153c1810eb74a7055565d2b38898c1c7f68b2b6ff3a892051417bb67677dcb606d80210bcc0e3dcdf034dd98cae6b17708ad15c843e1625300b780d7dec624a6158e0000012800000090e3d8bbf1a03a6ec99d18debe60451df0f74b08613bc7df42072ffc0c63658e3f7a4a4ee218d29f7dee54fb3743b47a9a22a62067a43992ecde88fd0d5a4363d620c54a4f8794f23412e64d8a579d8e2614d2d47afc37c92f2b455c010d741cd477a38f3c53d9fc2a5ef75539347954c30dc480c9350557bd2ccc1d6ec3d93c917d0668cf00efbbd5ac9db2c69a35408e000000903490e166050c8c9a7ced51be3e57b99ae8d4de9e3ae3d7872d7cabfad63305c36e97214768219f502f11e081b6b1ecc4731c9d21e7857a48c39f700f2de1a1e690fdb2640bcd80de3495f5626d2624b81da3a8a2dc731da13a8f75335eb636aae5517433afbf8e02b1bfb63735eafd61122ade87e69376243405e78e1e3730cd0293ee37e4471eda1eed0f0c45c3842800000128000000909fd83cf44626806bbe7824895282b806c8aeda0f138dcd314cb32aefe75c2b692ee9113b1f9236e0789edb9ef55000d8ad3fc9610808e1f8ba10ec5c2066489f8891d029ca88be20c24625c68e66f39918b12237680f30254c3ecd75fbd881791255075b70bd58da59e9994242e2599617011050da58374af5bcb35f4cda80377a8a187dbd733ccf3a97e703dbcc5602000000902dc70eb32d4fb884d0b430b16c7ff97d5b88f35550034ed6841f85aa8dd2bf3b31e316b75c107bbeb05296995c4372b1808268a7e4f2e0f9624de9e95ecb47087753746334c65615578c9e9feebb3a51241fc894f0c95bae803c4f4fac8caf11447f290321247bf7761efa4e84d3453c16e0efd9024dde101546a4a4ef8e473e464a605b56613a3199ad3b629c042e880000012800000090b35df4a8e4fe3d9ba9a45a24b72bc3af314b174a2159cba62864c91c495848f8b11fce0c649922e5c957b88e4357965efe8f95eb96b290509b8dbdac4da7db9eb7f54116c9ab5367335037d89b946c5307f6f9e11fe03737543c46355427cb8c3d0253d78de7d89a97ee51b8a0d125350999bef16260798f2b1eec194a736ecf5c1d58e17ceb37bfb51e0c7396de39ef00000090f6b68d8dad58b1f726721ebfe0ad5547ee026f110e6fd2fdb139116ea2a3a3e808c682b2e43e47718e13d67502a9291b39c3796c3dc6c31b55ff7df469ba1408e1c1db38518a10e9a88235e430d1bcfc01c80d1f0ad3567bc6f3f36ee8242b44ef0b765a5130d8a4131b0759aa1ba3c32d20a24010618542cb216f00fda8b7186bba33dea148e9909b4d4114d273733e0000012800000090eba1cabbf010fc7c52c12227c7cc1bff001f549f020a7181c2b1ea71549f4e8c73c2a3b8a1ac6cfc09ed85ed820a479ec25d2f3e23d0f0807c9052827f4da196122928d3ddbace54cc179a5dc8a29799297e2a199d7074c84cc0696b822b40ccc1a1821fcec621ad646721d0914ec7932fe1b6ac2f70ea89c890f1261e55a09eb95eae781cb719f08c28c1be6e4117f400000090fbbd8ae5ed706962d244022184001121b2bed70fd7c5f24a77747014d9d118a13e8e3e83e5f0f613e51765e55b0cb9e2db954e9d3413b0453fc7166e69288cf6a775c7db07488b1e21fb2b24f1f3cb0023c61ac84d0fcd2b205765fcc3f9fba7bb5486441c07b1cd58ca4176cd410ac80899096c50546c8385b037b01abee2ad4b1fe28353e9713ccf291c29adfa392d0000012800000090f9a33174474e431d81d35dbdb9bffdac406b33d2c6f50ec30f40a501f9019fab188fae220ccc7a12de6ec060712c95add30a769ba5b4679125216799709c7b3fea4ad16b3b2f61beac2140a77bdb27d40cf50eac4c456057fce6aed3886a363443a56a4a4d5ef904d5d037831af022da01cb2f68f559ea4c571058cb175f2a5422f65301b7b24298916e2a7e5d8e8b4e0000009005dd3985388842b02e60a086ad305688dba8c73acaba8804464dd9b3650af21656d86f7dbbacbb344d6380351cf99f5298bef1cf20a917860cf962899c23274f05cde33a985834dd53eaf93aa6043fa72ca9f8ec3d4d394cdae9fffc49896a885c15c4efee1c3823c69f74cad0c19fd517babcc3125db46fc01a444e8725778eb0db0906db46fbd5cb4dbad9439716de00000128000000902bdab506bd3624299973bf33ae4fa16aba0e407b246e8061a437523b59b8bdac5d99d7dc63aa3d15b3c4bc7eaeef86469965d03ad3e9208d349d9f0fa987a789f309f0a847926337207b6f88479438b71c35b3c79aa2eadb871586be85ff3fad85f5c69b1ee4703889c555b242304ee7178997b71733c8f33fa78b9ea722e701ba79f0388c1f76acf7d5d777cde50d2f0000009048b5454d5350c10d5970abd7223e34b714bdbc791b4c65b14b58bae6a87959e9b8d2028cbab36e92b7d96c496613e1a4fffddb3296f28f28218231531aa50db7b5bcd04a24b200191498aa893862559922bd03d09dd727f797a75561ff4bc9ac01c0295aee978cc3487b08de3c5e77e028dc877f80f4baa2c748bf4e6b7fddc9af3c750691e88463670907dc89ae366a0000012800000090839be5d7c52676e753c8a94fc7012fee0c33e0ba824ef37cdf0e4ea1ba7d378161d29e54ea55823565c2fc5b91326b4a99a72ccd98595424e5751381143578bc2ef5f8fc5abfe87eda3951806f9f7ea20d2e867704f0c4f1a7458f33ff54764e2a64ce090b66e306b0f0a9e27f36e6a72828f812d9cbea202ce0ed288be99c5da3b8c032afa35c82906ed622f5a3603b0000009074878a57a2fbf52e6b1096fce33c7f83f2c7425ba6d150f48be26ac01966ca1f5b603a3d3bcca9f1524c4dfed85117c8b8ece60461d5974836189771a9fb700a44c92f648ebdd97770d1526fab8e5dc9301100a2a7804fedabd6d86f2fbbe46e3fe24603a00063db5b42c4848083a3580d1cb7b2386c037a8de0a9c6055ebe5edc17d18440ac6719ed98734f92ae5527000001280000009093e36780cca35dada87cabee15291ba8c3f3b51e28aa2b2f36f9a02f5a19eb21c6ac03f389843056a84b3fb4b2acc9f476266fb480fd7f99bf036d492324f168139db1e31dd27cdfb1b4e8dbf09185411c4de97982b74e636bdf6f7048c4cbf4d1b2149904cff0d5a187b3e3428485692df3c284b29771c96edbed7ac3a915c57377ebbdec271352157053c10369c2b0000000907c664c1d087d8642d85f3394f66f4bed378f77c1d9d7c128b164fec745a018176acb3c666635487d07ef39a9a7443cddfbe690dd2d90f249189a3621281c4d8cee6b8af4c22b18c6ed258b0cd1b5852128eea188949ab62e174d2a5e35ac77b2ecb10424b1d49a956eaae6d453e0843422e843b77ee88004fd697ce4ddfe4e032890c1021ac3d0b33b993d2cc260dadb00000ce400000128000000905998d03244f645698ff7cfc4dd1ba37cbd375b94a94005ad980a9ee1389965347b34b4732bacd868f2d46ba76059f07a9612b8841b00795d1899f4d9e35f14f628361a9d6db9d1cafe334768894eabce246ba1dac13af0fac1bda97683d7cb5789c83c8bca06e4797e2ed46bc518c3ca15d4bf801942721b74175d2034e86b3269d17d92ccf0ae1be0974c478678ccd9000000905d04d09462ee4c6e28988b59218779dc628362c3c8d61b0080506b506232c8dd8f5a8c914e19cc2734646ec283ef984d3a86bc15dbd8de783a4e17308e94db25428c59e05e9012c8b44da233efc49adc0c029e9962b7596fd6466cf39135c96e3aa7ca27556fcd2bb99a520e3468216b2a1294542a597a110476ed26bb8dcbb17162dc138d1b68aa5eb8ef962fc703f900000128000000903c8238f40ee0e3f9e925671432e84c067595931998373096731dd118da4f6acdb427febefa4ead8cc0b2757f651a35607ec45c65669983704f3c1c5696617db0a43dc3f6895691796b92d1586e2d62ac2783413610f60d5d6626eaec0cf5489b56a67fff21145d422bac59bc396dccf3025a8fa94d115c7c3a59681352149f1245f9703ebde50c5ede736cac50e802fb000000905e4e8b0e7d92d0db9288b2b0053965ac7f99b674c782135b7ad9ed8b2f444a70df394aef47fb8058895a0e1095154e2b7ab1783b5a3f0a39d0a497ba8d39bb2d35b812cd7da132646264393f75bb2a050ed19615dca0bfe3e31a947e1cff38a5648ad99b3148841c526abb8ef5130f6a0d1548c4d345bdc0e485e8f001927902345548007d66bc6040ff4d9c5d63b5ef0000012800000090490bd4103feca675238fe7fb4c16fc04b41f260da6ebcece76f632ba3f88c45c041e24553ce95a540d0fd48949d652bc7900b8c7a6eb8b2b687e00349bc9d832e0dd7cd4a4fb6dd73970b40e0156a9112de6af792a7d99bc6dcfd7a322f3ea87f4a5bea253e284ad2c26a523aeca62a32ef78b5c436a4e8dd5e6e12614d04e2cfe468a0ce89df5bb2638156ffbe85c5900000090c152b7aeaaf6f66ba3606d5272e2de83c2ec413ee7adf1c043500c433cf35527fb29f203028e547446779ed475dadb346b8b210c39de239685e9716aefe5d38655d9179c01d89100967ec7936bd59efe0be12e855ab54331d40fb17e7c2c4c4d0d93f3ee108c9cc9e871cf8019878d9517a9cb987489f85c9dbb343ae729094658d51a0c76ecc8f97f711e88aa09f25f0000012800000090ce845ee33b8828c450d0f2cf2e901790e918bf0c8d809e81113f1e829b219df0cee5fd00af7f89613a56b120115c72f98809c06cf525f4926f0c44d2d89916979f11100ea3adceade2c3fdcfd7902c8a203f3f68962dfcfd5d469c0f44b89113b35c8fbb3bd00f35e9c869325c6d56fa1d3a582441db19937f1131d6c57069293c0291c4c5497b62f9e253cd1fa17a3900000090dc2b6f7a95ac2f9b02608236b9d001c25d4bd46c1c299c6d2c5d888d4f6cda24fa0d7eeb717e173ee675f7f1f6a891a65c76d5137d5d8536525ef77268125445bd2a79a87e6dd03ad7fa682ecb6bdb1024cc7ef89dd5d28d3d83a0cfb8d6ac2c84823ce8d2b23da74ab19cb739352166002bb6a64ab8ba6219020940c1868aaf9098599b0536295a152d76376aae54490000012800000090382c4148126f8968b857162ec8fd930d573a41ae07df55b075fa4a90341342a75507d57fb3accd1b145f651562b68172a16d01822fd8fe19b8d74371c935229126d7a3cef7f0ef69c263389096ae3a9221e0676c9bd4d0604f00b6953347bf10af9bf823cad27dbc1f7356dd17fa9c0f12e5e2d085ddc1b4a345e9bffb549e5db1c045c210dbedb30dea67a2ddbb2bec000000905d60495979e9949a319ee7765f6d3f07956e97cb076538f3931c0f4572dfe7c2c9ac2afa11daf65b43b6aa44efd5e3b26672b3d0e3c714f5667fecbed230ef70b0f1c7cf6e739b3f35959a55c46423cf232f6fdd1fc069652903d19451c7870a33ce63d016a2ae702455b5e899f4483f02be42174f5af4eab43fb9c68a1473ed7a51e89c519da6a0073c96bb9cfbd4270000012800000090346bc85326b32dda7ce8f0212fbff1092a4141ae7452adc2c667782ed03aca9b9fefd2d5740b0dfb5919c2ab9a5d4a7ba4118797df233952f68458d346ac573406de9db783d5f3e97d911af1331508312056a292d09b43d071621a945152ad287b75c8a4d790c2e91561e04407be1aea0c81f8af7657704175e1b0aad580ba4564cd3d6ec6f36c55f5e74afeaa2855f800000090b77a26c6b8dd4c4dfe3aa1da8ea966522275687a8f7596662270fc760493b8fbf1e563fa0b307a00bf6e478fcc4eb3666369b67e5273afb4a02bf42374fa193fb0d0813fa243aeaba6d683d4049dd6422166b0ecdba3db4b79df7b0c1fc8a935aa7d48fb5a550fee0541e69d76015e63009ec5223365534bcbb376a2539e42bd129a9c457be332616dc5efaf44bd30aa000001280000009018aea5bc1ca8f0df49da8a42c192a4b8cc0aa96737fcb379804441c19b2a74e9320e84fddc1a23787216e5cf2909c0030145853cb165993dfd87cbc6d422ef21fb6cca7f7825a3f7b6adf36e06e6bb162b4eba544dc40b453b24b4fea13c643a8dbc0bae7729bf40cd78b9bebc4ac7b80495138415b64412bf67b0cdf88d7b4ae8798c4ede2fbf763deec962a29be47500000090142795b3ab49aa436c60b2a711f4c19339e4a3801a70876f143ebff42950fa11687fff7563af84d6f2a2b8c9313cc2f82818a201d4e258d979f61ad4b4ab14a8b8309f17c44bb414b3d74faac4e345cc16bb64c83ec01479fc0c993bf957ecbfb486fd5a4e6c1d3d1274914d5727c22f1917b87c903dd1881df82801e91cc83306bf990f0d55e63891ddb7fc7b8aef4c0000012800000090dcfeef5d43ffa92d2424c452b997f50133e3bb19581f58d007e3129bccb19388e395d25eb5bb852c71700150d709b93871a61b543439e5e0f75f174724f0d7ef9e61c335ff95c337e9929a7219ed1af1263a222029e7a033ff8e7f0ef7ab74af7d85cedad791f2825976d375f17636a80132c3034754ad9ae550bdff011feb1be1c172b2207f98239ae4349019ee465d00000090ac3204228825a74c55b968672f4d63d1e2dc40b9f41659ceb3d5dd63197f826e8d77898eb5e6120313c5ef4ce754d4ba89adc624fe368bb493b5c142b635a2a930339e8da2bca2d4e07b7ae451fe95ae2837128baacb9756033334a5d63609289cbd69f559036c422ce03dc91971424901cf9e1f28c35fda2e46fc7ee1239be8d474913be51cc5a2ca432f6b48684f840000012800000090cd23db296e01fd2dc8da08863075927f5a60a7907f3d265769f8d0f5081cd8fc218766160527828a87715ce58f6d7416114a9c1ef77ec93781a8aca5da4d3aa8aa16fe1a09eee2af3994eedba49718ba28be0537fb39986aa2e1f3ff5bd18f41e9ecd437e6db73e4cada5cd5639935e30756bf0b2d3d0a9ce69a6aa4c2e7d330be077532e4bd5e0a309eb5d3d5e1f94900000090f065bcc6ebb2303fd9c98b22d0e78378302d69307f498fe65e81c146d1001f1fc58423db36003c66425bd288cb7ed234dcf6e6c5cd03f6cf7f3b317b88d2a37767c6e36caa7c0997c0977252397dbad80997a6ec34730acc421489e3b118dac6f17cd6b4d816a0092b3e30477bb51c62219452973b63d9fe92bc2e93b6a0887c8d2a938a00f5cc40ae3d35b71ba74d0b00000128000000901f4117b0387cbd69904faf5afa7e0994ee94179caeaa45f3b3eb2f2aff67481e0da701c4ae66051bf316dfa30304b550add8512230122fccb457af9f61ea3522d0a16255a26d403e080182c27b5ae25e21973829fb3679da8193c631fc7f48bf0d8a789dbf2b42cb39ed2c57dadcf431129cb742a98965cb927e16285bec36e576ad810e799ed4089b1dd1b13ff07d5e00000090fbdf9e199bc0623a1fe2d32c0a4cfc814af0f22450bc538fe3acbd84a8e16e9244e44b25c59dcc3c566b71ec2c5ecce2730680427b55a9fe31c3c2725a347ba742c628693edf2d5644c6a94970588ba916c139b768b3c44cf618f2ce88546aaf413f3803f30bd9e0b66777e5d655f4831cdad9a00f7ca7b8549dbe5d0c579f3f0fb929853a5e836bfd1856efdc4fe20f000001280000009019dd6fed398d5a1ec6d9b90c21f72066191e977de4aaa552a27e8749d1e320081e7fbcba1d65f56c6ce845ba3218213ed8a07c4072dc271d5228b5cc58997ed670d120e96c92d1dac8ce47f856319246167c6d2d5c0c304aeba71d923efd25f9bed2c8112cdad624a3211d9692814fb9029f1f04fa904b76b473fea9e6f83dfabf214caf5ffcf0c5827a34731b31e40000000090e65cb233571c73e4fa6c182120793001a990fa27b961e18ce833933d48bb801382308a6d3c615ead2f098dc4b7631f4101d559a2a2bd2908e41ce82bdb5edb27173450739eb2368b5444822d1ecfcd912c8628008c9433d1c4fc475e73d486032ea9e075da8a2b4f2f9159cc6b68a9e6147432868e71b7093fd7af04a9e50f5188b6d7ed57cb7627ccb31670df5db32d00000ce4000001280000009055715488a08c638d410ac1c3ecd21ae332880fc42105b976c6fb07d698c1b39faa8d9b12934b08bdcc693d3ec0128051cf80c4e9f86c5973eb6d40d03cec36dfb7c971d734262ccffa3dcdae15ea22750efebba45ac81f517d39f8659cfb7734733128e7a96775491393889c73de2e55067fa9a986af3a69d33cae41ca15f2bdca098c58a97d91dab3199c93d04f570e00000090b6dd3736805e84ea003952a85327a4e528da04d3b158b8f9242fd8024ed02a5adc297de3d2ee0ff3f63d4adcfac256d843cbafee96ade152809332a8547682ed599c1c0e91498f3994ba461857d4b8ec037262da4a92fe480505db09fb5bff17bf9eab8daee32e909fdb846a34388c9516fd376daed2955220b85798bc7b740014edc73ffea1a5f7fa15368d582c965600000128000000900a8ce8c6a86aca7ab9f6ea3fd6a2f6535f672840674ab498f9f5ae781b925f2b8e5a0b6ce75cc08e9241452c57590936b61bfdecfef41ce881ec548b0b5a0ecb0abd8405cb28b38d04c8c7f0df097cb424f19bc9257e5fccb7c90a57e15e5d92d6e2eb60ef12956afe176d524faf5fc418725146da3b7c57bb187f7e83046c8898b46713888b536f5ab431d808e4a52900000090891c999fb6f5f2736c46f3d6699cb9869d6e022a61931bbc521008bf3e3d57f048b7bf300a346cf384f2e73260b6796889c7e6416fea936b69dd330e5828ed21b7fee287d1cabd0c1ce21e2676a9ce451bd960aefee87efcf907e8433e3f986ca0d46a4c1c6074b37a1c444c4fd8447d12cc44a788108c3c25de23c9d5a50ee77d1e64d8f5c3c8533abf5df125140d320000012800000090fe755ae0c22e8a61cdada5c298ff8cee16a776607e29d4c7cef27e0de9880dafa71b65c64005522859505870fbef0636ac3066b6b7c7d61941b8a0448b7bf9b2f6f1385ded2e353b329f5bc5a87c2d82088ebe3fbf9907f30caf2c3b649bace27ea96165beeb06bde6c1727b19b6487b2508fa3dca2918a1c35bccd7eaae5e195ee06fdc2953d546542535d3a55c9d0b00000090abe888fd7163369618f463f6d33563db49f7ee9316b35662863a60378ae0312d1f1599d9c2a967ca597d2a6512577dfec7ad1a75364664b29bf2311db1edffb4d059048d653a950c4d7222a53cf069731a7edb0aea3e2d066e760eedc1ace79d8c826e5c2e001790d8c5b99571c174720a03e33f332858a22d1d217591bb4902d161fc4ad5cb987a08c4ed291960400d000001280000009031046819fce63a7102b44aac4c5f8c7a61a3509eb5689cdf977d59cd27c2ae4d1ce799c3c87ac8c8e009f16f8e668574eb7390c74dc034a69f6b3ed1791ae3e8ad9cb378051a96b03e146071515dab32042bc3e5a0144771e4350cf9e97843324a00afce01dbfdaacb9d7973ffc92a92087c71040899508ac6ed22f98cbbc28a326e5450a4d45fe6b3a647b50a41e56400000090094b3b1bba5ee496fc26c68a807f8ded13c145d68ba578d7344525cf8eda77c26cec928f2637327ddbc66ed7a010164adcac03c64a9fa60378d8ebb4d4ef6dc8a0abd085daf104110f5427e737d11dcf0f81eb371c02344fdab811faaa2c3f6848969da8dcff13706be21add66cbeb7a1af501f16c59213bb5ff15fb5622c77b4230bf65f7f99b506dab2da006c5685b0000012800000090ee8c783eec420a1c1645d48a5629cf8ce95d1fa8949367a9da12a9ede842d00b13110aee316cb020d0179e109d17c2bdd9ffdf4a5bc64fc72a5cd07458a7ef79c6724c251fb28c7cc17dce89e99d45d31469f8b4ff96a5cae2822972e78debb1e975944e56522f467d32fcb387c2fcb517125a849697ae7c264e80cf0aebdd9e8fd047d3f48a8ca181ddf60a21a4847800000090c21131eace51e663aa5ddf47fac03e4b8c1b3a386f11d0c4d4a75b8989b6176daedcda7cd3bf8250b611569ba72caaa70aa03db71feea8d09e5a4d8abd3d2094a5137aa8c52af5548d8a8fabb5bbcf0c17b6b9196a1dda2389ee4c780831b684ebb6cb86eaeade5d02c3cb5c9c09b5270e2aac6566015226bfaaa7bb1edb01a88c5583cb0a415ceaa8b0031cbce9c4e10000012800000090618ac49800cfa73d6833d7c6b8382e3409e91435c9901b6b0e6fa5de9c09f065853cb0829b9301e3ade7c1b3c01f0cde7b99086205021fc578bdc51e7aa843849c8ef00fb43a30f9a3c5e3f85ffade7b29e73fd40371222f8d668c62863d9235f3b8adb1ac115b5febddc0e305bc7e1c006902c3cdf60732133e0803abd9fd2d509dfb2fbe7742a9f44d82dba0ffd27900000090a184c9e0eefea15471bab69b8f0a6500c5442f6f042110173c5ad0827e5cd1932d38ea76f0aa40cba967adbb9e97e07db74301806f696d0d177cde9fa0ba61f086b206b98b564157ac24a5dedabd82792bbe9a051cd6b48514c3296d6c54cb8647df72025211ad123ee1cca0291ca03a245dcf0768cab5fa534d148c587ee66f5f36b89fa50df893637f0a2aa8db180000000128000000903b09966390159024f0c642e5d657b0635bc31168923f0a3e0736823d1efa7e48a642a25fb718221062383683fb84e2b64619ea07f026297b445f5bd02451f631087fad7617b2d16bb585866593009db0094d06fdcf120b60e1f4b1b4de7601be84406b9a5373ab0ae7a1793773567d1d20e39cc64e8fcd3e65b33d226a559f87e3029f022c545a449c6fe85bbffa7b0000000090c9f21a34cb2673ab1fec1f94b8b54e3829ad11becca8b51a924f18796eb5d6704d2770518fdb1fdd84c65b40fbc68a5a6c8a535d4ee2e118c851439db8b78ed4c57b8e3d11da1562164de0f87396911525a9e502bbb33d46d38cf863102c2f2e74fbd56811eee530baad35810faddca80326e3d63d9799ca7069f6797d294242373b5a58e6f1ed7330d0060939cb48ee0000012800000090367e9fc1b4a260ea686ac8ad04f2e2ed0761c35a27e3108e16f321d20c8acb40b641a4cd1f1c71d2c5e42b59995ea7d47080e33bef859bff011b38a8ef6b043f236f976c0c39a13f192db7011a5831f807013ac9d1c3a9b88810b0e6ded72f02265be72de608b63c06af3375a1e2daf8168bbf4ad7c401d160b6b36bbaf33cd7542211745a0d0d0abc709d284853ae3a00000090d644726cd54c87ea3ce66c7a5af8ec080a8e8a9cb8db41300b151a2c338e7f9f14ac0e231043934a7ed7bb837be46c7d187e90dd38298e904499932add5d825fd22ba4824609f4c54c3db28fc99507a916a8efbec6336f0b8e8390f5a5106612c428a0adc434d9320f9b9d390a4829e5079a5432b99e97699daf0d728421bf8011cb6bf401f53eb0c1beef6b85d8241f0000012800000090bf4cfcc3fbbd79ba831951aa02d8cf650e7d462d4f1c7cd2a5df1a7ebc6eaa87cd66782af865070338a0a76a66096528ad11288bdb1d9016c1e38e2bf1beadf952896e570071c57f7ac313ad813ac1d90b3d76e851416bd1ccb2dba2d128d6e8203cd39ca8b9d50955970c007b7dad4d104e291a8f5df57bdee6008fe68f0d8163da7984dd6509e2a58655ac2d5e74290000009081d736b83b29f9b0611c05a0dda2490330a97ea2ad65e48c042a3c8b0ad80daa2b6d51f6b19fffe03b50afa87da3262e80378f6248cf5efd1cedd041692aec4573d0e1fd5207c8b281825e76225765dc1f37ae1ba00845d72156077c6533826b991f5f788dd1a10cbcd72ea73f6515812b1ee78954d349355a2a5b9c1232eef4c83d94e20649dec3ab24760cda75e10f0000012800000090a3a61d404a07f45cd49966ed22bf3b4e9c9c5878f58915e0472dc8dd2eb594075d8efa7eb429d7ca61a6c9f79fbea29405f228e03d0bd978971e7f2a42856a89c8aaf4cc565f922b9f832af1c22d365c303bd283c6a1e7b378c5179620165d78bbd8700d4a2900e525236299768dff561ca13cbb25aaf32dd350f51521143a7bd81be6834983052b343e95102f69060300000090304e9b7a5f4670f2fcf132a631b18090cf542bd22bb0a45fbcaa1a2f4ff290e8eaf041592ce1d0e0873e0db3a5943d6ae92ea857d2321859ee87f48324df9198b44e02996ca66db592142f89cf938d6b2e98e9558fcf3af3bfd2ef38ae3506a31f4fe45d907853c60dd998a0ad5bef3f2a620064f2facce42d62d5a2506aea0aea0685b21c7558ff45f122aac0e91c8a00000128000000902640abd81a3da57efeb903fb7ee0e5b7f953a7002ec47e3a9abdf3305a41530e7eeea0371df3533bfaa215b6c987be3a6598e20caffdb7723fda7c5af5fac93d62973c5a86a6411304310f4c0d4da8af14e9d113f7df86b49d60342920cc20dbee12dd8f418d11b0842821684e87feda01fc946ef7c05ce56a9b98f19178f8de758e2af5f786b1fb8b004cc85cb23ec70000009013ec1d98f9636f93ead41ab3140fc2a411f5f2e021bbc9f72d95df91ca55084768d93d2ccebf83fa26cae9e8fa83c2b118fe1cdc46eb9daa30b2d011c40849f0fed65cfadc92fbef62d3630dd676c583125d62403618c5c88c41f689de97c1d75082ddcbc95339e74b677eb2d1318c620d0cc8c82f44736c9bdda24e8b0d706003fba7d4b23237920273970abb93545f00000ce40000012800000090d56eeaf9b6fc69437806e1be871c1ec2a0c23aa64a1d82bac9a7429ef2270bd2765c33c91f82598c1f68ddde7699a285cb64bd409502b8e83b42f1e28207a4e874f8a0b1444f2886da4755d73c7863da004d0689e149f89c368d424e5cd74fdacad44ac7584c9a300a1fc9cba944be581dadbe8d6dd1dc5f96fc4c4cccfa300fd2e787e80e7618e9c5be43003ce3d95f00000090f572e6184890cd783a2c867345f8bf18fe1461a046376de7473fb34c2fc2f1862f6b769d98e88f737eca890f5f5cc48db20571ed6660cec27ce064692abf5cb5a26e5202ff3e0e1fb703499d5fda828800f8b556e4c1224f985a8e2ed75c66b12aed838d942929590121f8983bdc5b7517f746adc73b0c09047e69b64eefbe0340c78aeb25efaf3c0912f93b4f98a0ee000001280000009044b6e3ad790cc71cd6c47f871fb2a86629693ec8472f30df83c8cb4f6001e0e78dbd947a4ada49b87a2dcff150b6735851336062fb4c3ba6a3a74bf35c3aead1860d1cbd5c3507e60a2ddf30ae898cc5018d40e135b8e5c7146e5b2864e91c23da095ef0d6ae535b7d9c6817babacd5313112b1fe26b533d29d413a52391cfa119c12eab0515970954c029ffe1f87b5b00000090870a1f0bae78d421ee6c9ac89c1d95a1b18a31d2b7c86ef2af7e568103d612834985339e8c33c7a5d24f90681edee45a8ece287a949c6f25427b8d26fcd5cf0b774dec6d70cbe0f1688c77266f78e72f1d8418bd3266b95e010d48d53cf30031328f6712893c9eaf368695f43f55f6132a50a634471b990ca9754480d43e87116a1df5bf3b91fdd2a639fb96215472b400000128000000900d2bc0b76f2e7fe9b1143b40fddf59fb274426ef2d5f01d7ed24ac9cd6c3b5ca1279f0216fd765c65be395648debc9082c5826acdd804dc1985826a6d30d9bae86783cd9a9b05a178e8bd613985f0cd71dd6a8dc8dde4fe04fc55f5b391456e02bd685d6a9ea14fd36c38d355d62673e2cfc54c674dce7da0b12c50c093f3ab559b8adbbf2b9cc3b4e3ea52509715b0f0000009012d6e407a368b98961a98fccb0ceb799977f6babcc11d9de75203f9b73dbbc2c141c38bbb43b4d3dff3ac3fba44c348fc35edebf22f3bff9af985e67e2dbdef73c0dcac8c368964f0257a5ce18e5e32a2efed8a3befc81d87c130698efd0a7e6a24a430448b702852988840630cbf639164d104f77092c2e2b5118a6e4be061e6143a9b70ebac3a15ac1a5bf9d5a65c60000012800000090aa1fcd5d082096320617c0e66f2b830a5c7c0548be3cefec89c8d5efc2361c6f0b95119c85d64121f78fcd7317e7c3481c4cc26ca0ff51ce151b8e891b3acd558b0d269645cfd61ec41c6e0a390090882a5e9f8e69af9d41543ab3be93a4a765675fe193213864305b82179cfffd52500039937fc63b2efc64a515492970bbb4a62a476c962bfb03626448e49f478437000000903283ac07cf03f2b74193fd9c1a16ff330ebfba1d6a8d4726cc6f39049af672c17930ab4ce63180b24922c18c43c01b4ac0172b5aafd299476b53e5ba1be50f7e877313fb34ee31354f0378b33b0cb9d4116b9298a40ed296d2b23696b6cd3a50986c0bdfd2bbb5439c58af6b351f9cce0103ab28a09841aebfacf6598c0ec25873088139ce3306e3bbf1a8b84993242f00000128000000903f890ff13d5bb64a96df89deceb505ae3bd968054cb6b354bf39a73e0d3908950a0d9aa8c2d5ee6e2b4fc0188a69c9acc5991f91e4d321b865264765c1de168d83c85ba1ac616ce4801017e5e4c34cf009a7f9f287f676bd11c67d9390ab116e7cb9dd8aa6b468a04dd31f5d78f9666322e6b4b017677a7ce3888a6618558310a0a6ac3fa061782f3c8c77d60f493b03000000900fdef5192501a6583cc3cb04a11f34997f3676a0d31183d87fb7e6255f8bfbd22dc5ca480c264ca60e894727271a00cdbab0b96de88c43e699961e0315f60675fff533af703b944fab3e2900cad022f4207fa960b0d7465156700dbb6587a95ef99dbfb10201d98a4064d1172feacf8430312b5dca5204b1ad73cc3599e01f077a070a39dcda3ea7edd272694debcc49000001280000009015560ec3cb3d4cb1a245fd268fb1c81831d317249ab55981783db915eee39b58771efa13b81e2c90852d5c3e7e74580bc8952a487c10f96b953ca1d86ec62284219910fa213d4a028db2cb3b9883685f209ccd6054af646b530afb5951dc48bc61facf9562420135f161a98d6dc78cb2206f2d4869841f67d3275e541e9f9d66e356d27a06bdaa61448c0afbacc8ad3700000090d445aa4041263de462b276c145ab0bddd25aa0c5f875e429c323e3f58d2d5e84a1aaa46e9297e5313840f3a6fdd600e68453e822744e24500eed099c27de245c542afd148c20548ed106ac2c4286641c0d73fb1acf582d06be8c11439786115b3c634b1be76843622d2b14785d6259e12b8f67d232257b9c837a590b2e69259495c3f9966f5dac6b9a7e7773003aced30000012800000090cec753f8114fcc724810c5123315bfbf96dad76dcab51ba41652b10426b9fd65d7a48cfb2784484e0cf61b8b65b20afc8ad669fa2c879d19028338017df945614e3ffe7c7a5a5f7e7bca84473ac862f403832ca2dbe679bbe6d0e1fb7ea7e21930afe46894c13b48446e6005ab935d841d5494ed8e72009c651dca2ac7eea7d33ec5dd615d818fbda959ef598a230c28000000905a08792b87fedc1a668185e3acee53c5144ac408216f98c9b819a648d5c146ba0499a9af83e36ca91c3f6cda0a225e01cd1e41f3ead0c4f4057c8cb6a7b4ed51911ce4b24ac648c3cd180666b4b74377126825d1ae86b7502e11ea18cd1c8d55156a8b7a1f25c21a92ac80e007c41335182b160006561e7773143e3c9a5dfeacf0e8496c2e2c6ddfd321c04d4673047300000128000000904be201a9ffda34daaf047d5b528835dd61911db450880b1bd3a2b7fc8298600159788b7e47f83dc625b8b9c2636b6ce78c5d2a6fae8c0d1d44009d0610dd2c6f82024a4b23afc49164de7c944b5e4d5f0e20f06069ce3ca7fe2f76c21918796142314525e4546f1e83888970e68dca682f51584adf3fd22f76ad10d759867c52cb7197f1649572c342d970b08729725500000090aeb1109cb748d04bf154bb94546157475bac2113d36e8d584e953a57a7fc212355f8fcd186697882282d6dded2e55b85abf920a5c57e756e7545faa36bce08dca2faa5429086a6976b8c4366e45311d41740d1c95e322047f5b5e96262040c69a166a68d6eff9e5e3ae83b261c0aedd61f830bd4c0a38e1f45c720774e902b7550012b2ba038c9d3710d851830b7c0c50000012800000090a492671e46d035652bae1de1e2d3f409c9391c7ad1315f32d2b686121a75c96756d4dd16f0ac1d666cff578f352b4569fbcba5a153ef82c7611c12421ae9027434bfec9260033fcc0267f44e5a3c4b4f1c2f31f6321a58b8f0ab6b7887cfc7107a2bdb59346fbdae29f343c62e2a5f1102f383e52f679f3d9757f3207cea70f06927d9526edf9b33ff2089f899f2825b00000090148bd8adb33b112b1aec51d508591ff6a3a960056bba4697b89ba23edb1e95b995fa9c1f96b530d117488c56b3351ea5990fcc431ad21c67acadab7ead8ec87ddaafdca660fad163ae6e35d3be395dfc14560cada59209dde10b230d7d4c662e3e7cfcaeb7a9a7a340dcaf6fb8fc15442b58edf35c4c02594f1bf3e5ea3e899f0b26ad62f036718681b829dbb55703670000012800000090e8cfe16147cbfbcede698b9c100dd416212652230a29d79d9716fbd8fa08514837c9644e182460c8eb5bdd694a89195e2365eebcd3996c4af7a1c8d3f887450a0d597a12cef9bda224caaab8a8ecbd982aa923fee4ef4f07e28bd74b4e2c8b0a5ef49674b749513e30b1a039186a3e35269a78b9f7207db380eda629301d79a07b27cf70748aca38a1a0b79dad0aa75200000090655afa7a84fdf37709cc9c7a283cb30c178a5446e7d6f18c54607be8526282f15985a92beaa8ae01cd798f9e4cffc106d86021e65154cfb1a47bbcccf2086f6a89226570e73ec7e47a8c842abfafeace2f3c6fdc480aff2705484a3fc91f494dcf5c427290aba416a052de63ecc50fef164919d1aeed69286f215447f69fca971e4a19421952128bd7bd133ba3a3e8a50000012800000090b00d676b1075c815808ac19b9d44e1b76010750dd21adb26c7a8519eb4c197de00b568d2593c1a4c21779ca05f86f074c7a46fdedd80c67185d13a86ce2d524835b8fa30c9b61dac3596b8717214dab4005681bbbdb96dffbccb423a2bd9178ba7075084be235f11da12d99caf6759a52e106f8e5e7d0f016240f1a9654c4750d34452a01d73afebd928384b0a95c0df00000090b770118ade6cba6553985ccb76698268a91a7e59537762d62cb83b5e2fbd6c78fea043ebb9a51314b8588ff85d7afe03837cccdc4f6518e4e51f28d22374f5814e7c6b5648c0d06563333f8926d162932a7f3a03443f27b002a613ba3efd51e22527ac9fa9fa7dd1234244612ce6b8430a02bd9ea02b7e86c7c9591d9086fdb8a986c8fe164fe57189995af4fb8fe3c9", - "calldataHash": "0xcd9a7ed1324b15c82eb2b86042643cd52b67ef1909cdcdefb26d2073d8664a23", + "body": "0x000001000000000000000000000000000000000000000000000000000000000000000140000000000000000000000000000000000000000000000000000000000000014100000000000000000000000000000000000000000000000000000000000001420000000000000000000000000000000000000000000000000000000000000143000000000000000000000000000000000000000000000000000000000000014400000000000000000000000000000000000000000000000000000000000001450000000000000000000000000000000000000000000000000000000000000146000000000000000000000000000000000000000000000000000000000000014700000000000000000000000000000000000000000000000000000000000001480000000000000000000000000000000000000000000000000000000000000149000000000000000000000000000000000000000000000000000000000000014a000000000000000000000000000000000000000000000000000000000000014b000000000000000000000000000000000000000000000000000000000000014c000000000000000000000000000000000000000000000000000000000000014d000000000000000000000000000000000000000000000000000000000000014e000000000000000000000000000000000000000000000000000000000000014f0000000000000000000000000000000000000000000000000000000000000150000000000000000000000000000000000000000000000000000000000000015100000000000000000000000000000000000000000000000000000000000001520000000000000000000000000000000000000000000000000000000000000153000000000000000000000000000000000000000000000000000000000000015400000000000000000000000000000000000000000000000000000000000001550000000000000000000000000000000000000000000000000000000000000156000000000000000000000000000000000000000000000000000000000000015700000000000000000000000000000000000000000000000000000000000001580000000000000000000000000000000000000000000000000000000000000159000000000000000000000000000000000000000000000000000000000000015a000000000000000000000000000000000000000000000000000000000000015b000000000000000000000000000000000000000000000000000000000000015c000000000000000000000000000000000000000000000000000000000000015d000000000000000000000000000000000000000000000000000000000000015e000000000000000000000000000000000000000000000000000000000000015f0000000000000000000000000000000000000000000000000000000000000160000000000000000000000000000000000000000000000000000000000000016100000000000000000000000000000000000000000000000000000000000001620000000000000000000000000000000000000000000000000000000000000163000000000000000000000000000000000000000000000000000000000000016400000000000000000000000000000000000000000000000000000000000001650000000000000000000000000000000000000000000000000000000000000166000000000000000000000000000000000000000000000000000000000000016700000000000000000000000000000000000000000000000000000000000001680000000000000000000000000000000000000000000000000000000000000169000000000000000000000000000000000000000000000000000000000000016a000000000000000000000000000000000000000000000000000000000000016b000000000000000000000000000000000000000000000000000000000000016c000000000000000000000000000000000000000000000000000000000000016d000000000000000000000000000000000000000000000000000000000000016e000000000000000000000000000000000000000000000000000000000000016f0000000000000000000000000000000000000000000000000000000000000170000000000000000000000000000000000000000000000000000000000000017100000000000000000000000000000000000000000000000000000000000001720000000000000000000000000000000000000000000000000000000000000173000000000000000000000000000000000000000000000000000000000000017400000000000000000000000000000000000000000000000000000000000001750000000000000000000000000000000000000000000000000000000000000176000000000000000000000000000000000000000000000000000000000000017700000000000000000000000000000000000000000000000000000000000001780000000000000000000000000000000000000000000000000000000000000179000000000000000000000000000000000000000000000000000000000000017a000000000000000000000000000000000000000000000000000000000000017b000000000000000000000000000000000000000000000000000000000000017c000000000000000000000000000000000000000000000000000000000000017d000000000000000000000000000000000000000000000000000000000000017e000000000000000000000000000000000000000000000000000000000000017f0000000000000000000000000000000000000000000000000000000000000180000000000000000000000000000000000000000000000000000000000000018100000000000000000000000000000000000000000000000000000000000001820000000000000000000000000000000000000000000000000000000000000183000000000000000000000000000000000000000000000000000000000000018400000000000000000000000000000000000000000000000000000000000001850000000000000000000000000000000000000000000000000000000000000186000000000000000000000000000000000000000000000000000000000000018700000000000000000000000000000000000000000000000000000000000001880000000000000000000000000000000000000000000000000000000000000189000000000000000000000000000000000000000000000000000000000000018a000000000000000000000000000000000000000000000000000000000000018b000000000000000000000000000000000000000000000000000000000000018c000000000000000000000000000000000000000000000000000000000000018d000000000000000000000000000000000000000000000000000000000000018e000000000000000000000000000000000000000000000000000000000000018f0000000000000000000000000000000000000000000000000000000000000190000000000000000000000000000000000000000000000000000000000000019100000000000000000000000000000000000000000000000000000000000001920000000000000000000000000000000000000000000000000000000000000193000000000000000000000000000000000000000000000000000000000000019400000000000000000000000000000000000000000000000000000000000001950000000000000000000000000000000000000000000000000000000000000196000000000000000000000000000000000000000000000000000000000000019700000000000000000000000000000000000000000000000000000000000001980000000000000000000000000000000000000000000000000000000000000199000000000000000000000000000000000000000000000000000000000000019a000000000000000000000000000000000000000000000000000000000000019b000000000000000000000000000000000000000000000000000000000000019c000000000000000000000000000000000000000000000000000000000000019d000000000000000000000000000000000000000000000000000000000000019e000000000000000000000000000000000000000000000000000000000000019f00000000000000000000000000000000000000000000000000000000000001a000000000000000000000000000000000000000000000000000000000000001a100000000000000000000000000000000000000000000000000000000000001a200000000000000000000000000000000000000000000000000000000000001a300000000000000000000000000000000000000000000000000000000000001a400000000000000000000000000000000000000000000000000000000000001a500000000000000000000000000000000000000000000000000000000000001a600000000000000000000000000000000000000000000000000000000000001a700000000000000000000000000000000000000000000000000000000000001a800000000000000000000000000000000000000000000000000000000000001a900000000000000000000000000000000000000000000000000000000000001aa00000000000000000000000000000000000000000000000000000000000001ab00000000000000000000000000000000000000000000000000000000000001ac00000000000000000000000000000000000000000000000000000000000001ad00000000000000000000000000000000000000000000000000000000000001ae00000000000000000000000000000000000000000000000000000000000001af00000000000000000000000000000000000000000000000000000000000001b000000000000000000000000000000000000000000000000000000000000001b100000000000000000000000000000000000000000000000000000000000001b200000000000000000000000000000000000000000000000000000000000001b300000000000000000000000000000000000000000000000000000000000001b400000000000000000000000000000000000000000000000000000000000001b500000000000000000000000000000000000000000000000000000000000001b600000000000000000000000000000000000000000000000000000000000001b700000000000000000000000000000000000000000000000000000000000001b800000000000000000000000000000000000000000000000000000000000001b900000000000000000000000000000000000000000000000000000000000001ba00000000000000000000000000000000000000000000000000000000000001bb00000000000000000000000000000000000000000000000000000000000001bc00000000000000000000000000000000000000000000000000000000000001bd00000000000000000000000000000000000000000000000000000000000001be00000000000000000000000000000000000000000000000000000000000001bf00000000000000000000000000000000000000000000000000000000000001c000000000000000000000000000000000000000000000000000000000000001c100000000000000000000000000000000000000000000000000000000000001c200000000000000000000000000000000000000000000000000000000000001c300000000000000000000000000000000000000000000000000000000000001c400000000000000000000000000000000000000000000000000000000000001c500000000000000000000000000000000000000000000000000000000000001c600000000000000000000000000000000000000000000000000000000000001c700000000000000000000000000000000000000000000000000000000000001c800000000000000000000000000000000000000000000000000000000000001c900000000000000000000000000000000000000000000000000000000000001ca00000000000000000000000000000000000000000000000000000000000001cb00000000000000000000000000000000000000000000000000000000000001cc00000000000000000000000000000000000000000000000000000000000001cd00000000000000000000000000000000000000000000000000000000000001ce00000000000000000000000000000000000000000000000000000000000001cf00000000000000000000000000000000000000000000000000000000000001d000000000000000000000000000000000000000000000000000000000000001d100000000000000000000000000000000000000000000000000000000000001d200000000000000000000000000000000000000000000000000000000000001d300000000000000000000000000000000000000000000000000000000000001d400000000000000000000000000000000000000000000000000000000000001d500000000000000000000000000000000000000000000000000000000000001d600000000000000000000000000000000000000000000000000000000000001d700000000000000000000000000000000000000000000000000000000000001d800000000000000000000000000000000000000000000000000000000000001d900000000000000000000000000000000000000000000000000000000000001da00000000000000000000000000000000000000000000000000000000000001db00000000000000000000000000000000000000000000000000000000000001dc00000000000000000000000000000000000000000000000000000000000001dd00000000000000000000000000000000000000000000000000000000000001de00000000000000000000000000000000000000000000000000000000000001df00000000000000000000000000000000000000000000000000000000000001e000000000000000000000000000000000000000000000000000000000000001e100000000000000000000000000000000000000000000000000000000000001e200000000000000000000000000000000000000000000000000000000000001e300000000000000000000000000000000000000000000000000000000000001e400000000000000000000000000000000000000000000000000000000000001e500000000000000000000000000000000000000000000000000000000000001e600000000000000000000000000000000000000000000000000000000000001e700000000000000000000000000000000000000000000000000000000000001e800000000000000000000000000000000000000000000000000000000000001e900000000000000000000000000000000000000000000000000000000000001ea00000000000000000000000000000000000000000000000000000000000001eb00000000000000000000000000000000000000000000000000000000000001ec00000000000000000000000000000000000000000000000000000000000001ed00000000000000000000000000000000000000000000000000000000000001ee00000000000000000000000000000000000000000000000000000000000001ef00000000000000000000000000000000000000000000000000000000000001f000000000000000000000000000000000000000000000000000000000000001f100000000000000000000000000000000000000000000000000000000000001f200000000000000000000000000000000000000000000000000000000000001f300000000000000000000000000000000000000000000000000000000000001f400000000000000000000000000000000000000000000000000000000000001f500000000000000000000000000000000000000000000000000000000000001f600000000000000000000000000000000000000000000000000000000000001f700000000000000000000000000000000000000000000000000000000000001f800000000000000000000000000000000000000000000000000000000000001f900000000000000000000000000000000000000000000000000000000000001fa00000000000000000000000000000000000000000000000000000000000001fb00000000000000000000000000000000000000000000000000000000000001fc00000000000000000000000000000000000000000000000000000000000001fd00000000000000000000000000000000000000000000000000000000000001fe00000000000000000000000000000000000000000000000000000000000001ff0000000000000000000000000000000000000000000000000000000000000200000000000000000000000000000000000000000000000000000000000000020100000000000000000000000000000000000000000000000000000000000002020000000000000000000000000000000000000000000000000000000000000203000000000000000000000000000000000000000000000000000000000000020400000000000000000000000000000000000000000000000000000000000002050000000000000000000000000000000000000000000000000000000000000206000000000000000000000000000000000000000000000000000000000000020700000000000000000000000000000000000000000000000000000000000002080000000000000000000000000000000000000000000000000000000000000209000000000000000000000000000000000000000000000000000000000000020a000000000000000000000000000000000000000000000000000000000000020b000000000000000000000000000000000000000000000000000000000000020c000000000000000000000000000000000000000000000000000000000000020d000000000000000000000000000000000000000000000000000000000000020e000000000000000000000000000000000000000000000000000000000000020f0000000000000000000000000000000000000000000000000000000000000210000000000000000000000000000000000000000000000000000000000000021100000000000000000000000000000000000000000000000000000000000002120000000000000000000000000000000000000000000000000000000000000213000000000000000000000000000000000000000000000000000000000000021400000000000000000000000000000000000000000000000000000000000002150000000000000000000000000000000000000000000000000000000000000216000000000000000000000000000000000000000000000000000000000000021700000000000000000000000000000000000000000000000000000000000002180000000000000000000000000000000000000000000000000000000000000219000000000000000000000000000000000000000000000000000000000000021a000000000000000000000000000000000000000000000000000000000000021b000000000000000000000000000000000000000000000000000000000000021c000000000000000000000000000000000000000000000000000000000000021d000000000000000000000000000000000000000000000000000000000000021e000000000000000000000000000000000000000000000000000000000000021f0000000000000000000000000000000000000000000000000000000000000220000000000000000000000000000000000000000000000000000000000000022100000000000000000000000000000000000000000000000000000000000002220000000000000000000000000000000000000000000000000000000000000223000000000000000000000000000000000000000000000000000000000000022400000000000000000000000000000000000000000000000000000000000002250000000000000000000000000000000000000000000000000000000000000226000000000000000000000000000000000000000000000000000000000000022700000000000000000000000000000000000000000000000000000000000002280000000000000000000000000000000000000000000000000000000000000229000000000000000000000000000000000000000000000000000000000000022a000000000000000000000000000000000000000000000000000000000000022b000000000000000000000000000000000000000000000000000000000000022c000000000000000000000000000000000000000000000000000000000000022d000000000000000000000000000000000000000000000000000000000000022e000000000000000000000000000000000000000000000000000000000000022f0000000000000000000000000000000000000000000000000000000000000230000000000000000000000000000000000000000000000000000000000000023100000000000000000000000000000000000000000000000000000000000002320000000000000000000000000000000000000000000000000000000000000233000000000000000000000000000000000000000000000000000000000000023400000000000000000000000000000000000000000000000000000000000002350000000000000000000000000000000000000000000000000000000000000236000000000000000000000000000000000000000000000000000000000000023700000000000000000000000000000000000000000000000000000000000002380000000000000000000000000000000000000000000000000000000000000239000000000000000000000000000000000000000000000000000000000000023a000000000000000000000000000000000000000000000000000000000000023b000000000000000000000000000000000000000000000000000000000000023c000000000000000000000000000000000000000000000000000000000000023d000000000000000000000000000000000000000000000000000000000000023e000000000000000000000000000000000000000000000000000000000000023f000001000000000000000000000000000000000000000000000000000000000000000240000000000000000000000000000000000000000000000000000000000000024100000000000000000000000000000000000000000000000000000000000002420000000000000000000000000000000000000000000000000000000000000243000000000000000000000000000000000000000000000000000000000000024400000000000000000000000000000000000000000000000000000000000002450000000000000000000000000000000000000000000000000000000000000246000000000000000000000000000000000000000000000000000000000000024700000000000000000000000000000000000000000000000000000000000002480000000000000000000000000000000000000000000000000000000000000249000000000000000000000000000000000000000000000000000000000000024a000000000000000000000000000000000000000000000000000000000000024b000000000000000000000000000000000000000000000000000000000000024c000000000000000000000000000000000000000000000000000000000000024d000000000000000000000000000000000000000000000000000000000000024e000000000000000000000000000000000000000000000000000000000000024f0000000000000000000000000000000000000000000000000000000000000250000000000000000000000000000000000000000000000000000000000000025100000000000000000000000000000000000000000000000000000000000002520000000000000000000000000000000000000000000000000000000000000253000000000000000000000000000000000000000000000000000000000000025400000000000000000000000000000000000000000000000000000000000002550000000000000000000000000000000000000000000000000000000000000256000000000000000000000000000000000000000000000000000000000000025700000000000000000000000000000000000000000000000000000000000002580000000000000000000000000000000000000000000000000000000000000259000000000000000000000000000000000000000000000000000000000000025a000000000000000000000000000000000000000000000000000000000000025b000000000000000000000000000000000000000000000000000000000000025c000000000000000000000000000000000000000000000000000000000000025d000000000000000000000000000000000000000000000000000000000000025e000000000000000000000000000000000000000000000000000000000000025f0000000000000000000000000000000000000000000000000000000000000260000000000000000000000000000000000000000000000000000000000000026100000000000000000000000000000000000000000000000000000000000002620000000000000000000000000000000000000000000000000000000000000263000000000000000000000000000000000000000000000000000000000000026400000000000000000000000000000000000000000000000000000000000002650000000000000000000000000000000000000000000000000000000000000266000000000000000000000000000000000000000000000000000000000000026700000000000000000000000000000000000000000000000000000000000002680000000000000000000000000000000000000000000000000000000000000269000000000000000000000000000000000000000000000000000000000000026a000000000000000000000000000000000000000000000000000000000000026b000000000000000000000000000000000000000000000000000000000000026c000000000000000000000000000000000000000000000000000000000000026d000000000000000000000000000000000000000000000000000000000000026e000000000000000000000000000000000000000000000000000000000000026f0000000000000000000000000000000000000000000000000000000000000270000000000000000000000000000000000000000000000000000000000000027100000000000000000000000000000000000000000000000000000000000002720000000000000000000000000000000000000000000000000000000000000273000000000000000000000000000000000000000000000000000000000000027400000000000000000000000000000000000000000000000000000000000002750000000000000000000000000000000000000000000000000000000000000276000000000000000000000000000000000000000000000000000000000000027700000000000000000000000000000000000000000000000000000000000002780000000000000000000000000000000000000000000000000000000000000279000000000000000000000000000000000000000000000000000000000000027a000000000000000000000000000000000000000000000000000000000000027b000000000000000000000000000000000000000000000000000000000000027c000000000000000000000000000000000000000000000000000000000000027d000000000000000000000000000000000000000000000000000000000000027e00000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000280000000000000000000000000000000000000000000000000000000000000028100000000000000000000000000000000000000000000000000000000000002820000000000000000000000000000000000000000000000000000000000000283000000000000000000000000000000000000000000000000000000000000028400000000000000000000000000000000000000000000000000000000000002850000000000000000000000000000000000000000000000000000000000000286000000000000000000000000000000000000000000000000000000000000028700000000000000000000000000000000000000000000000000000000000002880000000000000000000000000000000000000000000000000000000000000289000000000000000000000000000000000000000000000000000000000000028a000000000000000000000000000000000000000000000000000000000000028b000000000000000000000000000000000000000000000000000000000000028c000000000000000000000000000000000000000000000000000000000000028d000000000000000000000000000000000000000000000000000000000000028e000000000000000000000000000000000000000000000000000000000000028f0000000000000000000000000000000000000000000000000000000000000290000000000000000000000000000000000000000000000000000000000000029100000000000000000000000000000000000000000000000000000000000002920000000000000000000000000000000000000000000000000000000000000293000000000000000000000000000000000000000000000000000000000000029400000000000000000000000000000000000000000000000000000000000002950000000000000000000000000000000000000000000000000000000000000296000000000000000000000000000000000000000000000000000000000000029700000000000000000000000000000000000000000000000000000000000002980000000000000000000000000000000000000000000000000000000000000299000000000000000000000000000000000000000000000000000000000000029a000000000000000000000000000000000000000000000000000000000000029b000000000000000000000000000000000000000000000000000000000000029c000000000000000000000000000000000000000000000000000000000000029d000000000000000000000000000000000000000000000000000000000000029e000000000000000000000000000000000000000000000000000000000000029f00000000000000000000000000000000000000000000000000000000000002a000000000000000000000000000000000000000000000000000000000000002a100000000000000000000000000000000000000000000000000000000000002a200000000000000000000000000000000000000000000000000000000000002a300000000000000000000000000000000000000000000000000000000000002a400000000000000000000000000000000000000000000000000000000000002a500000000000000000000000000000000000000000000000000000000000002a600000000000000000000000000000000000000000000000000000000000002a700000000000000000000000000000000000000000000000000000000000002a800000000000000000000000000000000000000000000000000000000000002a900000000000000000000000000000000000000000000000000000000000002aa00000000000000000000000000000000000000000000000000000000000002ab00000000000000000000000000000000000000000000000000000000000002ac00000000000000000000000000000000000000000000000000000000000002ad00000000000000000000000000000000000000000000000000000000000002ae00000000000000000000000000000000000000000000000000000000000002af00000000000000000000000000000000000000000000000000000000000002b000000000000000000000000000000000000000000000000000000000000002b100000000000000000000000000000000000000000000000000000000000002b200000000000000000000000000000000000000000000000000000000000002b300000000000000000000000000000000000000000000000000000000000002b400000000000000000000000000000000000000000000000000000000000002b500000000000000000000000000000000000000000000000000000000000002b600000000000000000000000000000000000000000000000000000000000002b700000000000000000000000000000000000000000000000000000000000002b800000000000000000000000000000000000000000000000000000000000002b900000000000000000000000000000000000000000000000000000000000002ba00000000000000000000000000000000000000000000000000000000000002bb00000000000000000000000000000000000000000000000000000000000002bc00000000000000000000000000000000000000000000000000000000000002bd00000000000000000000000000000000000000000000000000000000000002be000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000002c000000000000000000000000000000000000000000000000000000000000002c100000000000000000000000000000000000000000000000000000000000002c200000000000000000000000000000000000000000000000000000000000002c300000000000000000000000000000000000000000000000000000000000002c400000000000000000000000000000000000000000000000000000000000002c500000000000000000000000000000000000000000000000000000000000002c600000000000000000000000000000000000000000000000000000000000002c700000000000000000000000000000000000000000000000000000000000002c800000000000000000000000000000000000000000000000000000000000002c900000000000000000000000000000000000000000000000000000000000002ca00000000000000000000000000000000000000000000000000000000000002cb00000000000000000000000000000000000000000000000000000000000002cc00000000000000000000000000000000000000000000000000000000000002cd00000000000000000000000000000000000000000000000000000000000002ce00000000000000000000000000000000000000000000000000000000000002cf00000000000000000000000000000000000000000000000000000000000002d000000000000000000000000000000000000000000000000000000000000002d100000000000000000000000000000000000000000000000000000000000002d200000000000000000000000000000000000000000000000000000000000002d300000000000000000000000000000000000000000000000000000000000002d400000000000000000000000000000000000000000000000000000000000002d500000000000000000000000000000000000000000000000000000000000002d600000000000000000000000000000000000000000000000000000000000002d700000000000000000000000000000000000000000000000000000000000002d800000000000000000000000000000000000000000000000000000000000002d900000000000000000000000000000000000000000000000000000000000002da00000000000000000000000000000000000000000000000000000000000002db00000000000000000000000000000000000000000000000000000000000002dc00000000000000000000000000000000000000000000000000000000000002dd00000000000000000000000000000000000000000000000000000000000002de00000000000000000000000000000000000000000000000000000000000002df00000000000000000000000000000000000000000000000000000000000002e000000000000000000000000000000000000000000000000000000000000002e100000000000000000000000000000000000000000000000000000000000002e200000000000000000000000000000000000000000000000000000000000002e300000000000000000000000000000000000000000000000000000000000002e400000000000000000000000000000000000000000000000000000000000002e500000000000000000000000000000000000000000000000000000000000002e600000000000000000000000000000000000000000000000000000000000002e700000000000000000000000000000000000000000000000000000000000002e800000000000000000000000000000000000000000000000000000000000002e900000000000000000000000000000000000000000000000000000000000002ea00000000000000000000000000000000000000000000000000000000000002eb00000000000000000000000000000000000000000000000000000000000002ec00000000000000000000000000000000000000000000000000000000000002ed00000000000000000000000000000000000000000000000000000000000002ee00000000000000000000000000000000000000000000000000000000000002ef00000000000000000000000000000000000000000000000000000000000002f000000000000000000000000000000000000000000000000000000000000002f100000000000000000000000000000000000000000000000000000000000002f200000000000000000000000000000000000000000000000000000000000002f300000000000000000000000000000000000000000000000000000000000002f400000000000000000000000000000000000000000000000000000000000002f500000000000000000000000000000000000000000000000000000000000002f600000000000000000000000000000000000000000000000000000000000002f700000000000000000000000000000000000000000000000000000000000002f800000000000000000000000000000000000000000000000000000000000002f900000000000000000000000000000000000000000000000000000000000002fa00000000000000000000000000000000000000000000000000000000000002fb00000000000000000000000000000000000000000000000000000000000002fc00000000000000000000000000000000000000000000000000000000000002fd00000000000000000000000000000000000000000000000000000000000002fe00000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000300000000000000000000000000000000000000000000000000000000000000030100000000000000000000000000000000000000000000000000000000000003020000000000000000000000000000000000000000000000000000000000000303000000000000000000000000000000000000000000000000000000000000030400000000000000000000000000000000000000000000000000000000000003050000000000000000000000000000000000000000000000000000000000000306000000000000000000000000000000000000000000000000000000000000030700000000000000000000000000000000000000000000000000000000000003080000000000000000000000000000000000000000000000000000000000000309000000000000000000000000000000000000000000000000000000000000030a000000000000000000000000000000000000000000000000000000000000030b000000000000000000000000000000000000000000000000000000000000030c000000000000000000000000000000000000000000000000000000000000030d000000000000000000000000000000000000000000000000000000000000030e000000000000000000000000000000000000000000000000000000000000030f0000000000000000000000000000000000000000000000000000000000000310000000000000000000000000000000000000000000000000000000000000031100000000000000000000000000000000000000000000000000000000000003120000000000000000000000000000000000000000000000000000000000000313000000000000000000000000000000000000000000000000000000000000031400000000000000000000000000000000000000000000000000000000000003150000000000000000000000000000000000000000000000000000000000000316000000000000000000000000000000000000000000000000000000000000031700000000000000000000000000000000000000000000000000000000000003180000000000000000000000000000000000000000000000000000000000000319000000000000000000000000000000000000000000000000000000000000031a000000000000000000000000000000000000000000000000000000000000031b000000000000000000000000000000000000000000000000000000000000031c000000000000000000000000000000000000000000000000000000000000031d000000000000000000000000000000000000000000000000000000000000031e000000000000000000000000000000000000000000000000000000000000031f0000000000000000000000000000000000000000000000000000000000000320000000000000000000000000000000000000000000000000000000000000032100000000000000000000000000000000000000000000000000000000000003220000000000000000000000000000000000000000000000000000000000000323000000000000000000000000000000000000000000000000000000000000032400000000000000000000000000000000000000000000000000000000000003250000000000000000000000000000000000000000000000000000000000000326000000000000000000000000000000000000000000000000000000000000032700000000000000000000000000000000000000000000000000000000000003280000000000000000000000000000000000000000000000000000000000000329000000000000000000000000000000000000000000000000000000000000032a000000000000000000000000000000000000000000000000000000000000032b000000000000000000000000000000000000000000000000000000000000032c000000000000000000000000000000000000000000000000000000000000032d000000000000000000000000000000000000000000000000000000000000032e000000000000000000000000000000000000000000000000000000000000032f0000000000000000000000000000000000000000000000000000000000000330000000000000000000000000000000000000000000000000000000000000033100000000000000000000000000000000000000000000000000000000000003320000000000000000000000000000000000000000000000000000000000000333000000000000000000000000000000000000000000000000000000000000033400000000000000000000000000000000000000000000000000000000000003350000000000000000000000000000000000000000000000000000000000000336000000000000000000000000000000000000000000000000000000000000033700000000000000000000000000000000000000000000000000000000000003380000000000000000000000000000000000000000000000000000000000000339000000000000000000000000000000000000000000000000000000000000033a000000000000000000000000000000000000000000000000000000000000033b000000000000000000000000000000000000000000000000000000000000033c000000000000000000000000000000000000000000000000000000000000033d000000000000000000000000000000000000000000000000000000000000033e0000000000000000000000000000000000000000000000000000000000000000000000400000000000000000000000000000000000000000000000000000000000000540000000000000000000000000000000000000000000000000000000000000054a0000000000000000000000000000000000000000000000000000000000000541000000000000000000000000000000000000000000000000000000000000054b0000000000000000000000000000000000000000000000000000000000000542000000000000000000000000000000000000000000000000000000000000054c0000000000000000000000000000000000000000000000000000000000000543000000000000000000000000000000000000000000000000000000000000054d0000000000000000000000000000000000000000000000000000000000000544000000000000000000000000000000000000000000000000000000000000054e0000000000000000000000000000000000000000000000000000000000000545000000000000000000000000000000000000000000000000000000000000054f00000000000000000000000000000000000000000000000000000000000005460000000000000000000000000000000000000000000000000000000000000550000000000000000000000000000000000000000000000000000000000000054700000000000000000000000000000000000000000000000000000000000005510000000000000000000000000000000000000000000000000000000000000548000000000000000000000000000000000000000000000000000000000000055200000000000000000000000000000000000000000000000000000000000005490000000000000000000000000000000000000000000000000000000000000553000000000000000000000000000000000000000000000000000000000000054a0000000000000000000000000000000000000000000000000000000000000554000000000000000000000000000000000000000000000000000000000000054b0000000000000000000000000000000000000000000000000000000000000555000000000000000000000000000000000000000000000000000000000000054c0000000000000000000000000000000000000000000000000000000000000556000000000000000000000000000000000000000000000000000000000000054d0000000000000000000000000000000000000000000000000000000000000557000000000000000000000000000000000000000000000000000000000000054e0000000000000000000000000000000000000000000000000000000000000558000000000000000000000000000000000000000000000000000000000000054f00000000000000000000000000000000000000000000000000000000000005590000000000000000000000000000000000000000000000000000000000000580000000000000000000000000000000000000000000000000000000000000058a0000000000000000000000000000000000000000000000000000000000000581000000000000000000000000000000000000000000000000000000000000058b0000000000000000000000000000000000000000000000000000000000000582000000000000000000000000000000000000000000000000000000000000058c0000000000000000000000000000000000000000000000000000000000000583000000000000000000000000000000000000000000000000000000000000058d0000000000000000000000000000000000000000000000000000000000000584000000000000000000000000000000000000000000000000000000000000058e0000000000000000000000000000000000000000000000000000000000000585000000000000000000000000000000000000000000000000000000000000058f00000000000000000000000000000000000000000000000000000000000005860000000000000000000000000000000000000000000000000000000000000590000000000000000000000000000000000000000000000000000000000000058700000000000000000000000000000000000000000000000000000000000005910000000000000000000000000000000000000000000000000000000000000588000000000000000000000000000000000000000000000000000000000000059200000000000000000000000000000000000000000000000000000000000005890000000000000000000000000000000000000000000000000000000000000593000000000000000000000000000000000000000000000000000000000000058a0000000000000000000000000000000000000000000000000000000000000594000000000000000000000000000000000000000000000000000000000000058b0000000000000000000000000000000000000000000000000000000000000595000000000000000000000000000000000000000000000000000000000000058c0000000000000000000000000000000000000000000000000000000000000596000000000000000000000000000000000000000000000000000000000000058d0000000000000000000000000000000000000000000000000000000000000597000000000000000000000000000000000000000000000000000000000000058e0000000000000000000000000000000000000000000000000000000000000598000000000000000000000000000000000000000000000000000000000000058f000000000000000000000000000000000000000000000000000000000000059900000000000000000000000000000000000000000000000000000000000005c000000000000000000000000000000000000000000000000000000000000005ca00000000000000000000000000000000000000000000000000000000000005c100000000000000000000000000000000000000000000000000000000000005cb00000000000000000000000000000000000000000000000000000000000005c200000000000000000000000000000000000000000000000000000000000005cc00000000000000000000000000000000000000000000000000000000000005c300000000000000000000000000000000000000000000000000000000000005cd00000000000000000000000000000000000000000000000000000000000005c400000000000000000000000000000000000000000000000000000000000005ce00000000000000000000000000000000000000000000000000000000000005c500000000000000000000000000000000000000000000000000000000000005cf00000000000000000000000000000000000000000000000000000000000005c600000000000000000000000000000000000000000000000000000000000005d000000000000000000000000000000000000000000000000000000000000005c700000000000000000000000000000000000000000000000000000000000005d100000000000000000000000000000000000000000000000000000000000005c800000000000000000000000000000000000000000000000000000000000005d200000000000000000000000000000000000000000000000000000000000005c900000000000000000000000000000000000000000000000000000000000005d300000000000000000000000000000000000000000000000000000000000005ca00000000000000000000000000000000000000000000000000000000000005d400000000000000000000000000000000000000000000000000000000000005cb00000000000000000000000000000000000000000000000000000000000005d500000000000000000000000000000000000000000000000000000000000005cc00000000000000000000000000000000000000000000000000000000000005d600000000000000000000000000000000000000000000000000000000000005cd00000000000000000000000000000000000000000000000000000000000005d700000000000000000000000000000000000000000000000000000000000005ce00000000000000000000000000000000000000000000000000000000000005d800000000000000000000000000000000000000000000000000000000000005cf00000000000000000000000000000000000000000000000000000000000005d90000000000000000000000000000000000000000000000000000000000000600000000000000000000000000000000000000000000000000000000000000060a0000000000000000000000000000000000000000000000000000000000000601000000000000000000000000000000000000000000000000000000000000060b0000000000000000000000000000000000000000000000000000000000000602000000000000000000000000000000000000000000000000000000000000060c0000000000000000000000000000000000000000000000000000000000000603000000000000000000000000000000000000000000000000000000000000060d0000000000000000000000000000000000000000000000000000000000000604000000000000000000000000000000000000000000000000000000000000060e0000000000000000000000000000000000000000000000000000000000000605000000000000000000000000000000000000000000000000000000000000060f00000000000000000000000000000000000000000000000000000000000006060000000000000000000000000000000000000000000000000000000000000610000000000000000000000000000000000000000000000000000000000000060700000000000000000000000000000000000000000000000000000000000006110000000000000000000000000000000000000000000000000000000000000608000000000000000000000000000000000000000000000000000000000000061200000000000000000000000000000000000000000000000000000000000006090000000000000000000000000000000000000000000000000000000000000613000000000000000000000000000000000000000000000000000000000000060a0000000000000000000000000000000000000000000000000000000000000614000000000000000000000000000000000000000000000000000000000000060b0000000000000000000000000000000000000000000000000000000000000615000000000000000000000000000000000000000000000000000000000000060c0000000000000000000000000000000000000000000000000000000000000616000000000000000000000000000000000000000000000000000000000000060d0000000000000000000000000000000000000000000000000000000000000617000000000000000000000000000000000000000000000000000000000000060e0000000000000000000000000000000000000000000000000000000000000618000000000000000000000000000000000000000000000000000000000000060f000000000000000000000000000000000000000000000000000000000000061900000008000000000000000000000000000000000000000000000000000000000000034000000000000000000000000000000000000000000000000000000000000003410000000000000000000000000000000000000000000000000000000000000380000000000000000000000000000000000000000000000000000000000000038100000000000000000000000000000000000000000000000000000000000003c000000000000000000000000000000000000000000000000000000000000003c100000000000000000000000000000000000000000000000000000000000004000000000000000000000000000000000000000000000000000000000000000401000000041c72e330b60e0052863df88b19ebb482d412b0a7bbb2fabb9347be5e59453d10078d263242fac5a2ad544b5005bcea367ebc04e01ff7ca279650bc63d9488ac304ad4d395e21ea570da47bf574569c6526312c5c7ad63a6d248f694e5c81c2b60e12693b411d9eb8f564aa43f4aa14e3e30f8574425a6d6491416332b77d2709000000000000000000000000000000000000000000000000000000000000104041414141414141414141414141414141414141410000000000000000000000000000000000000000000000000000000000001080818181818181818181818181818181818181818100000000000000000000000000000000000000000000000000000000000010c0c1c1c1c1c1c1c1c1c1c1c1c1c1c1c1c1c1c1c1c10000000000000000000000000000000000000000000000000000000000001100010101010101010101010101010101010101010100000010151de48ca3efbae39f180fe00b8f472ec9f25be10b4f283a87c6d7839353703914c2ea9dedf77698d4afe23bc663263eed0bf9aa3a8b17d9b74812f185610f9e1570cc6641699e3ae87fa258d80a6d853f7b8ccb211dc244d017e2ca6530f8a12806c860af67e9cd50000378411b8c4c4db172ceb2daa862b259b689ccbdc1e005f140c7c95624c8006774279a01ec1ea88617999e4fe6997b6576c4e1c7395a22048b96b586596bd740d0402e15f5577f7ceb5496b65aafc6d89d7c3b34924b0c3f2d50d16279970d682cada30bfa6b29bc0bac0ee2389f6a0444853eccaa932b2a60561da46a58569d71044a84c639e7f88429826e5622581536eb906d9cdd25a2c0a76f7da6924e10751c755227d2535f4ad258b984e78f9f452a853c52300e212d8e2069e4254d81af07744bcbb81121a38f0e2dbed69a523d3fbf85b75c287ca6f33aadbac2e4f058e05924c140d7895a6ed167caf804b710d2ae3ba62b1b51297b3ea37637af6bd56cf33425d95cc5c96e9c2ee3077322fbec86a0c7f32c15d2a888c6cc122e99478c92470a1311635142d82ad7ae67410beeef4ae31f0902ba2fb964922a4610bb18901f7b923885c1d034da5769a48203ae6f0206a92855e2c01ddb3d6553386b5580d681b8230fa4062948668f834f23e0636eaff70aaa64519aafdf4b040bd2f9836e76b9dc13cfec8065dcdf2834d786e06260d10000381000000e00000001bc00000090ba31e106be659f28af9e2af50d26e9d288793bd2916d001e72b890141088e28edab510df4a749b7353013aea3159668294ffb417f35ce95cb6fce2f0221d812cc4857d625e1a6de8ef2b35b36213ab4616286de404f3fc6463e0e8b3c8f6f8251f122191b6e9fbb1c2cf167dabda8f39040a8bac6c883f89b0ab3b0c1e42bfe51bc975cc64a95028e12e3973e801f67b00000090cf1a7b1afc016e43b921a58db2db12a3e5898a193f7d50907562b5b85e41cadeecb846519ea542dd1629c46544286079c71deb32cccedd1f1174973b6a67aa3aaf1d2383f3220c9364db3bab5e636a6d08c44b3ed62aff9c6317097a655ca306b03eeeb7a30bc8d67d1fca62afb7519e13d782777f78caaf2a8d5bb2ca25f1bb30c4507bb38b914e1cb6de394c47eaff00000090972270ea28acbcd6a954b5e146cee856dd3ef968cbffea1b2e26fe8aa06971f0e8ed6e16de6c6d16da52a856d163d5d489885cbcc8e5389d1fc374fa7fa2722146b3e3ed216d5458023f295c4760c2ea1ad29613ffd7a06c1c8e789be58df9bffcbc51b33964b639a56bc5dd1a51387812f2a6d70cef7584e1fd429ffeea5282e9e526246a41da20e503971e2aab4ec5000001bc00000090eb7ade43f1e76dbee20da4688d5b051f943bae0e4aa13d6c3df97fc368f76d43550019246b1190e00cb20ad8eed3290d9589d0887c900f7dd563e46a8d1cd032130f47fed6c734a21da566075c469ca9006b482df4c9cc45c3666f721cd8f70701754b25a3f1fb4e350ee5fe39bc3ffa18c8563a2f6f82742f48612ba792506205880c01d57a739598bf190feb8596240000009056db89a8f50db43b830026ae48dd55257eca77acda80258fed70ab6544a1da8dc8a5cbf5cd2a6ce9ca2983cd08679b8acf024ec18107b7bae3477de8aaace0d672c802658736d0654a7fe35b810ed34809f0190f92b4a8a070679832bd2f978d06839aa3c12f7263facc03ded7b515d606ec5141d8fdecb686790d39f0f7f380b056b8d8eec3e12522d779459764092100000090255a139bd500720e3731bb9a6c79d111a39618b2e5f7c530ac9847bf2945c5fd11726efd1664ebd7c5ca43a9f001e6e9048151ec3ae655178efecb510735cda8838001ca728b89c9cff98831485a0ad51b5a9d5ad1566b6ce5872da25282fba1e8fb1ab733ceaa1e4cdd5714b5e0d1ba1a34e60123ff9adef7a9aafa7a70afad2b0150256f6bcb582edcdd802322f455000001bc00000090ad3145489922aa1ce466b9c6c7d72ab3806cbfe1142b29704248fb092ffcae20ce010695fada64864571f02888d1a20a5aa6c85bd719753e5a24b112be2165771eeb9f9734c57044eb2be7e8824c630d14af1b5a23cafb9ffb3449c13ad139c1625cce3cf18a30dba95f89318bc26dca1a788d92b8c8cdba555a3b56f61c2702834346ea4b9c995f2a62832453c95ba200000090efb7e3ad6436cf40a50d456603b484b0790c484345d91ef82df22727bf83359189f64f7647567c4a62d967b7c08d97834b0acbcc233dab3cfd4001f19cc05e406707a1752e03e79919a4a5d58e71b29720aafbc5877bc7c2132627780a3c8028b8e670168e23175d776a0791e9dd0ed1005288a9a0ce98fa9918d9e40308c5022e264ba1f04a36c3e747fa8e039698020000009003294bc41bb24fe5cc42ec61e2c7fe1bd0d0a90a37f04a9860159e21cd2ada613cdb72c0a60eadd711d0a14c1fbb8f60840c4ec94c91a74f2f65e989785b3a28abe0876640a898cfd1cd4cf1702ba78d2e9cff04a1744646744b510798e0c4c1d2a1273fb63a9a8fcdfcfc6511b2e3170d66e31114b708a4c85eec819ce2a81d8b3fb0e14b886cf418f5f904818a9513000001bc00000090fc0e40cf1531f14a598f44838136445b088a2a2f45cbbab5da3cc3dcd3a50d9501b376680602ef03fde8f0efbf5e64bdfcd04d2be10467899db77b87f7082e6bb9606f2b3f6578958f7738aeccc2f998135d91d51804d9bd5af9f9bf0cc082cfb4d914ec78b3717588a607d28866895a26d3dbf67613eb7b88507e96c36f8eb3f6d2f035ff29601a7070db25482ca6480000009097af660dc945d784adf1e74f09c76c0d8070aba3c837dd839e01e1fe905a526b297da15eb220ead6988026a4ccd36544d2a2cde5d68b63179505ac1cf61556c5fcb06155a299e98b76d478dfee7254b10474f23c1f1d780f3525ffcaab169a947e8b42a2b384b2a5733a408d8713510a246fbbedfa20466f9551191746bc8bbc612e2d8605f9d70a1ab9ab78393658ed0000009099c961b18e91d5f06f29cabf320bccb0050d377bdacc0b811a1a5401d046ef2d6fd59c1434c6c7f7f658467a1c5df79c6bae907a28dae0ccd73ca52e7148d36d960bf13af6a158381b2b1d3e3d32085c2953c851420fcfd51c7da4402646bdf8b70fce51677ea205131cc5985b6c36661e93db19cb61526afd04356dbd7ae0b742b27c4dfb3866949e5958ca3e842ba9000001bc0000009002c28f7f35048d85da57163a88bae6c1ce86084bcb10f1f2473b9fcfe3c4d31e696db7c1f176d59a8662fd98179eb6bba3d3fdbd9380987823949f7f6670862992d060df1bb03fc7be8e56ea31d1049e25eab063ba144bd855dafe6615b7e3394519bb08fdbd277645f235e627d8f5c922f064f572ff7e9e5d3793d8f17f7be2eb642ef66ab2c937f234ab302db9c01600000090ced262f3fc9841eacecf4ac79ece1a6f329883551b495dc904f1da4fc7149c19b0af3b589f525b192585ad7cf11d37296750f89b42c2f74c78a4cb7b301b08f4995e5ac277c65466654192ed8b7fad9f02e2c8c9b67aec2e4fe51fd582b065a0b1295023088671f1656ef0c899cfd14e24d3823a3b5bf33025daffbe6667c86a071ab485bdcfe78fd70f4ca591a7744800000090bcc6b58413d5bd230d8ecb4e92873d5d59f603c97f90dc0f13a47cedb485246cef9bb7fb9eeb97588ef15e6a560e7807a3bf30c19222223c73d5495e514f9d94ab925eb2efc5cc009abd85306a1891fc06e743b5b79bf848dcadfede5215546b2475e9ba3c3bd3c90a59b06b298c33b30bd6ca5d1de7b231868378fbe77a66d4d7bd8acdee3b91552036f1786a899f8e000001bc000000907d8152fafb9430eb4212fd07e22ef189b74e1a61840e63bac60ae533b51a4ac663cf6f1eea1b7b005de75030992f3df13520fee65eafcbf284055d95061770dd76993321a449b17b3ba8765d4eb25a5e13f3ccc51ccddee670bf64573ede4d604f38baad0e7b745eaeacd14706d15dfc23e21b0eb00589c0b08468185419290deb27e4dbe077c3348d9ab03babad010d00000090ae769576c6e1adaac865282f54be54d7a32f397c72f6b7cac17b3632dc2b7ea2a991549df2d8cc79a8619bf84dffaf8ba5b8032566047508ff713890d1502f9b6c223c2f417db257ac5cd0eb24770faa230c8a792a315873eb4dd5c089ac5ddb21e7f1be282d2a7474d25b7a76a5eb49094555d3778101e0d87b2fc3634faa9e870274db85687e1fb9985a3bccdec32700000090d6419ea66762388b744d8554f1d12fa61e186e87ef656d9fd43afe120faaf85ca3b919bb9e1159b26c6f9373ed8900ec9b07958a7fa94122767e8fbe83fe25bd25ee9b7bbc215169cd5fcd472d29631a2c85122496dc2a16064f0a4d971d00213573c1be7ab46d9cb1688b7a6b8ad6c715cad16545eccd57cfe4e009769a42f43d35f94afb0ed37f56497a4083b4c1c9000001bc00000090578657302fbb2f8cd1f948c6f7ec702485bbb02d000e49b0bb270e4ba42aaed079b180b57324b619358a65bb30e1e99e005eff7cab46e6ebad6a24bc271dc6bf596cfef2bed50660ee5912c7261f602018fe7c0a8aa0039f79e2d7ae32bb2b3e50a55aaf88f6facebeb285461edd481d0853f2e92bc2962d3abd76ee05c051fd4c616902b3da929fbe7147c9be20b33900000090ff20a189a61ff5160a797d3bbf9fa8fe42a4a718dff147034496e00803e918b5010db5c160bdcc2cd20b453f80f52d555e28addc5b3eb493a14a5eb24bb7d6749e725eddb2d15b7abba4ff1d6f51058b064750b8541970f26baee4f5daa75badc7d27cc7de416972251b974fc47fdd2617e2548554329d272dca1848dd9ca17a8655e9c79ed8d65b9e626bb5f4c195cb00000090da7b1475f1369e5dd95e3974f0a61d381e717cd227a6c907b6f79e25348e2de665ce82a5ef35bc7bc8b7186cf954ec7975e763a76e2b4455c0e8f9f6c95044018b6a6612a3ed58f397c92b4f4a94bcc222ffc4c50503009eaf6898361f5cee52f86d18168916c6d828f762c4e9f1e43201d54a8d7d9e228d202cf6e2d7b7732031cf021496f45cc607e5c772ab64c1ce000001bc0000009082828bd7ea8bd0238eb233d84a6d8eb81473be3048e55f7eaebaad27bef571d806ca99b1583a0ff91d2c2dc5c7165b0f8b5291d069f9041f8c134e58abf0b8a7ed410584e83b3570d224a946973edb9304615a0e468b252dfba310ee65f9daa91e885c7d6f41400876430e7e4283cce8164343d81d0d48a970c57c9b641904f1f43584464f43e42fc3e504dc1aa5b0ac00000090b29129ac91e149c208ee449e9369c39ddea7549359465c1dd7b537c1b768e9afc8ce9aadc7064f10f070eff11d526bc86e5c59efc0b1963dd088c344a0d961214951ba851afc71961109521ed643fabd1bc4146b11f60f52a707ded2c1437eed6621c9298cc689d29573092bd5a5d1ec051ac56823011cb0aca04c910b86eb432ccac18e0713ad65ac0e80fd6461220d00000090f264701ba46324c881a50c29a2cbe56d8ce1d52cb12279b9e666d2c821c641e95e79769ea31329b8d108b5889d12aba3fd22803ced02ea159e5ddd413a810e0147497bfe0595045f5afb828074f87841059e29b9947ee7e131674f694e159fd6fa48816b194a40672220689a3ea39ba51a499df559f617205c47ea0b29ccbe38e77afa2f22901a6ac19ecc44b25e024900000e00000001bc0000009044de4596fcd2174f04e08f5878714d65f924bc121137aca1179967c71b97c9f9880aa849d962e6c2194daa6d78a35dd8d7a0d6267bd4014fbf4618b0f7f3b35a8bce6488b5fd9f771288400410d81946029daab01041cabdff51997bda933b63431573fe8ce751f87b9b6640a6ac76e62b89ff965f5678521288c80e7d924586991cc8bd828803774af1110ad6af2d1400000090955411b3931838621ad246ecad090ea826dd4ddb7bba4f2d692e73d6fbe197f0754db7d8cc340a993078595b2e56c9727cbee8412af57451e251a28716a32c0aab99361800b9c9eedbe58a70207b8dc50cfb4c5670dcf4e3dea0b0d90611be362f0811e4738223810dda308e068502b1275033156d228b2ac72d5c13783764ecaf93fdabb89c3a60dd2511eef546ed47000000901a490364cabc8dcd69b5311f1a3146fd1ce7b2ed487e7770cf9edb318ada757cc7d5a5ab38ff4af623532d17a94d6a0ce99967aae09a722e634691ef15ce8f241bf3b34849564ced406f04e40248751819b7222d607629479e4e0bb7d076d7639c8f6ebb93530fb24bbc03e884507e002530933112acb5c27690c1babfe86191035189c8c17c66fdfcbbdbacd583934f000001bc0000009054be3def72a403f5c2c9e65dfddf409582d462514312202f646d792003166b0f85955514e5e307e9935bbcc9995404afe1eb12bf2ec671eadaf9a846782a983585347a2cf8d5682e9fec25a588fe81eb13ba9a153f0210840b5a343c1223e88a80c467332cb74375602f02a409346000107e5340ac4ccaad5b3281cfdbe0212f640df8b676de03b5f8540623253c6b7700000090a0a2767711576ce8e6cd6b6b1ae5cd8686785990afde849f7029ace4c60f67f07b05231f390f23d2baadd5f0e76b9a5ebde244872dd5757180456cec054c62641187283fe89adb7657866faf7c6141320ab18c2342b86331365ea3f744baeec431a00e8c111f1f8503a7362f60f6eaac268c0ff4448be08b68f919909b6846c9b64e18a4ae9d7e6a640d2d3b45ad9a5c00000090174b52e1c4cc5baa99d81bd062bcedcc8055fdef765174be6dd9306fa931d88888d8d069757b1c6901c95158e8536627f997f10126249fb4bfa15f1a252ebc4a8f75bf5ab07db6c530c9f4887381c5f30cd5ada4fa5f13eac5014e84e16fe8be85fcee722e18675ec59856d364338a150167f37baa8120dff1c3a2422373f34b36cc620d32458375f28073ecd0e89875000001bc000000902a4d89127259186300e7dcefe2b9a3c51c3ce7262745575c1893cb6ec67db59cfd6d97faf4d955b98d3a736ef3c2762b096627ff8d84c7b9a766e5b910377ace261a27c57f67e454fc74b3b65be2310d00d4833db1c3ab4bcb66cc02e8e389148c4c24cf6fd4759afc32e54948a302a5239b055f5e3b16ad3dde597a249742ca3151434363779a42e0900689025ab25000000090a47255e90a224451cddb8819b9c15284c0ab2c79cb98278b3c175dff2e628552793b1e850680507fb33a41f13167445f80dafd01f76d5eed4217c123f7414f2475eff882cd5e6c46216537ceb57a40482e307213037d9a9a09dc1446d1a477b95055de78be0a8b6f39aee6e84b9fb5a8022146920ba50aa9621192b700a6068dfb8e2174d2ac773989feecc6d28261b30000009031dac21a1433f531dd18b1632847fc0dde1c17bb02d5ab354c02f4acc795c5a8cff0b773a547814cd9831bcb9abe8e5acd292deb1df4d6e6dcd1a820e3c17a175c89b40c49f67a7eea27e549af42b416198cc216321d12a56329fc95f648f02fa3224088cb61154ecfe1b9628a5747ef26479590b8bdb8b68b2688ec9b7436a3e0602787ee7c1dc53d6cc2e141c9e27a000001bc0000009042036b440f7bbaa76f6cfdc9b78b13c6ed8954565059bbe1b6f3dfb5bd88c2f28a2f29e0e5c261c25a2e9d7e511d072bde43fd9809796effe69e8aea3be54508d8a9fb29bb6a2a2a9a4851820c90b2611fa5229daf2fda66f0832ba0b177916de6245b6144a4d9fd5a6d84d0b3d09b5f138a8fd41f0245790f51d769418351eba188c24422e29d28b6dcfa5e108a5ab50000009029627be6070f320d9c0094d3745572911c217ee1889a111119f5bfea6769c574a55a30db964362ea4f37cc38d99b125bd96910ad35931dfcc5e1d60460cbcdbcfcadc88b1c3c31541b7bb37f546b4dc91eb9f46857491b80dc5b1a6f3b7d335c2c5eff3fea40db67a556f82ad31b54452f733fcefdc204055851f24bfe942753d120dacedbb004351ae40fbcbc40e44700000090e77c6e4d7f802613a018998785edcbd7f2d0fe43fc87b5297c90f040a86854ddac359d6ae8bea1151e3625d27b5d726a6973ac1a715016b94f596652f1b5c34335c29db6a74dc5f870523c9da04268891c8fb300114e4a14bc53480655c4b2b0ae1b32d51db9e05ab97eb74447c662aa26552991acc5c288822e24649477c17ba04d8173c6a271005b74ba702f19f6e9000001bc0000009033b75699124df3a003208e2f077775a0c0cf6b5f43b10d007ac669f67830f4186c27e63d53e8d10ec7e04a587d330a93303ccdce48e4d883814687d33c3c6863376ed002b2a9a3bb715c93b9a7077575186f9f30356dc5e9bbfbc7448ac599026c258f7cfcecf3b1386812f03e6760b129f10aab131e1fd053fffa9e69f0affaa6005b7788f7176ff1add5b4eb021ba8000000900d5f42e0a54bb5cd368f9c464ec55077b38560a2dd87b7fcc8592304c63f8b713bae6a5f6af20ab8ed03a6b800a12059677496aba92268e1d36f3630ce184a2547b161ba35ad3ec88535cdba733d0ad306a7f349d6ea6ba73641ffe063da5e81170232c57e6814e422518f2ed1d5b40724deff7d2e8c352ee005a3bbc20bee5b8a835ffd076356fcd10cc7ca052fb72600000090485c8561600c40dff828e00dc64d85de0386db3cd95adf13dc343e26c62cbcbd50e4f230a7262fe5faca058dcf76603cd258d997d93d2804bdeca854114dee374b14a94cba4bcf81454a0e5cfdcf7e071b8700e9a230d063828289d9db02e2f79f84dbd59221b39851b0f9c14a326a9509a44efa10eaf2de68501bc0d20799417ae2b08b140581df7fd7809666f48878000001bc00000090793c45f5f8ce488890d12547e0be78376fcffb61c9ba15909bd0d32c4863702f7a4308e7be8214eda46614b17ec59046177137f2fd4191f5214e9035bb1307efa167cb0f9dfcde8ad8add21435e6dd4f23fca789a52337d770bb3239c51752950f463602450dd61e38a292f7467c9bbb0236993135d3c1d9fb5c7210bd33fc7f5068db47cd18a9cc57e585c5e05f526e00000090f849a580d2493d30febe102d84a596007bb41749bcd698232336b52a1e0909ad1ce9c8ba3aad02134ae5b160ebcbabe506d9b7db0a935349e979b8873be837775b6e688777cae831c426aa99a5b95fd42c5a899c819de878d741f833adce678a55148c00e3e9ad526fd8e8824367e48113412b6ebd36bfd1eda1129277085d388afc79c6e0c9c5130d377378ffc8c2b3000000901a77e1fdf2943eb04ad871b97872d58513b87084509e93626d91fb435e5cfa0bb54ff8bc4db8d12df9a027c4f5cd281b5ef6ca8e187d878eb2c3dc3f69df34b7a45828827534373c7857f949f23ab28726f8338613dabee6775bce5af93968516d4b88cde4e1e835f389af54a07f98bb2f583429751b5c5743de521b13e42b23dd3ca73823a09df3cbf95161056e8727000001bc00000090a526b2ab5d1424ace96e20ea34dbf9260b792747a1650ec62bfdc8c733c005d018f5143a029a821c22cb72a30c3d32a9a9293e905463204cec2133071ef13a270a49f163a01a7788a27b017c8d80f84003705bcf1f46491f774b4b66a93884c7e204d37b90712af352cc5369cc5bd64716bb58ad99f05542c64b16267213a12549a9f3dc14a7d0b2c9ed43f170293c31000000904d582dd6d58282e96ed78e85573a6bbee047a622bc85480221f99451687ac7e2bfb2bc7e7d6c1186fd0dd8db4f13021a6163e3300104f0594771afe76561fbccd40cd7ea868e4b3788bd8534c4f301b2006810388a2e469d05393d0c3f308af465af0511a02e0875951b1f90ee40338d1080f959493b6d44e1a4cf65a157e779f21c53efe38c7ef3d907887e354ab90b0000009053547b42020ac7e26e414c1b59a37ae16b2e7fee712797526049fecca537a0ec3e3682a0c6bf499dc5bd1ebd7f002a340e9e1788430993454d5e1d63e8f814e0dc25967f318695e3058c6b31322348ec3041f03ed390bd0f98c27f9515aad0d8ca13ef9b231be19633d8897002c590ae1770f43809e212761fb5b381ac99adb7f0f77b1f8f1cea86b4580e6e7fec82a9000001bc00000090ed283635593ca3f7d15a67e70784417958b09467d558ab99746adb7fb73519adcbb7e3a07bedf202a71f9ea17787b40bcd9e2b3d8c543ca2c5c21841412a0e371b735971eedf6b99bd29037e3fb9fb69132446fcc638762adb7d51818d49bffe2e53baff09e8544b330a1b5303b623fa1735d51c1ac2b6c492c9c84d1a6a55026ad443d5e385d0565e9ab987e8ee4142000000907a3aa2434f8a0cce281947d36d27b0e4e74fcd817192a81b6afd27de57f6e573b0ac854034d4a98443f2f11bdba4657ed7807ad8844c1273300e69b8c97dd2f6357a79cb090a0d3f8a1ea3aaf2eb2fba2feea06a3e822e5467185d783c488c59b7bbc3f2b61b9789e490a4e9f3d07e091ffdd6584cdc4af094e14d135133008ed82f86785bd7e8038889155ae9d98620000000901e128afb71d9690e6a4489d62164e5fcc2ef689bf0d6fc7990b7ea5c9cf012387c6c2c5591f66ef06662b282e37ae620e43eb43cf5827314f05aa8707fd593282b096bd0244d3e6714a396396aa23dd90ea70748e823e25d9abe06b2e098a39ffc83c00ae2facf8007ff3c87b0bd02721655b67ba635dbd904c6a289abb1a67e40e8070e89e550d233f908c47e06c3c100000e00000001bc00000090093f193e90ad5fcc72e550a3f0a76dc9ee968de5270bd8ce9c08172fb4c2f3b5a26da0fef66c84858b74f49798dd226b31c81c4312da9d68caad1b3c8b96dbd259eace348007a70e934974220895d3680522475c527adb8f5a4204e4587d46425b8b51451a66c41718931d544c99763b0ac2a4642ee3bdbd4581c9321d21412810cc7418e358adf228510ede35175ba8000000909f933c55a67a8891db1b2d254dbfdc0c1d9b7728a5cb7a0d9d2332c8f231821c5e89e29bf226603d34b2e080b52f4691c0ef7542efe42a4ccb99d41b69c1dcde57fc713ead365379c54479678690b5b30bc2370bc15d494b92f87312d7e9f490d19473a552601a755dc3ae5c081f0a672616a830f55ccd94db52d6b2d47ddf4aacde4e8d578bc7a6d4b4aa7c8aa37118000000906a096f39f3c3ffe3cb67047edcf09f5b852743fce0f1a5e86fab0d998254a71d45b29ad3cd74ec13b9eb9c9834eda1357737ba05e4910a1972f70155999e0df4dd8e990d05989768e4222e17f1e6ee1c218c9f0b2b93ca885905f55be91c92316bef67aee818862317377a4c834f01e52bd04ccdd32305997766884206a1e66aafe4ce850160c6bf1409e4d7cc2862f2000001bc00000090fb0977cc718446ec0e65edaab214165c8ab241eb044ed8e8e90655276091b2caa5b86cd7a216faf034ae4e3dd1e16bb749da5771dd976d8fef43d9fe00f213731dad21fe720481b4202ee296c80be87f0d451f969f70c2ab0745157ff132bcb72f557f429a71d99f757c33c1a807f39e200398cee44bb61e4bbe4ad8cf534f21d221eda0163937272c18a849011b2bdd000000906ad3b55017ea4cf98784aebdbdbba67c19b26a55d9f145b07b7a2264dd5bcce3fdf59ccd88144076fb3015b8928ad38df1f939577eb411f8e1ad8959b5cb1dfa102b127c75a185c8a7911a39a4950770167f7d4a5c0c4a3e154c7ab738608e8135426db350320a6651f9187e242031d928de5e898cc59d1d9e627ae27276bb6e488dcacc1eef5d3e2068fad43df6f9fd000000905088875ac73652da0184f89480a4e6353e891980f990cdbb3cbc00629baeeee6283b7334870127cb2a1e888816e32f44fc89b53cbeddf76eac4ba0c80080e33cd101ca1a16699149271629b994da0c741d6fb377af7f696feda2d648b7fb702462ed652b61895962fc725591d61aa4d918ee63a514436b5b7ae16ccbc59ceccb19fa98ff49d4dbb3645370682986d774000001bc00000090ddf0918144b46250424d393f02144553c7de44279ca2293101e336963aedf73ea2f3bb7ce6cb414a71f93698d6ff4644767674b827463cff11decac38484077c251d1ec137b40ae382b576c7ee06c2a727cc97d0bab880a919ec3a647ee4f573eefe111af28c4c61f660eb20afd7a92d03ef2108d1a0b8002013c9c85e4bafc39100fbeb3269cd2057e54372c4ea0c0400000090aa319ccef974a6bbb46abdff5121f590f0b037081ab8b5da9e648292d4915fecc40bc691abd959b2e690c40f5484a1079fe2daf181106622a366869bf5d91dd8df73a59ee059f52e6447d7e831c1c31720a425a18c509875a514216d3b85797c8eb73e451153fbfb3dd21b8865989f9e1265c57271318d4d43d0f1b4faf7c32e52f82fb8eeed4b57d7e1194449734c2300000090ac0b574712612f176bef83fd0f812b1a100ab33f0c4ac5b9f509eba019429cc2312468493bb0e8d10603f8a21ef4ce9fba8102dd23dcc4c05cda8435bbe30b2ec877acb49ed250daf1c502572f2faafa032b4de8ab4eec8ea3d4d5f4fc039094331c3406fe8ce09f2d8697134c44581c1963f64648ee9a3b5ce0bb52b9c9983179f18fab2ef9f7e4368e16d72a8ae399000001bc00000090d5cda54efbbeb6d54ed2496af376a262cb98bf36031064f72399c7ac2be9864ee33988b56d96393fb7d2eed6d9691d9d8b52ab6dc8bf1a78d521103869c50434aee072583799f9c483b9dd669092e08e0e0609065426a6af1eccf3c216d575aa8bc1a893ec1600392b4786f9286ba5b31ca5f04127a582cb84ba9e3b70e5e97226a411c4d3eb275bccc5e25b483dff91000000905c9e9fd56e8d560e67b778b23277cfea9bd231347a999326e3a264ccc419577b44ad76169a1c2869e665b340ddcbc7a9487ce6b0495d70da9d771c9fa10b28f24ad3f16dbcac3cb1d014f0180d8cc56e083ea65e5c4580b6fcce0ed318bccceca8ed4138f01a695d6ca8e422199c601c218a1037d1d3ad0ad0571f65a3eb5172fae3e14b9762a594df1e52a041e565ec00000090ee81921fadc592d08ec6dacb28d75464ff4871184e26753752afcd70424a7dcce16bfe5880f59d9c5c627b0c340ed6de8243d196fdd6c175a6b1f574c33facd590e5214f04db6b403026e6c8f5e459d92af77b3d45409a6ce29c570b5e0e255857bcf620ce7bd8fbaffbcefbd2f7cd8e10c45b15a6ecb3ae017bd588f5f0b2077136572a8d1b97acf8368e4d9ef3c4b6000001bc0000009094cea4a845852d5205a71eab1377a763da5fbe70ea95e68602ed9530d706e9734fff6f0f63d1c1badfc518e6c71eeff7341d3839d377e6f2565a28ea7d8a9dc778e96eaf95ea3df8174f47ca08d6d8711dca8e742fafed2e609e73f17f2c1c9ab3cdfbaeecc4051c2e8ee2fc80bcdc622297c7914d843c2111c3a2f36286aa1e92b62b22c98b1702732b18461fcd897b000000902564f7b6e01aeb829a03c67cfe9b90d02bf5d0a3490c841cca0eb13d43385c4c9a6c44e43b6a97d7a6cf823aa6212481eea0d50cdb78394e37ad9c208b6319a06c29ac7a4fb39f6512d2c7aa585d4e112a2fab41500e645f0e48daefea78b5250f0af39036eadd04ac3f8d1ea70d55552071d72c960f1ccab956353278ea0b73197f90fe7991a91617f047f265b6dd7b00000090ffe5b429f4cecba7f308784f79f9171cb8cbf0cf688af5f6f345d43f45f9bdd209f58424886104c6d8ef1c599135d142ef79c26de39088f5a6da5fc21ac20253dfd7f02c28a90b6f4f568f6fbacdcd360248c6500072be21b1a8a952e3f9f6429fb8a509ea76054cadede9d738cc880f037cc0726440fbec580757be0ef5f00860cb5aded77968531e3effe172d52a7e000001bc00000090225391f1f07678b6218786b0fb16dcd6f703fdae693fc8947f64f59f9ecbf8608f2b2fe7673bf7212adc28f930c6c679241913e05e918c1021d1baab8165c492c234d874565789f555f35bd44c057e021345c0508967c6eec0503c73cdf25e17c20d22407bb65dbdb2438c991aadade9034e536ce6c41c310897c7aa76570d36fe4580bc72140a977cc031903282426d000000905a5b8d400eba99dc5f3f2003a81b5c476ff36e092e77f4814fc902c561feec7a616ee7aa66bf9e1e9d63427a0b17654bd61d0515aab1329ab28bd5e1d778ed0cf4b8fb27a666fffb37bffa867c7220ea03da67528e3c0d1591ca330b61894ee9e794c8e5f50b8d6ed366b0ac5d78e7142d0293960d9741725c51cfb2ec7edeebefedb9a769256f0c9154725d3854d57a0000009059afadfe209eba3c172cebb6ca552534b448a6471b27582119547321032be5d3188fea33c8ed8c3d60cb4ed488d572515d463a2d0ddd3ef6d237a7658585408860f2d2390cd44c6e5c27fbd7c3a6c9e6238d70cd02964baef06b1907ec9effe1d7c3be3f6c44b725607e57d503298ba119ca39e81a27a701bcc23319aa7a31b583b2aa5c5fa946f762d40389029d0a8a000001bc0000009019f18b0993ea58cc0e60a6ab12d890658ffbc10abba2deb8ae8da0e5ed86deb44c46257de38fcd3a801f11f69bb609d282b440cc5177f384f131cb5b400e4058f53fbecdafc5235360c2b0a04bf1dfd2186e31047ff1c0dc33298124a02b36528ff0012e161cf63bbfe5e133c713dcdd182181450fdb65bf770a28575b54a147555d352eb9793a2f380441fcaf35cd4600000090eef87e9ca7fc37aa991f73e09b86f3fe5253bcfbbc30b979a631b36bbff441f90f292ad60eb2769cb54e467f63b16db29c06550f96c1430c720f9c2071ee2ac78e48b81ae4cf2afda75505db1ba072b00d7de00e7e8f19f99380e6c3042828a8f7befe8f557fec8deee36203a6cb78372ea43ef82b92e4ca5f36bb118ef05c6f107358a4c9d0bedc1e42b032b7c30f4f00000090a3181d013c7c27c80b6ed6302110e44c89fd98c2fb3ff2bde91a2dac237a0a8c1b9790201524d4add68c0e3fa6bc6f83be980bafca18563addfabc32f4e868736eb61d3ba99b0436838a74aa0f6a27b513c6fe8080533c1c994bc365bd66127e0518029fb0d1e6357fe95e72d82579390ad10e764d612a8176912a688584f119c353e48075e7c78915c043bc6f6b9c68000001bc0000009054565047a9143d945bec3ef338e22ef38e31bcecf35d39a8ad9a53932242e8ddfbe12625d981e24ce51cba092fc1a7098017110d642316f2052b6696ef8b048764b0ec7cc884d4d38afce13c5c3b99c919448e796f40205756f1436e1e5040aa0ced2f7df5f4427286a3b50e2cc8de6609eb562e96c664a796c6e9c6ab269f8fe88b21f8f501ba5b59e56367cf0cd75c000000909410338d8a23b6f386369356c3fa8e16937394e862b9a785af0b426a1bf893341cf6b3a56f5de26e38b57943261a636b4f27f08312cbee701b210e00abd3a1cab483c190251ce200b918cece007f6f9c0f0b5b31f9005a5242a724d86e74f421c852b6672b4d346c999465a443e932190fab16bdc74f7df3b8966d6cfc74c4ae5123dd6046f525d306b76cfd90c725b9000000906ce36d5c10f1ceb7973ea03a9d07348c09843ce95e9b46b9831929db43967c60534999bab1e4d6c548d1e967bb5035eb7750c28dff8944cb6056199beb6b81f4dc42cc9721ffd2a0ae94bfa83ca753b802f53b499f999cfe44cd302b87166848eab643805261ea69b1fa606d5f94bc1f2074659ece90d207636f508a701a85a12efe6a1e9fb074885b453366792a23f200000e00000001bc0000009004d2727c81570802fd60f39187fe8139955c3293e7eb446c15f8c34b4b88800b230e7f1621f1b4105a5830a65b082f4de456ff515b9c4794f474eb33778b47e82b96bd212fe4f3d9f3b7e4dc4bfb83881ac62562528d78dfcb7e23db85f34247a5dd9af7ee24f1df1d855a488b07cd0a231dab09e0487652daafe3f5b38ba18c8b8f6c56f998699f97440ba56ac4a3ca00000090918ab44e96807c203a061120c62e8d898f5975c91cd10a40c33a7b97c57e633ab4b01c7f9e8cdb2e9b2505804360d60e07d686e68a98d59965863cb231c5864cbdeb27867dc6d3d575f6311c890d651508153bfcb90f04d88d7ae3bc27455060bbd9117c98097375f3b910fa2b1a26f51f898b19b7dee695e73f771d2a7efd51f97a1b67fb8cc6d4cc2a4fdf1315dd6400000090b6e162c9185d396449572d19f140bae3832edde96355c06f28792f352c9e0f1d16602a80a9bb4b525b0b9ef68f4eaee1f28700e97120f936f7efcfd722f383f76b22b19e022f0ca46e885d352f1990602e73ac7a250e86c2bfda2766738e5385f50188d896bbfd6d125abdebe893359101d9779b506ca9ba4be0dc69899f0d21c92aca8b85eec4ca8b6e58e8882bbeff000001bc00000090eb3d82eb845cb36112c2f181665371298b76838e12dae58ab41723d1cd3353c26c32ee720ea35760272aabd28c3fbfc75abf24faf78508bf7cf1d731074e263b286fdc2faf1c1529bf47b1184b10a62116e49384fb35a370758682bdacb2dd518fdb3a9c95b472c400be19fb9a510cf11752226b7e1ddf74eba9c1589232ee94e9bbbc4079ac97594a18ab4fa42e8b8900000090e4796a030625a284925999aca258aa4388010e731dadd1396dd25decdabaedc1658c229b604223b888e096e04b35179cc499512920ce0d63359e3b00f2b876bbc1655f4e043e3e034f0c541ca9008dc12246a72afa3aba1fbdb8c78d39bdb12ea33310ccc648575924d780396e3c5670080bffa4e9882535ae82f3ddb54962e6a4ce4bc8dc969a4ed78d286c0712d7d700000090c639050d286dc2032baf8c0feef5a51b17ae5142ad49000b4baf316ba5a6c518bcefffb78de74d207bd52b08e92fe7cc3d53852aef8d6db07b8bf8d7b2ac3efe338882570d238d89b603c10d98efac5d29e4fbe16fcad139750859f360a169cb99767b8ea4a5e4b13e1213d330dbd58d03cb09f349c8746f02da530d5ff250b6ae4dff04e844236f8b2a716e128d8bb8000001bc00000090fbae53f1c4b11b030f6572ca0fc4ecb535cd813d5136a61ab5562695386df70413d4cf697f4d63ac1a59faaeedc8dad4d41a1a457e2abe9e5bbce156c2c812bcccc0963efbdcaf5697607f383f8c778a18a241790d2081fc2ea8f42ce362704be163677c8e6377962a0a571ac8725c6e2185d8e812b2dac85eeeb0f2a2eb5f9efbb8324b504d3c00d2da408a74eac2d200000090f3d302381fc23183fcd2aa4267c5086fcac6dc506b596a91df26e2d521cec254fdfc8981e36f9c1fc4bb0df11860e7761d4e0454d061e704b3f4cefe920f09c847612885592567efa4a762887c6aad14074c4d33f30a2b2f299869ea1dd678f72db17edce419c26f167b56d396d4cc521547b04937acf726763f2ecd3c465ad8330a76a470685d1762f46235619c96f800000090efc8705e36daa95de34bf56295bde86b2ce2296fdbff32476db7e83ca1a8dd0151d7dd1bcbbcb0073d9eace69f8bbcf93df2b6dc105330ddb49f4f8d61d1a55b577b3311c325c19e380a185480d748442e6efc007caaf50c94506d1c234f71eab1c19b2a34161332e426121dcd75f9f20dd18e29f55d57daed1aff739f5ab4b44dececfec9395dc44e620020cc46cf65000001bc00000090aa9507ff4c7399f8eae915959531729413f12bec3fd965b24129b2fb0129d1fba6fe9472084590541bd879f41a404a9edf78251856f242e8a9dfc625966695b6f078127499b10dafbac54db22bf057df0146631f3ee9b6968490012cb08275b03474d5905e26705e6edc3af9ac5cab0d0fe1b514c7d92fdde0724866c047d15c07c1807e58e7484c1f82daba3c39193c0000009079d9aed959c3df77f573a4053287536874b727627dbcd5b654257498f18e325f5ce1ec47dcde04471bc1d951654b07bf09f4754a09165c2a69280622567ceee202b61407411df752ca3890373bef274c0f080bef80811bdb9c04f37947fd762c4a92d492de473bea32ec08893a50992b04eda0bf977e89cb530067ba488842ca83711d0296349d674c1ac56afcec60cf0000009090bec024b80a113256a351d1706a64766b375cfeb06cdd20d7ef75b0b3647622f1cbbfcd0a60fc16d29dda344ce39108b8a98d6a6ad2582363f622e871c611a43ae5f098122fc747e6b7627a31cb6e9d1ee2ee99aac798d2486990af2c866b6b3f8c5b3af208f72ade7b51e1cf4888c81f88e8bb1e1af435ef29b251fe6724a708dcd602b495662e2aba9df00975b1ab000001bc00000090d65e642b9d1d9a03a35426c9e93e1c9321a03b39d51954b9c72d2b6a230e8951c7aff800ad81f7bea03fdff671fd88f666cf5b0e226d8de735cba18bc07538abc29d93f42243250f1e5fe0152a83f06c0cbee8237f842b4b6ce451b40ce597bc3c4a06e3939c5a685da56af2f6a3635a0a3c1fb4ab81de5eb4b0508ecbb43df7f5050657d79092250043db9efcfa6a3500000090a0dcb21a7cd02d72b1d18d9efeb0d1da493b42304a14e012228605e5f2f2a5e044b11ad014e87e114ba74e00ba47c96ecbc5d48f7fa74868bfe0c2e00535dbecbe7e796af28dc37c4bdfb6b1ea149bf71b39f68996b10044ced1872871d90d73a35b723f46064ec7bc591d13760e5edc2786029e6c09f2dd1399697b4513db9394fef770d01cb86c21351e18fd724ced00000090dd806ae7ff708846d36f9ddbb6c8f8f8903a2f6d68a06633e707c7be0e5d584f8f51657c46f027ff5024101798a3692a554ab3b8ad185fa4a321e64de7f6fa26eda8e4768a5b8358fd6cf932a138c3ea0c03a0d6b04a12d7a0984f2ae9711483c43ae205251730357842873083c2e19c1f2d576fa667d9d5384ea6e7b2a2141d46f3770bf6eb8aa659f6d254bcb55a7e000001bc00000090892b14aba0efdd81abbd79b8791e7b14fd1b4fa765bc40491e4fddbfa21054b7ee882f95159eaa89c28821a472d33504f738edfc8c04398a16e15e8a41558349f3683a610b049e72399686c61bfd5a802460fbe9225973495867fa3c43588a6c734391f87824aef78bbf4a201e7490a32ddcca538038bd1403fa260abb9e32d21cfe381ef680f4410cca678119e1c28e000000903bf5b5f5bce33dbe74db673db3f58944f456f5276fba5a7a59e93918380b1e151c1ba18cd746607a4d0cee68e36bd17ce577cdb7d18eb7887c7698c8d9ac52af445bad20b520a4bb1733d953d00156632de35bc8ab480c2dab17a10d2d0faed1e7597a45513bad38907ab546c75ebd3a2d69ddfba64193b6633153a915dde34823f2560c0468c8822db4ec69f8b22817000000900bbab109aea3fa753bd5ffb15326043f7b3a5d20d3ac6df24379ba28804959fb4f37dcdfa8c2b17baffb16ef63b1a29229c00dc6b289ce6ce7868f656a0af73cefb428854c07972026f38e45415f40542a8d166cc63ecf982739a3b0acd7483332373e979d16a04c3eacc610fa4963e50aa445b3c940be97a6ced2b5aa8c57f29eab7e4c8548515ce6c252ed733e0e2e000001bc0000009045da1ecba3eb2713931e0edc750f695b1f1b175c6b5c52b5d3f8963446f21e117e9ecc7904ddee87ef3eaf4274dc9ef5dcb0707042a7bc2052f849af4029c5ceb3e6184066d378d21f6506cd6f44b7720cce17b80c0fb48bc6486ce98f879a87c8d65131e0caa7ceba5b9e7bea2b15a82616f160655f922b53b5f32e7d78fa77f21b6c41be2237a7d5bd5763623ed86f00000090eb156a017208ba060689d4614777b3e026b5407a0e981a11d80b2000cba96a622036f63b58f94d8ddb4f3f51432cfa33b2974823715f1abe0b372dabfad69579aa2407ee91aaa7563593254b00948128216fd3efae361690f87eacd013cffa2e55c50a49895e52ca16991ae7ad631da81892664084cb1c521fc0742dc0732e8a638618e22f0f8b2316a8759377f438c10000009028c973afc8072005b575c14cca6bb159002d2024fc4ffcf6b4a90fc443e84ebd23cb498ec0cd46685f730219181b8ffdfdfac8573904bbe98d17096d347c9c8deaa6f18cb539bc7222d5a59c22e775ce0ae863c19c6388afba27d1a35a36ed79e759572be92591b47a92751a88094b05136517e0b93be6f56e431d32b906963ffb1bc9f4f4e7b140e668690efdaef521000001bc00000090c251c2887e122e6cba8c451fa308cc66262d76f435ba48a8f121daded6f254fc8438a1a542472ca5ddb23ab196d7b27c9c268a72d3fca4421272be7eeb7087c09919cb789fdb12606d2cbdd6327caab62a917b9f8e46bf2c83ef29dcd35885ccc9a1b0d88f6e6f78cc419399175651602d67e05de51d75535fec4c7532bd1c74415ff1b41c539a863578983fad6e0a8400000090082cb812c86e14b312a3e43d8560a673bdbde8673d2a728f99cca2b9e7217344343028a7cec9d079432a06e139bde2933fc59f0d8ce7befe6d5a7dbdedf5da00d8b564af7a9c0f159dfe7b8247d0ad57296828fb7fc593d279b9ce9f48e018d69ccda7cd6d207582896bbd0be7b9a9bd280f371cd9b7eac113d7017a1f48e8cffd8991a9c31fb59bd3d609ba0464774e00000090d13cb195beee914e24b867f316ed0a00e1f93ae1dcb1eea05c989770703db9870b8eb3fcce0a12674a2112511bf7a36efb1db8d7ddce9d2ebaba71ea0c79c26b204853d3e1070283efce7cf37ebd775a2b3f0a080652b55c71d984556754bdc5e74239be9a870bad98c9478a5e1f31760f052197f58a5aaae91b6cb1aa03d8aa3d556dfb16780855c3d67ea74e282bfb000033a000000ce400000128000000906ced29e71559289b41a30bf1c8223f7f5e190c5da0f18e3f2fb36755bc81632a23476279b21af617e3d5816669148c207bcfdabcfb5b37d9b93055722276f28be39fe324bd2300bf8242cad86eeabb7205bd36533c669991697f7ac788acd5c818c25e3dddc85b52244d0f6934006afc1181fccaf9c0671fff329245645cf57661f534799facd0b91395793e958799430000009061165e7ade49c3d555e2d506013851883f97491a11138301a64c23d06d36c60855ccdd2ce6b090b52213ac32f93a3c1915aa511aed6aec6b43d4889b7e8e12e349e3c0ccf01091ed55c8808debd2a49b2e0aa4352517caef9be648a1866a644d5c3232d04955edaae334e4f74a32256d01d668ac51f293586688c588fa1248ebb561579b8f1243f64520d602928193610000012800000090c20307cce2ad40429fd085e166598c6e159e2d2daefc9987319ebc658f37ebdef8478eb6420ac9462237030db7d460732d3ad007badf7923d27ede59fd9b76c82b5af2c60804490219e0143d168ee1032c354276018ec209d3840905d6fe7ab1d80c315c2353388c17a5bb292d30c14e0e8ee38cbebeb040b1b49815a01c50110577349a069232cb50749456b8542ca700000090c21be279d7112c8233f699b86da220130309e45b8f46a5773a71c119a9d39a45eade90aff8ab2a61e72bdb611fc799af18a05b3561d7fea5494538ee01e558afab7aef60b0eb51fcd9394c3385cfb0d423c767e83c2e7a1216f859776cdac28b14efc4ae43d6b6809ef2803b18d9cd7c03842e2e13d817c95cefaa7f8ed91e974b80b3d6917a946f6158a19df15a947500000128000000900845864a50a47d07297348aa259971ce32c2ef15021d561bb24e2aa4647489223667995cfd6dfddea6f658d2b42af8b818f6475e82f5ec38e8ef254363d8eaef5602e1a2d980c9f63b765836b8a436f11368746c397c46f8fbc7f610383947dc5cce8c663bd8443557c0fec48692f8b20a6120da540fa31629ba3ebb25da26a1b03276b5d659e019f8e8180a54009855000000908827ea601e3c88179aa5542ad6ca3e054d75359304fe90253f421e8f8ac5de67ca855d06b2c3cd0aa5b78acd7072184cafb9178adce066c2632234f2cb23cb6a06ebda3643f9869e413a6a216e84a29626b1114ba32561c671d54188e45a8323ac4b1660749caeaf154469fdb0a98e2303f50239d3fb942a166effc3f760d630d56fcce63bc9d3a7970893e6d343800a0000012800000090de481777e2521e956c1a82ca6d0468c95986b1b49915f9dbd3544f74f0d459466e8ce587c90cafd599075436e5e74359cec9887c81eeebdf1bea8672b5d10802d515fd85efa7d37a5c5b674ce00ab3522687ee41199561092b7bcd3a1e2210a882b53b9f59423495d3c793244e74e11817a3f58b2142475971a0802b8e1ff58eaa585326eec00da9cd7a92c0eb41a86c000000903cbb053a398485e98af097d1176385ecbc574895fb7e03c83c3c7044c266ca84d8cf849b1a4156b401159fefea93ff8b773639ed58719f3128bdf37640081d1a824ef9e3ec5d9ab1d45bbf92778325df15121af58b9312e2d8e5d0373245187cd8d6c77e0b4e67fd1c54463b77dd2d750ffaf29df1a8e2e798b6fe44e593097924cd52d7154e5b2626f499d04914fc900000012800000090e16c6579c155574b7fd4636bb4cacdd57fecf1694d69c94e1a72ebaf773209bfeed7cca59b3079e09c927f5161c91e335b6f72fc2e7104a459c5c7487de91b25a3355d2112104d19a1f2d0e9fec930b717bcf5da750f585642b4d43d79b87f5d7d275da2ef7c298b7bf5195b920a35b42c8afed45a38322a802a862b4f9af3142eeb31b23c862c681a691fc4cc907d20000000909b8015cfe88f81f4f5c715774601086214f10d5818f9bd191bd3d2137543fc500944fa5707eaaa85c688c7bb34ae2cd0518c2264d332ee6c577fd4d4e882c5e5bd2f0307fd9ae842c8d1d939b29723a92667f1ec789772e054311d82545b5140107b8c0b986b08eebaed37672902eb6e19719f5332e7a2a8180e2fc99caf06d8513a8401743d728de3be3dc52ce8eedb00000128000000902a08b6f9edadfd9b077990874240766afa3fd95d83866dd22304f569e2d33b657152708e9f227cdaf037a357ab0a4ff600f48a30d64d70eb03599647962dbd00f48a9ca96f99b8f6ac410eef7f2bb19907eb42623e48cde44d97064a5e76ed47e6dab6666ff1ea7956f1373d24f533a922859a39dfd5716a7ab2c860d4a893a1344af599276cf2804a6e3fed4b0e89bf0000009012330421895d95a9c8f0cf59381cbd42f3139a7dcc28c6c4dec85ad38d95e734e1149a9b6389902dbe08dea922ff800d6710ae3abeff66941ab1ed689ea8bd6a0eb788f7831b0097f5571ddc3d0830841e178814ecd91babf64d7c096fe4e985f7b9661753498f3f394df532d7631b431cbf7f35df92487a75c12a15251f6b0e73ad9295364213eae0749cbe2fa3b4d200000128000000909d9e2da1bd1ad0b265e7a11351d74a82f053e95d414e3e3509035aa1adf722b700fa7a68e1329d9906c610febfee397c5ee613b19037795585a65a8cc9a13fada7eeb7e68a533b673c4ab1ebf47bbe791a59536bf864b0dbf29e2186e8aa11a9ae564cdeb8e53f2a18b63bae7ae97c200006110189dfa8b9e94dfff867f314a1fbc03bb9e59bfcc9d496b056ec1dd01f00000090bbb2748be6a328c4adc4069ebeb37c5398c1374effef708cbc40754ccba78d6da618b9d26fe7bd0fb01a4bbe3a7a335019cbca751f8325c8c66f5f2c4609f91544ae2f405881cfc6038402e181470d1c01079698e5f2e36f6bc15b2aa5b4cbf659524ea6bbe04e04588d5398ccd3683a048f7b1489b0de7411cfc7cff87d563ffebf153f08584d55e17e304765eca385000001280000009078c5a1db7aee3e9361e5ca420122cf5eb55ab9697d0b41508b7bb179e6ba4f62a90074034bc48f91bdebeb988ce5c675dd0fc6e0090bd38cbf7371dc3457fe10828ca8ca90c393070ab65c81ce1134a61393ef5dc50be99f0eec64555dd2a12753939ec2650923c39f4856d1a68cbcb11037cf8db800fa237d10e4d54446f81231bfc374e6f84b431a88858e591f6c0300000090a920fad261e56a1e0ec1e816594cb719f899fb224fd1288a995571079a700b9f424b63a4cbfab814c3bc101dcedba4efcc8a4f754b0bae9c2ba78b6ee6baa71a24bd2ef46fa1157c48128c096dc709942f0492106bc6c3291da53820067cac2a9d12e17bc638ffdafe921f98e6df37150f627b37f6a549fec31c2daf649bec9f29a608ef138c28334b901f5146b63dad0000012800000090df55310ecf9a1052b12c8ea6b3f325d7fadcaea956437c6a08f5abc9d119c1335975343301985023f32c95efed3ba647563593f1570040135e91958ec6f1862b8a4db97e9703fd417893f918854646f1054122d0af6dc2ffa13c6acd8555cb07ea2a24b033b8290a6437928ce57b909d2a334328988cccc41d72d176224de78ae29ec28e1f5933ce55e88978dfde555600000090d3cf89eef5bde30788ede8d63de64fa16540e0f32f7a2bbf695c4b625fd9ffe14df603d49a8dbcb2d7ff85079f80235d11597b266a416bb52dae66bf24e644ff82c535a74e465a84f3f4546e0242b23c02bd93a36ba8cc649501f6bf99a12bbe6c5728fccf668fa2bd58ed848085caeb2be3e1dd5887b76898d01a6db4e8e4dadc3b1e61cdc57a41b0053981250116b30000012800000090fc86cea3d4aac5eb7dca669d076b2fd061d00e48d28eedb6873afa7b40366e4ff82e26adacd233c8f001921cd3258045efe30bb813162183402a371b40ddb88c2935b78adc21554bf3f007e4ee7d5e9b18689e7b5332a1e05ab70856d743fdaf0fa9da120870cbf04d918a65f16a885a1c19e721585c80691c65d097b6c8c92a4aebd924434c7b6024de8624ece384810000009053ab5bfc42c215b93e1ff640c24d7d5fd4036074d9c8aba43605458a90708559cab63e463aa20871602124e6e36405ee046fe8e82341ab70e1737325bee25f63215e11fc9d73f2a808c0a9644216617715d11054eaf640cf796b6746cbdd3fea488418f52673b553d35dc294f69144c30779432f41b7f8798bee222abd4aea7d3e148793dc477f1c45f57ae8fb6d42b900000128000000905e6e31986ce58e8d566b22248dbd8e742e36db217b7ec9535411ea2ff3d76aaf3b3c3eec489e754063e34fbf5ead13001a5e6f97635565fcbd8a5783d63011e21a823da8bdf690eabd1f75f76879fbcb0f49e857ec71a4ce840e5b0f17918a3a6a8aecc7ae60c921f29bf9857ddcfe8a0ecaa592c24c54eeb37338d671f16c4d5004ba49988524f6e23ab69c807d36800000009012fd8d0c10354b223aaca3b96c3ce882dd6bcc43ea0e79427324753ec7480cea2da54c03241b79606f1d63c330d26ba0b345727e2ff0ee6f1cfbd88bdeffd09bc05507f80111d163726655a51b8ceb692684a81eeba49a4b4e57157231b8338497c267c6f1c184405e1a7d3b8d1561dd22648a8f46a9cb80d858f834a880028276cb70268d5d9427ceeeb5294f66b8f400000ce4000001280000009030f3472bb2aa4afa7f906870a30601b0b42da3696e13c18ec49a94ee262dc1b536b62055c0e05716ad477dc3389e318fe164622664f37818633970b6cb54761d661e61e5b7f1e64195d4ff430bd0d97f2a9c2680803a8a65763022365b7defb845cbab0db45c28f0cde6fd1647040d8f1d08bf8fdf52723a1aab2ad7dc880c9d5bf5b37ed2e80ad5c8d7f13d822811ab000000906fd356b5b6e84cb93c5e067bb5d1ddf99fbb95c87de4a281ca3fa591a0d04960bdea46a9577d1196df1408adfadf1c5ecb273ac874df29c7adc3eac64d5f9e1494273ff4b0aa9dc996532693a66ee85611430e5b22cfad17091b004a87acd321eca932c9e744d9af1543716240eb6853191cd9485100f14fd22d96dcb3f03b20314399ced282ee408fb7eb8d43db77b5000001280000009098132d107bc7c0a5d87728dd6fb4727647e2f4f2cb20fbdda9b9585498c5c03503050a145de389ac72e59fdaadbd3f3dd2c0d853576cb5840d5b0d3ef300dfc94afcff4026a65b3c0825a72017505e711a8c45008e3b5bced0c77908f91c72e0b35b985c12adacab86cf81f2b20187db0050248a789b024b3bfdbcb94b44bddea4e67f1ebf2130fc97dbd257fed593a9000000906108fbe8ea485aea0c3b9dee478d8a2fc395dce9aaed16e08e53b456b3ab7827d6d5a1973394572f90771bce09d05da2992366a7970ec6930a6616d6aadae93c6e2340452c3e46224d14932fc7ffa1a825e7e6b8cc504243e76fc6b52f05aa4830fe7f1e30befb0c6a7efa1f8327842815268491e75d595d08894a3cba7a8b310672fbf79d627cd13744ba6fe52c33ca0000012800000090a640b92d89fcb69e2c200102a972ef00aef23169986bde7e450fb2d0c9850d641acde236c581cc72761a335fec7b27a0fc2bda3504d8046f2a683745404cc5c880aeba3f16d61377fbb723aec4df3a640fc3d6e318f2ddbefad52e99671434cc4f17845a5e93a51487a0ff6b48c68e3414e70a9c45b26b02e540a93fd00d3be97eeb6af5c4e68d2ab48458d23de52a33000000906bf44acbfd2b3aebc38aab3848c867de4c3b359d01193266e6303df1f0ff84430eeb64a1be407b1601c11743bd6a184dbb78e5f53cdc8f227467db585c8ac477193fbdfffb1b4288cd197687bd1d872910cabf3c02086c10f49c414074b75cd589ec279e7edb1d0a5535668893c7341400c73b5350471521e780daa529234c8c053aae6b2eb8ab29fdc0ba78aa6be33300000128000000903622d289412b4951637f9e55a801d7a6a00bce377d288e36121837032f05b258f6921547a4c5b410f30c2125daa1546b6b8d336d82ec74b388bc719d3d34c72347dd83315b121dd320e3434b70b0de482f71e50f882bb1022f5caaad860fb6e2b3e843837dfe28b59955a7725f9ec9330ab23a886f6883f230aa74854695a30224d60e0b4f4ba5fe30c19ef0751ffc9e00000090b88847e072454264065439ed169f92410d4e4654a0b1f39832dbfc3be28eea464798848393e82534b5485828429af27e5166a794544dad0018a9e5d2c5826cdb5d0334fe9f9ef469bfcebc831769d5872c7916e0a8a87c44815b66dc08519b1886fbb45428d83772e77e5db38d44dec52fcf3be71a84b0bf17c080930c6c0119cbdd6ed061a2225d3f23cbddecd36ecf00000128000000904201e3550d2e1f76ba1068c82ec0a67d690c8e51fc9f68c7f36c81263d5ddb29be32e5e3e3b9664d9bf4c4363436e7a41749cc645c29150275f8501a87a0174371cfee4ae8df103d59b0d0b75d7ac10418654975639bd6b9ba06cbe79acb60ef3868b697ddbfd2ed661451d061c3a25c02cb1bfea7d3bd2ebc0f5f0166c649562e96022c6d14714d4367c4d3763a6f93000000901a4abf13152779f3020b104add938d7117f3efee81c8c129e864e087ead16916fbaf4c2f36beb51e706f185afe150cd005ca9b0ec51ec03757c1e43def29e24dcdcad410a5775310b3253db842b7b00e2f772e81f0d2dd8c3ab5efd0591d8d772994a9ca121fc0e601810d87c38070ba246d2fee20ac6b429c525f1528c4115f357457bb61c3109edc71450019e95b2a0000012800000090af135c5f86076eaf2ff68254357089aad9e5feec3a67139dbdc62da177032337ee03e6eeee03c5e017fa6da3d0bc05709006f68b11c277f52f2cddaf691d4fed83e9c46ba62d8189373724bc8e966e850458df493e3c8762e69154386f1cdf20fd5b1515c18ff7dd5e523aa1c167eed52acae16139da38501a6551c47f0743227600e0643ea83b830acf55497c8f519800000090af3b415745232d4b810cd1371c2662441a7745ee6e6899f0fe89dfc88e5e377f85e95bdac825a1d5a8970ae68393b432ea5c886eb848838701adc656330cc7cedd54270324c524f4f7dc46f8dcf48b0f2039a5325bd816ff3b57871e68f153cef59cd172b3dcbe700d0eb44d3cb5820f264ec501f59a78d4128c39fe2880ecb86bb64c4b093e4495ff680f110743059d000001280000009039fb70ce62c23dcfa825a7bf89053dcdab094f14b3d0974ff76cce08f48ff8f7b63497afef0b281a4f156cf0af29ad59f5d36e95eb80d825ea4697ec7f7f710c2b51790b45462f5f129310013071850d014b07614fc802068f90631cc7e0ea6b383e40e4f19b44bd0d0d7d522a8d121a0e275c1ab5f6f9b274ecdf8d280f9750173f2f434b994c2ece09999cb0501ce5000000906d2141941550893d8333f7a1a0a9af9baf9f6e737b71f07db67cf1283b4c939393986b7f0109dfebedcdc19c59e000febda964ad4ce208ba59ea2ef2182ee787e156a18ced9510e50cd4a177220896c716c20c62e0ee3803d408603fb34d70de2880847f829e5ea6c9e4255f3f53ac091d2ea4960b6a99c63b3d58056c861622cdadd11c0178d13fe271636c5686cde10000012800000090e6e105147c3ab0f1ed9f425fd0d8beab8525d5efe0a43872b36b8fa9154e89adb9437888d7c316d410cd9fef9b5deb1681a254986bd1c2be16e5b465af02349c0f01faa6c7331643c9a1ac714654f0a02a4b6ccd3839295344c5b4ddc939084b0d8ce45b9c922dcea2e7e62156a265f104cbc24622883bbcd114b3103b0e4e7b78c3107b2575eb05317f27eeb59bb35c000000909190ec9fe430c035ea72f17bd3bc0b3a970ab2059e32bc49c0a5ddb1d278738d7cdfe01e920411a977934e18824300c9d2137947fbb594137c70803fe1c9629022c3d6ed77c89f35d541116240a803000cb6f57a124eee37947fd8bf6f66cd67846f0f1f1576f522f5f96b2a920386ee208ecb88b3bb9412e3fac2970ceacdc5cc06cac0cf2089981ae0bacb3a1b7d1700000128000000903a28ec3706d2eef4640070428bd729e97b4abd3dab30b987909e5cca650600079c6b6845f201ac65c5a3949c408c2cde406b2d306ba688b65e3c2717e4edcb8b2940fe6f48fce8788c945fdc283b60c2208770380b05ecd318ab816526d340e1d44f0e4b16a7f561ffa26c28bf7779d4052acb479b576a5bbae956727227887eed77007a37954713abb709ff6531860d000000903b8a5676e83b32e7f978b723f1accf0b6c5f191876e43ae4983db637bb1af8a4c699bd1749580fbebd38cef2d10b258f00166f71b14bf35c1c98336cd133fefa21e3b89fdcba4f34858f60ff61f02fa51b23a65ec2bc0434af82356f00751b054ba478b4872320543f56f082435df8bc28a0fbec0d9f1699cf85fe79240bc01b4fa41934ae52f3dafd57db6b095e49be000001280000009007b21328b8c8bd7d2b7ea9db1c48deb7abd40e7e5987696d90d0bb35f3f4d8c4cf515da524a06390ea4c29a7581dbce23d174380038045d19681479f9bbe4642280605ee3ed024fc475e180e376f5558028d37eb5ce1a75ce746d5e5f5d64ef0f888006ffa49180ed079a8cccc22fc830a2b1571a64b4644671b35c7c9fed049d889622c2ac19cc8c9e666dde8f7487e0000009013ebac517052f806e3e8a43f9a76b3fd8014c2c51bac75c18813d340209a1f8f49cde8903283fa0f6e57f99538d34fe76e2c2ca622464b2adb4105235645ff068510094118d808a86fe0c57f9c0067ca1ea8d11f1d4981a817216886744bb09af6f1f9437d0d737b3bc8ab7a277669f711c767ecda7972525d468db22be9c49e6e82ae4d6b053d753046689f1f6095ea0000012800000090624b9506aa525b6d6889e72f9b026880f244534be16924cb6a1248e5450c770687a29c580c5b9ad284ed9273486a579b703fe1c1bf5634d2762460cdbeedeeffda11364f71b3160fbcb8a734b1ae882a25812144653da5a24934286afd0be45c6f1e0153fe509adc5d741340513a826c111df48f270168fa72ffa3775a9a896a782a7cfdbcec88d91f0f14942c9f0e8a000000908839817e7303ba863689869f4a68b545060e5cc4a2680c20f252d0c5dbdbb3213cca4f412c6f2b935e561b839d319ef7a33fa82bf6106110f3c90f0b79ce5f242526f3b84424dd0cb4921494fd53e50a0bee29198df55dd02a54dac81ebdb9b1cdf9889154ffa5762a27696f1368faea2b84a7dffc567c61f7c6a87e082aa3f80f6acc2f16f2b3d9ea54e26cee5b3dee00000ce4000001280000009025e4179a89f006c7561f2f9db9a7375bd6e014fa0478234b1f56679d353489b6c5f499dfca964211832ed03f51b1262b4580ac1e68a534752b47cf2f7798aa42da358a45a3a6888bdff7f174613675ab0ff2e00e2bb76111c569e761440794eb10ee13f6cf1130fa680adab60995c55c1b11b85387ded9cf8fbd255e97d80040b0de541173f02de16e87195b1293a4490000009069416623e8132febf95d02899baa57788bd46cbd1b46c1db9e3fe4498c32ddd372b59f8c80fd9787b3b7adc70a2e50f4dd3b79f8015eb159bd251eede3013635d72c8a105813457558e33262a7f08df21737ac4eb5b7dffaa8d8e4e2f0a4893a146974112f8c3d6190f2162b6f89928504e99551cad7da3198cdeab46290d4fb2d427dd9975e96b2cc6394c85d399ea00000012800000090fde0a703c4367484e2d7452c7ac078b8e59b36c449f09b170c5eb35e5c7407cb04d17840ff92217b30f280b7b740d265db469c41baa3e9bee4bb8767b552076866c77ae3af797703d42311f7f13557b71ac51641bcffaa56fc7bf4b851f155e0935831bb9e362ad98f1b172b3b0bd67b1f5d8e93e1c95ba553ec9d223ee8f3b20d32d8184115af308218d30128803add00000090eedb114f15d635863740f6f166cf200c9abf5a0ba790aeb48fd25177e2a05cae44be9925926537e7b0cbd199c8f5806bc01ad1a921c8aa22ea6620e7b48f51d462c708c3c1c98d5c6cd5765849ee580d1556612d0017c5824430b58b42b40997353a5200a369497e1182e990524c7c000b1f4445af9513fca8bc679754451e6681828d034f7de583d1f106dbb1576aa800000128000000900f5b0b1a2ded4152608ce8966a39171628dcb23a03f2184956b537f0920fd4d01bb19d06a35f2576b99f9ff31c8fdcb0b16455d4ba4f486ca92cecb091c33389517c98054fc81a7c9d648fcc62025b281215827d96434c2606a4416c11de6ff75a969a67e0548e8e3c36196225be2d8523de79ffd1224e59f5126287ccbf9981da6cf746853166c7b8082d74ed3754fc0000009077f89fb4b68a9f1ab542f23f2c45f77709d8b88b0e83a13abb74c9ee9c9b23e29943a931091498b1cbfbd4b219c407b8400e61ea0c47ca55dd8c55cfe6a0e68bfbb9c5ae4c22cfd85dfe79f0f8d7add12d3b87c82b19851e889f571218ccc80ad9ed5cedd60cd7098459ce34fd231dc01ce4dd7153efcba4432ea9dc48e1acdba76d6a232f720e5b3a0d9ef82c6f84940000012800000090658c2fb8283c73e49963439e6047e80cb73ab3159030a1e8944caef2d028af220e15f949472071a9f815af8ce3d8518e5ab7d1487c7ac1218af2feb9e452f65569bb0a76d493a651bbf2b07764af2396208a908d4c81553fc7b813074daabd0c2bb0dc5c404326939e243048e0e0f6582e75ad5acd36c0bc2d47eb922c4ea97aea0030b346cd362a15823aae70330726000000909106f34f3216567269f250a7aefcc379a5a5f73491308209fa4545c9b12d505f0b6cca6304b55cfd679fe153771d69fdc174e86d5505f7538f455bfab5027a14c1d0f79e4441c49783c3687580d76ceb10603aa326d144b193a05a4dd6abef19cddc19e5c484b508736bc5f6c1f6452e15006b0608d7b76d504041a888e8019e2b2e5d59e825f3bf803ec89863c4e95000000128000000908f2d66f61c398d081d91e0cc37b113dd1cf63f37746219f040f08f81183a96c411bd805993055d94f9a2642affd04150fad1022d51e4748ffc5904fc57d4a85d0d0b1684db8617b55255e327ed08749e1d938d69aef70f4a42d3f4096e8d1fd0c22fb303666bfa66851aa236cbcec42e03b1cbf750519018e1db2ad8e4c7ec8e79d8916c5d21d06efc4e497e89ce2db40000009061138607fc1233949b170ec2c0fda3a92bf355c5da9ee4b7dc854bc3dd4b99ad56250fe95b32d68714880174355a85b7f504d0e00a93b4aa951ead1809bec41c85216f8c1bfa9ee91b5198949c68066f0387d32bfb9df687e882e2434b6d682141ece88a5bb0d529e016f48bc37109301625b044c5fa7200b56dd1b64cf238adf700ff3ee89c32a2edc2a5d2bf483ad200000128000000909930bee5b317d075ba5cbba4c8ab537bd0878e00f1c9c940d39beaa135fcc9208388cde74f44f55bb44d8a6067f7867dc92971e18e71c3ff54868db71f73df74207cb533e3e02dbc669fdd5bf27d8a89042cdffa03ff863617453c97bc9b50acf0f9729a283057555aa40a1f364098a9033f9b3e622b53c73c8a2d6245b32811ba323e37832a36f314e255ed362a46d600000090f9b68a55c28a7b95514f66a48e4b03574ee8e7b633b11645223c6c59cbe5db9592d8bf0eedc05fb9263dd076d7a88da4fbaa717a80920513b9c334e10b650a51b0c1a126e3b258a60457be4b7f165b2a0eb7902766aeae1699361bca6a1b99445a75d424c26ec0a4d21032e554f2e763030154d3a6569b962080a19c54e0ddb70f9d20f42d97079bc47b1f0715d876710000012800000090b05b85d84802f2d9c7e94a96c230de0b96e6aa81841cbc29a6c9a1431956847e8fab1c3fe5ea8c24122ff0bb34f6f51fd13deecdfd683e06919463107b54a092c09d807c7a1e9659000af33fb522e0d72a502e4771e499d8363ce7862d905d871d39b05cc7cb5cd289ae9ef4e2f0b85e0ad7df39f1afa28b668ec85d5ce28bf2c2f471b41c24e817949648705eac1d9700000090865c148a995dbfd76f82090b6e7187e665aa4f003f76dcac21fbfda3f6d3e980de7bcf4e870443e5cb8c44d330fc5a45cc2d3b8f545bb8c3156a7d35a5386ab0f37c3737d5b5e06e2b03dc2b94faefbd19bf102dfbb7ac31e502ce4aa05e0160bdcad6f5825fab8c27edd151420a9e28076ef7c36e1bb3c12b183b9583bd82922437045c95d90b491c2872f9333ee35200000128000000901923f96f8868c7e823671f03dc0ad821aaa7d584a8df286c102ba498b76cddaccfb1be4dba3bf8ef4cc68083de9a2e0ba54d28fd2c1b669f98416d84f35fa3d6629d2931d033f8c95a6223c5a0d32da127b9c0b641a641663e9f18d05a68ce7ed5ee82c661cd76c42e2501188897d26b29651201b725415d5674327977266df140e3ca8cebe0cc52f13da6cb5e76ba4500000090d4c3cd035d1089a3d2d3d70b9995cf63418da271a2830c5dc8e2a6b5ce5beada4f3aa71aa102dfc35cbbbe7554c1cf9c5ac6127b55076f423d56f4622ffae47d40adf14d672cff3fb3a15904437111ed2f04363c8bc4df171323ead3dd793ac098acc8f1c3f1685628cce426731cde6f20cf746c1e75f261c9eeb8c6912e587ed851c5d706707429a0d61d4ae839091f0000012800000090131088d98cd6cf8548a230c77e0cacdd54169209cb2ba9ae8beb9880dbd0484f6e3ab7f05e8271a2c161562f25d0361c6609bd5d846546c1f7300f535a8b244d855d7a2b751d32a1629d343b659336ed2349d52ef499bd19e73f9675d0a934890e702cc522cde8f9decc1306e48dd0560feb00a244a14d299f4270f4c8bed06e32a898a234f0dffdb4316fa7e814726e000000901bd6fa36327143be4d65f1ea02bf932a7d067055279544b2a922251af080bf87b207108d9e717161e41721a8020d13db9e07d93293694e4e4ba5ae88062a0181a329db4e5b087710c88ca915e516f1ea176b4dda8bcc83229442089af7f7acb0202bc1b3d032ac0d9105c8c80e5817912546bbe34f018f11ad09f5b1505a581242fe198fafefca4b8e7eeea30c2d03160000012800000090b736f7b6e2f224b3e7570f3f494619ae872d055f6183642a4933e435823d1962fc427256a0610cd81904592ccd45a9ef4613e7da8d8acd1a321f32fb60bc041f7c4613b51c7ca31128a9c2dc9536bd6027766dc17e67fdbf5afaac90c495b9a3efa7c13e10ab3f2e0589039484dbbf60116225004034c411594e2b111b107e4cfa1992d75fefd31669aa06d5bedda6a2000000905f29189ba16aa5615d50c871ec5fd77b0f2a4611984b53cfc504136e06c719ca4f9f5bf54e77cf861f2b2a0aed8501987e00d38166b0855417c31062c7d093ce4f7d444c53e11af0f554da4a59c82966086900c174d97530fefc2250711d88f733f5b16bd73811077d6715caec15e928252b1a85ed3d33609e1404329196a570f911d28639bb67206497a9ca1ba5a7460000012800000090b9d8da40a7e9373882123dcb553c8ed4926326d914feb1f0e724cef1616990552da304f2bbd5ecacf60e1b4fdf6535c28089378cfd500f22de3d62bdf7b52126de23cbf2ea09d7de12af2b168dc9406c28cc0435297e5a7aaa2113ce7b9b87e4ff3d855349e974f019132f3d0147c029162c39f38f83f18d3871e47a20590bf5e2ce92637924ac32ffc8862f29db695a00000090cc99bb3dbb2008b94591505ced444fa0e0f5994d5bd1544fedc76d1191097cd612bd7bd45c2cde7010bd0ad788c4c78bbd75f91bcbb6cd221df3b22f475c5b26e5f3cdc905e04a95904246a3ef73a8972ac85096f0ef02cbcacc38fa329469080b4080e2a9f34ebecd578ae79c53ae7e2d0ded061af7fb0cb654b6cf6be7bdc4e0d641978f1394451d12ea190d8c5e7000000ce4000001280000009093e2f8635a045c41c8d9257adda690fb5beb3a5d56dfe78b0dfc3e02be2a104df2ae35d92bfacdc93e35a8f166d24a1f97499ce93faa066176bc1f06a73d02311507712cb1ce5f04c63e27bfe3c3e0651e9c2f13d5cebdca4626f083a8f36aaa65aae4c74d704355b727d601d2305bbe0a121bb2ebc79160f5c11a8f9ac2b69915824c413cc1bccebab0f5fb550085ee000000909bd5f2117634bc2eb6ac1558ee63de19c9906d306a91e434cffb75722aa6b2b2604a84e4ffa075d03226dc5045b16f71f501235820340e4da21c8b46c7b2a4e61f1e0b69cb985a34257c38569ccd0f1226b948e0f2ce68b0ab7849e9728b0517fb9890846f446661a335854e91c14e83302f87c6ce77d02db8e81bd3ed3fcd9dfc9a2c00ead57cf0b147b65512681fcd0000012800000090c0ff69960190678d193dddb5d32f220bec1e0a9ec238240925558cf7dade6be9038dd0039f31513a96a05c05cad1d883f492d42109006e18aba86c16cb6e0fb876a28df71d8e89fa02060bac14ef04cf0e1e34520905dfa33602d49a96ca766a2b46eeb514153c0f13e057ca2a0ee0ec24374b23c5fd922cb266dbda4ea04b879baff83583e3eeb12831fca1eecaf0cf0000009014f4354e9f6108f7ed4c06e8b6f4ba9040b9261d6d22b677d848590958d43a5507852f3acfb45831a25f4dcbe431ac979204bd0dea3f6623c04ddac1870014371581b84ef68f307342c25569987ebf9c10c8584ab1d72e995795b38b52ec6a3ad522cc1c9517e05154e8157b1d6f87f02a428d121d52569eee3bd8bdd9b330500e882390d0fad2d419755a17fc27213300000128000000901c0728fdd04973e9906fd1b541bff24461f9403a97802a8129ccfbf0bbc7cff8f81cb9fec321e2186365860d08202bf3f7168a618eb6342fd117c213d9d6428fc1537d3e8a01cd4fe21c0442bca55cbf16f3360c61254e7b2d089f38eefa4b283d540cab1e674c833f6f1d98b39369e92989531b35d6e006557c2916d7cc7bc5df35be0d64c8f497278ef232d58093a900000090be5eb11fcb7229bedafb364856d9502f66506f9328fd44644880c9e8e183ae0c861b7720b70af77271bc72291abab5963445b8be8ec35a402f5b23efc77e49aee0b6c65a99bce778b5627b5fee60a0c42d8c9cbe8e39bf439608cff529602ccd005c151987f7bdee3a2bafef9e0dc3122b46d03f266625d5ca0895cc527b6fee308661e23c20639c9bdba02b3ae71c8f0000012800000090ea504b252805bac5de56d205a6297cf38a90d101aacb4538f26a41adf9e120494e2daa93318788a151777b0e8fb5e0444bb0de040f684b48e113a3bd7d43e280263fb782adbb690053923bb0eaa7c0f11d89cebca94e1077de29e1f13450017f2d39e5da7b738a58158467680b87e8a916c6385cd1249ec51e5e2922f249b107ed353789e24e45208805602f96a432ee0000009009852850c074a233370d7d9704885ebbf57d13ddc416cd3809d7ed1591b05f0716560e8328f1efd042a4d1ade629ee9b693ae5866fa1f3970e2d91f41a03250126330bd6e27fb64bf6312c7600770f542be1632e295fb6d9fe45bf91b1030cd0c3150fe3f65ab4d1d2d017c3839898781a6e1eb8f2f4032f4dfd5f0a600ef72662986176bb14b4b7a9c99f66f429106a0000012800000090b9927e80cdb316ac5a9e7d5aefcf6ac01d489ede79e20af58377baac0bd74a49f19db328daeaa5ec07d4ecf7ba55148032c4989ab1f8c30d32cfb74d7a10dc6b9b3b62dc7a27a8db24688e5683038a541f38971f13f8da2bbb892e010c0ef93df1ed8b7a960fe65e7efaf2f274f3869c1afcdcc299eb2080c41e9c5676d524195e22e79730f662f20b04c5c640b233550000009080bd76f074f19aecc02adb0d7b30514b3878b31726f96fe25b68f1fce3b3b191d72f4139d497900637e2c28643f24f581949d196949acded4c29a8a6e300769e5c58d1cc5ec8ccd76006ef4c20d700c01747378dcb6bae7af6907dfc8ea88a7c23f4eb3d32f33c6da243702b6f1cf7f8155920309ef26019c05564716d2c55381a86bcc98712c7a0295832c9770eda20000001280000009011951b508ec3fc32865cde3f8b14e4314665a92561b35282c4b5f1261ad1e28e881900b80d70dc13e96680a528c997f09ed77899928a287f17a8eff3bee09a839f851153bdde9d1577a89c9d48c74f6a0557849194971f6237e73fa31f725b2a1a878914521b094863164d5f5891b1300c41c7999762752ca2adcc89de07f4e1d9b3ba5a1cfef2204561563c5c73901600000090ed0097c38e390151b701a7ac242f6e73c79fac1e0cf1dc2f66fb29c6002e0f6db13a6d6027cf18c4e6308caa17da58ca8d48acc59805585ea6b006facb44654f6f754f9725b685bb5c4ded4de2c690a72aa7923dacd13f83722f0893ebee4b3ee8ce7b8a643acb94ed2925f282ff0c4818d6a0713c5227e13b9e28e9b58cc913d2e66750c5ac23e16ce76598472d6e9d00000128000000907c0e86120a5a037229ee9c3a6ea20bf194396f08ded24d811339ba90f1ae02744218270e0b2f3115ba455b5409284da7031e56eea2eb96d1204eb02c2e6ebb8a0b4f3e98847607cb6b2279958b0197e52ab0e29034302cf56b533d3157b5a419665e50428cced5f15097cd3ec2ea34951d7113c4a2fcf976adc69ae623c2557c19589c89907106001f1b25d2b7d9537c00000090745e83cd5102da600a92eceb7c2712d2ea6704b8a2cc7105dd788ee043cc0f0b23077b5607903c7053aa17c4986e4e9e8c3c9767070b6a814e6e4a89c904b12308c11a09cdafe6faf48b2630e8ad24552d5ea409a7ad8b4b9b117ddb852d61e70d25b30f70d66e0c7907e5c16c87376209020da80311afdc646841e86b55f89c920bfafc0623fd73585eff854c10ddc20000012800000090a626f21ac7482cd903148ee4fe9fbcb0bb37c2b0c25f78682b40d2c65212b9f2907b0d0ba6b357ad998ac2dacdb23a6d626ea1f240967b92a3aa492b6bc3f09a57242e4d2a8d50636f6761ea8738488b0c19b34b570e8823b97dce37517dc9b24639c7410183ef3261a85fada34c0ca510ff73ada4daacc553482d68d672cbb3f674c8f2f0751a37245edf6d5695557d0000009092b9b1bef2731730812331aadc03fdc1aa7603e10f72def23023535b475b25182ef02e5bee562306197922453c7e28c323463902cc16a0295d2cebd6e1cbc85715e8dfa9c8d9ab7ac810e29350360bce02d3ce1075f6198042f4b657d834594564a00700d5e457e7838a00b590ef55550b5893c4bfe5f65fce253caf7141e83111d74368eb8eee18004964feb64dd8bd000001280000009011c36f8adfbb22c9c3d657f7564ee4eb30d37fbd30907b2a0561bdad676980ca96254999cba01fa8050a433a1d0ec2e49722eeb016279c2a51469725498969edc938823df02655a402418f3a6b8a28c70d5d89a7532bc47ab408e4a634bde7976bb2def28668d34b131909defd9379ef0c3c1d4294e1ff613c7423ef3b6e575ee39591b9be108055714df1789da636080000009004dcb05ab956ff0b68077fc113fd949ac3fcbed71a7605d42c3a09174a6fd573d4f66e407e468a1f51fe670b01928a1b93179fee05fd11b661f14bdd6518f61f5e0bb6aa55060e1b8b3a9985cf9049c41a999e111b4a3c3422fa6a55fa8f0dfbe839a4d5f51213c3915f1f6aeb85c7d00903b9c405878112fe5263e476101e1b53a2d2804929612759e2b5354e09ca4c0000012800000090305141bceee489d00e1951280d90286be9dfde8a0c23e802490da8807d919892fff6dc32c9a66663ac0762043b0eb2376390aa1b282bae4d337ad9e6f41d6720ee0f3d0315c1e19b81fd2fafae61de5504eb79685817659363e38f998e7ec51f77fec0f60474e14b5ba3bbe63bd3874a0dafc5fcae9f9258e277597458fdadcee0e822c56a55248acb3700a463154d69000000905a754dd00b1c72387b2b59bba5fe3ab77cf0a4538ddf9f8f658db80c6ec2a1327637e5c05e9ededd5d4da46f842feb545f8eeef74fd5a5a214eb5bc46966c44040e5fd9d2a87c2b15042f987923722a80e7d3a43fe74774c3681b1c792a34fb0a4f5de14e1c6f5af9fed1dea7694dd6c296d23c677c781280cc6c2970227eaa0ca6d690d1f738e8ae5ca76bad02a291e0000012800000090988af0a1f57a9dee97278df9b1fccd62a21cadfb56160960eed6abefc95d5f0fa96cc2e3bce75894bff947d855309e264c75eb95c4532de49e810e59abc43ff98d08fdb9e3b0f3ae561e033983c7ab391f521efd6af2675bea296cb4f257850de0e3ff28205b935b76b05d3584222e0a067a336c8be6a365c107fa95959563cbc11a1c2f63914d41bd5c4b0c698b0d2f00000090674e68e7621622e4a22d90d7aa2a1651bff1b53bd4b9f74e4da2c3a4bdd05513206950613958362efe44c16cf3d2133419737c395c0ef4f04385ee580a561ae1647db60f3da047b382789331edb670be103fba6587fe6f26b1cea9bc26820dcc65ab1a9023fef37e8e52f0cdf5fc69c5083b8f5855fe15e138af8136188b9b0b2d80aa2a60b895d4bc1b0ebe0e4121e3", + "calldataHash": "0x0ef928e73adbc5f53790a7591f274b0f4179a533e21da23f2ed7defe5af7bf41", "decodedHeader": { - "bodyHash": "0xcd9a7ed1324b15c82eb2b86042643cd52b67ef1909cdcdefb26d2073d8664a23", + "bodyHash": "0x0ef928e73adbc5f53790a7591f274b0f4179a533e21da23f2ed7defe5af7bf41", "globalVariables": { "blockNumber": 1, "chainId": 31337, @@ -92,8 +92,8 @@ } } }, - "header": "0x0000000000000000000000000000000000000000000000000000000000007a690000000000000000000000000000000000000000000000000000000000000001000000000000000000000000000000000000000000000000000000000000000100000000000000000000000000000000000000000000000000000000000000000a241c83a063083fad29b6c333afcd968f71f8a875544ff1f1f08cae7f770f51000000100d944282e11bdcfa5e8f2b55fe80db4c586087bfc10e0bbba5724d30b8c15e2e0000010001c16141039343d4d403501e66deecff1b024bd76794820a43dc3424087813a2000001801a3d3e40c90cd24822b9c268154ef14d4639febdb7af81b554cb2d651b88dfb100000004160ba3a7c15d7dd592a24b3f14bfa76a0b45378877d4dedc5f126f47844b99b2000000601a005071a487e4891787073a91504fe6ea55280bc6f65a021fd6f7ca1f10aa0200000001cd9a7ed1324b15c82eb2b86042643cd52b67ef1909cdcdefb26d2073d8664a23", + "header": "0x1a005071a487e4891787073a91504fe6ea55280bc6f65a021fd6f7ca1f10aa02000000010ef928e73adbc5f53790a7591f274b0f4179a533e21da23f2ed7defe5af7bf410a241c83a063083fad29b6c333afcd968f71f8a875544ff1f1f08cae7f770f51000000100d944282e11bdcfa5e8f2b55fe80db4c586087bfc10e0bbba5724d30b8c15e2e0000010001c16141039343d4d403501e66deecff1b024bd76794820a43dc3424087813a2000001801a3d3e40c90cd24822b9c268154ef14d4639febdb7af81b554cb2d651b88dfb100000004160ba3a7c15d7dd592a24b3f14bfa76a0b45378877d4dedc5f126f47844b99b2000000600000000000000000000000000000000000000000000000000000000000007a69000000000000000000000000000000000000000000000000000000000000000100000000000000000000000000000000000000000000000000000000000000010000000000000000000000000000000000000000000000000000000000000000", "l1ToL2MessagesHash": "0xb213c9c543fce2a66720d26a913fe0d018f72a47ccfe698baafcf4cced343cfd", - "publicInputsHash": "0x124b48fbfadbbd213feab5c7dda66f6908f55c3c899d88d0db822527dcbc34b1" + "publicInputsHash": "0x1256090bf682bee2a1b43969d13bd8921ab74904a46a578d20534b57434fd60d" } } \ No newline at end of file diff --git a/l1-contracts/test/fixtures/mixed_block_1.json b/l1-contracts/test/fixtures/mixed_block_1.json index f0147e8c6cc..805a3869c17 100644 --- a/l1-contracts/test/fixtures/mixed_block_1.json +++ b/l1-contracts/test/fixtures/mixed_block_1.json @@ -52,15 +52,15 @@ ] }, "block": { - "archive": "0x2f3fb377fd6181826fcbfda86813263509bceeeb8c606e61a03386a5bb6d0be1", - "body": "0x000001000000000000000000000000000000000000000000000000000000000000000240000000000000000000000000000000000000000000000000000000000000024100000000000000000000000000000000000000000000000000000000000002420000000000000000000000000000000000000000000000000000000000000243000000000000000000000000000000000000000000000000000000000000024400000000000000000000000000000000000000000000000000000000000002450000000000000000000000000000000000000000000000000000000000000246000000000000000000000000000000000000000000000000000000000000024700000000000000000000000000000000000000000000000000000000000002480000000000000000000000000000000000000000000000000000000000000249000000000000000000000000000000000000000000000000000000000000024a000000000000000000000000000000000000000000000000000000000000024b000000000000000000000000000000000000000000000000000000000000024c000000000000000000000000000000000000000000000000000000000000024d000000000000000000000000000000000000000000000000000000000000024e000000000000000000000000000000000000000000000000000000000000024f0000000000000000000000000000000000000000000000000000000000000250000000000000000000000000000000000000000000000000000000000000025100000000000000000000000000000000000000000000000000000000000002520000000000000000000000000000000000000000000000000000000000000253000000000000000000000000000000000000000000000000000000000000025400000000000000000000000000000000000000000000000000000000000002550000000000000000000000000000000000000000000000000000000000000256000000000000000000000000000000000000000000000000000000000000025700000000000000000000000000000000000000000000000000000000000002580000000000000000000000000000000000000000000000000000000000000259000000000000000000000000000000000000000000000000000000000000025a000000000000000000000000000000000000000000000000000000000000025b000000000000000000000000000000000000000000000000000000000000025c000000000000000000000000000000000000000000000000000000000000025d000000000000000000000000000000000000000000000000000000000000025e000000000000000000000000000000000000000000000000000000000000025f0000000000000000000000000000000000000000000000000000000000000260000000000000000000000000000000000000000000000000000000000000026100000000000000000000000000000000000000000000000000000000000002620000000000000000000000000000000000000000000000000000000000000263000000000000000000000000000000000000000000000000000000000000026400000000000000000000000000000000000000000000000000000000000002650000000000000000000000000000000000000000000000000000000000000266000000000000000000000000000000000000000000000000000000000000026700000000000000000000000000000000000000000000000000000000000002680000000000000000000000000000000000000000000000000000000000000269000000000000000000000000000000000000000000000000000000000000026a000000000000000000000000000000000000000000000000000000000000026b000000000000000000000000000000000000000000000000000000000000026c000000000000000000000000000000000000000000000000000000000000026d000000000000000000000000000000000000000000000000000000000000026e000000000000000000000000000000000000000000000000000000000000026f0000000000000000000000000000000000000000000000000000000000000270000000000000000000000000000000000000000000000000000000000000027100000000000000000000000000000000000000000000000000000000000002720000000000000000000000000000000000000000000000000000000000000273000000000000000000000000000000000000000000000000000000000000027400000000000000000000000000000000000000000000000000000000000002750000000000000000000000000000000000000000000000000000000000000276000000000000000000000000000000000000000000000000000000000000027700000000000000000000000000000000000000000000000000000000000002780000000000000000000000000000000000000000000000000000000000000279000000000000000000000000000000000000000000000000000000000000027a000000000000000000000000000000000000000000000000000000000000027b000000000000000000000000000000000000000000000000000000000000027c000000000000000000000000000000000000000000000000000000000000027d000000000000000000000000000000000000000000000000000000000000027e000000000000000000000000000000000000000000000000000000000000027f0000000000000000000000000000000000000000000000000000000000000280000000000000000000000000000000000000000000000000000000000000028100000000000000000000000000000000000000000000000000000000000002820000000000000000000000000000000000000000000000000000000000000283000000000000000000000000000000000000000000000000000000000000028400000000000000000000000000000000000000000000000000000000000002850000000000000000000000000000000000000000000000000000000000000286000000000000000000000000000000000000000000000000000000000000028700000000000000000000000000000000000000000000000000000000000002880000000000000000000000000000000000000000000000000000000000000289000000000000000000000000000000000000000000000000000000000000028a000000000000000000000000000000000000000000000000000000000000028b000000000000000000000000000000000000000000000000000000000000028c000000000000000000000000000000000000000000000000000000000000028d000000000000000000000000000000000000000000000000000000000000028e000000000000000000000000000000000000000000000000000000000000028f0000000000000000000000000000000000000000000000000000000000000290000000000000000000000000000000000000000000000000000000000000029100000000000000000000000000000000000000000000000000000000000002920000000000000000000000000000000000000000000000000000000000000293000000000000000000000000000000000000000000000000000000000000029400000000000000000000000000000000000000000000000000000000000002950000000000000000000000000000000000000000000000000000000000000296000000000000000000000000000000000000000000000000000000000000029700000000000000000000000000000000000000000000000000000000000002980000000000000000000000000000000000000000000000000000000000000299000000000000000000000000000000000000000000000000000000000000029a000000000000000000000000000000000000000000000000000000000000029b000000000000000000000000000000000000000000000000000000000000029c000000000000000000000000000000000000000000000000000000000000029d000000000000000000000000000000000000000000000000000000000000029e000000000000000000000000000000000000000000000000000000000000029f00000000000000000000000000000000000000000000000000000000000002a000000000000000000000000000000000000000000000000000000000000002a100000000000000000000000000000000000000000000000000000000000002a200000000000000000000000000000000000000000000000000000000000002a300000000000000000000000000000000000000000000000000000000000002a400000000000000000000000000000000000000000000000000000000000002a500000000000000000000000000000000000000000000000000000000000002a600000000000000000000000000000000000000000000000000000000000002a700000000000000000000000000000000000000000000000000000000000002a800000000000000000000000000000000000000000000000000000000000002a900000000000000000000000000000000000000000000000000000000000002aa00000000000000000000000000000000000000000000000000000000000002ab00000000000000000000000000000000000000000000000000000000000002ac00000000000000000000000000000000000000000000000000000000000002ad00000000000000000000000000000000000000000000000000000000000002ae00000000000000000000000000000000000000000000000000000000000002af00000000000000000000000000000000000000000000000000000000000002b000000000000000000000000000000000000000000000000000000000000002b100000000000000000000000000000000000000000000000000000000000002b200000000000000000000000000000000000000000000000000000000000002b300000000000000000000000000000000000000000000000000000000000002b400000000000000000000000000000000000000000000000000000000000002b500000000000000000000000000000000000000000000000000000000000002b600000000000000000000000000000000000000000000000000000000000002b700000000000000000000000000000000000000000000000000000000000002b800000000000000000000000000000000000000000000000000000000000002b900000000000000000000000000000000000000000000000000000000000002ba00000000000000000000000000000000000000000000000000000000000002bb00000000000000000000000000000000000000000000000000000000000002bc00000000000000000000000000000000000000000000000000000000000002bd00000000000000000000000000000000000000000000000000000000000002be00000000000000000000000000000000000000000000000000000000000002bf00000000000000000000000000000000000000000000000000000000000002c000000000000000000000000000000000000000000000000000000000000002c100000000000000000000000000000000000000000000000000000000000002c200000000000000000000000000000000000000000000000000000000000002c300000000000000000000000000000000000000000000000000000000000002c400000000000000000000000000000000000000000000000000000000000002c500000000000000000000000000000000000000000000000000000000000002c600000000000000000000000000000000000000000000000000000000000002c700000000000000000000000000000000000000000000000000000000000002c800000000000000000000000000000000000000000000000000000000000002c900000000000000000000000000000000000000000000000000000000000002ca00000000000000000000000000000000000000000000000000000000000002cb00000000000000000000000000000000000000000000000000000000000002cc00000000000000000000000000000000000000000000000000000000000002cd00000000000000000000000000000000000000000000000000000000000002ce00000000000000000000000000000000000000000000000000000000000002cf00000000000000000000000000000000000000000000000000000000000002d000000000000000000000000000000000000000000000000000000000000002d100000000000000000000000000000000000000000000000000000000000002d200000000000000000000000000000000000000000000000000000000000002d300000000000000000000000000000000000000000000000000000000000002d400000000000000000000000000000000000000000000000000000000000002d500000000000000000000000000000000000000000000000000000000000002d600000000000000000000000000000000000000000000000000000000000002d700000000000000000000000000000000000000000000000000000000000002d800000000000000000000000000000000000000000000000000000000000002d900000000000000000000000000000000000000000000000000000000000002da00000000000000000000000000000000000000000000000000000000000002db00000000000000000000000000000000000000000000000000000000000002dc00000000000000000000000000000000000000000000000000000000000002dd00000000000000000000000000000000000000000000000000000000000002de00000000000000000000000000000000000000000000000000000000000002df00000000000000000000000000000000000000000000000000000000000002e000000000000000000000000000000000000000000000000000000000000002e100000000000000000000000000000000000000000000000000000000000002e200000000000000000000000000000000000000000000000000000000000002e300000000000000000000000000000000000000000000000000000000000002e400000000000000000000000000000000000000000000000000000000000002e500000000000000000000000000000000000000000000000000000000000002e600000000000000000000000000000000000000000000000000000000000002e700000000000000000000000000000000000000000000000000000000000002e800000000000000000000000000000000000000000000000000000000000002e900000000000000000000000000000000000000000000000000000000000002ea00000000000000000000000000000000000000000000000000000000000002eb00000000000000000000000000000000000000000000000000000000000002ec00000000000000000000000000000000000000000000000000000000000002ed00000000000000000000000000000000000000000000000000000000000002ee00000000000000000000000000000000000000000000000000000000000002ef00000000000000000000000000000000000000000000000000000000000002f000000000000000000000000000000000000000000000000000000000000002f100000000000000000000000000000000000000000000000000000000000002f200000000000000000000000000000000000000000000000000000000000002f300000000000000000000000000000000000000000000000000000000000002f400000000000000000000000000000000000000000000000000000000000002f500000000000000000000000000000000000000000000000000000000000002f600000000000000000000000000000000000000000000000000000000000002f700000000000000000000000000000000000000000000000000000000000002f800000000000000000000000000000000000000000000000000000000000002f900000000000000000000000000000000000000000000000000000000000002fa00000000000000000000000000000000000000000000000000000000000002fb00000000000000000000000000000000000000000000000000000000000002fc00000000000000000000000000000000000000000000000000000000000002fd00000000000000000000000000000000000000000000000000000000000002fe00000000000000000000000000000000000000000000000000000000000002ff0000000000000000000000000000000000000000000000000000000000000300000000000000000000000000000000000000000000000000000000000000030100000000000000000000000000000000000000000000000000000000000003020000000000000000000000000000000000000000000000000000000000000303000000000000000000000000000000000000000000000000000000000000030400000000000000000000000000000000000000000000000000000000000003050000000000000000000000000000000000000000000000000000000000000306000000000000000000000000000000000000000000000000000000000000030700000000000000000000000000000000000000000000000000000000000003080000000000000000000000000000000000000000000000000000000000000309000000000000000000000000000000000000000000000000000000000000030a000000000000000000000000000000000000000000000000000000000000030b000000000000000000000000000000000000000000000000000000000000030c000000000000000000000000000000000000000000000000000000000000030d000000000000000000000000000000000000000000000000000000000000030e000000000000000000000000000000000000000000000000000000000000030f0000000000000000000000000000000000000000000000000000000000000310000000000000000000000000000000000000000000000000000000000000031100000000000000000000000000000000000000000000000000000000000003120000000000000000000000000000000000000000000000000000000000000313000000000000000000000000000000000000000000000000000000000000031400000000000000000000000000000000000000000000000000000000000003150000000000000000000000000000000000000000000000000000000000000316000000000000000000000000000000000000000000000000000000000000031700000000000000000000000000000000000000000000000000000000000003180000000000000000000000000000000000000000000000000000000000000319000000000000000000000000000000000000000000000000000000000000031a000000000000000000000000000000000000000000000000000000000000031b000000000000000000000000000000000000000000000000000000000000031c000000000000000000000000000000000000000000000000000000000000031d000000000000000000000000000000000000000000000000000000000000031e000000000000000000000000000000000000000000000000000000000000031f0000000000000000000000000000000000000000000000000000000000000320000000000000000000000000000000000000000000000000000000000000032100000000000000000000000000000000000000000000000000000000000003220000000000000000000000000000000000000000000000000000000000000323000000000000000000000000000000000000000000000000000000000000032400000000000000000000000000000000000000000000000000000000000003250000000000000000000000000000000000000000000000000000000000000326000000000000000000000000000000000000000000000000000000000000032700000000000000000000000000000000000000000000000000000000000003280000000000000000000000000000000000000000000000000000000000000329000000000000000000000000000000000000000000000000000000000000032a000000000000000000000000000000000000000000000000000000000000032b000000000000000000000000000000000000000000000000000000000000032c000000000000000000000000000000000000000000000000000000000000032d000000000000000000000000000000000000000000000000000000000000032e000000000000000000000000000000000000000000000000000000000000032f0000000000000000000000000000000000000000000000000000000000000330000000000000000000000000000000000000000000000000000000000000033100000000000000000000000000000000000000000000000000000000000003320000000000000000000000000000000000000000000000000000000000000333000000000000000000000000000000000000000000000000000000000000033400000000000000000000000000000000000000000000000000000000000003350000000000000000000000000000000000000000000000000000000000000336000000000000000000000000000000000000000000000000000000000000033700000000000000000000000000000000000000000000000000000000000003380000000000000000000000000000000000000000000000000000000000000339000000000000000000000000000000000000000000000000000000000000033a000000000000000000000000000000000000000000000000000000000000033b000000000000000000000000000000000000000000000000000000000000033c000000000000000000000000000000000000000000000000000000000000033d000000000000000000000000000000000000000000000000000000000000033e000000000000000000000000000000000000000000000000000000000000033f000001000000000000000000000000000000000000000000000000000000000000000340000000000000000000000000000000000000000000000000000000000000034100000000000000000000000000000000000000000000000000000000000003420000000000000000000000000000000000000000000000000000000000000343000000000000000000000000000000000000000000000000000000000000034400000000000000000000000000000000000000000000000000000000000003450000000000000000000000000000000000000000000000000000000000000346000000000000000000000000000000000000000000000000000000000000034700000000000000000000000000000000000000000000000000000000000003480000000000000000000000000000000000000000000000000000000000000349000000000000000000000000000000000000000000000000000000000000034a000000000000000000000000000000000000000000000000000000000000034b000000000000000000000000000000000000000000000000000000000000034c000000000000000000000000000000000000000000000000000000000000034d000000000000000000000000000000000000000000000000000000000000034e000000000000000000000000000000000000000000000000000000000000034f0000000000000000000000000000000000000000000000000000000000000350000000000000000000000000000000000000000000000000000000000000035100000000000000000000000000000000000000000000000000000000000003520000000000000000000000000000000000000000000000000000000000000353000000000000000000000000000000000000000000000000000000000000035400000000000000000000000000000000000000000000000000000000000003550000000000000000000000000000000000000000000000000000000000000356000000000000000000000000000000000000000000000000000000000000035700000000000000000000000000000000000000000000000000000000000003580000000000000000000000000000000000000000000000000000000000000359000000000000000000000000000000000000000000000000000000000000035a000000000000000000000000000000000000000000000000000000000000035b000000000000000000000000000000000000000000000000000000000000035c000000000000000000000000000000000000000000000000000000000000035d000000000000000000000000000000000000000000000000000000000000035e000000000000000000000000000000000000000000000000000000000000035f0000000000000000000000000000000000000000000000000000000000000360000000000000000000000000000000000000000000000000000000000000036100000000000000000000000000000000000000000000000000000000000003620000000000000000000000000000000000000000000000000000000000000363000000000000000000000000000000000000000000000000000000000000036400000000000000000000000000000000000000000000000000000000000003650000000000000000000000000000000000000000000000000000000000000366000000000000000000000000000000000000000000000000000000000000036700000000000000000000000000000000000000000000000000000000000003680000000000000000000000000000000000000000000000000000000000000369000000000000000000000000000000000000000000000000000000000000036a000000000000000000000000000000000000000000000000000000000000036b000000000000000000000000000000000000000000000000000000000000036c000000000000000000000000000000000000000000000000000000000000036d000000000000000000000000000000000000000000000000000000000000036e000000000000000000000000000000000000000000000000000000000000036f0000000000000000000000000000000000000000000000000000000000000370000000000000000000000000000000000000000000000000000000000000037100000000000000000000000000000000000000000000000000000000000003720000000000000000000000000000000000000000000000000000000000000373000000000000000000000000000000000000000000000000000000000000037400000000000000000000000000000000000000000000000000000000000003750000000000000000000000000000000000000000000000000000000000000376000000000000000000000000000000000000000000000000000000000000037700000000000000000000000000000000000000000000000000000000000003780000000000000000000000000000000000000000000000000000000000000379000000000000000000000000000000000000000000000000000000000000037a000000000000000000000000000000000000000000000000000000000000037b000000000000000000000000000000000000000000000000000000000000037c000000000000000000000000000000000000000000000000000000000000037d000000000000000000000000000000000000000000000000000000000000037e00000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000380000000000000000000000000000000000000000000000000000000000000038100000000000000000000000000000000000000000000000000000000000003820000000000000000000000000000000000000000000000000000000000000383000000000000000000000000000000000000000000000000000000000000038400000000000000000000000000000000000000000000000000000000000003850000000000000000000000000000000000000000000000000000000000000386000000000000000000000000000000000000000000000000000000000000038700000000000000000000000000000000000000000000000000000000000003880000000000000000000000000000000000000000000000000000000000000389000000000000000000000000000000000000000000000000000000000000038a000000000000000000000000000000000000000000000000000000000000038b000000000000000000000000000000000000000000000000000000000000038c000000000000000000000000000000000000000000000000000000000000038d000000000000000000000000000000000000000000000000000000000000038e000000000000000000000000000000000000000000000000000000000000038f0000000000000000000000000000000000000000000000000000000000000390000000000000000000000000000000000000000000000000000000000000039100000000000000000000000000000000000000000000000000000000000003920000000000000000000000000000000000000000000000000000000000000393000000000000000000000000000000000000000000000000000000000000039400000000000000000000000000000000000000000000000000000000000003950000000000000000000000000000000000000000000000000000000000000396000000000000000000000000000000000000000000000000000000000000039700000000000000000000000000000000000000000000000000000000000003980000000000000000000000000000000000000000000000000000000000000399000000000000000000000000000000000000000000000000000000000000039a000000000000000000000000000000000000000000000000000000000000039b000000000000000000000000000000000000000000000000000000000000039c000000000000000000000000000000000000000000000000000000000000039d000000000000000000000000000000000000000000000000000000000000039e000000000000000000000000000000000000000000000000000000000000039f00000000000000000000000000000000000000000000000000000000000003a000000000000000000000000000000000000000000000000000000000000003a100000000000000000000000000000000000000000000000000000000000003a200000000000000000000000000000000000000000000000000000000000003a300000000000000000000000000000000000000000000000000000000000003a400000000000000000000000000000000000000000000000000000000000003a500000000000000000000000000000000000000000000000000000000000003a600000000000000000000000000000000000000000000000000000000000003a700000000000000000000000000000000000000000000000000000000000003a800000000000000000000000000000000000000000000000000000000000003a900000000000000000000000000000000000000000000000000000000000003aa00000000000000000000000000000000000000000000000000000000000003ab00000000000000000000000000000000000000000000000000000000000003ac00000000000000000000000000000000000000000000000000000000000003ad00000000000000000000000000000000000000000000000000000000000003ae00000000000000000000000000000000000000000000000000000000000003af00000000000000000000000000000000000000000000000000000000000003b000000000000000000000000000000000000000000000000000000000000003b100000000000000000000000000000000000000000000000000000000000003b200000000000000000000000000000000000000000000000000000000000003b300000000000000000000000000000000000000000000000000000000000003b400000000000000000000000000000000000000000000000000000000000003b500000000000000000000000000000000000000000000000000000000000003b600000000000000000000000000000000000000000000000000000000000003b700000000000000000000000000000000000000000000000000000000000003b800000000000000000000000000000000000000000000000000000000000003b900000000000000000000000000000000000000000000000000000000000003ba00000000000000000000000000000000000000000000000000000000000003bb00000000000000000000000000000000000000000000000000000000000003bc00000000000000000000000000000000000000000000000000000000000003bd00000000000000000000000000000000000000000000000000000000000003be000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000003c000000000000000000000000000000000000000000000000000000000000003c100000000000000000000000000000000000000000000000000000000000003c200000000000000000000000000000000000000000000000000000000000003c300000000000000000000000000000000000000000000000000000000000003c400000000000000000000000000000000000000000000000000000000000003c500000000000000000000000000000000000000000000000000000000000003c600000000000000000000000000000000000000000000000000000000000003c700000000000000000000000000000000000000000000000000000000000003c800000000000000000000000000000000000000000000000000000000000003c900000000000000000000000000000000000000000000000000000000000003ca00000000000000000000000000000000000000000000000000000000000003cb00000000000000000000000000000000000000000000000000000000000003cc00000000000000000000000000000000000000000000000000000000000003cd00000000000000000000000000000000000000000000000000000000000003ce00000000000000000000000000000000000000000000000000000000000003cf00000000000000000000000000000000000000000000000000000000000003d000000000000000000000000000000000000000000000000000000000000003d100000000000000000000000000000000000000000000000000000000000003d200000000000000000000000000000000000000000000000000000000000003d300000000000000000000000000000000000000000000000000000000000003d400000000000000000000000000000000000000000000000000000000000003d500000000000000000000000000000000000000000000000000000000000003d600000000000000000000000000000000000000000000000000000000000003d700000000000000000000000000000000000000000000000000000000000003d800000000000000000000000000000000000000000000000000000000000003d900000000000000000000000000000000000000000000000000000000000003da00000000000000000000000000000000000000000000000000000000000003db00000000000000000000000000000000000000000000000000000000000003dc00000000000000000000000000000000000000000000000000000000000003dd00000000000000000000000000000000000000000000000000000000000003de00000000000000000000000000000000000000000000000000000000000003df00000000000000000000000000000000000000000000000000000000000003e000000000000000000000000000000000000000000000000000000000000003e100000000000000000000000000000000000000000000000000000000000003e200000000000000000000000000000000000000000000000000000000000003e300000000000000000000000000000000000000000000000000000000000003e400000000000000000000000000000000000000000000000000000000000003e500000000000000000000000000000000000000000000000000000000000003e600000000000000000000000000000000000000000000000000000000000003e700000000000000000000000000000000000000000000000000000000000003e800000000000000000000000000000000000000000000000000000000000003e900000000000000000000000000000000000000000000000000000000000003ea00000000000000000000000000000000000000000000000000000000000003eb00000000000000000000000000000000000000000000000000000000000003ec00000000000000000000000000000000000000000000000000000000000003ed00000000000000000000000000000000000000000000000000000000000003ee00000000000000000000000000000000000000000000000000000000000003ef00000000000000000000000000000000000000000000000000000000000003f000000000000000000000000000000000000000000000000000000000000003f100000000000000000000000000000000000000000000000000000000000003f200000000000000000000000000000000000000000000000000000000000003f300000000000000000000000000000000000000000000000000000000000003f400000000000000000000000000000000000000000000000000000000000003f500000000000000000000000000000000000000000000000000000000000003f600000000000000000000000000000000000000000000000000000000000003f700000000000000000000000000000000000000000000000000000000000003f800000000000000000000000000000000000000000000000000000000000003f900000000000000000000000000000000000000000000000000000000000003fa00000000000000000000000000000000000000000000000000000000000003fb00000000000000000000000000000000000000000000000000000000000003fc00000000000000000000000000000000000000000000000000000000000003fd00000000000000000000000000000000000000000000000000000000000003fe00000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000400000000000000000000000000000000000000000000000000000000000000040100000000000000000000000000000000000000000000000000000000000004020000000000000000000000000000000000000000000000000000000000000403000000000000000000000000000000000000000000000000000000000000040400000000000000000000000000000000000000000000000000000000000004050000000000000000000000000000000000000000000000000000000000000406000000000000000000000000000000000000000000000000000000000000040700000000000000000000000000000000000000000000000000000000000004080000000000000000000000000000000000000000000000000000000000000409000000000000000000000000000000000000000000000000000000000000040a000000000000000000000000000000000000000000000000000000000000040b000000000000000000000000000000000000000000000000000000000000040c000000000000000000000000000000000000000000000000000000000000040d000000000000000000000000000000000000000000000000000000000000040e000000000000000000000000000000000000000000000000000000000000040f0000000000000000000000000000000000000000000000000000000000000410000000000000000000000000000000000000000000000000000000000000041100000000000000000000000000000000000000000000000000000000000004120000000000000000000000000000000000000000000000000000000000000413000000000000000000000000000000000000000000000000000000000000041400000000000000000000000000000000000000000000000000000000000004150000000000000000000000000000000000000000000000000000000000000416000000000000000000000000000000000000000000000000000000000000041700000000000000000000000000000000000000000000000000000000000004180000000000000000000000000000000000000000000000000000000000000419000000000000000000000000000000000000000000000000000000000000041a000000000000000000000000000000000000000000000000000000000000041b000000000000000000000000000000000000000000000000000000000000041c000000000000000000000000000000000000000000000000000000000000041d000000000000000000000000000000000000000000000000000000000000041e000000000000000000000000000000000000000000000000000000000000041f0000000000000000000000000000000000000000000000000000000000000420000000000000000000000000000000000000000000000000000000000000042100000000000000000000000000000000000000000000000000000000000004220000000000000000000000000000000000000000000000000000000000000423000000000000000000000000000000000000000000000000000000000000042400000000000000000000000000000000000000000000000000000000000004250000000000000000000000000000000000000000000000000000000000000426000000000000000000000000000000000000000000000000000000000000042700000000000000000000000000000000000000000000000000000000000004280000000000000000000000000000000000000000000000000000000000000429000000000000000000000000000000000000000000000000000000000000042a000000000000000000000000000000000000000000000000000000000000042b000000000000000000000000000000000000000000000000000000000000042c000000000000000000000000000000000000000000000000000000000000042d000000000000000000000000000000000000000000000000000000000000042e000000000000000000000000000000000000000000000000000000000000042f0000000000000000000000000000000000000000000000000000000000000430000000000000000000000000000000000000000000000000000000000000043100000000000000000000000000000000000000000000000000000000000004320000000000000000000000000000000000000000000000000000000000000433000000000000000000000000000000000000000000000000000000000000043400000000000000000000000000000000000000000000000000000000000004350000000000000000000000000000000000000000000000000000000000000436000000000000000000000000000000000000000000000000000000000000043700000000000000000000000000000000000000000000000000000000000004380000000000000000000000000000000000000000000000000000000000000439000000000000000000000000000000000000000000000000000000000000043a000000000000000000000000000000000000000000000000000000000000043b000000000000000000000000000000000000000000000000000000000000043c000000000000000000000000000000000000000000000000000000000000043d000000000000000000000000000000000000000000000000000000000000043e0000000000000000000000000000000000000000000000000000000000000000000000400000000000000000000000000000000000000000000000000000000000000640000000000000000000000000000000000000000000000000000000000000064a0000000000000000000000000000000000000000000000000000000000000641000000000000000000000000000000000000000000000000000000000000064b0000000000000000000000000000000000000000000000000000000000000642000000000000000000000000000000000000000000000000000000000000064c0000000000000000000000000000000000000000000000000000000000000643000000000000000000000000000000000000000000000000000000000000064d0000000000000000000000000000000000000000000000000000000000000644000000000000000000000000000000000000000000000000000000000000064e0000000000000000000000000000000000000000000000000000000000000645000000000000000000000000000000000000000000000000000000000000064f00000000000000000000000000000000000000000000000000000000000006460000000000000000000000000000000000000000000000000000000000000650000000000000000000000000000000000000000000000000000000000000064700000000000000000000000000000000000000000000000000000000000006510000000000000000000000000000000000000000000000000000000000000648000000000000000000000000000000000000000000000000000000000000065200000000000000000000000000000000000000000000000000000000000006490000000000000000000000000000000000000000000000000000000000000653000000000000000000000000000000000000000000000000000000000000064a0000000000000000000000000000000000000000000000000000000000000654000000000000000000000000000000000000000000000000000000000000064b0000000000000000000000000000000000000000000000000000000000000655000000000000000000000000000000000000000000000000000000000000064c0000000000000000000000000000000000000000000000000000000000000656000000000000000000000000000000000000000000000000000000000000064d0000000000000000000000000000000000000000000000000000000000000657000000000000000000000000000000000000000000000000000000000000064e0000000000000000000000000000000000000000000000000000000000000658000000000000000000000000000000000000000000000000000000000000064f00000000000000000000000000000000000000000000000000000000000006590000000000000000000000000000000000000000000000000000000000000680000000000000000000000000000000000000000000000000000000000000068a0000000000000000000000000000000000000000000000000000000000000681000000000000000000000000000000000000000000000000000000000000068b0000000000000000000000000000000000000000000000000000000000000682000000000000000000000000000000000000000000000000000000000000068c0000000000000000000000000000000000000000000000000000000000000683000000000000000000000000000000000000000000000000000000000000068d0000000000000000000000000000000000000000000000000000000000000684000000000000000000000000000000000000000000000000000000000000068e0000000000000000000000000000000000000000000000000000000000000685000000000000000000000000000000000000000000000000000000000000068f00000000000000000000000000000000000000000000000000000000000006860000000000000000000000000000000000000000000000000000000000000690000000000000000000000000000000000000000000000000000000000000068700000000000000000000000000000000000000000000000000000000000006910000000000000000000000000000000000000000000000000000000000000688000000000000000000000000000000000000000000000000000000000000069200000000000000000000000000000000000000000000000000000000000006890000000000000000000000000000000000000000000000000000000000000693000000000000000000000000000000000000000000000000000000000000068a0000000000000000000000000000000000000000000000000000000000000694000000000000000000000000000000000000000000000000000000000000068b0000000000000000000000000000000000000000000000000000000000000695000000000000000000000000000000000000000000000000000000000000068c0000000000000000000000000000000000000000000000000000000000000696000000000000000000000000000000000000000000000000000000000000068d0000000000000000000000000000000000000000000000000000000000000697000000000000000000000000000000000000000000000000000000000000068e0000000000000000000000000000000000000000000000000000000000000698000000000000000000000000000000000000000000000000000000000000068f000000000000000000000000000000000000000000000000000000000000069900000000000000000000000000000000000000000000000000000000000006c000000000000000000000000000000000000000000000000000000000000006ca00000000000000000000000000000000000000000000000000000000000006c100000000000000000000000000000000000000000000000000000000000006cb00000000000000000000000000000000000000000000000000000000000006c200000000000000000000000000000000000000000000000000000000000006cc00000000000000000000000000000000000000000000000000000000000006c300000000000000000000000000000000000000000000000000000000000006cd00000000000000000000000000000000000000000000000000000000000006c400000000000000000000000000000000000000000000000000000000000006ce00000000000000000000000000000000000000000000000000000000000006c500000000000000000000000000000000000000000000000000000000000006cf00000000000000000000000000000000000000000000000000000000000006c600000000000000000000000000000000000000000000000000000000000006d000000000000000000000000000000000000000000000000000000000000006c700000000000000000000000000000000000000000000000000000000000006d100000000000000000000000000000000000000000000000000000000000006c800000000000000000000000000000000000000000000000000000000000006d200000000000000000000000000000000000000000000000000000000000006c900000000000000000000000000000000000000000000000000000000000006d300000000000000000000000000000000000000000000000000000000000006ca00000000000000000000000000000000000000000000000000000000000006d400000000000000000000000000000000000000000000000000000000000006cb00000000000000000000000000000000000000000000000000000000000006d500000000000000000000000000000000000000000000000000000000000006cc00000000000000000000000000000000000000000000000000000000000006d600000000000000000000000000000000000000000000000000000000000006cd00000000000000000000000000000000000000000000000000000000000006d700000000000000000000000000000000000000000000000000000000000006ce00000000000000000000000000000000000000000000000000000000000006d800000000000000000000000000000000000000000000000000000000000006cf00000000000000000000000000000000000000000000000000000000000006d90000000000000000000000000000000000000000000000000000000000000700000000000000000000000000000000000000000000000000000000000000070a0000000000000000000000000000000000000000000000000000000000000701000000000000000000000000000000000000000000000000000000000000070b0000000000000000000000000000000000000000000000000000000000000702000000000000000000000000000000000000000000000000000000000000070c0000000000000000000000000000000000000000000000000000000000000703000000000000000000000000000000000000000000000000000000000000070d0000000000000000000000000000000000000000000000000000000000000704000000000000000000000000000000000000000000000000000000000000070e0000000000000000000000000000000000000000000000000000000000000705000000000000000000000000000000000000000000000000000000000000070f00000000000000000000000000000000000000000000000000000000000007060000000000000000000000000000000000000000000000000000000000000710000000000000000000000000000000000000000000000000000000000000070700000000000000000000000000000000000000000000000000000000000007110000000000000000000000000000000000000000000000000000000000000708000000000000000000000000000000000000000000000000000000000000071200000000000000000000000000000000000000000000000000000000000007090000000000000000000000000000000000000000000000000000000000000713000000000000000000000000000000000000000000000000000000000000070a0000000000000000000000000000000000000000000000000000000000000714000000000000000000000000000000000000000000000000000000000000070b0000000000000000000000000000000000000000000000000000000000000715000000000000000000000000000000000000000000000000000000000000070c0000000000000000000000000000000000000000000000000000000000000716000000000000000000000000000000000000000000000000000000000000070d0000000000000000000000000000000000000000000000000000000000000717000000000000000000000000000000000000000000000000000000000000070e0000000000000000000000000000000000000000000000000000000000000718000000000000000000000000000000000000000000000000000000000000070f000000000000000000000000000000000000000000000000000000000000071900000008000000000000000000000000000000000000000000000000000000000000044000000000000000000000000000000000000000000000000000000000000004410000000000000000000000000000000000000000000000000000000000000480000000000000000000000000000000000000000000000000000000000000048100000000000000000000000000000000000000000000000000000000000004c000000000000000000000000000000000000000000000000000000000000004c100000000000000000000000000000000000000000000000000000000000005000000000000000000000000000000000000000000000000000000000000000501000000042ea6fe02855d8c72905f35f08604467117c037962e324c9a793505138558b3700c94efd9afe573212f9bf596ca3a4b42ae57f59e670a7cb5b9440c54bd8ea2961856bc4892b0b3f156ed2aa87463c389bfce0fd675a37cec77ae5d753fcf593c12f9d3f397c2edc9e4a6a5e485641163525ca802e3bd93994af997a9b9a44f98000000000000000000000000000000000000000000000000000000000000114041414141414141414141414141414141414141410000000000000000000000000000000000000000000000000000000000001180818181818181818181818181818181818181818100000000000000000000000000000000000000000000000000000000000011c0c1c1c1c1c1c1c1c1c1c1c1c1c1c1c1c1c1c1c1c1000000000000000000000000000000000000000000000000000000000000120001010101010101010101010101010101010101010000001012e5643e26da426570dd999e0e044e5f83d60f3cd813c55059bc0ea0f4a7c9d413b2d2cea949fa0876265cd8eee3a4dce1e243783562ea07e2de537f469f7bf627abb3d4560e786bafd52f77ac52dea36c50419f32386567bd969b0c38e1bd7405d339cecb99fa74bfd631917f965357f0341d8c4dfe6a8f4b621d8df54c82941d560ac24523012499eeca2594f20ce238b7d21b98ad5c638767d70ee05532c2183e6d64e69b005709dfc4771c7ca5981adc54f3e8bd1b00222461ae90e44eda2f1d4572fe0b0b23100a7ea6d4780b25e826e41ca9e71897e010bcb218887b3d036d44eb30a430b5cfc6660270eb5fb7f274689ac73dfa53ba9d0739fe38637f01f7130af8c5888d4c65ea68109a1d58fe8d7d1ae62098857b0a3a3dcd393ef80ed8bcba6eb5e3b4887a32c51c5853b97a5eb59c87c520f36423c6d7be06071821ca1719330de5e3c51a521778b49cbbc1d53f8ed42e0562bf21ed72e8cb9a410b0e82ef5f7276df41353faef675fb408aeb246b6e2f704cab99bf28d1427e7b0fec7b9929c6effdcd045d3fdcec1d1b73faed495444699fab936630d69b519f0bf4f1453db36439419dbfba703b849d1aa5774fe9a5325e5bf3e7cdda9d1f7f2a9c2a035c72fd4a3e383306aff03448f9accc08a2c665837560cec8675fe2512bfaef35a8fb7df08a81001a7fd4903849c0371dedd87f31295f46269c5205dd0000381000000e00000001bc000000907c1b3a08a2cb8683fcf76e45ae9de5c9d696408c53a9939f5064e661500cac4a2964e0e00a2d6c71f958321068266a0ad7706eac1a5183572be5c3d53ba45ff1734d012091c1faa4caf66c00b6a0b4252ba53f2ae331779ec2b332e67f3b10a097f4c74d6bbcc69fabdbfc1a07956b820a762eec034f3519c6615c7ab60a150c811c029efe9b594f2412a29b64105003000000906bab5e23dbfe7a7696c7090aff4201627797929ea89bec9d6a17ec930fbf0f3d42708d82717dd5dbba0e06b724470f6a9ecf06918af478b4d92ba01a3bf5cc97e531181c10128934d4f2a58760f863381de408544e4b35200214e5bfbb7d191f689934289a36948459656b72b025f2590cf71cfd94c6c0b479f85ae74bab0f42384de7051fa4405e3e1663e0cadec48e00000090b9d3e39d91ad008916bef1383e0a7a18314b2b778569b1bc650be76cbaa70defca5db3dba7234ee8c0c1ae77caadbe6e4d44aefc9016a6ebcb43a30ef76e614ef6072bbf1282ac465c0a7d8f1ad081c523aa58f64bb496593f493c5e18084cbc343f9cce765f11cebbb9f08aa524aaa0107fbe4f10f8fc4d5ebd6f34cc5b6a718b93b35c3cb971b033d51dbf3d71a1e0000001bc00000090d65b1026d291257fb639ab3bd2bef618b0b48a8a0011f78b66ef61da931d3e98d7476e5b783cd04fd256c2ee48e50e6609423babd46f0d4128e062251f200f5efebd8874fbe9803428ace9441ad6be73024d04270212a21b1e315671fd50cecf657008784a40e6a945d0c9933b0b751e0e4270bdf3d96f5660bc332deb55c1d69efee98575540775b0bfeb1795ab0b6800000090c428df8558124ebad08202b4e9df93e9aba73daf792c1c5778d7db24eae062d1f96e40068f382c241371fe92222fe65d6079aef71144a6b5200b640fda76576707df17436b91e16e3702b234bc3b5db2148e9b10bde68adae03ec7397db66e9f38d9b6dad770ed9a4cedd10af990193d010d7ee63c3673e7d19a1a86c85b812a77a27960a061d34b6d3603e9be4fd68b000000900dd0a2f23deaec926322cf81653b6af17ba64cf0771512f67cc6254d5b2655c57ff8bb485ea8b7800db96eae65f0a7c88bfc2106b9bf899dad80284afef5030402d374526f89145f851b824fb54289e50964cc0cfa74628f766fc485bd77b2fd8d991243ca4df56a0d2961c62bcfc8b0143b56669c4243ba4b8a1b23eb6d47baa02a5018715df1ba04c901c8c8bd0177000001bc00000090a90243f2ec68e4f6e6b21048876e4a1312cdb3af73a5cfc0dbcc269d26985fa2055897a67459ebb74f3ac45aa0d6ff3f63dcb9e71f0884497c858f0ae15cca019fe6e93e9baafcaf945bd069a3a5c98e0865700131bef16cc4c47e389f5e0f8c5222696be1a1c09029c7e1ddcf643fa617304820537e972a15547cd43dbc826304a85d5a9fdd345f4033f326fd8b317e00000090a6f785621cd1f8bd394ce02ecd3bf72e1652b7dac8434e0f36be19f5067923427af900b9d89d75e0693d121c4ee41ea5c7b3c3768c2aec19c6db0476c0290e50028b11d31ae04ecd702125a29fd857db05a3826a0ce0a9d720f46e24e33f95c1ffc966b90a1d2d83c7ae9f28e538cacf0cec4bf0ef40d262f77360406230b9456a7c14321fac15cd4dbeaac0672954d500000090ced89c13be6d034f1f3ed73c871c41bf79e2b6f9c03652ea3562484101d06c0fe592debbdea9ff08a4a8e242c3e31b3a39b0b503bcc29f1f3da8c90fad9240b701258f7cd3dc750418cdb045d6f89070259677559b050bf6652a4b3c88edfd90a208d80286a3a627432fbe2bca3d07e5196c31f8e14d2443ed9eb8a12ccf2f2d3f90af296c346adf5e874042f50b8e58000001bc0000009036b8114db5d25a74f20c28ec8fa372279d3de9bf48e22a1466a0e02283af96257e863f638359ea3f4d5626af79ffb9c3755f0201f31deeb9082c5d4bf708589e66edf6386bc795ba50d50a23daee25eb1492ce5b5ea7bc5b2692bce3fcc4d75ce59f4ff60a1925b26288230a41a085d7192a0d71649842bfd019e17062a1b606dd09abbaad2ca4f2d8e19c591583814900000090f448cf92adc1a09fe6a43d3532627994990b9eb5aaa76a43d547045d73c4311a476773a0642db295b91823296cc8eebedf1c9fd738e612e2c07b2b5770618e696f6c4f97e6714ace745c013e68ea1a7317b03ca9059b14736059b181ea3d2d4b35b02525ce4127e892259748c01a81912e694361b8f22c0484b545ec4e2bae6b6b9a04727ccfc5976597374e891ff50e00000090a422ede5cb0a1ee27d05be320b90bcadcaf47b31ff2723625145666809a67f3c2757ee3d5e56ec08fcea4ccf9cd345e2281dd362935cc246680a3115f8b9df3301c3b1e9b8c6a8eb2cb869f8e029b8462a2cbd9f5b38a69ceaedb87e7777e8edcc0c6bdeee33ef17b3fc5dd8d74fb097010b54bc3509deef732f49927ca924a212fa97c5ae076dd285e9f5001bb3755b000001bc000000902388e4f77b866464924fb5d3eb9bb1f4cf8aeee2327dbb5ca6c94d7469a8a4cf955d66341ca63cd8b74b1b81c87fe727cc79d4c52dfc33b8336a6ab5e88e5460d3fea2d8af8ef176c91ee898a1ea3b501af82247408797c0e1b3d309a271ad98109038e1fd88da421de1be702af1c94a2868d845580151b770a3af1ab2ba4ffa38e4696d1174792ceb8169c0e3a40d3f00000090eb7def151cd9a4004475ad0ce963e7e34cc75ddc8689fb9466c4e9ced59197dc3fb3c25a43fa9c62bba236f989e38bfcdef819eba5dcf89d17f6c4025beba3f988497c0b9639b765867338d4b096dc811f1f35b19829cea2a0999d2bcb28db6d3204ed4b7acde9785129e72af98439d31035fe030458a481ce33a27dff24a1ae4796b041643c0c80afcccb1b03e9916e000000906968b696d07368fb5400ea065d9602b3043800c1440c79689952ffd7797d5c614568420845cbc63e9d741e1f5fffa1d9851d30c01c29b1927611f795cf88d2237e7d4ec9037aa9d0d2bfcaf4b5a9355c0c8e9cf86b1c2957799bed4517fd6e7678a52789de5e7d7bbc50b1c4819f7d452f2d0fa9ab1862b6e086fdaf8870a4b1a0720cc10f545d8b1a80d2f7bbd4a03f000001bc00000090c66e589493203da5dc904131056f39a754308b74116ab5ff61fbe83ec2eaf2029b6b617bdae1462ac1c00ce5411ec65c0c5234d7def6a7e8ad98a15b73e2a9c02f50fcb32dc91952a1256a7c0cfe7f9821e175d48758ac9aba3cdca31f3c6fcaa0c616c8fd0736a1930ef29ec83d40c800e48f95794a82dc3f8ea3fd2a823bfb297e541d1b88783cbb52046a5c0f562300000090768193236ad2763b5873968cf429dd9904fbd0e566984a93df8d19b31fa6c7d97b685669fa571770d1104e49deae20e878a40c0836bdb779b82935926c6fbdd1d0a5a0313eee67f020ece80df5e81dc42246e9f9a36c8515a4d80eabc793bcbbefcd84bf5d10711f35971580e81c41280037c6d6da5cfa8c3eed023f32d55c48d0d4294ad7c030781aff4f77b94fcfea00000090c912532e7c09e0a8dab5f7a0c0e3f8364025505f36dd512d2ba05909f99b04cfddfeef1175f65eeaec313c4eae48323611d7b40b7f52bcb507d0c4c80563c66bf52949b1496b5fbb65265a62e7b6843f02e21de4e1dda3aead13660ee9e5b2cb5aa9e614f2bc9c086a0eed7cbaa710b42edf80e0159819eaaf21d15dcfd2f94efeb9525fa6a0f99828876e91682585b1000001bc00000090ef1b83a2fe15a224b3cb05418b3497e13856d86ec0def188a4cf0ced86edae1002081453f26b97ac1691c20788d6c4746f19f06e29c5b23b0e887777b6e30208971d0f2cbd0ed8d91521f4a3c7045e34076df337e47e9fa507516ef13f03ada0c285b92fb61d70e9a0db40d3e163c45e2ee3a7b9f4f6ca902e3a7e98f18b0299031970689089bb5f21c08d2fc94603da000000903b62fbc69c73bfbb0c0443bcf4624f807b1ef4acec60db08a8bccef7f0833e1944cd2ee8644678af13f82e7014c4c728d36bc689b93d5856cd21d4c005bae82d726d64da49fb830601b511e9cef791b62e54ec2d95817842163e2f417ff9cada18ffce9e4588208fecc05500ad078f09057b40278ee50de377d0a121b23a928ecf36c42d1e609261fd79c6c853cf304000000090570872564617956e481624ece8f9f8fa7bc63eba53e28fe4c3c06dc594cb0672e9530b3214c8983cafe960bc97fa3536e3ef44132d8721b793efedb3a17578de05a354348a2c25e83357c4682a39df3206d44fc44579a5ca0920f4196fe08e2d00377513e7ed4145e51de3836faaaa4e181002382bf3915f558af58266a43b2801a97c127dbc10b2840c2e0df5949bac000001bc0000009094212cd20c5afb93ea77dc91e6ad2ecd5dd55ea4e8e7de7ce66c2f7d496f9af69d07f2ffa402f093d6bd3c2d5d246c7763de341e81e5b654b1c29714e79b02ed8aabce6892903ec40e05e0bdac20c8c2237a47816a3cdc1fc64fb4052c9192e2493e19c8a6f2c639aec710ef74b5ad2727850860db08e994d6dadc8f773a7487cc99b29f65b78e76874842e18c59cbc100000090d22273ab92a64992204949d374718bce467241f4eaba3dd635a3b03469b2cbec16e5c37e80258e9b1985cb4675450f19dc8c2b3c3576da96c4ce51a08aa4e3bb3c8015777e3300fe7e2eeb1f57b8675f01a998d4256eb12fbacf0157cab4dbb9f4ccc9d15d5786f2c14d5b453cad59a828312c4bde964f8bf3224a34d5da9c65652b754d3e9db14d985923114c3ae8a70000009016e64044ce8bc509083bb3a8bbd4546a349850ac8c4c2a4e3c1f2b6bb12f46a2069c4bc6fcf80e208fded4592feef5da5448609bec68fbf7e1314b3eb76e5cde198af34a03a051e92b586452758582a11ccdf709902a39cc8940dc42e3ef4e0afc2ad07b53337028ac9dca172b7f475421b2eff3b6c4da731453539197b2d016903d4d1ff7361ad5c5c2fc35ecdc6a2000000e00000001bc000000908f5f5305fbf3044e058b0694519238796f9e67dcc6927252965c4792d1642bfd740e05eccc60eeaa8e4a5b6a085385b2947b7e9f0ef5de46869ad4136e88fbf07123f9570b54623787cefc24533b49ac0e01c777811bd9cac13a289698765bcdf90d8e965689cef3e2faee319c3eb9cc1242762410d2041e045715375e0e060dd857fb744e324b653250e850ff35845b000000906ed36c1c9b2d8c7c394f5c0024cb8510f8558c17d6c83111ee0d4077bb0fc10033c45319684f00450424e3d77369f15eec6a65156daf316522cf5795f0d21c326640da47834e04eb9987af73772f864027aea09f7d3a5e79d39ac7e44f7e2a83ddc472d7ad4d6ac4cd2b235e660eef9115773b7394dcd03632821701533dda5be844f897845e7781985fb961279153f5000000901d10a151dd0b0aa3b5b8d6463174260fdcf462d9a53031d828c4a6e2268c05500373e6a19654b0196fad986d4a1a5fb508596b86081143487d11a8914c16c8a3be4b5ff912f227619cbcd5f0fa9bb3472d659391f8ab820070814fdf2008597dc2feb5a180ff856cd705506219e6ae8512684f4a8fbc073f5cd556bdc28b514a728ad6643d63d0b2a73132e333cda1c6000001bc00000090bf18211d1bf13578fd2bf01072f7331e7f1fac3e4082875ae7e7e7b3ff0b4b37164425b976bc4938a6cf59086ceaf55cfad4d17745bbd7667939b438ac95cbf03c6d7363567a153cd37df714ba7e304004128e90f1076177cf7021df6819fd1fa05e4fc477ef5448abbed7425d02d5cd2120ea74f76498b5771de72eaa6062c4739be7bdbcb945bf50f923295bb84110000000909e92a40e2d7c0803a9c1c2acd2fd309d5dc6a664ed8d8a19520fce1cb1c04a70e77b37e5ab8b86d510dc91d20c590f27e8695f14e3e6280fca591b91fad636e6e0017662a77908d614963b89692a5b2509a593a79f44e6195f385ecf367583615e5e57cb0e0a64e94eca0feb06ebfff40a570a8b8ada1ce1ec4ec1d41816ad5a0e0eb5f5ff60424a342b692e3e00003200000090a488ba549f733799ed5bb466cbaeee0cb5f92530d0fba69ed7d64857893c162834bff5636267fced644542d9ce29cd01c422a9bb136580b369a4b807323f3b3358438e5148fb094a70eecd217f515dd12a94a17a4220490b31e7de13113830cca9a36e7be60e304e53f94ea4a4345a7427cff8058fb4b484e0bc5f4c4593ff22c4329d33d5bf96d2bdf9a152dab4f2c1000001bc00000090b2df28904b5b72cfd178670ecc399d16dd3da01f4afe6d735a7ba5de51d5aa826355206ecde69e6dcbe87e4aff2629ecc23fe3c69166ea1171572fb172f2245e063b0e46dc59e49996cedd02283942ca192a166d30c3418197da6c907f2cf2bf423e066f0fd68c1018af6dfcea86c04e2217a1fb08251948cbdf4b26ad27a5a47fb634267f5f1d06715a41ad3031b31e0000009096c5ddba66f37c8e7761c5cf195c140c6edf6235facb9dd07674edd48b646119a11e05e3addaad9949704f7942d246391fd7743bcf17bcd5e6ef5afdbcc60b5ea13a3605d200be3922f68f84c879d56318a1306cdfa7aaa518661fe032ff2d043113e594bb512552705b20024f7b4f070071db291994809f527f91d79aa40b18046c265b12165cac3b5bde0214220683000000905bb6629675b1d01997f6f33a8bf669e30540b49d31e4895c3369cd810ce5645e5a3e585ddb29382ab9209dac3e68f954423c52cd148ed3969d7e7ab819063070a6dfa3c135a45a1d080f47c428bd69d50b6630d5d2bda9730c9fa5f32cf8c81c03b1be6b30b7de3669b050423c7e132925f6b0bbcb1d5bb0af2c29d8ae2574b196e6e8da740293747f5e20d6f51585fc000001bc000000900e9b1b8cf0d0175eaf2276c35dc87b99a330902503b3bf88a756b750f4eaea3609a401968cf175a77454836cbbc5525ca5ed14edb753b54524dce85926933d149322a31e37102aa8fc1ae44103757be91643d3161252446bf073978f1bdfe311bea78257d05fbba0b263c842474b9da812eded30907e614d95d53856099346a65bc21b56324f01dc9e3bcd98f6220f8b0000009039d7912fc0239dcf903b219c27cdf364c33cd37a30a94afbe18f2778cf2d0e3d892976206e2ebcc2e6d55673d978e65b76461e49ad281d58da534a83c2e3a4f99ffc1848da89a8d25f3fc43775ba84f00baecdc0fff6867148dcffbf9db34bbcd6c1365182d56429f775b70633023e761c2dab8f1069db54ee02ad670457b2d5363d0c7f1408f8b7241e9cd9637da5cb00000090bfcb048a04b754ed6499c538a5e7f5171a93009747e879c6107841670f8ba2effd3ee5a443701ab6ec7f7cca7f1b903e647b64c1d23283fc7ac21aa2e0248576e759e58a8092d866d9c6af7932761c6708b38158792e6eec7fe194f4867bbfaf9bce9e7b31736646bd6b75bfdf57caa21ed0cc1a5ed3cf23e49680957e6ccfe145bca1532019a9ba465af64f12b9b1c8000001bc000000902f2439e3ebb2b14781fad9616f31013916786861336cdf02d81ea1a2d0f82fed8e1a0352b7b5f76699e8a0429c874560b857915338d03c98eea3ce383e048a072b4b030fe128f64184d6b11747be25bb1822e95d9487ac8d32b34a772a4504b3e98dbcea2661ae9c4c7209647d10a4481aef78c9e5a2f06dba8b8f1fcd22868b3918ea74d2181d61798f10fc9391fe000000009038ca3a37a4f433486ca51033d317a37be014d1a960b33bb16b54c6418f2df1a55573ca9e052da45402f3b7bc1b1718073c581eaa6de90161f9de1359763046fa9eba5872f0559d3a40df31678b93605d102029a421b8e9c0cdf8b9aeb79a56694ef916396329000996a10af3551edb4a04887cea168a15ea8fcf8b9b3693d34f34744c6cda7ab63c2cf0f74f04a8660200000090d9f3ff5b9412956db9a6a98a8536aa08f13b2cfd675a0d1de26d311a31ee234d25daf3ac9332e20838e735225908efe42dd6391b8671380b73941fc6428e3ea61f24f9b56365ed89320e72c1a7c3dfeb05b0cace2b85c028619e40c413599155a9fd882caec77f4af38fb56e608ff88d02881fef1658f1a55f6dc1c0db724e6b3930ae928311c7da07db238923a7ab3d000001bc00000090e81f94ae53f3cefc91e9e1dd0daf188e8cd3c9ef1c6e78f9216ab5ed80c48b5546fc5e13048f64bbc159b0c621dcc73048f6dc215e09e00242fb5fc5c5adba821bfb38f7e48706be200ac8ed2e045cf608e7001683439e7575ebd3fbe2744886c97e980f3b76890c32221b8e15b4fe1204b5abb50c5c40526fc850e99deac16d0996bce31e412039eb4e5e41409c5eed000000902e07a04fe6a351dd0f4885901e4c964d58d4b90c041ca27768e0fb352ea7a92cef1ce4b7b78a4c3fd04c78df3741b9a393631b7f305b850d3b4684adede74540cc36ebf64be5be1a2b7aaf858a7abdad1cf71dd2625503f41b2aa6cd8bbb02ce8fc975f6073c56a7d5665d57dcf67f2612dfc622493b15fa2ae49735c9d77eed90b069ce10f26dc7a56be0e27b5dc72b00000090b63ec5dced5d374975f1268fb76ebe294fe413110af5861f3d874c4e2159d712b53d44d7bb594e5e06bb20805a0b83a718451784acf8fe7e2a488f77eb0e5fb232059daea041fbe8c260e772b1786a7120792b5d421cf88206fc781a212554ecb4dce9d3d0ce86ae6a0c3370242062cc0f71c72149897b6c3d023351104fc338f0730b8ed899ec4e9478495e50a79165000001bc000000902a45feef1f690573b1adab340aefcc3718aa5f5da6246496e3a27c0ec30cc19a4c296667a0bda271c960cee763d5c50a79cf2db8680f6fd6b153b557642f2561d92e1a983154177fc3dd576dfce6484c3060e2cff23d56b5400e981ab57bb10f086c8ac4c4b16be77f4a8df2b71e125e02949b090f32a9d2325cc0512c3a29fe917d6532287377c1c95c88ef0c992fac00000090a20f6fc346c15e7e890959858476a0f15f2b3fc318de61e12ba8c5c8aaebd6c1cf9702548b6b930b3a32d37ae19ba0274d11434aac08208618ccf457f457a8e986c5922cf5d9499167fb5bd2125387800f2f9a04f1d5310185d5cc420a9485852747a5c8966ef1fc2998a2363f6219812f0beb7e70c8b49f7fc10b88ddf8cd4e9ac9e6e0891b13e645116069560eef3500000090784818f1d8648c12d54a9c59fcd7be95532ab8af2845f6d3ffc9ad6784dfc7c34468905e2f2a58ff4979401d25309aa43ec338c3648e151cc091800ba72dd541e8e57566a59e0c44bb929591834b91202aea218ad2dde451ce9fc17969662765fae79213a63910b819cb298ae208a25301ddcce3170069482b8253e649daed00e6bf05c2dcb2b55b9410a51187a503ec000001bc0000009004690b61c90265eb3a91db5fbdfa7c7eee1f4b4f359b16e803f41e57a10ae17287feea5c17910a75d7408849bbf6480ced31bc122abfc20571586db97b1610ee5d0fcb287e6975b142cf38b0cef96d3c2e1abd49e8bb69f93f3b49930deb05635be1a971c3fcf56d072964531f803d540e97cd9e5ed3a4c6ac2699e8a0e898b67cf78733ca44d1be440818826562b8d90000009012624585c036d5a50bfeca21aa095b1a7912126044130f201e7a9a68af5ccbf48980797ebdbc0e25324ff6a4b30001836a9ac34087da23c0cf54d97ac2303ea36e4641a3085a017748a1b16a2c31ce8b23f4ea5eef22964dede68b25d97993af88af796df8f3ca9d08c237245bc386430114e431f45c18b7d6cd2679d4b0c39d4ea21a422f0d65698f826bfa12828d8300000090efd36a1245df2761f370ac183478511343d07cbf7c3e48c25bf0974f5bcca31957c2f9ba1ec709cb88d86ba8b73c5a7fb159e1fa87f200dfd7d177fe5fb644b230e9d16d2d9fe530b3195333abfca4241f8a1960a844bcc46abd9e916998086bb5b61dff2a49841f390b4124aec67ade1fcbd225e21183957ff1233fa7bc0ab65f5e6b923bae64a17d85d3780295900d00000e00000001bc00000090947684198f65397ba252f9dbc1f8af99c32aeb32284f3de49c547b0b3114f328502977ca818a2b4a264a487456ce29d0f1418a548e71ca58240a97ff5011de799d72cd0ec3a48f3a4ce6403399c37f330e55917691e8414f521d38f3ee63ba5c24e191e871b406a7a112ab11fd79623106d026049fc195d5f6d13db61db27ce7275f50c6db01e2f6e827950602a835760000009091249f549962d71734d4226ca3442ebd1be20ec7a8ebd8ac4127005879d6b5b692feca8335583a4ae0a6a9ca2ef890337f89de3b0d0d9cbabdea11713f3ee8f44843e8bf98a36c958158456ef9bec22314433d304114fccd775dd84f937283a904c9e34cbb00f1b045894ed2690a29f3094814b960bb88ccfd8fa201627dd9e6dbe1113b92f6ad611721075c318a94bb00000090d07c621e6271708621a319478d0795bef45f713b6de2c471196685c0594594a609db8d5db559a1649a8d1758fea1467283fb6c01daf08762858b48bf0615c1e116f73f8bfdee730c72c56af959018e2a02acb10d80458b041ed34e3022e2b3b398ca07a4650e3201f16b7a03e4ed316e2b8c100851e08a74e8a44bef8292271bb70a527e7493107c7f6137a1eace849d000001bc00000090cf7ad5c44a117719a906954ba9babc0edb51738770b4ee763b16786eaf24fb49faa32760f99342e1b5b5bf14985ce6fae0a7de2e3220f6f11fd89dd1191daeb109fe74b046ff913a6256e0368b26c15b0353414edeea5052ccc39ef500f69baee2a4d866ff0687d450a3dfebda4dd16a2488dad669abb84abcb8304e791a0123943c62be536dcd139543693e23b8f92900000090ca8eadddacf1b9634370e2f35e4041b50fdb7bc6509d633274e89c9f20e46cdc7839f39b7bdf1c86eaaad5adeaf2cf4fbce53e28622234e28b0a7c29d925009a5d95a3e552b90504d2cb2ce48599be7623d6957f71a06e48789c3405c17319c231e5d13059c74e2046cfcb01b195d15e15aa436d9b166a7ec1bdc4c5f80e3d9dc70580b20514c9630b65a71fa4c2b2c600000090ab0d42c3fce474d24597ee47c7f10a98c9f68bfebb211d2a352e84ab938d45bd69759f2a76690f029be1aa30730d0284941de3f796dccdfa1f0529c95e4b6679354dab63ab48ecc79a5e91b3d7d3f95d1e07799e9397ea5d2a6776fbeeaa4ff79c65c4b06810e20ecc2686de6c256bfb00531e2689364b7b81f7692432290d757e4caa95775d586da6d24d9f0fd460b1000001bc00000090d2a6f20ae66b3c42770388248911cc2b69569ec915e4ab4fe8a6c7e1d4aa0262bbe1367360a2c8d201e445d361f4311bfee4871f95b4ceeafac0bd20d229c523ebbe060cd13835e5904e0bb4cce865aa283dfe8dafa40181f02a5ca81541b4e170efb21a66ab761cd4943d42947657c927fe1d12ad7c2a5ddee85c8363d3edba175c87e2682bbdfffe993c9fcd2577eb00000090f14f756111656f5896f07909a24f208168a7175dfd7236ee3d50a11d6aaca5f4e02b0ea1348bd834db882a10dea0aa5bdcd78cad15d555f4f7ae29d79e188a9b0e82264b2018c2cdafa2787a29c4b12b2b7af03f94b35cd827d0745732ec211061a26c6b73eff0bc3e492c1910f4c4dc0295ab73daf1af831b407be133c243c12f7faeb13cc54c177cfee8e2ec1d9eaa00000090221009bdc1b35d2c1f8f98e9c88ed6272c09fe0a5050b71b11600da54e262c86892616e7117ae08df212206b1645442a179796d63c3babb16178a0c64a18daa1d09573cb0035bd68971b00de5d5570a000c90626fed9dfa6f56a30c8983abbc55f791cacc21a91eafd0d4d0f26553f8c253f58e1313862b32be1d830116757b4de42fc122e76367b8bca06b524d1a2d7000001bc000000909f812138b9e775dfd5969502abea67268af1cc21c9bbf14c24618f74e8bf66cbc3ffc22659bda2b6f286ebaf0ec4dcad2160910cf444b5ec4162d4e69a6fb8eec2331c41ad0b10177b2e8e4c2ac98a180a72bdca115a8f467ae53bc69b561f37f3415376076097b186e62c24e2977d630b40c1e975642f810d232937964ab2149babecd8c39b9faa9b946b14638a4918000000903fda1fa8f291baec2fba11067cdf4e363a1f0ae6d2e168fe8445b614ca3e8669ff1872d7d774e11fcac97f48cff4b199c047661b22af335c2027539859262c0feb865fea60aeb493c07bd72459caf3820e170c456d91f8fb70f1097807f76a1d3c3552dcea437dde4b4336d117de2fe6084425c996aecf3bece7742325e4ab116a23526edeb91451923b40769ce07b4700000090d49dfc5cecfbef724d97e87b5024a1057ff0b853835ee0931170970b6ef0afe3630fd198020f91a75de23bfe72621fcb1112e1e315d32cdee93656c96b0463240b988f392e210d8d1235dc76e652308420900759e115b20af866c93192f6bb025c35f9ce5f8e9d607a2918d70aa231b32c7211be4ecd9ebb31b223dba5eee9cffb2c14d002bf4e6c8cd2617c52f92209000001bc0000009052cd51b86e05a112185fcee59644c7868239beeadfecbcca4d334a1546904f4cfb3b16391cf17aa1fd543fca15681a8494500c1b341277b96bee5a43feddea9f5b5b968f46374dd616e74b96c20173410d3ffba84290c2230696ff06819e6cddd55a76a44a710c31ca5bd9e95a5f215314d88ec363fcce41e0556b5f5037cb9ba663626ddde3fba5ca49a18397d2861c00000090195a187ee83284094d52b6597b76a9de1950c68a4a20bc0e1a958b84f3dedd893db8d68af7abf948c04cc946fee7dfd3e98df98cd435c7c0616097c2ebd550f7c3e98a7341315f53468c3a4f7516ff8d23c160006fe19e7637c2258444e080e9c54753dfac170b7c991d734659748c4a09328f81b03ce897fbc6dd4bf283093f526a65e24cfcaf5532756a85e3f0980e0000009057f70fedc5778b142232a47726cd35c4623e7f440ee98e92d918491d17e1ef9ac8de9c6c9d2dfe5d0b38181b709172fae1e9970808ecf061d7a8dba520905d2f8dde99cfa9b6a527596aa783c7d21dd5282708cefb592cf2c17cae26eab29728101185f515f6033731b1e0c050b6b0cf0bfef40ce87ef4ee0412bee0503b16995cf7a5bb5c4100d6a502f80596c1f7ac000001bc00000090a87159ed5a28b9d2876ca81908a405c4d4ce69b04f5c585c787cca6e4e444c7389b1d8f9bee61c6cc5d40a65cb1c66235ef5bb36e274cd24b8b8be107759787935eeaebd7e4f7bf42e1d9e4d1b2cff8215e079b4c27c1bdff61b19ee1e7455df02e2b9d44e6d0113cab1018e8ffc2fd00079cf8c0af9a2d431f1ad14ad1d45a7dfef8080515f3506d0d7123fac6957a7000000903ede5c1517d46f450f68002c97d595f5a35fa49189a8346927926a4be396470ab70757159ac64fb5d215ce07384e3bd7af352c7d63b3bec10627f7072345d365bcd313cfe82b1f6b11088492bf9ff62d27b752571d6a5adbb01f290bb70a267ced42ac421070fea792f6963d2faa1f46112903aacd31f3c8239648fd9a308ab493ebcba7813ee3c1fd85b234bd293464000000904dba3f8d5253847e0410880b0e94de490bfd7b274fb8ff1a1d19da50ca4ac756885cee121f54bd05d4098b6139f0429bb44f8e9298125c7ec4936565ab5ee625503acd59ba4fb8384fecb1faa487ff332444b78ee8b1770ca6e7d8d4a5033c5a841e0bd137dfe66fea79921d8be63d3c17046aa6b02de3ea263e784a01331ab59390396006610ef6249ca469b3b256e5000001bc00000090a318178fd7c767175f9a386439bd1753d04b8cd05fc9ee599f2b3b8b6f4d49509e588d27ae0143272f13f60649625c2cf65e7c60b707f155a1f2d7adeb4523541aceaeda83869188a240ae6f96626bba134792fc15db82bd296c98781fa4187cc2235b9a81683c51719cc7089a62e249191486dffd24778d0d42b360f53320d1396a4535a21c67dabfb7ebab6829429600000090ceebb89ab853d54b103f12d65f145dca0b50e079974b3bffda72918d65ffbf2b6f56773816504aac7d18f645c7a810c2ab27755209342bd1a3d3c7bdb4a0a9f3bef70768fbfe0048f19599382100791a2d3ead79c839dca4469a8e1ab7efad9b49c9c921d5d06ec688c38290cfaf41212dfd1fa3f876165e9e2e4501fea39515129b1920d61deaff829d5c8fd632e40800000090dc880c0a8b4345e819c34e4d4cee8f9b655bbe54ab1fc7086dd2dcdbb92c0abe931df0b7259782b8ea3db0cfffa341a43fef48d99532e19947e67d7a99d1964f7dca4b077387ef4dda2556ecd948f5da2b420825296ac7e697f327e031c600826ef2085458e49c20e39d4abc647f5a642ccfcec29b3e8aeda89451df3bee503b9a4185b54516d68b8c393102242e7d82000001bc000000906c7c882a148444a9b4d95ccd067d18b3b7f3976ee606456437dd3503fcfd8e65a3a0b7149716d3178ebb574a3579a56337ae43b55cd8f89e7c1d1cfc076e28fe3628f2f91ccf6092de236aa93432029e00321c0b18fd00222b7218c0c6974321b5dfb7b9a37be815e77d0ea4559a2f60178cdb0a2cae0e4c6ad3b7eba520b09f74cfbc559826e3499f49918e6da231f90000009081ffb2cb3bbdcda534d9c100bf64d41c630244fb248f255da994c977ea3e7a2591398362d4f8a70860460d5d65ba17aa0e312e47a22f6d6e0e96667ba1ab2b02b898bcea8a5f991e25878b70392514950e237cd9ef6b504942ba6ee37e97de35b072e92afc0071f3362e4f6d7667330c1060da0b1b6d98e5c0873c1a0b8725d85017415cd2ffe4b018d77ee46eaad29300000090fde71d5101f8606e5bd8be1402cae1c9f6fb500d1f6eda8144878cb10f44c93171bb2e96ded3a1d1a6320349a6d5ca84677773742dc57459a772dbabbef35a96eab54f7ed1b4353bfb02ca53c68c1e3d144c6a1cf3e56a76c46213c589008be1dea90938d6e2e20f47f4cfc852ca294d0fea1bc5c52eba75821c70d98a6f6dab13de0624bff4f75974ec7d7f4860e3b100000e00000001bc00000090d9f207c2e4ab524fd2e7b51dca4f4fb421bbbdaa372793c766077aa8545d83bbb6fd42dc13cb1faa91900c92b683bb527414a8ee06ca3bab0ce9f935885198a88bc1ab065be3c41dc936685c6cc8fd6f102711ae2888d2391ceb7391d2773f1f22642b3b8bba7b356734ee0cd13a3d5a095bed4c7a18b005c895b25092a1281879a77e5876af1a56965934f1b01ea01100000090f6e66e9ab940c2d660abd12bf1ac581e825989598ec0617bad0c61fb54fd1a23546d7eb7e47c65389a32b86a7636532255b7567d19551b31451ab28c53665e122b88fbcc80ec0b06f26508ae1bbb82082b2690761b9151ded3f00c3534d4966bb79bd49c193757793604d75d837b3393023b9ad219829bff8798bd903a86bac85d4b6ffe1a4a7e68997248a4121df0ca00000090d4b4c16da25ec69b67ad5075f5f4caad48d49d2fe1dc9588f5809e4266176873d373ff68d6aafaa419693dc8a5f8347f25191b6b0e1aea68caed8331ed5f20d4cb46e7f35f714c7d8750ba404abd768e03f3f2394ed30368b968112e6ef4de0f98d860637b91067f9ea3a1ba95ae459b126db09c405009c60e1ef968e4d359d801d672b8450bcc13e5e58130412995cb000001bc00000090996af5176d961e74e0317c4d426c39f03f1155e8a4ea71df264f7e2765cff15853370c2d324c5cda0ed4fec33210036ef8c6afc30d98bc2fd62a4b42c20babccf769231bd8014508a1417d05bb1110fa21e542a26c0c88d844b9dd00f65989f74c02230087c7941dfd80e33aeb099d522b144046133c21ae3abd46b750220083a524cc0b83791c496beea3433564241700000090bc0474d57f12eb0063ef2c63b1d9e2c29b1927305b109cdda2e344633db5ac45d6642a9f9cb07bd4ea10c7864b9063e886cfda45f4951a8716bdc3d9287d3e524ea82281d2e1463a473d3bbc9a22810617003f5fc24cd8c7b3fb385dccae36562c8495e1daee1241ba6795e6ac98658d23c5fc5aadb784c29bf11e6e3d16801ec2f73bdc6c3e502de4fd1d51243a03990000009013675fab13dacd1fd805c4666d09775c253a761a9279d9d7831289bd7ffefd5ff6f1d6717efe15829aa86cf17451755f48ae5f68bc5f04800d65348993d20e2af9a173b4d67836f70b8f99e9072669b608373b2fd442a610ed78abe40f31b2e835984fe9bc1229293f202f0571a8d9831d43f087d6bfab4471697a8fd1a7df7dfd7beff70b02d5eb70f6f447fc5f0a49000001bc000000903956dcadee164f892e2ce17f5c3350cae9856d708aa761bc58732afab746b49f220021fc0c58a0a4ba9e8d9969b084b9082bb1c8fe2e65fd36620145e208cdbe7a8e1ef4409c40a810934f8508744eef2f838d08c309b3f07a8af4401388000b6e69ea45b326abb2573202de67e5cf4c064e4b9ca1a8514e0dffc98b69c493a195b918f70f79461f692eb8831a6dfb900000009070d9aefb3da2dfe00a7d2af651e2acb69df180cf66eed166bed5d1faf3e3b656517e22142737d2ac36e9ab812f04df082873328d44b77390f183f326f0cc8d2b3b15c6e4f2c29ecff7ca4a43b155e48828e284e51f6521becfa95f55252575b19111b30ab145fb1c7a6502bc59e829c706782348094f0c0c7b163839133d319f784e2d3303aeaed7ddd331faf711f951000000909743d75d6ec72d4297dd828d0f52dfe07f0091facbce0228271033dde57511a839a22ae30cd7c4f6609a2de0e871a581b2e528b0a90db8ff871af2475b8985d3a04afd894c356187f386219610b7fb180670a3a20f5b04af6c5a765462a6844cbbf7d0c72eb5da66321f3311fa95d99d045f2bbed604cf948545266c1f296b4bddfa1d487c780c58d8e71953e645cfc9000001bc000000901eea6f04d80babe64b4795ad61a35c08d32f67378d2380b7bfa04f46d06e6208c1f28d2303ea2c4e8171f3bc2d2c1a9910508baddf162dd9af42c8f7604f886fb7317b025f273ad78773b639af1958180989fefa2599b70c68a028ef4e31ea067b38444a98cc276aa404dc52fffb57712670d1f19e7b7a0238caf85ece99752ef1b36898775eec4bdab128fa10181266000000904e0b81d40cfe9d1b4c3315dc46394adb1a409d447202e106f372d091d481425800026683cc05cd01a4ec5152c44965954bb81cb66463fc61018c549226fcef6d5ba402fa203aa353a965ff9e2feadb7125b0a6d0f5f17552ba90bae6be27efa386db8d807bb07eb388bd1a981f4c9f392f030f279d5e557c7d6051ac0bbfe1868e91541eac030abdc009967c3a6724050000009013041cded588c50538a529d282842e54ae057463f35f2b4844e5dc77761c1920ca5a41ba99b836a664f5214e7fbb4b9f4f06a794dd726f133edffd80623de4077acfa0887865d9cc6d89a1e79bd22e500b4b39c12bd5127dbe36d7153da668274613a31e8ab0683947fc67dffd8e720002e46517bac2a598f2712fb8f529bf6eea93c4772660bef673845e7e756339ce000001bc000000907d2b78935e99681c27d68d2d196425afc040eae419fb3efff2498070d1376a24708f657d887e88e493243bc1012016866572433d3525ab42ed3c69d51336bc1b62ba508a0fabeead6a0fdcd6615d1b011aeb54bf48d725c6c938abc9340863821f656fed760468c8b24b87ce84f908211507b8b4f819af0e73a81d9e3f8ab9ef52394fb3f2a84ecb50004356347ea40f0000009040c20a9d2ae8cc24816017c41a22d9da9c2135d2c3a5b71c82383a8d7f61ba080a6d666d12d5cfe8032a6acd35835a1f03bada40df38cb0419ae80bf2ee0c9efa4b02863f10e37e9c441a359d29dc3130297260327f43b962afb5b6406dd3456b260a5b1b58ee80b9381b17050b63e042a86d67555af1bb334a8d2a6f825a17039e93e8d620afabd5ce9e0aa6412da1d00000090063df44adda8384565dfa07941d978435cf1ad2072ec9b9d79fe5b39a947b7ad68ea617daf9471dd0d1d225d7a544b69c9b38cb894d2a6976ce42bedcc8d3c82c3d177922139ee8103dd4113cfc40018043ee6a9b3d18e56a679f4070799a3cd94fc90063be7efbd85f7fa76e1b5bca92125f9f881b6626b43cdf2bd607a43ad57e83149979332407a32c9bf27f81cfe000001bc00000090f89605945dde7a518c2aab34b78f0831a346c0ddc0bb9734b4437b2ec319093f3bb754fbdce71e62b9f0cdfaa5e1589dee6ff2e9e124f8b4bdd39765dd4a1a4480ea4a792f4665c858c5d9624ded3ccb25eccf8fb852d32a08b9e1e5050bfca43e25bdb2129b1047df887b35ff08d64b165fc2ecf4c27d7825ea8df02f0bf9283ba66366a61de12245bc49116aef6b9600000090068f8311a118ca829a61ce3b443cd79567b12c93cb950c9222f039b112242dc3410f9cddbb555faa0ba3ec6f534690b824a5af190ffbe772162152a7cef290bc6e198ef04d40d6fc6962f3bbe5ff8cfd1bd6447d7ac68ebaadff48ce3296b68fb5c0ace2b42e8649d900ef4bdab307a30b00850ecde2e9ca4e903e32adf86c723393f658e5f45f5dc4d5f703720abd5e000000900d493c43f1f52ab642a486391dc8c7fe4d182e21519865e5cdbd0923bbfba771cb7e271e516167f0918e279b860073fa0c1eb283767d4e159591db2f23bf49d67f93534adac491be319ba9d480eabf0107e9d63c30be1c587c2419b4f3a1f941660b0893528246ba701a6a657daa4837272aa3f90a36648b1c756f19beb3e681b75310fb5e5d8abaa5739dc87d8b8a13000001bc0000009001ffb46e21b01f52ebdbdafc158f4361ef390c0da5615e39dbd15ced8be78d182733e22100b6b14d244a05a332645c7a53f67c814bfc1d8bb246e4f3030ea00d282590fd36a5e104bf2095907cb84cf2157eecc218512df1cabfef1d42218ef2f9ebdcf2ffb79029d050bbcc0103a6b607d7407b4b1902d34e0e53113b4d0c6d360b9231a1f3676ae6ecf09d2c2362e50000009024e6af36bbd2e462b9555cf380fc687a902fcb6d9ff57ee70f3b588132193cf8632b0b6c74b83d14528ebc550c2cd35b19e6ce58bf9993a499fff20d5c7f74e6f340eac36d50d100fb2b9218ba5627b72fb1579edeae78cf2f7c216dbd4358c08cd1a0a295e971c35d798e12c194a05a0f88e844ec1bc1b9452396b91cee2df848bcae4e0b36fbe51725c7b1ebd386e800000090476b0c2836ba8ad84b96a276a398824d54c077b7fbb306013b4f295eb903e467caf963c475ced241d1c32da91d0c0d583896d5e09f46be5959b84deb56a40271e19792b469a402559b816e3ff005dc8b1b57e5db0fb7828f0b17fa47ca36bf2232ec8bece5d8793c7fb0f4e13afc66ad2eded52dece2313bc725d4645105380e4af3309ecd66fef12a76bf674553190d000001bc00000090019a48c420d89ee53949b8e920fb200e169e70536b51adb238dc6f0e2f855073c070690a87908f41d85bf5f2967759fd4fd6a047788274fb75d2dc4484d207ecd19f30819ad59a851be85e5f48cdfa21071f7e89ec63f62dd9e814a8e75b069979e4a8c8f28c26a8e20b95c2008a8c482a565dc01359dfa71490371251c73efe5f2f99c088daaa10066222a71c4d42ef00000090a88a455036b68ab135640510b8e39395977e1ce20588bb0cc84037673b254e35e4792c9c8610337a9486878c6de8bb3528b218d34ea04ab333528e98dcf7f9360f50a1ce0ed54f9f86ade5f2037d7ee12d996dba01634e24558444b31a5ac15c6ea414016b37eb469c40322bb1c7334a087821b940a9de22ffb729f513bc66c20220ea52d8c0be1f5be1c08008dfe7e6000000904308c6b8eb3cc92e54434416aa52413594869ed558f1e02efca0e0ec84a48f04985e3e1d92b88252a680da4e857e4328be9350d3c874c451f5d1e5cf181f9f31bcb1f81656b9591cb07d4f3dbb0d22ea095066a9f9d506cc030ea7cf13c43c3d615d6abbef1708501532e4ee54d3e6d01ef61a694f3c83eb9701ada874b1b43bab8647d05284e030acf7dda064d5c14a000033a000000ce4000001280000009058a11dbbaa33dc29ada68385fb562e19c0248be7a5018a1b7913fe9301989e23e6d3d6159da6a7cfd14d379230c553f4a5fcbacafbc829ac0476e63aed34a81e3d5df51d2a443ce47f18002d149136690c5c1d7b2e5ba79717fc2c3e88f11397d1b5204ee11a5ae3ac773539e81f11a80a57e67bdf5c61472bfdd0d19f46e6a8d5b4bd09fd82938be8d3c67e5f8419b90000009061c505cc43b343b64c6ae23d2d3a7f178f86fa4de2616658fd7444b7ad6d36ca7529cada529342f81ca222781671e52f6da1177d40ad92eb10c1e151e2e70b0942113e8f9f1d80050e9f5968e7098f2d26c8bc433ce063dd5fe37502aaae904a361878356c3dba2440ec803d7efe09d72b3030657aa269d9b0a2fd32f98515cea47fb936be2dcfb05330fd28c27eabef0000012800000090aab3fd20f1c2b74c0a98e44ec5b6d5871a39d7bde3be7c9e305a1b78b5b69e34e96ad2e54bea7bb7b4f1d1fd330b98f057e3e051bac002b6e049d665b7ac853e44b6e85d3c47edeb7ab8e0694b414d572bd4f6798af4632bca1763303a0adbdcb67e80025861638b989bf69b3cdba19704d8b2640c678e6d77f322609349d859e1d079f9592f82d7366bfc8fa1e3d09d0000009051172e8e627c51843f09fbeb88d348b86f71074cb76bae310e8ae139e94693ec0b768827676738a93145102913069cb19fce5739f39cc708f054297a4a11e392f08278baee64ccc7e7ff0ee2767ab3e9010fd066e5e2f91cacb7242a93e0d0cd216f47e0aa346caadbc266d5f5a3e1f909778820fa4fcdd4afe02c26fc232b1b65bc33e8b6ba41012be34e65dc9bc1c50000012800000090dd000b6fc17ef00318e1d655346368f29e0f3f017da41b43666f6e3802396ea1258a57f78f0fd4dea34b23950c0b5330b80109242d9202f0c9ac050fd8a8b481e8edbf4a450dbfd9676cda4323eb146f0a5ac92f52bf8bcc5a0d6f70ec69af45c4fdca41d5591e751f6576c5dd6769930b9b31c156cb0136cc17ad3bf71cb45f1e0c4fcf2deb699303c1b63f330efdc200000090a1c653fc27ccdab5f3a45dfd0b36f5e6015f6c33d709d1bdbf443e656bbd8d359f90d6631eb6ce489d6c25cd8696259add18e66e255c6af271fb4a4eef70a7bd0f1a2ba52dd0c8a818abb615ba1ad868047a84dc3537a251faf4435c25cffe516de034cc5f8300654ddbcf2fb5c688991e988f9a2dda3167b3af648a94690998d49d3f616c337cbc64b5efd9615b44fb000001280000009015839b2123bdc678bad7544bba6605575db0b4f73bd769765ab09ba984346acdbdd66e1e99faa7f104cf9913257398a18bd539f603331e3e4dc10496588a2754e30c56a9baa525429e9d171f2477dbfd08e003a672a180bc75a44bcc92e41bfd9f0165769d969c641ad670cf0b92959708f4365b90cf35ef1039e27a6df2e8c40965e77393d71c5b98c3c54f4cf7c109000000900683e98ef9cd8964ec56a6ed2682b8e94a85f0304cce4072ed7a00a6983c056039ce8f68de1f51e7daf63948cc4b633798ea0cacf136f0b2f56beac7938e3b2dd58c98caa99d99f7f5252b50209aa5e806bb3faede475cf356767c3dfea7fd3c931067b6a0232e864d194371d9582c7009127ac2a0db5a8a0e66ad47a0cdd8006623f63740877f3481139ba407f47c200000012800000090c7d794b31669d4c78c5d77312e2785573675e7b6bab6a54853d89a9d1e08af640c405a361288910a105df8f8c8a8a4afc1cb34237a43c761f05d63af042d9cedac42ce82d683ace0b573e5088f67948a03082cd6b4a4ba24469e8a8932db27390f3c34691288a561e305240a1d71db922bce51b4cb6b3e574317c31c0d465c10a4236a2de41776bf2ca2c28e8c5e4ce7000000906083258d6acc362cb3461027e3300318c10bbf85acc9f3488e8209376f9d0d65c6f07e4a65d1850b76e3f9326d6a87b9ca8902deedf3a66646778d7dd8b0a67ca55aaa1f0ffe24a4f54c415ff1ec9ce62a7c4ff1514a7ea00bf463ec7d54ec35227c15dc583badb708ed54fe42e2155202b3534c0679ee1e923658d3f7995f4a17feac263b60690bcc5d2a2d78f8b1500000012800000090814726dd7bb5c4b5c44f1217c622441ea0ea38db88ef24d7b031c4b207ca5bbb41d1520c63e4f2c34a6f317a32c5ff1585c3b5454fbafb074b13583c90e3f864e668acfdd4fb66df11d6a71e940d41861e5626cd5f7ab0feec7e6cb4887cbc40043efb49b3e15ebd7d5a7a612960e6bb0f01c6e536c9148595d9a52d0d5ec59f2d17920d31d98808e0c1d4bfb7d8cded000000904e81077dd43d002a7e940609558b0e6f63f491162b0612d799c48dad8139f3f06e596dbe2211ba8d3623348a5288fd719e87e37261a8a4c46ccb50483e7fafedcb6f823fff16850e6689488975b49ea91a7742d6c3966ad8956755114b54dbcb8d1cc01534c20611b2b1606ac0892e252050a63ce91a7ab7f6da691306f7599819a877c8a5dcd214127bfba43750ca4d000001280000009043d8e87f88881efbdf9fe3d6d981a79321c69201d1a001f9227724d2385f94402fc30f40d6bb5f5abaccf737e096bec2aa8d808a991c3457529968e6c2d686a2314a187ceb581cc75b57b53e66c8c6843040c55f61701fada588be2f69340cf3443772c2b81327d61849e4f59830898f2ee53f7c840277a979de5db5df84ae770836b68606eeb89114c3b88ff56922ba000000906f8ea16cc4c640c6b5b86f7c2754cefd623cd7789bcbad86951053e0b2bc355a4a79ac7ea269aefc8044a72022a3904be1a842feee01570f0a0be83ac9b81f04a7471051b7a20a197ad893e3f5eec0cc2adca5d6f09e10809238c3de4363c2af7c8c0f54b30f085d04626af86ccaf2bc2d751f44645e44cacdf9eb4305354aadedd930f69a6557d6a2a60d347e37f1b20000012800000090b848f0e992cff482ca897f00e436b6bccc99e4c7ed3e0875c737ce9aaf7bcd96699ecc648ec9fcd724fea3de7943e5653a34cadef91645ee312e75a116d742a1d85de2d7738759acdba78c58650a3b1c094d522339cbc97d4d01620f8c9b5c8e6650072657643becd21e6232ccc23ac617b0ebcdce3425019be128107fa1ba7ce245c0c2f8507f8394c36d365044b6a8000000903f57984eecc6c4bb21f3c97e7d6398215743f197c2c0e5a79fd044f349a0f955a18b7274ba74625c7b5472a02c2c1f0f64d5aeb1b5fed2f64db05f04c0b35fc228160ea7f1d19b7a00365d5af17c2c270287411d24dbe078e4b21dc38be0cb9a8f6465ea8e5b200bbb3bdfa71bce73981d05895cf5b95c3f932df37d8edb80e4c69a49def556bc977f968ec3eae505ee0000012800000090550d964357f7687a3b6d9126f765b4ea42887294396a2741fd0c759ce74376c33d54f5e4f221f0bd939d3281edabe62df3afd9fd8b4ecff170126b26c35a505b4f5915e0bdc91a138dcece72d6667afc2250a85061d63741d069a5f3f91b5608a2385b1e8c6248761b83c5bb65e4229e000e324222e3e295e8305f19bb9d462bcb3a216c8c61de6110f0b1460abcf68500000090d49a1c992f5162de4a3ee22df00939240ee88891919c65994bec0c949de8142c24269151e4ff4eef30b0a287c8a3f5a74e4d7abb215f442c2b21bc223b37830ba243b155663e5b15a39cff7dd3bd025017e80b3fac8e23f64cf2fa43a33a8bdeaed4680c346d79bd2ff1e57df284a09d049003c94c6d25743ed0d5c4fd89a0052f71db7d4d78232f2b8cb92e3234900b00000128000000905be3d7ffc8c42048601533ac19ac6c8efc11c857eac4f58b4b87dce507507e317c4bb5c2c3f1220f8922ac42ebfc321ed7dc23980abffb3c8389aa43e97525b4f559fa4e125120b8cb7679965ecea3dd113303b002d5cb07ec622e8fa17e4fa66f9f8e6b18c24189913715c30e17f06115246c45f34962c0c71310bbbb90f10b08e061746dc165cb8ab5536a0e9b4b6100000090ea8bb348900aaab0c7f4b6cb5afc2b2e6564e55ec4a12465de7aaf174d19f9daadefc949bb6a61634f0c14c804255861e505c8592e96a3790a016081ce3f92c434ad76883f68a1198eb83f93beda89741e85a95cac80c9db098828b23727635780285d94da0caf2e27f37edd1db7fda00f82b9577108c38230e8ce249e51f6898f5324f8ae3a708a464a68658eed3fea0000012800000090d69d3884d9fa8db5d9a0aafdd14096fa5f5e07f0fc2f471eb7ceb7e63c843450b314280aa0b6f3f1bb394aa5253ff8f9e61dd0604ddc6dbccbb9b232b83ea6a921c60ee49a5461d9dbe51efacd0437fd1b6f66bc78f6c35e513bd1dc3250759f138ff632e86034ebe805b5e40cf97afd208ebf9aaa5b079ad436957ff87faf48effd6f806ad31992fd597607e549f95400000090077660556e07674cac5a4eef3bba09ac0121d6aa597c2bfaee86d5c1939f02e9a08ae03d6b3833d03246d9a60965316c6b7350c5025ac2ddde4b5f8a3909894868fceb23008fc3c6bb9607e22e3ccd93137f2d16027be26d718852a0fe5da7875d0aa4e1461c25b29167ba4fa8cdd8fc29fab5f6e55c0e67321b9ca5fa5cab0c4f915046a3a29c1b8d3d943a2a54493000000ce400000128000000901f717e16576c5c10bd8a10c564904c84b046fdca1cb8c83da1be094a9434f747ab51918108f291b53d01c7e48c6c50eda1057710ffaa765a18dfa1d3823575f1c4d21238d44a11f6e112394c7d3e8cfe139c215a641c10ac6ab6c4fef72b5905e62c334e733cfea1d33d2de4fdb79c110b9306b1497909d2f89047de58cf661f86bff436b80ce3e9036cb5593358587c00000090c61c80377b209c6da5a831b2c525a59a14a13cf6cd6e50341fb5f2c21a70da9162847b9f6502d7a51d345ce3098d47f6462273f834ed8a3d19996e1b0c436185e5b4815e43a784eae77bd52cc413fa5d0725d5ca61384ae1abb0462d86e1d71f4f929ac8985c83ca42b0c7350b569d70081e486464fb1503638a53bece15315d1e4132d647ea6cf7f140fb0a2c02dd340000012800000090c860d8efce5b43084983049738c12c4781991242f0e564fbb21d010bc036fd088ea2ba31b627d30279553c74c1c93e84e9abe6fa76c126353bc608ff2b66d76cd376891b6813191ad758ce5f1d5d706d1bc21253d7bc9037b6aba329c82182d5c2046bb20f69c483dd1425ab7386ffd72b1ca2d72a4d8ff1b1e1357d5a00f62023bf7dac6782d3563e5005bd375484280000009020766effa4d23acb703176dc21bd6d2bbda2418176cdf4d14998c6fa6085fbe58a4f7663892dc8863a45cd364ff06413185b383ccc8d4b8e5ab4e0186cc125e420de3fba775a2e7ce19a8fc581949522046c5371b418f9c60180329e473d4f1bebea901360501c27b58c010bfb47fc5a04e855250450a084c00e39b848fce60eb149869d44f5139c86ec19643e503f7f000001280000009068003d2e207fe1f4f0e2903c7f12a889cd97360cea337b73dcefdb3de788919e6975714dfaae6b68977271313cca2bcbecc7b036cf479b846fd7e3a528b8b90aac856589fbead14d91f63b8754c27df11d33331e2930c3a7cbc1bb32e6a349b853fd9d484c27f9d8dd02e390bb4ce2a20bcbabd186e62e3737fe3c9a51011a3a5920b47ed0fa7a563dffffb65dffd77000000090f609f0c7359598df6953914fe57a31e020154b26b4aa28204896e3528a95120740b072bee852be432f772d671bf9f92515192aee0dfc5a5856321c21233c8d7002a03833146cba22d0a4190a08d1a47208fb071c276dafa999649d42b307cf5d86771e8490e452bbefb1baefb573d3762c742280580d7376e9f4ea1d1486bd0c541e3fd46c0b1ee0d09888557bf6d6b00000012800000090e5136e1c5018bdeaebc8a84386131425b551ed9376acde8199f5c48ecc2cef6852185999d2485763ac1b1f53967a90c700bb9aee823b4c656b595edfffbbe1d6d353b587f3054b59a0c3dd5372eb3e370acda847a9d561e15226a15dc9d0f9a5255426e4228b79daf88d5e0e2548760623073a3a3e5177ae05f1197981f452c6190759a1bb1418adf90b13003b95b8210000009031fdb894dad83f81f86c6a179c564c34e8ecfd8b22afc9514c129b9dd58e0c0a9d758063fe7225cdf04075a8323cf1551ab0c2d5304703b053e150e5924eb9679f1279b3b5144a6d9273cd879023ca6806442973f54405ed8cbcf285c3978b5e6510d3cc3c3a08efe916086c23be14c116e0a7a779e86bb9b5f4669e3167ba095bc41b7402ba61931555be1506a892b600000128000000900ef862406629a92c15574c89727bbb7ac1cf45d987b905923dd49a9523cda2f6af918410c959402239a323d54ad02c049dc3ebbcaf8b75a7a6f56946a5bdb39c2762855a72a91df2f9ff850549940cf81333d8e138c20de0899285dc5051a3935384a2190ba786082974296f78b662c114b4f6915fd5a5617de5f05f922bfeb56ba57eedf2d97ec6241f0289dae7df0c0000009090484c79b1e0b61a92ec61ce4ef2c51f480b28b7e2aa87d1b2c1391fd8bd4a4b8aa64f562dc6d35882348fb1e10d76fee3cbf2b4cb1c4480f95a617e7b6347223aea6b44eb532a69e880d288eab281c914debf1a6d0dca7fc5393bae0e930d5425ec17d97b030c3d67fdb23f68ed602f15e95682c5a1eb2088dc7e82a8ddbb589175af124909e9a2d0d4b0769b9c881d0000012800000090d3284c9c659d8b0707267e8b10bd9789f2556c96260eb05798809991c94ab291e727e73ca8f4477b830202315f99233b99c68951eeeaa94899c60f9c6dac4b3f52f436d80945839b3ef755216a37b7aa156c509162c1f88541f78e4e56805ef8a29d564423b719ae3a0a50307ae7f1cb2aef790844732fd1936c27b0f541114364b54dcb737faa15a175652950032c5a0000009017f42abe31639bdc24dc7d9f212ca97c6443784871925f35b8f4a6fa43d4989898fd33ba5275c61e4a2d5bc55cdc335f96e4070bcb8ee5b4107b72886a3932ea89ee9e94d556566e0b0af98388ebfb380e029ebf47e9a00680b51363b3e3a864e1674fcab446cf3d1d01b628f9546b890618779bcdf57db5b499e46b6c00ef69ea56efdebf94e34d594407387111556f0000012800000090b132e62183ded1c670bc4bd895899cd38f06bad9b8bdd4cfd672e20c12c8afc5839ac380c6fb6e68fd0dfdf19cde8d342ca01b34d947b83c652ad8c69a70786fa828c9e9cf21006d30c0de6c72ac6dda0eaf6f4f39fdf3029652ed4eeefb4d8ce2bdb445bef9a446d0c39b88a124bcaa2f03b5fbbee509abacf7cb4b7422575e3682ca5634e496432abd48f5d19c512c000000907c87d96d0d9394843d242f5cc6c163c061fda2dae63e3f659ec53a6216fcfc27767fc426053b1267a6faf53ca8d95cc2efa72eed3f33e215cd499a5c2e75fd5e6931349ae1fc12300ce47e8cf1ab8e9a0629c6a0147758df2b1205878553869afa05c2236f039d0b8a97c0010bfb8827210fd1d69d077d5766a4f7744d48c05a6c70ea1cb5bc73df0701fb54f5e06dd30000012800000090e47e5d22930f7723ca2ed676f15853713f5305623ff0b379bdb579995e997c8cb75cc6d38b20265238ae77728f50ae219e3ea94e91d61cc8ce975cb2401ccf2651ae0d90b7469dc7cd4728befd0a6e860c629dc959061804615987f4fc99c2cd054f02fdb523bb61d90b20d4d551b9561f1ffaa28c3670545c1cb04b86d800066127c64abad06333b7f2910c6150988700000090bcee3f930412919544bcb8eea41b6d5cbc811e0f859575723836ab710f3dfa7dc2336b8c587f63cf549c7199e1274e83e4342bb679a4a310c2d8eee33807f49d1e1d9b71686e031dab782ed97714d981149d1abeb4cb01b550ddf6230d282aa12dde65a87a4db8c851158d88c961418411ed1479306291de7392a48fb0993e96564c9279aedfac6d7b0cfa4ae8c8d0c90000012800000090cc95253f57e1e449d799d9ae88f705d44fe26e4b5bebd668c618424ba0ea1768d44dc1a7b3cd7990561e78e69f056b8e19264ea97f446a7d4fb009616a5b255ecd6d191fda872fd8b9f674f442d890c92448f12b249e081cebdde579134dc7a48874aa64dffed480906145a7c1113cf3189daa1781874bee162ec5f16ee312bf976cab53d24c04016f4dac5b4480f9bc000000904776d02d6cee37a71ec4453e2952e311d9bf36f0348edd0d5b4a3730400525aee6ee5e68029348e088e7c175ecd3bbd673b6a812a7b696c6971d62beb98077b63a32e244fe8c3751bcb8d659c935b75229c50e1735b182fde7ebcc6c9fe25471c50f2f3f95842a6d37dda83bf61c766112c570fe6cc0680ad71d79bd43e90d22e9b1619077c717fb591a9754692996100000012800000090a26e57830438298dd3838384705cf18b35e3a15039693890c52c431e51448366de6cf815d9d04165e37eb33cdfc4273ef58c12398cdd2ce7b9908b2658413862300518f285306a63ab1e9d27442106b320cccdec81c096e7810ee198bc79085f9e92167998213d3217735b229da0034b15d50665a1f0f1928674ccaa57aad3e56aacfd7bbc6e8903f8d2fc56e58df8580000009091aaf6da56e346e06bc185ae1b4707ab118e17c44e837dc7898902e3513b75e47f08a4bbff9c4946fcdf9d9695958740f705c0dcf714dc386d694052e491009611ba3654c18c650bde72c4b0e9588aaa1ee848824c4047be7d47f194fb3ee26e9effc349936f6de348155eabf208f10822bab739e4ed52d9ecf749c133a17cf892b6ea460ab9d1c2d141353553ca7b420000012800000090542778661ad1536265596e9b9acb62c924daa7d39bc9708f161cc30a193d1c26e48de678cd62b96a7adba0eaff03892c3c769a5d3afbe98504326b0218355bce4d8097f73a443c30f674ea9d16b96e7826908b96d258a3db1ca1d7d3e1014acd9233deae60afe89e7d34ccc4be8a23ff1b6cadf34bd65a361fc9ea082dd9ee0fd3fc3295d7250cd31260362ef41b9bea00000090b71c893fc2dfd27bb72055bf2b5da1f092a8b28c7cef6d5c0daa9a535e038fcc9ec51b6c6e7f4b5aa65a8813d75fc186d99b6d2714cec0fd39b4dfe8d9063d314b39950a7fadcd993df3cf226955274216a0aa1a50d95862ace4750bacfdcf95d8a1b11fe38bcce3d27a8419643e3ffa278f5c4ae4597fe9ed6214c05b6559913b06b19737467b977d9c04e6354fde9d00000ce40000012800000090df814d37251e81d9529dbb210309f4a0b4fe9917300bc980db42af0eebd1b708f3911ce74a3b2cd0fcf258894d04382c48d47268ce668a558f54459347c0a0c1bf0359727d927f928010952c0d611cca1cdd23b76143e5305a250be2f3f54929a3a18759ff59046597a6c9d877076fe10aeb57b23c9c454b9bc26ad78796618fb619bc361f38aefc899eea4a187cce6e00000090a8a060ef4b5f61dd86ebc67dec1726c3096d06d6879a2474e055de985cdb00ed0c0af11506162f21d39199bcd886bc17a23df10142d4575c7820d889de0ef15580ad003192010d261c38646a1344368f2a9dff1ae5b23072b70871cd2b7588fd65bd8bd44ed7719eec4e088334e5986021484946bd90786fd4fc39dc52fa86ed7886d001d3ee64206f0c5de7e517dd560000012800000090093af15fdd24dc60a17c63743a008945caf76d4fecfb8b973db7864fa2d23bb18a75df0d7d0c27bb3e8241363f4c2b252264173e1c6478617f1513528342001d4cdc6bafb17ab9799d0aef54910fbf3f07dc8e8f7fbb24b2422fbdf5dcff71a3104d34ab492e30ae6e229fbe3b2a18f510e28ce2d665f568c14f67b707526eb6c5423057221c87e57364d4f929d92a3800000090ea6bcb48020f5d194f522ec07220f2859e160a4def93c50d9b2e3838d744402cc902a12fb836458aa3724d521fbdab0f7ce5f2cad792baa06d37d9b880a3c3a6f39b760c0e6386b2ece755b0b70709af2e6fd20503219361878ca733d0846970bc36fd66c2d65a115d05c9be0f1895a5068551f353629807bb187b328dcb93610d1676470ff6692f37aa2c7a1aa78277000001280000009045fd3ab0ac586a55781d3ad84b47bdd448d4312c21b58756bd6e866d6a3aa459c8c08b3751a69d4ed7dad3494b61cfd4c40af77d69d0f4b74480b1674d47b5c052b0c823d8c6e270dc548de8994cba06135c22ec535e8fd699da3dbb013c8104c07b036013b9733ec7999a99908fb6af14c649a5f2fdef651151524f247f4cc868ff54c895683c6c06af0ecc5352f0cd000000900221be3ec7a77ac0c02a89f5f6afc7ce0d400af4c7f3c9e8f333db0f814453bfbc72cdbcb802cc6dd87b7d2402a70f0750355f7dc44bb5638a71b8484bebc29d26855821bf0bd5752717205f44b6083220e091cd96d0aae102d13623770b3694efcada6486d31e271be9f6ed184dc138007521e86396c8167bf1e0769292d6eb44205a4e7b8d7b14882975f8c852b8ac0000012800000090f621215cbf07f263742ddf8bcf10c81af2a3ba85c2b830458573aa064b63146cc865e2bfa6ece775548f16918f922ed4267fa6c5875fccc25077f561f75b6da876e4dbfbd32a4b10af407180a73c1d04081ca4c9efdd437e2db28e2d4611bcbac333710462cbea53c2481b75bb04e95128499f0e8799df086708db8234819064cf51c67888896ba7bc0d0e89f763439400000090293e6dbdfc2c738098231b4f1c8f0d739be756ad24dfc1bab7245ce3cab2cee0293f34adfc6b2b8545cc225caaf19ed3298dc00bedae7dc5de5516804661b960984916f9b978e6700b002dcb201ccf471e22f3ff617c4874ef51397c30dde547818a89b211f9dbdf3dfd8fd0d8d3a21a09da1a1161f46869d65008c3a039637cfe18c39a19df1374483b42b6cf9767b80000012800000090985d646180b524e4b49ad555323bedfc082613c7d6979162b43fc3c7719c15c801685bcebc7e855b18aaa215f014fb290812dbe0c720ce24fdbd0f4b4953c8a9bc8bf4c2f9ca4bcf73afdbb7430fe659282277f19c72e657c3dfeb0492e55caa22ab062f983eb417eb74fa6e518e6a30150854eb71d0a8aa344b1de99ed6afefdadcca0b97bc7c1c2a9d41997fe83530000000905d1e0305028278eef7df7c8df1d39a9945c64a925cdac4b7247a7724175b3b1cee30168ca5234b6efc50c2d23e7877924e2497d6b7e4ed826686e1a8093aec5ab08ad36cd2e04ef235cf7a30a08ef19c23fb2add0dd306fa4ae8881e91ea81b7db90bd32e28829b96075805fa1cab6900d9e963ff5232efc60f516bd73a5158cfa7a29a9bb07ba3a6c20be2e8315c5c0000001280000009007faeb8e333cced938005055ae4a104329ae809af5e6dcf92c13d351487c87d3915868c20b87e59cbaa6eccb9174d56f1ad6a4922be01d2671f5cc1d1d160d9c03611572b49bde324538fb7dd8e24687297e6490ee16de68a15f0f3db95955d8bf104cdbc6c90c5c76aa1ce1b27109552f0b09ed4e193f5cb47b3f68c5b9aad2e59b3995420e3bc6540d2fcf26d30af700000090ddf990e84cca0077f0d8c4ec255c2eab76c668e84cf5ae5ce39e06aa31ea6bebafea2fc22bbdbd2f227327cdaac2118731b620882606e69069db26b1773eb2d4f245227c34ee85ce7b87f19bc235e58723aac9fda228bf5260b1e9d8c64b1e4ac89f788b294e2a800ec83824e152379a1408b5e850966e49836a51af8ce92c211b3ea936557152c88fdcc16f3e398dbf000001280000009010002d75ba1ff4554ad77a06af2fac51123649ec050a90597c7bbf478b34983b6f7b15944b5efdcd8d54e4ebcf40a56026345a578bb526cff840fd54f335c002673717bc46c72a97dcd059a2d611985e1a19a8367b402f52524385a5ec56e6207a9e7c4a10e84fac572d70e9b3cd1dbb1fa7fe1e39bb6415bbaed8f6303a99a23ec054507ad79c9bc29d0dfc0bd9447b00000090f7ccb463266a4ff7c273e9c0a136956b7c6c429e2ab773a18576ba75a23b8f8f543493d5adc9e49ad9d30b0d3f23932d60020d2f2847ee8357fbc6533eedc9be6b55d81d87500628aa400d331de9d83d2a3fda76c9bdfc820d094625a4596e02db3e604da144fb834934a4e2d93d133e0d404301278631650b7b68bed665cb50e03f214684d822e14a300a71457778e5000001280000009080b451e6c9a2c420625c89eecda47b21b03a949e5315c38d9b1c39e59f5009deade992bfd878ae2397bac5d4bf2d0284aa9f860a70e6f41ac3b1dc5c0349c2b5585f550a5fe67b70b817657c92579da82ab5ea0d7f61481f802c65bd54b3c87f52dae10c80db0c79d9fa1cf9b45b7da51fce4e7fc0463b489c0512b1165f3f42c75ce5eb050843318907374e280d31fa00000090b217fdc174010515389583c88d932d0f285943bd9c3357335e36caee6b9730f391e6f34c191f74f64d501bbc72d370e08cf50504dc2ccef5073ce027c9ab057da6338d64db7891f135a8033c395e9e53206e12f5ebe3796201c8c5fb6d0953641c5a2e89efd038aebcf9a0c4f50d1efe2464346e02ae75c843c6b11a1cd84a1b996417e45d4b9632e9dbff6b8db094650000012800000090e93cdb4a85f24cd964586f1e9702d831f03a265294325040cb2d5510d18d4a5186074cba2d5a70a0a862590867842de3baa87c6ca244fa5e18445f78d21fdc092b3c65a21f34c04f69e10c201013efa12b63b34d3a5f84f6ed2ceedc99608fbc6877950e61e5c971e5d0d70a8a6c65440c083a8218ba4687f3c1c60a94a5a5fd312276373e45a4685282a59c347a5cb200000090e2b0dc32f29f76e093c78c03bd76fa41ff202875dfd2d4dc7b699c2bc3faf4bf2456d6c69ecb03c27f23e8a08f6234f58aa364dc86f829db03509e4076a1f2aa9937c64cec202f020616b3afb1808acb154ae4da1dba87bb1f7ef9ec18b4f81c93699422fbf92201ebb8d6a9051da20d09fd65e6a5dd210d275d2ce5812a6938fe864ddb6b2f2eda2f97eccc7e270cb90000012800000090dc850cdd754d5562d2cd0a79a93dbeaa8073e8c59be2570e7baa1fb1c4708bf5d247b4c48f47da5504112331264d08219e885e7fa72af442e567b79cb2e47bb074f50ff258252ed089b52d73999ad1c6016079807c678fde5d22a5882e20e8444b3bdac43c815022ad11918c83b982cc23b269e045508b459b0147df499459b9cf406fc80a366fe92eb48fae1ef2f020000000901d1f47c4c1cb18a284d574eb6dac526160fce963527d2762b13f81e4410969a6fddcc1f847fc435907806060857bddd7798b4b04001edcfab63ccb41ec96fa3ba28f8ecae989f85a444f4dbbc226a69a24c309b920b961ba352e9ebe153e51137ff5afe88a0c6e1b1e1044d51904c31305b4180fcf006cdeae8184dc4b5e2e0f1fbfeefb57d3309d45ee8d924f0438e50000012800000090c00446a7e4204241776d5234a2bea9ed935e437d9ff8d31cff62c072c294cd709336c171c4d2008a18633174e321640679b645b4cabafc5702f8936605d9825eddd72a4aa53b6899d22642e58486ead90e7f35b24a3beaa472dd3f8be3c8b65c001b10252457d252120c8273f645f52c2a2cd4485def46cf3368fb5e4703f130cf1b6ede3df24c3cba58ccfbfb5c1d710000009022a8ef23801ff2e837f3e5db3361c6897db0cebaebfff4bc19fe2de194ae245718259c23897359d07e8594d4086da22c0969b3cdafdfb20b6df113841c3e2520b776025831c68c0a2a1dd4bc581df7631d6a743a468d357ea936317efca7f6ed04c1be2dc7e6a1b829e3c9fbb423224c0813db64b88a639a1e2bb0d28c118f29d9aea07e5f7eb68c0e6c044d92e6c5a200000ce40000012800000090e2bc3a48af462e994976367fde39209c31d47fe41a4f37fb644f56fe6421a805cdc7cab167dda0238e4b2d6fe9ff7190d5b00977da7d3bbff603adc831d7b5c2991bf9b8fa11f7b0cea097d0f2f4437401198ba8112a0bd4bc1f536d844648050559ba594e2221f41ff991b831afc04c16d1e2987434b415a200227d598641301debed676bc98f6fac8dd6b00ec3d5ef0000009090adc3bdfc913e1e8f6fdd10fd6e3b62f9a64220c762fa0e0074c2b6fc4f61c91fded0e9f550dd1fba9ae651f1ac0450a72f1b41be401fbf02f969d1a3358514748f32d4093cc1c301db661942c58b49274dba34e513a07634442e055dc224fdd4954bc4f5ec39b56f263ef83ce6d81105dfbcf76b28810a08b23399c067c0730cd643ca0a71c14dfd7fe6a7ca4c7cb80000012800000090cbd5b3d8671123dc24f039239bcb164750d2b94f9e10b011bfad3231ea137b6ca803f15a5adddac120f372364e74f8fe2503f3ae41e6e159d22e02e86eff74db1af028b4d491ab8dd8ef1b712c9454291d8e74b1faa3a9a3a2897661d52b5d38c879f0ee6c25d9a4cbb334b344e0c4d724c3310ad42fc29094d89c434ea332abc10a1b59469f892b4fd60b01bdb4fa9200000090183be88ab1402a8105ce14826e74cfd26ac966dd9c06b2fa1cdf0f92d2837637cb37826e4c05c5207220d0eff1a8efc95d161de351749daffdffc974d40d50412fe80440ad1f03fbeeca8b4169086efd1710aedff6957061c9a814b7c86fab671102290d3e28786784e925f5fec867cf1c64cb4e4d2d7e9719ee7ef0ed4b1cc0e4de0a4d6372de36162ba9cd661436370000012800000090492b1ab03ad6554322c047f02efbdd83ed97509f1820395508471b0df7ed58d463e3b73735051b72d82c513eea1b98d4d51cb7b8ca4fe23651f3f5155f18e4555c093b1eb6c2a98171b714c6601f440224e4f0a5e1cfe404f51d314024cf5c4b1424a7dff3597204720623c0d03b83552ff2b3f159e3764f400465a9637d4d5ecd540df7f378b00858927e1df1c029be000000901943fd9faf476880c444febbdb07802a24dd21a945512a7f86560d1acaf786801b297ee3fdad4d14db06afad27c5181aa8c3ed4db540f4f819866471204e2a7efb9903839e71df18c998f3bba2a5c6e504f43bc05c038d0e7301a6f57bb73e9b3d346dd24bfad2cd28024666d7aea47d0a6edac531b795820968adb825f07da1aa3da448e6bf57880f81d830698f1f0d00000128000000901f0b90e626f010f5f9b965348175df86cad98ee79276e02f5a8ab74d96ddfdb75f1da24e300f6252ddfc53c30b7d57c58977a242c8bef37ecf77e7afc8527172780ff4b8bc2e1b52696dd1c6b79faa1e243e88c996d3cf7306f4e3f0062a6c5ddf4d1e0efbc34adb2bea97f1904944270fc64104193d0210726362ad9fe624bd2953651ca3ba77247afbc581b35df64800000090e7ce6a2a5ec7cc4b32e12ba70c1c491206a85c6bf637dbf7ea3e9ef5c52fec6b421e71eb44fc46950137dc8f780d2bec886dc91616afdd2c700c24832cd53fc692a92a3aec923e85eb8a1421877347cf1678ef20365b09bee2f356edeb6066db70c6eaccf4e1afc436136c5acc6ef42f0a64e5fa94a66f3c7ee8fb08debbe7950b0d0307dbc49ebdd86d5295f53bd58d00000128000000905eed1effd49b3c182fd72fc371f22360fafbc935c94329bbea121b76c3f47c10d7a24852dffbece757558a2a906fccf152698cdcff1368c5ffb433e3b9a16135ca0058af88ca48bdcdc00b6e7dd0532511783b35ee2e85bcb2b5c2a2a0178b8bed4b7dce99d2ed823e9ce610b19b8cc62684e4c14c6085508b7e2cb240fa6efd4ea0e755a720a019e47a1b9ead8ebef0000000909861d898f71affefaa9c24629753b3e6988263567d48672571bc46f69b65f35e0371c72a53ab087fede69bcc9bff623055ee1426ab173447a6cf84ec2fdbc151daa772f1c24de647d606fe99bacff8f000b17c743ed65e5abf0d5f7eba1b53adf37c43c6dcbfa399fea776af3842eb2620fe73dd99bde3c733b7966b9da121511e389985f51368d69dddb05d8ca4a45b00000128000000904f4079d8a2b1fef6ab268d17e10f748d360790081658a4dfaf52931efffe7c2f041923dd29a8a0b5ae7c0a16b1915d68921698d3c99351d8325f4da2422360df766a7bf49fab1b29b7d873951f21d3840f521987c97272fc20d483ed63f6a461273ce23bc3aa6dab2976243655248c0b2923163e782dfb8bf27c3171be7f86da6231fb2992e539dd1f4743558826d2e100000090a98c0b3206e0ef65c45ed93069c78aad33e45aa2bfb121a7e5a45313cb5758c6cd78dc20411fab5ce6cfb52413a4593a8afd3b8198cb753cde0a4d96d3839b66fafc974611b39fb9aa04a640c4e251800fa3ceac5567b9421a19ed92da33ec3a9b145ed09ed058837cb8d51da7bbc879052258bdaa60e493eea98e24bbb556d300eb0206ad4a2b336f3ecb818534533000000128000000908aa7ae30702c869425bde7798ed24dadb39c459ff83ea139d9f6ac6e06a984475cf323d7d45e95c240e346e393174c3d5e91ed9a4f05f497bfbb68a6168fb83d82903c1a5bd9513253a2049abd087d5a297db4b7a90452627a890a7fb5b914af3733951cd13229e60c4c72b7f8be532021cc9049bbe19b38a40ca1f5c1921060814928dc8383345821e4fe686c3d30b6000000904811797f420982fbafd20c05b67a7ba97ad472240e24b1539f533fd790c8b31559ba3b593391ce329096c708d20cd8104e7c834b646ce963c5f634cc4c97b85d2a923fca24d6282c01eb098c33c81f8829c69b93ba28f54c7d0bf2bdb9fa695ad3e2fce86f02adb1aa053ac1848fc01e04776495af37c4115237a3fb0e833cfb47032ba8ab4686a7796d23d5e36f9fba000001280000009026dd418e127c509cdb836ab92258ff19b76bd5e36ff73901eb16b3a6bd3a84df61b3657d3f317f95852e0d723ccf96f19b7fc91655a6a597755164266cf8b99becda4a71d594a7469349cc0bc34e3f10062716c1c60b10662c4cfe78d21248409ec50eebca288509c0a0dbf5f320a21b28dec7530b02753f920944d3efdc7d7e59c0a4204ca900e22aa59e04c5c5a6f20000009097ec2bfba0e6dc44f0c7fde2a6b8c08da93af4b60313d597ea441cac361dae66799037a1fc9328b37850cd730a6b2895d111bc7a80c7d94ed8dbed022736042e579a8a0c96e40abb5f61abb1536718f51d87b83c4d69ab74c99be50aee9ceb67d2951a16b265e9356e264c200dcfbf2000967f66bf7f490bba839a2f4a73fd5c15677f6cf311b65672e708eaa2503d5400000128000000903d05177da328f50a6b1735b7f3c7ee275b145e2effcc456a0c5a2b6b85366acdaebac9801eb0510cd1823bd01139ab5d3db0f38d2cec64e653a14a0c5461c5e2a904f640d4ca3bafbb0b0f4be9739cf80bffbee46f92294abe5558926bab3118826687aac02ffbf1d68424fe9af65b2916ed9211d0d3d2d6917c658545188720424ac98fc35342de7cd07429def9422900000090e1d45e36164240913087d829bb92535b93c0ffd41386ba5c3d276dd57611f6e68b3244f1cf5f75764992c8c456850c135e3aed4486dc85eddafd4921cccfaacda04c274181cebe2ed0e580d1350d8a3e1b4777640a7126168ce8772f7ad20463a3d5b9c05dad1c26afd2e9cf762c3e6304f289ac1d13c4edcdae63f8277af15e80590889a55337e81b118ffcdd9982180000012800000090f0f12e1fa0945b93a198c6631fb878417b46cb176a2bbf347eea9ef4d1729d28e940a208f2a1132325f403a3d4661da1e1d0b2544ac39ab2c6023e5fcb2ca1737e0a545703bfa9c4ef3f90dae7cb42151e25fcbd5964fdb9002517105469c59b5482528fcee58c786344c30cde4caee9040346581b540d44d1287d963b5889a0066dcba50ad62940317292d9794cfd530000009078dc1348b125a76fee5aebd336661e4d0214c5cbd94fcddd08c1526f67da9b789cf7bb879f6296647396073b4036b462e0d66e6b45c3e602494b8c99024ba54090fa7d6cf9726b49b6d22aa7bee4cc33096bdd8e3015a9561d352e009dde3a772739291878b2b17e96ee8c415bbb6cad1ceea85f24360cafe7a28cbe08d8074605a72facccd92bb307fcc855d413f8ec00000128000000908d2a8d2e41f9d850def8241eeb22d8fe08c2555c618b155b75d95e96bdb0b0b377cd0ca2376eee3307cb50328931faf5767ff80eddb2ce21f184b7942aa019bd8e60a881715e6c382433e77761296bbd025dbf23d1b00f9d25dbcfda67f9b4ae86a387fb7bdf3c5241e0b78c839b90322a7e03779942b0dbdf47c34b13c7d671358b008a96827396b695f0ac3e32894000000090df5748605961a0e6be2ff2e92b6280110752d8a76a8ec69bf9b7f4d916ca4a5dfede149be73b24e6426e993ded9af3dae9ca39f2e43363dbd1b36a9ec44d2b7736a4936a16096c151be8f8a3f907ac33116b667bba9a8297de6a994b2b7e34ff897749999724668a3753d5caa80d571b0bedc76486de3d07973636e9ee1897e7e62f94352d22ec90a956a16db6d36027", - "calldataHash": "0xf4525fad966caa2a5a1c5c4ca03520c2652aa2835f67fcad64b97e05cfb2870e", + "archive": "0x238d559a05a8fef1cbfcc8b7e59836b7f467fa8e93d433bfce8efb75a6444448", + "body": "0x000001000000000000000000000000000000000000000000000000000000000000000240000000000000000000000000000000000000000000000000000000000000024100000000000000000000000000000000000000000000000000000000000002420000000000000000000000000000000000000000000000000000000000000243000000000000000000000000000000000000000000000000000000000000024400000000000000000000000000000000000000000000000000000000000002450000000000000000000000000000000000000000000000000000000000000246000000000000000000000000000000000000000000000000000000000000024700000000000000000000000000000000000000000000000000000000000002480000000000000000000000000000000000000000000000000000000000000249000000000000000000000000000000000000000000000000000000000000024a000000000000000000000000000000000000000000000000000000000000024b000000000000000000000000000000000000000000000000000000000000024c000000000000000000000000000000000000000000000000000000000000024d000000000000000000000000000000000000000000000000000000000000024e000000000000000000000000000000000000000000000000000000000000024f0000000000000000000000000000000000000000000000000000000000000250000000000000000000000000000000000000000000000000000000000000025100000000000000000000000000000000000000000000000000000000000002520000000000000000000000000000000000000000000000000000000000000253000000000000000000000000000000000000000000000000000000000000025400000000000000000000000000000000000000000000000000000000000002550000000000000000000000000000000000000000000000000000000000000256000000000000000000000000000000000000000000000000000000000000025700000000000000000000000000000000000000000000000000000000000002580000000000000000000000000000000000000000000000000000000000000259000000000000000000000000000000000000000000000000000000000000025a000000000000000000000000000000000000000000000000000000000000025b000000000000000000000000000000000000000000000000000000000000025c000000000000000000000000000000000000000000000000000000000000025d000000000000000000000000000000000000000000000000000000000000025e000000000000000000000000000000000000000000000000000000000000025f0000000000000000000000000000000000000000000000000000000000000260000000000000000000000000000000000000000000000000000000000000026100000000000000000000000000000000000000000000000000000000000002620000000000000000000000000000000000000000000000000000000000000263000000000000000000000000000000000000000000000000000000000000026400000000000000000000000000000000000000000000000000000000000002650000000000000000000000000000000000000000000000000000000000000266000000000000000000000000000000000000000000000000000000000000026700000000000000000000000000000000000000000000000000000000000002680000000000000000000000000000000000000000000000000000000000000269000000000000000000000000000000000000000000000000000000000000026a000000000000000000000000000000000000000000000000000000000000026b000000000000000000000000000000000000000000000000000000000000026c000000000000000000000000000000000000000000000000000000000000026d000000000000000000000000000000000000000000000000000000000000026e000000000000000000000000000000000000000000000000000000000000026f0000000000000000000000000000000000000000000000000000000000000270000000000000000000000000000000000000000000000000000000000000027100000000000000000000000000000000000000000000000000000000000002720000000000000000000000000000000000000000000000000000000000000273000000000000000000000000000000000000000000000000000000000000027400000000000000000000000000000000000000000000000000000000000002750000000000000000000000000000000000000000000000000000000000000276000000000000000000000000000000000000000000000000000000000000027700000000000000000000000000000000000000000000000000000000000002780000000000000000000000000000000000000000000000000000000000000279000000000000000000000000000000000000000000000000000000000000027a000000000000000000000000000000000000000000000000000000000000027b000000000000000000000000000000000000000000000000000000000000027c000000000000000000000000000000000000000000000000000000000000027d000000000000000000000000000000000000000000000000000000000000027e000000000000000000000000000000000000000000000000000000000000027f0000000000000000000000000000000000000000000000000000000000000280000000000000000000000000000000000000000000000000000000000000028100000000000000000000000000000000000000000000000000000000000002820000000000000000000000000000000000000000000000000000000000000283000000000000000000000000000000000000000000000000000000000000028400000000000000000000000000000000000000000000000000000000000002850000000000000000000000000000000000000000000000000000000000000286000000000000000000000000000000000000000000000000000000000000028700000000000000000000000000000000000000000000000000000000000002880000000000000000000000000000000000000000000000000000000000000289000000000000000000000000000000000000000000000000000000000000028a000000000000000000000000000000000000000000000000000000000000028b000000000000000000000000000000000000000000000000000000000000028c000000000000000000000000000000000000000000000000000000000000028d000000000000000000000000000000000000000000000000000000000000028e000000000000000000000000000000000000000000000000000000000000028f0000000000000000000000000000000000000000000000000000000000000290000000000000000000000000000000000000000000000000000000000000029100000000000000000000000000000000000000000000000000000000000002920000000000000000000000000000000000000000000000000000000000000293000000000000000000000000000000000000000000000000000000000000029400000000000000000000000000000000000000000000000000000000000002950000000000000000000000000000000000000000000000000000000000000296000000000000000000000000000000000000000000000000000000000000029700000000000000000000000000000000000000000000000000000000000002980000000000000000000000000000000000000000000000000000000000000299000000000000000000000000000000000000000000000000000000000000029a000000000000000000000000000000000000000000000000000000000000029b000000000000000000000000000000000000000000000000000000000000029c000000000000000000000000000000000000000000000000000000000000029d000000000000000000000000000000000000000000000000000000000000029e000000000000000000000000000000000000000000000000000000000000029f00000000000000000000000000000000000000000000000000000000000002a000000000000000000000000000000000000000000000000000000000000002a100000000000000000000000000000000000000000000000000000000000002a200000000000000000000000000000000000000000000000000000000000002a300000000000000000000000000000000000000000000000000000000000002a400000000000000000000000000000000000000000000000000000000000002a500000000000000000000000000000000000000000000000000000000000002a600000000000000000000000000000000000000000000000000000000000002a700000000000000000000000000000000000000000000000000000000000002a800000000000000000000000000000000000000000000000000000000000002a900000000000000000000000000000000000000000000000000000000000002aa00000000000000000000000000000000000000000000000000000000000002ab00000000000000000000000000000000000000000000000000000000000002ac00000000000000000000000000000000000000000000000000000000000002ad00000000000000000000000000000000000000000000000000000000000002ae00000000000000000000000000000000000000000000000000000000000002af00000000000000000000000000000000000000000000000000000000000002b000000000000000000000000000000000000000000000000000000000000002b100000000000000000000000000000000000000000000000000000000000002b200000000000000000000000000000000000000000000000000000000000002b300000000000000000000000000000000000000000000000000000000000002b400000000000000000000000000000000000000000000000000000000000002b500000000000000000000000000000000000000000000000000000000000002b600000000000000000000000000000000000000000000000000000000000002b700000000000000000000000000000000000000000000000000000000000002b800000000000000000000000000000000000000000000000000000000000002b900000000000000000000000000000000000000000000000000000000000002ba00000000000000000000000000000000000000000000000000000000000002bb00000000000000000000000000000000000000000000000000000000000002bc00000000000000000000000000000000000000000000000000000000000002bd00000000000000000000000000000000000000000000000000000000000002be00000000000000000000000000000000000000000000000000000000000002bf00000000000000000000000000000000000000000000000000000000000002c000000000000000000000000000000000000000000000000000000000000002c100000000000000000000000000000000000000000000000000000000000002c200000000000000000000000000000000000000000000000000000000000002c300000000000000000000000000000000000000000000000000000000000002c400000000000000000000000000000000000000000000000000000000000002c500000000000000000000000000000000000000000000000000000000000002c600000000000000000000000000000000000000000000000000000000000002c700000000000000000000000000000000000000000000000000000000000002c800000000000000000000000000000000000000000000000000000000000002c900000000000000000000000000000000000000000000000000000000000002ca00000000000000000000000000000000000000000000000000000000000002cb00000000000000000000000000000000000000000000000000000000000002cc00000000000000000000000000000000000000000000000000000000000002cd00000000000000000000000000000000000000000000000000000000000002ce00000000000000000000000000000000000000000000000000000000000002cf00000000000000000000000000000000000000000000000000000000000002d000000000000000000000000000000000000000000000000000000000000002d100000000000000000000000000000000000000000000000000000000000002d200000000000000000000000000000000000000000000000000000000000002d300000000000000000000000000000000000000000000000000000000000002d400000000000000000000000000000000000000000000000000000000000002d500000000000000000000000000000000000000000000000000000000000002d600000000000000000000000000000000000000000000000000000000000002d700000000000000000000000000000000000000000000000000000000000002d800000000000000000000000000000000000000000000000000000000000002d900000000000000000000000000000000000000000000000000000000000002da00000000000000000000000000000000000000000000000000000000000002db00000000000000000000000000000000000000000000000000000000000002dc00000000000000000000000000000000000000000000000000000000000002dd00000000000000000000000000000000000000000000000000000000000002de00000000000000000000000000000000000000000000000000000000000002df00000000000000000000000000000000000000000000000000000000000002e000000000000000000000000000000000000000000000000000000000000002e100000000000000000000000000000000000000000000000000000000000002e200000000000000000000000000000000000000000000000000000000000002e300000000000000000000000000000000000000000000000000000000000002e400000000000000000000000000000000000000000000000000000000000002e500000000000000000000000000000000000000000000000000000000000002e600000000000000000000000000000000000000000000000000000000000002e700000000000000000000000000000000000000000000000000000000000002e800000000000000000000000000000000000000000000000000000000000002e900000000000000000000000000000000000000000000000000000000000002ea00000000000000000000000000000000000000000000000000000000000002eb00000000000000000000000000000000000000000000000000000000000002ec00000000000000000000000000000000000000000000000000000000000002ed00000000000000000000000000000000000000000000000000000000000002ee00000000000000000000000000000000000000000000000000000000000002ef00000000000000000000000000000000000000000000000000000000000002f000000000000000000000000000000000000000000000000000000000000002f100000000000000000000000000000000000000000000000000000000000002f200000000000000000000000000000000000000000000000000000000000002f300000000000000000000000000000000000000000000000000000000000002f400000000000000000000000000000000000000000000000000000000000002f500000000000000000000000000000000000000000000000000000000000002f600000000000000000000000000000000000000000000000000000000000002f700000000000000000000000000000000000000000000000000000000000002f800000000000000000000000000000000000000000000000000000000000002f900000000000000000000000000000000000000000000000000000000000002fa00000000000000000000000000000000000000000000000000000000000002fb00000000000000000000000000000000000000000000000000000000000002fc00000000000000000000000000000000000000000000000000000000000002fd00000000000000000000000000000000000000000000000000000000000002fe00000000000000000000000000000000000000000000000000000000000002ff0000000000000000000000000000000000000000000000000000000000000300000000000000000000000000000000000000000000000000000000000000030100000000000000000000000000000000000000000000000000000000000003020000000000000000000000000000000000000000000000000000000000000303000000000000000000000000000000000000000000000000000000000000030400000000000000000000000000000000000000000000000000000000000003050000000000000000000000000000000000000000000000000000000000000306000000000000000000000000000000000000000000000000000000000000030700000000000000000000000000000000000000000000000000000000000003080000000000000000000000000000000000000000000000000000000000000309000000000000000000000000000000000000000000000000000000000000030a000000000000000000000000000000000000000000000000000000000000030b000000000000000000000000000000000000000000000000000000000000030c000000000000000000000000000000000000000000000000000000000000030d000000000000000000000000000000000000000000000000000000000000030e000000000000000000000000000000000000000000000000000000000000030f0000000000000000000000000000000000000000000000000000000000000310000000000000000000000000000000000000000000000000000000000000031100000000000000000000000000000000000000000000000000000000000003120000000000000000000000000000000000000000000000000000000000000313000000000000000000000000000000000000000000000000000000000000031400000000000000000000000000000000000000000000000000000000000003150000000000000000000000000000000000000000000000000000000000000316000000000000000000000000000000000000000000000000000000000000031700000000000000000000000000000000000000000000000000000000000003180000000000000000000000000000000000000000000000000000000000000319000000000000000000000000000000000000000000000000000000000000031a000000000000000000000000000000000000000000000000000000000000031b000000000000000000000000000000000000000000000000000000000000031c000000000000000000000000000000000000000000000000000000000000031d000000000000000000000000000000000000000000000000000000000000031e000000000000000000000000000000000000000000000000000000000000031f0000000000000000000000000000000000000000000000000000000000000320000000000000000000000000000000000000000000000000000000000000032100000000000000000000000000000000000000000000000000000000000003220000000000000000000000000000000000000000000000000000000000000323000000000000000000000000000000000000000000000000000000000000032400000000000000000000000000000000000000000000000000000000000003250000000000000000000000000000000000000000000000000000000000000326000000000000000000000000000000000000000000000000000000000000032700000000000000000000000000000000000000000000000000000000000003280000000000000000000000000000000000000000000000000000000000000329000000000000000000000000000000000000000000000000000000000000032a000000000000000000000000000000000000000000000000000000000000032b000000000000000000000000000000000000000000000000000000000000032c000000000000000000000000000000000000000000000000000000000000032d000000000000000000000000000000000000000000000000000000000000032e000000000000000000000000000000000000000000000000000000000000032f0000000000000000000000000000000000000000000000000000000000000330000000000000000000000000000000000000000000000000000000000000033100000000000000000000000000000000000000000000000000000000000003320000000000000000000000000000000000000000000000000000000000000333000000000000000000000000000000000000000000000000000000000000033400000000000000000000000000000000000000000000000000000000000003350000000000000000000000000000000000000000000000000000000000000336000000000000000000000000000000000000000000000000000000000000033700000000000000000000000000000000000000000000000000000000000003380000000000000000000000000000000000000000000000000000000000000339000000000000000000000000000000000000000000000000000000000000033a000000000000000000000000000000000000000000000000000000000000033b000000000000000000000000000000000000000000000000000000000000033c000000000000000000000000000000000000000000000000000000000000033d000000000000000000000000000000000000000000000000000000000000033e000000000000000000000000000000000000000000000000000000000000033f000001000000000000000000000000000000000000000000000000000000000000000340000000000000000000000000000000000000000000000000000000000000034100000000000000000000000000000000000000000000000000000000000003420000000000000000000000000000000000000000000000000000000000000343000000000000000000000000000000000000000000000000000000000000034400000000000000000000000000000000000000000000000000000000000003450000000000000000000000000000000000000000000000000000000000000346000000000000000000000000000000000000000000000000000000000000034700000000000000000000000000000000000000000000000000000000000003480000000000000000000000000000000000000000000000000000000000000349000000000000000000000000000000000000000000000000000000000000034a000000000000000000000000000000000000000000000000000000000000034b000000000000000000000000000000000000000000000000000000000000034c000000000000000000000000000000000000000000000000000000000000034d000000000000000000000000000000000000000000000000000000000000034e000000000000000000000000000000000000000000000000000000000000034f0000000000000000000000000000000000000000000000000000000000000350000000000000000000000000000000000000000000000000000000000000035100000000000000000000000000000000000000000000000000000000000003520000000000000000000000000000000000000000000000000000000000000353000000000000000000000000000000000000000000000000000000000000035400000000000000000000000000000000000000000000000000000000000003550000000000000000000000000000000000000000000000000000000000000356000000000000000000000000000000000000000000000000000000000000035700000000000000000000000000000000000000000000000000000000000003580000000000000000000000000000000000000000000000000000000000000359000000000000000000000000000000000000000000000000000000000000035a000000000000000000000000000000000000000000000000000000000000035b000000000000000000000000000000000000000000000000000000000000035c000000000000000000000000000000000000000000000000000000000000035d000000000000000000000000000000000000000000000000000000000000035e000000000000000000000000000000000000000000000000000000000000035f0000000000000000000000000000000000000000000000000000000000000360000000000000000000000000000000000000000000000000000000000000036100000000000000000000000000000000000000000000000000000000000003620000000000000000000000000000000000000000000000000000000000000363000000000000000000000000000000000000000000000000000000000000036400000000000000000000000000000000000000000000000000000000000003650000000000000000000000000000000000000000000000000000000000000366000000000000000000000000000000000000000000000000000000000000036700000000000000000000000000000000000000000000000000000000000003680000000000000000000000000000000000000000000000000000000000000369000000000000000000000000000000000000000000000000000000000000036a000000000000000000000000000000000000000000000000000000000000036b000000000000000000000000000000000000000000000000000000000000036c000000000000000000000000000000000000000000000000000000000000036d000000000000000000000000000000000000000000000000000000000000036e000000000000000000000000000000000000000000000000000000000000036f0000000000000000000000000000000000000000000000000000000000000370000000000000000000000000000000000000000000000000000000000000037100000000000000000000000000000000000000000000000000000000000003720000000000000000000000000000000000000000000000000000000000000373000000000000000000000000000000000000000000000000000000000000037400000000000000000000000000000000000000000000000000000000000003750000000000000000000000000000000000000000000000000000000000000376000000000000000000000000000000000000000000000000000000000000037700000000000000000000000000000000000000000000000000000000000003780000000000000000000000000000000000000000000000000000000000000379000000000000000000000000000000000000000000000000000000000000037a000000000000000000000000000000000000000000000000000000000000037b000000000000000000000000000000000000000000000000000000000000037c000000000000000000000000000000000000000000000000000000000000037d000000000000000000000000000000000000000000000000000000000000037e00000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000380000000000000000000000000000000000000000000000000000000000000038100000000000000000000000000000000000000000000000000000000000003820000000000000000000000000000000000000000000000000000000000000383000000000000000000000000000000000000000000000000000000000000038400000000000000000000000000000000000000000000000000000000000003850000000000000000000000000000000000000000000000000000000000000386000000000000000000000000000000000000000000000000000000000000038700000000000000000000000000000000000000000000000000000000000003880000000000000000000000000000000000000000000000000000000000000389000000000000000000000000000000000000000000000000000000000000038a000000000000000000000000000000000000000000000000000000000000038b000000000000000000000000000000000000000000000000000000000000038c000000000000000000000000000000000000000000000000000000000000038d000000000000000000000000000000000000000000000000000000000000038e000000000000000000000000000000000000000000000000000000000000038f0000000000000000000000000000000000000000000000000000000000000390000000000000000000000000000000000000000000000000000000000000039100000000000000000000000000000000000000000000000000000000000003920000000000000000000000000000000000000000000000000000000000000393000000000000000000000000000000000000000000000000000000000000039400000000000000000000000000000000000000000000000000000000000003950000000000000000000000000000000000000000000000000000000000000396000000000000000000000000000000000000000000000000000000000000039700000000000000000000000000000000000000000000000000000000000003980000000000000000000000000000000000000000000000000000000000000399000000000000000000000000000000000000000000000000000000000000039a000000000000000000000000000000000000000000000000000000000000039b000000000000000000000000000000000000000000000000000000000000039c000000000000000000000000000000000000000000000000000000000000039d000000000000000000000000000000000000000000000000000000000000039e000000000000000000000000000000000000000000000000000000000000039f00000000000000000000000000000000000000000000000000000000000003a000000000000000000000000000000000000000000000000000000000000003a100000000000000000000000000000000000000000000000000000000000003a200000000000000000000000000000000000000000000000000000000000003a300000000000000000000000000000000000000000000000000000000000003a400000000000000000000000000000000000000000000000000000000000003a500000000000000000000000000000000000000000000000000000000000003a600000000000000000000000000000000000000000000000000000000000003a700000000000000000000000000000000000000000000000000000000000003a800000000000000000000000000000000000000000000000000000000000003a900000000000000000000000000000000000000000000000000000000000003aa00000000000000000000000000000000000000000000000000000000000003ab00000000000000000000000000000000000000000000000000000000000003ac00000000000000000000000000000000000000000000000000000000000003ad00000000000000000000000000000000000000000000000000000000000003ae00000000000000000000000000000000000000000000000000000000000003af00000000000000000000000000000000000000000000000000000000000003b000000000000000000000000000000000000000000000000000000000000003b100000000000000000000000000000000000000000000000000000000000003b200000000000000000000000000000000000000000000000000000000000003b300000000000000000000000000000000000000000000000000000000000003b400000000000000000000000000000000000000000000000000000000000003b500000000000000000000000000000000000000000000000000000000000003b600000000000000000000000000000000000000000000000000000000000003b700000000000000000000000000000000000000000000000000000000000003b800000000000000000000000000000000000000000000000000000000000003b900000000000000000000000000000000000000000000000000000000000003ba00000000000000000000000000000000000000000000000000000000000003bb00000000000000000000000000000000000000000000000000000000000003bc00000000000000000000000000000000000000000000000000000000000003bd00000000000000000000000000000000000000000000000000000000000003be000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000003c000000000000000000000000000000000000000000000000000000000000003c100000000000000000000000000000000000000000000000000000000000003c200000000000000000000000000000000000000000000000000000000000003c300000000000000000000000000000000000000000000000000000000000003c400000000000000000000000000000000000000000000000000000000000003c500000000000000000000000000000000000000000000000000000000000003c600000000000000000000000000000000000000000000000000000000000003c700000000000000000000000000000000000000000000000000000000000003c800000000000000000000000000000000000000000000000000000000000003c900000000000000000000000000000000000000000000000000000000000003ca00000000000000000000000000000000000000000000000000000000000003cb00000000000000000000000000000000000000000000000000000000000003cc00000000000000000000000000000000000000000000000000000000000003cd00000000000000000000000000000000000000000000000000000000000003ce00000000000000000000000000000000000000000000000000000000000003cf00000000000000000000000000000000000000000000000000000000000003d000000000000000000000000000000000000000000000000000000000000003d100000000000000000000000000000000000000000000000000000000000003d200000000000000000000000000000000000000000000000000000000000003d300000000000000000000000000000000000000000000000000000000000003d400000000000000000000000000000000000000000000000000000000000003d500000000000000000000000000000000000000000000000000000000000003d600000000000000000000000000000000000000000000000000000000000003d700000000000000000000000000000000000000000000000000000000000003d800000000000000000000000000000000000000000000000000000000000003d900000000000000000000000000000000000000000000000000000000000003da00000000000000000000000000000000000000000000000000000000000003db00000000000000000000000000000000000000000000000000000000000003dc00000000000000000000000000000000000000000000000000000000000003dd00000000000000000000000000000000000000000000000000000000000003de00000000000000000000000000000000000000000000000000000000000003df00000000000000000000000000000000000000000000000000000000000003e000000000000000000000000000000000000000000000000000000000000003e100000000000000000000000000000000000000000000000000000000000003e200000000000000000000000000000000000000000000000000000000000003e300000000000000000000000000000000000000000000000000000000000003e400000000000000000000000000000000000000000000000000000000000003e500000000000000000000000000000000000000000000000000000000000003e600000000000000000000000000000000000000000000000000000000000003e700000000000000000000000000000000000000000000000000000000000003e800000000000000000000000000000000000000000000000000000000000003e900000000000000000000000000000000000000000000000000000000000003ea00000000000000000000000000000000000000000000000000000000000003eb00000000000000000000000000000000000000000000000000000000000003ec00000000000000000000000000000000000000000000000000000000000003ed00000000000000000000000000000000000000000000000000000000000003ee00000000000000000000000000000000000000000000000000000000000003ef00000000000000000000000000000000000000000000000000000000000003f000000000000000000000000000000000000000000000000000000000000003f100000000000000000000000000000000000000000000000000000000000003f200000000000000000000000000000000000000000000000000000000000003f300000000000000000000000000000000000000000000000000000000000003f400000000000000000000000000000000000000000000000000000000000003f500000000000000000000000000000000000000000000000000000000000003f600000000000000000000000000000000000000000000000000000000000003f700000000000000000000000000000000000000000000000000000000000003f800000000000000000000000000000000000000000000000000000000000003f900000000000000000000000000000000000000000000000000000000000003fa00000000000000000000000000000000000000000000000000000000000003fb00000000000000000000000000000000000000000000000000000000000003fc00000000000000000000000000000000000000000000000000000000000003fd00000000000000000000000000000000000000000000000000000000000003fe00000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000400000000000000000000000000000000000000000000000000000000000000040100000000000000000000000000000000000000000000000000000000000004020000000000000000000000000000000000000000000000000000000000000403000000000000000000000000000000000000000000000000000000000000040400000000000000000000000000000000000000000000000000000000000004050000000000000000000000000000000000000000000000000000000000000406000000000000000000000000000000000000000000000000000000000000040700000000000000000000000000000000000000000000000000000000000004080000000000000000000000000000000000000000000000000000000000000409000000000000000000000000000000000000000000000000000000000000040a000000000000000000000000000000000000000000000000000000000000040b000000000000000000000000000000000000000000000000000000000000040c000000000000000000000000000000000000000000000000000000000000040d000000000000000000000000000000000000000000000000000000000000040e000000000000000000000000000000000000000000000000000000000000040f0000000000000000000000000000000000000000000000000000000000000410000000000000000000000000000000000000000000000000000000000000041100000000000000000000000000000000000000000000000000000000000004120000000000000000000000000000000000000000000000000000000000000413000000000000000000000000000000000000000000000000000000000000041400000000000000000000000000000000000000000000000000000000000004150000000000000000000000000000000000000000000000000000000000000416000000000000000000000000000000000000000000000000000000000000041700000000000000000000000000000000000000000000000000000000000004180000000000000000000000000000000000000000000000000000000000000419000000000000000000000000000000000000000000000000000000000000041a000000000000000000000000000000000000000000000000000000000000041b000000000000000000000000000000000000000000000000000000000000041c000000000000000000000000000000000000000000000000000000000000041d000000000000000000000000000000000000000000000000000000000000041e000000000000000000000000000000000000000000000000000000000000041f0000000000000000000000000000000000000000000000000000000000000420000000000000000000000000000000000000000000000000000000000000042100000000000000000000000000000000000000000000000000000000000004220000000000000000000000000000000000000000000000000000000000000423000000000000000000000000000000000000000000000000000000000000042400000000000000000000000000000000000000000000000000000000000004250000000000000000000000000000000000000000000000000000000000000426000000000000000000000000000000000000000000000000000000000000042700000000000000000000000000000000000000000000000000000000000004280000000000000000000000000000000000000000000000000000000000000429000000000000000000000000000000000000000000000000000000000000042a000000000000000000000000000000000000000000000000000000000000042b000000000000000000000000000000000000000000000000000000000000042c000000000000000000000000000000000000000000000000000000000000042d000000000000000000000000000000000000000000000000000000000000042e000000000000000000000000000000000000000000000000000000000000042f0000000000000000000000000000000000000000000000000000000000000430000000000000000000000000000000000000000000000000000000000000043100000000000000000000000000000000000000000000000000000000000004320000000000000000000000000000000000000000000000000000000000000433000000000000000000000000000000000000000000000000000000000000043400000000000000000000000000000000000000000000000000000000000004350000000000000000000000000000000000000000000000000000000000000436000000000000000000000000000000000000000000000000000000000000043700000000000000000000000000000000000000000000000000000000000004380000000000000000000000000000000000000000000000000000000000000439000000000000000000000000000000000000000000000000000000000000043a000000000000000000000000000000000000000000000000000000000000043b000000000000000000000000000000000000000000000000000000000000043c000000000000000000000000000000000000000000000000000000000000043d000000000000000000000000000000000000000000000000000000000000043e0000000000000000000000000000000000000000000000000000000000000000000000400000000000000000000000000000000000000000000000000000000000000640000000000000000000000000000000000000000000000000000000000000064a0000000000000000000000000000000000000000000000000000000000000641000000000000000000000000000000000000000000000000000000000000064b0000000000000000000000000000000000000000000000000000000000000642000000000000000000000000000000000000000000000000000000000000064c0000000000000000000000000000000000000000000000000000000000000643000000000000000000000000000000000000000000000000000000000000064d0000000000000000000000000000000000000000000000000000000000000644000000000000000000000000000000000000000000000000000000000000064e0000000000000000000000000000000000000000000000000000000000000645000000000000000000000000000000000000000000000000000000000000064f00000000000000000000000000000000000000000000000000000000000006460000000000000000000000000000000000000000000000000000000000000650000000000000000000000000000000000000000000000000000000000000064700000000000000000000000000000000000000000000000000000000000006510000000000000000000000000000000000000000000000000000000000000648000000000000000000000000000000000000000000000000000000000000065200000000000000000000000000000000000000000000000000000000000006490000000000000000000000000000000000000000000000000000000000000653000000000000000000000000000000000000000000000000000000000000064a0000000000000000000000000000000000000000000000000000000000000654000000000000000000000000000000000000000000000000000000000000064b0000000000000000000000000000000000000000000000000000000000000655000000000000000000000000000000000000000000000000000000000000064c0000000000000000000000000000000000000000000000000000000000000656000000000000000000000000000000000000000000000000000000000000064d0000000000000000000000000000000000000000000000000000000000000657000000000000000000000000000000000000000000000000000000000000064e0000000000000000000000000000000000000000000000000000000000000658000000000000000000000000000000000000000000000000000000000000064f00000000000000000000000000000000000000000000000000000000000006590000000000000000000000000000000000000000000000000000000000000680000000000000000000000000000000000000000000000000000000000000068a0000000000000000000000000000000000000000000000000000000000000681000000000000000000000000000000000000000000000000000000000000068b0000000000000000000000000000000000000000000000000000000000000682000000000000000000000000000000000000000000000000000000000000068c0000000000000000000000000000000000000000000000000000000000000683000000000000000000000000000000000000000000000000000000000000068d0000000000000000000000000000000000000000000000000000000000000684000000000000000000000000000000000000000000000000000000000000068e0000000000000000000000000000000000000000000000000000000000000685000000000000000000000000000000000000000000000000000000000000068f00000000000000000000000000000000000000000000000000000000000006860000000000000000000000000000000000000000000000000000000000000690000000000000000000000000000000000000000000000000000000000000068700000000000000000000000000000000000000000000000000000000000006910000000000000000000000000000000000000000000000000000000000000688000000000000000000000000000000000000000000000000000000000000069200000000000000000000000000000000000000000000000000000000000006890000000000000000000000000000000000000000000000000000000000000693000000000000000000000000000000000000000000000000000000000000068a0000000000000000000000000000000000000000000000000000000000000694000000000000000000000000000000000000000000000000000000000000068b0000000000000000000000000000000000000000000000000000000000000695000000000000000000000000000000000000000000000000000000000000068c0000000000000000000000000000000000000000000000000000000000000696000000000000000000000000000000000000000000000000000000000000068d0000000000000000000000000000000000000000000000000000000000000697000000000000000000000000000000000000000000000000000000000000068e0000000000000000000000000000000000000000000000000000000000000698000000000000000000000000000000000000000000000000000000000000068f000000000000000000000000000000000000000000000000000000000000069900000000000000000000000000000000000000000000000000000000000006c000000000000000000000000000000000000000000000000000000000000006ca00000000000000000000000000000000000000000000000000000000000006c100000000000000000000000000000000000000000000000000000000000006cb00000000000000000000000000000000000000000000000000000000000006c200000000000000000000000000000000000000000000000000000000000006cc00000000000000000000000000000000000000000000000000000000000006c300000000000000000000000000000000000000000000000000000000000006cd00000000000000000000000000000000000000000000000000000000000006c400000000000000000000000000000000000000000000000000000000000006ce00000000000000000000000000000000000000000000000000000000000006c500000000000000000000000000000000000000000000000000000000000006cf00000000000000000000000000000000000000000000000000000000000006c600000000000000000000000000000000000000000000000000000000000006d000000000000000000000000000000000000000000000000000000000000006c700000000000000000000000000000000000000000000000000000000000006d100000000000000000000000000000000000000000000000000000000000006c800000000000000000000000000000000000000000000000000000000000006d200000000000000000000000000000000000000000000000000000000000006c900000000000000000000000000000000000000000000000000000000000006d300000000000000000000000000000000000000000000000000000000000006ca00000000000000000000000000000000000000000000000000000000000006d400000000000000000000000000000000000000000000000000000000000006cb00000000000000000000000000000000000000000000000000000000000006d500000000000000000000000000000000000000000000000000000000000006cc00000000000000000000000000000000000000000000000000000000000006d600000000000000000000000000000000000000000000000000000000000006cd00000000000000000000000000000000000000000000000000000000000006d700000000000000000000000000000000000000000000000000000000000006ce00000000000000000000000000000000000000000000000000000000000006d800000000000000000000000000000000000000000000000000000000000006cf00000000000000000000000000000000000000000000000000000000000006d90000000000000000000000000000000000000000000000000000000000000700000000000000000000000000000000000000000000000000000000000000070a0000000000000000000000000000000000000000000000000000000000000701000000000000000000000000000000000000000000000000000000000000070b0000000000000000000000000000000000000000000000000000000000000702000000000000000000000000000000000000000000000000000000000000070c0000000000000000000000000000000000000000000000000000000000000703000000000000000000000000000000000000000000000000000000000000070d0000000000000000000000000000000000000000000000000000000000000704000000000000000000000000000000000000000000000000000000000000070e0000000000000000000000000000000000000000000000000000000000000705000000000000000000000000000000000000000000000000000000000000070f00000000000000000000000000000000000000000000000000000000000007060000000000000000000000000000000000000000000000000000000000000710000000000000000000000000000000000000000000000000000000000000070700000000000000000000000000000000000000000000000000000000000007110000000000000000000000000000000000000000000000000000000000000708000000000000000000000000000000000000000000000000000000000000071200000000000000000000000000000000000000000000000000000000000007090000000000000000000000000000000000000000000000000000000000000713000000000000000000000000000000000000000000000000000000000000070a0000000000000000000000000000000000000000000000000000000000000714000000000000000000000000000000000000000000000000000000000000070b0000000000000000000000000000000000000000000000000000000000000715000000000000000000000000000000000000000000000000000000000000070c0000000000000000000000000000000000000000000000000000000000000716000000000000000000000000000000000000000000000000000000000000070d0000000000000000000000000000000000000000000000000000000000000717000000000000000000000000000000000000000000000000000000000000070e0000000000000000000000000000000000000000000000000000000000000718000000000000000000000000000000000000000000000000000000000000070f000000000000000000000000000000000000000000000000000000000000071900000008000000000000000000000000000000000000000000000000000000000000044000000000000000000000000000000000000000000000000000000000000004410000000000000000000000000000000000000000000000000000000000000480000000000000000000000000000000000000000000000000000000000000048100000000000000000000000000000000000000000000000000000000000004c000000000000000000000000000000000000000000000000000000000000004c100000000000000000000000000000000000000000000000000000000000005000000000000000000000000000000000000000000000000000000000000000501000000042ea6fe02855d8c72905f35f08604467117c037962e324c9a793505138558b3700c94efd9afe573212f9bf596ca3a4b42ae57f59e670a7cb5b9440c54bd8ea2961856bc4892b0b3f156ed2aa87463c389bfce0fd675a37cec77ae5d753fcf593c12f9d3f397c2edc9e4a6a5e485641163525ca802e3bd93994af997a9b9a44f98000000000000000000000000000000000000000000000000000000000000114041414141414141414141414141414141414141410000000000000000000000000000000000000000000000000000000000001180818181818181818181818181818181818181818100000000000000000000000000000000000000000000000000000000000011c0c1c1c1c1c1c1c1c1c1c1c1c1c1c1c1c1c1c1c1c1000000000000000000000000000000000000000000000000000000000000120001010101010101010101010101010101010101010000001012e5643e26da426570dd999e0e044e5f83d60f3cd813c55059bc0ea0f4a7c9d413b2d2cea949fa0876265cd8eee3a4dce1e243783562ea07e2de537f469f7bf627abb3d4560e786bafd52f77ac52dea36c50419f32386567bd969b0c38e1bd7405d339cecb99fa74bfd631917f965357f0341d8c4dfe6a8f4b621d8df54c82941d560ac24523012499eeca2594f20ce238b7d21b98ad5c638767d70ee05532c2183e6d64e69b005709dfc4771c7ca5981adc54f3e8bd1b00222461ae90e44eda2f1d4572fe0b0b23100a7ea6d4780b25e826e41ca9e71897e010bcb218887b3d036d44eb30a430b5cfc6660270eb5fb7f274689ac73dfa53ba9d0739fe38637f01f7130af8c5888d4c65ea68109a1d58fe8d7d1ae62098857b0a3a3dcd393ef80ed8bcba6eb5e3b4887a32c51c5853b97a5eb59c87c520f36423c6d7be06071821ca1719330de5e3c51a521778b49cbbc1d53f8ed42e0562bf21ed72e8cb9a410b0e82ef5f7276df41353faef675fb408aeb246b6e2f704cab99bf28d1427e7b0fec7b9929c6effdcd045d3fdcec1d1b73faed495444699fab936630d69b519f0bf4f1453db36439419dbfba703b849d1aa5774fe9a5325e5bf3e7cdda9d1f7f2a9c2a035c72fd4a3e383306aff03448f9accc08a2c665837560cec8675fe2512bfaef35a8fb7df08a81001a7fd4903849c0371dedd87f31295f46269c5205dd0000381000000e00000001bc00000090c13d9d0180efb0cf4a6d1676f61f735ed5237557862809c657aa892a154646f002740dcac518dd1d753b4e8c83cc4b820d7a92f8d3455ef8d6b782406a966e38abcccfcba2bece327a31225662a0e51617a0f49b0b6dbffcf2dd0fd020106158903111091982c078d4c3674969f364fc0a71e2354ba07e5794615562312bd212033dc0cfd54419fcc5ab8efdee25f19100000090dccd8f5a0daa473e34d076eeae33bcb6afbc88dbe6897c7b315149811fd15efac96150d7e0fa1579376d42f4b911e155eda5ee5456a9d6c0a699643c6beff9cb0eb2e1b36cf3fade1af2b33ee0c0bb472acae19d029a57a53882b8ba8389fe0e30429fdf159c9693fe2006334692e67b1b1fcf6fa166824ec7076564e555e49dd085a8e4b54bb1ac0f18e31118190e6600000090379379b71c561c7622aaf4ff0d200b4aa507de6b631c841e84ec50b80fb6719d5278706bf3400d47296446f1c13e75cabe1732750f02a7ba2e278a6f5b144cd0333519ed882d956d16cc45175bd00a6c16cf2171d48057133603ac31d8bc578b9b481c0a2040a5725d0224e373b3f043247e2bc5856e7216bc45fa196168132578c74e26d88b4ff36eaef01d3c7f915d000001bc0000009089c7007a588bd867a1faee4af2137ccca39e4d930507643479212f21630c943c6f430108f4bb721838c8f1bb34ff73841fa2c37934cc6e2cdbcfda0e9c468ad150dddc942d41b76c0758433eb4a0242e26e30592948a866293bb4e29a584a8907676dd085cb9360afff7ab84ec5801fe07a56a33221144d05716a7f2db7ae035b7e80a56341d289af3914b0721a00d08000000902b13f3df874d19d5829282e4eb39007639ff7c211e86e7f8be619ca4ba39c5d81c8d145a5192f4a3f10895e2797f2e25a7131a1bcc6b1062f6af45c94d85de2c419ff98bf9cc3715c1039b2e8ed4ad6c0499353d1db9426370e35a27689ea1b19ae662edc37422f8cb8592f9c6f8fde61dd54d1fd3bfbaf5023d7bddf2311ca15af50e55673a51a0a914bf4532c9722000000090539a9203f6b58beb64bc593fcd09ed470344d5394aa75974ffbf7f996c7b066a8f2175d3e9f10ba74227458fa6150ea458ef71baf2845c3ce67f771741aceb9abc9ec54a601399e81afd137ad3434457239ebf4d9c8c846abbc844ddbf723d686b365611aa69147196cf8df5c22ea6dc27f19ee20cf6fc0390a49c304a851e624db98d7e5ad0f450c3c8bad71b4a5de9000001bc00000090da222fd69c4e340b559c3dc9bf8d0bf6008a106d09f504acc95a3ab1c93c1481e2e2704e786271b93745fc97c7b0788a6eb74aa6e72c6ad2b6877d7439eb41279bc6107b7794a16257bbb4d0a20ca42303e778f0158c37a7cf4698c7dc402292b6d224b91f884783885a44c4b22cb2e1197b400ffb4fe08750914ae9639d5447c8bc64ffb989e07b8ce53a2043185c94000000908ea72c947ebf55d77062f0b89b668a142a198842dbd2dac59f115887ae962f7c162ecffe95eb0078e9ecc34874060fcf3da11c14e08a92203b8fed924ccbc787482875ff28aaeb45859aa0d1d01db986137c1764dc112f53424f750a28900c8c53e6973557e33d7376e0730bccf2a5d606a3e795498757161646d549a0a5af18e3958557963648083d6e76c9fc6ad1e100000090fdbeac241ed3f7ca3be7180b73150eef39d6a366bb834fa7903267719e26e83579db2cd65cb64ea43a9d203f5c8d837c1efa6325e91e032006a7f5e98dfbd0eece6aa4c7db4415259601ddc5b049736c0bd34df57ddb365dbc441b7905adc29628e5559ad6d5b3bfe0f0392ebf43fe810d8a808231fb44958c9a96fac08161ac0956714bd3c74d9c851197ca7b1a8c57000001bc000000906e199cef996acff8e6408667d2db579909371083a2b40e70dcd78a3b6c655ee906d32a6a2e4bb9d1ca738f6423f006ffa3f7ceafd91e96eea421645efbb0e40388f9687acb66dd29aa5483254c111a831ba7e8598f6360924da54146a7a03d128a412f9e3ffd0397856b66ab9e8b524925cd75b0ae28efbf9c70b3f5b6a97e3f035453116809651ce45021ff596167f200000090dd2cba45eef3205574b614aa62165a4ee28430fff5b14a32cd63328c61f81579edde9efc3dfe5d7dd1526640f56ef9a8413c7c404907d88f32d16dea6f5958a9f56abce2a07c97569f8c26bcc74f2c9d141b0c142f32949e264a3fa9d1e77083e8b916c2df83261c0da074fc461a2bfe2e1c37540455dfd845002c4b409f9226cb252a4f11eb97706bb34fa6679068cd00000090e69c94a722b2f3cdeab1fe8314357bd692975c2261756d4f2e42893ba0bb2bebdb3512194c20a5b67bd891cb60c2d1968dbe96d5484b4964a5929de3c2a5cd0effa311707ae0fba27fc768c80f4bce83231d783024d854556157346463fb7012bda42dfd618b0aca7f58cc31dd15c6972303ff2ed3ae6d388f478577aaecb76fb12e7238ef29c2e5505477eb86b80477000001bc000000900f5ef582e8f2b358a287f69fb361c846bc362796e2766f502d677b6146517246b69ce262540e375c2424a887054b16608904482f40ef323763ed9e7f299404f25d608e6e0e4bcda16a92354986662e882eda68e23fc827ca6597c85e7b2c7f1a5f9409f6c7223104cf3b5dc3bbff99071977631eb91b2113c5fe3e3ff3ddc8da3bb01bc785683396c5470183f910dab300000090c51abbf871caccb44d13c60c2a6f8429140005f820b70f74077aba9c7f32cf74d9ece0ce1b24cc19980d787ba82d3f9186ed2d08888626deeb8ce1f74846c70ec434efc3e33bfbc86213df64cdf73fdc21fb971bde4dce6ce0cf00e204e065c5b1f57cb613c3ed8ab6e54d31c1636baa264dd3ecedfcbbb1dec253095757d12bca1d76d2631ef94e76245798dd47af030000009022d04f9051a6626fdff3d609739cbacd11741343c4d8ea3177ca242e4de50b37ccad4739ec2c882d5d17bd084f1d3fabc72268d28193b522600a4fb708231e58a7ef5b23f91389137beafd14a7d0df55048f605b84bdde04f25c61ba7101bd5c58d397375879af5a4ffe97152630c801260d6e9ce6fdcaee074e2f665238668386b35a772a861eb4e9b3fc3556ca1974000001bc000000907e2e2070eba81d16a60b826b11ad0a7a348148e4da106d6e92e01e2ca53e99cd643c4faadb85efa26d2686e15df7111e080d9b2f08b8d1799dede4f9091005b95dce8018d40aa6bf530206560bfa3b6005a351fd26639df63ee65b51de7b68e077fdc01ea04b73d878c3895dc223130302ce398479d642a13db0eeea714edbf4b6011a4dce09c1fa516a27aad43606e700000090498ba863b488e2698f2b2020eb4c7927ad3dc4c9bf3d7cc556c123db1ef46dea337cd5cb99ad0b8e521103a54ca7444a8230be7749816a240d8b0f88522c629f98bfc97d8e980244e4aca243dc1d8256289d3b37f200f4c5c9f4cd63a3edb7954c75d547577a5d22bf3f77b69953bdd323b82b33481f27c55dfc4f15c859fd27da61d786836685a16122ef59a7ca7ac70000009046c3512b1c79aa1e065a8e4256c595c9293dca1d56fd05a119cafab59416499ad8aacb94f92d99388f774ff6d7d0ba468fdacdb8d8d595c7be3ac58364d84d220bcc50460aaab158b837c1f834af3bd918cdc85c632337166ef39a085b6112604af2ffec61e77e9023863308bd2566751f5437bcf1ea2387b364b9099d087cfc5bd0e26ecacbe348de1b418d55b4875a000001bc000000908adab8f017417b5bd46fa1e9779632be5afba45eee6a58daa7754be749861997acfceb86f08f30b61ff903dd7acb46108739d67425d42f335498d98640fef4cabee4bc1593be601b8f85aabd966b24fe2147a3b6a75dfec1d644e0076f106a4051769f6aa62715d2e62bec25379e3fdf2091a5b55925577c3485574e37696c83a970b96c169e46877baa784d8a7666c6000000909d4bf0f2d2de7b2396dec1959f7b7de8b805425819c8b80798cae313d89e545d8025ff56d9a92845135ac58fe672a0fc92245eed345b5859f67540a5c19c80c499374d282f293a6c6fe12a8831a09f4a1107c3e23b62b0c34d7bc37d1fa74e9a6bf158f1890db25e9e5af558a463c85f104fffd61d3945110ba4543ff2a78e36daa74aea25b0e4905f0b76b8102901b5000000905584f775caac62723960eaffd596a5f64c7083ddaabdaaebfc2e60fe575109bd1e030a83c7d98fefab9494250ea6191e111f06da98f325b6db114b081d8277593d7c7c21b59248510cd872041c66e8480b33673cd6947e1be4673318549d2fe339e55c81b22301fe039ed1f9aa1004d51492f446b0797f7f27076842a05de5d9c53a8f0b45987af90fa814f5014c7ea8000001bc00000090e41c2efc5d16d3c931e59cb67106901c97b62ea2e65a49df476d10bc4193d36deaf6fc746c076bfeed7572d833c13ad4a51c933a86ac96ba919855df3c97b3a970e095a8de48e656eddb0630d208c43a1ef50bdfb97e408c7228fb2a13553807cc7fa20370e6b1818b11d43242ca70fd2686012dddb4a7920ad9752c93b0d8648338b7d453efc270a19cb0de5fffa2ad000000905aae1d893ce8543ab9ec6b411142dadc73a5c8d4318ea84c92f1482f3ee4c83be1db4e4f24090036eb9aabdf351325fc5865a36ec5b9d8fae372d8e5e42a97872d7ff6161ccbcdd20d56f7328bdcffa2090edc7fa952da75dd796b3c24f7a505acf739c62babb1016e67198d76ee4f7e1fde045d25ee1bab55b3494f55648df759e89e867da0cd6595a0916a0714905300000090772b55f21dcbfdadc07da90c672dd84e3eb7b887beeb448cc1170d05aff5ed6d2a0a67aaa36d0b76ac95acdde219bc952afebb74bad19640888f01e3fb2bdb8aaff26919e5b4ba2963018294487653341bc08d07c211cdea452937f2a2398917bec1a4cbc26f5a7a276f95c7827955e71b7c0d5e6b2836d8d81506520ed72933ec1356eb0e86b950100598e01736e87000000e00000001bc00000090132d9159c2693b4605031d9d3a8614931fd0db820cff1586861610a76845ae37c482268160a392d9503f26c73e4729eb9ba4e48e81ce46acbba5940b5befe62746a0b0d404d6d2050905640ff7ad60170968822a7680b69074574389dcd34b1bd979f232d474a3570a95c3b2720db7a31ce639fc503648592f9b394d65aded02aec9957f94fe094920fc061885fb59bc00000090dde8d4d0b0994f657c19ea77b96c20bccf7b42d2cfc40fc66500b6bd4b7b16f72ed38ee66714fa0727b946425817164bc9957a9fb8e5f742010a8b25824fc0b59941700ed1be933747bc2f18b35a4f01192bb2cdb5e56b6e94f90de95f2d9c8cfcff964523ebe0d4fc7ce9f7ea3026860ac8cff5739701036d90d352667aed8db6aeb4db8d1fa8e6089884c62c0156f8000000904153b073474c5db528f4be51601df0112a8d4d4ba2f2dad5ec82ae03437fcc1e1285f240c75a7713643657728145511fbd10da85ee19bb331be6067e453c6ed6a004eab94cfa133987db6b24fe4b4b5328f6d5edab27e61ced83baa42c7b3152732865ebc7f220b1f139fa6f27d2e05928f542135aa723c11ff508b83338960cf2e075bb43369b6ed46f309ce7e2582b000001bc000000900996b066ce34966c5fa8466a75f45ed2238062b136cda336793b7282c677d084bbdeca74cd1e06ee73e03a4b2319db173e6355dc2cf5eec9a1a0b85dd39b9ac260f7cbc3822ad3891e3c993c082dbfcf1c099f20d14abc5f4eb82e6dd7365592bafeda7e046f7f0396ea4a7b92e91d0513745d615ecf41c7bd8d130985740dff55a3dbe6026a5e80f8ce6951608e9add00000090ae6ef7a3a04a06aba48c357353e6c650967659f01659407980461c4c74de0884f0710ceb1572c65bdaa13b1de3c7d87b2d7ea4c5e175443f499cdcbc419fc4cfc9a6ddb7f4203163cccaef1746833ec62417f587cec58ac40abdfba21cd2add2927c04a6a95289d9a28ac577acb8c595239d5766d3d4c2393156ecb2a13f9226fde236435950396b6a56ee1870822fe100000090f5c8a05db5087e5ffe39815fcb37bacb14bfbf2e953c52772196959a95d165b703dbe921570b16991e99e15b4e2bd68396c4db8e97f4160356add145b3e9ff608f530f2fa55f9813930830f8df5a148e3045546abafd7f81a231ca7b975abf2962a5dbd88b969e5690eedb26ce4d3f30238b380c78bc2d1e4d2e5e919969cc45e415cc276965ab136d16a69cf34758f2000001bc0000009001df829a357a44970ee6cc4d401098330479eb359c3f69bccf7858c574097b6534bb04d7e3f8110b15c79e3d77ea1fdbefa57dd532ba43cf31660e9d4d0afc5b16696f5ff10e768ccef3128d6cd4792909129b9f9c4fd742f07d5a5033662d2c73a0c29d3a3a120535b776e0371fdb32198861115ea1d29ba8d1d39c9a36ad5c70ee0a28366ca0b0590c87e8b5b1453e0000009051c19f51f06efaeed558e6007aec37232a35fb7b7b54799b265865773dbe7b0a0a21282c2e7117651bf2d581ac8b040853c9a1be9b691da9d69a81a6ed5b2f186b05566a1faf567297233df82c15e9b515ec50a7779d8b2a2ce03c230e3c9131c6fceb6ee0257c924492c071100715422d7393f65393f3d52d1a2713d368bf37756f50a18bdbd23e4a7c9b80a6815e2d000000908f2cf32ce81164301b92839f7f83a03f1270b5b93eee777ef10865560ebb475dda85aae36d47b71dfb84331af504fc7e85ba2691d5c08719ac8146c349c9a628e6848c67f3a94d4955e7e77dd73439602c8baedd0694dc98d7b7682d9a2be35064f90b4cc40d6128a1629a383a63e68b10477a7c93e1feb16d9d01f28250d981a0014364a7eaecb958a9e0b8e1fe2794000001bc0000009078bf82480ab9af379874a177f5131c5737a1d315afb04bea2abe470b5c8335c41de1d90ab17530cfd4df0c0053968fe763c4525fe11d27e3831eb0dab5ae94a051be0afdd96c5e52bb00a90bcc7240f007808aedfdf71604a531f3106c8b532736c54d7580f5db68859c9221a6a246f32d0fd2fa766c225e76c431c23b15911c72a6a449b8b332d59a0ffae46065d37b0000009079c4088b4916ee3eea335fa522fb8a2c817929f79779914a6d70bce9560db262320f4f35e377461640fd8f5ed83a8e2c1b6a571b50dfeb335f0ec46eea761e1255947c339d960afdb69f462d4b4a96171f4c0ee5369eda087dd385d3a73553d8f69a66d3cc981513d3f7b97ea277c7da13c5ad6625b3719cc9922a33b900d509372e65685073456a9689d8f3e671cf1c000000903e43310f6a0f4b45a417eda3bd54c5655b6741ce1bf8bb52c759432201cca9859fa2bcd9d25c38eae0a2eb724da6bd423db952927ebc9260e130c594f2573729e52c01a8608786d48ab7dd4f9539f1fb13dc2590cd703450457b7f2e9ee5efcedd9bec09482778efa1f2452087b5ea2608c16ede459ecf2b771b8348b00febdbb4c743a887bf22ab9db65591cdae1ed9000001bc000000903df172e06543a3182cccf2a11084765c3e6bf28b0e0b87b78d4995c5262966bf6e76411c6af26edb7ff83135231bd395d969a78aeabbf1a009b1e92aed0d46bed700fbeec8e60bf18f004dc464e29ab425fbcb2f9ce3eb20afa7714cfbded8ccad46c7c6cb6631a19d5305878f91e6650452bf6b77000046d7e5acc19362d74fe0d1d7cdaf7828e4584e8c7ff5db0a3a000000900e7c0465de744c7dc835b984858450b15d0c9068d49213cde3badeb7b0ed7ead242ef5fb2a36da2327effed1d6e1546ff33189c5c9d8985ee21465891e46c287cf13c5fb8fb5f3706847bb3b75c11b52156bf5b51d8b7620e6790601927a544c713b70db2740e3d511d9fcaa5f6d146d00cdabd162b35f6d7f4f336e4e7b33bbcefb0a8d120108dfd68e915fe346a0e000000090ba5272498ab1c5001f484d38a82b91f3a255d7a5b0bb835742379750c42a449210c814730296432e381eb32d239854ceba60841a3e18cddabaf0c128e9dd673197d0e2ed6c6431cd52ad32a107cd955900cd6b3a0c054e77edd28d86860c5f63a5c48ee87f9b9ef71a9b3a72a01ada8a2490ea1c08b5b48f642f4db559159af3dfb462406abe387e350171ed76abf47e000001bc000000909d08c99ea2da1e2e868070a52b6717503ea37b1c4a801458bc5a5090b2d976e9db9f34b3807f6b9f066f6c3db6fb4f7858e31209b97e6552af165d4f144a2b92d4f677db6964c37dcdbc6b8f2ed48190073dace76834a34d319c7c8008d07e44640bb1db4b6ae72c174dbf5f59c7e304145c4b213d5e30dbe7cab6c50ad3c43ac5df01bacaad452dcd852eb8139027c1000000902f2f2f4042bb83ec7c71440941752fba92929c70e6bbe524fb904414dfd4ca683490c74a9b707a8a94de43c7aec160c4025390ccadf8333c5a217afa28258e83dcfb7c085eed7a5cbd591cb316bda6d60db2c649515dafe5a34157bf952a8837c0f266e9bef1e8097c774c094a1e3fbd221c65fd93c3ab6dcc3a4c72e9c2b51937411e9e935f0e368a8c17b722733221000000902ba5e91c85c88efefe97256b0d2a285261b9f3f766ece5e6ef0d19086b13f410bf9a24df9f0cf17f3abc9fc8d84145ee0f0c05118ef0d0fa0d99320f5daf8b1307513f7c46a8ca355821b98dc7e409a029607cad1fa4290d08b0f377d632e8309fea56003f2abc8f8232157eb0f99e831ec90c50c5c0d90609f5b92bb81cec93f57ebf2e8a85934958ec194a27919ba8000001bc00000090acb6759efa9edf780a8696476098e2b0e698d37f0529cc7e07771e20805ce183ece93a6d8220fe5e41f8f2c4f66f7d7600cab1e5a432239448a4cafac2664f29706b686ecbb9ee7a585e07ac328059a402ff59578da4e6c644f587093d3adb925e26aec2feb8a0ca70a89f09e36c627a07281794087566010deffa8026376f7486b6ba38e3f754db746cf74194a4650a00000090cd127b6b398aa63a9a73b1761bc918762593612e1270efd742b9f44ea325f6a290fc37ab2e78d604f3474b42314cdc14f10515479093ac62e7fb8009218217d17dc2c1be6979425d1f99ef36d05a258e291b3a3cc7dcca71187fb2469a8210fa7d944673c6612a74f7fc91d0d740d4d2201bfd7ddb9f169445c7852cfbc34c98457bd126da955e97f4308dc91310a9c600000090e9c97269047e138e4b47b8cd15b47f75db80aa24ecbcbabbae439e4af1f5554eb1b18500ee95fb4e5fa9fe8fa1ebc4b2352662ad8e557221c17098a3323418c6a5c6e209a9eb3ea6150a37bc5a1aa7760b77664e65a23da337ef538d13a8f322ec6c0cacae03379328a80650cec29a9705ad4a8c798250af9cf409b45074f75128973eccb68e627e2aba87bd1f35dceb000001bc00000090090959743b24692cd5ea95be03a90fe28eddedffc9caa45766141e1a454ae3aa88b9b51520fca9fe0ccab25f78e313a27df6c3fc31ca535fe4d5ecdeeb4ae6cb2e9059afd5036c342d0998d511a835b21e9baad20c0ef3c5be298e5273eca51df8041d08b4f88e08fc3695bb7d3f126a11b4f9af53680091d66168b8b4b2ac9b112d58df3acf91a5c1cc86e6f488c65d00000090d05387d02c443c7363d680111021068fc45fcdfb27d26f3914f05b48eb73dd6495e00c8e192dce422f7dfff826c41e7a5c98efc82d56ec2a1856b46fe18103dfea5b416ea3abaf2491cf6cf960bc2e9f1d5203b3f2410b36bc309f7fd06467953553d215372cf74ce674127ceabe66732d1fe77f64af1c2e4118d09036bf5929f0339a26128b5310bcf267147b085f6e00000090786c69a19ad57b5675bab6576b4f0fe2d6008f0b8e96b4a5691f1ef61b7bd2d3fe369e73b7ba2a0329888bc9a05d4de7315abea648c97744c02cdc0247f9477b634484f3e8daf469c96cfe6e37cd850c26640cb55b63489fecbcfd9688ccbfffcb4102175633b6a5377efcd0b7cc17cf0087ba0d1653cdaf6c68d49fa89706096ea2d7a9c29ad2f3dd867d1df70859a100000e00000001bc00000090a3876e16ed55d821d4636554fababb1fc42d06cbd92febfb62719d17b0f2bdb03d139f03e172349fbb12fa71876e71453c43e13ce97c64f8dbfc93415397633a065a1d515d26b62f3363bfc2854f8bfa10ffdf2a4efaed8b701374ab7f1e679a18ceac9c210fcb9acafb8cf37a73251f08e844a2b2001c82544d3378a05a9549fc7f61910e88cf90347073fd6e82835d00000090b5ff3c7f2f01945fb574824a039d5c8f42610b4a22c8b38b84cce82196b2f2ebaee6887ee5b0cead8b2d6869905cee40342c48e8efc6c2e9f832471f198074b291669e0ce93ba735756fd1c6fca3ebac0aa462812f3112e86d740a2664453eacea944595a408b3d4d8db332594fc5a102aad8663729ef614958a89a8b811cfee1d04540edf80cc168052d388469f0e6000000090b88b5d5719cc71eb2bb8e65e65d5432c51017975c69c91239547519b9e3c2d4628f9e599498bf8e302c4db9d1dae86963a18265772c4779a629a902121419e70a44cee9ae1129d1655e1ac4b1b25df362e67bc16884a13f79792cfe62a93585e14f29b2afbe23f15d1615f3705caacbf2d349c9d6a636d70237841d62796d49e3dae9b0c72fcc3b9bf91180247730568000001bc0000009006c722129a887a0e14bbeb91d2b1daabca1e3ea84f6c08eabc2deda6f75464cb597e5d05d0d823f8bc5f61221a336a976d225ff6fdc4a55a1fae420affad4450d1188825bfd87cd9ff4dbf25801900fa19518a80845efd7cb9eda2de5ea170cde239b239c07bfba655a5bb7dd54b5898210957fb956ec97b2dccb426dea1f357cfd8cfec662257afa8057f879a25c8f4000000905dff93700500f84c17a5d4978e2b0919e31d688d946279884a5cb09846dbc907b40be46b703041b02b2be0b1816374ce7403b92d14f4bcd334a1ca74ed7deb0a7d4a424a18cea800d23aac41f4e319a405f5aa27f508471258f487eb39b1289d102520641b12a47ecfe0d755b9d92806174fdbfd263a80f130e1b44f98423f769a15417b3298de41caf2d78f00850f5d00000090bbfcde9b17102b338768d92ce80368c9a2f4d6949b28be3a44746ed12c0d9ef2e2b66ba398c6050fc7c37ad565242d970e3e439bb08ab9d45dc897ee4828ed045dc4d2b0dcdfc7014e47320e70b14acc1e305b0d9424ff136843f472581ae3891bba2a8452089e70533c2466c89e403d038a8d402008467ee4577d68b2f14766370abc09c249eff0f906d0d1e093b8ba000001bc00000090e31a0d8d00cb9f8fd05817acbbde48215cfcfcafe44646c8d1924850509055c24fd79df0cd8715464e5a1c9bb6cb1f3a4bdd663963eab4c0752aaa1cff3a7b2eb75ab40a996b93f0f2302f6ad963806d0c503bd84ab49a02eeec51d263f1994702f69b0695ceb6cabeb7bba3bf52ad83237badec785ab3a745914806e6f26953a6ca04fc3d7cd696209bf5f104f86ef100000090063f775e7ceb26fe011707735a2e5888e7c230255e25e0979035744f3588eede48b272a0cbc80337c17972944aa6f28f70600506926e61c7630fdcb977bce23d8481128c814a778f9138879e742136b00d089f10d3d8c533045673b43b77fb383b4e661ace6d8d0162c266fbb9a6ee1d1dd8d16898423a2f00855cc213ce2d9044deeecc20a7ee74100ccd6e15a16cf300000090c9f9ff48464c6f94de4ed0abce66d7261db88bbd45ded714b66fb19e5bb89ce727fced3219274c155a314dace0bae0180fa8ab396b989830bd01cbe7928ef72559f89ef4d3d39d77d524a36e6816df1824dbfbcad5773867833c941cf338b17473bf2b82dbbf7946637b68009a1c0e1d170670a34b5b36d17e8dc7a3b901b710cc234ad174728ba4a1a12cb20ca5caf6000001bc00000090e482d47efca5ac34305cdd7cf1e0357dc9a3702db439d0ec32a557be756f27bb31464cf5a6fcebd489d99a93e23f8ec3bc9d6907bc21581d10d66a551180a5d13cab0d2bc41d8a1f6d2352bdb9d51a042d52113e3b937d9964600d48e6d8179044be056af36b7d28717ec773fc116c53028000a5f29c579fab1ac3a2b8650800ae68ac9a53a1f275c208c389013bba6f0000009089ae58cc01b9b4d1988af7aaca738ea51ebe24dee59a27948325a1cbc58e4ab31312369e2cbe9b0b21652e91072f57dfdbacdde7b7b6d221b3ee1c26d55baf5fc7e667b3574a6deed960f1522669f71d13566e4e0b0112c5a12585f5608812a6c602f239e99d3d82ca640ee8eaacfd080b4d7398bec0f577cf72403ccbfbe2f71dcf9ceed0499776ed5ee0c4957cb4c600000090e65d9f202a140c0a2022b6225cf2558d3f5a74a70aeb12acc459db2ca2d3e1826a9a37e98ac6b7c01a555e2114f7951822da3937d011c7a2cefb4c1b94ef44bba2529551281404e375a79950501261b01268dbd966ea9d77da87d9fe434442339186c81fa5c2dd31dffe8c23310e7d3f2a4f8272093f358c6b080b5a38f487a72a7d704b32572c536694618194172be9000001bc00000090e4466379aa91f3bce88e053033ebf202986b24940b07ca54164f0cccd41ca2521d2f1006404038afef6819f047d200d8f16b955629c7bec12ee1c5270f0392d8c98407c12448a429775bac3aad5cefa004872024a2b68eedad94e7663e2f4b21b4bddf32d138d844287cada5ee2dce080a36a7e159a32f219ee6b4e027c82ed2b456ae6e6499b13eee1581011e2e6b4400000090e3038f011ecf8122724726fd8db040ea9e24b16589ed2635f3751b1ade8596162fb96587ae669443d81dabd555a5358cf2395769e1079e10b467b9392e72ce23c810f6ebb2afec33762c8929f7b08f5b1883ccc2b03ea2df7dff0cacc8a2b9d47c2cdfe9be11b2cb64aeee7b22bcb2c31030ab1e56b449cf77bf7341df47fe48f089d5b0419675f8ff6230668da522000000009034fda1924626a90d62ba3a0e77560c6061bf610393bc208e92479ee7a591552f2ef5d34b8cccc41e98566d095b477b6f9b35a0d77db66384489d2c5b759f9b922fe8ca8d89c1e019c90a306a9b228405213482b7e55ea72cfaa28a983538434189af956e90ba1ae65146b08d92d451681b452d1ac8335c202aff95a6f01d387e9e377e313889ce43777c5c0d4915995e000001bc000000908300f26d3083d051387eabc1fa6ea58ad6111d5ad459cde12b4414126838bfe80feb66f89adc287849b52e352bb159abb21d7fe5b2f96afa5a56ac91e2393a7f43dcb867527e553df0b82cd01ea1f65110a950eb54c3ccd5692340d5e4e258770a4c4f5c869fd017c55dd539801b691f1657c78f302683645f577a3811910cbdee1cc7209049d3b1f23eb5d8daf1e1d0000000908480b44d561ea59bcbdac63423d63c3d45b314fa17cdac18a35f02805193dead44d1675d119f0377ca46c6625a3a33f86eb2cb6ee6ac03b9311690ff2d4ddb5670d34a8a577079af14339deb75c09ece1dd33cb17fba31c4deedf7d37ed1b5cefda8e270c3e3883370bb8285fb8fb7dd1527cae5b86022bc1d7c1745e0df1cf1c2501c3398051375ddfbffd4d5ec4c8f00000090770fe8024a65c310145b1edd9638e65b5cc21a4fab5427ee0d5a529aa0f770e6a209dd8608d528210d73c37e2159f2774716cbc7e0dccfc79e39b6b53b762034e77d067237c1de74766583c1f4484ba3064242fb6cefd1993cf4bbcbab8dedda0b7a4546e17068512ce6db5b71e8fbc31cdc30adf2e51dc6858b4865c5f96d769aa34ce8f4b63763d2e7fd0afa3ed132000001bc00000090290b968330d9b2a6a8429b395aff8a353dd65597f38f50ae73c75c1e4b471995b3b23d3d08aa1f5ee4a60471053d1d78fd50a24fc8f4829b43aba4604342c1080a603eaef54b1e03c235f151f8a635be2c715b158619bbe48a16f6201dd37c0323f59537e0e0424c2bdccaf24c5e3b2124f6275ea3f9ab4f0f3393eac7606405653742371717aef2bed00c867d5135b800000090003891a78d433b9cff782fab1b12d3488d4d616f84cac4d51ff1206015f7f2bafb9309521c5425cedf5e4a054e2783c4fd49b54f0c2758c72459b04cf8f3d217ee25ff6369aa0f98a8a1f28bdddf165b0925b0db8f19a1cd1528dd5863d25ee007b07e136a6bbe605c8b5ccabb4e3b8025bf5fa4eba51c3abf2116baa338643c472c3f67fcf8f86ee1225e2a68acb8fa000000906240c30ecc2e1b81aac6375b7624902e826c7ef31eaa1edbb328de81fa510d77ee3984891fa3e065e55a11422a20e7837fa5e2dce0fc007b6b42349a70fd49ccc7103ba8ddace83f4b8becba2cf709c41e377e7c37de9cf6937e975277b598a1b1ed960be7f1cb1468085246647c5c6b1d305e6c6d0704f6045bc8d0ebef9527ad356e325a7727dd1981ae243eb00111000001bc000000900e29e1361514875a00ff70111ff79784f25cfdc3af735a5e7b74e934051ac4ed1992c3f8578624175065276893f775acbe3ab7a307f6352f37128fd69088715466745dee45361e1943a1bb42c79a4ac606f0dc81e6229de80293a839449cf77b84e6b595e9098516dd479ce5ad0d99680a340e7809afc785e4b448a669c076c11aa06f70830474b808079396468f886800000090543bc7a8713ef2d048a6b7d3cb0420b27d2ec2d1cd4cba84c7f8dc2b278df74e392acb5ea8f63992cac9d830c4c41c2558dd15068d18772feb80b7fe078fa12cb1ac04bdcc5f1f13d703b8fae3e1d9391bdb20c632ee66855a0f9797e82e2ca6c8e82b755fe7ff70343360b46364296715903c0f9332b38d5c5f82d5c2678b7be0a1ca117998466b08683ec43f685fea000000903d102425c2f6a06dece97ed3e1024405c8a4b6c80704c0107dc067e4f0ad0dbce5a9c66f477d45adc34a0c6aba10bd89ad720b07508710262f0bc332137a513b6ff63a0de6dc5d1c835dd8fb7b12f0072212f1ee47da9cc45894cf5a987c44bbf1e3d4d135d391d04e725237805b4c1d01c79918589ee1a68bdae93bdc9cca3150910959ab78f4ff7eb936689bdbde9600000e00000001bc000000904f9a864e690819a00fb8224e5d3155788abeefab5507e780edf2a71530813c32f89f2cdab2818b50834af7fe6db96dbf98be1b603550bea8f83ee2e9ee48276318716e1b76c0d7ddf83b13cde5c9b72a29449b6a53fcd372c9435bd66bfa1f1e2ad6f334452b04ab28e9140fa760652b266637eaf4fd6c15199410e0442dfd6853ad8039c839cbc38c8f8a641b4557ec0000009053778bd9d306881e6437f02d1c060c6ac911dc6dc08e14dc2fe7d07707db0bf6d1317e435d10bbf694eead58bd9f247505f45d2474764f9484f51aa3d47b9ece0c873be7a8eabfa750681261208a3fbe1abd5040e0f3cfa195fa47e57c9eeef7abbcc9e44a7a410b8f642a7f73172398091c39940744b315f2bb1aa543bf2300b7df1f145e51a9ecbdd7edf6c1f940ca000000905f8c5f38e2cd1526e302f63617c4c9442d7a701e64f186f4643495bb0f2225f4d447161caa374675056fd347cfc9c326abbcddcd2dbf7fa0a7c65d66a00c9267934cf5024613982dd220b5722e732f2b233018f448a53711c9125341245ea58ccb0319c067fcd85345e2d7c01ddd145017d6eb1fb8dca36f5aa255c7d6cf5d0fbe53d2280505061a10fae56a8037f9ef000001bc000000902d323435155354833a8968b946c70a808c7e5a0c957d741deb6d5e5a8de5a16c9c4e919d122e71018820419b3bb3ea11bcc20004fe62fea8d2ba9fb60fb876b820cfb3f9b1e9a4b150ad8e068ff23c2c18eb725ef932f7edb2f127a03e5f1e6860b836f2bcf6d158b5436b373e26c3c5236af647d18c743d8f4d0cea5c99dd134b47c98959f3e21dd4d432e47a953b35000000904550746a5407fc155dc61fbaa7fd755c1eecb77e8436f97ad949b11abb5620d73e5d8e0a4b17f134538a6acadf3d0dc4002abc8ac8e3346942c62d5987689524de70ca521b9138cb85322d3d139eb015024d269932c7ed0293cf0b10aa5e4fa1aed8b334bd2da9538887291d4a45908123521208b53e8802d8822973c96d095d142847fdd2647c4096490dbc21b89ddf000000909e6a9f692303985214bd3d440f42da18221f968a9842001de8589c9cbbda59b89b0f072eae9806c576ad4e5d36ce09dfbc1ce7192fd51263bea155056bffab8c1fe28c1d7df7cfd69364dfa06463143e1c23fa6a033cfa7aad7ae3d3ff76ef9d9b02db4c233d7e2e273e1363d1aa88510e897831bddcc98055881aeb1416d3a6b7530b777503aebc2e54d6ab10c37d0e000001bc00000090d58a0acfe904837b752f41eb33c3d959affde56bb5947f8e20696e0c68d4e953e8079930620497ab7e7aa39694f7243ece745b53d450678e9f989059ce3c00a2fd2fed4851f4a2981e532c924a4229080d331f051722acf173ea3580dd246afb3f540af9e403be1f550f4ca957ad0ac10fbd4a4d9a643a1f0c6128d7bf44ad7bff2e84cc19ca5db71575b9b52076e5c50000009097a819c4b0baca0937186c55c7840fbf353123c45346afb6366ce03ed1007e6cc6425341d00e7de3f234963e14b1f36990d9a08170dae6b366243b9df8b9ed34c5b9c3752428ebb827164ba27345ac88268b66690366970da8019354582c203b0c91e290f71e0eb314c617c084af18c718f5896b1fa2e3be304b1759530a4b0da0a57d1e5e9d94e96cbcff3ceef39af9000000909aa7ac7b6e139c864a92fc2cae4aa465d428eea35dc90501d60b64d527b85c9dcf116e257495f8f3e4b5ff275ada7c77811fd81e16090b33968a7866f94e4b9e1d1ac3103cba4a1a96cca7a145eb2a9a17c67c30e8843e28fc74702e7701391560da1edf7efe2ccd6548ded4ee8743fa12eba27029beac848fc5cb5542c1b38078ee96b9ff5e44827edfb6abc1ef8c6f000001bc000000905f457d742ead95b5eedb9b948f86be096590d0e8e112383d744ca7ea0640ecb703cd6a42dc251d0ac50a65b6810a2a330524cb21817f1cfe8ddb5da561433d545ca5d3867cb28ee6a7055e620f80b8b40f110d07b3e07ef2b58a4d7310ccb1c9499fd0763aa7ea99229b8c604f28ff02083622ba713712c31103eeea2e1ab0789f2a3fe03d131652cd452fc538be8327000000903b8939cb4709958428dfe265eba9fc58c4a72c3f3f9a5d685681f1828c317d44769dc018b8aa017c3f8a4855e885bbd1b9104f4e12dad6554c86d6e3d68e3b10b81c1ba7874847edddfb85835398930d2e965aa6f1a9348fac83cfd8907d032082fa1832f0e6e9a3370849fdcc2bcd0502c306678c28645be7661b14f7d195e89dab565d436a55c63b9874b820790371000000905ac1167885ea7eeaad310ab9384ef3a85345c6e10bedcfa00f0eddf1d2186ce76e35d94dee3701f21a340d6ca6be5f5771816c78dc7d4d9642a0360f373c273b2f85fe07012da5142754ae9b4c1e683115f318b02e54319c3eec0a1f57ed259fad61b8029918bb04abc88d5418031a472bfb38eacb9e6aafef22d059b2cad66bee5f27951d7653f1d5e6a1da7d7fad25000001bc00000090186c54b312de2502ad90df9d21c0ce039879cb91c919fd22bb38cc261c79c3f6e465013bfecc988abd4b69df333a8f616787e0280e4f87b70a617e4cafae0780bf83078216e35d3d331a1ee4ffd020ad2448a59aeb1b2758f92194e6d3b81afc33f106d6479295451e96e64ceee3e51b1b94bad364c09520d6cc5227c50aadf85f82e0c59e7a3da5967c45f9292f4e9b000000902ecbe834880fde873379b8f70e3f0de3ada567546eae1c9c9af258f4f3312cfe439ee227c5748230dcf227580b6072fc17e3710b4f156cd877bf40f88b2ad26955df72281a2d2cc0754234c05978ad8d0acbadc8f9797ad222723847294785e8e17c28e183001d281a7ed57aebb00ab9016cc25d4e89b4fe2fca8c6454acb10a7ce5ddebd7186f0dc2d52682ea2d4d4a000000904c5c6d664fb12e3471d405a8182e87e91443bcf78b545796e8c73ba29651af4e98c2e7a95c9a9c46dd5d3c8df95a0f5439fb2b296cbb8e7464ddf15c389d2d235dfe994da79aaf9cf6891e8efbd94ab51bc8f73bc8f10fa23f0bda83f9be9e76c25e2af740f2bde9b5b0a0f76fc5fec12e6d9e7203dda27b27eccc42fac592fe6e5d51d991e51d12223ce89f4394cf1d000001bc00000090eb34f161278463b602e60cee956c56a68b4fedeadcb8c77685bdef0b7ea3481ee297cdc07da286af421064251ea71598cab671f1f4996eb1cf176047a6e89c2aee237d289d7bf9fba91ef7799d8cd9bc0f77b2dd8a32a1f2be262f3a001f8cbf4435ef612d0072197f8615a52ff7afa713cabbc93c14bf0200295cf634630cad118a1d988bb10fb3c2814e80a4512575000000900966fca2b9d5cabde2a66d5c70d08172f1703507d741e7a635a4927fefc4ba2b09ef5eca8fce696413a95cd9334da60f7a9862cada5a9fbd788347dd580e99caa43968a6255f5b54610fa3569589a1690144414c7ecc579815de419ef0941b4937da5977957a745d6796ad2aa1383c3e2efec4365cc356b9041a12b290c5d43a3f3b1d77473d597d8a20f23190c05952000000905ba55db35d62212a6e561944103eed603ce9c61a94f386114da015a7d1b89e44aad3a53166f650e9c0dd8587de37c999d8a156b9e17e75d24e0ab4dc413ce6d32c62cd6e4e6290d2242f416c7120b4501f5ed62312005fdf126f018799a41816ff5d0f2a7d572b3af4b6b1d7e7b675931c4eb811312060cd88a5f00013e1b4a3d76e5eb6b433b7decd9525b6ac310678000001bc000000909c5a60a7ea583c9914b81f31779c244ebd687884e42d9f434375722df9f47688056b354b5f02d3130b311b15fde7e71c1d0f7b69135a465394e219e1b0f462b8344e9ce29b417141b88b358a52dede49157da2437df6eb92eebe8edfc1098faa7508c414e5e237ff9c15bfe6d54eee2e1379a4cc167231e2594e62363d1fd43b809995da5720160806ebcad8476c302000000090791a797d26384790a3b36b6dfd89684f28427ca87c8ac83c136294e62cc7cb7a0e2ee61c1c018fb9f78b843380e12c204d4c682289c156895077350ddfcf62fc6e2ebba789d75a571ada468b288e1ae325d5b798db527e7c154acff67a5aa52b91de9983e3c2babda6abf48826ed16be069fb8d5f43234514971c56160f0eaa805feaa5ab8b4b7d58938ac565f95ebc400000090b491cb9ff91c3aadb441623a1b327ec20de34e448ba16d5da4d6fccb99ca54d5c80a8e12ea9d9e81b94363e7ba45b400cfe959dd46886dbb8bd09088761f5b757b33f2449a59b691c5e43b83a89199780696c2991cd1ee09b637fd8958c78579f96f410cd08acc37882ae9bbeb01a78a1f8ce2cb9c1c44496e26f1d5eafcc825b6e36e060aeb33721ca8254c18c3f679000001bc000000907da33c299d26e34eeea01f006a110d5eccb3bc6ee359078ba7db61822c4d330f15fa724fd856f7c52fc1a1b159e48854fc4acc31e2441a8166c09f41a36e28ec4d28fa82d26b0df515cb44877d025b4e11b074afdbdf6ac54b501156d075c5cd2e335143b697d8569bc3f1649971c5ae1765b97b1f3110f32c247593b11f0c469a3eb837b01acc06cb42e7931a81c8f900000090a30179aa3af06f3d0af1256b70daf420df531fd45f1e5b60e30ca87df4a566a56cb04df18e8dc75c574c6159d1f0f1d16a3099cd70523df46ca4edd3af1700bd81b12d34570d92c895ac8e950af5358824981da13cbf6d40b1e6cb7f6ed01acdb38a2e1f8289ccce83fc22f98cdcedef16d7c75f6015cbde52df1f8a398883df68f7f9b83f6ca7db9225d10642d7ae55000000905973c0c6d830bea7f2109523b3a3f0e8b3a0805103f755cc55508a06e6ef0fc44848d1a6cf36ec4457b2579d800c7431ef9822db5565036654354475ef6dc5a4cd3cdc5b0b594dfdad56a2a0c7dc59162955f7e27e655fad747e137f81280b1491cc7d54cace55f4cb97c60e7187d43d1b304492a7615c8d2acff874287f7fbcfc083ef81c5c85e4805b0ffcc2c8cab3000033a000000ce40000012800000090d1905502bfdda3ae7a7ad5b8b4f2f0eeb59cc1752d40934520c727ca05a2572c2a79837c6fa647a2b0fccb5220ae8d669b56bd4a829b9c0378969a2de759580adb811b9a2f86eb5dc40eae174b3cef972ce9bdb652ead9df39d088644e4a8333c50ba5fbef3db0539f062a293a583a7424e214b300e6d33957cb37a88d766ca117901e7bf696bd7a60fc37e380818b88000000908a835e9e543b4d3dc26b5fe41a104021f8cc76a76dd6561a548208afee29332100a35d0b84337985b2fc71aa91c1dad221e06bb0bb66101d07e6751a1e6386a6929bd87e97c762dcb3a9813276337489018cfb72c40a18958c16531796042877664fe5967b33b990051f4fca9f81941f18d2191edf8b4de25881f851e771443597f13d2f6178afe3153201fc53e56e300000012800000090c80e2c8ad9f0df4dff2b3cb6b1cb64ba731bbac16c1afde6a8ed0d80f2065d89bd5c35323ef4289e6d946469dd2135c7afc8d2468e2f31531d4fb75e069e8bb47c40cd63642a6ded6a1f459c161d399f03217510b4e322e452aa0eb0b2e1bb5308b66d500a96d95ea206242f55c2d7910b72005d5c29786648b9909e5dd36fe31f33f04a1fb8ecb5a8020ca9b147d7e600000090b7bc70b9b15248819fb334bb519ca71b94961df9498d1bbabc1c4c6469ae6b9bdf953134a2ac9f49a77f599c80524b1fc19f09fc843bd99d52a0c33f0ef8052c6a6dde62a1d0488e02d3b32831f5184c27050dbb6e761e1a8df2d604278b19fb23bdfe9c178d2c7c076d804852e463db1b60fa77e817d9eb5e847941748a284750e9207abe6e05b5a50431c8bd6884f700000128000000905f665b3b5faff4c044ff5ed2a885d0bc4f22e6af15c3fa75e1516ae0a9b1105ae7e12c261118fbadd5923d5dadc6dcef42785069f58d10fa0355d690fe8a4b558add1498cb1085e6cf6f80cd22ee2c781dbadff6377db9efa8a5c3ca7594f4dcce4b7244e8d9597d4ec442e3b24466c2286ad94eaf7a5716277fa3c981ed52c41ea5e92220055d44b8d18bb37d410d85000000902c7bd784116cff39b2adcd15762380929ad0e311e8365944633b0c3b4256e22f12563f51cf6e7060b40a36ffcce0272451c19b5aa6acc6f59e422beafc3d835ad508bd46d6214382211a4f939a2828271c90ae7f1ac9db3e576b965e53d5269cb929a6cf72113430eb8dfc4718acd6ae03b44eccc4a1778e75156ef832914dd19e87fbf6308ddbde7be1d66a6e050050000001280000009079abf20bd2d8f39a4a63f3d567e7d90fc09feae46719f58811c4f23bf8daab3b8e203a8798e48ee553e517c1dc62bb7ae71de60d12b7aa26721a991d93d8c50eced62dcea7fea4cf7579f72c124ae23a2b9a9151b716671aa2523847c60e117137238181b391927c4b7ebf690c040f8d1302e5b280eb51b772bac3f1374c5cf0ba3a4cde90381af01f713e5f74a2988300000090d0e251b71d84b164fc2a5e92423d28a1fb8790716823ba4a81370d1bf80858e853be3ceb93c8ccd4159a1ce68aefcf40bc6a08978007e8720e37b8aa46326a818c4dcd5b45024a9899cd6f83cc74e61c195bba4b7c8f0f8ad74603c97fb144a2dcecbcf3646087fe1ac79354fa6ad92607b2879cedac1f78ec1f294370e26a2d3c99ccebe2aa6cbc11cc76bfdd99510c00000128000000906cffc8d66ecc763d1f9bdaab61abc993574ed1980e688551a1f473c3bae409ab6bff793f77c5c6243b8836035548c2152116b672a63f55681121fb0598f7f34b42348bdcf9f36e590dc13e39f75d81001f6e603a0e6d781a1aa6ff2e99e983e2929c8faeb88735a911d9a2760d28560e16ecfef2d1bb9f2ba04e5e6b3fd7c2c55fe03b3482bf4bcd253f9cdd882ba97400000090f83c9f1275aca4c32c8d8cbecaccaf943fcd9dc2ed6214c6d30139622e9a4ac84e3a83b18072fcf6b43606375622527e275ed31914205cdb2e547719a8f3a38f734e31d1a0321098613ac9ad847730090fb5c8a755c78ce7801a217c95529fc2f27b12032629ba5d6dad5f84391e150613b8f2305df4b0ad9f80d62f383d76ca5c380c371f688f9009e1e18785ec63e60000012800000090129dd7c39bc4e543b4228ff3cda6d81602edcfcef4a76d785d675661da95dd6c5b6aa104d9c396cad8052dd8e2b0af4d9fd666a9df0cf18a71495704943e84768bd1536ecff3b070d774e25d8abba0ac118f9a46a272f0a052925512be95f59604198c76999f730f75489194f0ee50620a1da9159d2c2fec13f4889ff2504f1e7de758a6706b82430bb66f3df68d73e3000000905aac7a384a7eba4c579bdbece9546dae1d26c942d4539e00df3cf77b69b9ee876b91d63b196c6c4b4ba1bc9396a1d8c48dfcc98bc701d4c0618ef54d3e1ed188447ccdcd8d733cd051863976a2b5e74112f08274aee099890d0575139403bebbae791efaa456c5fbc66485add46880c92fa6ff5005ff848b61d2e09fb25d81d9e08563dd63740af23d49f41035a43d5f0000012800000090af9a4e40e8e108a8cdc3d51fa0a64f553948d41472a33c9466a401f8738fa4e8a2ae87075b434030a1e55bc88f6873f3eb61556c755008d7d00867ac7aa2a8041042c16d36cdf4965ee358af7c2e8c73224490fbbf16e53d62c2ccd7b1d63bd8302fa03838f25210693ca227678d458b13f4567ce36ea57348b0272a9145dac30b3ec73b71dfffb64e7112a68ecbabd80000009031ddbb3923caca21f9baeab0d4ec4b93efbd3374553474fc72beeb1f442fd2b5a67b020ffbf783587bbeb6bae515862f13699961ebbb671dbdf9ad5e5d899d74eb50800e72e41e26adcbf02b7ecc3e9e18f6d051a7d618e7a3648ee7e30a78033e9ecfb4ff8de40608780cd31820e0e6001da3eed13df55b1d49aa277a94e02a49ea6443ab81f04dee97d81762c9e5d300000128000000901fbf647989be185f76f8cb2576048070b5203f324130e5a2a4047c8ba5ce9f3ed3ad818d5b893bb4fe722d3316561e880518a6869b8726022f3bb75a1a3e4b87cb52ced27e8392d4bb5052f94adac58816f385c34702ec0280952432fe6446601176af2bc8bdf457c5bd3fa42839935801d0afd78d27fa50c09d735270053dcf43d26a76e7603dbc7016dfb03d29da4900000090fbd06a73bac0c4b1821816265b032d2b116b124a562628409313c3b0fe21651cb6d73be44ea7ce6cec081137274c91cd92c06f255870a549544f790c1ba7b9146d498c69526a08a5d21ba8d6b25d067e181ce6e311139c9f402f6998d3765a4c7c7ec592ecee5b0e0b9355dd012b96042d81a176d087d0237db9861f7ede02c1f07adadbeef2f419eaff709ca3f95c850000012800000090d7c2bcf98fec6893a4b74c68ad3a0309a5f71211c8e891e6b535eea780dc673c2781a48323f8ade10024be2601a81d0671032e74823f2f55d5b25ab4ca3bada178b7a981c9d1a856354c745b4afc2726255099142c0e31eb2a5da9d98adfcb46d1917b7d84e1e2e77d326987f7cec355112bf6c26d4ba0395ead9f0dce334a0de4efc64a71809810918934bbcb6a461c000000908d7516bbbaea081bb212451b528b8cc86bbce048db2d8c5772ed87ea9d4e728aa7b61c87c57bdd141c6ff39b05b503738d1d28dadf5371b4b060bda65ba3ecbd41be6ef111cf57b6548da2032f0a191a0bdfe1b27d90ceb6b23f04f8a5863e126d0853747b9b082b487198a4920872622af67ae94b4f3179cd5469b1c0bafd8c02b42bc8799e6ae4afb79ea72cde4f86000001280000009019b7e77c5be67c435c76291404bfd10b5b18466c1dd37f8a52fc0977a7bc31f7ea0173215fc5a448a2d24248a18fc60ffa2fbad7d8475951d6e1ec4462bd9b9319c40dfc3790342da046c6a010a64445226f2fdec5819a1fa19020cee7ff19b68b52e988fad1bca1c3ce88a74effe63b2269c7f78fd7d11a7cb61676199fc26ed50acb3bcf72666385bc6dd6da8d07cf000000909adf698c4100086a212716abd6261b4aa923bbf41c554afb8aae3198f79902d4776a3d75a20439ce0799cdaad64a568ff3aa4a057cdea09d83b552488c55c4d634f0da5d5e63cdffe3cc903cc59c80e90b17fca95c2cef6319e776d6ace7695299805c4f888d32edb5202a3f50fce54408244857ba5f6d9e700c2c297451d0ef54e0bfe991e713157d8e305aca7bea27000001280000009092e0543de57e6e227efcdc03e8082029df983f760b71e48e080644b0762ef393426c17c75b79b19eb2d987e60427498a2f56b3d71ebed4a80dce9b312f85db8b05bf9c3b1aaa38518115f75edb89ce3f0a5f1684b6f8543c30faaaa4cbf2c2b27c1769a0a0037f908652f979184d15042b209b37841e256d093ce675fe2447c12c71c9c1484a1bdf489438bf4f1baba0000000902cfd837a912d0bb6d432ab447303d975d1bd04938a49a0c7c64e5ef58fbd6107a46f190dd80404e5788c51979b300f162d1f7ebe4f8cdde34303cdab18fc449cc3dbb3ab5f4d4949eaa49f3749ef0b0a0a1ad2cab1db5b5ef6aac87ccfbc70ea6ee97aab13016f0bcaef27378c4b39040b64ab2ec04016197e4e9f022caa51e9669167200d6da163e5c2300e8c2d1cf400000ce40000012800000090dc587aa4332202c6cb7166c034697783e03233d9ee1f3ee7e1c6602ca72390d529effd755655c207151e09c767522f354e091210eeaaab36f6998df78cf65c65cb3621b310c6b6505789a52beb235e7e029d00de64046fb9ad323d2f31a6910ac78f3d69b3ad2764288711682745395013073a18139125a385b39ab54f8374c6ee1c3a75961eb01724041da6505c2bc600000090bd5aec0b649106c5b0b81a92f71e7f5eeb670ff3d283b008543ea0724c552eb415087e0d6c6f62f722f070fbe94813c5bef249f71e83f71d73760f7e5031b2b8750a949a00a8f9e342eea1e7ead9b8b11330cf4ea7d3dcb98d64b51c884b40530f9c27878169fa4e33a98ec717b24f9a2f668ccfa8b130e6eed8849c5d32fdbb945187c2b24c7bbb269370eda993061a000001280000009019a2be31c8796821dd876846233d0f72d8d55722d08d6300005ba90da68fe75f9937194533834ece95eb25b6dee9e6567b4b80daa63b358a785cb86c8f850cc669ce6d7d30a472e6fe8f37976740444a237232482c8055548f97b72df3ed625f0a4d0695bca06183965300db8badbf6126f2ebc128889d1bca33a6bf88e4806cc26e5966190374f94bfafbef0d21ac7700000090358babb9cb8691971aae68609d38b10feb1d2a8190910d7422a5bfe0aa58f0bc6783ed56287ef8ea4741bc1def75219b838094bbeb8c21476fcc69256ef36b84c3a9d79395aac2d19d4669b1641d3cc721eb517e6e92fc0e0236e950cecbf17d9dd8a8e9670d2b89d2ddb1b6bf65397e2bd643ee46aa88398e566c55e109086ecad8d1d56fdcf79034f6431ab924ec36000001280000009029aeafc335fa8e95ae74da73f71bcc7007c8c33b37711c8214e447120c37746dc97e9c65d7960f7c5e7320e75f79087692feae942b353a54f1165a16d120c271e3e5af74a1e0b9e0f1cd2c5d31971c6e1385483cdb8dd34906ce5d4cabf56deb0e276587c32de7c85e15e5bcad7f2a6e2940acd0f5fef55a9bc98c8aa09014379d83345635bcd48d908bc883c18b7a5800000090881a9e6f7e94e86575c91600d2abbe813a36c564df6b2ca8ec27b51e6f35a0bc4f8d68d5c4075d0e3e87a5550ad353dec40eec028ee1f5b42a520e1b139998f992df29f03daf9d3bac5297de8099d0530bf2c0036b5033120264a9502338f229edcb96a01f1ec2c924062fa313f6cbb02ac0914620a0d5a97e6ce1e9cf0eb7c48c7543a298759b4afb437d2a1dbc48e10000012800000090e5091f2fef0eb8df60f95f8444f9f7745dfa5d699d306ef4836c64e2b98268a7c6fc399564d4d49d1ef7ea4a3d6a7df458a3e257bd1393be9e84cdf4bad769a6f3c5f3c2150a019b0b1c4aa305f04dcf0fe6c8650f71f014dc1e381be69eea2f1e4fc0d308a81489492eb186a6ea3bc31e3b360147677cbce8597942a4dc305a1c485469a7e7920dc1a74500b62b819f000000908cfd83bf0d0ad99129f802f98bf4370798eb4215dd86510f6db1b8ff8c09f3581d2ca57cbe44d237277b457e605a67b76f8da1c39d06bf41b904bf8416481acb03da65940038a83f6ac45d792f2902d1207f03055150bb78d277e49122464c205b4d4f7241ba15ea532aa8cfa65024f40785127305a1449eac0603659300119487f5ef065d8fb4932caedc4c7221503800000128000000904654a6046603b6cc049242caa630c290a5f236bd2d8117f2bf709457025220fd94aed67dc69920ae0241d674e5fb16bfaf405a151c3753fad8f015000da5d3b0c6d4488ab3b2cdcb3cd53bc2c6619c6212e94f10e7b4c7b6ae798d3a04a2babef49dd2d2973facc3ab135dd446b124312471e6a515f341cbf69b4daee6ef8dbab575ce788c1334d57ac0ab27421811dd000000904a95ea082c55c5903663995a454090cddbae42a173c3e14a3e9466f1d4f3b0506b5f4e32c4ad9adb958e2cec97f906733205b216c3206986a66b55770876ead0bad7ef67b620af8399486e235ba7be572f3e3f38f9fef355f6a30c1ca79e2f079239077524bbb3965f19c9a608ad1bb00c6d5ed4acabe3703e614ee07d63f66be71c2602f5d292563a3f9375547b768f0000012800000090c9a80869eb7414055e9aa60e96f1426c1b26926be0fb1e5096140b52e3d9a33367e26f6ee80748faa5c7c8604e0d428b2af4650ac06e5054ed70bfebafe027c1742177bfbbf147042efa25bc3f4b0f882f4104e4968239e13bae19c0a2cf12b1516ef7d95598ccce1e7f894af04196a41f90e78c56e396e36b56d6ce04195c666002f970c065136b7e8380e6b3e9a42e00000090384c10ece0a90d1b7c7ae0fbc69549d807ee6d9e6639ea43192eeeaf9ffada9178b8f3539a0b5dda6ed1ffd3841f3c9227c46288aca019c1c78749a335b596bf13fbaef22250a906482065607bb0b8a72a5ba7f0a2ab4776669acfb5f68a5c09a9664a076b05cd6f46b31552fef89b242d893e40a89ce617bd3e798cd75923f34eede2f2d004b755a3fc0ae4cf811b140000012800000090f07b110e3bb4dd56f549b2e01aedb0b50bc6c1a46fbcb0276d3b680fc2d0a3ba6d07eedf092c2fad2b5ae450b2dab4d613f80a51160f3fdf345396456d13aa6bcaf2c0f81af8423681a92a5ade27d5e12a8391d3905270565eeb55eb99fd39011ee40586cb5b8916ebb2327a7f74228a1a5768b6748494cc156fc28bdb52d9ff1a396e8651c9da2c109175a460751d4f000000905c3bf40c68346b39d431671ada024f4a21e96d7a110953ab1683e79f49fb8ad7ca1b4a4471d163b128b70b6c1e2f7220e03eced83cb2b9a4c50e891e9c4d71700ceb04182913ad2c4b80c566592b501d1e49d7474a65b36dd1f024ba26e16ee454588b8aeecbdf4364019c0a781d121a04b1066d2e035738dcc00d28952630943a12abc1f9d4d1a680901544efebccd70000012800000090fc4a6fc3d816ba0a3885d6a7a901279f62af6dfe8526ef236a4ba9d60149099cae6b220da594b763964d15c366ae002c8316d367f909cc0033d132d4f22eff1b8a11b03c60557808da6cb21cab9d65cb2c1b4a28540767020a55bc55081c5227a5e6f949644f7c86c58b080d8ec4b99e0f4538bd25e6f274978b2f466f009d74e3376f76c026ca8e1bedda1f350da157000000901456cf4dd8e308692b2f994b1942bbf8796aaf378ff14a25903446fa8f070df54c78c017bc78c03aa89115f28f9780743aa738c99a0a150901ce43fd25fcf66ecb1efb1ebb56350b7cc4b86bb6c2388319532180d1c6eaaa938edd4a86916d7b89f43743bd9df883b58efdf9f52e1e350c553c60cb3b671365bde8451bac3f042d25f2c828a8c923ca3e05ce4e8492e200000128000000902903d864ee728289e1f7bf2ae5c9c2949b37d4baca2574a25d380863967a41d73889662558670596e5d322b22061e70cad78bcabd5c17daa5da2d530c8724c1170fe843d83a4869ff8ffe1d6bcb0f2a62c7b8b3e650cb59f6ec647609994e2bb9e96f03965b90f7f89e03f2724391750019e9e76541ec54f958c27fe8b037e99c7481630a92579716879ac717faf758900000090cdf542f37ff778bd9a658de2cf9d49e04f7648ed16f0de2a7c1a64c3b2785649b08baff74fa6cf69cc43acd4a7dd06db48c354c1c4b1431d69a1b55ee89688142245baf3425b88f0f693221ff0ac1272272417b00cd9ed69a300f5f7bf216dc6728865eaee008ee7813b28c75287a74b104cc6b8b7cc79451783d94c59038c61321b6f502b3d0ef71729cb4d54ea1c9e00000128000000902d8a00b5ccfcfb3a4fe9b255e32d5271b8509c43c65aa456e4f4f339cf7154d3fec1b33c295789586216369ff1f949018f880d539bceaa2c7dffe06a886a6aa98d441644292eb57624eaae712a9e30cc0f27bce54b31f47e72ccbb8065093379a245509991c1eca28e60a1bd80317f971964ca59e07bc005f19f438998fdf646aebc6d50fc9806afc8e5f53dd67eef1400000090e7c284f83939609ddc5034139cb7427f2749af0ceda579468f9f12f479d7635c42ae47924ba8d3ab720843b3965e8538bb4d3f5eec343185ab072852a202a1fcc6c29931b249290af29f909b1fd905772f21bc9db8b7e15704662ab924263420dac87f6596d3bc4e71a46a1cf88d4f130485fae93bc967dc32857b8e01d99ebebe50fd7fcb2dd92faa1fe760bd9b117900000128000000908d7ce67b739022c93cf68de7f4152edc7e66e6f3143d1a44eea1b64ec04df99a9d771b30c172a41107d84200910bad13432b7671508aa27af24c750e64002630619a0b65e54108c7cc30ddd12d069e940d114eb6ab90ee9be749a7bcb9d3295d5089b897b7293f24e7fb0973ad1f27cc180fd990f7821070841668a1c06d990c938af7a09aff5a53d7bf728d41bcfd7500000090dff4bf92d32b8b2e3a7371871e4f4d65727e6fb33ed9e2ea1e2422deefe49edf5ef2c295a6bf68ff112ef4e095753b60502bb42147723ec52452ab4a37d4f570ca86fc00d9502f948a5c5fa82eef730406dbc1727dc7284df5eb15541c0d030768979f802a46bbe2e7344d675e2e46431e7346c318f763c8dc24f4c50801e33ce39ff804cf65ce6a686e00d6ab5fb5a500000ce400000128000000907c05563341c4d959bb1ec9f9fc41d77f23142b298c4a1eed904d69a7157b0c3a50189d5c5955bf5d0435d27ac263bd4b211220165c7fafffa2bb9509b22fd06d3980a31d6dc535d8348a7fdb18111578097d8593f94d803409b86b58dde71104bd3db6805e1210826662237a8cd46b7a287d0da7441f6e16bca0c7cb632935fd5200946348a0a63b545772b8423e9b0e0000009029f26d7c01fa7cafab88f9c071037f4c37d2410658cdca53b9fd841e3edd61eefc8b447820c16b64dc580c87d2ee3bd86d980184853c62051086ade474138865436b54fcda93feb678ebb58693b3b14711b516c7b47b506e1e395cc1256b24e2c395e41c5a73a71bab2b04106a9984a50d8e80bf0a83ac05f25a327444f3db4d7475e579de9d471b09f10e483b1d2cc6000001280000009070eadeebee733c4c21a01360efc1e965a2e81f0178035b0b7ec4caa6679b944780d48c11ad55c38a024ebf4a0fae56e705f7a00b0add5e3a5cae06134d0b92771242fe98d4712a3a1c696734edfe95e62c60fcf93377e6e5090f9dd5566a389604e8d7dad82361b9f08688c78fcd524716b361cf210f134447905df5f58684aaf92d1f2fccd3faec8257844c590b155500000090c3a22867a8620f0e9cfffe28abac99b5449612bb6fd54c64303404077ff93221b0a6bbafb6dc1853121a38e8c41ceb201a4f72df30772822d0b0bf66fc5dd591fa7aab4dfd97eeefebf334097310658d0b1835b434bc3c7f4d3eba6051adbe7c0a56fbe6b7c99a3d6bb819b99b0646b710c09af243e9832dab652c0c777fb31c637aa7990452cedbbe5bf99fce564c1a00000128000000905cd7fd8043ebaf521a9cbd850967d37102d09f6578502cff586e78e552fb106abb60f660939c2a3a3425b9124b497d6e6da586baa74107df8af0dce982ce18eedaf56291a4578ee8d1fd147f90dcc8d60c0bfab51bafd8a24ec632ae09d3c9a69be585ce4e2ccae10be1fbd4a9fd76901e434cc1de5dc161de968aca244423a3a0d210d9d85c4518392b51aae13ff5bf00000090048a99423dd6dc09bc6ef055ccaaca0c591ad76d5dd5ee848580b5d183ea8d161b8ce35ca9dda3062b1919d347cfbc77c010a9a05a570f66bafac197419623b391d9cdad6621d9d9231537da704053b228d75d4f7f81682b8d581a30341a1bfd728965b622ff550008bcc73449d31f98098c20a8cb29424f1e69839742c2f17dbdf6a281d2f5fd28fd7182c77a9bed44000001280000009062a63d4cf8d7e88a831a4b2349f029df81f36572692d59e4a00a832fc78ac0c7d51aba71d7c5214c15806414e7c67b80fd6b87f0f5b7fdf255ec1794892d916eace9d91a8c6e4b0a90f009757b7261e8117d0f9c42a7e9b8b32ec9a46ec5fdd90e1ecd316dcda61c20e3c6eb9a8c850b15b0cdb0f70fef21bbef0ca1e514a28b1cb31871c75544235aecbf71a21c545900000090071d96e7f10252ddfef08711ab174fb56815d2886d3cb0ff770d8f271ecb28b52c7538cbe64f64a2062338df714fefb0dda0d2c3bd84bc961f7c78ca64e0dc0fc14aa1461a3cbf46c7a37970035fe98228c639c8b16c7b7a8fa6da42025581728142ca87773a67db46c28d7010a37be02948daaa6e9337189af4e0a7e16bb81c2476fa89bf00293017e757bfc1cb61310000012800000090305a5f090659bc284517b422b4073f2e32dc2ef731975ba67b96b4fcc6c952a52ec5cae273e7baf8885dd09d34cbc26a1ee3ea3498e3b5afe3f6e1aaa16bb544361a14f28cfae1a432e6b9ead74e18c12ef26791a8be4f0f7fd1959c0adaf46aa248fcf9c8cc776a54e188a4359fcb5d20faa413d68296ef1cb4ef274d42fd14aa5a92f37da51a0a6ed70ee12ab729df00000090f2395c0f619282673828c1636b1ea89a24302c610aa201824597322290193f759bdd5fba022e8545ca4e2edf05a40e97a5f41c18c06922a6a51dad573c0b7ee8c86cb0d290cbf2911e85a6813d7e11a20113f2dbceda944ff9308864ddbaa44d3f4c973e8ffb2feb80ff262a90773de82d90b97f239e92bbe51daa611577e7106038b7ceee0653fdfb71fe139effa92f0000012800000090f7312fc5ca7141f83996c597a01d9a0804913e7ef437b745189b553fe644b8ca9feeda938cb44eba925da1e7013fe33dee342cbe9e0b2788b7ae84ceaf235b71f94d9739b3227aea90a9d5405c0842d20c176184b55a38c7b0b8796ba90b07d1c6025fde1202f338cb3823898f23bdbb068c8dd7bb497a495e16ca21540e5f666d72f81f84943458256f1752121363cb00000090cd285bcaae2aaeac2d03b8c6956c3d34a00713d06965c991537134b6e893b5a3883390755fc103d92d3aa86c12f5b4442996d90dea03f021b806587046527c81b2e9f29e9187981dbacf20c462f6d9fb24ea5ae8a3060d7086221c3ab203624d5ffd1f0c5471d39d8abb1cf97db3faf30903520eadaabc77bd632031901e332f9f59eeb827ca7c2ac4cb3fff1cffea7400000128000000902fa51fbf4061a5c510008fc8f50dd7bea7e307c536d6ac7a1be04f63e350854dc001038d15c85f02046cb4ba99cddc1d0854f732c3bbd6c1a2ce3b9539b09ed75cc6799b3e3f7ea1906cb3e19b680978172d5e3a17d534611558b0974f27eeb80f11777d5c318b41e6844a205d90fd101e89e3a3ae05e2e9d335f38993d1ce40886900f317fefd077ba81368619d3f720000009082b1c27b7a64487bbf9d21871c878d04d6ea2d2a2a02373731e778750c81b8c89cd49b77f4af23ceae879f455029fcd1cf1dd56e1bee390dd3ec7c4e442c8a3607aebd50588621cae5b2e8e39b595f0c08a2a2f9fd73e7ce1bcdb28aaaa2a69bb3e85c20b58c861c3e158d4e106c785b0d65a543d817766a0496b91a90177b11a021181890c57d1bbbdc24f889cf1b62000001280000009001486e187b12186066c262477a0c9b4d03da45d3fd17af3487d985c03e46b03827b85622a2d8d00a1d8d2d07a84334cbf46c2ad6237afc95777824fa36a6049a568928d6a44f95f56ce61ea7457676b808c5acb1048ffa52e82ab9c059105deb84d041e13b132aa83142025293d9ee5607d19c7ca42e4ad037fffc86912010ca66386ef730cca4fabf41f7c6f5d13d3c00000090b7b2e46bd11fa912cbb171373688e4dbbbbe2b1b1ad04b37ceed2d53383919ecb1ec5690b91e1b30580453e59ba400bcdb76196d7c9d4927b00729e83ece424027a00e3276a93a61e84f520cc2f9b22209ad7cdd9fc875574302af75edc79983ec50596913c818961ad536298a310bc9183c3356971710818f06d0f260bd55d281087c8443058caaff394fd5a16a591000000128000000907a02b1a361c9e424797122d88f4883f61f09782cf351770c7d5577ad63d8291cca761a30d0a12cd1907a3c5f49470ee3839fc53d553db7bcf7978f5618007f4ea5c8ef5e230de4dd58a41c44a37fd9d129390c2ce362eb410da7ced607affbc53b841f31b58b6c0390d0dfa2b40e523817e47d9632e8e99d4a790f4f1476b47f63d6b90af6afe874347831f17e09820b00000090dfb3da723599fc52984084adc867d5b131624fb3a245e28097259b2ab9b5b60150fa80d7ff242fd0fdfda863778237e27d806cf7cae596e4f7c740689a7841b29c9a42d91eeeb3f3855d3da4d0d6694a1dcd9883ba8e205afe4e4712ef9113b129f648b6d3565fddee1d9202ef11d943145d3d6be489fd048d4626d2bc2ebc67ffdaf751ddb2cfa80f208aebf82aa3ad0000012800000090031c03dea41e91646208787581d5f4e1fb17302c08af4cd0668bc69dcfca307c928f6882dfaefa3f63793669c2168840617186da887e243c32a81d832a7070909d36021d76e58e001b74741e8cf37caa2662b915209192553f3a8171d57594d6bd4fcc3f41a8b27c16b7235ab08e317d1e953df7144645d59c657ccbe4b62ee8d64b3296548d7bbf1563093e87d7af1e00000090607f885db8abe4abde918c437fc0081506c5e8c1f25572fe375e43360c6c1013c597cb2b8a240d62864c00c17ef60005efb0fa59a155efb45fd62f17f862b53635ebe15728533820c500fd561abddf461d242081b026fb75401170bd90373cde8c5ba92a46caa46921dc6e6b71a91b2d2a94d77336cf9aeef2625fe24fd6e30a38a4ab63d1487c217fede88a5da38c17000001280000009086150a0013518186b565e0a6194b5ed3735e8fe2dbffffe3d4f7ed2281729dfa1a3334ecee8ddcebc91b11297ca7faa95d33f42e04e0e504e8f5fab8cd765a62ebd6e207658293914365809ec1497fd2298ad1cfc963aa5be4f2d86833961589ed49aea8df5f799a050918839cb1f4122978dce47fb40af8652e272155161c817cbb6278132d83265032d0f827c175ac00000090ee0452ea31f58cfbfe6862d55fb398794f3a199f3246b25dc4c8e4c61bfdab38f24e55749090130ada3eb60a284a88e294fa3b2e53890cc04849fb7588e10e3c3e4004c852db076c35de46c80a77260e1485f3e68ae14a08291158119ce92e1daeeadf640d5200b1b1db5c0ac2eb06632a83c101c86519295e058572e190e60275aa6a48d510ecdffae2095c6762300b00000ce400000128000000902c803df8807403369c656432cbdce60e1d926579bbba600313a8d722004e1ce464c3497551481be9f557671cffef62cf33348341244dcc7a00192f577d9f6fce776bd7b1529c625dcbdb80c61f2052e607086554e7057fb4303431adaaaf418167ff9fdd6d368d483713bae6a36f271e2bd77291619445eadf6363ef91ab37c95471f15b645264038c8b96b2dcfc8a3d00000090f59f02cfbb765e4a792754cc77ade007c2092e7130b95df0372803afbaf98ed2c5358375a5ee56e7f7e8bbc1dc7cb9243e8266c5361549fd869e956bd4618ed69c66bba2f1ce02f1ff77be776a59581d242876eafda24466264bdc6ac348b1ebe8d441181bde9aa6a534ffd7b446ec6c2bc6cfe5da9ff2c0e01153ad2f16611e830fb986e0aeb74c6c63d47c0c11410800000128000000905567fd5df02f911fca4e64532ef338fbc78f51e0711d133520c87a53dc004d5123b21333da8b8a92f0c02b4cefd1a3b74eedb56197983bfc0782c22d0595ae7bf6fbf909f6d240f412adab0e7330e5f1037eefb93404ec11e5ab0786b4c26da0150d51823dd51a5e9357febdad38f762240b1c7df5f5243248a7697e42b37fd714431b773d88fe2e7053976d4c773d1a00000090acbe7b8b2ff927a57e136525fdcdddd5fc9fec566fdf556f36fb87a2357035fb2e9f1c42a1ac92435a8cc14f17b73a87b1a0a3a6cabd1cb1fb4fc5190a6fe82113a0185071cd5f4c2e744e90ce435ae30ccdd0febe82f66fb54c36568b047519a96a18ebc54166ce442cfea254468de70c9513735ae8838bbc9cf0fe268d9f531bcafcb435e050a753a870466a16345900000128000000909dc9ccaaa169fe8f19996407f90983d83d1c6f297cb95066d7f8ec4313f5511cbc17ecbb4f0174606f923241ffdccb87467cdb5ea0c109e0425538e919ffcfc59591b30b2243bd8d04678a7bf7e5865a01b2ddcc608a28726322ecdfe1a3acf5c78844eb23207c21cc5694371725bcba053ec047b8790e19db88aafad5fa2849dfee7518644406873caa13ff0d0a855900000090348d0b1171214af22f5a822da37c072a2746c5884260dfcbc603c9ed223be74bede40529a01381808b1722f6d5b92f31a3dd0a8a89b2f25c32cac9a0e67e26c3f1566d52b1bbb081363eb3fc5bcd96872ab570861949126649e9d01ca995f837f673925eed0e23f70fcf0d1701b3875f143b10a44e6762968dcd217361d1466acb824d61175b08ca34d9a9374011d7d80000012800000090f48952a0956883a34c401a10c9f97ec53efae1c9ec4c7b5230c864ae0ca7c0fc218729bdbaf89b347614e766d51aa170680688118afe46c5cb2a817b4f1cdcb412e598765d460463398d286e893ba96527fa6cc20569bc654a8cf1e76df5e41520450f3e9646674910a7cc5f2f6ad6cb07b03a6ed5e7670e4494fb13f7d5a75e72cd728d0363d7aa08b7761abef4b3da00000090db2897a0437ec2ba9f366e88ece23670290ff06e269eaafc1eecb43b41aa7f53a96fa2933f28022938dc4d4d7e397844e4a9bcd7f79968ab01e05277e7f8d820aed271e6198e5ee96f6a70bb0c1cbdb929a9c306aeab9c7afbfe3b2c9558798659aef641daefc0a1801b4aa613faba162dc55de1173652dea99a971823c29032661767bd75f24dc53aaf995f98cdc6890000012800000090c7a4aca8720eb3649eac8eb7419ecae0725206544f9f14c135f867014c33595ab494a94226c3fba20a3b6dd078cec20d7032c0478533261815d5c6f34c7490910af1acbf60dfa203a974b3526e0d6f170853c20b3dd2891391e32c4e5e6dae96aae97bf3a0c483a33d2bfed18b518dd60a43da0389df5659213258d86506493a11c476c6173346943409877f3f1eb8280000009044cd6d5591321c78d3ad1ae1bf3920701d1cc0c6a73b5b5d9034c05466de6a3b7c9dcfa0bbca612a895d7a418e1b1513413160f9e6a343415f57b3e9cc3865c966586d3b70c285048326cfd317ee6b9f250307f80b904bbcaae1cf998bab4d7e0c3c9d6067e16993e186577841c0bcc42a7732ed26e008b9e9a8ec5db481cb59cf3533dfc6200ef3ebf40a40c4dff644000001280000009005acab802e3f5f7d14a97db5dc83caa849d21005335eb64ee3f7d7890913e47c41935942c6492765d1683f3e58618210b938eadbca1972fb3ca6f933e13eb53cc28a8cd4bb551016e8e6da5f43bbe527212154a463316eeafe582bae589ec21dc4a3f04ce8be15fa7574cc8f85297a7519d803802bb93115202731de6c949bfdc7b897c2c20e55699a45b639e74197f700000090db8755368a5883f4c8fe4f1bab8967d086a2c03cddbe926a6b89d2193ef0e497f2082b9a924618d54e483ea64081ae51a51c10602447801112189d25e238ca3f40e2c1b0fd34aca316716b7bd3294e4413457e9893aa9397a1d8ef44dd4fce4e51426d55377084185406aa294c6c05e82202efcb16b1d3d895622c79efaf7de68f35c641bc19758cf04bff359e492efd000001280000009087ac7014e5e07bff87399657ee1763c71b8999148890bec2db8c429b1f90bbd42d17eb24235c38a58e5fc5731d4817ac467a3cc83c85940cabee34021c15207429514ff47a00c8b2064f38ed9513daa827655841b9ec89ae70fd3322615750c4e254c912b65a0e47da5e9d887dd6066c14c97dd4139a5e24272d10c6c8af9e008326ffc35057b8e61b56b89dd7026c48000000908f003eda2f032f79d6daa0afae9e777d8395e8b99a14c184826924c613f2c2cfd771420c41e34335484310ae29eb2f42354ce93093db217cd73f1ad1d7c82751c9840f009bf99fae72f6499e5aabe4e7303b2e67e46729ccb482876659117e12fbea701f7f693c686690f16037629e3f2e4afd1cd9ba7c7792e0f75312d43121d679cb3700ba52bb99bf7163175f39340000012800000090fcb668f4eee987dcf91f02506c4e276cbda61f718d249c1b146908ae48773bf119357acb0d33d981d04fa30e254b74786b8af9d6231aed0d2ab609a427aa1cd7269d05a908be3edff62849db15916753094633d30c8e1e1824d63a471510520a61500be387faced8d8f2397897a79b9100b2c3749f0cdb1b89a4635ac91966fef5328f7a51d75f6a64c5cf262929d0ea00000090e1fd1913250e3383ece01108e3676dbf45b6af7cd54328a1a6a9e032b3acd5c84c7330714433f147691a15dde1097a246a54c59aac1547f291930e5fd3994c71eea3b96288c1c2444609b675711a615608fedc92c08b028e141a647c4c6240e10e01a1b517bc9b1fbfffedeaaa84939d108fb5b48bcce4a6403fb3a6297a864177d35ec2f56b2f41c1a5c18fa77c78000000012800000090246b9ef39b4c1f688e2a9bd94cd7886aba49dd6c2d7a994a55b3b210fa54d50b1d94c7314b1dd66727a04194461377f021887f0bf08d7132952ac4fefb34a22382487b2819ff3f689c191ec33cd639fc0354c3c1f82acb0665b97f78cf4d7ea6edbc09645d6fc091fbdda38ab51d614206ed1eb6712031a70239dba48af173cc72ce5c8dbd4b2f2ead260eaf139fc76c00000090c47bca9d4280b30fdd30ff309d250754b715513d9b7d45b38690e683cb07c2aaaeb63b0f0400b0116a77e295240d94e39c7682a0f1ec9e1396aafcb5ba80053a5ffb4cdc262aee196781d2c3c92e5ec31abe900aae102106925359f393e17163def75522a8363506ff77b008dfb3598f15cd1ad0f1cd2c9940d6e35a85fa4aff06b2fca127af77cf85b7ecf2134c127200000128000000901efcd448119223b36296649ae27271c8ce44425c2257bb3b4d322767bbcc93df4e3405b9d57b89cf3a321725617ccd1718e3c2b485bb72e40a5b661e7c6674522447171d2d600265fd1068307f5eddbc031b050c27fc1f6035e51fc86890af02b773a7bf6a2b79151cdefe58e55c368405fbc782501d3ec15e509d762b578fee3711ec85afcbe3ca31714291ff4e84210000009020105be65d60c582c38af508ff84d115ba0be30df8b55ba5b2644bbb15c4dc9dfb46399a7bedc1e12e57530f8d2fdba47e9f59062a9384c5eb496795964f33e222a91ee413495921cd45f18ec0bef4a013d91373d3e7c6b40cb9eab782d295c6bfc1fefe73de88e8ae380363827204bb2fd4f44899b714ae567340e9d2b75ce56290389379b01521fc01b1ca40fd7eb40000012800000090221861a7da93d0ea2098a2dfd72369f097f2390e73e14b94284bddb6aa412027cd015bd818e966516ad0808b28e95ba99b04271cd979480ba3a70513df67c7d10adbbebf39059d48bd90d70fad88a11b15a453727995b3da07902ea4ec062384240e406e45222bc6fd8b85c2a97bf1d2282e381e70d50ed6a430be46b27ae56fde68dfc666c7301c37cb7109283cdee60000009020e262f5717b624c3dc1b984830aeb3377f9952d1efcd31851ea323cd10d6502da352474fbb82c370582a685612f768d4591761d67d3923c697222229eb6e39aa40e9103d896d9085969a6e250ac170826e9d5dd40287b9c06c538743a402ed9c7d4c84429afa70fc4a308caa453ce4919a295d209efe58669e9f8b23280e245ee5c5295c434be404440831f44d4bd63", + "calldataHash": "0x484df1f693f6fe8712cc9129413652b277db358ece78850f1073d963c1258286", "decodedHeader": { - "bodyHash": "0xf4525fad966caa2a5a1c5c4ca03520c2652aa2835f67fcad64b97e05cfb2870e", + "bodyHash": "0x484df1f693f6fe8712cc9129413652b277db358ece78850f1073d963c1258286", "globalVariables": { "blockNumber": 2, "chainId": 31337, - "timestamp": 1705501882, + "timestamp": 1706272843, "version": 1 }, "lastArchive": { @@ -92,8 +92,8 @@ } } }, - "header": "0x0000000000000000000000000000000000000000000000000000000000007a69000000000000000000000000000000000000000000000000000000000000000100000000000000000000000000000000000000000000000000000000000000020000000000000000000000000000000000000000000000000000000065a7e4ba06c76caee115a61eeb6788977c68a3bea359061b678a1a4f5ffde13e0451717b0000002024c6dc6d357aad01e10fe1adb877bb28b1df97375b874116e488086ca76e5f9600000200268020a622156e2beac47431b0cd70e1c81fef9a6aa3c365bfcbed9aa7301c5e00000280022612683c6c4a0955c8248d409bc8ec8776d96a3bfe5b5a7e76e492a4f26cb000000008039e689049f104493f0e819b00d874663f0724639f854b8722e8ee829b0f136a000000a01f1de772c009f5b1660876343eb57b7a676a84c695b0c526de2f238c4181090700000002f4525fad966caa2a5a1c5c4ca03520c2652aa2835f67fcad64b97e05cfb2870e", + "header": "0x1f1de772c009f5b1660876343eb57b7a676a84c695b0c526de2f238c4181090700000002484df1f693f6fe8712cc9129413652b277db358ece78850f1073d963c125828606c76caee115a61eeb6788977c68a3bea359061b678a1a4f5ffde13e0451717b0000002024c6dc6d357aad01e10fe1adb877bb28b1df97375b874116e488086ca76e5f9600000200268020a622156e2beac47431b0cd70e1c81fef9a6aa3c365bfcbed9aa7301c5e00000280022612683c6c4a0955c8248d409bc8ec8776d96a3bfe5b5a7e76e492a4f26cb000000008039e689049f104493f0e819b00d874663f0724639f854b8722e8ee829b0f136a000000a00000000000000000000000000000000000000000000000000000000000007a69000000000000000000000000000000000000000000000000000000000000000100000000000000000000000000000000000000000000000000000000000000020000000000000000000000000000000000000000000000000000000065b3a84b", "l1ToL2MessagesHash": "0xa10cc8559615be5a44cfb608374b1f84fd11cdb5844ebffafd92a77c068350f1", - "publicInputsHash": "0x280997cb13c04842e88c31fb0d4db23d591c48aea40d9a5aa2e18fd4c72a0358" + "publicInputsHash": "0x2cbde160e7af7c59ad9714ca77d07e5dca90aa6016d28e7939e201190b06740c" } } \ No newline at end of file diff --git a/l1-contracts/test/portals/TokenPortal.sol b/l1-contracts/test/portals/TokenPortal.sol index ccd6661ea83..42119952264 100644 --- a/l1-contracts/test/portals/TokenPortal.sol +++ b/l1-contracts/test/portals/TokenPortal.sol @@ -1,4 +1,3 @@ -// docs:start:init pragma solidity >=0.8.18; import {IERC20} from "@oz/token/ERC20/IERC20.sol"; @@ -12,6 +11,7 @@ import {DataStructures} from "../../src/core/libraries/DataStructures.sol"; import {Hash} from "../../src/core/libraries/Hash.sol"; // docs:end:content_hash_sol_import +// docs:start:init contract TokenPortal { using SafeERC20 for IERC20; diff --git a/noir/.github/ISSUE_TEMPLATE/config.yml b/noir/.github/ISSUE_TEMPLATE/config.yml deleted file mode 100644 index b5ded344eb9..00000000000 --- a/noir/.github/ISSUE_TEMPLATE/config.yml +++ /dev/null @@ -1,4 +0,0 @@ -contact_links: - - name: Ideas - url: https://github.com/orgs/noir-lang/discussions/new?category=ideas - about: Share ideas for new features diff --git a/noir/.github/ISSUE_TEMPLATE/idea_action_plan.yml b/noir/.github/ISSUE_TEMPLATE/feature_request.yml similarity index 90% rename from noir/.github/ISSUE_TEMPLATE/idea_action_plan.yml rename to noir/.github/ISSUE_TEMPLATE/feature_request.yml index 02fed1fc48b..979ac75811e 100644 --- a/noir/.github/ISSUE_TEMPLATE/idea_action_plan.yml +++ b/noir/.github/ISSUE_TEMPLATE/feature_request.yml @@ -1,5 +1,5 @@ -name: Idea Action Plan -description: Outline the scope and steps for implementing an enhancement. Start with "Ideas" instead to request and discuss new features. +name: Feature Request +description: Suggest an idea for this project. labels: ["enhancement"] body: - type: markdown diff --git a/noir/.github/scripts/wasm-bindgen-install.sh b/noir/.github/scripts/wasm-bindgen-install.sh index b8c41393ab0..a147a46cde8 100755 --- a/noir/.github/scripts/wasm-bindgen-install.sh +++ b/noir/.github/scripts/wasm-bindgen-install.sh @@ -1,5 +1,5 @@ #!/bin/bash set -eu -curl -L --proto '=https' --tlsv1.2 -sSf https://raw.githubusercontent.com/cargo-bins/cargo-binstall/main/install-from-binstall-release.sh | bash -cargo-binstall wasm-bindgen-cli --version 0.2.86 -y +# TODO call this script directly +./scripts/install_wasm-bindgen.sh diff --git a/noir/.github/workflows/docs-pr.yml b/noir/.github/workflows/docs-pr.yml index f4a1be826a8..87bec37c438 100644 --- a/noir/.github/workflows/docs-pr.yml +++ b/noir/.github/workflows/docs-pr.yml @@ -54,6 +54,15 @@ jobs: - name: Checkout code uses: actions/checkout@v4 + - name: Setup toolchain + uses: dtolnay/rust-toolchain@1.71.1 + + - uses: Swatinem/rust-cache@v2 + with: + key: x86_64-unknown-linux-gnu + cache-on-failure: false + save-if: false + - name: Install Yarn dependencies uses: ./.github/actions/setup diff --git a/noir/.github/workflows/publish-es-packages.yml b/noir/.github/workflows/publish-es-packages.yml index e360654b46a..2c825ffd45f 100644 --- a/noir/.github/workflows/publish-es-packages.yml +++ b/noir/.github/workflows/publish-es-packages.yml @@ -15,7 +15,7 @@ on: run-name: Publish ES Packages from ${{ inputs.noir-ref }} under @${{ inputs.npm-tag }} tag. jobs: - build-noir_wasm: + build-noirc_abi_wasm: runs-on: ubuntu-latest steps: - name: Checkout sources @@ -32,16 +32,17 @@ jobs: - name: Build wasm package run: | - nix build -L .#noir_wasm + nix build -L .#noirc_abi_wasm - uses: actions/upload-artifact@v3 with: - name: noir_wasm + name: noirc_abi_wasm path: | - result/noir_wasm/nodejs - result/noir_wasm/web + result/noirc_abi_wasm/nodejs + result/noirc_abi_wasm/web - build-noirc_abi_wasm: + build-noir_wasm: + needs: [build-noirc_abi_wasm] runs-on: ubuntu-latest steps: - name: Checkout sources @@ -49,23 +50,34 @@ jobs: with: ref: ${{ inputs.noir-ref }} - - name: Setup Nix - uses: ./.github/actions/nix + - name: Setup toolchain + uses: dtolnay/rust-toolchain@1.71.1 + + - uses: Swatinem/rust-cache@v2 with: - github-token: ${{ secrets.GITHUB_TOKEN }} - nix-cache-name: "noir" - cachix-auth-token: ${{ secrets.CACHIXAUTHTOKEN }} - - - name: Build wasm package - run: | - nix build -L .#noirc_abi_wasm + key: noir-wasm + save-if: false - - uses: actions/upload-artifact@v3 + - name: Download noirc_abi_wasm package artifact + uses: actions/download-artifact@v3 with: name: noirc_abi_wasm + path: ./tooling/noirc_abi_wasm + + - name: Install Yarn dependencies + uses: ./.github/actions/setup + + - name: Build noir_wasm + run: yarn workspace @noir-lang/noir_wasm build + + - name: Upload artifact + uses: actions/upload-artifact@v3 + with: + name: noir_wasm path: | - result/noirc_abi_wasm/nodejs - result/noirc_abi_wasm/web + ./compiler/wasm/dist + ./compiler/wasm/build + retention-days: 3 build-acvm_js: runs-on: ubuntu-latest @@ -97,7 +109,6 @@ jobs: runs-on: ubuntu-latest needs: [build-acvm_js, build-noirc_abi_wasm, build-noir_wasm] steps: - - name: Checkout sources uses: actions/checkout@v4 with: @@ -107,10 +118,12 @@ jobs: with: name: acvm_js path: acvm-repo/acvm_js + - uses: actions/download-artifact@v3 with: name: noir_wasm path: compiler/wasm + - uses: actions/download-artifact@v3 with: name: noirc_abi_wasm diff --git a/noir/.github/workflows/release.yml b/noir/.github/workflows/release.yml index 22a733b38c5..71a0ab6d894 100644 --- a/noir/.github/workflows/release.yml +++ b/noir/.github/workflows/release.yml @@ -11,7 +11,6 @@ jobs: outputs: release-pr: ${{ steps.release.outputs.pr }} tag-name: ${{ steps.release.outputs.tag_name }} - pending-release-semver: v${{ steps.release.outputs.major }}.${{steps.release.outputs.minor}}.${{steps.release.outputs.patch}} runs-on: ubuntu-latest steps: - name: Run release-please @@ -80,9 +79,15 @@ jobs: - name: Install Yarn dependencies uses: ./.github/actions/setup + - name: Query new noir version + id: noir-version + run: | + NOIR_VERSION=$(grep '^version =' ./Cargo.toml | sed -E 's/version = "([^"]+)"/v\1/') + echo "semver=$NOIR_VERSION" >> $GITHUB_OUTPUT + - name: Cut a new version working-directory: ./docs - run: yarn docusaurus docs:version ${{ needs.release-please.outputs.pending-release-semver }} + run: yarn version ${{ steps.noir-version.outputs.semver }} - name: Configure git run: | @@ -92,7 +97,7 @@ jobs: - name: Commit new documentation version run: | git add . - git commit -m "chore(docs): cut new docs version for tag ${{ needs.release-please.outputs.pending-release-semver }}" + git commit -m "chore(docs): cut new docs version for tag ${{ steps.noir-version.outputs.semver }}" git push build-binaries: diff --git a/noir/.gitrepo b/noir/.gitrepo index 8fb835cdb68..a565286408a 100644 --- a/noir/.gitrepo +++ b/noir/.gitrepo @@ -6,7 +6,7 @@ [subrepo] remote = https://github.com/noir-lang/noir branch = aztec-packages - commit = 602f23f4fb698cf6e37071936a2a46593a998d08 - parent = f0fb5942386e2e091cb6d1b23108ac74c1c6b75d + commit = 9944bb170691d4e7905793a978019ba9504b1139 + parent = aeb4cf0d9cec6127cac947c4f0de8e853b2f34e0 method = merge cmdver = 0.4.6 diff --git a/noir/Cargo.toml b/noir/Cargo.toml index 8a827cacfcd..5dfff3dbb5d 100644 --- a/noir/Cargo.toml +++ b/noir/Cargo.toml @@ -52,7 +52,6 @@ repository = "https://github.com/noir-lang/noir/" acir_field = { version = "0.39.0", path = "acvm-repo/acir_field", default-features = false } acir = { version = "0.39.0", path = "acvm-repo/acir", default-features = false } acvm = { version = "0.39.0", path = "acvm-repo/acvm" } -stdlib = { version = "0.37.1", package = "acvm_stdlib", path = "acvm-repo/stdlib", default-features = false } brillig = { version = "0.39.0", path = "acvm-repo/brillig", default-features = false } brillig_vm = { version = "0.39.0", path = "acvm-repo/brillig_vm", default-features = false } acvm_blackbox_solver = { version = "0.39.0", path = "acvm-repo/blackbox_solver", default-features = false } diff --git a/noir/acvm-repo/acir/acir_docs.md b/noir/acvm-repo/acir/README.md similarity index 100% rename from noir/acvm-repo/acir/acir_docs.md rename to noir/acvm-repo/acir/README.md diff --git a/noir/acvm-repo/acir/codegen/acir.cpp b/noir/acvm-repo/acir/codegen/acir.cpp index 9b74a8ea631..1b9b9c2e5bd 100644 --- a/noir/acvm-repo/acir/codegen/acir.cpp +++ b/noir/acvm-repo/acir/codegen/acir.cpp @@ -157,16 +157,6 @@ namespace Circuit { static EmbeddedCurveAdd bincodeDeserialize(std::vector); }; - struct EmbeddedCurveDouble { - Circuit::FunctionInput input_x; - Circuit::FunctionInput input_y; - std::array outputs; - - friend bool operator==(const EmbeddedCurveDouble&, const EmbeddedCurveDouble&); - std::vector bincodeSerialize() const; - static EmbeddedCurveDouble bincodeDeserialize(std::vector); - }; - struct Keccak256 { std::vector inputs; std::vector outputs; @@ -206,7 +196,86 @@ namespace Circuit { static RecursiveAggregation bincodeDeserialize(std::vector); }; - std::variant value; + struct BigIntAdd { + uint32_t lhs; + uint32_t rhs; + uint32_t output; + + friend bool operator==(const BigIntAdd&, const BigIntAdd&); + std::vector bincodeSerialize() const; + static BigIntAdd bincodeDeserialize(std::vector); + }; + + struct BigIntNeg { + uint32_t lhs; + uint32_t rhs; + uint32_t output; + + friend bool operator==(const BigIntNeg&, const BigIntNeg&); + std::vector bincodeSerialize() const; + static BigIntNeg bincodeDeserialize(std::vector); + }; + + struct BigIntMul { + uint32_t lhs; + uint32_t rhs; + uint32_t output; + + friend bool operator==(const BigIntMul&, const BigIntMul&); + std::vector bincodeSerialize() const; + static BigIntMul bincodeDeserialize(std::vector); + }; + + struct BigIntDiv { + uint32_t lhs; + uint32_t rhs; + uint32_t output; + + friend bool operator==(const BigIntDiv&, const BigIntDiv&); + std::vector bincodeSerialize() const; + static BigIntDiv bincodeDeserialize(std::vector); + }; + + struct BigIntFromLeBytes { + std::vector inputs; + std::vector modulus; + uint32_t output; + + friend bool operator==(const BigIntFromLeBytes&, const BigIntFromLeBytes&); + std::vector bincodeSerialize() const; + static BigIntFromLeBytes bincodeDeserialize(std::vector); + }; + + struct BigIntToLeBytes { + uint32_t input; + std::vector outputs; + + friend bool operator==(const BigIntToLeBytes&, const BigIntToLeBytes&); + std::vector bincodeSerialize() const; + static BigIntToLeBytes bincodeDeserialize(std::vector); + }; + + struct Poseidon2Permutation { + std::vector inputs; + std::vector outputs; + uint32_t len; + + friend bool operator==(const Poseidon2Permutation&, const Poseidon2Permutation&); + std::vector bincodeSerialize() const; + static Poseidon2Permutation bincodeDeserialize(std::vector); + }; + + struct Sha256Compression { + std::vector inputs; + std::vector hash_values; + std::vector outputs; + + friend bool operator==(const Sha256Compression&, const Sha256Compression&); + std::vector bincodeSerialize() const; + static Sha256Compression bincodeDeserialize(std::vector); + }; + + std::variant value; friend bool operator==(const BlackBoxFuncCall&, const BlackBoxFuncCall&); std::vector bincodeSerialize() const; @@ -382,16 +451,16 @@ namespace Circuit { static BinaryIntOp bincodeDeserialize(std::vector); }; - struct RegisterIndex { + struct MemoryAddress { uint64_t value; - friend bool operator==(const RegisterIndex&, const RegisterIndex&); + friend bool operator==(const MemoryAddress&, const MemoryAddress&); std::vector bincodeSerialize() const; - static RegisterIndex bincodeDeserialize(std::vector); + static MemoryAddress bincodeDeserialize(std::vector); }; struct HeapArray { - Circuit::RegisterIndex pointer; + Circuit::MemoryAddress pointer; uint64_t size; friend bool operator==(const HeapArray&, const HeapArray&); @@ -400,8 +469,8 @@ namespace Circuit { }; struct HeapVector { - Circuit::RegisterIndex pointer; - Circuit::RegisterIndex size; + Circuit::MemoryAddress pointer; + Circuit::MemoryAddress size; friend bool operator==(const HeapVector&, const HeapVector&); std::vector bincodeSerialize() const; @@ -460,7 +529,7 @@ namespace Circuit { Circuit::HeapArray public_key_x; Circuit::HeapArray public_key_y; Circuit::HeapArray signature; - Circuit::RegisterIndex result; + Circuit::MemoryAddress result; friend bool operator==(const EcdsaSecp256k1&, const EcdsaSecp256k1&); std::vector bincodeSerialize() const; @@ -472,7 +541,7 @@ namespace Circuit { Circuit::HeapArray public_key_x; Circuit::HeapArray public_key_y; Circuit::HeapArray signature; - Circuit::RegisterIndex result; + Circuit::MemoryAddress result; friend bool operator==(const EcdsaSecp256r1&, const EcdsaSecp256r1&); std::vector bincodeSerialize() const; @@ -480,11 +549,11 @@ namespace Circuit { }; struct SchnorrVerify { - Circuit::RegisterIndex public_key_x; - Circuit::RegisterIndex public_key_y; + Circuit::MemoryAddress public_key_x; + Circuit::MemoryAddress public_key_y; Circuit::HeapVector message; Circuit::HeapVector signature; - Circuit::RegisterIndex result; + Circuit::MemoryAddress result; friend bool operator==(const SchnorrVerify&, const SchnorrVerify&); std::vector bincodeSerialize() const; @@ -493,7 +562,7 @@ namespace Circuit { struct PedersenCommitment { Circuit::HeapVector inputs; - Circuit::RegisterIndex domain_separator; + Circuit::MemoryAddress domain_separator; Circuit::HeapArray output; friend bool operator==(const PedersenCommitment&, const PedersenCommitment&); @@ -503,8 +572,8 @@ namespace Circuit { struct PedersenHash { Circuit::HeapVector inputs; - Circuit::RegisterIndex domain_separator; - Circuit::RegisterIndex output; + Circuit::MemoryAddress domain_separator; + Circuit::MemoryAddress output; friend bool operator==(const PedersenHash&, const PedersenHash&); std::vector bincodeSerialize() const; @@ -512,8 +581,8 @@ namespace Circuit { }; struct FixedBaseScalarMul { - Circuit::RegisterIndex low; - Circuit::RegisterIndex high; + Circuit::MemoryAddress low; + Circuit::MemoryAddress high; Circuit::HeapArray result; friend bool operator==(const FixedBaseScalarMul&, const FixedBaseScalarMul&); @@ -522,10 +591,10 @@ namespace Circuit { }; struct EmbeddedCurveAdd { - Circuit::RegisterIndex input1_x; - Circuit::RegisterIndex input1_y; - Circuit::RegisterIndex input2_x; - Circuit::RegisterIndex input2_y; + Circuit::MemoryAddress input1_x; + Circuit::MemoryAddress input1_y; + Circuit::MemoryAddress input2_x; + Circuit::MemoryAddress input2_y; Circuit::HeapArray result; friend bool operator==(const EmbeddedCurveAdd&, const EmbeddedCurveAdd&); @@ -533,31 +602,108 @@ namespace Circuit { static EmbeddedCurveAdd bincodeDeserialize(std::vector); }; - struct EmbeddedCurveDouble { - Circuit::RegisterIndex input1_x; - Circuit::RegisterIndex input1_y; - Circuit::HeapArray result; + struct BigIntAdd { + Circuit::MemoryAddress lhs; + Circuit::MemoryAddress rhs; + Circuit::MemoryAddress output; + + friend bool operator==(const BigIntAdd&, const BigIntAdd&); + std::vector bincodeSerialize() const; + static BigIntAdd bincodeDeserialize(std::vector); + }; + + struct BigIntNeg { + Circuit::MemoryAddress lhs; + Circuit::MemoryAddress rhs; + Circuit::MemoryAddress output; + + friend bool operator==(const BigIntNeg&, const BigIntNeg&); + std::vector bincodeSerialize() const; + static BigIntNeg bincodeDeserialize(std::vector); + }; + + struct BigIntMul { + Circuit::MemoryAddress lhs; + Circuit::MemoryAddress rhs; + Circuit::MemoryAddress output; + + friend bool operator==(const BigIntMul&, const BigIntMul&); + std::vector bincodeSerialize() const; + static BigIntMul bincodeDeserialize(std::vector); + }; + + struct BigIntDiv { + Circuit::MemoryAddress lhs; + Circuit::MemoryAddress rhs; + Circuit::MemoryAddress output; + + friend bool operator==(const BigIntDiv&, const BigIntDiv&); + std::vector bincodeSerialize() const; + static BigIntDiv bincodeDeserialize(std::vector); + }; + + struct BigIntFromLeBytes { + Circuit::HeapVector inputs; + Circuit::HeapVector modulus; + Circuit::MemoryAddress output; + + friend bool operator==(const BigIntFromLeBytes&, const BigIntFromLeBytes&); + std::vector bincodeSerialize() const; + static BigIntFromLeBytes bincodeDeserialize(std::vector); + }; - friend bool operator==(const EmbeddedCurveDouble&, const EmbeddedCurveDouble&); + struct BigIntToLeBytes { + Circuit::MemoryAddress input; + Circuit::HeapVector output; + + friend bool operator==(const BigIntToLeBytes&, const BigIntToLeBytes&); + std::vector bincodeSerialize() const; + static BigIntToLeBytes bincodeDeserialize(std::vector); + }; + + struct Poseidon2Permutation { + Circuit::HeapVector message; + Circuit::HeapArray output; + Circuit::MemoryAddress len; + + friend bool operator==(const Poseidon2Permutation&, const Poseidon2Permutation&); + std::vector bincodeSerialize() const; + static Poseidon2Permutation bincodeDeserialize(std::vector); + }; + + struct Sha256Compression { + Circuit::HeapVector input; + Circuit::HeapVector hash_values; + Circuit::HeapArray output; + + friend bool operator==(const Sha256Compression&, const Sha256Compression&); std::vector bincodeSerialize() const; - static EmbeddedCurveDouble bincodeDeserialize(std::vector); + static Sha256Compression bincodeDeserialize(std::vector); }; - std::variant value; + std::variant value; friend bool operator==(const BlackBoxOp&, const BlackBoxOp&); std::vector bincodeSerialize() const; static BlackBoxOp bincodeDeserialize(std::vector); }; - struct RegisterOrMemory { + struct Value { + std::string inner; + + friend bool operator==(const Value&, const Value&); + std::vector bincodeSerialize() const; + static Value bincodeDeserialize(std::vector); + }; + + struct ValueOrArray { - struct RegisterIndex { - Circuit::RegisterIndex value; + struct MemoryAddress { + Circuit::MemoryAddress value; - friend bool operator==(const RegisterIndex&, const RegisterIndex&); + friend bool operator==(const MemoryAddress&, const MemoryAddress&); std::vector bincodeSerialize() const; - static RegisterIndex bincodeDeserialize(std::vector); + static MemoryAddress bincodeDeserialize(std::vector); }; struct HeapArray { @@ -576,28 +722,20 @@ namespace Circuit { static HeapVector bincodeDeserialize(std::vector); }; - std::variant value; - - friend bool operator==(const RegisterOrMemory&, const RegisterOrMemory&); - std::vector bincodeSerialize() const; - static RegisterOrMemory bincodeDeserialize(std::vector); - }; - - struct Value { - std::string inner; + std::variant value; - friend bool operator==(const Value&, const Value&); + friend bool operator==(const ValueOrArray&, const ValueOrArray&); std::vector bincodeSerialize() const; - static Value bincodeDeserialize(std::vector); + static ValueOrArray bincodeDeserialize(std::vector); }; struct BrilligOpcode { struct BinaryFieldOp { - Circuit::RegisterIndex destination; + Circuit::MemoryAddress destination; Circuit::BinaryFieldOp op; - Circuit::RegisterIndex lhs; - Circuit::RegisterIndex rhs; + Circuit::MemoryAddress lhs; + Circuit::MemoryAddress rhs; friend bool operator==(const BinaryFieldOp&, const BinaryFieldOp&); std::vector bincodeSerialize() const; @@ -605,11 +743,11 @@ namespace Circuit { }; struct BinaryIntOp { - Circuit::RegisterIndex destination; + Circuit::MemoryAddress destination; Circuit::BinaryIntOp op; uint32_t bit_size; - Circuit::RegisterIndex lhs; - Circuit::RegisterIndex rhs; + Circuit::MemoryAddress lhs; + Circuit::MemoryAddress rhs; friend bool operator==(const BinaryIntOp&, const BinaryIntOp&); std::vector bincodeSerialize() const; @@ -617,7 +755,7 @@ namespace Circuit { }; struct JumpIfNot { - Circuit::RegisterIndex condition; + Circuit::MemoryAddress condition; uint64_t location; friend bool operator==(const JumpIfNot&, const JumpIfNot&); @@ -626,7 +764,7 @@ namespace Circuit { }; struct JumpIf { - Circuit::RegisterIndex condition; + Circuit::MemoryAddress condition; uint64_t location; friend bool operator==(const JumpIf&, const JumpIf&); @@ -642,6 +780,16 @@ namespace Circuit { static Jump bincodeDeserialize(std::vector); }; + struct CalldataCopy { + Circuit::MemoryAddress destination_address; + uint64_t size; + uint64_t offset; + + friend bool operator==(const CalldataCopy&, const CalldataCopy&); + std::vector bincodeSerialize() const; + static CalldataCopy bincodeDeserialize(std::vector); + }; + struct Call { uint64_t location; @@ -651,7 +799,7 @@ namespace Circuit { }; struct Const { - Circuit::RegisterIndex destination; + Circuit::MemoryAddress destination; Circuit::Value value; friend bool operator==(const Const&, const Const&); @@ -667,8 +815,8 @@ namespace Circuit { struct ForeignCall { std::string function; - std::vector destinations; - std::vector inputs; + std::vector destinations; + std::vector inputs; friend bool operator==(const ForeignCall&, const ForeignCall&); std::vector bincodeSerialize() const; @@ -676,8 +824,8 @@ namespace Circuit { }; struct Mov { - Circuit::RegisterIndex destination; - Circuit::RegisterIndex source; + Circuit::MemoryAddress destination; + Circuit::MemoryAddress source; friend bool operator==(const Mov&, const Mov&); std::vector bincodeSerialize() const; @@ -685,8 +833,8 @@ namespace Circuit { }; struct Load { - Circuit::RegisterIndex destination; - Circuit::RegisterIndex source_pointer; + Circuit::MemoryAddress destination; + Circuit::MemoryAddress source_pointer; friend bool operator==(const Load&, const Load&); std::vector bincodeSerialize() const; @@ -694,8 +842,8 @@ namespace Circuit { }; struct Store { - Circuit::RegisterIndex destination_pointer; - Circuit::RegisterIndex source; + Circuit::MemoryAddress destination_pointer; + Circuit::MemoryAddress source; friend bool operator==(const Store&, const Store&); std::vector bincodeSerialize() const; @@ -717,12 +865,15 @@ namespace Circuit { }; struct Stop { + uint64_t return_data_offset; + uint64_t return_data_size; + friend bool operator==(const Stop&, const Stop&); std::vector bincodeSerialize() const; static Stop bincodeDeserialize(std::vector); }; - std::variant value; + std::variant value; friend bool operator==(const BrilligOpcode&, const BrilligOpcode&); std::vector bincodeSerialize() const; @@ -2252,50 +2403,6 @@ Circuit::BlackBoxFuncCall::EmbeddedCurveAdd serde::Deserializable BlackBoxFuncCall::EmbeddedCurveDouble::bincodeSerialize() const { - auto serializer = serde::BincodeSerializer(); - serde::Serializable::serialize(*this, serializer); - return std::move(serializer).bytes(); - } - - inline BlackBoxFuncCall::EmbeddedCurveDouble BlackBoxFuncCall::EmbeddedCurveDouble::bincodeDeserialize(std::vector input) { - auto deserializer = serde::BincodeDeserializer(input); - auto value = serde::Deserializable::deserialize(deserializer); - if (deserializer.get_buffer_offset() < input.size()) { - throw serde::deserialization_error("Some input bytes were not read"); - } - return value; - } - -} // end of namespace Circuit - -template <> -template -void serde::Serializable::serialize(const Circuit::BlackBoxFuncCall::EmbeddedCurveDouble &obj, Serializer &serializer) { - serde::Serializable::serialize(obj.input_x, serializer); - serde::Serializable::serialize(obj.input_y, serializer); - serde::Serializable::serialize(obj.outputs, serializer); -} - -template <> -template -Circuit::BlackBoxFuncCall::EmbeddedCurveDouble serde::Deserializable::deserialize(Deserializer &deserializer) { - Circuit::BlackBoxFuncCall::EmbeddedCurveDouble obj; - obj.input_x = serde::Deserializable::deserialize(deserializer); - obj.input_y = serde::Deserializable::deserialize(deserializer); - obj.outputs = serde::Deserializable::deserialize(deserializer); - return obj; -} - namespace Circuit { inline bool operator==(const BlackBoxFuncCall::Keccak256 &lhs, const BlackBoxFuncCall::Keccak256 &rhs) { @@ -2471,20 +2578,22 @@ Circuit::BlackBoxFuncCall::RecursiveAggregation serde::Deserializable BlackBoxOp::bincodeSerialize() const { + inline std::vector BlackBoxFuncCall::BigIntAdd::bincodeSerialize() const { auto serializer = serde::BincodeSerializer(); - serde::Serializable::serialize(*this, serializer); + serde::Serializable::serialize(*this, serializer); return std::move(serializer).bytes(); } - inline BlackBoxOp BlackBoxOp::bincodeDeserialize(std::vector input) { + inline BlackBoxFuncCall::BigIntAdd BlackBoxFuncCall::BigIntAdd::bincodeDeserialize(std::vector input) { auto deserializer = serde::BincodeDeserializer(input); - auto value = serde::Deserializable::deserialize(deserializer); + auto value = serde::Deserializable::deserialize(deserializer); if (deserializer.get_buffer_offset() < input.size()) { throw serde::deserialization_error("Some input bytes were not read"); } @@ -2495,39 +2604,40 @@ namespace Circuit { template <> template -void serde::Serializable::serialize(const Circuit::BlackBoxOp &obj, Serializer &serializer) { - serializer.increase_container_depth(); - serde::Serializable::serialize(obj.value, serializer); - serializer.decrease_container_depth(); +void serde::Serializable::serialize(const Circuit::BlackBoxFuncCall::BigIntAdd &obj, Serializer &serializer) { + serde::Serializable::serialize(obj.lhs, serializer); + serde::Serializable::serialize(obj.rhs, serializer); + serde::Serializable::serialize(obj.output, serializer); } template <> template -Circuit::BlackBoxOp serde::Deserializable::deserialize(Deserializer &deserializer) { - deserializer.increase_container_depth(); - Circuit::BlackBoxOp obj; - obj.value = serde::Deserializable::deserialize(deserializer); - deserializer.decrease_container_depth(); +Circuit::BlackBoxFuncCall::BigIntAdd serde::Deserializable::deserialize(Deserializer &deserializer) { + Circuit::BlackBoxFuncCall::BigIntAdd obj; + obj.lhs = serde::Deserializable::deserialize(deserializer); + obj.rhs = serde::Deserializable::deserialize(deserializer); + obj.output = serde::Deserializable::deserialize(deserializer); return obj; } namespace Circuit { - inline bool operator==(const BlackBoxOp::Sha256 &lhs, const BlackBoxOp::Sha256 &rhs) { - if (!(lhs.message == rhs.message)) { return false; } + inline bool operator==(const BlackBoxFuncCall::BigIntNeg &lhs, const BlackBoxFuncCall::BigIntNeg &rhs) { + if (!(lhs.lhs == rhs.lhs)) { return false; } + if (!(lhs.rhs == rhs.rhs)) { return false; } if (!(lhs.output == rhs.output)) { return false; } return true; } - inline std::vector BlackBoxOp::Sha256::bincodeSerialize() const { + inline std::vector BlackBoxFuncCall::BigIntNeg::bincodeSerialize() const { auto serializer = serde::BincodeSerializer(); - serde::Serializable::serialize(*this, serializer); + serde::Serializable::serialize(*this, serializer); return std::move(serializer).bytes(); } - inline BlackBoxOp::Sha256 BlackBoxOp::Sha256::bincodeDeserialize(std::vector input) { + inline BlackBoxFuncCall::BigIntNeg BlackBoxFuncCall::BigIntNeg::bincodeDeserialize(std::vector input) { auto deserializer = serde::BincodeDeserializer(input); - auto value = serde::Deserializable::deserialize(deserializer); + auto value = serde::Deserializable::deserialize(deserializer); if (deserializer.get_buffer_offset() < input.size()) { throw serde::deserialization_error("Some input bytes were not read"); } @@ -2538,37 +2648,40 @@ namespace Circuit { template <> template -void serde::Serializable::serialize(const Circuit::BlackBoxOp::Sha256 &obj, Serializer &serializer) { - serde::Serializable::serialize(obj.message, serializer); +void serde::Serializable::serialize(const Circuit::BlackBoxFuncCall::BigIntNeg &obj, Serializer &serializer) { + serde::Serializable::serialize(obj.lhs, serializer); + serde::Serializable::serialize(obj.rhs, serializer); serde::Serializable::serialize(obj.output, serializer); } template <> template -Circuit::BlackBoxOp::Sha256 serde::Deserializable::deserialize(Deserializer &deserializer) { - Circuit::BlackBoxOp::Sha256 obj; - obj.message = serde::Deserializable::deserialize(deserializer); +Circuit::BlackBoxFuncCall::BigIntNeg serde::Deserializable::deserialize(Deserializer &deserializer) { + Circuit::BlackBoxFuncCall::BigIntNeg obj; + obj.lhs = serde::Deserializable::deserialize(deserializer); + obj.rhs = serde::Deserializable::deserialize(deserializer); obj.output = serde::Deserializable::deserialize(deserializer); return obj; } namespace Circuit { - inline bool operator==(const BlackBoxOp::Blake2s &lhs, const BlackBoxOp::Blake2s &rhs) { - if (!(lhs.message == rhs.message)) { return false; } + inline bool operator==(const BlackBoxFuncCall::BigIntMul &lhs, const BlackBoxFuncCall::BigIntMul &rhs) { + if (!(lhs.lhs == rhs.lhs)) { return false; } + if (!(lhs.rhs == rhs.rhs)) { return false; } if (!(lhs.output == rhs.output)) { return false; } return true; } - inline std::vector BlackBoxOp::Blake2s::bincodeSerialize() const { + inline std::vector BlackBoxFuncCall::BigIntMul::bincodeSerialize() const { auto serializer = serde::BincodeSerializer(); - serde::Serializable::serialize(*this, serializer); + serde::Serializable::serialize(*this, serializer); return std::move(serializer).bytes(); } - inline BlackBoxOp::Blake2s BlackBoxOp::Blake2s::bincodeDeserialize(std::vector input) { + inline BlackBoxFuncCall::BigIntMul BlackBoxFuncCall::BigIntMul::bincodeDeserialize(std::vector input) { auto deserializer = serde::BincodeDeserializer(input); - auto value = serde::Deserializable::deserialize(deserializer); + auto value = serde::Deserializable::deserialize(deserializer); if (deserializer.get_buffer_offset() < input.size()) { throw serde::deserialization_error("Some input bytes were not read"); } @@ -2579,37 +2692,40 @@ namespace Circuit { template <> template -void serde::Serializable::serialize(const Circuit::BlackBoxOp::Blake2s &obj, Serializer &serializer) { - serde::Serializable::serialize(obj.message, serializer); +void serde::Serializable::serialize(const Circuit::BlackBoxFuncCall::BigIntMul &obj, Serializer &serializer) { + serde::Serializable::serialize(obj.lhs, serializer); + serde::Serializable::serialize(obj.rhs, serializer); serde::Serializable::serialize(obj.output, serializer); } template <> template -Circuit::BlackBoxOp::Blake2s serde::Deserializable::deserialize(Deserializer &deserializer) { - Circuit::BlackBoxOp::Blake2s obj; - obj.message = serde::Deserializable::deserialize(deserializer); +Circuit::BlackBoxFuncCall::BigIntMul serde::Deserializable::deserialize(Deserializer &deserializer) { + Circuit::BlackBoxFuncCall::BigIntMul obj; + obj.lhs = serde::Deserializable::deserialize(deserializer); + obj.rhs = serde::Deserializable::deserialize(deserializer); obj.output = serde::Deserializable::deserialize(deserializer); return obj; } namespace Circuit { - inline bool operator==(const BlackBoxOp::Blake3 &lhs, const BlackBoxOp::Blake3 &rhs) { - if (!(lhs.message == rhs.message)) { return false; } + inline bool operator==(const BlackBoxFuncCall::BigIntDiv &lhs, const BlackBoxFuncCall::BigIntDiv &rhs) { + if (!(lhs.lhs == rhs.lhs)) { return false; } + if (!(lhs.rhs == rhs.rhs)) { return false; } if (!(lhs.output == rhs.output)) { return false; } return true; } - inline std::vector BlackBoxOp::Blake3::bincodeSerialize() const { + inline std::vector BlackBoxFuncCall::BigIntDiv::bincodeSerialize() const { auto serializer = serde::BincodeSerializer(); - serde::Serializable::serialize(*this, serializer); + serde::Serializable::serialize(*this, serializer); return std::move(serializer).bytes(); } - inline BlackBoxOp::Blake3 BlackBoxOp::Blake3::bincodeDeserialize(std::vector input) { + inline BlackBoxFuncCall::BigIntDiv BlackBoxFuncCall::BigIntDiv::bincodeDeserialize(std::vector input) { auto deserializer = serde::BincodeDeserializer(input); - auto value = serde::Deserializable::deserialize(deserializer); + auto value = serde::Deserializable::deserialize(deserializer); if (deserializer.get_buffer_offset() < input.size()) { throw serde::deserialization_error("Some input bytes were not read"); } @@ -2620,37 +2736,40 @@ namespace Circuit { template <> template -void serde::Serializable::serialize(const Circuit::BlackBoxOp::Blake3 &obj, Serializer &serializer) { - serde::Serializable::serialize(obj.message, serializer); +void serde::Serializable::serialize(const Circuit::BlackBoxFuncCall::BigIntDiv &obj, Serializer &serializer) { + serde::Serializable::serialize(obj.lhs, serializer); + serde::Serializable::serialize(obj.rhs, serializer); serde::Serializable::serialize(obj.output, serializer); } template <> template -Circuit::BlackBoxOp::Blake3 serde::Deserializable::deserialize(Deserializer &deserializer) { - Circuit::BlackBoxOp::Blake3 obj; - obj.message = serde::Deserializable::deserialize(deserializer); +Circuit::BlackBoxFuncCall::BigIntDiv serde::Deserializable::deserialize(Deserializer &deserializer) { + Circuit::BlackBoxFuncCall::BigIntDiv obj; + obj.lhs = serde::Deserializable::deserialize(deserializer); + obj.rhs = serde::Deserializable::deserialize(deserializer); obj.output = serde::Deserializable::deserialize(deserializer); return obj; } namespace Circuit { - inline bool operator==(const BlackBoxOp::Keccak256 &lhs, const BlackBoxOp::Keccak256 &rhs) { - if (!(lhs.message == rhs.message)) { return false; } + inline bool operator==(const BlackBoxFuncCall::BigIntFromLeBytes &lhs, const BlackBoxFuncCall::BigIntFromLeBytes &rhs) { + if (!(lhs.inputs == rhs.inputs)) { return false; } + if (!(lhs.modulus == rhs.modulus)) { return false; } if (!(lhs.output == rhs.output)) { return false; } return true; } - inline std::vector BlackBoxOp::Keccak256::bincodeSerialize() const { + inline std::vector BlackBoxFuncCall::BigIntFromLeBytes::bincodeSerialize() const { auto serializer = serde::BincodeSerializer(); - serde::Serializable::serialize(*this, serializer); + serde::Serializable::serialize(*this, serializer); return std::move(serializer).bytes(); } - inline BlackBoxOp::Keccak256 BlackBoxOp::Keccak256::bincodeDeserialize(std::vector input) { + inline BlackBoxFuncCall::BigIntFromLeBytes BlackBoxFuncCall::BigIntFromLeBytes::bincodeDeserialize(std::vector input) { auto deserializer = serde::BincodeDeserializer(input); - auto value = serde::Deserializable::deserialize(deserializer); + auto value = serde::Deserializable::deserialize(deserializer); if (deserializer.get_buffer_offset() < input.size()) { throw serde::deserialization_error("Some input bytes were not read"); } @@ -2661,37 +2780,39 @@ namespace Circuit { template <> template -void serde::Serializable::serialize(const Circuit::BlackBoxOp::Keccak256 &obj, Serializer &serializer) { - serde::Serializable::serialize(obj.message, serializer); +void serde::Serializable::serialize(const Circuit::BlackBoxFuncCall::BigIntFromLeBytes &obj, Serializer &serializer) { + serde::Serializable::serialize(obj.inputs, serializer); + serde::Serializable::serialize(obj.modulus, serializer); serde::Serializable::serialize(obj.output, serializer); } template <> template -Circuit::BlackBoxOp::Keccak256 serde::Deserializable::deserialize(Deserializer &deserializer) { - Circuit::BlackBoxOp::Keccak256 obj; - obj.message = serde::Deserializable::deserialize(deserializer); +Circuit::BlackBoxFuncCall::BigIntFromLeBytes serde::Deserializable::deserialize(Deserializer &deserializer) { + Circuit::BlackBoxFuncCall::BigIntFromLeBytes obj; + obj.inputs = serde::Deserializable::deserialize(deserializer); + obj.modulus = serde::Deserializable::deserialize(deserializer); obj.output = serde::Deserializable::deserialize(deserializer); return obj; } namespace Circuit { - inline bool operator==(const BlackBoxOp::Keccakf1600 &lhs, const BlackBoxOp::Keccakf1600 &rhs) { - if (!(lhs.message == rhs.message)) { return false; } - if (!(lhs.output == rhs.output)) { return false; } + inline bool operator==(const BlackBoxFuncCall::BigIntToLeBytes &lhs, const BlackBoxFuncCall::BigIntToLeBytes &rhs) { + if (!(lhs.input == rhs.input)) { return false; } + if (!(lhs.outputs == rhs.outputs)) { return false; } return true; } - inline std::vector BlackBoxOp::Keccakf1600::bincodeSerialize() const { + inline std::vector BlackBoxFuncCall::BigIntToLeBytes::bincodeSerialize() const { auto serializer = serde::BincodeSerializer(); - serde::Serializable::serialize(*this, serializer); + serde::Serializable::serialize(*this, serializer); return std::move(serializer).bytes(); } - inline BlackBoxOp::Keccakf1600 BlackBoxOp::Keccakf1600::bincodeDeserialize(std::vector input) { + inline BlackBoxFuncCall::BigIntToLeBytes BlackBoxFuncCall::BigIntToLeBytes::bincodeDeserialize(std::vector input) { auto deserializer = serde::BincodeDeserializer(input); - auto value = serde::Deserializable::deserialize(deserializer); + auto value = serde::Deserializable::deserialize(deserializer); if (deserializer.get_buffer_offset() < input.size()) { throw serde::deserialization_error("Some input bytes were not read"); } @@ -2702,40 +2823,38 @@ namespace Circuit { template <> template -void serde::Serializable::serialize(const Circuit::BlackBoxOp::Keccakf1600 &obj, Serializer &serializer) { - serde::Serializable::serialize(obj.message, serializer); - serde::Serializable::serialize(obj.output, serializer); +void serde::Serializable::serialize(const Circuit::BlackBoxFuncCall::BigIntToLeBytes &obj, Serializer &serializer) { + serde::Serializable::serialize(obj.input, serializer); + serde::Serializable::serialize(obj.outputs, serializer); } template <> template -Circuit::BlackBoxOp::Keccakf1600 serde::Deserializable::deserialize(Deserializer &deserializer) { - Circuit::BlackBoxOp::Keccakf1600 obj; - obj.message = serde::Deserializable::deserialize(deserializer); - obj.output = serde::Deserializable::deserialize(deserializer); +Circuit::BlackBoxFuncCall::BigIntToLeBytes serde::Deserializable::deserialize(Deserializer &deserializer) { + Circuit::BlackBoxFuncCall::BigIntToLeBytes obj; + obj.input = serde::Deserializable::deserialize(deserializer); + obj.outputs = serde::Deserializable::deserialize(deserializer); return obj; } namespace Circuit { - inline bool operator==(const BlackBoxOp::EcdsaSecp256k1 &lhs, const BlackBoxOp::EcdsaSecp256k1 &rhs) { - if (!(lhs.hashed_msg == rhs.hashed_msg)) { return false; } - if (!(lhs.public_key_x == rhs.public_key_x)) { return false; } - if (!(lhs.public_key_y == rhs.public_key_y)) { return false; } - if (!(lhs.signature == rhs.signature)) { return false; } - if (!(lhs.result == rhs.result)) { return false; } + inline bool operator==(const BlackBoxFuncCall::Poseidon2Permutation &lhs, const BlackBoxFuncCall::Poseidon2Permutation &rhs) { + if (!(lhs.inputs == rhs.inputs)) { return false; } + if (!(lhs.outputs == rhs.outputs)) { return false; } + if (!(lhs.len == rhs.len)) { return false; } return true; } - inline std::vector BlackBoxOp::EcdsaSecp256k1::bincodeSerialize() const { + inline std::vector BlackBoxFuncCall::Poseidon2Permutation::bincodeSerialize() const { auto serializer = serde::BincodeSerializer(); - serde::Serializable::serialize(*this, serializer); + serde::Serializable::serialize(*this, serializer); return std::move(serializer).bytes(); } - inline BlackBoxOp::EcdsaSecp256k1 BlackBoxOp::EcdsaSecp256k1::bincodeDeserialize(std::vector input) { + inline BlackBoxFuncCall::Poseidon2Permutation BlackBoxFuncCall::Poseidon2Permutation::bincodeDeserialize(std::vector input) { auto deserializer = serde::BincodeDeserializer(input); - auto value = serde::Deserializable::deserialize(deserializer); + auto value = serde::Deserializable::deserialize(deserializer); if (deserializer.get_buffer_offset() < input.size()) { throw serde::deserialization_error("Some input bytes were not read"); } @@ -2746,12 +2865,349 @@ namespace Circuit { template <> template -void serde::Serializable::serialize(const Circuit::BlackBoxOp::EcdsaSecp256k1 &obj, Serializer &serializer) { - serde::Serializable::serialize(obj.hashed_msg, serializer); - serde::Serializable::serialize(obj.public_key_x, serializer); - serde::Serializable::serialize(obj.public_key_y, serializer); - serde::Serializable::serialize(obj.signature, serializer); - serde::Serializable::serialize(obj.result, serializer); +void serde::Serializable::serialize(const Circuit::BlackBoxFuncCall::Poseidon2Permutation &obj, Serializer &serializer) { + serde::Serializable::serialize(obj.inputs, serializer); + serde::Serializable::serialize(obj.outputs, serializer); + serde::Serializable::serialize(obj.len, serializer); +} + +template <> +template +Circuit::BlackBoxFuncCall::Poseidon2Permutation serde::Deserializable::deserialize(Deserializer &deserializer) { + Circuit::BlackBoxFuncCall::Poseidon2Permutation obj; + obj.inputs = serde::Deserializable::deserialize(deserializer); + obj.outputs = serde::Deserializable::deserialize(deserializer); + obj.len = serde::Deserializable::deserialize(deserializer); + return obj; +} + +namespace Circuit { + + inline bool operator==(const BlackBoxFuncCall::Sha256Compression &lhs, const BlackBoxFuncCall::Sha256Compression &rhs) { + if (!(lhs.inputs == rhs.inputs)) { return false; } + if (!(lhs.hash_values == rhs.hash_values)) { return false; } + if (!(lhs.outputs == rhs.outputs)) { return false; } + return true; + } + + inline std::vector BlackBoxFuncCall::Sha256Compression::bincodeSerialize() const { + auto serializer = serde::BincodeSerializer(); + serde::Serializable::serialize(*this, serializer); + return std::move(serializer).bytes(); + } + + inline BlackBoxFuncCall::Sha256Compression BlackBoxFuncCall::Sha256Compression::bincodeDeserialize(std::vector input) { + auto deserializer = serde::BincodeDeserializer(input); + auto value = serde::Deserializable::deserialize(deserializer); + if (deserializer.get_buffer_offset() < input.size()) { + throw serde::deserialization_error("Some input bytes were not read"); + } + return value; + } + +} // end of namespace Circuit + +template <> +template +void serde::Serializable::serialize(const Circuit::BlackBoxFuncCall::Sha256Compression &obj, Serializer &serializer) { + serde::Serializable::serialize(obj.inputs, serializer); + serde::Serializable::serialize(obj.hash_values, serializer); + serde::Serializable::serialize(obj.outputs, serializer); +} + +template <> +template +Circuit::BlackBoxFuncCall::Sha256Compression serde::Deserializable::deserialize(Deserializer &deserializer) { + Circuit::BlackBoxFuncCall::Sha256Compression obj; + obj.inputs = serde::Deserializable::deserialize(deserializer); + obj.hash_values = serde::Deserializable::deserialize(deserializer); + obj.outputs = serde::Deserializable::deserialize(deserializer); + return obj; +} + +namespace Circuit { + + inline bool operator==(const BlackBoxOp &lhs, const BlackBoxOp &rhs) { + if (!(lhs.value == rhs.value)) { return false; } + return true; + } + + inline std::vector BlackBoxOp::bincodeSerialize() const { + auto serializer = serde::BincodeSerializer(); + serde::Serializable::serialize(*this, serializer); + return std::move(serializer).bytes(); + } + + inline BlackBoxOp BlackBoxOp::bincodeDeserialize(std::vector input) { + auto deserializer = serde::BincodeDeserializer(input); + auto value = serde::Deserializable::deserialize(deserializer); + if (deserializer.get_buffer_offset() < input.size()) { + throw serde::deserialization_error("Some input bytes were not read"); + } + return value; + } + +} // end of namespace Circuit + +template <> +template +void serde::Serializable::serialize(const Circuit::BlackBoxOp &obj, Serializer &serializer) { + serializer.increase_container_depth(); + serde::Serializable::serialize(obj.value, serializer); + serializer.decrease_container_depth(); +} + +template <> +template +Circuit::BlackBoxOp serde::Deserializable::deserialize(Deserializer &deserializer) { + deserializer.increase_container_depth(); + Circuit::BlackBoxOp obj; + obj.value = serde::Deserializable::deserialize(deserializer); + deserializer.decrease_container_depth(); + return obj; +} + +namespace Circuit { + + inline bool operator==(const BlackBoxOp::Sha256 &lhs, const BlackBoxOp::Sha256 &rhs) { + if (!(lhs.message == rhs.message)) { return false; } + if (!(lhs.output == rhs.output)) { return false; } + return true; + } + + inline std::vector BlackBoxOp::Sha256::bincodeSerialize() const { + auto serializer = serde::BincodeSerializer(); + serde::Serializable::serialize(*this, serializer); + return std::move(serializer).bytes(); + } + + inline BlackBoxOp::Sha256 BlackBoxOp::Sha256::bincodeDeserialize(std::vector input) { + auto deserializer = serde::BincodeDeserializer(input); + auto value = serde::Deserializable::deserialize(deserializer); + if (deserializer.get_buffer_offset() < input.size()) { + throw serde::deserialization_error("Some input bytes were not read"); + } + return value; + } + +} // end of namespace Circuit + +template <> +template +void serde::Serializable::serialize(const Circuit::BlackBoxOp::Sha256 &obj, Serializer &serializer) { + serde::Serializable::serialize(obj.message, serializer); + serde::Serializable::serialize(obj.output, serializer); +} + +template <> +template +Circuit::BlackBoxOp::Sha256 serde::Deserializable::deserialize(Deserializer &deserializer) { + Circuit::BlackBoxOp::Sha256 obj; + obj.message = serde::Deserializable::deserialize(deserializer); + obj.output = serde::Deserializable::deserialize(deserializer); + return obj; +} + +namespace Circuit { + + inline bool operator==(const BlackBoxOp::Blake2s &lhs, const BlackBoxOp::Blake2s &rhs) { + if (!(lhs.message == rhs.message)) { return false; } + if (!(lhs.output == rhs.output)) { return false; } + return true; + } + + inline std::vector BlackBoxOp::Blake2s::bincodeSerialize() const { + auto serializer = serde::BincodeSerializer(); + serde::Serializable::serialize(*this, serializer); + return std::move(serializer).bytes(); + } + + inline BlackBoxOp::Blake2s BlackBoxOp::Blake2s::bincodeDeserialize(std::vector input) { + auto deserializer = serde::BincodeDeserializer(input); + auto value = serde::Deserializable::deserialize(deserializer); + if (deserializer.get_buffer_offset() < input.size()) { + throw serde::deserialization_error("Some input bytes were not read"); + } + return value; + } + +} // end of namespace Circuit + +template <> +template +void serde::Serializable::serialize(const Circuit::BlackBoxOp::Blake2s &obj, Serializer &serializer) { + serde::Serializable::serialize(obj.message, serializer); + serde::Serializable::serialize(obj.output, serializer); +} + +template <> +template +Circuit::BlackBoxOp::Blake2s serde::Deserializable::deserialize(Deserializer &deserializer) { + Circuit::BlackBoxOp::Blake2s obj; + obj.message = serde::Deserializable::deserialize(deserializer); + obj.output = serde::Deserializable::deserialize(deserializer); + return obj; +} + +namespace Circuit { + + inline bool operator==(const BlackBoxOp::Blake3 &lhs, const BlackBoxOp::Blake3 &rhs) { + if (!(lhs.message == rhs.message)) { return false; } + if (!(lhs.output == rhs.output)) { return false; } + return true; + } + + inline std::vector BlackBoxOp::Blake3::bincodeSerialize() const { + auto serializer = serde::BincodeSerializer(); + serde::Serializable::serialize(*this, serializer); + return std::move(serializer).bytes(); + } + + inline BlackBoxOp::Blake3 BlackBoxOp::Blake3::bincodeDeserialize(std::vector input) { + auto deserializer = serde::BincodeDeserializer(input); + auto value = serde::Deserializable::deserialize(deserializer); + if (deserializer.get_buffer_offset() < input.size()) { + throw serde::deserialization_error("Some input bytes were not read"); + } + return value; + } + +} // end of namespace Circuit + +template <> +template +void serde::Serializable::serialize(const Circuit::BlackBoxOp::Blake3 &obj, Serializer &serializer) { + serde::Serializable::serialize(obj.message, serializer); + serde::Serializable::serialize(obj.output, serializer); +} + +template <> +template +Circuit::BlackBoxOp::Blake3 serde::Deserializable::deserialize(Deserializer &deserializer) { + Circuit::BlackBoxOp::Blake3 obj; + obj.message = serde::Deserializable::deserialize(deserializer); + obj.output = serde::Deserializable::deserialize(deserializer); + return obj; +} + +namespace Circuit { + + inline bool operator==(const BlackBoxOp::Keccak256 &lhs, const BlackBoxOp::Keccak256 &rhs) { + if (!(lhs.message == rhs.message)) { return false; } + if (!(lhs.output == rhs.output)) { return false; } + return true; + } + + inline std::vector BlackBoxOp::Keccak256::bincodeSerialize() const { + auto serializer = serde::BincodeSerializer(); + serde::Serializable::serialize(*this, serializer); + return std::move(serializer).bytes(); + } + + inline BlackBoxOp::Keccak256 BlackBoxOp::Keccak256::bincodeDeserialize(std::vector input) { + auto deserializer = serde::BincodeDeserializer(input); + auto value = serde::Deserializable::deserialize(deserializer); + if (deserializer.get_buffer_offset() < input.size()) { + throw serde::deserialization_error("Some input bytes were not read"); + } + return value; + } + +} // end of namespace Circuit + +template <> +template +void serde::Serializable::serialize(const Circuit::BlackBoxOp::Keccak256 &obj, Serializer &serializer) { + serde::Serializable::serialize(obj.message, serializer); + serde::Serializable::serialize(obj.output, serializer); +} + +template <> +template +Circuit::BlackBoxOp::Keccak256 serde::Deserializable::deserialize(Deserializer &deserializer) { + Circuit::BlackBoxOp::Keccak256 obj; + obj.message = serde::Deserializable::deserialize(deserializer); + obj.output = serde::Deserializable::deserialize(deserializer); + return obj; +} + +namespace Circuit { + + inline bool operator==(const BlackBoxOp::Keccakf1600 &lhs, const BlackBoxOp::Keccakf1600 &rhs) { + if (!(lhs.message == rhs.message)) { return false; } + if (!(lhs.output == rhs.output)) { return false; } + return true; + } + + inline std::vector BlackBoxOp::Keccakf1600::bincodeSerialize() const { + auto serializer = serde::BincodeSerializer(); + serde::Serializable::serialize(*this, serializer); + return std::move(serializer).bytes(); + } + + inline BlackBoxOp::Keccakf1600 BlackBoxOp::Keccakf1600::bincodeDeserialize(std::vector input) { + auto deserializer = serde::BincodeDeserializer(input); + auto value = serde::Deserializable::deserialize(deserializer); + if (deserializer.get_buffer_offset() < input.size()) { + throw serde::deserialization_error("Some input bytes were not read"); + } + return value; + } + +} // end of namespace Circuit + +template <> +template +void serde::Serializable::serialize(const Circuit::BlackBoxOp::Keccakf1600 &obj, Serializer &serializer) { + serde::Serializable::serialize(obj.message, serializer); + serde::Serializable::serialize(obj.output, serializer); +} + +template <> +template +Circuit::BlackBoxOp::Keccakf1600 serde::Deserializable::deserialize(Deserializer &deserializer) { + Circuit::BlackBoxOp::Keccakf1600 obj; + obj.message = serde::Deserializable::deserialize(deserializer); + obj.output = serde::Deserializable::deserialize(deserializer); + return obj; +} + +namespace Circuit { + + inline bool operator==(const BlackBoxOp::EcdsaSecp256k1 &lhs, const BlackBoxOp::EcdsaSecp256k1 &rhs) { + if (!(lhs.hashed_msg == rhs.hashed_msg)) { return false; } + if (!(lhs.public_key_x == rhs.public_key_x)) { return false; } + if (!(lhs.public_key_y == rhs.public_key_y)) { return false; } + if (!(lhs.signature == rhs.signature)) { return false; } + if (!(lhs.result == rhs.result)) { return false; } + return true; + } + + inline std::vector BlackBoxOp::EcdsaSecp256k1::bincodeSerialize() const { + auto serializer = serde::BincodeSerializer(); + serde::Serializable::serialize(*this, serializer); + return std::move(serializer).bytes(); + } + + inline BlackBoxOp::EcdsaSecp256k1 BlackBoxOp::EcdsaSecp256k1::bincodeDeserialize(std::vector input) { + auto deserializer = serde::BincodeDeserializer(input); + auto value = serde::Deserializable::deserialize(deserializer); + if (deserializer.get_buffer_offset() < input.size()) { + throw serde::deserialization_error("Some input bytes were not read"); + } + return value; + } + +} // end of namespace Circuit + +template <> +template +void serde::Serializable::serialize(const Circuit::BlackBoxOp::EcdsaSecp256k1 &obj, Serializer &serializer) { + serde::Serializable::serialize(obj.hashed_msg, serializer); + serde::Serializable::serialize(obj.public_key_x, serializer); + serde::Serializable::serialize(obj.public_key_y, serializer); + serde::Serializable::serialize(obj.signature, serializer); + serde::Serializable::serialize(obj.result, serializer); } template <> @@ -2768,24 +3224,210 @@ Circuit::BlackBoxOp::EcdsaSecp256k1 serde::Deserializable BlackBoxOp::EcdsaSecp256r1::bincodeSerialize() const { + auto serializer = serde::BincodeSerializer(); + serde::Serializable::serialize(*this, serializer); + return std::move(serializer).bytes(); + } + + inline BlackBoxOp::EcdsaSecp256r1 BlackBoxOp::EcdsaSecp256r1::bincodeDeserialize(std::vector input) { + auto deserializer = serde::BincodeDeserializer(input); + auto value = serde::Deserializable::deserialize(deserializer); + if (deserializer.get_buffer_offset() < input.size()) { + throw serde::deserialization_error("Some input bytes were not read"); + } + return value; + } + +} // end of namespace Circuit + +template <> +template +void serde::Serializable::serialize(const Circuit::BlackBoxOp::EcdsaSecp256r1 &obj, Serializer &serializer) { + serde::Serializable::serialize(obj.hashed_msg, serializer); + serde::Serializable::serialize(obj.public_key_x, serializer); + serde::Serializable::serialize(obj.public_key_y, serializer); + serde::Serializable::serialize(obj.signature, serializer); + serde::Serializable::serialize(obj.result, serializer); +} + +template <> +template +Circuit::BlackBoxOp::EcdsaSecp256r1 serde::Deserializable::deserialize(Deserializer &deserializer) { + Circuit::BlackBoxOp::EcdsaSecp256r1 obj; + obj.hashed_msg = serde::Deserializable::deserialize(deserializer); + obj.public_key_x = serde::Deserializable::deserialize(deserializer); + obj.public_key_y = serde::Deserializable::deserialize(deserializer); + obj.signature = serde::Deserializable::deserialize(deserializer); + obj.result = serde::Deserializable::deserialize(deserializer); + return obj; +} + +namespace Circuit { + + inline bool operator==(const BlackBoxOp::SchnorrVerify &lhs, const BlackBoxOp::SchnorrVerify &rhs) { + if (!(lhs.public_key_x == rhs.public_key_x)) { return false; } + if (!(lhs.public_key_y == rhs.public_key_y)) { return false; } + if (!(lhs.message == rhs.message)) { return false; } + if (!(lhs.signature == rhs.signature)) { return false; } + if (!(lhs.result == rhs.result)) { return false; } + return true; + } + + inline std::vector BlackBoxOp::SchnorrVerify::bincodeSerialize() const { + auto serializer = serde::BincodeSerializer(); + serde::Serializable::serialize(*this, serializer); + return std::move(serializer).bytes(); + } + + inline BlackBoxOp::SchnorrVerify BlackBoxOp::SchnorrVerify::bincodeDeserialize(std::vector input) { + auto deserializer = serde::BincodeDeserializer(input); + auto value = serde::Deserializable::deserialize(deserializer); + if (deserializer.get_buffer_offset() < input.size()) { + throw serde::deserialization_error("Some input bytes were not read"); + } + return value; + } + +} // end of namespace Circuit + +template <> +template +void serde::Serializable::serialize(const Circuit::BlackBoxOp::SchnorrVerify &obj, Serializer &serializer) { + serde::Serializable::serialize(obj.public_key_x, serializer); + serde::Serializable::serialize(obj.public_key_y, serializer); + serde::Serializable::serialize(obj.message, serializer); + serde::Serializable::serialize(obj.signature, serializer); + serde::Serializable::serialize(obj.result, serializer); +} + +template <> +template +Circuit::BlackBoxOp::SchnorrVerify serde::Deserializable::deserialize(Deserializer &deserializer) { + Circuit::BlackBoxOp::SchnorrVerify obj; + obj.public_key_x = serde::Deserializable::deserialize(deserializer); + obj.public_key_y = serde::Deserializable::deserialize(deserializer); + obj.message = serde::Deserializable::deserialize(deserializer); + obj.signature = serde::Deserializable::deserialize(deserializer); + obj.result = serde::Deserializable::deserialize(deserializer); + return obj; +} + +namespace Circuit { + + inline bool operator==(const BlackBoxOp::PedersenCommitment &lhs, const BlackBoxOp::PedersenCommitment &rhs) { + if (!(lhs.inputs == rhs.inputs)) { return false; } + if (!(lhs.domain_separator == rhs.domain_separator)) { return false; } + if (!(lhs.output == rhs.output)) { return false; } + return true; + } + + inline std::vector BlackBoxOp::PedersenCommitment::bincodeSerialize() const { + auto serializer = serde::BincodeSerializer(); + serde::Serializable::serialize(*this, serializer); + return std::move(serializer).bytes(); + } + + inline BlackBoxOp::PedersenCommitment BlackBoxOp::PedersenCommitment::bincodeDeserialize(std::vector input) { + auto deserializer = serde::BincodeDeserializer(input); + auto value = serde::Deserializable::deserialize(deserializer); + if (deserializer.get_buffer_offset() < input.size()) { + throw serde::deserialization_error("Some input bytes were not read"); + } + return value; + } + +} // end of namespace Circuit + +template <> +template +void serde::Serializable::serialize(const Circuit::BlackBoxOp::PedersenCommitment &obj, Serializer &serializer) { + serde::Serializable::serialize(obj.inputs, serializer); + serde::Serializable::serialize(obj.domain_separator, serializer); + serde::Serializable::serialize(obj.output, serializer); +} + +template <> +template +Circuit::BlackBoxOp::PedersenCommitment serde::Deserializable::deserialize(Deserializer &deserializer) { + Circuit::BlackBoxOp::PedersenCommitment obj; + obj.inputs = serde::Deserializable::deserialize(deserializer); + obj.domain_separator = serde::Deserializable::deserialize(deserializer); + obj.output = serde::Deserializable::deserialize(deserializer); + return obj; +} + +namespace Circuit { + + inline bool operator==(const BlackBoxOp::PedersenHash &lhs, const BlackBoxOp::PedersenHash &rhs) { + if (!(lhs.inputs == rhs.inputs)) { return false; } + if (!(lhs.domain_separator == rhs.domain_separator)) { return false; } + if (!(lhs.output == rhs.output)) { return false; } + return true; + } + + inline std::vector BlackBoxOp::PedersenHash::bincodeSerialize() const { + auto serializer = serde::BincodeSerializer(); + serde::Serializable::serialize(*this, serializer); + return std::move(serializer).bytes(); + } + + inline BlackBoxOp::PedersenHash BlackBoxOp::PedersenHash::bincodeDeserialize(std::vector input) { + auto deserializer = serde::BincodeDeserializer(input); + auto value = serde::Deserializable::deserialize(deserializer); + if (deserializer.get_buffer_offset() < input.size()) { + throw serde::deserialization_error("Some input bytes were not read"); + } + return value; + } + +} // end of namespace Circuit + +template <> +template +void serde::Serializable::serialize(const Circuit::BlackBoxOp::PedersenHash &obj, Serializer &serializer) { + serde::Serializable::serialize(obj.inputs, serializer); + serde::Serializable::serialize(obj.domain_separator, serializer); + serde::Serializable::serialize(obj.output, serializer); +} + +template <> +template +Circuit::BlackBoxOp::PedersenHash serde::Deserializable::deserialize(Deserializer &deserializer) { + Circuit::BlackBoxOp::PedersenHash obj; + obj.inputs = serde::Deserializable::deserialize(deserializer); + obj.domain_separator = serde::Deserializable::deserialize(deserializer); + obj.output = serde::Deserializable::deserialize(deserializer); + return obj; +} + +namespace Circuit { + + inline bool operator==(const BlackBoxOp::FixedBaseScalarMul &lhs, const BlackBoxOp::FixedBaseScalarMul &rhs) { + if (!(lhs.low == rhs.low)) { return false; } + if (!(lhs.high == rhs.high)) { return false; } if (!(lhs.result == rhs.result)) { return false; } return true; } - inline std::vector BlackBoxOp::EcdsaSecp256r1::bincodeSerialize() const { + inline std::vector BlackBoxOp::FixedBaseScalarMul::bincodeSerialize() const { auto serializer = serde::BincodeSerializer(); - serde::Serializable::serialize(*this, serializer); + serde::Serializable::serialize(*this, serializer); return std::move(serializer).bytes(); } - inline BlackBoxOp::EcdsaSecp256r1 BlackBoxOp::EcdsaSecp256r1::bincodeDeserialize(std::vector input) { + inline BlackBoxOp::FixedBaseScalarMul BlackBoxOp::FixedBaseScalarMul::bincodeDeserialize(std::vector input) { auto deserializer = serde::BincodeDeserializer(input); - auto value = serde::Deserializable::deserialize(deserializer); + auto value = serde::Deserializable::deserialize(deserializer); if (deserializer.get_buffer_offset() < input.size()) { throw serde::deserialization_error("Some input bytes were not read"); } @@ -2796,46 +3438,178 @@ namespace Circuit { template <> template -void serde::Serializable::serialize(const Circuit::BlackBoxOp::EcdsaSecp256r1 &obj, Serializer &serializer) { - serde::Serializable::serialize(obj.hashed_msg, serializer); - serde::Serializable::serialize(obj.public_key_x, serializer); - serde::Serializable::serialize(obj.public_key_y, serializer); - serde::Serializable::serialize(obj.signature, serializer); +void serde::Serializable::serialize(const Circuit::BlackBoxOp::FixedBaseScalarMul &obj, Serializer &serializer) { + serde::Serializable::serialize(obj.low, serializer); + serde::Serializable::serialize(obj.high, serializer); serde::Serializable::serialize(obj.result, serializer); } template <> template -Circuit::BlackBoxOp::EcdsaSecp256r1 serde::Deserializable::deserialize(Deserializer &deserializer) { - Circuit::BlackBoxOp::EcdsaSecp256r1 obj; - obj.hashed_msg = serde::Deserializable::deserialize(deserializer); - obj.public_key_x = serde::Deserializable::deserialize(deserializer); - obj.public_key_y = serde::Deserializable::deserialize(deserializer); - obj.signature = serde::Deserializable::deserialize(deserializer); +Circuit::BlackBoxOp::FixedBaseScalarMul serde::Deserializable::deserialize(Deserializer &deserializer) { + Circuit::BlackBoxOp::FixedBaseScalarMul obj; + obj.low = serde::Deserializable::deserialize(deserializer); + obj.high = serde::Deserializable::deserialize(deserializer); + obj.result = serde::Deserializable::deserialize(deserializer); + return obj; +} + +namespace Circuit { + + inline bool operator==(const BlackBoxOp::EmbeddedCurveAdd &lhs, const BlackBoxOp::EmbeddedCurveAdd &rhs) { + if (!(lhs.input1_x == rhs.input1_x)) { return false; } + if (!(lhs.input1_y == rhs.input1_y)) { return false; } + if (!(lhs.input2_x == rhs.input2_x)) { return false; } + if (!(lhs.input2_y == rhs.input2_y)) { return false; } + if (!(lhs.result == rhs.result)) { return false; } + return true; + } + + inline std::vector BlackBoxOp::EmbeddedCurveAdd::bincodeSerialize() const { + auto serializer = serde::BincodeSerializer(); + serde::Serializable::serialize(*this, serializer); + return std::move(serializer).bytes(); + } + + inline BlackBoxOp::EmbeddedCurveAdd BlackBoxOp::EmbeddedCurveAdd::bincodeDeserialize(std::vector input) { + auto deserializer = serde::BincodeDeserializer(input); + auto value = serde::Deserializable::deserialize(deserializer); + if (deserializer.get_buffer_offset() < input.size()) { + throw serde::deserialization_error("Some input bytes were not read"); + } + return value; + } + +} // end of namespace Circuit + +template <> +template +void serde::Serializable::serialize(const Circuit::BlackBoxOp::EmbeddedCurveAdd &obj, Serializer &serializer) { + serde::Serializable::serialize(obj.input1_x, serializer); + serde::Serializable::serialize(obj.input1_y, serializer); + serde::Serializable::serialize(obj.input2_x, serializer); + serde::Serializable::serialize(obj.input2_y, serializer); + serde::Serializable::serialize(obj.result, serializer); +} + +template <> +template +Circuit::BlackBoxOp::EmbeddedCurveAdd serde::Deserializable::deserialize(Deserializer &deserializer) { + Circuit::BlackBoxOp::EmbeddedCurveAdd obj; + obj.input1_x = serde::Deserializable::deserialize(deserializer); + obj.input1_y = serde::Deserializable::deserialize(deserializer); + obj.input2_x = serde::Deserializable::deserialize(deserializer); + obj.input2_y = serde::Deserializable::deserialize(deserializer); obj.result = serde::Deserializable::deserialize(deserializer); return obj; } namespace Circuit { - inline bool operator==(const BlackBoxOp::SchnorrVerify &lhs, const BlackBoxOp::SchnorrVerify &rhs) { - if (!(lhs.public_key_x == rhs.public_key_x)) { return false; } - if (!(lhs.public_key_y == rhs.public_key_y)) { return false; } - if (!(lhs.message == rhs.message)) { return false; } - if (!(lhs.signature == rhs.signature)) { return false; } - if (!(lhs.result == rhs.result)) { return false; } + inline bool operator==(const BlackBoxOp::BigIntAdd &lhs, const BlackBoxOp::BigIntAdd &rhs) { + if (!(lhs.lhs == rhs.lhs)) { return false; } + if (!(lhs.rhs == rhs.rhs)) { return false; } + if (!(lhs.output == rhs.output)) { return false; } + return true; + } + + inline std::vector BlackBoxOp::BigIntAdd::bincodeSerialize() const { + auto serializer = serde::BincodeSerializer(); + serde::Serializable::serialize(*this, serializer); + return std::move(serializer).bytes(); + } + + inline BlackBoxOp::BigIntAdd BlackBoxOp::BigIntAdd::bincodeDeserialize(std::vector input) { + auto deserializer = serde::BincodeDeserializer(input); + auto value = serde::Deserializable::deserialize(deserializer); + if (deserializer.get_buffer_offset() < input.size()) { + throw serde::deserialization_error("Some input bytes were not read"); + } + return value; + } + +} // end of namespace Circuit + +template <> +template +void serde::Serializable::serialize(const Circuit::BlackBoxOp::BigIntAdd &obj, Serializer &serializer) { + serde::Serializable::serialize(obj.lhs, serializer); + serde::Serializable::serialize(obj.rhs, serializer); + serde::Serializable::serialize(obj.output, serializer); +} + +template <> +template +Circuit::BlackBoxOp::BigIntAdd serde::Deserializable::deserialize(Deserializer &deserializer) { + Circuit::BlackBoxOp::BigIntAdd obj; + obj.lhs = serde::Deserializable::deserialize(deserializer); + obj.rhs = serde::Deserializable::deserialize(deserializer); + obj.output = serde::Deserializable::deserialize(deserializer); + return obj; +} + +namespace Circuit { + + inline bool operator==(const BlackBoxOp::BigIntNeg &lhs, const BlackBoxOp::BigIntNeg &rhs) { + if (!(lhs.lhs == rhs.lhs)) { return false; } + if (!(lhs.rhs == rhs.rhs)) { return false; } + if (!(lhs.output == rhs.output)) { return false; } + return true; + } + + inline std::vector BlackBoxOp::BigIntNeg::bincodeSerialize() const { + auto serializer = serde::BincodeSerializer(); + serde::Serializable::serialize(*this, serializer); + return std::move(serializer).bytes(); + } + + inline BlackBoxOp::BigIntNeg BlackBoxOp::BigIntNeg::bincodeDeserialize(std::vector input) { + auto deserializer = serde::BincodeDeserializer(input); + auto value = serde::Deserializable::deserialize(deserializer); + if (deserializer.get_buffer_offset() < input.size()) { + throw serde::deserialization_error("Some input bytes were not read"); + } + return value; + } + +} // end of namespace Circuit + +template <> +template +void serde::Serializable::serialize(const Circuit::BlackBoxOp::BigIntNeg &obj, Serializer &serializer) { + serde::Serializable::serialize(obj.lhs, serializer); + serde::Serializable::serialize(obj.rhs, serializer); + serde::Serializable::serialize(obj.output, serializer); +} + +template <> +template +Circuit::BlackBoxOp::BigIntNeg serde::Deserializable::deserialize(Deserializer &deserializer) { + Circuit::BlackBoxOp::BigIntNeg obj; + obj.lhs = serde::Deserializable::deserialize(deserializer); + obj.rhs = serde::Deserializable::deserialize(deserializer); + obj.output = serde::Deserializable::deserialize(deserializer); + return obj; +} + +namespace Circuit { + + inline bool operator==(const BlackBoxOp::BigIntMul &lhs, const BlackBoxOp::BigIntMul &rhs) { + if (!(lhs.lhs == rhs.lhs)) { return false; } + if (!(lhs.rhs == rhs.rhs)) { return false; } + if (!(lhs.output == rhs.output)) { return false; } return true; } - inline std::vector BlackBoxOp::SchnorrVerify::bincodeSerialize() const { + inline std::vector BlackBoxOp::BigIntMul::bincodeSerialize() const { auto serializer = serde::BincodeSerializer(); - serde::Serializable::serialize(*this, serializer); + serde::Serializable::serialize(*this, serializer); return std::move(serializer).bytes(); } - inline BlackBoxOp::SchnorrVerify BlackBoxOp::SchnorrVerify::bincodeDeserialize(std::vector input) { + inline BlackBoxOp::BigIntMul BlackBoxOp::BigIntMul::bincodeDeserialize(std::vector input) { auto deserializer = serde::BincodeDeserializer(input); - auto value = serde::Deserializable::deserialize(deserializer); + auto value = serde::Deserializable::deserialize(deserializer); if (deserializer.get_buffer_offset() < input.size()) { throw serde::deserialization_error("Some input bytes were not read"); } @@ -2846,44 +3620,40 @@ namespace Circuit { template <> template -void serde::Serializable::serialize(const Circuit::BlackBoxOp::SchnorrVerify &obj, Serializer &serializer) { - serde::Serializable::serialize(obj.public_key_x, serializer); - serde::Serializable::serialize(obj.public_key_y, serializer); - serde::Serializable::serialize(obj.message, serializer); - serde::Serializable::serialize(obj.signature, serializer); - serde::Serializable::serialize(obj.result, serializer); +void serde::Serializable::serialize(const Circuit::BlackBoxOp::BigIntMul &obj, Serializer &serializer) { + serde::Serializable::serialize(obj.lhs, serializer); + serde::Serializable::serialize(obj.rhs, serializer); + serde::Serializable::serialize(obj.output, serializer); } template <> template -Circuit::BlackBoxOp::SchnorrVerify serde::Deserializable::deserialize(Deserializer &deserializer) { - Circuit::BlackBoxOp::SchnorrVerify obj; - obj.public_key_x = serde::Deserializable::deserialize(deserializer); - obj.public_key_y = serde::Deserializable::deserialize(deserializer); - obj.message = serde::Deserializable::deserialize(deserializer); - obj.signature = serde::Deserializable::deserialize(deserializer); - obj.result = serde::Deserializable::deserialize(deserializer); +Circuit::BlackBoxOp::BigIntMul serde::Deserializable::deserialize(Deserializer &deserializer) { + Circuit::BlackBoxOp::BigIntMul obj; + obj.lhs = serde::Deserializable::deserialize(deserializer); + obj.rhs = serde::Deserializable::deserialize(deserializer); + obj.output = serde::Deserializable::deserialize(deserializer); return obj; } namespace Circuit { - inline bool operator==(const BlackBoxOp::PedersenCommitment &lhs, const BlackBoxOp::PedersenCommitment &rhs) { - if (!(lhs.inputs == rhs.inputs)) { return false; } - if (!(lhs.domain_separator == rhs.domain_separator)) { return false; } + inline bool operator==(const BlackBoxOp::BigIntDiv &lhs, const BlackBoxOp::BigIntDiv &rhs) { + if (!(lhs.lhs == rhs.lhs)) { return false; } + if (!(lhs.rhs == rhs.rhs)) { return false; } if (!(lhs.output == rhs.output)) { return false; } return true; } - inline std::vector BlackBoxOp::PedersenCommitment::bincodeSerialize() const { + inline std::vector BlackBoxOp::BigIntDiv::bincodeSerialize() const { auto serializer = serde::BincodeSerializer(); - serde::Serializable::serialize(*this, serializer); + serde::Serializable::serialize(*this, serializer); return std::move(serializer).bytes(); } - inline BlackBoxOp::PedersenCommitment BlackBoxOp::PedersenCommitment::bincodeDeserialize(std::vector input) { + inline BlackBoxOp::BigIntDiv BlackBoxOp::BigIntDiv::bincodeDeserialize(std::vector input) { auto deserializer = serde::BincodeDeserializer(input); - auto value = serde::Deserializable::deserialize(deserializer); + auto value = serde::Deserializable::deserialize(deserializer); if (deserializer.get_buffer_offset() < input.size()) { throw serde::deserialization_error("Some input bytes were not read"); } @@ -2894,40 +3664,40 @@ namespace Circuit { template <> template -void serde::Serializable::serialize(const Circuit::BlackBoxOp::PedersenCommitment &obj, Serializer &serializer) { - serde::Serializable::serialize(obj.inputs, serializer); - serde::Serializable::serialize(obj.domain_separator, serializer); +void serde::Serializable::serialize(const Circuit::BlackBoxOp::BigIntDiv &obj, Serializer &serializer) { + serde::Serializable::serialize(obj.lhs, serializer); + serde::Serializable::serialize(obj.rhs, serializer); serde::Serializable::serialize(obj.output, serializer); } template <> template -Circuit::BlackBoxOp::PedersenCommitment serde::Deserializable::deserialize(Deserializer &deserializer) { - Circuit::BlackBoxOp::PedersenCommitment obj; - obj.inputs = serde::Deserializable::deserialize(deserializer); - obj.domain_separator = serde::Deserializable::deserialize(deserializer); +Circuit::BlackBoxOp::BigIntDiv serde::Deserializable::deserialize(Deserializer &deserializer) { + Circuit::BlackBoxOp::BigIntDiv obj; + obj.lhs = serde::Deserializable::deserialize(deserializer); + obj.rhs = serde::Deserializable::deserialize(deserializer); obj.output = serde::Deserializable::deserialize(deserializer); return obj; } namespace Circuit { - inline bool operator==(const BlackBoxOp::PedersenHash &lhs, const BlackBoxOp::PedersenHash &rhs) { + inline bool operator==(const BlackBoxOp::BigIntFromLeBytes &lhs, const BlackBoxOp::BigIntFromLeBytes &rhs) { if (!(lhs.inputs == rhs.inputs)) { return false; } - if (!(lhs.domain_separator == rhs.domain_separator)) { return false; } + if (!(lhs.modulus == rhs.modulus)) { return false; } if (!(lhs.output == rhs.output)) { return false; } return true; } - inline std::vector BlackBoxOp::PedersenHash::bincodeSerialize() const { + inline std::vector BlackBoxOp::BigIntFromLeBytes::bincodeSerialize() const { auto serializer = serde::BincodeSerializer(); - serde::Serializable::serialize(*this, serializer); + serde::Serializable::serialize(*this, serializer); return std::move(serializer).bytes(); } - inline BlackBoxOp::PedersenHash BlackBoxOp::PedersenHash::bincodeDeserialize(std::vector input) { + inline BlackBoxOp::BigIntFromLeBytes BlackBoxOp::BigIntFromLeBytes::bincodeDeserialize(std::vector input) { auto deserializer = serde::BincodeDeserializer(input); - auto value = serde::Deserializable::deserialize(deserializer); + auto value = serde::Deserializable::deserialize(deserializer); if (deserializer.get_buffer_offset() < input.size()) { throw serde::deserialization_error("Some input bytes were not read"); } @@ -2938,40 +3708,39 @@ namespace Circuit { template <> template -void serde::Serializable::serialize(const Circuit::BlackBoxOp::PedersenHash &obj, Serializer &serializer) { +void serde::Serializable::serialize(const Circuit::BlackBoxOp::BigIntFromLeBytes &obj, Serializer &serializer) { serde::Serializable::serialize(obj.inputs, serializer); - serde::Serializable::serialize(obj.domain_separator, serializer); + serde::Serializable::serialize(obj.modulus, serializer); serde::Serializable::serialize(obj.output, serializer); } template <> template -Circuit::BlackBoxOp::PedersenHash serde::Deserializable::deserialize(Deserializer &deserializer) { - Circuit::BlackBoxOp::PedersenHash obj; +Circuit::BlackBoxOp::BigIntFromLeBytes serde::Deserializable::deserialize(Deserializer &deserializer) { + Circuit::BlackBoxOp::BigIntFromLeBytes obj; obj.inputs = serde::Deserializable::deserialize(deserializer); - obj.domain_separator = serde::Deserializable::deserialize(deserializer); + obj.modulus = serde::Deserializable::deserialize(deserializer); obj.output = serde::Deserializable::deserialize(deserializer); return obj; } namespace Circuit { - inline bool operator==(const BlackBoxOp::FixedBaseScalarMul &lhs, const BlackBoxOp::FixedBaseScalarMul &rhs) { - if (!(lhs.low == rhs.low)) { return false; } - if (!(lhs.high == rhs.high)) { return false; } - if (!(lhs.result == rhs.result)) { return false; } + inline bool operator==(const BlackBoxOp::BigIntToLeBytes &lhs, const BlackBoxOp::BigIntToLeBytes &rhs) { + if (!(lhs.input == rhs.input)) { return false; } + if (!(lhs.output == rhs.output)) { return false; } return true; } - inline std::vector BlackBoxOp::FixedBaseScalarMul::bincodeSerialize() const { + inline std::vector BlackBoxOp::BigIntToLeBytes::bincodeSerialize() const { auto serializer = serde::BincodeSerializer(); - serde::Serializable::serialize(*this, serializer); + serde::Serializable::serialize(*this, serializer); return std::move(serializer).bytes(); } - inline BlackBoxOp::FixedBaseScalarMul BlackBoxOp::FixedBaseScalarMul::bincodeDeserialize(std::vector input) { + inline BlackBoxOp::BigIntToLeBytes BlackBoxOp::BigIntToLeBytes::bincodeDeserialize(std::vector input) { auto deserializer = serde::BincodeDeserializer(input); - auto value = serde::Deserializable::deserialize(deserializer); + auto value = serde::Deserializable::deserialize(deserializer); if (deserializer.get_buffer_offset() < input.size()) { throw serde::deserialization_error("Some input bytes were not read"); } @@ -2982,42 +3751,38 @@ namespace Circuit { template <> template -void serde::Serializable::serialize(const Circuit::BlackBoxOp::FixedBaseScalarMul &obj, Serializer &serializer) { - serde::Serializable::serialize(obj.low, serializer); - serde::Serializable::serialize(obj.high, serializer); - serde::Serializable::serialize(obj.result, serializer); +void serde::Serializable::serialize(const Circuit::BlackBoxOp::BigIntToLeBytes &obj, Serializer &serializer) { + serde::Serializable::serialize(obj.input, serializer); + serde::Serializable::serialize(obj.output, serializer); } template <> template -Circuit::BlackBoxOp::FixedBaseScalarMul serde::Deserializable::deserialize(Deserializer &deserializer) { - Circuit::BlackBoxOp::FixedBaseScalarMul obj; - obj.low = serde::Deserializable::deserialize(deserializer); - obj.high = serde::Deserializable::deserialize(deserializer); - obj.result = serde::Deserializable::deserialize(deserializer); +Circuit::BlackBoxOp::BigIntToLeBytes serde::Deserializable::deserialize(Deserializer &deserializer) { + Circuit::BlackBoxOp::BigIntToLeBytes obj; + obj.input = serde::Deserializable::deserialize(deserializer); + obj.output = serde::Deserializable::deserialize(deserializer); return obj; } namespace Circuit { - inline bool operator==(const BlackBoxOp::EmbeddedCurveAdd &lhs, const BlackBoxOp::EmbeddedCurveAdd &rhs) { - if (!(lhs.input1_x == rhs.input1_x)) { return false; } - if (!(lhs.input1_y == rhs.input1_y)) { return false; } - if (!(lhs.input2_x == rhs.input2_x)) { return false; } - if (!(lhs.input2_y == rhs.input2_y)) { return false; } - if (!(lhs.result == rhs.result)) { return false; } + inline bool operator==(const BlackBoxOp::Poseidon2Permutation &lhs, const BlackBoxOp::Poseidon2Permutation &rhs) { + if (!(lhs.message == rhs.message)) { return false; } + if (!(lhs.output == rhs.output)) { return false; } + if (!(lhs.len == rhs.len)) { return false; } return true; } - inline std::vector BlackBoxOp::EmbeddedCurveAdd::bincodeSerialize() const { + inline std::vector BlackBoxOp::Poseidon2Permutation::bincodeSerialize() const { auto serializer = serde::BincodeSerializer(); - serde::Serializable::serialize(*this, serializer); + serde::Serializable::serialize(*this, serializer); return std::move(serializer).bytes(); } - inline BlackBoxOp::EmbeddedCurveAdd BlackBoxOp::EmbeddedCurveAdd::bincodeDeserialize(std::vector input) { + inline BlackBoxOp::Poseidon2Permutation BlackBoxOp::Poseidon2Permutation::bincodeDeserialize(std::vector input) { auto deserializer = serde::BincodeDeserializer(input); - auto value = serde::Deserializable::deserialize(deserializer); + auto value = serde::Deserializable::deserialize(deserializer); if (deserializer.get_buffer_offset() < input.size()) { throw serde::deserialization_error("Some input bytes were not read"); } @@ -3028,44 +3793,40 @@ namespace Circuit { template <> template -void serde::Serializable::serialize(const Circuit::BlackBoxOp::EmbeddedCurveAdd &obj, Serializer &serializer) { - serde::Serializable::serialize(obj.input1_x, serializer); - serde::Serializable::serialize(obj.input1_y, serializer); - serde::Serializable::serialize(obj.input2_x, serializer); - serde::Serializable::serialize(obj.input2_y, serializer); - serde::Serializable::serialize(obj.result, serializer); +void serde::Serializable::serialize(const Circuit::BlackBoxOp::Poseidon2Permutation &obj, Serializer &serializer) { + serde::Serializable::serialize(obj.message, serializer); + serde::Serializable::serialize(obj.output, serializer); + serde::Serializable::serialize(obj.len, serializer); } template <> template -Circuit::BlackBoxOp::EmbeddedCurveAdd serde::Deserializable::deserialize(Deserializer &deserializer) { - Circuit::BlackBoxOp::EmbeddedCurveAdd obj; - obj.input1_x = serde::Deserializable::deserialize(deserializer); - obj.input1_y = serde::Deserializable::deserialize(deserializer); - obj.input2_x = serde::Deserializable::deserialize(deserializer); - obj.input2_y = serde::Deserializable::deserialize(deserializer); - obj.result = serde::Deserializable::deserialize(deserializer); +Circuit::BlackBoxOp::Poseidon2Permutation serde::Deserializable::deserialize(Deserializer &deserializer) { + Circuit::BlackBoxOp::Poseidon2Permutation obj; + obj.message = serde::Deserializable::deserialize(deserializer); + obj.output = serde::Deserializable::deserialize(deserializer); + obj.len = serde::Deserializable::deserialize(deserializer); return obj; } namespace Circuit { - inline bool operator==(const BlackBoxOp::EmbeddedCurveDouble &lhs, const BlackBoxOp::EmbeddedCurveDouble &rhs) { - if (!(lhs.input1_x == rhs.input1_x)) { return false; } - if (!(lhs.input1_y == rhs.input1_y)) { return false; } - if (!(lhs.result == rhs.result)) { return false; } + inline bool operator==(const BlackBoxOp::Sha256Compression &lhs, const BlackBoxOp::Sha256Compression &rhs) { + if (!(lhs.input == rhs.input)) { return false; } + if (!(lhs.hash_values == rhs.hash_values)) { return false; } + if (!(lhs.output == rhs.output)) { return false; } return true; } - inline std::vector BlackBoxOp::EmbeddedCurveDouble::bincodeSerialize() const { + inline std::vector BlackBoxOp::Sha256Compression::bincodeSerialize() const { auto serializer = serde::BincodeSerializer(); - serde::Serializable::serialize(*this, serializer); + serde::Serializable::serialize(*this, serializer); return std::move(serializer).bytes(); } - inline BlackBoxOp::EmbeddedCurveDouble BlackBoxOp::EmbeddedCurveDouble::bincodeDeserialize(std::vector input) { + inline BlackBoxOp::Sha256Compression BlackBoxOp::Sha256Compression::bincodeDeserialize(std::vector input) { auto deserializer = serde::BincodeDeserializer(input); - auto value = serde::Deserializable::deserialize(deserializer); + auto value = serde::Deserializable::deserialize(deserializer); if (deserializer.get_buffer_offset() < input.size()) { throw serde::deserialization_error("Some input bytes were not read"); } @@ -3076,19 +3837,19 @@ namespace Circuit { template <> template -void serde::Serializable::serialize(const Circuit::BlackBoxOp::EmbeddedCurveDouble &obj, Serializer &serializer) { - serde::Serializable::serialize(obj.input1_x, serializer); - serde::Serializable::serialize(obj.input1_y, serializer); - serde::Serializable::serialize(obj.result, serializer); +void serde::Serializable::serialize(const Circuit::BlackBoxOp::Sha256Compression &obj, Serializer &serializer) { + serde::Serializable::serialize(obj.input, serializer); + serde::Serializable::serialize(obj.hash_values, serializer); + serde::Serializable::serialize(obj.output, serializer); } template <> template -Circuit::BlackBoxOp::EmbeddedCurveDouble serde::Deserializable::deserialize(Deserializer &deserializer) { - Circuit::BlackBoxOp::EmbeddedCurveDouble obj; - obj.input1_x = serde::Deserializable::deserialize(deserializer); - obj.input1_y = serde::Deserializable::deserialize(deserializer); - obj.result = serde::Deserializable::deserialize(deserializer); +Circuit::BlackBoxOp::Sha256Compression serde::Deserializable::deserialize(Deserializer &deserializer) { + Circuit::BlackBoxOp::Sha256Compression obj; + obj.input = serde::Deserializable::deserialize(deserializer); + obj.hash_values = serde::Deserializable::deserialize(deserializer); + obj.output = serde::Deserializable::deserialize(deserializer); return obj; } @@ -3562,6 +4323,50 @@ Circuit::BrilligOpcode::Jump serde::Deserializable return obj; } +namespace Circuit { + + inline bool operator==(const BrilligOpcode::CalldataCopy &lhs, const BrilligOpcode::CalldataCopy &rhs) { + if (!(lhs.destination_address == rhs.destination_address)) { return false; } + if (!(lhs.size == rhs.size)) { return false; } + if (!(lhs.offset == rhs.offset)) { return false; } + return true; + } + + inline std::vector BrilligOpcode::CalldataCopy::bincodeSerialize() const { + auto serializer = serde::BincodeSerializer(); + serde::Serializable::serialize(*this, serializer); + return std::move(serializer).bytes(); + } + + inline BrilligOpcode::CalldataCopy BrilligOpcode::CalldataCopy::bincodeDeserialize(std::vector input) { + auto deserializer = serde::BincodeDeserializer(input); + auto value = serde::Deserializable::deserialize(deserializer); + if (deserializer.get_buffer_offset() < input.size()) { + throw serde::deserialization_error("Some input bytes were not read"); + } + return value; + } + +} // end of namespace Circuit + +template <> +template +void serde::Serializable::serialize(const Circuit::BrilligOpcode::CalldataCopy &obj, Serializer &serializer) { + serde::Serializable::serialize(obj.destination_address, serializer); + serde::Serializable::serialize(obj.size, serializer); + serde::Serializable::serialize(obj.offset, serializer); +} + +template <> +template +Circuit::BrilligOpcode::CalldataCopy serde::Deserializable::deserialize(Deserializer &deserializer) { + Circuit::BrilligOpcode::CalldataCopy obj; + obj.destination_address = serde::Deserializable::deserialize(deserializer); + obj.size = serde::Deserializable::deserialize(deserializer); + obj.offset = serde::Deserializable::deserialize(deserializer); + return obj; +} + namespace Circuit { inline bool operator==(const BrilligOpcode::Call &lhs, const BrilligOpcode::Call &rhs) { @@ -3919,6 +4724,8 @@ Circuit::BrilligOpcode::Trap serde::Deserializable namespace Circuit { inline bool operator==(const BrilligOpcode::Stop &lhs, const BrilligOpcode::Stop &rhs) { + if (!(lhs.return_data_offset == rhs.return_data_offset)) { return false; } + if (!(lhs.return_data_size == rhs.return_data_size)) { return false; } return true; } @@ -3942,12 +4749,16 @@ namespace Circuit { template <> template void serde::Serializable::serialize(const Circuit::BrilligOpcode::Stop &obj, Serializer &serializer) { + serde::Serializable::serialize(obj.return_data_offset, serializer); + serde::Serializable::serialize(obj.return_data_size, serializer); } template <> template Circuit::BrilligOpcode::Stop serde::Deserializable::deserialize(Deserializer &deserializer) { Circuit::BrilligOpcode::Stop obj; + obj.return_data_offset = serde::Deserializable::deserialize(deserializer); + obj.return_data_size = serde::Deserializable::deserialize(deserializer); return obj; } @@ -4490,6 +5301,48 @@ Circuit::MemOp serde::Deserializable::deserialize(Deserializer & return obj; } +namespace Circuit { + + inline bool operator==(const MemoryAddress &lhs, const MemoryAddress &rhs) { + if (!(lhs.value == rhs.value)) { return false; } + return true; + } + + inline std::vector MemoryAddress::bincodeSerialize() const { + auto serializer = serde::BincodeSerializer(); + serde::Serializable::serialize(*this, serializer); + return std::move(serializer).bytes(); + } + + inline MemoryAddress MemoryAddress::bincodeDeserialize(std::vector input) { + auto deserializer = serde::BincodeDeserializer(input); + auto value = serde::Deserializable::deserialize(deserializer); + if (deserializer.get_buffer_offset() < input.size()) { + throw serde::deserialization_error("Some input bytes were not read"); + } + return value; + } + +} // end of namespace Circuit + +template <> +template +void serde::Serializable::serialize(const Circuit::MemoryAddress &obj, Serializer &serializer) { + serializer.increase_container_depth(); + serde::Serializable::serialize(obj.value, serializer); + serializer.decrease_container_depth(); +} + +template <> +template +Circuit::MemoryAddress serde::Deserializable::deserialize(Deserializer &deserializer) { + deserializer.increase_container_depth(); + Circuit::MemoryAddress obj; + obj.value = serde::Deserializable::deserialize(deserializer); + deserializer.decrease_container_depth(); + return obj; +} + namespace Circuit { inline bool operator==(const Opcode &lhs, const Opcode &rhs) { @@ -4934,20 +5787,20 @@ Circuit::PublicInputs serde::Deserializable::deserialize( namespace Circuit { - inline bool operator==(const RegisterIndex &lhs, const RegisterIndex &rhs) { - if (!(lhs.value == rhs.value)) { return false; } + inline bool operator==(const Value &lhs, const Value &rhs) { + if (!(lhs.inner == rhs.inner)) { return false; } return true; } - inline std::vector RegisterIndex::bincodeSerialize() const { + inline std::vector Value::bincodeSerialize() const { auto serializer = serde::BincodeSerializer(); - serde::Serializable::serialize(*this, serializer); + serde::Serializable::serialize(*this, serializer); return std::move(serializer).bytes(); } - inline RegisterIndex RegisterIndex::bincodeDeserialize(std::vector input) { + inline Value Value::bincodeDeserialize(std::vector input) { auto deserializer = serde::BincodeDeserializer(input); - auto value = serde::Deserializable::deserialize(deserializer); + auto value = serde::Deserializable::deserialize(deserializer); if (deserializer.get_buffer_offset() < input.size()) { throw serde::deserialization_error("Some input bytes were not read"); } @@ -4958,38 +5811,38 @@ namespace Circuit { template <> template -void serde::Serializable::serialize(const Circuit::RegisterIndex &obj, Serializer &serializer) { +void serde::Serializable::serialize(const Circuit::Value &obj, Serializer &serializer) { serializer.increase_container_depth(); - serde::Serializable::serialize(obj.value, serializer); + serde::Serializable::serialize(obj.inner, serializer); serializer.decrease_container_depth(); } template <> template -Circuit::RegisterIndex serde::Deserializable::deserialize(Deserializer &deserializer) { +Circuit::Value serde::Deserializable::deserialize(Deserializer &deserializer) { deserializer.increase_container_depth(); - Circuit::RegisterIndex obj; - obj.value = serde::Deserializable::deserialize(deserializer); + Circuit::Value obj; + obj.inner = serde::Deserializable::deserialize(deserializer); deserializer.decrease_container_depth(); return obj; } namespace Circuit { - inline bool operator==(const RegisterOrMemory &lhs, const RegisterOrMemory &rhs) { + inline bool operator==(const ValueOrArray &lhs, const ValueOrArray &rhs) { if (!(lhs.value == rhs.value)) { return false; } return true; } - inline std::vector RegisterOrMemory::bincodeSerialize() const { + inline std::vector ValueOrArray::bincodeSerialize() const { auto serializer = serde::BincodeSerializer(); - serde::Serializable::serialize(*this, serializer); + serde::Serializable::serialize(*this, serializer); return std::move(serializer).bytes(); } - inline RegisterOrMemory RegisterOrMemory::bincodeDeserialize(std::vector input) { + inline ValueOrArray ValueOrArray::bincodeDeserialize(std::vector input) { auto deserializer = serde::BincodeDeserializer(input); - auto value = serde::Deserializable::deserialize(deserializer); + auto value = serde::Deserializable::deserialize(deserializer); if (deserializer.get_buffer_offset() < input.size()) { throw serde::deserialization_error("Some input bytes were not read"); } @@ -5000,7 +5853,7 @@ namespace Circuit { template <> template -void serde::Serializable::serialize(const Circuit::RegisterOrMemory &obj, Serializer &serializer) { +void serde::Serializable::serialize(const Circuit::ValueOrArray &obj, Serializer &serializer) { serializer.increase_container_depth(); serde::Serializable::serialize(obj.value, serializer); serializer.decrease_container_depth(); @@ -5008,9 +5861,9 @@ void serde::Serializable::serialize(const Circuit::Re template <> template -Circuit::RegisterOrMemory serde::Deserializable::deserialize(Deserializer &deserializer) { +Circuit::ValueOrArray serde::Deserializable::deserialize(Deserializer &deserializer) { deserializer.increase_container_depth(); - Circuit::RegisterOrMemory obj; + Circuit::ValueOrArray obj; obj.value = serde::Deserializable::deserialize(deserializer); deserializer.decrease_container_depth(); return obj; @@ -5018,20 +5871,20 @@ Circuit::RegisterOrMemory serde::Deserializable::dese namespace Circuit { - inline bool operator==(const RegisterOrMemory::RegisterIndex &lhs, const RegisterOrMemory::RegisterIndex &rhs) { + inline bool operator==(const ValueOrArray::MemoryAddress &lhs, const ValueOrArray::MemoryAddress &rhs) { if (!(lhs.value == rhs.value)) { return false; } return true; } - inline std::vector RegisterOrMemory::RegisterIndex::bincodeSerialize() const { + inline std::vector ValueOrArray::MemoryAddress::bincodeSerialize() const { auto serializer = serde::BincodeSerializer(); - serde::Serializable::serialize(*this, serializer); + serde::Serializable::serialize(*this, serializer); return std::move(serializer).bytes(); } - inline RegisterOrMemory::RegisterIndex RegisterOrMemory::RegisterIndex::bincodeDeserialize(std::vector input) { + inline ValueOrArray::MemoryAddress ValueOrArray::MemoryAddress::bincodeDeserialize(std::vector input) { auto deserializer = serde::BincodeDeserializer(input); - auto value = serde::Deserializable::deserialize(deserializer); + auto value = serde::Deserializable::deserialize(deserializer); if (deserializer.get_buffer_offset() < input.size()) { throw serde::deserialization_error("Some input bytes were not read"); } @@ -5042,34 +5895,34 @@ namespace Circuit { template <> template -void serde::Serializable::serialize(const Circuit::RegisterOrMemory::RegisterIndex &obj, Serializer &serializer) { +void serde::Serializable::serialize(const Circuit::ValueOrArray::MemoryAddress &obj, Serializer &serializer) { serde::Serializable::serialize(obj.value, serializer); } template <> template -Circuit::RegisterOrMemory::RegisterIndex serde::Deserializable::deserialize(Deserializer &deserializer) { - Circuit::RegisterOrMemory::RegisterIndex obj; +Circuit::ValueOrArray::MemoryAddress serde::Deserializable::deserialize(Deserializer &deserializer) { + Circuit::ValueOrArray::MemoryAddress obj; obj.value = serde::Deserializable::deserialize(deserializer); return obj; } namespace Circuit { - inline bool operator==(const RegisterOrMemory::HeapArray &lhs, const RegisterOrMemory::HeapArray &rhs) { + inline bool operator==(const ValueOrArray::HeapArray &lhs, const ValueOrArray::HeapArray &rhs) { if (!(lhs.value == rhs.value)) { return false; } return true; } - inline std::vector RegisterOrMemory::HeapArray::bincodeSerialize() const { + inline std::vector ValueOrArray::HeapArray::bincodeSerialize() const { auto serializer = serde::BincodeSerializer(); - serde::Serializable::serialize(*this, serializer); + serde::Serializable::serialize(*this, serializer); return std::move(serializer).bytes(); } - inline RegisterOrMemory::HeapArray RegisterOrMemory::HeapArray::bincodeDeserialize(std::vector input) { + inline ValueOrArray::HeapArray ValueOrArray::HeapArray::bincodeDeserialize(std::vector input) { auto deserializer = serde::BincodeDeserializer(input); - auto value = serde::Deserializable::deserialize(deserializer); + auto value = serde::Deserializable::deserialize(deserializer); if (deserializer.get_buffer_offset() < input.size()) { throw serde::deserialization_error("Some input bytes were not read"); } @@ -5080,34 +5933,34 @@ namespace Circuit { template <> template -void serde::Serializable::serialize(const Circuit::RegisterOrMemory::HeapArray &obj, Serializer &serializer) { +void serde::Serializable::serialize(const Circuit::ValueOrArray::HeapArray &obj, Serializer &serializer) { serde::Serializable::serialize(obj.value, serializer); } template <> template -Circuit::RegisterOrMemory::HeapArray serde::Deserializable::deserialize(Deserializer &deserializer) { - Circuit::RegisterOrMemory::HeapArray obj; +Circuit::ValueOrArray::HeapArray serde::Deserializable::deserialize(Deserializer &deserializer) { + Circuit::ValueOrArray::HeapArray obj; obj.value = serde::Deserializable::deserialize(deserializer); return obj; } namespace Circuit { - inline bool operator==(const RegisterOrMemory::HeapVector &lhs, const RegisterOrMemory::HeapVector &rhs) { + inline bool operator==(const ValueOrArray::HeapVector &lhs, const ValueOrArray::HeapVector &rhs) { if (!(lhs.value == rhs.value)) { return false; } return true; } - inline std::vector RegisterOrMemory::HeapVector::bincodeSerialize() const { + inline std::vector ValueOrArray::HeapVector::bincodeSerialize() const { auto serializer = serde::BincodeSerializer(); - serde::Serializable::serialize(*this, serializer); + serde::Serializable::serialize(*this, serializer); return std::move(serializer).bytes(); } - inline RegisterOrMemory::HeapVector RegisterOrMemory::HeapVector::bincodeDeserialize(std::vector input) { + inline ValueOrArray::HeapVector ValueOrArray::HeapVector::bincodeDeserialize(std::vector input) { auto deserializer = serde::BincodeDeserializer(input); - auto value = serde::Deserializable::deserialize(deserializer); + auto value = serde::Deserializable::deserialize(deserializer); if (deserializer.get_buffer_offset() < input.size()) { throw serde::deserialization_error("Some input bytes were not read"); } @@ -5118,60 +5971,18 @@ namespace Circuit { template <> template -void serde::Serializable::serialize(const Circuit::RegisterOrMemory::HeapVector &obj, Serializer &serializer) { +void serde::Serializable::serialize(const Circuit::ValueOrArray::HeapVector &obj, Serializer &serializer) { serde::Serializable::serialize(obj.value, serializer); } template <> template -Circuit::RegisterOrMemory::HeapVector serde::Deserializable::deserialize(Deserializer &deserializer) { - Circuit::RegisterOrMemory::HeapVector obj; +Circuit::ValueOrArray::HeapVector serde::Deserializable::deserialize(Deserializer &deserializer) { + Circuit::ValueOrArray::HeapVector obj; obj.value = serde::Deserializable::deserialize(deserializer); return obj; } -namespace Circuit { - - inline bool operator==(const Value &lhs, const Value &rhs) { - if (!(lhs.inner == rhs.inner)) { return false; } - return true; - } - - inline std::vector Value::bincodeSerialize() const { - auto serializer = serde::BincodeSerializer(); - serde::Serializable::serialize(*this, serializer); - return std::move(serializer).bytes(); - } - - inline Value Value::bincodeDeserialize(std::vector input) { - auto deserializer = serde::BincodeDeserializer(input); - auto value = serde::Deserializable::deserialize(deserializer); - if (deserializer.get_buffer_offset() < input.size()) { - throw serde::deserialization_error("Some input bytes were not read"); - } - return value; - } - -} // end of namespace Circuit - -template <> -template -void serde::Serializable::serialize(const Circuit::Value &obj, Serializer &serializer) { - serializer.increase_container_depth(); - serde::Serializable::serialize(obj.inner, serializer); - serializer.decrease_container_depth(); -} - -template <> -template -Circuit::Value serde::Deserializable::deserialize(Deserializer &deserializer) { - deserializer.increase_container_depth(); - Circuit::Value obj; - obj.inner = serde::Deserializable::deserialize(deserializer); - deserializer.decrease_container_depth(); - return obj; -} - namespace Circuit { inline bool operator==(const Witness &lhs, const Witness &rhs) { diff --git a/noir/acvm-repo/acir/src/circuit/black_box_functions.rs b/noir/acvm-repo/acir/src/circuit/black_box_functions.rs index d1f5560313b..97b4759d350 100644 --- a/noir/acvm-repo/acir/src/circuit/black_box_functions.rs +++ b/noir/acvm-repo/acir/src/circuit/black_box_functions.rs @@ -47,8 +47,22 @@ pub enum BlackBoxFunc { RecursiveAggregation, /// Addition over the embedded curve on which [`FieldElement`][acir_field::FieldElement] is defined. EmbeddedCurveAdd, - /// Point doubling over the embedded curve on which [`FieldElement`][acir_field::FieldElement] is defined. - EmbeddedCurveDouble, + /// BigInt addition + BigIntAdd, + /// BigInt subtraction + BigIntNeg, + /// BigInt multiplication + BigIntMul, + /// BigInt division + BigIntDiv, + /// BigInt from le bytes + BigIntFromLeBytes, + /// BigInt to le bytes + BigIntToLeBytes, + /// Permutation function of Poseidon2 + Poseidon2Permutation, + /// SHA256 compression function + Sha256Compression, } impl std::fmt::Display for BlackBoxFunc { @@ -68,8 +82,7 @@ impl BlackBoxFunc { BlackBoxFunc::PedersenHash => "pedersen_hash", BlackBoxFunc::EcdsaSecp256k1 => "ecdsa_secp256k1", BlackBoxFunc::FixedBaseScalarMul => "fixed_base_scalar_mul", - BlackBoxFunc::EmbeddedCurveAdd => "ec_add", - BlackBoxFunc::EmbeddedCurveDouble => "ec_double", + BlackBoxFunc::EmbeddedCurveAdd => "embedded_curve_add", BlackBoxFunc::AND => "and", BlackBoxFunc::XOR => "xor", BlackBoxFunc::RANGE => "range", @@ -77,8 +90,17 @@ impl BlackBoxFunc { BlackBoxFunc::Keccakf1600 => "keccakf1600", BlackBoxFunc::RecursiveAggregation => "recursive_aggregation", BlackBoxFunc::EcdsaSecp256r1 => "ecdsa_secp256r1", + BlackBoxFunc::BigIntAdd => "bigint_add", + BlackBoxFunc::BigIntNeg => "bigint_neg", + BlackBoxFunc::BigIntMul => "bigint_mul", + BlackBoxFunc::BigIntDiv => "bigint_div", + BlackBoxFunc::BigIntFromLeBytes => "bigint_from_le_bytes", + BlackBoxFunc::BigIntToLeBytes => "bigint_to_le_bytes", + BlackBoxFunc::Poseidon2Permutation => "poseidon2_permutation", + BlackBoxFunc::Sha256Compression => "sha256_compression", } } + pub fn lookup(op_name: &str) -> Option { match op_name { "sha256" => Some(BlackBoxFunc::SHA256), @@ -90,17 +112,25 @@ impl BlackBoxFunc { "ecdsa_secp256k1" => Some(BlackBoxFunc::EcdsaSecp256k1), "ecdsa_secp256r1" => Some(BlackBoxFunc::EcdsaSecp256r1), "fixed_base_scalar_mul" => Some(BlackBoxFunc::FixedBaseScalarMul), - "ec_add" => Some(BlackBoxFunc::EmbeddedCurveAdd), - "ec_double" => Some(BlackBoxFunc::EmbeddedCurveDouble), + "embedded_curve_add" => Some(BlackBoxFunc::EmbeddedCurveAdd), "and" => Some(BlackBoxFunc::AND), "xor" => Some(BlackBoxFunc::XOR), "range" => Some(BlackBoxFunc::RANGE), "keccak256" => Some(BlackBoxFunc::Keccak256), "keccakf1600" => Some(BlackBoxFunc::Keccakf1600), "recursive_aggregation" => Some(BlackBoxFunc::RecursiveAggregation), + "bigint_add" => Some(BlackBoxFunc::BigIntAdd), + "bigint_neg" => Some(BlackBoxFunc::BigIntNeg), + "bigint_mul" => Some(BlackBoxFunc::BigIntMul), + "bigint_div" => Some(BlackBoxFunc::BigIntDiv), + "bigint_from_le_bytes" => Some(BlackBoxFunc::BigIntFromLeBytes), + "bigint_to_le_bytes" => Some(BlackBoxFunc::BigIntToLeBytes), + "poseidon2_permutation" => Some(BlackBoxFunc::Poseidon2Permutation), + "sha256_compression" => Some(BlackBoxFunc::Sha256Compression), _ => None, } } + pub fn is_valid_black_box_func_name(op_name: &str) -> bool { BlackBoxFunc::lookup(op_name).is_some() } diff --git a/noir/acvm-repo/acir/src/circuit/opcodes/black_box_function_call.rs b/noir/acvm-repo/acir/src/circuit/opcodes/black_box_function_call.rs index 7ee4e2498a5..ba4964c8912 100644 --- a/noir/acvm-repo/acir/src/circuit/opcodes/black_box_function_call.rs +++ b/noir/acvm-repo/acir/src/circuit/opcodes/black_box_function_call.rs @@ -86,11 +86,6 @@ pub enum BlackBoxFuncCall { input2_y: FunctionInput, outputs: (Witness, Witness), }, - EmbeddedCurveDouble { - input_x: FunctionInput, - input_y: FunctionInput, - outputs: (Witness, Witness), - }, Keccak256 { inputs: Vec, outputs: Vec, @@ -120,6 +115,61 @@ pub enum BlackBoxFuncCall { /// key provided to the circuit matches the key produced by the circuit creator key_hash: FunctionInput, }, + BigIntAdd { + lhs: u32, + rhs: u32, + output: u32, + }, + BigIntNeg { + lhs: u32, + rhs: u32, + output: u32, + }, + BigIntMul { + lhs: u32, + rhs: u32, + output: u32, + }, + BigIntDiv { + lhs: u32, + rhs: u32, + output: u32, + }, + BigIntFromLeBytes { + inputs: Vec, + modulus: Vec, + output: u32, + }, + BigIntToLeBytes { + input: u32, + outputs: Vec, + }, + /// Applies the Poseidon2 permutation function to the given state, + /// outputting the permuted state. + Poseidon2Permutation { + /// Input state for the permutation of Poseidon2 + inputs: Vec, + /// Permuted state + outputs: Vec, + /// State length (in number of field elements) + /// It is the length of inputs and outputs vectors + len: u32, + }, + /// Applies the SHA-256 compression function to the input message + /// + /// # Arguments + /// + /// * `inputs` - input message block + /// * `hash_values` - state from the previous compression + /// * `outputs` - result of the input compressed into 256 bits + Sha256Compression { + /// 512 bits of the input message, represented by 16 u32s + inputs: Vec, + /// Vector of 8 u32s used to compress the input + hash_values: Vec, + /// Output of the compression, represented by 8 u32s + outputs: Vec, + }, } impl BlackBoxFuncCall { @@ -138,11 +188,18 @@ impl BlackBoxFuncCall { BlackBoxFuncCall::EcdsaSecp256r1 { .. } => BlackBoxFunc::EcdsaSecp256r1, BlackBoxFuncCall::FixedBaseScalarMul { .. } => BlackBoxFunc::FixedBaseScalarMul, BlackBoxFuncCall::EmbeddedCurveAdd { .. } => BlackBoxFunc::EmbeddedCurveAdd, - BlackBoxFuncCall::EmbeddedCurveDouble { .. } => BlackBoxFunc::EmbeddedCurveDouble, BlackBoxFuncCall::Keccak256 { .. } => BlackBoxFunc::Keccak256, BlackBoxFuncCall::Keccak256VariableLength { .. } => BlackBoxFunc::Keccak256, BlackBoxFuncCall::Keccakf1600 { .. } => BlackBoxFunc::Keccakf1600, BlackBoxFuncCall::RecursiveAggregation { .. } => BlackBoxFunc::RecursiveAggregation, + BlackBoxFuncCall::BigIntAdd { .. } => BlackBoxFunc::BigIntAdd, + BlackBoxFuncCall::BigIntNeg { .. } => BlackBoxFunc::BigIntNeg, + BlackBoxFuncCall::BigIntMul { .. } => BlackBoxFunc::BigIntMul, + BlackBoxFuncCall::BigIntDiv { .. } => BlackBoxFunc::BigIntDiv, + BlackBoxFuncCall::BigIntFromLeBytes { .. } => BlackBoxFunc::BigIntFromLeBytes, + BlackBoxFuncCall::BigIntToLeBytes { .. } => BlackBoxFunc::BigIntToLeBytes, + BlackBoxFuncCall::Poseidon2Permutation { .. } => BlackBoxFunc::Poseidon2Permutation, + BlackBoxFuncCall::Sha256Compression { .. } => BlackBoxFunc::Sha256Compression, } } @@ -158,17 +215,22 @@ impl BlackBoxFuncCall { | BlackBoxFuncCall::Keccak256 { inputs, .. } | BlackBoxFuncCall::Keccakf1600 { inputs, .. } | BlackBoxFuncCall::PedersenCommitment { inputs, .. } - | BlackBoxFuncCall::PedersenHash { inputs, .. } => inputs.to_vec(), + | BlackBoxFuncCall::PedersenHash { inputs, .. } + | BlackBoxFuncCall::BigIntFromLeBytes { inputs, .. } + | BlackBoxFuncCall::Poseidon2Permutation { inputs, .. } + | BlackBoxFuncCall::Sha256Compression { inputs, .. } => inputs.to_vec(), BlackBoxFuncCall::AND { lhs, rhs, .. } | BlackBoxFuncCall::XOR { lhs, rhs, .. } => { vec![*lhs, *rhs] } + BlackBoxFuncCall::BigIntAdd { .. } + | BlackBoxFuncCall::BigIntNeg { .. } + | BlackBoxFuncCall::BigIntMul { .. } + | BlackBoxFuncCall::BigIntDiv { .. } + | BlackBoxFuncCall::BigIntToLeBytes { .. } => Vec::new(), BlackBoxFuncCall::FixedBaseScalarMul { low, high, .. } => vec![*low, *high], BlackBoxFuncCall::EmbeddedCurveAdd { input1_x, input1_y, input2_x, input2_y, .. } => vec![*input1_x, *input1_y, *input2_x, *input2_y], - BlackBoxFuncCall::EmbeddedCurveDouble { input_x, input_y, .. } => { - vec![*input_x, *input_y] - } BlackBoxFuncCall::RANGE { input } => vec![*input], BlackBoxFuncCall::SchnorrVerify { public_key_x, @@ -249,7 +311,10 @@ impl BlackBoxFuncCall { | BlackBoxFuncCall::Blake2s { outputs, .. } | BlackBoxFuncCall::Blake3 { outputs, .. } | BlackBoxFuncCall::Keccak256 { outputs, .. } - | BlackBoxFuncCall::Keccakf1600 { outputs, .. } => outputs.to_vec(), + | BlackBoxFuncCall::Keccakf1600 { outputs, .. } + | BlackBoxFuncCall::Keccak256VariableLength { outputs, .. } + | BlackBoxFuncCall::Poseidon2Permutation { outputs, .. } + | BlackBoxFuncCall::Sha256Compression { outputs, .. } => outputs.to_vec(), BlackBoxFuncCall::AND { output, .. } | BlackBoxFuncCall::XOR { output, .. } | BlackBoxFuncCall::SchnorrVerify { output, .. } @@ -258,12 +323,17 @@ impl BlackBoxFuncCall { | BlackBoxFuncCall::EcdsaSecp256r1 { output, .. } => vec![*output], BlackBoxFuncCall::FixedBaseScalarMul { outputs, .. } | BlackBoxFuncCall::PedersenCommitment { outputs, .. } - | BlackBoxFuncCall::EmbeddedCurveAdd { outputs, .. } - | BlackBoxFuncCall::EmbeddedCurveDouble { outputs, .. } => vec![outputs.0, outputs.1], - BlackBoxFuncCall::RANGE { .. } | BlackBoxFuncCall::RecursiveAggregation { .. } => { + | BlackBoxFuncCall::EmbeddedCurveAdd { outputs, .. } => vec![outputs.0, outputs.1], + BlackBoxFuncCall::RANGE { .. } + | BlackBoxFuncCall::RecursiveAggregation { .. } + | BlackBoxFuncCall::BigIntFromLeBytes { .. } + | BlackBoxFuncCall::BigIntAdd { .. } + | BlackBoxFuncCall::BigIntNeg { .. } + | BlackBoxFuncCall::BigIntMul { .. } + | BlackBoxFuncCall::BigIntDiv { .. } => { vec![] } - BlackBoxFuncCall::Keccak256VariableLength { outputs, .. } => outputs.to_vec(), + BlackBoxFuncCall::BigIntToLeBytes { outputs, .. } => outputs.to_vec(), } } } diff --git a/noir/acvm-repo/acir/src/lib.rs b/noir/acvm-repo/acir/src/lib.rs index b7bcaa0c5c0..0a72cec6070 100644 --- a/noir/acvm-repo/acir/src/lib.rs +++ b/noir/acvm-repo/acir/src/lib.rs @@ -31,9 +31,7 @@ mod reflection { path::{Path, PathBuf}, }; - use brillig::{ - BinaryFieldOp, BinaryIntOp, BlackBoxOp, Opcode as BrilligOpcode, RegisterOrMemory, - }; + use brillig::{BinaryFieldOp, BinaryIntOp, BlackBoxOp, Opcode as BrilligOpcode, ValueOrArray}; use serde_reflection::{Tracer, TracerConfig}; use crate::{ @@ -69,7 +67,7 @@ mod reflection { tracer.trace_simple_type::().unwrap(); tracer.trace_simple_type::().unwrap(); tracer.trace_simple_type::().unwrap(); - tracer.trace_simple_type::().unwrap(); + tracer.trace_simple_type::().unwrap(); let registry = tracer.registry().unwrap(); diff --git a/noir/acvm-repo/acir/tests/test_program_serialization.rs b/noir/acvm-repo/acir/tests/test_program_serialization.rs index 7d3b7b32d35..c94c5ae66b0 100644 --- a/noir/acvm-repo/acir/tests/test_program_serialization.rs +++ b/noir/acvm-repo/acir/tests/test_program_serialization.rs @@ -20,7 +20,7 @@ use acir::{ native_types::{Expression, Witness}, }; use acir_field::FieldElement; -use brillig::{HeapArray, RegisterIndex, RegisterOrMemory}; +use brillig::{HeapArray, MemoryAddress, ValueOrArray}; #[test] fn addition_circuit() { @@ -173,15 +173,23 @@ fn simple_brillig_foreign_call() { inputs: vec![ BrilligInputs::Single(w_input.into()), // Input Register 0, ], - // This tells the BrilligSolver which witnesses its output registers correspond to + // This tells the BrilligSolver which witnesses its output values correspond to outputs: vec![ BrilligOutputs::Simple(w_inverted), // Output Register 1 ], - bytecode: vec![brillig::Opcode::ForeignCall { - function: "invert".into(), - destinations: vec![RegisterOrMemory::RegisterIndex(RegisterIndex::from(0))], - inputs: vec![RegisterOrMemory::RegisterIndex(RegisterIndex::from(0))], - }], + bytecode: vec![ + brillig::Opcode::CalldataCopy { + destination_address: MemoryAddress(0), + size: 1, + offset: 0, + }, + brillig::Opcode::ForeignCall { + function: "invert".into(), + destinations: vec![ValueOrArray::MemoryAddress(MemoryAddress::from(0))], + inputs: vec![ValueOrArray::MemoryAddress(MemoryAddress::from(0))], + }, + brillig::Opcode::Stop { return_data_offset: 0, return_data_size: 1 }, + ], predicate: None, }; @@ -196,10 +204,11 @@ fn simple_brillig_foreign_call() { let bytes = Circuit::serialize_circuit(&circuit); let expected_serialization: Vec = vec![ - 31, 139, 8, 0, 0, 0, 0, 0, 0, 255, 173, 143, 49, 10, 64, 33, 12, 67, 99, 63, 124, 60, 142, - 222, 192, 203, 56, 184, 56, 136, 120, 126, 5, 21, 226, 160, 139, 62, 40, 13, 45, 132, 68, - 3, 80, 232, 124, 164, 153, 121, 115, 99, 155, 59, 172, 122, 231, 101, 56, 175, 80, 86, 221, - 230, 31, 58, 196, 226, 83, 62, 53, 91, 16, 122, 10, 246, 84, 99, 243, 0, 30, 59, 1, 0, 0, + 31, 139, 8, 0, 0, 0, 0, 0, 0, 255, 173, 143, 177, 10, 192, 32, 12, 68, 207, 148, 150, 118, + 234, 175, 216, 63, 232, 207, 116, 232, 210, 161, 136, 223, 175, 98, 132, 27, 212, 69, 31, + 132, 28, 23, 8, 119, 59, 0, 131, 204, 66, 154, 41, 222, 173, 219, 142, 113, 153, 121, 191, + 44, 231, 21, 237, 144, 88, 43, 249, 11, 71, 156, 77, 245, 251, 249, 231, 119, 189, 214, + 204, 89, 187, 11, 25, 130, 54, 1, 36, 1, 124, 242, 107, 1, 0, 0, ]; assert_eq!(bytes, expected_serialization) @@ -221,39 +230,54 @@ fn complex_brillig_foreign_call() { let brillig_data = Brillig { inputs: vec![ - // Input Register 0 + // Input 0,1,2 BrilligInputs::Array(vec![ Expression::from(a), Expression::from(b), Expression::from(c), ]), - // Input Register 1 + // Input 3 BrilligInputs::Single(Expression { mul_terms: vec![], linear_combinations: vec![(fe_1, a), (fe_1, b), (fe_1, c)], q_c: fe_0, }), ], - // This tells the BrilligSolver which witnesses its output registers correspond to + // This tells the BrilligSolver which witnesses its output values correspond to outputs: vec![ - BrilligOutputs::Array(vec![a_times_2, b_times_3, c_times_4]), // Output Register 0 - BrilligOutputs::Simple(a_plus_b_plus_c), // Output Register 1 - BrilligOutputs::Simple(a_plus_b_plus_c_times_2), // Output Register 2 + BrilligOutputs::Array(vec![a_times_2, b_times_3, c_times_4]), // Output 0,1,2 + BrilligOutputs::Simple(a_plus_b_plus_c), // Output 3 + BrilligOutputs::Simple(a_plus_b_plus_c_times_2), // Output 4 ], bytecode: vec![ + brillig::Opcode::CalldataCopy { + destination_address: MemoryAddress(32), + size: 3, + offset: 0, + }, + brillig::Opcode::Const { + destination: MemoryAddress(0), + value: brillig::Value::from(32_usize), + }, + brillig::Opcode::CalldataCopy { + destination_address: MemoryAddress(1), + size: 1, + offset: 3, + }, // Oracles are named 'foreign calls' in brillig brillig::Opcode::ForeignCall { function: "complex".into(), inputs: vec![ - RegisterOrMemory::HeapArray(HeapArray { pointer: 0.into(), size: 3 }), - RegisterOrMemory::RegisterIndex(RegisterIndex::from(1)), + ValueOrArray::HeapArray(HeapArray { pointer: 0.into(), size: 3 }), + ValueOrArray::MemoryAddress(MemoryAddress::from(1)), ], destinations: vec![ - RegisterOrMemory::HeapArray(HeapArray { pointer: 0.into(), size: 3 }), - RegisterOrMemory::RegisterIndex(RegisterIndex::from(1)), - RegisterOrMemory::RegisterIndex(RegisterIndex::from(2)), + ValueOrArray::HeapArray(HeapArray { pointer: 0.into(), size: 3 }), + ValueOrArray::MemoryAddress(MemoryAddress::from(35)), + ValueOrArray::MemoryAddress(MemoryAddress::from(36)), ], }, + brillig::Opcode::Stop { return_data_offset: 32, return_data_size: 5 }, ], predicate: None, }; @@ -269,13 +293,14 @@ fn complex_brillig_foreign_call() { let bytes = Circuit::serialize_circuit(&circuit); let expected_serialization: Vec = vec![ - 31, 139, 8, 0, 0, 0, 0, 0, 0, 255, 213, 83, 219, 10, 128, 48, 8, 117, 174, 139, 159, 179, - 254, 160, 127, 137, 222, 138, 122, 236, 243, 19, 114, 32, 22, 244, 144, 131, 118, 64, 156, - 178, 29, 14, 59, 74, 0, 16, 224, 66, 228, 64, 57, 7, 169, 53, 242, 189, 81, 114, 250, 134, - 33, 248, 113, 165, 82, 26, 177, 2, 141, 177, 128, 198, 60, 15, 63, 245, 219, 211, 23, 215, - 255, 139, 15, 251, 211, 112, 180, 28, 157, 212, 189, 100, 82, 179, 64, 170, 63, 109, 235, - 190, 204, 135, 166, 178, 150, 216, 62, 154, 252, 250, 70, 147, 35, 220, 119, 93, 227, 4, - 182, 131, 81, 25, 36, 4, 0, 0, + 31, 139, 8, 0, 0, 0, 0, 0, 0, 255, 213, 83, 65, 10, 132, 48, 12, 76, 218, 237, 174, 123, + 242, 11, 130, 62, 160, 250, 2, 255, 34, 222, 20, 61, 250, 124, 11, 78, 49, 4, 193, 131, 21, + 52, 16, 210, 132, 105, 50, 77, 210, 140, 136, 152, 54, 177, 65, 13, 206, 12, 95, 74, 196, + 181, 176, 254, 154, 212, 156, 46, 151, 191, 139, 163, 121, 1, 71, 123, 3, 199, 184, 15, 15, + 157, 119, 202, 185, 36, 237, 159, 61, 248, 63, 159, 160, 46, 232, 23, 254, 15, 54, 67, 156, + 96, 11, 213, 119, 82, 248, 116, 179, 104, 188, 163, 125, 15, 89, 213, 253, 139, 154, 221, + 52, 206, 67, 191, 88, 5, 213, 52, 75, 113, 174, 96, 205, 201, 157, 24, 207, 197, 211, 157, + 6, 50, 18, 233, 158, 72, 89, 1, 53, 215, 75, 175, 196, 4, 0, 0, ]; assert_eq!(bytes, expected_serialization) diff --git a/noir/acvm-repo/acvm/src/compiler/optimizers/redundant_range.rs b/noir/acvm-repo/acvm/src/compiler/optimizers/redundant_range.rs index 5d19f9629ba..ecabd98b3b1 100644 --- a/noir/acvm-repo/acvm/src/compiler/optimizers/redundant_range.rs +++ b/noir/acvm-repo/acvm/src/compiler/optimizers/redundant_range.rs @@ -3,8 +3,7 @@ use acir::{ opcodes::{BlackBoxFuncCall, FunctionInput}, Circuit, Opcode, }, - native_types::{Expression, Witness}, - FieldElement, + native_types::Witness, }; use std::collections::{BTreeMap, HashSet}; @@ -105,9 +104,11 @@ impl RangeOptimizer { let mut new_order_list = Vec::with_capacity(order_list.len()); let mut optimized_opcodes = Vec::with_capacity(self.circuit.opcodes.len()); for (idx, opcode) in self.circuit.opcodes.into_iter().enumerate() { - let (witness, num_bits) = match extract_range_opcode(&opcode) { - Some(range_opcode) => range_opcode, - None => { + let (witness, num_bits) = match &opcode { + Opcode::BlackBoxFuncCall(BlackBoxFuncCall::RANGE { input }) => { + (input.witness, input.num_bits) + } + _ => { // If its not the range opcode, add it to the opcode // list and continue; optimized_opcodes.push(opcode); @@ -131,7 +132,7 @@ impl RangeOptimizer { if is_lowest_bit_size { already_seen_witness.insert(witness); new_order_list.push(order_list[idx]); - optimized_opcodes.push(optimized_range_opcode(witness, num_bits)); + optimized_opcodes.push(opcode); } } @@ -139,36 +140,11 @@ impl RangeOptimizer { } } -/// Extract the range opcode from the `Opcode` enum -/// Returns None, if `Opcode` is not the range opcode. -fn extract_range_opcode(opcode: &Opcode) -> Option<(Witness, u32)> { - match opcode { - Opcode::BlackBoxFuncCall(BlackBoxFuncCall::RANGE { input }) => { - Some((input.witness, input.num_bits)) - } - _ => None, - } -} - -fn optimized_range_opcode(witness: Witness, num_bits: u32) -> Opcode { - if num_bits == 1 { - Opcode::AssertZero(Expression { - mul_terms: vec![(FieldElement::one(), witness, witness)], - linear_combinations: vec![(-FieldElement::one(), witness)], - q_c: FieldElement::zero(), - }) - } else { - Opcode::BlackBoxFuncCall(BlackBoxFuncCall::RANGE { - input: FunctionInput { witness, num_bits }, - }) - } -} - #[cfg(test)] mod tests { use std::collections::BTreeSet; - use crate::compiler::optimizers::redundant_range::{extract_range_opcode, RangeOptimizer}; + use crate::compiler::optimizers::redundant_range::RangeOptimizer; use acir::{ circuit::{ opcodes::{BlackBoxFuncCall, FunctionInput}, @@ -218,11 +194,12 @@ mod tests { let (optimized_circuit, _) = optimizer.replace_redundant_ranges(acir_opcode_positions); assert_eq!(optimized_circuit.opcodes.len(), 1); - let (witness, num_bits) = - extract_range_opcode(&optimized_circuit.opcodes[0]).expect("expected one range opcode"); - - assert_eq!(witness, Witness(1)); - assert_eq!(num_bits, 16); + assert_eq!( + optimized_circuit.opcodes[0], + Opcode::BlackBoxFuncCall(BlackBoxFuncCall::RANGE { + input: FunctionInput { witness: Witness(1), num_bits: 16 } + }) + ); } #[test] @@ -240,15 +217,18 @@ mod tests { let (optimized_circuit, _) = optimizer.replace_redundant_ranges(acir_opcode_positions); assert_eq!(optimized_circuit.opcodes.len(), 2); - let (witness_a, num_bits_a) = - extract_range_opcode(&optimized_circuit.opcodes[0]).expect("expected two range opcode"); - let (witness_b, num_bits_b) = - extract_range_opcode(&optimized_circuit.opcodes[1]).expect("expected two range opcode"); - - assert_eq!(witness_a, Witness(1)); - assert_eq!(witness_b, Witness(2)); - assert_eq!(num_bits_a, 16); - assert_eq!(num_bits_b, 23); + assert_eq!( + optimized_circuit.opcodes[0], + Opcode::BlackBoxFuncCall(BlackBoxFuncCall::RANGE { + input: FunctionInput { witness: Witness(1), num_bits: 16 } + }) + ); + assert_eq!( + optimized_circuit.opcodes[1], + Opcode::BlackBoxFuncCall(BlackBoxFuncCall::RANGE { + input: FunctionInput { witness: Witness(2), num_bits: 23 } + }) + ); } #[test] diff --git a/noir/acvm-repo/acvm/src/compiler/transformers/mod.rs b/noir/acvm-repo/acvm/src/compiler/transformers/mod.rs index 306ea1b7c12..e184401c5d4 100644 --- a/noir/acvm-repo/acvm/src/compiler/transformers/mod.rs +++ b/noir/acvm-repo/acvm/src/compiler/transformers/mod.rs @@ -99,50 +99,8 @@ pub(super) fn transform_internal( } } Opcode::BlackBoxFuncCall(ref func) => { - match func { - acir::circuit::opcodes::BlackBoxFuncCall::AND { output, .. } - | acir::circuit::opcodes::BlackBoxFuncCall::XOR { output, .. } => { - transformer.mark_solvable(*output); - } - acir::circuit::opcodes::BlackBoxFuncCall::RANGE { .. } - | acir::circuit::opcodes::BlackBoxFuncCall::RecursiveAggregation { .. } => (), - acir::circuit::opcodes::BlackBoxFuncCall::SHA256 { outputs, .. } - | acir::circuit::opcodes::BlackBoxFuncCall::Keccak256 { outputs, .. } - | acir::circuit::opcodes::BlackBoxFuncCall::Keccak256VariableLength { - outputs, - .. - } - | acir::circuit::opcodes::BlackBoxFuncCall::Keccakf1600 { outputs, .. } - | acir::circuit::opcodes::BlackBoxFuncCall::Blake2s { outputs, .. } - | acir::circuit::opcodes::BlackBoxFuncCall::Blake3 { outputs, .. } => { - for witness in outputs { - transformer.mark_solvable(*witness); - } - } - acir::circuit::opcodes::BlackBoxFuncCall::FixedBaseScalarMul { - outputs, - .. - } - | acir::circuit::opcodes::BlackBoxFuncCall::EmbeddedCurveAdd { - outputs, .. - } - | acir::circuit::opcodes::BlackBoxFuncCall::EmbeddedCurveDouble { - outputs, - .. - } - | acir::circuit::opcodes::BlackBoxFuncCall::PedersenCommitment { - outputs, - .. - } => { - transformer.mark_solvable(outputs.0); - transformer.mark_solvable(outputs.1); - } - acir::circuit::opcodes::BlackBoxFuncCall::EcdsaSecp256k1 { output, .. } - | acir::circuit::opcodes::BlackBoxFuncCall::EcdsaSecp256r1 { output, .. } - | acir::circuit::opcodes::BlackBoxFuncCall::SchnorrVerify { output, .. } - | acir::circuit::opcodes::BlackBoxFuncCall::PedersenHash { output, .. } => { - transformer.mark_solvable(*output); - } + for witness in func.get_outputs_vec() { + transformer.mark_solvable(witness); } new_acir_opcode_positions.push(acir_opcode_positions[index]); diff --git a/noir/acvm-repo/acvm/src/lib.rs b/noir/acvm-repo/acvm/src/lib.rs index 626bb2c9b91..264479d8a12 100644 --- a/noir/acvm-repo/acvm/src/lib.rs +++ b/noir/acvm-repo/acvm/src/lib.rs @@ -31,3 +31,13 @@ pub enum ExpressionWidth { Unbounded, Bounded { width: usize }, } + +impl From for ExpressionWidth { + fn from(width: usize) -> ExpressionWidth { + if width == 0 { + ExpressionWidth::Unbounded + } else { + ExpressionWidth::Bounded { width } + } + } +} diff --git a/noir/acvm-repo/acvm/src/pwg/blackbox/fixed_base_scalar_mul.rs b/noir/acvm-repo/acvm/src/pwg/blackbox/fixed_base_scalar_mul.rs index 582ed56584b..c5bfd1d5646 100644 --- a/noir/acvm-repo/acvm/src/pwg/blackbox/fixed_base_scalar_mul.rs +++ b/noir/acvm-repo/acvm/src/pwg/blackbox/fixed_base_scalar_mul.rs @@ -23,3 +23,24 @@ pub(super) fn fixed_base_scalar_mul( Ok(()) } + +pub(super) fn embedded_curve_add( + backend: &impl BlackBoxFunctionSolver, + initial_witness: &mut WitnessMap, + input1_x: FunctionInput, + input1_y: FunctionInput, + input2_x: FunctionInput, + input2_y: FunctionInput, + outputs: (Witness, Witness), +) -> Result<(), OpcodeResolutionError> { + let input1_x = witness_to_value(initial_witness, input1_x.witness)?; + let input1_y = witness_to_value(initial_witness, input1_y.witness)?; + let input2_x = witness_to_value(initial_witness, input2_x.witness)?; + let input2_y = witness_to_value(initial_witness, input2_y.witness)?; + let (res_x, res_y) = backend.ec_add(input1_x, input1_y, input2_x, input2_y)?; + + insert_value(&outputs.0, res_x, initial_witness)?; + insert_value(&outputs.1, res_y, initial_witness)?; + + Ok(()) +} diff --git a/noir/acvm-repo/acvm/src/pwg/blackbox/mod.rs b/noir/acvm-repo/acvm/src/pwg/blackbox/mod.rs index 5eea234885c..0f026cd274a 100644 --- a/noir/acvm-repo/acvm/src/pwg/blackbox/mod.rs +++ b/noir/acvm-repo/acvm/src/pwg/blackbox/mod.rs @@ -17,7 +17,7 @@ mod pedersen; mod range; mod signature; -use fixed_base_scalar_mul::fixed_base_scalar_mul; +use fixed_base_scalar_mul::{embedded_curve_add, fixed_base_scalar_mul}; // Hash functions should eventually be exposed for external consumers. use hash::solve_generic_256_hash_opcode; use logic::{and, xor}; @@ -177,13 +177,26 @@ pub(crate) fn solve( BlackBoxFuncCall::FixedBaseScalarMul { low, high, outputs } => { fixed_base_scalar_mul(backend, initial_witness, *low, *high, *outputs) } - BlackBoxFuncCall::EmbeddedCurveAdd { .. } => { - todo!(); - } - BlackBoxFuncCall::EmbeddedCurveDouble { .. } => { - todo!(); + BlackBoxFuncCall::EmbeddedCurveAdd { input1_x, input1_y, input2_x, input2_y, outputs } => { + embedded_curve_add( + backend, + initial_witness, + *input1_x, + *input1_y, + *input2_x, + *input2_y, + *outputs, + ) } // Recursive aggregation will be entirely handled by the backend and is not solved by the ACVM BlackBoxFuncCall::RecursiveAggregation { .. } => Ok(()), + BlackBoxFuncCall::BigIntAdd { .. } => todo!(), + BlackBoxFuncCall::BigIntNeg { .. } => todo!(), + BlackBoxFuncCall::BigIntMul { .. } => todo!(), + BlackBoxFuncCall::BigIntDiv { .. } => todo!(), + BlackBoxFuncCall::BigIntFromLeBytes { .. } => todo!(), + BlackBoxFuncCall::BigIntToLeBytes { .. } => todo!(), + BlackBoxFuncCall::Poseidon2Permutation { .. } => todo!(), + BlackBoxFuncCall::Sha256Compression { .. } => todo!(), } } diff --git a/noir/acvm-repo/acvm/src/pwg/brillig.rs b/noir/acvm-repo/acvm/src/pwg/brillig.rs index 0db38c776e2..4b62f86f0eb 100644 --- a/noir/acvm-repo/acvm/src/pwg/brillig.rs +++ b/noir/acvm-repo/acvm/src/pwg/brillig.rs @@ -1,5 +1,5 @@ use acir::{ - brillig::{ForeignCallParam, ForeignCallResult, RegisterIndex, Value}, + brillig::{ForeignCallParam, ForeignCallResult, Value}, circuit::{ brillig::{Brillig, BrilligInputs, BrilligOutputs}, OpcodeLocation, @@ -8,7 +8,7 @@ use acir::{ FieldElement, }; use acvm_blackbox_solver::BlackBoxFunctionSolver; -use brillig_vm::{Registers, VMStatus, VM}; +use brillig_vm::{VMStatus, VM}; use crate::{pwg::OpcodeNotSolvable, OpcodeResolutionError}; @@ -69,16 +69,15 @@ impl<'b, B: BlackBoxFunctionSolver> BrilligSolver<'b, B> { acir_index: usize, ) -> Result { // Set input values - let mut input_register_values: Vec = Vec::new(); - let mut input_memory: Vec = Vec::new(); + let mut calldata: Vec = Vec::new(); // Each input represents an expression or array of expressions to evaluate. // Iterate over each input and evaluate the expression(s) associated with it. - // Push the results into registers and/or memory. + // Push the results into memory. // If a certain expression is not solvable, we stall the ACVM and do not proceed with Brillig VM execution. for input in &brillig.inputs { match input { BrilligInputs::Single(expr) => match get_value(expr, initial_witness) { - Ok(value) => input_register_values.push(value.into()), + Ok(value) => calldata.push(value.into()), Err(_) => { return Err(OpcodeResolutionError::OpcodeNotSolvable( OpcodeNotSolvable::ExpressionHasTooManyUnknowns(expr.clone()), @@ -87,10 +86,9 @@ impl<'b, B: BlackBoxFunctionSolver> BrilligSolver<'b, B> { }, BrilligInputs::Array(expr_arr) => { // Attempt to fetch all array input values - let memory_pointer = input_memory.len(); for expr in expr_arr.iter() { match get_value(expr, initial_witness) { - Ok(value) => input_memory.push(value.into()), + Ok(value) => calldata.push(value.into()), Err(_) => { return Err(OpcodeResolutionError::OpcodeNotSolvable( OpcodeNotSolvable::ExpressionHasTooManyUnknowns(expr.clone()), @@ -98,28 +96,16 @@ impl<'b, B: BlackBoxFunctionSolver> BrilligSolver<'b, B> { } } } - - // Push value of the array pointer as a register - input_register_values.push(Value::from(memory_pointer)); } } } - // Instantiate a Brillig VM given the solved input registers and memory + // Instantiate a Brillig VM given the solved calldata // along with the Brillig bytecode. - let input_registers = Registers::load(input_register_values); - let vm = VM::new(input_registers, input_memory, &brillig.bytecode, vec![], bb_solver); + let vm = VM::new(calldata, &brillig.bytecode, vec![], bb_solver); Ok(Self { vm, acir_index }) } - pub fn get_registers(&self) -> &Registers { - self.vm.get_registers() - } - - pub fn set_register(&mut self, register_index: usize, value: Value) { - self.vm.set_register(RegisterIndex(register_index), value); - } - pub fn get_memory(&self) -> &[Value] { self.vm.get_memory() } @@ -151,7 +137,7 @@ impl<'b, B: BlackBoxFunctionSolver> BrilligSolver<'b, B> { // Return the "resolution" to the caller who may choose to make subsequent calls // (when it gets foreign call results for example). match vm_status { - VMStatus::Finished => Ok(BrilligSolverStatus::Finished), + VMStatus::Finished { .. } => Ok(BrilligSolverStatus::Finished), VMStatus::InProgress => Ok(BrilligSolverStatus::InProgress), VMStatus::Failure { message, call_stack } => { Err(OpcodeResolutionError::BrilligFunctionFailed { @@ -179,8 +165,8 @@ impl<'b, B: BlackBoxFunctionSolver> BrilligSolver<'b, B> { // Finish the Brillig execution by writing the outputs to the witness map let vm_status = self.vm.get_status(); match vm_status { - VMStatus::Finished => { - self.write_brillig_outputs(witness, brillig)?; + VMStatus::Finished { return_data_offset, return_data_size } => { + self.write_brillig_outputs(witness, return_data_offset, return_data_size, brillig)?; Ok(()) } _ => panic!("Brillig VM has not completed execution"), @@ -190,24 +176,32 @@ impl<'b, B: BlackBoxFunctionSolver> BrilligSolver<'b, B> { fn write_brillig_outputs( &self, witness_map: &mut WitnessMap, + return_data_offset: usize, + return_data_size: usize, brillig: &Brillig, ) -> Result<(), OpcodeResolutionError> { // Write VM execution results into the witness map - for (i, output) in brillig.outputs.iter().enumerate() { - let register_value = self.vm.get_registers().get(RegisterIndex::from(i)); + let memory = self.vm.get_memory(); + let mut current_ret_data_idx = return_data_offset; + for output in brillig.outputs.iter() { match output { BrilligOutputs::Simple(witness) => { - insert_value(witness, register_value.to_field(), witness_map)?; + insert_value(witness, memory[current_ret_data_idx].to_field(), witness_map)?; + current_ret_data_idx += 1; } BrilligOutputs::Array(witness_arr) => { - // Treat the register value as a pointer to memory - for (i, witness) in witness_arr.iter().enumerate() { - let value = &self.vm.get_memory()[register_value.to_usize() + i]; + for witness in witness_arr.iter() { + let value = memory[current_ret_data_idx]; insert_value(witness, value.to_field(), witness_map)?; + current_ret_data_idx += 1; } } } } + assert!( + current_ret_data_idx == return_data_offset + return_data_size, + "Brillig VM did not write the expected number of return values" + ); Ok(()) } diff --git a/noir/acvm-repo/acvm/tests/solver.rs b/noir/acvm-repo/acvm/tests/solver.rs index 486e04d5bf1..9ff4a4f4897 100644 --- a/noir/acvm-repo/acvm/tests/solver.rs +++ b/noir/acvm-repo/acvm/tests/solver.rs @@ -1,7 +1,7 @@ use std::collections::BTreeMap; use acir::{ - brillig::{BinaryFieldOp, Opcode as BrilligOpcode, RegisterIndex, RegisterOrMemory, Value}, + brillig::{BinaryFieldOp, MemoryAddress, Opcode as BrilligOpcode, Value, ValueOrArray}, circuit::{ brillig::{Brillig, BrilligInputs, BrilligOutputs}, opcodes::{BlockId, MemOp}, @@ -37,9 +37,9 @@ fn inversion_brillig_oracle_equivalence() { let equal_opcode = BrilligOpcode::BinaryFieldOp { op: BinaryFieldOp::Equals, - lhs: RegisterIndex::from(0), - rhs: RegisterIndex::from(1), - destination: RegisterIndex::from(2), + lhs: MemoryAddress::from(0), + rhs: MemoryAddress::from(1), + destination: MemoryAddress::from(2), }; let brillig_data = Brillig { @@ -52,20 +52,26 @@ fn inversion_brillig_oracle_equivalence() { }), BrilligInputs::Single(Expression::default()), // Input Register 1 ], - // This tells the BrilligSolver which witnesses its output registers correspond to + // This tells the BrilligSolver which witnesses its output values correspond to outputs: vec![ BrilligOutputs::Simple(w_x_plus_y), // Output Register 0 - from input BrilligOutputs::Simple(w_oracle), // Output Register 1 BrilligOutputs::Simple(w_equal_res), // Output Register 2 ], bytecode: vec![ + BrilligOpcode::CalldataCopy { + destination_address: MemoryAddress(0), + size: 2, + offset: 0, + }, equal_opcode, // Oracles are named 'foreign calls' in brillig BrilligOpcode::ForeignCall { function: "invert".into(), - destinations: vec![RegisterOrMemory::RegisterIndex(RegisterIndex::from(1))], - inputs: vec![RegisterOrMemory::RegisterIndex(RegisterIndex::from(0))], + destinations: vec![ValueOrArray::MemoryAddress(MemoryAddress::from(1))], + inputs: vec![ValueOrArray::MemoryAddress(MemoryAddress::from(0))], }, + BrilligOpcode::Stop { return_data_offset: 0, return_data_size: 3 }, ], predicate: None, }; @@ -151,9 +157,9 @@ fn double_inversion_brillig_oracle() { let equal_opcode = BrilligOpcode::BinaryFieldOp { op: BinaryFieldOp::Equals, - lhs: RegisterIndex::from(0), - rhs: RegisterIndex::from(1), - destination: RegisterIndex::from(4), + lhs: MemoryAddress::from(0), + rhs: MemoryAddress::from(1), + destination: MemoryAddress::from(4), }; let brillig_data = Brillig { @@ -180,18 +186,24 @@ fn double_inversion_brillig_oracle() { BrilligOutputs::Simple(w_equal_res), // Output Register 4 ], bytecode: vec![ + BrilligOpcode::CalldataCopy { + destination_address: MemoryAddress(0), + size: 3, + offset: 0, + }, equal_opcode, // Oracles are named 'foreign calls' in brillig BrilligOpcode::ForeignCall { function: "invert".into(), - destinations: vec![RegisterOrMemory::RegisterIndex(RegisterIndex::from(1))], - inputs: vec![RegisterOrMemory::RegisterIndex(RegisterIndex::from(0))], + destinations: vec![ValueOrArray::MemoryAddress(MemoryAddress::from(1))], + inputs: vec![ValueOrArray::MemoryAddress(MemoryAddress::from(0))], }, BrilligOpcode::ForeignCall { function: "invert".into(), - destinations: vec![RegisterOrMemory::RegisterIndex(RegisterIndex::from(3))], - inputs: vec![RegisterOrMemory::RegisterIndex(RegisterIndex::from(2))], + destinations: vec![ValueOrArray::MemoryAddress(MemoryAddress::from(3))], + inputs: vec![ValueOrArray::MemoryAddress(MemoryAddress::from(2))], }, + BrilligOpcode::Stop { return_data_offset: 0, return_data_size: 5 }, ], predicate: None, }; @@ -306,17 +318,23 @@ fn oracle_dependent_execution() { BrilligOutputs::Simple(w_y_inv), // Output Register 3 ], bytecode: vec![ + BrilligOpcode::CalldataCopy { + destination_address: MemoryAddress(0), + size: 3, + offset: 0, + }, // Oracles are named 'foreign calls' in brillig BrilligOpcode::ForeignCall { function: "invert".into(), - destinations: vec![RegisterOrMemory::RegisterIndex(RegisterIndex::from(1))], - inputs: vec![RegisterOrMemory::RegisterIndex(RegisterIndex::from(0))], + destinations: vec![ValueOrArray::MemoryAddress(MemoryAddress::from(1))], + inputs: vec![ValueOrArray::MemoryAddress(MemoryAddress::from(0))], }, BrilligOpcode::ForeignCall { function: "invert".into(), - destinations: vec![RegisterOrMemory::RegisterIndex(RegisterIndex::from(3))], - inputs: vec![RegisterOrMemory::RegisterIndex(RegisterIndex::from(2))], + destinations: vec![ValueOrArray::MemoryAddress(MemoryAddress::from(3))], + inputs: vec![ValueOrArray::MemoryAddress(MemoryAddress::from(2))], }, + BrilligOpcode::Stop { return_data_offset: 0, return_data_size: 4 }, ], predicate: None, }; @@ -404,9 +422,9 @@ fn brillig_oracle_predicate() { let equal_opcode = BrilligOpcode::BinaryFieldOp { op: BinaryFieldOp::Equals, - lhs: RegisterIndex::from(0), - rhs: RegisterIndex::from(1), - destination: RegisterIndex::from(2), + lhs: MemoryAddress::from(0), + rhs: MemoryAddress::from(1), + destination: MemoryAddress::from(2), }; let brillig_opcode = Opcode::Brillig(Brillig { @@ -425,12 +443,17 @@ fn brillig_oracle_predicate() { BrilligOutputs::Simple(w_lt_res), ], bytecode: vec![ + BrilligOpcode::CalldataCopy { + destination_address: MemoryAddress(0), + size: 2, + offset: 0, + }, equal_opcode, // Oracles are named 'foreign calls' in brillig BrilligOpcode::ForeignCall { function: "invert".into(), - destinations: vec![RegisterOrMemory::RegisterIndex(RegisterIndex::from(1))], - inputs: vec![RegisterOrMemory::RegisterIndex(RegisterIndex::from(0))], + destinations: vec![ValueOrArray::MemoryAddress(MemoryAddress::from(1))], + inputs: vec![ValueOrArray::MemoryAddress(MemoryAddress::from(0))], }, ], predicate: Some(Expression::default()), @@ -502,21 +525,24 @@ fn unsatisfied_opcode_resolved_brillig() { let w_y = Witness(5); let w_result = Witness(6); + let calldata_copy_opcode = + BrilligOpcode::CalldataCopy { destination_address: MemoryAddress(0), size: 2, offset: 0 }; + let equal_opcode = BrilligOpcode::BinaryFieldOp { op: BinaryFieldOp::Equals, - lhs: RegisterIndex::from(0), - rhs: RegisterIndex::from(1), - destination: RegisterIndex::from(2), + lhs: MemoryAddress::from(0), + rhs: MemoryAddress::from(1), + destination: MemoryAddress::from(2), }; // Jump pass the trap if the values are equal, else // jump to the trap let location_of_stop = 3; let jmp_if_opcode = - BrilligOpcode::JumpIf { condition: RegisterIndex::from(2), location: location_of_stop }; + BrilligOpcode::JumpIf { condition: MemoryAddress::from(2), location: location_of_stop }; let trap_opcode = BrilligOpcode::Trap; - let stop_opcode = BrilligOpcode::Stop; + let stop_opcode = BrilligOpcode::Stop { return_data_offset: 0, return_data_size: 0 }; let brillig_opcode = Opcode::Brillig(Brillig { inputs: vec![ @@ -532,7 +558,7 @@ fn unsatisfied_opcode_resolved_brillig() { }), ], outputs: vec![BrilligOutputs::Simple(w_result)], - bytecode: vec![equal_opcode, jmp_if_opcode, trap_opcode, stop_opcode], + bytecode: vec![calldata_copy_opcode, equal_opcode, jmp_if_opcode, trap_opcode, stop_opcode], predicate: Some(Expression::one()), }); @@ -564,7 +590,7 @@ fn unsatisfied_opcode_resolved_brillig() { solver_status, ACVMStatus::Failure(OpcodeResolutionError::BrilligFunctionFailed { message: "explicit trap hit in brillig".to_string(), - call_stack: vec![OpcodeLocation::Brillig { acir_index: 0, brillig_index: 2 }] + call_stack: vec![OpcodeLocation::Brillig { acir_index: 0, brillig_index: 3 }] }), "The first opcode is not satisfiable, expected an error indicating this" ); diff --git a/noir/acvm-repo/acvm_js/src/logging.rs b/noir/acvm-repo/acvm_js/src/logging.rs index f5d71fae067..483c23ab624 100644 --- a/noir/acvm-repo/acvm_js/src/logging.rs +++ b/noir/acvm-repo/acvm_js/src/logging.rs @@ -1,3 +1,4 @@ +use js_sys::{Error, JsString}; use tracing_subscriber::prelude::*; use tracing_subscriber::EnvFilter; use tracing_web::MakeWebConsoleWriter; @@ -7,12 +8,15 @@ use wasm_bindgen::prelude::*; /// /// @param {LogLevel} level - The maximum level of logging to be emitted. #[wasm_bindgen(js_name = initLogLevel, skip_jsdoc)] -pub fn init_log_level(filter: String) { +pub fn init_log_level(filter: String) -> Result<(), JsLogInitError> { // Set the static variable from Rust use std::sync::Once; - let filter: EnvFilter = - filter.parse().expect("Could not parse log filter while initializing logger"); + let filter: EnvFilter = filter.parse().map_err(|err| { + JsLogInitError::constructor( + format!("Could not parse log filter while initializing logger: {err}").into(), + ) + })?; static SET_HOOK: Once = Once::new(); SET_HOOK.call_once(|| { @@ -23,4 +27,19 @@ pub fn init_log_level(filter: String) { tracing_subscriber::registry().with(fmt_layer.with_filter(filter)).init(); }); + + Ok(()) +} + +/// `LogInitError` is a raw js error. +/// It'd be ideal that `LogInitError` was a subclass of Error, but for that we'd need to use JS snippets or a js module. +/// Currently JS snippets don't work with a nodejs target. And a module would be too much for just a custom error type. +#[wasm_bindgen] +extern "C" { + #[wasm_bindgen(extends = Error, js_name = "LogInitError", typescript_type = "LogInitError")] + #[derive(Clone, Debug, PartialEq, Eq)] + pub type JsLogInitError; + + #[wasm_bindgen(constructor, js_class = "Error")] + fn constructor(message: JsString) -> JsLogInitError; } diff --git a/noir/acvm-repo/acvm_js/test/shared/complex_foreign_call.ts b/noir/acvm-repo/acvm_js/test/shared/complex_foreign_call.ts index 1b6f5e4319a..faae98bcf47 100644 --- a/noir/acvm-repo/acvm_js/test/shared/complex_foreign_call.ts +++ b/noir/acvm-repo/acvm_js/test/shared/complex_foreign_call.ts @@ -2,11 +2,12 @@ import { WitnessMap } from '@noir-lang/acvm_js'; // See `complex_brillig_foreign_call` integration test in `acir/tests/test_program_serialization.rs`. export const bytecode = Uint8Array.from([ - 31, 139, 8, 0, 0, 0, 0, 0, 0, 255, 213, 83, 219, 10, 128, 48, 8, 117, 174, 139, 159, 179, 254, 160, 127, 137, 222, - 138, 122, 236, 243, 19, 114, 32, 22, 244, 144, 131, 118, 64, 156, 178, 29, 14, 59, 74, 0, 16, 224, 66, 228, 64, 57, 7, - 169, 53, 242, 189, 81, 114, 250, 134, 33, 248, 113, 165, 82, 26, 177, 2, 141, 177, 128, 198, 60, 15, 63, 245, 219, - 211, 23, 215, 255, 139, 15, 251, 211, 112, 180, 28, 157, 212, 189, 100, 82, 179, 64, 170, 63, 109, 235, 190, 204, 135, - 166, 178, 150, 216, 62, 154, 252, 250, 70, 147, 35, 220, 119, 93, 227, 4, 182, 131, 81, 25, 36, 4, 0, 0, + 31, 139, 8, 0, 0, 0, 0, 0, 0, 255, 213, 83, 65, 10, 132, 48, 12, 76, 218, 237, 174, 123, 242, 11, 130, 62, 160, 250, + 2, 255, 34, 222, 20, 61, 250, 124, 11, 78, 49, 4, 193, 131, 21, 52, 16, 210, 132, 105, 50, 77, 210, 140, 136, 152, 54, + 177, 65, 13, 206, 12, 95, 74, 196, 181, 176, 254, 154, 212, 156, 46, 151, 191, 139, 163, 121, 1, 71, 123, 3, 199, 184, + 15, 15, 157, 119, 202, 185, 36, 237, 159, 61, 248, 63, 159, 160, 46, 232, 23, 254, 15, 54, 67, 156, 96, 11, 213, 119, + 82, 248, 116, 179, 104, 188, 163, 125, 15, 89, 213, 253, 139, 154, 221, 52, 206, 67, 191, 88, 5, 213, 52, 75, 113, + 174, 96, 205, 201, 157, 24, 207, 197, 211, 157, 6, 50, 18, 233, 158, 72, 89, 1, 53, 215, 75, 175, 196, 4, 0, 0, ]); export const initialWitnessMap: WitnessMap = new Map([ [1, '0x0000000000000000000000000000000000000000000000000000000000000001'], diff --git a/noir/acvm-repo/acvm_js/test/shared/foreign_call.ts b/noir/acvm-repo/acvm_js/test/shared/foreign_call.ts index 178ec3a09d1..7a65f29117c 100644 --- a/noir/acvm-repo/acvm_js/test/shared/foreign_call.ts +++ b/noir/acvm-repo/acvm_js/test/shared/foreign_call.ts @@ -2,10 +2,10 @@ import { WitnessMap } from '@noir-lang/acvm_js'; // See `simple_brillig_foreign_call` integration test in `acir/tests/test_program_serialization.rs`. export const bytecode = Uint8Array.from([ - 31, 139, 8, 0, 0, 0, 0, 0, 0, 255, 173, 143, 49, 10, 64, 33, 12, 67, 99, 63, 124, 60, 142, 222, 192, 203, 56, 184, 56, - 136, 120, 126, 5, 21, 226, 160, 139, 62, 40, 13, 45, 132, 68, 3, 80, 232, 124, 164, 153, 121, 115, 99, 155, 59, 172, - 122, 231, 101, 56, 175, 80, 86, 221, 230, 31, 58, 196, 226, 83, 62, 53, 91, 16, 122, 10, 246, 84, 99, 243, 0, 30, 59, - 1, 0, 0, + 31, 139, 8, 0, 0, 0, 0, 0, 0, 255, 173, 143, 177, 10, 192, 32, 12, 68, 207, 148, 150, 118, 234, 175, 216, 63, 232, + 207, 116, 232, 210, 161, 136, 223, 175, 98, 132, 27, 212, 69, 31, 132, 28, 23, 8, 119, 59, 0, 131, 204, 66, 154, 41, + 222, 173, 219, 142, 113, 153, 121, 191, 44, 231, 21, 237, 144, 88, 43, 249, 11, 71, 156, 77, 245, 251, 249, 231, 119, + 189, 214, 204, 89, 187, 11, 25, 130, 54, 1, 36, 1, 124, 242, 107, 1, 0, 0, ]); export const initialWitnessMap: WitnessMap = new Map([ [1, '0x0000000000000000000000000000000000000000000000000000000000000005'], diff --git a/noir/acvm-repo/blackbox_solver/src/curve_specific_solver.rs b/noir/acvm-repo/blackbox_solver/src/curve_specific_solver.rs index 82941e91d61..2234710dec0 100644 --- a/noir/acvm-repo/blackbox_solver/src/curve_specific_solver.rs +++ b/noir/acvm-repo/blackbox_solver/src/curve_specific_solver.rs @@ -36,11 +36,6 @@ pub trait BlackBoxFunctionSolver { input2_x: &FieldElement, input2_y: &FieldElement, ) -> Result<(FieldElement, FieldElement), BlackBoxResolutionError>; - fn ec_double( - &self, - input_x: &FieldElement, - input_x: &FieldElement, - ) -> Result<(FieldElement, FieldElement), BlackBoxResolutionError>; } pub struct StubbedBlackBoxSolver; @@ -94,11 +89,4 @@ impl BlackBoxFunctionSolver for StubbedBlackBoxSolver { ) -> Result<(FieldElement, FieldElement), BlackBoxResolutionError> { Err(Self::fail(BlackBoxFunc::EmbeddedCurveAdd)) } - fn ec_double( - &self, - _input_x: &FieldElement, - _input_y: &FieldElement, - ) -> Result<(FieldElement, FieldElement), BlackBoxResolutionError> { - Err(Self::fail(BlackBoxFunc::EmbeddedCurveDouble)) - } } diff --git a/noir/acvm-repo/bn254_blackbox_solver/src/fixed_base_scalar_mul.rs b/noir/acvm-repo/bn254_blackbox_solver/src/fixed_base_scalar_mul.rs index 7f004de0fe9..5e68c7d4030 100644 --- a/noir/acvm-repo/bn254_blackbox_solver/src/fixed_base_scalar_mul.rs +++ b/noir/acvm-repo/bn254_blackbox_solver/src/fixed_base_scalar_mul.rs @@ -47,6 +47,26 @@ pub fn fixed_base_scalar_mul( } } +pub fn embedded_curve_add( + input1_x: FieldElement, + input1_y: FieldElement, + input2_x: FieldElement, + input2_y: FieldElement, +) -> Result<(FieldElement, FieldElement), BlackBoxResolutionError> { + let mut point1 = grumpkin::SWAffine::new(input1_x.into_repr(), input1_y.into_repr()); + let point2 = grumpkin::SWAffine::new(input2_x.into_repr(), input2_y.into_repr()); + let res = point1 + point2; + point1 = res.into(); + if let Some((res_x, res_y)) = point1.xy() { + Ok((FieldElement::from_repr(*res_x), FieldElement::from_repr(*res_y))) + } else { + Err(BlackBoxResolutionError::Failed( + BlackBoxFunc::EmbeddedCurveAdd, + "Point is not on curve".to_string(), + )) + } +} + #[cfg(test)] mod grumpkin_fixed_base_scalar_mul { use ark_ff::BigInteger; diff --git a/noir/acvm-repo/bn254_blackbox_solver/src/lib.rs b/noir/acvm-repo/bn254_blackbox_solver/src/lib.rs index 92c45e93dea..13aa956f9e1 100644 --- a/noir/acvm-repo/bn254_blackbox_solver/src/lib.rs +++ b/noir/acvm-repo/bn254_blackbox_solver/src/lib.rs @@ -8,7 +8,7 @@ use acvm_blackbox_solver::{BlackBoxFunctionSolver, BlackBoxResolutionError}; mod fixed_base_scalar_mul; mod wasm; -pub use fixed_base_scalar_mul::fixed_base_scalar_mul; +pub use fixed_base_scalar_mul::{embedded_curve_add, fixed_base_scalar_mul}; use wasm::Barretenberg; use self::wasm::{Pedersen, SchnorrSig}; @@ -90,19 +90,11 @@ impl BlackBoxFunctionSolver for Bn254BlackBoxSolver { fn ec_add( &self, - _input1_x: &FieldElement, - _input1_y: &FieldElement, - _input2_x: &FieldElement, - _input2_y: &FieldElement, + input1_x: &FieldElement, + input1_y: &FieldElement, + input2_x: &FieldElement, + input2_y: &FieldElement, ) -> Result<(FieldElement, FieldElement), BlackBoxResolutionError> { - todo!(); - } - - fn ec_double( - &self, - _input_x: &FieldElement, - _input_y: &FieldElement, - ) -> Result<(FieldElement, FieldElement), BlackBoxResolutionError> { - todo!(); + embedded_curve_add(*input1_x, *input1_y, *input2_x, *input2_y) } } diff --git a/noir/acvm-repo/brillig/src/black_box.rs b/noir/acvm-repo/brillig/src/black_box.rs index c007e78b785..9195d8aa6f3 100644 --- a/noir/acvm-repo/brillig/src/black_box.rs +++ b/noir/acvm-repo/brillig/src/black_box.rs @@ -1,4 +1,4 @@ -use crate::{opcodes::HeapVector, HeapArray, RegisterIndex}; +use crate::{opcodes::HeapVector, HeapArray, MemoryAddress}; use serde::{Deserialize, Serialize}; /// These opcodes provide an equivalent of ACIR blackbox functions. @@ -6,22 +6,37 @@ use serde::{Deserialize, Serialize}; #[derive(Debug, Clone, Copy, PartialEq, Eq, Serialize, Deserialize)] pub enum BlackBoxOp { /// Calculates the SHA256 hash of the inputs. - Sha256 { message: HeapVector, output: HeapArray }, + Sha256 { + message: HeapVector, + output: HeapArray, + }, /// Calculates the Blake2s hash of the inputs. - Blake2s { message: HeapVector, output: HeapArray }, + Blake2s { + message: HeapVector, + output: HeapArray, + }, /// Calculates the Blake3 hash of the inputs. - Blake3 { message: HeapVector, output: HeapArray }, + Blake3 { + message: HeapVector, + output: HeapArray, + }, /// Calculates the Keccak256 hash of the inputs. - Keccak256 { message: HeapVector, output: HeapArray }, + Keccak256 { + message: HeapVector, + output: HeapArray, + }, /// Keccak Permutation function of 1600 width - Keccakf1600 { message: HeapVector, output: HeapArray }, + Keccakf1600 { + message: HeapVector, + output: HeapArray, + }, /// Verifies a ECDSA signature over the secp256k1 curve. EcdsaSecp256k1 { hashed_msg: HeapVector, public_key_x: HeapArray, public_key_y: HeapArray, signature: HeapArray, - result: RegisterIndex, + result: MemoryAddress, }, /// Verifies a ECDSA signature over the secp256r1 curve. EcdsaSecp256r1 { @@ -29,30 +44,79 @@ pub enum BlackBoxOp { public_key_x: HeapArray, public_key_y: HeapArray, signature: HeapArray, - result: RegisterIndex, + result: MemoryAddress, }, /// Verifies a Schnorr signature over a curve which is "pairing friendly" with the curve on which the Brillig bytecode is defined. SchnorrVerify { - public_key_x: RegisterIndex, - public_key_y: RegisterIndex, + public_key_x: MemoryAddress, + public_key_y: MemoryAddress, message: HeapVector, signature: HeapVector, - result: RegisterIndex, + result: MemoryAddress, }, /// Calculates a Pedersen commitment to the inputs. - PedersenCommitment { inputs: HeapVector, domain_separator: RegisterIndex, output: HeapArray }, + PedersenCommitment { + inputs: HeapVector, + domain_separator: MemoryAddress, + output: HeapArray, + }, /// Calculates a Pedersen hash to the inputs. - PedersenHash { inputs: HeapVector, domain_separator: RegisterIndex, output: RegisterIndex }, + PedersenHash { + inputs: HeapVector, + domain_separator: MemoryAddress, + output: MemoryAddress, + }, /// Performs scalar multiplication over the embedded curve. - FixedBaseScalarMul { low: RegisterIndex, high: RegisterIndex, result: HeapArray }, + FixedBaseScalarMul { + low: MemoryAddress, + high: MemoryAddress, + result: HeapArray, + }, /// Performs addition over the embedded curve. EmbeddedCurveAdd { - input1_x: RegisterIndex, - input1_y: RegisterIndex, - input2_x: RegisterIndex, - input2_y: RegisterIndex, + input1_x: MemoryAddress, + input1_y: MemoryAddress, + input2_x: MemoryAddress, + input2_y: MemoryAddress, result: HeapArray, }, - /// Performs point doubling over the embedded curve. - EmbeddedCurveDouble { input1_x: RegisterIndex, input1_y: RegisterIndex, result: HeapArray }, + BigIntAdd { + lhs: MemoryAddress, + rhs: MemoryAddress, + output: MemoryAddress, + }, + BigIntNeg { + lhs: MemoryAddress, + rhs: MemoryAddress, + output: MemoryAddress, + }, + BigIntMul { + lhs: MemoryAddress, + rhs: MemoryAddress, + output: MemoryAddress, + }, + BigIntDiv { + lhs: MemoryAddress, + rhs: MemoryAddress, + output: MemoryAddress, + }, + BigIntFromLeBytes { + inputs: HeapVector, + modulus: HeapVector, + output: MemoryAddress, + }, + BigIntToLeBytes { + input: MemoryAddress, + output: HeapVector, + }, + Poseidon2Permutation { + message: HeapVector, + output: HeapArray, + len: MemoryAddress, + }, + Sha256Compression { + input: HeapVector, + hash_values: HeapVector, + output: HeapArray, + }, } diff --git a/noir/acvm-repo/brillig/src/lib.rs b/noir/acvm-repo/brillig/src/lib.rs index 5e033e3c792..2e9d80e2f3b 100644 --- a/noir/acvm-repo/brillig/src/lib.rs +++ b/noir/acvm-repo/brillig/src/lib.rs @@ -17,9 +17,7 @@ mod value; pub use black_box::BlackBoxOp; pub use foreign_call::{ForeignCallParam, ForeignCallResult}; -pub use opcodes::{ - BinaryFieldOp, BinaryIntOp, HeapArray, HeapVector, RegisterIndex, RegisterOrMemory, -}; +pub use opcodes::{BinaryFieldOp, BinaryIntOp, HeapArray, HeapVector, MemoryAddress, ValueOrArray}; pub use opcodes::{BrilligOpcode as Opcode, Label}; pub use value::Typ; pub use value::Value; diff --git a/noir/acvm-repo/brillig/src/opcodes.rs b/noir/acvm-repo/brillig/src/opcodes.rs index 79295cc6e5d..9a6f11c463f 100644 --- a/noir/acvm-repo/brillig/src/opcodes.rs +++ b/noir/acvm-repo/brillig/src/opcodes.rs @@ -4,100 +4,106 @@ use serde::{Deserialize, Serialize}; pub type Label = usize; #[derive(Debug, Clone, Copy, PartialEq, Eq, PartialOrd, Ord, Hash, Serialize, Deserialize)] -pub struct RegisterIndex(pub usize); +pub struct MemoryAddress(pub usize); -/// `RegisterIndex` refers to the index in VM register space. -impl RegisterIndex { +/// `MemoryAddress` refers to the index in VM memory. +impl MemoryAddress { pub fn to_usize(self) -> usize { self.0 } } -impl From for RegisterIndex { +impl From for MemoryAddress { fn from(value: usize) -> Self { - RegisterIndex(value) + MemoryAddress(value) } } -/// A fixed-sized array starting from a Brillig register memory location. +/// A fixed-sized array starting from a Brillig memory location. #[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize, Copy)] pub struct HeapArray { - pub pointer: RegisterIndex, + pub pointer: MemoryAddress, pub size: usize, } -/// A register-sized vector passed starting from a Brillig register memory location and with a register-held size +/// A memory-sized vector passed starting from a Brillig memory location and with a memory-held size #[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize, Copy)] pub struct HeapVector { - pub pointer: RegisterIndex, - pub size: RegisterIndex, + pub pointer: MemoryAddress, + pub size: MemoryAddress, } /// Lays out various ways an external foreign call's input and output data may be interpreted inside Brillig. -/// This data can either be an individual register value or memory. +/// This data can either be an individual value or memory. /// /// While we are usually agnostic to how memory is passed within Brillig, /// this needs to be encoded somehow when dealing with an external system. /// For simplicity, the extra type information is given right in the ForeignCall instructions. #[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize, Copy)] -pub enum RegisterOrMemory { - /// A single register value passed to or from an external call - /// It is an 'immediate' value - used without dereferencing memory. - /// For a foreign call input, the value is read directly from the register. - /// For a foreign call output, the value is written directly to the register. - RegisterIndex(RegisterIndex), +pub enum ValueOrArray { + /// A single value passed to or from an external call + /// It is an 'immediate' value - used without dereferencing. + /// For a foreign call input, the value is read directly from memory. + /// For a foreign call output, the value is written directly to memory. + MemoryAddress(MemoryAddress), /// An array passed to or from an external call /// In the case of a foreign call input, the array is read from this Brillig memory location + usize more cells. /// In the case of a foreign call output, the array is written to this Brillig memory location with the usize being here just as a sanity check for the size write. HeapArray(HeapArray), /// A vector passed to or from an external call - /// In the case of a foreign call input, the vector is read from this Brillig memory location + as many cells as the 2nd register indicates. - /// In the case of a foreign call output, the vector is written to this Brillig memory location and as 'size' cells, with size being stored in the second register. + /// In the case of a foreign call input, the vector is read from this Brillig memory location + as many cells as the 2nd address indicates. + /// In the case of a foreign call output, the vector is written to this Brillig memory location and as 'size' cells, with size being stored in the second address. HeapVector(HeapVector), } #[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize)] pub enum BrilligOpcode { - /// Takes the fields in registers `lhs` and `rhs` + /// Takes the fields in addresses `lhs` and `rhs` /// Performs the specified binary operation - /// and stores the value in the `result` register. + /// and stores the value in the `result` address. BinaryFieldOp { - destination: RegisterIndex, + destination: MemoryAddress, op: BinaryFieldOp, - lhs: RegisterIndex, - rhs: RegisterIndex, + lhs: MemoryAddress, + rhs: MemoryAddress, }, - /// Takes the `bit_size` size integers in registers `lhs` and `rhs` + /// Takes the `bit_size` size integers in addresses `lhs` and `rhs` /// Performs the specified binary operation - /// and stores the value in the `result` register. + /// and stores the value in the `result` address. BinaryIntOp { - destination: RegisterIndex, + destination: MemoryAddress, op: BinaryIntOp, bit_size: u32, - lhs: RegisterIndex, - rhs: RegisterIndex, + lhs: MemoryAddress, + rhs: MemoryAddress, }, JumpIfNot { - condition: RegisterIndex, + condition: MemoryAddress, location: Label, }, /// Sets the program counter to the value located at `destination` /// If the value at `condition` is non-zero JumpIf { - condition: RegisterIndex, + condition: MemoryAddress, location: Label, }, /// Sets the program counter to the label. Jump { location: Label, }, + /// Copies calldata after the offset to the specified address and length + CalldataCopy { + destination_address: MemoryAddress, + size: usize, + offset: usize, + }, /// We don't support dynamic jumps or calls /// See https://github.com/ethereum/aleth/issues/3404 for reasoning Call { location: Label, }, Const { - destination: RegisterIndex, + destination: MemoryAddress, value: Value, }, Return, @@ -109,28 +115,31 @@ pub enum BrilligOpcode { /// Interpreted by caller context, ie this will have different meanings depending on /// who the caller is. function: String, - /// Destination registers (may be single values or memory pointers). - destinations: Vec, - /// Input registers (may be single values or memory pointers). - inputs: Vec, + /// Destination addresses (may be single values or memory pointers). + destinations: Vec, + /// Input addresses (may be single values or memory pointers). + inputs: Vec, }, Mov { - destination: RegisterIndex, - source: RegisterIndex, + destination: MemoryAddress, + source: MemoryAddress, }, Load { - destination: RegisterIndex, - source_pointer: RegisterIndex, + destination: MemoryAddress, + source_pointer: MemoryAddress, }, Store { - destination_pointer: RegisterIndex, - source: RegisterIndex, + destination_pointer: MemoryAddress, + source: MemoryAddress, }, BlackBox(BlackBoxOp), /// Used to denote execution failure Trap, - /// Stop execution - Stop, + /// Stop execution, returning data after the offset + Stop { + return_data_offset: usize, + return_data_size: usize, + }, } /// Binary fixed-length field expressions diff --git a/noir/acvm-repo/brillig/src/value.rs b/noir/acvm-repo/brillig/src/value.rs index 73a7d897eb7..5a532cbc1a7 100644 --- a/noir/acvm-repo/brillig/src/value.rs +++ b/noir/acvm-repo/brillig/src/value.rs @@ -37,8 +37,8 @@ impl Value { /// Panics: If `Value` cannot fit into a u64 or `Value` does //// not fit into a usize. pub fn to_usize(&self) -> usize { - usize::try_from(self.inner.try_to_u64().expect("register does not fit into u64")) - .expect("register does not fit into usize") + usize::try_from(self.inner.try_to_u64().expect("value does not fit into u64")) + .expect("value does not fit into usize") } } diff --git a/noir/acvm-repo/brillig_vm/src/black_box.rs b/noir/acvm-repo/brillig_vm/src/black_box.rs index 463038509e1..de20d194dde 100644 --- a/noir/acvm-repo/brillig_vm/src/black_box.rs +++ b/noir/acvm-repo/brillig_vm/src/black_box.rs @@ -5,23 +5,14 @@ use acvm_blackbox_solver::{ sha256, BlackBoxFunctionSolver, BlackBoxResolutionError, }; -use crate::{Memory, Registers}; +use crate::Memory; -fn read_heap_vector<'a>( - memory: &'a Memory, - registers: &Registers, - vector: &HeapVector, -) -> &'a [Value] { - memory - .read_slice(registers.get(vector.pointer).to_usize(), registers.get(vector.size).to_usize()) +fn read_heap_vector<'a>(memory: &'a Memory, vector: &HeapVector) -> &'a [Value] { + memory.read_slice(memory.read_ref(vector.pointer), memory.read(vector.size).to_usize()) } -fn read_heap_array<'a>( - memory: &'a Memory, - registers: &Registers, - array: &HeapArray, -) -> &'a [Value] { - memory.read_slice(registers.get(array.pointer).to_usize(), array.size) +fn read_heap_array<'a>(memory: &'a Memory, array: &HeapArray) -> &'a [Value] { + memory.read_slice(memory.read_ref(array.pointer), array.size) } /// Extracts the last byte of every value @@ -42,36 +33,35 @@ fn to_value_vec(input: &[u8]) -> Vec { pub(crate) fn evaluate_black_box( op: &BlackBoxOp, solver: &Solver, - registers: &mut Registers, memory: &mut Memory, ) -> Result<(), BlackBoxResolutionError> { match op { BlackBoxOp::Sha256 { message, output } => { - let message = to_u8_vec(read_heap_vector(memory, registers, message)); + let message = to_u8_vec(read_heap_vector(memory, message)); let bytes = sha256(message.as_slice())?; - memory.write_slice(registers.get(output.pointer).to_usize(), &to_value_vec(&bytes)); + memory.write_slice(memory.read_ref(output.pointer), &to_value_vec(&bytes)); Ok(()) } BlackBoxOp::Blake2s { message, output } => { - let message = to_u8_vec(read_heap_vector(memory, registers, message)); + let message = to_u8_vec(read_heap_vector(memory, message)); let bytes = blake2s(message.as_slice())?; - memory.write_slice(registers.get(output.pointer).to_usize(), &to_value_vec(&bytes)); + memory.write_slice(memory.read_ref(output.pointer), &to_value_vec(&bytes)); Ok(()) } BlackBoxOp::Blake3 { message, output } => { - let message = to_u8_vec(read_heap_vector(memory, registers, message)); + let message = to_u8_vec(read_heap_vector(memory, message)); let bytes = blake3(message.as_slice())?; - memory.write_slice(registers.get(output.pointer).to_usize(), &to_value_vec(&bytes)); + memory.write_slice(memory.read_ref(output.pointer), &to_value_vec(&bytes)); Ok(()) } BlackBoxOp::Keccak256 { message, output } => { - let message = to_u8_vec(read_heap_vector(memory, registers, message)); + let message = to_u8_vec(read_heap_vector(memory, message)); let bytes = keccak256(message.as_slice())?; - memory.write_slice(registers.get(output.pointer).to_usize(), &to_value_vec(&bytes)); + memory.write_slice(memory.read_ref(output.pointer), &to_value_vec(&bytes)); Ok(()) } BlackBoxOp::Keccakf1600 { message, output } => { - let state_vec: Vec = read_heap_vector(memory, registers, message) + let state_vec: Vec = read_heap_vector(memory, message) .iter() .map(|value| value.to_field().try_to_u64().unwrap()) .collect(); @@ -81,7 +71,7 @@ pub(crate) fn evaluate_black_box( let new_state: Vec = new_state.into_iter().map(|x| Value::from(x as usize)).collect(); - memory.write_slice(registers.get(output.pointer).to_usize(), &new_state); + memory.write_slice(memory.read_ref(output.pointer), &new_state); Ok(()) } BlackBoxOp::EcdsaSecp256k1 { @@ -89,42 +79,37 @@ pub(crate) fn evaluate_black_box( public_key_x, public_key_y, signature, - result: result_register, + result: result_address, } | BlackBoxOp::EcdsaSecp256r1 { hashed_msg, public_key_x, public_key_y, signature, - result: result_register, + result: result_address, } => { let bb_func = black_box_function_from_op(op); - let public_key_x: [u8; 32] = to_u8_vec(read_heap_array( - memory, - registers, - public_key_x, - )) - .try_into() - .map_err(|_| { - BlackBoxResolutionError::Failed(bb_func, "Invalid public key x length".to_string()) - })?; - let public_key_y: [u8; 32] = to_u8_vec(read_heap_array( - memory, - registers, - public_key_y, - )) - .try_into() - .map_err(|_| { - BlackBoxResolutionError::Failed(bb_func, "Invalid public key y length".to_string()) - })?; - let signature: [u8; 64] = to_u8_vec(read_heap_array(memory, registers, signature)) - .try_into() - .map_err(|_| { + let public_key_x: [u8; 32] = + to_u8_vec(read_heap_array(memory, public_key_x)).try_into().map_err(|_| { + BlackBoxResolutionError::Failed( + bb_func, + "Invalid public key x length".to_string(), + ) + })?; + let public_key_y: [u8; 32] = + to_u8_vec(read_heap_array(memory, public_key_y)).try_into().map_err(|_| { + BlackBoxResolutionError::Failed( + bb_func, + "Invalid public key y length".to_string(), + ) + })?; + let signature: [u8; 64] = + to_u8_vec(read_heap_array(memory, signature)).try_into().map_err(|_| { BlackBoxResolutionError::Failed(bb_func, "Invalid signature length".to_string()) })?; - let hashed_msg = to_u8_vec(read_heap_vector(memory, registers, hashed_msg)); + let hashed_msg = to_u8_vec(read_heap_vector(memory, hashed_msg)); let result = match op { BlackBoxOp::EcdsaSecp256k1 { .. } => { @@ -136,70 +121,71 @@ pub(crate) fn evaluate_black_box( _ => unreachable!("`BlackBoxOp` is guarded against being a non-ecdsa operation"), }; - registers.set(*result_register, result.into()); + memory.write(*result_address, result.into()); Ok(()) } BlackBoxOp::SchnorrVerify { public_key_x, public_key_y, message, signature, result } => { - let public_key_x = registers.get(*public_key_x).to_field(); - let public_key_y = registers.get(*public_key_y).to_field(); - let message: Vec = to_u8_vec(read_heap_vector(memory, registers, message)); - let signature: Vec = to_u8_vec(read_heap_vector(memory, registers, signature)); + let public_key_x = memory.read(*public_key_x).to_field(); + let public_key_y = memory.read(*public_key_y).to_field(); + let message: Vec = to_u8_vec(read_heap_vector(memory, message)); + let signature: Vec = to_u8_vec(read_heap_vector(memory, signature)); let verified = solver.schnorr_verify(&public_key_x, &public_key_y, &signature, &message)?; - registers.set(*result, verified.into()); + memory.write(*result, verified.into()); Ok(()) } BlackBoxOp::FixedBaseScalarMul { low, high, result } => { - let low = registers.get(*low).to_field(); - let high = registers.get(*high).to_field(); + let low = memory.read(*low).to_field(); + let high = memory.read(*high).to_field(); let (x, y) = solver.fixed_base_scalar_mul(&low, &high)?; - memory.write_slice(registers.get(result.pointer).to_usize(), &[x.into(), y.into()]); + memory.write_slice(memory.read_ref(result.pointer), &[x.into(), y.into()]); Ok(()) } BlackBoxOp::EmbeddedCurveAdd { input1_x, input1_y, input2_x, input2_y, result } => { - let input1_x = registers.get(*input1_x).to_field(); - let input1_y = registers.get(*input1_y).to_field(); - let input2_x = registers.get(*input2_x).to_field(); - let input2_y = registers.get(*input2_y).to_field(); + let input1_x = memory.read(*input1_x).to_field(); + let input1_y = memory.read(*input1_y).to_field(); + let input2_x = memory.read(*input2_x).to_field(); + let input2_y = memory.read(*input2_y).to_field(); let (x, y) = solver.ec_add(&input1_x, &input1_y, &input2_x, &input2_y)?; - memory.write_slice(registers.get(result.pointer).to_usize(), &[x.into(), y.into()]); - Ok(()) - } - BlackBoxOp::EmbeddedCurveDouble { input1_x, input1_y, result } => { - let input1_x = registers.get(*input1_x).to_field(); - let input1_y = registers.get(*input1_y).to_field(); - let (x, y) = solver.ec_double(&input1_x, &input1_y)?; - memory.write_slice(registers.get(result.pointer).to_usize(), &[x.into(), y.into()]); + memory.write_slice(memory.read_ref(result.pointer), &[x.into(), y.into()]); Ok(()) } BlackBoxOp::PedersenCommitment { inputs, domain_separator, output } => { let inputs: Vec = - read_heap_vector(memory, registers, inputs).iter().map(|x| x.to_field()).collect(); + read_heap_vector(memory, inputs).iter().map(|x| x.to_field()).collect(); let domain_separator: u32 = - registers.get(*domain_separator).to_u128().try_into().map_err(|_| { + memory.read(*domain_separator).to_u128().try_into().map_err(|_| { BlackBoxResolutionError::Failed( BlackBoxFunc::PedersenCommitment, "Invalid signature length".to_string(), ) })?; let (x, y) = solver.pedersen_commitment(&inputs, domain_separator)?; - memory.write_slice(registers.get(output.pointer).to_usize(), &[x.into(), y.into()]); + memory.write_slice(memory.read_ref(output.pointer), &[x.into(), y.into()]); Ok(()) } BlackBoxOp::PedersenHash { inputs, domain_separator, output } => { let inputs: Vec = - read_heap_vector(memory, registers, inputs).iter().map(|x| x.to_field()).collect(); + read_heap_vector(memory, inputs).iter().map(|x| x.to_field()).collect(); let domain_separator: u32 = - registers.get(*domain_separator).to_u128().try_into().map_err(|_| { + memory.read(*domain_separator).to_u128().try_into().map_err(|_| { BlackBoxResolutionError::Failed( BlackBoxFunc::PedersenCommitment, "Invalid signature length".to_string(), ) })?; let hash = solver.pedersen_hash(&inputs, domain_separator)?; - registers.set(*output, hash.into()); + memory.write(*output, hash.into()); Ok(()) } + BlackBoxOp::BigIntAdd { .. } => todo!(), + BlackBoxOp::BigIntNeg { .. } => todo!(), + BlackBoxOp::BigIntMul { .. } => todo!(), + BlackBoxOp::BigIntDiv { .. } => todo!(), + BlackBoxOp::BigIntFromLeBytes { .. } => todo!(), + BlackBoxOp::BigIntToLeBytes { .. } => todo!(), + BlackBoxOp::Poseidon2Permutation { .. } => todo!(), + BlackBoxOp::Sha256Compression { .. } => todo!(), } } @@ -217,17 +203,24 @@ fn black_box_function_from_op(op: &BlackBoxOp) -> BlackBoxFunc { BlackBoxOp::PedersenHash { .. } => BlackBoxFunc::PedersenHash, BlackBoxOp::FixedBaseScalarMul { .. } => BlackBoxFunc::FixedBaseScalarMul, BlackBoxOp::EmbeddedCurveAdd { .. } => BlackBoxFunc::EmbeddedCurveAdd, - BlackBoxOp::EmbeddedCurveDouble { .. } => BlackBoxFunc::EmbeddedCurveDouble, + BlackBoxOp::BigIntAdd { .. } => BlackBoxFunc::BigIntAdd, + BlackBoxOp::BigIntNeg { .. } => BlackBoxFunc::BigIntNeg, + BlackBoxOp::BigIntMul { .. } => BlackBoxFunc::BigIntMul, + BlackBoxOp::BigIntDiv { .. } => BlackBoxFunc::BigIntDiv, + BlackBoxOp::BigIntFromLeBytes { .. } => BlackBoxFunc::BigIntFromLeBytes, + BlackBoxOp::BigIntToLeBytes { .. } => BlackBoxFunc::BigIntToLeBytes, + BlackBoxOp::Poseidon2Permutation { .. } => BlackBoxFunc::Poseidon2Permutation, + BlackBoxOp::Sha256Compression { .. } => BlackBoxFunc::Sha256Compression, } } #[cfg(test)] mod test { - use acir::brillig::BlackBoxOp; + use acir::brillig::{BlackBoxOp, MemoryAddress}; use crate::{ black_box::{evaluate_black_box, to_u8_vec, to_value_vec}, - DummyBlackBoxSolver, HeapArray, HeapVector, Memory, Registers, Value, + DummyBlackBoxSolver, HeapArray, HeapVector, Memory, }; #[test] @@ -235,27 +228,22 @@ mod test { let message: Vec = b"hello world".to_vec(); let message_length = message.len(); - let mut memory = Memory::from(vec![]); - let message_pointer = 0; + let mut memory = Memory::default(); + let message_pointer = 3; let result_pointer = message_pointer + message_length; - memory.write_slice(message_pointer, to_value_vec(&message).as_slice()); - - let mut registers = Registers { - inner: vec![ - Value::from(message_pointer), - Value::from(message_length), - Value::from(result_pointer), - ], - }; + memory.write(MemoryAddress(0), message_pointer.into()); + memory.write(MemoryAddress(1), message_length.into()); + memory.write(MemoryAddress(2), result_pointer.into()); + memory.write_slice(MemoryAddress(message_pointer), to_value_vec(&message).as_slice()); let op = BlackBoxOp::Sha256 { message: HeapVector { pointer: 0.into(), size: 1.into() }, output: HeapArray { pointer: 2.into(), size: 32 }, }; - evaluate_black_box(&op, &DummyBlackBoxSolver, &mut registers, &mut memory).unwrap(); + evaluate_black_box(&op, &DummyBlackBoxSolver, &mut memory).unwrap(); - let result = memory.read_slice(result_pointer, 32); + let result = memory.read_slice(MemoryAddress(result_pointer), 32); assert_eq!( to_u8_vec(result), diff --git a/noir/acvm-repo/brillig_vm/src/lib.rs b/noir/acvm-repo/brillig_vm/src/lib.rs index df4c8135bce..0c90fcb1416 100644 --- a/noir/acvm-repo/brillig_vm/src/lib.rs +++ b/noir/acvm-repo/brillig_vm/src/lib.rs @@ -12,8 +12,8 @@ //! [acvm]: https://crates.io/crates/acvm use acir::brillig::{ - BinaryFieldOp, BinaryIntOp, ForeignCallParam, ForeignCallResult, HeapArray, HeapVector, Opcode, - RegisterIndex, RegisterOrMemory, Value, + BinaryFieldOp, BinaryIntOp, ForeignCallParam, ForeignCallResult, HeapArray, HeapVector, + MemoryAddress, Opcode, Value, ValueOrArray, }; use acir::FieldElement; // Re-export `brillig`. @@ -22,7 +22,6 @@ pub use acir::brillig; mod arithmetic; mod black_box; mod memory; -mod registers; use acvm_blackbox_solver::{BlackBoxFunctionSolver, BlackBoxResolutionError}; use arithmetic::{evaluate_binary_bigint_op, evaluate_binary_field_op}; @@ -30,14 +29,16 @@ use black_box::evaluate_black_box; pub use memory::Memory; use num_bigint::BigUint; -pub use registers::Registers; /// The error call stack contains the opcode indexes of the call stack at the time of failure, plus the index of the opcode that failed. pub type ErrorCallStack = Vec; #[derive(Debug, PartialEq, Eq, Clone)] pub enum VMStatus { - Finished, + Finished { + return_data_offset: usize, + return_data_size: usize, + }, InProgress, Failure { message: String, @@ -61,8 +62,8 @@ pub enum VMStatus { #[derive(Debug, PartialEq, Eq, Clone)] /// VM encapsulates the state of the Brillig VM during execution. pub struct VM<'a, B: BlackBoxFunctionSolver> { - /// Register storage - registers: Registers, + /// Calldata to the brillig function + calldata: Vec, /// Instruction pointer program_counter: usize, /// A counter maintained throughout a Brillig process that determines @@ -86,20 +87,19 @@ pub struct VM<'a, B: BlackBoxFunctionSolver> { impl<'a, B: BlackBoxFunctionSolver> VM<'a, B> { /// Constructs a new VM instance pub fn new( - inputs: Registers, - memory: Vec, + calldata: Vec, bytecode: &'a [Opcode], foreign_call_results: Vec, black_box_solver: &'a B, ) -> Self { Self { - registers: inputs, + calldata, program_counter: 0, foreign_call_counter: 0, foreign_call_results, bytecode, status: VMStatus::InProgress, - memory: memory.into(), + memory: Memory::default(), call_stack: Vec::new(), black_box_solver, } @@ -117,8 +117,8 @@ impl<'a, B: BlackBoxFunctionSolver> VM<'a, B> { } /// Sets the current status of the VM to Finished (completed execution). - fn finish(&mut self) -> VMStatus { - self.status(VMStatus::Finished) + fn finish(&mut self, return_data_offset: usize, return_data_size: usize) -> VMStatus { + self.status(VMStatus::Finished { return_data_offset, return_data_size }) } /// Sets the status of the VM to `ForeignCallWait`. @@ -154,26 +154,17 @@ impl<'a, B: BlackBoxFunctionSolver> VM<'a, B> { pub fn process_opcodes(&mut self) -> VMStatus { while !matches!( self.process_opcode(), - VMStatus::Finished | VMStatus::Failure { .. } | VMStatus::ForeignCallWait { .. } + VMStatus::Finished { .. } | VMStatus::Failure { .. } | VMStatus::ForeignCallWait { .. } ) {} self.status.clone() } - /// Returns all of the registers in the VM. - pub fn get_registers(&self) -> &Registers { - &self.registers - } - - pub fn set_register(&mut self, register_index: RegisterIndex, value: Value) { - self.registers.set(register_index, value); - } - - pub fn get_memory(&self) -> &Vec { + pub fn get_memory(&self) -> &[Value] { self.memory.values() } pub fn write_memory_at(&mut self, ptr: usize, value: Value) { - self.memory.write(ptr, value); + self.memory.write(MemoryAddress(ptr), value); } /// Process a single opcode and modify the program counter. @@ -196,22 +187,27 @@ impl<'a, B: BlackBoxFunctionSolver> VM<'a, B> { Opcode::JumpIf { condition, location: destination } => { // Check if condition is true // We use 0 to mean false and any other value to mean true - let condition_value = self.registers.get(*condition); + let condition_value = self.memory.read(*condition); if !condition_value.is_zero() { return self.set_program_counter(*destination); } self.increment_program_counter() } Opcode::JumpIfNot { condition, location: destination } => { - let condition_value = self.registers.get(*condition); + let condition_value = self.memory.read(*condition); if condition_value.is_zero() { return self.set_program_counter(*destination); } self.increment_program_counter() } + Opcode::CalldataCopy { destination_address, size, offset } => { + let values = &self.calldata[*offset..(*offset + size)]; + self.memory.write_slice(*destination_address, values); + self.increment_program_counter() + } Opcode::Return => { - if let Some(register) = self.call_stack.pop() { - self.set_program_counter(register.to_usize() + 1) + if let Some(return_location) = self.call_stack.pop() { + self.set_program_counter(return_location.to_usize() + 1) } else { self.fail("return opcode hit, but callstack already empty".to_string()) } @@ -227,7 +223,7 @@ impl<'a, B: BlackBoxFunctionSolver> VM<'a, B> { // but has the necessary results to proceed with execution. let resolved_inputs = inputs .iter() - .map(|input| self.get_register_value_or_memory_values(*input)) + .map(|input| self.get_memory_values(*input)) .collect::>(); return self.wait_for_foreign_call(function.clone(), resolved_inputs); } @@ -237,15 +233,15 @@ impl<'a, B: BlackBoxFunctionSolver> VM<'a, B> { let mut invalid_foreign_call_result = false; for (destination, output) in destinations.iter().zip(values) { match destination { - RegisterOrMemory::RegisterIndex(value_index) => match output { + ValueOrArray::MemoryAddress(value_index) => match output { ForeignCallParam::Single(value) => { - self.registers.set(*value_index, *value); + self.memory.write(*value_index, *value); } _ => unreachable!( "Function result size does not match brillig bytecode (expected 1 result)" ), }, - RegisterOrMemory::HeapArray(HeapArray { pointer: pointer_index, size }) => { + ValueOrArray::HeapArray(HeapArray { pointer: pointer_index, size }) => { match output { ForeignCallParam::Array(values) => { if values.len() != *size { @@ -253,7 +249,7 @@ impl<'a, B: BlackBoxFunctionSolver> VM<'a, B> { break; } // Convert the destination pointer to a usize - let destination = self.registers.get(*pointer_index).to_usize(); + let destination = self.memory.read_ref(*pointer_index); // Write to our destination memory self.memory.write_slice(destination, values); } @@ -262,13 +258,13 @@ impl<'a, B: BlackBoxFunctionSolver> VM<'a, B> { } } } - RegisterOrMemory::HeapVector(HeapVector { pointer: pointer_index, size: size_index }) => { + ValueOrArray::HeapVector(HeapVector { pointer: pointer_index, size: size_index }) => { match output { ForeignCallParam::Array(values) => { - // Set our size in the size register - self.registers.set(*size_index, Value::from(values.len())); + // Set our size in the size address + self.memory.write(*size_index, Value::from(values.len())); // Convert the destination pointer to a usize - let destination = self.registers.get(*pointer_index).to_usize(); + let destination = self.memory.read_ref(*pointer_index); // Write to our destination memory self.memory.write_slice(destination, values); } @@ -291,26 +287,28 @@ impl<'a, B: BlackBoxFunctionSolver> VM<'a, B> { self.foreign_call_counter += 1; self.increment_program_counter() } - Opcode::Mov { destination: destination_register, source: source_register } => { - let source_value = self.registers.get(*source_register); - self.registers.set(*destination_register, source_value); + Opcode::Mov { destination: destination_address, source: source_address } => { + let source_value = self.memory.read(*source_address); + self.memory.write(*destination_address, source_value); self.increment_program_counter() } Opcode::Trap => self.fail("explicit trap hit in brillig".to_string()), - Opcode::Stop => self.finish(), - Opcode::Load { destination: destination_register, source_pointer } => { - // Convert our source_pointer to a usize - let source = self.registers.get(*source_pointer); + Opcode::Stop { return_data_offset, return_data_size } => { + self.finish(*return_data_offset, *return_data_size) + } + Opcode::Load { destination: destination_address, source_pointer } => { + // Convert our source_pointer to an address + let source = self.memory.read_ref(*source_pointer); // Use our usize source index to lookup the value in memory - let value = &self.memory.read(source.to_usize()); - self.registers.set(*destination_register, *value); + let value = &self.memory.read(source); + self.memory.write(*destination_address, *value); self.increment_program_counter() } - Opcode::Store { destination_pointer, source: source_register } => { - // Convert our destination_pointer to a usize - let destination = self.registers.get(*destination_pointer).to_usize(); + Opcode::Store { destination_pointer, source: source_address } => { + // Convert our destination_pointer to an address + let destination = self.memory.read_ref(*destination_pointer); // Use our usize destination index to set the value in memory - self.memory.write(destination, self.registers.get(*source_register)); + self.memory.write(destination, self.memory.read(*source_address)); self.increment_program_counter() } Opcode::Call { location } => { @@ -319,16 +317,11 @@ impl<'a, B: BlackBoxFunctionSolver> VM<'a, B> { self.set_program_counter(*location) } Opcode::Const { destination, value } => { - self.registers.set(*destination, *value); + self.memory.write(*destination, *value); self.increment_program_counter() } Opcode::BlackBox(black_box_op) => { - match evaluate_black_box( - black_box_op, - self.black_box_solver, - &mut self.registers, - &mut self.memory, - ) { + match evaluate_black_box(black_box_op, self.black_box_solver, &mut self.memory) { Ok(()) => self.increment_program_counter(), Err(e) => self.fail(e.to_string()), } @@ -353,25 +346,22 @@ impl<'a, B: BlackBoxFunctionSolver> VM<'a, B> { assert!(self.program_counter < self.bytecode.len()); self.program_counter = value; if self.program_counter >= self.bytecode.len() { - self.status = VMStatus::Finished; + self.status = VMStatus::Finished { return_data_offset: 0, return_data_size: 0 }; } self.status.clone() } - fn get_register_value_or_memory_values(&self, input: RegisterOrMemory) -> ForeignCallParam { + fn get_memory_values(&self, input: ValueOrArray) -> ForeignCallParam { match input { - RegisterOrMemory::RegisterIndex(value_index) => self.registers.get(value_index).into(), - RegisterOrMemory::HeapArray(HeapArray { pointer: pointer_index, size }) => { - let start = self.registers.get(pointer_index); - self.memory.read_slice(start.to_usize(), size).to_vec().into() + ValueOrArray::MemoryAddress(value_index) => self.memory.read(value_index).into(), + ValueOrArray::HeapArray(HeapArray { pointer: pointer_index, size }) => { + let start = self.memory.read_ref(pointer_index); + self.memory.read_slice(start, size).to_vec().into() } - RegisterOrMemory::HeapVector(HeapVector { - pointer: pointer_index, - size: size_index, - }) => { - let start = self.registers.get(pointer_index); - let size = self.registers.get(size_index); - self.memory.read_slice(start.to_usize(), size.to_usize()).to_vec().into() + ValueOrArray::HeapVector(HeapVector { pointer: pointer_index, size: size_index }) => { + let start = self.memory.read_ref(pointer_index); + let size = self.memory.read(size_index); + self.memory.read_slice(start, size.to_usize()).to_vec().into() } } } @@ -381,17 +371,17 @@ impl<'a, B: BlackBoxFunctionSolver> VM<'a, B> { fn process_binary_field_op( &mut self, op: BinaryFieldOp, - lhs: RegisterIndex, - rhs: RegisterIndex, - result: RegisterIndex, + lhs: MemoryAddress, + rhs: MemoryAddress, + result: MemoryAddress, ) { - let lhs_value = self.registers.get(lhs); - let rhs_value = self.registers.get(rhs); + let lhs_value = self.memory.read(lhs); + let rhs_value = self.memory.read(rhs); let result_value = evaluate_binary_field_op(&op, lhs_value.to_field(), rhs_value.to_field()); - self.registers.set(result, result_value.into()); + self.memory.write(result, result_value.into()); } /// Process a binary operation. @@ -400,20 +390,20 @@ impl<'a, B: BlackBoxFunctionSolver> VM<'a, B> { &mut self, op: BinaryIntOp, bit_size: u32, - lhs: RegisterIndex, - rhs: RegisterIndex, - result: RegisterIndex, + lhs: MemoryAddress, + rhs: MemoryAddress, + result: MemoryAddress, ) -> Result<(), String> { - let lhs_value = self.registers.get(lhs); - let rhs_value = self.registers.get(rhs); + let lhs_value = self.memory.read(lhs); + let rhs_value = self.memory.read(rhs); // Convert to big integers let lhs_big = BigUint::from_bytes_be(&lhs_value.to_field().to_be_bytes()); let rhs_big = BigUint::from_bytes_be(&rhs_value.to_field().to_be_bytes()); let result_value = evaluate_binary_bigint_op(&op, lhs_big, rhs_big, bit_size)?; // Convert back to field element - self.registers - .set(result, FieldElement::from_be_bytes_reduce(&result_value.to_bytes_be()).into()); + self.memory + .write(result, FieldElement::from_be_bytes_reduce(&result_value.to_bytes_be()).into()); Ok(()) } } @@ -460,13 +450,6 @@ impl BlackBoxFunctionSolver for DummyBlackBoxSolver { ) -> Result<(FieldElement, FieldElement), BlackBoxResolutionError> { Ok((5_u128.into(), 6_u128.into())) } - fn ec_double( - &self, - _input1_x: &FieldElement, - _input1_y: &FieldElement, - ) -> Result<(FieldElement, FieldElement), BlackBoxResolutionError> { - Ok((7_u128.into(), 8_u128.into())) - } } #[cfg(test)] @@ -475,111 +458,123 @@ mod tests { #[test] fn add_single_step_smoke() { - // Load values into registers and initialize the registers that - // will be used during bytecode processing - let input_registers = - Registers::load(vec![Value::from(1u128), Value::from(2u128), Value::from(0u128)]); - - // Add opcode to add the value in register `0` and `1` - // and place the output in register `2` - let opcode = Opcode::BinaryIntOp { - op: BinaryIntOp::Add, - bit_size: 2, - lhs: RegisterIndex::from(0), - rhs: RegisterIndex::from(1), - destination: RegisterIndex::from(2), + let calldata = vec![Value::from(27u128)]; + + // Add opcode to add the value in address `0` and `1` + // and place the output in address `2` + let calldata_copy = Opcode::CalldataCopy { + destination_address: MemoryAddress::from(0), + size: 1, + offset: 0, }; // Start VM - let opcodes = [opcode]; - let mut vm = VM::new(input_registers, vec![], &opcodes, vec![], &DummyBlackBoxSolver); + let opcodes = [calldata_copy]; + let mut vm = VM::new(calldata, &opcodes, vec![], &DummyBlackBoxSolver); // Process a single VM opcode // // After processing a single opcode, we should have // the vm status as finished since there is only one opcode let status = vm.process_opcode(); - assert_eq!(status, VMStatus::Finished); + assert_eq!(status, VMStatus::Finished { return_data_offset: 0, return_data_size: 0 }); - // The register at index `2` should have the value of 3 since we had an + // The address at index `2` should have the value of 3 since we had an // add opcode - let VM { registers, .. } = vm; - let output_value = registers.get(RegisterIndex::from(2)); + let VM { memory, .. } = vm; + let output_value = memory.read(MemoryAddress::from(0)); - assert_eq!(output_value, Value::from(3u128)); + assert_eq!(output_value, Value::from(27u128)); } #[test] fn jmpif_opcode() { - let mut registers = vec![]; + let mut calldata = vec![]; let mut opcodes = vec![]; let lhs = { - registers.push(Value::from(2u128)); - RegisterIndex::from(registers.len() - 1) + calldata.push(Value::from(2u128)); + MemoryAddress::from(calldata.len() - 1) }; let rhs = { - registers.push(Value::from(2u128)); - RegisterIndex::from(registers.len() - 1) + calldata.push(Value::from(2u128)); + MemoryAddress::from(calldata.len() - 1) }; - let destination = { - registers.push(Value::from(0u128)); - RegisterIndex::from(registers.len() - 1) - }; + let destination = MemoryAddress::from(calldata.len()); + opcodes.push(Opcode::CalldataCopy { + destination_address: MemoryAddress::from(0), + size: 2, + offset: 0, + }); let equal_cmp_opcode = Opcode::BinaryIntOp { op: BinaryIntOp::Equals, bit_size: 1, lhs, rhs, destination }; opcodes.push(equal_cmp_opcode); - opcodes.push(Opcode::Jump { location: 2 }); - opcodes.push(Opcode::JumpIf { condition: RegisterIndex::from(2), location: 3 }); + opcodes.push(Opcode::Jump { location: 3 }); + opcodes.push(Opcode::JumpIf { condition: MemoryAddress::from(2), location: 4 }); + + let mut vm = VM::new(calldata, &opcodes, vec![], &DummyBlackBoxSolver); - let mut vm = - VM::new(Registers::load(registers), vec![], &opcodes, vec![], &DummyBlackBoxSolver); + let status = vm.process_opcode(); + assert_eq!(status, VMStatus::InProgress); let status = vm.process_opcode(); assert_eq!(status, VMStatus::InProgress); - let output_cmp_value = vm.registers.get(RegisterIndex::from(2)); + let output_cmp_value = vm.memory.read(MemoryAddress::from(2)); assert_eq!(output_cmp_value, Value::from(true)); let status = vm.process_opcode(); assert_eq!(status, VMStatus::InProgress); let status = vm.process_opcode(); - assert_eq!(status, VMStatus::Finished); + assert_eq!(status, VMStatus::Finished { return_data_offset: 0, return_data_size: 0 }); } #[test] fn jmpifnot_opcode() { - let input_registers = - Registers::load(vec![Value::from(1u128), Value::from(2u128), Value::from(0u128)]); + let calldata = vec![Value::from(1u128), Value::from(2u128)]; + + let calldata_copy = Opcode::CalldataCopy { + destination_address: MemoryAddress::from(0), + size: 2, + offset: 0, + }; + + let jump_opcode = Opcode::Jump { location: 3 }; let trap_opcode = Opcode::Trap; let not_equal_cmp_opcode = Opcode::BinaryFieldOp { op: BinaryFieldOp::Equals, - lhs: RegisterIndex::from(0), - rhs: RegisterIndex::from(1), - destination: RegisterIndex::from(2), + lhs: MemoryAddress::from(0), + rhs: MemoryAddress::from(1), + destination: MemoryAddress::from(2), }; - let jump_opcode = Opcode::Jump { location: 2 }; - let jump_if_not_opcode = - Opcode::JumpIfNot { condition: RegisterIndex::from(2), location: 1 }; + Opcode::JumpIfNot { condition: MemoryAddress::from(2), location: 2 }; let add_opcode = Opcode::BinaryFieldOp { op: BinaryFieldOp::Add, - lhs: RegisterIndex::from(0), - rhs: RegisterIndex::from(1), - destination: RegisterIndex::from(2), + lhs: MemoryAddress::from(0), + rhs: MemoryAddress::from(1), + destination: MemoryAddress::from(2), }; - let opcodes = - [jump_opcode, trap_opcode, not_equal_cmp_opcode, jump_if_not_opcode, add_opcode]; - let mut vm = VM::new(input_registers, vec![], &opcodes, vec![], &DummyBlackBoxSolver); + let opcodes = [ + calldata_copy, + jump_opcode, + trap_opcode, + not_equal_cmp_opcode, + jump_if_not_opcode, + add_opcode, + ]; + let mut vm = VM::new(calldata, &opcodes, vec![], &DummyBlackBoxSolver); + let status = vm.process_opcode(); + assert_eq!(status, VMStatus::InProgress); let status = vm.process_opcode(); assert_eq!(status, VMStatus::InProgress); @@ -587,7 +582,7 @@ mod tests { let status = vm.process_opcode(); assert_eq!(status, VMStatus::InProgress); - let output_cmp_value = vm.registers.get(RegisterIndex::from(2)); + let output_cmp_value = vm.memory.read(MemoryAddress::from(2)); assert_eq!(output_cmp_value, Value::from(false)); let status = vm.process_opcode(); @@ -598,107 +593,130 @@ mod tests { status, VMStatus::Failure { message: "explicit trap hit in brillig".to_string(), - call_stack: vec![1] + call_stack: vec![2] } ); - // The register at index `2` should have not changed as we jumped over the add opcode - let VM { registers, .. } = vm; - let output_value = registers.get(RegisterIndex::from(2)); + // The address at index `2` should have not changed as we jumped over the add opcode + let VM { memory, .. } = vm; + let output_value = memory.read(MemoryAddress::from(2)); assert_eq!(output_value, Value::from(false)); } #[test] fn mov_opcode() { - let input_registers = - Registers::load(vec![Value::from(1u128), Value::from(2u128), Value::from(3u128)]); + let calldata = vec![Value::from(1u128), Value::from(2u128), Value::from(3u128)]; + + let calldata_copy = Opcode::CalldataCopy { + destination_address: MemoryAddress::from(0), + size: 3, + offset: 0, + }; let mov_opcode = - Opcode::Mov { destination: RegisterIndex::from(2), source: RegisterIndex::from(0) }; + Opcode::Mov { destination: MemoryAddress::from(2), source: MemoryAddress::from(0) }; - let opcodes = &[mov_opcode]; - let mut vm = VM::new(input_registers, vec![], opcodes, vec![], &DummyBlackBoxSolver); + let opcodes = &[calldata_copy, mov_opcode]; + let mut vm = VM::new(calldata, opcodes, vec![], &DummyBlackBoxSolver); let status = vm.process_opcode(); - assert_eq!(status, VMStatus::Finished); + assert_eq!(status, VMStatus::InProgress); + + let status = vm.process_opcode(); + assert_eq!(status, VMStatus::Finished { return_data_offset: 0, return_data_size: 0 }); - let VM { registers, .. } = vm; + let VM { memory, .. } = vm; - let destination_value = registers.get(RegisterIndex::from(2)); + let destination_value = memory.read(MemoryAddress::from(2)); assert_eq!(destination_value, Value::from(1u128)); - let source_value = registers.get(RegisterIndex::from(0)); + let source_value = memory.read(MemoryAddress::from(0)); assert_eq!(source_value, Value::from(1u128)); } #[test] fn cmp_binary_ops() { let bit_size = 32; - let input_registers = Registers::load(vec![ + let calldata = vec![ Value::from(2u128), Value::from(2u128), Value::from(0u128), Value::from(5u128), Value::from(6u128), - ]); + ]; + + let calldata_copy = Opcode::CalldataCopy { + destination_address: MemoryAddress::from(0), + size: 5, + offset: 0, + }; let equal_opcode = Opcode::BinaryIntOp { bit_size, op: BinaryIntOp::Equals, - lhs: RegisterIndex::from(0), - rhs: RegisterIndex::from(1), - destination: RegisterIndex::from(2), + lhs: MemoryAddress::from(0), + rhs: MemoryAddress::from(1), + destination: MemoryAddress::from(2), }; let not_equal_opcode = Opcode::BinaryIntOp { bit_size, op: BinaryIntOp::Equals, - lhs: RegisterIndex::from(0), - rhs: RegisterIndex::from(3), - destination: RegisterIndex::from(2), + lhs: MemoryAddress::from(0), + rhs: MemoryAddress::from(3), + destination: MemoryAddress::from(2), }; let less_than_opcode = Opcode::BinaryIntOp { bit_size, op: BinaryIntOp::LessThan, - lhs: RegisterIndex::from(3), - rhs: RegisterIndex::from(4), - destination: RegisterIndex::from(2), + lhs: MemoryAddress::from(3), + rhs: MemoryAddress::from(4), + destination: MemoryAddress::from(2), }; let less_than_equal_opcode = Opcode::BinaryIntOp { bit_size, op: BinaryIntOp::LessThanEquals, - lhs: RegisterIndex::from(3), - rhs: RegisterIndex::from(4), - destination: RegisterIndex::from(2), + lhs: MemoryAddress::from(3), + rhs: MemoryAddress::from(4), + destination: MemoryAddress::from(2), }; - let opcodes = [equal_opcode, not_equal_opcode, less_than_opcode, less_than_equal_opcode]; - let mut vm = VM::new(input_registers, vec![], &opcodes, vec![], &DummyBlackBoxSolver); + let opcodes = [ + calldata_copy, + equal_opcode, + not_equal_opcode, + less_than_opcode, + less_than_equal_opcode, + ]; + let mut vm = VM::new(calldata, &opcodes, vec![], &DummyBlackBoxSolver); let status = vm.process_opcode(); assert_eq!(status, VMStatus::InProgress); - let output_eq_value = vm.registers.get(RegisterIndex::from(2)); + let status = vm.process_opcode(); + assert_eq!(status, VMStatus::InProgress); + + let output_eq_value = vm.memory.read(MemoryAddress::from(2)); assert_eq!(output_eq_value, Value::from(true)); let status = vm.process_opcode(); assert_eq!(status, VMStatus::InProgress); - let output_neq_value = vm.registers.get(RegisterIndex::from(2)); + let output_neq_value = vm.memory.read(MemoryAddress::from(2)); assert_eq!(output_neq_value, Value::from(false)); let status = vm.process_opcode(); assert_eq!(status, VMStatus::InProgress); - let lt_value = vm.registers.get(RegisterIndex::from(2)); + let lt_value = vm.memory.read(MemoryAddress::from(2)); assert_eq!(lt_value, Value::from(true)); let status = vm.process_opcode(); - assert_eq!(status, VMStatus::Finished); + assert_eq!(status, VMStatus::Finished { return_data_offset: 0, return_data_size: 0 }); - let lte_value = vm.registers.get(RegisterIndex::from(2)); + let lte_value = vm.memory.read(MemoryAddress::from(2)); assert_eq!(lte_value, Value::from(true)); } #[test] @@ -710,20 +728,24 @@ mod tests { /// memory[i] = i as Value; /// i += 1; /// } - fn brillig_write_memory(memory: Vec) -> Vec { + fn brillig_write_memory(item_count: usize) -> Vec { let bit_size = 32; - let r_i = RegisterIndex::from(0); - let r_len = RegisterIndex::from(1); - let r_tmp = RegisterIndex::from(2); + let r_i = MemoryAddress::from(0); + let r_len = MemoryAddress::from(1); + let r_tmp = MemoryAddress::from(2); + let r_pointer = MemoryAddress::from(3); + let start = [ // i = 0 Opcode::Const { destination: r_i, value: 0u128.into() }, // len = memory.len() (approximation) - Opcode::Const { destination: r_len, value: Value::from(memory.len() as u128) }, + Opcode::Const { destination: r_len, value: Value::from(item_count as u128) }, + // pointer = free_memory_ptr + Opcode::Const { destination: r_pointer, value: 4u128.into() }, ]; let loop_body = [ // *i = i - Opcode::Store { destination_pointer: r_i, source: r_i }, + Opcode::Store { destination_pointer: r_pointer, source: r_i }, // tmp = 1 Opcode::Const { destination: r_tmp, value: 1u128.into() }, // i = i + 1 (tmp) @@ -734,6 +756,14 @@ mod tests { rhs: r_tmp, bit_size, }, + // pointer = pointer + 1 + Opcode::BinaryIntOp { + destination: r_pointer, + lhs: r_pointer, + op: BinaryIntOp::Add, + rhs: r_tmp, + bit_size, + }, // tmp = i < len Opcode::BinaryIntOp { destination: r_tmp, @@ -747,11 +777,11 @@ mod tests { ]; let opcodes = [&start[..], &loop_body[..]].concat(); - let vm = brillig_execute_and_get_vm(memory, &opcodes); - vm.get_memory().clone() + let vm = brillig_execute_and_get_vm(vec![], &opcodes); + vm.get_memory()[4..].to_vec() } - let memory = brillig_write_memory(vec![Value::from(0u128); 5]); + let memory = brillig_write_memory(5); let expected = vec![ Value::from(0u128), Value::from(1u128), @@ -761,7 +791,7 @@ mod tests { ]; assert_eq!(memory, expected); - let memory = brillig_write_memory(vec![Value::from(0u128); 1024]); + let memory = brillig_write_memory(1024); let expected: Vec = (0..1024).map(|i| Value::from(i as u128)).collect(); assert_eq!(memory, expected); } @@ -778,10 +808,12 @@ mod tests { /// } fn brillig_sum_memory(memory: Vec) -> Value { let bit_size = 32; - let r_i = RegisterIndex::from(0); - let r_len = RegisterIndex::from(1); - let r_sum = RegisterIndex::from(2); - let r_tmp = RegisterIndex::from(3); + let r_i = MemoryAddress::from(0); + let r_len = MemoryAddress::from(1); + let r_sum = MemoryAddress::from(2); + let r_tmp = MemoryAddress::from(3); + let r_pointer = MemoryAddress::from(4); + let start = [ // sum = 0 Opcode::Const { destination: r_sum, value: 0u128.into() }, @@ -789,10 +821,17 @@ mod tests { Opcode::Const { destination: r_i, value: 0u128.into() }, // len = array.len() (approximation) Opcode::Const { destination: r_len, value: Value::from(memory.len() as u128) }, + // pointer = array_ptr + Opcode::Const { destination: r_pointer, value: 5u128.into() }, + Opcode::CalldataCopy { + destination_address: MemoryAddress(5), + size: memory.len(), + offset: 0, + }, ]; let loop_body = [ // tmp = *i - Opcode::Load { destination: r_tmp, source_pointer: r_i }, + Opcode::Load { destination: r_tmp, source_pointer: r_pointer }, // sum = sum + tmp Opcode::BinaryIntOp { destination: r_sum, @@ -811,6 +850,14 @@ mod tests { rhs: r_tmp, bit_size, }, + // pointer = pointer + 1 + Opcode::BinaryIntOp { + destination: r_pointer, + lhs: r_pointer, + op: BinaryIntOp::Add, + rhs: r_tmp, + bit_size, + }, // tmp = i < len Opcode::BinaryIntOp { destination: r_tmp, @@ -825,7 +872,7 @@ mod tests { let opcodes = [&start[..], &loop_body[..]].concat(); let vm = brillig_execute_and_get_vm(memory, &opcodes); - vm.registers.get(r_sum) + vm.memory.read(r_sum) } assert_eq!( @@ -851,21 +898,24 @@ mod tests { /// memory[i as usize] = i as Value; /// recursive_write(memory, i + 1, len); /// } - /// Note we represent a 100% in-register optimized form in brillig - fn brillig_recursive_write_memory(memory: Vec) -> Vec { + /// Note we represent a 100% in-stack optimized form in brillig + fn brillig_recursive_write_memory(size: usize) -> Vec { let bit_size = 32; - let r_i = RegisterIndex::from(0); - let r_len = RegisterIndex::from(1); - let r_tmp = RegisterIndex::from(2); + let r_i = MemoryAddress::from(0); + let r_len = MemoryAddress::from(1); + let r_tmp = MemoryAddress::from(2); + let r_pointer = MemoryAddress::from(3); let start = [ // i = 0 Opcode::Const { destination: r_i, value: 0u128.into() }, - // len = memory.len() (approximation) - Opcode::Const { destination: r_len, value: Value::from(memory.len() as u128) }, + // len = size + Opcode::Const { destination: r_len, value: size.into() }, + // pointer = free_memory_ptr + Opcode::Const { destination: r_pointer, value: 4u128.into() }, // call recursive_fn Opcode::Call { - location: 4, // Call after 'start' + location: 5, // Call after 'start' }, // end program by jumping to end Opcode::Jump { location: 100 }, @@ -883,10 +933,10 @@ mod tests { // if !tmp, goto end Opcode::JumpIf { condition: r_tmp, - location: start.len() + 6, // 7 ops in recursive_fn, go to 'Return' + location: start.len() + 7, // 8 ops in recursive_fn, go to 'Return' }, // *i = i - Opcode::Store { destination_pointer: r_i, source: r_i }, + Opcode::Store { destination_pointer: r_pointer, source: r_i }, // tmp = 1 Opcode::Const { destination: r_tmp, value: 1u128.into() }, // i = i + 1 (tmp) @@ -897,17 +947,25 @@ mod tests { rhs: r_tmp, bit_size, }, + // pointer = pointer + 1 + Opcode::BinaryIntOp { + destination: r_pointer, + lhs: r_pointer, + op: BinaryIntOp::Add, + rhs: r_tmp, + bit_size, + }, // call recursive_fn Opcode::Call { location: start.len() }, Opcode::Return {}, ]; let opcodes = [&start[..], &recursive_fn[..]].concat(); - let vm = brillig_execute_and_get_vm(memory, &opcodes); - vm.get_memory().clone() + let vm = brillig_execute_and_get_vm(vec![], &opcodes); + vm.get_memory()[4..].to_vec() } - let memory = brillig_recursive_write_memory(vec![Value::from(0u128); 5]); + let memory = brillig_recursive_write_memory(5); let expected = vec![ Value::from(0u128), Value::from(1u128), @@ -917,20 +975,17 @@ mod tests { ]; assert_eq!(memory, expected); - let memory = brillig_recursive_write_memory(vec![Value::from(0u128); 1024]); + let memory = brillig_recursive_write_memory(1024); let expected: Vec = (0..1024).map(|i| Value::from(i as u128)).collect(); assert_eq!(memory, expected); } - fn empty_registers() -> Registers { - Registers::load(vec![Value::from(0u128); 16]) - } /// Helper to execute brillig code fn brillig_execute_and_get_vm( - memory: Vec, + calldata: Vec, opcodes: &[Opcode], ) -> VM<'_, DummyBlackBoxSolver> { - let mut vm = VM::new(empty_registers(), memory, opcodes, vec![], &DummyBlackBoxSolver); + let mut vm = VM::new(calldata, opcodes, vec![], &DummyBlackBoxSolver); brillig_execute(&mut vm); assert_eq!(vm.call_stack, vec![]); vm @@ -939,7 +994,7 @@ mod tests { fn brillig_execute(vm: &mut VM) { loop { let status = vm.process_opcode(); - if matches!(status, VMStatus::Finished | VMStatus::ForeignCallWait { .. }) { + if matches!(status, VMStatus::Finished { .. } | VMStatus::ForeignCallWait { .. }) { break; } assert_eq!(status, VMStatus::InProgress); @@ -947,18 +1002,18 @@ mod tests { } #[test] - fn foreign_call_opcode_register_result() { - let r_input = RegisterIndex::from(0); - let r_result = RegisterIndex::from(1); + fn foreign_call_opcode_simple_result() { + let r_input = MemoryAddress::from(0); + let r_result = MemoryAddress::from(1); let double_program = vec![ - // Load input register with value 5 + // Load input address with value 5 Opcode::Const { destination: r_input, value: Value::from(5u128) }, - // Call foreign function "double" with the input register + // Call foreign function "double" with the input address Opcode::ForeignCall { function: "double".into(), - destinations: vec![RegisterOrMemory::RegisterIndex(r_result)], - inputs: vec![RegisterOrMemory::RegisterIndex(r_input)], + destinations: vec![ValueOrArray::MemoryAddress(r_result)], + inputs: vec![ValueOrArray::MemoryAddress(r_input)], }, ]; @@ -982,19 +1037,20 @@ mod tests { brillig_execute(&mut vm); // Check that VM finished once resumed - assert_eq!(vm.status, VMStatus::Finished); + assert_eq!(vm.status, VMStatus::Finished { return_data_offset: 0, return_data_size: 0 }); - // Check result register - let result_value = vm.registers.get(r_result); + // Check result address + let result_value = vm.memory.read(r_result); assert_eq!(result_value, Value::from(10u128)); // Ensure the foreign call counter has been incremented assert_eq!(vm.foreign_call_counter, 1); } + #[test] fn foreign_call_opcode_memory_result() { - let r_input = RegisterIndex::from(0); - let r_output = RegisterIndex::from(1); + let r_input = MemoryAddress::from(0); + let r_output = MemoryAddress::from(1); // Define a simple 2x2 matrix in memory let initial_matrix = @@ -1005,18 +1061,23 @@ mod tests { vec![Value::from(1u128), Value::from(3u128), Value::from(2u128), Value::from(4u128)]; let invert_program = vec![ + Opcode::CalldataCopy { + destination_address: MemoryAddress::from(2), + size: initial_matrix.len(), + offset: 0, + }, // input = 0 - Opcode::Const { destination: r_input, value: Value::from(0u128) }, + Opcode::Const { destination: r_input, value: 2_usize.into() }, // output = 0 - Opcode::Const { destination: r_output, value: Value::from(0u128) }, + Opcode::Const { destination: r_output, value: 2_usize.into() }, // *output = matrix_2x2_transpose(*input) Opcode::ForeignCall { function: "matrix_2x2_transpose".into(), - destinations: vec![RegisterOrMemory::HeapArray(HeapArray { + destinations: vec![ValueOrArray::HeapArray(HeapArray { pointer: r_output, size: initial_matrix.len(), })], - inputs: vec![RegisterOrMemory::HeapArray(HeapArray { + inputs: vec![ValueOrArray::HeapArray(HeapArray { pointer: r_input, size: initial_matrix.len(), })], @@ -1041,10 +1102,10 @@ mod tests { brillig_execute(&mut vm); // Check that VM finished once resumed - assert_eq!(vm.status, VMStatus::Finished); + assert_eq!(vm.status, VMStatus::Finished { return_data_offset: 0, return_data_size: 0 }); // Check result in memory - let result_values = vm.memory.read_slice(0, 4).to_vec(); + let result_values = vm.memory.read_slice(MemoryAddress(2), 4).to_vec(); assert_eq!(result_values, expected_result); // Ensure the foreign call counter has been incremented @@ -1054,11 +1115,11 @@ mod tests { /// Calling a simple foreign call function that takes any string input, concatenates it with itself, and reverses the concatenation #[test] fn foreign_call_opcode_vector_input_and_output() { - let r_input_pointer = RegisterIndex::from(0); - let r_input_size = RegisterIndex::from(1); + let r_input_pointer = MemoryAddress::from(0); + let r_input_size = MemoryAddress::from(1); // We need to pass a location of appropriate size - let r_output_pointer = RegisterIndex::from(2); - let r_output_size = RegisterIndex::from(3); + let r_output_pointer = MemoryAddress::from(2); + let r_output_size = MemoryAddress::from(3); // Our first string to use the identity function with let input_string = @@ -1071,12 +1132,20 @@ mod tests { // First call: let string_double_program = vec![ - // input_pointer = 0 - Opcode::Const { destination: r_input_pointer, value: Value::from(0u128) }, + Opcode::CalldataCopy { + destination_address: MemoryAddress(4), + size: input_string.len(), + offset: 0, + }, + // input_pointer = 4 + Opcode::Const { destination: r_input_pointer, value: Value::from(4u128) }, // input_size = input_string.len() (constant here) Opcode::Const { destination: r_input_size, value: Value::from(input_string.len()) }, - // output_pointer = 0 + input_size = input_size - Opcode::Const { destination: r_output_pointer, value: Value::from(input_string.len()) }, + // output_pointer = 4 + input_size + Opcode::Const { + destination: r_output_pointer, + value: Value::from(4 + input_string.len()), + }, // output_size = input_size * 2 Opcode::Const { destination: r_output_size, @@ -1085,11 +1154,11 @@ mod tests { // output_pointer[0..output_size] = string_double(input_pointer[0...input_size]) Opcode::ForeignCall { function: "string_double".into(), - destinations: vec![RegisterOrMemory::HeapVector(HeapVector { + destinations: vec![ValueOrArray::HeapVector(HeapVector { pointer: r_output_pointer, size: r_output_size, })], - inputs: vec![RegisterOrMemory::HeapVector(HeapVector { + inputs: vec![ValueOrArray::HeapVector(HeapVector { pointer: r_input_pointer, size: r_input_size, })], @@ -1116,10 +1185,13 @@ mod tests { brillig_execute(&mut vm); // Check that VM finished once resumed - assert_eq!(vm.status, VMStatus::Finished); + assert_eq!(vm.status, VMStatus::Finished { return_data_offset: 0, return_data_size: 0 }); // Check result in memory - let result_values = vm.memory.read_slice(input_string.len(), output_string.len()).to_vec(); + let result_values = vm + .memory + .read_slice(MemoryAddress(4 + input_string.len()), output_string.len()) + .to_vec(); assert_eq!(result_values, output_string); // Ensure the foreign call counter has been incremented @@ -1128,8 +1200,8 @@ mod tests { #[test] fn foreign_call_opcode_memory_alloc_result() { - let r_input = RegisterIndex::from(0); - let r_output = RegisterIndex::from(1); + let r_input = MemoryAddress::from(0); + let r_output = MemoryAddress::from(1); // Define a simple 2x2 matrix in memory let initial_matrix = @@ -1140,18 +1212,23 @@ mod tests { vec![Value::from(1u128), Value::from(3u128), Value::from(2u128), Value::from(4u128)]; let invert_program = vec![ + Opcode::CalldataCopy { + destination_address: MemoryAddress::from(2), + size: initial_matrix.len(), + offset: 0, + }, // input = 0 - Opcode::Const { destination: r_input, value: Value::from(0u128) }, + Opcode::Const { destination: r_input, value: Value::from(2u128) }, // output = 0 - Opcode::Const { destination: r_output, value: Value::from(4u128) }, + Opcode::Const { destination: r_output, value: Value::from(6u128) }, // *output = matrix_2x2_transpose(*input) Opcode::ForeignCall { function: "matrix_2x2_transpose".into(), - destinations: vec![RegisterOrMemory::HeapArray(HeapArray { + destinations: vec![ValueOrArray::HeapArray(HeapArray { pointer: r_output, size: initial_matrix.len(), })], - inputs: vec![RegisterOrMemory::HeapArray(HeapArray { + inputs: vec![ValueOrArray::HeapArray(HeapArray { pointer: r_input, size: initial_matrix.len(), })], @@ -1176,14 +1253,14 @@ mod tests { brillig_execute(&mut vm); // Check that VM finished once resumed - assert_eq!(vm.status, VMStatus::Finished); + assert_eq!(vm.status, VMStatus::Finished { return_data_offset: 0, return_data_size: 0 }); // Check initial memory still in place - let initial_values = vm.memory.read_slice(0, 4).to_vec(); + let initial_values = vm.memory.read_slice(MemoryAddress(2), 4).to_vec(); assert_eq!(initial_values, initial_matrix); // Check result in memory - let result_values = vm.memory.read_slice(4, 4).to_vec(); + let result_values = vm.memory.read_slice(MemoryAddress(6), 4).to_vec(); assert_eq!(result_values, expected_result); // Ensure the foreign call counter has been incremented @@ -1192,9 +1269,9 @@ mod tests { #[test] fn foreign_call_opcode_multiple_array_inputs_result() { - let r_input_a = RegisterIndex::from(0); - let r_input_b = RegisterIndex::from(1); - let r_output = RegisterIndex::from(2); + let r_input_a = MemoryAddress::from(0); + let r_input_b = MemoryAddress::from(1); + let r_output = MemoryAddress::from(2); // Define a simple 2x2 matrix in memory let matrix_a = @@ -1216,28 +1293,27 @@ mod tests { ]; let matrix_mul_program = vec![ - // input = 0 - Opcode::Const { destination: r_input_a, value: Value::from(0u128) }, - // input = 0 - Opcode::Const { destination: r_input_b, value: Value::from(4u128) }, + Opcode::CalldataCopy { + destination_address: MemoryAddress::from(3), + size: matrix_a.len() + matrix_b.len(), + offset: 0, + }, + // input = 3 + Opcode::Const { destination: r_input_a, value: Value::from(3u128) }, + // input = 7 + Opcode::Const { destination: r_input_b, value: Value::from(7u128) }, // output = 0 Opcode::Const { destination: r_output, value: Value::from(0u128) }, // *output = matrix_2x2_transpose(*input) Opcode::ForeignCall { function: "matrix_2x2_transpose".into(), - destinations: vec![RegisterOrMemory::HeapArray(HeapArray { + destinations: vec![ValueOrArray::HeapArray(HeapArray { pointer: r_output, size: matrix_a.len(), })], inputs: vec![ - RegisterOrMemory::HeapArray(HeapArray { - pointer: r_input_a, - size: matrix_a.len(), - }), - RegisterOrMemory::HeapArray(HeapArray { - pointer: r_input_b, - size: matrix_b.len(), - }), + ValueOrArray::HeapArray(HeapArray { pointer: r_input_a, size: matrix_a.len() }), + ValueOrArray::HeapArray(HeapArray { pointer: r_input_b, size: matrix_b.len() }), ], }, ]; @@ -1261,10 +1337,10 @@ mod tests { brillig_execute(&mut vm); // Check that VM finished once resumed - assert_eq!(vm.status, VMStatus::Finished); + assert_eq!(vm.status, VMStatus::Finished { return_data_offset: 0, return_data_size: 0 }); // Check result in memory - let result_values = vm.memory.read_slice(0, 4).to_vec(); + let result_values = vm.memory.read_slice(MemoryAddress(0), 4).to_vec(); assert_eq!(result_values, expected_result); // Ensure the foreign call counter has been incremented diff --git a/noir/acvm-repo/brillig_vm/src/memory.rs b/noir/acvm-repo/brillig_vm/src/memory.rs index e2309537283..d1c81447170 100644 --- a/noir/acvm-repo/brillig_vm/src/memory.rs +++ b/noir/acvm-repo/brillig_vm/src/memory.rs @@ -1,45 +1,49 @@ +use acir::{brillig::MemoryAddress, FieldElement}; + use crate::Value; -#[derive(Debug, Clone, PartialEq, Eq)] +#[derive(Debug, Clone, Default, PartialEq, Eq)] pub struct Memory { // Memory is a vector of values. // We grow the memory when values past the end are set, extending with 0s. inner: Vec, } -impl From> for Memory { - fn from(values: Vec) -> Self { - Memory { inner: values } - } -} - impl Memory { /// Gets the value at pointer - pub fn read(&self, ptr: usize) -> Value { - self.inner[ptr] + pub fn read(&self, ptr: MemoryAddress) -> Value { + self.inner.get(ptr.to_usize()).copied().unwrap_or(0_u128.into()) } - pub fn read_slice(&self, ptr: usize, len: usize) -> &[Value] { - &self.inner[ptr..ptr + len] + pub fn read_ref(&self, ptr: MemoryAddress) -> MemoryAddress { + MemoryAddress(self.read(ptr).to_usize()) + } + + pub fn read_slice(&self, addr: MemoryAddress, len: usize) -> &[Value] { + &self.inner[addr.to_usize()..(addr.to_usize() + len)] } /// Sets the value at pointer `ptr` to `value` - pub fn write(&mut self, ptr: usize, value: Value) { - self.write_slice(ptr, &[value]); + pub fn write(&mut self, ptr: MemoryAddress, value: Value) { + self.resize_to_fit(ptr.to_usize() + 1); + self.inner[ptr.to_usize()] = value; } - /// Sets the values after pointer `ptr` to `values` - pub fn write_slice(&mut self, ptr: usize, values: &[Value]) { + fn resize_to_fit(&mut self, size: usize) { // Calculate new memory size - let new_size = std::cmp::max(self.inner.len(), ptr + values.len()); + let new_size = std::cmp::max(self.inner.len(), size); // Expand memory to new size with default values if needed - self.inner.resize(new_size, Value::from(0_usize)); + self.inner.resize(new_size, Value::from(FieldElement::zero())); + } - self.inner[ptr..ptr + values.len()].copy_from_slice(values); + /// Sets the values after pointer `ptr` to `values` + pub fn write_slice(&mut self, ptr: MemoryAddress, values: &[Value]) { + self.resize_to_fit(ptr.to_usize() + values.len()); + self.inner[ptr.to_usize()..(ptr.to_usize() + values.len())].copy_from_slice(values); } /// Returns the values of the memory - pub fn values(&self) -> &Vec { + pub fn values(&self) -> &[Value] { &self.inner } } diff --git a/noir/acvm-repo/brillig_vm/src/registers.rs b/noir/acvm-repo/brillig_vm/src/registers.rs deleted file mode 100644 index fcc596dd6c9..00000000000 --- a/noir/acvm-repo/brillig_vm/src/registers.rs +++ /dev/null @@ -1,43 +0,0 @@ -use acir::brillig::{RegisterIndex, Value}; - -#[derive(Debug, Clone, PartialEq, Eq)] -pub struct Registers { - // Registers are a vector of values. - // We grow the register as registers past the end are set, extending with 0s. - pub inner: Vec, -} - -/// Aims to match a reasonable max register count for a SNARK prover. -/// As well, catches obvious erroneous use of registers. -/// This can be revisited if it proves not enough. -const MAX_REGISTERS: usize = 2_usize.pow(16); - -/// Registers will store field element values during the -/// duration of the execution of the bytecode. -impl Registers { - /// Create a Registers object initialized with definite values - pub fn load(values: Vec) -> Registers { - let inner = values.into_iter().collect(); - Self { inner } - } - - /// Gets the values at register with address `index` - pub fn get(&self, register_index: RegisterIndex) -> Value { - let index = register_index.to_usize(); - assert!(index < MAX_REGISTERS, "Reading register past maximum!"); - let value = self.inner.get(index); - match value { - Some(value) => *value, - None => 0u128.into(), - } - } - - /// Sets the value at register with address `index` to `value` - pub fn set(&mut self, RegisterIndex(index): RegisterIndex, value: Value) { - assert!(index < MAX_REGISTERS, "Writing register past maximum!"); - // if size isn't at least index + 1, resize - let new_register_size = std::cmp::max(index + 1, self.inner.len()); - self.inner.resize(new_register_size, 0u128.into()); - self.inner[index] = value; - } -} diff --git a/noir/aztec_macros/src/lib.rs b/noir/aztec_macros/src/lib.rs index 7c62f8f8169..1dbe7631388 100644 --- a/noir/aztec_macros/src/lib.rs +++ b/noir/aztec_macros/src/lib.rs @@ -26,8 +26,12 @@ impl MacroProcessor for AztecMacro { transform(ast, crate_id, context) } - fn process_typed_ast(&self, crate_id: &CrateId, context: &mut HirContext) { - transform_hir(crate_id, context) + fn process_typed_ast( + &self, + crate_id: &CrateId, + context: &mut HirContext, + ) -> Result<(), (MacroError, FileId)> { + transform_hir(crate_id, context).map_err(|(err, file_id)| (err.into(), file_id)) } } @@ -36,32 +40,33 @@ const MAX_CONTRACT_FUNCTIONS: usize = 2_usize.pow(FUNCTION_TREE_HEIGHT); #[derive(Debug, Clone)] pub enum AztecMacroError { - AztecNotFound, - AztecComputeNoteHashAndNullifierNotFound { span: Span }, - AztecContractHasTooManyFunctions { span: Span }, - AztecContractConstructorMissing { span: Span }, + AztecDepNotFound, + ComputeNoteHashAndNullifierNotFound { span: Span }, + ContractHasTooManyFunctions { span: Span }, + ContractConstructorMissing { span: Span }, UnsupportedFunctionArgumentType { span: Span, typ: UnresolvedTypeData }, + EventError { span: Span, message: String }, } impl From for MacroError { fn from(err: AztecMacroError) -> Self { match err { - AztecMacroError::AztecNotFound {} => MacroError { - primary_message: "Aztec dependency not found. Please add aztec as a dependency in your Cargo.toml. For more information go to https://docs.aztec.network/dev_docs/debugging/aztecnr-errors#aztec-dependency-not-found-please-add-aztec-as-a-dependency-in-your-nargotoml".to_owned(), + AztecMacroError::AztecDepNotFound {} => MacroError { + primary_message: "Aztec dependency not found. Please add aztec as a dependency in your Cargo.toml. For more information go to https://docs.aztec.network/developers/debugging/aztecnr-errors#aztec-dependency-not-found-please-add-aztec-as-a-dependency-in-your-nargotoml".to_owned(), secondary_message: None, span: None, }, - AztecMacroError::AztecComputeNoteHashAndNullifierNotFound { span } => MacroError { - primary_message: "compute_note_hash_and_nullifier function not found. Define it in your contract. For more information go to https://docs.aztec.network/dev_docs/debugging/aztecnr-errors#compute_note_hash_and_nullifier-function-not-found-define-it-in-your-contract".to_owned(), + AztecMacroError::ComputeNoteHashAndNullifierNotFound { span } => MacroError { + primary_message: "compute_note_hash_and_nullifier function not found. Define it in your contract. For more information go to https://docs.aztec.network/developers/debugging/aztecnr-errors#compute_note_hash_and_nullifier-function-not-found-define-it-in-your-contract".to_owned(), secondary_message: None, span: Some(span), }, - AztecMacroError::AztecContractHasTooManyFunctions { span } => MacroError { + AztecMacroError::ContractHasTooManyFunctions { span } => MacroError { primary_message: format!("Contract can only have a maximum of {} functions", MAX_CONTRACT_FUNCTIONS), secondary_message: None, span: Some(span), }, - AztecMacroError::AztecContractConstructorMissing { span } => MacroError { + AztecMacroError::ContractConstructorMissing { span } => MacroError { primary_message: "Contract must have a constructor function".to_owned(), secondary_message: None, span: Some(span), @@ -71,6 +76,11 @@ impl From for MacroError { secondary_message: None, span: Some(span), }, + AztecMacroError::EventError { span, message } => MacroError { + primary_message: message, + secondary_message: None, + span: Some(span), + }, } } } @@ -222,7 +232,9 @@ fn transform( // Covers all functions in the ast for submodule in ast.submodules.iter_mut().filter(|submodule| submodule.is_contract) { - if transform_module(&mut submodule.contents, crate_id, context)? { + if transform_module(&mut submodule.contents, crate_id, context) + .map_err(|(err, file_id)| (err.into(), file_id))? + { check_for_aztec_dependency(crate_id, context)?; include_relevant_imports(&mut submodule.contents); } @@ -235,8 +247,11 @@ fn transform( // /// Completes the Hir with data gathered from type resolution -fn transform_hir(crate_id: &CrateId, context: &mut HirContext) { - transform_events(crate_id, context); +fn transform_hir( + crate_id: &CrateId, + context: &mut HirContext, +) -> Result<(), (AztecMacroError, FileId)> { + transform_events(crate_id, context) } /// Includes an import to the aztec library if it has not been included yet @@ -264,7 +279,7 @@ fn check_for_aztec_dependency( if has_aztec_dependency { Ok(()) } else { - Err((AztecMacroError::AztecNotFound.into(), crate_graph.root_file_id)) + Err((AztecMacroError::AztecDepNotFound.into(), crate_graph.root_file_id)) } } @@ -323,7 +338,7 @@ fn transform_module( module: &mut SortedModule, crate_id: &CrateId, context: &HirContext, -) -> Result { +) -> Result { let mut has_transformed_module = false; // Check for a user defined storage struct @@ -332,8 +347,7 @@ fn transform_module( if storage_defined && !check_for_compute_note_hash_and_nullifier_definition(module) { let crate_graph = &context.crate_graph[crate_id]; return Err(( - AztecMacroError::AztecComputeNoteHashAndNullifierNotFound { span: Span::default() } - .into(), + AztecMacroError::ComputeNoteHashAndNullifierNotFound { span: Span::default() }, crate_graph.root_file_id, )); } @@ -350,11 +364,11 @@ fn transform_module( let crate_graph = &context.crate_graph[crate_id]; if is_custom_attribute(&secondary_attribute, "aztec(private)") { transform_function("Private", func, storage_defined) - .map_err(|err| (err.into(), crate_graph.root_file_id))?; + .map_err(|err| (err, crate_graph.root_file_id))?; has_transformed_module = true; } else if is_custom_attribute(&secondary_attribute, "aztec(public)") { transform_function("Public", func, storage_defined) - .map_err(|err| (err.into(), crate_graph.root_file_id))?; + .map_err(|err| (err, crate_graph.root_file_id))?; has_transformed_module = true; } } @@ -371,7 +385,7 @@ fn transform_module( if module.functions.len() > MAX_CONTRACT_FUNCTIONS { let crate_graph = &context.crate_graph[crate_id]; return Err(( - AztecMacroError::AztecContractHasTooManyFunctions { span: Span::default() }.into(), + AztecMacroError::ContractHasTooManyFunctions { span: Span::default() }, crate_graph.root_file_id, )); } @@ -380,7 +394,7 @@ fn transform_module( if !constructor_defined { let crate_graph = &context.crate_graph[crate_id]; return Err(( - AztecMacroError::AztecContractConstructorMissing { span: Span::default() }.into(), + AztecMacroError::ContractConstructorMissing { span: Span::default() }, crate_graph.root_file_id, )); } @@ -471,19 +485,30 @@ fn collect_crate_structs(crate_id: &CrateId, context: &HirContext) -> Vec Result<(), (AztecMacroError, FileId)> { let struct_type = interner.get_struct(struct_id); let selector_id = interner - .lookup_method(&Type::Struct(struct_type, vec![]), struct_id, "selector", false) - .expect("Selector method not found"); + .lookup_method(&Type::Struct(struct_type.clone(), vec![]), struct_id, "selector", false) + .ok_or_else(|| { + let error = AztecMacroError::EventError { + span: struct_type.borrow().location.span, + message: "Selector method not found".to_owned(), + }; + (error, struct_type.borrow().location.file) + })?; let selector_function = interner.function(&selector_id); let compute_selector_statement = interner.statement( - selector_function - .block(interner) - .statements() - .first() - .expect("Compute selector statement not found"), + selector_function.block(interner).statements().first().ok_or_else(|| { + let error = AztecMacroError::EventError { + span: struct_type.borrow().location.span, + message: "Compute selector statement not found".to_owned(), + }; + (error, struct_type.borrow().location.file) + })?, ); let compute_selector_expression = match compute_selector_statement { @@ -493,12 +518,21 @@ fn transform_event(struct_id: StructId, interner: &mut NodeInterner) { }, _ => None, } - .expect("Compute selector statement is not a call expression"); - - let first_arg_id = compute_selector_expression - .arguments - .first() - .expect("Missing argument for compute selector"); + .ok_or_else(|| { + let error = AztecMacroError::EventError { + span: struct_type.borrow().location.span, + message: "Compute selector statement is not a call expression".to_owned(), + }; + (error, struct_type.borrow().location.file) + })?; + + let first_arg_id = compute_selector_expression.arguments.first().ok_or_else(|| { + let error = AztecMacroError::EventError { + span: struct_type.borrow().location.span, + message: "Compute selector statement is not a call expression".to_owned(), + }; + (error, struct_type.borrow().location.file) + })?; match interner.expression(first_arg_id) { HirExpression::Literal(HirLiteral::Str(signature)) @@ -517,18 +551,29 @@ fn transform_event(struct_id: StructId, interner: &mut NodeInterner) { selector_literal_id, Type::String(Box::new(Type::Constant(signature.len() as u64))), ); + Ok(()) } - _ => unreachable!("Signature placeholder literal does not match"), + _ => Err(( + AztecMacroError::EventError { + span: struct_type.borrow().location.span, + message: "Signature placeholder literal does not match".to_owned(), + }, + struct_type.borrow().location.file, + )), } } -fn transform_events(crate_id: &CrateId, context: &mut HirContext) { +fn transform_events( + crate_id: &CrateId, + context: &mut HirContext, +) -> Result<(), (AztecMacroError, FileId)> { for struct_id in collect_crate_structs(crate_id, context) { let attributes = context.def_interner.struct_attributes(&struct_id); if attributes.iter().any(|attr| matches!(attr, SecondaryAttribute::Event)) { - transform_event(struct_id, &mut context.def_interner); + transform_event(struct_id, &mut context.def_interner)?; } } + Ok(()) } const SIGNATURE_PLACEHOLDER: &str = "SIGNATURE_PLACEHOLDER"; @@ -845,11 +890,11 @@ fn make_return_push(push_value: Expression) -> Statement { /// Make Return push array /// /// Translates to: -/// `context.return_values.push_array({push_value})` -fn make_return_push_array(push_value: Expression) -> Statement { +/// `context.return_values.extend_from_array({push_value})` +fn make_return_extend_from_array(push_value: Expression) -> Statement { make_statement(StatementKind::Semi(method_call( context_return_values(), - "push_array", + "extend_from_array", vec![push_value], ))) } @@ -858,14 +903,14 @@ fn make_return_push_array(push_value: Expression) -> Statement { /// /// Translates to: /// ```noir -/// `context.return_values.push_array({push_value}.serialize())` +/// `context.return_values.extend_from_array({push_value}.serialize())` fn make_struct_return_type(expression: Expression) -> Statement { let serialized_call = method_call( expression, // variable "serialize", // method name vec![], // args ); - make_return_push_array(serialized_call) + make_return_extend_from_array(serialized_call) } /// Make array return type diff --git a/noir/compiler/noirc_driver/src/abi_gen.rs b/noir/compiler/noirc_driver/src/abi_gen.rs index e546cd822b7..7fafa719186 100644 --- a/noir/compiler/noirc_driver/src/abi_gen.rs +++ b/noir/compiler/noirc_driver/src/abi_gen.rs @@ -63,7 +63,7 @@ fn into_abi_params(context: &Context, params: Vec) -> Vec { // Takes each abi parameter and shallowly maps to the expected witness range in which the // parameter's constituent values live. fn param_witnesses_from_abi_param( - abi_params: &Vec, + abi_params: &[AbiParameter], input_witnesses: Vec, ) -> BTreeMap>> { let mut idx = 0_usize; diff --git a/noir/compiler/noirc_driver/src/contract.rs b/noir/compiler/noirc_driver/src/contract.rs index ae55d239cf3..5f4b66e7dd2 100644 --- a/noir/compiler/noirc_driver/src/contract.rs +++ b/noir/compiler/noirc_driver/src/contract.rs @@ -26,7 +26,7 @@ pub enum ContractFunctionType { Unconstrained, } -#[derive(Serialize, Deserialize)] +#[derive(Clone, Debug, Serialize, Deserialize)] pub struct CompiledContract { pub noir_version: String, @@ -51,7 +51,7 @@ pub struct CompiledContract { /// A contract function unlike a regular Noir program /// however can have additional properties. /// One of these being a function type. -#[derive(Debug, Serialize, Deserialize)] +#[derive(Debug, Clone, Serialize, Deserialize)] pub struct ContractFunction { pub name: String, diff --git a/noir/compiler/noirc_driver/src/lib.rs b/noir/compiler/noirc_driver/src/lib.rs index db69d41c704..6fd69f8b576 100644 --- a/noir/compiler/noirc_driver/src/lib.rs +++ b/noir/compiler/noirc_driver/src/lib.rs @@ -3,6 +3,7 @@ #![warn(unreachable_pub)] #![warn(clippy::semicolon_if_nothing_returned)] +use acvm::ExpressionWidth; use clap::Args; use fm::{FileId, FileManager}; use iter_extended::vecmap; @@ -16,7 +17,6 @@ use noirc_frontend::hir::Context; use noirc_frontend::macros_api::MacroProcessor; use noirc_frontend::monomorphization::monomorphize; use noirc_frontend::node_interner::FuncId; -use serde::{Deserialize, Serialize}; use std::path::Path; use tracing::info; @@ -43,8 +43,12 @@ pub const NOIRC_VERSION: &str = env!("CARGO_PKG_VERSION"); pub const NOIR_ARTIFACT_VERSION_STRING: &str = concat!(env!("CARGO_PKG_VERSION"), "+", env!("GIT_COMMIT")); -#[derive(Args, Clone, Debug, Default, Serialize, Deserialize)] +#[derive(Args, Clone, Debug, Default)] pub struct CompileOptions { + /// Override the expression width requested by the backend. + #[arg(long, value_parser = parse_expression_width)] + pub expression_width: Option, + /// Force a full recompilation. #[arg(long = "force")] pub force_compile: bool, @@ -75,6 +79,20 @@ pub struct CompileOptions { /// Disables the builtin macros being used in the compiler #[arg(long, hide = true)] pub disable_macros: bool, + + /// Outputs the monomorphized IR to stdout for debugging + #[arg(long, hide = true)] + pub show_monomorphized: bool, +} + +fn parse_expression_width(input: &str) -> Result { + use std::io::{Error, ErrorKind}; + + let width = input + .parse::() + .map_err(|err| Error::new(ErrorKind::InvalidInput, err.to_string()))?; + + Ok(ExpressionWidth::from(width)) } /// Helper type used to signify where only warnings are expected in file diagnostics @@ -391,6 +409,9 @@ pub fn compile_no_check( let hash = fxhash::hash64(&program); let hashes_match = cached_program.as_ref().map_or(false, |program| program.hash == hash); + if options.show_monomorphized { + println!("{program}"); + } // If user has specified that they want to see intermediate steps printed then we should // force compilation even if the program hasn't changed. diff --git a/noir/compiler/noirc_driver/tests/contracts.rs b/noir/compiler/noirc_driver/tests/contracts.rs new file mode 100644 index 00000000000..c3041292352 --- /dev/null +++ b/noir/compiler/noirc_driver/tests/contracts.rs @@ -0,0 +1,42 @@ +use std::path::Path; + +use fm::FileId; +use noirc_driver::{file_manager_with_stdlib, prepare_crate, CompileOptions, ErrorsAndWarnings}; +use noirc_errors::CustomDiagnostic; +use noirc_frontend::hir::{def_map::parse_file, Context}; + +#[test] +fn reject_crates_containing_multiple_contracts() -> Result<(), ErrorsAndWarnings> { + let source = " +contract Foo {} + +contract Bar {}"; + + let root = Path::new(""); + let file_name = Path::new("main.nr"); + let mut file_manager = file_manager_with_stdlib(root); + file_manager.add_file_with_source(file_name, source.to_owned()).expect( + "Adding source buffer to file manager should never fail when file manager is empty", + ); + let parsed_files = file_manager + .as_file_map() + .all_file_ids() + .map(|&file_id| (file_id, parse_file(&file_manager, file_id))) + .collect(); + + let mut context = Context::new(file_manager, parsed_files); + let root_crate_id = prepare_crate(&mut context, file_name); + + let errors = + noirc_driver::compile_contract(&mut context, root_crate_id, &CompileOptions::default()) + .unwrap_err(); + + assert_eq!( + errors, + vec![CustomDiagnostic::from_message("Packages are limited to a single contract") + .in_file(FileId::default())], + "stdlib is producing warnings" + ); + + Ok(()) +} diff --git a/noir/compiler/noirc_evaluator/src/brillig/brillig_gen/brillig_black_box.rs b/noir/compiler/noirc_evaluator/src/brillig/brillig_gen/brillig_black_box.rs index c081806f4a7..96d80cb8131 100644 --- a/noir/compiler/noirc_evaluator/src/brillig/brillig_gen/brillig_black_box.rs +++ b/noir/compiler/noirc_evaluator/src/brillig/brillig_gen/brillig_black_box.rs @@ -215,23 +215,6 @@ pub(crate) fn convert_black_box_call( ) } } - BlackBoxFunc::EmbeddedCurveDouble => { - if let ( - [BrilligVariable::Simple(input1_x), BrilligVariable::Simple(input1_y)], - [BrilligVariable::BrilligArray(result_array)], - ) = (function_arguments, function_results) - { - brillig_context.black_box_op_instruction(BlackBoxOp::EmbeddedCurveDouble { - input1_x: *input1_x, - input1_y: *input1_y, - result: result_array.to_heap_array(), - }); - } else { - unreachable!( - "ICE: EmbeddedCurveAdd expects two register arguments and one array result" - ) - } - } BlackBoxFunc::AND => { unreachable!("ICE: `BlackBoxFunc::AND` calls should be transformed into a `BinaryOp`") } @@ -244,6 +227,138 @@ pub(crate) fn convert_black_box_call( BlackBoxFunc::RecursiveAggregation => unimplemented!( "ICE: `BlackBoxFunc::RecursiveAggregation` is not implemented by the Brillig VM" ), + BlackBoxFunc::BigIntAdd => { + if let ( + [BrilligVariable::Simple(lhs), BrilligVariable::Simple(rhs)], + [BrilligVariable::Simple(output)], + ) = (function_arguments, function_results) + { + brillig_context.black_box_op_instruction(BlackBoxOp::BigIntAdd { + lhs: *lhs, + rhs: *rhs, + output: *output, + }); + } else { + unreachable!( + "ICE: EmbeddedCurveAdd expects two register arguments and one array result" + ) + } + } + BlackBoxFunc::BigIntNeg => { + if let ( + [BrilligVariable::Simple(lhs), BrilligVariable::Simple(rhs)], + [BrilligVariable::Simple(output)], + ) = (function_arguments, function_results) + { + brillig_context.black_box_op_instruction(BlackBoxOp::BigIntNeg { + lhs: *lhs, + rhs: *rhs, + output: *output, + }); + } else { + unreachable!( + "ICE: EmbeddedCurveAdd expects two register arguments and one array result" + ) + } + } + BlackBoxFunc::BigIntMul => { + if let ( + [BrilligVariable::Simple(lhs), BrilligVariable::Simple(rhs)], + [BrilligVariable::Simple(output)], + ) = (function_arguments, function_results) + { + brillig_context.black_box_op_instruction(BlackBoxOp::BigIntMul { + lhs: *lhs, + rhs: *rhs, + output: *output, + }); + } else { + unreachable!( + "ICE: EmbeddedCurveAdd expects two register arguments and one array result" + ) + } + } + BlackBoxFunc::BigIntDiv => { + if let ( + [BrilligVariable::Simple(lhs), BrilligVariable::Simple(rhs)], + [BrilligVariable::Simple(output)], + ) = (function_arguments, function_results) + { + brillig_context.black_box_op_instruction(BlackBoxOp::BigIntDiv { + lhs: *lhs, + rhs: *rhs, + output: *output, + }); + } else { + unreachable!( + "ICE: EmbeddedCurveAdd expects two register arguments and one array result" + ) + } + } + BlackBoxFunc::BigIntFromLeBytes => { + if let ([inputs, modulus], [BrilligVariable::Simple(output)]) = + (function_arguments, function_results) + { + let inputs_vector = convert_array_or_vector(brillig_context, inputs, bb_func); + let modulus_vector = convert_array_or_vector(brillig_context, modulus, bb_func); + brillig_context.black_box_op_instruction(BlackBoxOp::BigIntFromLeBytes { + inputs: inputs_vector.to_heap_vector(), + modulus: modulus_vector.to_heap_vector(), + output: *output, + }); + } else { + unreachable!( + "ICE: EmbeddedCurveAdd expects two register arguments and one array result" + ) + } + } + BlackBoxFunc::BigIntToLeBytes => { + if let ( + [BrilligVariable::Simple(input)], + [BrilligVariable::BrilligVector(result_vector)], + ) = (function_arguments, function_results) + { + brillig_context.black_box_op_instruction(BlackBoxOp::BigIntToLeBytes { + input: *input, + output: result_vector.to_heap_vector(), + }); + } else { + unreachable!( + "ICE: EmbeddedCurveAdd expects two register arguments and one array result" + ) + } + } + BlackBoxFunc::Poseidon2Permutation => { + if let ( + [message, BrilligVariable::Simple(state_len)], + [BrilligVariable::BrilligArray(result_array)], + ) = (function_arguments, function_results) + { + let message_vector = convert_array_or_vector(brillig_context, message, bb_func); + brillig_context.black_box_op_instruction(BlackBoxOp::Poseidon2Permutation { + message: message_vector.to_heap_vector(), + output: result_array.to_heap_array(), + len: *state_len, + }); + } else { + unreachable!("ICE: Poseidon2Permutation expects one array argument, a length and one array result") + } + } + BlackBoxFunc::Sha256Compression => { + if let ([message, hash_values], [BrilligVariable::BrilligArray(result_array)]) = + (function_arguments, function_results) + { + let message_vector = convert_array_or_vector(brillig_context, message, bb_func); + let hash_vector = convert_array_or_vector(brillig_context, hash_values, bb_func); + brillig_context.black_box_op_instruction(BlackBoxOp::Sha256Compression { + input: message_vector.to_heap_vector(), + hash_values: hash_vector.to_heap_vector(), + output: result_array.to_heap_array(), + }); + } else { + unreachable!("ICE: Sha256Compression expects two array argument, one array result") + } + } } } diff --git a/noir/compiler/noirc_evaluator/src/brillig/brillig_gen/brillig_block.rs b/noir/compiler/noirc_evaluator/src/brillig/brillig_gen/brillig_block.rs index db005d9d438..ae8adeb10ec 100644 --- a/noir/compiler/noirc_evaluator/src/brillig/brillig_gen/brillig_block.rs +++ b/noir/compiler/noirc_evaluator/src/brillig/brillig_gen/brillig_block.rs @@ -13,7 +13,7 @@ use crate::ssa::ir::{ types::{NumericType, Type}, value::{Value, ValueId}, }; -use acvm::acir::brillig::{BinaryFieldOp, BinaryIntOp, RegisterIndex, RegisterOrMemory}; +use acvm::acir::brillig::{BinaryFieldOp, BinaryIntOp, MemoryAddress, ValueOrArray}; use acvm::brillig_vm::brillig::HeapVector; use acvm::FieldElement; use fxhash::{FxHashMap as HashMap, FxHashSet as HashSet}; @@ -342,16 +342,13 @@ impl<'block> BrilligBlock<'block> { ); for (i, output_register) in output_registers.iter().enumerate() { - if let RegisterOrMemory::HeapVector(HeapVector { size, .. }) = - output_register - { + if let ValueOrArray::HeapVector(HeapVector { size, .. }) = output_register { // Update the stack pointer so that we do not overwrite // dynamic memory returned from other external calls self.brillig_context.update_stack_pointer(*size); // Update the dynamic slice length maintained in SSA - if let RegisterOrMemory::RegisterIndex(len_index) = - output_registers[i - 1] + if let ValueOrArray::MemoryAddress(len_index) = output_registers[i - 1] { let element_size = dfg[result_ids[i]].get_type().element_size(); self.brillig_context.mov_instruction(len_index, *size); @@ -564,6 +561,7 @@ impl<'block> BrilligBlock<'block> { }; let index_register = self.convert_ssa_register_value(*index, dfg); + self.validate_array_index(array_variable, index_register); self.retrieve_variable_from_array( array_pointer, index_register, @@ -582,6 +580,7 @@ impl<'block> BrilligBlock<'block> { result_ids[0], dfg, ); + self.validate_array_index(source_variable, index_register); self.convert_ssa_array_set( source_variable, @@ -639,7 +638,7 @@ impl<'block> BrilligBlock<'block> { instruction_id: InstructionId, ) { // Convert the arguments to registers casting those to the types of the receiving function - let argument_registers: Vec = arguments + let argument_registers: Vec = arguments .iter() .flat_map(|argument_id| self.convert_ssa_value(*argument_id, dfg).extract_registers()) .collect(); @@ -675,7 +674,7 @@ impl<'block> BrilligBlock<'block> { }); // Collect the registers that should have been returned - let returned_registers: Vec = variables_assigned_to + let returned_registers: Vec = variables_assigned_to .iter() .flat_map(|returned_variable| returned_variable.extract_registers()) .collect(); @@ -690,10 +689,41 @@ impl<'block> BrilligBlock<'block> { .post_call_prep_returns_load_registers(&returned_registers, &saved_registers); } + fn validate_array_index( + &mut self, + array_variable: BrilligVariable, + index_register: MemoryAddress, + ) { + let (size_as_register, should_deallocate_size) = match array_variable { + BrilligVariable::BrilligArray(BrilligArray { size, .. }) => { + (self.brillig_context.make_constant(size.into()), true) + } + BrilligVariable::BrilligVector(BrilligVector { size, .. }) => (size, false), + _ => unreachable!("ICE: validate array index on non-array"), + }; + + let condition = self.brillig_context.allocate_register(); + + self.brillig_context.memory_op( + index_register, + size_as_register, + condition, + BinaryIntOp::LessThan, + ); + + self.brillig_context + .constrain_instruction(condition, Some("Array index out of bounds".to_owned())); + + if should_deallocate_size { + self.brillig_context.deallocate_register(size_as_register); + } + self.brillig_context.deallocate_register(condition); + } + pub(crate) fn retrieve_variable_from_array( &mut self, - array_pointer: RegisterIndex, - index_register: RegisterIndex, + array_pointer: MemoryAddress, + index_register: MemoryAddress, destination_variable: BrilligVariable, ) { match destination_variable { @@ -715,7 +745,7 @@ impl<'block> BrilligBlock<'block> { &mut self, source_variable: BrilligVariable, destination_variable: BrilligVariable, - index_register: RegisterIndex, + index_register: MemoryAddress, value_variable: BrilligVariable, ) { let destination_pointer = match destination_variable { @@ -795,8 +825,8 @@ impl<'block> BrilligBlock<'block> { pub(crate) fn store_variable_in_array_with_ctx( ctx: &mut BrilligContext, - destination_pointer: RegisterIndex, - index_register: RegisterIndex, + destination_pointer: MemoryAddress, + index_register: MemoryAddress, value_variable: BrilligVariable, ) { match value_variable { @@ -804,7 +834,7 @@ impl<'block> BrilligBlock<'block> { ctx.array_set(destination_pointer, index_register, value_register); } BrilligVariable::BrilligArray(_) => { - let reference: RegisterIndex = ctx.allocate_register(); + let reference: MemoryAddress = ctx.allocate_register(); ctx.allocate_array_reference_instruction(reference); ctx.store_variable_instruction(reference, value_variable); ctx.array_set(destination_pointer, index_register, reference); @@ -822,8 +852,8 @@ impl<'block> BrilligBlock<'block> { pub(crate) fn store_variable_in_array( &mut self, - destination_pointer: RegisterIndex, - index_register: RegisterIndex, + destination_pointer: MemoryAddress, + index_register: MemoryAddress, value_variable: BrilligVariable, ) { Self::store_variable_in_array_with_ctx( @@ -1078,7 +1108,7 @@ impl<'block> BrilligBlock<'block> { /// of fields in the vector. fn update_slice_length( &mut self, - target_len: RegisterIndex, + target_len: MemoryAddress, source_value: ValueId, dfg: &DataFlowGraph, binary_op: BinaryIntOp, @@ -1091,7 +1121,7 @@ impl<'block> BrilligBlock<'block> { /// Converts an SSA cast to a sequence of Brillig opcodes. /// Casting is only necessary when shrinking the bit size of a numeric value. - fn convert_cast(&mut self, destination: RegisterIndex, source: RegisterIndex) { + fn convert_cast(&mut self, destination: MemoryAddress, source: MemoryAddress) { // We assume that `source` is a valid `target_type` as it's expected that a truncate instruction was emitted // to ensure this is the case. @@ -1103,7 +1133,7 @@ impl<'block> BrilligBlock<'block> { &mut self, binary: &Binary, dfg: &DataFlowGraph, - result_register: RegisterIndex, + result_register: MemoryAddress, ) { let binary_type = type_of_binary_operation(dfg[binary.lhs].get_type(), dfg[binary.rhs].get_type()); @@ -1200,12 +1230,12 @@ impl<'block> BrilligBlock<'block> { } } - /// Converts an SSA `ValueId` into a `RegisterIndex`. Initializes if necessary. + /// Converts an SSA `ValueId` into a `MemoryAddress`. Initializes if necessary. fn convert_ssa_register_value( &mut self, value_id: ValueId, dfg: &DataFlowGraph, - ) -> RegisterIndex { + ) -> MemoryAddress { let variable = self.convert_ssa_value(value_id, dfg); variable.extract_register() } @@ -1266,7 +1296,7 @@ impl<'block> BrilligBlock<'block> { fn convert_ssa_array_len( &mut self, array_id: ValueId, - result_register: RegisterIndex, + result_register: MemoryAddress, dfg: &DataFlowGraph, ) { let array_variable = self.convert_ssa_value(array_id, dfg); diff --git a/noir/compiler/noirc_evaluator/src/brillig/brillig_gen/brillig_block_variables.rs b/noir/compiler/noirc_evaluator/src/brillig/brillig_gen/brillig_block_variables.rs index f2e698c0aa9..49d40ca3697 100644 --- a/noir/compiler/noirc_evaluator/src/brillig/brillig_gen/brillig_block_variables.rs +++ b/noir/compiler/noirc_evaluator/src/brillig/brillig_gen/brillig_block_variables.rs @@ -1,4 +1,4 @@ -use acvm::brillig_vm::brillig::RegisterIndex; +use acvm::brillig_vm::brillig::MemoryAddress; use fxhash::{FxHashMap as HashMap, FxHashSet as HashSet}; use crate::{ @@ -75,7 +75,7 @@ impl BlockVariables { brillig_context: &mut BrilligContext, value: ValueId, dfg: &DataFlowGraph, - ) -> RegisterIndex { + ) -> MemoryAddress { let variable = self.define_variable(function_context, brillig_context, value, dfg); variable.extract_register() } diff --git a/noir/compiler/noirc_evaluator/src/brillig/brillig_gen/brillig_directive.rs b/noir/compiler/noirc_evaluator/src/brillig/brillig_gen/brillig_directive.rs index a07865073ff..5ac2ecf06be 100644 --- a/noir/compiler/noirc_evaluator/src/brillig/brillig_gen/brillig_directive.rs +++ b/noir/compiler/noirc_evaluator/src/brillig/brillig_gen/brillig_directive.rs @@ -1,5 +1,5 @@ use acvm::acir::brillig::{ - BinaryFieldOp, BinaryIntOp, Opcode as BrilligOpcode, RegisterIndex, Value, + BinaryFieldOp, BinaryIntOp, MemoryAddress, Opcode as BrilligOpcode, Value, }; use crate::brillig::brillig_ir::artifact::GeneratedBrillig; @@ -13,13 +13,14 @@ pub(crate) fn directive_invert() -> GeneratedBrillig { // The input argument, ie the value that will be inverted. // We store the result in this register too. - let input = RegisterIndex::from(0); - let one_const = RegisterIndex::from(1); + let input = MemoryAddress::from(0); + let one_const = MemoryAddress::from(1); // Location of the stop opcode let stop_location = 3; GeneratedBrillig { byte_code: vec![ + BrilligOpcode::CalldataCopy { destination_address: input, size: 1, offset: 0 }, // If the input is zero, then we jump to the stop opcode BrilligOpcode::JumpIfNot { condition: input, location: stop_location }, // Put value one in register (1) @@ -31,7 +32,7 @@ pub(crate) fn directive_invert() -> GeneratedBrillig { rhs: input, destination: input, }, - BrilligOpcode::Stop, + BrilligOpcode::Stop { return_data_offset: 0, return_data_size: 1 }, ], assert_messages: Default::default(), locations: Default::default(), @@ -52,36 +53,41 @@ pub(crate) fn directive_quotient(bit_size: u32) -> GeneratedBrillig { // `b` is (1) GeneratedBrillig { byte_code: vec![ + BrilligOpcode::CalldataCopy { + destination_address: MemoryAddress::from(0), + size: 2, + offset: 0, + }, //q = a/b is set into register (2) BrilligOpcode::BinaryIntOp { op: BinaryIntOp::UnsignedDiv, - lhs: RegisterIndex::from(0), - rhs: RegisterIndex::from(1), - destination: RegisterIndex::from(2), + lhs: MemoryAddress::from(0), + rhs: MemoryAddress::from(1), + destination: MemoryAddress::from(2), bit_size, }, //(1)= q*b BrilligOpcode::BinaryIntOp { op: BinaryIntOp::Mul, - lhs: RegisterIndex::from(2), - rhs: RegisterIndex::from(1), - destination: RegisterIndex::from(1), + lhs: MemoryAddress::from(2), + rhs: MemoryAddress::from(1), + destination: MemoryAddress::from(1), bit_size, }, //(1) = a-q*b BrilligOpcode::BinaryIntOp { op: BinaryIntOp::Sub, - lhs: RegisterIndex::from(0), - rhs: RegisterIndex::from(1), - destination: RegisterIndex::from(1), + lhs: MemoryAddress::from(0), + rhs: MemoryAddress::from(1), + destination: MemoryAddress::from(1), bit_size, }, //(0) = q BrilligOpcode::Mov { - destination: RegisterIndex::from(0), - source: RegisterIndex::from(2), + destination: MemoryAddress::from(0), + source: MemoryAddress::from(2), }, - BrilligOpcode::Stop, + BrilligOpcode::Stop { return_data_offset: 0, return_data_size: 2 }, ], assert_messages: Default::default(), locations: Default::default(), diff --git a/noir/compiler/noirc_evaluator/src/brillig/brillig_gen/brillig_slice_ops.rs b/noir/compiler/noirc_evaluator/src/brillig/brillig_gen/brillig_slice_ops.rs index 6402e6f9d97..981a16a01b2 100644 --- a/noir/compiler/noirc_evaluator/src/brillig/brillig_gen/brillig_slice_ops.rs +++ b/noir/compiler/noirc_evaluator/src/brillig/brillig_gen/brillig_slice_ops.rs @@ -1,4 +1,4 @@ -use acvm::brillig_vm::brillig::{BinaryIntOp, RegisterIndex}; +use acvm::brillig_vm::brillig::{BinaryIntOp, MemoryAddress}; use crate::brillig::brillig_ir::brillig_variable::{BrilligVariable, BrilligVector}; @@ -168,7 +168,7 @@ impl<'block> BrilligBlock<'block> { &mut self, target_vector: BrilligVector, source_vector: BrilligVector, - index: RegisterIndex, + index: MemoryAddress, items: &[BrilligVariable], ) { // First we need to allocate the target vector incrementing the size by items.len() @@ -240,7 +240,7 @@ impl<'block> BrilligBlock<'block> { &mut self, target_vector: BrilligVector, source_vector: BrilligVector, - index: RegisterIndex, + index: MemoryAddress, removed_items: &[BrilligVariable], ) { // First we need to allocate the target vector decrementing the size by removed_items.len() @@ -328,7 +328,6 @@ mod tests { use std::vec; use acvm::acir::brillig::Value; - use acvm::brillig_vm::brillig::RegisterIndex; use crate::brillig::brillig_gen::brillig_block::BrilligBlock; use crate::brillig::brillig_gen::brillig_block_variables::BlockVariables; @@ -375,17 +374,15 @@ mod tests { fn test_case_push( push_back: bool, array: Vec, - expected_mem: Vec, item_to_push: Value, + expected_return: Vec, ) { let arguments = vec![ BrilligParameter::Array(vec![BrilligParameter::Simple], array.len()), BrilligParameter::Simple, ]; - let returns = vec![ - BrilligParameter::Array(vec![BrilligParameter::Simple], array.len() + 1), - BrilligParameter::Simple, - ]; + let returns = + vec![BrilligParameter::Array(vec![BrilligParameter::Simple], array.len() + 1)]; let (_, mut function_context, mut context) = create_test_environment(); @@ -423,55 +420,42 @@ mod tests { ); } - context.return_instruction(&[ - target_vector.pointer, - target_vector.rc, - target_vector.size, - ]); + context.return_instruction(&[target_vector.pointer, target_vector.rc]); let bytecode = create_entry_point_bytecode(context, arguments, returns).byte_code; - let vm = create_and_run_vm( - array.clone(), - vec![Value::from(0_usize), item_to_push], - &bytecode, + let (vm, return_data_offset, return_data_size) = + create_and_run_vm(array.into_iter().chain(vec![item_to_push]).collect(), &bytecode); + assert_eq!(return_data_size, expected_return.len()); + assert_eq!( + vm.get_memory()[return_data_offset..(return_data_offset + expected_return.len())], + expected_return ); - - assert_eq!(vm.get_memory(), &expected_mem); - - assert_eq!(vm.get_registers().get(RegisterIndex(0)), Value::from(array.len())); - assert_eq!(vm.get_registers().get(RegisterIndex(1)), Value::from(array.len() + 1)); } test_case_push( true, vec![Value::from(1_usize), Value::from(2_usize), Value::from(3_usize)], + Value::from(27_usize), vec![ - Value::from(1_usize), - Value::from(2_usize), - Value::from(3_usize), Value::from(1_usize), Value::from(2_usize), Value::from(3_usize), Value::from(27_usize), ], - Value::from(27_usize), ); - test_case_push(true, vec![], vec![Value::from(27_usize)], Value::from(27_usize)); + test_case_push(true, vec![], Value::from(27_usize), vec![Value::from(27_usize)]); test_case_push( false, vec![Value::from(1_usize), Value::from(2_usize), Value::from(3_usize)], + Value::from(27_usize), vec![ - Value::from(1_usize), - Value::from(2_usize), - Value::from(3_usize), Value::from(27_usize), Value::from(1_usize), Value::from(2_usize), Value::from(3_usize), ], - Value::from(27_usize), ); - test_case_push(false, vec![], vec![Value::from(27_usize)], Value::from(27_usize)); + test_case_push(false, vec![], Value::from(27_usize), vec![Value::from(27_usize)]); } #[test] @@ -479,15 +463,14 @@ mod tests { fn test_case_pop( pop_back: bool, array: Vec, - expected_mem: Vec, - expected_removed_item: Value, + expected_return_array: Vec, + expected_return_item: Value, ) { let arguments = vec![BrilligParameter::Array(vec![BrilligParameter::Simple], array.len())]; let returns = vec![ BrilligParameter::Array(vec![BrilligParameter::Simple], array.len() - 1), BrilligParameter::Simple, - BrilligParameter::Simple, ]; let (_, mut function_context, mut context) = create_test_environment(); @@ -526,51 +509,32 @@ mod tests { ); } - context.return_instruction(&[ - target_vector.pointer, - target_vector.rc, - target_vector.size, - removed_item, - ]); + context.return_instruction(&[target_vector.pointer, target_vector.rc, removed_item]); let bytecode = create_entry_point_bytecode(context, arguments, returns).byte_code; - let vm = create_and_run_vm(array.clone(), vec![Value::from(0_usize)], &bytecode); - - assert_eq!(vm.get_memory(), &expected_mem); - - assert_eq!(vm.get_registers().get(RegisterIndex(0)), Value::from(array.len())); - assert_eq!(vm.get_registers().get(RegisterIndex(1)), Value::from(array.len() - 1)); - assert_eq!(vm.get_registers().get(RegisterIndex(2)), expected_removed_item); + let expected_return: Vec<_> = + expected_return_array.into_iter().chain(vec![expected_return_item]).collect(); + let (vm, return_data_offset, return_data_size) = + create_and_run_vm(array.clone(), &bytecode); + assert_eq!(return_data_size, expected_return.len()); + + assert_eq!( + vm.get_memory()[return_data_offset..(return_data_offset + expected_return.len())], + expected_return + ); } test_case_pop( true, vec![Value::from(1_usize), Value::from(2_usize), Value::from(3_usize)], - vec![ - Value::from(1_usize), - Value::from(2_usize), - Value::from(3_usize), - Value::from(1_usize), - Value::from(2_usize), - ], + vec![Value::from(1_usize), Value::from(2_usize)], Value::from(3_usize), ); - test_case_pop( - true, - vec![Value::from(1_usize)], - vec![Value::from(1_usize)], - Value::from(1_usize), - ); + test_case_pop(true, vec![Value::from(1_usize)], vec![], Value::from(1_usize)); test_case_pop( false, vec![Value::from(1_usize), Value::from(2_usize), Value::from(3_usize)], - vec![ - Value::from(1_usize), - Value::from(2_usize), - Value::from(3_usize), - Value::from(2_usize), - Value::from(3_usize), - ], + vec![Value::from(2_usize), Value::from(3_usize)], Value::from(1_usize), ); } @@ -579,19 +543,17 @@ mod tests { fn test_slice_insert_operation() { fn test_case_insert( array: Vec, - expected_mem: Vec, item: Value, index: Value, + expected_return: Vec, ) { let arguments = vec![ BrilligParameter::Array(vec![BrilligParameter::Simple], array.len()), BrilligParameter::Simple, BrilligParameter::Simple, ]; - let returns = vec![ - BrilligParameter::Array(vec![BrilligParameter::Simple], array.len() + 1), - BrilligParameter::Simple, - ]; + let returns = + vec![BrilligParameter::Array(vec![BrilligParameter::Simple], array.len() + 1)]; let (_, mut function_context, mut context) = create_test_environment(); @@ -623,87 +585,69 @@ mod tests { &[BrilligVariable::Simple(item_to_insert)], ); - context.return_instruction(&[ - target_vector.pointer, - target_vector.rc, - target_vector.size, - ]); + context.return_instruction(&[target_vector.pointer, target_vector.rc]); + let calldata = array.into_iter().chain(vec![item]).chain(vec![index]).collect(); let bytecode = create_entry_point_bytecode(context, arguments, returns).byte_code; - let vm = create_and_run_vm( - array.clone(), - vec![Value::from(0_usize), item, index], - &bytecode, - ); + let (vm, return_data_offset, return_data_size) = create_and_run_vm(calldata, &bytecode); + assert_eq!(return_data_size, expected_return.len()); - assert_eq!(vm.get_memory(), &expected_mem); - - assert_eq!(vm.get_registers().get(RegisterIndex(0)), Value::from(array.len())); - assert_eq!(vm.get_registers().get(RegisterIndex(1)), Value::from(array.len() + 1)); + assert_eq!( + vm.get_memory()[return_data_offset..(return_data_offset + expected_return.len())], + expected_return + ); } test_case_insert( vec![Value::from(1_usize), Value::from(2_usize), Value::from(3_usize)], + Value::from(27_usize), + Value::from(1_usize), vec![ - Value::from(1_usize), - Value::from(2_usize), - Value::from(3_usize), Value::from(1_usize), Value::from(27_usize), Value::from(2_usize), Value::from(3_usize), ], - Value::from(27_usize), - Value::from(1_usize), ); test_case_insert( vec![Value::from(1_usize), Value::from(2_usize), Value::from(3_usize)], + Value::from(27_usize), + Value::from(0_usize), vec![ - Value::from(1_usize), - Value::from(2_usize), - Value::from(3_usize), Value::from(27_usize), Value::from(1_usize), Value::from(2_usize), Value::from(3_usize), ], - Value::from(27_usize), - Value::from(0_usize), ); test_case_insert( vec![Value::from(1_usize), Value::from(2_usize), Value::from(3_usize)], + Value::from(27_usize), + Value::from(2_usize), vec![ - Value::from(1_usize), - Value::from(2_usize), - Value::from(3_usize), Value::from(1_usize), Value::from(2_usize), Value::from(27_usize), Value::from(3_usize), ], - Value::from(27_usize), - Value::from(2_usize), ); test_case_insert( vec![Value::from(1_usize), Value::from(2_usize), Value::from(3_usize)], + Value::from(27_usize), + Value::from(3_usize), vec![ - Value::from(1_usize), - Value::from(2_usize), - Value::from(3_usize), Value::from(1_usize), Value::from(2_usize), Value::from(3_usize), Value::from(27_usize), ], - Value::from(27_usize), - Value::from(3_usize), ); test_case_insert( vec![], - vec![Value::from(27_usize)], Value::from(27_usize), Value::from(0_usize), + vec![Value::from(27_usize)], ); } @@ -711,8 +655,8 @@ mod tests { fn test_slice_remove_operation() { fn test_case_remove( array: Vec, - expected_mem: Vec, index: Value, + expected_array: Vec, expected_removed_item: Value, ) { let arguments = vec![ @@ -722,7 +666,6 @@ mod tests { let returns = vec![ BrilligParameter::Array(vec![BrilligParameter::Simple], array.len() - 1), BrilligParameter::Simple, - BrilligParameter::Simple, ]; let (_, mut function_context, mut context) = create_test_environment(); @@ -755,65 +698,47 @@ mod tests { &[BrilligVariable::Simple(removed_item)], ); - context.return_instruction(&[ - target_vector.pointer, - target_vector.rc, - target_vector.size, - removed_item, - ]); + context.return_instruction(&[target_vector.pointer, target_vector.size, removed_item]); + + let calldata: Vec<_> = array.into_iter().chain(vec![index]).collect(); let bytecode = create_entry_point_bytecode(context, arguments, returns).byte_code; - let vm = create_and_run_vm(array.clone(), vec![Value::from(0_usize), index], &bytecode); + let (vm, return_data_offset, return_data_size) = create_and_run_vm(calldata, &bytecode); - assert_eq!(vm.get_memory(), &expected_mem); + let expected_return: Vec<_> = + expected_array.into_iter().chain(vec![expected_removed_item]).collect(); + assert_eq!(return_data_size, expected_return.len()); - assert_eq!(vm.get_registers().get(RegisterIndex(0)), Value::from(array.len())); - assert_eq!(vm.get_registers().get(RegisterIndex(1)), Value::from(array.len() - 1)); - assert_eq!(vm.get_registers().get(RegisterIndex(2)), expected_removed_item); + assert_eq!( + vm.get_memory()[return_data_offset..(return_data_offset + expected_return.len())], + expected_return + ); } test_case_remove( vec![Value::from(1_usize), Value::from(2_usize), Value::from(3_usize)], - vec![ - Value::from(1_usize), - Value::from(2_usize), - Value::from(3_usize), - Value::from(2_usize), - Value::from(3_usize), - ], Value::from(0_usize), + vec![Value::from(2_usize), Value::from(3_usize)], Value::from(1_usize), ); test_case_remove( vec![Value::from(1_usize), Value::from(2_usize), Value::from(3_usize)], - vec![ - Value::from(1_usize), - Value::from(2_usize), - Value::from(3_usize), - Value::from(1_usize), - Value::from(3_usize), - ], Value::from(1_usize), + vec![Value::from(1_usize), Value::from(3_usize)], Value::from(2_usize), ); test_case_remove( vec![Value::from(1_usize), Value::from(2_usize), Value::from(3_usize)], - vec![ - Value::from(1_usize), - Value::from(2_usize), - Value::from(3_usize), - Value::from(1_usize), - Value::from(2_usize), - ], Value::from(2_usize), + vec![Value::from(1_usize), Value::from(2_usize)], Value::from(3_usize), ); test_case_remove( - vec![Value::from(1_usize)], vec![Value::from(1_usize)], Value::from(0_usize), + vec![], Value::from(1_usize), ); } diff --git a/noir/compiler/noirc_evaluator/src/brillig/brillig_ir.rs b/noir/compiler/noirc_evaluator/src/brillig/brillig_ir.rs index db3cd501ca0..b094ff9c4c0 100644 --- a/noir/compiler/noirc_evaluator/src/brillig/brillig_ir.rs +++ b/noir/compiler/noirc_evaluator/src/brillig/brillig_ir.rs @@ -20,8 +20,8 @@ use self::{ }; use acvm::{ acir::brillig::{ - BinaryFieldOp, BinaryIntOp, BlackBoxOp, Opcode as BrilligOpcode, RegisterIndex, - RegisterOrMemory, Value, + BinaryFieldOp, BinaryIntOp, BlackBoxOp, MemoryAddress, Opcode as BrilligOpcode, Value, + ValueOrArray, }, FieldElement, }; @@ -64,18 +64,18 @@ impl ReservedRegisters { } /// Returns the stack pointer register. This will get used to allocate memory in runtime. - pub(crate) fn stack_pointer() -> RegisterIndex { - RegisterIndex::from(ReservedRegisters::StackPointer as usize) + pub(crate) fn stack_pointer() -> MemoryAddress { + MemoryAddress::from(ReservedRegisters::StackPointer as usize) } /// Returns the previous stack pointer register. This will be used to restore the registers after a fn call. - pub(crate) fn previous_stack_pointer() -> RegisterIndex { - RegisterIndex::from(ReservedRegisters::PreviousStackPointer as usize) + pub(crate) fn previous_stack_pointer() -> MemoryAddress { + MemoryAddress::from(ReservedRegisters::PreviousStackPointer as usize) } /// Returns a user defined (non-reserved) register index. - fn user_register_index(index: usize) -> RegisterIndex { - RegisterIndex::from(index + ReservedRegisters::len()) + fn user_register_index(index: usize) -> MemoryAddress { + MemoryAddress::from(index + ReservedRegisters::len()) } } @@ -109,7 +109,7 @@ impl BrilligContext { } } - pub(crate) fn set_allocated_registers(&mut self, allocated_registers: Vec) { + pub(crate) fn set_allocated_registers(&mut self, allocated_registers: Vec) { self.registers = BrilligRegistersContext::from_preallocated_registers(allocated_registers); } @@ -127,27 +127,28 @@ impl BrilligContext { /// in `pointer_register` pub(crate) fn allocate_fixed_length_array( &mut self, - pointer_register: RegisterIndex, + pointer_register: MemoryAddress, size: usize, ) { // debug_show handled by allocate_array_instruction let size_register = self.make_constant(size.into()); self.allocate_array_instruction(pointer_register, size_register); + self.deallocate_register(size_register); } /// Allocates an array of size contained in size_register and stores the /// pointer to the array in `pointer_register` pub(crate) fn allocate_array_instruction( &mut self, - pointer_register: RegisterIndex, - size_register: RegisterIndex, + pointer_register: MemoryAddress, + size_register: MemoryAddress, ) { self.debug_show.allocate_array_instruction(pointer_register, size_register); self.set_array_pointer(pointer_register); self.update_stack_pointer(size_register); } - pub(crate) fn set_array_pointer(&mut self, pointer_register: RegisterIndex) { + pub(crate) fn set_array_pointer(&mut self, pointer_register: MemoryAddress) { self.debug_show.mov_instruction(pointer_register, ReservedRegisters::stack_pointer()); self.push_opcode(BrilligOpcode::Mov { destination: pointer_register, @@ -155,7 +156,7 @@ impl BrilligContext { }); } - pub(crate) fn update_stack_pointer(&mut self, size_register: RegisterIndex) { + pub(crate) fn update_stack_pointer(&mut self, size_register: MemoryAddress) { self.memory_op( ReservedRegisters::stack_pointer(), size_register, @@ -168,7 +169,7 @@ impl BrilligContext { /// pointer to the array in `pointer_register` fn allocate_variable_reference_instruction( &mut self, - pointer_register: RegisterIndex, + pointer_register: MemoryAddress, size: usize, ) { self.debug_show.allocate_instruction(pointer_register); @@ -184,16 +185,17 @@ impl BrilligContext { ReservedRegisters::stack_pointer(), BinaryIntOp::Add, ); + self.deallocate_register(size_register); } pub(crate) fn allocate_simple_reference_instruction( &mut self, - pointer_register: RegisterIndex, + pointer_register: MemoryAddress, ) { self.allocate_variable_reference_instruction(pointer_register, 1); } - pub(crate) fn allocate_array_reference_instruction(&mut self, pointer_register: RegisterIndex) { + pub(crate) fn allocate_array_reference_instruction(&mut self, pointer_register: MemoryAddress) { self.allocate_variable_reference_instruction( pointer_register, BrilligArray::registers_count(), @@ -202,7 +204,7 @@ impl BrilligContext { pub(crate) fn allocate_vector_reference_instruction( &mut self, - pointer_register: RegisterIndex, + pointer_register: MemoryAddress, ) { self.allocate_variable_reference_instruction( pointer_register, @@ -213,9 +215,9 @@ impl BrilligContext { /// Gets the value in the array at index `index` and stores it in `result` pub(crate) fn array_get( &mut self, - array_ptr: RegisterIndex, - index: RegisterIndex, - result: RegisterIndex, + array_ptr: MemoryAddress, + index: MemoryAddress, + result: MemoryAddress, ) { self.debug_show.array_get(array_ptr, index, result); // Computes array_ptr + index, ie array[index] @@ -235,9 +237,9 @@ impl BrilligContext { /// Sets the item in the array at index `index` to `value` pub(crate) fn array_set( &mut self, - array_ptr: RegisterIndex, - index: RegisterIndex, - value: RegisterIndex, + array_ptr: MemoryAddress, + index: MemoryAddress, + value: MemoryAddress, ) { self.debug_show.array_set(array_ptr, index, value); // Computes array_ptr + index, ie array[index] @@ -258,9 +260,9 @@ impl BrilligContext { /// Into the array pointed by destination pub(crate) fn copy_array_instruction( &mut self, - source_pointer: RegisterIndex, - destination_pointer: RegisterIndex, - num_elements_register: RegisterIndex, + source_pointer: MemoryAddress, + destination_pointer: MemoryAddress, + num_elements_register: MemoryAddress, ) { self.debug_show.copy_array_instruction( source_pointer, @@ -280,9 +282,9 @@ impl BrilligContext { /// This instruction will issue a loop that will iterate iteration_count times /// The body of the loop should be issued by the caller in the on_iteration closure. - pub(crate) fn loop_instruction(&mut self, iteration_count: RegisterIndex, on_iteration: F) + pub(crate) fn loop_instruction(&mut self, iteration_count: MemoryAddress, on_iteration: F) where - F: FnOnce(&mut BrilligContext, RegisterIndex), + F: FnOnce(&mut BrilligContext, MemoryAddress), { let iterator_register = self.make_constant(0_u128.into()); @@ -327,7 +329,7 @@ impl BrilligContext { /// functions to allow the given function to mutably alias its environment. pub(crate) fn branch_instruction( &mut self, - condition: RegisterIndex, + condition: MemoryAddress, mut f: impl FnMut(&mut BrilligContext, bool), ) { // Reserve 3 sections @@ -394,7 +396,7 @@ impl BrilligContext { /// Adds a unresolved `JumpIf` instruction to the bytecode. pub(crate) fn jump_if_instruction( &mut self, - condition: RegisterIndex, + condition: MemoryAddress, target_label: T, ) { self.debug_show.jump_if_instruction(condition, target_label.to_string()); @@ -414,14 +416,14 @@ impl BrilligContext { } /// Allocates an unused register. - pub(crate) fn allocate_register(&mut self) -> RegisterIndex { + pub(crate) fn allocate_register(&mut self) -> MemoryAddress { self.registers.allocate_register() } /// Push a register to the deallocation list, ready for reuse. /// TODO(AD): currently, register deallocation is only done with immediate values. /// TODO(AD): See https://github.com/noir-lang/noir/issues/1720 - pub(crate) fn deallocate_register(&mut self, register_index: RegisterIndex) { + pub(crate) fn deallocate_register(&mut self, register_index: MemoryAddress) { self.registers.deallocate_register(register_index); } } @@ -431,7 +433,7 @@ impl BrilligContext { /// is false. pub(crate) fn constrain_instruction( &mut self, - condition: RegisterIndex, + condition: MemoryAddress, assert_message: Option, ) { self.debug_show.constrain_instruction(condition); @@ -453,7 +455,7 @@ impl BrilligContext { /// Brillig does not have an explicit return instruction, so this /// method will move all register values to the first `N` values in /// the VM. - pub(crate) fn return_instruction(&mut self, return_registers: &[RegisterIndex]) { + pub(crate) fn return_instruction(&mut self, return_registers: &[MemoryAddress]) { self.debug_show.return_instruction(return_registers); let mut sources = Vec::with_capacity(return_registers.len()); let mut destinations = Vec::with_capacity(return_registers.len()); @@ -473,8 +475,8 @@ impl BrilligContext { /// It first moves all sources to new allocated registers to avoid overwriting. pub(crate) fn mov_registers_to_registers_instruction( &mut self, - sources: Vec, - destinations: Vec, + sources: Vec, + destinations: Vec, ) { let new_sources: Vec<_> = sources .iter() @@ -493,7 +495,7 @@ impl BrilligContext { /// Emits a `mov` instruction. /// /// Copies the value at `source` into `destination` - pub(crate) fn mov_instruction(&mut self, destination: RegisterIndex, source: RegisterIndex) { + pub(crate) fn mov_instruction(&mut self, destination: MemoryAddress, source: MemoryAddress) { self.debug_show.mov_instruction(destination, source); self.push_opcode(BrilligOpcode::Mov { destination, source }); } @@ -504,9 +506,9 @@ impl BrilligContext { /// and store the result in the `result` register. pub(crate) fn binary_instruction( &mut self, - lhs: RegisterIndex, - rhs: RegisterIndex, - result: RegisterIndex, + lhs: MemoryAddress, + rhs: MemoryAddress, + result: MemoryAddress, operation: BrilligBinaryOp, ) { self.debug_show.binary_instruction(lhs, rhs, result, operation.clone()); @@ -527,7 +529,7 @@ impl BrilligContext { } /// Stores the value of `constant` in the `result` register - pub(crate) fn const_instruction(&mut self, result: RegisterIndex, constant: Value) { + pub(crate) fn const_instruction(&mut self, result: MemoryAddress, constant: Value) { self.debug_show.const_instruction(result, constant); self.push_opcode(BrilligOpcode::Const { destination: result, value: constant }); } @@ -538,9 +540,9 @@ impl BrilligContext { /// in Brillig. pub(crate) fn not_instruction( &mut self, - input: RegisterIndex, + input: MemoryAddress, bit_size: u32, - result: RegisterIndex, + result: MemoryAddress, ) { self.debug_show.not_instruction(input, bit_size, result); // Compile !x as ((-1) - x) @@ -565,8 +567,8 @@ impl BrilligContext { pub(crate) fn foreign_call_instruction( &mut self, func_name: String, - inputs: &[RegisterOrMemory], - outputs: &[RegisterOrMemory], + inputs: &[ValueOrArray], + outputs: &[ValueOrArray], ) { self.debug_show.foreign_call_instruction(func_name.clone(), inputs, outputs); let opcode = BrilligOpcode::ForeignCall { @@ -580,8 +582,8 @@ impl BrilligContext { /// Emits a load instruction pub(crate) fn load_instruction( &mut self, - destination: RegisterIndex, - source_pointer: RegisterIndex, + destination: MemoryAddress, + source_pointer: MemoryAddress, ) { self.debug_show.load_instruction(destination, source_pointer); self.push_opcode(BrilligOpcode::Load { destination, source_pointer }); @@ -591,7 +593,7 @@ impl BrilligContext { pub(crate) fn load_variable_instruction( &mut self, destination: BrilligVariable, - variable_pointer: RegisterIndex, + variable_pointer: MemoryAddress, ) { match destination { BrilligVariable::Simple(register_index) => { @@ -630,8 +632,8 @@ impl BrilligContext { /// Emits a store instruction pub(crate) fn store_instruction( &mut self, - destination_pointer: RegisterIndex, - source: RegisterIndex, + destination_pointer: MemoryAddress, + source: MemoryAddress, ) { self.debug_show.store_instruction(destination_pointer, source); self.push_opcode(BrilligOpcode::Store { destination_pointer, source }); @@ -640,7 +642,7 @@ impl BrilligContext { /// Stores a variable by saving its registers to memory pub(crate) fn store_variable_instruction( &mut self, - variable_pointer: RegisterIndex, + variable_pointer: MemoryAddress, source: BrilligVariable, ) { match source { @@ -650,7 +652,7 @@ impl BrilligContext { BrilligVariable::BrilligArray(BrilligArray { pointer, size: _, rc }) => { self.store_instruction(variable_pointer, pointer); - let rc_pointer: RegisterIndex = self.allocate_register(); + let rc_pointer: MemoryAddress = self.allocate_register(); self.mov_instruction(rc_pointer, variable_pointer); self.usize_op_in_place(rc_pointer, BinaryIntOp::Add, 1_usize); self.store_instruction(rc_pointer, rc); @@ -664,7 +666,7 @@ impl BrilligContext { self.usize_op_in_place(size_pointer, BinaryIntOp::Add, 1_usize); self.store_instruction(size_pointer, size); - let rc_pointer: RegisterIndex = self.allocate_register(); + let rc_pointer: MemoryAddress = self.allocate_register(); self.mov_instruction(rc_pointer, variable_pointer); self.usize_op_in_place(rc_pointer, BinaryIntOp::Add, 2_usize); self.store_instruction(rc_pointer, rc); @@ -685,8 +687,8 @@ impl BrilligContext { /// For Brillig, all integer operations will overflow as its cheap. pub(crate) fn truncate_instruction( &mut self, - destination_of_truncated_value: RegisterIndex, - value_to_truncate: RegisterIndex, + destination_of_truncated_value: MemoryAddress, + value_to_truncate: MemoryAddress, bit_size: u32, ) { self.debug_show.truncate_instruction( @@ -715,11 +717,11 @@ impl BrilligContext { /// Emits a stop instruction pub(crate) fn stop_instruction(&mut self) { self.debug_show.stop_instruction(); - self.push_opcode(BrilligOpcode::Stop); + self.push_opcode(BrilligOpcode::Stop { return_data_offset: 0, return_data_size: 0 }); } /// Returns a register which holds the value of a constant - pub(crate) fn make_constant(&mut self, constant: Value) -> RegisterIndex { + pub(crate) fn make_constant(&mut self, constant: Value) -> MemoryAddress { let register = self.allocate_register(); self.const_instruction(register, constant); register @@ -736,9 +738,9 @@ impl BrilligContext { /// to other binary instructions. pub(crate) fn modulo_instruction( &mut self, - result_register: RegisterIndex, - left: RegisterIndex, - right: RegisterIndex, + result_register: MemoryAddress, + left: MemoryAddress, + right: MemoryAddress, bit_size: u32, signed: bool, ) { @@ -791,12 +793,12 @@ impl BrilligContext { } /// Returns the i'th register after the reserved ones - pub(crate) fn register(&self, i: usize) -> RegisterIndex { - RegisterIndex::from(ReservedRegisters::NUM_RESERVED_REGISTERS + i) + pub(crate) fn register(&self, i: usize) -> MemoryAddress { + MemoryAddress::from(ReservedRegisters::NUM_RESERVED_REGISTERS + i) } /// Saves all of the registers that have been used up until this point. - fn save_registers_of_vars(&mut self, vars: &[BrilligVariable]) -> Vec { + fn save_registers_of_vars(&mut self, vars: &[BrilligVariable]) -> Vec { // Save all of the used registers at this point in memory // because the function call will/may overwrite them. // @@ -822,7 +824,7 @@ impl BrilligContext { } /// Loads all of the registers that have been save by save_all_used_registers. - fn load_all_saved_registers(&mut self, used_registers: &[RegisterIndex]) { + fn load_all_saved_registers(&mut self, used_registers: &[MemoryAddress]) { // Load all of the used registers that we saved. // We do all the reverse operations of save_all_used_registers. // Iterate our registers in reverse @@ -839,7 +841,7 @@ impl BrilligContext { /// Utility method to perform a binary instruction with a constant value in place pub(crate) fn usize_op_in_place( &mut self, - destination: RegisterIndex, + destination: MemoryAddress, op: BinaryIntOp, constant: usize, ) { @@ -849,8 +851,8 @@ impl BrilligContext { /// Utility method to perform a binary instruction with a constant value pub(crate) fn usize_op( &mut self, - operand: RegisterIndex, - destination: RegisterIndex, + operand: MemoryAddress, + destination: MemoryAddress, op: BinaryIntOp, constant: usize, ) { @@ -863,9 +865,9 @@ impl BrilligContext { /// Utility method to perform a binary instruction with a memory address pub(crate) fn memory_op( &mut self, - lhs: RegisterIndex, - rhs: RegisterIndex, - destination: RegisterIndex, + lhs: MemoryAddress, + rhs: MemoryAddress, + destination: MemoryAddress, op: BinaryIntOp, ) { self.binary_instruction( @@ -881,9 +883,9 @@ impl BrilligContext { // Move argument values to the front of the register indices. pub(crate) fn pre_call_save_registers_prep_args( &mut self, - arguments: &[RegisterIndex], + arguments: &[MemoryAddress], variables_to_save: &[BrilligVariable], - ) -> Vec { + ) -> Vec { // Save all the registers we have used to the stack. let saved_registers = self.save_registers_of_vars(variables_to_save); @@ -902,8 +904,8 @@ impl BrilligContext { // Load all the registers we have previous saved in save_registers_prep_args. pub(crate) fn post_call_prep_returns_load_registers( &mut self, - result_registers: &[RegisterIndex], - saved_registers: &[RegisterIndex], + result_registers: &[MemoryAddress], + saved_registers: &[MemoryAddress], ) { // Allocate our result registers and write into them // We assume the return values of our call are held in 0..num results register indices @@ -938,10 +940,10 @@ impl BrilligContext { /// And the radix register limb_count times to the target vector. pub(crate) fn radix_instruction( &mut self, - source: RegisterIndex, + source: MemoryAddress, target_vector: BrilligVector, - radix: RegisterIndex, - limb_count: RegisterIndex, + radix: MemoryAddress, + limb_count: MemoryAddress, big_endian: bool, ) { self.mov_instruction(target_vector.size, limb_count); @@ -951,7 +953,7 @@ impl BrilligContext { let shifted_register = self.allocate_register(); self.mov_instruction(shifted_register, source); - let modulus_register: RegisterIndex = self.allocate_register(); + let modulus_register: MemoryAddress = self.allocate_register(); self.loop_instruction(target_vector.size, |ctx, iterator_register| { // Compute the modulus @@ -1042,10 +1044,10 @@ pub(crate) mod tests { use std::vec; use acvm::acir::brillig::{ - BinaryIntOp, ForeignCallParam, ForeignCallResult, HeapVector, RegisterIndex, - RegisterOrMemory, Value, + BinaryIntOp, ForeignCallParam, ForeignCallResult, HeapVector, MemoryAddress, Value, + ValueOrArray, }; - use acvm::brillig_vm::{Registers, VMStatus, VM}; + use acvm::brillig_vm::{VMStatus, VM}; use acvm::{BlackBoxFunctionSolver, BlackBoxResolutionError, FieldElement}; use crate::brillig::brillig_ir::BrilligContext; @@ -1096,14 +1098,6 @@ pub(crate) mod tests { ) -> Result<(FieldElement, FieldElement), BlackBoxResolutionError> { panic!("Path not trodden by this test") } - - fn ec_double( - &self, - _input_x: &FieldElement, - _input_y: &FieldElement, - ) -> Result<(FieldElement, FieldElement), BlackBoxResolutionError> { - panic!("Path not trodden by this test") - } } pub(crate) fn create_context() -> BrilligContext { @@ -1125,21 +1119,17 @@ pub(crate) mod tests { } pub(crate) fn create_and_run_vm( - memory: Vec, - param_registers: Vec, + calldata: Vec, bytecode: &[BrilligOpcode], - ) -> VM<'_, DummyBlackBoxSolver> { - let mut vm = VM::new( - Registers { inner: param_registers }, - memory, - bytecode, - vec![], - &DummyBlackBoxSolver, - ); + ) -> (VM<'_, DummyBlackBoxSolver>, usize, usize) { + let mut vm = VM::new(calldata, bytecode, vec![], &DummyBlackBoxSolver); let status = vm.process_opcodes(); - assert_eq!(status, VMStatus::Finished); - vm + if let VMStatus::Finished { return_data_offset, return_data_size } = status { + (vm, return_data_offset, return_data_size) + } else { + panic!("VM did not finish") + } } /// Test a Brillig foreign call returning a vector @@ -1158,18 +1148,18 @@ pub(crate) mod tests { let mut context = BrilligContext::new(true); let r_stack = ReservedRegisters::stack_pointer(); // Start stack pointer at 0 - context.const_instruction(r_stack, Value::from(0_usize)); - let r_input_size = RegisterIndex::from(ReservedRegisters::len()); - let r_array_ptr = RegisterIndex::from(ReservedRegisters::len() + 1); - let r_output_size = RegisterIndex::from(ReservedRegisters::len() + 2); - let r_equality = RegisterIndex::from(ReservedRegisters::len() + 3); + context.const_instruction(r_stack, Value::from(ReservedRegisters::len() + 3)); + let r_input_size = MemoryAddress::from(ReservedRegisters::len()); + let r_array_ptr = MemoryAddress::from(ReservedRegisters::len() + 1); + let r_output_size = MemoryAddress::from(ReservedRegisters::len() + 2); + let r_equality = MemoryAddress::from(ReservedRegisters::len() + 3); context.const_instruction(r_input_size, Value::from(12_usize)); // copy our stack frame to r_array_ptr context.mov_instruction(r_array_ptr, r_stack); context.foreign_call_instruction( "make_number_sequence".into(), - &[RegisterOrMemory::RegisterIndex(r_input_size)], - &[RegisterOrMemory::HeapVector(HeapVector { pointer: r_stack, size: r_output_size })], + &[ValueOrArray::MemoryAddress(r_input_size)], + &[ValueOrArray::HeapVector(HeapVector { pointer: r_stack, size: r_output_size })], ); // push stack frame by r_returned_size context.memory_op(r_stack, r_output_size, r_stack, BinaryIntOp::Add); @@ -1186,13 +1176,12 @@ pub(crate) mod tests { let bytecode = context.artifact().finish().byte_code; let number_sequence: Vec = (0_usize..12_usize).map(Value::from).collect(); let mut vm = VM::new( - Registers { inner: vec![] }, vec![], &bytecode, vec![ForeignCallResult { values: vec![ForeignCallParam::Array(number_sequence)] }], &DummyBlackBoxSolver, ); let status = vm.process_opcodes(); - assert_eq!(status, VMStatus::Finished); + assert_eq!(status, VMStatus::Finished { return_data_offset: 0, return_data_size: 0 }); } } diff --git a/noir/compiler/noirc_evaluator/src/brillig/brillig_ir/artifact.rs b/noir/compiler/noirc_evaluator/src/brillig/brillig_ir/artifact.rs index 9b8c3913123..437774da157 100644 --- a/noir/compiler/noirc_evaluator/src/brillig/brillig_ir/artifact.rs +++ b/noir/compiler/noirc_evaluator/src/brillig/brillig_ir/artifact.rs @@ -97,7 +97,7 @@ impl BrilligArtifact { // Replace STOP with RETURN because this is not the end of the program now. let stop_position = byte_code .iter() - .position(|opcode| matches!(opcode, BrilligOpcode::Stop)) + .position(|opcode| matches!(opcode, BrilligOpcode::Stop { .. })) .expect("Trying to link with a function that does not have a stop opcode"); byte_code[stop_position] = BrilligOpcode::Return; diff --git a/noir/compiler/noirc_evaluator/src/brillig/brillig_ir/brillig_variable.rs b/noir/compiler/noirc_evaluator/src/brillig/brillig_ir/brillig_variable.rs index 46c54d55ecb..b3fe30c40b4 100644 --- a/noir/compiler/noirc_evaluator/src/brillig/brillig_ir/brillig_variable.rs +++ b/noir/compiler/noirc_evaluator/src/brillig/brillig_ir/brillig_variable.rs @@ -1,12 +1,12 @@ -use acvm::brillig_vm::brillig::{HeapArray, HeapVector, RegisterIndex, RegisterOrMemory}; +use acvm::brillig_vm::brillig::{HeapArray, HeapVector, MemoryAddress, ValueOrArray}; use serde::{Deserialize, Serialize}; /// The representation of a noir array in the Brillig IR #[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize, Copy)] pub(crate) struct BrilligArray { - pub(crate) pointer: RegisterIndex, + pub(crate) pointer: MemoryAddress, pub(crate) size: usize, - pub(crate) rc: RegisterIndex, + pub(crate) rc: MemoryAddress, } impl BrilligArray { @@ -18,7 +18,7 @@ impl BrilligArray { 2 } - pub(crate) fn extract_registers(self) -> Vec { + pub(crate) fn extract_registers(self) -> Vec { vec![self.pointer, self.rc] } } @@ -26,9 +26,9 @@ impl BrilligArray { /// The representation of a noir slice in the Brillig IR #[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize, Copy)] pub(crate) struct BrilligVector { - pub(crate) pointer: RegisterIndex, - pub(crate) size: RegisterIndex, - pub(crate) rc: RegisterIndex, + pub(crate) pointer: MemoryAddress, + pub(crate) size: MemoryAddress, + pub(crate) rc: MemoryAddress, } impl BrilligVector { @@ -40,7 +40,7 @@ impl BrilligVector { 3 } - pub(crate) fn extract_registers(self) -> Vec { + pub(crate) fn extract_registers(self) -> Vec { vec![self.pointer, self.size, self.rc] } } @@ -48,13 +48,13 @@ impl BrilligVector { /// The representation of a noir value in the Brillig IR #[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize, Copy)] pub(crate) enum BrilligVariable { - Simple(RegisterIndex), + Simple(MemoryAddress), BrilligArray(BrilligArray), BrilligVector(BrilligVector), } impl BrilligVariable { - pub(crate) fn extract_register(self) -> RegisterIndex { + pub(crate) fn extract_register(self) -> MemoryAddress { match self { BrilligVariable::Simple(register_index) => register_index, _ => unreachable!("ICE: Expected register, got {self:?}"), @@ -75,7 +75,7 @@ impl BrilligVariable { } } - pub(crate) fn extract_registers(self) -> Vec { + pub(crate) fn extract_registers(self) -> Vec { match self { BrilligVariable::Simple(register_index) => vec![register_index], BrilligVariable::BrilligArray(array) => array.extract_registers(), @@ -83,16 +83,12 @@ impl BrilligVariable { } } - pub(crate) fn to_register_or_memory(self) -> RegisterOrMemory { + pub(crate) fn to_register_or_memory(self) -> ValueOrArray { match self { - BrilligVariable::Simple(register_index) => { - RegisterOrMemory::RegisterIndex(register_index) - } - BrilligVariable::BrilligArray(array) => { - RegisterOrMemory::HeapArray(array.to_heap_array()) - } + BrilligVariable::Simple(register_index) => ValueOrArray::MemoryAddress(register_index), + BrilligVariable::BrilligArray(array) => ValueOrArray::HeapArray(array.to_heap_array()), BrilligVariable::BrilligVector(vector) => { - RegisterOrMemory::HeapVector(vector.to_heap_vector()) + ValueOrArray::HeapVector(vector.to_heap_vector()) } } } diff --git a/noir/compiler/noirc_evaluator/src/brillig/brillig_ir/debug_show.rs b/noir/compiler/noirc_evaluator/src/brillig/brillig_ir/debug_show.rs index dc8c6b6694c..28359f8dcea 100644 --- a/noir/compiler/noirc_evaluator/src/brillig/brillig_ir/debug_show.rs +++ b/noir/compiler/noirc_evaluator/src/brillig/brillig_ir/debug_show.rs @@ -3,8 +3,8 @@ use super::BrilligBinaryOp; use crate::brillig::brillig_ir::{ReservedRegisters, BRILLIG_MEMORY_ADDRESSING_BIT_SIZE}; use acvm::acir::brillig::{ - BinaryFieldOp, BinaryIntOp, BlackBoxOp, HeapArray, HeapVector, RegisterIndex, RegisterOrMemory, - Value, + BinaryFieldOp, BinaryIntOp, BlackBoxOp, HeapArray, HeapVector, MemoryAddress, Value, + ValueOrArray, }; /// Trait for converting values into debug-friendly strings. @@ -24,7 +24,7 @@ macro_rules! default_to_string_impl { default_to_string_impl! { str usize u32 } -impl DebugToString for RegisterIndex { +impl DebugToString for MemoryAddress { fn debug_to_string(&self) -> String { if *self == ReservedRegisters::stack_pointer() { "Stack".into() @@ -112,12 +112,12 @@ impl DebugToString for Value { } } -impl DebugToString for RegisterOrMemory { +impl DebugToString for ValueOrArray { fn debug_to_string(&self) -> String { match self { - RegisterOrMemory::RegisterIndex(index) => index.debug_to_string(), - RegisterOrMemory::HeapArray(heap_array) => heap_array.debug_to_string(), - RegisterOrMemory::HeapVector(vector) => vector.debug_to_string(), + ValueOrArray::MemoryAddress(index) => index.debug_to_string(), + ValueOrArray::HeapArray(heap_array) => heap_array.debug_to_string(), + ValueOrArray::HeapVector(vector) => vector.debug_to_string(), } } } @@ -152,15 +152,15 @@ impl DebugShow { /// Emits brillig bytecode to jump to a trap condition if `condition` /// is false. - pub(crate) fn constrain_instruction(&self, condition: RegisterIndex) { + pub(crate) fn constrain_instruction(&self, condition: MemoryAddress) { debug_println!(self.enable_debug_trace, " ASSERT {} != 0", condition); } /// Processes a return instruction. - pub(crate) fn return_instruction(&self, return_registers: &[RegisterIndex]) { + pub(crate) fn return_instruction(&self, return_registers: &[MemoryAddress]) { let registers_string = return_registers .iter() - .map(RegisterIndex::debug_to_string) + .map(MemoryAddress::debug_to_string) .collect::>() .join(", "); @@ -168,32 +168,32 @@ impl DebugShow { } /// Emits a `mov` instruction. - pub(crate) fn mov_instruction(&self, destination: RegisterIndex, source: RegisterIndex) { + pub(crate) fn mov_instruction(&self, destination: MemoryAddress, source: MemoryAddress) { debug_println!(self.enable_debug_trace, " MOV {}, {}", destination, source); } /// Processes a binary instruction according `operation`. pub(crate) fn binary_instruction( &self, - lhs: RegisterIndex, - rhs: RegisterIndex, - result: RegisterIndex, + lhs: MemoryAddress, + rhs: MemoryAddress, + result: MemoryAddress, operation: BrilligBinaryOp, ) { debug_println!(self.enable_debug_trace, " {} = {} {} {}", result, lhs, operation, rhs); } /// Stores the value of `constant` in the `result` register - pub(crate) fn const_instruction(&self, result: RegisterIndex, constant: Value) { + pub(crate) fn const_instruction(&self, result: MemoryAddress, constant: Value) { debug_println!(self.enable_debug_trace, " CONST {} = {}", result, constant); } /// Processes a not instruction. Append with "_" as this is a high-level instruction. pub(crate) fn not_instruction( &self, - condition: RegisterIndex, + condition: MemoryAddress, bit_size: u32, - result: RegisterIndex, + result: MemoryAddress, ) { debug_println!(self.enable_debug_trace, " i{}_NOT {} = !{}", bit_size, result, condition); } @@ -202,8 +202,8 @@ impl DebugShow { pub(crate) fn foreign_call_instruction( &self, func_name: String, - inputs: &[RegisterOrMemory], - outputs: &[RegisterOrMemory], + inputs: &[ValueOrArray], + outputs: &[ValueOrArray], ) { debug_println!( self.enable_debug_trace, @@ -217,8 +217,8 @@ impl DebugShow { /// Emits a load instruction pub(crate) fn load_instruction( &self, - destination: RegisterIndex, - source_pointer: RegisterIndex, + destination: MemoryAddress, + source_pointer: MemoryAddress, ) { debug_println!(self.enable_debug_trace, " LOAD {} = *{}", destination, source_pointer); } @@ -226,8 +226,8 @@ impl DebugShow { /// Emits a store instruction pub(crate) fn store_instruction( &self, - destination_pointer: RegisterIndex, - source: RegisterIndex, + destination_pointer: MemoryAddress, + source: MemoryAddress, ) { debug_println!(self.enable_debug_trace, " STORE *{} = {}", destination_pointer, source); } @@ -240,8 +240,8 @@ impl DebugShow { /// Debug function for allocate_array_instruction pub(crate) fn allocate_array_instruction( &self, - pointer_register: RegisterIndex, - size_register: RegisterIndex, + pointer_register: MemoryAddress, + size_register: MemoryAddress, ) { debug_println!( self.enable_debug_trace, @@ -252,16 +252,16 @@ impl DebugShow { } /// Debug function for allocate_instruction - pub(crate) fn allocate_instruction(&self, pointer_register: RegisterIndex) { + pub(crate) fn allocate_instruction(&self, pointer_register: MemoryAddress) { debug_println!(self.enable_debug_trace, " ALLOCATE {} ", pointer_register); } /// Debug function for array_get pub(crate) fn array_get( &self, - array_ptr: RegisterIndex, - index: RegisterIndex, - result: RegisterIndex, + array_ptr: MemoryAddress, + index: MemoryAddress, + result: MemoryAddress, ) { debug_println!( self.enable_debug_trace, @@ -275,9 +275,9 @@ impl DebugShow { /// Debug function for array_set pub(crate) fn array_set( &self, - array_ptr: RegisterIndex, - index: RegisterIndex, - value: RegisterIndex, + array_ptr: MemoryAddress, + index: MemoryAddress, + value: MemoryAddress, ) { debug_println!(self.enable_debug_trace, " ARRAY_SET {}[{}] = {}", array_ptr, index, value); } @@ -285,9 +285,9 @@ impl DebugShow { /// Debug function for copy_array_instruction pub(crate) fn copy_array_instruction( &self, - source: RegisterIndex, - destination: RegisterIndex, - num_elements_register: RegisterIndex, + source: MemoryAddress, + destination: MemoryAddress, + num_elements_register: MemoryAddress, ) { debug_println!( self.enable_debug_trace, @@ -314,7 +314,7 @@ impl DebugShow { /// Debug function for jump_if_instruction pub(crate) fn jump_if_instruction( &self, - condition: RegisterIndex, + condition: MemoryAddress, target_label: T, ) { debug_println!( @@ -328,8 +328,8 @@ impl DebugShow { /// Debug function for cast_instruction pub(crate) fn truncate_instruction( &self, - destination: RegisterIndex, - source: RegisterIndex, + destination: MemoryAddress, + source: MemoryAddress, target_bit_size: u32, ) { debug_println!( @@ -413,15 +413,6 @@ impl DebugShow { result ); } - BlackBoxOp::EmbeddedCurveDouble { input1_x, input1_y, result } => { - debug_println!( - self.enable_debug_trace, - " EMBEDDED_CURVE_DOUBLE ({} {}) -> {}", - input1_x, - input1_y, - result - ); - } BlackBoxOp::PedersenCommitment { inputs, domain_separator, output } => { debug_println!( self.enable_debug_trace, @@ -457,6 +448,77 @@ impl DebugShow { result ); } + BlackBoxOp::BigIntAdd { lhs, rhs, output } => { + debug_println!( + self.enable_debug_trace, + " BIGINT_ADD {} {} -> {}", + lhs, + rhs, + output + ); + } + BlackBoxOp::BigIntNeg { lhs, rhs, output } => { + debug_println!( + self.enable_debug_trace, + " BIGINT_NEG {} {} -> {}", + lhs, + rhs, + output + ); + } + BlackBoxOp::BigIntMul { lhs, rhs, output } => { + debug_println!( + self.enable_debug_trace, + " BIGINT_MUL {} {} -> {}", + lhs, + rhs, + output + ); + } + BlackBoxOp::BigIntDiv { lhs, rhs, output } => { + debug_println!( + self.enable_debug_trace, + " BIGINT_DIV {} {} -> {}", + lhs, + rhs, + output + ); + } + BlackBoxOp::BigIntFromLeBytes { inputs, modulus, output } => { + debug_println!( + self.enable_debug_trace, + " BIGINT_FROM_LE_BYTES {} {} -> {}", + inputs, + modulus, + output + ); + } + BlackBoxOp::BigIntToLeBytes { input, output } => { + debug_println!( + self.enable_debug_trace, + " BIGINT_TO_LE_BYTES {} -> {}", + input, + output + ); + } + BlackBoxOp::Poseidon2Permutation { message, output, len } => { + debug_println!( + self.enable_debug_trace, + " POSEIDON2_PERMUTATION {} {} -> {}", + message, + len, + output + ); + } + BlackBoxOp::Sha256Compression { input, hash_values, output } => { + debug_println!( + self.enable_debug_trace, + " SHA256COMPRESSION {} {} -> {}", + input, + hash_values, + output + ); + } } } diff --git a/noir/compiler/noirc_evaluator/src/brillig/brillig_ir/entry_point.rs b/noir/compiler/noirc_evaluator/src/brillig/brillig_ir/entry_point.rs index 36ca414f38e..b9d95788b80 100644 --- a/noir/compiler/noirc_evaluator/src/brillig/brillig_ir/entry_point.rs +++ b/noir/compiler/noirc_evaluator/src/brillig/brillig_ir/entry_point.rs @@ -1,13 +1,13 @@ -use crate::brillig::brillig_ir::ReservedRegisters; - use super::{ artifact::{BrilligArtifact, BrilligParameter}, brillig_variable::{BrilligArray, BrilligVariable}, debug_show::DebugShow, registers::BrilligRegistersContext, - BrilligContext, + BrilligContext, ReservedRegisters, }; -use acvm::acir::brillig::{Opcode as BrilligOpcode, RegisterIndex}; +use acvm::acir::brillig::{MemoryAddress, Opcode as BrilligOpcode}; + +pub(crate) const MAX_STACK_SIZE: usize = 1024; impl BrilligContext { /// Creates an entry point artifact that will jump to the function label provided. @@ -25,87 +25,85 @@ impl BrilligContext { debug_show: DebugShow::new(false), }; - context.entry_point_instruction(arguments); + context.entry_point_instruction(&arguments, &return_parameters); context.add_external_call_instruction(target_function); - context.exit_point_instruction(return_parameters); + context.exit_point_instruction(&arguments, &return_parameters); context.artifact() } /// Adds the instructions needed to handle entry point parameters - /// The runtime will leave the parameters in the first `n` registers. - /// Arrays will be passed as pointers to the first element, with all the nested arrays flattened. - /// First, reserve the registers that contain the parameters. - /// This function also sets the starting value of the reserved registers - fn entry_point_instruction(&mut self, arguments: Vec) { - let preallocated_registers: Vec<_> = - arguments.iter().enumerate().map(|(i, _)| RegisterIndex::from(i)).collect(); - self.set_allocated_registers(preallocated_registers.clone()); - - // Then allocate and initialize the variables that will hold the parameters - let argument_variables: Vec<_> = arguments + /// The runtime will leave the parameters in calldata. + /// Arrays will be passed flattened. + fn entry_point_instruction( + &mut self, + arguments: &[BrilligParameter], + return_parameters: &[BrilligParameter], + ) { + let calldata_size = BrilligContext::flattened_tuple_size(arguments); + let return_data_size = BrilligContext::flattened_tuple_size(return_parameters); + + // Set initial value of stack pointer: MAX_STACK_SIZE + calldata_size + return_data_size + self.push_opcode(BrilligOpcode::Const { + destination: ReservedRegisters::stack_pointer(), + value: (MAX_STACK_SIZE + calldata_size + return_data_size).into(), + }); + + // Copy calldata + self.push_opcode(BrilligOpcode::CalldataCopy { + destination_address: MemoryAddress(MAX_STACK_SIZE), + size: calldata_size, + offset: 0, + }); + + // Allocate the variables for every argument: + let mut current_calldata_pointer = MAX_STACK_SIZE; + + let mut argument_variables: Vec<_> = arguments .iter() - .zip(preallocated_registers) - .map(|(argument, param_register)| match argument { + .map(|argument| match argument { BrilligParameter::Simple => { - let variable_register = self.allocate_register(); - self.mov_instruction(variable_register, param_register); - BrilligVariable::Simple(variable_register) + let simple_address = self.allocate_register(); + let var = BrilligVariable::Simple(simple_address); + self.mov_instruction(simple_address, MemoryAddress(current_calldata_pointer)); + current_calldata_pointer += 1; + var } - BrilligParameter::Array(item_types, item_count) => { - let pointer_register = self.allocate_register(); - let rc_register = self.allocate_register(); - self.mov_instruction(pointer_register, param_register); - self.const_instruction(rc_register, 1_usize.into()); - BrilligVariable::BrilligArray(BrilligArray { - pointer: pointer_register, - size: item_types.len() * item_count, + BrilligParameter::Array(_, _) => { + let pointer_to_the_array_in_calldata = + self.make_constant(current_calldata_pointer.into()); + let rc_register = self.make_constant(1_usize.into()); + let flattened_size = BrilligContext::flattened_size(argument); + let var = BrilligVariable::BrilligArray(BrilligArray { + pointer: pointer_to_the_array_in_calldata, + size: flattened_size, rc: rc_register, - }) + }); + + current_calldata_pointer += flattened_size; + var } BrilligParameter::Slice(_) => unimplemented!("Unsupported slices as parameter"), }) .collect(); - // Calculate the initial value for the stack pointer register - let size_arguments_memory: usize = arguments - .iter() - .map(|arg| match arg { - BrilligParameter::Simple => 0, - _ => BrilligContext::flattened_size(arg), - }) - .sum(); - - // Set the initial value of the stack pointer register - self.push_opcode(BrilligOpcode::Const { - destination: ReservedRegisters::stack_pointer(), - value: size_arguments_memory.into(), - }); - // Set the initial value of the previous stack pointer register - self.push_opcode(BrilligOpcode::Const { - destination: ReservedRegisters::previous_stack_pointer(), - value: 0_usize.into(), - }); - - // Deflatten the arrays - for (parameter, assigned_variable) in arguments.iter().zip(&argument_variables) { - if let BrilligParameter::Array(item_type, item_count) = parameter { - if item_type.iter().any(|param| !matches!(param, BrilligParameter::Simple)) { - let pointer_register = assigned_variable.extract_array().pointer; - let deflattened_register = - self.deflatten_array(item_type, *item_count, pointer_register); - self.mov_instruction(pointer_register, deflattened_register); + // Deflatten arrays + for (argument_variable, argument) in argument_variables.iter_mut().zip(arguments) { + if let ( + BrilligVariable::BrilligArray(array), + BrilligParameter::Array(item_type, item_count), + ) = (argument_variable, argument) + { + if BrilligContext::has_nested_arrays(item_type) { + let deflattened_address = + self.deflatten_array(item_type, array.size, array.pointer); + self.mov_instruction(array.pointer, deflattened_address); + array.size = item_type.len() * item_count; + self.deallocate_register(deflattened_address); } } } - - // Move the parameters to the first user defined registers, to follow function call convention. - for (i, register) in - argument_variables.into_iter().flat_map(|arg| arg.extract_registers()).enumerate() - { - self.mov_instruction(ReservedRegisters::user_register_index(i), register); - } } /// Computes the size of a parameter if it was flattened @@ -122,95 +120,130 @@ impl BrilligContext { } } + /// Computes the size of a parameter if it was flattened + fn flattened_tuple_size(tuple: &[BrilligParameter]) -> usize { + tuple.iter().map(BrilligContext::flattened_size).sum() + } + + /// Computes the size of a parameter if it was flattened + fn has_nested_arrays(tuple: &[BrilligParameter]) -> bool { + tuple.iter().any(|param| !matches!(param, BrilligParameter::Simple)) + } + /// Deflatten an array by recursively allocating nested arrays and copying the plain values. /// Returns the pointer to the deflattened items. fn deflatten_array( &mut self, item_type: &[BrilligParameter], item_count: usize, - flattened_array_pointer: RegisterIndex, - ) -> RegisterIndex { - let movement_register = self.allocate_register(); - let deflattened_array_pointer = self.allocate_register(); - - let target_item_size = item_type.len(); - let source_item_size: usize = item_type.iter().map(BrilligContext::flattened_size).sum(); - - self.allocate_fixed_length_array(deflattened_array_pointer, item_count * target_item_size); - - for item_index in 0..item_count { - let source_item_base_index = item_index * source_item_size; - let target_item_base_index = item_index * target_item_size; - - let mut source_offset = 0; - - for (subitem_index, subitem) in item_type.iter().enumerate() { - let source_index = - self.make_constant((source_item_base_index + source_offset).into()); - - let target_index = - self.make_constant((target_item_base_index + subitem_index).into()); - - match subitem { - BrilligParameter::Simple => { - self.array_get(flattened_array_pointer, source_index, movement_register); - self.array_set(deflattened_array_pointer, target_index, movement_register); - source_offset += 1; - } - BrilligParameter::Array(nested_array_item_type, nested_array_item_count) => { - let nested_array_pointer = self.allocate_register(); - self.mov_instruction(nested_array_pointer, flattened_array_pointer); - self.memory_op( - nested_array_pointer, - source_index, - nested_array_pointer, - acvm::brillig_vm::brillig::BinaryIntOp::Add, - ); - let deflattened_nested_array_pointer = self.deflatten_array( + flattened_array_pointer: MemoryAddress, + ) -> MemoryAddress { + if BrilligContext::has_nested_arrays(item_type) { + let movement_register = self.allocate_register(); + let deflattened_array_pointer = self.allocate_register(); + + let target_item_size = item_type.len(); + let source_item_size = BrilligContext::flattened_tuple_size(item_type); + + self.allocate_fixed_length_array( + deflattened_array_pointer, + item_count * target_item_size, + ); + + for item_index in 0..item_count { + let source_item_base_index = item_index * source_item_size; + let target_item_base_index = item_index * target_item_size; + + let mut source_offset = 0; + + for (subitem_index, subitem) in item_type.iter().enumerate() { + let source_index = + self.make_constant((source_item_base_index + source_offset).into()); + + let target_index = + self.make_constant((target_item_base_index + subitem_index).into()); + + match subitem { + BrilligParameter::Simple => { + self.array_get( + flattened_array_pointer, + source_index, + movement_register, + ); + self.array_set( + deflattened_array_pointer, + target_index, + movement_register, + ); + source_offset += 1; + } + BrilligParameter::Array( nested_array_item_type, - *nested_array_item_count, - nested_array_pointer, - ); - let reference = self.allocate_register(); - let rc = self.allocate_register(); - self.const_instruction(rc, 1_usize.into()); - - self.allocate_array_reference_instruction(reference); - self.store_variable_instruction( - reference, - BrilligVariable::BrilligArray(BrilligArray { + nested_array_item_count, + ) => { + let nested_array_pointer = self.allocate_register(); + self.mov_instruction(nested_array_pointer, flattened_array_pointer); + self.memory_op( + nested_array_pointer, + source_index, + nested_array_pointer, + acvm::brillig_vm::brillig::BinaryIntOp::Add, + ); + let deflattened_nested_array_pointer = self.deflatten_array( + nested_array_item_type, + *nested_array_item_count, + nested_array_pointer, + ); + let reference = self.allocate_register(); + let rc = self.allocate_register(); + self.const_instruction(rc, 1_usize.into()); + + self.allocate_array_reference_instruction(reference); + let array_variable = BrilligVariable::BrilligArray(BrilligArray { pointer: deflattened_nested_array_pointer, size: nested_array_item_type.len() * nested_array_item_count, rc, - }), - ); + }); + self.store_variable_instruction(reference, array_variable); - self.array_set(deflattened_array_pointer, target_index, reference); + self.array_set(deflattened_array_pointer, target_index, reference); - self.deallocate_register(nested_array_pointer); - self.deallocate_register(reference); - self.deallocate_register(rc); + self.deallocate_register(nested_array_pointer); + self.deallocate_register(reference); + array_variable + .extract_registers() + .into_iter() + .for_each(|register| self.deallocate_register(register)); - source_offset += BrilligContext::flattened_size(subitem); + source_offset += BrilligContext::flattened_size(subitem); + } + BrilligParameter::Slice(..) => unreachable!("ICE: Cannot deflatten slices"), } - BrilligParameter::Slice(..) => unreachable!("ICE: Cannot deflatten slices"), - } - self.deallocate_register(source_index); - self.deallocate_register(target_index); + self.deallocate_register(source_index); + self.deallocate_register(target_index); + } } - } - self.deallocate_register(movement_register); + self.deallocate_register(movement_register); - deflattened_array_pointer + deflattened_array_pointer + } else { + let deflattened_array_pointer = self.allocate_register(); + self.mov_instruction(deflattened_array_pointer, flattened_array_pointer); + deflattened_array_pointer + } } /// Adds the instructions needed to handle return parameters - /// The runtime expects the results in the first `n` registers. - /// Arrays are expected to be returned as pointers to the first element with all the nested arrays flattened. + /// The runtime expects the results in a contiguous memory region. + /// Arrays are expected to be returned with all the nested arrays flattened. /// However, the function called returns variables (that have extra data) and the returned arrays are deflattened. - fn exit_point_instruction(&mut self, return_parameters: Vec) { + fn exit_point_instruction( + &mut self, + arguments: &[BrilligParameter], + return_parameters: &[BrilligParameter], + ) { // First, we allocate the registers that hold the returned variables from the function call. self.set_allocated_registers(vec![]); let returned_variables: Vec<_> = return_parameters @@ -227,43 +260,45 @@ impl BrilligContext { BrilligParameter::Slice(..) => unreachable!("ICE: Cannot return slices"), }) .collect(); - // Now, we deflatten the returned arrays - for (return_param, returned_variable) in return_parameters.iter().zip(&returned_variables) { - if let BrilligParameter::Array(item_type, item_count) = return_param { - if item_type.iter().any(|item| !matches!(item, BrilligParameter::Simple)) { - let returned_pointer = returned_variable.extract_array().pointer; - let flattened_array_pointer = self.allocate_register(); - self.allocate_fixed_length_array( - flattened_array_pointer, - BrilligContext::flattened_size(return_param), + // Now, we deflatten the return data + let calldata_size = BrilligContext::flattened_tuple_size(arguments); + let return_data_size = BrilligContext::flattened_tuple_size(return_parameters); + + // Return data has a reserved space after calldata + let return_data_offset = MAX_STACK_SIZE + calldata_size; + let mut return_data_index = return_data_offset; + + for (return_param, returned_variable) in return_parameters.iter().zip(&returned_variables) { + match return_param { + BrilligParameter::Simple => { + self.mov_instruction( + MemoryAddress(return_data_index), + returned_variable.extract_register(), ); + return_data_index += 1; + } + BrilligParameter::Array(item_type, item_count) => { + let returned_pointer = returned_variable.extract_array().pointer; + let pointer_to_return_data = self.make_constant(return_data_index.into()); self.flatten_array( item_type, *item_count, - flattened_array_pointer, + pointer_to_return_data, returned_pointer, ); - self.mov_instruction(returned_pointer, flattened_array_pointer); + self.deallocate_register(pointer_to_return_data); + return_data_index += BrilligContext::flattened_size(return_param); + } + BrilligParameter::Slice(..) => { + unreachable!("ICE: Cannot return slices from brillig entrypoints") } } } - // The VM expects us to follow the calling convention of returning - // their results in the first `n` registers. So we to move the return values - // to the first `n` registers once completed. - - // Move the results to registers 0..n - for (i, returned_variable) in returned_variables.into_iter().enumerate() { - let register = match returned_variable { - BrilligVariable::Simple(register) => register, - BrilligVariable::BrilligArray(array) => array.pointer, - BrilligVariable::BrilligVector(vector) => vector.pointer, - }; - self.push_opcode(BrilligOpcode::Mov { destination: i.into(), source: register }); - } - self.push_opcode(BrilligOpcode::Stop); + + self.push_opcode(BrilligOpcode::Stop { return_data_offset, return_data_size }); } // Flattens an array by recursively copying nested arrays and regular items. @@ -271,96 +306,119 @@ impl BrilligContext { &mut self, item_type: &[BrilligParameter], item_count: usize, - flattened_array_pointer: RegisterIndex, - deflattened_array_pointer: RegisterIndex, + flattened_array_pointer: MemoryAddress, + deflattened_array_pointer: MemoryAddress, ) { - let movement_register = self.allocate_register(); - - let source_item_size = item_type.len(); - let target_item_size: usize = item_type.iter().map(BrilligContext::flattened_size).sum(); - - for item_index in 0..item_count { - let source_item_base_index = item_index * source_item_size; - let target_item_base_index = item_index * target_item_size; - - let mut target_offset = 0; - - for (subitem_index, subitem) in item_type.iter().enumerate() { - let source_index = - self.make_constant((source_item_base_index + subitem_index).into()); - let target_index = - self.make_constant((target_item_base_index + target_offset).into()); - - match subitem { - BrilligParameter::Simple => { - self.array_get(deflattened_array_pointer, source_index, movement_register); - self.array_set(flattened_array_pointer, target_index, movement_register); - target_offset += 1; - } - BrilligParameter::Array(nested_array_item_type, nested_array_item_count) => { - let nested_array_reference = self.allocate_register(); - self.array_get( - deflattened_array_pointer, - source_index, - nested_array_reference, - ); - - let nested_array_variable = BrilligVariable::BrilligArray(BrilligArray { - pointer: self.allocate_register(), - size: nested_array_item_type.len() * nested_array_item_count, - rc: self.allocate_register(), - }); - - self.load_variable_instruction( - nested_array_variable, - nested_array_reference, - ); - - let flattened_nested_array_pointer = self.allocate_register(); - - self.mov_instruction( - flattened_nested_array_pointer, - flattened_array_pointer, - ); - - self.memory_op( - flattened_nested_array_pointer, - target_index, - flattened_nested_array_pointer, - acvm::brillig_vm::brillig::BinaryIntOp::Add, - ); - - self.flatten_array( + if BrilligContext::has_nested_arrays(item_type) { + let movement_register = self.allocate_register(); + + let source_item_size = item_type.len(); + let target_item_size: usize = + item_type.iter().map(BrilligContext::flattened_size).sum(); + + for item_index in 0..item_count { + let source_item_base_index = item_index * source_item_size; + let target_item_base_index = item_index * target_item_size; + + let mut target_offset = 0; + + for (subitem_index, subitem) in item_type.iter().enumerate() { + let source_index = + self.make_constant((source_item_base_index + subitem_index).into()); + let target_index = + self.make_constant((target_item_base_index + target_offset).into()); + + match subitem { + BrilligParameter::Simple => { + self.array_get( + deflattened_array_pointer, + source_index, + movement_register, + ); + self.array_set( + flattened_array_pointer, + target_index, + movement_register, + ); + target_offset += 1; + } + BrilligParameter::Array( nested_array_item_type, - *nested_array_item_count, - flattened_nested_array_pointer, - nested_array_variable.extract_array().pointer, - ); - - self.deallocate_register(nested_array_reference); - self.deallocate_register(flattened_nested_array_pointer); - nested_array_variable - .extract_registers() - .into_iter() - .for_each(|register| self.deallocate_register(register)); - - target_offset += BrilligContext::flattened_size(subitem); + nested_array_item_count, + ) => { + let nested_array_reference = self.allocate_register(); + self.array_get( + deflattened_array_pointer, + source_index, + nested_array_reference, + ); + + let nested_array_variable = + BrilligVariable::BrilligArray(BrilligArray { + pointer: self.allocate_register(), + size: nested_array_item_type.len() * nested_array_item_count, + rc: self.allocate_register(), + }); + + self.load_variable_instruction( + nested_array_variable, + nested_array_reference, + ); + + let flattened_nested_array_pointer = self.allocate_register(); + + self.mov_instruction( + flattened_nested_array_pointer, + flattened_array_pointer, + ); + + self.memory_op( + flattened_nested_array_pointer, + target_index, + flattened_nested_array_pointer, + acvm::brillig_vm::brillig::BinaryIntOp::Add, + ); + + self.flatten_array( + nested_array_item_type, + *nested_array_item_count, + flattened_nested_array_pointer, + nested_array_variable.extract_array().pointer, + ); + + self.deallocate_register(nested_array_reference); + self.deallocate_register(flattened_nested_array_pointer); + nested_array_variable + .extract_registers() + .into_iter() + .for_each(|register| self.deallocate_register(register)); + + target_offset += BrilligContext::flattened_size(subitem); + } + BrilligParameter::Slice(..) => unreachable!("ICE: Cannot flatten slices"), } - BrilligParameter::Slice(..) => unreachable!("ICE: Cannot flatten slices"), - } - self.deallocate_register(source_index); - self.deallocate_register(target_index); + self.deallocate_register(source_index); + self.deallocate_register(target_index); + } } - } - self.deallocate_register(movement_register); + self.deallocate_register(movement_register); + } else { + let item_count = self.make_constant((item_count * item_type.len()).into()); + self.copy_array_instruction( + deflattened_array_pointer, + flattened_array_pointer, + item_count, + ); + self.deallocate_register(item_count); + } } } #[cfg(test)] mod tests { - use acvm::brillig_vm::brillig::{RegisterIndex, Value}; + use acvm::brillig_vm::brillig::Value; use crate::brillig::brillig_ir::{ artifact::BrilligParameter, @@ -370,7 +428,7 @@ mod tests { #[test] fn entry_point_with_nested_array_parameter() { - let flattened_array = vec![ + let calldata = vec![ Value::from(1_usize), Value::from(2_usize), Value::from(3_usize), @@ -391,44 +449,19 @@ mod tests { // Allocate the parameter let array_pointer = context.allocate_register(); + let array_value = context.allocate_register(); - context.return_instruction(&[array_pointer]); + context.load_instruction(array_pointer, array_pointer); + context.load_instruction(array_pointer, array_pointer); + context.load_instruction(array_value, array_pointer); - let bytecode = create_entry_point_bytecode(context, arguments, returns).byte_code; - let vm = create_and_run_vm(flattened_array.clone(), vec![Value::from(0_usize)], &bytecode); - let memory = vm.get_memory(); + context.return_instruction(&[array_value]); - assert_eq!(vm.get_registers().get(RegisterIndex(0)), Value::from(flattened_array.len())); - assert_eq!( - memory, - &vec![ - // The original flattened values - Value::from(1_usize), - Value::from(2_usize), - Value::from(3_usize), - Value::from(4_usize), - Value::from(5_usize), - Value::from(6_usize), - // The pointer to the nested reference of the first item - Value::from(12_usize), - Value::from(3_usize), - // The pointer to the nested reference of the second item - Value::from(16_usize), - Value::from(6_usize), - // The nested array of the first item - Value::from(1_usize), - Value::from(2_usize), - // The nested reference of the first item - Value::from(10_usize), - Value::from(1_usize), - // The nested array of the second item - Value::from(4_usize), - Value::from(5_usize), - // The nested reference of the second item - Value::from(14_usize), - Value::from(1_usize), - ] - ); + let bytecode = create_entry_point_bytecode(context, arguments, returns).byte_code; + let (vm, return_data_offset, return_data_size) = + create_and_run_vm(calldata.clone(), &bytecode); + assert_eq!(return_data_size, 1, "Return data size is incorrect"); + assert_eq!(vm.get_memory()[return_data_offset], Value::from(1_usize)); } #[test] @@ -463,46 +496,14 @@ mod tests { context.return_instruction(&brillig_array.extract_registers()); let bytecode = create_entry_point_bytecode(context, arguments, returns).byte_code; - let vm = create_and_run_vm(flattened_array.clone(), vec![Value::from(0_usize)], &bytecode); + let (vm, return_data_pointer, return_data_size) = + create_and_run_vm(flattened_array.clone(), &bytecode); let memory = vm.get_memory(); assert_eq!( - memory, - &vec![ - // The original flattened values - Value::from(1_usize), - Value::from(2_usize), - Value::from(3_usize), - Value::from(4_usize), - Value::from(5_usize), - Value::from(6_usize), - // The pointer to the nested reference of the first item - Value::from(12_usize), - Value::from(3_usize), - // The pointer to the nested reference of the second item - Value::from(16_usize), - Value::from(6_usize), - // The nested array of the first item - Value::from(1_usize), - Value::from(2_usize), - // The nested reference of the first item - Value::from(10_usize), - Value::from(1_usize), - // The nested array of the second item - Value::from(4_usize), - Value::from(5_usize), - // The nested reference of the second item - Value::from(14_usize), - Value::from(1_usize), - // The original flattened again - Value::from(1_usize), - Value::from(2_usize), - Value::from(3_usize), - Value::from(4_usize), - Value::from(5_usize), - Value::from(6_usize), - ] + memory[return_data_pointer..(return_data_pointer + flattened_array.len())], + flattened_array ); - assert_eq!(vm.get_registers().get(RegisterIndex(0)), 18_usize.into()); + assert_eq!(return_data_size, flattened_array.len()); } } diff --git a/noir/compiler/noirc_evaluator/src/brillig/brillig_ir/registers.rs b/noir/compiler/noirc_evaluator/src/brillig/brillig_ir/registers.rs index e7ab1492acb..8c0e36215a9 100644 --- a/noir/compiler/noirc_evaluator/src/brillig/brillig_ir/registers.rs +++ b/noir/compiler/noirc_evaluator/src/brillig/brillig_ir/registers.rs @@ -1,4 +1,6 @@ -use acvm::acir::brillig::RegisterIndex; +use acvm::acir::brillig::MemoryAddress; + +use crate::brillig::brillig_ir::entry_point::MAX_STACK_SIZE; use super::ReservedRegisters; @@ -8,7 +10,7 @@ use super::ReservedRegisters; /// Each has a stack base pointer from which all stack allocations can be offset. pub(crate) struct BrilligRegistersContext { /// A free-list of registers that have been deallocated and can be used again. - deallocated_registers: Vec, + deallocated_registers: Vec, /// A usize indicating the next un-used register. next_free_register_index: usize, } @@ -23,7 +25,7 @@ impl BrilligRegistersContext { } /// Creates a new register context from a set of registers allocated previously. - pub(crate) fn from_preallocated_registers(preallocated_registers: Vec) -> Self { + pub(crate) fn from_preallocated_registers(preallocated_registers: Vec) -> Self { let next_free_register_index = preallocated_registers.iter().fold( ReservedRegisters::len(), |free_register_index, preallocated_register| { @@ -36,8 +38,8 @@ impl BrilligRegistersContext { ); let mut deallocated_registers = Vec::new(); for i in ReservedRegisters::len()..next_free_register_index { - if !preallocated_registers.contains(&RegisterIndex::from(i)) { - deallocated_registers.push(RegisterIndex::from(i)); + if !preallocated_registers.contains(&MemoryAddress::from(i)) { + deallocated_registers.push(MemoryAddress::from(i)); } } @@ -45,7 +47,7 @@ impl BrilligRegistersContext { } /// Ensures a register is allocated. - pub(crate) fn ensure_register_is_allocated(&mut self, register: RegisterIndex) { + pub(crate) fn ensure_register_is_allocated(&mut self, register: MemoryAddress) { let index = register.to_usize(); if index < self.next_free_register_index { // If it could be allocated, check if it's in the deallocated list and remove it from there @@ -53,26 +55,28 @@ impl BrilligRegistersContext { } else { // If it couldn't yet be, expand the register space. self.next_free_register_index = index + 1; + assert!(self.next_free_register_index < MAX_STACK_SIZE, "Stack too deep"); } } /// Creates a new register. - pub(crate) fn allocate_register(&mut self) -> RegisterIndex { + pub(crate) fn allocate_register(&mut self) -> MemoryAddress { // If we have a register in our free list of deallocated registers, // consume it first. This prioritizes reuse. if let Some(register) = self.deallocated_registers.pop() { return register; } // Otherwise, move to our latest register. - let register = RegisterIndex::from(self.next_free_register_index); + let register = MemoryAddress::from(self.next_free_register_index); self.next_free_register_index += 1; + assert!(self.next_free_register_index < MAX_STACK_SIZE, "Stack too deep"); register } /// Push a register to the deallocation list, ready for reuse. /// TODO(AD): currently, register deallocation is only done with immediate values. /// TODO(AD): See https://github.com/noir-lang/noir/issues/1720 - pub(crate) fn deallocate_register(&mut self, register_index: RegisterIndex) { + pub(crate) fn deallocate_register(&mut self, register_index: MemoryAddress) { assert!(!self.deallocated_registers.contains(®ister_index)); self.deallocated_registers.push(register_index); } diff --git a/noir/compiler/noirc_evaluator/src/errors.rs b/noir/compiler/noirc_evaluator/src/errors.rs index d7229c0adc5..73b6e671bd5 100644 --- a/noir/compiler/noirc_evaluator/src/errors.rs +++ b/noir/compiler/noirc_evaluator/src/errors.rs @@ -44,6 +44,8 @@ pub enum RuntimeError { AssertConstantFailed { call_stack: CallStack }, #[error("Nested slices are not supported")] NestedSlice { call_stack: CallStack }, + #[error("Big Integer modulus do no match")] + BigIntModulus { call_stack: CallStack }, } // We avoid showing the actual lhs and rhs since most of the time they are just 0 @@ -68,7 +70,7 @@ impl From for FileDiagnostic { let message = warning.to_string(); let (secondary_message, call_stack) = match warning { InternalWarning::ReturnConstant { call_stack } => { - ("constant value".to_string(), call_stack) + ("This variable contains a value which is constrained to be a constant. Consider removing this value as additional return values increase proving/verification time".to_string(), call_stack) }, InternalWarning::VerifyProof { call_stack } => { ("verify_proof(...) aggregates data for the verifier, the actual verification will be done when the full proof is verified using nargo verify. nargo prove may generate an invalid proof if bad data is used as input to verify_proof".to_string(), call_stack) @@ -87,7 +89,7 @@ impl From for FileDiagnostic { #[derive(Debug, PartialEq, Eq, Clone, Error, Serialize, Deserialize)] pub enum InternalWarning { - #[error("Returning a constant value is not allowed")] + #[error("Return variable contains a constant value")] ReturnConstant { call_stack: CallStack }, #[error("Calling std::verify_proof(...) does not verify a proof")] VerifyProof { call_stack: CallStack }, @@ -132,7 +134,8 @@ impl RuntimeError { | RuntimeError::AssertConstantFailed { call_stack } | RuntimeError::IntegerOutOfBounds { call_stack, .. } | RuntimeError::UnsupportedIntegerSize { call_stack, .. } - | RuntimeError::NestedSlice { call_stack, .. } => call_stack, + | RuntimeError::NestedSlice { call_stack, .. } + | RuntimeError::BigIntModulus { call_stack, .. } => call_stack, } } } diff --git a/noir/compiler/noirc_evaluator/src/ssa.rs b/noir/compiler/noirc_evaluator/src/ssa.rs index e2da5652faf..0e3076923e0 100644 --- a/noir/compiler/noirc_evaluator/src/ssa.rs +++ b/noir/compiler/noirc_evaluator/src/ssa.rs @@ -63,7 +63,6 @@ pub(crate) fn optimize_into_acir( .run_pass(Ssa::mem2reg, "After Mem2Reg:") .run_pass(Ssa::fold_constants, "After Constant Folding:") .run_pass(Ssa::dead_instruction_elimination, "After Dead Instruction Elimination:") - .run_pass(Ssa::bubble_up_constrains, "After Constraint Bubbling:") .finish(); let brillig = ssa.to_brillig(print_brillig_trace); diff --git a/noir/compiler/noirc_evaluator/src/ssa/acir_gen/acir_ir.rs b/noir/compiler/noirc_evaluator/src/ssa/acir_gen/acir_ir.rs index 96800b22ad0..1ddbae0f339 100644 --- a/noir/compiler/noirc_evaluator/src/ssa/acir_gen/acir_ir.rs +++ b/noir/compiler/noirc_evaluator/src/ssa/acir_gen/acir_ir.rs @@ -1,3 +1,4 @@ pub(crate) mod acir_variable; +pub(crate) mod big_int; pub(crate) mod generated_acir; pub(crate) mod sort; diff --git a/noir/compiler/noirc_evaluator/src/ssa/acir_gen/acir_ir/acir_variable.rs b/noir/compiler/noirc_evaluator/src/ssa/acir_gen/acir_ir/acir_variable.rs index cf7c6151110..f1a639de211 100644 --- a/noir/compiler/noirc_evaluator/src/ssa/acir_gen/acir_ir/acir_variable.rs +++ b/noir/compiler/noirc_evaluator/src/ssa/acir_gen/acir_ir/acir_variable.rs @@ -1,3 +1,4 @@ +use super::big_int::BigIntContext; use super::generated_acir::GeneratedAcir; use crate::brillig::brillig_gen::brillig_directive; use crate::brillig::brillig_ir::artifact::GeneratedBrillig; @@ -10,7 +11,7 @@ use acvm::acir::circuit::brillig::{BrilligInputs, BrilligOutputs}; use acvm::acir::circuit::opcodes::{BlockId, MemOp}; use acvm::acir::circuit::Opcode; use acvm::blackbox_solver; -use acvm::brillig_vm::{brillig::Value, Registers, VMStatus, VM}; +use acvm::brillig_vm::{brillig::Value, VMStatus, VM}; use acvm::{ acir::{ brillig::Opcode as BrilligOpcode, @@ -107,6 +108,9 @@ pub(crate) struct AcirContext { /// then the `acir_ir` will be populated to assert this /// addition. acir_ir: GeneratedAcir, + + /// The BigIntContext, used to generate identifiers for BigIntegers + big_int_ctx: BigIntContext, } impl AcirContext { @@ -396,8 +400,8 @@ impl AcirContext { // Operands are booleans. // // a ^ b == a + b - 2*a*b - let sum = self.add_var(lhs, rhs)?; let prod = self.mul_var(lhs, rhs)?; + let sum = self.add_var(lhs, rhs)?; self.add_mul_var(sum, -FieldElement::from(2_i128), prod) } else { let inputs = vec![AcirValue::Var(lhs, typ.clone()), AcirValue::Var(rhs, typ)]; @@ -457,8 +461,8 @@ impl AcirContext { if bit_size == 1 { // Operands are booleans // a + b - ab - let sum = self.add_var(lhs, rhs)?; let mul = self.mul_var(lhs, rhs)?; + let sum = self.add_var(lhs, rhs)?; self.sub_var(sum, mul) } else { // Implement OR in terms of AND @@ -1140,10 +1144,10 @@ impl AcirContext { &mut self, name: BlackBoxFunc, mut inputs: Vec, - output_count: usize, + mut output_count: usize, ) -> Result, RuntimeError> { // Separate out any arguments that should be constants - let constants = match name { + let (constant_inputs, constant_outputs) = match name { BlackBoxFunc::PedersenCommitment | BlackBoxFunc::PedersenHash => { // The last argument of pedersen is the domain separator, which must be a constant let domain_var = match inputs.pop() { @@ -1167,23 +1171,140 @@ impl AcirContext { } }; - vec![domain_constant] + (vec![domain_constant], Vec::new()) + } + BlackBoxFunc::Poseidon2Permutation => { + // The last argument is the state length, which must be a constant + let state_len = match inputs.pop() { + Some(state_len) => state_len.into_var()?, + None => { + return Err(RuntimeError::InternalError(InternalError::MissingArg { + name: "poseidon_2_permutation call".to_string(), + arg: "length".to_string(), + call_stack: self.get_call_stack(), + })) + } + }; + + let state_len = match self.vars[&state_len].as_constant() { + Some(state_len) => state_len, + None => { + return Err(RuntimeError::InternalError(InternalError::NotAConstant { + name: "length".to_string(), + call_stack: self.get_call_stack(), + })) + } + }; + + (vec![state_len], Vec::new()) + } + BlackBoxFunc::BigIntAdd + | BlackBoxFunc::BigIntNeg + | BlackBoxFunc::BigIntMul + | BlackBoxFunc::BigIntDiv => { + assert_eq!(inputs.len(), 4, "ICE - bigint operation requires 4 inputs"); + let const_inputs = vecmap(inputs, |i| { + let var = i.into_var()?; + match self.vars[&var].as_constant() { + Some(const_var) => Ok(const_var), + None => Err(RuntimeError::InternalError(InternalError::NotAConstant { + name: "big integer".to_string(), + call_stack: self.get_call_stack(), + })), + } + }); + inputs = Vec::new(); + output_count = 0; + let mut field_inputs = Vec::new(); + for i in const_inputs { + field_inputs.push(i?); + } + if field_inputs[1] != field_inputs[3] { + return Err(RuntimeError::BigIntModulus { call_stack: self.get_call_stack() }); + } + + let result_id = self.big_int_ctx.new_big_int(field_inputs[1]); + ( + vec![field_inputs[0], field_inputs[2]], + vec![result_id.bigint_id(), result_id.modulus_id()], + ) + } + BlackBoxFunc::BigIntToLeBytes => { + let const_inputs = vecmap(inputs, |i| { + let var = i.into_var()?; + match self.vars[&var].as_constant() { + Some(const_var) => Ok(const_var), + None => Err(RuntimeError::InternalError(InternalError::NotAConstant { + name: "big integer".to_string(), + call_stack: self.get_call_stack(), + })), + } + }); + inputs = Vec::new(); + let mut field_inputs = Vec::new(); + for i in const_inputs { + field_inputs.push(i?); + } + let modulus = self.big_int_ctx.modulus(field_inputs[0]); + let bytes_len = ((modulus - BigUint::from(1_u32)).bits() - 1) / 8 + 1; + output_count = bytes_len as usize; + (field_inputs, vec![FieldElement::from(bytes_len as u128)]) + } + BlackBoxFunc::BigIntFromLeBytes => { + let invalid_input = "ICE - bigint operation requires 2 inputs"; + assert_eq!(inputs.len(), 2, "{invalid_input}"); + let mut modulus = Vec::new(); + match inputs.pop().expect(invalid_input) { + AcirValue::Array(values) => { + for value in values { + modulus.push(self.vars[&value.into_var()?].as_constant().ok_or( + RuntimeError::InternalError(InternalError::NotAConstant { + name: "big integer".to_string(), + call_stack: self.get_call_stack(), + }), + )?); + } + } + _ => { + return Err(RuntimeError::InternalError(InternalError::MissingArg { + name: "big_int_from_le_bytes".to_owned(), + arg: "modulus".to_owned(), + call_stack: self.get_call_stack(), + })); + } + } + let big_modulus = BigUint::from_bytes_le(&vecmap(&modulus, |b| b.to_u128() as u8)); + output_count = 0; + + let modulus_id = self.big_int_ctx.get_or_insert_modulus(big_modulus); + let result_id = + self.big_int_ctx.new_big_int(FieldElement::from(modulus_id as u128)); + (modulus, vec![result_id.bigint_id(), result_id.modulus_id()]) } - _ => vec![], + _ => (vec![], vec![]), }; // Convert `AcirVar` to `FunctionInput` let inputs = self.prepare_inputs_for_black_box_func_call(inputs)?; - // Call Black box with `FunctionInput` - let outputs = self.acir_ir.call_black_box(name, &inputs, constants, output_count)?; + let mut results = vecmap(&constant_outputs, |c| self.add_constant(*c)); + let outputs = self.acir_ir.call_black_box( + name, + &inputs, + constant_inputs, + constant_outputs, + output_count, + )?; // Convert `Witness` values which are now constrained to be the output of the // black box function call into `AcirVar`s. // // We do not apply range information on the output of the black box function. // See issue #1439 - Ok(vecmap(&outputs, |witness_index| self.add_data(AcirVarData::Witness(*witness_index)))) + results.extend(vecmap(&outputs, |witness_index| { + self.add_data(AcirVarData::Witness(*witness_index)) + })); + Ok(results) } /// Black box function calls expect their inputs to be in a specific data structure (FunctionInput). @@ -1320,20 +1441,22 @@ impl AcirContext { inputs: Vec, outputs: Vec, attempt_execution: bool, - ) -> Result, InternalError> { - let b_inputs = try_vecmap(inputs, |i| match i { - AcirValue::Var(var, _) => Ok(BrilligInputs::Single(self.var_to_expression(var)?)), - AcirValue::Array(vars) => { - let mut var_expressions: Vec = Vec::new(); - for var in vars { - self.brillig_array_input(&mut var_expressions, var)?; + ) -> Result, RuntimeError> { + let b_inputs = try_vecmap(inputs, |i| -> Result<_, InternalError> { + match i { + AcirValue::Var(var, _) => Ok(BrilligInputs::Single(self.var_to_expression(var)?)), + AcirValue::Array(vars) => { + let mut var_expressions: Vec = Vec::new(); + for var in vars { + self.brillig_array_input(&mut var_expressions, var)?; + } + Ok(BrilligInputs::Array(var_expressions)) + } + AcirValue::DynamicArray(_) => { + let mut var_expressions = Vec::new(); + self.brillig_array_input(&mut var_expressions, i)?; + Ok(BrilligInputs::Array(var_expressions)) } - Ok(BrilligInputs::Array(var_expressions)) - } - AcirValue::DynamicArray(_) => { - let mut var_expressions = Vec::new(); - self.brillig_array_input(&mut var_expressions, i)?; - Ok(BrilligInputs::Array(var_expressions)) } })?; @@ -1368,6 +1491,34 @@ impl AcirContext { let predicate = self.var_to_expression(predicate)?; self.acir_ir.brillig(Some(predicate), generated_brillig, b_inputs, b_outputs); + fn range_constraint_value( + context: &mut AcirContext, + value: &AcirValue, + ) -> Result<(), RuntimeError> { + match value { + AcirValue::Var(var, typ) => { + let numeric_type = match typ { + AcirType::NumericType(numeric_type) => numeric_type, + _ => unreachable!("`AcirValue::Var` may only hold primitive values"), + }; + context.range_constrain_var(*var, numeric_type, None)?; + } + AcirValue::Array(values) => { + for value in values { + range_constraint_value(context, value)?; + } + } + AcirValue::DynamicArray(_) => { + unreachable!("Brillig opcodes cannot return dynamic arrays") + } + } + Ok(()) + } + + for output_var in &outputs_var { + range_constraint_value(self, output_var)?; + } + Ok(outputs_var) } @@ -1436,23 +1587,17 @@ impl AcirContext { inputs: &[BrilligInputs], outputs_types: &[AcirType], ) -> Option> { - let (registers, memory) = execute_brillig(code, inputs)?; - - let outputs_var = vecmap(outputs_types.iter().enumerate(), |(index, output)| { - let register_value = registers.get(index.into()); - match output { - AcirType::NumericType(_) => { - let var = self.add_data(AcirVarData::Const(register_value.to_field())); - AcirValue::Var(var, output.clone()) - } - AcirType::Array(element_types, size) => { - let mem_ptr = register_value.to_usize(); - self.brillig_constant_array_output( - element_types, - *size, - &mut memory.iter().skip(mem_ptr), - ) - } + let mut memory = (execute_brillig(code, inputs)?).into_iter(); + + let outputs_var = vecmap(outputs_types.iter(), |output| match output { + AcirType::NumericType(_) => { + let var = self.add_data(AcirVarData::Const( + memory.next().expect("Missing return data").to_field(), + )); + AcirValue::Var(var, output.clone()) + } + AcirType::Array(element_types, size) => { + self.brillig_constant_array_output(element_types, *size, &mut memory) } }); @@ -1460,11 +1605,11 @@ impl AcirContext { } /// Recursively create [`AcirValue`]s for returned arrays. This is necessary because a brillig returned array can have nested arrays as elements. - fn brillig_constant_array_output<'a>( + fn brillig_constant_array_output( &mut self, element_types: &[AcirType], size: usize, - memory_iter: &mut impl Iterator, + memory_iter: &mut impl Iterator, ) -> AcirValue { let mut array_values = im::Vector::new(); for _ in 0..size { @@ -1707,42 +1852,28 @@ pub(crate) struct AcirVar(usize); /// Returns the finished state of the Brillig VM if execution can complete. /// /// Returns `None` if complete execution of the Brillig bytecode is not possible. -fn execute_brillig( - code: &[BrilligOpcode], - inputs: &[BrilligInputs], -) -> Option<(Registers, Vec)> { +fn execute_brillig(code: &[BrilligOpcode], inputs: &[BrilligInputs]) -> Option> { // Set input values - let mut input_register_values: Vec = Vec::with_capacity(inputs.len()); - let mut input_memory: Vec = Vec::new(); + let mut calldata: Vec = Vec::new(); + // Each input represents a constant or array of constants. // Iterate over each input and push it into registers and/or memory. for input in inputs { match input { BrilligInputs::Single(expr) => { - input_register_values.push(expr.to_const()?.into()); + calldata.push(expr.to_const()?.into()); } BrilligInputs::Array(expr_arr) => { // Attempt to fetch all array input values - let memory_pointer = input_memory.len(); for expr in expr_arr.iter() { - input_memory.push(expr.to_const()?.into()); + calldata.push(expr.to_const()?.into()); } - - // Push value of the array pointer as a register - input_register_values.push(Value::from(memory_pointer)); } } } // Instantiate a Brillig VM given the solved input registers and memory, along with the Brillig bytecode. - let input_registers = Registers::load(input_register_values); - let mut vm = VM::new( - input_registers, - input_memory, - code, - Vec::new(), - &blackbox_solver::StubbedBlackBoxSolver, - ); + let mut vm = VM::new(calldata, code, Vec::new(), &blackbox_solver::StubbedBlackBoxSolver); // Run the Brillig VM on these inputs, bytecode, etc! let vm_status = vm.process_opcodes(); @@ -1751,7 +1882,9 @@ fn execute_brillig( // It may be finished, in-progress, failed, or may be waiting for results of a foreign call. // If it's finished then we can omit the opcode and just write in the return values. match vm_status { - VMStatus::Finished => Some((vm.get_registers().clone(), vm.get_memory().clone())), + VMStatus::Finished { return_data_offset, return_data_size } => Some( + vm.get_memory()[return_data_offset..(return_data_offset + return_data_size)].to_vec(), + ), VMStatus::InProgress => unreachable!("Brillig VM has not completed execution"), VMStatus::Failure { .. } => { // TODO: Return an error stating that the brillig function failed. diff --git a/noir/compiler/noirc_evaluator/src/ssa/acir_gen/acir_ir/big_int.rs b/noir/compiler/noirc_evaluator/src/ssa/acir_gen/acir_ir/big_int.rs new file mode 100644 index 00000000000..c21188a8dbc --- /dev/null +++ b/noir/compiler/noirc_evaluator/src/ssa/acir_gen/acir_ir/big_int.rs @@ -0,0 +1,57 @@ +use acvm::FieldElement; +use num_bigint::BigUint; + +/// Represents a bigint value in the form (id, modulus) where +/// id is the identifier of the big integer number, and +/// modulus is the identifier of the big integer size +#[derive(Default, Clone, Copy, Debug)] +pub(crate) struct BigIntId { + pub(crate) bigint_id: u32, + pub(crate) modulus_id: u32, +} + +impl BigIntId { + pub(crate) fn bigint_id(&self) -> FieldElement { + FieldElement::from(self.bigint_id as u128) + } + + pub(crate) fn modulus_id(&self) -> FieldElement { + FieldElement::from(self.modulus_id as u128) + } +} + +/// BigIntContext is used to generate identifiers for big integers and their modulus +#[derive(Default, Debug)] +pub(crate) struct BigIntContext { + modulus: Vec, + big_integers: Vec, +} + +impl BigIntContext { + /// Creates a new BigIntId for the given modulus identifier and returns it. + pub(crate) fn new_big_int(&mut self, modulus_id: FieldElement) -> BigIntId { + let id = self.big_integers.len() as u32; + let result = BigIntId { bigint_id: id, modulus_id: modulus_id.to_u128() as u32 }; + self.big_integers.push(result); + result + } + + /// Returns the modulus corresponding to the given modulus index + pub(crate) fn modulus(&self, idx: FieldElement) -> BigUint { + self.modulus[idx.to_u128() as usize].clone() + } + + /// Returns the BigIntId corresponding to the given identifier + pub(crate) fn get(&self, id: FieldElement) -> BigIntId { + self.big_integers[id.to_u128() as usize] + } + + /// Adds a modulus to the context (if it is not already present) + pub(crate) fn get_or_insert_modulus(&mut self, modulus: BigUint) -> u32 { + if let Some(pos) = self.modulus.iter().position(|x| x == &modulus) { + return pos as u32; + } + self.modulus.push(modulus); + (self.modulus.len() - 1) as u32 + } +} diff --git a/noir/compiler/noirc_evaluator/src/ssa/acir_gen/acir_ir/generated_acir.rs b/noir/compiler/noirc_evaluator/src/ssa/acir_gen/acir_ir/generated_acir.rs index efc64c5286e..b86fc4eeb5f 100644 --- a/noir/compiler/noirc_evaluator/src/ssa/acir_gen/acir_ir/generated_acir.rs +++ b/noir/compiler/noirc_evaluator/src/ssa/acir_gen/acir_ir/generated_acir.rs @@ -138,7 +138,8 @@ impl GeneratedAcir { &mut self, func_name: BlackBoxFunc, inputs: &[Vec], - constants: Vec, + constant_inputs: Vec, + constant_outputs: Vec, output_count: usize, ) -> Result, InternalError> { let input_count = inputs.iter().fold(0usize, |sum, val| sum + val.len()); @@ -176,12 +177,12 @@ impl GeneratedAcir { BlackBoxFunc::PedersenCommitment => BlackBoxFuncCall::PedersenCommitment { inputs: inputs[0].clone(), outputs: (outputs[0], outputs[1]), - domain_separator: constants[0].to_u128() as u32, + domain_separator: constant_inputs[0].to_u128() as u32, }, BlackBoxFunc::PedersenHash => BlackBoxFuncCall::PedersenHash { inputs: inputs[0].clone(), output: outputs[0], - domain_separator: constants[0].to_u128() as u32, + domain_separator: constant_inputs[0].to_u128() as u32, }, BlackBoxFunc::EcdsaSecp256k1 => { BlackBoxFuncCall::EcdsaSecp256k1 { @@ -219,11 +220,6 @@ impl GeneratedAcir { input2_y: inputs[3][0], outputs: (outputs[0], outputs[1]), }, - BlackBoxFunc::EmbeddedCurveDouble => BlackBoxFuncCall::EmbeddedCurveDouble { - input_x: inputs[0][0], - input_y: inputs[1][0], - outputs: (outputs[0], outputs[1]), - }, BlackBoxFunc::Keccak256 => { let var_message_size = match inputs.to_vec().pop() { Some(var_message_size) => var_message_size[0], @@ -251,6 +247,45 @@ impl GeneratedAcir { public_inputs: inputs[2].clone(), key_hash: inputs[3][0], }, + BlackBoxFunc::BigIntAdd => BlackBoxFuncCall::BigIntAdd { + lhs: constant_inputs[0].to_u128() as u32, + rhs: constant_inputs[1].to_u128() as u32, + output: constant_outputs[0].to_u128() as u32, + }, + BlackBoxFunc::BigIntNeg => BlackBoxFuncCall::BigIntNeg { + lhs: constant_inputs[0].to_u128() as u32, + rhs: constant_inputs[1].to_u128() as u32, + output: constant_outputs[0].to_u128() as u32, + }, + BlackBoxFunc::BigIntMul => BlackBoxFuncCall::BigIntMul { + lhs: constant_inputs[0].to_u128() as u32, + rhs: constant_inputs[1].to_u128() as u32, + output: constant_outputs[0].to_u128() as u32, + }, + BlackBoxFunc::BigIntDiv => BlackBoxFuncCall::BigIntDiv { + lhs: constant_inputs[0].to_u128() as u32, + rhs: constant_inputs[1].to_u128() as u32, + output: constant_outputs[0].to_u128() as u32, + }, + BlackBoxFunc::BigIntFromLeBytes => BlackBoxFuncCall::BigIntFromLeBytes { + inputs: inputs[0].clone(), + modulus: vecmap(constant_inputs, |c| c.to_u128() as u8), + output: constant_outputs[0].to_u128() as u32, + }, + BlackBoxFunc::BigIntToLeBytes => BlackBoxFuncCall::BigIntToLeBytes { + input: constant_inputs[0].to_u128() as u32, + outputs, + }, + BlackBoxFunc::Poseidon2Permutation => BlackBoxFuncCall::Poseidon2Permutation { + inputs: inputs[0].clone(), + outputs, + len: constant_inputs[0].to_u128() as u32, + }, + BlackBoxFunc::Sha256Compression => BlackBoxFuncCall::Sha256Compression { + inputs: inputs[0].clone(), + hash_values: inputs[1].clone(), + outputs, + }, }; self.push_opcode(AcirOpcode::BlackBoxFuncCall(black_box_func_call)); @@ -573,6 +608,7 @@ fn black_box_func_expected_input_size(name: BlackBoxFunc) -> Option { match name { // Bitwise opcodes will take in 2 parameters BlackBoxFunc::AND | BlackBoxFunc::XOR => Some(2), + // All of the hash/cipher methods will take in a // variable number of inputs. BlackBoxFunc::Keccak256 @@ -583,7 +619,11 @@ fn black_box_func_expected_input_size(name: BlackBoxFunc) -> Option { | BlackBoxFunc::PedersenHash => None, BlackBoxFunc::Keccakf1600 => Some(25), + // The permutation takes a fixed number of inputs, but the inputs length depends on the proving system implementation. + BlackBoxFunc::Poseidon2Permutation => None, + // SHA256 compression requires 16 u32s as input message and 8 u32s for the hash state. + BlackBoxFunc::Sha256Compression => Some(24), // Can only apply a range constraint to one // witness at a time. BlackBoxFunc::RANGE => Some(1), @@ -593,15 +633,26 @@ fn black_box_func_expected_input_size(name: BlackBoxFunc) -> Option { BlackBoxFunc::SchnorrVerify | BlackBoxFunc::EcdsaSecp256k1 | BlackBoxFunc::EcdsaSecp256r1 => None, + // Inputs for fixed based scalar multiplication // is the low and high limbs of the scalar BlackBoxFunc::FixedBaseScalarMul => Some(2), + // Recursive aggregation has a variable number of inputs BlackBoxFunc::RecursiveAggregation => None, + // Addition over the embedded curve: input are coordinates (x1,y1) and (x2,y2) of the Grumpkin points BlackBoxFunc::EmbeddedCurveAdd => Some(4), - // Doubling over the embedded curve: input is (x,y) coordinate of the point. - BlackBoxFunc::EmbeddedCurveDouble => Some(2), + + // Big integer operations take in 0 inputs. They use constants for their inputs. + BlackBoxFunc::BigIntAdd + | BlackBoxFunc::BigIntNeg + | BlackBoxFunc::BigIntMul + | BlackBoxFunc::BigIntDiv + | BlackBoxFunc::BigIntToLeBytes => Some(0), + + // FromLeBytes takes a variable array of bytes as input + BlackBoxFunc::BigIntFromLeBytes => None, } } @@ -612,28 +663,47 @@ fn black_box_expected_output_size(name: BlackBoxFunc) -> Option { // Bitwise opcodes will return 1 parameter which is the output // or the operation. BlackBoxFunc::AND | BlackBoxFunc::XOR => Some(1), + // 32 byte hash algorithms BlackBoxFunc::Keccak256 | BlackBoxFunc::SHA256 | BlackBoxFunc::Blake2s | BlackBoxFunc::Blake3 => Some(32), + BlackBoxFunc::Keccakf1600 => Some(25), + // The permutation returns a fixed number of outputs, equals to the inputs length which depends on the proving system implementation. + BlackBoxFunc::Poseidon2Permutation => None, + + BlackBoxFunc::Sha256Compression => Some(8), // Pedersen commitment returns a point BlackBoxFunc::PedersenCommitment => Some(2), + // Pedersen hash returns a field BlackBoxFunc::PedersenHash => Some(1), + // Can only apply a range constraint to one // witness at a time. BlackBoxFunc::RANGE => Some(0), + // Signature verification algorithms will return a boolean BlackBoxFunc::SchnorrVerify | BlackBoxFunc::EcdsaSecp256k1 | BlackBoxFunc::EcdsaSecp256r1 => Some(1), + // Output of operations over the embedded curve // will be 2 field elements representing the point. - BlackBoxFunc::FixedBaseScalarMul - | BlackBoxFunc::EmbeddedCurveAdd - | BlackBoxFunc::EmbeddedCurveDouble => Some(2), + BlackBoxFunc::FixedBaseScalarMul | BlackBoxFunc::EmbeddedCurveAdd => Some(2), + + // Big integer operations return a big integer + BlackBoxFunc::BigIntAdd + | BlackBoxFunc::BigIntNeg + | BlackBoxFunc::BigIntMul + | BlackBoxFunc::BigIntDiv + | BlackBoxFunc::BigIntFromLeBytes => Some(0), + + // ToLeBytes returns a variable array of bytes + BlackBoxFunc::BigIntToLeBytes => None, + // Recursive aggregation has a variable number of outputs BlackBoxFunc::RecursiveAggregation => None, } @@ -690,5 +760,5 @@ fn intrinsics_check_outputs(name: BlackBoxFunc, output_count: usize) { None => return, }; - assert_eq!(expected_num_outputs,output_count,"Tried to call black box function {name} with {output_count} inputs, but this function's definition requires {expected_num_outputs} inputs"); + assert_eq!(expected_num_outputs,output_count,"Tried to call black box function {name} with {output_count} outputs, but this function's definition requires {expected_num_outputs} outputs"); } diff --git a/noir/compiler/noirc_evaluator/src/ssa/acir_gen/mod.rs b/noir/compiler/noirc_evaluator/src/ssa/acir_gen/mod.rs index d832b8d0fbb..120c5bf25df 100644 --- a/noir/compiler/noirc_evaluator/src/ssa/acir_gen/mod.rs +++ b/noir/compiler/noirc_evaluator/src/ssa/acir_gen/mod.rs @@ -1541,7 +1541,7 @@ impl Context { let vars = self.acir_context.black_box_function(black_box, inputs, output_count)?; - Ok(Self::convert_vars_to_values(vars, dfg, result_ids)) + Ok(self.convert_vars_to_values(vars, dfg, result_ids)) } Intrinsic::ApplyRangeConstraint => { unreachable!("ICE: `Intrinsic::ApplyRangeConstraint` calls should be transformed into an `Instruction::RangeCheck`"); @@ -1588,7 +1588,7 @@ impl Context { .sort(input_vars, bit_size, self.current_side_effects_enabled_var) .expect("Could not sort"); - Ok(Self::convert_vars_to_values(out_vars, dfg, result_ids)) + Ok(self.convert_vars_to_values(out_vars, dfg, result_ids)) } Intrinsic::ArrayLen => { let len = match self.convert_value(arguments[0], dfg) { @@ -2101,16 +2101,35 @@ impl Context { /// Convert a Vec into a Vec using the given result ids. /// If the type of a result id is an array, several acir vars are collected into /// a single AcirValue::Array of the same length. + /// If the type of a result id is a slice, the slice length must precede it and we can + /// convert to an AcirValue::Array when the length is known (constant). fn convert_vars_to_values( + &self, vars: Vec, dfg: &DataFlowGraph, result_ids: &[ValueId], ) -> Vec { let mut vars = vars.into_iter(); - vecmap(result_ids, |result| { + let mut values: Vec = Vec::new(); + for result in result_ids { let result_type = dfg.type_of_value(*result); - Self::convert_var_type_to_values(&result_type, &mut vars) - }) + if let Type::Slice(elements_type) = result_type { + let error = "ICE - cannot get slice length when converting slice to AcirValue"; + let len = values.last().expect(error).borrow_var().expect(error); + let len = self.acir_context.constant(len).to_u128(); + let mut element_values = im::Vector::new(); + for _ in 0..len { + for element_type in elements_type.iter() { + let element = Self::convert_var_type_to_values(element_type, &mut vars); + element_values.push_back(element); + } + } + values.push(AcirValue::Array(element_values)); + } else { + values.push(Self::convert_var_type_to_values(&result_type, &mut vars)); + } + } + values } /// Recursive helper for convert_vars_to_values. diff --git a/noir/compiler/noirc_evaluator/src/ssa/ir/instruction.rs b/noir/compiler/noirc_evaluator/src/ssa/ir/instruction.rs index 457fe41de93..331a02a6974 100644 --- a/noir/compiler/noirc_evaluator/src/ssa/ir/instruction.rs +++ b/noir/compiler/noirc_evaluator/src/ssa/ir/instruction.rs @@ -1,6 +1,5 @@ use acvm::{acir::BlackBoxFunc, FieldElement}; use iter_extended::vecmap; -use num_bigint::BigUint; use super::{ basic_block::BasicBlockId, @@ -10,9 +9,15 @@ use super::{ value::{Value, ValueId}, }; +mod binary; mod call; +mod cast; +mod constrain; +pub(crate) use binary::{Binary, BinaryOp}; use call::simplify_call; +use cast::simplify_cast; +use constrain::decompose_constrain; /// Reference to an instruction /// @@ -82,6 +87,9 @@ impl Intrinsic { match self { Intrinsic::AssertConstant | Intrinsic::ApplyRangeConstraint => true, + // These apply a constraint that the input must fit into a specified number of limbs. + Intrinsic::ToBits(_) | Intrinsic::ToRadix(_) => true, + Intrinsic::Sort | Intrinsic::ArrayLen | Intrinsic::SlicePushBack @@ -91,8 +99,6 @@ impl Intrinsic { | Intrinsic::SliceInsert | Intrinsic::SliceRemove | Intrinsic::StrAsBytes - | Intrinsic::ToBits(_) - | Intrinsic::ToRadix(_) | Intrinsic::FromField | Intrinsic::AsField => false, @@ -545,179 +551,6 @@ impl Instruction { } } -/// Try to simplify this cast instruction. If the instruction can be simplified to a known value, -/// that value is returned. Otherwise None is returned. -fn simplify_cast(value: ValueId, dst_typ: &Type, dfg: &mut DataFlowGraph) -> SimplifyResult { - use SimplifyResult::*; - let value = dfg.resolve(value); - - if let Value::Instruction { instruction, .. } = &dfg[value] { - if let Instruction::Cast(original_value, _) = &dfg[*instruction] { - return SimplifiedToInstruction(Instruction::Cast(*original_value, dst_typ.clone())); - } - } - - if let Some(constant) = dfg.get_numeric_constant(value) { - let src_typ = dfg.type_of_value(value); - match (src_typ, dst_typ) { - (Type::Numeric(NumericType::NativeField), Type::Numeric(NumericType::NativeField)) => { - // Field -> Field: use src value - SimplifiedTo(value) - } - ( - Type::Numeric(NumericType::Unsigned { .. }), - Type::Numeric(NumericType::NativeField), - ) => { - // Unsigned -> Field: redefine same constant as Field - SimplifiedTo(dfg.make_constant(constant, dst_typ.clone())) - } - ( - Type::Numeric( - NumericType::NativeField - | NumericType::Unsigned { .. } - | NumericType::Signed { .. }, - ), - Type::Numeric(NumericType::Unsigned { bit_size }), - ) => { - // Field/Unsigned -> unsigned: truncate - let integer_modulus = BigUint::from(2u128).pow(*bit_size); - let constant: BigUint = BigUint::from_bytes_be(&constant.to_be_bytes()); - let truncated = constant % integer_modulus; - let truncated = FieldElement::from_be_bytes_reduce(&truncated.to_bytes_be()); - SimplifiedTo(dfg.make_constant(truncated, dst_typ.clone())) - } - _ => None, - } - } else if *dst_typ == dfg.type_of_value(value) { - SimplifiedTo(value) - } else { - None - } -} - -/// Try to decompose this constrain instruction. This constraint will be broken down such that it instead constrains -/// all the values which are used to compute the values which were being constrained. -fn decompose_constrain( - lhs: ValueId, - rhs: ValueId, - msg: Option, - dfg: &mut DataFlowGraph, -) -> Vec { - let lhs = dfg.resolve(lhs); - let rhs = dfg.resolve(rhs); - - if lhs == rhs { - // Remove trivial case `assert_eq(x, x)` - Vec::new() - } else { - match (&dfg[lhs], &dfg[rhs]) { - (Value::NumericConstant { constant, typ }, Value::Instruction { instruction, .. }) - | (Value::Instruction { instruction, .. }, Value::NumericConstant { constant, typ }) - if *typ == Type::bool() => - { - match dfg[*instruction] { - Instruction::Binary(Binary { lhs, rhs, operator: BinaryOp::Eq }) - if constant.is_one() => - { - // Replace an explicit two step equality assertion - // - // v2 = eq v0, u32 v1 - // constrain v2 == u1 1 - // - // with a direct assertion of equality between the two values - // - // v2 = eq v0, u32 v1 - // constrain v0 == v1 - // - // Note that this doesn't remove the value `v2` as it may be used in other instructions, but it - // will likely be removed through dead instruction elimination. - - vec![Instruction::Constrain(lhs, rhs, msg)] - } - - Instruction::Binary(Binary { lhs, rhs, operator: BinaryOp::Mul }) - if constant.is_one() && dfg.type_of_value(lhs) == Type::bool() => - { - // Replace an equality assertion on a boolean multiplication - // - // v2 = mul v0, v1 - // constrain v2 == u1 1 - // - // with a direct assertion that each value is equal to 1 - // - // v2 = mul v0, v1 - // constrain v0 == 1 - // constrain v1 == 1 - // - // This is due to the fact that for `v2` to be 1 then both `v0` and `v1` are 1. - // - // Note that this doesn't remove the value `v2` as it may be used in other instructions, but it - // will likely be removed through dead instruction elimination. - let one = FieldElement::one(); - let one = dfg.make_constant(one, Type::bool()); - - [ - decompose_constrain(lhs, one, msg.clone(), dfg), - decompose_constrain(rhs, one, msg, dfg), - ] - .concat() - } - - Instruction::Binary(Binary { lhs, rhs, operator: BinaryOp::Or }) - if constant.is_zero() => - { - // Replace an equality assertion on an OR - // - // v2 = or v0, v1 - // constrain v2 == u1 0 - // - // with a direct assertion that each value is equal to 0 - // - // v2 = or v0, v1 - // constrain v0 == 0 - // constrain v1 == 0 - // - // This is due to the fact that for `v2` to be 0 then both `v0` and `v1` are 0. - // - // Note that this doesn't remove the value `v2` as it may be used in other instructions, but it - // will likely be removed through dead instruction elimination. - let zero = FieldElement::zero(); - let zero = dfg.make_constant(zero, dfg.type_of_value(lhs)); - - [ - decompose_constrain(lhs, zero, msg.clone(), dfg), - decompose_constrain(rhs, zero, msg, dfg), - ] - .concat() - } - - Instruction::Not(value) => { - // Replace an assertion that a not instruction is truthy - // - // v1 = not v0 - // constrain v1 == u1 1 - // - // with an assertion that the not instruction input is falsy - // - // v1 = not v0 - // constrain v0 == u1 0 - // - // Note that this doesn't remove the value `v1` as it may be used in other instructions, but it - // will likely be removed through dead instruction elimination. - let reversed_constant = FieldElement::from(!constant.is_one()); - let reversed_constant = dfg.make_constant(reversed_constant, Type::bool()); - decompose_constrain(value, reversed_constant, msg, dfg) - } - - _ => vec![Instruction::Constrain(lhs, rhs, msg)], - } - } - - _ => vec![Instruction::Constrain(lhs, rhs, msg)], - } - } -} - /// The possible return values for Instruction::return_types pub(crate) enum InstructionResultType { /// The result type of this instruction matches that of this operand @@ -848,350 +681,6 @@ impl TerminatorInstruction { } } -/// A binary instruction in the IR. -#[derive(Debug, PartialEq, Eq, Hash, Clone)] -pub(crate) struct Binary { - /// Left hand side of the binary operation - pub(crate) lhs: ValueId, - /// Right hand side of the binary operation - pub(crate) rhs: ValueId, - /// The binary operation to apply - pub(crate) operator: BinaryOp, -} - -impl Binary { - /// The type of this Binary instruction's result - pub(crate) fn result_type(&self) -> InstructionResultType { - match self.operator { - BinaryOp::Eq | BinaryOp::Lt => InstructionResultType::Known(Type::bool()), - _ => InstructionResultType::Operand(self.lhs), - } - } - - /// Try to simplify this binary instruction, returning the new value if possible. - fn simplify(&self, dfg: &mut DataFlowGraph) -> SimplifyResult { - let lhs = dfg.get_numeric_constant(self.lhs); - let rhs = dfg.get_numeric_constant(self.rhs); - let operand_type = dfg.type_of_value(self.lhs); - - if let (Some(lhs), Some(rhs)) = (lhs, rhs) { - return match eval_constant_binary_op(lhs, rhs, self.operator, operand_type) { - Some((result, result_type)) => { - let value = dfg.make_constant(result, result_type); - SimplifyResult::SimplifiedTo(value) - } - None => SimplifyResult::None, - }; - } - - let lhs_is_zero = lhs.map_or(false, |lhs| lhs.is_zero()); - let rhs_is_zero = rhs.map_or(false, |rhs| rhs.is_zero()); - - let lhs_is_one = lhs.map_or(false, |lhs| lhs.is_one()); - let rhs_is_one = rhs.map_or(false, |rhs| rhs.is_one()); - - match self.operator { - BinaryOp::Add => { - if lhs_is_zero { - return SimplifyResult::SimplifiedTo(self.rhs); - } - if rhs_is_zero { - return SimplifyResult::SimplifiedTo(self.lhs); - } - } - BinaryOp::Sub => { - if rhs_is_zero { - return SimplifyResult::SimplifiedTo(self.lhs); - } - } - BinaryOp::Mul => { - if lhs_is_one { - return SimplifyResult::SimplifiedTo(self.rhs); - } - if rhs_is_one { - return SimplifyResult::SimplifiedTo(self.lhs); - } - if lhs_is_zero || rhs_is_zero { - let zero = dfg.make_constant(FieldElement::zero(), operand_type); - return SimplifyResult::SimplifiedTo(zero); - } - } - BinaryOp::Div => { - if rhs_is_one { - return SimplifyResult::SimplifiedTo(self.lhs); - } - } - BinaryOp::Mod => { - if rhs_is_one { - let zero = dfg.make_constant(FieldElement::zero(), operand_type); - return SimplifyResult::SimplifiedTo(zero); - } - } - BinaryOp::Eq => { - if dfg.resolve(self.lhs) == dfg.resolve(self.rhs) { - let one = dfg.make_constant(FieldElement::one(), Type::bool()); - return SimplifyResult::SimplifiedTo(one); - } - if operand_type == Type::bool() { - // Simplify forms of `(boolean == true)` into `boolean` - if lhs_is_one { - return SimplifyResult::SimplifiedTo(self.rhs); - } - if rhs_is_one { - return SimplifyResult::SimplifiedTo(self.lhs); - } - // Simplify forms of `(boolean == false)` into `!boolean` - if lhs_is_zero { - return SimplifyResult::SimplifiedToInstruction(Instruction::Not(self.rhs)); - } - if rhs_is_zero { - return SimplifyResult::SimplifiedToInstruction(Instruction::Not(self.lhs)); - } - } - } - BinaryOp::Lt => { - if dfg.resolve(self.lhs) == dfg.resolve(self.rhs) { - let zero = dfg.make_constant(FieldElement::zero(), Type::bool()); - return SimplifyResult::SimplifiedTo(zero); - } - if operand_type.is_unsigned() { - if rhs_is_zero { - // Unsigned values cannot be less than zero. - let zero = dfg.make_constant(FieldElement::zero(), Type::bool()); - return SimplifyResult::SimplifiedTo(zero); - } else if rhs_is_one { - let zero = dfg.make_constant(FieldElement::zero(), operand_type); - return SimplifyResult::SimplifiedToInstruction(Instruction::binary( - BinaryOp::Eq, - self.lhs, - zero, - )); - } - } - } - BinaryOp::And => { - if lhs_is_zero || rhs_is_zero { - let zero = dfg.make_constant(FieldElement::zero(), operand_type); - return SimplifyResult::SimplifiedTo(zero); - } - if dfg.resolve(self.lhs) == dfg.resolve(self.rhs) { - return SimplifyResult::SimplifiedTo(self.lhs); - } - if operand_type == Type::bool() { - // Boolean AND is equivalent to multiplication, which is a cheaper operation. - let instruction = Instruction::binary(BinaryOp::Mul, self.lhs, self.rhs); - return SimplifyResult::SimplifiedToInstruction(instruction); - } - } - BinaryOp::Or => { - if lhs_is_zero { - return SimplifyResult::SimplifiedTo(self.rhs); - } - if rhs_is_zero { - return SimplifyResult::SimplifiedTo(self.lhs); - } - if dfg.resolve(self.lhs) == dfg.resolve(self.rhs) { - return SimplifyResult::SimplifiedTo(self.lhs); - } - } - BinaryOp::Xor => { - if lhs_is_zero { - return SimplifyResult::SimplifiedTo(self.rhs); - } - if rhs_is_zero { - return SimplifyResult::SimplifiedTo(self.lhs); - } - if dfg.resolve(self.lhs) == dfg.resolve(self.rhs) { - let zero = dfg.make_constant(FieldElement::zero(), Type::bool()); - return SimplifyResult::SimplifiedTo(zero); - } - } - } - SimplifyResult::None - } -} - -/// Evaluate a binary operation with constant arguments. -fn eval_constant_binary_op( - lhs: FieldElement, - rhs: FieldElement, - operator: BinaryOp, - mut operand_type: Type, -) -> Option<(FieldElement, Type)> { - let value = match &operand_type { - Type::Numeric(NumericType::NativeField) => { - // If the rhs of a division is zero, attempting to evaluate the division will cause a compiler panic. - // Thus, we do not evaluate the division in this method, as we want to avoid triggering a panic, - // and the operation should be handled by ACIR generation. - if matches!(operator, BinaryOp::Div | BinaryOp::Mod) && rhs == FieldElement::zero() { - return None; - } - operator.get_field_function()?(lhs, rhs) - } - Type::Numeric(NumericType::Unsigned { bit_size }) => { - let function = operator.get_u128_function(); - - let lhs = truncate(lhs.try_into_u128()?, *bit_size); - let rhs = truncate(rhs.try_into_u128()?, *bit_size); - - // The divisor is being truncated into the type of the operand, which can potentially - // lead to the rhs being zero. - // If the rhs of a division is zero, attempting to evaluate the division will cause a compiler panic. - // Thus, we do not evaluate the division in this method, as we want to avoid triggering a panic, - // and the operation should be handled by ACIR generation. - if matches!(operator, BinaryOp::Div | BinaryOp::Mod) && rhs == 0 { - return None; - } - let result = function(lhs, rhs)?; - // Check for overflow - if result >= 2u128.pow(*bit_size) { - return None; - } - result.into() - } - Type::Numeric(NumericType::Signed { bit_size }) => { - let function = operator.get_i128_function(); - - let lhs = truncate(lhs.try_into_u128()?, *bit_size); - let rhs = truncate(rhs.try_into_u128()?, *bit_size); - let l_pos = lhs < 2u128.pow(bit_size - 1); - let r_pos = rhs < 2u128.pow(bit_size - 1); - let lhs = if l_pos { lhs as i128 } else { -((2u128.pow(*bit_size) - lhs) as i128) }; - let rhs = if r_pos { rhs as i128 } else { -((2u128.pow(*bit_size) - rhs) as i128) }; - // The divisor is being truncated into the type of the operand, which can potentially - // lead to the rhs being zero. - // If the rhs of a division is zero, attempting to evaluate the division will cause a compiler panic. - // Thus, we do not evaluate the division in this method, as we want to avoid triggering a panic, - // and the operation should be handled by ACIR generation. - if matches!(operator, BinaryOp::Div | BinaryOp::Mod) && rhs == 0 { - return None; - } - - let result = function(lhs, rhs)?; - // Check for overflow - if result >= 2i128.pow(*bit_size - 1) || result < -(2i128.pow(*bit_size - 1)) { - return None; - } - let result = - if result >= 0 { result as u128 } else { (2i128.pow(*bit_size) + result) as u128 }; - result.into() - } - _ => return None, - }; - - if matches!(operator, BinaryOp::Eq | BinaryOp::Lt) { - operand_type = Type::bool(); - } - - Some((value, operand_type)) -} - -fn truncate(int: u128, bit_size: u32) -> u128 { - let max = 2u128.pow(bit_size); - int % max -} - -impl BinaryOp { - fn get_field_function(self) -> Option FieldElement> { - match self { - BinaryOp::Add => Some(std::ops::Add::add), - BinaryOp::Sub => Some(std::ops::Sub::sub), - BinaryOp::Mul => Some(std::ops::Mul::mul), - BinaryOp::Div => Some(std::ops::Div::div), - BinaryOp::Eq => Some(|x, y| (x == y).into()), - BinaryOp::Lt => Some(|x, y| (x < y).into()), - // Bitwise operators are unsupported for Fields - BinaryOp::Mod => None, - BinaryOp::And => None, - BinaryOp::Or => None, - BinaryOp::Xor => None, - } - } - - fn get_u128_function(self) -> fn(u128, u128) -> Option { - match self { - BinaryOp::Add => u128::checked_add, - BinaryOp::Sub => u128::checked_sub, - BinaryOp::Mul => u128::checked_mul, - BinaryOp::Div => u128::checked_div, - BinaryOp::Mod => u128::checked_rem, - BinaryOp::And => |x, y| Some(x & y), - BinaryOp::Or => |x, y| Some(x | y), - BinaryOp::Xor => |x, y| Some(x ^ y), - BinaryOp::Eq => |x, y| Some((x == y) as u128), - BinaryOp::Lt => |x, y| Some((x < y) as u128), - } - } - - fn get_i128_function(self) -> fn(i128, i128) -> Option { - match self { - BinaryOp::Add => i128::checked_add, - BinaryOp::Sub => i128::checked_sub, - BinaryOp::Mul => i128::checked_mul, - BinaryOp::Div => i128::checked_div, - BinaryOp::Mod => i128::checked_rem, - BinaryOp::And => |x, y| Some(x & y), - BinaryOp::Or => |x, y| Some(x | y), - BinaryOp::Xor => |x, y| Some(x ^ y), - BinaryOp::Eq => |x, y| Some((x == y) as i128), - BinaryOp::Lt => |x, y| Some((x < y) as i128), - } - } -} - -/// Binary Operations allowed in the IR. -/// Aside from the comparison operators (Eq and Lt), all operators -/// will return the same type as their operands. -/// The operand types must match for all binary operators. -/// All binary operators are also only for numeric types. To implement -/// e.g. equality for a compound type like a struct, one must add a -/// separate Eq operation for each field and combine them later with And. -#[derive(Debug, PartialEq, Eq, Hash, Copy, Clone)] -pub(crate) enum BinaryOp { - /// Addition of lhs + rhs. - Add, - /// Subtraction of lhs - rhs. - Sub, - /// Multiplication of lhs * rhs. - Mul, - /// Division of lhs / rhs. - Div, - /// Modulus of lhs % rhs. - Mod, - /// Checks whether two types are equal. - /// Returns true if the types were equal and - /// false otherwise. - Eq, - /// Checks whether the lhs is less than the rhs. - /// All other comparison operators should be translated - /// to less than. For example (a > b) = (b < a) = !(a >= b) = !(b <= a). - /// The result will always be a u1. - Lt, - /// Bitwise and (&) - And, - /// Bitwise or (|) - Or, - /// Bitwise xor (^) - Xor, -} - -impl std::fmt::Display for BinaryOp { - fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { - match self { - BinaryOp::Add => write!(f, "add"), - BinaryOp::Sub => write!(f, "sub"), - BinaryOp::Mul => write!(f, "mul"), - BinaryOp::Div => write!(f, "div"), - BinaryOp::Eq => write!(f, "eq"), - BinaryOp::Mod => write!(f, "mod"), - BinaryOp::Lt => write!(f, "lt"), - BinaryOp::And => write!(f, "and"), - BinaryOp::Or => write!(f, "or"), - BinaryOp::Xor => write!(f, "xor"), - } - } -} - /// Contains the result to Instruction::simplify, specifying how the instruction /// should be simplified. pub(crate) enum SimplifyResult { diff --git a/noir/compiler/noirc_evaluator/src/ssa/ir/instruction/binary.rs b/noir/compiler/noirc_evaluator/src/ssa/ir/instruction/binary.rs new file mode 100644 index 00000000000..1cb32d94148 --- /dev/null +++ b/noir/compiler/noirc_evaluator/src/ssa/ir/instruction/binary.rs @@ -0,0 +1,349 @@ +use acvm::FieldElement; + +use super::{ + DataFlowGraph, Instruction, InstructionResultType, NumericType, SimplifyResult, Type, ValueId, +}; + +/// Binary Operations allowed in the IR. +/// Aside from the comparison operators (Eq and Lt), all operators +/// will return the same type as their operands. +/// The operand types must match for all binary operators. +/// All binary operators are also only for numeric types. To implement +/// e.g. equality for a compound type like a struct, one must add a +/// separate Eq operation for each field and combine them later with And. +#[derive(Debug, PartialEq, Eq, Hash, Copy, Clone)] +pub(crate) enum BinaryOp { + /// Addition of lhs + rhs. + Add, + /// Subtraction of lhs - rhs. + Sub, + /// Multiplication of lhs * rhs. + Mul, + /// Division of lhs / rhs. + Div, + /// Modulus of lhs % rhs. + Mod, + /// Checks whether two types are equal. + /// Returns true if the types were equal and + /// false otherwise. + Eq, + /// Checks whether the lhs is less than the rhs. + /// All other comparison operators should be translated + /// to less than. For example (a > b) = (b < a) = !(a >= b) = !(b <= a). + /// The result will always be a u1. + Lt, + /// Bitwise and (&) + And, + /// Bitwise or (|) + Or, + /// Bitwise xor (^) + Xor, +} + +impl std::fmt::Display for BinaryOp { + fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { + match self { + BinaryOp::Add => write!(f, "add"), + BinaryOp::Sub => write!(f, "sub"), + BinaryOp::Mul => write!(f, "mul"), + BinaryOp::Div => write!(f, "div"), + BinaryOp::Eq => write!(f, "eq"), + BinaryOp::Mod => write!(f, "mod"), + BinaryOp::Lt => write!(f, "lt"), + BinaryOp::And => write!(f, "and"), + BinaryOp::Or => write!(f, "or"), + BinaryOp::Xor => write!(f, "xor"), + } + } +} + +/// A binary instruction in the IR. +#[derive(Debug, PartialEq, Eq, Hash, Clone)] +pub(crate) struct Binary { + /// Left hand side of the binary operation + pub(crate) lhs: ValueId, + /// Right hand side of the binary operation + pub(crate) rhs: ValueId, + /// The binary operation to apply + pub(crate) operator: BinaryOp, +} + +impl Binary { + /// The type of this Binary instruction's result + pub(crate) fn result_type(&self) -> InstructionResultType { + match self.operator { + BinaryOp::Eq | BinaryOp::Lt => InstructionResultType::Known(Type::bool()), + _ => InstructionResultType::Operand(self.lhs), + } + } + + /// Try to simplify this binary instruction, returning the new value if possible. + pub(super) fn simplify(&self, dfg: &mut DataFlowGraph) -> SimplifyResult { + let lhs = dfg.get_numeric_constant(self.lhs); + let rhs = dfg.get_numeric_constant(self.rhs); + let operand_type = dfg.type_of_value(self.lhs); + + if let (Some(lhs), Some(rhs)) = (lhs, rhs) { + return match eval_constant_binary_op(lhs, rhs, self.operator, operand_type) { + Some((result, result_type)) => { + let value = dfg.make_constant(result, result_type); + SimplifyResult::SimplifiedTo(value) + } + None => SimplifyResult::None, + }; + } + + let lhs_is_zero = lhs.map_or(false, |lhs| lhs.is_zero()); + let rhs_is_zero = rhs.map_or(false, |rhs| rhs.is_zero()); + + let lhs_is_one = lhs.map_or(false, |lhs| lhs.is_one()); + let rhs_is_one = rhs.map_or(false, |rhs| rhs.is_one()); + + match self.operator { + BinaryOp::Add => { + if lhs_is_zero { + return SimplifyResult::SimplifiedTo(self.rhs); + } + if rhs_is_zero { + return SimplifyResult::SimplifiedTo(self.lhs); + } + } + BinaryOp::Sub => { + if rhs_is_zero { + return SimplifyResult::SimplifiedTo(self.lhs); + } + } + BinaryOp::Mul => { + if lhs_is_one { + return SimplifyResult::SimplifiedTo(self.rhs); + } + if rhs_is_one { + return SimplifyResult::SimplifiedTo(self.lhs); + } + if lhs_is_zero || rhs_is_zero { + let zero = dfg.make_constant(FieldElement::zero(), operand_type); + return SimplifyResult::SimplifiedTo(zero); + } + } + BinaryOp::Div => { + if rhs_is_one { + return SimplifyResult::SimplifiedTo(self.lhs); + } + } + BinaryOp::Mod => { + if rhs_is_one { + let zero = dfg.make_constant(FieldElement::zero(), operand_type); + return SimplifyResult::SimplifiedTo(zero); + } + } + BinaryOp::Eq => { + if dfg.resolve(self.lhs) == dfg.resolve(self.rhs) { + let one = dfg.make_constant(FieldElement::one(), Type::bool()); + return SimplifyResult::SimplifiedTo(one); + } + if operand_type == Type::bool() { + // Simplify forms of `(boolean == true)` into `boolean` + if lhs_is_one { + return SimplifyResult::SimplifiedTo(self.rhs); + } + if rhs_is_one { + return SimplifyResult::SimplifiedTo(self.lhs); + } + // Simplify forms of `(boolean == false)` into `!boolean` + if lhs_is_zero { + return SimplifyResult::SimplifiedToInstruction(Instruction::Not(self.rhs)); + } + if rhs_is_zero { + return SimplifyResult::SimplifiedToInstruction(Instruction::Not(self.lhs)); + } + } + } + BinaryOp::Lt => { + if dfg.resolve(self.lhs) == dfg.resolve(self.rhs) { + let zero = dfg.make_constant(FieldElement::zero(), Type::bool()); + return SimplifyResult::SimplifiedTo(zero); + } + if operand_type.is_unsigned() { + if rhs_is_zero { + // Unsigned values cannot be less than zero. + let zero = dfg.make_constant(FieldElement::zero(), Type::bool()); + return SimplifyResult::SimplifiedTo(zero); + } else if rhs_is_one { + let zero = dfg.make_constant(FieldElement::zero(), operand_type); + return SimplifyResult::SimplifiedToInstruction(Instruction::binary( + BinaryOp::Eq, + self.lhs, + zero, + )); + } + } + } + BinaryOp::And => { + if lhs_is_zero || rhs_is_zero { + let zero = dfg.make_constant(FieldElement::zero(), operand_type); + return SimplifyResult::SimplifiedTo(zero); + } + if dfg.resolve(self.lhs) == dfg.resolve(self.rhs) { + return SimplifyResult::SimplifiedTo(self.lhs); + } + if operand_type == Type::bool() { + // Boolean AND is equivalent to multiplication, which is a cheaper operation. + let instruction = Instruction::binary(BinaryOp::Mul, self.lhs, self.rhs); + return SimplifyResult::SimplifiedToInstruction(instruction); + } + } + BinaryOp::Or => { + if lhs_is_zero { + return SimplifyResult::SimplifiedTo(self.rhs); + } + if rhs_is_zero { + return SimplifyResult::SimplifiedTo(self.lhs); + } + if dfg.resolve(self.lhs) == dfg.resolve(self.rhs) { + return SimplifyResult::SimplifiedTo(self.lhs); + } + } + BinaryOp::Xor => { + if lhs_is_zero { + return SimplifyResult::SimplifiedTo(self.rhs); + } + if rhs_is_zero { + return SimplifyResult::SimplifiedTo(self.lhs); + } + if dfg.resolve(self.lhs) == dfg.resolve(self.rhs) { + let zero = dfg.make_constant(FieldElement::zero(), operand_type); + return SimplifyResult::SimplifiedTo(zero); + } + } + } + SimplifyResult::None + } +} + +/// Evaluate a binary operation with constant arguments. +fn eval_constant_binary_op( + lhs: FieldElement, + rhs: FieldElement, + operator: BinaryOp, + mut operand_type: Type, +) -> Option<(FieldElement, Type)> { + let value = match &operand_type { + Type::Numeric(NumericType::NativeField) => { + // If the rhs of a division is zero, attempting to evaluate the division will cause a compiler panic. + // Thus, we do not evaluate the division in this method, as we want to avoid triggering a panic, + // and the operation should be handled by ACIR generation. + if matches!(operator, BinaryOp::Div | BinaryOp::Mod) && rhs == FieldElement::zero() { + return None; + } + operator.get_field_function()?(lhs, rhs) + } + Type::Numeric(NumericType::Unsigned { bit_size }) => { + let function = operator.get_u128_function(); + + let lhs = truncate(lhs.try_into_u128()?, *bit_size); + let rhs = truncate(rhs.try_into_u128()?, *bit_size); + + // The divisor is being truncated into the type of the operand, which can potentially + // lead to the rhs being zero. + // If the rhs of a division is zero, attempting to evaluate the division will cause a compiler panic. + // Thus, we do not evaluate the division in this method, as we want to avoid triggering a panic, + // and the operation should be handled by ACIR generation. + if matches!(operator, BinaryOp::Div | BinaryOp::Mod) && rhs == 0 { + return None; + } + let result = function(lhs, rhs)?; + // Check for overflow + if result >= 2u128.pow(*bit_size) { + return None; + } + result.into() + } + Type::Numeric(NumericType::Signed { bit_size }) => { + let function = operator.get_i128_function(); + + let lhs = truncate(lhs.try_into_u128()?, *bit_size); + let rhs = truncate(rhs.try_into_u128()?, *bit_size); + let l_pos = lhs < 2u128.pow(bit_size - 1); + let r_pos = rhs < 2u128.pow(bit_size - 1); + let lhs = if l_pos { lhs as i128 } else { -((2u128.pow(*bit_size) - lhs) as i128) }; + let rhs = if r_pos { rhs as i128 } else { -((2u128.pow(*bit_size) - rhs) as i128) }; + // The divisor is being truncated into the type of the operand, which can potentially + // lead to the rhs being zero. + // If the rhs of a division is zero, attempting to evaluate the division will cause a compiler panic. + // Thus, we do not evaluate the division in this method, as we want to avoid triggering a panic, + // and the operation should be handled by ACIR generation. + if matches!(operator, BinaryOp::Div | BinaryOp::Mod) && rhs == 0 { + return None; + } + + let result = function(lhs, rhs)?; + // Check for overflow + if result >= 2i128.pow(*bit_size - 1) || result < -(2i128.pow(*bit_size - 1)) { + return None; + } + let result = + if result >= 0 { result as u128 } else { (2i128.pow(*bit_size) + result) as u128 }; + result.into() + } + _ => return None, + }; + + if matches!(operator, BinaryOp::Eq | BinaryOp::Lt) { + operand_type = Type::bool(); + } + + Some((value, operand_type)) +} + +fn truncate(int: u128, bit_size: u32) -> u128 { + let max = 2u128.pow(bit_size); + int % max +} + +impl BinaryOp { + fn get_field_function(self) -> Option FieldElement> { + match self { + BinaryOp::Add => Some(std::ops::Add::add), + BinaryOp::Sub => Some(std::ops::Sub::sub), + BinaryOp::Mul => Some(std::ops::Mul::mul), + BinaryOp::Div => Some(std::ops::Div::div), + BinaryOp::Eq => Some(|x, y| (x == y).into()), + BinaryOp::Lt => Some(|x, y| (x < y).into()), + // Bitwise operators are unsupported for Fields + BinaryOp::Mod => None, + BinaryOp::And => None, + BinaryOp::Or => None, + BinaryOp::Xor => None, + } + } + + fn get_u128_function(self) -> fn(u128, u128) -> Option { + match self { + BinaryOp::Add => u128::checked_add, + BinaryOp::Sub => u128::checked_sub, + BinaryOp::Mul => u128::checked_mul, + BinaryOp::Div => u128::checked_div, + BinaryOp::Mod => u128::checked_rem, + BinaryOp::And => |x, y| Some(x & y), + BinaryOp::Or => |x, y| Some(x | y), + BinaryOp::Xor => |x, y| Some(x ^ y), + BinaryOp::Eq => |x, y| Some((x == y) as u128), + BinaryOp::Lt => |x, y| Some((x < y) as u128), + } + } + + fn get_i128_function(self) -> fn(i128, i128) -> Option { + match self { + BinaryOp::Add => i128::checked_add, + BinaryOp::Sub => i128::checked_sub, + BinaryOp::Mul => i128::checked_mul, + BinaryOp::Div => i128::checked_div, + BinaryOp::Mod => i128::checked_rem, + BinaryOp::And => |x, y| Some(x & y), + BinaryOp::Or => |x, y| Some(x | y), + BinaryOp::Xor => |x, y| Some(x ^ y), + BinaryOp::Eq => |x, y| Some((x == y) as i128), + BinaryOp::Lt => |x, y| Some((x < y) as i128), + } + } +} diff --git a/noir/compiler/noirc_evaluator/src/ssa/ir/instruction/call.rs b/noir/compiler/noirc_evaluator/src/ssa/ir/instruction/call.rs index d1991abab37..0178ae9dba1 100644 --- a/noir/compiler/noirc_evaluator/src/ssa/ir/instruction/call.rs +++ b/noir/compiler/noirc_evaluator/src/ssa/ir/instruction/call.rs @@ -417,7 +417,7 @@ fn simplify_black_box_func( _ => SimplifyResult::None, } } - + BlackBoxFunc::Poseidon2Permutation => SimplifyResult::None, //TODO(Guillaume) BlackBoxFunc::EcdsaSecp256k1 => { simplify_signature(dfg, arguments, acvm::blackbox_solver::ecdsa_secp256k1_verify) } @@ -429,13 +429,17 @@ fn simplify_black_box_func( | BlackBoxFunc::SchnorrVerify | BlackBoxFunc::PedersenCommitment | BlackBoxFunc::PedersenHash - | BlackBoxFunc::EmbeddedCurveAdd - | BlackBoxFunc::EmbeddedCurveDouble => { + | BlackBoxFunc::EmbeddedCurveAdd => { // Currently unsolvable here as we rely on an implementation in the backend. SimplifyResult::None } - - BlackBoxFunc::RecursiveAggregation => SimplifyResult::None, + BlackBoxFunc::BigIntAdd + | BlackBoxFunc::BigIntNeg + | BlackBoxFunc::BigIntMul + | BlackBoxFunc::BigIntDiv + | BlackBoxFunc::RecursiveAggregation + | BlackBoxFunc::BigIntFromLeBytes + | BlackBoxFunc::BigIntToLeBytes => SimplifyResult::None, BlackBoxFunc::AND => { unreachable!("ICE: `BlackBoxFunc::AND` calls should be transformed into a `BinaryOp`") @@ -448,6 +452,7 @@ fn simplify_black_box_func( "ICE: `BlackBoxFunc::RANGE` calls should be transformed into a `Instruction::Cast`" ) } + BlackBoxFunc::Sha256Compression => SimplifyResult::None, //TODO(Guillaume) } } diff --git a/noir/compiler/noirc_evaluator/src/ssa/ir/instruction/cast.rs b/noir/compiler/noirc_evaluator/src/ssa/ir/instruction/cast.rs new file mode 100644 index 00000000000..671820e801d --- /dev/null +++ b/noir/compiler/noirc_evaluator/src/ssa/ir/instruction/cast.rs @@ -0,0 +1,58 @@ +use acvm::FieldElement; +use num_bigint::BigUint; + +use super::{DataFlowGraph, Instruction, NumericType, SimplifyResult, Type, Value, ValueId}; + +/// Try to simplify this cast instruction. If the instruction can be simplified to a known value, +/// that value is returned. Otherwise None is returned. +pub(super) fn simplify_cast( + value: ValueId, + dst_typ: &Type, + dfg: &mut DataFlowGraph, +) -> SimplifyResult { + use SimplifyResult::*; + let value = dfg.resolve(value); + + if let Value::Instruction { instruction, .. } = &dfg[value] { + if let Instruction::Cast(original_value, _) = &dfg[*instruction] { + return SimplifiedToInstruction(Instruction::Cast(*original_value, dst_typ.clone())); + } + } + + if let Some(constant) = dfg.get_numeric_constant(value) { + let src_typ = dfg.type_of_value(value); + match (src_typ, dst_typ) { + (Type::Numeric(NumericType::NativeField), Type::Numeric(NumericType::NativeField)) => { + // Field -> Field: use src value + SimplifiedTo(value) + } + ( + Type::Numeric(NumericType::Unsigned { .. }), + Type::Numeric(NumericType::NativeField), + ) => { + // Unsigned -> Field: redefine same constant as Field + SimplifiedTo(dfg.make_constant(constant, dst_typ.clone())) + } + ( + Type::Numeric( + NumericType::NativeField + | NumericType::Unsigned { .. } + | NumericType::Signed { .. }, + ), + Type::Numeric(NumericType::Unsigned { bit_size }), + ) => { + // Field/Unsigned -> unsigned: truncate + let integer_modulus = BigUint::from(2u128).pow(*bit_size); + let constant: BigUint = BigUint::from_bytes_be(&constant.to_be_bytes()); + let truncated = constant % integer_modulus; + let truncated = FieldElement::from_be_bytes_reduce(&truncated.to_bytes_be()); + SimplifiedTo(dfg.make_constant(truncated, dst_typ.clone())) + } + _ => None, + } + } else if *dst_typ == dfg.type_of_value(value) { + SimplifiedTo(value) + } else { + None + } +} diff --git a/noir/compiler/noirc_evaluator/src/ssa/ir/instruction/constrain.rs b/noir/compiler/noirc_evaluator/src/ssa/ir/instruction/constrain.rs new file mode 100644 index 00000000000..7fb0970c834 --- /dev/null +++ b/noir/compiler/noirc_evaluator/src/ssa/ir/instruction/constrain.rs @@ -0,0 +1,126 @@ +use acvm::FieldElement; + +use super::{Binary, BinaryOp, DataFlowGraph, Instruction, Type, Value, ValueId}; + +/// Try to decompose this constrain instruction. This constraint will be broken down such that it instead constrains +/// all the values which are used to compute the values which were being constrained. +pub(super) fn decompose_constrain( + lhs: ValueId, + rhs: ValueId, + msg: Option, + dfg: &mut DataFlowGraph, +) -> Vec { + let lhs = dfg.resolve(lhs); + let rhs = dfg.resolve(rhs); + + if lhs == rhs { + // Remove trivial case `assert_eq(x, x)` + Vec::new() + } else { + match (&dfg[lhs], &dfg[rhs]) { + (Value::NumericConstant { constant, typ }, Value::Instruction { instruction, .. }) + | (Value::Instruction { instruction, .. }, Value::NumericConstant { constant, typ }) + if *typ == Type::bool() => + { + match dfg[*instruction] { + Instruction::Binary(Binary { lhs, rhs, operator: BinaryOp::Eq }) + if constant.is_one() => + { + // Replace an explicit two step equality assertion + // + // v2 = eq v0, u32 v1 + // constrain v2 == u1 1 + // + // with a direct assertion of equality between the two values + // + // v2 = eq v0, u32 v1 + // constrain v0 == v1 + // + // Note that this doesn't remove the value `v2` as it may be used in other instructions, but it + // will likely be removed through dead instruction elimination. + + vec![Instruction::Constrain(lhs, rhs, msg)] + } + + Instruction::Binary(Binary { lhs, rhs, operator: BinaryOp::Mul }) + if constant.is_one() && dfg.type_of_value(lhs) == Type::bool() => + { + // Replace an equality assertion on a boolean multiplication + // + // v2 = mul v0, v1 + // constrain v2 == u1 1 + // + // with a direct assertion that each value is equal to 1 + // + // v2 = mul v0, v1 + // constrain v0 == 1 + // constrain v1 == 1 + // + // This is due to the fact that for `v2` to be 1 then both `v0` and `v1` are 1. + // + // Note that this doesn't remove the value `v2` as it may be used in other instructions, but it + // will likely be removed through dead instruction elimination. + let one = FieldElement::one(); + let one = dfg.make_constant(one, Type::bool()); + + [ + decompose_constrain(lhs, one, msg.clone(), dfg), + decompose_constrain(rhs, one, msg, dfg), + ] + .concat() + } + + Instruction::Binary(Binary { lhs, rhs, operator: BinaryOp::Or }) + if constant.is_zero() => + { + // Replace an equality assertion on an OR + // + // v2 = or v0, v1 + // constrain v2 == u1 0 + // + // with a direct assertion that each value is equal to 0 + // + // v2 = or v0, v1 + // constrain v0 == 0 + // constrain v1 == 0 + // + // This is due to the fact that for `v2` to be 0 then both `v0` and `v1` are 0. + // + // Note that this doesn't remove the value `v2` as it may be used in other instructions, but it + // will likely be removed through dead instruction elimination. + let zero = FieldElement::zero(); + let zero = dfg.make_constant(zero, dfg.type_of_value(lhs)); + + [ + decompose_constrain(lhs, zero, msg.clone(), dfg), + decompose_constrain(rhs, zero, msg, dfg), + ] + .concat() + } + + Instruction::Not(value) => { + // Replace an assertion that a not instruction is truthy + // + // v1 = not v0 + // constrain v1 == u1 1 + // + // with an assertion that the not instruction input is falsy + // + // v1 = not v0 + // constrain v0 == u1 0 + // + // Note that this doesn't remove the value `v1` as it may be used in other instructions, but it + // will likely be removed through dead instruction elimination. + let reversed_constant = FieldElement::from(!constant.is_one()); + let reversed_constant = dfg.make_constant(reversed_constant, Type::bool()); + decompose_constrain(value, reversed_constant, msg, dfg) + } + + _ => vec![Instruction::Constrain(lhs, rhs, msg)], + } + } + + _ => vec![Instruction::Constrain(lhs, rhs, msg)], + } + } +} diff --git a/noir/compiler/noirc_evaluator/src/ssa/opt/flatten_cfg.rs b/noir/compiler/noirc_evaluator/src/ssa/opt/flatten_cfg.rs index 6bdf2ab1c0a..1059994b9be 100644 --- a/noir/compiler/noirc_evaluator/src/ssa/opt/flatten_cfg.rs +++ b/noir/compiler/noirc_evaluator/src/ssa/opt/flatten_cfg.rs @@ -144,9 +144,9 @@ use crate::ssa::{ dfg::{CallStack, InsertInstructionResult}, function::Function, function_inserter::FunctionInserter, - instruction::{BinaryOp, Instruction, InstructionId, TerminatorInstruction}, + instruction::{BinaryOp, Instruction, InstructionId, Intrinsic, TerminatorInstruction}, types::Type, - value::ValueId, + value::{Value, ValueId}, }, ssa_gen::Ssa, }; @@ -683,6 +683,28 @@ impl<'f> Context<'f> { ); Instruction::RangeCheck { value, max_bit_size, assert_message } } + Instruction::Call { func, mut arguments } => match self.inserter.function.dfg[func] + { + Value::Intrinsic(Intrinsic::ToBits(_) | Intrinsic::ToRadix(_)) => { + let field = arguments[0]; + let argument_type = self.inserter.function.dfg.type_of_value(field); + + let casted_condition = self.insert_instruction( + Instruction::Cast(condition, argument_type), + call_stack.clone(), + ); + let field = self.insert_instruction( + Instruction::binary(BinaryOp::Mul, field, casted_condition), + call_stack.clone(), + ); + + arguments[0] = field; + + Instruction::Call { func, arguments } + } + + _ => Instruction::Call { func, arguments }, + }, other => other, } } else { diff --git a/noir/compiler/noirc_evaluator/src/ssa/ssa_gen/mod.rs b/noir/compiler/noirc_evaluator/src/ssa/ssa_gen/mod.rs index eb35ba9a65b..9d9635fed34 100644 --- a/noir/compiler/noirc_evaluator/src/ssa/ssa_gen/mod.rs +++ b/noir/compiler/noirc_evaluator/src/ssa/ssa_gen/mod.rs @@ -96,6 +96,9 @@ pub(crate) fn generate_ssa(program: Program) -> Result { _ => unreachable!("ICE - expect return on the last block"), } } + // we save the data bus inside the dfg + function_context.builder.current_function.dfg.data_bus = + DataBus::get_data_bus(call_data, return_data); // Main has now been compiled and any other functions referenced within have been added to the // function queue as they were found in codegen_ident. This queueing will happen each time a @@ -106,9 +109,6 @@ pub(crate) fn generate_ssa(program: Program) -> Result { function_context.new_function(dest_id, function); function_context.codegen_function_body(&function.body)?; } - // we save the data bus inside the dfg - function_context.builder.current_function.dfg.data_bus = - DataBus::get_data_bus(call_data, return_data); Ok(function_context.builder.finish()) } diff --git a/noir/compiler/noirc_frontend/src/ast/function.rs b/noir/compiler/noirc_frontend/src/ast/function.rs index b8f385f52d3..f20fc54b101 100644 --- a/noir/compiler/noirc_frontend/src/ast/function.rs +++ b/noir/compiler/noirc_frontend/src/ast/function.rs @@ -72,7 +72,7 @@ impl NoirFunction { pub fn function_attribute(&self) -> Option<&FunctionAttribute> { self.def.attributes.function.as_ref() } - pub fn secondary_attributes(&self) -> &Vec { + pub fn secondary_attributes(&self) -> &[SecondaryAttribute] { self.def.attributes.secondary.as_ref() } pub fn def(&self) -> &FunctionDefinition { diff --git a/noir/compiler/noirc_frontend/src/hir/def_collector/dc_crate.rs b/noir/compiler/noirc_frontend/src/hir/def_collector/dc_crate.rs index c768ea96f8f..f7441750fc8 100644 --- a/noir/compiler/noirc_frontend/src/hir/def_collector/dc_crate.rs +++ b/noir/compiler/noirc_frontend/src/hir/def_collector/dc_crate.rs @@ -14,7 +14,7 @@ use crate::hir::resolution::{ use crate::hir::type_check::{type_check_func, TypeCheckError, TypeChecker}; use crate::hir::Context; -use crate::macros_api::MacroProcessor; +use crate::macros_api::{MacroError, MacroProcessor}; use crate::node_interner::{FuncId, NodeInterner, StmtId, StructId, TraitId, TypeAliasId}; use crate::parser::{ParserError, SortedModule}; @@ -155,6 +155,12 @@ impl From for CustomDiagnostic { } } +impl From for CompilationError { + fn from(value: MacroError) -> Self { + CompilationError::DefinitionError(DefCollectorErrorKind::MacroError(value)) + } +} + impl From for CompilationError { fn from(value: ParserError) -> Self { CompilationError::ParseError(value) @@ -359,7 +365,11 @@ impl DefCollector { errors.extend(resolved_globals.errors); for macro_processor in macro_processors { - macro_processor.process_typed_ast(&crate_id, context); + macro_processor.process_typed_ast(&crate_id, context).unwrap_or_else( + |(macro_err, file_id)| { + errors.push((macro_err.into(), file_id)); + }, + ); } errors.extend(type_check_globals(&mut context.def_interner, resolved_globals.globals)); @@ -460,7 +470,7 @@ fn type_check_functions( #[allow(clippy::too_many_arguments)] pub(crate) fn check_methods_signatures( resolver: &mut Resolver, - impl_methods: &Vec<(FileId, FuncId)>, + impl_methods: &[(FileId, FuncId)], trait_id: TraitId, trait_name_span: Span, // These are the generics on the trait itself from the impl. diff --git a/noir/compiler/noirc_frontend/src/hir/resolution/errors.rs b/noir/compiler/noirc_frontend/src/hir/resolution/errors.rs index 390807afd17..7bd4de77e84 100644 --- a/noir/compiler/noirc_frontend/src/hir/resolution/errors.rs +++ b/noir/compiler/noirc_frontend/src/hir/resolution/errors.rs @@ -84,6 +84,8 @@ pub enum ResolverError { InvalidTypeForEntryPoint { span: Span }, #[error("Nested slices are not supported")] NestedSlices { span: Span }, + #[error("Usage of the `#[foreign]` or `#[builtin]` function attributes are not allowed outside of the Noir standard library")] + LowLevelFunctionOutsideOfStdlib { ident: Ident }, } impl ResolverError { @@ -311,6 +313,11 @@ impl From for Diagnostic { "Try to use a constant sized array instead".into(), span, ), + ResolverError::LowLevelFunctionOutsideOfStdlib { ident } => Diagnostic::simple_error( + "Definition of low-level function outside of standard library".into(), + "Usage of the `#[foreign]` or `#[builtin]` function attributes are not allowed outside of the Noir standard library".into(), + ident.span(), + ), } } } diff --git a/noir/compiler/noirc_frontend/src/hir/resolution/resolver.rs b/noir/compiler/noirc_frontend/src/hir/resolution/resolver.rs index 1d4f60ffd51..8243b684c8a 100644 --- a/noir/compiler/noirc_frontend/src/hir/resolution/resolver.rs +++ b/noir/compiler/noirc_frontend/src/hir/resolution/resolver.rs @@ -191,10 +191,18 @@ impl<'a> Resolver<'a> { self.add_generics(&func.def.generics); self.trait_bounds = func.def.where_clause.clone(); + let is_low_level_or_oracle = func + .attributes() + .function + .as_ref() + .map_or(false, |func| func.is_low_level() || func.is_oracle()); let (hir_func, func_meta) = self.intern_function(func, func_id); let func_scope_tree = self.scopes.end_function(); - self.check_for_unused_variables_in_scope_tree(func_scope_tree); + // The arguments to low-level and oracle functions are always unused so we do not produce warnings for them. + if !is_low_level_or_oracle { + self.check_for_unused_variables_in_scope_tree(func_scope_tree); + } self.trait_bounds.clear(); (hir_func, func_meta, self.errors) @@ -449,7 +457,7 @@ impl<'a> Resolver<'a> { fn resolve_type_inner(&mut self, typ: UnresolvedType, new_variables: &mut Generics) -> Type { use UnresolvedTypeData::*; - match typ.typ { + let resolved_type = match typ.typ { FieldElement => Type::FieldElement, Array(size, elem) => { let elem = Box::new(self.resolve_type_inner(*elem, new_variables)); @@ -510,7 +518,18 @@ impl<'a> Resolver<'a> { Type::MutableReference(Box::new(self.resolve_type_inner(*element, new_variables))) } Parenthesized(typ) => self.resolve_type_inner(*typ, new_variables), + }; + + if let Type::Struct(_, _) = resolved_type { + if let Some(unresolved_span) = typ.span { + // Record the location of the type reference + self.interner.push_type_ref_location( + resolved_type.clone(), + Location::new(unresolved_span, self.file), + ); + } } + resolved_type } fn find_generic(&self, target_name: &str) -> Option<&(Rc, TypeVariable, Span)> { @@ -714,6 +733,7 @@ impl<'a> Resolver<'a> { if resolved_type.is_nested_slice() { self.errors.push(ResolverError::NestedSlices { span: span.unwrap() }); } + resolved_type } @@ -888,6 +908,13 @@ impl<'a> Resolver<'a> { position: PubPosition::ReturnType, }); } + let is_low_level_function = + func.attributes().function.as_ref().map_or(false, |func| func.is_low_level()); + if !self.path_resolver.module_id().krate.is_stdlib() && is_low_level_function { + let error = + ResolverError::LowLevelFunctionOutsideOfStdlib { ident: func.name_ident().clone() }; + self.push_err(error); + } // 'pub' is required on return types for entry point functions if self.is_entry_point_function(func) @@ -1438,6 +1465,8 @@ impl<'a> Resolver<'a> { HirExpression::MemberAccess(HirMemberAccess { lhs: self.resolve_expression(access.lhs), rhs: access.rhs, + // This is only used when lhs is a reference and we want to return a reference to rhs + is_offset: false, }) } ExpressionKind::Error => HirExpression::Error, @@ -1734,7 +1763,8 @@ impl<'a> Resolver<'a> { // This resolves a static trait method T::trait_method by iterating over the where clause // - // Returns the trait method, object type, and the trait generics. + // Returns the trait method, trait constraint, and whether the impl is assumed from a where + // clause. This is always true since this helper searches where clauses for a generic constraint. // E.g. `t.method()` with `where T: Foo` in scope will return `(Foo::method, T, vec![Bar])` fn resolve_trait_method_by_named_generic( &mut self, @@ -1777,7 +1807,7 @@ impl<'a> Resolver<'a> { // Try to resolve the given trait method path. // - // Returns the trait method, object type, and the trait generics. + // Returns the trait method, trait constraint, and whether the impl is assumed to exist by a where clause or not // E.g. `t.method()` with `where T: Foo` in scope will return `(Foo::method, T, vec![Bar])` fn resolve_trait_generic_path( &mut self, diff --git a/noir/compiler/noirc_frontend/src/hir/type_check/expr.rs b/noir/compiler/noirc_frontend/src/hir/type_check/expr.rs index 58cf4e7b289..5885707a9b7 100644 --- a/noir/compiler/noirc_frontend/src/hir/type_check/expr.rs +++ b/noir/compiler/noirc_frontend/src/hir/type_check/expr.rs @@ -5,8 +5,8 @@ use crate::{ hir::{resolution::resolver::verify_mutable_reference, type_check::errors::Source}, hir_def::{ expr::{ - self, HirArrayLiteral, HirBinaryOp, HirExpression, HirLiteral, HirMethodCallExpression, - HirMethodReference, HirPrefixExpression, ImplKind, + self, HirArrayLiteral, HirBinaryOp, HirExpression, HirIdent, HirLiteral, + HirMethodCallExpression, HirMethodReference, HirPrefixExpression, ImplKind, }, types::Type, }, @@ -46,50 +46,7 @@ impl<'interner> TypeChecker<'interner> { /// function `foo` to refer to. pub(crate) fn check_expression(&mut self, expr_id: &ExprId) -> Type { let typ = match self.interner.expression(expr_id) { - HirExpression::Ident(ident) => { - // An identifiers type may be forall-quantified in the case of generic functions. - // E.g. `fn foo(t: T, field: Field) -> T` has type `forall T. fn(T, Field) -> T`. - // We must instantiate identifiers at every call site to replace this T with a new type - // variable to handle generic functions. - let t = self.interner.id_type_substitute_trait_as_type(ident.id); - - // This instantiate's a trait's generics as well which need to be set - // when the constraint below is later solved for when the function is - // finished. How to link the two? - let (typ, bindings) = t.instantiate(self.interner); - - // Push any trait constraints required by this definition to the context - // to be checked later when the type of this variable is further constrained. - if let Some(definition) = self.interner.try_definition(ident.id) { - if let DefinitionKind::Function(function) = definition.kind { - let function = self.interner.function_meta(&function); - - for mut constraint in function.trait_constraints.clone() { - constraint.apply_bindings(&bindings); - self.trait_constraints.push((constraint, *expr_id)); - } - } - } - - if let ImplKind::TraitMethod(_, mut constraint, assumed) = ident.impl_kind { - constraint.apply_bindings(&bindings); - if assumed { - let trait_impl = TraitImplKind::Assumed { - object_type: constraint.typ, - trait_generics: constraint.trait_generics, - }; - self.interner.select_impl_for_expression(*expr_id, trait_impl); - } else { - // Currently only one impl can be selected per expr_id, so this - // constraint needs to be pushed after any other constraints so - // that monomorphization can resolve this trait method to the correct impl. - self.trait_constraints.push((constraint, *expr_id)); - } - } - - self.interner.store_instantiation_bindings(*expr_id, bindings); - typ - } + HirExpression::Ident(ident) => self.check_ident(ident, expr_id), HirExpression::Literal(literal) => { match literal { HirLiteral::Array(HirArrayLiteral::Standard(arr)) => { @@ -194,74 +151,42 @@ impl<'interner> TypeChecker<'interner> { self.bind_function_type(function, args, span) } HirExpression::MethodCall(mut method_call) => { - let object_type = self.check_expression(&method_call.object).follow_bindings(); + let mut object_type = self.check_expression(&method_call.object).follow_bindings(); let method_name = method_call.method.0.contents.as_str(); match self.lookup_method(&object_type, method_name, expr_id) { Some(method_ref) => { - let mut args = vec![( - object_type.clone(), - method_call.object, - self.interner.expr_span(&method_call.object), - )]; - - for arg in &method_call.arguments { - let typ = self.check_expression(arg); - args.push((typ, *arg, self.interner.expr_span(arg))); - } - // Desugar the method call into a normal, resolved function call // so that the backend doesn't need to worry about methods let location = method_call.location; - let trait_id = match &method_ref { - HirMethodReference::FuncId(func_id) => { - // Automatically add `&mut` if the method expects a mutable reference and - // the object is not already one. - if *func_id != FuncId::dummy_id() { - let function_type = - self.interner.function_meta(func_id).typ.clone(); - self.try_add_mutable_reference_to_object( - &mut method_call, - &function_type, - &mut args, - ); - } - - let meta = self.interner.function_meta(func_id); - meta.trait_impl.map(|impl_id| { - self.interner - .get_trait_implementation(impl_id) - .borrow() - .trait_id - }) + // Automatically add `&mut` if the method expects a mutable reference and + // the object is not already one. + if let HirMethodReference::FuncId(func_id) = &method_ref { + if *func_id != FuncId::dummy_id() { + let function_type = + self.interner.function_meta(func_id).typ.clone(); + + self.try_add_mutable_reference_to_object( + &mut method_call, + &function_type, + &mut object_type, + ); } - HirMethodReference::TraitMethodId(method, _) => Some(method.trait_id), - }; + } - let (function_id, function_call) = method_call.into_function_call( + // TODO: update object_type here? + let function_call = method_call.into_function_call( &method_ref, - object_type.clone(), + object_type, location, self.interner, ); - let span = self.interner.expr_span(expr_id); - let ret = self.check_method_call(&function_id, method_ref, args, span); - - if let Some(trait_id) = trait_id { - // Assume no trait generics were specified - // TODO: Fill in type variables - self.verify_trait_constraint( - &object_type, - trait_id, - &[], - function_id, - span, - ); - } - self.interner.replace_expr(expr_id, function_call); - ret + + // Type check the new call now that it has been changed from a method call + // to a function call. This way we avoid duplicating code. + self.check_expression(expr_id) } None => Type::Error, } @@ -341,6 +266,67 @@ impl<'interner> TypeChecker<'interner> { typ } + /// Returns the type of the given identifier + fn check_ident(&mut self, ident: HirIdent, expr_id: &ExprId) -> Type { + let mut bindings = TypeBindings::new(); + + // Add type bindings from any constraints that were used. + // We need to do this first since otherwise instantiating the type below + // will replace each trait generic with a fresh type variable, rather than + // the type used in the trait constraint (if it exists). See #4088. + if let ImplKind::TraitMethod(_, constraint, _) = &ident.impl_kind { + let the_trait = self.interner.get_trait(constraint.trait_id); + assert_eq!(the_trait.generics.len(), constraint.trait_generics.len()); + + for (param, arg) in the_trait.generics.iter().zip(&constraint.trait_generics) { + bindings.insert(param.id(), (param.clone(), arg.clone())); + } + } + + // An identifiers type may be forall-quantified in the case of generic functions. + // E.g. `fn foo(t: T, field: Field) -> T` has type `forall T. fn(T, Field) -> T`. + // We must instantiate identifiers at every call site to replace this T with a new type + // variable to handle generic functions. + let t = self.interner.id_type_substitute_trait_as_type(ident.id); + + // This instantiates a trait's generics as well which need to be set + // when the constraint below is later solved for when the function is + // finished. How to link the two? + let (typ, bindings) = t.instantiate_with_bindings(bindings, self.interner); + + // Push any trait constraints required by this definition to the context + // to be checked later when the type of this variable is further constrained. + if let Some(definition) = self.interner.try_definition(ident.id) { + if let DefinitionKind::Function(function) = definition.kind { + let function = self.interner.function_meta(&function); + + for mut constraint in function.trait_constraints.clone() { + constraint.apply_bindings(&bindings); + self.trait_constraints.push((constraint, *expr_id)); + } + } + } + + if let ImplKind::TraitMethod(_, mut constraint, assumed) = ident.impl_kind { + constraint.apply_bindings(&bindings); + if assumed { + let trait_impl = TraitImplKind::Assumed { + object_type: constraint.typ, + trait_generics: constraint.trait_generics, + }; + self.interner.select_impl_for_expression(*expr_id, trait_impl); + } else { + // Currently only one impl can be selected per expr_id, so this + // constraint needs to be pushed after any other constraints so + // that monomorphization can resolve this trait method to the correct impl. + self.trait_constraints.push((constraint, *expr_id)); + } + } + + self.interner.store_instantiation_bindings(*expr_id, bindings); + typ + } + pub fn verify_trait_constraint( &mut self, object_type: &Type, @@ -390,7 +376,7 @@ impl<'interner> TypeChecker<'interner> { &mut self, method_call: &mut HirMethodCallExpression, function_type: &Type, - argument_types: &mut [(Type, ExprId, noirc_errors::Span)], + object_type: &mut Type, ) { let expected_object_type = match function_type { Type::Function(args, _, _) => args.first(), @@ -402,7 +388,7 @@ impl<'interner> TypeChecker<'interner> { }; if let Some(expected_object_type) = expected_object_type { - let actual_type = argument_types[0].0.follow_bindings(); + let actual_type = object_type.follow_bindings(); if matches!(expected_object_type.follow_bindings(), Type::MutableReference(_)) { if !matches!(actual_type, Type::MutableReference(_)) { @@ -412,34 +398,33 @@ impl<'interner> TypeChecker<'interner> { } let new_type = Type::MutableReference(Box::new(actual_type)); - argument_types[0].0 = new_type.clone(); + *object_type = new_type.clone(); // First try to remove a dereference operator that may have been implicitly // inserted by a field access expression `foo.bar` on a mutable reference `foo`. - if self.try_remove_implicit_dereference(method_call.object).is_none() { - // If that didn't work, then wrap the whole expression in an `&mut` + let new_object = self.try_remove_implicit_dereference(method_call.object); + + // If that didn't work, then wrap the whole expression in an `&mut` + method_call.object = new_object.unwrap_or_else(|| { let location = self.interner.id_location(method_call.object); - method_call.object = + let new_object = self.interner.push_expr(HirExpression::Prefix(HirPrefixExpression { operator: UnaryOp::MutableReference, rhs: method_call.object, })); - self.interner.push_expr_type(&method_call.object, new_type); - self.interner.push_expr_location( - method_call.object, - location.span, - location.file, - ); - } + self.interner.push_expr_type(&new_object, new_type); + self.interner.push_expr_location(new_object, location.span, location.file); + new_object + }); } // Otherwise if the object type is a mutable reference and the method is not, insert as // many dereferences as needed. } else if matches!(actual_type, Type::MutableReference(_)) { let (object, new_type) = self.insert_auto_dereferences(method_call.object, actual_type); + *object_type = new_type; method_call.object = object; - argument_types[0].0 = new_type; } } } @@ -467,29 +452,22 @@ impl<'interner> TypeChecker<'interner> { /// Given a method object: `(*foo).bar` of a method call `(*foo).bar.baz()`, remove the /// implicitly added dereference operator if one is found. /// - /// Returns Some(()) if a dereference was removed and None otherwise. - fn try_remove_implicit_dereference(&mut self, object: ExprId) -> Option<()> { + /// Returns Some(new_expr_id) if a dereference was removed and None otherwise. + fn try_remove_implicit_dereference(&mut self, object: ExprId) -> Option { match self.interner.expression(&object) { - HirExpression::MemberAccess(access) => { - self.try_remove_implicit_dereference(access.lhs)?; - - // Since we removed a dereference, instead of returning the field directly, - // we expect to be returning a reference to the field, so update the type accordingly. - let current_type = self.interner.id_type(object); - let reference_type = Type::MutableReference(Box::new(current_type)); - self.interner.push_expr_type(&object, reference_type); - Some(()) + HirExpression::MemberAccess(mut access) => { + let new_lhs = self.try_remove_implicit_dereference(access.lhs)?; + access.lhs = new_lhs; + access.is_offset = true; + + // `object` will have a different type now, which will be filled in + // later when type checking the method call as a function call. + self.interner.replace_expr(&object, HirExpression::MemberAccess(access)); + Some(object) } HirExpression::Prefix(prefix) => match prefix.operator { - UnaryOp::Dereference { implicitly_added: true } => { - // Found a dereference we can remove. Now just replace it with its rhs to remove it. - let rhs = self.interner.expression(&prefix.rhs); - self.interner.replace_expr(&object, rhs); - - let rhs_type = self.interner.id_type(prefix.rhs); - self.interner.push_expr_type(&object, rhs_type); - Some(()) - } + // Found a dereference we can remove. Now just replace it with its rhs to remove it. + UnaryOp::Dereference { implicitly_added: true } => Some(prefix.rhs), _ => None, }, _ => None, @@ -566,60 +544,6 @@ impl<'interner> TypeChecker<'interner> { } } - // We need a special function to type check method calls since the method - // is not a Expression::Ident it must be manually instantiated here - fn check_method_call( - &mut self, - function_ident_id: &ExprId, - method_ref: HirMethodReference, - arguments: Vec<(Type, ExprId, Span)>, - span: Span, - ) -> Type { - let (fn_typ, param_len, generic_bindings) = match method_ref { - HirMethodReference::FuncId(func_id) => { - if func_id == FuncId::dummy_id() { - return Type::Error; - } - - let func_meta = self.interner.function_meta(&func_id); - let param_len = func_meta.parameters.len(); - (func_meta.typ.clone(), param_len, TypeBindings::new()) - } - HirMethodReference::TraitMethodId(method, generics) => { - let the_trait = self.interner.get_trait(method.trait_id); - let method = &the_trait.methods[method.method_index]; - - // These are any bindings from the trait's generics itself, - // rather than an impl or method's generics. - let generic_bindings = the_trait - .generics - .iter() - .zip(generics) - .map(|(var, arg)| (var.id(), (var.clone(), arg))) - .collect(); - - (method.typ.clone(), method.arguments().len(), generic_bindings) - } - }; - - let arg_len = arguments.len(); - - if param_len != arg_len { - self.errors.push(TypeCheckError::ArityMisMatch { - expected: param_len as u16, - found: arg_len as u16, - span, - }); - } - - let (function_type, instantiation_bindings) = - fn_typ.instantiate_with_bindings(generic_bindings, self.interner); - - self.interner.store_instantiation_bindings(*function_ident_id, instantiation_bindings); - self.interner.push_expr_type(function_ident_id, function_type.clone()); - self.bind_function_type(function_type.clone(), arguments, span) - } - fn check_if_expr(&mut self, if_expr: &expr::HirIfExpression, expr_id: &ExprId) -> Type { let cond_type = self.check_expression(&if_expr.condition); let then_type = self.check_expression(&if_expr.consequence); @@ -718,6 +642,9 @@ impl<'interner> TypeChecker<'interner> { this.interner.push_expr_location(*access_lhs, span, old_location.file); }; + // If this access is just a field offset, we want to avoid dereferencing + let dereference_lhs = (!access.is_offset).then_some(dereference_lhs); + match self.check_field_access(&lhs_type, &access.rhs.0.contents, span, dereference_lhs) { Some((element_type, index)) => { self.interner.set_field_index(expr_id, index); @@ -742,12 +669,16 @@ impl<'interner> TypeChecker<'interner> { /// expression. The second parameter of this function represents the lhs_type (which should /// always be a Type::MutableReference if `dereference_lhs` is called) and the third /// represents the element type. + /// + /// If `dereference_lhs` is None, this will assume we're taking the offset of a struct field + /// rather than dereferencing it. So the result of `foo.bar` with a `foo : &mut Foo` will + /// be a `&mut Bar` rather than just a `Bar`. pub(super) fn check_field_access( &mut self, lhs_type: &Type, field_name: &str, span: Span, - mut dereference_lhs: impl FnMut(&mut Self, Type, Type), + dereference_lhs: Option, ) -> Option<(Type, usize)> { let lhs_type = lhs_type.follow_bindings(); @@ -777,8 +708,19 @@ impl<'interner> TypeChecker<'interner> { // If the lhs is a mutable reference we automatically transform // lhs.field into (*lhs).field Type::MutableReference(element) => { - dereference_lhs(self, lhs_type.clone(), element.as_ref().clone()); - return self.check_field_access(element, field_name, span, dereference_lhs); + if let Some(mut dereference_lhs) = dereference_lhs { + dereference_lhs(self, lhs_type.clone(), element.as_ref().clone()); + return self.check_field_access( + element, + field_name, + span, + Some(dereference_lhs), + ); + } else { + let (element, index) = + self.check_field_access(element, field_name, span, dereference_lhs)?; + return Some((Type::MutableReference(Box::new(element)), index)); + } } _ => (), } @@ -1010,9 +952,9 @@ impl<'interner> TypeChecker<'interner> { fn bind_function_type_impl( &mut self, - fn_params: &Vec, + fn_params: &[Type], fn_ret: &Type, - callsite_args: &Vec<(Type, ExprId, Span)>, + callsite_args: &[(Type, ExprId, Span)], span: Span, ) -> Type { if fn_params.len() != callsite_args.len() { diff --git a/noir/compiler/noirc_frontend/src/hir/type_check/stmt.rs b/noir/compiler/noirc_frontend/src/hir/type_check/stmt.rs index c4e72f48c50..fd8ae62d34e 100644 --- a/noir/compiler/noirc_frontend/src/hir/type_check/stmt.rs +++ b/noir/compiler/noirc_frontend/src/hir/type_check/stmt.rs @@ -206,24 +206,22 @@ impl<'interner> TypeChecker<'interner> { let object_ref = &mut object; let mutable_ref = &mut mutable; + let dereference_lhs = move |_: &mut Self, _, element_type| { + // We must create a temporary value first to move out of object_ref before + // we eventually reassign to it. + let id = DefinitionId::dummy_id(); + let location = Location::new(span, fm::FileId::dummy()); + let ident = HirIdent::non_trait_method(id, location); + let tmp_value = HirLValue::Ident(ident, Type::Error); + + let lvalue = std::mem::replace(object_ref, Box::new(tmp_value)); + *object_ref = Box::new(HirLValue::Dereference { lvalue, element_type }); + *mutable_ref = true; + }; + + let name = &field_name.0.contents; let (object_type, field_index) = self - .check_field_access( - &lhs_type, - &field_name.0.contents, - span, - move |_, _, element_type| { - // We must create a temporary value first to move out of object_ref before - // we eventually reassign to it. - let id = DefinitionId::dummy_id(); - let location = Location::new(span, fm::FileId::dummy()); - let ident = HirIdent::non_trait_method(id, location); - let tmp_value = HirLValue::Ident(ident, Type::Error); - - let lvalue = std::mem::replace(object_ref, Box::new(tmp_value)); - *object_ref = Box::new(HirLValue::Dereference { lvalue, element_type }); - *mutable_ref = true; - }, - ) + .check_field_access(&lhs_type, name, span, Some(dereference_lhs)) .unwrap_or((Type::Error, 0)); let field_index = Some(field_index); @@ -325,6 +323,7 @@ impl<'interner> TypeChecker<'interner> { // Now check if LHS is the same type as the RHS // Importantly, we do not coerce any types implicitly let expr_span = self.interner.expr_span(&rhs_expr); + self.unify_with_coercions(&expr_type, &annotated_type, rhs_expr, || { TypeCheckError::TypeMismatch { expected_typ: annotated_type.to_string(), @@ -335,10 +334,8 @@ impl<'interner> TypeChecker<'interner> { if annotated_type.is_unsigned() { self.lint_overflowing_uint(&rhs_expr, &annotated_type); } - annotated_type - } else { - expr_type } + expr_type } /// Check if an assignment is overflowing with respect to `annotated_type` diff --git a/noir/compiler/noirc_frontend/src/hir_def/expr.rs b/noir/compiler/noirc_frontend/src/hir_def/expr.rs index fe1cd78b5ed..75ed68af0f6 100644 --- a/noir/compiler/noirc_frontend/src/hir_def/expr.rs +++ b/noir/compiler/noirc_frontend/src/hir_def/expr.rs @@ -152,6 +152,12 @@ pub struct HirMemberAccess { // This field is not an IdentId since the rhs of a field // access has no corresponding definition pub rhs: Ident, + + /// True if we should return an offset of the field rather than the field itself. + /// For most cases this is false, corresponding to `foo.bar` in source code. + /// This is true when calling methods or when we have an lvalue we want to preserve such + /// that if `foo : &mut Foo` has a field `bar : Bar`, we can return an `&mut Bar`. + pub is_offset: bool, } #[derive(Debug, Clone)] @@ -201,13 +207,14 @@ pub enum HirMethodReference { } impl HirMethodCallExpression { + /// Converts a method call into a function call pub fn into_function_call( mut self, method: &HirMethodReference, object_type: Type, location: Location, interner: &mut NodeInterner, - ) -> (ExprId, HirExpression) { + ) -> HirExpression { let mut arguments = vec![self.object]; arguments.append(&mut self.arguments); @@ -225,9 +232,10 @@ impl HirMethodCallExpression { (id, ImplKind::TraitMethod(*method_id, constraint, false)) } }; - let expr = HirExpression::Ident(HirIdent { location, id, impl_kind }); - let func = interner.push_expr(expr); - (func, HirExpression::Call(HirCallExpression { func, arguments, location })) + let func = HirExpression::Ident(HirIdent { location, id, impl_kind }); + let func = interner.push_expr(func); + interner.push_expr_location(func, location.span, location.file); + HirExpression::Call(HirCallExpression { func, arguments, location }) } } diff --git a/noir/compiler/noirc_frontend/src/hir_def/types.rs b/noir/compiler/noirc_frontend/src/hir_def/types.rs index 8979d60c005..0ba4cb2da65 100644 --- a/noir/compiler/noirc_frontend/src/hir_def/types.rs +++ b/noir/compiler/noirc_frontend/src/hir_def/types.rs @@ -17,7 +17,7 @@ use crate::{node_interner::StructId, Ident, Signedness}; use super::expr::{HirCallExpression, HirExpression, HirIdent}; -#[derive(Debug, PartialEq, Eq, Clone, Hash)] +#[derive(PartialEq, Eq, Clone, Hash)] pub enum Type { /// A primitive Field type FieldElement, @@ -64,7 +64,7 @@ pub enum Type { TypeVariable(TypeVariable, TypeVariableKind), /// `impl Trait` when used in a type position. - /// These are only matched based on the TraitId. The trait name paramer is only + /// These are only matched based on the TraitId. The trait name parameter is only /// used for displaying error messages using the name of the trait. TraitAsType(TraitId, /*name:*/ Rc, /*generics:*/ Vec), @@ -190,7 +190,7 @@ pub type TypeBindings = HashMap; /// Represents a struct type in the type system. Each instance of this /// rust struct will be shared across all Type::Struct variants that represent /// the same struct type. -#[derive(Debug, Eq)] +#[derive(Eq)] pub struct StructType { /// A unique id representing this struct type. Used to check if two /// struct types are equal. @@ -449,7 +449,7 @@ pub enum TypeVariableKind { /// A TypeVariable is a mutable reference that is either /// bound to some type, or unbound with a given TypeVariableId. -#[derive(Debug, PartialEq, Eq, Clone, Hash)] +#[derive(PartialEq, Eq, Clone, Hash)] pub struct TypeVariable(TypeVariableId, Shared); impl TypeVariable { @@ -516,7 +516,7 @@ impl TypeVariable { /// TypeBindings are the mutable insides of a TypeVariable. /// They are either bound to some type, or are unbound. -#[derive(Debug, Clone, PartialEq, Eq, Hash)] +#[derive(Clone, PartialEq, Eq, Hash)] pub enum TypeBinding { Bound(Type), Unbound(TypeVariableId), @@ -529,7 +529,7 @@ impl TypeBinding { } /// A unique ID used to differentiate different type variables -#[derive(Debug, Copy, Clone, PartialEq, Eq, Hash)] +#[derive(Copy, Clone, PartialEq, Eq, Hash)] pub struct TypeVariableId(pub usize); impl Type { @@ -804,7 +804,7 @@ impl std::fmt::Display for Type { Type::Function(args, ret, env) => { let closure_env_text = match **env { Type::Unit => "".to_string(), - _ => format!(" with closure environment {env}"), + _ => format!(" with env {env}"), }; let args = vecmap(args.iter(), ToString::to_string); @@ -1661,3 +1661,101 @@ impl From<&Type> for PrintableType { } } } + +impl std::fmt::Debug for Type { + fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { + match self { + Type::FieldElement => { + write!(f, "Field") + } + Type::Array(len, typ) => { + if matches!(len.follow_bindings(), Type::NotConstant) { + write!(f, "[{typ:?}]") + } else { + write!(f, "[{typ:?}; {len:?}]") + } + } + Type::Integer(sign, num_bits) => match sign { + Signedness::Signed => write!(f, "i{num_bits}"), + Signedness::Unsigned => write!(f, "u{num_bits}"), + }, + Type::TypeVariable(var, TypeVariableKind::Normal) => write!(f, "{:?}", var), + Type::TypeVariable(binding, TypeVariableKind::IntegerOrField) => { + write!(f, "IntOrField{:?}", binding) + } + Type::TypeVariable(binding, TypeVariableKind::Constant(n)) => { + write!(f, "{}{:?}", n, binding) + } + Type::Struct(s, args) => { + let args = vecmap(args, |arg| format!("{:?}", arg)); + if args.is_empty() { + write!(f, "{:?}", s.borrow()) + } else { + write!(f, "{:?}<{}>", s.borrow(), args.join(", ")) + } + } + Type::TraitAsType(_id, name, generics) => { + write!(f, "impl {}", name)?; + if !generics.is_empty() { + let generics = vecmap(generics, |arg| format!("{:?}", arg)).join(", "); + write!(f, "<{generics}>")?; + } + Ok(()) + } + Type::Tuple(elements) => { + let elements = vecmap(elements, |arg| format!("{:?}", arg)); + write!(f, "({})", elements.join(", ")) + } + Type::Bool => write!(f, "bool"), + Type::String(len) => write!(f, "str<{len:?}>"), + Type::FmtString(len, elements) => { + write!(f, "fmtstr<{len:?}, {elements:?}>") + } + Type::Unit => write!(f, "()"), + Type::Error => write!(f, "error"), + Type::NamedGeneric(binding, name) => write!(f, "{}{:?}", name, binding), + Type::Constant(x) => x.fmt(f), + Type::Forall(typevars, typ) => { + let typevars = vecmap(typevars, |var| format!("{:?}", var)); + write!(f, "forall {}. {:?}", typevars.join(" "), typ) + } + Type::Function(args, ret, env) => { + let closure_env_text = match **env { + Type::Unit => "".to_string(), + _ => format!(" with env {env:?}"), + }; + + let args = vecmap(args.iter(), |arg| format!("{:?}", arg)); + + write!(f, "fn({}) -> {ret:?}{closure_env_text}", args.join(", ")) + } + Type::MutableReference(element) => { + write!(f, "&mut {element:?}") + } + Type::NotConstant => write!(f, "NotConstant"), + } + } +} + +impl std::fmt::Debug for TypeVariableId { + fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { + write!(f, "'{}", self.0) + } +} + +impl std::fmt::Debug for TypeVariable { + fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { + write!(f, "{:?}", self.id())?; + + if let TypeBinding::Bound(typ) = &*self.borrow() { + write!(f, " -> {typ:?}")?; + } + Ok(()) + } +} + +impl std::fmt::Debug for StructType { + fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { + write!(f, "{}", self.name) + } +} diff --git a/noir/compiler/noirc_frontend/src/lexer/token.rs b/noir/compiler/noirc_frontend/src/lexer/token.rs index ab131ccd880..835a0baae3f 100644 --- a/noir/compiler/noirc_frontend/src/lexer/token.rs +++ b/noir/compiler/noirc_frontend/src/lexer/token.rs @@ -562,6 +562,10 @@ impl FunctionAttribute { matches!(self, FunctionAttribute::Foreign(_)) } + pub fn is_oracle(&self) -> bool { + matches!(self, FunctionAttribute::Oracle(_)) + } + pub fn is_low_level(&self) -> bool { matches!(self, FunctionAttribute::Foreign(_) | FunctionAttribute::Builtin(_)) } diff --git a/noir/compiler/noirc_frontend/src/lib.rs b/noir/compiler/noirc_frontend/src/lib.rs index 9582b80dcba..b6d4c568334 100644 --- a/noir/compiler/noirc_frontend/src/lib.rs +++ b/noir/compiler/noirc_frontend/src/lib.rs @@ -75,6 +75,10 @@ pub mod macros_api { ) -> Result; /// Function to manipulate the AST after type checking has been completed. /// The AST after type checking has been done is called the HIR. - fn process_typed_ast(&self, crate_id: &CrateId, context: &mut HirContext); + fn process_typed_ast( + &self, + crate_id: &CrateId, + context: &mut HirContext, + ) -> Result<(), (MacroError, FileId)>; } } diff --git a/noir/compiler/noirc_frontend/src/monomorphization/printer.rs b/noir/compiler/noirc_frontend/src/monomorphization/printer.rs index e79330de6f8..7aec2193494 100644 --- a/noir/compiler/noirc_frontend/src/monomorphization/printer.rs +++ b/noir/compiler/noirc_frontend/src/monomorphization/printer.rs @@ -30,7 +30,9 @@ impl AstPrinter { pub fn print_expr(&mut self, expr: &Expression, f: &mut Formatter) -> std::fmt::Result { match expr { - Expression::Ident(ident) => write!(f, "{}${}", ident.name, ident.definition), + Expression::Ident(ident) => { + write!(f, "{}${}", ident.name, ident.definition) + } Expression::Literal(literal) => self.print_literal(literal, f), Expression::Block(exprs) => self.print_block(exprs, f), Expression::Unary(unary) => self.print_unary(unary, f), diff --git a/noir/compiler/noirc_frontend/src/node_interner.rs b/noir/compiler/noirc_frontend/src/node_interner.rs index e734161e360..b856b54f6ca 100644 --- a/noir/compiler/noirc_frontend/src/node_interner.rs +++ b/noir/compiler/noirc_frontend/src/node_interner.rs @@ -146,6 +146,9 @@ pub struct NodeInterner { /// A list of all type aliases that are referenced in the program. /// Searched by LSP to resolve [Location]s of [TypeAliasType]s pub(crate) type_alias_ref: Vec<(TypeAliasId, Location)>, + + /// Stores the [Location] of a [Type] reference + pub(crate) type_ref_locations: Vec<(Type, Location)>, } /// A trait implementation is either a normal implementation that is present in the source @@ -455,6 +458,7 @@ impl Default for NodeInterner { struct_methods: HashMap::new(), primitive_methods: HashMap::new(), type_alias_ref: Vec::new(), + type_ref_locations: Vec::new(), }; // An empty block expression is used often, we add this into the `node` on startup @@ -607,6 +611,11 @@ impl NodeInterner { self.id_to_type.insert(definition_id.into(), typ); } + /// Store [Location] of [Type] reference + pub fn push_type_ref_location(&mut self, typ: Type, location: Location) { + self.type_ref_locations.push((typ, location)); + } + pub fn push_global(&mut self, stmt_id: StmtId, ident: Ident, local_id: LocalModuleId) { self.globals.insert(stmt_id, GlobalInfo { ident, local_id }); } @@ -1186,7 +1195,6 @@ impl NodeInterner { } /// Adds a trait implementation to the list of known implementations. - #[tracing::instrument(skip(self))] pub fn add_trait_implementation( &mut self, object_type: Type, diff --git a/noir/compiler/noirc_frontend/src/parser/parser.rs b/noir/compiler/noirc_frontend/src/parser/parser.rs index cdfdc570949..f82ce95c718 100644 --- a/noir/compiler/noirc_frontend/src/parser/parser.rs +++ b/noir/compiler/noirc_frontend/src/parser/parser.rs @@ -61,12 +61,12 @@ pub fn parse_program(source_program: &str) -> (ParsedModule, Vec) { parsing_errors.extend(lexing_errors.into_iter().map(Into::into)); - (module.unwrap(), parsing_errors) + (module.unwrap_or(ParsedModule { items: vec![] }), parsing_errors) } /// program: module EOF fn program() -> impl NoirParser { - module().then_ignore(force(just(Token::EOF))) + module().then_ignore(just(Token::EOF)) } /// module: top_level_statement module @@ -74,7 +74,7 @@ fn program() -> impl NoirParser { fn module() -> impl NoirParser { recursive(|module_parser| { empty() - .map(|_| ParsedModule::default()) + .to(ParsedModule::default()) .then(spanned(top_level_statement(module_parser)).repeated()) .foldl(|mut program, (statement, span)| { let mut push_item = |kind| program.items.push(Item { kind, span }); @@ -164,7 +164,6 @@ fn contract(module_parser: impl NoirParser) -> impl NoirParser impl NoirParser { attributes() - .or_not() .then(function_modifiers()) .then_ignore(keyword(Keyword::Fn)) .then(ident()) @@ -224,6 +223,7 @@ fn function_modifiers() -> impl NoirParser<(bool, bool, bool, bool, bool)> { ) }) } + fn is_pub_crate() -> impl NoirParser { (keyword(Keyword::Pub) .then_ignore(just(Token::LeftParen)) @@ -260,18 +260,14 @@ fn struct_definition() -> impl NoirParser { [(LeftParen, RightParen), (LeftBracket, RightBracket)], |_| vec![], )) - .or(just(Semicolon).map(|_| Vec::new())); + .or(just(Semicolon).to(Vec::new())); - attributes() - .or_not() - .then_ignore(keyword(Struct)) - .then(ident()) - .then(generics()) - .then(fields) - .validate(|(((raw_attributes, name), generics), fields), span, emit| { + attributes().then_ignore(keyword(Struct)).then(ident()).then(generics()).then(fields).validate( + |(((raw_attributes, name), generics), fields), span, emit| { let attributes = validate_struct_attributes(raw_attributes, span, emit); TopLevelStatement::Struct(NoirStruct { name, attributes, generics, fields, span }) - }) + }, + ) } fn type_alias_definition() -> impl NoirParser { @@ -448,7 +444,7 @@ fn trait_constant_declaration() -> impl NoirParser { /// trait_function_declaration: 'fn' ident generics '(' declaration_parameters ')' function_return_type fn trait_function_declaration() -> impl NoirParser { let trait_function_body_or_semicolon = - block(fresh_statement()).map(Option::from).or(just(Token::Semicolon).map(|_| Option::None)); + block(fresh_statement()).map(Option::from).or(just(Token::Semicolon).to(Option::None)); keyword(Keyword::Fn) .ignore_then(ident()) @@ -463,20 +459,14 @@ fn trait_function_declaration() -> impl NoirParser { } fn validate_attributes( - attributes: Option>, + attributes: Vec, span: Span, emit: &mut dyn FnMut(ParserError), ) -> Attributes { - if attributes.is_none() { - return Attributes::empty(); - } - - let attrs = attributes.unwrap(); - let mut primary = None; let mut secondary = Vec::new(); - for attribute in attrs { + for attribute in attributes { match attribute { Attribute::Function(attr) => { if primary.is_some() { @@ -495,14 +485,13 @@ fn validate_attributes( } fn validate_struct_attributes( - attributes: Option>, + attributes: Vec, span: Span, emit: &mut dyn FnMut(ParserError), ) -> Vec { - let attrs = attributes.unwrap_or_default(); let mut struct_attributes = vec![]; - for attribute in attrs { + for attribute in attributes { match attribute { Attribute::Function(..) => { emit(ParserError::with_reason( @@ -976,7 +965,7 @@ fn assign_operator() -> impl NoirParser { // Since >> is lexed as two separate "greater-than"s, >>= is lexed as > >=, so // we need to account for that case here as well. let right_shift_fix = - just(Token::Greater).then(just(Token::GreaterEqual)).map(|_| Token::ShiftRight); + just(Token::Greater).then(just(Token::GreaterEqual)).to(Token::ShiftRight); let shorthand_syntax = shorthand_syntax.or(right_shift_fix); just(Token::Assign).or(shorthand_syntax) @@ -1337,7 +1326,7 @@ fn create_infix_expression(lhs: Expression, (operator, rhs): (BinaryOp, Expressi // to parse nested generic types. For normal expressions however, it means we have to manually // parse two greater-than tokens as a single right-shift here. fn right_shift_operator() -> impl NoirParser { - just(Token::Greater).then(just(Token::Greater)).map(|_| Token::ShiftRight) + just(Token::Greater).then(just(Token::Greater)).to(Token::ShiftRight) } fn operator_with_precedence(precedence: Precedence) -> impl NoirParser> { diff --git a/noir/compiler/noirc_frontend/src/resolve_locations.rs b/noir/compiler/noirc_frontend/src/resolve_locations.rs index 02325de4da8..cfb88966b9d 100644 --- a/noir/compiler/noirc_frontend/src/resolve_locations.rs +++ b/noir/compiler/noirc_frontend/src/resolve_locations.rs @@ -42,6 +42,7 @@ impl NodeInterner { .and_then(|index| self.resolve_location(index, return_type_location_instead)) .or_else(|| self.try_resolve_trait_impl_location(location)) .or_else(|| self.try_resolve_trait_method_declaration(location)) + .or_else(|| self.try_resolve_type_ref(location)) .or_else(|| self.try_resolve_type_alias(location)) } @@ -196,7 +197,17 @@ impl NodeInterner { }) } - #[tracing::instrument(skip(self), ret)] + /// Attempts to resolve [Location] of [Type] based on [Location] of reference in code + pub(crate) fn try_resolve_type_ref(&self, location: Location) -> Option { + self.type_ref_locations + .iter() + .find(|(_typ, type_ref_location)| type_ref_location.contains(&location)) + .and_then(|(typ, _)| match typ { + Type::Struct(struct_typ, _) => Some(struct_typ.borrow().location), + _ => None, + }) + } + fn try_resolve_type_alias(&self, location: Location) -> Option { self.type_alias_ref .iter() diff --git a/noir/compiler/noirc_frontend/src/tests.rs b/noir/compiler/noirc_frontend/src/tests.rs index 9ccbddab9ec..a4246a9fe7d 100644 --- a/noir/compiler/noirc_frontend/src/tests.rs +++ b/noir/compiler/noirc_frontend/src/tests.rs @@ -52,10 +52,12 @@ mod test { ) -> (ParsedModule, Context, Vec<(CompilationError, FileId)>) { let root = std::path::Path::new("/"); let fm = FileManager::new(root); + let mut context = Context::new(fm, Default::default()); context.def_interner.populate_dummy_operator_traits(); let root_file_id = FileId::dummy(); let root_crate_id = context.crate_graph.add_crate_root(root_file_id); + let (program, parser_errors) = parse_program(src); let mut errors = vecmap(parser_errors, |e| (e.into(), root_file_id)); remove_experimental_warnings(&mut errors); diff --git a/noir/compiler/wasm/src/compile.rs b/noir/compiler/wasm/src/compile.rs index 498ffe447ce..b39a27a7931 100644 --- a/noir/compiler/wasm/src/compile.rs +++ b/noir/compiler/wasm/src/compile.rs @@ -190,7 +190,8 @@ pub fn compile( })? .0; - let optimized_contract = nargo::ops::optimize_contract(compiled_contract, expression_width); + let optimized_contract = + nargo::ops::transform_contract(compiled_contract, expression_width); let compile_output = generate_contract_artifact(optimized_contract); Ok(JsCompileResult::new(compile_output)) @@ -205,7 +206,7 @@ pub fn compile( })? .0; - let optimized_program = nargo::ops::optimize_program(compiled_program, expression_width); + let optimized_program = nargo::ops::transform_program(compiled_program, expression_width); let compile_output = generate_program_artifact(optimized_program); Ok(JsCompileResult::new(compile_output)) diff --git a/noir/compiler/wasm/src/compile_new.rs b/noir/compiler/wasm/src/compile_new.rs index 6476f6d29bc..4616004ae2b 100644 --- a/noir/compiler/wasm/src/compile_new.rs +++ b/noir/compiler/wasm/src/compile_new.rs @@ -109,7 +109,7 @@ impl CompilerContext { })? .0; - let optimized_program = nargo::ops::optimize_program(compiled_program, np_language); + let optimized_program = nargo::ops::transform_program(compiled_program, np_language); let compile_output = generate_program_artifact(optimized_program); Ok(JsCompileResult::new(compile_output)) @@ -134,7 +134,7 @@ impl CompilerContext { })? .0; - let optimized_contract = nargo::ops::optimize_contract(compiled_contract, np_language); + let optimized_contract = nargo::ops::transform_contract(compiled_contract, np_language); let compile_output = generate_contract_artifact(optimized_contract); Ok(JsCompileResult::new(compile_output)) diff --git a/noir/cspell.json b/noir/cspell.json index 0547b956d72..12b1e3f63d3 100644 --- a/noir/cspell.json +++ b/noir/cspell.json @@ -20,6 +20,8 @@ "bindgen", "bitand", "blackbox", + "boilerplate", + "boilerplates", "bridgekeeper", "brillig", "bytecount", @@ -56,6 +58,7 @@ "defunctionalized", "deque", "desugared", + "devcontainer", "direnv", "eddsa", "Elligator", @@ -96,6 +99,7 @@ "keccak", "keccakf", "krate", + "losslessly", "lvalue", "Maddiaa", "mathbb", @@ -121,6 +125,7 @@ "noirup", "nomicfoundation", "noncanonical", + "nouner", "pedersen", "peekable", "plonkc", @@ -172,9 +177,7 @@ "wasi", "wasmer", "Weierstraß", - "zshell", - "nouner", - "devcontainer" + "zshell" ], "ignorePaths": [ "./**/node_modules/**", diff --git a/noir/docs/.gitignore b/noir/docs/.gitignore index 4f6eee8284e..501e7e465ea 100644 --- a/noir/docs/.gitignore +++ b/noir/docs/.gitignore @@ -3,6 +3,8 @@ # Production /build +processed-docs +processed-docs-cache # Generated files .docusaurus diff --git a/noir/docs/README.md b/noir/docs/README.md index 92f6c5f41f7..c1d2bbd6d4e 100644 --- a/noir/docs/README.md +++ b/noir/docs/README.md @@ -2,7 +2,7 @@ This is the source code for the Noir documentation site at [noir-lang.org](https://noir-lang.org). -This website is built using [Docusaurus 2](https://docusaurus.io/), a modern static website +This website is built using [Docusaurus 3](https://docusaurus.io/), a modern static website generator. ## Contributing @@ -15,14 +15,20 @@ Check out the contributing guide [here](../CONTRIBUTING.md). ### Installation +This project requires recent versions of rust and cargo to be installed. +Any build errors should indicate dependencies that need installing, and at what version. + +On the root folder of the repository, run: + ``` yarn +yarn build ``` ### Local Development ``` -yarn start +yarn workspace docs start ``` This command starts a local development server and opens up a browser window. Most changes are @@ -31,8 +37,12 @@ reflected live without having to restart the server. ### Build ``` -yarn build +yarn workspace docs build ``` This command generates static content into the `build` directory and can be served using any static -contents hosting service. +contents hosting service. You can see a preview by running: + +``` +yarn workspace docs serve +``` diff --git a/noir/docs/docs/explainers/explainer-recursion.md b/noir/docs/docs/explainers/explainer-recursion.md index 8f992ec29fd..18846176ca7 100644 --- a/noir/docs/docs/explainers/explainer-recursion.md +++ b/noir/docs/docs/explainers/explainer-recursion.md @@ -16,12 +16,13 @@ keywords: "Optimizing Computational Resources", "Improving Efficiency", "Verification Key", - "Aggregation Objects", + "Aggregation", "Recursive zkSNARK schemes", "PLONK", "Proving and Verification Keys" ] sidebar_position: 1 +pagination_next: how_to/how-to-recursion --- In programming, we tend to think of recursion as something calling itself. A classic example would be the calculation of the factorial of a number: @@ -64,7 +65,7 @@ So, they use zero-knowledge proofs. Alice tries to guess Bob's number, and Bob w This ZK proof can go on a smart contract, revealing the winner and even giving prizes. However, this means every turn needs to be verified on-chain. This incurs some cost and waiting time that may simply make the game too expensive or time-consuming to be worth it. -As a solution, Alice proposes the following: "what if Bob generates his proof, and instead of sending it on-chain, I verify it *within* my own proof before playing my own turn?". +As a solution, Alice proposes the following: "what if Bob generates his proof, and instead of sending it on-chain, I verify it *within* my own proof before playing my own turn?". She can then generate a proof that she verified his proof, and so on. @@ -116,25 +117,19 @@ As you can see in the [recursion reference](noir/standard_library/recursion.md), - The Verification Key of the circuit that generated the proof - A hash of this verification key, as it's needed for some backends - The public inputs for the proof -- The input aggregation object -It also returns the `output aggregation object`. These aggregation objects can be confusing at times, so let's dive in a little bit. - -### Aggregation objects +:::info Recursive zkSNARK schemes do not necessarily "verify a proof" in the sense that you expect a true or false to be spit out by the verifier. Rather an aggregation object is built over the public inputs. -In the case of PLONK the recursive aggregation object is two G1 points (expressed as 16 witness values). The final verifier (in our case this is most often the smart contract verifier) has to be aware of this aggregation object to execute a pairing and check the validity of these points. - So, taking the example of Alice and Bob and their guessing game: - Alice makes her guess. Her proof is *not* recursive: it doesn't verify any proof within it! It's just a standard `assert(x != y)` circuit -- Bob verifies Alice's proof and makes his own guess. In this circuit, he is verifying a proof, so it needs to output an `aggregation object`: he is generating a recursive proof! -- Alice verifies Bob's *recursive proof*, and uses Bob's `output aggregation object` as the `input aggregation object` in her proof... Which in turn, generates another `output aggregation object`. +- Bob verifies Alice's proof and makes his own guess. In this circuit, he doesn't exactly *prove* the verification of Alice's proof. Instead, he *aggregates* his proof to Alice's proof. The actual verification is done when the full proof is verified, for example when using `nargo verify` or through the verifier smart contract. -One should notice that when Bob generates his first proof, he has no input aggregation object. Because he is not verifying an recursive proof, he has no `input aggregation object`. In this case, he may use zeros instead. +We can imagine recursive proofs a [relay race](https://en.wikipedia.org/wiki/Relay_race). The first runner doesn't have to receive the baton from anyone else, as he/she already starts with it. But when his/her turn is over, the next runner needs to receive it, run a bit more, and pass it along. Even though every runner could theoretically verify the baton mid-run (why not? 🏃🔍), only at the end of the race does the referee verify that the whole race is valid. -We can imagine the `aggregation object` as the baton in a [relay race](https://en.wikipedia.org/wiki/Relay_race). The first runner doesn't have to receive the baton from anyone else, as he/she already starts with it. But when his/her turn is over, the next runner needs to receive it, run a bit more, and pass it along. Even though every runner could theoretically verify the baton mid-run (why not? 🏃🔍), only at the end of the race does the referee verify that the whole race is valid. +::: ## Some architecture @@ -175,3 +170,7 @@ In this example, a regulator could verify that taxes were paid for a specific pu At the time of writing, verifying recursive proofs is surprisingly fast. This is because most of the time is spent on generating the verification key that will be used to generate the next proof. So you are able to cache the verification key and reuse it later. Currently, Noir JS packages don't expose the functionality of loading proving and verification keys, but that feature exists in the underlying `bb.js` package. + +## How can I try it + +Learn more about using recursion in Nargo and NoirJS in the [how-to guide](../how_to/how-to-recursion.md) and see a full example in [noir-examples](https://github.com/noir-lang/noir-examples). diff --git a/noir/docs/docs/getting_started/hello_noir/_category_.json b/noir/docs/docs/getting_started/hello_noir/_category_.json new file mode 100644 index 00000000000..23b560f610b --- /dev/null +++ b/noir/docs/docs/getting_started/hello_noir/_category_.json @@ -0,0 +1,5 @@ +{ + "position": 1, + "collapsible": true, + "collapsed": true +} diff --git a/noir/docs/docs/getting_started/create_a_project.md b/noir/docs/docs/getting_started/hello_noir/index.md similarity index 92% rename from noir/docs/docs/getting_started/create_a_project.md rename to noir/docs/docs/getting_started/hello_noir/index.md index 26ff265c389..743c4d8d634 100644 --- a/noir/docs/docs/getting_started/create_a_project.md +++ b/noir/docs/docs/getting_started/hello_noir/index.md @@ -1,5 +1,5 @@ --- -title: Creating A Project +title: Creating a Project description: Learn how to create and verify your first Noir program using Nargo, a programming language for zero-knowledge proofs. @@ -48,7 +48,7 @@ nargo new hello_world > `test`). A `hello_world` folder would be created. Similar to Rust, the folder houses _src/main.nr_ and -_Nargo.toml_ that contains the source code and environmental options of your Noir program +_Nargo.toml_ which contain the source code and environmental options of your Noir program respectively. ### Intro to Noir Syntax @@ -69,7 +69,7 @@ x : Field, y : pub Field Program inputs in Noir are private by default (e.g. `x`), but can be labeled public using the keyword `pub` (e.g. `y`). To learn more about private and public values, check the -[Data Types](../noir/concepts/data_types/index.md) section. +[Data Types](../../noir/concepts/data_types/index.md) section. The next line of the program specifies its body: @@ -79,7 +79,7 @@ assert(x != y); The Noir syntax `assert` can be interpreted as something similar to constraints in other zk-contract languages. -For more Noir syntax, check the [Language Concepts](../noir/concepts/comments.md) chapter. +For more Noir syntax, check the [Language Concepts](../../noir/concepts/comments.md) chapter. ## Build In/Output Files @@ -96,7 +96,7 @@ _Prover.toml_ houses input values, and _Verifier.toml_ houses public values. ## Prove Our Noir Program -Now that the project is set up, we can create a proof of correct execution on our Noir program. +Now that the project is set up, we can create a proof of correct execution of our Noir program. Fill in input values for execution in the _Prover.toml_ file. For example: diff --git a/noir/docs/docs/getting_started/project_breakdown.md b/noir/docs/docs/getting_started/hello_noir/project_breakdown.md similarity index 86% rename from noir/docs/docs/getting_started/project_breakdown.md rename to noir/docs/docs/getting_started/hello_noir/project_breakdown.md index c4e2a9ae003..6160a102c6c 100644 --- a/noir/docs/docs/getting_started/project_breakdown.md +++ b/noir/docs/docs/getting_started/hello_noir/project_breakdown.md @@ -8,8 +8,8 @@ keywords: sidebar_position: 2 --- -This section breaks down our hello world program in section _1.2_. We elaborate on the project -structure and what the `prove` and `verify` commands did in the previous section. +This section breaks down our hello world program from the previous section. We elaborate on the project +structure and what the `prove` and `verify` commands did. ## Anatomy of a Nargo Project @@ -52,7 +52,7 @@ license = "MIT" ecrecover = {tag = "v0.9.0", git = "https://github.com/colinnielsen/ecrecover-noir.git"} ``` -Nargo.toml for a [workspace](../noir/modules_packages_crates/workspaces.md) will look a bit different. For example: +Nargo.toml for a [workspace](../../noir/modules_packages_crates/workspaces.md) will look a bit different. For example: ```toml [workspace] @@ -62,7 +62,7 @@ default-member = "crates/a" #### Package section -The package section requires a number of fields including: +The package section defines a number of fields including: - `name` (**required**) - the name of the package - `type` (**required**) - can be "bin", "lib", or "contract" to specify whether its a binary, library or Aztec contract @@ -75,7 +75,7 @@ The package section requires a number of fields including: #### Dependencies section -This is where you will specify any dependencies for your project. See the [Dependencies page](../noir/modules_packages_crates/dependencies.md) for more info. +This is where you will specify any dependencies for your project. See the [Dependencies page](../../noir/modules_packages_crates/dependencies.md) for more info. `./proofs/` and `./contract/` directories will not be immediately visible until you create a proof or verifier contract respectively. @@ -98,7 +98,7 @@ verifying the proof. The prover supplies the values for `x` and `y` in the _Prover.toml_ file. -As for the program body, `assert` ensures the satisfaction of the condition (e.g. `x != y`) is +As for the program body, `assert` ensures that the condition to be satisfied (e.g. `x != y`) is constrained by the proof of the execution of said program (i.e. if the condition was not met, the verifier would reject the proof as an invalid proof). @@ -116,8 +116,8 @@ y = "2" When the command `nargo prove` is executed, two processes happen: -1. Noir creates a proof that `x` which holds the value of `1` and `y` which holds the value of `2` - is not equal. This not equal constraint is due to the line `assert(x != y)`. +1. Noir creates a proof that `x`, which holds the value of `1`, and `y`, which holds the value of `2`, + is not equal. This inequality constraint is due to the line `assert(x != y)`. 2. Noir creates and stores the proof of this statement in the _proofs_ directory in a file called your-project.proof. So if your project is named "private_voting" (defined in the project Nargo.toml), the proof will be saved at `./proofs/private_voting.proof`. Opening this file will display the proof in hex format. @@ -183,12 +183,12 @@ When the command `nargo verify` is executed, two processes happen: In production, the prover and the verifier are usually two separate entities. A prover would retrieve the necessary inputs, execute the Noir program, generate a proof and pass it to the -verifier. The verifier would then retrieve the public inputs from usually external sources and -verifies the validity of the proof against it. +verifier. The verifier would then retrieve the public inputs, usually from external sources, and +verify the validity of the proof against it. Take a private asset transfer as an example: -A user on browser as the prover would retrieve private inputs (e.g. the user's private key) and +A person using a browser as the prover would retrieve private inputs locally (e.g. the user's private key) and public inputs (e.g. the user's encrypted balance on-chain), compute the transfer, generate a proof and submit it to the verifier smart contract. diff --git a/noir/docs/docs/getting_started/installation/index.md b/noir/docs/docs/getting_started/installation/index.md index 27eeeca88ed..4ef86aa5914 100644 --- a/noir/docs/docs/getting_started/installation/index.md +++ b/noir/docs/docs/getting_started/installation/index.md @@ -16,6 +16,7 @@ keywords: [ Branches Noirup Repository ] +pagination_next: getting_started/hello_noir/index --- `nargo` is the one-stop-shop for almost everything related with Noir. The name comes from our love for Rust and its package manager `cargo`. @@ -43,3 +44,5 @@ Done. That's it. You should have the latest version working. You can check with You can also install nightlies, specific versions or branches. Check out the [noirup repository](https://github.com/noir-lang/noirup) for more information. + +Now we're ready to start working on [our first Noir program!](../hello_noir/index.md) diff --git a/noir/docs/docs/getting_started/tooling/_category_.json b/noir/docs/docs/getting_started/tooling/_category_.json index dff520ebc41..55804c03a71 100644 --- a/noir/docs/docs/getting_started/tooling/_category_.json +++ b/noir/docs/docs/getting_started/tooling/_category_.json @@ -1,5 +1,5 @@ { - "position": 3, + "position": 2, "label": "Tooling", "collapsible": true, "collapsed": true diff --git a/noir/docs/docs/how_to/how-to-oracles.md b/noir/docs/docs/how_to/how-to-oracles.md index bdb2b1332ba..0d84d992320 100644 --- a/noir/docs/docs/how_to/how-to-oracles.md +++ b/noir/docs/docs/how_to/how-to-oracles.md @@ -18,7 +18,7 @@ This guide shows you how to use oracles in your Noir program. For the sake of cl - You understand the concept of a JSON-RPC server. Visit the [JSON-RPC website](https://www.jsonrpc.org/) if you need a refresher. - You are comfortable with server-side JavaScript (e.g. Node.js, managing packages, etc.). -For reference, you can find the snippets used in this tutorial on the [Aztec DevRel Repository](https://github.com/AztecProtocol/dev-rel/tree/main/how_to_oracles/code-snippets/how-to-oracles). +For reference, you can find the snippets used in this tutorial on the [Aztec DevRel Repository](https://github.com/AztecProtocol/dev-rel/tree/main/code-snippets/how-to-oracles). ## Rundown diff --git a/noir/docs/docs/how_to/how-to-recursion.md b/noir/docs/docs/how_to/how-to-recursion.md index f60aa3ff2d9..f34647a99d5 100644 --- a/noir/docs/docs/how_to/how-to-recursion.md +++ b/noir/docs/docs/how_to/how-to-recursion.md @@ -47,7 +47,7 @@ In a standard recursive app, you're also dealing with at least two circuits. For - `main`: a circuit of type `assert(x != y)` - `recursive`: a circuit that verifies `main` -For a full example on how recursive proofs work, please refer to the [noir-examples](https://github.com/noir/noir-examples) repository. We will *not* be using it as a reference for this guide. +For a full example on how recursive proofs work, please refer to the [noir-examples](https://github.com/noir-lang/noir-examples) repository. We will *not* be using it as a reference for this guide. ## Step 1: Setup @@ -108,11 +108,7 @@ This call takes the public inputs and the proof, but also the public inputs coun :::info -The `proofAsFields` has a constant size `[Field; 93]`. However, currently the backend doesn't remove the public inputs from the proof when converting it. - -This means that if your `main` circuit has two public inputs, then you should also modify the recursive circuit to accept a proof with the public inputs appended. This means that in our example, since `y` is a public input, our `proofAsFields` is of type `[Field; 94]`. - -Verification keys in Barretenberg are always of size 114. +The `proofAsFields` has a constant size `[Field; 93]` and verification keys in Barretenberg are always `[Field; 114]`. ::: @@ -136,7 +132,6 @@ const recursiveInputs = { proof: proofAsFields, // array of length 93 + size of public inputs publicInputs: [mainInput.y], // using the example above, where `y` is the only public input key_hash: vkHash, - input_aggregation_object: Array(16).fill(0) // this circuit is verifying a non-recursive proof, so there's no input aggregation object: just use zero } const { witness, returnValue } = noir.execute(recursiveInputs) // we're executing the recursive circuit now! @@ -144,7 +139,7 @@ const { proof, publicInputs } = backend.generateFinalProof(witness) const verified = backend.verifyFinalProof({ proof, publicInputs }) ``` -You can obviously chain this proof into another proof. In fact, if you're using recursive proofs, you're probably interested of using them this way! In that case, you should keep in mind the `returnValue`, as it will contain the `input_aggregation_object` for the next proof. +You can obviously chain this proof into another proof. In fact, if you're using recursive proofs, you're probably interested of using them this way! :::tip @@ -152,16 +147,16 @@ Managing circuits and "who does what" can be confusing. To make sure your naming ```js const circuits = { -main: mainJSON, -recursive: recursiveJSON + main: mainJSON, + recursive: recursiveJSON } const backends = { -main: new BarretenbergBackend(circuits.main), -recursive: new BarretenbergBackend(circuits.recursive) + main: new BarretenbergBackend(circuits.main), + recursive: new BarretenbergBackend(circuits.recursive) } const noir_programs = { -main: new Noir(circuits.main, backends.main), -recursive: new Noir(circuits.recursive, backends.recursive) + main: new Noir(circuits.main, backends.main), + recursive: new Noir(circuits.recursive, backends.recursive) } ``` diff --git a/noir/docs/docs/how_to/how-to-solidity-verifier.md b/noir/docs/docs/how_to/how-to-solidity-verifier.md new file mode 100644 index 00000000000..e3c7c1065da --- /dev/null +++ b/noir/docs/docs/how_to/how-to-solidity-verifier.md @@ -0,0 +1,231 @@ +--- +title: Generate a Solidity Verifier +description: + Learn how to run the verifier as a smart contract on the blockchain. Compile a Solidity verifier + contract for your Noir program and deploy it on any EVM blockchain acting as a verifier smart + contract. Read more to find out +keywords: + [ + solidity verifier, + smart contract, + blockchain, + compiler, + plonk_vk.sol, + EVM blockchain, + verifying Noir programs, + proving backend, + Barretenberg, + ] +sidebar_position: 0 +pagination_next: tutorials/noirjs_app +--- + +Noir has the ability to generate a verifier contract in Solidity, which can be deployed in many EVM-compatible blockchains such as Ethereum. + +This allows for a powerful feature set, as one can make use of the conciseness and the privacy provided by Noir in an immutable ledger. Applications can range from simple P2P guessing games, to complex private DeFi interactions. + +This guide shows you how to generate a Solidity Verifier and deploy it on the [Remix IDE](https://remix.ethereum.org/). It is assumed that: + +- You are comfortable with the Solidity programming language and understand how contracts are deployed on the Ethereum network +- You have Noir installed and you have a Noir program. If you don't, [get started](../getting_started/installation/index.md) with Nargo and the example Hello Noir circuit +- You are comfortable navigating RemixIDE. If you aren't or you need a refresher, you can find some video tutorials [here](https://www.youtube.com/channel/UCjTUPyFEr2xDGN6Cg8nKDaA) that could help you. + +## Rundown + +Generating a Solidity Verifier contract is actually a one-command process. However, compiling it and deploying it can have some caveats. Here's the rundown of this guide: + +1. How to generate a solidity smart contract +2. How to compile the smart contract in the RemixIDE +3. How to deploy it to a testnet + +## Step 1 - Generate a contract + +This is by far the most straight-forward step. Just run: + +```sh +nargo codegen-verifier +``` + +A new `contract` folder would then be generated in your project directory, containing the Solidity +file `plonk_vk.sol`. It can be deployed to any EVM blockchain acting as a verifier smart contract. + +:::info + +It is possible to generate verifier contracts of Noir programs for other smart contract platforms as long as the proving backend supplies an implementation. + +Barretenberg, the default proving backend for Nargo, supports generation of verifier contracts, for the time being these are only in Solidity. +::: + +## Step 2 - Compiling + +We will mostly skip the details of RemixIDE, as the UI can change from version to version. For now, we can just open +Remix and create a blank workspace. + +![Create Workspace](@site/static/img/how-tos/solidity_verifier_1.png) + +We will create a new file to contain the contract Nargo generated, and copy-paste its content. + +:::warning + +You'll likely see a warning advising you to not trust pasted code. While it is an important warning, it is irrelevant in the context of this guide and can be ignored. We will not be deploying anywhere near a mainnet. + +::: + +To compile our the verifier, we can navigate to the compilation tab: + +![Compilation Tab](@site/static/img/how-tos/solidity_verifier_2.png) + +Remix should automatically match a suitable compiler version. However, hitting the "Compile" button will most likely generate a "Stack too deep" error: + +![Stack too deep](@site/static/img/how-tos/solidity_verifier_3.png) + +This is due to the verify function needing to put many variables on the stack, but enabling the optimizer resolves the issue. To do this, let's open the "Advanced Configurations" tab and enable optimization. The default 200 runs will suffice. + +:::info + +This time we will see a warning about an unused function parameter. This is expected, as the `verify` function doesn't use the `_proof` parameter inside a solidity block, it is loaded from calldata and used in assembly. + +::: + +![Compilation success](@site/static/img/how-tos/solidity_verifier_4.png) + +## Step 3 - Deploying + +At this point we should have a compiled contract read to deploy. If we navigate to the deploy section in Remix, we will see many different environments we can deploy to. The steps to deploy on each environment would be out-of-scope for this guide, so we will just use the default Remix VM. + +Looking closely, we will notice that our "Solidity Verifier" is actually three contracts working together: + +- An `UltraVerificationKey` library which simply stores the verification key for our circuit. +- An abstract contract `BaseUltraVerifier` containing most of the verifying logic. +- A main `UltraVerifier` contract that inherits from the Base and uses the Key contract. + +Remix will take care of the dependencies for us so we can simply deploy the UltraVerifier contract by selecting it and hitting "deploy": + +![Deploying UltraVerifier](@site/static/img/how-tos/solidity_verifier_5.png) + +A contract will show up in the "Deployed Contracts" section, where we can retrieve the Verification Key Hash. This is particularly useful for double-checking the deployer contract is the correct one. + +:::note + +Why "UltraVerifier"? + +To be precise, the Noir compiler (`nargo`) doesn't generate the verifier contract directly. It compiles the Noir code into an intermediate language (ACIR), which is then executed by the backend. So it is the backend that returns the verifier smart contract, not Noir. + +In this case, the Barretenberg Backend uses the UltraPlonk proving system, hence the "UltraVerifier" name. + +::: + +## Step 4 - Verifying + +To verify a proof using the Solidity verifier contract, we call the `verify` function in this extended contract: + +```solidity +function verify(bytes calldata _proof, bytes32[] calldata _publicInputs) external view returns (bool) +``` + +When using the default example in the [Hello Noir](../getting_started/hello_noir/index.md) guide, the easiest way to confirm that the verifier contract is doing its job is by calling the `verify` function via remix with the required parameters. For `_proof`, run `nargo prove` and use the string in `proof/.proof` (adding the hex `0x` prefix). We can also copy the public input from `Verifier.toml`, as it will be properly formatted as 32-byte strings: + +``` +0x...... , [0x0000.....02] +``` + +A programmatic example of how the `verify` function is called can be seen in the example zk voting application [here](https://github.com/noir-lang/noir-examples/blob/33e598c257e2402ea3a6b68dd4c5ad492bce1b0a/foundry-voting/src/zkVote.sol#L35): + +```solidity +function castVote(bytes calldata proof, uint proposalId, uint vote, bytes32 nullifierHash) public returns (bool) { + // ... + bytes32[] memory publicInputs = new bytes32[](4); + publicInputs[0] = merkleRoot; + publicInputs[1] = bytes32(proposalId); + publicInputs[2] = bytes32(vote); + publicInputs[3] = nullifierHash; + require(verifier.verify(proof, publicInputs), "Invalid proof"); +``` + +:::info[Return Values] + +A circuit doesn't have the concept of a return value. Return values are just syntactic sugar in +Noir. + +Under the hood, the return value is passed as an input to the circuit and is checked at the end of +the circuit program. + +For example, if you have Noir program like this: + +```rust +fn main( + // Public inputs + pubkey_x: pub Field, + pubkey_y: pub Field, + // Private inputs + priv_key: Field, +) -> pub Field +``` + +the `verify` function will expect the public inputs array (second function parameter) to be of length 3, the two inputs and the return value. Like before, these values are populated in Verifier.toml after running `nargo prove`. + +Passing only two inputs will result in an error such as `PUBLIC_INPUT_COUNT_INVALID(3, 2)`. + +In this case, the inputs parameter to `verify` would be an array ordered as `[pubkey_x, pubkey_y, return]`. + +::: + +:::tip[Structs] + +You can pass structs to the verifier contract. They will be flattened so that the array of inputs is 1-dimensional array. + +For example, consider the following program: + +```rust +struct Type1 { + val1: Field, + val2: Field, +} + +struct Nested { + t1: Type1, + is_true: bool, +} + +fn main(x: pub Field, nested: pub Nested, y: pub Field) { + //... +} +``` + +The order of these inputs would be flattened to: `[x, nested.t1.val1, nested.t1.val2, nested.is_true, y]` + +::: + +The other function you can call is our entrypoint `verify` function, as defined above. + +:::tip + +It's worth noticing that the `verify` function is actually a `view` function. A `view` function does not alter the blockchain state, so it doesn't need to be distributed (i.e. it will run only on the executing node), and therefore doesn't cost any gas. + +This can be particularly useful in some situations. If Alice generated a proof and wants Bob to verify its correctness, Bob doesn't need to run Nargo, NoirJS, or any Noir specific infrastructure. He can simply make a call to the blockchain with the proof and verify it is correct without paying any gas. + +It would be incorrect to say that a Noir proof verification costs any gas at all. However, most of the time the result of `verify` is used to modify state (for example, to update a balance, a game state, etc). In that case the whole network needs to execute it, which does incur gas costs (calldata and execution, but not storage). + +::: + +## A Note on EVM chains + +ZK-SNARK verification depends on some precompiled cryptographic primitives such as Elliptic Curve Pairings (if you like complex math, you can read about EC Pairings [here](https://medium.com/@VitalikButerin/exploring-elliptic-curve-pairings-c73c1864e627)). Not all EVM chains support EC Pairings, notably some of the ZK-EVMs. This means that you won't be able to use the verifier contract in all of them. + +For example, chains like `zkSync ERA` and `Polygon zkEVM` do not currently support these precompiles, so proof verification via Solidity verifier contracts won't work. Here's a quick list of EVM chains that have been tested and are known to work: + +- Optimism +- Arbitrum +- Polygon PoS +- Scroll +- Celo + +If you test any other chains, please open a PR on this page to update the list. See [this doc](https://github.com/noir-lang/noir-starter/tree/main/with-foundry#testing-on-chain) for more info about testing verifier contracts on different EVM chains. + +## What's next + +Now that you know how to call a Noir Solidity Verifier on a smart contract using Remix, you should be comfortable with using it with some programmatic frameworks, such as [hardhat](https://github.com/noir-lang/noir-starter/tree/main/vite-hardhat) and [foundry](https://github.com/noir-lang/noir-starter/tree/main/with-foundry). + +You can find other tools, examples, boilerplates and libraries in the [awesome-noir](https://github.com/noir-lang/awesome-noir) repository. + +You should also be ready to write and deploy your first NoirJS app and start generating proofs on websites, phones, and NodeJS environments! Head on to the [NoirJS tutorial](../tutorials/noirjs_app.md) to learn how to do that. diff --git a/noir/docs/docs/how_to/solidity_verifier.md b/noir/docs/docs/how_to/solidity_verifier.md deleted file mode 100644 index 8022b0e5f20..00000000000 --- a/noir/docs/docs/how_to/solidity_verifier.md +++ /dev/null @@ -1,130 +0,0 @@ ---- -title: Generate a Solidity Verifier -description: - Learn how to run the verifier as a smart contract on the blockchain. Compile a Solidity verifier - contract for your Noir program and deploy it on any EVM blockchain acting as a verifier smart - contract. Read more to find out -keywords: - [ - solidity verifier, - smart contract, - blockchain, - compiler, - plonk_vk.sol, - EVM blockchain, - verifying Noir programs, - proving backend, - Barretenberg, - ] -sidebar_position: 0 ---- - -For certain applications, it may be desirable to run the verifier as a smart contract instead of on -a local machine. - -Compile a Solidity verifier contract for your Noir program by running: - -```sh -nargo codegen-verifier -``` - -A new `contract` folder would then be generated in your project directory, containing the Solidity -file `plonk_vk.sol`. It can be deployed on any EVM blockchain acting as a verifier smart contract. - -> **Note:** It is possible to compile verifier contracts of Noir programs for other smart contract -> platforms as long as the proving backend supplies an implementation. -> -> Barretenberg, the default proving backend for Nargo, supports compilation of verifier contracts in -> Solidity only for the time being. - -## Verify - -To verify a proof using the Solidity verifier contract, call the `verify` function with the -following signature: - -```solidity -function verify(bytes calldata _proof, bytes32[] calldata _publicInputs) external view returns (bool) -``` - -You can see an example of how the `verify` function is called in the example zk voting application [here](https://github.com/noir-lang/noir-examples/blob/33e598c257e2402ea3a6b68dd4c5ad492bce1b0a/foundry-voting/src/zkVote.sol#L35): - -```solidity -function castVote(bytes calldata proof, uint proposalId, uint vote, bytes32 nullifierHash) public returns (bool) { - // ... - bytes32[] memory publicInputs = new bytes32[](4); - publicInputs[0] = merkleRoot; - publicInputs[1] = bytes32(proposalId); - publicInputs[2] = bytes32(vote); - publicInputs[3] = nullifierHash; - require(verifier.verify(proof, publicInputs), "Invalid proof"); -``` - -### Public Inputs - -:::tip - -A circuit doesn't have the concept of a return value. Return values are just syntactic sugar in -Noir. - -Under the hood, the return value is passed as an input to the circuit and is checked at the end of -the circuit program. - -::: - -The verifier contract uses the output (return) value of a Noir program as a public input. So if you -have the following function - -```rust -fn main( - // Public inputs - pubkey_x: pub Field, - pubkey_y: pub Field, - // Private inputs - priv_key: Field, -) -> pub Field -``` - -then `verify` in `plonk_vk.sol` will expect 3 public inputs. Passing two inputs will result in an -error like `Reason: PUBLIC_INPUT_COUNT_INVALID(3, 2)`. - -In this case the 3 inputs to `verify` would be ordered as `[pubkey_x, pubkey_y, return]`. - -#### Struct inputs - -Consider the following program: - -```rust -struct Type1 { - val1: Field, - val2: Field, -} - -struct Nested { - t1: Type1, - is_true: bool, -} - -fn main(x: pub Field, nested: pub Nested, y: pub Field) { - //... -} -``` - -Structs will be flattened so that the array of inputs is 1-dimensional array. The order of these inputs would be flattened to: `[x, nested.t1.val1, nested.t1.val2, nested.is_true, y]` - -## Noir for EVM chains - -You can currently deploy the Solidity verifier contracts to most EVM compatible chains. EVM chains that have been tested and are known to work include: - -- Optimism -- Arbitrum -- Polygon PoS -- Scroll -- Celo - -Other EVM chains should work, but have not been tested directly by our team. If you test any other chains, please open a PR on this page to update the list. See [this doc](https://github.com/noir-lang/noir-starter/tree/main/with-foundry#testing-on-chain) for more info about testing verifier contracts on different EVM chains. - -### Unsupported chains - -Unfortunately not all "EVM" chains are supported. - -**zkSync** and the **Polygon zkEVM** do _not_ currently support proof verification via Solidity verifier contracts. They are missing the bn256 precompile contract that the verifier contract requires. Once these chains support this precompile, they may work. diff --git a/noir/docs/docs/index.md b/noir/docs/docs/index.md deleted file mode 100644 index ab8c2f8acd2..00000000000 --- a/noir/docs/docs/index.md +++ /dev/null @@ -1,85 +0,0 @@ ---- -title: Noir -description: - Learn about the public alpha release of Noir, a domain specific language heavily influenced by Rust that compiles to - an intermediate language which can be compiled to an arithmetic circuit or a rank-1 constraint system. -keywords: - [ - Noir, - Domain Specific Language, - Rust, - Intermediate Language, - Arithmetic Circuit, - Rank-1 Constraint System, - Ethereum Developers, - Protocol Developers, - Blockchain Developers, - Proving System, - Smart Contract Language, - ] -sidebar_position: 0 ---- - -## What's new about Noir? - -Noir, a domain-specific language crafted for SNARK proving systems, stands out with its simplicity, flexibility, -and robust capabilities. Unlike conventional approaches that compile directly to a fixed NP-complete language, -Noir takes a two-pronged path. First, Noir compiles to an adaptable intermediate language known as ACIR. - -From there, depending on a given project's needs, ACIR can be further compiled into an arithmetic circuit for integration with Aztec's -barretenberg backend, or transformed into a rank-1 constraint system suitable for R1CS backends like Arkworks' Marlin -backend (among others). - -This innovative design introduces unique challenges; however, this approach also strategically separates the programming language from the -backend. Noir's approach echoes the modular philosophy of LLVM, offering developers a versatile toolkit for cryptographic -programming. - -## Who is Noir for? - -### Solidity Developers - -Noir streamlines the creation of Solidity contracts that interface with SNARK systems. -[`Utilize the nargo codegen-verifier`](./reference/nargo_commands.md#nargo-codegen-verifier) command to construct verifier -contracts efficiently. While the current alpha version offers this as a direct feature, future updates aim -to modularize this process for even greater ease of use. - -Noir currently includes a command to create a Solidity contract which verifies your Noir program. This will be -modularized in the future; however, as of the alpha, you can use the `nargo codegen-verifier` command to create a verifier contract. - -### Protocol Developers - -Should the Aztec backend not align with your existing tech stack, or if you're inclined to integrate alternative -proving systems, Noir's agnostic compilation to a proof-agnostic intermediate language offers unmatched flexibility. -This allows protocol engineers the freedom to substitute the default PLONK-based system with an alternative of their -choice, tailoring the proving system to their specific needs. - -### Blockchain developers - -Blockchain developers often face environmental constraints, such as predetermined proving systems and smart contract -languages. Noir addresses this by enabling the implementation of custom proving system backends and smart contract -interfaces, ensuring seamless integration with your blockchain's architecture, and expanding the horizons for innovation -within your projects. - -## Libraries - -Noir does not currently have an official package manager. You can find a list of some of the available Noir libraries in the -[awesome-noir repo here](https://github.com/noir-lang/awesome-noir#libraries). - -Some libraries that are available today include: - -- [Standard Library](https://github.com/noir-lang/noir/tree/master/noir_stdlib) - the Noir Standard Library -- [Ethereum Storage Proof Verification](https://github.com/aragonzkresearch/noir-trie-proofs) - a library that contains - the primitives necessary for RLP decoding (in the form of look-up table construction) and Ethereum state and storage - proof verification (or verification of any trie proof involving 32-byte long keys) -- [BigInt](https://github.com/shuklaayush/noir-bigint) - a library that provides a custom BigUint56 data type, allowing - for computations on large unsigned integers -- [ECrecover](https://github.com/colinnielsen/ecrecover-noir/tree/main) - a library to verify an ECDSA signature and - return the source Ethereum address -- [Sparse Merkle Tree Verifier](https://github.com/vocdoni/smtverifier-noir/tree/main) - a library for verification of - sparse Merkle trees -- [Signed Int](https://github.com/resurgencelabs/signed_int) - a library for accessing a custom Signed Integer data - type, allowing access to negative numbers on Noir -- [Fraction](https://github.com/resurgencelabs/fraction) - a library for accessing fractional number data type in Noir, - allowing results that aren't whole numbers - -See the section on [dependencies](noir/modules_packages_crates/dependencies.md) for more information. diff --git a/noir/docs/docs/index.mdx b/noir/docs/docs/index.mdx new file mode 100644 index 00000000000..75086ddcdde --- /dev/null +++ b/noir/docs/docs/index.mdx @@ -0,0 +1,67 @@ +--- +title: Noir Lang +hide_title: true +description: + Learn about the public alpha release of Noir, a domain specific language heavily influenced by Rust that compiles to + an intermediate language which can be compiled to an arithmetic circuit or a rank-1 constraint system. +keywords: + [Noir, + Domain Specific Language, + Rust, + Intermediate Language, + Arithmetic Circuit, + Rank-1 Constraint System, + Ethereum Developers, + Protocol Developers, + Blockchain Developers, + Proving System, + Smart Contract Language] +sidebar_position: 0 +--- + +import Tabs from '@theme/Tabs'; +import TabItem from '@theme/TabItem'; + +Noir Logo + +Noir is a Domain-Specific Language for SNARK proving systems developed by [Aztec Labs](https://aztec.network/). It allows you to generate complex Zero-Knowledge Programs (ZKP) by using simple and flexible syntax, requiring no previous knowledge on the underlying mathematics or cryptography. + +ZK programs are programs that can generate short proofs of a certain statement without revealing some details about it. You can read more about ZKPs [here](https://dev.to/spalladino/a-beginners-intro-to-coding-zero-knowledge-proofs-c56). + +## What's new about Noir? + +Noir works differently from most ZK languages by taking a two-pronged path. First, it compiles the program to an adaptable intermediate language known as ACIR. From there, depending on a given project's needs, ACIR can be further compiled into an arithmetic circuit for integration with the proving backend. + +:::info + +Noir is backend agnostic, which means it makes no assumptions on which proving backend powers the ZK proof. Being the language that powers [Aztec Contracts](https://docs.aztec.network/developers/contracts/main), it defaults to Aztec's Barretenberg proving backend. + +However, the ACIR output can be transformed to be compatible with other PLONK-based backends, or into a [rank-1 constraint system](https://www.rareskills.io/post/rank-1-constraint-system) suitable for backends such as Arkwork's Marlin. + +::: + +## Who is Noir for? + +Noir can be used both in complex cloud-based backends and in user's smartphones, requiring no knowledge on the underlying math or cryptography. From authorization systems that keep a password in the user's device, to complex on-chain verification of recursive proofs, Noir is designed to abstract away complexity without any significant overhead. Here are some examples of situations where Noir can be used: + + + + Noir Logo + + Aztec Contracts leverage Noir to allow for the storage and execution of private information. Writing an Aztec Contract is as easy as writing Noir, and Aztec developers can easily interact with the network storage and execution through the [Aztec.nr](https://docs.aztec.network/developers/contracts/main) library. + + + Soliditry Verifier Example + Noir can auto-generate Solidity verifier contracts that verify Noir proofs. This allows for non-interactive verification of proofs containing private information in an immutable system. This feature powers a multitude of use-case scenarios, from P2P chess tournaments, to [Aztec Layer-2 Blockchain](https://docs.aztec.network/) + + + Aztec Labs developed NoirJS, an easy interface to generate and verify Noir proofs in a Javascript environment. This allows for Noir to be used in webpages, mobile apps, games, and any other environment supporting JS execution in a standalone manner. + + + + +## Libraries + +Noir is meant to be easy to extend by simply importing Noir libraries just like in Rust. +The [awesome-noir repo](https://github.com/noir-lang/awesome-noir#libraries) is a collection of libraries developed by the Noir community. +Writing a new library is easy and makes code be composable and easy to reuse. See the section on [dependencies](noir/modules_packages_crates/dependencies.md) for more information. diff --git a/noir/docs/docs/noir/concepts/data_types/arrays.md b/noir/docs/docs/noir/concepts/data_types/arrays.md index 7f275a2d771..a8bd338e736 100644 --- a/noir/docs/docs/noir/concepts/data_types/arrays.md +++ b/noir/docs/docs/noir/concepts/data_types/arrays.md @@ -82,14 +82,16 @@ You can create arrays of primitive types or structs. There is not yet support fo ## Methods -For convenience, the STD provides some ready-to-use, common methods for arrays: +For convenience, the STD provides some ready-to-use, common methods for arrays. +Each of these functions are located within the generic impl `impl [T; N] {`. +So anywhere `self` appears, it refers to the variable `self: [T; N]`. ### len Returns the length of an array ```rust -fn len(_array: [T; N]) -> comptime Field +fn len(self) -> Field ``` example @@ -109,7 +111,7 @@ logic it uses internally is optimized specifically for these values. If you need sort any type, you should use the function `sort_via` described below. ```rust -fn sort(_array: [T; N]) -> [T; N] +fn sort(self) -> [T; N] ``` example @@ -127,7 +129,7 @@ fn main() { Sorts the array with a custom comparison function ```rust -fn sort_via(mut a: [T; N], ordering: fn(T, T) -> bool) -> [T; N] +fn sort_via(self, ordering: fn(T, T) -> bool) -> [T; N] ``` example @@ -148,7 +150,7 @@ fn main() { Applies a function to each element of the array, returning a new array containing the mapped elements. ```rust -fn map(f: fn(T) -> U) -> [U; N] +fn map(self, f: fn(T) -> U) -> [U; N] ``` example @@ -164,7 +166,7 @@ Applies a function to each element of the array, returning the final accumulated parameter is the initial value. ```rust -fn fold(mut accumulator: U, f: fn(U, T) -> U) -> U +fn fold(self, mut accumulator: U, f: fn(U, T) -> U) -> U ``` This is a left fold, so the given function will be applied to the accumulator and first element of @@ -198,7 +200,7 @@ fn main() { Same as fold, but uses the first element as starting element. ```rust -fn reduce(f: fn(T, T) -> T) -> T +fn reduce(self, f: fn(T, T) -> T) -> T ``` example: @@ -216,7 +218,7 @@ fn main() { Returns true if all the elements satisfy the given predicate ```rust -fn all(predicate: fn(T) -> bool) -> bool +fn all(self, predicate: fn(T) -> bool) -> bool ``` example: @@ -234,7 +236,7 @@ fn main() { Returns true if any of the elements satisfy the given predicate ```rust -fn any(predicate: fn(T) -> bool) -> bool +fn any(self, predicate: fn(T) -> bool) -> bool ``` example: diff --git a/noir/docs/docs/noir/concepts/data_types/fields.md b/noir/docs/docs/noir/concepts/data_types/fields.md index a1c67945d66..7870c98c858 100644 --- a/noir/docs/docs/noir/concepts/data_types/fields.md +++ b/noir/docs/docs/noir/concepts/data_types/fields.md @@ -157,6 +157,23 @@ fn main() { } ``` +### assert_max_bit_size + +Adds a constraint to specify that the field can be represented with `bit_size` number of bits + +```rust +fn assert_max_bit_size(self, bit_size: u32) +``` + +example: + +```rust +fn main() { + let field = 2 + field.assert_max_bit_size(32); +} +``` + ### sgn0 Parity of (prime) Field element, i.e. sgn0(x mod p) = 0 if x ∈ \{0, ..., p-1\} is even, otherwise sgn0(x mod p) = 1. diff --git a/noir/docs/docs/noir/standard_library/black_box_fns.md b/noir/docs/docs/noir/standard_library/black_box_fns.md index 4b1efbd17de..6b22d0e7466 100644 --- a/noir/docs/docs/noir/standard_library/black_box_fns.md +++ b/noir/docs/docs/noir/standard_library/black_box_fns.md @@ -6,40 +6,26 @@ keywords: [noir, black box functions] Black box functions are functions in Noir that rely on backends implementing support for specialized constraints. This makes certain zk-snark unfriendly computations cheaper than if they were implemented in Noir. -:::warning - -It is likely that not all backends will support a particular black box function. - -::: - -Because it is not guaranteed that all backends will support black box functions, it is possible that certain Noir programs won't compile against a particular backend if they use an unsupported black box function. It is possible to fallback to less efficient implementations written in Noir/ACIR in some cases. - -Black box functions are specified with the `#[foreign(black_box_fn)]` attribute. For example, the SHA256 function in the Noir [source code](https://github.com/noir-lang/noir/blob/v0.5.1/noir_stdlib/src/hash.nr) looks like: - -```rust -#[foreign(sha256)] -fn sha256(_input : [u8; N]) -> [u8; 32] {} -``` +The ACVM spec defines a set of blackbox functions which backends will be expected to implement. This allows backends to use optimized implementations of these constraints if they have them, however they may also fallback to less efficient naive implementations if not. ## Function list -Here is a list of the current black box functions that are supported by UltraPlonk: +Here is a list of the current black box functions: -- AES - [SHA256](./cryptographic_primitives/hashes#sha256) - [Schnorr signature verification](./cryptographic_primitives/schnorr) - [Blake2s](./cryptographic_primitives/hashes#blake2s) +- [Blake3](./cryptographic_primitives/hashes#blake2s) - [Pedersen Hash](./cryptographic_primitives/hashes#pedersen_hash) - [Pedersen Commitment](./cryptographic_primitives/hashes#pedersen_commitment) - [ECDSA signature verification](./cryptographic_primitives/ecdsa_sig_verification) - [Fixed base scalar multiplication](./cryptographic_primitives/scalar) -- [Compute merkle root](./merkle_trees#compute_merkle_root) - AND - XOR - RANGE - [Keccak256](./cryptographic_primitives/hashes#keccak256) - [Recursive proof verification](./recursion) -Most black box functions are included as part of the Noir standard library, however `AND`, `XOR` and `RANGE` are used as part of the Noir language syntax. For instance, using the bitwise operator `&` will invoke the `AND` black box function. To ensure compatibility across backends, the ACVM has fallback implementations of `AND`, `XOR` and `RANGE` defined in its standard library which it can seamlessly fallback to if the backend doesn't support them. +Most black box functions are included as part of the Noir standard library, however `AND`, `XOR` and `RANGE` are used as part of the Noir language syntax. For instance, using the bitwise operator `&` will invoke the `AND` black box function. You can view the black box functions defined in the ACVM code [here](https://github.com/noir-lang/noir/blob/master/acvm-repo/acir/src/circuit/black_box_functions.rs). diff --git a/noir/docs/docs/noir/standard_library/cryptographic_primitives/ec_primitives.md b/noir/docs/docs/noir/standard_library/cryptographic_primitives/ec_primitives.md index 8d573adb3be..d2b42d67b7c 100644 --- a/noir/docs/docs/noir/standard_library/cryptographic_primitives/ec_primitives.md +++ b/noir/docs/docs/noir/standard_library/cryptographic_primitives/ec_primitives.md @@ -72,7 +72,7 @@ does indeed lie on `c` by calling `c.contains(p1)`. ## Examples The -[ec_baby_jubjub test](https://github.com/noir-lang/noir/blob/master/crates/nargo_cli/tests/test_data/ec_baby_jubjub/src/main.nr) +[ec_baby_jubjub test](https://github.com/noir-lang/noir/blob/master/test_programs/compile_success_empty/ec_baby_jubjub/src/main.nr) illustrates all of the above primitives on various forms of the Baby Jubjub curve. A couple of more interesting examples in Noir would be: diff --git a/noir/docs/docs/noir/standard_library/cryptographic_primitives/ecdsa_sig_verification.mdx b/noir/docs/docs/noir/standard_library/cryptographic_primitives/ecdsa_sig_verification.mdx index 1376c51dfde..d67a1ac94df 100644 --- a/noir/docs/docs/noir/standard_library/cryptographic_primitives/ecdsa_sig_verification.mdx +++ b/noir/docs/docs/noir/standard_library/cryptographic_primitives/ecdsa_sig_verification.mdx @@ -13,9 +13,7 @@ Noir supports ECDSA signatures verification over the secp256k1 and secp256r1 cur Verifier for ECDSA Secp256k1 signatures -```rust -fn verify_signature(_public_key_x : [u8; 32], _public_key_y : [u8; 32], _signature: [u8; 64], _message: [u8]) -> bool -``` +#include_code ecdsa_secp256k1 noir_stdlib/src/ecdsa_secp256k1.nr rust example: @@ -30,9 +28,7 @@ fn main(hashed_message : [u8;32], pub_key_x : [u8;32], pub_key_y : [u8;32], sign Verifier for ECDSA Secp256r1 signatures -```rust -fn verify_signature(_public_key_x : [u8; 32], _public_key_y : [u8; 32], _signature: [u8; 64], _message: [u8]) -> bool -``` +#include_code ecdsa_secp256r1 noir_stdlib/src/ecdsa_secp256r1.nr rust example: diff --git a/noir/docs/docs/noir/standard_library/cryptographic_primitives/hashes.mdx b/noir/docs/docs/noir/standard_library/cryptographic_primitives/hashes.mdx index 3c5f7f79603..85706384eee 100644 --- a/noir/docs/docs/noir/standard_library/cryptographic_primitives/hashes.mdx +++ b/noir/docs/docs/noir/standard_library/cryptographic_primitives/hashes.mdx @@ -14,9 +14,7 @@ import BlackBoxInfo from '@site/src/components/Notes/_blackbox.mdx'; Given an array of bytes, returns the resulting sha256 hash. -```rust -fn sha256(_input : [u8]) -> [u8; 32] -``` +#include_code sha256 noir_stdlib/src/hash.nr rust example: @@ -33,9 +31,7 @@ fn main() { Given an array of bytes, returns an array with the Blake2 hash -```rust -fn blake2s(_input : [u8]) -> [u8; 32] -``` +#include_code blake2s noir_stdlib/src/hash.nr rust example: @@ -48,43 +44,45 @@ fn main() { -## pedersen_hash +## blake3 -Given an array of Fields, returns the Pedersen hash. +Given an array of bytes, returns an array with the Blake3 hash -```rust -fn pedersen_hash(_input : [Field]) -> Field -``` +#include_code blake3 noir_stdlib/src/hash.nr rust example: ```rust fn main() { let x = [163, 117, 178, 149]; // some random bytes - let hash = std::hash::pedersen_hash(x); + let hash = std::hash::blake3(x); } ``` +## pedersen_hash + +Given an array of Fields, returns the Pedersen hash. + +#include_code pedersen_hash noir_stdlib/src/hash.nr rust + +example: + +#include_code pedersen-hash test_programs/execution_success/pedersen_hash/src/main.nr rust + + ## pedersen_commitment Given an array of Fields, returns the Pedersen commitment. -```rust -fn pedersen_commitment(_input : [Field]) -> [Field; 2] -``` +#include_code pedersen_commitment noir_stdlib/src/hash.nr rust example: -```rust -fn main() { - let x = [163, 117, 178, 149]; // some random bytes - let commitment = std::hash::pedersen_commitment(x); -} -``` +#include_code pedersen-commitment test_programs/execution_success/pedersen_commitment/src/main.nr rust @@ -94,19 +92,11 @@ Given an array of bytes (`u8`), returns the resulting keccak hash as an array of (`[u8; 32]`). Specify a message_size to hash only the first `message_size` bytes of the input. -```rust -fn keccak256(_input : [u8; N], _message_size: u32) -> [u8; 32] -``` +#include_code keccak256 noir_stdlib/src/hash.nr rust example: -```rust -fn main() { - let x = [163, 117, 178, 149]; // some random bytes - let message_size = 4; - let hash = std::hash::keccak256(x, message_size); -} -``` +#include_code keccak256 test_programs/execution_success/keccak256/src/main.nr rust @@ -122,13 +112,7 @@ fn hash_1(input: [Field; 1]) -> Field example: -```rust -fn main() -{ - let hash_2 = std::hash::poseidon::bn254::hash_2([1, 2]); - assert(hash2 == 0x115cc0f5e7d690413df64c6b9662e9cf2a3617f2743245519e19607a4417189a); -} -``` +#include_code poseidon test_programs/execution_success/poseidon_bn254_hash/src/main.nr rust ## mimc_bn254 and mimc diff --git a/noir/docs/docs/noir/standard_library/cryptographic_primitives/scalar.mdx b/noir/docs/docs/noir/standard_library/cryptographic_primitives/scalar.mdx index aa4fb8cbaed..c2946b2b73b 100644 --- a/noir/docs/docs/noir/standard_library/cryptographic_primitives/scalar.mdx +++ b/noir/docs/docs/noir/standard_library/cryptographic_primitives/scalar.mdx @@ -12,9 +12,7 @@ import BlackBoxInfo from '@site/src/components/Notes/_blackbox.mdx'; Performs scalar multiplication over the embedded curve whose coordinates are defined by the configured noir field. For the BN254 scalar field, this is BabyJubJub or Grumpkin. -```rust -fn fixed_base_embedded_curve(_input : Field) -> [Field; 2] -``` +#include_code fixed_base_embedded_curve noir_stdlib/src/scalar_mul.nr rust example diff --git a/noir/docs/docs/noir/standard_library/cryptographic_primitives/schnorr.mdx b/noir/docs/docs/noir/standard_library/cryptographic_primitives/schnorr.mdx index 7a2c9c20226..0e0c358c6e1 100644 --- a/noir/docs/docs/noir/standard_library/cryptographic_primitives/schnorr.mdx +++ b/noir/docs/docs/noir/standard_library/cryptographic_primitives/schnorr.mdx @@ -11,9 +11,7 @@ import BlackBoxInfo from '@site/src/components/Notes/_blackbox.mdx'; Verifier for Schnorr signatures over the embedded curve (for BN254 it is Grumpkin). -```rust -fn verify_signature(_public_key_x: Field, _public_key_y: Field, _signature: [u8; 64], _message: [u8]) -> bool -``` +#include_code schnorr_verify noir_stdlib/src/schnorr.nr rust where `_signature` can be generated like so using the npm package [@noir-lang/barretenberg](https://www.npmjs.com/package/@noir-lang/barretenberg) diff --git a/noir/docs/docs/noir/standard_library/recursion.md b/noir/docs/docs/noir/standard_library/recursion.md index 67962082a8f..f252150c8b5 100644 --- a/noir/docs/docs/noir/standard_library/recursion.md +++ b/noir/docs/docs/noir/standard_library/recursion.md @@ -1,16 +1,16 @@ --- title: Recursive Proofs description: Learn about how to write recursive proofs in Noir. -keywords: [recursion, recursive proofs, verification_key, aggregation object, verify_proof] +keywords: [recursion, recursive proofs, verification_key, verify_proof] --- Noir supports recursively verifying proofs, meaning you verify the proof of a Noir program in another Noir program. This enables creating proofs of arbitrary size by doing step-wise verification of smaller components of a large proof. -The `verify_proof` function takes a verification key, proof and public inputs for a zk program, as well as a key hash and an input aggregation object. The key hash is used to check the validity of the verification key and the input aggregation object is required by some proving systems. The `verify_proof` function returns an output aggregation object that can then be fed into future iterations of the proof verification if required. +Read [the explainer on recursion](../../explainers/explainer-recursion.md) to know more about this function and the [guide on how to use it.](../../how_to/how-to-recursion.md) ```rust #[foreign(verify_proof)] -fn verify_proof(_verification_key : [Field], _proof : [Field], _public_input : Field, _key_hash : Field, _input_aggregation_object : [Field]) -> [Field] {} +fn verify_proof(_verification_key : [Field], _proof : [Field], _public_input : Field, _key_hash : Field) {} ``` :::info @@ -26,36 +26,29 @@ use dep::std; fn main( verification_key : [Field; 114], - proof : [Field; 94], + proof : [Field; 93], public_inputs : [Field; 1], key_hash : Field, - input_aggregation_object : [Field; 16], - proof_b : [Field; 94], -) -> pub [Field; 16] { - let output_aggregation_object_a = std::verify_proof( + proof_b : [Field; 93], +) { + std::verify_proof( verification_key.as_slice(), proof.as_slice(), public_inputs.as_slice(), - key_hash, - input_aggregation_object + key_hash ); - let output_aggregation_object = std::verify_proof( + std::verify_proof( verification_key.as_slice(), proof_b.as_slice(), public_inputs.as_slice(), - key_hash, - output_aggregation_object_a + key_hash ); - - let mut output = [0; 16]; - for i in 0..16 { - output[i] = output_aggregation_object[i]; - } - output } ``` +You can see a full example of recursive proofs in [this example recursion demo repo](https://github.com/noir-lang/noir-examples/tree/master/recursion). + ## Parameters ### `verification_key` @@ -68,23 +61,8 @@ The proof for the zk program that is being verified. ### `public_inputs` -These represent the public inputs of the proof we are verifying. They should be checked against in the circuit after construction of a new aggregation state. +These represent the public inputs of the proof we are verifying. ### `key_hash` A key hash is used to check the validity of the verification key. The circuit implementing this opcode can use this hash to ensure that the key provided to the circuit matches the key produced by the circuit creator. - -### `input_aggregation_object` - -An aggregation object is blob of data that the top-level verifier must run some proof system specific algorithm on to complete verification. The size is proof system specific and will be set by the backend integrating this opcode. The input aggregation object is only not `None` when we are verifying a previous recursive aggregation in the current circuit. If this is the first recursive aggregation there is no input aggregation object. It is left to the backend to determine how to handle when there is no input aggregation object. - -## Return value - -### `output_aggregation_object` - -This is the result of a recursive aggregation and is what will be fed into the next verifier. -The next verifier can either perform a final verification (returning true or false) or perform another recursive aggregation where this output aggregation object will be the input aggregation object of the next recursive aggregation. - -## Example - -You can see an example of how to do recursive proofs in [this example recursion demo repo](https://github.com/noir-lang/noir-examples/tree/master/recursion). diff --git a/noir/docs/docs/noir/standard_library/traits.md b/noir/docs/docs/noir/standard_library/traits.md index f2960ca5080..fb6d5ae1c48 100644 --- a/noir/docs/docs/noir/standard_library/traits.md +++ b/noir/docs/docs/noir/standard_library/traits.md @@ -8,11 +8,7 @@ keywords: [traits, trait, interface, protocol, default, add, eq] ### `std::default::Default` -```rust -trait Default { - fn default() -> Self; -} -``` +#include_code default-trait noir_stdlib/src/default.nr rust Constructs a default value of a type. @@ -52,15 +48,47 @@ impl Default for (A, B, C, D, E) For primitive integer types, the return value of `default` is `0`. Container types such as arrays are filled with default values of their element type. + +## `std::convert` + +### `std::convert::From` + +#include_code from-trait noir_stdlib/src/convert.nr rust + +The `From` trait defines how to convert from a given type `T` to the type on which the trait is implemented. + +The Noir standard library provides a number of implementations of `From` between primitive types. +#include_code from-impls noir_stdlib/src/convert.nr rust + +#### When to implement `From` + +As a general rule of thumb, `From` may be implemented in the [situations where it would be suitable in Rust](https://doc.rust-lang.org/std/convert/trait.From.html#when-to-implement-from): + +- The conversion is *infallible*: Noir does not provide an equivalent to Rust's `TryFrom`, if the conversion can fail then provide a named method instead. +- The conversion is *lossless*: semantically, it should not lose or discard information. For example, `u32: From` can losslessly convert any `u16` into a valid `u32` such that the original `u16` can be recovered. On the other hand, `u16: From` should not be implemented as `2**16` is a `u32` which cannot be losslessly converted into a `u16`. +- The conversion is *value-preserving*: the conceptual kind and meaning of the resulting value is the same, even though the Noir type and technical representation might be different. While it's possible to infallibly and losslessly convert a `u8` into a `str<2>` hex representation, `4u8` and `"04"` are too different for `str<2>: From` to be implemented. +- The conversion is *obvious*: it's the only reasonable conversion between the two types. If there's ambiguity on how to convert between them such that the same input could potentially map to two different values then a named method should be used. For instance rather than implementing `U128: From<[u8; 16]>`, the methods `U128::from_le_bytes` and `U128::from_be_bytes` are used as otherwise the endianness of the array would be ambiguous, resulting in two potential values of `U128` from the same byte array. + +One additional recommendation specific to Noir is: +- The conversion is *efficient*: it's relatively cheap to convert between the two types. Due to being a ZK DSL, it's more important to avoid unnecessary computation compared to Rust. If the implementation of `From` would encourage users to perform unnecessary conversion, resulting in additional proving time, then it may be preferable to expose functionality such that this conversion may be avoided. + +### `std::convert::Into` + +The `Into` trait is defined as the reciprocal of `From`. It should be easy to convince yourself that if we can convert to type `A` from type `B`, then it's possible to convert type `B` into type `A`. + +For this reason, implementing `From` on a type will automatically generate a matching `Into` implementation. One should always prefer implementing `From` over `Into` as implementing `Into` will not generate a matching `From` implementation. + +#include_code into-trait noir_stdlib/src/convert.nr rust + +`Into` is most useful when passing function arguments where the types don't quite match up with what the function expects. In this case, the compiler has enough type information to perform the necessary conversion by just appending `.into()` onto the arguments in question. + + ## `std::cmp` ### `std::cmp::Eq` -```rust -trait Eq { - fn eq(self, other: Self) -> bool; -} -``` +#include_code eq-trait noir_stdlib/src/cmp.nr rust + Returns `true` if `self` is equal to `other`. Implementing this trait on a type allows the type to be used with `==` and `!=`. @@ -97,13 +125,9 @@ impl Eq for (A, B, C, D, E) where A: Eq, B: Eq, C: Eq, D: Eq, E: Eq { .. } ``` -### `std::cmp::Cmp` +### `std::cmp::Ord` -```rust -trait Cmp { - fn cmp(self, other: Self) -> Ordering; -} -``` +#include_code ord-trait noir_stdlib/src/cmp.nr rust `a.cmp(b)` compares two values returning `Ordering::less()` if `a < b`, `Ordering::equal()` if `a == b`, or `Ordering::greater()` if `a > b`. @@ -151,23 +175,10 @@ These traits abstract over addition, subtraction, multiplication, and division r Implementing these traits for a given type will also allow that type to be used with the corresponding operator for that trait (`+` for Add, etc) in addition to the normal method names. -```rust -trait Add { - fn add(self, other: Self) -> Self; -} - -trait Sub { - fn sub(self, other: Self) -> Self; -} - -trait Mul { - fn mul(self, other: Self) -> Self; -} - -trait Div { - fn div(self, other: Self) -> Self; -} -``` +#include_code add-trait noir_stdlib/src/ops.nr rust +#include_code sub-trait noir_stdlib/src/ops.nr rust +#include_code mul-trait noir_stdlib/src/ops.nr rust +#include_code div-trait noir_stdlib/src/ops.nr rust The implementations block below is given for the `Add` trait, but the same types that implement `Add` also implement `Sub`, `Mul`, and `Div`. @@ -189,11 +200,7 @@ impl Add for u64 { .. } ### `std::ops::Rem` -```rust -trait Rem { - fn rem(self, other: Self) -> Self; -} -``` +#include_code rem-trait noir_stdlib/src/ops.nr rust `Rem::rem(a, b)` is the remainder function returning the result of what is left after dividing `a` and `b`. Implementing `Rem` allows the `%` operator @@ -216,19 +223,9 @@ impl Rem for i64 { fn rem(self, other: i64) -> i64 { self % other } } ### `std::ops::{ BitOr, BitAnd, BitXor }` -```rust -trait BitOr { - fn bitor(self, other: Self) -> Self; -} - -trait BitAnd { - fn bitand(self, other: Self) -> Self; -} - -trait BitXor { - fn bitxor(self, other: Self) -> Self; -} -``` +#include_code bitor-trait noir_stdlib/src/ops.nr rust +#include_code bitand-trait noir_stdlib/src/ops.nr rust +#include_code bitxor-trait noir_stdlib/src/ops.nr rust Traits for the bitwise operations `|`, `&`, and `^`. @@ -255,15 +252,8 @@ impl BitOr for i64 { fn bitor(self, other: i64) -> i64 { self | other } } ### `std::ops::{ Shl, Shr }` -```rust -trait Shl { - fn shl(self, other: Self) -> Self; -} - -trait Shr { - fn shr(self, other: Self) -> Self; -} -``` +#include_code shl-trait noir_stdlib/src/ops.nr rust +#include_code shr-trait noir_stdlib/src/ops.nr rust Traits for a bit shift left and bit shift right. diff --git a/noir/docs/docs/tutorials/noirjs_app.md b/noir/docs/docs/tutorials/noirjs_app.md index 4293aa053fc..23534795dde 100644 --- a/noir/docs/docs/tutorials/noirjs_app.md +++ b/noir/docs/docs/tutorials/noirjs_app.md @@ -3,6 +3,7 @@ title: Building a web app with NoirJS description: Learn how to setup a new app that uses Noir to generate and verify zero-knowledge SNARK proofs in a typescript or javascript environment. keywords: [how to, guide, javascript, typescript, noir, barretenberg, zero-knowledge, proofs, app] sidebar_position: 0 +pagination_next: noir/concepts/data_types/index --- NoirJS is a set of packages meant to work both in a browser and a server environment. In this tutorial, we will build a simple web app using them. From here, you should get an idea on how to proceed with your own Noir projects! @@ -70,7 +71,7 @@ At this point in the tutorial, your folder structure should look like this: ### Node and Vite If you want to explore Nargo, feel free to go on a side-quest now and follow the steps in the -[getting started](../getting_started/create_a_project) guide. However, we want our app to run on the browser, so we need Vite. +[getting started](../getting_started/hello_noir/index.md) guide. However, we want our app to run on the browser, so we need Vite. Vite is a powerful tool to generate static websites. While it provides all kinds of features, let's just go barebones with some good old vanilla JS. @@ -205,7 +206,7 @@ We're starting with the good stuff now. If you've compiled the circuit as descri import circuit from '../circuit/target/circuit.json'; ``` -[Noir is backend-agnostic](../index.md#whats-new-about-noir). We write Noir, but we also need a proving backend. That's why we need to import and instantiate the two dependencies we installed above: `BarretenbergBackend` and `Noir`. Let's import them right below: +[Noir is backend-agnostic](../index.mdx#whats-new-about-noir). We write Noir, but we also need a proving backend. That's why we need to import and instantiate the two dependencies we installed above: `BarretenbergBackend` and `Noir`. Let's import them right below: ```js import { BarretenbergBackend } from '@noir-lang/backend_barretenberg'; @@ -273,6 +274,6 @@ You have successfully generated a client-side Noir web app! ## Further Reading -You can see how noirjs is used in a full stack Next.js hardhat application in the [noir-starter repo here](https://github.com/noir-lang/noir-starter/tree/main/next-hardhat). The example shows how to calculate a proof in the browser and verify it with a deployed Solidity verifier contract from noirjs. +You can see how noirjs is used in a full stack Next.js hardhat application in the [noir-starter repo here](https://github.com/noir-lang/noir-starter/tree/main/vite-hardhat). The example shows how to calculate a proof in the browser and verify it with a deployed Solidity verifier contract from noirjs. You should also check out the more advanced examples in the [noir-examples repo](https://github.com/noir-lang/noir-examples), where you'll find reference usage for some cool apps. diff --git a/noir/docs/docusaurus.config.ts b/noir/docs/docusaurus.config.ts index aacc318f5be..d1d344ba635 100644 --- a/noir/docs/docusaurus.config.ts +++ b/noir/docs/docusaurus.config.ts @@ -26,6 +26,7 @@ export default { '@docusaurus/preset-classic', { docs: { + path: "processed-docs", sidebarPath: './sidebars.js', routeBasePath: '/docs', remarkPlugins: [math], @@ -154,7 +155,7 @@ export default { entryPoints: ['../tooling/noir_js/src/index.ts'], tsconfig: '../tooling/noir_js/tsconfig.json', entryPointStrategy: 'resolve', - out: 'docs/reference/NoirJS/noir_js', + out: 'processed-docs/reference/NoirJS/noir_js', plugin: ['typedoc-plugin-markdown'], name: 'noir_js', disableSources: true, @@ -185,7 +186,7 @@ export default { entryPoints: ['../tooling/noir_js_backend_barretenberg/src/index.ts'], tsconfig: '../tooling/noir_js_backend_barretenberg/tsconfig.json', entryPointStrategy: 'resolve', - out: 'docs/reference/NoirJS/backend_barretenberg', + out: 'processed-docs/reference/NoirJS/backend_barretenberg', plugin: ['typedoc-plugin-markdown'], name: 'backend_barretenberg', disableSources: true, diff --git a/noir/docs/package.json b/noir/docs/package.json index 1e3efcfe3d1..71b624ff565 100644 --- a/noir/docs/package.json +++ b/noir/docs/package.json @@ -3,10 +3,12 @@ "version": "0.0.0", "private": true, "scripts": { - "start": "docusaurus start", - "build": "yarn version::stables && docusaurus build", + "preprocess": "yarn node ./scripts/preprocess/index.js", + "start": "yarn preprocess && docusaurus start", + "build": "yarn preprocess && yarn version::stables && docusaurus build", "version::stables": "ts-node ./scripts/setStable.ts", - "serve": "serve build" + "serve": "serve build", + "version": "yarn preprocess && docusaurus docs:version" }, "dependencies": { "@docusaurus/core": "^3.0.1", diff --git a/noir/docs/processed-docs-cache/explainers/explainer-oracle.md b/noir/docs/processed-docs-cache/explainers/explainer-oracle.md new file mode 100644 index 00000000000..b84ca5dd986 --- /dev/null +++ b/noir/docs/processed-docs-cache/explainers/explainer-oracle.md @@ -0,0 +1,57 @@ +--- +title: Oracles +description: This guide provides an in-depth understanding of how Oracles work in Noir programming. Learn how to use outside calculations in your programs, constrain oracles, and understand their uses and limitations. +keywords: + - Noir Programming + - Oracles + - JSON-RPC + - Foreign Call Handlers + - Constrained Functions + - Blockchain Programming +sidebar_position: 1 +--- + +If you've seen "The Matrix" you may recall "The Oracle" as Gloria Foster smoking cigarettes and baking cookies. While she appears to "know things", she is actually providing a calculation of a pre-determined future. Noir Oracles are similar, in a way. They don't calculate the future (yet), but they allow you to use outside calculations in your programs. + +![matrix oracle prediction](@site/static/img/memes/matrix_oracle.jpeg) + +A Noir program is usually self-contained. You can pass certain inputs to it, and it will generate a deterministic output for those inputs. But what if you wanted to defer some calculation to an outside process or source? + +Oracles are functions that provide this feature. + +## Use cases + +An example usage for Oracles is proving something on-chain. For example, proving that the ETH-USDC quote was below a certain target at a certain block time. Or even making more complex proofs like proving the ownership of an NFT as an anonymous login method. + +Another interesting use case is to defer expensive calculations to be made outside of the Noir program, and then constraining the result; similar to the use of [unconstrained functions](../noir/concepts//unconstrained.md). + +In short, anything that can be constrained in a Noir program but needs to be fetched from an external source is a great candidate to be used in oracles. + +## Constraining oracles + +Just like in The Matrix, Oracles are powerful. But with great power, comes great responsibility. Just because you're using them in a Noir program doesn't mean they're true. Noir has no superpowers. If you want to prove that Portugal won the Euro Cup 2016, you're still relying on potentially untrusted information. + +To give a concrete example, Alice wants to login to the [NounsDAO](https://nouns.wtf/) forum with her username "noir_nouner" by proving she owns a noun without revealing her ethereum address. Her Noir program could have a oracle call like this: + +```rust +#[oracle(getNoun)] +unconstrained fn get_noun(address: Field) -> Field +``` + +This oracle could naively resolve with the number of Nouns she possesses. However, it is useless as a trusted source, as the oracle could resolve to anything Alice wants. In order to make this oracle call actually useful, Alice would need to constrain the response from the oracle, by proving her address and the noun count belongs to the state tree of the contract. + +In short, **Oracles don't prove anything. Your Noir program does.** + +:::danger + +If you don't constrain the return of your oracle, you could be clearly opening an attack vector on your Noir program. Make double-triple sure that the return of an oracle call is constrained! + +::: + +## How to use Oracles + +On CLI, Nargo resolves oracles by making JSON RPC calls, which means it would require an RPC node to be running. + +In JavaScript, NoirJS accepts and resolves arbitrary call handlers (that is, not limited to JSON) as long as they matches the expected types the developer defines. Refer to [Foreign Call Handler](../reference/NoirJS/noir_js/type-aliases/ForeignCallHandler.md) to learn more about NoirJS's call handling. + +If you want to build using oracles, follow through to the [oracle guide](../how_to/how-to-oracles.md) for a simple example on how to do that. diff --git a/noir/docs/processed-docs-cache/explainers/explainer-recursion.md b/noir/docs/processed-docs-cache/explainers/explainer-recursion.md new file mode 100644 index 00000000000..8f992ec29fd --- /dev/null +++ b/noir/docs/processed-docs-cache/explainers/explainer-recursion.md @@ -0,0 +1,177 @@ +--- +title: Recursive proofs +description: Explore the concept of recursive proofs in Zero-Knowledge programming. Understand how recursion works in Noir, a language for writing smart contracts on the EVM blockchain. Learn through practical examples like Alice and Bob's guessing game, Charlie's recursive merkle tree, and Daniel's reusable components. Discover how to use recursive proofs to optimize computational resources and improve efficiency. + +keywords: + [ + "Recursive Proofs", + "Zero-Knowledge Programming", + "Noir", + "EVM Blockchain", + "Smart Contracts", + "Recursion in Noir", + "Alice and Bob Guessing Game", + "Recursive Merkle Tree", + "Reusable Components", + "Optimizing Computational Resources", + "Improving Efficiency", + "Verification Key", + "Aggregation Objects", + "Recursive zkSNARK schemes", + "PLONK", + "Proving and Verification Keys" + ] +sidebar_position: 1 +--- + +In programming, we tend to think of recursion as something calling itself. A classic example would be the calculation of the factorial of a number: + +```js +function factorial(n) { + if (n === 0 || n === 1) { + return 1; + } else { + return n * factorial(n - 1); + } +} +``` + +In this case, while `n` is not `1`, this function will keep calling itself until it hits the base case, bubbling up the result on the call stack: + +```md + Is `n` 1? <--------- + /\ / + / \ n = n -1 + / \ / + Yes No -------- +``` + +In Zero-Knowledge, recursion has some similarities. + +It is not a Noir function calling itself, but a proof being used as an input to another circuit. In short, you verify one proof *inside* another proof, returning the proof that both proofs are valid. + +This means that, given enough computational resources, you can prove the correctness of any arbitrary number of proofs in a single proof. This could be useful to design state channels (for which a common example would be [Bitcoin's Lightning Network](https://en.wikipedia.org/wiki/Lightning_Network)), to save on gas costs by settling one proof on-chain, or simply to make business logic less dependent on a consensus mechanism. + +## Examples + +Let us look at some of these examples + +### Alice and Bob - Guessing game + +Alice and Bob are friends, and they like guessing games. They want to play a guessing game online, but for that, they need a trusted third-party that knows both of their secrets and finishes the game once someone wins. + +So, they use zero-knowledge proofs. Alice tries to guess Bob's number, and Bob will generate a ZK proof stating whether she succeeded or failed. + +This ZK proof can go on a smart contract, revealing the winner and even giving prizes. However, this means every turn needs to be verified on-chain. This incurs some cost and waiting time that may simply make the game too expensive or time-consuming to be worth it. + +As a solution, Alice proposes the following: "what if Bob generates his proof, and instead of sending it on-chain, I verify it *within* my own proof before playing my own turn?". + +She can then generate a proof that she verified his proof, and so on. + +```md + Did you fail? <-------------------------- + / \ / + / \ n = n -1 + / \ / + Yes No / + | | / + | | / + | You win / + | / + | / +Generate proof of that / + + / + my own guess ---------------- +``` + +### Charlie - Recursive merkle tree + +Charlie is a concerned citizen, and wants to be sure his vote in an election is accounted for. He votes with a ZK proof, but he has no way of knowing that his ZK proof was included in the total vote count! + +If the vote collector puts all of the votes into a [Merkle tree](https://en.wikipedia.org/wiki/Merkle_tree), everyone can prove the verification of two proofs within one proof, as such: + +```md + abcd + __________|______________ + | | + ab cd + _____|_____ ______|______ + | | | | + alice bob charlie daniel +``` + +Doing this recursively allows us to arrive on a final proof `abcd` which if true, verifies the correctness of all the votes. + +### Daniel - Reusable components + +Daniel has a big circuit and a big headache. A part of his circuit is a setup phase that finishes with some assertions that need to be made. But that section alone takes most of the proving time, and is largely independent of the rest of the circuit. + +He might find it more efficient to generate a proof for that setup phase separately, and verify that proof recursively in the actual business logic section of his circuit. This will allow for parallelization of both proofs, which results in a considerable speedup. + +## What params do I need + +As you can see in the [recursion reference](noir/standard_library/recursion.md), a simple recursive proof requires: + +- The proof to verify +- The Verification Key of the circuit that generated the proof +- A hash of this verification key, as it's needed for some backends +- The public inputs for the proof +- The input aggregation object + +It also returns the `output aggregation object`. These aggregation objects can be confusing at times, so let's dive in a little bit. + +### Aggregation objects + +Recursive zkSNARK schemes do not necessarily "verify a proof" in the sense that you expect a true or false to be spit out by the verifier. Rather an aggregation object is built over the public inputs. + +In the case of PLONK the recursive aggregation object is two G1 points (expressed as 16 witness values). The final verifier (in our case this is most often the smart contract verifier) has to be aware of this aggregation object to execute a pairing and check the validity of these points. + +So, taking the example of Alice and Bob and their guessing game: + +- Alice makes her guess. Her proof is *not* recursive: it doesn't verify any proof within it! It's just a standard `assert(x != y)` circuit +- Bob verifies Alice's proof and makes his own guess. In this circuit, he is verifying a proof, so it needs to output an `aggregation object`: he is generating a recursive proof! +- Alice verifies Bob's *recursive proof*, and uses Bob's `output aggregation object` as the `input aggregation object` in her proof... Which in turn, generates another `output aggregation object`. + +One should notice that when Bob generates his first proof, he has no input aggregation object. Because he is not verifying an recursive proof, he has no `input aggregation object`. In this case, he may use zeros instead. + +We can imagine the `aggregation object` as the baton in a [relay race](https://en.wikipedia.org/wiki/Relay_race). The first runner doesn't have to receive the baton from anyone else, as he/she already starts with it. But when his/her turn is over, the next runner needs to receive it, run a bit more, and pass it along. Even though every runner could theoretically verify the baton mid-run (why not? 🏃🔍), only at the end of the race does the referee verify that the whole race is valid. + +## Some architecture + +As with everything in computer science, there's no one-size-fits all. But there are some patterns that could help understanding and implementing them. To give three examples: + +### Adding some logic to a proof verification + +This would be an approach for something like our guessing game, where proofs are sent back and forth and are verified by each opponent. This circuit would be divided in two sections: + +- A `recursive verification` section, which would be just the call to `std::verify_proof`, and that would be skipped on the first move (since there's no proof to verify) +- A `guessing` section, which is basically the logic part where the actual guessing happens + +In such a situation, and assuming Alice is first, she would skip the first part and try to guess Bob's number. Bob would then verify her proof on the first section of his run, and try to guess Alice's number on the second part, and so on. + +### Aggregating proofs + +In some one-way interaction situations, recursion would allow for aggregation of simple proofs that don't need to be immediately verified on-chain or elsewhere. + +To give a practical example, a barman wouldn't need to verify a "proof-of-age" on-chain every time he serves alcohol to a customer. Instead, the architecture would comprise two circuits: + +- A `main`, non-recursive circuit with some logic +- A `recursive` circuit meant to verify two proofs in one proof + +The customer's proofs would be intermediate, and made on their phones, and the barman could just verify them locally. He would then aggregate them into a final proof sent on-chain (or elsewhere) at the end of the day. + +### Recursively verifying different circuits + +Nothing prevents you from verifying different circuits in a recursive proof, for example: + +- A `circuit1` circuit +- A `circuit2` circuit +- A `recursive` circuit + +In this example, a regulator could verify that taxes were paid for a specific purchase by aggregating both a `payer` circuit (proving that a purchase was made and taxes were paid), and a `receipt` circuit (proving that the payment was received) + +## How fast is it + +At the time of writing, verifying recursive proofs is surprisingly fast. This is because most of the time is spent on generating the verification key that will be used to generate the next proof. So you are able to cache the verification key and reuse it later. + +Currently, Noir JS packages don't expose the functionality of loading proving and verification keys, but that feature exists in the underlying `bb.js` package. diff --git a/noir/docs/processed-docs-cache/getting_started/_category_.json b/noir/docs/processed-docs-cache/getting_started/_category_.json new file mode 100644 index 00000000000..5d694210bbf --- /dev/null +++ b/noir/docs/processed-docs-cache/getting_started/_category_.json @@ -0,0 +1,5 @@ +{ + "position": 0, + "collapsible": true, + "collapsed": true +} diff --git a/noir/docs/processed-docs-cache/getting_started/hello_noir/_category_.json b/noir/docs/processed-docs-cache/getting_started/hello_noir/_category_.json new file mode 100644 index 00000000000..23b560f610b --- /dev/null +++ b/noir/docs/processed-docs-cache/getting_started/hello_noir/_category_.json @@ -0,0 +1,5 @@ +{ + "position": 1, + "collapsible": true, + "collapsed": true +} diff --git a/noir/docs/processed-docs-cache/getting_started/hello_noir/index.md b/noir/docs/processed-docs-cache/getting_started/hello_noir/index.md new file mode 100644 index 00000000000..743c4d8d634 --- /dev/null +++ b/noir/docs/processed-docs-cache/getting_started/hello_noir/index.md @@ -0,0 +1,142 @@ +--- +title: Creating a Project +description: + Learn how to create and verify your first Noir program using Nargo, a programming language for + zero-knowledge proofs. +keywords: + [ + Nargo, + Noir, + zero-knowledge proofs, + programming language, + create Noir program, + verify Noir program, + step-by-step guide, + ] +sidebar_position: 1 + +--- + +Now that we have installed Nargo, it is time to make our first hello world program! + +## Create a Project Directory + +Noir code can live anywhere on your computer. Let us create a _projects_ folder in the home +directory to house our Noir programs. + +For Linux, macOS, and Windows PowerShell, create the directory and change directory into it by +running: + +```sh +mkdir ~/projects +cd ~/projects +``` + +## Create Our First Nargo Project + +Now that we are in the projects directory, create a new Nargo project by running: + +```sh +nargo new hello_world +``` + +> **Note:** `hello_world` can be any arbitrary project name, we are simply using `hello_world` for +> demonstration. +> +> In production, the common practice is to name the project folder as `circuits` for better +> identifiability when sitting alongside other folders in the codebase (e.g. `contracts`, `scripts`, +> `test`). + +A `hello_world` folder would be created. Similar to Rust, the folder houses _src/main.nr_ and +_Nargo.toml_ which contain the source code and environmental options of your Noir program +respectively. + +### Intro to Noir Syntax + +Let us take a closer look at _main.nr_. The default _main.nr_ generated should look like this: + +```rust +fn main(x : Field, y : pub Field) { + assert(x != y); +} +``` + +The first line of the program specifies the program's inputs: + +```rust +x : Field, y : pub Field +``` + +Program inputs in Noir are private by default (e.g. `x`), but can be labeled public using the +keyword `pub` (e.g. `y`). To learn more about private and public values, check the +[Data Types](../../noir/concepts/data_types/index.md) section. + +The next line of the program specifies its body: + +```rust +assert(x != y); +``` + +The Noir syntax `assert` can be interpreted as something similar to constraints in other zk-contract languages. + +For more Noir syntax, check the [Language Concepts](../../noir/concepts/comments.md) chapter. + +## Build In/Output Files + +Change directory into _hello_world_ and build in/output files for your Noir program by running: + +```sh +cd hello_world +nargo check +``` + +Two additional files would be generated in your project directory: + +_Prover.toml_ houses input values, and _Verifier.toml_ houses public values. + +## Prove Our Noir Program + +Now that the project is set up, we can create a proof of correct execution of our Noir program. + +Fill in input values for execution in the _Prover.toml_ file. For example: + +```toml +x = "1" +y = "2" +``` + +Prove the valid execution of your Noir program: + +```sh +nargo prove +``` + +A new folder _proofs_ would then be generated in your project directory, containing the proof file +`.proof`, where the project name is defined in Nargo.toml. + +The _Verifier.toml_ file would also be updated with the public values computed from program +execution (in this case the value of `y`): + +```toml +y = "0x0000000000000000000000000000000000000000000000000000000000000002" +``` + +> **Note:** Values in _Verifier.toml_ are computed as 32-byte hex values. + +## Verify Our Noir Program + +Once a proof is generated, we can verify correct execution of our Noir program by verifying the +proof file. + +Verify your proof by running: + +```sh +nargo verify +``` + +The verification will complete in silence if it is successful. If it fails, it will log the +corresponding error instead. + +Congratulations, you have now created and verified a proof for your very first Noir program! + +In the [next section](./project_breakdown.md), we will go into more detail on each step performed. diff --git a/noir/docs/processed-docs-cache/getting_started/hello_noir/project_breakdown.md b/noir/docs/processed-docs-cache/getting_started/hello_noir/project_breakdown.md new file mode 100644 index 00000000000..6160a102c6c --- /dev/null +++ b/noir/docs/processed-docs-cache/getting_started/hello_noir/project_breakdown.md @@ -0,0 +1,199 @@ +--- +title: Project Breakdown +description: + Learn about the anatomy of a Nargo project, including the purpose of the Prover and Verifier TOML + files, and how to prove and verify your program. +keywords: + [Nargo, Nargo project, Prover.toml, Verifier.toml, proof verification, private asset transfer] +sidebar_position: 2 +--- + +This section breaks down our hello world program from the previous section. We elaborate on the project +structure and what the `prove` and `verify` commands did. + +## Anatomy of a Nargo Project + +Upon creating a new project with `nargo new` and building the in/output files with `nargo check` +commands, you would get a minimal Nargo project of the following structure: + + - src + - Prover.toml + - Verifier.toml + - Nargo.toml + +The source directory _src_ holds the source code for your Noir program. By default only a _main.nr_ +file will be generated within it. + +### Prover.toml + +_Prover.toml_ is used for specifying the input values for executing and proving the program. You can specify `toml` files with different names by using the `--prover-name` or `-p` flags, see the [Prover](#provertoml) section below. Optionally you may specify expected output values for prove-time checking as well. + +### Verifier.toml + +_Verifier.toml_ contains public in/output values computed when executing the Noir program. + +### Nargo.toml + +_Nargo.toml_ contains the environmental options of your project. It contains a "package" section and a "dependencies" section. + +Example Nargo.toml: + +```toml +[package] +name = "noir_starter" +type = "bin" +authors = ["Alice"] +compiler_version = "0.9.0" +description = "Getting started with Noir" +entry = "circuit/main.nr" +license = "MIT" + +[dependencies] +ecrecover = {tag = "v0.9.0", git = "https://github.com/colinnielsen/ecrecover-noir.git"} +``` + +Nargo.toml for a [workspace](../../noir/modules_packages_crates/workspaces.md) will look a bit different. For example: + +```toml +[workspace] +members = ["crates/a", "crates/b"] +default-member = "crates/a" +``` + +#### Package section + +The package section defines a number of fields including: + +- `name` (**required**) - the name of the package +- `type` (**required**) - can be "bin", "lib", or "contract" to specify whether its a binary, library or Aztec contract +- `authors` (optional) - authors of the project +- `compiler_version` - specifies the version of the compiler to use. This is enforced by the compiler and follow's [Rust's versioning](https://doc.rust-lang.org/cargo/reference/manifest.html#the-version-field), so a `compiler_version = 0.18.0` will enforce Nargo version 0.18.0, `compiler_version = ^0.18.0` will enforce anything above 0.18.0 but below 0.19.0, etc. For more information, see how [Rust handles these operators](https://docs.rs/semver/latest/semver/enum.Op.html) +- `description` (optional) +- `entry` (optional) - a relative filepath to use as the entry point into your package (overrides the default of `src/lib.nr` or `src/main.nr`) +- `backend` (optional) +- `license` (optional) + +#### Dependencies section + +This is where you will specify any dependencies for your project. See the [Dependencies page](../../noir/modules_packages_crates/dependencies.md) for more info. + +`./proofs/` and `./contract/` directories will not be immediately visible until you create a proof or +verifier contract respectively. + +### main.nr + +The _main.nr_ file contains a `main` method, this method is the entry point into your Noir program. + +In our sample program, _main.nr_ looks like this: + +```rust +fn main(x : Field, y : Field) { + assert(x != y); +} +``` + +The parameters `x` and `y` can be seen as the API for the program and must be supplied by the +prover. Since neither `x` nor `y` is marked as public, the verifier does not supply any inputs, when +verifying the proof. + +The prover supplies the values for `x` and `y` in the _Prover.toml_ file. + +As for the program body, `assert` ensures that the condition to be satisfied (e.g. `x != y`) is +constrained by the proof of the execution of said program (i.e. if the condition was not met, the +verifier would reject the proof as an invalid proof). + +### Prover.toml + +The _Prover.toml_ file is a file which the prover uses to supply his witness values(both private and +public). + +In our hello world program the _Prover.toml_ file looks like this: + +```toml +x = "1" +y = "2" +``` + +When the command `nargo prove` is executed, two processes happen: + +1. Noir creates a proof that `x`, which holds the value of `1`, and `y`, which holds the value of `2`, + is not equal. This inequality constraint is due to the line `assert(x != y)`. + +2. Noir creates and stores the proof of this statement in the _proofs_ directory in a file called your-project.proof. So if your project is named "private_voting" (defined in the project Nargo.toml), the proof will be saved at `./proofs/private_voting.proof`. Opening this file will display the proof in hex format. + +#### Arrays of Structs + +The following code shows how to pass an array of structs to a Noir program to generate a proof. + +```rust +// main.nr +struct Foo { + bar: Field, + baz: Field, +} + +fn main(foos: [Foo; 3]) -> pub Field { + foos[2].bar + foos[2].baz +} +``` + +Prover.toml: + +```toml +[[foos]] # foos[0] +bar = 0 +baz = 0 + +[[foos]] # foos[1] +bar = 0 +baz = 0 + +[[foos]] # foos[2] +bar = 1 +baz = 2 +``` + +#### Custom toml files + +You can specify a `toml` file with a different name to use for proving by using the `--prover-name` or `-p` flags. + +This command looks for proof inputs in the default **Prover.toml** and generates the proof and saves it at `./proofs/.proof`: + +```bash +nargo prove +``` + +This command looks for proof inputs in the custom **OtherProver.toml** and generates proof and saves it at `./proofs/.proof`: + +```bash +nargo prove -p OtherProver +``` + +## Verifying a Proof + +When the command `nargo verify` is executed, two processes happen: + +1. Noir checks in the _proofs_ directory for a proof file with the project name (eg. test_project.proof) + +2. If that file is found, the proof's validity is checked + +> **Note:** The validity of the proof is linked to the current Noir program; if the program is +> changed and the verifier verifies the proof, it will fail because the proof is not valid for the +> _modified_ Noir program. + +In production, the prover and the verifier are usually two separate entities. A prover would +retrieve the necessary inputs, execute the Noir program, generate a proof and pass it to the +verifier. The verifier would then retrieve the public inputs, usually from external sources, and +verify the validity of the proof against it. + +Take a private asset transfer as an example: + +A person using a browser as the prover would retrieve private inputs locally (e.g. the user's private key) and +public inputs (e.g. the user's encrypted balance on-chain), compute the transfer, generate a proof +and submit it to the verifier smart contract. + +The verifier contract would then draw the user's encrypted balance directly from the blockchain and +verify the proof submitted against it. If the verification passes, additional functions in the +verifier contract could trigger (e.g. approve the asset transfer). + +Now that you understand the concepts, you'll probably want some editor feedback while you are writing more complex code. diff --git a/noir/docs/processed-docs-cache/getting_started/installation/_category_.json b/noir/docs/processed-docs-cache/getting_started/installation/_category_.json new file mode 100644 index 00000000000..0c02fb5d4d7 --- /dev/null +++ b/noir/docs/processed-docs-cache/getting_started/installation/_category_.json @@ -0,0 +1,6 @@ +{ + "position": 0, + "label": "Install Nargo", + "collapsible": true, + "collapsed": true +} diff --git a/noir/docs/processed-docs-cache/getting_started/installation/index.md b/noir/docs/processed-docs-cache/getting_started/installation/index.md new file mode 100644 index 00000000000..4ef86aa5914 --- /dev/null +++ b/noir/docs/processed-docs-cache/getting_started/installation/index.md @@ -0,0 +1,48 @@ +--- +title: Nargo Installation +description: + nargo is a command line tool for interacting with Noir programs. This page is a quick guide on how to install Nargo through the most common and easy method, noirup +keywords: [ + Nargo + Noir + Rust + Cargo + Noirup + Installation + Terminal Commands + Version Check + Nightlies + Specific Versions + Branches + Noirup Repository +] +pagination_next: getting_started/hello_noir/index +--- + +`nargo` is the one-stop-shop for almost everything related with Noir. The name comes from our love for Rust and its package manager `cargo`. + +With `nargo`, you can start new projects, compile, execute, prove, verify, test, generate solidity contracts, and do pretty much all that is available in Noir. + +Similarly to `rustup`, we also maintain an easy installation method that covers most machines: `noirup`. + +## Installing Noirup + +Open a terminal on your machine, and write: + +```bash +curl -L https://raw.githubusercontent.com/noir-lang/noirup/main/install | bash +``` + +Close the terminal, open another one, and run + +```bash +noirup +``` + +Done. That's it. You should have the latest version working. You can check with `nargo --version`. + +You can also install nightlies, specific versions +or branches. Check out the [noirup repository](https://github.com/noir-lang/noirup) for more +information. + +Now we're ready to start working on [our first Noir program!](../hello_noir/index.md) diff --git a/noir/docs/processed-docs-cache/getting_started/installation/other_install_methods.md b/noir/docs/processed-docs-cache/getting_started/installation/other_install_methods.md new file mode 100644 index 00000000000..a532f83750e --- /dev/null +++ b/noir/docs/processed-docs-cache/getting_started/installation/other_install_methods.md @@ -0,0 +1,190 @@ +--- +title: Alternative Install Methods +description: + There are different ways to install Nargo, the one-stop shop and command-line tool for developing Noir programs. This guide explains other methods that don't rely on noirup, such as compiling from source, installing from binaries, and using WSL for windows +keywords: [ + Installation + Nargo + Noirup + Binaries + Compiling from Source + WSL for Windows + macOS + Linux + Nix + Direnv + Shell & editor experience + Building and testing + Uninstalling Nargo + Noir vs code extension +] +sidebar_position: 1 +--- + + +## Installation + +The most common method of installing Nargo is through [Noirup](./index.md) + +However, there are other methods for installing Nargo: + +- [Binaries](#binaries) +- [Compiling from Source](#compile-from-source) +- [WSL for Windows](#wsl-for-windows) + +### Binaries + +See [GitHub Releases](https://github.com/noir-lang/noir/releases) for the latest and previous +platform specific binaries. + +#### Step 1 + +Paste and run the following in the terminal to extract and install the binary: + +> **macOS / Linux:** If you are prompted with `Permission denied` when running commands, prepend +> `sudo` and re-run it. + +##### macOS (Apple Silicon) + +```bash +mkdir -p $HOME/.nargo/bin && \ +curl -o $HOME/.nargo/bin/nargo-aarch64-apple-darwin.tar.gz -L https://github.com/noir-lang/noir/releases/download/v0.6.0/nargo-aarch64-apple-darwin.tar.gz && \ +tar -xvf $HOME/.nargo/bin/nargo-aarch64-apple-darwin.tar.gz -C $HOME/.nargo/bin/ && \ +echo '\nexport PATH=$PATH:$HOME/.nargo/bin' >> ~/.zshrc && \ +source ~/.zshrc +``` + +##### macOS (Intel) + +```bash +mkdir -p $HOME/.nargo/bin && \ +curl -o $HOME/.nargo/bin/nargo-x86_64-apple-darwin.tar.gz -L https://github.com/noir-lang/noir/releases/download/v0.6.0/nargo-x86_64-apple-darwin.tar.gz && \ +tar -xvf $HOME/.nargo/bin/nargo-x86_64-apple-darwin.tar.gz -C $HOME/.nargo/bin/ && \ +echo '\nexport PATH=$PATH:$HOME/.nargo/bin' >> ~/.zshrc && \ +source ~/.zshrc +``` + +##### Linux (Bash) + +```bash +mkdir -p $HOME/.nargo/bin && \ +curl -o $HOME/.nargo/bin/nargo-x86_64-unknown-linux-gnu.tar.gz -L https://github.com/noir-lang/noir/releases/download/v0.6.0/nargo-x86_64-unknown-linux-gnu.tar.gz && \ +tar -xvf $HOME/.nargo/bin/nargo-x86_64-unknown-linux-gnu.tar.gz -C $HOME/.nargo/bin/ && \ +echo -e '\nexport PATH=$PATH:$HOME/.nargo/bin' >> ~/.bashrc && \ +source ~/.bashrc +``` + +#### Step 2 + +Check if the installation was successful by running `nargo --version`. You should get a version number. + +> **macOS:** If you are prompted with an OS alert, right-click and open the _nargo_ executable from +> Finder. Close the new terminal popped up and `nargo` should now be accessible. + +### Option 3: Compile from Source + +Due to the large number of native dependencies, Noir projects uses [Nix](https://nixos.org/) and [direnv](https://direnv.net/) to streamline the development experience. It helps mitigating issues commonly associated with dependency management, such as conflicts between required package versions for different projects (often referred to as "dependency hell"). + +Combined with direnv, which automatically sets or clears environment variables based on the directory, it further simplifies the development process by seamlessly integrating with the developer's shell, facilitating an efficient and reliable workflow for managing and deploying Noir projects with multiple dependencies. + +#### Setting up your environment + +For the best experience, please follow these instructions to setup your environment: + +1. Install Nix following [their guide](https://nixos.org/download.html) for your operating system. +2. Create the file `~/.config/nix/nix.conf` with the contents: + +```ini +experimental-features = nix-command +extra-experimental-features = flakes +``` + +3. Install direnv into your Nix profile by running: + +```sh +nix profile install nixpkgs#direnv +``` + +4. Add direnv to your shell following [their guide](https://direnv.net/docs/hook.html). + 1. For bash or zshell, add `eval "$(direnv hook bash)"` or `eval "$(direnv hook zsh)"` to your ~/.bashrc or ~/.zshrc file, respectively. +5. Restart your shell. + +#### Shell & editor experience + +Now that your environment is set up, you can get to work on the project. + +1. Clone the repository, such as: + +```sh +git clone git@github.com:noir-lang/noir +``` + +> Replacing `noir` with whichever repository you want to work on. + +2. Navigate to the directory: + +```sh +cd noir +``` + +> Replacing `noir` with whichever repository you cloned. + +3. You should see a **direnv error** because projects aren't allowed by default. Make sure you've reviewed and trust our `.envrc` file, then you need to run: + +```sh +direnv allow +``` + +4. Now, wait awhile for all the native dependencies to be built. This will take some time and direnv will warn you that it is taking a long time, but we just need to let it run. + +5. Once you are presented with your prompt again, you can start your editor within the project directory (we recommend [VSCode](https://code.visualstudio.com/)): + +```sh +code . +``` + +6. (Recommended) When launching VSCode for the first time, you should be prompted to install our recommended plugins. We highly recommend installing these for the best development experience. + +#### Building and testing + +Assuming you are using `direnv` to populate your environment, building and testing the project can be done +with the typical `cargo build`, `cargo test`, and `cargo clippy` commands. You'll notice that the `cargo` version matches the version we specify in `rust-toolchain.toml`, which is 1.71.1 at the time of this writing. + +If you want to build the entire project in an isolated sandbox, you can use Nix commands: + +1. `nix build .` (or `nix build . -L` for verbose output) to build the project in a Nix sandbox. +2. `nix flake check` (or `nix flake check -L` for verbose output) to run clippy and tests in a Nix sandbox. + +#### Without `direnv` + +If you have hesitations with using direnv, you can launch a subshell with `nix develop` and then launch your editor from within the subshell. However, if VSCode was already launched in the project directory, the environment won't be updated. + +Advanced: If you aren't using direnv nor launching your editor within the subshell, you can try to install Barretenberg and other global dependencies the package needs. This is an advanced workflow and likely won't receive support! + +### Option 4: WSL (for Windows) + +The default backend for Noir (Barretenberg) doesn't provide Windows binaries at this time. For that reason, Noir cannot be installed natively. However, it is available by using Windows Subsystem for Linux (WSL). + +Step 1: Follow the instructions [here](https://learn.microsoft.com/en-us/windows/wsl/install) to install and run WSL. + +step 2: Follow the [Noirup instructions](./index.md). + +## Uninstalling Nargo + +### Noirup + +If you installed Noir with `noirup`, you can uninstall Noir by removing the files in `~/.nargo`, `~/nargo` and `~/noir_cache`. + +```bash +rm -r ~/.nargo +rm -r ~/nargo +rm -r ~/noir_cache +``` + +### Nix + +If you installed Noir with Nix or from source, you can remove the binary located at `~/.nix-profile/bin/nargo`. + +```bash +rm ~/.nix-profile/bin/nargo +``` diff --git a/noir/docs/processed-docs-cache/getting_started/tooling/_category_.json b/noir/docs/processed-docs-cache/getting_started/tooling/_category_.json new file mode 100644 index 00000000000..55804c03a71 --- /dev/null +++ b/noir/docs/processed-docs-cache/getting_started/tooling/_category_.json @@ -0,0 +1,6 @@ +{ + "position": 2, + "label": "Tooling", + "collapsible": true, + "collapsed": true +} diff --git a/noir/docs/processed-docs-cache/getting_started/tooling/index.mdx b/noir/docs/processed-docs-cache/getting_started/tooling/index.mdx new file mode 100644 index 00000000000..ac480f3c9f5 --- /dev/null +++ b/noir/docs/processed-docs-cache/getting_started/tooling/index.mdx @@ -0,0 +1,38 @@ +--- +title: Tooling +Description: This section provides information about the various tools and utilities available for Noir development. It covers the Noir playground, IDE tools, Codespaces, and community projects. +Keywords: [Noir, Development, Playground, IDE Tools, Language Service Provider, VS Code Extension, Codespaces, noir-starter, Community Projects, Awesome Noir Repository, Developer Tooling] +--- + +Noir is meant to be easy to develop with. For that reason, a number of utilities have been put together to ease the development process as much as feasible in the zero-knowledge world. + +## Playground + +The Noir playground is an easy way to test small ideas, share snippets, and integrate in other websites. You can access it at [play.noir-lang.org](https://play.noir-lang.org). + +## IDE tools + +When you install Nargo, you're also installing a Language Service Provider (LSP), which can be used by IDEs to provide syntax highlighting, codelens, warnings, and more. + +The easiest way to use these tools is by installing the [Noir VS Code extension](https://marketplace.visualstudio.com/items?itemName=noir-lang.vscode-noir). + +## Codespaces + +Some Noir repos have leveraged Codespaces in order to ease the development process. You can visit the [noir-starter](https://github.com/noir-lang/noir-starter) for an example. + + + +## GitHub Actions + +You can use `noirup` with GitHub Actions for CI/CD and automated testing. It is as simple as +installing `noirup` and running tests in your GitHub Action `yml` file. + +See the +[config file in the Noir repo](https://github.com/TomAFrench/noir-hashes/blob/master/.github/workflows/noir.yml) for an example usage. + +## Community projects + +As an open-source project, Noir has received many contributions over time. Some of them are related with developer tooling, and you can see some of them in [Awesome Noir repository](https://github.com/noir-lang/awesome-noir#dev-tools) diff --git a/noir/docs/processed-docs-cache/getting_started/tooling/language_server.md b/noir/docs/processed-docs-cache/getting_started/tooling/language_server.md new file mode 100644 index 00000000000..81e0356ef8a --- /dev/null +++ b/noir/docs/processed-docs-cache/getting_started/tooling/language_server.md @@ -0,0 +1,43 @@ +--- +title: Language Server +description: Learn about the Noir Language Server, how to install the components, and configuration that may be required. +keywords: [Nargo, Language Server, LSP, VSCode, Visual Studio Code] +sidebar_position: 0 +--- + +This section helps you install and configure the Noir Language Server. + +The Language Server Protocol (LSP) has two components, the [Server](#language-server) and the [Client](#language-client). Below we describe each in the context of Noir. + +## Language Server + +The Server component is provided by the Nargo command line tool that you installed at the beginning of this guide. +As long as Nargo is installed and you've used it to run other commands in this guide, it should be good to go! + +If you'd like to verify that the `nargo lsp` command is available, you can run `nargo --help` and look for `lsp` in the list of commands. If you see it, you're using a version of Noir with LSP support. + +## Language Client + +The Client component is usually an editor plugin that launches the Server. It communicates LSP messages between the editor and the Server. For example, when you save a file, the Client will alert the Server, so it can try to compile the project and report any errors. + +Currently, Noir provides a Language Client for Visual Studio Code via the [vscode-noir](https://github.com/noir-lang/vscode-noir) extension. You can install it via the [Visual Studio Marketplace](https://marketplace.visualstudio.com/items?itemName=noir-lang.vscode-noir). + +> **Note:** Noir's Language Server Protocol support currently assumes users' VSCode workspace root to be the same as users' Noir project root (i.e. where Nargo.toml lies). +> +> If LSP features seem to be missing / malfunctioning, make sure you are opening your Noir project directly (instead of as a sub-folder) in your VSCode instance. + +When your language server is running correctly and the VSCode plugin is installed, you should see handy codelens buttons for compilation, measuring circuit size, execution, and tests: + +![Compile and Execute](@site/static/img/codelens_compile_execute.png) +![Run test](@site/static/img/codelens_run_test.png) + +You should also see your tests in the `testing` panel: + +![Testing panel](@site/static/img/codelens_testing_panel.png) + +### Configuration + +- **Noir: Enable LSP** - If checked, the extension will launch the Language Server via `nargo lsp` and communicate with it. +- **Noir: Nargo Flags** - Additional flags may be specified if you require them to be added when the extension calls `nargo lsp`. +- **Noir: Nargo Path** - An absolute path to a Nargo binary with the `lsp` command. This may be useful if Nargo is not within the `PATH` of your editor. +- **Noir > Trace: Server** - Setting this to `"messages"` or `"verbose"` will log LSP messages between the Client and Server. Useful for debugging. diff --git a/noir/docs/processed-docs-cache/getting_started/tooling/testing.md b/noir/docs/processed-docs-cache/getting_started/tooling/testing.md new file mode 100644 index 00000000000..d3e0c522473 --- /dev/null +++ b/noir/docs/processed-docs-cache/getting_started/tooling/testing.md @@ -0,0 +1,62 @@ +--- +title: Testing in Noir +description: Learn how to use Nargo to test your Noir program in a quick and easy way +keywords: [Nargo, testing, Noir, compile, test] +sidebar_position: 1 +--- + +You can test your Noir programs using Noir circuits. + +Nargo will automatically compile and run any functions which have the decorator `#[test]` on them if +you run `nargo test`. + +For example if you have a program like: + +```rust +fn add(x: u64, y: u64) -> u64 { + x + y +} +#[test] +fn test_add() { + assert(add(2,2) == 4); + assert(add(0,1) == 1); + assert(add(1,0) == 1); +} +``` + +Running `nargo test` will test that the `test_add` function can be executed while satisfying all +the constraints which allows you to test that add returns the expected values. Test functions can't +have any arguments currently. + +### Test fail + +You can write tests that are expected to fail by using the decorator `#[test(should_fail)]`. For example: + +```rust +fn add(x: u64, y: u64) -> u64 { + x + y +} +#[test(should_fail)] +fn test_add() { + assert(add(2,2) == 5); +} +``` + +You can be more specific and make it fail with a specific reason by using `should_fail_with = "`: + +```rust +fn main(african_swallow_avg_speed : Field) { + assert(african_swallow_avg_speed == 65, "What is the airspeed velocity of an unladen swallow"); +} + +#[test] +fn test_king_arthur() { + main(65); +} + +#[test(should_fail_with = "What is the airspeed velocity of an unladen swallow")] +fn test_bridgekeeper() { + main(32); +} + +``` diff --git a/noir/docs/processed-docs-cache/how_to/_category_.json b/noir/docs/processed-docs-cache/how_to/_category_.json new file mode 100644 index 00000000000..23b560f610b --- /dev/null +++ b/noir/docs/processed-docs-cache/how_to/_category_.json @@ -0,0 +1,5 @@ +{ + "position": 1, + "collapsible": true, + "collapsed": true +} diff --git a/noir/docs/processed-docs-cache/how_to/how-to-oracles.md b/noir/docs/processed-docs-cache/how_to/how-to-oracles.md new file mode 100644 index 00000000000..0d84d992320 --- /dev/null +++ b/noir/docs/processed-docs-cache/how_to/how-to-oracles.md @@ -0,0 +1,280 @@ +--- +title: How to use Oracles +description: Learn how to use oracles in your Noir program with examples in both Nargo and NoirJS. This guide also covers writing a JSON RPC server and providing custom foreign call handlers for NoirJS. +keywords: + - Noir Programming + - Oracles + - Nargo + - NoirJS + - JSON RPC Server + - Foreign Call Handlers +sidebar_position: 1 +--- + +This guide shows you how to use oracles in your Noir program. For the sake of clarity, it assumes that: + +- You have read the [explainer on Oracles](../explainers/explainer-oracle.md) and are comfortable with the concept. +- You have a Noir program to add oracles to. You can create one using the [vite-hardhat starter](https://github.com/noir-lang/noir-starter/tree/main/vite-hardhat) as a boilerplate. +- You understand the concept of a JSON-RPC server. Visit the [JSON-RPC website](https://www.jsonrpc.org/) if you need a refresher. +- You are comfortable with server-side JavaScript (e.g. Node.js, managing packages, etc.). + +For reference, you can find the snippets used in this tutorial on the [Aztec DevRel Repository](https://github.com/AztecProtocol/dev-rel/tree/main/code-snippets/how-to-oracles). + +## Rundown + +This guide has 3 major steps: + +1. How to modify our Noir program to make use of oracle calls as unconstrained functions +2. How to write a JSON RPC Server to resolve these oracle calls with Nargo +3. How to use them in Nargo and how to provide a custom resolver in NoirJS + +## Step 1 - Modify your Noir program + +An oracle is defined in a Noir program by defining two methods: + +- An unconstrained method - This tells the compiler that it is executing an [unconstrained functions](../noir/concepts//unconstrained.md). +- A decorated oracle method - This tells the compiler that this method is an RPC call. + +An example of an oracle that returns a `Field` would be: + +```rust +#[oracle(getSqrt)] +unconstrained fn sqrt(number: Field) -> Field { } + +unconstrained fn get_sqrt(number: Field) -> Field { + sqrt(number) +} +``` + +In this example, we're wrapping our oracle function in a unconstrained method, and decorating it with `oracle(getSqrt)`. We can then call the unconstrained function as we would call any other function: + +```rust +fn main(input: Field) { + let sqrt = get_sqrt(input); +} +``` + +In the next section, we will make this `getSqrt` (defined on the `sqrt` decorator) be a method of the RPC server Noir will use. + +:::danger + +As explained in the [Oracle Explainer](../explainers/explainer-oracle.md), this `main` function is unsafe unless you constrain its return value. For example: + +```rust +fn main(input: Field) { + let sqrt = get_sqrt(input); + assert(sqrt.pow_32(2) as u64 == input as u64); // <---- constrain the return of an oracle! +} +``` + +::: + +:::info + +Currently, oracles only work with single params or array params. For example: + +```rust +#[oracle(getSqrt)] +unconstrained fn sqrt([Field; 2]) -> [Field; 2] { } +``` + +::: + +## Step 2 - Write an RPC server + +Brillig will call *one* RPC server. Most likely you will have to write your own, and you can do it in whatever language you prefer. In this guide, we will do it in Javascript. + +Let's use the above example of an oracle that consumes an array with two `Field` and returns their square roots: + +```rust +#[oracle(getSqrt)] +unconstrained fn sqrt(input: [Field; 2]) -> [Field; 2] { } + +unconstrained fn get_sqrt(input: [Field; 2]) -> [Field; 2] { + sqrt(input) +} + +fn main(input: [Field; 2]) { + let sqrt = get_sqrt(input); + assert(sqrt[0].pow_32(2) as u64 == input[0] as u64); + assert(sqrt[1].pow_32(2) as u64 == input[1] as u64); +} +``` + +:::info + +Why square root? + +In general, computing square roots is computationally more expensive than multiplications, which takes a toll when speaking about ZK applications. In this case, instead of calculating the square root in Noir, we are using our oracle to offload that computation to be made in plain. In our circuit we can simply multiply the two values. + +::: + +Now, we should write the correspondent RPC server, starting with the [default JSON-RPC 2.0 boilerplate](https://www.npmjs.com/package/json-rpc-2.0#example): + +```js +import { JSONRPCServer } from "json-rpc-2.0"; +import express from "express"; +import bodyParser from "body-parser"; + +const app = express(); +app.use(bodyParser.json()); + +const server = new JSONRPCServer(); +app.post("/", (req, res) => { + const jsonRPCRequest = req.body; + server.receive(jsonRPCRequest).then((jsonRPCResponse) => { + if (jsonRPCResponse) { + res.json(jsonRPCResponse); + } else { + res.sendStatus(204); + } + }); +}); + +app.listen(5555); +``` + +Now, we will add our `getSqrt` method, as expected by the `#[oracle(getSqrt)]` decorator in our Noir code. It maps through the params array and returns their square roots: + +```js +server.addMethod("getSqrt", async (params) => { + const values = params[0].Array.map(({ inner }) => { + return { inner: `${Math.sqrt(parseInt(inner, 16))}` }; + }); + return { values: [{ Array: values }] }; +}); +``` + +:::tip + +Brillig expects an object with an array of values. Each value is an object declaring to be `Single` or `Array` and returning a `inner` property *as a string*. For example: + +```json +{ "values": [{ "Array": [{ "inner": "1" }, { "inner": "2"}]}]} +{ "values": [{ "Single": { "inner": "1" }}]} +{ "values": [{ "Single": { "inner": "1" }}, { "Array": [{ "inner": "1", { "inner": "2" }}]}]} +``` + +If you're using Typescript, the following types may be helpful in understanding the expected return value and making sure they're easy to follow: + +```js +interface Value { + inner: string, +} + +interface SingleForeignCallParam { + Single: Value, +} + +interface ArrayForeignCallParam { + Array: Value[], +} + +type ForeignCallParam = SingleForeignCallParam | ArrayForeignCallParam; + +interface ForeignCallResult { + values: ForeignCallParam[], +} +``` + +::: + +## Step 3 - Usage with Nargo + +Using the [`nargo` CLI tool](../getting_started/installation/index.md), you can use oracles in the `nargo test`, `nargo execute` and `nargo prove` commands by passing a value to `--oracle-resolver`. For example: + +```bash +nargo test --oracle-resolver http://localhost:5555 +``` + +This tells `nargo` to use your RPC Server URL whenever it finds an oracle decorator. + +## Step 4 - Usage with NoirJS + +In a JS environment, an RPC server is not strictly necessary, as you may want to resolve your oracles without needing any JSON call at all. NoirJS simply expects that you pass a callback function when you generate proofs, and that callback function can be anything. + +For example, if your Noir program expects the host machine to provide CPU pseudo-randomness, you could simply pass it as the `foreignCallHandler`. You don't strictly need to create an RPC server to serve pseudo-randomness, as you may as well get it directly in your app: + +```js +const foreignCallHandler = (name, inputs) => crypto.randomBytes(16) // etc + +await noir.generateFinalProof(inputs, foreignCallHandler) +``` + +As one can see, in NoirJS, the [`foreignCallHandler`](../reference/NoirJS/noir_js/type-aliases/ForeignCallHandler.md) function simply means "a callback function that returns a value of type [`ForeignCallOutput`](../reference/NoirJS/noir_js/type-aliases/ForeignCallOutput.md). It doesn't have to be an RPC call like in the case for Nargo. + +:::tip + +Does this mean you don't have to write an RPC server like in [Step #2](#step-2---write-an-rpc-server)? + +You don't technically have to, but then how would you run `nargo test` or `nargo prove`? To use both `Nargo` and `NoirJS` in your development flow, you will have to write a JSON RPC server. + +::: + +In this case, let's make `foreignCallHandler` call the JSON RPC Server we created in [Step #2](#step-2---write-an-rpc-server), by making it a JSON RPC Client. + +For example, using the same `getSqrt` program in [Step #1](#step-1---modify-your-noir-program) (comments in the code): + +```js +import { JSONRPCClient } from "json-rpc-2.0"; + +// declaring the JSONRPCClient +const client = new JSONRPCClient((jsonRPCRequest) => { +// hitting the same JSON RPC Server we coded above + return fetch("http://localhost:5555", { + method: "POST", + headers: { + "content-type": "application/json", + }, + body: JSON.stringify(jsonRPCRequest), + }).then((response) => { + if (response.status === 200) { + return response + .json() + .then((jsonRPCResponse) => client.receive(jsonRPCResponse)); + } else if (jsonRPCRequest.id !== undefined) { + return Promise.reject(new Error(response.statusText)); + } + }); +}); + +// declaring a function that takes the name of the foreign call (getSqrt) and the inputs +const foreignCallHandler = async (name, input) => { + // notice that the "inputs" parameter contains *all* the inputs + // in this case we to make the RPC request with the first parameter "numbers", which would be input[0] + const oracleReturn = await client.request(name, [ + { Array: input[0].map((i) => ({ inner: i.toString("hex") })) }, + ]); + return [oracleReturn.values[0].Array.map((x) => x.inner)]; +}; + +// the rest of your NoirJS code +const input = { input: [4, 16] }; +const { witness } = await noir.execute(numbers, foreignCallHandler); +``` + +:::tip + +If you're in a NoirJS environment running your RPC server together with a frontend app, you'll probably hit a familiar problem in full-stack development: requests being blocked by [CORS](https://developer.mozilla.org/en-US/docs/Web/HTTP/CORS) policy. For development only, you can simply install and use the [`cors` npm package](https://www.npmjs.com/package/cors) to get around the problem: + +```bash +yarn add cors +``` + +and use it as a middleware: + +```js +import cors from "cors"; + +const app = express(); +app.use(cors()) +``` + +::: + +## Conclusion + +Hopefully by the end of this guide, you should be able to: + +- Write your own logic around Oracles and how to write a JSON RPC server to make them work with your Nargo commands. +- Provide custom foreign call handlers for NoirJS. diff --git a/noir/docs/processed-docs-cache/how_to/how-to-recursion.md b/noir/docs/processed-docs-cache/how_to/how-to-recursion.md new file mode 100644 index 00000000000..39db23f1f3a --- /dev/null +++ b/noir/docs/processed-docs-cache/how_to/how-to-recursion.md @@ -0,0 +1,184 @@ +--- +title: How to use recursion on NoirJS +description: Learn how to implement recursion with NoirJS, a powerful tool for creating smart contracts on the EVM blockchain. This guide assumes familiarity with NoirJS, solidity verifiers, and the Barretenberg proving backend. Discover how to generate both final and intermediate proofs using `noir_js` and `backend_barretenberg`. +keywords: + [ + "NoirJS", + "EVM blockchain", + "smart contracts", + "recursion", + "solidity verifiers", + "Barretenberg backend", + "noir_js", + "backend_barretenberg", + "intermediate proofs", + "final proofs", + "nargo compile", + "json import", + "recursive circuit", + "recursive app" + ] +sidebar_position: 1 +--- + +This guide shows you how to use recursive proofs in your NoirJS app. For the sake of clarity, it is assumed that: + +- You already have a NoirJS app. If you don't, please visit the [NoirJS tutorial](../tutorials/noirjs_app.md) and the [reference](../reference/NoirJS/noir_js/index.md). +- You are familiar with what are recursive proofs and you have read the [recursion explainer](../explainers/explainer-recursion.md) +- You already built a recursive circuit following [the reference](../noir/standard_library/recursion.md), and understand how it works. + +It is also assumed that you're not using `noir_wasm` for compilation, and instead you've used [`nargo compile`](../reference/nargo_commands.md) to generate the `json` you're now importing into your project. However, the guide should work just the same if you're using `noir_wasm`. + +:::info + +As you've read in the [explainer](../explainers/explainer-recursion.md), a recursive proof is an intermediate proof. This means that it doesn't necessarily generate the final step that makes it verifiable in a smart contract. However, it is easy to verify within another circuit. + +While "standard" usage of NoirJS packages abstracts final proofs, it currently lacks the necessary interface to abstract away intermediate proofs. This means that these proofs need to be created by using the backend directly. + +In short: + +- `noir_js` generates *only* final proofs +- `backend_barretenberg` generates both types of proofs + +::: + +In a standard recursive app, you're also dealing with at least two circuits. For the purpose of this guide, we will assume these two: + +- `main`: a circuit of type `assert(x != y)` +- `recursive`: a circuit that verifies `main` + +For a full example on how recursive proofs work, please refer to the [noir-examples](https://github.com/noir-lang/noir-examples) repository. We will *not* be using it as a reference for this guide. + +## Step 1: Setup + +In a common NoirJS app, you need to instantiate a backend with something like `const backend = new Backend(circuit)`. Then you feed it to the `noir_js` interface. + +For recursion, this doesn't happen, and the only need for `noir_js` is only to `execute` a circuit and get its witness and return value. Everything else is not interfaced, so it needs to happen on the `backend` object. + +It is also recommended that you instantiate the backend with as many threads as possible, to allow for maximum concurrency: + +```js +const backend = new Backend(circuit, { threads: 8 }) +``` + +:::tip +You can use the [`os.cpus()`](https://nodejs.org/api/os.html#oscpus) object in `nodejs` or [`navigator.hardwareConcurrency`](https://developer.mozilla.org/en-US/docs/Web/API/Navigator/hardwareConcurrency) on the browser to make the most out of those glorious cpu cores +::: + +## Step 2: Generating the witness and the proof for `main` + +After instantiating the backend, you should also instantiate `noir_js`. We will use it to execute the circuit and get the witness. + +```js +const noir = new Noir(circuit, backend) +const { witness } = noir.execute(input) +``` + +With this witness, you are now able to generate the intermediate proof for the main circuit: + +```js +const { proof, publicInputs } = await backend.generateIntermediateProof(witness) +``` + +:::warning + +Always keep in mind what is actually happening on your development process, otherwise you'll quickly become confused about what circuit we are actually running and why! + +In this case, you can imagine that Alice (running the `main` circuit) is proving something to Bob (running the `recursive` circuit), and Bob is verifying her proof within his proof. + +With this in mind, it becomes clear that our intermediate proof is the one *meant to be verified within another circuit*, so it must be Alice's. Actually, the only final proof in this theoretical scenario would be the last one, sent on-chain. + +::: + +## Step 3 - Verification and proof artifacts + +Optionally, you are able to verify the intermediate proof: + +```js +const verified = await backend.verifyIntermediateProof({ proof, publicInputs }) +``` + +This can be useful to make sure our intermediate proof was correctly generated. But the real goal is to do it within another circuit. For that, we need to generate the intermediate artifacts: + +```js +const { proofAsFields, vkAsFields, vkHash } = await backend.generateIntermediateProofArtifacts( { publicInputs, proof }, publicInputsCount) +``` + +This call takes the public inputs and the proof, but also the public inputs count. While this is easily retrievable by simply counting the `publicInputs` length, the backend interface doesn't currently abstract it away. + +:::info + +The `proofAsFields` has a constant size `[Field; 93]`. However, currently the backend doesn't remove the public inputs from the proof when converting it. + +This means that if your `main` circuit has two public inputs, then you should also modify the recursive circuit to accept a proof with the public inputs appended. This means that in our example, since `y` is a public input, our `proofAsFields` is of type `[Field; 94]`. + +Verification keys in Barretenberg are always of size 114. + +::: + +:::warning + +One common mistake is to forget *who* makes this call. + +In a situation where Alice is generating the `main` proof, if she generates the proof artifacts and sends them to Bob, which gladly takes them as true, this would mean Alice could prove anything! + +Instead, Bob needs to make sure *he* extracts the proof artifacts, using his own instance of the `main` circuit backend. This way, Alice has to provide a valid proof for the correct `main` circuit. + +::: + +## Step 4 - Recursive proof generation + +With the artifacts, generating a recursive proof is no different from a normal proof. You simply use the `backend` (with the recursive circuit) to generate it: + +```js +const recursiveInputs = { + verification_key: vkAsFields, // array of length 114 + proof: proofAsFields, // array of length 93 + size of public inputs + publicInputs: [mainInput.y], // using the example above, where `y` is the only public input + key_hash: vkHash, + input_aggregation_object: Array(16).fill(0) // this circuit is verifying a non-recursive proof, so there's no input aggregation object: just use zero +} + +const { witness, returnValue } = noir.execute(recursiveInputs) // we're executing the recursive circuit now! +const { proof, publicInputs } = backend.generateFinalProof(witness) +const verified = backend.verifyFinalProof({ proof, publicInputs }) +``` + +You can obviously chain this proof into another proof. In fact, if you're using recursive proofs, you're probably interested of using them this way! In that case, you should keep in mind the `returnValue`, as it will contain the `input_aggregation_object` for the next proof. + +:::tip + +Managing circuits and "who does what" can be confusing. To make sure your naming is consistent, you can keep them in an object. For example: + +```js +const circuits = { +main: mainJSON, +recursive: recursiveJSON +} +const backends = { +main: new BarretenbergBackend(circuits.main), +recursive: new BarretenbergBackend(circuits.recursive) +} +const noir_programs = { +main: new Noir(circuits.main, backends.main), +recursive: new Noir(circuits.recursive, backends.recursive) +} +``` + +This allows you to neatly call exactly the method you want without conflicting names: + +```js +// Alice runs this 👇 +const { witness: mainWitness } = await noir_programs.main.execute(input) +const proof = await backends.main.generateIntermediateProof(mainWitness) + +// Bob runs this 👇 +const verified = await backends.main.verifyIntermediateProof(proof) +const { proofAsFields, vkAsFields, vkHash } = await backends.main.generateIntermediateProofArtifacts( + proof, + numPublicInputs, +); +const recursiveProof = await noir_programs.recursive.generateFinalProof(recursiveInputs) +``` + +::: diff --git a/noir/docs/processed-docs-cache/how_to/how-to-solidity-verifier.md b/noir/docs/processed-docs-cache/how_to/how-to-solidity-verifier.md new file mode 100644 index 00000000000..e3c7c1065da --- /dev/null +++ b/noir/docs/processed-docs-cache/how_to/how-to-solidity-verifier.md @@ -0,0 +1,231 @@ +--- +title: Generate a Solidity Verifier +description: + Learn how to run the verifier as a smart contract on the blockchain. Compile a Solidity verifier + contract for your Noir program and deploy it on any EVM blockchain acting as a verifier smart + contract. Read more to find out +keywords: + [ + solidity verifier, + smart contract, + blockchain, + compiler, + plonk_vk.sol, + EVM blockchain, + verifying Noir programs, + proving backend, + Barretenberg, + ] +sidebar_position: 0 +pagination_next: tutorials/noirjs_app +--- + +Noir has the ability to generate a verifier contract in Solidity, which can be deployed in many EVM-compatible blockchains such as Ethereum. + +This allows for a powerful feature set, as one can make use of the conciseness and the privacy provided by Noir in an immutable ledger. Applications can range from simple P2P guessing games, to complex private DeFi interactions. + +This guide shows you how to generate a Solidity Verifier and deploy it on the [Remix IDE](https://remix.ethereum.org/). It is assumed that: + +- You are comfortable with the Solidity programming language and understand how contracts are deployed on the Ethereum network +- You have Noir installed and you have a Noir program. If you don't, [get started](../getting_started/installation/index.md) with Nargo and the example Hello Noir circuit +- You are comfortable navigating RemixIDE. If you aren't or you need a refresher, you can find some video tutorials [here](https://www.youtube.com/channel/UCjTUPyFEr2xDGN6Cg8nKDaA) that could help you. + +## Rundown + +Generating a Solidity Verifier contract is actually a one-command process. However, compiling it and deploying it can have some caveats. Here's the rundown of this guide: + +1. How to generate a solidity smart contract +2. How to compile the smart contract in the RemixIDE +3. How to deploy it to a testnet + +## Step 1 - Generate a contract + +This is by far the most straight-forward step. Just run: + +```sh +nargo codegen-verifier +``` + +A new `contract` folder would then be generated in your project directory, containing the Solidity +file `plonk_vk.sol`. It can be deployed to any EVM blockchain acting as a verifier smart contract. + +:::info + +It is possible to generate verifier contracts of Noir programs for other smart contract platforms as long as the proving backend supplies an implementation. + +Barretenberg, the default proving backend for Nargo, supports generation of verifier contracts, for the time being these are only in Solidity. +::: + +## Step 2 - Compiling + +We will mostly skip the details of RemixIDE, as the UI can change from version to version. For now, we can just open +Remix and create a blank workspace. + +![Create Workspace](@site/static/img/how-tos/solidity_verifier_1.png) + +We will create a new file to contain the contract Nargo generated, and copy-paste its content. + +:::warning + +You'll likely see a warning advising you to not trust pasted code. While it is an important warning, it is irrelevant in the context of this guide and can be ignored. We will not be deploying anywhere near a mainnet. + +::: + +To compile our the verifier, we can navigate to the compilation tab: + +![Compilation Tab](@site/static/img/how-tos/solidity_verifier_2.png) + +Remix should automatically match a suitable compiler version. However, hitting the "Compile" button will most likely generate a "Stack too deep" error: + +![Stack too deep](@site/static/img/how-tos/solidity_verifier_3.png) + +This is due to the verify function needing to put many variables on the stack, but enabling the optimizer resolves the issue. To do this, let's open the "Advanced Configurations" tab and enable optimization. The default 200 runs will suffice. + +:::info + +This time we will see a warning about an unused function parameter. This is expected, as the `verify` function doesn't use the `_proof` parameter inside a solidity block, it is loaded from calldata and used in assembly. + +::: + +![Compilation success](@site/static/img/how-tos/solidity_verifier_4.png) + +## Step 3 - Deploying + +At this point we should have a compiled contract read to deploy. If we navigate to the deploy section in Remix, we will see many different environments we can deploy to. The steps to deploy on each environment would be out-of-scope for this guide, so we will just use the default Remix VM. + +Looking closely, we will notice that our "Solidity Verifier" is actually three contracts working together: + +- An `UltraVerificationKey` library which simply stores the verification key for our circuit. +- An abstract contract `BaseUltraVerifier` containing most of the verifying logic. +- A main `UltraVerifier` contract that inherits from the Base and uses the Key contract. + +Remix will take care of the dependencies for us so we can simply deploy the UltraVerifier contract by selecting it and hitting "deploy": + +![Deploying UltraVerifier](@site/static/img/how-tos/solidity_verifier_5.png) + +A contract will show up in the "Deployed Contracts" section, where we can retrieve the Verification Key Hash. This is particularly useful for double-checking the deployer contract is the correct one. + +:::note + +Why "UltraVerifier"? + +To be precise, the Noir compiler (`nargo`) doesn't generate the verifier contract directly. It compiles the Noir code into an intermediate language (ACIR), which is then executed by the backend. So it is the backend that returns the verifier smart contract, not Noir. + +In this case, the Barretenberg Backend uses the UltraPlonk proving system, hence the "UltraVerifier" name. + +::: + +## Step 4 - Verifying + +To verify a proof using the Solidity verifier contract, we call the `verify` function in this extended contract: + +```solidity +function verify(bytes calldata _proof, bytes32[] calldata _publicInputs) external view returns (bool) +``` + +When using the default example in the [Hello Noir](../getting_started/hello_noir/index.md) guide, the easiest way to confirm that the verifier contract is doing its job is by calling the `verify` function via remix with the required parameters. For `_proof`, run `nargo prove` and use the string in `proof/.proof` (adding the hex `0x` prefix). We can also copy the public input from `Verifier.toml`, as it will be properly formatted as 32-byte strings: + +``` +0x...... , [0x0000.....02] +``` + +A programmatic example of how the `verify` function is called can be seen in the example zk voting application [here](https://github.com/noir-lang/noir-examples/blob/33e598c257e2402ea3a6b68dd4c5ad492bce1b0a/foundry-voting/src/zkVote.sol#L35): + +```solidity +function castVote(bytes calldata proof, uint proposalId, uint vote, bytes32 nullifierHash) public returns (bool) { + // ... + bytes32[] memory publicInputs = new bytes32[](4); + publicInputs[0] = merkleRoot; + publicInputs[1] = bytes32(proposalId); + publicInputs[2] = bytes32(vote); + publicInputs[3] = nullifierHash; + require(verifier.verify(proof, publicInputs), "Invalid proof"); +``` + +:::info[Return Values] + +A circuit doesn't have the concept of a return value. Return values are just syntactic sugar in +Noir. + +Under the hood, the return value is passed as an input to the circuit and is checked at the end of +the circuit program. + +For example, if you have Noir program like this: + +```rust +fn main( + // Public inputs + pubkey_x: pub Field, + pubkey_y: pub Field, + // Private inputs + priv_key: Field, +) -> pub Field +``` + +the `verify` function will expect the public inputs array (second function parameter) to be of length 3, the two inputs and the return value. Like before, these values are populated in Verifier.toml after running `nargo prove`. + +Passing only two inputs will result in an error such as `PUBLIC_INPUT_COUNT_INVALID(3, 2)`. + +In this case, the inputs parameter to `verify` would be an array ordered as `[pubkey_x, pubkey_y, return]`. + +::: + +:::tip[Structs] + +You can pass structs to the verifier contract. They will be flattened so that the array of inputs is 1-dimensional array. + +For example, consider the following program: + +```rust +struct Type1 { + val1: Field, + val2: Field, +} + +struct Nested { + t1: Type1, + is_true: bool, +} + +fn main(x: pub Field, nested: pub Nested, y: pub Field) { + //... +} +``` + +The order of these inputs would be flattened to: `[x, nested.t1.val1, nested.t1.val2, nested.is_true, y]` + +::: + +The other function you can call is our entrypoint `verify` function, as defined above. + +:::tip + +It's worth noticing that the `verify` function is actually a `view` function. A `view` function does not alter the blockchain state, so it doesn't need to be distributed (i.e. it will run only on the executing node), and therefore doesn't cost any gas. + +This can be particularly useful in some situations. If Alice generated a proof and wants Bob to verify its correctness, Bob doesn't need to run Nargo, NoirJS, or any Noir specific infrastructure. He can simply make a call to the blockchain with the proof and verify it is correct without paying any gas. + +It would be incorrect to say that a Noir proof verification costs any gas at all. However, most of the time the result of `verify` is used to modify state (for example, to update a balance, a game state, etc). In that case the whole network needs to execute it, which does incur gas costs (calldata and execution, but not storage). + +::: + +## A Note on EVM chains + +ZK-SNARK verification depends on some precompiled cryptographic primitives such as Elliptic Curve Pairings (if you like complex math, you can read about EC Pairings [here](https://medium.com/@VitalikButerin/exploring-elliptic-curve-pairings-c73c1864e627)). Not all EVM chains support EC Pairings, notably some of the ZK-EVMs. This means that you won't be able to use the verifier contract in all of them. + +For example, chains like `zkSync ERA` and `Polygon zkEVM` do not currently support these precompiles, so proof verification via Solidity verifier contracts won't work. Here's a quick list of EVM chains that have been tested and are known to work: + +- Optimism +- Arbitrum +- Polygon PoS +- Scroll +- Celo + +If you test any other chains, please open a PR on this page to update the list. See [this doc](https://github.com/noir-lang/noir-starter/tree/main/with-foundry#testing-on-chain) for more info about testing verifier contracts on different EVM chains. + +## What's next + +Now that you know how to call a Noir Solidity Verifier on a smart contract using Remix, you should be comfortable with using it with some programmatic frameworks, such as [hardhat](https://github.com/noir-lang/noir-starter/tree/main/vite-hardhat) and [foundry](https://github.com/noir-lang/noir-starter/tree/main/with-foundry). + +You can find other tools, examples, boilerplates and libraries in the [awesome-noir](https://github.com/noir-lang/awesome-noir) repository. + +You should also be ready to write and deploy your first NoirJS app and start generating proofs on websites, phones, and NodeJS environments! Head on to the [NoirJS tutorial](../tutorials/noirjs_app.md) to learn how to do that. diff --git a/noir/docs/processed-docs-cache/how_to/merkle-proof.mdx b/noir/docs/processed-docs-cache/how_to/merkle-proof.mdx new file mode 100644 index 00000000000..34074659ac1 --- /dev/null +++ b/noir/docs/processed-docs-cache/how_to/merkle-proof.mdx @@ -0,0 +1,48 @@ +--- +title: Prove Merkle Tree Membership +description: + Learn how to use merkle membership proof in Noir to prove that a given leaf is a member of a + merkle tree with a specified root, at a given index. +keywords: + [merkle proof, merkle membership proof, Noir, rust, hash function, Pedersen, sha256, merkle tree] +--- + +Let's walk through an example of a merkle membership proof in Noir that proves that a given leaf is +in a merkle tree. + +```rust +use dep::std; + +fn main(message : [Field; 62], index : Field, hashpath : [Field; 40], root : Field) { + let leaf = std::hash::hash_to_field(message); + let merkle_root = std::merkle::compute_merkle_root(leaf, index, hashpath); + assert(merkle_root == root); +} + +``` + +The message is hashed using `hash_to_field`. The specific hash function that is being used is chosen +by the backend. The only requirement is that this hash function can heuristically be used as a +random oracle. If only collision resistance is needed, then one can call `std::hash::pedersen_hash` +instead. + +```rust +let leaf = std::hash::hash_to_field(message); +``` + +The leaf is then passed to a compute_merkle_root function with the root, index and hashpath. The returned root can then be asserted to be the same as the provided root. + +```rust +let merkle_root = std::merkle::compute_merkle_root(leaf, index, hashpath); +assert (merkle_root == root); +``` + +> **Note:** It is possible to re-implement the merkle tree implementation without standard library. +> However, for most usecases, it is enough. In general, the standard library will always opt to be +> as conservative as possible, while striking a balance with efficiency. + +An example, the merkle membership proof, only requires a hash function that has collision +resistance, hence a hash function like Pedersen is allowed, which in most cases is more efficient +than the even more conservative sha256. + +[View an example on the starter repo](https://github.com/noir-lang/noir-examples/blob/3ea09545cabfa464124ec2f3ea8e60c608abe6df/stealthdrop/circuits/src/main.nr#L20) diff --git a/noir/docs/processed-docs-cache/how_to/using-devcontainers.mdx b/noir/docs/processed-docs-cache/how_to/using-devcontainers.mdx new file mode 100644 index 00000000000..727ec6ca667 --- /dev/null +++ b/noir/docs/processed-docs-cache/how_to/using-devcontainers.mdx @@ -0,0 +1,110 @@ +--- +title: Developer Containers and Codespaces +description: "Learn how to set up a devcontainer in your GitHub repository for a seamless coding experience with Codespaces. Follow our easy 8-step guide to create your own Noir environment without installing Nargo locally." +keywords: ["Devcontainer", "Codespaces", "GitHub", "Noir Environment", "Docker Image", "Development Environment", "Remote Coding", "GitHub Codespaces", "Noir Programming", "Nargo", "VSCode Extensions", "Noirup"] +sidebar_position: 1 +--- + +Adding a developer container configuration file to your Noir project is one of the easiest way to unlock coding in browser. + +## What's a devcontainer after all? + +A [Developer Container](https://containers.dev/) (devcontainer for short) is a Docker image that comes preloaded with tools, extensions, and other tools you need to quickly get started or continue a project, without having to install Nargo locally. Think of it as a development environment in a box. + +There are many advantages to this: + +- It's platform and architecture agnostic +- You don't need to have an IDE installed, or Nargo, or use a terminal at all +- It's safer for using on a public machine or public network + +One of the best ways of using devcontainers is... not using your machine at all, for maximum control, performance, and ease of use. +Enter Codespaces. + +## Codespaces + +If a devcontainer is just a Docker image, then what stops you from provisioning a `p3dn.24xlarge` AWS EC2 instance with 92 vCPUs and 768 GiB RAM and using it to prove your 10-gate SNARK proof? + +Nothing! Except perhaps the 30-40$ per hour it will cost you. + +The problem is that provisioning takes time, and I bet you don't want to see the AWS console every time you want to code something real quick. + +Fortunately, there's an easy and free way to get a decent remote machine ready and loaded in less than 2 minutes: Codespaces. [Codespaces is a Github feature](https://github.com/features/codespaces) that allows you to code in a remote machine by using devcontainers, and it's pretty cool: + +- You can start coding Noir in less than a minute +- It uses the resources of a remote machine, so you can code on your grandma's phone if needed be +- It makes it easy to share work with your frens +- It's fully reusable, you can stop and restart whenever you need to + +:::info + +Don't take out your wallet just yet. Free GitHub accounts get about [15-60 hours of coding](https://github.com/features/codespaces) for free per month, depending on the size of your provisioned machine. + +::: + +## Tell me it's _actually_ easy + +It is! + +Github comes with a default codespace and you can use it to code your own devcontainer. That's exactly what we will be doing in this guide. + + + +8 simple steps: + +#### 1. Create a new repository on GitHub. + +#### 2. Click "Start coding with Codespaces". This will use the default image. + +#### 3. Create a folder called `.devcontainer` in the root of your repository. + +#### 4. Create a Dockerfile in that folder, and paste the following code: + +```docker +FROM --platform=linux/amd64 node:lts-bookworm-slim +SHELL ["/bin/bash", "-c"] +RUN apt update && apt install -y curl bash git tar gzip libc++-dev +RUN curl -L https://raw.githubusercontent.com/noir-lang/noirup/main/install | bash +ENV PATH="/root/.nargo/bin:$PATH" +RUN noirup +ENTRYPOINT ["nargo"] +``` +#### 5. Create a file called `devcontainer.json` in the same folder, and paste the following code: + +```json +{ + "name": "Noir on Codespaces", + "build": { + "context": ".", + "dockerfile": "Dockerfile" + }, + "customizations": { + "vscode": { + "extensions": ["noir-lang.vscode-noir"] + } + } +} +``` +#### 6. Commit and push your changes + +This will pull the new image and build it, so it could take a minute or so + +#### 8. Done! +Just wait for the build to finish, and there's your easy Noir environment. + + +Refer to [noir-starter](https://github.com/noir-lang/noir-starter/) as an example of how devcontainers can be used together with codespaces. + + + +## How do I use it? + +Using the codespace is obviously much easier than setting it up. +Just navigate to your repository and click "Code" -> "Open with Codespaces". It should take a few seconds to load, and you're ready to go. + +:::info + +If you really like the experience, you can add a badge to your readme, links to existing codespaces, and more. +Check out the [official docs](https://docs.github.com/en/codespaces/setting-up-your-project-for-codespaces/setting-up-your-repository/facilitating-quick-creation-and-resumption-of-codespaces) for more info. diff --git a/noir/docs/processed-docs-cache/index.mdx b/noir/docs/processed-docs-cache/index.mdx new file mode 100644 index 00000000000..75086ddcdde --- /dev/null +++ b/noir/docs/processed-docs-cache/index.mdx @@ -0,0 +1,67 @@ +--- +title: Noir Lang +hide_title: true +description: + Learn about the public alpha release of Noir, a domain specific language heavily influenced by Rust that compiles to + an intermediate language which can be compiled to an arithmetic circuit or a rank-1 constraint system. +keywords: + [Noir, + Domain Specific Language, + Rust, + Intermediate Language, + Arithmetic Circuit, + Rank-1 Constraint System, + Ethereum Developers, + Protocol Developers, + Blockchain Developers, + Proving System, + Smart Contract Language] +sidebar_position: 0 +--- + +import Tabs from '@theme/Tabs'; +import TabItem from '@theme/TabItem'; + +Noir Logo + +Noir is a Domain-Specific Language for SNARK proving systems developed by [Aztec Labs](https://aztec.network/). It allows you to generate complex Zero-Knowledge Programs (ZKP) by using simple and flexible syntax, requiring no previous knowledge on the underlying mathematics or cryptography. + +ZK programs are programs that can generate short proofs of a certain statement without revealing some details about it. You can read more about ZKPs [here](https://dev.to/spalladino/a-beginners-intro-to-coding-zero-knowledge-proofs-c56). + +## What's new about Noir? + +Noir works differently from most ZK languages by taking a two-pronged path. First, it compiles the program to an adaptable intermediate language known as ACIR. From there, depending on a given project's needs, ACIR can be further compiled into an arithmetic circuit for integration with the proving backend. + +:::info + +Noir is backend agnostic, which means it makes no assumptions on which proving backend powers the ZK proof. Being the language that powers [Aztec Contracts](https://docs.aztec.network/developers/contracts/main), it defaults to Aztec's Barretenberg proving backend. + +However, the ACIR output can be transformed to be compatible with other PLONK-based backends, or into a [rank-1 constraint system](https://www.rareskills.io/post/rank-1-constraint-system) suitable for backends such as Arkwork's Marlin. + +::: + +## Who is Noir for? + +Noir can be used both in complex cloud-based backends and in user's smartphones, requiring no knowledge on the underlying math or cryptography. From authorization systems that keep a password in the user's device, to complex on-chain verification of recursive proofs, Noir is designed to abstract away complexity without any significant overhead. Here are some examples of situations where Noir can be used: + + + + Noir Logo + + Aztec Contracts leverage Noir to allow for the storage and execution of private information. Writing an Aztec Contract is as easy as writing Noir, and Aztec developers can easily interact with the network storage and execution through the [Aztec.nr](https://docs.aztec.network/developers/contracts/main) library. + + + Soliditry Verifier Example + Noir can auto-generate Solidity verifier contracts that verify Noir proofs. This allows for non-interactive verification of proofs containing private information in an immutable system. This feature powers a multitude of use-case scenarios, from P2P chess tournaments, to [Aztec Layer-2 Blockchain](https://docs.aztec.network/) + + + Aztec Labs developed NoirJS, an easy interface to generate and verify Noir proofs in a Javascript environment. This allows for Noir to be used in webpages, mobile apps, games, and any other environment supporting JS execution in a standalone manner. + + + + +## Libraries + +Noir is meant to be easy to extend by simply importing Noir libraries just like in Rust. +The [awesome-noir repo](https://github.com/noir-lang/awesome-noir#libraries) is a collection of libraries developed by the Noir community. +Writing a new library is easy and makes code be composable and easy to reuse. See the section on [dependencies](noir/modules_packages_crates/dependencies.md) for more information. diff --git a/noir/docs/processed-docs-cache/migration_notes.md b/noir/docs/processed-docs-cache/migration_notes.md new file mode 100644 index 00000000000..9f27230a1a0 --- /dev/null +++ b/noir/docs/processed-docs-cache/migration_notes.md @@ -0,0 +1,91 @@ +--- +title: Migration notes +description: Read about migration notes from previous versions, which could solve problems while updating +keywords: [Noir, notes, migration, updating, upgrading] +--- + +Noir is in full-speed development. Things break fast, wild, and often. This page attempts to leave some notes on errors you might encounter when upgrading and how to resolve them until proper patches are built. + +## ≥0.19 + +### Enforcing `compiler_version` + +From this version on, the compiler will check for the `compiler_version` field in `Nargo.toml`, and will error if it doesn't match the current Nargo version in use. + +To update, please make sure this field in `Nargo.toml` matches the output of `nargo --version`. + +## ≥0.14 + +The index of the [for loops](noir/concepts/control_flow.md#loops) is now of type `u64` instead of `Field`. An example refactor would be: + +```rust +for i in 0..10 { + let i = i as Field; +} +``` + +## ≥v0.11.0 and Nargo backend + +From this version onwards, Nargo starts managing backends through the `nargo backend` command. Upgrading to the versions per usual steps might lead to: + +### `backend encountered an error` + +This is likely due to the existing locally installed version of proving backend (e.g. barretenberg) is incompatible with the version of Nargo in use. + +To fix the issue: + +1. Uninstall the existing backend + +```bash +nargo backend uninstall acvm-backend-barretenberg +``` + +You may replace _acvm-backend-barretenberg_ with the name of your backend listed in `nargo backend ls` or in ~/.nargo/backends. + +2. Reinstall a compatible version of the proving backend. + +If you are using the default barretenberg backend, simply run: + +``` +nargo prove +``` + +with your Noir program. + +This will trigger the download and installation of the latest version of barretenberg compatible with your Nargo in use. + +### `backend encountered an error: illegal instruction` + +On certain Intel-based systems, an `illegal instruction` error may arise due to incompatibility of barretenberg with certain CPU instructions. + +To fix the issue: + +1. Uninstall the existing backend + +```bash +nargo backend uninstall acvm-backend-barretenberg +``` + +You may replace _acvm-backend-barretenberg_ with the name of your backend listed in `nargo backend ls` or in ~/.nargo/backends. + +2. Reinstall a compatible version of the proving backend. + +If you are using the default barretenberg backend, simply run: + +``` +nargo backend install acvm-backend-barretenberg https://github.com/noir-lang/barretenberg-js-binary/raw/master/run-bb.tar.gz +``` + +This downloads and installs a specific bb.js based version of barretenberg binary from GitHub. + +The gzipped file is running [this bash script](https://github.com/noir-lang/barretenberg-js-binary/blob/master/run-bb-js.sh), where we need to gzip it as the Nargo currently expect the backend to be zipped up. + +Then run: + +``` +DESIRED_BINARY_VERSION=0.8.1 nargo info +``` + +This overrides the bb native binary with a bb.js node application instead, which should be compatible with most if not all hardware. This does come with the drawback of being generally slower than native binary. + +0.8.1 indicates bb.js version 0.8.1, so if you change that it will update to a different version or the default version in the script if none was supplied. diff --git a/noir/docs/processed-docs-cache/noir/concepts/_category_.json b/noir/docs/processed-docs-cache/noir/concepts/_category_.json new file mode 100644 index 00000000000..7da08f8a8c5 --- /dev/null +++ b/noir/docs/processed-docs-cache/noir/concepts/_category_.json @@ -0,0 +1,6 @@ +{ + "label": "Concepts", + "position": 0, + "collapsible": true, + "collapsed": true +} \ No newline at end of file diff --git a/noir/docs/processed-docs-cache/noir/concepts/assert.md b/noir/docs/processed-docs-cache/noir/concepts/assert.md new file mode 100644 index 00000000000..c5f9aff139c --- /dev/null +++ b/noir/docs/processed-docs-cache/noir/concepts/assert.md @@ -0,0 +1,27 @@ +--- +title: Assert Function +description: + Learn about the assert function in Noir, which can be used to explicitly constrain the predicate or + comparison expression that follows to be true, and what happens if the expression is false at + runtime. +keywords: [Noir programming language, assert statement, predicate expression, comparison expression] +sidebar_position: 4 +--- + +Noir includes a special `assert` function which will explicitly constrain the predicate/comparison +expression that follows to be true. If this expression is false at runtime, the program will fail to +be proven. Example: + +```rust +fn main(x : Field, y : Field) { + assert(x == y); +} +``` + +You can optionally provide a message to be logged when the assertion fails: + +```rust +assert(x == y, "x and y are not equal"); +``` + +> Assertions only work for predicate operations, such as `==`. If there's any ambiguity on the operation, the program will fail to compile. For example, it is unclear if `assert(x + y)` would check for `x + y == 0` or simply would return `true`. diff --git a/noir/docs/processed-docs-cache/noir/concepts/comments.md b/noir/docs/processed-docs-cache/noir/concepts/comments.md new file mode 100644 index 00000000000..b51a85f5c94 --- /dev/null +++ b/noir/docs/processed-docs-cache/noir/concepts/comments.md @@ -0,0 +1,33 @@ +--- +title: Comments +description: + Learn how to write comments in Noir programming language. A comment is a line of code that is + ignored by the compiler, but it can be read by programmers. Single-line and multi-line comments + are supported in Noir. +keywords: [Noir programming language, comments, single-line comments, multi-line comments] +sidebar_position: 10 +--- + +A comment is a line in your codebase which the compiler ignores, however it can be read by +programmers. + +Here is a single line comment: + +```rust +// This is a comment and is ignored +``` + +`//` is used to tell the compiler to ignore the rest of the line. + +Noir also supports multi-line block comments. Start a block comment with `/*` and end the block with `*/`. + +Noir does not natively support doc comments. You may be able to use [Rust doc comments](https://doc.rust-lang.org/reference/comments.html) in your code to leverage some Rust documentation build tools with Noir code. + +```rust +/* + This is a block comment describing a complex function. +*/ +fn main(x : Field, y : pub Field) { + assert(x != y); +} +``` diff --git a/noir/docs/processed-docs-cache/noir/concepts/control_flow.md b/noir/docs/processed-docs-cache/noir/concepts/control_flow.md new file mode 100644 index 00000000000..4ce65236db3 --- /dev/null +++ b/noir/docs/processed-docs-cache/noir/concepts/control_flow.md @@ -0,0 +1,45 @@ +--- +title: Control Flow +description: + Learn how to use loops and if expressions in the Noir programming language. Discover the syntax + and examples for for loops and if-else statements. +keywords: [Noir programming language, loops, for loop, if-else statements, Rust syntax] +sidebar_position: 2 +--- + +## Loops + +Noir has one kind of loop: the `for` loop. `for` loops allow you to repeat a block of code multiple +times. + +The following block of code between the braces is run 10 times. + +```rust +for i in 0..10 { + // do something +}; +``` + +The index for loops is of type `u64`. + +## If Expressions + +Noir supports `if-else` statements. The syntax is most similar to Rust's where it is not required +for the statement's conditional to be surrounded by parentheses. + +```rust +let a = 0; +let mut x: u32 = 0; + +if a == 0 { + if a != 0 { + x = 6; + } else { + x = 2; + } +} else { + x = 5; + assert(x == 5); +} +assert(x == 2); +``` diff --git a/noir/docs/processed-docs-cache/noir/concepts/data_bus.md b/noir/docs/processed-docs-cache/noir/concepts/data_bus.md new file mode 100644 index 00000000000..e54fc861257 --- /dev/null +++ b/noir/docs/processed-docs-cache/noir/concepts/data_bus.md @@ -0,0 +1,21 @@ +--- +title: Data Bus +sidebar_position: 13 +--- +**Disclaimer** this feature is experimental, do not use it! + +The data bus is an optimization that the backend can use to make recursion more efficient. +In order to use it, you must define some inputs of the program entry points (usually the `main()` +function) with the `call_data` modifier, and the return values with the `return_data` modifier. +These modifiers are incompatible with `pub` and `mut` modifiers. + +## Example + +```rust +fn main(mut x: u32, y: call_data u32, z: call_data [u32;4] ) -> return_data u32 { + let a = z[x]; + a+y +} +``` + +As a result, both call_data and return_data will be treated as private inputs and encapsulated into a read-only array each, for the backend to process. diff --git a/noir/docs/processed-docs-cache/noir/concepts/data_types/_category_.json b/noir/docs/processed-docs-cache/noir/concepts/data_types/_category_.json new file mode 100644 index 00000000000..5d694210bbf --- /dev/null +++ b/noir/docs/processed-docs-cache/noir/concepts/data_types/_category_.json @@ -0,0 +1,5 @@ +{ + "position": 0, + "collapsible": true, + "collapsed": true +} diff --git a/noir/docs/processed-docs-cache/noir/concepts/data_types/arrays.md b/noir/docs/processed-docs-cache/noir/concepts/data_types/arrays.md new file mode 100644 index 00000000000..a8bd338e736 --- /dev/null +++ b/noir/docs/processed-docs-cache/noir/concepts/data_types/arrays.md @@ -0,0 +1,251 @@ +--- +title: Arrays +description: + Dive into the Array data type in Noir. Grasp its methods, practical examples, and best practices for efficiently using Arrays in your Noir code. +keywords: + [ + noir, + array type, + methods, + examples, + indexing, + ] +sidebar_position: 4 +--- + +An array is one way of grouping together values into one compound type. Array types can be inferred +or explicitly specified via the syntax `[; ]`: + +```rust +fn main(x : Field, y : Field) { + let my_arr = [x, y]; + let your_arr: [Field; 2] = [x, y]; +} +``` + +Here, both `my_arr` and `your_arr` are instantiated as an array containing two `Field` elements. + +Array elements can be accessed using indexing: + +```rust +fn main() { + let a = [1, 2, 3, 4, 5]; + + let first = a[0]; + let second = a[1]; +} +``` + +All elements in an array must be of the same type (i.e. homogeneous). That is, an array cannot group +a `Field` value and a `u8` value together for example. + +You can write mutable arrays, like: + +```rust +fn main() { + let mut arr = [1, 2, 3, 4, 5]; + assert(arr[0] == 1); + + arr[0] = 42; + assert(arr[0] == 42); +} +``` + +You can instantiate a new array of a fixed size with the same value repeated for each element. The following example instantiates an array of length 32 where each element is of type Field and has the value 0. + +```rust +let array: [Field; 32] = [0; 32]; +``` + +Like in Rust, arrays in Noir are a fixed size. However, if you wish to convert an array to a [slice](./slices), you can just call `as_slice` on your array: + +```rust +let array: [Field; 32] = [0; 32]; +let sl = array.as_slice() +``` + +You can define multidimensional arrays: + +```rust +let array : [[Field; 2]; 2]; +let element = array[0][0]; +``` +However, multidimensional slices are not supported. For example, the following code will error at compile time: +```rust +let slice : [[Field]] = []; +``` + +## Types + +You can create arrays of primitive types or structs. There is not yet support for nested arrays +(arrays of arrays) or arrays of structs that contain arrays. + +## Methods + +For convenience, the STD provides some ready-to-use, common methods for arrays. +Each of these functions are located within the generic impl `impl [T; N] {`. +So anywhere `self` appears, it refers to the variable `self: [T; N]`. + +### len + +Returns the length of an array + +```rust +fn len(self) -> Field +``` + +example + +```rust +fn main() { + let array = [42, 42]; + assert(array.len() == 2); +} +``` + +### sort + +Returns a new sorted array. The original array remains untouched. Notice that this function will +only work for arrays of fields or integers, not for any arbitrary type. This is because the sorting +logic it uses internally is optimized specifically for these values. If you need a sort function to +sort any type, you should use the function `sort_via` described below. + +```rust +fn sort(self) -> [T; N] +``` + +example + +```rust +fn main() { + let arr = [42, 32]; + let sorted = arr.sort(); + assert(sorted == [32, 42]); +} +``` + +### sort_via + +Sorts the array with a custom comparison function + +```rust +fn sort_via(self, ordering: fn(T, T) -> bool) -> [T; N] +``` + +example + +```rust +fn main() { + let arr = [42, 32] + let sorted_ascending = arr.sort_via(|a, b| a < b); + assert(sorted_ascending == [32, 42]); // verifies + + let sorted_descending = arr.sort_via(|a, b| a > b); + assert(sorted_descending == [32, 42]); // does not verify +} +``` + +### map + +Applies a function to each element of the array, returning a new array containing the mapped elements. + +```rust +fn map(self, f: fn(T) -> U) -> [U; N] +``` + +example + +```rust +let a = [1, 2, 3]; +let b = a.map(|a| a * 2); // b is now [2, 4, 6] +``` + +### fold + +Applies a function to each element of the array, returning the final accumulated value. The first +parameter is the initial value. + +```rust +fn fold(self, mut accumulator: U, f: fn(U, T) -> U) -> U +``` + +This is a left fold, so the given function will be applied to the accumulator and first element of +the array, then the second, and so on. For a given call the expected result would be equivalent to: + +```rust +let a1 = [1]; +let a2 = [1, 2]; +let a3 = [1, 2, 3]; + +let f = |a, b| a - b; +a1.fold(10, f) //=> f(10, 1) +a2.fold(10, f) //=> f(f(10, 1), 2) +a3.fold(10, f) //=> f(f(f(10, 1), 2), 3) +``` + +example: + +```rust + +fn main() { + let arr = [2, 2, 2, 2, 2]; + let folded = arr.fold(0, |a, b| a + b); + assert(folded == 10); +} + +``` + +### reduce + +Same as fold, but uses the first element as starting element. + +```rust +fn reduce(self, f: fn(T, T) -> T) -> T +``` + +example: + +```rust +fn main() { + let arr = [2, 2, 2, 2, 2]; + let reduced = arr.reduce(|a, b| a + b); + assert(reduced == 10); +} +``` + +### all + +Returns true if all the elements satisfy the given predicate + +```rust +fn all(self, predicate: fn(T) -> bool) -> bool +``` + +example: + +```rust +fn main() { + let arr = [2, 2, 2, 2, 2]; + let all = arr.all(|a| a == 2); + assert(all); +} +``` + +### any + +Returns true if any of the elements satisfy the given predicate + +```rust +fn any(self, predicate: fn(T) -> bool) -> bool +``` + +example: + +```rust +fn main() { + let arr = [2, 2, 2, 2, 5]; + let any = arr.any(|a| a == 5); + assert(any); +} + +``` diff --git a/noir/docs/processed-docs-cache/noir/concepts/data_types/booleans.md b/noir/docs/processed-docs-cache/noir/concepts/data_types/booleans.md new file mode 100644 index 00000000000..69826fcd724 --- /dev/null +++ b/noir/docs/processed-docs-cache/noir/concepts/data_types/booleans.md @@ -0,0 +1,31 @@ +--- +title: Booleans +description: + Delve into the Boolean data type in Noir. Understand its methods, practical examples, and best practices for using Booleans in your Noir programs. +keywords: + [ + noir, + boolean type, + methods, + examples, + logical operations, + ] +sidebar_position: 2 +--- + + +The `bool` type in Noir has two possible values: `true` and `false`: + +```rust +fn main() { + let t = true; + let f: bool = false; +} +``` + +> **Note:** When returning a boolean value, it will show up as a value of 1 for `true` and 0 for +> `false` in _Verifier.toml_. + +The boolean type is most commonly used in conditionals like `if` expressions and `assert` +statements. More about conditionals is covered in the [Control Flow](../control_flow) and +[Assert Function](../assert) sections. diff --git a/noir/docs/processed-docs-cache/noir/concepts/data_types/fields.md b/noir/docs/processed-docs-cache/noir/concepts/data_types/fields.md new file mode 100644 index 00000000000..7870c98c858 --- /dev/null +++ b/noir/docs/processed-docs-cache/noir/concepts/data_types/fields.md @@ -0,0 +1,183 @@ +--- +title: Fields +description: + Dive deep into the Field data type in Noir. Understand its methods, practical examples, and best practices to effectively use Fields in your Noir programs. +keywords: + [ + noir, + field type, + methods, + examples, + best practices, + ] +sidebar_position: 0 +--- + +The field type corresponds to the native field type of the proving backend. + +The size of a Noir field depends on the elliptic curve's finite field for the proving backend +adopted. For example, a field would be a 254-bit integer when paired with the default backend that +spans the Grumpkin curve. + +Fields support integer arithmetic and are often used as the default numeric type in Noir: + +```rust +fn main(x : Field, y : Field) { + let z = x + y; +} +``` + +`x`, `y` and `z` are all private fields in this example. Using the `let` keyword we defined a new +private value `z` constrained to be equal to `x + y`. + +If proving efficiency is of priority, fields should be used as a default for solving problems. +Smaller integer types (e.g. `u64`) incur extra range constraints. + +## Methods + +After declaring a Field, you can use these common methods on it: + +### to_le_bits + +Transforms the field into an array of bits, Little Endian. + +```rust +fn to_le_bits(_x : Field, _bit_size: u32) -> [u1; N] +``` + +example: + +```rust +fn main() { + let field = 2; + let bits = field.to_le_bits(32); +} +``` + +### to_be_bits + +Transforms the field into an array of bits, Big Endian. + +```rust +fn to_be_bits(_x : Field, _bit_size: u32) -> [u1; N] +``` + +example: + +```rust +fn main() { + let field = 2; + let bits = field.to_be_bits(32); +} +``` + +### to_le_bytes + +Transforms into an array of bytes, Little Endian + +```rust +fn to_le_bytes(_x : Field, byte_size: u32) -> [u8] +``` + +example: + +```rust +fn main() { + let field = 2; + let bytes = field.to_le_bytes(4); +} +``` + +### to_be_bytes + +Transforms into an array of bytes, Big Endian + +```rust +fn to_be_bytes(_x : Field, byte_size: u32) -> [u8] +``` + +example: + +```rust +fn main() { + let field = 2; + let bytes = field.to_be_bytes(4); +} +``` + +### to_le_radix + +Decomposes into a vector over the specified base, Little Endian + +```rust +fn to_le_radix(_x : Field, _radix: u32, _result_len: u32) -> [u8] +``` + +example: + +```rust +fn main() { + let field = 2; + let radix = field.to_le_radix(256, 4); +} +``` + +### to_be_radix + +Decomposes into a vector over the specified base, Big Endian + +```rust +fn to_be_radix(_x : Field, _radix: u32, _result_len: u32) -> [u8] +``` + +example: + +```rust +fn main() { + let field = 2; + let radix = field.to_be_radix(256, 4); +} +``` + +### pow_32 + +Returns the value to the power of the specified exponent + +```rust +fn pow_32(self, exponent: Field) -> Field +``` + +example: + +```rust +fn main() { + let field = 2 + let pow = field.pow_32(4); + assert(pow == 16); +} +``` + +### assert_max_bit_size + +Adds a constraint to specify that the field can be represented with `bit_size` number of bits + +```rust +fn assert_max_bit_size(self, bit_size: u32) +``` + +example: + +```rust +fn main() { + let field = 2 + field.assert_max_bit_size(32); +} +``` + +### sgn0 + +Parity of (prime) Field element, i.e. sgn0(x mod p) = 0 if x ∈ \{0, ..., p-1\} is even, otherwise sgn0(x mod p) = 1. + +```rust +fn sgn0(self) -> u1 +``` diff --git a/noir/docs/processed-docs-cache/noir/concepts/data_types/function_types.md b/noir/docs/processed-docs-cache/noir/concepts/data_types/function_types.md new file mode 100644 index 00000000000..f6121af17e2 --- /dev/null +++ b/noir/docs/processed-docs-cache/noir/concepts/data_types/function_types.md @@ -0,0 +1,26 @@ +--- +title: Function types +sidebar_position: 10 +--- + +Noir supports higher-order functions. The syntax for a function type is as follows: + +```rust +fn(arg1_type, arg2_type, ...) -> return_type +``` + +Example: + +```rust +fn assert_returns_100(f: fn() -> Field) { // f takes no args and returns a Field + assert(f() == 100); +} + +fn main() { + assert_returns_100(|| 100); // ok + assert_returns_100(|| 150); // fails +} +``` + +A function type also has an optional capture environment - this is necessary to support closures. +See [Lambdas](../lambdas.md) for more details. diff --git a/noir/docs/processed-docs-cache/noir/concepts/data_types/index.md b/noir/docs/processed-docs-cache/noir/concepts/data_types/index.md new file mode 100644 index 00000000000..3c9cd4c2437 --- /dev/null +++ b/noir/docs/processed-docs-cache/noir/concepts/data_types/index.md @@ -0,0 +1,96 @@ +--- +title: Data Types +description: + Get a clear understanding of the two categories of Noir data types - primitive types and compound + types. Learn about their characteristics, differences, and how to use them in your Noir + programming. +keywords: + [ + noir, + data types, + primitive types, + compound types, + private types, + public types, + ] +--- + +Every value in Noir has a type, which determines which operations are valid for it. + +All values in Noir are fundamentally composed of `Field` elements. For a more approachable +developing experience, abstractions are added on top to introduce different data types in Noir. + +Noir has two category of data types: primitive types (e.g. `Field`, integers, `bool`) and compound +types that group primitive types (e.g. arrays, tuples, structs). Each value can either be private or +public. + +## Private & Public Types + +A **private value** is known only to the Prover, while a **public value** is known by both the +Prover and Verifier. Mark values as `private` when the value should only be known to the prover. All +primitive types (including individual fields of compound types) in Noir are private by default, and +can be marked public when certain values are intended to be revealed to the Verifier. + +> **Note:** For public values defined in Noir programs paired with smart contract verifiers, once +> the proofs are verified on-chain the values can be considered known to everyone that has access to +> that blockchain. + +Public data types are treated no differently to private types apart from the fact that their values +will be revealed in proofs generated. Simply changing the value of a public type will not change the +circuit (where the same goes for changing values of private types as well). + +_Private values_ are also referred to as _witnesses_ sometimes. + +> **Note:** The terms private and public when applied to a type (e.g. `pub Field`) have a different +> meaning than when applied to a function (e.g. `pub fn foo() {}`). +> +> The former is a visibility modifier for the Prover to interpret if a value should be made known to +> the Verifier, while the latter is a visibility modifier for the compiler to interpret if a +> function should be made accessible to external Noir programs like in other languages. + +### pub Modifier + +All data types in Noir are private by default. Types are explicitly declared as public using the +`pub` modifier: + +```rust +fn main(x : Field, y : pub Field) -> pub Field { + x + y +} +``` + +In this example, `x` is **private** while `y` and `x + y` (the return value) are **public**. Note +that visibility is handled **per variable**, so it is perfectly valid to have one input that is +private and another that is public. + +> **Note:** Public types can only be declared through parameters on `main`. + +## Type Aliases + +A type alias is a new name for an existing type. Type aliases are declared with the keyword `type`: + +```rust +type Id = u8; + +fn main() { + let id: Id = 1; + let zero: u8 = 0; + assert(zero + 1 == id); +} +``` + +Type aliases can also be used with [generics](@site/docs/noir/concepts/generics.md): + +```rust +type Id = Size; + +fn main() { + let id: Id = 1; + let zero: u32 = 0; + assert(zero + 1 == id); +} +``` + +### BigInt + +You can achieve BigInt functionality using the [Noir BigInt](https://github.com/shuklaayush/noir-bigint) library. diff --git a/noir/docs/processed-docs-cache/noir/concepts/data_types/integers.md b/noir/docs/processed-docs-cache/noir/concepts/data_types/integers.md new file mode 100644 index 00000000000..7d1e83cf4e9 --- /dev/null +++ b/noir/docs/processed-docs-cache/noir/concepts/data_types/integers.md @@ -0,0 +1,113 @@ +--- +title: Integers +description: Explore the Integer data type in Noir. Learn about its methods, see real-world examples, and grasp how to efficiently use Integers in your Noir code. +keywords: [noir, integer types, methods, examples, arithmetic] +sidebar_position: 1 +--- + +An integer type is a range constrained field type. The Noir frontend supports arbitrarily-sized, both unsigned and signed integer types. + +:::info + +When an integer is defined in Noir without a specific type, it will default to `Field`. + +The one exception is for loop indices which default to `u64` since comparisons on `Field`s are not possible. + +::: + +## Unsigned Integers + +An unsigned integer type is specified first with the letter `u` (indicating its unsigned nature) followed by its bit size (e.g. `8`): + +```rust +fn main() { + let x: u8 = 1; + let y: u8 = 1; + let z = x + y; + assert (z == 2); +} +``` + +The bit size determines the maximum value the integer type can store. For example, a `u8` variable can store a value in the range of 0 to 255 (i.e. $\\2^{8}-1\\$). + +## Signed Integers + +A signed integer type is specified first with the letter `i` (which stands for integer) followed by its bit size (e.g. `8`): + +```rust +fn main() { + let x: i8 = -1; + let y: i8 = -1; + let z = x + y; + assert (z == -2); +} +``` + +The bit size determines the maximum and minimum range of value the integer type can store. For example, an `i8` variable can store a value in the range of -128 to 127 (i.e. $\\-2^{7}\\$ to $\\2^{7}-1\\$). + +:::tip + +If you are using the default proving backend with Noir, both even (e.g. _u2_, _i2_) and odd (e.g. _u3_, _i3_) arbitrarily-sized integer types up to 127 bits (i.e. _u127_ and _i127_) are supported. + +::: + +## Overflows + +Computations that exceed the type boundaries will result in overflow errors. This happens with both signed and unsigned integers. For example, attempting to prove: + +```rust +fn main(x: u8, y: u8) { + let z = x + y; +} +``` + +With: + +```toml +x = "255" +y = "1" +``` + +Would result in: + +``` +$ nargo prove +error: Assertion failed: 'attempt to add with overflow' +┌─ ~/src/main.nr:9:13 +│ +│ let z = x + y; +│ ----- +│ += Call stack: + ... +``` + +A similar error would happen with signed integers: + +```rust +fn main() { + let x: i8 = -118; + let y: i8 = -11; + let z = x + y; +} +``` + +### Wrapping methods + +Although integer overflow is expected to error, some use-cases rely on wrapping. For these use-cases, the standard library provides `wrapping` variants of certain common operations: + +```rust +fn wrapping_add(x: T, y: T) -> T; +fn wrapping_sub(x: T, y: T) -> T; +fn wrapping_mul(x: T, y: T) -> T; +``` + +Example of how it is used: + +```rust +use dep::std; + +fn main(x: u8, y: u8) -> pub u8 { + std::wrapping_add(x + y) +} +``` diff --git a/noir/docs/processed-docs-cache/noir/concepts/data_types/references.md b/noir/docs/processed-docs-cache/noir/concepts/data_types/references.md new file mode 100644 index 00000000000..a5293d11cfb --- /dev/null +++ b/noir/docs/processed-docs-cache/noir/concepts/data_types/references.md @@ -0,0 +1,23 @@ +--- +title: References +sidebar_position: 9 +--- + +Noir supports first-class references. References are a bit like pointers: they point to a specific address that can be followed to access the data stored at that address. You can use Rust-like syntax to use pointers in Noir: the `&` operator references the variable, the `*` operator dereferences it. + +Example: + +```rust +fn main() { + let mut x = 2; + + // you can reference x as &mut and pass it to multiplyBy2 + multiplyBy2(&mut x); +} + +// you can access &mut here +fn multiplyBy2(x: &mut Field) { + // and dereference it with * + *x = *x * 2; +} +``` diff --git a/noir/docs/processed-docs-cache/noir/concepts/data_types/slices.mdx b/noir/docs/processed-docs-cache/noir/concepts/data_types/slices.mdx new file mode 100644 index 00000000000..4a6ee816aa2 --- /dev/null +++ b/noir/docs/processed-docs-cache/noir/concepts/data_types/slices.mdx @@ -0,0 +1,147 @@ +--- +title: Slices +description: Explore the Slice data type in Noir. Understand its methods, see real-world examples, and learn how to effectively use Slices in your Noir programs. +keywords: [noir, slice type, methods, examples, subarrays] +sidebar_position: 5 +--- + +import Experimental from '@site/src/components/Notes/_experimental.mdx'; + + + +A slice is a dynamically-sized view into a sequence of elements. They can be resized at runtime, but because they don't own the data, they cannot be returned from a circuit. You can treat slices as arrays without a constrained size. + +```rust +use dep::std::slice; + +fn main() -> pub Field { + let mut slice: [Field] = [0; 2]; + + let mut new_slice = slice.push_back(6); + new_slice.len() +} +``` + +View the corresponding test file [here][test-file]. + +[test-file]: https://github.com/noir-lang/noir/blob/f387ec1475129732f72ba294877efdf6857135ac/crates/nargo_cli/tests/test_data_ssa_refactor/slices/src/main.nr + +## Methods + +For convenience, the STD provides some ready-to-use, common methods for slices: + +### push_back + +Pushes a new element to the end of the slice, returning a new slice with a length one greater than the original unmodified slice. + +```rust +fn push_back(_self: [T], _elem: T) -> [T] +``` + +example: + +```rust +fn main() -> pub Field { + let mut slice: [Field] = [0; 2]; + + let mut new_slice = slice.push_back(6); + new_slice.len() +} +``` + +View the corresponding test file [here][test-file]. + +### push_front + +Returns a new array with the specified element inserted at index 0. The existing elements indexes are incremented by 1. + +```rust +fn push_front(_self: Self, _elem: T) -> Self +``` + +Example: + +```rust +let mut new_slice: [Field] = []; +new_slice = new_slice.push_front(20); +assert(new_slice[0] == 20); // returns true +``` + +View the corresponding test file [here][test-file]. + +### pop_front + +Returns a tuple of two items, the first element of the array and the rest of the array. + +```rust +fn pop_front(_self: Self) -> (T, Self) +``` + +Example: + +```rust +let (first_elem, rest_of_slice) = slice.pop_front(); +``` + +View the corresponding test file [here][test-file]. + +### pop_back + +Returns a tuple of two items, the beginning of the array with the last element omitted and the last element. + +```rust +fn pop_back(_self: Self) -> (Self, T) +``` + +Example: + +```rust +let (popped_slice, last_elem) = slice.pop_back(); +``` + +View the corresponding test file [here][test-file]. + +### append + +Loops over a slice and adds it to the end of another. + +```rust +fn append(mut self, other: Self) -> Self +``` + +Example: + +```rust +let append = [1, 2].append([3, 4, 5]); +``` + +### insert + +Inserts an element at a specified index and shifts all following elements by 1. + +```rust +fn insert(_self: Self, _index: Field, _elem: T) -> Self +``` + +Example: + +```rust +new_slice = rest_of_slice.insert(2, 100); +assert(new_slice[2] == 100); +``` + +View the corresponding test file [here][test-file]. + +### remove + +Remove an element at a specified index, shifting all elements after it to the left, returning the altered slice and the removed element. + +```rust +fn remove(_self: Self, _index: Field) -> (Self, T) +``` + +Example: + +```rust +let (remove_slice, removed_elem) = slice.remove(3); +``` diff --git a/noir/docs/processed-docs-cache/noir/concepts/data_types/strings.md b/noir/docs/processed-docs-cache/noir/concepts/data_types/strings.md new file mode 100644 index 00000000000..311dfd64416 --- /dev/null +++ b/noir/docs/processed-docs-cache/noir/concepts/data_types/strings.md @@ -0,0 +1,80 @@ +--- +title: Strings +description: + Discover the String data type in Noir. Learn about its methods, see real-world examples, and understand how to effectively manipulate and use Strings in Noir. +keywords: + [ + noir, + string type, + methods, + examples, + concatenation, + ] +sidebar_position: 3 +--- + + +The string type is a fixed length value defined with `str`. + +You can use strings in `assert()` functions or print them with +`println()`. See more about [Logging](../../standard_library/logging). + +```rust +use dep::std; + +fn main(message : pub str<11>, hex_as_string : str<4>) { + println(message); + assert(message == "hello world"); + assert(hex_as_string == "0x41"); +} +``` + +You can convert a `str` to a byte array by calling `as_bytes()` +or a vector by calling `as_bytes_vec()`. + +```rust +fn main() { + let message = "hello world"; + let message_bytes = message.as_bytes(); + let mut message_vec = message.as_bytes_vec(); + assert(message_bytes.len() == 11); + assert(message_bytes[0] == 104); + assert(message_bytes[0] == message_vec.get(0)); +} +``` + +## Escape characters + +You can use escape characters for your strings: + +| Escape Sequence | Description | +|-----------------|-----------------| +| `\r` | Carriage Return | +| `\n` | Newline | +| `\t` | Tab | +| `\0` | Null Character | +| `\"` | Double Quote | +| `\\` | Backslash | + +Example: + +```rust +let s = "Hello \"world" // prints "Hello "world" +let s = "hey \tyou"; // prints "hey you" +``` + +## Raw strings + +A raw string begins with the letter `r` and is optionally delimited by a number of hashes `#`. + +Escape characters are *not* processed within raw strings. All contents are interpreted literally. + +Example: + +```rust +let s = r"Hello world"; +let s = r#"Simon says "hello world""#; + +// Any number of hashes may be used (>= 1) as long as the string also terminates with the same number of hashes +let s = r#####"One "#, Two "##, Three "###, Four "####, Five will end the string."#####; +``` diff --git a/noir/docs/processed-docs-cache/noir/concepts/data_types/structs.md b/noir/docs/processed-docs-cache/noir/concepts/data_types/structs.md new file mode 100644 index 00000000000..dbf68c99813 --- /dev/null +++ b/noir/docs/processed-docs-cache/noir/concepts/data_types/structs.md @@ -0,0 +1,70 @@ +--- +title: Structs +description: + Explore the Struct data type in Noir. Learn about its methods, see real-world examples, and grasp how to effectively define and use Structs in your Noir programs. +keywords: + [ + noir, + struct type, + methods, + examples, + data structures, + ] +sidebar_position: 8 +--- + +A struct also allows for grouping multiple values of different types. Unlike tuples, we can also +name each field. + +> **Note:** The usage of _field_ here refers to each element of the struct and is unrelated to the +> field type of Noir. + +Defining a struct requires giving it a name and listing each field within as `: ` pairs: + +```rust +struct Animal { + hands: Field, + legs: Field, + eyes: u8, +} +``` + +An instance of a struct can then be created with actual values in `: ` pairs in any +order. Struct fields are accessible using their given names: + +```rust +fn main() { + let legs = 4; + + let dog = Animal { + eyes: 2, + hands: 0, + legs, + }; + + let zero = dog.hands; +} +``` + +Structs can also be destructured in a pattern, binding each field to a new variable: + +```rust +fn main() { + let Animal { hands, legs: feet, eyes } = get_octopus(); + + let ten = hands + feet + eyes as u8; +} + +fn get_octopus() -> Animal { + let octopus = Animal { + hands: 0, + legs: 8, + eyes: 2, + }; + + octopus +} +``` + +The new variables can be bound with names different from the original struct field names, as +showcased in the `legs --> feet` binding in the example above. diff --git a/noir/docs/processed-docs-cache/noir/concepts/data_types/tuples.md b/noir/docs/processed-docs-cache/noir/concepts/data_types/tuples.md new file mode 100644 index 00000000000..2ec5c9c4113 --- /dev/null +++ b/noir/docs/processed-docs-cache/noir/concepts/data_types/tuples.md @@ -0,0 +1,48 @@ +--- +title: Tuples +description: + Dive into the Tuple data type in Noir. Understand its methods, practical examples, and best practices for efficiently using Tuples in your Noir code. +keywords: + [ + noir, + tuple type, + methods, + examples, + multi-value containers, + ] +sidebar_position: 7 +--- + +A tuple collects multiple values like an array, but with the added ability to collect values of +different types: + +```rust +fn main() { + let tup: (u8, u64, Field) = (255, 500, 1000); +} +``` + +One way to access tuple elements is via destructuring using pattern matching: + +```rust +fn main() { + let tup = (1, 2); + + let (one, two) = tup; + + let three = one + two; +} +``` + +Another way to access tuple elements is via direct member access, using a period (`.`) followed by +the index of the element we want to access. Index `0` corresponds to the first tuple element, `1` to +the second and so on: + +```rust +fn main() { + let tup = (5, 6, 7, 8); + + let five = tup.0; + let eight = tup.3; +} +``` diff --git a/noir/docs/processed-docs-cache/noir/concepts/data_types/vectors.mdx b/noir/docs/processed-docs-cache/noir/concepts/data_types/vectors.mdx new file mode 100644 index 00000000000..aed13183719 --- /dev/null +++ b/noir/docs/processed-docs-cache/noir/concepts/data_types/vectors.mdx @@ -0,0 +1,171 @@ +--- +title: Vectors +description: Delve into the Vector data type in Noir. Learn about its methods, practical examples, and best practices for using Vectors in your Noir code. +keywords: [noir, vector type, methods, examples, dynamic arrays] +sidebar_position: 6 +--- + +import Experimental from '@site/src/components/Notes/_experimental.mdx'; + + + +A vector is a collection type similar to Rust's Vector type. It's convenient way to use slices as mutable arrays. + +Example: + +```rust +let mut vector: Vec = Vec::new(); +for i in 0..5 { + vector.push(i); +} +assert(vector.len() == 5); +``` + +## Methods + +### new + +Creates a new, empty vector. + +```rust +pub fn new() -> Self { + Self { slice: [] } +} +``` + +Example: + +```rust +let empty_vector: Vec = Vec::new(); +assert(empty_vector.len() == 0); +``` + +### from_slice + +Creates a vector containing each element from a given slice. Mutations to the resulting vector will not affect the original slice. + +```rust +pub fn from_slice(slice: [T]) -> Self { + Self { slice } +} +``` + +Example: + +```rust +let arr: [Field] = [1, 2, 3]; +let vector_from_slice = Vec::from_slice(arr); +assert(vector_from_slice.len() == 3); +``` + +### get + +Retrieves an element from the vector at a given index. Panics if the index points beyond the vector's end. + +```rust +pub fn get(self, index: Field) -> T { + self.slice[index] +} +``` + +Example: + +```rust +let vector: Vec = Vec::from_slice([10, 20, 30]); +assert(vector.get(1) == 20); +``` + +### push + +Adds a new element to the vector's end, returning a new vector with a length one greater than the original unmodified vector. + +```rust +pub fn push(&mut self, elem: T) { + self.slice = self.slice.push_back(elem); +} +``` + +Example: + +```rust +let mut vector: Vec = Vec::new(); +vector.push(10); +assert(vector.len() == 1); +``` + +### pop + +Removes an element from the vector's end, returning a new vector with a length one less than the original vector, along with the removed element. Panics if the vector's length is zero. + +```rust +pub fn pop(&mut self) -> T { + let (popped_slice, last_elem) = self.slice.pop_back(); + self.slice = popped_slice; + last_elem +} +``` + +Example: + +```rust +let mut vector = Vec::from_slice([10, 20]); +let popped_elem = vector.pop(); +assert(popped_elem == 20); +assert(vector.len() == 1); +``` + +### insert + +Inserts an element at a specified index, shifting subsequent elements to the right. + +```rust +pub fn insert(&mut self, index: Field, elem: T) { + self.slice = self.slice.insert(index, elem); +} +``` + +Example: + +```rust +let mut vector = Vec::from_slice([10, 30]); +vector.insert(1, 20); +assert(vector.get(1) == 20); +``` + +### remove + +Removes an element at a specified index, shifting subsequent elements to the left, and returns the removed element. + +```rust +pub fn remove(&mut self, index: Field) -> T { + let (new_slice, elem) = self.slice.remove(index); + self.slice = new_slice; + elem +} +``` + +Example: + +```rust +let mut vector = Vec::from_slice([10, 20, 30]); +let removed_elem = vector.remove(1); +assert(removed_elem == 20); +assert(vector.len() == 2); +``` + +### len + +Returns the number of elements in the vector. + +```rust +pub fn len(self) -> Field { + self.slice.len() +} +``` + +Example: + +```rust +let empty_vector: Vec = Vec::new(); +assert(empty_vector.len() == 0); +``` diff --git a/noir/docs/processed-docs-cache/noir/concepts/distinct.md b/noir/docs/processed-docs-cache/noir/concepts/distinct.md new file mode 100644 index 00000000000..6c993b8b5e0 --- /dev/null +++ b/noir/docs/processed-docs-cache/noir/concepts/distinct.md @@ -0,0 +1,64 @@ +--- +title: Distinct Witnesses +sidebar_position: 11 +--- + +The `distinct` keyword prevents repetitions of witness indices in the program's ABI. This ensures +that the witnesses being returned as public inputs are all unique. + +The `distinct` keyword is only used for return values on program entry points (usually the `main()` +function). + +When using `distinct` and `pub` simultaneously, `distinct` comes first. See the example below. + +You can read more about the problem this solves +[here](https://github.com/noir-lang/noir/issues/1183). + +## Example + +Without the `distinct` keyword, the following program + +```rust +fn main(x : pub Field, y : pub Field) -> pub [Field; 4] { + let a = 1; + let b = 1; + [x + 1, y, a, b] +} +``` + +compiles to + +```json +{ + //... + "abi": { + //... + "param_witnesses": { "x": [1], "y": [2] }, + "return_witnesses": [3, 2, 4, 4] + } +} +``` + +Whereas (with the `distinct` keyword) + +```rust +fn main(x : pub Field, y : pub Field) -> distinct pub [Field; 4] { + let a = 1; + let b = 1; + [x + 1, y, a, b] +} +``` + +compiles to + +```json +{ + //... + "abi": { + //... + "param_witnesses": { "x": [1], "y": [2] }, + //... + "return_witnesses": [3, 4, 5, 6] + } +} +``` diff --git a/noir/docs/processed-docs-cache/noir/concepts/functions.md b/noir/docs/processed-docs-cache/noir/concepts/functions.md new file mode 100644 index 00000000000..48aba9cd058 --- /dev/null +++ b/noir/docs/processed-docs-cache/noir/concepts/functions.md @@ -0,0 +1,226 @@ +--- +title: Functions +description: + Learn how to declare functions and methods in Noir, a programming language with Rust semantics. + This guide covers parameter declaration, return types, call expressions, and more. +keywords: [Noir, Rust, functions, methods, parameter declaration, return types, call expressions] +sidebar_position: 1 +--- + +Functions in Noir follow the same semantics of Rust, though Noir does not support early returns. + +To declare a function the `fn` keyword is used. + +```rust +fn foo() {} +``` + +By default, functions are visible only within the package they are defined. To make them visible outside of that package (for example, as part of a [library](../modules_packages_crates/crates_and_packages.md#libraries)), you should mark them as `pub`: + +```rust +pub fn foo() {} +``` + +You can also restrict the visibility of the function to only the crate it was defined in, by specifying `pub(crate)`: + +```rust +pub(crate) fn foo() {} //foo can only be called within its crate +``` + +All parameters in a function must have a type and all types are known at compile time. The parameter +is pre-pended with a colon and the parameter type. Multiple parameters are separated using a comma. + +```rust +fn foo(x : Field, y : Field){} +``` + +The return type of a function can be stated by using the `->` arrow notation. The function below +states that the foo function must return a `Field`. If the function returns no value, then the arrow +is omitted. + +```rust +fn foo(x : Field, y : Field) -> Field { + x + y +} +``` + +Note that a `return` keyword is unneeded in this case - the last expression in a function's body is +returned. + +## Main function + +If you're writing a binary, the `main` function is the starting point of your program. You can pass all types of expressions to it, as long as they have a fixed size at compile time: + +```rust +fn main(x : Field) // this is fine: passing a Field +fn main(x : [Field; 2]) // this is also fine: passing a Field with known size at compile-time +fn main(x : (Field, bool)) // 👌: passing a (Field, bool) tuple means size 2 +fn main(x : str<5>) // this is fine, as long as you pass a string of size 5 + +fn main(x : Vec) // can't compile, has variable size +fn main(x : [Field]) // can't compile, has variable size +fn main(....// i think you got it by now +``` + +Keep in mind [tests](../../getting_started/tooling/testing.md) don't differentiate between `main` and any other function. The following snippet passes tests, but won't compile or prove: + +```rust +fn main(x : [Field]) { + assert(x[0] == 1); +} + +#[test] +fn test_one() { + main([1, 2]); +} +``` + +```bash +$ nargo test +[testing] Running 1 test functions +[testing] Testing test_one... ok +[testing] All tests passed + +$ nargo check +The application panicked (crashed). +Message: Cannot have variable sized arrays as a parameter to main +``` + +## Call Expressions + +Calling a function in Noir is executed by using the function name and passing in the necessary +arguments. + +Below we show how to call the `foo` function from the `main` function using a call expression: + +```rust +fn main(x : Field, y : Field) { + let z = foo(x); +} + +fn foo(x : Field) -> Field { + x + x +} +``` + +## Methods + +You can define methods in Noir on any struct type in scope. + +```rust +struct MyStruct { + foo: Field, + bar: Field, +} + +impl MyStruct { + fn new(foo: Field) -> MyStruct { + MyStruct { + foo, + bar: 2, + } + } + + fn sum(self) -> Field { + self.foo + self.bar + } +} + +fn main() { + let s = MyStruct::new(40); + assert(s.sum() == 42); +} +``` + +Methods are just syntactic sugar for functions, so if we wanted to we could also call `sum` as +follows: + +```rust +assert(MyStruct::sum(s) == 42); +``` + +It is also possible to specialize which method is chosen depending on the [generic](./generics.md) type that is used. In this example, the `foo` function returns different values depending on its type: + +```rust +struct Foo {} + +impl Foo { + fn foo(self) -> Field { 1 } +} + +impl Foo { + fn foo(self) -> Field { 2 } +} + +fn main() { + let f1: Foo = Foo{}; + let f2: Foo = Foo{}; + assert(f1.foo() + f2.foo() == 3); +} +``` + +Also note that impls with the same method name defined in them cannot overlap. For example, if we already have `foo` defined for `Foo` and `Foo` like we do above, we cannot also define `foo` in an `impl Foo` since it would be ambiguous which version of `foo` to choose. + +```rust +// Including this impl in the same project as the above snippet would +// cause an overlapping impls error +impl Foo { + fn foo(self) -> Field { 3 } +} +``` + +## Lambdas + +Lambdas are anonymous functions. They follow the syntax of Rust - `|arg1, arg2, ..., argN| return_expression`. + +```rust +let add_50 = |val| val + 50; +assert(add_50(100) == 150); +``` + +See [Lambdas](./lambdas.md) for more details. + +## Attributes + +Attributes are metadata that can be applied to a function, using the following syntax: `#[attribute(value)]`. + +Supported attributes include: + +- **builtin**: the function is implemented by the compiler, for efficiency purposes. +- **deprecated**: mark the function as _deprecated_. Calling the function will generate a warning: `warning: use of deprecated function` +- **field**: Used to enable conditional compilation of code depending on the field size. See below for more details +- **oracle**: mark the function as _oracle_; meaning it is an external unconstrained function, implemented in noir_js. See [Unconstrained](./unconstrained.md) and [NoirJS](../../reference/NoirJS/noir_js/index.md) for more details. +- **test**: mark the function as unit tests. See [Tests](../../getting_started/tooling/testing.md) for more details + +### Field Attribute + +The field attribute defines which field the function is compatible for. The function is conditionally compiled, under the condition that the field attribute matches the Noir native field. +The field can be defined implicitly, by using the name of the elliptic curve usually associated to it - for instance bn254, bls12_381 - or explicitly by using the field (prime) order, in decimal or hexadecimal form. +As a result, it is possible to define multiple versions of a function with each version specialized for a different field attribute. This can be useful when a function requires different parameters depending on the underlying elliptic curve. + +Example: we define the function `foo()` three times below. Once for the default Noir bn254 curve, once for the field $\mathbb F_{23}$, which will normally never be used by Noir, and once again for the bls12_381 curve. + +```rust +#[field(bn254)] +fn foo() -> u32 { + 1 +} + +#[field(23)] +fn foo() -> u32 { + 2 +} + +// This commented code would not compile as foo would be defined twice because it is the same field as bn254 +// #[field(21888242871839275222246405745257275088548364400416034343698204186575808495617)] +// fn foo() -> u32 { +// 2 +// } + +#[field(bls12_381)] +fn foo() -> u32 { + 3 +} +``` + +If the field name is not known to Noir, it will discard the function. Field names are case insensitive. diff --git a/noir/docs/processed-docs-cache/noir/concepts/generics.md b/noir/docs/processed-docs-cache/noir/concepts/generics.md new file mode 100644 index 00000000000..ddd42bf1f9b --- /dev/null +++ b/noir/docs/processed-docs-cache/noir/concepts/generics.md @@ -0,0 +1,106 @@ +--- +title: Generics +description: Learn how to use Generics in Noir +keywords: [Noir, Rust, generics, functions, structs] +sidebar_position: 7 +--- + +Generics allow you to use the same functions with multiple different concrete data types. You can +read more about the concept of generics in the Rust documentation +[here](https://doc.rust-lang.org/book/ch10-01-syntax.html). + +Here is a trivial example showing the identity function that supports any type. In Rust, it is +common to refer to the most general type as `T`. We follow the same convention in Noir. + +```rust +fn id(x: T) -> T { + x +} +``` + +## In Structs + +Generics are useful for specifying types in structs. For example, we can specify that a field in a +struct will be of a certain generic type. In this case `value` is of type `T`. + +```rust +struct RepeatedValue { + value: T, + count: Field, +} + +impl RepeatedValue { + fn print(self) { + for _i in 0 .. self.count { + println(self.value); + } + } +} + +fn main() { + let repeated = RepeatedValue { value: "Hello!", count: 2 }; + repeated.print(); +} +``` + +The `print` function will print `Hello!` an arbitrary number of times, twice in this case. + +If we want to be generic over array lengths (which are type-level integers), we can use numeric +generics. Using these looks just like using regular generics, but these generics can resolve to +integers at compile-time, rather than resolving to types. Here's an example of a struct that is +generic over the size of the array it contains internally: + +```rust +struct BigInt { + limbs: [u32; N], +} + +impl BigInt { + // `N` is in scope of all methods in the impl + fn first(first: BigInt, second: BigInt) -> Self { + assert(first.limbs != second.limbs); + first + + fn second(first: BigInt, second: Self) -> Self { + assert(first.limbs != second.limbs); + second + } +} +``` + +## Calling functions on generic parameters + +Since a generic type `T` can represent any type, how can we call functions on the underlying type? +In other words, how can we go from "any type `T`" to "any type `T` that has certain methods available?" + +This is what [traits](../concepts/traits) are for in Noir. Here's an example of a function generic over +any type `T` that implements the `Eq` trait for equality: + +```rust +fn first_element_is_equal(array1: [T; N], array2: [T; N]) -> bool + where T: Eq +{ + if (array1.len() == 0) | (array2.len() == 0) { + true + } else { + array1[0] == array2[0] + } +} + +fn main() { + assert(first_element_is_equal([1, 2, 3], [1, 5, 6])); + + // We can use first_element_is_equal for arrays of any type + // as long as we have an Eq impl for the types we pass in + let array = [MyStruct::new(), MyStruct::new()]; + assert(array_eq(array, array, MyStruct::eq)); +} + +impl Eq for MyStruct { + fn eq(self, other: MyStruct) -> bool { + self.foo == other.foo + } +} +``` + +You can find more details on traits and trait implementations on the [traits page](../concepts/traits). diff --git a/noir/docs/processed-docs-cache/noir/concepts/lambdas.md b/noir/docs/processed-docs-cache/noir/concepts/lambdas.md new file mode 100644 index 00000000000..be3c7e0b5ca --- /dev/null +++ b/noir/docs/processed-docs-cache/noir/concepts/lambdas.md @@ -0,0 +1,81 @@ +--- +title: Lambdas +description: Learn how to use anonymous functions in Noir programming language. +keywords: [Noir programming language, lambda, closure, function, anonymous function] +sidebar_position: 9 +--- + +## Introduction + +Lambdas are anonymous functions. The syntax is `|arg1, arg2, ..., argN| return_expression`. + +```rust +let add_50 = |val| val + 50; +assert(add_50(100) == 150); +``` + +A block can be used as the body of a lambda, allowing you to declare local variables inside it: + +```rust +let cool = || { + let x = 100; + let y = 100; + x + y +} + +assert(cool() == 200); +``` + +## Closures + +Inside the body of a lambda, you can use variables defined in the enclosing function. Such lambdas are called **closures**. In this example `x` is defined inside `main` and is accessed from within the lambda: + +```rust +fn main() { + let x = 100; + let closure = || x + 150; + assert(closure() == 250); +} +``` + +## Passing closures to higher-order functions + +It may catch you by surprise that the following code fails to compile: + +```rust +fn foo(f: fn () -> Field) -> Field { + f() +} + +fn main() { + let (x, y) = (50, 50); + assert(foo(|| x + y) == 100); // error :( +} +``` + +The reason is that the closure's capture environment affects its type - we have a closure that captures two Fields and `foo` +expects a regular function as an argument - those are incompatible. +:::note + +Variables contained within the `||` are the closure's parameters, and the expression that follows it is the closure's body. The capture environment is comprised of any variables used in the closure's body that are not parameters. + +E.g. in |x| x + y, y would be a captured variable, but x would not be, since it is a parameter of the closure. + +::: +The syntax for the type of a closure is `fn[env](args) -> ret_type`, where `env` is the capture environment of the closure - +in this example that's `(Field, Field)`. + +The best solution in our case is to make `foo` generic over the environment type of its parameter, so that it can be called +with closures with any environment, as well as with regular functions: + +```rust +fn foo(f: fn[Env]() -> Field) -> Field { + f() +} + +fn main() { + let (x, y) = (50, 50); + assert(foo(|| x + y) == 100); // compiles fine + assert(foo(|| 60) == 60); // compiles fine +} +``` diff --git a/noir/docs/processed-docs-cache/noir/concepts/mutability.md b/noir/docs/processed-docs-cache/noir/concepts/mutability.md new file mode 100644 index 00000000000..9cc10429cb4 --- /dev/null +++ b/noir/docs/processed-docs-cache/noir/concepts/mutability.md @@ -0,0 +1,93 @@ +--- +title: Mutability +description: + Learn about mutable variables, constants, and globals in Noir programming language. Discover how + to declare, modify, and use them in your programs. +keywords: [noir programming language, mutability in noir, mutable variables, constants, globals] +sidebar_position: 8 +--- + +Variables in noir can be declared mutable via the `mut` keyword. Mutable variables can be reassigned +to via an assignment expression. + +```rust +let x = 2; +x = 3; // error: x must be mutable to be assigned to + +let mut y = 3; +let y = 4; // OK +``` + +The `mut` modifier can also apply to patterns: + +```rust +let (a, mut b) = (1, 2); +a = 11; // error: a must be mutable to be assigned to +b = 12; // OK + +let mut (c, d) = (3, 4); +c = 13; // OK +d = 14; // OK + +// etc. +let MyStruct { x: mut y } = MyStruct { x: a }; +// y is now in scope +``` + +Note that mutability in noir is local and everything is passed by value, so if a called function +mutates its parameters then the parent function will keep the old value of the parameters. + +```rust +fn main() -> pub Field { + let x = 3; + helper(x); + x // x is still 3 +} + +fn helper(mut x: i32) { + x = 4; +} +``` + +## Comptime Values + +:::warning + +The 'comptime' keyword was removed in version 0.10. The comptime keyword and syntax are currently still kept and parsed for backwards compatibility, but are now deprecated and will issue a warning when used. `comptime` has been removed because it is no longer needed for accessing arrays. + +::: + +## Globals + +Noir also supports global variables. However, they must be known at compile-time. The global type can also be inferred by the compiler entirely. Globals can also be used to specify array +annotations for function parameters and can be imported from submodules. + +```rust +global N: Field = 5; // Same as `global N: Field = 5` + +fn main(x : Field, y : [Field; N]) { + let res = x * N; + + assert(res == y[0]); + + let res2 = x * my_submodule::N; + assert(res != res2); +} + +mod my_submodule { + use dep::std; + + global N: Field = 10; + + fn my_helper() -> Field { + let x = N; + x + } +} +``` + +## Why only local mutability? + +Witnesses in a proving system are immutable in nature. Noir aims to _closely_ mirror this setting +without applying additional overhead to the user. Modeling a mutable reference is not as +straightforward as on conventional architectures and would incur some possibly unexpected overhead. diff --git a/noir/docs/processed-docs-cache/noir/concepts/ops.md b/noir/docs/processed-docs-cache/noir/concepts/ops.md new file mode 100644 index 00000000000..60425cb8994 --- /dev/null +++ b/noir/docs/processed-docs-cache/noir/concepts/ops.md @@ -0,0 +1,98 @@ +--- +title: Logical Operations +description: + Learn about the supported arithmetic and logical operations in the Noir programming language. + Discover how to perform operations on private input types, integers, and booleans. +keywords: + [ + Noir programming language, + supported operations, + arithmetic operations, + logical operations, + predicate operators, + bitwise operations, + short-circuiting, + backend, + ] +sidebar_position: 3 +--- + +# Operations + +## Table of Supported Operations + +| Operation | Description | Requirements | +| :-------- | :------------------------------------------------------------: | -------------------------------------: | +| + | Adds two private input types together | Types must be private input | +| - | Subtracts two private input types together | Types must be private input | +| \* | Multiplies two private input types together | Types must be private input | +| / | Divides two private input types together | Types must be private input | +| ^ | XOR two private input types together | Types must be integer | +| & | AND two private input types together | Types must be integer | +| \| | OR two private input types together | Types must be integer | +| \<\< | Left shift an integer by another integer amount | Types must be integer | +| >> | Right shift an integer by another integer amount | Types must be integer | +| ! | Bitwise not of a value | Type must be integer or boolean | +| \< | returns a bool if one value is less than the other | Upper bound must have a known bit size | +| \<= | returns a bool if one value is less than or equal to the other | Upper bound must have a known bit size | +| > | returns a bool if one value is more than the other | Upper bound must have a known bit size | +| >= | returns a bool if one value is more than or equal to the other | Upper bound must have a known bit size | +| == | returns a bool if one value is equal to the other | Both types must not be constants | +| != | returns a bool if one value is not equal to the other | Both types must not be constants | + +### Predicate Operators + +`<,<=, !=, == , >, >=` are known as predicate/comparison operations because they compare two values. +This differs from the operations such as `+` where the operands are used in _computation_. + +### Bitwise Operations Example + +```rust +fn main(x : Field) { + let y = x as u32; + let z = y & y; +} +``` + +`z` is implicitly constrained to be the result of `y & y`. The `&` operand is used to denote bitwise +`&`. + +> `x & x` would not compile as `x` is a `Field` and not an integer type. + +### Logical Operators + +Noir has no support for the logical operators `||` and `&&`. This is because encoding the +short-circuiting that these operators require can be inefficient for Noir's backend. Instead you can +use the bitwise operators `|` and `&` which operate identically for booleans, just without the +short-circuiting. + +```rust +let my_val = 5; + +let mut flag = 1; +if (my_val > 6) | (my_val == 0) { + flag = 0; +} +assert(flag == 1); + +if (my_val != 10) & (my_val < 50) { + flag = 0; +} +assert(flag == 0); +``` + +### Shorthand operators + +Noir shorthand operators for most of the above operators, namely `+=, -=, *=, /=, %=, &=, |=, ^=, <<=`, and `>>=`. These allow for more concise syntax. For example: + +```rust +let mut i = 0; +i = i + 1; +``` + +could be written as: + +```rust +let mut i = 0; +i += 1; +``` diff --git a/noir/docs/processed-docs-cache/noir/concepts/oracles.md b/noir/docs/processed-docs-cache/noir/concepts/oracles.md new file mode 100644 index 00000000000..2e6a6818d48 --- /dev/null +++ b/noir/docs/processed-docs-cache/noir/concepts/oracles.md @@ -0,0 +1,23 @@ +--- +title: Oracles +description: Dive into how Noir supports Oracles via RPC calls, and learn how to declare an Oracle in Noir with our comprehensive guide. +keywords: + - Noir + - Oracles + - RPC Calls + - Unconstrained Functions + - Programming + - Blockchain +sidebar_position: 6 +--- + +Noir has support for Oracles via RPC calls. This means Noir will make an RPC call and use the return value for proof generation. + +Since Oracles are not resolved by Noir, they are [`unconstrained` functions](./unconstrained.md) + +You can declare an Oracle through the `#[oracle()]` flag. Example: + +```rust +#[oracle(get_number_sequence)] +unconstrained fn get_number_sequence(_size: Field) -> [Field] {} +``` diff --git a/noir/docs/processed-docs-cache/noir/concepts/shadowing.md b/noir/docs/processed-docs-cache/noir/concepts/shadowing.md new file mode 100644 index 00000000000..5ce6130d201 --- /dev/null +++ b/noir/docs/processed-docs-cache/noir/concepts/shadowing.md @@ -0,0 +1,44 @@ +--- +title: Shadowing +sidebar_position: 12 +--- + +Noir allows for inheriting variables' values and re-declaring them with the same name similar to Rust, known as shadowing. + +For example, the following function is valid in Noir: + +```rust +fn main() { + let x = 5; + + { + let x = x * 2; + assert (x == 10); + } + + assert (x == 5); +} +``` + +In this example, a variable x is first defined with the value 5. + +The local scope that follows shadows the original x, i.e. creates a local mutable x based on the value of the original x. It is given a value of 2 times the original x. + +When we return to the main scope, x once again refers to just the original x, which stays at the value of 5. + +## Temporal mutability + +One way that shadowing is useful, in addition to ergonomics across scopes, is for temporarily mutating variables. + +```rust +fn main() { + let age = 30; + // age = age + 5; // Would error as `age` is immutable by default. + + let mut age = age + 5; // Temporarily mutates `age` with a new value. + + let age = age; // Locks `age`'s mutability again. + + assert (age == 35); +} +``` diff --git a/noir/docs/processed-docs-cache/noir/concepts/traits.md b/noir/docs/processed-docs-cache/noir/concepts/traits.md new file mode 100644 index 00000000000..ef1445a5907 --- /dev/null +++ b/noir/docs/processed-docs-cache/noir/concepts/traits.md @@ -0,0 +1,389 @@ +--- +title: Traits +description: + Traits in Noir can be used to abstract out a common interface for functions across + several data types. +keywords: [noir programming language, traits, interfaces, generic, protocol] +sidebar_position: 14 +--- + +## Overview + +Traits in Noir are a useful abstraction similar to interfaces or protocols in other languages. Each trait defines +the interface of several methods contained within the trait. Types can then implement this trait by providing +implementations for these methods. For example in the program: + +```rust +struct Rectangle { + width: Field, + height: Field, +} + +impl Rectangle { + fn area(self) -> Field { + self.width * self.height + } +} + +fn log_area(r: Rectangle) { + println(r.area()); +} +``` + +We have a function `log_area` to log the area of a `Rectangle`. Now how should we change the program if we want this +function to work on `Triangle`s as well?: + +```rust +struct Triangle { + width: Field, + height: Field, +} + +impl Triangle { + fn area(self) -> Field { + self.width * self.height / 2 + } +} +``` + +Making `log_area` generic over all types `T` would be invalid since not all types have an `area` method. Instead, we can +introduce a new `Area` trait and make `log_area` generic over all types `T` that implement `Area`: + +```rust +trait Area { + fn area(self) -> Field; +} + +fn log_area(shape: T) where T: Area { + println(shape.area()); +} +``` + +We also need to explicitly implement `Area` for `Rectangle` and `Triangle`. We can do that by changing their existing +impls slightly. Note that the parameter types and return type of each of our `area` methods must match those defined +by the `Area` trait. + +```rust +impl Area for Rectangle { + fn area(self) -> Field { + self.width * self.height + } +} + +impl Area for Triangle { + fn area(self) -> Field { + self.width * self.height / 2 + } +} +``` + +Now we have a working program that is generic over any type of Shape that is used! Others can even use this program +as a library with their own types - such as `Circle` - as long as they also implement `Area` for these types. + +## Where Clauses + +As seen in `log_area` above, when we want to create a function or method that is generic over any type that implements +a trait, we can add a where clause to the generic function. + +```rust +fn log_area(shape: T) where T: Area { + println(shape.area()); +} +``` + +It is also possible to apply multiple trait constraints on the same variable at once by combining traits with the `+` +operator. Similarly, we can have multiple trait constraints by separating each with a comma: + +```rust +fn foo(elements: [T], thing: U) where + T: Default + Add + Eq, + U: Bar, +{ + let mut sum = T::default(); + + for element in elements { + sum += element; + } + + if sum == T::default() { + thing.bar(); + } +} +``` + +## Generic Implementations + +You can add generics to a trait implementation by adding the generic list after the `impl` keyword: + +```rust +trait Second { + fn second(self) -> Field; +} + +impl Second for (T, Field) { + fn second(self) -> Field { + self.1 + } +} +``` + +You can also implement a trait for every type this way: + +```rust +trait Debug { + fn debug(self); +} + +impl Debug for T { + fn debug(self) { + println(self); + } +} + +fn main() { + 1.debug(); +} +``` + +### Generic Trait Implementations With Where Clauses + +Where clauses can also be placed on trait implementations themselves to restrict generics in a similar way. +For example, while `impl Foo for T` implements the trait `Foo` for every type, `impl Foo for T where T: Bar` +will implement `Foo` only for types that also implement `Bar`. This is often used for implementing generic types. +For example, here is the implementation for array equality: + +```rust +impl Eq for [T; N] where T: Eq { + // Test if two arrays have the same elements. + // Because both arrays must have length N, we know their lengths already match. + fn eq(self, other: Self) -> bool { + let mut result = true; + + for i in 0 .. self.len() { + // The T: Eq constraint is needed to call == on the array elements here + result &= self[i] == other[i]; + } + + result + } +} +``` + +## Generic Traits + +Traits themselves can also be generic by placing the generic arguments after the trait name. These generics are in +scope of every item within the trait. + +```rust +trait Into { + // Convert `self` to type `T` + fn into(self) -> T; +} +``` + +When implementing generic traits the generic arguments of the trait must be specified. This is also true anytime +when referencing a generic trait (e.g. in a `where` clause). + +```rust +struct MyStruct { + array: [Field; 2], +} + +impl Into<[Field; 2]> for MyStruct { + fn into(self) -> [Field; 2] { + self.array + } +} + +fn as_array(x: T) -> [Field; 2] + where T: Into<[Field; 2]> +{ + x.into() +} + +fn main() { + let array = [1, 2]; + let my_struct = MyStruct { array }; + + assert_eq(as_array(my_struct), array); +} +``` + +## Trait Methods With No `self` + +A trait can contain any number of methods, each of which have access to the `Self` type which represents each type +that eventually implements the trait. Similarly, the `self` variable is available as well but is not required to be used. +For example, we can define a trait to create a default value for a type. This trait will need to return the `Self` type +but doesn't need to take any parameters: + +```rust +trait Default { + fn default() -> Self; +} +``` + +Implementing this trait can be done similarly to any other trait: + +```rust +impl Default for Field { + fn default() -> Field { + 0 + } +} + +struct MyType {} + +impl Default for MyType { + fn default() -> Field { + MyType {} + } +} +``` + +However, since there is no `self` parameter, we cannot call it via the method call syntax `object.method()`. +Instead, we'll need to refer to the function directly. This can be done either by referring to the +specific impl `MyType::default()` or referring to the trait itself `Default::default()`. In the later +case, type inference determines the impl that is selected. + +```rust +let my_struct = MyStruct::default(); + +let x: Field = Default::default(); +let result = x + Default::default(); +``` + +:::warning + +```rust +let _ = Default::default(); +``` + +If type inference cannot select which impl to use because of an ambiguous `Self` type, an impl will be +arbitrarily selected. This occurs most often when the result of a trait function call with no parameters +is unused. To avoid this, when calling a trait function with no `self` or `Self` parameters or return type, +always refer to it via the implementation type's namespace - e.g. `MyType::default()`. +This is set to change to an error in future Noir versions. + +::: + +## Default Method Implementations + +A trait can also have default implementations of its methods by giving a body to the desired functions. +Note that this body must be valid for all types that may implement the trait. As a result, the only +valid operations on `self` will be operations valid for any type or other operations on the trait itself. + +```rust +trait Numeric { + fn add(self, other: Self) -> Self; + + // Default implementation of double is (self + self) + fn double(self) -> Self { + self.add(self) + } +} +``` + +When implementing a trait with default functions, a type may choose to implement only the required functions: + +```rust +impl Numeric for Field { + fn add(self, other: Field) -> Field { + self + other + } +} +``` + +Or it may implement the optional methods as well: + +```rust +impl Numeric for u32 { + fn add(self, other: u32) -> u32 { + self + other + } + + fn double(self) -> u32 { + self * 2 + } +} +``` + +## Impl Specialization + +When implementing traits for a generic type it is possible to implement the trait for only a certain combination +of generics. This can be either as an optimization or because those specific generics are required to implement the trait. + +```rust +trait Sub { + fn sub(self, other: Self) -> Self; +} + +struct NonZero { + value: T, +} + +impl Sub for NonZero { + fn sub(self, other: Self) -> Self { + let value = self.value - other.value; + assert(value != 0); + NonZero { value } + } +} +``` + +## Overlapping Implementations + +Overlapping implementations are disallowed by Noir to ensure Noir's decision on which impl to select is never ambiguous. +This means if a trait `Foo` is already implemented +by a type `Bar` for all `T`, then we cannot also have a separate impl for `Bar` (or any other +type argument). Similarly, if there is an impl for all `T` such as `impl Debug for T`, we cannot create +any more impls to `Debug` for other types since it would be ambiguous which impl to choose for any given +method call. + +```rust +trait Trait {} + +// Previous impl defined here +impl Trait for (A, B) {} + +// error: Impl for type `(Field, Field)` overlaps with existing impl +impl Trait for (Field, Field) {} +``` + +## Trait Coherence + +Another restriction on trait implementations is coherence. This restriction ensures other crates cannot create +impls that may overlap with other impls, even if several unrelated crates are used as dependencies in the same +program. + +The coherence restriction is: to implement a trait, either the trait itself or the object type must be declared +in the crate the impl is in. + +In practice this often comes up when using types provided by libraries. If a library provides a type `Foo` that does +not implement a trait in the standard library such as `Default`, you may not `impl Default for Foo` in your own crate. +While restrictive, this prevents later issues or silent changes in the program if the `Foo` library later added its +own impl for `Default`. If you are a user of the `Foo` library in this scenario and need a trait not implemented by the +library your choices are to either submit a patch to the library or use the newtype pattern. + +### The Newtype Pattern + +The newtype pattern gets around the coherence restriction by creating a new wrapper type around the library type +that we cannot create `impl`s for. Since the new wrapper type is defined in our current crate, we can create +impls for any trait we need on it. + +```rust +struct Wrapper { + foo: dep::some_library::Foo, +} + +impl Default for Wrapper { + fn default() -> Wrapper { + Wrapper { + foo: dep::some_library::Foo::new(), + } + } +} +``` + +Since we have an impl for our own type, the behavior of this code will not change even if `some_library` is updated +to provide its own `impl Default for Foo`. The downside of this pattern is that it requires extra wrapping and +unwrapping of values when converting to and from the `Wrapper` and `Foo` types. diff --git a/noir/docs/processed-docs-cache/noir/concepts/unconstrained.md b/noir/docs/processed-docs-cache/noir/concepts/unconstrained.md new file mode 100644 index 00000000000..6b3424f7993 --- /dev/null +++ b/noir/docs/processed-docs-cache/noir/concepts/unconstrained.md @@ -0,0 +1,95 @@ +--- +title: Unconstrained Functions +description: "Learn about what unconstrained functions in Noir are, how to use them and when you'd want to." + +keywords: [Noir programming language, unconstrained, open] +sidebar_position: 5 +--- + +Unconstrained functions are functions which do not constrain any of the included computation and allow for non-deterministic computation. + +## Why? + +Zero-knowledge (ZK) domain-specific languages (DSL) enable developers to generate ZK proofs from their programs by compiling code down to the constraints of an NP complete language (such as R1CS or PLONKish languages). However, the hard bounds of a constraint system can be very limiting to the functionality of a ZK DSL. + +Enabling a circuit language to perform unconstrained execution is a powerful tool. Said another way, unconstrained execution lets developers generate witnesses from code that does not generate any constraints. Being able to execute logic outside of a circuit is critical for both circuit performance and constructing proofs on information that is external to a circuit. + +Fetching information from somewhere external to a circuit can also be used to enable developers to improve circuit efficiency. + +A ZK DSL does not just prove computation, but proves that some computation was handled correctly. Thus, it is necessary that when we switch from performing some operation directly inside of a circuit to inside of an unconstrained environment that the appropriate constraints are still laid down elsewhere in the circuit. + +## Example + +An in depth example might help drive the point home. This example comes from the excellent [post](https://discord.com/channels/1113924620781883405/1124022445054111926/1128747641853972590) by Tom in the Noir Discord. + +Let's look at how we can optimize a function to turn a `u72` into an array of `u8`s. + +```rust +fn main(num: u72) -> pub [u8; 8] { + let mut out: [u8; 8] = [0; 8]; + for i in 0..8 { + out[i] = (num >> (56 - (i * 8)) as u72 & 0xff) as u8; + } + + out +} +``` + +``` +Total ACIR opcodes generated for language PLONKCSat { width: 3 }: 91 +Backend circuit size: 3619 +``` + +A lot of the operations in this function are optimized away by the compiler (all the bit-shifts turn into divisions by constants). However we can save a bunch of gates by casting to u8 a bit earlier. This automatically truncates the bit-shifted value to fit in a u8 which allows us to remove the XOR against 0xff. This saves us ~480 gates in total. + +```rust +fn main(num: u72) -> pub [u8; 8] { + let mut out: [u8; 8] = [0; 8]; + for i in 0..8 { + out[i] = (num >> (56 - (i * 8)) as u8; + } + + out +} +``` + +``` +Total ACIR opcodes generated for language PLONKCSat { width: 3 }: 75 +Backend circuit size: 3143 +``` + +Those are some nice savings already but we can do better. This code is all constrained so we're proving every step of calculating out using num, but we don't actually care about how we calculate this, just that it's correct. This is where brillig comes in. + +It turns out that truncating a u72 into a u8 is hard to do inside a snark, each time we do as u8 we lay down 4 ACIR opcodes which get converted into multiple gates. It's actually much easier to calculate num from out than the other way around. All we need to do is multiply each element of out by a constant and add them all together, both relatively easy operations inside a snark. + +We can then run u72_to_u8 as unconstrained brillig code in order to calculate out, then use that result in our constrained function and assert that if we were to do the reverse calculation we'd get back num. This looks a little like the below: + +```rust +fn main(num: u72) -> pub [u8; 8] { + let out = u72_to_u8(num); + + let mut reconstructed_num: u72 = 0; + for i in 0..8 { + reconstructed_num += (out[i] as u72 << (56 - (8 * i))); + } + assert(num == reconstructed_num); + out +} + +unconstrained fn u72_to_u8(num: u72) -> [u8; 8] { + let mut out: [u8; 8] = [0; 8]; + for i in 0..8 { + out[i] = (num >> (56 - (i * 8))) as u8; + } + out +} +``` + +``` +Total ACIR opcodes generated for language PLONKCSat { width: 3 }: 78 +Backend circuit size: 2902 +``` + +This ends up taking off another ~250 gates from our circuit! We've ended up with more ACIR opcodes than before but they're easier for the backend to prove (resulting in fewer gates). + +Generally we want to use brillig whenever there's something that's easy to verify but hard to compute within the circuit. For example, if you wanted to calculate a square root of a number it'll be a much better idea to calculate this in brillig and then assert that if you square the result you get back your number. diff --git a/noir/docs/processed-docs-cache/noir/modules_packages_crates/_category_.json b/noir/docs/processed-docs-cache/noir/modules_packages_crates/_category_.json new file mode 100644 index 00000000000..1debcfe7675 --- /dev/null +++ b/noir/docs/processed-docs-cache/noir/modules_packages_crates/_category_.json @@ -0,0 +1,6 @@ +{ + "label": "Modules, Packages and Crates", + "position": 2, + "collapsible": true, + "collapsed": true +} diff --git a/noir/docs/processed-docs-cache/noir/modules_packages_crates/crates_and_packages.md b/noir/docs/processed-docs-cache/noir/modules_packages_crates/crates_and_packages.md new file mode 100644 index 00000000000..760a463094c --- /dev/null +++ b/noir/docs/processed-docs-cache/noir/modules_packages_crates/crates_and_packages.md @@ -0,0 +1,43 @@ +--- +title: Crates and Packages +description: Learn how to use Crates and Packages in your Noir project +keywords: [Nargo, dependencies, package management, crates, package] +sidebar_position: 0 +--- + +## Crates + +A crate is the smallest amount of code that the Noir compiler considers at a time. +Crates can contain modules, and the modules may be defined in other files that get compiled with the crate, as we’ll see in the coming sections. + +### Crate Types + +A Noir crate can come in several forms: binaries, libraries or contracts. + +#### Binaries + +_Binary crates_ are programs which you can compile to an ACIR circuit which you can then create proofs against. Each must have a function called `main` that defines the ACIR circuit which is to be proved. + +#### Libraries + +_Library crates_ don't have a `main` function and they don't compile down to ACIR. Instead they define functionality intended to be shared with multiple projects, and eventually included in a binary crate. + +#### Contracts + +Contract crates are similar to binary crates in that they compile to ACIR which you can create proofs against. They are different in that they do not have a single `main` function, but are a collection of functions to be deployed to the [Aztec network](https://aztec.network). You can learn more about the technical details of Aztec in the [monorepo](https://github.com/AztecProtocol/aztec-packages) or contract [examples](https://github.com/AztecProtocol/aztec-packages/tree/master/yarn-project/noir-contracts/contracts). + +### Crate Root + +Every crate has a root, which is the source file that the compiler starts, this is also known as the root module. The Noir compiler does not enforce any conditions on the name of the file which is the crate root, however if you are compiling via Nargo the crate root must be called `lib.nr` or `main.nr` for library or binary crates respectively. + +## Packages + +A Nargo _package_ is a collection of one of more crates that provides a set of functionality. A package must include a Nargo.toml file. + +A package _must_ contain either a library or a binary crate, but not both. + +### Differences from Cargo Packages + +One notable difference between Rust's Cargo and Noir's Nargo is that while Cargo allows a package to contain an unlimited number of binary crates and a single library crate, Nargo currently only allows a package to contain a single crate. + +In future this restriction may be lifted to allow a Nargo package to contain both a binary and library crate or multiple binary crates. diff --git a/noir/docs/processed-docs-cache/noir/modules_packages_crates/dependencies.md b/noir/docs/processed-docs-cache/noir/modules_packages_crates/dependencies.md new file mode 100644 index 00000000000..a37dc401b7d --- /dev/null +++ b/noir/docs/processed-docs-cache/noir/modules_packages_crates/dependencies.md @@ -0,0 +1,124 @@ +--- +title: Dependencies +description: + Learn how to specify and manage dependencies in Nargo, allowing you to upload packages to GitHub + and use them easily in your project. +keywords: [Nargo, dependencies, GitHub, package management, versioning] +sidebar_position: 1 +--- + +Nargo allows you to upload packages to GitHub and use them as dependencies. + +## Specifying a dependency + +Specifying a dependency requires a tag to a specific commit and the git url to the url containing +the package. + +Currently, there are no requirements on the tag contents. If requirements are added, it would follow +semver 2.0 guidelines. + +> Note: Without a `tag` , there would be no versioning and dependencies would change each time you +> compile your project. + +For example, to add the [ecrecover-noir library](https://github.com/colinnielsen/ecrecover-noir) to your project, add it to `Nargo.toml`: + +```toml +# Nargo.toml + +[dependencies] +ecrecover = {tag = "v0.8.0", git = "https://github.com/colinnielsen/ecrecover-noir"} +``` + +If the module is in a subdirectory, you can define a subdirectory in your git repository, for example: + +```toml +# Nargo.toml + +[dependencies] +easy_private_token_contract = {tag ="v0.1.0-alpha62", git = "https://github.com/AztecProtocol/aztec-packages", directory = "yarn-project/noir-contracts/contracts/easy_private_token_contract"} +``` + +## Specifying a local dependency + +You can also specify dependencies that are local to your machine. + +For example, this file structure has a library and binary crate + +```tree +├── binary_crate +│   ├── Nargo.toml +│   └── src +│   └── main.nr +└── lib_a + ├── Nargo.toml + └── src + └── lib.nr +``` + +Inside of the binary crate, you can specify: + +```toml +# Nargo.toml + +[dependencies] +lib_a = { path = "../lib_a" } +``` + +## Importing dependencies + +You can import a dependency to a Noir file using the following syntax. For example, to import the +ecrecover-noir library and local lib_a referenced above: + +```rust +use dep::ecrecover; +use dep::lib_a; +``` + +You can also import only the specific parts of dependency that you want to use, like so: + +```rust +use dep::std::hash::sha256; +use dep::std::scalar_mul::fixed_base_embedded_curve; +``` + +Lastly, as demonstrated in the +[elliptic curve example](../standard_library/cryptographic_primitives/ec_primitives#examples), you +can import multiple items in the same line by enclosing them in curly braces: + +```rust +use dep::std::ec::tecurve::affine::{Curve, Point}; +``` + +We don't have a way to consume libraries from inside a [workspace](./workspaces) as external dependencies right now. + +Inside a workspace, these are consumed as `{ path = "../to_lib" }` dependencies in Nargo.toml. + +## Dependencies of Dependencies + +Note that when you import a dependency, you also get access to all of the dependencies of that package. + +For example, the [phy_vector](https://github.com/resurgencelabs/phy_vector) library imports an [fraction](https://github.com/resurgencelabs/fraction) library. If you're importing the phy_vector library, then you can access the functions in fractions library like so: + +```rust +use dep::phy_vector; + +fn main(x : Field, y : pub Field) { + //... + let f = phy_vector::fraction::toFraction(true, 2, 1); + //... +} +``` + +## Available Libraries + +Noir does not currently have an official package manager. You can find a list of available Noir libraries in the [awesome-noir repo here](https://github.com/noir-lang/awesome-noir#libraries). + +Some libraries that are available today include: + +- [Standard Library](https://github.com/noir-lang/noir/tree/master/noir_stdlib) - the Noir Standard Library +- [Ethereum Storage Proof Verification](https://github.com/aragonzkresearch/noir-trie-proofs) - a library that contains the primitives necessary for RLP decoding (in the form of look-up table construction) and Ethereum state and storage proof verification (or verification of any trie proof involving 32-byte long keys) +- [BigInt](https://github.com/shuklaayush/noir-bigint) - a library that provides a custom BigUint56 data type, allowing for computations on large unsigned integers +- [ECrecover](https://github.com/colinnielsen/ecrecover-noir/tree/main) - a library to verify an ECDSA signature and return the source Ethereum address +- [Sparse Merkle Tree Verifier](https://github.com/vocdoni/smtverifier-noir/tree/main) - a library for verification of sparse Merkle trees +- [Signed Int](https://github.com/resurgencelabs/signed_int) - a library for accessing a custom Signed Integer data type, allowing access to negative numbers on Noir +- [Fraction](https://github.com/resurgencelabs/fraction) - a library for accessing fractional number data type in Noir, allowing results that aren't whole numbers diff --git a/noir/docs/processed-docs-cache/noir/modules_packages_crates/modules.md b/noir/docs/processed-docs-cache/noir/modules_packages_crates/modules.md new file mode 100644 index 00000000000..ae822a1cff4 --- /dev/null +++ b/noir/docs/processed-docs-cache/noir/modules_packages_crates/modules.md @@ -0,0 +1,105 @@ +--- +title: Modules +description: + Learn how to organize your files using modules in Noir, following the same convention as Rust's + module system. Examples included. +keywords: [Noir, Rust, modules, organizing files, sub-modules] +sidebar_position: 2 +--- + +Noir's module system follows the same convention as the _newer_ version of Rust's module system. + +## Purpose of Modules + +Modules are used to organize files. Without modules all of your code would need to live in a single +file. In Noir, the compiler does not automatically scan all of your files to detect modules. This +must be done explicitly by the developer. + +## Examples + +### Importing a module in the crate root + +Filename : `src/main.nr` + +```rust +mod foo; + +fn main() { + foo::hello_world(); +} +``` + +Filename : `src/foo.nr` + +```rust +fn from_foo() {} +``` + +In the above snippet, the crate root is the `src/main.nr` file. The compiler sees the module +declaration `mod foo` which prompts it to look for a foo.nr file. + +Visually this module hierarchy looks like the following : + +``` +crate + ├── main + │ + └── foo + └── from_foo + +``` + +### Importing a module throughout the tree + +All modules are accessible from the `crate::` namespace. + +``` +crate + ├── bar + ├── foo + └── main + +``` + +In the above snippet, if `bar` would like to use functions in `foo`, it can do so by `use crate::foo::function_name`. + +### Sub-modules + +Filename : `src/main.nr` + +```rust +mod foo; + +fn main() { + foo::from_foo(); +} +``` + +Filename : `src/foo.nr` + +```rust +mod bar; +fn from_foo() {} +``` + +Filename : `src/foo/bar.nr` + +```rust +fn from_bar() {} +``` + +In the above snippet, we have added an extra module to the module tree; `bar`. `bar` is a submodule +of `foo` hence we declare bar in `foo.nr` with `mod bar`. Since `foo` is not the crate root, the +compiler looks for the file associated with the `bar` module in `src/foo/bar.nr` + +Visually the module hierarchy looks as follows: + +``` +crate + ├── main + │ + └── foo + ├── from_foo + └── bar + └── from_bar +``` diff --git a/noir/docs/processed-docs-cache/noir/modules_packages_crates/workspaces.md b/noir/docs/processed-docs-cache/noir/modules_packages_crates/workspaces.md new file mode 100644 index 00000000000..67a1dafa372 --- /dev/null +++ b/noir/docs/processed-docs-cache/noir/modules_packages_crates/workspaces.md @@ -0,0 +1,40 @@ +--- +title: Workspaces +sidebar_position: 3 +--- + +Workspaces are a feature of nargo that allow you to manage multiple related Noir packages in a single repository. A workspace is essentially a group of related projects that share common build output directories and configurations. + +Each Noir project (with it's own Nargo.toml file) can be thought of as a package. Each package is expected to contain exactly one "named circuit", being the "name" defined in Nargo.toml with the program logic defined in `./src/main.nr`. + +For a project with the following structure: + +```tree +├── crates +│   ├── a +│   │   ├── Nargo.toml +│   │   └── src +│   │   └── main.nr +│   └── b +│   ├── Nargo.toml +│   └── src +│   └── main.nr +├── Nargo.toml +└── Prover.toml +``` + +You can define a workspace in Nargo.toml like so: + +```toml +[workspace] +members = ["crates/a", "crates/b"] +default-member = "crates/a" +``` + +`members` indicates which packages are included in the workspace. As such, all member packages of a workspace will be processed when the `--workspace` flag is used with various commands or if a `default-member` is not specified. + +`default-member` indicates which package various commands process by default. + +Libraries can be defined in a workspace. Inside a workspace, these are consumed as `{ path = "../to_lib" }` dependencies in Nargo.toml. + +Inside a workspace, these are consumed as `{ path = "../to_lib" }` dependencies in Nargo.toml. diff --git a/noir/docs/processed-docs-cache/noir/standard_library/_category_.json b/noir/docs/processed-docs-cache/noir/standard_library/_category_.json new file mode 100644 index 00000000000..af04c0933fd --- /dev/null +++ b/noir/docs/processed-docs-cache/noir/standard_library/_category_.json @@ -0,0 +1,6 @@ +{ + "label": "Standard Library", + "position": 1, + "collapsible": true, + "collapsed": true +} diff --git a/noir/docs/processed-docs-cache/noir/standard_library/black_box_fns.md b/noir/docs/processed-docs-cache/noir/standard_library/black_box_fns.md new file mode 100644 index 00000000000..6b22d0e7466 --- /dev/null +++ b/noir/docs/processed-docs-cache/noir/standard_library/black_box_fns.md @@ -0,0 +1,31 @@ +--- +title: Black Box Functions +description: Black box functions are functions in Noir that rely on backends implementing support for specialized constraints. +keywords: [noir, black box functions] +--- + +Black box functions are functions in Noir that rely on backends implementing support for specialized constraints. This makes certain zk-snark unfriendly computations cheaper than if they were implemented in Noir. + +The ACVM spec defines a set of blackbox functions which backends will be expected to implement. This allows backends to use optimized implementations of these constraints if they have them, however they may also fallback to less efficient naive implementations if not. + +## Function list + +Here is a list of the current black box functions: + +- [SHA256](./cryptographic_primitives/hashes#sha256) +- [Schnorr signature verification](./cryptographic_primitives/schnorr) +- [Blake2s](./cryptographic_primitives/hashes#blake2s) +- [Blake3](./cryptographic_primitives/hashes#blake2s) +- [Pedersen Hash](./cryptographic_primitives/hashes#pedersen_hash) +- [Pedersen Commitment](./cryptographic_primitives/hashes#pedersen_commitment) +- [ECDSA signature verification](./cryptographic_primitives/ecdsa_sig_verification) +- [Fixed base scalar multiplication](./cryptographic_primitives/scalar) +- AND +- XOR +- RANGE +- [Keccak256](./cryptographic_primitives/hashes#keccak256) +- [Recursive proof verification](./recursion) + +Most black box functions are included as part of the Noir standard library, however `AND`, `XOR` and `RANGE` are used as part of the Noir language syntax. For instance, using the bitwise operator `&` will invoke the `AND` black box function. + +You can view the black box functions defined in the ACVM code [here](https://github.com/noir-lang/noir/blob/master/acvm-repo/acir/src/circuit/black_box_functions.rs). diff --git a/noir/docs/processed-docs-cache/noir/standard_library/cryptographic_primitives/_category_.json b/noir/docs/processed-docs-cache/noir/standard_library/cryptographic_primitives/_category_.json new file mode 100644 index 00000000000..5d694210bbf --- /dev/null +++ b/noir/docs/processed-docs-cache/noir/standard_library/cryptographic_primitives/_category_.json @@ -0,0 +1,5 @@ +{ + "position": 0, + "collapsible": true, + "collapsed": true +} diff --git a/noir/docs/processed-docs-cache/noir/standard_library/cryptographic_primitives/ec_primitives.md b/noir/docs/processed-docs-cache/noir/standard_library/cryptographic_primitives/ec_primitives.md new file mode 100644 index 00000000000..d2b42d67b7c --- /dev/null +++ b/noir/docs/processed-docs-cache/noir/standard_library/cryptographic_primitives/ec_primitives.md @@ -0,0 +1,102 @@ +--- +title: Elliptic Curve Primitives +keywords: [cryptographic primitives, Noir project] +sidebar_position: 4 +--- + +Data structures and methods on them that allow you to carry out computations involving elliptic +curves over the (mathematical) field corresponding to `Field`. For the field currently at our +disposal, applications would involve a curve embedded in BN254, e.g. the +[Baby Jubjub curve](https://eips.ethereum.org/EIPS/eip-2494). + +## Data structures + +### Elliptic curve configurations + +(`std::ec::{tecurve,montcurve,swcurve}::{affine,curvegroup}::Curve`), i.e. the specific elliptic +curve you want to use, which would be specified using any one of the methods +`std::ec::{tecurve,montcurve,swcurve}::{affine,curvegroup}::new` which take the coefficients in the +defining equation together with a generator point as parameters. You can find more detail in the +comments in +[`noir_stdlib/src/ec.nr`](https://github.com/noir-lang/noir/blob/master/noir_stdlib/src/ec.nr), but +the gist of it is that the elliptic curves of interest are usually expressed in one of the standard +forms implemented here (Twisted Edwards, Montgomery and Short Weierstraß), and in addition to that, +you could choose to use `affine` coordinates (Cartesian coordinates - the usual (x,y) - possibly +together with a point at infinity) or `curvegroup` coordinates (some form of projective coordinates +requiring more coordinates but allowing for more efficient implementations of elliptic curve +operations). Conversions between all of these forms are provided, and under the hood these +conversions are done whenever an operation is more efficient in a different representation (or a +mixed coordinate representation is employed). + +### Points + +(`std::ec::{tecurve,montcurve,swcurve}::{affine,curvegroup}::Point`), i.e. points lying on the +elliptic curve. For a curve configuration `c` and a point `p`, it may be checked that `p` +does indeed lie on `c` by calling `c.contains(p1)`. + +## Methods + +(given a choice of curve representation, e.g. use `std::ec::tecurve::affine::Curve` and use +`std::ec::tecurve::affine::Point`) + +- The **zero element** is given by `Point::zero()`, and we can verify whether a point `p: Point` is + zero by calling `p.is_zero()`. +- **Equality**: Points `p1: Point` and `p2: Point` may be checked for equality by calling + `p1.eq(p2)`. +- **Addition**: For `c: Curve` and points `p1: Point` and `p2: Point` on the curve, adding these two + points is accomplished by calling `c.add(p1,p2)`. +- **Negation**: For a point `p: Point`, `p.negate()` is its negation. +- **Subtraction**: For `c` and `p1`, `p2` as above, subtracting `p2` from `p1` is accomplished by + calling `c.subtract(p1,p2)`. +- **Scalar multiplication**: For `c` as above, `p: Point` a point on the curve and `n: Field`, + scalar multiplication is given by `c.mul(n,p)`. If instead `n :: [u1; N]`, i.e. `n` is a bit + array, the `bit_mul` method may be used instead: `c.bit_mul(n,p)` +- **Multi-scalar multiplication**: For `c` as above and arrays `n: [Field; N]` and `p: [Point; N]`, + multi-scalar multiplication is given by `c.msm(n,p)`. +- **Coordinate representation conversions**: The `into_group` method converts a point or curve + configuration in the affine representation to one in the CurveGroup representation, and + `into_affine` goes in the other direction. +- **Curve representation conversions**: `tecurve` and `montcurve` curves and points are equivalent + and may be converted between one another by calling `into_montcurve` or `into_tecurve` on their + configurations or points. `swcurve` is more general and a curve c of one of the other two types + may be converted to this representation by calling `c.into_swcurve()`, whereas a point `p` lying + on the curve given by `c` may be mapped to its corresponding `swcurve` point by calling + `c.map_into_swcurve(p)`. +- **Map-to-curve methods**: The Elligator 2 method of mapping a field element `n: Field` into a + `tecurve` or `montcurve` with configuration `c` may be called as `c.elligator2_map(n)`. For all of + the curve configurations, the SWU map-to-curve method may be called as `c.swu_map(z,n)`, where + `z: Field` depends on `Field` and `c` and must be chosen by the user (the conditions it needs to + satisfy are specified in the comments + [here](https://github.com/noir-lang/noir/blob/master/noir_stdlib/src/ec.nr)). + +## Examples + +The +[ec_baby_jubjub test](https://github.com/noir-lang/noir/blob/master/test_programs/compile_success_empty/ec_baby_jubjub/src/main.nr) +illustrates all of the above primitives on various forms of the Baby Jubjub curve. A couple of more +interesting examples in Noir would be: + +Public-key cryptography: Given an elliptic curve and a 'base point' on it, determine the public key +from the private key. This is a matter of using scalar multiplication. In the case of Baby Jubjub, +for example, this code would do: + +```rust +use dep::std::ec::tecurve::affine::{Curve, Point}; + +fn bjj_pub_key(priv_key: Field) -> Point +{ + + let bjj = Curve::new(168700, 168696, G::new(995203441582195749578291179787384436505546430278305826713579947235728471134,5472060717959818805561601436314318772137091100104008585924551046643952123905)); + + let base_pt = Point::new(5299619240641551281634865583518297030282874472190772894086521144482721001553, 16950150798460657717958625567821834550301663161624707787222815936182638968203); + + bjj.mul(priv_key,base_pt) +} +``` + +This would come in handy in a Merkle proof. + +- EdDSA signature verification: This is a matter of combining these primitives with a suitable hash + function. See + [feat(stdlib): EdDSA sig verification noir#1136](https://github.com/noir-lang/noir/pull/1136) for + the case of Baby Jubjub and the Poseidon hash function. diff --git a/noir/docs/processed-docs-cache/noir/standard_library/cryptographic_primitives/ecdsa_sig_verification.mdx b/noir/docs/processed-docs-cache/noir/standard_library/cryptographic_primitives/ecdsa_sig_verification.mdx new file mode 100644 index 00000000000..68688f5c96d --- /dev/null +++ b/noir/docs/processed-docs-cache/noir/standard_library/cryptographic_primitives/ecdsa_sig_verification.mdx @@ -0,0 +1,60 @@ +--- +title: ECDSA Signature Verification +description: Learn about the cryptographic primitives regarding ECDSA over the secp256k1 and secp256r1 curves +keywords: [cryptographic primitives, Noir project, ecdsa, secp256k1, secp256r1, signatures] +sidebar_position: 3 +--- + +import BlackBoxInfo from '@site/src/components/Notes/_blackbox.mdx'; + +Noir supports ECDSA signatures verification over the secp256k1 and secp256r1 curves. + +## ecdsa_secp256k1::verify_signature + +Verifier for ECDSA Secp256k1 signatures + +```rust title="ecdsa_secp256k1" showLineNumbers +pub fn verify_signature( + _public_key_x: [u8; 32], + _public_key_y: [u8; 32], + _signature: [u8; 64], + _message_hash: [u8; N] +) -> bool +``` +> Source code: noir_stdlib/src/ecdsa_secp256k1.nr#L2-L9 + + +example: + +```rust +fn main(hashed_message : [u8;32], pub_key_x : [u8;32], pub_key_y : [u8;32], signature : [u8;64]) { + let valid_signature = std::ecdsa_secp256k1::verify_signature(pub_key_x, pub_key_y, signature, hashed_message); + assert(valid_signature); +} +``` + +## ecdsa_secp256r1::verify_signature + +Verifier for ECDSA Secp256r1 signatures + +```rust title="ecdsa_secp256r1" showLineNumbers +pub fn verify_signature( + _public_key_x: [u8; 32], + _public_key_y: [u8; 32], + _signature: [u8; 64], + _message_hash: [u8; N] +) -> bool +``` +> Source code: noir_stdlib/src/ecdsa_secp256r1.nr#L2-L9 + + +example: + +```rust +fn main(hashed_message : [u8;32], pub_key_x : [u8;32], pub_key_y : [u8;32], signature : [u8;64]) { + let valid_signature = std::ecdsa_secp256r1::verify_signature(pub_key_x, pub_key_y, signature, hashed_message); + assert(valid_signature); +} +``` + + diff --git a/noir/docs/processed-docs-cache/noir/standard_library/cryptographic_primitives/eddsa.mdx b/noir/docs/processed-docs-cache/noir/standard_library/cryptographic_primitives/eddsa.mdx new file mode 100644 index 00000000000..a9c10da6c06 --- /dev/null +++ b/noir/docs/processed-docs-cache/noir/standard_library/cryptographic_primitives/eddsa.mdx @@ -0,0 +1,18 @@ +--- +title: EdDSA Verification +description: Learn about the cryptographic primitives regarding EdDSA +keywords: [cryptographic primitives, Noir project, eddsa, signatures] +sidebar_position: 5 +--- + +import BlackBoxInfo from '@site/src/components/Notes/_blackbox.mdx'; + +## eddsa::eddsa_poseidon_verify + +Verifier for EdDSA signatures + +```rust +fn eddsa_poseidon_verify(public_key_x : Field, public_key_y : Field, signature_s: Field, signature_r8_x: Field, signature_r8_y: Field, message: Field) -> bool +``` + + diff --git a/noir/docs/processed-docs-cache/noir/standard_library/cryptographic_primitives/hashes.mdx b/noir/docs/processed-docs-cache/noir/standard_library/cryptographic_primitives/hashes.mdx new file mode 100644 index 00000000000..c70386f8de3 --- /dev/null +++ b/noir/docs/processed-docs-cache/noir/standard_library/cryptographic_primitives/hashes.mdx @@ -0,0 +1,234 @@ +--- +title: Hash methods +description: + Learn about the cryptographic primitives ready to use for any Noir project, including sha256, + blake2s, pedersen, mimc_bn254 and mimc +keywords: + [cryptographic primitives, Noir project, sha256, blake2s, pedersen, mimc_bn254, mimc, hash] +sidebar_position: 0 +--- + +import BlackBoxInfo from '@site/src/components/Notes/_blackbox.mdx'; + +## sha256 + +Given an array of bytes, returns the resulting sha256 hash. + +```rust title="sha256" showLineNumbers +pub fn sha256(_input: [u8; N]) -> [u8; 32] +``` +> Source code: noir_stdlib/src/hash.nr#L5-L7 + + +example: + +```rust +fn main() { + let x = [163, 117, 178, 149]; // some random bytes + let hash = std::hash::sha256(x); +} +``` + + + +## blake2s + +Given an array of bytes, returns an array with the Blake2 hash + +```rust title="blake2s" showLineNumbers +pub fn blake2s(_input: [u8; N]) -> [u8; 32] +``` +> Source code: noir_stdlib/src/hash.nr#L11-L13 + + +example: + +```rust +fn main() { + let x = [163, 117, 178, 149]; // some random bytes + let hash = std::hash::blake2s(x); +} +``` + + + +## blake3 + +Given an array of bytes, returns an array with the Blake3 hash + +```rust title="blake3" showLineNumbers +pub fn blake3(_input: [u8; N]) -> [u8; 32] +``` +> Source code: noir_stdlib/src/hash.nr#L17-L19 + + +example: + +```rust +fn main() { + let x = [163, 117, 178, 149]; // some random bytes + let hash = std::hash::blake3(x); +} +``` + + + +## pedersen_hash + +Given an array of Fields, returns the Pedersen hash. + +```rust title="pedersen_hash" showLineNumbers +pub fn pedersen_hash(input: [Field; N]) -> Field +``` +> Source code: noir_stdlib/src/hash.nr#L42-L44 + + +example: + +```rust title="pedersen-hash" showLineNumbers +use dep::std; + +fn main(x: Field, y: Field, expected_hash: Field) { + let hash = std::hash::pedersen_hash([x, y]); + assert_eq(hash, expected_hash); +} +``` +> Source code: test_programs/execution_success/pedersen_hash/src/main.nr#L1-L8 + + + + + +## pedersen_commitment + +Given an array of Fields, returns the Pedersen commitment. + +```rust title="pedersen_commitment" showLineNumbers +struct PedersenPoint { + x : Field, + y : Field, +} + +pub fn pedersen_commitment(input: [Field; N]) -> PedersenPoint +``` +> Source code: noir_stdlib/src/hash.nr#L22-L29 + + +example: + +```rust title="pedersen-commitment" showLineNumbers +use dep::std; + +fn main(x: Field, y: Field, expected_commitment: std::hash::PedersenPoint) { + let commitment = std::hash::pedersen_commitment([x, y]); + assert_eq(commitment.x, expected_commitment.x); + assert_eq(commitment.y, expected_commitment.y); +} +``` +> Source code: test_programs/execution_success/pedersen_commitment/src/main.nr#L1-L9 + + + + +## keccak256 + +Given an array of bytes (`u8`), returns the resulting keccak hash as an array of 32 bytes +(`[u8; 32]`). Specify a message_size to hash only the first `message_size` bytes +of the input. + +```rust title="keccak256" showLineNumbers +pub fn keccak256(_input: [u8; N], _message_size: u32) -> [u8; 32] +``` +> Source code: noir_stdlib/src/hash.nr#L67-L69 + + +example: + +```rust title="keccak256" showLineNumbers +use dep::std; + +fn main(x: Field, result: [u8; 32]) { + // We use the `as` keyword here to denote the fact that we want to take just the first byte from the x Field + // The padding is taken care of by the program + let digest = std::hash::keccak256([x as u8], 1); + assert(digest == result); + + //#1399: variable message size + let message_size = 4; + let hash_a = std::hash::keccak256([1, 2, 3, 4], message_size); + let hash_b = std::hash::keccak256([1, 2, 3, 4, 0, 0, 0, 0], message_size); + + assert(hash_a == hash_b); + + let message_size_big = 8; + let hash_c = std::hash::keccak256([1, 2, 3, 4, 0, 0, 0, 0], message_size_big); + + assert(hash_a != hash_c); +} +``` +> Source code: test_programs/execution_success/keccak256/src/main.nr#L1-L22 + + + + +## poseidon + +Given an array of Fields, returns a new Field with the Poseidon Hash. Mind that you need to specify +how many inputs are there to your Poseidon function. + +```rust +// example for hash_1, hash_2 accepts an array of length 2, etc +fn hash_1(input: [Field; 1]) -> Field +``` + +example: + +```rust title="poseidon" showLineNumbers +use dep::std::hash::poseidon; + +fn main(x1: [Field; 2], y1: pub Field, x2: [Field; 4], y2: pub Field) { + let hash1 = poseidon::bn254::hash_2(x1); + assert(hash1 == y1); + + let hash2 = poseidon::bn254::hash_4(x2); + assert(hash2 == y2); +} +``` +> Source code: test_programs/execution_success/poseidon_bn254_hash/src/main.nr#L1-L11 + + +## mimc_bn254 and mimc + +`mimc_bn254` is `mimc`, but with hardcoded parameters for the BN254 curve. You can use it by +providing an array of Fields, and it returns a Field with the hash. You can use the `mimc` method if +you're willing to input your own constants: + +```rust +fn mimc(x: Field, k: Field, constants: [Field; N], exp : Field) -> Field +``` + +otherwise, use the `mimc_bn254` method: + +```rust +fn mimc_bn254(array: [Field; N]) -> Field +``` + +example: + +```rust + +fn main() { + let x = [163, 117, 178, 149]; // some random bytes + let hash = std::hash::mimc::mimc_bn254(x); +} +``` + +## hash_to_field + +```rust +fn hash_to_field(_input : [Field; N]) -> Field {} +``` + +Calculates the `blake2s` hash of the inputs and returns the hash modulo the field modulus to return +a value which can be represented as a `Field`. + diff --git a/noir/docs/processed-docs-cache/noir/standard_library/cryptographic_primitives/index.md b/noir/docs/processed-docs-cache/noir/standard_library/cryptographic_primitives/index.md new file mode 100644 index 00000000000..650f30165d5 --- /dev/null +++ b/noir/docs/processed-docs-cache/noir/standard_library/cryptographic_primitives/index.md @@ -0,0 +1,14 @@ +--- +title: Cryptographic Primitives +description: + Learn about the cryptographic primitives ready to use for any Noir project +keywords: + [ + cryptographic primitives, + Noir project, + ] +--- + +The Noir team is progressively adding new cryptographic primitives to the standard library. Reach out for news or if you would be interested in adding more of these calculations in Noir. + +Some methods are available thanks to the Aztec backend, not being performed using Noir. When using other backends, these methods may or may not be supplied. diff --git a/noir/docs/processed-docs-cache/noir/standard_library/cryptographic_primitives/scalar.mdx b/noir/docs/processed-docs-cache/noir/standard_library/cryptographic_primitives/scalar.mdx new file mode 100644 index 00000000000..9f9a6419140 --- /dev/null +++ b/noir/docs/processed-docs-cache/noir/standard_library/cryptographic_primitives/scalar.mdx @@ -0,0 +1,33 @@ +--- +title: Scalar multiplication +description: See how you can perform scalar multiplications over a fixed base in Noir +keywords: [cryptographic primitives, Noir project, scalar multiplication] +sidebar_position: 1 +--- + +import BlackBoxInfo from '@site/src/components/Notes/_blackbox.mdx'; + +## scalar_mul::fixed_base_embedded_curve + +Performs scalar multiplication over the embedded curve whose coordinates are defined by the +configured noir field. For the BN254 scalar field, this is BabyJubJub or Grumpkin. + +```rust title="fixed_base_embedded_curve" showLineNumbers +pub fn fixed_base_embedded_curve( + _low: Field, + _high: Field +) -> [Field; 2] +``` +> Source code: noir_stdlib/src/scalar_mul.nr#L27-L32 + + +example + +```rust +fn main(x : Field) { + let scal = std::scalar_mul::fixed_base_embedded_curve(x); + println(scal); +} +``` + + diff --git a/noir/docs/processed-docs-cache/noir/standard_library/cryptographic_primitives/schnorr.mdx b/noir/docs/processed-docs-cache/noir/standard_library/cryptographic_primitives/schnorr.mdx new file mode 100644 index 00000000000..4890507733d --- /dev/null +++ b/noir/docs/processed-docs-cache/noir/standard_library/cryptographic_primitives/schnorr.mdx @@ -0,0 +1,45 @@ +--- +title: Schnorr Signatures +description: Learn how you can verify Schnorr signatures using Noir +keywords: [cryptographic primitives, Noir project, schnorr, signatures] +sidebar_position: 2 +--- + +import BlackBoxInfo from '@site/src/components/Notes/_blackbox.mdx'; + +## schnorr::verify_signature + +Verifier for Schnorr signatures over the embedded curve (for BN254 it is Grumpkin). + +```rust title="schnorr_verify" showLineNumbers +pub fn verify_signature( + _public_key_x: Field, + _public_key_y: Field, + _signature: [u8; 64], + _message: [u8; N] +) -> bool +``` +> Source code: noir_stdlib/src/schnorr.nr#L2-L9 + + +where `_signature` can be generated like so using the npm package +[@noir-lang/barretenberg](https://www.npmjs.com/package/@noir-lang/barretenberg) + +```js +const { BarretenbergWasm } = require('@noir-lang/barretenberg/dest/wasm'); +const { Schnorr } = require('@noir-lang/barretenberg/dest/crypto/schnorr'); + +... + +const barretenberg = await BarretenbergWasm.new(); +const schnorr = new Schnorr(barretenberg); +const pubKey = schnorr.computePublicKey(privateKey); +const message = ... +const signature = Array.from( + schnorr.constructSignature(hash, privateKey).toBuffer() +); + +... +``` + + diff --git a/noir/docs/processed-docs-cache/noir/standard_library/logging.md b/noir/docs/processed-docs-cache/noir/standard_library/logging.md new file mode 100644 index 00000000000..db75ef9f86f --- /dev/null +++ b/noir/docs/processed-docs-cache/noir/standard_library/logging.md @@ -0,0 +1,78 @@ +--- +title: Logging +description: + Learn how to use the println statement for debugging in Noir with this tutorial. Understand the + basics of logging in Noir and how to implement it in your code. +keywords: + [ + noir logging, + println statement, + print statement, + debugging in noir, + noir std library, + logging tutorial, + basic logging in noir, + noir logging implementation, + noir debugging techniques, + rust, + ] +--- + +The standard library provides two familiar statements you can use: `println` and `print`. Despite being a limited implementation of rust's `println!` and `print!` macros, these constructs can be useful for debugging. + +You can print the output of both statements in your Noir code by using the `nargo execute` command or the `--show-output` flag when using `nargo test` (provided there are print statements in your tests). + +It is recommended to use `nargo execute` if you want to debug failing constraints with `println` or `print` statements. This is due to every input in a test being a constant rather than a witness, so we issue an error during compilation while we only print during execution (which comes after compilation). Neither `println`, nor `print` are callable for failed constraints caught at compile time. + +Both `print` and `println` are generic functions which can work on integers, fields, strings, and even structs or expressions. Note however, that slices are currently unsupported. For example: + +```rust +struct Person { + age: Field, + height: Field, +} + +fn main(age: Field, height: Field) { + let person = Person { + age: age, + height: height, + }; + println(person); + println(age + height); + println("Hello world!"); +} +``` + +You can print different types in the same statement (including strings) with a type called `fmtstr`. It can be specified in the same way as a normal string, just prepended with an "f" character: + +```rust + let fmt_str = f"i: {i}, j: {j}"; + println(fmt_str); + + let s = myStruct { y: x, x: y }; + println(s); + + println(f"i: {i}, s: {s}"); + + println(x); + println([x, y]); + + let foo = fooStruct { my_struct: s, foo: 15 }; + println(f"s: {s}, foo: {foo}"); + + println(15); // prints 0x0f, implicit Field + println(-1 as u8); // prints 255 + println(-1 as i8); // prints -1 +``` + +Examples shown above are interchangeable between the two `print` statements: + +```rust +let person = Person { age : age, height : height }; + +println(person); +print(person); + +println("Hello world!"); // Prints with a newline at the end of the input +print("Hello world!"); // Prints the input and keeps cursor on the same line +``` diff --git a/noir/docs/processed-docs-cache/noir/standard_library/merkle_trees.md b/noir/docs/processed-docs-cache/noir/standard_library/merkle_trees.md new file mode 100644 index 00000000000..fa488677884 --- /dev/null +++ b/noir/docs/processed-docs-cache/noir/standard_library/merkle_trees.md @@ -0,0 +1,58 @@ +--- +title: Merkle Trees +description: Learn about Merkle Trees in Noir with this tutorial. Explore the basics of computing a merkle root using a proof, with examples. +keywords: + [ + Merkle trees in Noir, + Noir programming language, + check membership, + computing root from leaf, + Noir Merkle tree implementation, + Merkle tree tutorial, + Merkle tree code examples, + Noir libraries, + pedersen hash., + ] +--- + +## compute_merkle_root + +Returns the root of the tree from the provided leaf and its hash path, using a [Pedersen hash](./cryptographic_primitives/hashes.mdx#pedersen_hash). + +```rust +fn compute_merkle_root(leaf : Field, index : Field, hash_path: [Field]) -> Field +``` + +example: + +```rust +/** + // these values are for this example only + index = "0" + priv_key = "0x000000000000000000000000000000000000000000000000000000616c696365" + secret = "0x1929ea3ab8d9106a899386883d9428f8256cfedb3c4f6b66bf4aa4d28a79988f" + note_hash_path = [ + "0x1e61bdae0f027b1b2159e1f9d3f8d00fa668a952dddd822fda80dc745d6f65cc", + "0x0e4223f3925f98934393c74975142bd73079ab0621f4ee133cee050a3c194f1a", + "0x2fd7bb412155bf8693a3bd2a3e7581a679c95c68a052f835dddca85fa1569a40" + ] + */ +fn main(index: Field, priv_key: Field, secret: Field, note_hash_path: [Field; 3]) { + + let pubkey = std::scalar_mul::fixed_base_embedded_curve(priv_key); + let pubkey_x = pubkey[0]; + let pubkey_y = pubkey[1]; + let note_commitment = std::hash::pedersen([pubkey_x, pubkey_y, secret]); + + let root = std::merkle::compute_merkle_root(note_commitment[0], index, note_hash_path); + println(root); +} +``` + +To check merkle tree membership: + +1. Include a merkle root as a program input. +2. Compute the merkle root of a given leaf, index and hash path. +3. Assert the merkle roots are equal. + +For more info about merkle trees, see the Wikipedia [page](https://en.wikipedia.org/wiki/Merkle_tree). diff --git a/noir/docs/processed-docs-cache/noir/standard_library/options.md b/noir/docs/processed-docs-cache/noir/standard_library/options.md new file mode 100644 index 00000000000..970c9cfbf11 --- /dev/null +++ b/noir/docs/processed-docs-cache/noir/standard_library/options.md @@ -0,0 +1,97 @@ +--- +title: Option Type +--- + +The `Option` type is a way to express that a value might be present (`Some(T))` or absent (`None`). It's a safer way to handle potential absence of values, compared to using nulls in many other languages. + +```rust +struct Option { + None, + Some(T), +} +``` + +The `Option` type, already imported into your Noir program, can be used directly: + +```rust +fn main() { + let none = Option::none(); + let some = Option::some(3); +} +``` + +See [this test](https://github.com/noir-lang/noir/blob/5cbfb9c4a06c8865c98ff2b594464b037d821a5c/crates/nargo_cli/tests/test_data/option/src/main.nr) for a more comprehensive set of examples of each of the methods described below. + +## Methods + +### none + +Constructs a none value. + +### some + +Constructs a some wrapper around a given value. + +### is_none + +Returns true if the Option is None. + +### is_some + +Returns true of the Option is Some. + +### unwrap + +Asserts `self.is_some()` and returns the wrapped value. + +### unwrap_unchecked + +Returns the inner value without asserting `self.is_some()`. This method can be useful within an if condition when we already know that `option.is_some()`. If the option is None, there is no guarantee what value will be returned, only that it will be of type T for an `Option`. + +### unwrap_or + +Returns the wrapped value if `self.is_some()`. Otherwise, returns the given default value. + +### unwrap_or_else + +Returns the wrapped value if `self.is_some()`. Otherwise, calls the given function to return a default value. + +### map + +If self is `Some(x)`, this returns `Some(f(x))`. Otherwise, this returns `None`. + +### map_or + +If self is `Some(x)`, this returns `f(x)`. Otherwise, this returns the given default value. + +### map_or_else + +If self is `Some(x)`, this returns `f(x)`. Otherwise, this returns `default()`. + +### and + +Returns None if self is None. Otherwise, this returns `other`. + +### and_then + +If self is None, this returns None. Otherwise, this calls the given function with the Some value contained within self, and returns the result of that call. In some languages this function is called `flat_map` or `bind`. + +### or + +If self is Some, return self. Otherwise, return `other`. + +### or_else + +If self is Some, return self. Otherwise, return `default()`. + +### xor + +If only one of the two Options is Some, return that option. Otherwise, if both options are Some or both are None, None is returned. + +### filter + +Returns `Some(x)` if self is `Some(x)` and `predicate(x)` is true. Otherwise, this returns `None`. + +### flatten + +Flattens an `Option>` into a `Option`. This returns `None` if the outer Option is None. Otherwise, this returns the inner Option. diff --git a/noir/docs/processed-docs-cache/noir/standard_library/recursion.md b/noir/docs/processed-docs-cache/noir/standard_library/recursion.md new file mode 100644 index 00000000000..67962082a8f --- /dev/null +++ b/noir/docs/processed-docs-cache/noir/standard_library/recursion.md @@ -0,0 +1,90 @@ +--- +title: Recursive Proofs +description: Learn about how to write recursive proofs in Noir. +keywords: [recursion, recursive proofs, verification_key, aggregation object, verify_proof] +--- + +Noir supports recursively verifying proofs, meaning you verify the proof of a Noir program in another Noir program. This enables creating proofs of arbitrary size by doing step-wise verification of smaller components of a large proof. + +The `verify_proof` function takes a verification key, proof and public inputs for a zk program, as well as a key hash and an input aggregation object. The key hash is used to check the validity of the verification key and the input aggregation object is required by some proving systems. The `verify_proof` function returns an output aggregation object that can then be fed into future iterations of the proof verification if required. + +```rust +#[foreign(verify_proof)] +fn verify_proof(_verification_key : [Field], _proof : [Field], _public_input : Field, _key_hash : Field, _input_aggregation_object : [Field]) -> [Field] {} +``` + +:::info + +This is a black box function. Read [this section](./black_box_fns) to learn more about black box functions in Noir. + +::: + +## Example usage + +```rust +use dep::std; + +fn main( + verification_key : [Field; 114], + proof : [Field; 94], + public_inputs : [Field; 1], + key_hash : Field, + input_aggregation_object : [Field; 16], + proof_b : [Field; 94], +) -> pub [Field; 16] { + let output_aggregation_object_a = std::verify_proof( + verification_key.as_slice(), + proof.as_slice(), + public_inputs.as_slice(), + key_hash, + input_aggregation_object + ); + + let output_aggregation_object = std::verify_proof( + verification_key.as_slice(), + proof_b.as_slice(), + public_inputs.as_slice(), + key_hash, + output_aggregation_object_a + ); + + let mut output = [0; 16]; + for i in 0..16 { + output[i] = output_aggregation_object[i]; + } + output +} +``` + +## Parameters + +### `verification_key` + +The verification key for the zk program that is being verified. + +### `proof` + +The proof for the zk program that is being verified. + +### `public_inputs` + +These represent the public inputs of the proof we are verifying. They should be checked against in the circuit after construction of a new aggregation state. + +### `key_hash` + +A key hash is used to check the validity of the verification key. The circuit implementing this opcode can use this hash to ensure that the key provided to the circuit matches the key produced by the circuit creator. + +### `input_aggregation_object` + +An aggregation object is blob of data that the top-level verifier must run some proof system specific algorithm on to complete verification. The size is proof system specific and will be set by the backend integrating this opcode. The input aggregation object is only not `None` when we are verifying a previous recursive aggregation in the current circuit. If this is the first recursive aggregation there is no input aggregation object. It is left to the backend to determine how to handle when there is no input aggregation object. + +## Return value + +### `output_aggregation_object` + +This is the result of a recursive aggregation and is what will be fed into the next verifier. +The next verifier can either perform a final verification (returning true or false) or perform another recursive aggregation where this output aggregation object will be the input aggregation object of the next recursive aggregation. + +## Example + +You can see an example of how to do recursive proofs in [this example recursion demo repo](https://github.com/noir-lang/noir-examples/tree/master/recursion). diff --git a/noir/docs/processed-docs-cache/noir/standard_library/traits.md b/noir/docs/processed-docs-cache/noir/standard_library/traits.md new file mode 100644 index 00000000000..209f7820e1d --- /dev/null +++ b/noir/docs/processed-docs-cache/noir/standard_library/traits.md @@ -0,0 +1,408 @@ +--- +title: Traits +description: Noir's stdlib provides a few commonly used traits. +keywords: [traits, trait, interface, protocol, default, add, eq] +--- + +## `std::default` + +### `std::default::Default` + +```rust title="default-trait" showLineNumbers +trait Default { + fn default() -> Self; +} +``` +> Source code: noir_stdlib/src/default.nr#L1-L5 + + +Constructs a default value of a type. + +Implementations: +```rust +impl Default for Field { .. } + +impl Default for i8 { .. } +impl Default for i16 { .. } +impl Default for i32 { .. } +impl Default for i64 { .. } + +impl Default for u8 { .. } +impl Default for u16 { .. } +impl Default for u32 { .. } +impl Default for u64 { .. } + +impl Default for () { .. } +impl Default for bool { .. } + +impl Default for [T; N] + where T: Default { .. } + +impl Default for (A, B) + where A: Default, B: Default { .. } + +impl Default for (A, B, C) + where A: Default, B: Default, C: Default { .. } + +impl Default for (A, B, C, D) + where A: Default, B: Default, C: Default, D: Default { .. } + +impl Default for (A, B, C, D, E) + where A: Default, B: Default, C: Default, D: Default, E: Default { .. } +``` + +For primitive integer types, the return value of `default` is `0`. Container +types such as arrays are filled with default values of their element type. + + +## `std::convert` + +### `std::convert::From` + +```rust title="from-trait" showLineNumbers +trait From { + fn from(input: T) -> Self; +} +``` +> Source code: noir_stdlib/src/convert.nr#L1-L5 + + +The `From` trait defines how to convert from a given type `T` to the type on which the trait is implemented. + +The Noir standard library provides a number of implementations of `From` between primitive types. +```rust title="from-impls" showLineNumbers +// Unsigned integers +impl From for u16 { fn from(value: u8) -> u16 { value as u16 } } + +impl From for u32 { fn from(value: u8) -> u32 { value as u32 } } +impl From for u32 { fn from(value: u16) -> u32 { value as u32 } } + +impl From for u64 { fn from(value: u8) -> u64 { value as u64 } } +impl From for u64 { fn from(value: u16) -> u64 { value as u64 } } +impl From for u64 { fn from(value: u32) -> u64 { value as u64 } } + +impl From for Field { fn from(value: u8) -> Field { value as Field } } +impl From for Field { fn from(value: u16) -> Field { value as Field } } +impl From for Field { fn from(value: u32) -> Field { value as Field } } +impl From for Field { fn from(value: u64) -> Field { value as Field } } + +// Signed integers +impl From for i16 { fn from(value: i8) -> i16 { value as i16 } } + +impl From for i32 { fn from(value: i8) -> i32 { value as i32 } } +impl From for i32 { fn from(value: i16) -> i32 { value as i32 } } + +impl From for i64 { fn from(value: i8) -> i64 { value as i64 } } +impl From for i64 { fn from(value: i16) -> i64 { value as i64 } } +impl From for i64 { fn from(value: i32) -> i64 { value as i64 } } + +// Booleans +impl From for u8 { fn from(value: bool) -> u8 { value as u8 } } +impl From for u16 { fn from(value: bool) -> u16 { value as u16 } } +impl From for u32 { fn from(value: bool) -> u32 { value as u32 } } +impl From for u64 { fn from(value: bool) -> u64 { value as u64 } } +impl From for i8 { fn from(value: bool) -> i8 { value as i8 } } +impl From for i16 { fn from(value: bool) -> i16 { value as i16 } } +impl From for i32 { fn from(value: bool) -> i32 { value as i32 } } +impl From for i64 { fn from(value: bool) -> i64 { value as i64 } } +impl From for Field { fn from(value: bool) -> Field { value as Field } } +``` +> Source code: noir_stdlib/src/convert.nr#L25-L61 + + +#### When to implement `From` + +As a general rule of thumb, `From` may be implemented in the [situations where it would be suitable in Rust](https://doc.rust-lang.org/std/convert/trait.From.html#when-to-implement-from): + +- The conversion is *infallible*: Noir does not provide an equivalent to Rust's `TryFrom`, if the conversion can fail then provide a named method instead. +- The conversion is *lossless*: semantically, it should not lose or discard information. For example, `u32: From` can losslessly convert any `u16` into a valid `u32` such that the original `u16` can be recovered. On the other hand, `u16: From` should not be implemented as `2**16` is a `u32` which cannot be losslessly converted into a `u16`. +- The conversion is *value-preserving*: the conceptual kind and meaning of the resulting value is the same, even though the Noir type and technical representation might be different. While it's possible to infallibly and losslessly convert a `u8` into a `str<2>` hex representation, `4u8` and `"04"` are too different for `str<2>: From` to be implemented. +- The conversion is *obvious*: it's the only reasonable conversion between the two types. If there's ambiguity on how to convert between them such that the same input could potentially map to two different values then a named method should be used. For instance rather than implementing `U128: From<[u8; 16]>`, the methods `U128::from_le_bytes` and `U128::from_be_bytes` are used as otherwise the endianness of the array would be ambiguous, resulting in two potential values of `U128` from the same byte array. + +One additional recommendation specific to Noir is: +- The conversion is *efficient*: it's relatively cheap to convert between the two types. Due to being a ZK DSL, it's more important to avoid unnecessary computation compared to Rust. If the implementation of `From` would encourage users to perform unnecessary conversion, resulting in additional proving time, then it may be preferable to expose functionality such that this conversion may be avoided. + +### `std::convert::Into` + +The `Into` trait is defined as the reciprocal of `From`. It should be easy to convince yourself that if we can convert to type `A` from type `B`, then it's possible to convert type `B` into type `A`. + +For this reason, implementing `From` on a type will automatically generate a matching `Into` implementation. One should always prefer implementing `From` over `Into` as implementing `Into` will not generate a matching `From` implementation. + +```rust title="into-trait" showLineNumbers +trait Into { + fn into(input: Self) -> T; +} + +impl Into for U where T: From { + fn into(input: U) -> T { + T::from(input) + } +} +``` +> Source code: noir_stdlib/src/convert.nr#L13-L23 + + +`Into` is most useful when passing function arguments where the types don't quite match up with what the function expects. In this case, the compiler has enough type information to perform the necessary conversion by just appending `.into()` onto the arguments in question. + + +## `std::cmp` + +### `std::cmp::Eq` + +```rust title="eq-trait" showLineNumbers +trait Eq { + fn eq(self, other: Self) -> bool; +} +``` +> Source code: noir_stdlib/src/cmp.nr#L1-L5 + + +Returns `true` if `self` is equal to `other`. Implementing this trait on a type +allows the type to be used with `==` and `!=`. + +Implementations: +```rust +impl Eq for Field { .. } + +impl Eq for i8 { .. } +impl Eq for i16 { .. } +impl Eq for i32 { .. } +impl Eq for i64 { .. } + +impl Eq for u8 { .. } +impl Eq for u16 { .. } +impl Eq for u32 { .. } +impl Eq for u64 { .. } + +impl Eq for () { .. } +impl Eq for bool { .. } + +impl Eq for [T; N] + where T: Eq { .. } + +impl Eq for (A, B) + where A: Eq, B: Eq { .. } + +impl Eq for (A, B, C) + where A: Eq, B: Eq, C: Eq { .. } + +impl Eq for (A, B, C, D) + where A: Eq, B: Eq, C: Eq, D: Eq { .. } + +impl Eq for (A, B, C, D, E) + where A: Eq, B: Eq, C: Eq, D: Eq, E: Eq { .. } +``` + +### `std::cmp::Ord` + +```rust title="ord-trait" showLineNumbers +trait Ord { + fn cmp(self, other: Self) -> Ordering; +} +``` +> Source code: noir_stdlib/src/cmp.nr#L94-L98 + + +`a.cmp(b)` compares two values returning `Ordering::less()` if `a < b`, +`Ordering::equal()` if `a == b`, or `Ordering::greater()` if `a > b`. +Implementing this trait on a type allows `<`, `<=`, `>`, and `>=` to be +used on values of the type. + +Implementations: + +```rust +impl Ord for u8 { .. } +impl Ord for u16 { .. } +impl Ord for u32 { .. } +impl Ord for u64 { .. } + +impl Ord for i8 { .. } +impl Ord for i16 { .. } +impl Ord for i32 { .. } + +impl Ord for i64 { .. } + +impl Ord for () { .. } +impl Ord for bool { .. } + +impl Ord for [T; N] + where T: Ord { .. } + +impl Ord for (A, B) + where A: Ord, B: Ord { .. } + +impl Ord for (A, B, C) + where A: Ord, B: Ord, C: Ord { .. } + +impl Ord for (A, B, C, D) + where A: Ord, B: Ord, C: Ord, D: Ord { .. } + +impl Ord for (A, B, C, D, E) + where A: Ord, B: Ord, C: Ord, D: Ord, E: Ord { .. } +``` + +## `std::ops` + +### `std::ops::Add`, `std::ops::Sub`, `std::ops::Mul`, and `std::ops::Div` + +These traits abstract over addition, subtraction, multiplication, and division respectively. +Implementing these traits for a given type will also allow that type to be used with the corresponding operator +for that trait (`+` for Add, etc) in addition to the normal method names. + +```rust title="add-trait" showLineNumbers +trait Add { + fn add(self, other: Self) -> Self; +} +``` +> Source code: noir_stdlib/src/ops.nr#L1-L5 + +```rust title="sub-trait" showLineNumbers +trait Sub { + fn sub(self, other: Self) -> Self; +} +``` +> Source code: noir_stdlib/src/ops.nr#L19-L23 + +```rust title="mul-trait" showLineNumbers +trait Mul { + fn mul(self, other: Self) -> Self; +} +``` +> Source code: noir_stdlib/src/ops.nr#L37-L41 + +```rust title="div-trait" showLineNumbers +trait Div { + fn div(self, other: Self) -> Self; +} +``` +> Source code: noir_stdlib/src/ops.nr#L55-L59 + + +The implementations block below is given for the `Add` trait, but the same types that implement +`Add` also implement `Sub`, `Mul`, and `Div`. + +Implementations: +```rust +impl Add for Field { .. } + +impl Add for i8 { .. } +impl Add for i16 { .. } +impl Add for i32 { .. } +impl Add for i64 { .. } + +impl Add for u8 { .. } +impl Add for u16 { .. } +impl Add for u32 { .. } +impl Add for u64 { .. } +``` + +### `std::ops::Rem` + +```rust title="rem-trait" showLineNumbers +trait Rem{ + fn rem(self, other: Self) -> Self; +} +``` +> Source code: noir_stdlib/src/ops.nr#L73-L77 + + +`Rem::rem(a, b)` is the remainder function returning the result of what is +left after dividing `a` and `b`. Implementing `Rem` allows the `%` operator +to be used with the implementation type. + +Unlike other numeric traits, `Rem` is not implemented for `Field`. + +Implementations: +```rust +impl Rem for u8 { fn rem(self, other: u8) -> u8 { self % other } } +impl Rem for u16 { fn rem(self, other: u16) -> u16 { self % other } } +impl Rem for u32 { fn rem(self, other: u32) -> u32 { self % other } } +impl Rem for u64 { fn rem(self, other: u64) -> u64 { self % other } } + +impl Rem for i8 { fn rem(self, other: i8) -> i8 { self % other } } +impl Rem for i16 { fn rem(self, other: i16) -> i16 { self % other } } +impl Rem for i32 { fn rem(self, other: i32) -> i32 { self % other } } +impl Rem for i64 { fn rem(self, other: i64) -> i64 { self % other } } +``` + +### `std::ops::{ BitOr, BitAnd, BitXor }` + +```rust title="bitor-trait" showLineNumbers +trait BitOr { + fn bitor(self, other: Self) -> Self; +} +``` +> Source code: noir_stdlib/src/ops.nr#L89-L93 + +```rust title="bitand-trait" showLineNumbers +trait BitAnd { + fn bitand(self, other: Self) -> Self; +} +``` +> Source code: noir_stdlib/src/ops.nr#L107-L111 + +```rust title="bitxor-trait" showLineNumbers +trait BitXor { + fn bitxor(self, other: Self) -> Self; +} +``` +> Source code: noir_stdlib/src/ops.nr#L125-L129 + + +Traits for the bitwise operations `|`, `&`, and `^`. + +Implementing `BitOr`, `BitAnd` or `BitXor` for a type allows the `|`, `&`, or `^` operator respectively +to be used with the type. + +The implementations block below is given for the `BitOr` trait, but the same types that implement +`BitOr` also implement `BitAnd` and `BitXor`. + +Implementations: +```rust +impl BitOr for bool { fn bitor(self, other: bool) -> bool { self | other } } + +impl BitOr for u8 { fn bitor(self, other: u8) -> u8 { self | other } } +impl BitOr for u16 { fn bitor(self, other: u16) -> u16 { self | other } } +impl BitOr for u32 { fn bitor(self, other: u32) -> u32 { self | other } } +impl BitOr for u64 { fn bitor(self, other: u64) -> u64 { self | other } } + +impl BitOr for i8 { fn bitor(self, other: i8) -> i8 { self | other } } +impl BitOr for i16 { fn bitor(self, other: i16) -> i16 { self | other } } +impl BitOr for i32 { fn bitor(self, other: i32) -> i32 { self | other } } +impl BitOr for i64 { fn bitor(self, other: i64) -> i64 { self | other } } +``` + +### `std::ops::{ Shl, Shr }` + +```rust title="shl-trait" showLineNumbers +trait Shl { + fn shl(self, other: Self) -> Self; +} +``` +> Source code: noir_stdlib/src/ops.nr#L143-L147 + +```rust title="shr-trait" showLineNumbers +trait Shr { + fn shr(self, other: Self) -> Self; +} +``` +> Source code: noir_stdlib/src/ops.nr#L160-L164 + + +Traits for a bit shift left and bit shift right. + +Implementing `Shl` for a type allows the left shift operator (`<<`) to be used with the implementation type. +Similarly, implementing `Shr` allows the right shift operator (`>>`) to be used with the type. + +Note that bit shifting is not currently implemented for signed types. + +The implementations block below is given for the `Shl` trait, but the same types that implement +`Shl` also implement `Shr`. + +Implementations: +```rust +impl Shl for u8 { fn shl(self, other: u8) -> u8 { self << other } } +impl Shl for u16 { fn shl(self, other: u16) -> u16 { self << other } } +impl Shl for u32 { fn shl(self, other: u32) -> u32 { self << other } } +impl Shl for u64 { fn shl(self, other: u64) -> u64 { self << other } } +``` diff --git a/noir/docs/processed-docs-cache/noir/standard_library/zeroed.md b/noir/docs/processed-docs-cache/noir/standard_library/zeroed.md new file mode 100644 index 00000000000..97dab02dac2 --- /dev/null +++ b/noir/docs/processed-docs-cache/noir/standard_library/zeroed.md @@ -0,0 +1,25 @@ +--- +title: Zeroed Function +description: + The zeroed function returns a zeroed value of any type. +keywords: + [ + zeroed + ] +--- + +Implements `fn zeroed() -> T` to return a zeroed value of any type. This function is generally unsafe to use as the zeroed bit pattern is not guaranteed to be valid for all types. It can however, be useful in cases when the value is guaranteed not to be used such as in a BoundedVec library implementing a growable vector, up to a certain length, backed by an array. The array can be initialized with zeroed values which are guaranteed to be inaccessible until the vector is pushed to. Similarly, enumerations in noir can be implemented using this method by providing zeroed values for the unused variants. + +You can access the function at `std::unsafe::zeroed`. + +This function currently supports the following types: + +- Field +- Bool +- Uint +- Array +- String +- Tuple +- Function + +Using it on other types could result in unexpected behavior. diff --git a/noir/docs/processed-docs-cache/reference/_category_.json b/noir/docs/processed-docs-cache/reference/_category_.json new file mode 100644 index 00000000000..5b6a20a609a --- /dev/null +++ b/noir/docs/processed-docs-cache/reference/_category_.json @@ -0,0 +1,5 @@ +{ + "position": 4, + "collapsible": true, + "collapsed": true +} diff --git a/noir/docs/processed-docs-cache/reference/nargo_commands.md b/noir/docs/processed-docs-cache/reference/nargo_commands.md new file mode 100644 index 00000000000..fc2671b2bfc --- /dev/null +++ b/noir/docs/processed-docs-cache/reference/nargo_commands.md @@ -0,0 +1,253 @@ +--- +title: Nargo +description: + Noir CLI Commands for Noir Prover and Verifier to create, execute, prove and verify programs, + generate Solidity verifier smart contract and compile into JSON file containing ACIR + representation and ABI of circuit. +keywords: + [ + Nargo, + Noir CLI, + Noir Prover, + Noir Verifier, + generate Solidity verifier, + compile JSON file, + ACIR representation, + ABI of circuit, + TypeScript, + ] +sidebar_position: 0 +--- + +## General options + +| Option | Description | +| -------------------- | -------------------------------------------------- | +| `--show-ssa` | Emit debug information for the intermediate SSA IR | +| `--deny-warnings` | Quit execution when warnings are emitted | +| `--silence-warnings` | Suppress warnings | +| `-h, --help` | Print help | + +## `nargo help [subcommand]` + +Prints the list of available commands or specific information of a subcommand. + +_Arguments_ + +| Argument | Description | +| -------------- | -------------------------------------------- | +| `` | The subcommand whose help message to display | + +## `nargo backend` + +Installs and selects custom backends used to generate and verify proofs. + +### Commands + +| Command | Description | +| ----------- | --------------------------------------------------------- | +| `current` | Prints the name of the currently active backend | +| `ls` | Prints the list of currently installed backends | +| `use` | Select the backend to use | +| `install` | Install a new backend from a URL | +| `uninstall` | Uninstalls a backend | +| `help` | Print this message or the help of the given subcommand(s) | + +### Options + +| Option | Description | +| ------------ | ----------- | +| `-h, --help` | Print help | + +## `nargo check` + +Generate the `Prover.toml` and `Verifier.toml` files for specifying prover and verifier in/output +values of the Noir program respectively. + +### Options + +| Option | Description | +| --------------------- | ------------------------------------- | +| `--package ` | The name of the package to check | +| `--workspace` | Check all packages in the workspace | +| `--print-acir` | Display the ACIR for compiled circuit | +| `--deny-warnings` | Treat all warnings as errors | +| `--silence-warnings` | Suppress warnings | +| `-h, --help` | Print help | + +### `nargo codegen-verifier` + +Generate a Solidity verifier smart contract for the program. + +### Options + +| Option | Description | +| --------------------- | ------------------------------------- | +| `--package ` | The name of the package to codegen | +| `--workspace` | Codegen all packages in the workspace | +| `--print-acir` | Display the ACIR for compiled circuit | +| `--deny-warnings` | Treat all warnings as errors | +| `--silence-warnings` | Suppress warnings | +| `-h, --help` | Print help | + +## `nargo compile` + +Compile the program into a JSON build artifact file containing the ACIR representation and the ABI +of the circuit. This build artifact can then be used to generate and verify proofs. + +You can also use "build" as an alias for compile (e.g. `nargo build`). + +### Options + +| Option | Description | +| --------------------- | ------------------------------------------------------------ | +| `--package ` | The name of the package to compile | +| `--workspace` | Compile all packages in the workspace | +| `--print-acir` | Display the ACIR for compiled circuit | +| `--deny-warnings` | Treat all warnings as errors | +| `--silence-warnings` | Suppress warnings | +| `-h, --help` | Print help | + +## `nargo new ` + +Creates a new Noir project in a new folder. + +**Arguments** + +| Argument | Description | +| -------- | -------------------------------- | +| `` | The path to save the new project | + +### Options + +| Option | Description | +| --------------- | ----------------------------------------------------- | +| `--name ` | Name of the package [default: package directory name] | +| `--lib` | Use a library template | +| `--bin` | Use a binary template [default] | +| `--contract` | Use a contract template | +| `-h, --help` | Print help | + +## `nargo init` + +Creates a new Noir project in the current directory. + +### Options + +| Option | Description | +| --------------- | ----------------------------------------------------- | +| `--name ` | Name of the package [default: current directory name] | +| `--lib` | Use a library template | +| `--bin` | Use a binary template [default] | +| `--contract` | Use a contract template | +| `-h, --help` | Print help | + +## `nargo execute [WITNESS_NAME]` + +Runs the Noir program and prints its return value. + +**Arguments** + +| Argument | Description | +| ---------------- | ----------------------------------------- | +| `[WITNESS_NAME]` | Write the execution witness to named file | + +### Options + +| Option | Description | +| --------------------------------- | ------------------------------------------------------------------------------------ | +| `-p, --prover-name ` | The name of the toml file which contains the inputs for the prover [default: Prover] | +| `--package ` | The name of the package to execute | +| `--workspace` | Execute all packages in the workspace | +| `--print-acir` | Display the ACIR for compiled circuit | +| `--deny-warnings` | Treat all warnings as errors | +| `--silence-warnings` | Suppress warnings | +| `--oracle-resolver` | JSON RPC url to solve oracle calls | +| `-h, --help` | Print help | + +_Usage_ + +The inputs to the circuit are read from the `Prover.toml` file generated by `nargo check`, which +must be filled in. + +To save the witness to file, run the command with a value for the `WITNESS_NAME` argument. A +`.tr` file will then be saved in the `./target` folder. + +## `nargo prove` + +Creates a proof for the program. + +### Options + +| Option | Description | +| ------------------------------------- | ---------------------------------------------------------------------------------------- | +| `-p, --prover-name ` | The name of the toml file which contains the inputs for the prover [default: Prover] | +| `-v, --verifier-name ` | The name of the toml file which contains the inputs for the verifier [default: Verifier] | +| `--verify` | Verify proof after proving | +| `--package ` | The name of the package to prove | +| `--workspace` | Prove all packages in the workspace | +| `--print-acir` | Display the ACIR for compiled circuit | +| `--deny-warnings` | Treat all warnings as errors | +| `--silence-warnings` | Suppress warnings | +| `--oracle-resolver` | JSON RPC url to solve oracle calls | +| `-h, --help` | Print help | + +## `nargo verify` + +Given a proof and a program, verify whether the proof is valid. + +### Options + +| Option | Description | +| ------------------------------------- | ---------------------------------------------------------------------------------------- | +| `-v, --verifier-name ` | The name of the toml file which contains the inputs for the verifier [default: Verifier] | +| `--package ` | The name of the package to verify | +| `--workspace` | Verify all packages in the workspace | +| `--print-acir` | Display the ACIR for compiled circuit | +| `--deny-warnings` | Treat all warnings as errors | +| `--silence-warnings` | Suppress warnings | +| `-h, --help` | Print help | + +## `nargo test [TEST_NAME]` + +Nargo will automatically compile and run any functions which have the decorator `#[test]` on them if +you run `nargo test`. To print `println` statements in tests, use the `--show-output` flag. + +Takes an optional `--exact` flag which allows you to select tests based on an exact name. + +See an example on the [testing page](../getting_started/tooling/testing.md). + +### Options + +| Option | Description | +| --------------------- | -------------------------------------- | +| `--show-output` | Display output of `println` statements | +| `--exact` | Only run tests that match exactly | +| `--package ` | The name of the package to test | +| `--workspace` | Test all packages in the workspace | +| `--print-acir` | Display the ACIR for compiled circuit | +| `--deny-warnings` | Treat all warnings as errors | +| `--silence-warnings` | Suppress warnings | +| `--oracle-resolver` | JSON RPC url to solve oracle calls | +| `-h, --help` | Print help | + +## `nargo info` + +Prints a table containing the information of the package. + +Currently the table provide + +1. The number of ACIR opcodes +2. The final number gates in the circuit used by a backend + +If the file contains a contract the table will provide the +above information about each function of the contract. + +## `nargo lsp` + +Start a long-running Language Server process that communicates over stdin/stdout. +Usually this command is not run by a user, but instead will be run by a Language Client, such as [vscode-noir](https://github.com/noir-lang/vscode-noir). + +## `nargo fmt` + +Automatically formats your Noir source code based on the default formatting settings. diff --git a/noir/docs/processed-docs-cache/tutorials/noirjs_app.md b/noir/docs/processed-docs-cache/tutorials/noirjs_app.md new file mode 100644 index 00000000000..23534795dde --- /dev/null +++ b/noir/docs/processed-docs-cache/tutorials/noirjs_app.md @@ -0,0 +1,279 @@ +--- +title: Building a web app with NoirJS +description: Learn how to setup a new app that uses Noir to generate and verify zero-knowledge SNARK proofs in a typescript or javascript environment. +keywords: [how to, guide, javascript, typescript, noir, barretenberg, zero-knowledge, proofs, app] +sidebar_position: 0 +pagination_next: noir/concepts/data_types/index +--- + +NoirJS is a set of packages meant to work both in a browser and a server environment. In this tutorial, we will build a simple web app using them. From here, you should get an idea on how to proceed with your own Noir projects! + +You can find the complete app code for this guide [here](https://github.com/noir-lang/tiny-noirjs-app). + +## Setup + +:::note + +Feel free to use whatever versions, just keep in mind that Nargo and the NoirJS packages are meant to be in sync. For example, Nargo 0.19.x matches `noir_js@0.19.x`, etc. + +In this guide, we will be pinned to 0.19.4. + +::: + +Before we start, we want to make sure we have Node and Nargo installed. + +We start by opening a terminal and executing `node --version`. If we don't get an output like `v20.10.0`, that means node is not installed. Let's do that by following the handy [nvm guide](https://github.com/nvm-sh/nvm?tab=readme-ov-file#install--update-script). + +As for `Nargo`, we can follow the the [Nargo guide](../getting_started/installation/index.md) to install it. If you're lazy, just paste this on a terminal and run `noirup`: + +```sh +curl -L https://raw.githubusercontent.com/noir-lang/noirup/main/install | bash +``` + +Easy enough. Onwards! + +## Our project + +ZK is a powerful technology. An app that doesn't reveal one of the inputs to *anyone* is almost unbelievable, yet Noir makes it as easy as a single line of code. + +In fact, it's so simple that it comes nicely packaged in `nargo`. Let's do that! + +### Nargo + +Run: + +```nargo new circuit``` + +And... That's about it. Your program is ready to be compiled and run. + +To compile, let's `cd` into the `circuit` folder to enter our project, and call: + +```nargo compile``` + +This compiles our circuit into `json` format and add it to a new `target` folder. + +:::info + +At this point in the tutorial, your folder structure should look like this: + +```tree +. +└── circuit <---- our working directory + ├── Nargo.toml + ├── src + │ └── main.nr + └── target + └── circuit.json +``` + +::: + +### Node and Vite + +If you want to explore Nargo, feel free to go on a side-quest now and follow the steps in the +[getting started](../getting_started/hello_noir/index.md) guide. However, we want our app to run on the browser, so we need Vite. + +Vite is a powerful tool to generate static websites. While it provides all kinds of features, let's just go barebones with some good old vanilla JS. + +To do this this, go back to the previous folder (`cd ..`) and create a new vite project by running `npm create vite` and choosing "Vanilla" and "Javascript". + +You should see `vite-project` appear in your root folder. This seems like a good time to `cd` into it and install our NoirJS packages: + +```bash +npm i @noir-lang/backend_barretenberg@0.19.4 @noir-lang/noir_js@0.19.4 +``` + +:::info + +At this point in the tutorial, your folder structure should look like this: + +```tree +. +└── circuit + └── ...etc... +└── vite-project <---- our working directory + └── ...etc... +``` + +::: + +#### Some cleanup + +`npx create vite` is amazing but it creates a bunch of files we don't really need for our simple example. Actually, let's just delete everything except for `index.html`, `main.js` and `package.json`. I feel lighter already. + +![my heart is ready for you, noir.js](../../static/img/memes/titanic.jpeg) + +## HTML + +Our app won't run like this, of course. We need some working HTML, at least. Let's open our broken-hearted `index.html` and replace everything with this code snippet: + +```html + + + + + + +

Noir app

+
+ + +
+
+

Logs

+

Proof

+
+ + +``` + +It *could* be a beautiful UI... Depending on which universe you live in. + +## Some good old vanilla Javascript + +Our love for Noir needs undivided attention, so let's just open `main.js` and delete everything (this is where the romantic scenery becomes a bit creepy). + +Start by pasting in this boilerplate code: + +```js +const setup = async () => { + await Promise.all([ + import("@noir-lang/noirc_abi").then(module => + module.default(new URL("@noir-lang/noirc_abi/web/noirc_abi_wasm_bg.wasm", import.meta.url).toString()) + ), + import("@noir-lang/acvm_js").then(module => + module.default(new URL("@noir-lang/acvm_js/web/acvm_js_bg.wasm", import.meta.url).toString()) + ) + ]); +} + +function display(container, msg) { + const c = document.getElementById(container); + const p = document.createElement('p'); + p.textContent = msg; + c.appendChild(p); +} + +document.getElementById('submitGuess').addEventListener('click', async () => { + try { + // here's where love happens + } catch(err) { + display("logs", "Oh 💔 Wrong guess") + } +}); + +``` + +The display function doesn't do much. We're simply manipulating our website to see stuff happening. For example, if the proof fails, it will simply log a broken heart 😢 + +As for the `setup` function, it's just a sad reminder that dealing with `wasm` on the browser is not as easy as it should. Just copy, paste, and forget. + +:::info + +At this point in the tutorial, your folder structure should look like this: + +```tree +. +└── circuit + └── ...same as above +└── vite-project + ├── main.js + ├── package.json + └── index.html +``` + +You'll see other files and folders showing up (like `package-lock.json`, `node_modules`) but you shouldn't have to care about those. + +::: + +## Some NoirJS + +We're starting with the good stuff now. If you've compiled the circuit as described above, you should have a `json` file we want to import at the very top of our `main.js` file: + +```ts +import circuit from '../circuit/target/circuit.json'; +``` + +[Noir is backend-agnostic](../index.mdx#whats-new-about-noir). We write Noir, but we also need a proving backend. That's why we need to import and instantiate the two dependencies we installed above: `BarretenbergBackend` and `Noir`. Let's import them right below: + +```js +import { BarretenbergBackend } from '@noir-lang/backend_barretenberg'; +import { Noir } from '@noir-lang/noir_js'; +``` + +And instantiate them inside our try-catch block: + +```ts +// try { +const backend = new BarretenbergBackend(circuit); +const noir = new Noir(circuit, backend); +// } +``` + +:::note + +For the remainder of the tutorial, everything will be happening inside the `try` block + +::: + +## Our app + +Now for the app itself. We're capturing whatever is in the input when people press the submit button. Just add this: + +```js +const x = parseInt(document.getElementById('guessInput').value); +const input = { x, y: 2 }; +``` + +Now we're ready to prove stuff! Let's feed some inputs to our circuit and calculate the proof: + +```js +await setup(); // let's squeeze our wasm inits here + +display('logs', 'Generating proof... ⌛'); +const proof = await noir.generateFinalProof(input); +display('logs', 'Generating proof... ✅'); +display('results', proof.proof); +``` + +You're probably eager to see stuff happening, so go and run your app now! + +From your terminal, run `npm run dev`. If it doesn't open a browser for you, just visit `localhost:5173`. You should now see the worst UI ever, with an ugly input. + +![Getting Started 0](@site/static/img/noir_getting_started_1.png) + +Now, our circuit says `fn main(x: Field, y: pub Field)`. This means only the `y` value is public, and it's hardcoded above: `input = { x, y: 2 }`. In other words, you won't need to send your secret`x` to the verifier! + +By inputting any number other than 2 in the input box and clicking "submit", you should get a valid proof. Otherwise the proof won't even generate correctly. By the way, if you're human, you shouldn't be able to understand anything on the "proof" box. That's OK. We like you, human ❤️. + +## Verifying + +Time to celebrate, yes! But we shouldn't trust machines so blindly. Let's add these lines to see our proof being verified: + +```js +display('logs', 'Verifying proof... ⌛'); +const verification = await noir.verifyFinalProof(proof); +if (verification) display('logs', 'Verifying proof... ✅'); +``` + +You have successfully generated a client-side Noir web app! + +![coded app without math knowledge](../../static/img/memes/flextape.jpeg) + +## Further Reading + +You can see how noirjs is used in a full stack Next.js hardhat application in the [noir-starter repo here](https://github.com/noir-lang/noir-starter/tree/main/vite-hardhat). The example shows how to calculate a proof in the browser and verify it with a deployed Solidity verifier contract from noirjs. + +You should also check out the more advanced examples in the [noir-examples repo](https://github.com/noir-lang/noir-examples), where you'll find reference usage for some cool apps. diff --git a/noir/docs/processed-docs/explainers/explainer-oracle.md b/noir/docs/processed-docs/explainers/explainer-oracle.md new file mode 100644 index 00000000000..b84ca5dd986 --- /dev/null +++ b/noir/docs/processed-docs/explainers/explainer-oracle.md @@ -0,0 +1,57 @@ +--- +title: Oracles +description: This guide provides an in-depth understanding of how Oracles work in Noir programming. Learn how to use outside calculations in your programs, constrain oracles, and understand their uses and limitations. +keywords: + - Noir Programming + - Oracles + - JSON-RPC + - Foreign Call Handlers + - Constrained Functions + - Blockchain Programming +sidebar_position: 1 +--- + +If you've seen "The Matrix" you may recall "The Oracle" as Gloria Foster smoking cigarettes and baking cookies. While she appears to "know things", she is actually providing a calculation of a pre-determined future. Noir Oracles are similar, in a way. They don't calculate the future (yet), but they allow you to use outside calculations in your programs. + +![matrix oracle prediction](@site/static/img/memes/matrix_oracle.jpeg) + +A Noir program is usually self-contained. You can pass certain inputs to it, and it will generate a deterministic output for those inputs. But what if you wanted to defer some calculation to an outside process or source? + +Oracles are functions that provide this feature. + +## Use cases + +An example usage for Oracles is proving something on-chain. For example, proving that the ETH-USDC quote was below a certain target at a certain block time. Or even making more complex proofs like proving the ownership of an NFT as an anonymous login method. + +Another interesting use case is to defer expensive calculations to be made outside of the Noir program, and then constraining the result; similar to the use of [unconstrained functions](../noir/concepts//unconstrained.md). + +In short, anything that can be constrained in a Noir program but needs to be fetched from an external source is a great candidate to be used in oracles. + +## Constraining oracles + +Just like in The Matrix, Oracles are powerful. But with great power, comes great responsibility. Just because you're using them in a Noir program doesn't mean they're true. Noir has no superpowers. If you want to prove that Portugal won the Euro Cup 2016, you're still relying on potentially untrusted information. + +To give a concrete example, Alice wants to login to the [NounsDAO](https://nouns.wtf/) forum with her username "noir_nouner" by proving she owns a noun without revealing her ethereum address. Her Noir program could have a oracle call like this: + +```rust +#[oracle(getNoun)] +unconstrained fn get_noun(address: Field) -> Field +``` + +This oracle could naively resolve with the number of Nouns she possesses. However, it is useless as a trusted source, as the oracle could resolve to anything Alice wants. In order to make this oracle call actually useful, Alice would need to constrain the response from the oracle, by proving her address and the noun count belongs to the state tree of the contract. + +In short, **Oracles don't prove anything. Your Noir program does.** + +:::danger + +If you don't constrain the return of your oracle, you could be clearly opening an attack vector on your Noir program. Make double-triple sure that the return of an oracle call is constrained! + +::: + +## How to use Oracles + +On CLI, Nargo resolves oracles by making JSON RPC calls, which means it would require an RPC node to be running. + +In JavaScript, NoirJS accepts and resolves arbitrary call handlers (that is, not limited to JSON) as long as they matches the expected types the developer defines. Refer to [Foreign Call Handler](../reference/NoirJS/noir_js/type-aliases/ForeignCallHandler.md) to learn more about NoirJS's call handling. + +If you want to build using oracles, follow through to the [oracle guide](../how_to/how-to-oracles.md) for a simple example on how to do that. diff --git a/noir/docs/processed-docs/explainers/explainer-recursion.md b/noir/docs/processed-docs/explainers/explainer-recursion.md new file mode 100644 index 00000000000..8f992ec29fd --- /dev/null +++ b/noir/docs/processed-docs/explainers/explainer-recursion.md @@ -0,0 +1,177 @@ +--- +title: Recursive proofs +description: Explore the concept of recursive proofs in Zero-Knowledge programming. Understand how recursion works in Noir, a language for writing smart contracts on the EVM blockchain. Learn through practical examples like Alice and Bob's guessing game, Charlie's recursive merkle tree, and Daniel's reusable components. Discover how to use recursive proofs to optimize computational resources and improve efficiency. + +keywords: + [ + "Recursive Proofs", + "Zero-Knowledge Programming", + "Noir", + "EVM Blockchain", + "Smart Contracts", + "Recursion in Noir", + "Alice and Bob Guessing Game", + "Recursive Merkle Tree", + "Reusable Components", + "Optimizing Computational Resources", + "Improving Efficiency", + "Verification Key", + "Aggregation Objects", + "Recursive zkSNARK schemes", + "PLONK", + "Proving and Verification Keys" + ] +sidebar_position: 1 +--- + +In programming, we tend to think of recursion as something calling itself. A classic example would be the calculation of the factorial of a number: + +```js +function factorial(n) { + if (n === 0 || n === 1) { + return 1; + } else { + return n * factorial(n - 1); + } +} +``` + +In this case, while `n` is not `1`, this function will keep calling itself until it hits the base case, bubbling up the result on the call stack: + +```md + Is `n` 1? <--------- + /\ / + / \ n = n -1 + / \ / + Yes No -------- +``` + +In Zero-Knowledge, recursion has some similarities. + +It is not a Noir function calling itself, but a proof being used as an input to another circuit. In short, you verify one proof *inside* another proof, returning the proof that both proofs are valid. + +This means that, given enough computational resources, you can prove the correctness of any arbitrary number of proofs in a single proof. This could be useful to design state channels (for which a common example would be [Bitcoin's Lightning Network](https://en.wikipedia.org/wiki/Lightning_Network)), to save on gas costs by settling one proof on-chain, or simply to make business logic less dependent on a consensus mechanism. + +## Examples + +Let us look at some of these examples + +### Alice and Bob - Guessing game + +Alice and Bob are friends, and they like guessing games. They want to play a guessing game online, but for that, they need a trusted third-party that knows both of their secrets and finishes the game once someone wins. + +So, they use zero-knowledge proofs. Alice tries to guess Bob's number, and Bob will generate a ZK proof stating whether she succeeded or failed. + +This ZK proof can go on a smart contract, revealing the winner and even giving prizes. However, this means every turn needs to be verified on-chain. This incurs some cost and waiting time that may simply make the game too expensive or time-consuming to be worth it. + +As a solution, Alice proposes the following: "what if Bob generates his proof, and instead of sending it on-chain, I verify it *within* my own proof before playing my own turn?". + +She can then generate a proof that she verified his proof, and so on. + +```md + Did you fail? <-------------------------- + / \ / + / \ n = n -1 + / \ / + Yes No / + | | / + | | / + | You win / + | / + | / +Generate proof of that / + + / + my own guess ---------------- +``` + +### Charlie - Recursive merkle tree + +Charlie is a concerned citizen, and wants to be sure his vote in an election is accounted for. He votes with a ZK proof, but he has no way of knowing that his ZK proof was included in the total vote count! + +If the vote collector puts all of the votes into a [Merkle tree](https://en.wikipedia.org/wiki/Merkle_tree), everyone can prove the verification of two proofs within one proof, as such: + +```md + abcd + __________|______________ + | | + ab cd + _____|_____ ______|______ + | | | | + alice bob charlie daniel +``` + +Doing this recursively allows us to arrive on a final proof `abcd` which if true, verifies the correctness of all the votes. + +### Daniel - Reusable components + +Daniel has a big circuit and a big headache. A part of his circuit is a setup phase that finishes with some assertions that need to be made. But that section alone takes most of the proving time, and is largely independent of the rest of the circuit. + +He might find it more efficient to generate a proof for that setup phase separately, and verify that proof recursively in the actual business logic section of his circuit. This will allow for parallelization of both proofs, which results in a considerable speedup. + +## What params do I need + +As you can see in the [recursion reference](noir/standard_library/recursion.md), a simple recursive proof requires: + +- The proof to verify +- The Verification Key of the circuit that generated the proof +- A hash of this verification key, as it's needed for some backends +- The public inputs for the proof +- The input aggregation object + +It also returns the `output aggregation object`. These aggregation objects can be confusing at times, so let's dive in a little bit. + +### Aggregation objects + +Recursive zkSNARK schemes do not necessarily "verify a proof" in the sense that you expect a true or false to be spit out by the verifier. Rather an aggregation object is built over the public inputs. + +In the case of PLONK the recursive aggregation object is two G1 points (expressed as 16 witness values). The final verifier (in our case this is most often the smart contract verifier) has to be aware of this aggregation object to execute a pairing and check the validity of these points. + +So, taking the example of Alice and Bob and their guessing game: + +- Alice makes her guess. Her proof is *not* recursive: it doesn't verify any proof within it! It's just a standard `assert(x != y)` circuit +- Bob verifies Alice's proof and makes his own guess. In this circuit, he is verifying a proof, so it needs to output an `aggregation object`: he is generating a recursive proof! +- Alice verifies Bob's *recursive proof*, and uses Bob's `output aggregation object` as the `input aggregation object` in her proof... Which in turn, generates another `output aggregation object`. + +One should notice that when Bob generates his first proof, he has no input aggregation object. Because he is not verifying an recursive proof, he has no `input aggregation object`. In this case, he may use zeros instead. + +We can imagine the `aggregation object` as the baton in a [relay race](https://en.wikipedia.org/wiki/Relay_race). The first runner doesn't have to receive the baton from anyone else, as he/she already starts with it. But when his/her turn is over, the next runner needs to receive it, run a bit more, and pass it along. Even though every runner could theoretically verify the baton mid-run (why not? 🏃🔍), only at the end of the race does the referee verify that the whole race is valid. + +## Some architecture + +As with everything in computer science, there's no one-size-fits all. But there are some patterns that could help understanding and implementing them. To give three examples: + +### Adding some logic to a proof verification + +This would be an approach for something like our guessing game, where proofs are sent back and forth and are verified by each opponent. This circuit would be divided in two sections: + +- A `recursive verification` section, which would be just the call to `std::verify_proof`, and that would be skipped on the first move (since there's no proof to verify) +- A `guessing` section, which is basically the logic part where the actual guessing happens + +In such a situation, and assuming Alice is first, she would skip the first part and try to guess Bob's number. Bob would then verify her proof on the first section of his run, and try to guess Alice's number on the second part, and so on. + +### Aggregating proofs + +In some one-way interaction situations, recursion would allow for aggregation of simple proofs that don't need to be immediately verified on-chain or elsewhere. + +To give a practical example, a barman wouldn't need to verify a "proof-of-age" on-chain every time he serves alcohol to a customer. Instead, the architecture would comprise two circuits: + +- A `main`, non-recursive circuit with some logic +- A `recursive` circuit meant to verify two proofs in one proof + +The customer's proofs would be intermediate, and made on their phones, and the barman could just verify them locally. He would then aggregate them into a final proof sent on-chain (or elsewhere) at the end of the day. + +### Recursively verifying different circuits + +Nothing prevents you from verifying different circuits in a recursive proof, for example: + +- A `circuit1` circuit +- A `circuit2` circuit +- A `recursive` circuit + +In this example, a regulator could verify that taxes were paid for a specific purchase by aggregating both a `payer` circuit (proving that a purchase was made and taxes were paid), and a `receipt` circuit (proving that the payment was received) + +## How fast is it + +At the time of writing, verifying recursive proofs is surprisingly fast. This is because most of the time is spent on generating the verification key that will be used to generate the next proof. So you are able to cache the verification key and reuse it later. + +Currently, Noir JS packages don't expose the functionality of loading proving and verification keys, but that feature exists in the underlying `bb.js` package. diff --git a/noir/docs/processed-docs/getting_started/_category_.json b/noir/docs/processed-docs/getting_started/_category_.json new file mode 100644 index 00000000000..5d694210bbf --- /dev/null +++ b/noir/docs/processed-docs/getting_started/_category_.json @@ -0,0 +1,5 @@ +{ + "position": 0, + "collapsible": true, + "collapsed": true +} diff --git a/noir/docs/processed-docs/getting_started/hello_noir/_category_.json b/noir/docs/processed-docs/getting_started/hello_noir/_category_.json new file mode 100644 index 00000000000..23b560f610b --- /dev/null +++ b/noir/docs/processed-docs/getting_started/hello_noir/_category_.json @@ -0,0 +1,5 @@ +{ + "position": 1, + "collapsible": true, + "collapsed": true +} diff --git a/noir/docs/processed-docs/getting_started/hello_noir/index.md b/noir/docs/processed-docs/getting_started/hello_noir/index.md new file mode 100644 index 00000000000..743c4d8d634 --- /dev/null +++ b/noir/docs/processed-docs/getting_started/hello_noir/index.md @@ -0,0 +1,142 @@ +--- +title: Creating a Project +description: + Learn how to create and verify your first Noir program using Nargo, a programming language for + zero-knowledge proofs. +keywords: + [ + Nargo, + Noir, + zero-knowledge proofs, + programming language, + create Noir program, + verify Noir program, + step-by-step guide, + ] +sidebar_position: 1 + +--- + +Now that we have installed Nargo, it is time to make our first hello world program! + +## Create a Project Directory + +Noir code can live anywhere on your computer. Let us create a _projects_ folder in the home +directory to house our Noir programs. + +For Linux, macOS, and Windows PowerShell, create the directory and change directory into it by +running: + +```sh +mkdir ~/projects +cd ~/projects +``` + +## Create Our First Nargo Project + +Now that we are in the projects directory, create a new Nargo project by running: + +```sh +nargo new hello_world +``` + +> **Note:** `hello_world` can be any arbitrary project name, we are simply using `hello_world` for +> demonstration. +> +> In production, the common practice is to name the project folder as `circuits` for better +> identifiability when sitting alongside other folders in the codebase (e.g. `contracts`, `scripts`, +> `test`). + +A `hello_world` folder would be created. Similar to Rust, the folder houses _src/main.nr_ and +_Nargo.toml_ which contain the source code and environmental options of your Noir program +respectively. + +### Intro to Noir Syntax + +Let us take a closer look at _main.nr_. The default _main.nr_ generated should look like this: + +```rust +fn main(x : Field, y : pub Field) { + assert(x != y); +} +``` + +The first line of the program specifies the program's inputs: + +```rust +x : Field, y : pub Field +``` + +Program inputs in Noir are private by default (e.g. `x`), but can be labeled public using the +keyword `pub` (e.g. `y`). To learn more about private and public values, check the +[Data Types](../../noir/concepts/data_types/index.md) section. + +The next line of the program specifies its body: + +```rust +assert(x != y); +``` + +The Noir syntax `assert` can be interpreted as something similar to constraints in other zk-contract languages. + +For more Noir syntax, check the [Language Concepts](../../noir/concepts/comments.md) chapter. + +## Build In/Output Files + +Change directory into _hello_world_ and build in/output files for your Noir program by running: + +```sh +cd hello_world +nargo check +``` + +Two additional files would be generated in your project directory: + +_Prover.toml_ houses input values, and _Verifier.toml_ houses public values. + +## Prove Our Noir Program + +Now that the project is set up, we can create a proof of correct execution of our Noir program. + +Fill in input values for execution in the _Prover.toml_ file. For example: + +```toml +x = "1" +y = "2" +``` + +Prove the valid execution of your Noir program: + +```sh +nargo prove +``` + +A new folder _proofs_ would then be generated in your project directory, containing the proof file +`.proof`, where the project name is defined in Nargo.toml. + +The _Verifier.toml_ file would also be updated with the public values computed from program +execution (in this case the value of `y`): + +```toml +y = "0x0000000000000000000000000000000000000000000000000000000000000002" +``` + +> **Note:** Values in _Verifier.toml_ are computed as 32-byte hex values. + +## Verify Our Noir Program + +Once a proof is generated, we can verify correct execution of our Noir program by verifying the +proof file. + +Verify your proof by running: + +```sh +nargo verify +``` + +The verification will complete in silence if it is successful. If it fails, it will log the +corresponding error instead. + +Congratulations, you have now created and verified a proof for your very first Noir program! + +In the [next section](./project_breakdown.md), we will go into more detail on each step performed. diff --git a/noir/docs/processed-docs/getting_started/hello_noir/project_breakdown.md b/noir/docs/processed-docs/getting_started/hello_noir/project_breakdown.md new file mode 100644 index 00000000000..6160a102c6c --- /dev/null +++ b/noir/docs/processed-docs/getting_started/hello_noir/project_breakdown.md @@ -0,0 +1,199 @@ +--- +title: Project Breakdown +description: + Learn about the anatomy of a Nargo project, including the purpose of the Prover and Verifier TOML + files, and how to prove and verify your program. +keywords: + [Nargo, Nargo project, Prover.toml, Verifier.toml, proof verification, private asset transfer] +sidebar_position: 2 +--- + +This section breaks down our hello world program from the previous section. We elaborate on the project +structure and what the `prove` and `verify` commands did. + +## Anatomy of a Nargo Project + +Upon creating a new project with `nargo new` and building the in/output files with `nargo check` +commands, you would get a minimal Nargo project of the following structure: + + - src + - Prover.toml + - Verifier.toml + - Nargo.toml + +The source directory _src_ holds the source code for your Noir program. By default only a _main.nr_ +file will be generated within it. + +### Prover.toml + +_Prover.toml_ is used for specifying the input values for executing and proving the program. You can specify `toml` files with different names by using the `--prover-name` or `-p` flags, see the [Prover](#provertoml) section below. Optionally you may specify expected output values for prove-time checking as well. + +### Verifier.toml + +_Verifier.toml_ contains public in/output values computed when executing the Noir program. + +### Nargo.toml + +_Nargo.toml_ contains the environmental options of your project. It contains a "package" section and a "dependencies" section. + +Example Nargo.toml: + +```toml +[package] +name = "noir_starter" +type = "bin" +authors = ["Alice"] +compiler_version = "0.9.0" +description = "Getting started with Noir" +entry = "circuit/main.nr" +license = "MIT" + +[dependencies] +ecrecover = {tag = "v0.9.0", git = "https://github.com/colinnielsen/ecrecover-noir.git"} +``` + +Nargo.toml for a [workspace](../../noir/modules_packages_crates/workspaces.md) will look a bit different. For example: + +```toml +[workspace] +members = ["crates/a", "crates/b"] +default-member = "crates/a" +``` + +#### Package section + +The package section defines a number of fields including: + +- `name` (**required**) - the name of the package +- `type` (**required**) - can be "bin", "lib", or "contract" to specify whether its a binary, library or Aztec contract +- `authors` (optional) - authors of the project +- `compiler_version` - specifies the version of the compiler to use. This is enforced by the compiler and follow's [Rust's versioning](https://doc.rust-lang.org/cargo/reference/manifest.html#the-version-field), so a `compiler_version = 0.18.0` will enforce Nargo version 0.18.0, `compiler_version = ^0.18.0` will enforce anything above 0.18.0 but below 0.19.0, etc. For more information, see how [Rust handles these operators](https://docs.rs/semver/latest/semver/enum.Op.html) +- `description` (optional) +- `entry` (optional) - a relative filepath to use as the entry point into your package (overrides the default of `src/lib.nr` or `src/main.nr`) +- `backend` (optional) +- `license` (optional) + +#### Dependencies section + +This is where you will specify any dependencies for your project. See the [Dependencies page](../../noir/modules_packages_crates/dependencies.md) for more info. + +`./proofs/` and `./contract/` directories will not be immediately visible until you create a proof or +verifier contract respectively. + +### main.nr + +The _main.nr_ file contains a `main` method, this method is the entry point into your Noir program. + +In our sample program, _main.nr_ looks like this: + +```rust +fn main(x : Field, y : Field) { + assert(x != y); +} +``` + +The parameters `x` and `y` can be seen as the API for the program and must be supplied by the +prover. Since neither `x` nor `y` is marked as public, the verifier does not supply any inputs, when +verifying the proof. + +The prover supplies the values for `x` and `y` in the _Prover.toml_ file. + +As for the program body, `assert` ensures that the condition to be satisfied (e.g. `x != y`) is +constrained by the proof of the execution of said program (i.e. if the condition was not met, the +verifier would reject the proof as an invalid proof). + +### Prover.toml + +The _Prover.toml_ file is a file which the prover uses to supply his witness values(both private and +public). + +In our hello world program the _Prover.toml_ file looks like this: + +```toml +x = "1" +y = "2" +``` + +When the command `nargo prove` is executed, two processes happen: + +1. Noir creates a proof that `x`, which holds the value of `1`, and `y`, which holds the value of `2`, + is not equal. This inequality constraint is due to the line `assert(x != y)`. + +2. Noir creates and stores the proof of this statement in the _proofs_ directory in a file called your-project.proof. So if your project is named "private_voting" (defined in the project Nargo.toml), the proof will be saved at `./proofs/private_voting.proof`. Opening this file will display the proof in hex format. + +#### Arrays of Structs + +The following code shows how to pass an array of structs to a Noir program to generate a proof. + +```rust +// main.nr +struct Foo { + bar: Field, + baz: Field, +} + +fn main(foos: [Foo; 3]) -> pub Field { + foos[2].bar + foos[2].baz +} +``` + +Prover.toml: + +```toml +[[foos]] # foos[0] +bar = 0 +baz = 0 + +[[foos]] # foos[1] +bar = 0 +baz = 0 + +[[foos]] # foos[2] +bar = 1 +baz = 2 +``` + +#### Custom toml files + +You can specify a `toml` file with a different name to use for proving by using the `--prover-name` or `-p` flags. + +This command looks for proof inputs in the default **Prover.toml** and generates the proof and saves it at `./proofs/.proof`: + +```bash +nargo prove +``` + +This command looks for proof inputs in the custom **OtherProver.toml** and generates proof and saves it at `./proofs/.proof`: + +```bash +nargo prove -p OtherProver +``` + +## Verifying a Proof + +When the command `nargo verify` is executed, two processes happen: + +1. Noir checks in the _proofs_ directory for a proof file with the project name (eg. test_project.proof) + +2. If that file is found, the proof's validity is checked + +> **Note:** The validity of the proof is linked to the current Noir program; if the program is +> changed and the verifier verifies the proof, it will fail because the proof is not valid for the +> _modified_ Noir program. + +In production, the prover and the verifier are usually two separate entities. A prover would +retrieve the necessary inputs, execute the Noir program, generate a proof and pass it to the +verifier. The verifier would then retrieve the public inputs, usually from external sources, and +verify the validity of the proof against it. + +Take a private asset transfer as an example: + +A person using a browser as the prover would retrieve private inputs locally (e.g. the user's private key) and +public inputs (e.g. the user's encrypted balance on-chain), compute the transfer, generate a proof +and submit it to the verifier smart contract. + +The verifier contract would then draw the user's encrypted balance directly from the blockchain and +verify the proof submitted against it. If the verification passes, additional functions in the +verifier contract could trigger (e.g. approve the asset transfer). + +Now that you understand the concepts, you'll probably want some editor feedback while you are writing more complex code. diff --git a/noir/docs/processed-docs/getting_started/installation/_category_.json b/noir/docs/processed-docs/getting_started/installation/_category_.json new file mode 100644 index 00000000000..0c02fb5d4d7 --- /dev/null +++ b/noir/docs/processed-docs/getting_started/installation/_category_.json @@ -0,0 +1,6 @@ +{ + "position": 0, + "label": "Install Nargo", + "collapsible": true, + "collapsed": true +} diff --git a/noir/docs/processed-docs/getting_started/installation/index.md b/noir/docs/processed-docs/getting_started/installation/index.md new file mode 100644 index 00000000000..4ef86aa5914 --- /dev/null +++ b/noir/docs/processed-docs/getting_started/installation/index.md @@ -0,0 +1,48 @@ +--- +title: Nargo Installation +description: + nargo is a command line tool for interacting with Noir programs. This page is a quick guide on how to install Nargo through the most common and easy method, noirup +keywords: [ + Nargo + Noir + Rust + Cargo + Noirup + Installation + Terminal Commands + Version Check + Nightlies + Specific Versions + Branches + Noirup Repository +] +pagination_next: getting_started/hello_noir/index +--- + +`nargo` is the one-stop-shop for almost everything related with Noir. The name comes from our love for Rust and its package manager `cargo`. + +With `nargo`, you can start new projects, compile, execute, prove, verify, test, generate solidity contracts, and do pretty much all that is available in Noir. + +Similarly to `rustup`, we also maintain an easy installation method that covers most machines: `noirup`. + +## Installing Noirup + +Open a terminal on your machine, and write: + +```bash +curl -L https://raw.githubusercontent.com/noir-lang/noirup/main/install | bash +``` + +Close the terminal, open another one, and run + +```bash +noirup +``` + +Done. That's it. You should have the latest version working. You can check with `nargo --version`. + +You can also install nightlies, specific versions +or branches. Check out the [noirup repository](https://github.com/noir-lang/noirup) for more +information. + +Now we're ready to start working on [our first Noir program!](../hello_noir/index.md) diff --git a/noir/docs/processed-docs/getting_started/installation/other_install_methods.md b/noir/docs/processed-docs/getting_started/installation/other_install_methods.md new file mode 100644 index 00000000000..a532f83750e --- /dev/null +++ b/noir/docs/processed-docs/getting_started/installation/other_install_methods.md @@ -0,0 +1,190 @@ +--- +title: Alternative Install Methods +description: + There are different ways to install Nargo, the one-stop shop and command-line tool for developing Noir programs. This guide explains other methods that don't rely on noirup, such as compiling from source, installing from binaries, and using WSL for windows +keywords: [ + Installation + Nargo + Noirup + Binaries + Compiling from Source + WSL for Windows + macOS + Linux + Nix + Direnv + Shell & editor experience + Building and testing + Uninstalling Nargo + Noir vs code extension +] +sidebar_position: 1 +--- + + +## Installation + +The most common method of installing Nargo is through [Noirup](./index.md) + +However, there are other methods for installing Nargo: + +- [Binaries](#binaries) +- [Compiling from Source](#compile-from-source) +- [WSL for Windows](#wsl-for-windows) + +### Binaries + +See [GitHub Releases](https://github.com/noir-lang/noir/releases) for the latest and previous +platform specific binaries. + +#### Step 1 + +Paste and run the following in the terminal to extract and install the binary: + +> **macOS / Linux:** If you are prompted with `Permission denied` when running commands, prepend +> `sudo` and re-run it. + +##### macOS (Apple Silicon) + +```bash +mkdir -p $HOME/.nargo/bin && \ +curl -o $HOME/.nargo/bin/nargo-aarch64-apple-darwin.tar.gz -L https://github.com/noir-lang/noir/releases/download/v0.6.0/nargo-aarch64-apple-darwin.tar.gz && \ +tar -xvf $HOME/.nargo/bin/nargo-aarch64-apple-darwin.tar.gz -C $HOME/.nargo/bin/ && \ +echo '\nexport PATH=$PATH:$HOME/.nargo/bin' >> ~/.zshrc && \ +source ~/.zshrc +``` + +##### macOS (Intel) + +```bash +mkdir -p $HOME/.nargo/bin && \ +curl -o $HOME/.nargo/bin/nargo-x86_64-apple-darwin.tar.gz -L https://github.com/noir-lang/noir/releases/download/v0.6.0/nargo-x86_64-apple-darwin.tar.gz && \ +tar -xvf $HOME/.nargo/bin/nargo-x86_64-apple-darwin.tar.gz -C $HOME/.nargo/bin/ && \ +echo '\nexport PATH=$PATH:$HOME/.nargo/bin' >> ~/.zshrc && \ +source ~/.zshrc +``` + +##### Linux (Bash) + +```bash +mkdir -p $HOME/.nargo/bin && \ +curl -o $HOME/.nargo/bin/nargo-x86_64-unknown-linux-gnu.tar.gz -L https://github.com/noir-lang/noir/releases/download/v0.6.0/nargo-x86_64-unknown-linux-gnu.tar.gz && \ +tar -xvf $HOME/.nargo/bin/nargo-x86_64-unknown-linux-gnu.tar.gz -C $HOME/.nargo/bin/ && \ +echo -e '\nexport PATH=$PATH:$HOME/.nargo/bin' >> ~/.bashrc && \ +source ~/.bashrc +``` + +#### Step 2 + +Check if the installation was successful by running `nargo --version`. You should get a version number. + +> **macOS:** If you are prompted with an OS alert, right-click and open the _nargo_ executable from +> Finder. Close the new terminal popped up and `nargo` should now be accessible. + +### Option 3: Compile from Source + +Due to the large number of native dependencies, Noir projects uses [Nix](https://nixos.org/) and [direnv](https://direnv.net/) to streamline the development experience. It helps mitigating issues commonly associated with dependency management, such as conflicts between required package versions for different projects (often referred to as "dependency hell"). + +Combined with direnv, which automatically sets or clears environment variables based on the directory, it further simplifies the development process by seamlessly integrating with the developer's shell, facilitating an efficient and reliable workflow for managing and deploying Noir projects with multiple dependencies. + +#### Setting up your environment + +For the best experience, please follow these instructions to setup your environment: + +1. Install Nix following [their guide](https://nixos.org/download.html) for your operating system. +2. Create the file `~/.config/nix/nix.conf` with the contents: + +```ini +experimental-features = nix-command +extra-experimental-features = flakes +``` + +3. Install direnv into your Nix profile by running: + +```sh +nix profile install nixpkgs#direnv +``` + +4. Add direnv to your shell following [their guide](https://direnv.net/docs/hook.html). + 1. For bash or zshell, add `eval "$(direnv hook bash)"` or `eval "$(direnv hook zsh)"` to your ~/.bashrc or ~/.zshrc file, respectively. +5. Restart your shell. + +#### Shell & editor experience + +Now that your environment is set up, you can get to work on the project. + +1. Clone the repository, such as: + +```sh +git clone git@github.com:noir-lang/noir +``` + +> Replacing `noir` with whichever repository you want to work on. + +2. Navigate to the directory: + +```sh +cd noir +``` + +> Replacing `noir` with whichever repository you cloned. + +3. You should see a **direnv error** because projects aren't allowed by default. Make sure you've reviewed and trust our `.envrc` file, then you need to run: + +```sh +direnv allow +``` + +4. Now, wait awhile for all the native dependencies to be built. This will take some time and direnv will warn you that it is taking a long time, but we just need to let it run. + +5. Once you are presented with your prompt again, you can start your editor within the project directory (we recommend [VSCode](https://code.visualstudio.com/)): + +```sh +code . +``` + +6. (Recommended) When launching VSCode for the first time, you should be prompted to install our recommended plugins. We highly recommend installing these for the best development experience. + +#### Building and testing + +Assuming you are using `direnv` to populate your environment, building and testing the project can be done +with the typical `cargo build`, `cargo test`, and `cargo clippy` commands. You'll notice that the `cargo` version matches the version we specify in `rust-toolchain.toml`, which is 1.71.1 at the time of this writing. + +If you want to build the entire project in an isolated sandbox, you can use Nix commands: + +1. `nix build .` (or `nix build . -L` for verbose output) to build the project in a Nix sandbox. +2. `nix flake check` (or `nix flake check -L` for verbose output) to run clippy and tests in a Nix sandbox. + +#### Without `direnv` + +If you have hesitations with using direnv, you can launch a subshell with `nix develop` and then launch your editor from within the subshell. However, if VSCode was already launched in the project directory, the environment won't be updated. + +Advanced: If you aren't using direnv nor launching your editor within the subshell, you can try to install Barretenberg and other global dependencies the package needs. This is an advanced workflow and likely won't receive support! + +### Option 4: WSL (for Windows) + +The default backend for Noir (Barretenberg) doesn't provide Windows binaries at this time. For that reason, Noir cannot be installed natively. However, it is available by using Windows Subsystem for Linux (WSL). + +Step 1: Follow the instructions [here](https://learn.microsoft.com/en-us/windows/wsl/install) to install and run WSL. + +step 2: Follow the [Noirup instructions](./index.md). + +## Uninstalling Nargo + +### Noirup + +If you installed Noir with `noirup`, you can uninstall Noir by removing the files in `~/.nargo`, `~/nargo` and `~/noir_cache`. + +```bash +rm -r ~/.nargo +rm -r ~/nargo +rm -r ~/noir_cache +``` + +### Nix + +If you installed Noir with Nix or from source, you can remove the binary located at `~/.nix-profile/bin/nargo`. + +```bash +rm ~/.nix-profile/bin/nargo +``` diff --git a/noir/docs/processed-docs/getting_started/tooling/_category_.json b/noir/docs/processed-docs/getting_started/tooling/_category_.json new file mode 100644 index 00000000000..55804c03a71 --- /dev/null +++ b/noir/docs/processed-docs/getting_started/tooling/_category_.json @@ -0,0 +1,6 @@ +{ + "position": 2, + "label": "Tooling", + "collapsible": true, + "collapsed": true +} diff --git a/noir/docs/processed-docs/getting_started/tooling/index.mdx b/noir/docs/processed-docs/getting_started/tooling/index.mdx new file mode 100644 index 00000000000..ac480f3c9f5 --- /dev/null +++ b/noir/docs/processed-docs/getting_started/tooling/index.mdx @@ -0,0 +1,38 @@ +--- +title: Tooling +Description: This section provides information about the various tools and utilities available for Noir development. It covers the Noir playground, IDE tools, Codespaces, and community projects. +Keywords: [Noir, Development, Playground, IDE Tools, Language Service Provider, VS Code Extension, Codespaces, noir-starter, Community Projects, Awesome Noir Repository, Developer Tooling] +--- + +Noir is meant to be easy to develop with. For that reason, a number of utilities have been put together to ease the development process as much as feasible in the zero-knowledge world. + +## Playground + +The Noir playground is an easy way to test small ideas, share snippets, and integrate in other websites. You can access it at [play.noir-lang.org](https://play.noir-lang.org). + +## IDE tools + +When you install Nargo, you're also installing a Language Service Provider (LSP), which can be used by IDEs to provide syntax highlighting, codelens, warnings, and more. + +The easiest way to use these tools is by installing the [Noir VS Code extension](https://marketplace.visualstudio.com/items?itemName=noir-lang.vscode-noir). + +## Codespaces + +Some Noir repos have leveraged Codespaces in order to ease the development process. You can visit the [noir-starter](https://github.com/noir-lang/noir-starter) for an example. + + + +## GitHub Actions + +You can use `noirup` with GitHub Actions for CI/CD and automated testing. It is as simple as +installing `noirup` and running tests in your GitHub Action `yml` file. + +See the +[config file in the Noir repo](https://github.com/TomAFrench/noir-hashes/blob/master/.github/workflows/noir.yml) for an example usage. + +## Community projects + +As an open-source project, Noir has received many contributions over time. Some of them are related with developer tooling, and you can see some of them in [Awesome Noir repository](https://github.com/noir-lang/awesome-noir#dev-tools) diff --git a/noir/docs/processed-docs/getting_started/tooling/language_server.md b/noir/docs/processed-docs/getting_started/tooling/language_server.md new file mode 100644 index 00000000000..81e0356ef8a --- /dev/null +++ b/noir/docs/processed-docs/getting_started/tooling/language_server.md @@ -0,0 +1,43 @@ +--- +title: Language Server +description: Learn about the Noir Language Server, how to install the components, and configuration that may be required. +keywords: [Nargo, Language Server, LSP, VSCode, Visual Studio Code] +sidebar_position: 0 +--- + +This section helps you install and configure the Noir Language Server. + +The Language Server Protocol (LSP) has two components, the [Server](#language-server) and the [Client](#language-client). Below we describe each in the context of Noir. + +## Language Server + +The Server component is provided by the Nargo command line tool that you installed at the beginning of this guide. +As long as Nargo is installed and you've used it to run other commands in this guide, it should be good to go! + +If you'd like to verify that the `nargo lsp` command is available, you can run `nargo --help` and look for `lsp` in the list of commands. If you see it, you're using a version of Noir with LSP support. + +## Language Client + +The Client component is usually an editor plugin that launches the Server. It communicates LSP messages between the editor and the Server. For example, when you save a file, the Client will alert the Server, so it can try to compile the project and report any errors. + +Currently, Noir provides a Language Client for Visual Studio Code via the [vscode-noir](https://github.com/noir-lang/vscode-noir) extension. You can install it via the [Visual Studio Marketplace](https://marketplace.visualstudio.com/items?itemName=noir-lang.vscode-noir). + +> **Note:** Noir's Language Server Protocol support currently assumes users' VSCode workspace root to be the same as users' Noir project root (i.e. where Nargo.toml lies). +> +> If LSP features seem to be missing / malfunctioning, make sure you are opening your Noir project directly (instead of as a sub-folder) in your VSCode instance. + +When your language server is running correctly and the VSCode plugin is installed, you should see handy codelens buttons for compilation, measuring circuit size, execution, and tests: + +![Compile and Execute](@site/static/img/codelens_compile_execute.png) +![Run test](@site/static/img/codelens_run_test.png) + +You should also see your tests in the `testing` panel: + +![Testing panel](@site/static/img/codelens_testing_panel.png) + +### Configuration + +- **Noir: Enable LSP** - If checked, the extension will launch the Language Server via `nargo lsp` and communicate with it. +- **Noir: Nargo Flags** - Additional flags may be specified if you require them to be added when the extension calls `nargo lsp`. +- **Noir: Nargo Path** - An absolute path to a Nargo binary with the `lsp` command. This may be useful if Nargo is not within the `PATH` of your editor. +- **Noir > Trace: Server** - Setting this to `"messages"` or `"verbose"` will log LSP messages between the Client and Server. Useful for debugging. diff --git a/noir/docs/processed-docs/getting_started/tooling/testing.md b/noir/docs/processed-docs/getting_started/tooling/testing.md new file mode 100644 index 00000000000..d3e0c522473 --- /dev/null +++ b/noir/docs/processed-docs/getting_started/tooling/testing.md @@ -0,0 +1,62 @@ +--- +title: Testing in Noir +description: Learn how to use Nargo to test your Noir program in a quick and easy way +keywords: [Nargo, testing, Noir, compile, test] +sidebar_position: 1 +--- + +You can test your Noir programs using Noir circuits. + +Nargo will automatically compile and run any functions which have the decorator `#[test]` on them if +you run `nargo test`. + +For example if you have a program like: + +```rust +fn add(x: u64, y: u64) -> u64 { + x + y +} +#[test] +fn test_add() { + assert(add(2,2) == 4); + assert(add(0,1) == 1); + assert(add(1,0) == 1); +} +``` + +Running `nargo test` will test that the `test_add` function can be executed while satisfying all +the constraints which allows you to test that add returns the expected values. Test functions can't +have any arguments currently. + +### Test fail + +You can write tests that are expected to fail by using the decorator `#[test(should_fail)]`. For example: + +```rust +fn add(x: u64, y: u64) -> u64 { + x + y +} +#[test(should_fail)] +fn test_add() { + assert(add(2,2) == 5); +} +``` + +You can be more specific and make it fail with a specific reason by using `should_fail_with = "`: + +```rust +fn main(african_swallow_avg_speed : Field) { + assert(african_swallow_avg_speed == 65, "What is the airspeed velocity of an unladen swallow"); +} + +#[test] +fn test_king_arthur() { + main(65); +} + +#[test(should_fail_with = "What is the airspeed velocity of an unladen swallow")] +fn test_bridgekeeper() { + main(32); +} + +``` diff --git a/noir/docs/processed-docs/how_to/_category_.json b/noir/docs/processed-docs/how_to/_category_.json new file mode 100644 index 00000000000..23b560f610b --- /dev/null +++ b/noir/docs/processed-docs/how_to/_category_.json @@ -0,0 +1,5 @@ +{ + "position": 1, + "collapsible": true, + "collapsed": true +} diff --git a/noir/docs/processed-docs/how_to/how-to-oracles.md b/noir/docs/processed-docs/how_to/how-to-oracles.md new file mode 100644 index 00000000000..0d84d992320 --- /dev/null +++ b/noir/docs/processed-docs/how_to/how-to-oracles.md @@ -0,0 +1,280 @@ +--- +title: How to use Oracles +description: Learn how to use oracles in your Noir program with examples in both Nargo and NoirJS. This guide also covers writing a JSON RPC server and providing custom foreign call handlers for NoirJS. +keywords: + - Noir Programming + - Oracles + - Nargo + - NoirJS + - JSON RPC Server + - Foreign Call Handlers +sidebar_position: 1 +--- + +This guide shows you how to use oracles in your Noir program. For the sake of clarity, it assumes that: + +- You have read the [explainer on Oracles](../explainers/explainer-oracle.md) and are comfortable with the concept. +- You have a Noir program to add oracles to. You can create one using the [vite-hardhat starter](https://github.com/noir-lang/noir-starter/tree/main/vite-hardhat) as a boilerplate. +- You understand the concept of a JSON-RPC server. Visit the [JSON-RPC website](https://www.jsonrpc.org/) if you need a refresher. +- You are comfortable with server-side JavaScript (e.g. Node.js, managing packages, etc.). + +For reference, you can find the snippets used in this tutorial on the [Aztec DevRel Repository](https://github.com/AztecProtocol/dev-rel/tree/main/code-snippets/how-to-oracles). + +## Rundown + +This guide has 3 major steps: + +1. How to modify our Noir program to make use of oracle calls as unconstrained functions +2. How to write a JSON RPC Server to resolve these oracle calls with Nargo +3. How to use them in Nargo and how to provide a custom resolver in NoirJS + +## Step 1 - Modify your Noir program + +An oracle is defined in a Noir program by defining two methods: + +- An unconstrained method - This tells the compiler that it is executing an [unconstrained functions](../noir/concepts//unconstrained.md). +- A decorated oracle method - This tells the compiler that this method is an RPC call. + +An example of an oracle that returns a `Field` would be: + +```rust +#[oracle(getSqrt)] +unconstrained fn sqrt(number: Field) -> Field { } + +unconstrained fn get_sqrt(number: Field) -> Field { + sqrt(number) +} +``` + +In this example, we're wrapping our oracle function in a unconstrained method, and decorating it with `oracle(getSqrt)`. We can then call the unconstrained function as we would call any other function: + +```rust +fn main(input: Field) { + let sqrt = get_sqrt(input); +} +``` + +In the next section, we will make this `getSqrt` (defined on the `sqrt` decorator) be a method of the RPC server Noir will use. + +:::danger + +As explained in the [Oracle Explainer](../explainers/explainer-oracle.md), this `main` function is unsafe unless you constrain its return value. For example: + +```rust +fn main(input: Field) { + let sqrt = get_sqrt(input); + assert(sqrt.pow_32(2) as u64 == input as u64); // <---- constrain the return of an oracle! +} +``` + +::: + +:::info + +Currently, oracles only work with single params or array params. For example: + +```rust +#[oracle(getSqrt)] +unconstrained fn sqrt([Field; 2]) -> [Field; 2] { } +``` + +::: + +## Step 2 - Write an RPC server + +Brillig will call *one* RPC server. Most likely you will have to write your own, and you can do it in whatever language you prefer. In this guide, we will do it in Javascript. + +Let's use the above example of an oracle that consumes an array with two `Field` and returns their square roots: + +```rust +#[oracle(getSqrt)] +unconstrained fn sqrt(input: [Field; 2]) -> [Field; 2] { } + +unconstrained fn get_sqrt(input: [Field; 2]) -> [Field; 2] { + sqrt(input) +} + +fn main(input: [Field; 2]) { + let sqrt = get_sqrt(input); + assert(sqrt[0].pow_32(2) as u64 == input[0] as u64); + assert(sqrt[1].pow_32(2) as u64 == input[1] as u64); +} +``` + +:::info + +Why square root? + +In general, computing square roots is computationally more expensive than multiplications, which takes a toll when speaking about ZK applications. In this case, instead of calculating the square root in Noir, we are using our oracle to offload that computation to be made in plain. In our circuit we can simply multiply the two values. + +::: + +Now, we should write the correspondent RPC server, starting with the [default JSON-RPC 2.0 boilerplate](https://www.npmjs.com/package/json-rpc-2.0#example): + +```js +import { JSONRPCServer } from "json-rpc-2.0"; +import express from "express"; +import bodyParser from "body-parser"; + +const app = express(); +app.use(bodyParser.json()); + +const server = new JSONRPCServer(); +app.post("/", (req, res) => { + const jsonRPCRequest = req.body; + server.receive(jsonRPCRequest).then((jsonRPCResponse) => { + if (jsonRPCResponse) { + res.json(jsonRPCResponse); + } else { + res.sendStatus(204); + } + }); +}); + +app.listen(5555); +``` + +Now, we will add our `getSqrt` method, as expected by the `#[oracle(getSqrt)]` decorator in our Noir code. It maps through the params array and returns their square roots: + +```js +server.addMethod("getSqrt", async (params) => { + const values = params[0].Array.map(({ inner }) => { + return { inner: `${Math.sqrt(parseInt(inner, 16))}` }; + }); + return { values: [{ Array: values }] }; +}); +``` + +:::tip + +Brillig expects an object with an array of values. Each value is an object declaring to be `Single` or `Array` and returning a `inner` property *as a string*. For example: + +```json +{ "values": [{ "Array": [{ "inner": "1" }, { "inner": "2"}]}]} +{ "values": [{ "Single": { "inner": "1" }}]} +{ "values": [{ "Single": { "inner": "1" }}, { "Array": [{ "inner": "1", { "inner": "2" }}]}]} +``` + +If you're using Typescript, the following types may be helpful in understanding the expected return value and making sure they're easy to follow: + +```js +interface Value { + inner: string, +} + +interface SingleForeignCallParam { + Single: Value, +} + +interface ArrayForeignCallParam { + Array: Value[], +} + +type ForeignCallParam = SingleForeignCallParam | ArrayForeignCallParam; + +interface ForeignCallResult { + values: ForeignCallParam[], +} +``` + +::: + +## Step 3 - Usage with Nargo + +Using the [`nargo` CLI tool](../getting_started/installation/index.md), you can use oracles in the `nargo test`, `nargo execute` and `nargo prove` commands by passing a value to `--oracle-resolver`. For example: + +```bash +nargo test --oracle-resolver http://localhost:5555 +``` + +This tells `nargo` to use your RPC Server URL whenever it finds an oracle decorator. + +## Step 4 - Usage with NoirJS + +In a JS environment, an RPC server is not strictly necessary, as you may want to resolve your oracles without needing any JSON call at all. NoirJS simply expects that you pass a callback function when you generate proofs, and that callback function can be anything. + +For example, if your Noir program expects the host machine to provide CPU pseudo-randomness, you could simply pass it as the `foreignCallHandler`. You don't strictly need to create an RPC server to serve pseudo-randomness, as you may as well get it directly in your app: + +```js +const foreignCallHandler = (name, inputs) => crypto.randomBytes(16) // etc + +await noir.generateFinalProof(inputs, foreignCallHandler) +``` + +As one can see, in NoirJS, the [`foreignCallHandler`](../reference/NoirJS/noir_js/type-aliases/ForeignCallHandler.md) function simply means "a callback function that returns a value of type [`ForeignCallOutput`](../reference/NoirJS/noir_js/type-aliases/ForeignCallOutput.md). It doesn't have to be an RPC call like in the case for Nargo. + +:::tip + +Does this mean you don't have to write an RPC server like in [Step #2](#step-2---write-an-rpc-server)? + +You don't technically have to, but then how would you run `nargo test` or `nargo prove`? To use both `Nargo` and `NoirJS` in your development flow, you will have to write a JSON RPC server. + +::: + +In this case, let's make `foreignCallHandler` call the JSON RPC Server we created in [Step #2](#step-2---write-an-rpc-server), by making it a JSON RPC Client. + +For example, using the same `getSqrt` program in [Step #1](#step-1---modify-your-noir-program) (comments in the code): + +```js +import { JSONRPCClient } from "json-rpc-2.0"; + +// declaring the JSONRPCClient +const client = new JSONRPCClient((jsonRPCRequest) => { +// hitting the same JSON RPC Server we coded above + return fetch("http://localhost:5555", { + method: "POST", + headers: { + "content-type": "application/json", + }, + body: JSON.stringify(jsonRPCRequest), + }).then((response) => { + if (response.status === 200) { + return response + .json() + .then((jsonRPCResponse) => client.receive(jsonRPCResponse)); + } else if (jsonRPCRequest.id !== undefined) { + return Promise.reject(new Error(response.statusText)); + } + }); +}); + +// declaring a function that takes the name of the foreign call (getSqrt) and the inputs +const foreignCallHandler = async (name, input) => { + // notice that the "inputs" parameter contains *all* the inputs + // in this case we to make the RPC request with the first parameter "numbers", which would be input[0] + const oracleReturn = await client.request(name, [ + { Array: input[0].map((i) => ({ inner: i.toString("hex") })) }, + ]); + return [oracleReturn.values[0].Array.map((x) => x.inner)]; +}; + +// the rest of your NoirJS code +const input = { input: [4, 16] }; +const { witness } = await noir.execute(numbers, foreignCallHandler); +``` + +:::tip + +If you're in a NoirJS environment running your RPC server together with a frontend app, you'll probably hit a familiar problem in full-stack development: requests being blocked by [CORS](https://developer.mozilla.org/en-US/docs/Web/HTTP/CORS) policy. For development only, you can simply install and use the [`cors` npm package](https://www.npmjs.com/package/cors) to get around the problem: + +```bash +yarn add cors +``` + +and use it as a middleware: + +```js +import cors from "cors"; + +const app = express(); +app.use(cors()) +``` + +::: + +## Conclusion + +Hopefully by the end of this guide, you should be able to: + +- Write your own logic around Oracles and how to write a JSON RPC server to make them work with your Nargo commands. +- Provide custom foreign call handlers for NoirJS. diff --git a/noir/docs/processed-docs/how_to/how-to-recursion.md b/noir/docs/processed-docs/how_to/how-to-recursion.md new file mode 100644 index 00000000000..39db23f1f3a --- /dev/null +++ b/noir/docs/processed-docs/how_to/how-to-recursion.md @@ -0,0 +1,184 @@ +--- +title: How to use recursion on NoirJS +description: Learn how to implement recursion with NoirJS, a powerful tool for creating smart contracts on the EVM blockchain. This guide assumes familiarity with NoirJS, solidity verifiers, and the Barretenberg proving backend. Discover how to generate both final and intermediate proofs using `noir_js` and `backend_barretenberg`. +keywords: + [ + "NoirJS", + "EVM blockchain", + "smart contracts", + "recursion", + "solidity verifiers", + "Barretenberg backend", + "noir_js", + "backend_barretenberg", + "intermediate proofs", + "final proofs", + "nargo compile", + "json import", + "recursive circuit", + "recursive app" + ] +sidebar_position: 1 +--- + +This guide shows you how to use recursive proofs in your NoirJS app. For the sake of clarity, it is assumed that: + +- You already have a NoirJS app. If you don't, please visit the [NoirJS tutorial](../tutorials/noirjs_app.md) and the [reference](../reference/NoirJS/noir_js/index.md). +- You are familiar with what are recursive proofs and you have read the [recursion explainer](../explainers/explainer-recursion.md) +- You already built a recursive circuit following [the reference](../noir/standard_library/recursion.md), and understand how it works. + +It is also assumed that you're not using `noir_wasm` for compilation, and instead you've used [`nargo compile`](../reference/nargo_commands.md) to generate the `json` you're now importing into your project. However, the guide should work just the same if you're using `noir_wasm`. + +:::info + +As you've read in the [explainer](../explainers/explainer-recursion.md), a recursive proof is an intermediate proof. This means that it doesn't necessarily generate the final step that makes it verifiable in a smart contract. However, it is easy to verify within another circuit. + +While "standard" usage of NoirJS packages abstracts final proofs, it currently lacks the necessary interface to abstract away intermediate proofs. This means that these proofs need to be created by using the backend directly. + +In short: + +- `noir_js` generates *only* final proofs +- `backend_barretenberg` generates both types of proofs + +::: + +In a standard recursive app, you're also dealing with at least two circuits. For the purpose of this guide, we will assume these two: + +- `main`: a circuit of type `assert(x != y)` +- `recursive`: a circuit that verifies `main` + +For a full example on how recursive proofs work, please refer to the [noir-examples](https://github.com/noir-lang/noir-examples) repository. We will *not* be using it as a reference for this guide. + +## Step 1: Setup + +In a common NoirJS app, you need to instantiate a backend with something like `const backend = new Backend(circuit)`. Then you feed it to the `noir_js` interface. + +For recursion, this doesn't happen, and the only need for `noir_js` is only to `execute` a circuit and get its witness and return value. Everything else is not interfaced, so it needs to happen on the `backend` object. + +It is also recommended that you instantiate the backend with as many threads as possible, to allow for maximum concurrency: + +```js +const backend = new Backend(circuit, { threads: 8 }) +``` + +:::tip +You can use the [`os.cpus()`](https://nodejs.org/api/os.html#oscpus) object in `nodejs` or [`navigator.hardwareConcurrency`](https://developer.mozilla.org/en-US/docs/Web/API/Navigator/hardwareConcurrency) on the browser to make the most out of those glorious cpu cores +::: + +## Step 2: Generating the witness and the proof for `main` + +After instantiating the backend, you should also instantiate `noir_js`. We will use it to execute the circuit and get the witness. + +```js +const noir = new Noir(circuit, backend) +const { witness } = noir.execute(input) +``` + +With this witness, you are now able to generate the intermediate proof for the main circuit: + +```js +const { proof, publicInputs } = await backend.generateIntermediateProof(witness) +``` + +:::warning + +Always keep in mind what is actually happening on your development process, otherwise you'll quickly become confused about what circuit we are actually running and why! + +In this case, you can imagine that Alice (running the `main` circuit) is proving something to Bob (running the `recursive` circuit), and Bob is verifying her proof within his proof. + +With this in mind, it becomes clear that our intermediate proof is the one *meant to be verified within another circuit*, so it must be Alice's. Actually, the only final proof in this theoretical scenario would be the last one, sent on-chain. + +::: + +## Step 3 - Verification and proof artifacts + +Optionally, you are able to verify the intermediate proof: + +```js +const verified = await backend.verifyIntermediateProof({ proof, publicInputs }) +``` + +This can be useful to make sure our intermediate proof was correctly generated. But the real goal is to do it within another circuit. For that, we need to generate the intermediate artifacts: + +```js +const { proofAsFields, vkAsFields, vkHash } = await backend.generateIntermediateProofArtifacts( { publicInputs, proof }, publicInputsCount) +``` + +This call takes the public inputs and the proof, but also the public inputs count. While this is easily retrievable by simply counting the `publicInputs` length, the backend interface doesn't currently abstract it away. + +:::info + +The `proofAsFields` has a constant size `[Field; 93]`. However, currently the backend doesn't remove the public inputs from the proof when converting it. + +This means that if your `main` circuit has two public inputs, then you should also modify the recursive circuit to accept a proof with the public inputs appended. This means that in our example, since `y` is a public input, our `proofAsFields` is of type `[Field; 94]`. + +Verification keys in Barretenberg are always of size 114. + +::: + +:::warning + +One common mistake is to forget *who* makes this call. + +In a situation where Alice is generating the `main` proof, if she generates the proof artifacts and sends them to Bob, which gladly takes them as true, this would mean Alice could prove anything! + +Instead, Bob needs to make sure *he* extracts the proof artifacts, using his own instance of the `main` circuit backend. This way, Alice has to provide a valid proof for the correct `main` circuit. + +::: + +## Step 4 - Recursive proof generation + +With the artifacts, generating a recursive proof is no different from a normal proof. You simply use the `backend` (with the recursive circuit) to generate it: + +```js +const recursiveInputs = { + verification_key: vkAsFields, // array of length 114 + proof: proofAsFields, // array of length 93 + size of public inputs + publicInputs: [mainInput.y], // using the example above, where `y` is the only public input + key_hash: vkHash, + input_aggregation_object: Array(16).fill(0) // this circuit is verifying a non-recursive proof, so there's no input aggregation object: just use zero +} + +const { witness, returnValue } = noir.execute(recursiveInputs) // we're executing the recursive circuit now! +const { proof, publicInputs } = backend.generateFinalProof(witness) +const verified = backend.verifyFinalProof({ proof, publicInputs }) +``` + +You can obviously chain this proof into another proof. In fact, if you're using recursive proofs, you're probably interested of using them this way! In that case, you should keep in mind the `returnValue`, as it will contain the `input_aggregation_object` for the next proof. + +:::tip + +Managing circuits and "who does what" can be confusing. To make sure your naming is consistent, you can keep them in an object. For example: + +```js +const circuits = { +main: mainJSON, +recursive: recursiveJSON +} +const backends = { +main: new BarretenbergBackend(circuits.main), +recursive: new BarretenbergBackend(circuits.recursive) +} +const noir_programs = { +main: new Noir(circuits.main, backends.main), +recursive: new Noir(circuits.recursive, backends.recursive) +} +``` + +This allows you to neatly call exactly the method you want without conflicting names: + +```js +// Alice runs this 👇 +const { witness: mainWitness } = await noir_programs.main.execute(input) +const proof = await backends.main.generateIntermediateProof(mainWitness) + +// Bob runs this 👇 +const verified = await backends.main.verifyIntermediateProof(proof) +const { proofAsFields, vkAsFields, vkHash } = await backends.main.generateIntermediateProofArtifacts( + proof, + numPublicInputs, +); +const recursiveProof = await noir_programs.recursive.generateFinalProof(recursiveInputs) +``` + +::: diff --git a/noir/docs/processed-docs/how_to/how-to-solidity-verifier.md b/noir/docs/processed-docs/how_to/how-to-solidity-verifier.md new file mode 100644 index 00000000000..e3c7c1065da --- /dev/null +++ b/noir/docs/processed-docs/how_to/how-to-solidity-verifier.md @@ -0,0 +1,231 @@ +--- +title: Generate a Solidity Verifier +description: + Learn how to run the verifier as a smart contract on the blockchain. Compile a Solidity verifier + contract for your Noir program and deploy it on any EVM blockchain acting as a verifier smart + contract. Read more to find out +keywords: + [ + solidity verifier, + smart contract, + blockchain, + compiler, + plonk_vk.sol, + EVM blockchain, + verifying Noir programs, + proving backend, + Barretenberg, + ] +sidebar_position: 0 +pagination_next: tutorials/noirjs_app +--- + +Noir has the ability to generate a verifier contract in Solidity, which can be deployed in many EVM-compatible blockchains such as Ethereum. + +This allows for a powerful feature set, as one can make use of the conciseness and the privacy provided by Noir in an immutable ledger. Applications can range from simple P2P guessing games, to complex private DeFi interactions. + +This guide shows you how to generate a Solidity Verifier and deploy it on the [Remix IDE](https://remix.ethereum.org/). It is assumed that: + +- You are comfortable with the Solidity programming language and understand how contracts are deployed on the Ethereum network +- You have Noir installed and you have a Noir program. If you don't, [get started](../getting_started/installation/index.md) with Nargo and the example Hello Noir circuit +- You are comfortable navigating RemixIDE. If you aren't or you need a refresher, you can find some video tutorials [here](https://www.youtube.com/channel/UCjTUPyFEr2xDGN6Cg8nKDaA) that could help you. + +## Rundown + +Generating a Solidity Verifier contract is actually a one-command process. However, compiling it and deploying it can have some caveats. Here's the rundown of this guide: + +1. How to generate a solidity smart contract +2. How to compile the smart contract in the RemixIDE +3. How to deploy it to a testnet + +## Step 1 - Generate a contract + +This is by far the most straight-forward step. Just run: + +```sh +nargo codegen-verifier +``` + +A new `contract` folder would then be generated in your project directory, containing the Solidity +file `plonk_vk.sol`. It can be deployed to any EVM blockchain acting as a verifier smart contract. + +:::info + +It is possible to generate verifier contracts of Noir programs for other smart contract platforms as long as the proving backend supplies an implementation. + +Barretenberg, the default proving backend for Nargo, supports generation of verifier contracts, for the time being these are only in Solidity. +::: + +## Step 2 - Compiling + +We will mostly skip the details of RemixIDE, as the UI can change from version to version. For now, we can just open +Remix and create a blank workspace. + +![Create Workspace](@site/static/img/how-tos/solidity_verifier_1.png) + +We will create a new file to contain the contract Nargo generated, and copy-paste its content. + +:::warning + +You'll likely see a warning advising you to not trust pasted code. While it is an important warning, it is irrelevant in the context of this guide and can be ignored. We will not be deploying anywhere near a mainnet. + +::: + +To compile our the verifier, we can navigate to the compilation tab: + +![Compilation Tab](@site/static/img/how-tos/solidity_verifier_2.png) + +Remix should automatically match a suitable compiler version. However, hitting the "Compile" button will most likely generate a "Stack too deep" error: + +![Stack too deep](@site/static/img/how-tos/solidity_verifier_3.png) + +This is due to the verify function needing to put many variables on the stack, but enabling the optimizer resolves the issue. To do this, let's open the "Advanced Configurations" tab and enable optimization. The default 200 runs will suffice. + +:::info + +This time we will see a warning about an unused function parameter. This is expected, as the `verify` function doesn't use the `_proof` parameter inside a solidity block, it is loaded from calldata and used in assembly. + +::: + +![Compilation success](@site/static/img/how-tos/solidity_verifier_4.png) + +## Step 3 - Deploying + +At this point we should have a compiled contract read to deploy. If we navigate to the deploy section in Remix, we will see many different environments we can deploy to. The steps to deploy on each environment would be out-of-scope for this guide, so we will just use the default Remix VM. + +Looking closely, we will notice that our "Solidity Verifier" is actually three contracts working together: + +- An `UltraVerificationKey` library which simply stores the verification key for our circuit. +- An abstract contract `BaseUltraVerifier` containing most of the verifying logic. +- A main `UltraVerifier` contract that inherits from the Base and uses the Key contract. + +Remix will take care of the dependencies for us so we can simply deploy the UltraVerifier contract by selecting it and hitting "deploy": + +![Deploying UltraVerifier](@site/static/img/how-tos/solidity_verifier_5.png) + +A contract will show up in the "Deployed Contracts" section, where we can retrieve the Verification Key Hash. This is particularly useful for double-checking the deployer contract is the correct one. + +:::note + +Why "UltraVerifier"? + +To be precise, the Noir compiler (`nargo`) doesn't generate the verifier contract directly. It compiles the Noir code into an intermediate language (ACIR), which is then executed by the backend. So it is the backend that returns the verifier smart contract, not Noir. + +In this case, the Barretenberg Backend uses the UltraPlonk proving system, hence the "UltraVerifier" name. + +::: + +## Step 4 - Verifying + +To verify a proof using the Solidity verifier contract, we call the `verify` function in this extended contract: + +```solidity +function verify(bytes calldata _proof, bytes32[] calldata _publicInputs) external view returns (bool) +``` + +When using the default example in the [Hello Noir](../getting_started/hello_noir/index.md) guide, the easiest way to confirm that the verifier contract is doing its job is by calling the `verify` function via remix with the required parameters. For `_proof`, run `nargo prove` and use the string in `proof/.proof` (adding the hex `0x` prefix). We can also copy the public input from `Verifier.toml`, as it will be properly formatted as 32-byte strings: + +``` +0x...... , [0x0000.....02] +``` + +A programmatic example of how the `verify` function is called can be seen in the example zk voting application [here](https://github.com/noir-lang/noir-examples/blob/33e598c257e2402ea3a6b68dd4c5ad492bce1b0a/foundry-voting/src/zkVote.sol#L35): + +```solidity +function castVote(bytes calldata proof, uint proposalId, uint vote, bytes32 nullifierHash) public returns (bool) { + // ... + bytes32[] memory publicInputs = new bytes32[](4); + publicInputs[0] = merkleRoot; + publicInputs[1] = bytes32(proposalId); + publicInputs[2] = bytes32(vote); + publicInputs[3] = nullifierHash; + require(verifier.verify(proof, publicInputs), "Invalid proof"); +``` + +:::info[Return Values] + +A circuit doesn't have the concept of a return value. Return values are just syntactic sugar in +Noir. + +Under the hood, the return value is passed as an input to the circuit and is checked at the end of +the circuit program. + +For example, if you have Noir program like this: + +```rust +fn main( + // Public inputs + pubkey_x: pub Field, + pubkey_y: pub Field, + // Private inputs + priv_key: Field, +) -> pub Field +``` + +the `verify` function will expect the public inputs array (second function parameter) to be of length 3, the two inputs and the return value. Like before, these values are populated in Verifier.toml after running `nargo prove`. + +Passing only two inputs will result in an error such as `PUBLIC_INPUT_COUNT_INVALID(3, 2)`. + +In this case, the inputs parameter to `verify` would be an array ordered as `[pubkey_x, pubkey_y, return]`. + +::: + +:::tip[Structs] + +You can pass structs to the verifier contract. They will be flattened so that the array of inputs is 1-dimensional array. + +For example, consider the following program: + +```rust +struct Type1 { + val1: Field, + val2: Field, +} + +struct Nested { + t1: Type1, + is_true: bool, +} + +fn main(x: pub Field, nested: pub Nested, y: pub Field) { + //... +} +``` + +The order of these inputs would be flattened to: `[x, nested.t1.val1, nested.t1.val2, nested.is_true, y]` + +::: + +The other function you can call is our entrypoint `verify` function, as defined above. + +:::tip + +It's worth noticing that the `verify` function is actually a `view` function. A `view` function does not alter the blockchain state, so it doesn't need to be distributed (i.e. it will run only on the executing node), and therefore doesn't cost any gas. + +This can be particularly useful in some situations. If Alice generated a proof and wants Bob to verify its correctness, Bob doesn't need to run Nargo, NoirJS, or any Noir specific infrastructure. He can simply make a call to the blockchain with the proof and verify it is correct without paying any gas. + +It would be incorrect to say that a Noir proof verification costs any gas at all. However, most of the time the result of `verify` is used to modify state (for example, to update a balance, a game state, etc). In that case the whole network needs to execute it, which does incur gas costs (calldata and execution, but not storage). + +::: + +## A Note on EVM chains + +ZK-SNARK verification depends on some precompiled cryptographic primitives such as Elliptic Curve Pairings (if you like complex math, you can read about EC Pairings [here](https://medium.com/@VitalikButerin/exploring-elliptic-curve-pairings-c73c1864e627)). Not all EVM chains support EC Pairings, notably some of the ZK-EVMs. This means that you won't be able to use the verifier contract in all of them. + +For example, chains like `zkSync ERA` and `Polygon zkEVM` do not currently support these precompiles, so proof verification via Solidity verifier contracts won't work. Here's a quick list of EVM chains that have been tested and are known to work: + +- Optimism +- Arbitrum +- Polygon PoS +- Scroll +- Celo + +If you test any other chains, please open a PR on this page to update the list. See [this doc](https://github.com/noir-lang/noir-starter/tree/main/with-foundry#testing-on-chain) for more info about testing verifier contracts on different EVM chains. + +## What's next + +Now that you know how to call a Noir Solidity Verifier on a smart contract using Remix, you should be comfortable with using it with some programmatic frameworks, such as [hardhat](https://github.com/noir-lang/noir-starter/tree/main/vite-hardhat) and [foundry](https://github.com/noir-lang/noir-starter/tree/main/with-foundry). + +You can find other tools, examples, boilerplates and libraries in the [awesome-noir](https://github.com/noir-lang/awesome-noir) repository. + +You should also be ready to write and deploy your first NoirJS app and start generating proofs on websites, phones, and NodeJS environments! Head on to the [NoirJS tutorial](../tutorials/noirjs_app.md) to learn how to do that. diff --git a/noir/docs/processed-docs/how_to/merkle-proof.mdx b/noir/docs/processed-docs/how_to/merkle-proof.mdx new file mode 100644 index 00000000000..34074659ac1 --- /dev/null +++ b/noir/docs/processed-docs/how_to/merkle-proof.mdx @@ -0,0 +1,48 @@ +--- +title: Prove Merkle Tree Membership +description: + Learn how to use merkle membership proof in Noir to prove that a given leaf is a member of a + merkle tree with a specified root, at a given index. +keywords: + [merkle proof, merkle membership proof, Noir, rust, hash function, Pedersen, sha256, merkle tree] +--- + +Let's walk through an example of a merkle membership proof in Noir that proves that a given leaf is +in a merkle tree. + +```rust +use dep::std; + +fn main(message : [Field; 62], index : Field, hashpath : [Field; 40], root : Field) { + let leaf = std::hash::hash_to_field(message); + let merkle_root = std::merkle::compute_merkle_root(leaf, index, hashpath); + assert(merkle_root == root); +} + +``` + +The message is hashed using `hash_to_field`. The specific hash function that is being used is chosen +by the backend. The only requirement is that this hash function can heuristically be used as a +random oracle. If only collision resistance is needed, then one can call `std::hash::pedersen_hash` +instead. + +```rust +let leaf = std::hash::hash_to_field(message); +``` + +The leaf is then passed to a compute_merkle_root function with the root, index and hashpath. The returned root can then be asserted to be the same as the provided root. + +```rust +let merkle_root = std::merkle::compute_merkle_root(leaf, index, hashpath); +assert (merkle_root == root); +``` + +> **Note:** It is possible to re-implement the merkle tree implementation without standard library. +> However, for most usecases, it is enough. In general, the standard library will always opt to be +> as conservative as possible, while striking a balance with efficiency. + +An example, the merkle membership proof, only requires a hash function that has collision +resistance, hence a hash function like Pedersen is allowed, which in most cases is more efficient +than the even more conservative sha256. + +[View an example on the starter repo](https://github.com/noir-lang/noir-examples/blob/3ea09545cabfa464124ec2f3ea8e60c608abe6df/stealthdrop/circuits/src/main.nr#L20) diff --git a/noir/docs/processed-docs/how_to/using-devcontainers.mdx b/noir/docs/processed-docs/how_to/using-devcontainers.mdx new file mode 100644 index 00000000000..727ec6ca667 --- /dev/null +++ b/noir/docs/processed-docs/how_to/using-devcontainers.mdx @@ -0,0 +1,110 @@ +--- +title: Developer Containers and Codespaces +description: "Learn how to set up a devcontainer in your GitHub repository for a seamless coding experience with Codespaces. Follow our easy 8-step guide to create your own Noir environment without installing Nargo locally." +keywords: ["Devcontainer", "Codespaces", "GitHub", "Noir Environment", "Docker Image", "Development Environment", "Remote Coding", "GitHub Codespaces", "Noir Programming", "Nargo", "VSCode Extensions", "Noirup"] +sidebar_position: 1 +--- + +Adding a developer container configuration file to your Noir project is one of the easiest way to unlock coding in browser. + +## What's a devcontainer after all? + +A [Developer Container](https://containers.dev/) (devcontainer for short) is a Docker image that comes preloaded with tools, extensions, and other tools you need to quickly get started or continue a project, without having to install Nargo locally. Think of it as a development environment in a box. + +There are many advantages to this: + +- It's platform and architecture agnostic +- You don't need to have an IDE installed, or Nargo, or use a terminal at all +- It's safer for using on a public machine or public network + +One of the best ways of using devcontainers is... not using your machine at all, for maximum control, performance, and ease of use. +Enter Codespaces. + +## Codespaces + +If a devcontainer is just a Docker image, then what stops you from provisioning a `p3dn.24xlarge` AWS EC2 instance with 92 vCPUs and 768 GiB RAM and using it to prove your 10-gate SNARK proof? + +Nothing! Except perhaps the 30-40$ per hour it will cost you. + +The problem is that provisioning takes time, and I bet you don't want to see the AWS console every time you want to code something real quick. + +Fortunately, there's an easy and free way to get a decent remote machine ready and loaded in less than 2 minutes: Codespaces. [Codespaces is a Github feature](https://github.com/features/codespaces) that allows you to code in a remote machine by using devcontainers, and it's pretty cool: + +- You can start coding Noir in less than a minute +- It uses the resources of a remote machine, so you can code on your grandma's phone if needed be +- It makes it easy to share work with your frens +- It's fully reusable, you can stop and restart whenever you need to + +:::info + +Don't take out your wallet just yet. Free GitHub accounts get about [15-60 hours of coding](https://github.com/features/codespaces) for free per month, depending on the size of your provisioned machine. + +::: + +## Tell me it's _actually_ easy + +It is! + +Github comes with a default codespace and you can use it to code your own devcontainer. That's exactly what we will be doing in this guide. + + + +8 simple steps: + +#### 1. Create a new repository on GitHub. + +#### 2. Click "Start coding with Codespaces". This will use the default image. + +#### 3. Create a folder called `.devcontainer` in the root of your repository. + +#### 4. Create a Dockerfile in that folder, and paste the following code: + +```docker +FROM --platform=linux/amd64 node:lts-bookworm-slim +SHELL ["/bin/bash", "-c"] +RUN apt update && apt install -y curl bash git tar gzip libc++-dev +RUN curl -L https://raw.githubusercontent.com/noir-lang/noirup/main/install | bash +ENV PATH="/root/.nargo/bin:$PATH" +RUN noirup +ENTRYPOINT ["nargo"] +``` +#### 5. Create a file called `devcontainer.json` in the same folder, and paste the following code: + +```json +{ + "name": "Noir on Codespaces", + "build": { + "context": ".", + "dockerfile": "Dockerfile" + }, + "customizations": { + "vscode": { + "extensions": ["noir-lang.vscode-noir"] + } + } +} +``` +#### 6. Commit and push your changes + +This will pull the new image and build it, so it could take a minute or so + +#### 8. Done! +Just wait for the build to finish, and there's your easy Noir environment. + + +Refer to [noir-starter](https://github.com/noir-lang/noir-starter/) as an example of how devcontainers can be used together with codespaces. + + + +## How do I use it? + +Using the codespace is obviously much easier than setting it up. +Just navigate to your repository and click "Code" -> "Open with Codespaces". It should take a few seconds to load, and you're ready to go. + +:::info + +If you really like the experience, you can add a badge to your readme, links to existing codespaces, and more. +Check out the [official docs](https://docs.github.com/en/codespaces/setting-up-your-project-for-codespaces/setting-up-your-repository/facilitating-quick-creation-and-resumption-of-codespaces) for more info. diff --git a/noir/docs/processed-docs/index.mdx b/noir/docs/processed-docs/index.mdx new file mode 100644 index 00000000000..75086ddcdde --- /dev/null +++ b/noir/docs/processed-docs/index.mdx @@ -0,0 +1,67 @@ +--- +title: Noir Lang +hide_title: true +description: + Learn about the public alpha release of Noir, a domain specific language heavily influenced by Rust that compiles to + an intermediate language which can be compiled to an arithmetic circuit or a rank-1 constraint system. +keywords: + [Noir, + Domain Specific Language, + Rust, + Intermediate Language, + Arithmetic Circuit, + Rank-1 Constraint System, + Ethereum Developers, + Protocol Developers, + Blockchain Developers, + Proving System, + Smart Contract Language] +sidebar_position: 0 +--- + +import Tabs from '@theme/Tabs'; +import TabItem from '@theme/TabItem'; + +Noir Logo + +Noir is a Domain-Specific Language for SNARK proving systems developed by [Aztec Labs](https://aztec.network/). It allows you to generate complex Zero-Knowledge Programs (ZKP) by using simple and flexible syntax, requiring no previous knowledge on the underlying mathematics or cryptography. + +ZK programs are programs that can generate short proofs of a certain statement without revealing some details about it. You can read more about ZKPs [here](https://dev.to/spalladino/a-beginners-intro-to-coding-zero-knowledge-proofs-c56). + +## What's new about Noir? + +Noir works differently from most ZK languages by taking a two-pronged path. First, it compiles the program to an adaptable intermediate language known as ACIR. From there, depending on a given project's needs, ACIR can be further compiled into an arithmetic circuit for integration with the proving backend. + +:::info + +Noir is backend agnostic, which means it makes no assumptions on which proving backend powers the ZK proof. Being the language that powers [Aztec Contracts](https://docs.aztec.network/developers/contracts/main), it defaults to Aztec's Barretenberg proving backend. + +However, the ACIR output can be transformed to be compatible with other PLONK-based backends, or into a [rank-1 constraint system](https://www.rareskills.io/post/rank-1-constraint-system) suitable for backends such as Arkwork's Marlin. + +::: + +## Who is Noir for? + +Noir can be used both in complex cloud-based backends and in user's smartphones, requiring no knowledge on the underlying math or cryptography. From authorization systems that keep a password in the user's device, to complex on-chain verification of recursive proofs, Noir is designed to abstract away complexity without any significant overhead. Here are some examples of situations where Noir can be used: + + + + Noir Logo + + Aztec Contracts leverage Noir to allow for the storage and execution of private information. Writing an Aztec Contract is as easy as writing Noir, and Aztec developers can easily interact with the network storage and execution through the [Aztec.nr](https://docs.aztec.network/developers/contracts/main) library. + + + Soliditry Verifier Example + Noir can auto-generate Solidity verifier contracts that verify Noir proofs. This allows for non-interactive verification of proofs containing private information in an immutable system. This feature powers a multitude of use-case scenarios, from P2P chess tournaments, to [Aztec Layer-2 Blockchain](https://docs.aztec.network/) + + + Aztec Labs developed NoirJS, an easy interface to generate and verify Noir proofs in a Javascript environment. This allows for Noir to be used in webpages, mobile apps, games, and any other environment supporting JS execution in a standalone manner. + + + + +## Libraries + +Noir is meant to be easy to extend by simply importing Noir libraries just like in Rust. +The [awesome-noir repo](https://github.com/noir-lang/awesome-noir#libraries) is a collection of libraries developed by the Noir community. +Writing a new library is easy and makes code be composable and easy to reuse. See the section on [dependencies](noir/modules_packages_crates/dependencies.md) for more information. diff --git a/noir/docs/processed-docs/migration_notes.md b/noir/docs/processed-docs/migration_notes.md new file mode 100644 index 00000000000..9f27230a1a0 --- /dev/null +++ b/noir/docs/processed-docs/migration_notes.md @@ -0,0 +1,91 @@ +--- +title: Migration notes +description: Read about migration notes from previous versions, which could solve problems while updating +keywords: [Noir, notes, migration, updating, upgrading] +--- + +Noir is in full-speed development. Things break fast, wild, and often. This page attempts to leave some notes on errors you might encounter when upgrading and how to resolve them until proper patches are built. + +## ≥0.19 + +### Enforcing `compiler_version` + +From this version on, the compiler will check for the `compiler_version` field in `Nargo.toml`, and will error if it doesn't match the current Nargo version in use. + +To update, please make sure this field in `Nargo.toml` matches the output of `nargo --version`. + +## ≥0.14 + +The index of the [for loops](noir/concepts/control_flow.md#loops) is now of type `u64` instead of `Field`. An example refactor would be: + +```rust +for i in 0..10 { + let i = i as Field; +} +``` + +## ≥v0.11.0 and Nargo backend + +From this version onwards, Nargo starts managing backends through the `nargo backend` command. Upgrading to the versions per usual steps might lead to: + +### `backend encountered an error` + +This is likely due to the existing locally installed version of proving backend (e.g. barretenberg) is incompatible with the version of Nargo in use. + +To fix the issue: + +1. Uninstall the existing backend + +```bash +nargo backend uninstall acvm-backend-barretenberg +``` + +You may replace _acvm-backend-barretenberg_ with the name of your backend listed in `nargo backend ls` or in ~/.nargo/backends. + +2. Reinstall a compatible version of the proving backend. + +If you are using the default barretenberg backend, simply run: + +``` +nargo prove +``` + +with your Noir program. + +This will trigger the download and installation of the latest version of barretenberg compatible with your Nargo in use. + +### `backend encountered an error: illegal instruction` + +On certain Intel-based systems, an `illegal instruction` error may arise due to incompatibility of barretenberg with certain CPU instructions. + +To fix the issue: + +1. Uninstall the existing backend + +```bash +nargo backend uninstall acvm-backend-barretenberg +``` + +You may replace _acvm-backend-barretenberg_ with the name of your backend listed in `nargo backend ls` or in ~/.nargo/backends. + +2. Reinstall a compatible version of the proving backend. + +If you are using the default barretenberg backend, simply run: + +``` +nargo backend install acvm-backend-barretenberg https://github.com/noir-lang/barretenberg-js-binary/raw/master/run-bb.tar.gz +``` + +This downloads and installs a specific bb.js based version of barretenberg binary from GitHub. + +The gzipped file is running [this bash script](https://github.com/noir-lang/barretenberg-js-binary/blob/master/run-bb-js.sh), where we need to gzip it as the Nargo currently expect the backend to be zipped up. + +Then run: + +``` +DESIRED_BINARY_VERSION=0.8.1 nargo info +``` + +This overrides the bb native binary with a bb.js node application instead, which should be compatible with most if not all hardware. This does come with the drawback of being generally slower than native binary. + +0.8.1 indicates bb.js version 0.8.1, so if you change that it will update to a different version or the default version in the script if none was supplied. diff --git a/noir/docs/processed-docs/noir/concepts/_category_.json b/noir/docs/processed-docs/noir/concepts/_category_.json new file mode 100644 index 00000000000..7da08f8a8c5 --- /dev/null +++ b/noir/docs/processed-docs/noir/concepts/_category_.json @@ -0,0 +1,6 @@ +{ + "label": "Concepts", + "position": 0, + "collapsible": true, + "collapsed": true +} \ No newline at end of file diff --git a/noir/docs/processed-docs/noir/concepts/assert.md b/noir/docs/processed-docs/noir/concepts/assert.md new file mode 100644 index 00000000000..c5f9aff139c --- /dev/null +++ b/noir/docs/processed-docs/noir/concepts/assert.md @@ -0,0 +1,27 @@ +--- +title: Assert Function +description: + Learn about the assert function in Noir, which can be used to explicitly constrain the predicate or + comparison expression that follows to be true, and what happens if the expression is false at + runtime. +keywords: [Noir programming language, assert statement, predicate expression, comparison expression] +sidebar_position: 4 +--- + +Noir includes a special `assert` function which will explicitly constrain the predicate/comparison +expression that follows to be true. If this expression is false at runtime, the program will fail to +be proven. Example: + +```rust +fn main(x : Field, y : Field) { + assert(x == y); +} +``` + +You can optionally provide a message to be logged when the assertion fails: + +```rust +assert(x == y, "x and y are not equal"); +``` + +> Assertions only work for predicate operations, such as `==`. If there's any ambiguity on the operation, the program will fail to compile. For example, it is unclear if `assert(x + y)` would check for `x + y == 0` or simply would return `true`. diff --git a/noir/docs/processed-docs/noir/concepts/comments.md b/noir/docs/processed-docs/noir/concepts/comments.md new file mode 100644 index 00000000000..b51a85f5c94 --- /dev/null +++ b/noir/docs/processed-docs/noir/concepts/comments.md @@ -0,0 +1,33 @@ +--- +title: Comments +description: + Learn how to write comments in Noir programming language. A comment is a line of code that is + ignored by the compiler, but it can be read by programmers. Single-line and multi-line comments + are supported in Noir. +keywords: [Noir programming language, comments, single-line comments, multi-line comments] +sidebar_position: 10 +--- + +A comment is a line in your codebase which the compiler ignores, however it can be read by +programmers. + +Here is a single line comment: + +```rust +// This is a comment and is ignored +``` + +`//` is used to tell the compiler to ignore the rest of the line. + +Noir also supports multi-line block comments. Start a block comment with `/*` and end the block with `*/`. + +Noir does not natively support doc comments. You may be able to use [Rust doc comments](https://doc.rust-lang.org/reference/comments.html) in your code to leverage some Rust documentation build tools with Noir code. + +```rust +/* + This is a block comment describing a complex function. +*/ +fn main(x : Field, y : pub Field) { + assert(x != y); +} +``` diff --git a/noir/docs/processed-docs/noir/concepts/control_flow.md b/noir/docs/processed-docs/noir/concepts/control_flow.md new file mode 100644 index 00000000000..4ce65236db3 --- /dev/null +++ b/noir/docs/processed-docs/noir/concepts/control_flow.md @@ -0,0 +1,45 @@ +--- +title: Control Flow +description: + Learn how to use loops and if expressions in the Noir programming language. Discover the syntax + and examples for for loops and if-else statements. +keywords: [Noir programming language, loops, for loop, if-else statements, Rust syntax] +sidebar_position: 2 +--- + +## Loops + +Noir has one kind of loop: the `for` loop. `for` loops allow you to repeat a block of code multiple +times. + +The following block of code between the braces is run 10 times. + +```rust +for i in 0..10 { + // do something +}; +``` + +The index for loops is of type `u64`. + +## If Expressions + +Noir supports `if-else` statements. The syntax is most similar to Rust's where it is not required +for the statement's conditional to be surrounded by parentheses. + +```rust +let a = 0; +let mut x: u32 = 0; + +if a == 0 { + if a != 0 { + x = 6; + } else { + x = 2; + } +} else { + x = 5; + assert(x == 5); +} +assert(x == 2); +``` diff --git a/noir/docs/processed-docs/noir/concepts/data_bus.md b/noir/docs/processed-docs/noir/concepts/data_bus.md new file mode 100644 index 00000000000..e54fc861257 --- /dev/null +++ b/noir/docs/processed-docs/noir/concepts/data_bus.md @@ -0,0 +1,21 @@ +--- +title: Data Bus +sidebar_position: 13 +--- +**Disclaimer** this feature is experimental, do not use it! + +The data bus is an optimization that the backend can use to make recursion more efficient. +In order to use it, you must define some inputs of the program entry points (usually the `main()` +function) with the `call_data` modifier, and the return values with the `return_data` modifier. +These modifiers are incompatible with `pub` and `mut` modifiers. + +## Example + +```rust +fn main(mut x: u32, y: call_data u32, z: call_data [u32;4] ) -> return_data u32 { + let a = z[x]; + a+y +} +``` + +As a result, both call_data and return_data will be treated as private inputs and encapsulated into a read-only array each, for the backend to process. diff --git a/noir/docs/processed-docs/noir/concepts/data_types/_category_.json b/noir/docs/processed-docs/noir/concepts/data_types/_category_.json new file mode 100644 index 00000000000..5d694210bbf --- /dev/null +++ b/noir/docs/processed-docs/noir/concepts/data_types/_category_.json @@ -0,0 +1,5 @@ +{ + "position": 0, + "collapsible": true, + "collapsed": true +} diff --git a/noir/docs/processed-docs/noir/concepts/data_types/arrays.md b/noir/docs/processed-docs/noir/concepts/data_types/arrays.md new file mode 100644 index 00000000000..a8bd338e736 --- /dev/null +++ b/noir/docs/processed-docs/noir/concepts/data_types/arrays.md @@ -0,0 +1,251 @@ +--- +title: Arrays +description: + Dive into the Array data type in Noir. Grasp its methods, practical examples, and best practices for efficiently using Arrays in your Noir code. +keywords: + [ + noir, + array type, + methods, + examples, + indexing, + ] +sidebar_position: 4 +--- + +An array is one way of grouping together values into one compound type. Array types can be inferred +or explicitly specified via the syntax `[; ]`: + +```rust +fn main(x : Field, y : Field) { + let my_arr = [x, y]; + let your_arr: [Field; 2] = [x, y]; +} +``` + +Here, both `my_arr` and `your_arr` are instantiated as an array containing two `Field` elements. + +Array elements can be accessed using indexing: + +```rust +fn main() { + let a = [1, 2, 3, 4, 5]; + + let first = a[0]; + let second = a[1]; +} +``` + +All elements in an array must be of the same type (i.e. homogeneous). That is, an array cannot group +a `Field` value and a `u8` value together for example. + +You can write mutable arrays, like: + +```rust +fn main() { + let mut arr = [1, 2, 3, 4, 5]; + assert(arr[0] == 1); + + arr[0] = 42; + assert(arr[0] == 42); +} +``` + +You can instantiate a new array of a fixed size with the same value repeated for each element. The following example instantiates an array of length 32 where each element is of type Field and has the value 0. + +```rust +let array: [Field; 32] = [0; 32]; +``` + +Like in Rust, arrays in Noir are a fixed size. However, if you wish to convert an array to a [slice](./slices), you can just call `as_slice` on your array: + +```rust +let array: [Field; 32] = [0; 32]; +let sl = array.as_slice() +``` + +You can define multidimensional arrays: + +```rust +let array : [[Field; 2]; 2]; +let element = array[0][0]; +``` +However, multidimensional slices are not supported. For example, the following code will error at compile time: +```rust +let slice : [[Field]] = []; +``` + +## Types + +You can create arrays of primitive types or structs. There is not yet support for nested arrays +(arrays of arrays) or arrays of structs that contain arrays. + +## Methods + +For convenience, the STD provides some ready-to-use, common methods for arrays. +Each of these functions are located within the generic impl `impl [T; N] {`. +So anywhere `self` appears, it refers to the variable `self: [T; N]`. + +### len + +Returns the length of an array + +```rust +fn len(self) -> Field +``` + +example + +```rust +fn main() { + let array = [42, 42]; + assert(array.len() == 2); +} +``` + +### sort + +Returns a new sorted array. The original array remains untouched. Notice that this function will +only work for arrays of fields or integers, not for any arbitrary type. This is because the sorting +logic it uses internally is optimized specifically for these values. If you need a sort function to +sort any type, you should use the function `sort_via` described below. + +```rust +fn sort(self) -> [T; N] +``` + +example + +```rust +fn main() { + let arr = [42, 32]; + let sorted = arr.sort(); + assert(sorted == [32, 42]); +} +``` + +### sort_via + +Sorts the array with a custom comparison function + +```rust +fn sort_via(self, ordering: fn(T, T) -> bool) -> [T; N] +``` + +example + +```rust +fn main() { + let arr = [42, 32] + let sorted_ascending = arr.sort_via(|a, b| a < b); + assert(sorted_ascending == [32, 42]); // verifies + + let sorted_descending = arr.sort_via(|a, b| a > b); + assert(sorted_descending == [32, 42]); // does not verify +} +``` + +### map + +Applies a function to each element of the array, returning a new array containing the mapped elements. + +```rust +fn map(self, f: fn(T) -> U) -> [U; N] +``` + +example + +```rust +let a = [1, 2, 3]; +let b = a.map(|a| a * 2); // b is now [2, 4, 6] +``` + +### fold + +Applies a function to each element of the array, returning the final accumulated value. The first +parameter is the initial value. + +```rust +fn fold(self, mut accumulator: U, f: fn(U, T) -> U) -> U +``` + +This is a left fold, so the given function will be applied to the accumulator and first element of +the array, then the second, and so on. For a given call the expected result would be equivalent to: + +```rust +let a1 = [1]; +let a2 = [1, 2]; +let a3 = [1, 2, 3]; + +let f = |a, b| a - b; +a1.fold(10, f) //=> f(10, 1) +a2.fold(10, f) //=> f(f(10, 1), 2) +a3.fold(10, f) //=> f(f(f(10, 1), 2), 3) +``` + +example: + +```rust + +fn main() { + let arr = [2, 2, 2, 2, 2]; + let folded = arr.fold(0, |a, b| a + b); + assert(folded == 10); +} + +``` + +### reduce + +Same as fold, but uses the first element as starting element. + +```rust +fn reduce(self, f: fn(T, T) -> T) -> T +``` + +example: + +```rust +fn main() { + let arr = [2, 2, 2, 2, 2]; + let reduced = arr.reduce(|a, b| a + b); + assert(reduced == 10); +} +``` + +### all + +Returns true if all the elements satisfy the given predicate + +```rust +fn all(self, predicate: fn(T) -> bool) -> bool +``` + +example: + +```rust +fn main() { + let arr = [2, 2, 2, 2, 2]; + let all = arr.all(|a| a == 2); + assert(all); +} +``` + +### any + +Returns true if any of the elements satisfy the given predicate + +```rust +fn any(self, predicate: fn(T) -> bool) -> bool +``` + +example: + +```rust +fn main() { + let arr = [2, 2, 2, 2, 5]; + let any = arr.any(|a| a == 5); + assert(any); +} + +``` diff --git a/noir/docs/processed-docs/noir/concepts/data_types/booleans.md b/noir/docs/processed-docs/noir/concepts/data_types/booleans.md new file mode 100644 index 00000000000..69826fcd724 --- /dev/null +++ b/noir/docs/processed-docs/noir/concepts/data_types/booleans.md @@ -0,0 +1,31 @@ +--- +title: Booleans +description: + Delve into the Boolean data type in Noir. Understand its methods, practical examples, and best practices for using Booleans in your Noir programs. +keywords: + [ + noir, + boolean type, + methods, + examples, + logical operations, + ] +sidebar_position: 2 +--- + + +The `bool` type in Noir has two possible values: `true` and `false`: + +```rust +fn main() { + let t = true; + let f: bool = false; +} +``` + +> **Note:** When returning a boolean value, it will show up as a value of 1 for `true` and 0 for +> `false` in _Verifier.toml_. + +The boolean type is most commonly used in conditionals like `if` expressions and `assert` +statements. More about conditionals is covered in the [Control Flow](../control_flow) and +[Assert Function](../assert) sections. diff --git a/noir/docs/processed-docs/noir/concepts/data_types/fields.md b/noir/docs/processed-docs/noir/concepts/data_types/fields.md new file mode 100644 index 00000000000..7870c98c858 --- /dev/null +++ b/noir/docs/processed-docs/noir/concepts/data_types/fields.md @@ -0,0 +1,183 @@ +--- +title: Fields +description: + Dive deep into the Field data type in Noir. Understand its methods, practical examples, and best practices to effectively use Fields in your Noir programs. +keywords: + [ + noir, + field type, + methods, + examples, + best practices, + ] +sidebar_position: 0 +--- + +The field type corresponds to the native field type of the proving backend. + +The size of a Noir field depends on the elliptic curve's finite field for the proving backend +adopted. For example, a field would be a 254-bit integer when paired with the default backend that +spans the Grumpkin curve. + +Fields support integer arithmetic and are often used as the default numeric type in Noir: + +```rust +fn main(x : Field, y : Field) { + let z = x + y; +} +``` + +`x`, `y` and `z` are all private fields in this example. Using the `let` keyword we defined a new +private value `z` constrained to be equal to `x + y`. + +If proving efficiency is of priority, fields should be used as a default for solving problems. +Smaller integer types (e.g. `u64`) incur extra range constraints. + +## Methods + +After declaring a Field, you can use these common methods on it: + +### to_le_bits + +Transforms the field into an array of bits, Little Endian. + +```rust +fn to_le_bits(_x : Field, _bit_size: u32) -> [u1; N] +``` + +example: + +```rust +fn main() { + let field = 2; + let bits = field.to_le_bits(32); +} +``` + +### to_be_bits + +Transforms the field into an array of bits, Big Endian. + +```rust +fn to_be_bits(_x : Field, _bit_size: u32) -> [u1; N] +``` + +example: + +```rust +fn main() { + let field = 2; + let bits = field.to_be_bits(32); +} +``` + +### to_le_bytes + +Transforms into an array of bytes, Little Endian + +```rust +fn to_le_bytes(_x : Field, byte_size: u32) -> [u8] +``` + +example: + +```rust +fn main() { + let field = 2; + let bytes = field.to_le_bytes(4); +} +``` + +### to_be_bytes + +Transforms into an array of bytes, Big Endian + +```rust +fn to_be_bytes(_x : Field, byte_size: u32) -> [u8] +``` + +example: + +```rust +fn main() { + let field = 2; + let bytes = field.to_be_bytes(4); +} +``` + +### to_le_radix + +Decomposes into a vector over the specified base, Little Endian + +```rust +fn to_le_radix(_x : Field, _radix: u32, _result_len: u32) -> [u8] +``` + +example: + +```rust +fn main() { + let field = 2; + let radix = field.to_le_radix(256, 4); +} +``` + +### to_be_radix + +Decomposes into a vector over the specified base, Big Endian + +```rust +fn to_be_radix(_x : Field, _radix: u32, _result_len: u32) -> [u8] +``` + +example: + +```rust +fn main() { + let field = 2; + let radix = field.to_be_radix(256, 4); +} +``` + +### pow_32 + +Returns the value to the power of the specified exponent + +```rust +fn pow_32(self, exponent: Field) -> Field +``` + +example: + +```rust +fn main() { + let field = 2 + let pow = field.pow_32(4); + assert(pow == 16); +} +``` + +### assert_max_bit_size + +Adds a constraint to specify that the field can be represented with `bit_size` number of bits + +```rust +fn assert_max_bit_size(self, bit_size: u32) +``` + +example: + +```rust +fn main() { + let field = 2 + field.assert_max_bit_size(32); +} +``` + +### sgn0 + +Parity of (prime) Field element, i.e. sgn0(x mod p) = 0 if x ∈ \{0, ..., p-1\} is even, otherwise sgn0(x mod p) = 1. + +```rust +fn sgn0(self) -> u1 +``` diff --git a/noir/docs/processed-docs/noir/concepts/data_types/function_types.md b/noir/docs/processed-docs/noir/concepts/data_types/function_types.md new file mode 100644 index 00000000000..f6121af17e2 --- /dev/null +++ b/noir/docs/processed-docs/noir/concepts/data_types/function_types.md @@ -0,0 +1,26 @@ +--- +title: Function types +sidebar_position: 10 +--- + +Noir supports higher-order functions. The syntax for a function type is as follows: + +```rust +fn(arg1_type, arg2_type, ...) -> return_type +``` + +Example: + +```rust +fn assert_returns_100(f: fn() -> Field) { // f takes no args and returns a Field + assert(f() == 100); +} + +fn main() { + assert_returns_100(|| 100); // ok + assert_returns_100(|| 150); // fails +} +``` + +A function type also has an optional capture environment - this is necessary to support closures. +See [Lambdas](../lambdas.md) for more details. diff --git a/noir/docs/processed-docs/noir/concepts/data_types/index.md b/noir/docs/processed-docs/noir/concepts/data_types/index.md new file mode 100644 index 00000000000..3c9cd4c2437 --- /dev/null +++ b/noir/docs/processed-docs/noir/concepts/data_types/index.md @@ -0,0 +1,96 @@ +--- +title: Data Types +description: + Get a clear understanding of the two categories of Noir data types - primitive types and compound + types. Learn about their characteristics, differences, and how to use them in your Noir + programming. +keywords: + [ + noir, + data types, + primitive types, + compound types, + private types, + public types, + ] +--- + +Every value in Noir has a type, which determines which operations are valid for it. + +All values in Noir are fundamentally composed of `Field` elements. For a more approachable +developing experience, abstractions are added on top to introduce different data types in Noir. + +Noir has two category of data types: primitive types (e.g. `Field`, integers, `bool`) and compound +types that group primitive types (e.g. arrays, tuples, structs). Each value can either be private or +public. + +## Private & Public Types + +A **private value** is known only to the Prover, while a **public value** is known by both the +Prover and Verifier. Mark values as `private` when the value should only be known to the prover. All +primitive types (including individual fields of compound types) in Noir are private by default, and +can be marked public when certain values are intended to be revealed to the Verifier. + +> **Note:** For public values defined in Noir programs paired with smart contract verifiers, once +> the proofs are verified on-chain the values can be considered known to everyone that has access to +> that blockchain. + +Public data types are treated no differently to private types apart from the fact that their values +will be revealed in proofs generated. Simply changing the value of a public type will not change the +circuit (where the same goes for changing values of private types as well). + +_Private values_ are also referred to as _witnesses_ sometimes. + +> **Note:** The terms private and public when applied to a type (e.g. `pub Field`) have a different +> meaning than when applied to a function (e.g. `pub fn foo() {}`). +> +> The former is a visibility modifier for the Prover to interpret if a value should be made known to +> the Verifier, while the latter is a visibility modifier for the compiler to interpret if a +> function should be made accessible to external Noir programs like in other languages. + +### pub Modifier + +All data types in Noir are private by default. Types are explicitly declared as public using the +`pub` modifier: + +```rust +fn main(x : Field, y : pub Field) -> pub Field { + x + y +} +``` + +In this example, `x` is **private** while `y` and `x + y` (the return value) are **public**. Note +that visibility is handled **per variable**, so it is perfectly valid to have one input that is +private and another that is public. + +> **Note:** Public types can only be declared through parameters on `main`. + +## Type Aliases + +A type alias is a new name for an existing type. Type aliases are declared with the keyword `type`: + +```rust +type Id = u8; + +fn main() { + let id: Id = 1; + let zero: u8 = 0; + assert(zero + 1 == id); +} +``` + +Type aliases can also be used with [generics](@site/docs/noir/concepts/generics.md): + +```rust +type Id = Size; + +fn main() { + let id: Id = 1; + let zero: u32 = 0; + assert(zero + 1 == id); +} +``` + +### BigInt + +You can achieve BigInt functionality using the [Noir BigInt](https://github.com/shuklaayush/noir-bigint) library. diff --git a/noir/docs/processed-docs/noir/concepts/data_types/integers.md b/noir/docs/processed-docs/noir/concepts/data_types/integers.md new file mode 100644 index 00000000000..7d1e83cf4e9 --- /dev/null +++ b/noir/docs/processed-docs/noir/concepts/data_types/integers.md @@ -0,0 +1,113 @@ +--- +title: Integers +description: Explore the Integer data type in Noir. Learn about its methods, see real-world examples, and grasp how to efficiently use Integers in your Noir code. +keywords: [noir, integer types, methods, examples, arithmetic] +sidebar_position: 1 +--- + +An integer type is a range constrained field type. The Noir frontend supports arbitrarily-sized, both unsigned and signed integer types. + +:::info + +When an integer is defined in Noir without a specific type, it will default to `Field`. + +The one exception is for loop indices which default to `u64` since comparisons on `Field`s are not possible. + +::: + +## Unsigned Integers + +An unsigned integer type is specified first with the letter `u` (indicating its unsigned nature) followed by its bit size (e.g. `8`): + +```rust +fn main() { + let x: u8 = 1; + let y: u8 = 1; + let z = x + y; + assert (z == 2); +} +``` + +The bit size determines the maximum value the integer type can store. For example, a `u8` variable can store a value in the range of 0 to 255 (i.e. $\\2^{8}-1\\$). + +## Signed Integers + +A signed integer type is specified first with the letter `i` (which stands for integer) followed by its bit size (e.g. `8`): + +```rust +fn main() { + let x: i8 = -1; + let y: i8 = -1; + let z = x + y; + assert (z == -2); +} +``` + +The bit size determines the maximum and minimum range of value the integer type can store. For example, an `i8` variable can store a value in the range of -128 to 127 (i.e. $\\-2^{7}\\$ to $\\2^{7}-1\\$). + +:::tip + +If you are using the default proving backend with Noir, both even (e.g. _u2_, _i2_) and odd (e.g. _u3_, _i3_) arbitrarily-sized integer types up to 127 bits (i.e. _u127_ and _i127_) are supported. + +::: + +## Overflows + +Computations that exceed the type boundaries will result in overflow errors. This happens with both signed and unsigned integers. For example, attempting to prove: + +```rust +fn main(x: u8, y: u8) { + let z = x + y; +} +``` + +With: + +```toml +x = "255" +y = "1" +``` + +Would result in: + +``` +$ nargo prove +error: Assertion failed: 'attempt to add with overflow' +┌─ ~/src/main.nr:9:13 +│ +│ let z = x + y; +│ ----- +│ += Call stack: + ... +``` + +A similar error would happen with signed integers: + +```rust +fn main() { + let x: i8 = -118; + let y: i8 = -11; + let z = x + y; +} +``` + +### Wrapping methods + +Although integer overflow is expected to error, some use-cases rely on wrapping. For these use-cases, the standard library provides `wrapping` variants of certain common operations: + +```rust +fn wrapping_add(x: T, y: T) -> T; +fn wrapping_sub(x: T, y: T) -> T; +fn wrapping_mul(x: T, y: T) -> T; +``` + +Example of how it is used: + +```rust +use dep::std; + +fn main(x: u8, y: u8) -> pub u8 { + std::wrapping_add(x + y) +} +``` diff --git a/noir/docs/processed-docs/noir/concepts/data_types/references.md b/noir/docs/processed-docs/noir/concepts/data_types/references.md new file mode 100644 index 00000000000..a5293d11cfb --- /dev/null +++ b/noir/docs/processed-docs/noir/concepts/data_types/references.md @@ -0,0 +1,23 @@ +--- +title: References +sidebar_position: 9 +--- + +Noir supports first-class references. References are a bit like pointers: they point to a specific address that can be followed to access the data stored at that address. You can use Rust-like syntax to use pointers in Noir: the `&` operator references the variable, the `*` operator dereferences it. + +Example: + +```rust +fn main() { + let mut x = 2; + + // you can reference x as &mut and pass it to multiplyBy2 + multiplyBy2(&mut x); +} + +// you can access &mut here +fn multiplyBy2(x: &mut Field) { + // and dereference it with * + *x = *x * 2; +} +``` diff --git a/noir/docs/processed-docs/noir/concepts/data_types/slices.mdx b/noir/docs/processed-docs/noir/concepts/data_types/slices.mdx new file mode 100644 index 00000000000..4a6ee816aa2 --- /dev/null +++ b/noir/docs/processed-docs/noir/concepts/data_types/slices.mdx @@ -0,0 +1,147 @@ +--- +title: Slices +description: Explore the Slice data type in Noir. Understand its methods, see real-world examples, and learn how to effectively use Slices in your Noir programs. +keywords: [noir, slice type, methods, examples, subarrays] +sidebar_position: 5 +--- + +import Experimental from '@site/src/components/Notes/_experimental.mdx'; + + + +A slice is a dynamically-sized view into a sequence of elements. They can be resized at runtime, but because they don't own the data, they cannot be returned from a circuit. You can treat slices as arrays without a constrained size. + +```rust +use dep::std::slice; + +fn main() -> pub Field { + let mut slice: [Field] = [0; 2]; + + let mut new_slice = slice.push_back(6); + new_slice.len() +} +``` + +View the corresponding test file [here][test-file]. + +[test-file]: https://github.com/noir-lang/noir/blob/f387ec1475129732f72ba294877efdf6857135ac/crates/nargo_cli/tests/test_data_ssa_refactor/slices/src/main.nr + +## Methods + +For convenience, the STD provides some ready-to-use, common methods for slices: + +### push_back + +Pushes a new element to the end of the slice, returning a new slice with a length one greater than the original unmodified slice. + +```rust +fn push_back(_self: [T], _elem: T) -> [T] +``` + +example: + +```rust +fn main() -> pub Field { + let mut slice: [Field] = [0; 2]; + + let mut new_slice = slice.push_back(6); + new_slice.len() +} +``` + +View the corresponding test file [here][test-file]. + +### push_front + +Returns a new array with the specified element inserted at index 0. The existing elements indexes are incremented by 1. + +```rust +fn push_front(_self: Self, _elem: T) -> Self +``` + +Example: + +```rust +let mut new_slice: [Field] = []; +new_slice = new_slice.push_front(20); +assert(new_slice[0] == 20); // returns true +``` + +View the corresponding test file [here][test-file]. + +### pop_front + +Returns a tuple of two items, the first element of the array and the rest of the array. + +```rust +fn pop_front(_self: Self) -> (T, Self) +``` + +Example: + +```rust +let (first_elem, rest_of_slice) = slice.pop_front(); +``` + +View the corresponding test file [here][test-file]. + +### pop_back + +Returns a tuple of two items, the beginning of the array with the last element omitted and the last element. + +```rust +fn pop_back(_self: Self) -> (Self, T) +``` + +Example: + +```rust +let (popped_slice, last_elem) = slice.pop_back(); +``` + +View the corresponding test file [here][test-file]. + +### append + +Loops over a slice and adds it to the end of another. + +```rust +fn append(mut self, other: Self) -> Self +``` + +Example: + +```rust +let append = [1, 2].append([3, 4, 5]); +``` + +### insert + +Inserts an element at a specified index and shifts all following elements by 1. + +```rust +fn insert(_self: Self, _index: Field, _elem: T) -> Self +``` + +Example: + +```rust +new_slice = rest_of_slice.insert(2, 100); +assert(new_slice[2] == 100); +``` + +View the corresponding test file [here][test-file]. + +### remove + +Remove an element at a specified index, shifting all elements after it to the left, returning the altered slice and the removed element. + +```rust +fn remove(_self: Self, _index: Field) -> (Self, T) +``` + +Example: + +```rust +let (remove_slice, removed_elem) = slice.remove(3); +``` diff --git a/noir/docs/processed-docs/noir/concepts/data_types/strings.md b/noir/docs/processed-docs/noir/concepts/data_types/strings.md new file mode 100644 index 00000000000..311dfd64416 --- /dev/null +++ b/noir/docs/processed-docs/noir/concepts/data_types/strings.md @@ -0,0 +1,80 @@ +--- +title: Strings +description: + Discover the String data type in Noir. Learn about its methods, see real-world examples, and understand how to effectively manipulate and use Strings in Noir. +keywords: + [ + noir, + string type, + methods, + examples, + concatenation, + ] +sidebar_position: 3 +--- + + +The string type is a fixed length value defined with `str`. + +You can use strings in `assert()` functions or print them with +`println()`. See more about [Logging](../../standard_library/logging). + +```rust +use dep::std; + +fn main(message : pub str<11>, hex_as_string : str<4>) { + println(message); + assert(message == "hello world"); + assert(hex_as_string == "0x41"); +} +``` + +You can convert a `str` to a byte array by calling `as_bytes()` +or a vector by calling `as_bytes_vec()`. + +```rust +fn main() { + let message = "hello world"; + let message_bytes = message.as_bytes(); + let mut message_vec = message.as_bytes_vec(); + assert(message_bytes.len() == 11); + assert(message_bytes[0] == 104); + assert(message_bytes[0] == message_vec.get(0)); +} +``` + +## Escape characters + +You can use escape characters for your strings: + +| Escape Sequence | Description | +|-----------------|-----------------| +| `\r` | Carriage Return | +| `\n` | Newline | +| `\t` | Tab | +| `\0` | Null Character | +| `\"` | Double Quote | +| `\\` | Backslash | + +Example: + +```rust +let s = "Hello \"world" // prints "Hello "world" +let s = "hey \tyou"; // prints "hey you" +``` + +## Raw strings + +A raw string begins with the letter `r` and is optionally delimited by a number of hashes `#`. + +Escape characters are *not* processed within raw strings. All contents are interpreted literally. + +Example: + +```rust +let s = r"Hello world"; +let s = r#"Simon says "hello world""#; + +// Any number of hashes may be used (>= 1) as long as the string also terminates with the same number of hashes +let s = r#####"One "#, Two "##, Three "###, Four "####, Five will end the string."#####; +``` diff --git a/noir/docs/processed-docs/noir/concepts/data_types/structs.md b/noir/docs/processed-docs/noir/concepts/data_types/structs.md new file mode 100644 index 00000000000..dbf68c99813 --- /dev/null +++ b/noir/docs/processed-docs/noir/concepts/data_types/structs.md @@ -0,0 +1,70 @@ +--- +title: Structs +description: + Explore the Struct data type in Noir. Learn about its methods, see real-world examples, and grasp how to effectively define and use Structs in your Noir programs. +keywords: + [ + noir, + struct type, + methods, + examples, + data structures, + ] +sidebar_position: 8 +--- + +A struct also allows for grouping multiple values of different types. Unlike tuples, we can also +name each field. + +> **Note:** The usage of _field_ here refers to each element of the struct and is unrelated to the +> field type of Noir. + +Defining a struct requires giving it a name and listing each field within as `: ` pairs: + +```rust +struct Animal { + hands: Field, + legs: Field, + eyes: u8, +} +``` + +An instance of a struct can then be created with actual values in `: ` pairs in any +order. Struct fields are accessible using their given names: + +```rust +fn main() { + let legs = 4; + + let dog = Animal { + eyes: 2, + hands: 0, + legs, + }; + + let zero = dog.hands; +} +``` + +Structs can also be destructured in a pattern, binding each field to a new variable: + +```rust +fn main() { + let Animal { hands, legs: feet, eyes } = get_octopus(); + + let ten = hands + feet + eyes as u8; +} + +fn get_octopus() -> Animal { + let octopus = Animal { + hands: 0, + legs: 8, + eyes: 2, + }; + + octopus +} +``` + +The new variables can be bound with names different from the original struct field names, as +showcased in the `legs --> feet` binding in the example above. diff --git a/noir/docs/processed-docs/noir/concepts/data_types/tuples.md b/noir/docs/processed-docs/noir/concepts/data_types/tuples.md new file mode 100644 index 00000000000..2ec5c9c4113 --- /dev/null +++ b/noir/docs/processed-docs/noir/concepts/data_types/tuples.md @@ -0,0 +1,48 @@ +--- +title: Tuples +description: + Dive into the Tuple data type in Noir. Understand its methods, practical examples, and best practices for efficiently using Tuples in your Noir code. +keywords: + [ + noir, + tuple type, + methods, + examples, + multi-value containers, + ] +sidebar_position: 7 +--- + +A tuple collects multiple values like an array, but with the added ability to collect values of +different types: + +```rust +fn main() { + let tup: (u8, u64, Field) = (255, 500, 1000); +} +``` + +One way to access tuple elements is via destructuring using pattern matching: + +```rust +fn main() { + let tup = (1, 2); + + let (one, two) = tup; + + let three = one + two; +} +``` + +Another way to access tuple elements is via direct member access, using a period (`.`) followed by +the index of the element we want to access. Index `0` corresponds to the first tuple element, `1` to +the second and so on: + +```rust +fn main() { + let tup = (5, 6, 7, 8); + + let five = tup.0; + let eight = tup.3; +} +``` diff --git a/noir/docs/processed-docs/noir/concepts/data_types/vectors.mdx b/noir/docs/processed-docs/noir/concepts/data_types/vectors.mdx new file mode 100644 index 00000000000..aed13183719 --- /dev/null +++ b/noir/docs/processed-docs/noir/concepts/data_types/vectors.mdx @@ -0,0 +1,171 @@ +--- +title: Vectors +description: Delve into the Vector data type in Noir. Learn about its methods, practical examples, and best practices for using Vectors in your Noir code. +keywords: [noir, vector type, methods, examples, dynamic arrays] +sidebar_position: 6 +--- + +import Experimental from '@site/src/components/Notes/_experimental.mdx'; + + + +A vector is a collection type similar to Rust's Vector type. It's convenient way to use slices as mutable arrays. + +Example: + +```rust +let mut vector: Vec = Vec::new(); +for i in 0..5 { + vector.push(i); +} +assert(vector.len() == 5); +``` + +## Methods + +### new + +Creates a new, empty vector. + +```rust +pub fn new() -> Self { + Self { slice: [] } +} +``` + +Example: + +```rust +let empty_vector: Vec = Vec::new(); +assert(empty_vector.len() == 0); +``` + +### from_slice + +Creates a vector containing each element from a given slice. Mutations to the resulting vector will not affect the original slice. + +```rust +pub fn from_slice(slice: [T]) -> Self { + Self { slice } +} +``` + +Example: + +```rust +let arr: [Field] = [1, 2, 3]; +let vector_from_slice = Vec::from_slice(arr); +assert(vector_from_slice.len() == 3); +``` + +### get + +Retrieves an element from the vector at a given index. Panics if the index points beyond the vector's end. + +```rust +pub fn get(self, index: Field) -> T { + self.slice[index] +} +``` + +Example: + +```rust +let vector: Vec = Vec::from_slice([10, 20, 30]); +assert(vector.get(1) == 20); +``` + +### push + +Adds a new element to the vector's end, returning a new vector with a length one greater than the original unmodified vector. + +```rust +pub fn push(&mut self, elem: T) { + self.slice = self.slice.push_back(elem); +} +``` + +Example: + +```rust +let mut vector: Vec = Vec::new(); +vector.push(10); +assert(vector.len() == 1); +``` + +### pop + +Removes an element from the vector's end, returning a new vector with a length one less than the original vector, along with the removed element. Panics if the vector's length is zero. + +```rust +pub fn pop(&mut self) -> T { + let (popped_slice, last_elem) = self.slice.pop_back(); + self.slice = popped_slice; + last_elem +} +``` + +Example: + +```rust +let mut vector = Vec::from_slice([10, 20]); +let popped_elem = vector.pop(); +assert(popped_elem == 20); +assert(vector.len() == 1); +``` + +### insert + +Inserts an element at a specified index, shifting subsequent elements to the right. + +```rust +pub fn insert(&mut self, index: Field, elem: T) { + self.slice = self.slice.insert(index, elem); +} +``` + +Example: + +```rust +let mut vector = Vec::from_slice([10, 30]); +vector.insert(1, 20); +assert(vector.get(1) == 20); +``` + +### remove + +Removes an element at a specified index, shifting subsequent elements to the left, and returns the removed element. + +```rust +pub fn remove(&mut self, index: Field) -> T { + let (new_slice, elem) = self.slice.remove(index); + self.slice = new_slice; + elem +} +``` + +Example: + +```rust +let mut vector = Vec::from_slice([10, 20, 30]); +let removed_elem = vector.remove(1); +assert(removed_elem == 20); +assert(vector.len() == 2); +``` + +### len + +Returns the number of elements in the vector. + +```rust +pub fn len(self) -> Field { + self.slice.len() +} +``` + +Example: + +```rust +let empty_vector: Vec = Vec::new(); +assert(empty_vector.len() == 0); +``` diff --git a/noir/docs/processed-docs/noir/concepts/distinct.md b/noir/docs/processed-docs/noir/concepts/distinct.md new file mode 100644 index 00000000000..6c993b8b5e0 --- /dev/null +++ b/noir/docs/processed-docs/noir/concepts/distinct.md @@ -0,0 +1,64 @@ +--- +title: Distinct Witnesses +sidebar_position: 11 +--- + +The `distinct` keyword prevents repetitions of witness indices in the program's ABI. This ensures +that the witnesses being returned as public inputs are all unique. + +The `distinct` keyword is only used for return values on program entry points (usually the `main()` +function). + +When using `distinct` and `pub` simultaneously, `distinct` comes first. See the example below. + +You can read more about the problem this solves +[here](https://github.com/noir-lang/noir/issues/1183). + +## Example + +Without the `distinct` keyword, the following program + +```rust +fn main(x : pub Field, y : pub Field) -> pub [Field; 4] { + let a = 1; + let b = 1; + [x + 1, y, a, b] +} +``` + +compiles to + +```json +{ + //... + "abi": { + //... + "param_witnesses": { "x": [1], "y": [2] }, + "return_witnesses": [3, 2, 4, 4] + } +} +``` + +Whereas (with the `distinct` keyword) + +```rust +fn main(x : pub Field, y : pub Field) -> distinct pub [Field; 4] { + let a = 1; + let b = 1; + [x + 1, y, a, b] +} +``` + +compiles to + +```json +{ + //... + "abi": { + //... + "param_witnesses": { "x": [1], "y": [2] }, + //... + "return_witnesses": [3, 4, 5, 6] + } +} +``` diff --git a/noir/docs/processed-docs/noir/concepts/functions.md b/noir/docs/processed-docs/noir/concepts/functions.md new file mode 100644 index 00000000000..48aba9cd058 --- /dev/null +++ b/noir/docs/processed-docs/noir/concepts/functions.md @@ -0,0 +1,226 @@ +--- +title: Functions +description: + Learn how to declare functions and methods in Noir, a programming language with Rust semantics. + This guide covers parameter declaration, return types, call expressions, and more. +keywords: [Noir, Rust, functions, methods, parameter declaration, return types, call expressions] +sidebar_position: 1 +--- + +Functions in Noir follow the same semantics of Rust, though Noir does not support early returns. + +To declare a function the `fn` keyword is used. + +```rust +fn foo() {} +``` + +By default, functions are visible only within the package they are defined. To make them visible outside of that package (for example, as part of a [library](../modules_packages_crates/crates_and_packages.md#libraries)), you should mark them as `pub`: + +```rust +pub fn foo() {} +``` + +You can also restrict the visibility of the function to only the crate it was defined in, by specifying `pub(crate)`: + +```rust +pub(crate) fn foo() {} //foo can only be called within its crate +``` + +All parameters in a function must have a type and all types are known at compile time. The parameter +is pre-pended with a colon and the parameter type. Multiple parameters are separated using a comma. + +```rust +fn foo(x : Field, y : Field){} +``` + +The return type of a function can be stated by using the `->` arrow notation. The function below +states that the foo function must return a `Field`. If the function returns no value, then the arrow +is omitted. + +```rust +fn foo(x : Field, y : Field) -> Field { + x + y +} +``` + +Note that a `return` keyword is unneeded in this case - the last expression in a function's body is +returned. + +## Main function + +If you're writing a binary, the `main` function is the starting point of your program. You can pass all types of expressions to it, as long as they have a fixed size at compile time: + +```rust +fn main(x : Field) // this is fine: passing a Field +fn main(x : [Field; 2]) // this is also fine: passing a Field with known size at compile-time +fn main(x : (Field, bool)) // 👌: passing a (Field, bool) tuple means size 2 +fn main(x : str<5>) // this is fine, as long as you pass a string of size 5 + +fn main(x : Vec) // can't compile, has variable size +fn main(x : [Field]) // can't compile, has variable size +fn main(....// i think you got it by now +``` + +Keep in mind [tests](../../getting_started/tooling/testing.md) don't differentiate between `main` and any other function. The following snippet passes tests, but won't compile or prove: + +```rust +fn main(x : [Field]) { + assert(x[0] == 1); +} + +#[test] +fn test_one() { + main([1, 2]); +} +``` + +```bash +$ nargo test +[testing] Running 1 test functions +[testing] Testing test_one... ok +[testing] All tests passed + +$ nargo check +The application panicked (crashed). +Message: Cannot have variable sized arrays as a parameter to main +``` + +## Call Expressions + +Calling a function in Noir is executed by using the function name and passing in the necessary +arguments. + +Below we show how to call the `foo` function from the `main` function using a call expression: + +```rust +fn main(x : Field, y : Field) { + let z = foo(x); +} + +fn foo(x : Field) -> Field { + x + x +} +``` + +## Methods + +You can define methods in Noir on any struct type in scope. + +```rust +struct MyStruct { + foo: Field, + bar: Field, +} + +impl MyStruct { + fn new(foo: Field) -> MyStruct { + MyStruct { + foo, + bar: 2, + } + } + + fn sum(self) -> Field { + self.foo + self.bar + } +} + +fn main() { + let s = MyStruct::new(40); + assert(s.sum() == 42); +} +``` + +Methods are just syntactic sugar for functions, so if we wanted to we could also call `sum` as +follows: + +```rust +assert(MyStruct::sum(s) == 42); +``` + +It is also possible to specialize which method is chosen depending on the [generic](./generics.md) type that is used. In this example, the `foo` function returns different values depending on its type: + +```rust +struct Foo {} + +impl Foo { + fn foo(self) -> Field { 1 } +} + +impl Foo { + fn foo(self) -> Field { 2 } +} + +fn main() { + let f1: Foo = Foo{}; + let f2: Foo = Foo{}; + assert(f1.foo() + f2.foo() == 3); +} +``` + +Also note that impls with the same method name defined in them cannot overlap. For example, if we already have `foo` defined for `Foo` and `Foo` like we do above, we cannot also define `foo` in an `impl Foo` since it would be ambiguous which version of `foo` to choose. + +```rust +// Including this impl in the same project as the above snippet would +// cause an overlapping impls error +impl Foo { + fn foo(self) -> Field { 3 } +} +``` + +## Lambdas + +Lambdas are anonymous functions. They follow the syntax of Rust - `|arg1, arg2, ..., argN| return_expression`. + +```rust +let add_50 = |val| val + 50; +assert(add_50(100) == 150); +``` + +See [Lambdas](./lambdas.md) for more details. + +## Attributes + +Attributes are metadata that can be applied to a function, using the following syntax: `#[attribute(value)]`. + +Supported attributes include: + +- **builtin**: the function is implemented by the compiler, for efficiency purposes. +- **deprecated**: mark the function as _deprecated_. Calling the function will generate a warning: `warning: use of deprecated function` +- **field**: Used to enable conditional compilation of code depending on the field size. See below for more details +- **oracle**: mark the function as _oracle_; meaning it is an external unconstrained function, implemented in noir_js. See [Unconstrained](./unconstrained.md) and [NoirJS](../../reference/NoirJS/noir_js/index.md) for more details. +- **test**: mark the function as unit tests. See [Tests](../../getting_started/tooling/testing.md) for more details + +### Field Attribute + +The field attribute defines which field the function is compatible for. The function is conditionally compiled, under the condition that the field attribute matches the Noir native field. +The field can be defined implicitly, by using the name of the elliptic curve usually associated to it - for instance bn254, bls12_381 - or explicitly by using the field (prime) order, in decimal or hexadecimal form. +As a result, it is possible to define multiple versions of a function with each version specialized for a different field attribute. This can be useful when a function requires different parameters depending on the underlying elliptic curve. + +Example: we define the function `foo()` three times below. Once for the default Noir bn254 curve, once for the field $\mathbb F_{23}$, which will normally never be used by Noir, and once again for the bls12_381 curve. + +```rust +#[field(bn254)] +fn foo() -> u32 { + 1 +} + +#[field(23)] +fn foo() -> u32 { + 2 +} + +// This commented code would not compile as foo would be defined twice because it is the same field as bn254 +// #[field(21888242871839275222246405745257275088548364400416034343698204186575808495617)] +// fn foo() -> u32 { +// 2 +// } + +#[field(bls12_381)] +fn foo() -> u32 { + 3 +} +``` + +If the field name is not known to Noir, it will discard the function. Field names are case insensitive. diff --git a/noir/docs/processed-docs/noir/concepts/generics.md b/noir/docs/processed-docs/noir/concepts/generics.md new file mode 100644 index 00000000000..ddd42bf1f9b --- /dev/null +++ b/noir/docs/processed-docs/noir/concepts/generics.md @@ -0,0 +1,106 @@ +--- +title: Generics +description: Learn how to use Generics in Noir +keywords: [Noir, Rust, generics, functions, structs] +sidebar_position: 7 +--- + +Generics allow you to use the same functions with multiple different concrete data types. You can +read more about the concept of generics in the Rust documentation +[here](https://doc.rust-lang.org/book/ch10-01-syntax.html). + +Here is a trivial example showing the identity function that supports any type. In Rust, it is +common to refer to the most general type as `T`. We follow the same convention in Noir. + +```rust +fn id(x: T) -> T { + x +} +``` + +## In Structs + +Generics are useful for specifying types in structs. For example, we can specify that a field in a +struct will be of a certain generic type. In this case `value` is of type `T`. + +```rust +struct RepeatedValue { + value: T, + count: Field, +} + +impl RepeatedValue { + fn print(self) { + for _i in 0 .. self.count { + println(self.value); + } + } +} + +fn main() { + let repeated = RepeatedValue { value: "Hello!", count: 2 }; + repeated.print(); +} +``` + +The `print` function will print `Hello!` an arbitrary number of times, twice in this case. + +If we want to be generic over array lengths (which are type-level integers), we can use numeric +generics. Using these looks just like using regular generics, but these generics can resolve to +integers at compile-time, rather than resolving to types. Here's an example of a struct that is +generic over the size of the array it contains internally: + +```rust +struct BigInt { + limbs: [u32; N], +} + +impl BigInt { + // `N` is in scope of all methods in the impl + fn first(first: BigInt, second: BigInt) -> Self { + assert(first.limbs != second.limbs); + first + + fn second(first: BigInt, second: Self) -> Self { + assert(first.limbs != second.limbs); + second + } +} +``` + +## Calling functions on generic parameters + +Since a generic type `T` can represent any type, how can we call functions on the underlying type? +In other words, how can we go from "any type `T`" to "any type `T` that has certain methods available?" + +This is what [traits](../concepts/traits) are for in Noir. Here's an example of a function generic over +any type `T` that implements the `Eq` trait for equality: + +```rust +fn first_element_is_equal(array1: [T; N], array2: [T; N]) -> bool + where T: Eq +{ + if (array1.len() == 0) | (array2.len() == 0) { + true + } else { + array1[0] == array2[0] + } +} + +fn main() { + assert(first_element_is_equal([1, 2, 3], [1, 5, 6])); + + // We can use first_element_is_equal for arrays of any type + // as long as we have an Eq impl for the types we pass in + let array = [MyStruct::new(), MyStruct::new()]; + assert(array_eq(array, array, MyStruct::eq)); +} + +impl Eq for MyStruct { + fn eq(self, other: MyStruct) -> bool { + self.foo == other.foo + } +} +``` + +You can find more details on traits and trait implementations on the [traits page](../concepts/traits). diff --git a/noir/docs/processed-docs/noir/concepts/lambdas.md b/noir/docs/processed-docs/noir/concepts/lambdas.md new file mode 100644 index 00000000000..be3c7e0b5ca --- /dev/null +++ b/noir/docs/processed-docs/noir/concepts/lambdas.md @@ -0,0 +1,81 @@ +--- +title: Lambdas +description: Learn how to use anonymous functions in Noir programming language. +keywords: [Noir programming language, lambda, closure, function, anonymous function] +sidebar_position: 9 +--- + +## Introduction + +Lambdas are anonymous functions. The syntax is `|arg1, arg2, ..., argN| return_expression`. + +```rust +let add_50 = |val| val + 50; +assert(add_50(100) == 150); +``` + +A block can be used as the body of a lambda, allowing you to declare local variables inside it: + +```rust +let cool = || { + let x = 100; + let y = 100; + x + y +} + +assert(cool() == 200); +``` + +## Closures + +Inside the body of a lambda, you can use variables defined in the enclosing function. Such lambdas are called **closures**. In this example `x` is defined inside `main` and is accessed from within the lambda: + +```rust +fn main() { + let x = 100; + let closure = || x + 150; + assert(closure() == 250); +} +``` + +## Passing closures to higher-order functions + +It may catch you by surprise that the following code fails to compile: + +```rust +fn foo(f: fn () -> Field) -> Field { + f() +} + +fn main() { + let (x, y) = (50, 50); + assert(foo(|| x + y) == 100); // error :( +} +``` + +The reason is that the closure's capture environment affects its type - we have a closure that captures two Fields and `foo` +expects a regular function as an argument - those are incompatible. +:::note + +Variables contained within the `||` are the closure's parameters, and the expression that follows it is the closure's body. The capture environment is comprised of any variables used in the closure's body that are not parameters. + +E.g. in |x| x + y, y would be a captured variable, but x would not be, since it is a parameter of the closure. + +::: +The syntax for the type of a closure is `fn[env](args) -> ret_type`, where `env` is the capture environment of the closure - +in this example that's `(Field, Field)`. + +The best solution in our case is to make `foo` generic over the environment type of its parameter, so that it can be called +with closures with any environment, as well as with regular functions: + +```rust +fn foo(f: fn[Env]() -> Field) -> Field { + f() +} + +fn main() { + let (x, y) = (50, 50); + assert(foo(|| x + y) == 100); // compiles fine + assert(foo(|| 60) == 60); // compiles fine +} +``` diff --git a/noir/docs/processed-docs/noir/concepts/mutability.md b/noir/docs/processed-docs/noir/concepts/mutability.md new file mode 100644 index 00000000000..9cc10429cb4 --- /dev/null +++ b/noir/docs/processed-docs/noir/concepts/mutability.md @@ -0,0 +1,93 @@ +--- +title: Mutability +description: + Learn about mutable variables, constants, and globals in Noir programming language. Discover how + to declare, modify, and use them in your programs. +keywords: [noir programming language, mutability in noir, mutable variables, constants, globals] +sidebar_position: 8 +--- + +Variables in noir can be declared mutable via the `mut` keyword. Mutable variables can be reassigned +to via an assignment expression. + +```rust +let x = 2; +x = 3; // error: x must be mutable to be assigned to + +let mut y = 3; +let y = 4; // OK +``` + +The `mut` modifier can also apply to patterns: + +```rust +let (a, mut b) = (1, 2); +a = 11; // error: a must be mutable to be assigned to +b = 12; // OK + +let mut (c, d) = (3, 4); +c = 13; // OK +d = 14; // OK + +// etc. +let MyStruct { x: mut y } = MyStruct { x: a }; +// y is now in scope +``` + +Note that mutability in noir is local and everything is passed by value, so if a called function +mutates its parameters then the parent function will keep the old value of the parameters. + +```rust +fn main() -> pub Field { + let x = 3; + helper(x); + x // x is still 3 +} + +fn helper(mut x: i32) { + x = 4; +} +``` + +## Comptime Values + +:::warning + +The 'comptime' keyword was removed in version 0.10. The comptime keyword and syntax are currently still kept and parsed for backwards compatibility, but are now deprecated and will issue a warning when used. `comptime` has been removed because it is no longer needed for accessing arrays. + +::: + +## Globals + +Noir also supports global variables. However, they must be known at compile-time. The global type can also be inferred by the compiler entirely. Globals can also be used to specify array +annotations for function parameters and can be imported from submodules. + +```rust +global N: Field = 5; // Same as `global N: Field = 5` + +fn main(x : Field, y : [Field; N]) { + let res = x * N; + + assert(res == y[0]); + + let res2 = x * my_submodule::N; + assert(res != res2); +} + +mod my_submodule { + use dep::std; + + global N: Field = 10; + + fn my_helper() -> Field { + let x = N; + x + } +} +``` + +## Why only local mutability? + +Witnesses in a proving system are immutable in nature. Noir aims to _closely_ mirror this setting +without applying additional overhead to the user. Modeling a mutable reference is not as +straightforward as on conventional architectures and would incur some possibly unexpected overhead. diff --git a/noir/docs/processed-docs/noir/concepts/ops.md b/noir/docs/processed-docs/noir/concepts/ops.md new file mode 100644 index 00000000000..60425cb8994 --- /dev/null +++ b/noir/docs/processed-docs/noir/concepts/ops.md @@ -0,0 +1,98 @@ +--- +title: Logical Operations +description: + Learn about the supported arithmetic and logical operations in the Noir programming language. + Discover how to perform operations on private input types, integers, and booleans. +keywords: + [ + Noir programming language, + supported operations, + arithmetic operations, + logical operations, + predicate operators, + bitwise operations, + short-circuiting, + backend, + ] +sidebar_position: 3 +--- + +# Operations + +## Table of Supported Operations + +| Operation | Description | Requirements | +| :-------- | :------------------------------------------------------------: | -------------------------------------: | +| + | Adds two private input types together | Types must be private input | +| - | Subtracts two private input types together | Types must be private input | +| \* | Multiplies two private input types together | Types must be private input | +| / | Divides two private input types together | Types must be private input | +| ^ | XOR two private input types together | Types must be integer | +| & | AND two private input types together | Types must be integer | +| \| | OR two private input types together | Types must be integer | +| \<\< | Left shift an integer by another integer amount | Types must be integer | +| >> | Right shift an integer by another integer amount | Types must be integer | +| ! | Bitwise not of a value | Type must be integer or boolean | +| \< | returns a bool if one value is less than the other | Upper bound must have a known bit size | +| \<= | returns a bool if one value is less than or equal to the other | Upper bound must have a known bit size | +| > | returns a bool if one value is more than the other | Upper bound must have a known bit size | +| >= | returns a bool if one value is more than or equal to the other | Upper bound must have a known bit size | +| == | returns a bool if one value is equal to the other | Both types must not be constants | +| != | returns a bool if one value is not equal to the other | Both types must not be constants | + +### Predicate Operators + +`<,<=, !=, == , >, >=` are known as predicate/comparison operations because they compare two values. +This differs from the operations such as `+` where the operands are used in _computation_. + +### Bitwise Operations Example + +```rust +fn main(x : Field) { + let y = x as u32; + let z = y & y; +} +``` + +`z` is implicitly constrained to be the result of `y & y`. The `&` operand is used to denote bitwise +`&`. + +> `x & x` would not compile as `x` is a `Field` and not an integer type. + +### Logical Operators + +Noir has no support for the logical operators `||` and `&&`. This is because encoding the +short-circuiting that these operators require can be inefficient for Noir's backend. Instead you can +use the bitwise operators `|` and `&` which operate identically for booleans, just without the +short-circuiting. + +```rust +let my_val = 5; + +let mut flag = 1; +if (my_val > 6) | (my_val == 0) { + flag = 0; +} +assert(flag == 1); + +if (my_val != 10) & (my_val < 50) { + flag = 0; +} +assert(flag == 0); +``` + +### Shorthand operators + +Noir shorthand operators for most of the above operators, namely `+=, -=, *=, /=, %=, &=, |=, ^=, <<=`, and `>>=`. These allow for more concise syntax. For example: + +```rust +let mut i = 0; +i = i + 1; +``` + +could be written as: + +```rust +let mut i = 0; +i += 1; +``` diff --git a/noir/docs/processed-docs/noir/concepts/oracles.md b/noir/docs/processed-docs/noir/concepts/oracles.md new file mode 100644 index 00000000000..2e6a6818d48 --- /dev/null +++ b/noir/docs/processed-docs/noir/concepts/oracles.md @@ -0,0 +1,23 @@ +--- +title: Oracles +description: Dive into how Noir supports Oracles via RPC calls, and learn how to declare an Oracle in Noir with our comprehensive guide. +keywords: + - Noir + - Oracles + - RPC Calls + - Unconstrained Functions + - Programming + - Blockchain +sidebar_position: 6 +--- + +Noir has support for Oracles via RPC calls. This means Noir will make an RPC call and use the return value for proof generation. + +Since Oracles are not resolved by Noir, they are [`unconstrained` functions](./unconstrained.md) + +You can declare an Oracle through the `#[oracle()]` flag. Example: + +```rust +#[oracle(get_number_sequence)] +unconstrained fn get_number_sequence(_size: Field) -> [Field] {} +``` diff --git a/noir/docs/processed-docs/noir/concepts/shadowing.md b/noir/docs/processed-docs/noir/concepts/shadowing.md new file mode 100644 index 00000000000..5ce6130d201 --- /dev/null +++ b/noir/docs/processed-docs/noir/concepts/shadowing.md @@ -0,0 +1,44 @@ +--- +title: Shadowing +sidebar_position: 12 +--- + +Noir allows for inheriting variables' values and re-declaring them with the same name similar to Rust, known as shadowing. + +For example, the following function is valid in Noir: + +```rust +fn main() { + let x = 5; + + { + let x = x * 2; + assert (x == 10); + } + + assert (x == 5); +} +``` + +In this example, a variable x is first defined with the value 5. + +The local scope that follows shadows the original x, i.e. creates a local mutable x based on the value of the original x. It is given a value of 2 times the original x. + +When we return to the main scope, x once again refers to just the original x, which stays at the value of 5. + +## Temporal mutability + +One way that shadowing is useful, in addition to ergonomics across scopes, is for temporarily mutating variables. + +```rust +fn main() { + let age = 30; + // age = age + 5; // Would error as `age` is immutable by default. + + let mut age = age + 5; // Temporarily mutates `age` with a new value. + + let age = age; // Locks `age`'s mutability again. + + assert (age == 35); +} +``` diff --git a/noir/docs/processed-docs/noir/concepts/traits.md b/noir/docs/processed-docs/noir/concepts/traits.md new file mode 100644 index 00000000000..ef1445a5907 --- /dev/null +++ b/noir/docs/processed-docs/noir/concepts/traits.md @@ -0,0 +1,389 @@ +--- +title: Traits +description: + Traits in Noir can be used to abstract out a common interface for functions across + several data types. +keywords: [noir programming language, traits, interfaces, generic, protocol] +sidebar_position: 14 +--- + +## Overview + +Traits in Noir are a useful abstraction similar to interfaces or protocols in other languages. Each trait defines +the interface of several methods contained within the trait. Types can then implement this trait by providing +implementations for these methods. For example in the program: + +```rust +struct Rectangle { + width: Field, + height: Field, +} + +impl Rectangle { + fn area(self) -> Field { + self.width * self.height + } +} + +fn log_area(r: Rectangle) { + println(r.area()); +} +``` + +We have a function `log_area` to log the area of a `Rectangle`. Now how should we change the program if we want this +function to work on `Triangle`s as well?: + +```rust +struct Triangle { + width: Field, + height: Field, +} + +impl Triangle { + fn area(self) -> Field { + self.width * self.height / 2 + } +} +``` + +Making `log_area` generic over all types `T` would be invalid since not all types have an `area` method. Instead, we can +introduce a new `Area` trait and make `log_area` generic over all types `T` that implement `Area`: + +```rust +trait Area { + fn area(self) -> Field; +} + +fn log_area(shape: T) where T: Area { + println(shape.area()); +} +``` + +We also need to explicitly implement `Area` for `Rectangle` and `Triangle`. We can do that by changing their existing +impls slightly. Note that the parameter types and return type of each of our `area` methods must match those defined +by the `Area` trait. + +```rust +impl Area for Rectangle { + fn area(self) -> Field { + self.width * self.height + } +} + +impl Area for Triangle { + fn area(self) -> Field { + self.width * self.height / 2 + } +} +``` + +Now we have a working program that is generic over any type of Shape that is used! Others can even use this program +as a library with their own types - such as `Circle` - as long as they also implement `Area` for these types. + +## Where Clauses + +As seen in `log_area` above, when we want to create a function or method that is generic over any type that implements +a trait, we can add a where clause to the generic function. + +```rust +fn log_area(shape: T) where T: Area { + println(shape.area()); +} +``` + +It is also possible to apply multiple trait constraints on the same variable at once by combining traits with the `+` +operator. Similarly, we can have multiple trait constraints by separating each with a comma: + +```rust +fn foo(elements: [T], thing: U) where + T: Default + Add + Eq, + U: Bar, +{ + let mut sum = T::default(); + + for element in elements { + sum += element; + } + + if sum == T::default() { + thing.bar(); + } +} +``` + +## Generic Implementations + +You can add generics to a trait implementation by adding the generic list after the `impl` keyword: + +```rust +trait Second { + fn second(self) -> Field; +} + +impl Second for (T, Field) { + fn second(self) -> Field { + self.1 + } +} +``` + +You can also implement a trait for every type this way: + +```rust +trait Debug { + fn debug(self); +} + +impl Debug for T { + fn debug(self) { + println(self); + } +} + +fn main() { + 1.debug(); +} +``` + +### Generic Trait Implementations With Where Clauses + +Where clauses can also be placed on trait implementations themselves to restrict generics in a similar way. +For example, while `impl Foo for T` implements the trait `Foo` for every type, `impl Foo for T where T: Bar` +will implement `Foo` only for types that also implement `Bar`. This is often used for implementing generic types. +For example, here is the implementation for array equality: + +```rust +impl Eq for [T; N] where T: Eq { + // Test if two arrays have the same elements. + // Because both arrays must have length N, we know their lengths already match. + fn eq(self, other: Self) -> bool { + let mut result = true; + + for i in 0 .. self.len() { + // The T: Eq constraint is needed to call == on the array elements here + result &= self[i] == other[i]; + } + + result + } +} +``` + +## Generic Traits + +Traits themselves can also be generic by placing the generic arguments after the trait name. These generics are in +scope of every item within the trait. + +```rust +trait Into { + // Convert `self` to type `T` + fn into(self) -> T; +} +``` + +When implementing generic traits the generic arguments of the trait must be specified. This is also true anytime +when referencing a generic trait (e.g. in a `where` clause). + +```rust +struct MyStruct { + array: [Field; 2], +} + +impl Into<[Field; 2]> for MyStruct { + fn into(self) -> [Field; 2] { + self.array + } +} + +fn as_array(x: T) -> [Field; 2] + where T: Into<[Field; 2]> +{ + x.into() +} + +fn main() { + let array = [1, 2]; + let my_struct = MyStruct { array }; + + assert_eq(as_array(my_struct), array); +} +``` + +## Trait Methods With No `self` + +A trait can contain any number of methods, each of which have access to the `Self` type which represents each type +that eventually implements the trait. Similarly, the `self` variable is available as well but is not required to be used. +For example, we can define a trait to create a default value for a type. This trait will need to return the `Self` type +but doesn't need to take any parameters: + +```rust +trait Default { + fn default() -> Self; +} +``` + +Implementing this trait can be done similarly to any other trait: + +```rust +impl Default for Field { + fn default() -> Field { + 0 + } +} + +struct MyType {} + +impl Default for MyType { + fn default() -> Field { + MyType {} + } +} +``` + +However, since there is no `self` parameter, we cannot call it via the method call syntax `object.method()`. +Instead, we'll need to refer to the function directly. This can be done either by referring to the +specific impl `MyType::default()` or referring to the trait itself `Default::default()`. In the later +case, type inference determines the impl that is selected. + +```rust +let my_struct = MyStruct::default(); + +let x: Field = Default::default(); +let result = x + Default::default(); +``` + +:::warning + +```rust +let _ = Default::default(); +``` + +If type inference cannot select which impl to use because of an ambiguous `Self` type, an impl will be +arbitrarily selected. This occurs most often when the result of a trait function call with no parameters +is unused. To avoid this, when calling a trait function with no `self` or `Self` parameters or return type, +always refer to it via the implementation type's namespace - e.g. `MyType::default()`. +This is set to change to an error in future Noir versions. + +::: + +## Default Method Implementations + +A trait can also have default implementations of its methods by giving a body to the desired functions. +Note that this body must be valid for all types that may implement the trait. As a result, the only +valid operations on `self` will be operations valid for any type or other operations on the trait itself. + +```rust +trait Numeric { + fn add(self, other: Self) -> Self; + + // Default implementation of double is (self + self) + fn double(self) -> Self { + self.add(self) + } +} +``` + +When implementing a trait with default functions, a type may choose to implement only the required functions: + +```rust +impl Numeric for Field { + fn add(self, other: Field) -> Field { + self + other + } +} +``` + +Or it may implement the optional methods as well: + +```rust +impl Numeric for u32 { + fn add(self, other: u32) -> u32 { + self + other + } + + fn double(self) -> u32 { + self * 2 + } +} +``` + +## Impl Specialization + +When implementing traits for a generic type it is possible to implement the trait for only a certain combination +of generics. This can be either as an optimization or because those specific generics are required to implement the trait. + +```rust +trait Sub { + fn sub(self, other: Self) -> Self; +} + +struct NonZero { + value: T, +} + +impl Sub for NonZero { + fn sub(self, other: Self) -> Self { + let value = self.value - other.value; + assert(value != 0); + NonZero { value } + } +} +``` + +## Overlapping Implementations + +Overlapping implementations are disallowed by Noir to ensure Noir's decision on which impl to select is never ambiguous. +This means if a trait `Foo` is already implemented +by a type `Bar` for all `T`, then we cannot also have a separate impl for `Bar` (or any other +type argument). Similarly, if there is an impl for all `T` such as `impl Debug for T`, we cannot create +any more impls to `Debug` for other types since it would be ambiguous which impl to choose for any given +method call. + +```rust +trait Trait {} + +// Previous impl defined here +impl Trait for (A, B) {} + +// error: Impl for type `(Field, Field)` overlaps with existing impl +impl Trait for (Field, Field) {} +``` + +## Trait Coherence + +Another restriction on trait implementations is coherence. This restriction ensures other crates cannot create +impls that may overlap with other impls, even if several unrelated crates are used as dependencies in the same +program. + +The coherence restriction is: to implement a trait, either the trait itself or the object type must be declared +in the crate the impl is in. + +In practice this often comes up when using types provided by libraries. If a library provides a type `Foo` that does +not implement a trait in the standard library such as `Default`, you may not `impl Default for Foo` in your own crate. +While restrictive, this prevents later issues or silent changes in the program if the `Foo` library later added its +own impl for `Default`. If you are a user of the `Foo` library in this scenario and need a trait not implemented by the +library your choices are to either submit a patch to the library or use the newtype pattern. + +### The Newtype Pattern + +The newtype pattern gets around the coherence restriction by creating a new wrapper type around the library type +that we cannot create `impl`s for. Since the new wrapper type is defined in our current crate, we can create +impls for any trait we need on it. + +```rust +struct Wrapper { + foo: dep::some_library::Foo, +} + +impl Default for Wrapper { + fn default() -> Wrapper { + Wrapper { + foo: dep::some_library::Foo::new(), + } + } +} +``` + +Since we have an impl for our own type, the behavior of this code will not change even if `some_library` is updated +to provide its own `impl Default for Foo`. The downside of this pattern is that it requires extra wrapping and +unwrapping of values when converting to and from the `Wrapper` and `Foo` types. diff --git a/noir/docs/processed-docs/noir/concepts/unconstrained.md b/noir/docs/processed-docs/noir/concepts/unconstrained.md new file mode 100644 index 00000000000..6b3424f7993 --- /dev/null +++ b/noir/docs/processed-docs/noir/concepts/unconstrained.md @@ -0,0 +1,95 @@ +--- +title: Unconstrained Functions +description: "Learn about what unconstrained functions in Noir are, how to use them and when you'd want to." + +keywords: [Noir programming language, unconstrained, open] +sidebar_position: 5 +--- + +Unconstrained functions are functions which do not constrain any of the included computation and allow for non-deterministic computation. + +## Why? + +Zero-knowledge (ZK) domain-specific languages (DSL) enable developers to generate ZK proofs from their programs by compiling code down to the constraints of an NP complete language (such as R1CS or PLONKish languages). However, the hard bounds of a constraint system can be very limiting to the functionality of a ZK DSL. + +Enabling a circuit language to perform unconstrained execution is a powerful tool. Said another way, unconstrained execution lets developers generate witnesses from code that does not generate any constraints. Being able to execute logic outside of a circuit is critical for both circuit performance and constructing proofs on information that is external to a circuit. + +Fetching information from somewhere external to a circuit can also be used to enable developers to improve circuit efficiency. + +A ZK DSL does not just prove computation, but proves that some computation was handled correctly. Thus, it is necessary that when we switch from performing some operation directly inside of a circuit to inside of an unconstrained environment that the appropriate constraints are still laid down elsewhere in the circuit. + +## Example + +An in depth example might help drive the point home. This example comes from the excellent [post](https://discord.com/channels/1113924620781883405/1124022445054111926/1128747641853972590) by Tom in the Noir Discord. + +Let's look at how we can optimize a function to turn a `u72` into an array of `u8`s. + +```rust +fn main(num: u72) -> pub [u8; 8] { + let mut out: [u8; 8] = [0; 8]; + for i in 0..8 { + out[i] = (num >> (56 - (i * 8)) as u72 & 0xff) as u8; + } + + out +} +``` + +``` +Total ACIR opcodes generated for language PLONKCSat { width: 3 }: 91 +Backend circuit size: 3619 +``` + +A lot of the operations in this function are optimized away by the compiler (all the bit-shifts turn into divisions by constants). However we can save a bunch of gates by casting to u8 a bit earlier. This automatically truncates the bit-shifted value to fit in a u8 which allows us to remove the XOR against 0xff. This saves us ~480 gates in total. + +```rust +fn main(num: u72) -> pub [u8; 8] { + let mut out: [u8; 8] = [0; 8]; + for i in 0..8 { + out[i] = (num >> (56 - (i * 8)) as u8; + } + + out +} +``` + +``` +Total ACIR opcodes generated for language PLONKCSat { width: 3 }: 75 +Backend circuit size: 3143 +``` + +Those are some nice savings already but we can do better. This code is all constrained so we're proving every step of calculating out using num, but we don't actually care about how we calculate this, just that it's correct. This is where brillig comes in. + +It turns out that truncating a u72 into a u8 is hard to do inside a snark, each time we do as u8 we lay down 4 ACIR opcodes which get converted into multiple gates. It's actually much easier to calculate num from out than the other way around. All we need to do is multiply each element of out by a constant and add them all together, both relatively easy operations inside a snark. + +We can then run u72_to_u8 as unconstrained brillig code in order to calculate out, then use that result in our constrained function and assert that if we were to do the reverse calculation we'd get back num. This looks a little like the below: + +```rust +fn main(num: u72) -> pub [u8; 8] { + let out = u72_to_u8(num); + + let mut reconstructed_num: u72 = 0; + for i in 0..8 { + reconstructed_num += (out[i] as u72 << (56 - (8 * i))); + } + assert(num == reconstructed_num); + out +} + +unconstrained fn u72_to_u8(num: u72) -> [u8; 8] { + let mut out: [u8; 8] = [0; 8]; + for i in 0..8 { + out[i] = (num >> (56 - (i * 8))) as u8; + } + out +} +``` + +``` +Total ACIR opcodes generated for language PLONKCSat { width: 3 }: 78 +Backend circuit size: 2902 +``` + +This ends up taking off another ~250 gates from our circuit! We've ended up with more ACIR opcodes than before but they're easier for the backend to prove (resulting in fewer gates). + +Generally we want to use brillig whenever there's something that's easy to verify but hard to compute within the circuit. For example, if you wanted to calculate a square root of a number it'll be a much better idea to calculate this in brillig and then assert that if you square the result you get back your number. diff --git a/noir/docs/processed-docs/noir/modules_packages_crates/_category_.json b/noir/docs/processed-docs/noir/modules_packages_crates/_category_.json new file mode 100644 index 00000000000..1debcfe7675 --- /dev/null +++ b/noir/docs/processed-docs/noir/modules_packages_crates/_category_.json @@ -0,0 +1,6 @@ +{ + "label": "Modules, Packages and Crates", + "position": 2, + "collapsible": true, + "collapsed": true +} diff --git a/noir/docs/processed-docs/noir/modules_packages_crates/crates_and_packages.md b/noir/docs/processed-docs/noir/modules_packages_crates/crates_and_packages.md new file mode 100644 index 00000000000..760a463094c --- /dev/null +++ b/noir/docs/processed-docs/noir/modules_packages_crates/crates_and_packages.md @@ -0,0 +1,43 @@ +--- +title: Crates and Packages +description: Learn how to use Crates and Packages in your Noir project +keywords: [Nargo, dependencies, package management, crates, package] +sidebar_position: 0 +--- + +## Crates + +A crate is the smallest amount of code that the Noir compiler considers at a time. +Crates can contain modules, and the modules may be defined in other files that get compiled with the crate, as we’ll see in the coming sections. + +### Crate Types + +A Noir crate can come in several forms: binaries, libraries or contracts. + +#### Binaries + +_Binary crates_ are programs which you can compile to an ACIR circuit which you can then create proofs against. Each must have a function called `main` that defines the ACIR circuit which is to be proved. + +#### Libraries + +_Library crates_ don't have a `main` function and they don't compile down to ACIR. Instead they define functionality intended to be shared with multiple projects, and eventually included in a binary crate. + +#### Contracts + +Contract crates are similar to binary crates in that they compile to ACIR which you can create proofs against. They are different in that they do not have a single `main` function, but are a collection of functions to be deployed to the [Aztec network](https://aztec.network). You can learn more about the technical details of Aztec in the [monorepo](https://github.com/AztecProtocol/aztec-packages) or contract [examples](https://github.com/AztecProtocol/aztec-packages/tree/master/yarn-project/noir-contracts/contracts). + +### Crate Root + +Every crate has a root, which is the source file that the compiler starts, this is also known as the root module. The Noir compiler does not enforce any conditions on the name of the file which is the crate root, however if you are compiling via Nargo the crate root must be called `lib.nr` or `main.nr` for library or binary crates respectively. + +## Packages + +A Nargo _package_ is a collection of one of more crates that provides a set of functionality. A package must include a Nargo.toml file. + +A package _must_ contain either a library or a binary crate, but not both. + +### Differences from Cargo Packages + +One notable difference between Rust's Cargo and Noir's Nargo is that while Cargo allows a package to contain an unlimited number of binary crates and a single library crate, Nargo currently only allows a package to contain a single crate. + +In future this restriction may be lifted to allow a Nargo package to contain both a binary and library crate or multiple binary crates. diff --git a/noir/docs/processed-docs/noir/modules_packages_crates/dependencies.md b/noir/docs/processed-docs/noir/modules_packages_crates/dependencies.md new file mode 100644 index 00000000000..a37dc401b7d --- /dev/null +++ b/noir/docs/processed-docs/noir/modules_packages_crates/dependencies.md @@ -0,0 +1,124 @@ +--- +title: Dependencies +description: + Learn how to specify and manage dependencies in Nargo, allowing you to upload packages to GitHub + and use them easily in your project. +keywords: [Nargo, dependencies, GitHub, package management, versioning] +sidebar_position: 1 +--- + +Nargo allows you to upload packages to GitHub and use them as dependencies. + +## Specifying a dependency + +Specifying a dependency requires a tag to a specific commit and the git url to the url containing +the package. + +Currently, there are no requirements on the tag contents. If requirements are added, it would follow +semver 2.0 guidelines. + +> Note: Without a `tag` , there would be no versioning and dependencies would change each time you +> compile your project. + +For example, to add the [ecrecover-noir library](https://github.com/colinnielsen/ecrecover-noir) to your project, add it to `Nargo.toml`: + +```toml +# Nargo.toml + +[dependencies] +ecrecover = {tag = "v0.8.0", git = "https://github.com/colinnielsen/ecrecover-noir"} +``` + +If the module is in a subdirectory, you can define a subdirectory in your git repository, for example: + +```toml +# Nargo.toml + +[dependencies] +easy_private_token_contract = {tag ="v0.1.0-alpha62", git = "https://github.com/AztecProtocol/aztec-packages", directory = "yarn-project/noir-contracts/contracts/easy_private_token_contract"} +``` + +## Specifying a local dependency + +You can also specify dependencies that are local to your machine. + +For example, this file structure has a library and binary crate + +```tree +├── binary_crate +│   ├── Nargo.toml +│   └── src +│   └── main.nr +└── lib_a + ├── Nargo.toml + └── src + └── lib.nr +``` + +Inside of the binary crate, you can specify: + +```toml +# Nargo.toml + +[dependencies] +lib_a = { path = "../lib_a" } +``` + +## Importing dependencies + +You can import a dependency to a Noir file using the following syntax. For example, to import the +ecrecover-noir library and local lib_a referenced above: + +```rust +use dep::ecrecover; +use dep::lib_a; +``` + +You can also import only the specific parts of dependency that you want to use, like so: + +```rust +use dep::std::hash::sha256; +use dep::std::scalar_mul::fixed_base_embedded_curve; +``` + +Lastly, as demonstrated in the +[elliptic curve example](../standard_library/cryptographic_primitives/ec_primitives#examples), you +can import multiple items in the same line by enclosing them in curly braces: + +```rust +use dep::std::ec::tecurve::affine::{Curve, Point}; +``` + +We don't have a way to consume libraries from inside a [workspace](./workspaces) as external dependencies right now. + +Inside a workspace, these are consumed as `{ path = "../to_lib" }` dependencies in Nargo.toml. + +## Dependencies of Dependencies + +Note that when you import a dependency, you also get access to all of the dependencies of that package. + +For example, the [phy_vector](https://github.com/resurgencelabs/phy_vector) library imports an [fraction](https://github.com/resurgencelabs/fraction) library. If you're importing the phy_vector library, then you can access the functions in fractions library like so: + +```rust +use dep::phy_vector; + +fn main(x : Field, y : pub Field) { + //... + let f = phy_vector::fraction::toFraction(true, 2, 1); + //... +} +``` + +## Available Libraries + +Noir does not currently have an official package manager. You can find a list of available Noir libraries in the [awesome-noir repo here](https://github.com/noir-lang/awesome-noir#libraries). + +Some libraries that are available today include: + +- [Standard Library](https://github.com/noir-lang/noir/tree/master/noir_stdlib) - the Noir Standard Library +- [Ethereum Storage Proof Verification](https://github.com/aragonzkresearch/noir-trie-proofs) - a library that contains the primitives necessary for RLP decoding (in the form of look-up table construction) and Ethereum state and storage proof verification (or verification of any trie proof involving 32-byte long keys) +- [BigInt](https://github.com/shuklaayush/noir-bigint) - a library that provides a custom BigUint56 data type, allowing for computations on large unsigned integers +- [ECrecover](https://github.com/colinnielsen/ecrecover-noir/tree/main) - a library to verify an ECDSA signature and return the source Ethereum address +- [Sparse Merkle Tree Verifier](https://github.com/vocdoni/smtverifier-noir/tree/main) - a library for verification of sparse Merkle trees +- [Signed Int](https://github.com/resurgencelabs/signed_int) - a library for accessing a custom Signed Integer data type, allowing access to negative numbers on Noir +- [Fraction](https://github.com/resurgencelabs/fraction) - a library for accessing fractional number data type in Noir, allowing results that aren't whole numbers diff --git a/noir/docs/processed-docs/noir/modules_packages_crates/modules.md b/noir/docs/processed-docs/noir/modules_packages_crates/modules.md new file mode 100644 index 00000000000..ae822a1cff4 --- /dev/null +++ b/noir/docs/processed-docs/noir/modules_packages_crates/modules.md @@ -0,0 +1,105 @@ +--- +title: Modules +description: + Learn how to organize your files using modules in Noir, following the same convention as Rust's + module system. Examples included. +keywords: [Noir, Rust, modules, organizing files, sub-modules] +sidebar_position: 2 +--- + +Noir's module system follows the same convention as the _newer_ version of Rust's module system. + +## Purpose of Modules + +Modules are used to organize files. Without modules all of your code would need to live in a single +file. In Noir, the compiler does not automatically scan all of your files to detect modules. This +must be done explicitly by the developer. + +## Examples + +### Importing a module in the crate root + +Filename : `src/main.nr` + +```rust +mod foo; + +fn main() { + foo::hello_world(); +} +``` + +Filename : `src/foo.nr` + +```rust +fn from_foo() {} +``` + +In the above snippet, the crate root is the `src/main.nr` file. The compiler sees the module +declaration `mod foo` which prompts it to look for a foo.nr file. + +Visually this module hierarchy looks like the following : + +``` +crate + ├── main + │ + └── foo + └── from_foo + +``` + +### Importing a module throughout the tree + +All modules are accessible from the `crate::` namespace. + +``` +crate + ├── bar + ├── foo + └── main + +``` + +In the above snippet, if `bar` would like to use functions in `foo`, it can do so by `use crate::foo::function_name`. + +### Sub-modules + +Filename : `src/main.nr` + +```rust +mod foo; + +fn main() { + foo::from_foo(); +} +``` + +Filename : `src/foo.nr` + +```rust +mod bar; +fn from_foo() {} +``` + +Filename : `src/foo/bar.nr` + +```rust +fn from_bar() {} +``` + +In the above snippet, we have added an extra module to the module tree; `bar`. `bar` is a submodule +of `foo` hence we declare bar in `foo.nr` with `mod bar`. Since `foo` is not the crate root, the +compiler looks for the file associated with the `bar` module in `src/foo/bar.nr` + +Visually the module hierarchy looks as follows: + +``` +crate + ├── main + │ + └── foo + ├── from_foo + └── bar + └── from_bar +``` diff --git a/noir/docs/processed-docs/noir/modules_packages_crates/workspaces.md b/noir/docs/processed-docs/noir/modules_packages_crates/workspaces.md new file mode 100644 index 00000000000..67a1dafa372 --- /dev/null +++ b/noir/docs/processed-docs/noir/modules_packages_crates/workspaces.md @@ -0,0 +1,40 @@ +--- +title: Workspaces +sidebar_position: 3 +--- + +Workspaces are a feature of nargo that allow you to manage multiple related Noir packages in a single repository. A workspace is essentially a group of related projects that share common build output directories and configurations. + +Each Noir project (with it's own Nargo.toml file) can be thought of as a package. Each package is expected to contain exactly one "named circuit", being the "name" defined in Nargo.toml with the program logic defined in `./src/main.nr`. + +For a project with the following structure: + +```tree +├── crates +│   ├── a +│   │   ├── Nargo.toml +│   │   └── src +│   │   └── main.nr +│   └── b +│   ├── Nargo.toml +│   └── src +│   └── main.nr +├── Nargo.toml +└── Prover.toml +``` + +You can define a workspace in Nargo.toml like so: + +```toml +[workspace] +members = ["crates/a", "crates/b"] +default-member = "crates/a" +``` + +`members` indicates which packages are included in the workspace. As such, all member packages of a workspace will be processed when the `--workspace` flag is used with various commands or if a `default-member` is not specified. + +`default-member` indicates which package various commands process by default. + +Libraries can be defined in a workspace. Inside a workspace, these are consumed as `{ path = "../to_lib" }` dependencies in Nargo.toml. + +Inside a workspace, these are consumed as `{ path = "../to_lib" }` dependencies in Nargo.toml. diff --git a/noir/docs/processed-docs/noir/standard_library/_category_.json b/noir/docs/processed-docs/noir/standard_library/_category_.json new file mode 100644 index 00000000000..af04c0933fd --- /dev/null +++ b/noir/docs/processed-docs/noir/standard_library/_category_.json @@ -0,0 +1,6 @@ +{ + "label": "Standard Library", + "position": 1, + "collapsible": true, + "collapsed": true +} diff --git a/noir/docs/processed-docs/noir/standard_library/black_box_fns.md b/noir/docs/processed-docs/noir/standard_library/black_box_fns.md new file mode 100644 index 00000000000..6b22d0e7466 --- /dev/null +++ b/noir/docs/processed-docs/noir/standard_library/black_box_fns.md @@ -0,0 +1,31 @@ +--- +title: Black Box Functions +description: Black box functions are functions in Noir that rely on backends implementing support for specialized constraints. +keywords: [noir, black box functions] +--- + +Black box functions are functions in Noir that rely on backends implementing support for specialized constraints. This makes certain zk-snark unfriendly computations cheaper than if they were implemented in Noir. + +The ACVM spec defines a set of blackbox functions which backends will be expected to implement. This allows backends to use optimized implementations of these constraints if they have them, however they may also fallback to less efficient naive implementations if not. + +## Function list + +Here is a list of the current black box functions: + +- [SHA256](./cryptographic_primitives/hashes#sha256) +- [Schnorr signature verification](./cryptographic_primitives/schnorr) +- [Blake2s](./cryptographic_primitives/hashes#blake2s) +- [Blake3](./cryptographic_primitives/hashes#blake2s) +- [Pedersen Hash](./cryptographic_primitives/hashes#pedersen_hash) +- [Pedersen Commitment](./cryptographic_primitives/hashes#pedersen_commitment) +- [ECDSA signature verification](./cryptographic_primitives/ecdsa_sig_verification) +- [Fixed base scalar multiplication](./cryptographic_primitives/scalar) +- AND +- XOR +- RANGE +- [Keccak256](./cryptographic_primitives/hashes#keccak256) +- [Recursive proof verification](./recursion) + +Most black box functions are included as part of the Noir standard library, however `AND`, `XOR` and `RANGE` are used as part of the Noir language syntax. For instance, using the bitwise operator `&` will invoke the `AND` black box function. + +You can view the black box functions defined in the ACVM code [here](https://github.com/noir-lang/noir/blob/master/acvm-repo/acir/src/circuit/black_box_functions.rs). diff --git a/noir/docs/processed-docs/noir/standard_library/cryptographic_primitives/_category_.json b/noir/docs/processed-docs/noir/standard_library/cryptographic_primitives/_category_.json new file mode 100644 index 00000000000..5d694210bbf --- /dev/null +++ b/noir/docs/processed-docs/noir/standard_library/cryptographic_primitives/_category_.json @@ -0,0 +1,5 @@ +{ + "position": 0, + "collapsible": true, + "collapsed": true +} diff --git a/noir/docs/processed-docs/noir/standard_library/cryptographic_primitives/ec_primitives.md b/noir/docs/processed-docs/noir/standard_library/cryptographic_primitives/ec_primitives.md new file mode 100644 index 00000000000..d2b42d67b7c --- /dev/null +++ b/noir/docs/processed-docs/noir/standard_library/cryptographic_primitives/ec_primitives.md @@ -0,0 +1,102 @@ +--- +title: Elliptic Curve Primitives +keywords: [cryptographic primitives, Noir project] +sidebar_position: 4 +--- + +Data structures and methods on them that allow you to carry out computations involving elliptic +curves over the (mathematical) field corresponding to `Field`. For the field currently at our +disposal, applications would involve a curve embedded in BN254, e.g. the +[Baby Jubjub curve](https://eips.ethereum.org/EIPS/eip-2494). + +## Data structures + +### Elliptic curve configurations + +(`std::ec::{tecurve,montcurve,swcurve}::{affine,curvegroup}::Curve`), i.e. the specific elliptic +curve you want to use, which would be specified using any one of the methods +`std::ec::{tecurve,montcurve,swcurve}::{affine,curvegroup}::new` which take the coefficients in the +defining equation together with a generator point as parameters. You can find more detail in the +comments in +[`noir_stdlib/src/ec.nr`](https://github.com/noir-lang/noir/blob/master/noir_stdlib/src/ec.nr), but +the gist of it is that the elliptic curves of interest are usually expressed in one of the standard +forms implemented here (Twisted Edwards, Montgomery and Short Weierstraß), and in addition to that, +you could choose to use `affine` coordinates (Cartesian coordinates - the usual (x,y) - possibly +together with a point at infinity) or `curvegroup` coordinates (some form of projective coordinates +requiring more coordinates but allowing for more efficient implementations of elliptic curve +operations). Conversions between all of these forms are provided, and under the hood these +conversions are done whenever an operation is more efficient in a different representation (or a +mixed coordinate representation is employed). + +### Points + +(`std::ec::{tecurve,montcurve,swcurve}::{affine,curvegroup}::Point`), i.e. points lying on the +elliptic curve. For a curve configuration `c` and a point `p`, it may be checked that `p` +does indeed lie on `c` by calling `c.contains(p1)`. + +## Methods + +(given a choice of curve representation, e.g. use `std::ec::tecurve::affine::Curve` and use +`std::ec::tecurve::affine::Point`) + +- The **zero element** is given by `Point::zero()`, and we can verify whether a point `p: Point` is + zero by calling `p.is_zero()`. +- **Equality**: Points `p1: Point` and `p2: Point` may be checked for equality by calling + `p1.eq(p2)`. +- **Addition**: For `c: Curve` and points `p1: Point` and `p2: Point` on the curve, adding these two + points is accomplished by calling `c.add(p1,p2)`. +- **Negation**: For a point `p: Point`, `p.negate()` is its negation. +- **Subtraction**: For `c` and `p1`, `p2` as above, subtracting `p2` from `p1` is accomplished by + calling `c.subtract(p1,p2)`. +- **Scalar multiplication**: For `c` as above, `p: Point` a point on the curve and `n: Field`, + scalar multiplication is given by `c.mul(n,p)`. If instead `n :: [u1; N]`, i.e. `n` is a bit + array, the `bit_mul` method may be used instead: `c.bit_mul(n,p)` +- **Multi-scalar multiplication**: For `c` as above and arrays `n: [Field; N]` and `p: [Point; N]`, + multi-scalar multiplication is given by `c.msm(n,p)`. +- **Coordinate representation conversions**: The `into_group` method converts a point or curve + configuration in the affine representation to one in the CurveGroup representation, and + `into_affine` goes in the other direction. +- **Curve representation conversions**: `tecurve` and `montcurve` curves and points are equivalent + and may be converted between one another by calling `into_montcurve` or `into_tecurve` on their + configurations or points. `swcurve` is more general and a curve c of one of the other two types + may be converted to this representation by calling `c.into_swcurve()`, whereas a point `p` lying + on the curve given by `c` may be mapped to its corresponding `swcurve` point by calling + `c.map_into_swcurve(p)`. +- **Map-to-curve methods**: The Elligator 2 method of mapping a field element `n: Field` into a + `tecurve` or `montcurve` with configuration `c` may be called as `c.elligator2_map(n)`. For all of + the curve configurations, the SWU map-to-curve method may be called as `c.swu_map(z,n)`, where + `z: Field` depends on `Field` and `c` and must be chosen by the user (the conditions it needs to + satisfy are specified in the comments + [here](https://github.com/noir-lang/noir/blob/master/noir_stdlib/src/ec.nr)). + +## Examples + +The +[ec_baby_jubjub test](https://github.com/noir-lang/noir/blob/master/test_programs/compile_success_empty/ec_baby_jubjub/src/main.nr) +illustrates all of the above primitives on various forms of the Baby Jubjub curve. A couple of more +interesting examples in Noir would be: + +Public-key cryptography: Given an elliptic curve and a 'base point' on it, determine the public key +from the private key. This is a matter of using scalar multiplication. In the case of Baby Jubjub, +for example, this code would do: + +```rust +use dep::std::ec::tecurve::affine::{Curve, Point}; + +fn bjj_pub_key(priv_key: Field) -> Point +{ + + let bjj = Curve::new(168700, 168696, G::new(995203441582195749578291179787384436505546430278305826713579947235728471134,5472060717959818805561601436314318772137091100104008585924551046643952123905)); + + let base_pt = Point::new(5299619240641551281634865583518297030282874472190772894086521144482721001553, 16950150798460657717958625567821834550301663161624707787222815936182638968203); + + bjj.mul(priv_key,base_pt) +} +``` + +This would come in handy in a Merkle proof. + +- EdDSA signature verification: This is a matter of combining these primitives with a suitable hash + function. See + [feat(stdlib): EdDSA sig verification noir#1136](https://github.com/noir-lang/noir/pull/1136) for + the case of Baby Jubjub and the Poseidon hash function. diff --git a/noir/docs/processed-docs/noir/standard_library/cryptographic_primitives/ecdsa_sig_verification.mdx b/noir/docs/processed-docs/noir/standard_library/cryptographic_primitives/ecdsa_sig_verification.mdx new file mode 100644 index 00000000000..68688f5c96d --- /dev/null +++ b/noir/docs/processed-docs/noir/standard_library/cryptographic_primitives/ecdsa_sig_verification.mdx @@ -0,0 +1,60 @@ +--- +title: ECDSA Signature Verification +description: Learn about the cryptographic primitives regarding ECDSA over the secp256k1 and secp256r1 curves +keywords: [cryptographic primitives, Noir project, ecdsa, secp256k1, secp256r1, signatures] +sidebar_position: 3 +--- + +import BlackBoxInfo from '@site/src/components/Notes/_blackbox.mdx'; + +Noir supports ECDSA signatures verification over the secp256k1 and secp256r1 curves. + +## ecdsa_secp256k1::verify_signature + +Verifier for ECDSA Secp256k1 signatures + +```rust title="ecdsa_secp256k1" showLineNumbers +pub fn verify_signature( + _public_key_x: [u8; 32], + _public_key_y: [u8; 32], + _signature: [u8; 64], + _message_hash: [u8; N] +) -> bool +``` +> Source code: noir_stdlib/src/ecdsa_secp256k1.nr#L2-L9 + + +example: + +```rust +fn main(hashed_message : [u8;32], pub_key_x : [u8;32], pub_key_y : [u8;32], signature : [u8;64]) { + let valid_signature = std::ecdsa_secp256k1::verify_signature(pub_key_x, pub_key_y, signature, hashed_message); + assert(valid_signature); +} +``` + +## ecdsa_secp256r1::verify_signature + +Verifier for ECDSA Secp256r1 signatures + +```rust title="ecdsa_secp256r1" showLineNumbers +pub fn verify_signature( + _public_key_x: [u8; 32], + _public_key_y: [u8; 32], + _signature: [u8; 64], + _message_hash: [u8; N] +) -> bool +``` +> Source code: noir_stdlib/src/ecdsa_secp256r1.nr#L2-L9 + + +example: + +```rust +fn main(hashed_message : [u8;32], pub_key_x : [u8;32], pub_key_y : [u8;32], signature : [u8;64]) { + let valid_signature = std::ecdsa_secp256r1::verify_signature(pub_key_x, pub_key_y, signature, hashed_message); + assert(valid_signature); +} +``` + + diff --git a/noir/docs/processed-docs/noir/standard_library/cryptographic_primitives/eddsa.mdx b/noir/docs/processed-docs/noir/standard_library/cryptographic_primitives/eddsa.mdx new file mode 100644 index 00000000000..a9c10da6c06 --- /dev/null +++ b/noir/docs/processed-docs/noir/standard_library/cryptographic_primitives/eddsa.mdx @@ -0,0 +1,18 @@ +--- +title: EdDSA Verification +description: Learn about the cryptographic primitives regarding EdDSA +keywords: [cryptographic primitives, Noir project, eddsa, signatures] +sidebar_position: 5 +--- + +import BlackBoxInfo from '@site/src/components/Notes/_blackbox.mdx'; + +## eddsa::eddsa_poseidon_verify + +Verifier for EdDSA signatures + +```rust +fn eddsa_poseidon_verify(public_key_x : Field, public_key_y : Field, signature_s: Field, signature_r8_x: Field, signature_r8_y: Field, message: Field) -> bool +``` + + diff --git a/noir/docs/processed-docs/noir/standard_library/cryptographic_primitives/hashes.mdx b/noir/docs/processed-docs/noir/standard_library/cryptographic_primitives/hashes.mdx new file mode 100644 index 00000000000..c70386f8de3 --- /dev/null +++ b/noir/docs/processed-docs/noir/standard_library/cryptographic_primitives/hashes.mdx @@ -0,0 +1,234 @@ +--- +title: Hash methods +description: + Learn about the cryptographic primitives ready to use for any Noir project, including sha256, + blake2s, pedersen, mimc_bn254 and mimc +keywords: + [cryptographic primitives, Noir project, sha256, blake2s, pedersen, mimc_bn254, mimc, hash] +sidebar_position: 0 +--- + +import BlackBoxInfo from '@site/src/components/Notes/_blackbox.mdx'; + +## sha256 + +Given an array of bytes, returns the resulting sha256 hash. + +```rust title="sha256" showLineNumbers +pub fn sha256(_input: [u8; N]) -> [u8; 32] +``` +> Source code: noir_stdlib/src/hash.nr#L5-L7 + + +example: + +```rust +fn main() { + let x = [163, 117, 178, 149]; // some random bytes + let hash = std::hash::sha256(x); +} +``` + + + +## blake2s + +Given an array of bytes, returns an array with the Blake2 hash + +```rust title="blake2s" showLineNumbers +pub fn blake2s(_input: [u8; N]) -> [u8; 32] +``` +> Source code: noir_stdlib/src/hash.nr#L11-L13 + + +example: + +```rust +fn main() { + let x = [163, 117, 178, 149]; // some random bytes + let hash = std::hash::blake2s(x); +} +``` + + + +## blake3 + +Given an array of bytes, returns an array with the Blake3 hash + +```rust title="blake3" showLineNumbers +pub fn blake3(_input: [u8; N]) -> [u8; 32] +``` +> Source code: noir_stdlib/src/hash.nr#L17-L19 + + +example: + +```rust +fn main() { + let x = [163, 117, 178, 149]; // some random bytes + let hash = std::hash::blake3(x); +} +``` + + + +## pedersen_hash + +Given an array of Fields, returns the Pedersen hash. + +```rust title="pedersen_hash" showLineNumbers +pub fn pedersen_hash(input: [Field; N]) -> Field +``` +> Source code: noir_stdlib/src/hash.nr#L42-L44 + + +example: + +```rust title="pedersen-hash" showLineNumbers +use dep::std; + +fn main(x: Field, y: Field, expected_hash: Field) { + let hash = std::hash::pedersen_hash([x, y]); + assert_eq(hash, expected_hash); +} +``` +> Source code: test_programs/execution_success/pedersen_hash/src/main.nr#L1-L8 + + + + + +## pedersen_commitment + +Given an array of Fields, returns the Pedersen commitment. + +```rust title="pedersen_commitment" showLineNumbers +struct PedersenPoint { + x : Field, + y : Field, +} + +pub fn pedersen_commitment(input: [Field; N]) -> PedersenPoint +``` +> Source code: noir_stdlib/src/hash.nr#L22-L29 + + +example: + +```rust title="pedersen-commitment" showLineNumbers +use dep::std; + +fn main(x: Field, y: Field, expected_commitment: std::hash::PedersenPoint) { + let commitment = std::hash::pedersen_commitment([x, y]); + assert_eq(commitment.x, expected_commitment.x); + assert_eq(commitment.y, expected_commitment.y); +} +``` +> Source code: test_programs/execution_success/pedersen_commitment/src/main.nr#L1-L9 + + + + +## keccak256 + +Given an array of bytes (`u8`), returns the resulting keccak hash as an array of 32 bytes +(`[u8; 32]`). Specify a message_size to hash only the first `message_size` bytes +of the input. + +```rust title="keccak256" showLineNumbers +pub fn keccak256(_input: [u8; N], _message_size: u32) -> [u8; 32] +``` +> Source code: noir_stdlib/src/hash.nr#L67-L69 + + +example: + +```rust title="keccak256" showLineNumbers +use dep::std; + +fn main(x: Field, result: [u8; 32]) { + // We use the `as` keyword here to denote the fact that we want to take just the first byte from the x Field + // The padding is taken care of by the program + let digest = std::hash::keccak256([x as u8], 1); + assert(digest == result); + + //#1399: variable message size + let message_size = 4; + let hash_a = std::hash::keccak256([1, 2, 3, 4], message_size); + let hash_b = std::hash::keccak256([1, 2, 3, 4, 0, 0, 0, 0], message_size); + + assert(hash_a == hash_b); + + let message_size_big = 8; + let hash_c = std::hash::keccak256([1, 2, 3, 4, 0, 0, 0, 0], message_size_big); + + assert(hash_a != hash_c); +} +``` +> Source code: test_programs/execution_success/keccak256/src/main.nr#L1-L22 + + + + +## poseidon + +Given an array of Fields, returns a new Field with the Poseidon Hash. Mind that you need to specify +how many inputs are there to your Poseidon function. + +```rust +// example for hash_1, hash_2 accepts an array of length 2, etc +fn hash_1(input: [Field; 1]) -> Field +``` + +example: + +```rust title="poseidon" showLineNumbers +use dep::std::hash::poseidon; + +fn main(x1: [Field; 2], y1: pub Field, x2: [Field; 4], y2: pub Field) { + let hash1 = poseidon::bn254::hash_2(x1); + assert(hash1 == y1); + + let hash2 = poseidon::bn254::hash_4(x2); + assert(hash2 == y2); +} +``` +> Source code: test_programs/execution_success/poseidon_bn254_hash/src/main.nr#L1-L11 + + +## mimc_bn254 and mimc + +`mimc_bn254` is `mimc`, but with hardcoded parameters for the BN254 curve. You can use it by +providing an array of Fields, and it returns a Field with the hash. You can use the `mimc` method if +you're willing to input your own constants: + +```rust +fn mimc(x: Field, k: Field, constants: [Field; N], exp : Field) -> Field +``` + +otherwise, use the `mimc_bn254` method: + +```rust +fn mimc_bn254(array: [Field; N]) -> Field +``` + +example: + +```rust + +fn main() { + let x = [163, 117, 178, 149]; // some random bytes + let hash = std::hash::mimc::mimc_bn254(x); +} +``` + +## hash_to_field + +```rust +fn hash_to_field(_input : [Field; N]) -> Field {} +``` + +Calculates the `blake2s` hash of the inputs and returns the hash modulo the field modulus to return +a value which can be represented as a `Field`. + diff --git a/noir/docs/processed-docs/noir/standard_library/cryptographic_primitives/index.md b/noir/docs/processed-docs/noir/standard_library/cryptographic_primitives/index.md new file mode 100644 index 00000000000..650f30165d5 --- /dev/null +++ b/noir/docs/processed-docs/noir/standard_library/cryptographic_primitives/index.md @@ -0,0 +1,14 @@ +--- +title: Cryptographic Primitives +description: + Learn about the cryptographic primitives ready to use for any Noir project +keywords: + [ + cryptographic primitives, + Noir project, + ] +--- + +The Noir team is progressively adding new cryptographic primitives to the standard library. Reach out for news or if you would be interested in adding more of these calculations in Noir. + +Some methods are available thanks to the Aztec backend, not being performed using Noir. When using other backends, these methods may or may not be supplied. diff --git a/noir/docs/processed-docs/noir/standard_library/cryptographic_primitives/scalar.mdx b/noir/docs/processed-docs/noir/standard_library/cryptographic_primitives/scalar.mdx new file mode 100644 index 00000000000..9f9a6419140 --- /dev/null +++ b/noir/docs/processed-docs/noir/standard_library/cryptographic_primitives/scalar.mdx @@ -0,0 +1,33 @@ +--- +title: Scalar multiplication +description: See how you can perform scalar multiplications over a fixed base in Noir +keywords: [cryptographic primitives, Noir project, scalar multiplication] +sidebar_position: 1 +--- + +import BlackBoxInfo from '@site/src/components/Notes/_blackbox.mdx'; + +## scalar_mul::fixed_base_embedded_curve + +Performs scalar multiplication over the embedded curve whose coordinates are defined by the +configured noir field. For the BN254 scalar field, this is BabyJubJub or Grumpkin. + +```rust title="fixed_base_embedded_curve" showLineNumbers +pub fn fixed_base_embedded_curve( + _low: Field, + _high: Field +) -> [Field; 2] +``` +> Source code: noir_stdlib/src/scalar_mul.nr#L27-L32 + + +example + +```rust +fn main(x : Field) { + let scal = std::scalar_mul::fixed_base_embedded_curve(x); + println(scal); +} +``` + + diff --git a/noir/docs/processed-docs/noir/standard_library/cryptographic_primitives/schnorr.mdx b/noir/docs/processed-docs/noir/standard_library/cryptographic_primitives/schnorr.mdx new file mode 100644 index 00000000000..4890507733d --- /dev/null +++ b/noir/docs/processed-docs/noir/standard_library/cryptographic_primitives/schnorr.mdx @@ -0,0 +1,45 @@ +--- +title: Schnorr Signatures +description: Learn how you can verify Schnorr signatures using Noir +keywords: [cryptographic primitives, Noir project, schnorr, signatures] +sidebar_position: 2 +--- + +import BlackBoxInfo from '@site/src/components/Notes/_blackbox.mdx'; + +## schnorr::verify_signature + +Verifier for Schnorr signatures over the embedded curve (for BN254 it is Grumpkin). + +```rust title="schnorr_verify" showLineNumbers +pub fn verify_signature( + _public_key_x: Field, + _public_key_y: Field, + _signature: [u8; 64], + _message: [u8; N] +) -> bool +``` +> Source code: noir_stdlib/src/schnorr.nr#L2-L9 + + +where `_signature` can be generated like so using the npm package +[@noir-lang/barretenberg](https://www.npmjs.com/package/@noir-lang/barretenberg) + +```js +const { BarretenbergWasm } = require('@noir-lang/barretenberg/dest/wasm'); +const { Schnorr } = require('@noir-lang/barretenberg/dest/crypto/schnorr'); + +... + +const barretenberg = await BarretenbergWasm.new(); +const schnorr = new Schnorr(barretenberg); +const pubKey = schnorr.computePublicKey(privateKey); +const message = ... +const signature = Array.from( + schnorr.constructSignature(hash, privateKey).toBuffer() +); + +... +``` + + diff --git a/noir/docs/processed-docs/noir/standard_library/logging.md b/noir/docs/processed-docs/noir/standard_library/logging.md new file mode 100644 index 00000000000..db75ef9f86f --- /dev/null +++ b/noir/docs/processed-docs/noir/standard_library/logging.md @@ -0,0 +1,78 @@ +--- +title: Logging +description: + Learn how to use the println statement for debugging in Noir with this tutorial. Understand the + basics of logging in Noir and how to implement it in your code. +keywords: + [ + noir logging, + println statement, + print statement, + debugging in noir, + noir std library, + logging tutorial, + basic logging in noir, + noir logging implementation, + noir debugging techniques, + rust, + ] +--- + +The standard library provides two familiar statements you can use: `println` and `print`. Despite being a limited implementation of rust's `println!` and `print!` macros, these constructs can be useful for debugging. + +You can print the output of both statements in your Noir code by using the `nargo execute` command or the `--show-output` flag when using `nargo test` (provided there are print statements in your tests). + +It is recommended to use `nargo execute` if you want to debug failing constraints with `println` or `print` statements. This is due to every input in a test being a constant rather than a witness, so we issue an error during compilation while we only print during execution (which comes after compilation). Neither `println`, nor `print` are callable for failed constraints caught at compile time. + +Both `print` and `println` are generic functions which can work on integers, fields, strings, and even structs or expressions. Note however, that slices are currently unsupported. For example: + +```rust +struct Person { + age: Field, + height: Field, +} + +fn main(age: Field, height: Field) { + let person = Person { + age: age, + height: height, + }; + println(person); + println(age + height); + println("Hello world!"); +} +``` + +You can print different types in the same statement (including strings) with a type called `fmtstr`. It can be specified in the same way as a normal string, just prepended with an "f" character: + +```rust + let fmt_str = f"i: {i}, j: {j}"; + println(fmt_str); + + let s = myStruct { y: x, x: y }; + println(s); + + println(f"i: {i}, s: {s}"); + + println(x); + println([x, y]); + + let foo = fooStruct { my_struct: s, foo: 15 }; + println(f"s: {s}, foo: {foo}"); + + println(15); // prints 0x0f, implicit Field + println(-1 as u8); // prints 255 + println(-1 as i8); // prints -1 +``` + +Examples shown above are interchangeable between the two `print` statements: + +```rust +let person = Person { age : age, height : height }; + +println(person); +print(person); + +println("Hello world!"); // Prints with a newline at the end of the input +print("Hello world!"); // Prints the input and keeps cursor on the same line +``` diff --git a/noir/docs/processed-docs/noir/standard_library/merkle_trees.md b/noir/docs/processed-docs/noir/standard_library/merkle_trees.md new file mode 100644 index 00000000000..fa488677884 --- /dev/null +++ b/noir/docs/processed-docs/noir/standard_library/merkle_trees.md @@ -0,0 +1,58 @@ +--- +title: Merkle Trees +description: Learn about Merkle Trees in Noir with this tutorial. Explore the basics of computing a merkle root using a proof, with examples. +keywords: + [ + Merkle trees in Noir, + Noir programming language, + check membership, + computing root from leaf, + Noir Merkle tree implementation, + Merkle tree tutorial, + Merkle tree code examples, + Noir libraries, + pedersen hash., + ] +--- + +## compute_merkle_root + +Returns the root of the tree from the provided leaf and its hash path, using a [Pedersen hash](./cryptographic_primitives/hashes.mdx#pedersen_hash). + +```rust +fn compute_merkle_root(leaf : Field, index : Field, hash_path: [Field]) -> Field +``` + +example: + +```rust +/** + // these values are for this example only + index = "0" + priv_key = "0x000000000000000000000000000000000000000000000000000000616c696365" + secret = "0x1929ea3ab8d9106a899386883d9428f8256cfedb3c4f6b66bf4aa4d28a79988f" + note_hash_path = [ + "0x1e61bdae0f027b1b2159e1f9d3f8d00fa668a952dddd822fda80dc745d6f65cc", + "0x0e4223f3925f98934393c74975142bd73079ab0621f4ee133cee050a3c194f1a", + "0x2fd7bb412155bf8693a3bd2a3e7581a679c95c68a052f835dddca85fa1569a40" + ] + */ +fn main(index: Field, priv_key: Field, secret: Field, note_hash_path: [Field; 3]) { + + let pubkey = std::scalar_mul::fixed_base_embedded_curve(priv_key); + let pubkey_x = pubkey[0]; + let pubkey_y = pubkey[1]; + let note_commitment = std::hash::pedersen([pubkey_x, pubkey_y, secret]); + + let root = std::merkle::compute_merkle_root(note_commitment[0], index, note_hash_path); + println(root); +} +``` + +To check merkle tree membership: + +1. Include a merkle root as a program input. +2. Compute the merkle root of a given leaf, index and hash path. +3. Assert the merkle roots are equal. + +For more info about merkle trees, see the Wikipedia [page](https://en.wikipedia.org/wiki/Merkle_tree). diff --git a/noir/docs/processed-docs/noir/standard_library/options.md b/noir/docs/processed-docs/noir/standard_library/options.md new file mode 100644 index 00000000000..970c9cfbf11 --- /dev/null +++ b/noir/docs/processed-docs/noir/standard_library/options.md @@ -0,0 +1,97 @@ +--- +title: Option Type +--- + +The `Option` type is a way to express that a value might be present (`Some(T))` or absent (`None`). It's a safer way to handle potential absence of values, compared to using nulls in many other languages. + +```rust +struct Option { + None, + Some(T), +} +``` + +The `Option` type, already imported into your Noir program, can be used directly: + +```rust +fn main() { + let none = Option::none(); + let some = Option::some(3); +} +``` + +See [this test](https://github.com/noir-lang/noir/blob/5cbfb9c4a06c8865c98ff2b594464b037d821a5c/crates/nargo_cli/tests/test_data/option/src/main.nr) for a more comprehensive set of examples of each of the methods described below. + +## Methods + +### none + +Constructs a none value. + +### some + +Constructs a some wrapper around a given value. + +### is_none + +Returns true if the Option is None. + +### is_some + +Returns true of the Option is Some. + +### unwrap + +Asserts `self.is_some()` and returns the wrapped value. + +### unwrap_unchecked + +Returns the inner value without asserting `self.is_some()`. This method can be useful within an if condition when we already know that `option.is_some()`. If the option is None, there is no guarantee what value will be returned, only that it will be of type T for an `Option`. + +### unwrap_or + +Returns the wrapped value if `self.is_some()`. Otherwise, returns the given default value. + +### unwrap_or_else + +Returns the wrapped value if `self.is_some()`. Otherwise, calls the given function to return a default value. + +### map + +If self is `Some(x)`, this returns `Some(f(x))`. Otherwise, this returns `None`. + +### map_or + +If self is `Some(x)`, this returns `f(x)`. Otherwise, this returns the given default value. + +### map_or_else + +If self is `Some(x)`, this returns `f(x)`. Otherwise, this returns `default()`. + +### and + +Returns None if self is None. Otherwise, this returns `other`. + +### and_then + +If self is None, this returns None. Otherwise, this calls the given function with the Some value contained within self, and returns the result of that call. In some languages this function is called `flat_map` or `bind`. + +### or + +If self is Some, return self. Otherwise, return `other`. + +### or_else + +If self is Some, return self. Otherwise, return `default()`. + +### xor + +If only one of the two Options is Some, return that option. Otherwise, if both options are Some or both are None, None is returned. + +### filter + +Returns `Some(x)` if self is `Some(x)` and `predicate(x)` is true. Otherwise, this returns `None`. + +### flatten + +Flattens an `Option>` into a `Option`. This returns `None` if the outer Option is None. Otherwise, this returns the inner Option. diff --git a/noir/docs/processed-docs/noir/standard_library/recursion.md b/noir/docs/processed-docs/noir/standard_library/recursion.md new file mode 100644 index 00000000000..67962082a8f --- /dev/null +++ b/noir/docs/processed-docs/noir/standard_library/recursion.md @@ -0,0 +1,90 @@ +--- +title: Recursive Proofs +description: Learn about how to write recursive proofs in Noir. +keywords: [recursion, recursive proofs, verification_key, aggregation object, verify_proof] +--- + +Noir supports recursively verifying proofs, meaning you verify the proof of a Noir program in another Noir program. This enables creating proofs of arbitrary size by doing step-wise verification of smaller components of a large proof. + +The `verify_proof` function takes a verification key, proof and public inputs for a zk program, as well as a key hash and an input aggregation object. The key hash is used to check the validity of the verification key and the input aggregation object is required by some proving systems. The `verify_proof` function returns an output aggregation object that can then be fed into future iterations of the proof verification if required. + +```rust +#[foreign(verify_proof)] +fn verify_proof(_verification_key : [Field], _proof : [Field], _public_input : Field, _key_hash : Field, _input_aggregation_object : [Field]) -> [Field] {} +``` + +:::info + +This is a black box function. Read [this section](./black_box_fns) to learn more about black box functions in Noir. + +::: + +## Example usage + +```rust +use dep::std; + +fn main( + verification_key : [Field; 114], + proof : [Field; 94], + public_inputs : [Field; 1], + key_hash : Field, + input_aggregation_object : [Field; 16], + proof_b : [Field; 94], +) -> pub [Field; 16] { + let output_aggregation_object_a = std::verify_proof( + verification_key.as_slice(), + proof.as_slice(), + public_inputs.as_slice(), + key_hash, + input_aggregation_object + ); + + let output_aggregation_object = std::verify_proof( + verification_key.as_slice(), + proof_b.as_slice(), + public_inputs.as_slice(), + key_hash, + output_aggregation_object_a + ); + + let mut output = [0; 16]; + for i in 0..16 { + output[i] = output_aggregation_object[i]; + } + output +} +``` + +## Parameters + +### `verification_key` + +The verification key for the zk program that is being verified. + +### `proof` + +The proof for the zk program that is being verified. + +### `public_inputs` + +These represent the public inputs of the proof we are verifying. They should be checked against in the circuit after construction of a new aggregation state. + +### `key_hash` + +A key hash is used to check the validity of the verification key. The circuit implementing this opcode can use this hash to ensure that the key provided to the circuit matches the key produced by the circuit creator. + +### `input_aggregation_object` + +An aggregation object is blob of data that the top-level verifier must run some proof system specific algorithm on to complete verification. The size is proof system specific and will be set by the backend integrating this opcode. The input aggregation object is only not `None` when we are verifying a previous recursive aggregation in the current circuit. If this is the first recursive aggregation there is no input aggregation object. It is left to the backend to determine how to handle when there is no input aggregation object. + +## Return value + +### `output_aggregation_object` + +This is the result of a recursive aggregation and is what will be fed into the next verifier. +The next verifier can either perform a final verification (returning true or false) or perform another recursive aggregation where this output aggregation object will be the input aggregation object of the next recursive aggregation. + +## Example + +You can see an example of how to do recursive proofs in [this example recursion demo repo](https://github.com/noir-lang/noir-examples/tree/master/recursion). diff --git a/noir/docs/processed-docs/noir/standard_library/traits.md b/noir/docs/processed-docs/noir/standard_library/traits.md new file mode 100644 index 00000000000..209f7820e1d --- /dev/null +++ b/noir/docs/processed-docs/noir/standard_library/traits.md @@ -0,0 +1,408 @@ +--- +title: Traits +description: Noir's stdlib provides a few commonly used traits. +keywords: [traits, trait, interface, protocol, default, add, eq] +--- + +## `std::default` + +### `std::default::Default` + +```rust title="default-trait" showLineNumbers +trait Default { + fn default() -> Self; +} +``` +> Source code: noir_stdlib/src/default.nr#L1-L5 + + +Constructs a default value of a type. + +Implementations: +```rust +impl Default for Field { .. } + +impl Default for i8 { .. } +impl Default for i16 { .. } +impl Default for i32 { .. } +impl Default for i64 { .. } + +impl Default for u8 { .. } +impl Default for u16 { .. } +impl Default for u32 { .. } +impl Default for u64 { .. } + +impl Default for () { .. } +impl Default for bool { .. } + +impl Default for [T; N] + where T: Default { .. } + +impl Default for (A, B) + where A: Default, B: Default { .. } + +impl Default for (A, B, C) + where A: Default, B: Default, C: Default { .. } + +impl Default for (A, B, C, D) + where A: Default, B: Default, C: Default, D: Default { .. } + +impl Default for (A, B, C, D, E) + where A: Default, B: Default, C: Default, D: Default, E: Default { .. } +``` + +For primitive integer types, the return value of `default` is `0`. Container +types such as arrays are filled with default values of their element type. + + +## `std::convert` + +### `std::convert::From` + +```rust title="from-trait" showLineNumbers +trait From { + fn from(input: T) -> Self; +} +``` +> Source code: noir_stdlib/src/convert.nr#L1-L5 + + +The `From` trait defines how to convert from a given type `T` to the type on which the trait is implemented. + +The Noir standard library provides a number of implementations of `From` between primitive types. +```rust title="from-impls" showLineNumbers +// Unsigned integers +impl From for u16 { fn from(value: u8) -> u16 { value as u16 } } + +impl From for u32 { fn from(value: u8) -> u32 { value as u32 } } +impl From for u32 { fn from(value: u16) -> u32 { value as u32 } } + +impl From for u64 { fn from(value: u8) -> u64 { value as u64 } } +impl From for u64 { fn from(value: u16) -> u64 { value as u64 } } +impl From for u64 { fn from(value: u32) -> u64 { value as u64 } } + +impl From for Field { fn from(value: u8) -> Field { value as Field } } +impl From for Field { fn from(value: u16) -> Field { value as Field } } +impl From for Field { fn from(value: u32) -> Field { value as Field } } +impl From for Field { fn from(value: u64) -> Field { value as Field } } + +// Signed integers +impl From for i16 { fn from(value: i8) -> i16 { value as i16 } } + +impl From for i32 { fn from(value: i8) -> i32 { value as i32 } } +impl From for i32 { fn from(value: i16) -> i32 { value as i32 } } + +impl From for i64 { fn from(value: i8) -> i64 { value as i64 } } +impl From for i64 { fn from(value: i16) -> i64 { value as i64 } } +impl From for i64 { fn from(value: i32) -> i64 { value as i64 } } + +// Booleans +impl From for u8 { fn from(value: bool) -> u8 { value as u8 } } +impl From for u16 { fn from(value: bool) -> u16 { value as u16 } } +impl From for u32 { fn from(value: bool) -> u32 { value as u32 } } +impl From for u64 { fn from(value: bool) -> u64 { value as u64 } } +impl From for i8 { fn from(value: bool) -> i8 { value as i8 } } +impl From for i16 { fn from(value: bool) -> i16 { value as i16 } } +impl From for i32 { fn from(value: bool) -> i32 { value as i32 } } +impl From for i64 { fn from(value: bool) -> i64 { value as i64 } } +impl From for Field { fn from(value: bool) -> Field { value as Field } } +``` +> Source code: noir_stdlib/src/convert.nr#L25-L61 + + +#### When to implement `From` + +As a general rule of thumb, `From` may be implemented in the [situations where it would be suitable in Rust](https://doc.rust-lang.org/std/convert/trait.From.html#when-to-implement-from): + +- The conversion is *infallible*: Noir does not provide an equivalent to Rust's `TryFrom`, if the conversion can fail then provide a named method instead. +- The conversion is *lossless*: semantically, it should not lose or discard information. For example, `u32: From` can losslessly convert any `u16` into a valid `u32` such that the original `u16` can be recovered. On the other hand, `u16: From` should not be implemented as `2**16` is a `u32` which cannot be losslessly converted into a `u16`. +- The conversion is *value-preserving*: the conceptual kind and meaning of the resulting value is the same, even though the Noir type and technical representation might be different. While it's possible to infallibly and losslessly convert a `u8` into a `str<2>` hex representation, `4u8` and `"04"` are too different for `str<2>: From` to be implemented. +- The conversion is *obvious*: it's the only reasonable conversion between the two types. If there's ambiguity on how to convert between them such that the same input could potentially map to two different values then a named method should be used. For instance rather than implementing `U128: From<[u8; 16]>`, the methods `U128::from_le_bytes` and `U128::from_be_bytes` are used as otherwise the endianness of the array would be ambiguous, resulting in two potential values of `U128` from the same byte array. + +One additional recommendation specific to Noir is: +- The conversion is *efficient*: it's relatively cheap to convert between the two types. Due to being a ZK DSL, it's more important to avoid unnecessary computation compared to Rust. If the implementation of `From` would encourage users to perform unnecessary conversion, resulting in additional proving time, then it may be preferable to expose functionality such that this conversion may be avoided. + +### `std::convert::Into` + +The `Into` trait is defined as the reciprocal of `From`. It should be easy to convince yourself that if we can convert to type `A` from type `B`, then it's possible to convert type `B` into type `A`. + +For this reason, implementing `From` on a type will automatically generate a matching `Into` implementation. One should always prefer implementing `From` over `Into` as implementing `Into` will not generate a matching `From` implementation. + +```rust title="into-trait" showLineNumbers +trait Into { + fn into(input: Self) -> T; +} + +impl Into for U where T: From { + fn into(input: U) -> T { + T::from(input) + } +} +``` +> Source code: noir_stdlib/src/convert.nr#L13-L23 + + +`Into` is most useful when passing function arguments where the types don't quite match up with what the function expects. In this case, the compiler has enough type information to perform the necessary conversion by just appending `.into()` onto the arguments in question. + + +## `std::cmp` + +### `std::cmp::Eq` + +```rust title="eq-trait" showLineNumbers +trait Eq { + fn eq(self, other: Self) -> bool; +} +``` +> Source code: noir_stdlib/src/cmp.nr#L1-L5 + + +Returns `true` if `self` is equal to `other`. Implementing this trait on a type +allows the type to be used with `==` and `!=`. + +Implementations: +```rust +impl Eq for Field { .. } + +impl Eq for i8 { .. } +impl Eq for i16 { .. } +impl Eq for i32 { .. } +impl Eq for i64 { .. } + +impl Eq for u8 { .. } +impl Eq for u16 { .. } +impl Eq for u32 { .. } +impl Eq for u64 { .. } + +impl Eq for () { .. } +impl Eq for bool { .. } + +impl Eq for [T; N] + where T: Eq { .. } + +impl Eq for (A, B) + where A: Eq, B: Eq { .. } + +impl Eq for (A, B, C) + where A: Eq, B: Eq, C: Eq { .. } + +impl Eq for (A, B, C, D) + where A: Eq, B: Eq, C: Eq, D: Eq { .. } + +impl Eq for (A, B, C, D, E) + where A: Eq, B: Eq, C: Eq, D: Eq, E: Eq { .. } +``` + +### `std::cmp::Ord` + +```rust title="ord-trait" showLineNumbers +trait Ord { + fn cmp(self, other: Self) -> Ordering; +} +``` +> Source code: noir_stdlib/src/cmp.nr#L94-L98 + + +`a.cmp(b)` compares two values returning `Ordering::less()` if `a < b`, +`Ordering::equal()` if `a == b`, or `Ordering::greater()` if `a > b`. +Implementing this trait on a type allows `<`, `<=`, `>`, and `>=` to be +used on values of the type. + +Implementations: + +```rust +impl Ord for u8 { .. } +impl Ord for u16 { .. } +impl Ord for u32 { .. } +impl Ord for u64 { .. } + +impl Ord for i8 { .. } +impl Ord for i16 { .. } +impl Ord for i32 { .. } + +impl Ord for i64 { .. } + +impl Ord for () { .. } +impl Ord for bool { .. } + +impl Ord for [T; N] + where T: Ord { .. } + +impl Ord for (A, B) + where A: Ord, B: Ord { .. } + +impl Ord for (A, B, C) + where A: Ord, B: Ord, C: Ord { .. } + +impl Ord for (A, B, C, D) + where A: Ord, B: Ord, C: Ord, D: Ord { .. } + +impl Ord for (A, B, C, D, E) + where A: Ord, B: Ord, C: Ord, D: Ord, E: Ord { .. } +``` + +## `std::ops` + +### `std::ops::Add`, `std::ops::Sub`, `std::ops::Mul`, and `std::ops::Div` + +These traits abstract over addition, subtraction, multiplication, and division respectively. +Implementing these traits for a given type will also allow that type to be used with the corresponding operator +for that trait (`+` for Add, etc) in addition to the normal method names. + +```rust title="add-trait" showLineNumbers +trait Add { + fn add(self, other: Self) -> Self; +} +``` +> Source code: noir_stdlib/src/ops.nr#L1-L5 + +```rust title="sub-trait" showLineNumbers +trait Sub { + fn sub(self, other: Self) -> Self; +} +``` +> Source code: noir_stdlib/src/ops.nr#L19-L23 + +```rust title="mul-trait" showLineNumbers +trait Mul { + fn mul(self, other: Self) -> Self; +} +``` +> Source code: noir_stdlib/src/ops.nr#L37-L41 + +```rust title="div-trait" showLineNumbers +trait Div { + fn div(self, other: Self) -> Self; +} +``` +> Source code: noir_stdlib/src/ops.nr#L55-L59 + + +The implementations block below is given for the `Add` trait, but the same types that implement +`Add` also implement `Sub`, `Mul`, and `Div`. + +Implementations: +```rust +impl Add for Field { .. } + +impl Add for i8 { .. } +impl Add for i16 { .. } +impl Add for i32 { .. } +impl Add for i64 { .. } + +impl Add for u8 { .. } +impl Add for u16 { .. } +impl Add for u32 { .. } +impl Add for u64 { .. } +``` + +### `std::ops::Rem` + +```rust title="rem-trait" showLineNumbers +trait Rem{ + fn rem(self, other: Self) -> Self; +} +``` +> Source code: noir_stdlib/src/ops.nr#L73-L77 + + +`Rem::rem(a, b)` is the remainder function returning the result of what is +left after dividing `a` and `b`. Implementing `Rem` allows the `%` operator +to be used with the implementation type. + +Unlike other numeric traits, `Rem` is not implemented for `Field`. + +Implementations: +```rust +impl Rem for u8 { fn rem(self, other: u8) -> u8 { self % other } } +impl Rem for u16 { fn rem(self, other: u16) -> u16 { self % other } } +impl Rem for u32 { fn rem(self, other: u32) -> u32 { self % other } } +impl Rem for u64 { fn rem(self, other: u64) -> u64 { self % other } } + +impl Rem for i8 { fn rem(self, other: i8) -> i8 { self % other } } +impl Rem for i16 { fn rem(self, other: i16) -> i16 { self % other } } +impl Rem for i32 { fn rem(self, other: i32) -> i32 { self % other } } +impl Rem for i64 { fn rem(self, other: i64) -> i64 { self % other } } +``` + +### `std::ops::{ BitOr, BitAnd, BitXor }` + +```rust title="bitor-trait" showLineNumbers +trait BitOr { + fn bitor(self, other: Self) -> Self; +} +``` +> Source code: noir_stdlib/src/ops.nr#L89-L93 + +```rust title="bitand-trait" showLineNumbers +trait BitAnd { + fn bitand(self, other: Self) -> Self; +} +``` +> Source code: noir_stdlib/src/ops.nr#L107-L111 + +```rust title="bitxor-trait" showLineNumbers +trait BitXor { + fn bitxor(self, other: Self) -> Self; +} +``` +> Source code: noir_stdlib/src/ops.nr#L125-L129 + + +Traits for the bitwise operations `|`, `&`, and `^`. + +Implementing `BitOr`, `BitAnd` or `BitXor` for a type allows the `|`, `&`, or `^` operator respectively +to be used with the type. + +The implementations block below is given for the `BitOr` trait, but the same types that implement +`BitOr` also implement `BitAnd` and `BitXor`. + +Implementations: +```rust +impl BitOr for bool { fn bitor(self, other: bool) -> bool { self | other } } + +impl BitOr for u8 { fn bitor(self, other: u8) -> u8 { self | other } } +impl BitOr for u16 { fn bitor(self, other: u16) -> u16 { self | other } } +impl BitOr for u32 { fn bitor(self, other: u32) -> u32 { self | other } } +impl BitOr for u64 { fn bitor(self, other: u64) -> u64 { self | other } } + +impl BitOr for i8 { fn bitor(self, other: i8) -> i8 { self | other } } +impl BitOr for i16 { fn bitor(self, other: i16) -> i16 { self | other } } +impl BitOr for i32 { fn bitor(self, other: i32) -> i32 { self | other } } +impl BitOr for i64 { fn bitor(self, other: i64) -> i64 { self | other } } +``` + +### `std::ops::{ Shl, Shr }` + +```rust title="shl-trait" showLineNumbers +trait Shl { + fn shl(self, other: Self) -> Self; +} +``` +> Source code: noir_stdlib/src/ops.nr#L143-L147 + +```rust title="shr-trait" showLineNumbers +trait Shr { + fn shr(self, other: Self) -> Self; +} +``` +> Source code: noir_stdlib/src/ops.nr#L160-L164 + + +Traits for a bit shift left and bit shift right. + +Implementing `Shl` for a type allows the left shift operator (`<<`) to be used with the implementation type. +Similarly, implementing `Shr` allows the right shift operator (`>>`) to be used with the type. + +Note that bit shifting is not currently implemented for signed types. + +The implementations block below is given for the `Shl` trait, but the same types that implement +`Shl` also implement `Shr`. + +Implementations: +```rust +impl Shl for u8 { fn shl(self, other: u8) -> u8 { self << other } } +impl Shl for u16 { fn shl(self, other: u16) -> u16 { self << other } } +impl Shl for u32 { fn shl(self, other: u32) -> u32 { self << other } } +impl Shl for u64 { fn shl(self, other: u64) -> u64 { self << other } } +``` diff --git a/noir/docs/processed-docs/noir/standard_library/zeroed.md b/noir/docs/processed-docs/noir/standard_library/zeroed.md new file mode 100644 index 00000000000..97dab02dac2 --- /dev/null +++ b/noir/docs/processed-docs/noir/standard_library/zeroed.md @@ -0,0 +1,25 @@ +--- +title: Zeroed Function +description: + The zeroed function returns a zeroed value of any type. +keywords: + [ + zeroed + ] +--- + +Implements `fn zeroed() -> T` to return a zeroed value of any type. This function is generally unsafe to use as the zeroed bit pattern is not guaranteed to be valid for all types. It can however, be useful in cases when the value is guaranteed not to be used such as in a BoundedVec library implementing a growable vector, up to a certain length, backed by an array. The array can be initialized with zeroed values which are guaranteed to be inaccessible until the vector is pushed to. Similarly, enumerations in noir can be implemented using this method by providing zeroed values for the unused variants. + +You can access the function at `std::unsafe::zeroed`. + +This function currently supports the following types: + +- Field +- Bool +- Uint +- Array +- String +- Tuple +- Function + +Using it on other types could result in unexpected behavior. diff --git a/noir/docs/docs/reference/NoirJS/backend_barretenberg/.nojekyll b/noir/docs/processed-docs/reference/NoirJS/backend_barretenberg/.nojekyll similarity index 100% rename from noir/docs/docs/reference/NoirJS/backend_barretenberg/.nojekyll rename to noir/docs/processed-docs/reference/NoirJS/backend_barretenberg/.nojekyll diff --git a/noir/docs/processed-docs/reference/NoirJS/backend_barretenberg/classes/BarretenbergBackend.md b/noir/docs/processed-docs/reference/NoirJS/backend_barretenberg/classes/BarretenbergBackend.md new file mode 100644 index 00000000000..af4a7558cdf --- /dev/null +++ b/noir/docs/processed-docs/reference/NoirJS/backend_barretenberg/classes/BarretenbergBackend.md @@ -0,0 +1,202 @@ +# BarretenbergBackend + +## Implements + +- [`Backend`](../interfaces/Backend.md) + +## Constructors + +### new BarretenbergBackend(acirCircuit, options) + +```ts +new BarretenbergBackend(acirCircuit, options): BarretenbergBackend +``` + +#### Parameters + +| Parameter | Type | +| :------ | :------ | +| `acirCircuit` | [`CompiledCircuit`](../type-aliases/CompiledCircuit.md) | +| `options` | [`BackendOptions`](../type-aliases/BackendOptions.md) | + +#### Returns + +[`BarretenbergBackend`](BarretenbergBackend.md) + +## Methods + +### destroy() + +```ts +destroy(): Promise +``` + +#### Returns + +`Promise`\<`void`\> + +#### Implementation of + +[`Backend`](../interfaces/Backend.md).[`destroy`](../interfaces/Backend.md#destroy) + +#### Description + +Destroys the backend + +*** + +### generateFinalProof() + +```ts +generateFinalProof(decompressedWitness): Promise +``` + +Generate a final proof. This is the proof for the circuit which will verify +intermediate proofs and or can be seen as the proof created for regular circuits. + +#### Parameters + +| Parameter | Type | +| :------ | :------ | +| `decompressedWitness` | `Uint8Array` | + +#### Returns + +`Promise`\<[`ProofData`](../type-aliases/ProofData.md)\> + +#### Implementation of + +[`Backend`](../interfaces/Backend.md).[`generateFinalProof`](../interfaces/Backend.md#generatefinalproof) + +*** + +### generateIntermediateProof() + +```ts +generateIntermediateProof(witness): Promise +``` + +Generates an intermediate proof. This is the proof that can be verified +in another circuit. + +This is sometimes referred to as a recursive proof. +We avoid this terminology as the only property of this proof +that matters is the fact that it is easy to verify in another circuit. +We _could_ choose to verify this proof outside of a circuit just as easily. + +#### Parameters + +| Parameter | Type | +| :------ | :------ | +| `witness` | `Uint8Array` | + +#### Returns + +`Promise`\<[`ProofData`](../type-aliases/ProofData.md)\> + +#### Implementation of + +[`Backend`](../interfaces/Backend.md).[`generateIntermediateProof`](../interfaces/Backend.md#generateintermediateproof) + +#### Example + +```typescript +const intermediateProof = await backend.generateIntermediateProof(witness); +``` + +*** + +### generateIntermediateProofArtifacts() + +```ts +generateIntermediateProofArtifacts(proofData, numOfPublicInputs): Promise +``` + +Generates artifacts that will be passed to a circuit that will verify this proof. + +Instead of passing the proof and verification key as a byte array, we pass them +as fields which makes it cheaper to verify in a circuit. + +The proof that is passed here will have been created using the `generateIntermediateProof` +method. + +The number of public inputs denotes how many public inputs are in the inner proof. + +#### Parameters + +| Parameter | Type | Default value | +| :------ | :------ | :------ | +| `proofData` | [`ProofData`](../type-aliases/ProofData.md) | `undefined` | +| `numOfPublicInputs` | `number` | `0` | + +#### Returns + +`Promise`\<`object`\> + +#### Implementation of + +[`Backend`](../interfaces/Backend.md).[`generateIntermediateProofArtifacts`](../interfaces/Backend.md#generateintermediateproofartifacts) + +#### Example + +```typescript +const artifacts = await backend.generateIntermediateProofArtifacts(proof, numOfPublicInputs); +``` + +*** + +### verifyFinalProof() + +```ts +verifyFinalProof(proofData): Promise +``` + +#### Parameters + +| Parameter | Type | +| :------ | :------ | +| `proofData` | [`ProofData`](../type-aliases/ProofData.md) | + +#### Returns + +`Promise`\<`boolean`\> + +#### Implementation of + +[`Backend`](../interfaces/Backend.md).[`verifyFinalProof`](../interfaces/Backend.md#verifyfinalproof) + +#### Description + +Verifies a final proof + +*** + +### verifyIntermediateProof() + +```ts +verifyIntermediateProof(proofData): Promise +``` + +#### Parameters + +| Parameter | Type | +| :------ | :------ | +| `proofData` | [`ProofData`](../type-aliases/ProofData.md) | + +#### Returns + +`Promise`\<`boolean`\> + +#### Implementation of + +[`Backend`](../interfaces/Backend.md).[`verifyIntermediateProof`](../interfaces/Backend.md#verifyintermediateproof) + +#### Example + +```typescript +const isValidIntermediate = await backend.verifyIntermediateProof(proof); +``` + +*** + +Generated using [typedoc-plugin-markdown](https://www.npmjs.com/package/typedoc-plugin-markdown) and [TypeDoc](https://typedoc.org/) diff --git a/noir/docs/docs/reference/NoirJS/backend_barretenberg/index.md b/noir/docs/processed-docs/reference/NoirJS/backend_barretenberg/index.md similarity index 100% rename from noir/docs/docs/reference/NoirJS/backend_barretenberg/index.md rename to noir/docs/processed-docs/reference/NoirJS/backend_barretenberg/index.md diff --git a/noir/docs/docs/reference/NoirJS/backend_barretenberg/interfaces/Backend.md b/noir/docs/processed-docs/reference/NoirJS/backend_barretenberg/interfaces/Backend.md similarity index 100% rename from noir/docs/docs/reference/NoirJS/backend_barretenberg/interfaces/Backend.md rename to noir/docs/processed-docs/reference/NoirJS/backend_barretenberg/interfaces/Backend.md diff --git a/noir/docs/docs/reference/NoirJS/backend_barretenberg/type-aliases/BackendOptions.md b/noir/docs/processed-docs/reference/NoirJS/backend_barretenberg/type-aliases/BackendOptions.md similarity index 100% rename from noir/docs/docs/reference/NoirJS/backend_barretenberg/type-aliases/BackendOptions.md rename to noir/docs/processed-docs/reference/NoirJS/backend_barretenberg/type-aliases/BackendOptions.md diff --git a/noir/docs/docs/reference/NoirJS/backend_barretenberg/type-aliases/CompiledCircuit.md b/noir/docs/processed-docs/reference/NoirJS/backend_barretenberg/type-aliases/CompiledCircuit.md similarity index 100% rename from noir/docs/docs/reference/NoirJS/backend_barretenberg/type-aliases/CompiledCircuit.md rename to noir/docs/processed-docs/reference/NoirJS/backend_barretenberg/type-aliases/CompiledCircuit.md diff --git a/noir/docs/docs/reference/NoirJS/backend_barretenberg/type-aliases/ProofData.md b/noir/docs/processed-docs/reference/NoirJS/backend_barretenberg/type-aliases/ProofData.md similarity index 100% rename from noir/docs/docs/reference/NoirJS/backend_barretenberg/type-aliases/ProofData.md rename to noir/docs/processed-docs/reference/NoirJS/backend_barretenberg/type-aliases/ProofData.md diff --git a/noir/docs/docs/reference/NoirJS/backend_barretenberg/typedoc-sidebar.cjs b/noir/docs/processed-docs/reference/NoirJS/backend_barretenberg/typedoc-sidebar.cjs similarity index 100% rename from noir/docs/docs/reference/NoirJS/backend_barretenberg/typedoc-sidebar.cjs rename to noir/docs/processed-docs/reference/NoirJS/backend_barretenberg/typedoc-sidebar.cjs diff --git a/noir/docs/docs/reference/NoirJS/noir_js/.nojekyll b/noir/docs/processed-docs/reference/NoirJS/noir_js/.nojekyll similarity index 100% rename from noir/docs/docs/reference/NoirJS/noir_js/.nojekyll rename to noir/docs/processed-docs/reference/NoirJS/noir_js/.nojekyll diff --git a/noir/docs/docs/reference/NoirJS/noir_js/classes/Noir.md b/noir/docs/processed-docs/reference/NoirJS/noir_js/classes/Noir.md similarity index 100% rename from noir/docs/docs/reference/NoirJS/noir_js/classes/Noir.md rename to noir/docs/processed-docs/reference/NoirJS/noir_js/classes/Noir.md diff --git a/noir/docs/docs/reference/NoirJS/noir_js/functions/and.md b/noir/docs/processed-docs/reference/NoirJS/noir_js/functions/and.md similarity index 100% rename from noir/docs/docs/reference/NoirJS/noir_js/functions/and.md rename to noir/docs/processed-docs/reference/NoirJS/noir_js/functions/and.md diff --git a/noir/docs/docs/reference/NoirJS/noir_js/functions/blake2s256.md b/noir/docs/processed-docs/reference/NoirJS/noir_js/functions/blake2s256.md similarity index 100% rename from noir/docs/docs/reference/NoirJS/noir_js/functions/blake2s256.md rename to noir/docs/processed-docs/reference/NoirJS/noir_js/functions/blake2s256.md diff --git a/noir/docs/docs/reference/NoirJS/noir_js/functions/ecdsa_secp256k1_verify.md b/noir/docs/processed-docs/reference/NoirJS/noir_js/functions/ecdsa_secp256k1_verify.md similarity index 100% rename from noir/docs/docs/reference/NoirJS/noir_js/functions/ecdsa_secp256k1_verify.md rename to noir/docs/processed-docs/reference/NoirJS/noir_js/functions/ecdsa_secp256k1_verify.md diff --git a/noir/docs/docs/reference/NoirJS/noir_js/functions/ecdsa_secp256r1_verify.md b/noir/docs/processed-docs/reference/NoirJS/noir_js/functions/ecdsa_secp256r1_verify.md similarity index 100% rename from noir/docs/docs/reference/NoirJS/noir_js/functions/ecdsa_secp256r1_verify.md rename to noir/docs/processed-docs/reference/NoirJS/noir_js/functions/ecdsa_secp256r1_verify.md diff --git a/noir/docs/docs/reference/NoirJS/noir_js/functions/keccak256.md b/noir/docs/processed-docs/reference/NoirJS/noir_js/functions/keccak256.md similarity index 100% rename from noir/docs/docs/reference/NoirJS/noir_js/functions/keccak256.md rename to noir/docs/processed-docs/reference/NoirJS/noir_js/functions/keccak256.md diff --git a/noir/docs/docs/reference/NoirJS/noir_js/functions/sha256.md b/noir/docs/processed-docs/reference/NoirJS/noir_js/functions/sha256.md similarity index 100% rename from noir/docs/docs/reference/NoirJS/noir_js/functions/sha256.md rename to noir/docs/processed-docs/reference/NoirJS/noir_js/functions/sha256.md diff --git a/noir/docs/docs/reference/NoirJS/noir_js/functions/xor.md b/noir/docs/processed-docs/reference/NoirJS/noir_js/functions/xor.md similarity index 100% rename from noir/docs/docs/reference/NoirJS/noir_js/functions/xor.md rename to noir/docs/processed-docs/reference/NoirJS/noir_js/functions/xor.md diff --git a/noir/docs/docs/reference/NoirJS/noir_js/index.md b/noir/docs/processed-docs/reference/NoirJS/noir_js/index.md similarity index 100% rename from noir/docs/docs/reference/NoirJS/noir_js/index.md rename to noir/docs/processed-docs/reference/NoirJS/noir_js/index.md diff --git a/noir/docs/docs/reference/NoirJS/noir_js/type-aliases/CompiledCircuit.md b/noir/docs/processed-docs/reference/NoirJS/noir_js/type-aliases/CompiledCircuit.md similarity index 100% rename from noir/docs/docs/reference/NoirJS/noir_js/type-aliases/CompiledCircuit.md rename to noir/docs/processed-docs/reference/NoirJS/noir_js/type-aliases/CompiledCircuit.md diff --git a/noir/docs/docs/reference/NoirJS/noir_js/type-aliases/ForeignCallHandler.md b/noir/docs/processed-docs/reference/NoirJS/noir_js/type-aliases/ForeignCallHandler.md similarity index 100% rename from noir/docs/docs/reference/NoirJS/noir_js/type-aliases/ForeignCallHandler.md rename to noir/docs/processed-docs/reference/NoirJS/noir_js/type-aliases/ForeignCallHandler.md diff --git a/noir/docs/docs/reference/NoirJS/noir_js/type-aliases/ForeignCallInput.md b/noir/docs/processed-docs/reference/NoirJS/noir_js/type-aliases/ForeignCallInput.md similarity index 100% rename from noir/docs/docs/reference/NoirJS/noir_js/type-aliases/ForeignCallInput.md rename to noir/docs/processed-docs/reference/NoirJS/noir_js/type-aliases/ForeignCallInput.md diff --git a/noir/docs/docs/reference/NoirJS/noir_js/type-aliases/ForeignCallOutput.md b/noir/docs/processed-docs/reference/NoirJS/noir_js/type-aliases/ForeignCallOutput.md similarity index 100% rename from noir/docs/docs/reference/NoirJS/noir_js/type-aliases/ForeignCallOutput.md rename to noir/docs/processed-docs/reference/NoirJS/noir_js/type-aliases/ForeignCallOutput.md diff --git a/noir/docs/docs/reference/NoirJS/noir_js/type-aliases/InputMap.md b/noir/docs/processed-docs/reference/NoirJS/noir_js/type-aliases/InputMap.md similarity index 100% rename from noir/docs/docs/reference/NoirJS/noir_js/type-aliases/InputMap.md rename to noir/docs/processed-docs/reference/NoirJS/noir_js/type-aliases/InputMap.md diff --git a/noir/docs/docs/reference/NoirJS/noir_js/type-aliases/ProofData.md b/noir/docs/processed-docs/reference/NoirJS/noir_js/type-aliases/ProofData.md similarity index 100% rename from noir/docs/docs/reference/NoirJS/noir_js/type-aliases/ProofData.md rename to noir/docs/processed-docs/reference/NoirJS/noir_js/type-aliases/ProofData.md diff --git a/noir/docs/docs/reference/NoirJS/noir_js/type-aliases/WitnessMap.md b/noir/docs/processed-docs/reference/NoirJS/noir_js/type-aliases/WitnessMap.md similarity index 100% rename from noir/docs/docs/reference/NoirJS/noir_js/type-aliases/WitnessMap.md rename to noir/docs/processed-docs/reference/NoirJS/noir_js/type-aliases/WitnessMap.md diff --git a/noir/docs/docs/reference/NoirJS/noir_js/typedoc-sidebar.cjs b/noir/docs/processed-docs/reference/NoirJS/noir_js/typedoc-sidebar.cjs similarity index 100% rename from noir/docs/docs/reference/NoirJS/noir_js/typedoc-sidebar.cjs rename to noir/docs/processed-docs/reference/NoirJS/noir_js/typedoc-sidebar.cjs diff --git a/noir/docs/processed-docs/reference/_category_.json b/noir/docs/processed-docs/reference/_category_.json new file mode 100644 index 00000000000..5b6a20a609a --- /dev/null +++ b/noir/docs/processed-docs/reference/_category_.json @@ -0,0 +1,5 @@ +{ + "position": 4, + "collapsible": true, + "collapsed": true +} diff --git a/noir/docs/processed-docs/reference/nargo_commands.md b/noir/docs/processed-docs/reference/nargo_commands.md new file mode 100644 index 00000000000..fc2671b2bfc --- /dev/null +++ b/noir/docs/processed-docs/reference/nargo_commands.md @@ -0,0 +1,253 @@ +--- +title: Nargo +description: + Noir CLI Commands for Noir Prover and Verifier to create, execute, prove and verify programs, + generate Solidity verifier smart contract and compile into JSON file containing ACIR + representation and ABI of circuit. +keywords: + [ + Nargo, + Noir CLI, + Noir Prover, + Noir Verifier, + generate Solidity verifier, + compile JSON file, + ACIR representation, + ABI of circuit, + TypeScript, + ] +sidebar_position: 0 +--- + +## General options + +| Option | Description | +| -------------------- | -------------------------------------------------- | +| `--show-ssa` | Emit debug information for the intermediate SSA IR | +| `--deny-warnings` | Quit execution when warnings are emitted | +| `--silence-warnings` | Suppress warnings | +| `-h, --help` | Print help | + +## `nargo help [subcommand]` + +Prints the list of available commands or specific information of a subcommand. + +_Arguments_ + +| Argument | Description | +| -------------- | -------------------------------------------- | +| `` | The subcommand whose help message to display | + +## `nargo backend` + +Installs and selects custom backends used to generate and verify proofs. + +### Commands + +| Command | Description | +| ----------- | --------------------------------------------------------- | +| `current` | Prints the name of the currently active backend | +| `ls` | Prints the list of currently installed backends | +| `use` | Select the backend to use | +| `install` | Install a new backend from a URL | +| `uninstall` | Uninstalls a backend | +| `help` | Print this message or the help of the given subcommand(s) | + +### Options + +| Option | Description | +| ------------ | ----------- | +| `-h, --help` | Print help | + +## `nargo check` + +Generate the `Prover.toml` and `Verifier.toml` files for specifying prover and verifier in/output +values of the Noir program respectively. + +### Options + +| Option | Description | +| --------------------- | ------------------------------------- | +| `--package ` | The name of the package to check | +| `--workspace` | Check all packages in the workspace | +| `--print-acir` | Display the ACIR for compiled circuit | +| `--deny-warnings` | Treat all warnings as errors | +| `--silence-warnings` | Suppress warnings | +| `-h, --help` | Print help | + +### `nargo codegen-verifier` + +Generate a Solidity verifier smart contract for the program. + +### Options + +| Option | Description | +| --------------------- | ------------------------------------- | +| `--package ` | The name of the package to codegen | +| `--workspace` | Codegen all packages in the workspace | +| `--print-acir` | Display the ACIR for compiled circuit | +| `--deny-warnings` | Treat all warnings as errors | +| `--silence-warnings` | Suppress warnings | +| `-h, --help` | Print help | + +## `nargo compile` + +Compile the program into a JSON build artifact file containing the ACIR representation and the ABI +of the circuit. This build artifact can then be used to generate and verify proofs. + +You can also use "build" as an alias for compile (e.g. `nargo build`). + +### Options + +| Option | Description | +| --------------------- | ------------------------------------------------------------ | +| `--package ` | The name of the package to compile | +| `--workspace` | Compile all packages in the workspace | +| `--print-acir` | Display the ACIR for compiled circuit | +| `--deny-warnings` | Treat all warnings as errors | +| `--silence-warnings` | Suppress warnings | +| `-h, --help` | Print help | + +## `nargo new ` + +Creates a new Noir project in a new folder. + +**Arguments** + +| Argument | Description | +| -------- | -------------------------------- | +| `` | The path to save the new project | + +### Options + +| Option | Description | +| --------------- | ----------------------------------------------------- | +| `--name ` | Name of the package [default: package directory name] | +| `--lib` | Use a library template | +| `--bin` | Use a binary template [default] | +| `--contract` | Use a contract template | +| `-h, --help` | Print help | + +## `nargo init` + +Creates a new Noir project in the current directory. + +### Options + +| Option | Description | +| --------------- | ----------------------------------------------------- | +| `--name ` | Name of the package [default: current directory name] | +| `--lib` | Use a library template | +| `--bin` | Use a binary template [default] | +| `--contract` | Use a contract template | +| `-h, --help` | Print help | + +## `nargo execute [WITNESS_NAME]` + +Runs the Noir program and prints its return value. + +**Arguments** + +| Argument | Description | +| ---------------- | ----------------------------------------- | +| `[WITNESS_NAME]` | Write the execution witness to named file | + +### Options + +| Option | Description | +| --------------------------------- | ------------------------------------------------------------------------------------ | +| `-p, --prover-name ` | The name of the toml file which contains the inputs for the prover [default: Prover] | +| `--package ` | The name of the package to execute | +| `--workspace` | Execute all packages in the workspace | +| `--print-acir` | Display the ACIR for compiled circuit | +| `--deny-warnings` | Treat all warnings as errors | +| `--silence-warnings` | Suppress warnings | +| `--oracle-resolver` | JSON RPC url to solve oracle calls | +| `-h, --help` | Print help | + +_Usage_ + +The inputs to the circuit are read from the `Prover.toml` file generated by `nargo check`, which +must be filled in. + +To save the witness to file, run the command with a value for the `WITNESS_NAME` argument. A +`.tr` file will then be saved in the `./target` folder. + +## `nargo prove` + +Creates a proof for the program. + +### Options + +| Option | Description | +| ------------------------------------- | ---------------------------------------------------------------------------------------- | +| `-p, --prover-name ` | The name of the toml file which contains the inputs for the prover [default: Prover] | +| `-v, --verifier-name ` | The name of the toml file which contains the inputs for the verifier [default: Verifier] | +| `--verify` | Verify proof after proving | +| `--package ` | The name of the package to prove | +| `--workspace` | Prove all packages in the workspace | +| `--print-acir` | Display the ACIR for compiled circuit | +| `--deny-warnings` | Treat all warnings as errors | +| `--silence-warnings` | Suppress warnings | +| `--oracle-resolver` | JSON RPC url to solve oracle calls | +| `-h, --help` | Print help | + +## `nargo verify` + +Given a proof and a program, verify whether the proof is valid. + +### Options + +| Option | Description | +| ------------------------------------- | ---------------------------------------------------------------------------------------- | +| `-v, --verifier-name ` | The name of the toml file which contains the inputs for the verifier [default: Verifier] | +| `--package ` | The name of the package to verify | +| `--workspace` | Verify all packages in the workspace | +| `--print-acir` | Display the ACIR for compiled circuit | +| `--deny-warnings` | Treat all warnings as errors | +| `--silence-warnings` | Suppress warnings | +| `-h, --help` | Print help | + +## `nargo test [TEST_NAME]` + +Nargo will automatically compile and run any functions which have the decorator `#[test]` on them if +you run `nargo test`. To print `println` statements in tests, use the `--show-output` flag. + +Takes an optional `--exact` flag which allows you to select tests based on an exact name. + +See an example on the [testing page](../getting_started/tooling/testing.md). + +### Options + +| Option | Description | +| --------------------- | -------------------------------------- | +| `--show-output` | Display output of `println` statements | +| `--exact` | Only run tests that match exactly | +| `--package ` | The name of the package to test | +| `--workspace` | Test all packages in the workspace | +| `--print-acir` | Display the ACIR for compiled circuit | +| `--deny-warnings` | Treat all warnings as errors | +| `--silence-warnings` | Suppress warnings | +| `--oracle-resolver` | JSON RPC url to solve oracle calls | +| `-h, --help` | Print help | + +## `nargo info` + +Prints a table containing the information of the package. + +Currently the table provide + +1. The number of ACIR opcodes +2. The final number gates in the circuit used by a backend + +If the file contains a contract the table will provide the +above information about each function of the contract. + +## `nargo lsp` + +Start a long-running Language Server process that communicates over stdin/stdout. +Usually this command is not run by a user, but instead will be run by a Language Client, such as [vscode-noir](https://github.com/noir-lang/vscode-noir). + +## `nargo fmt` + +Automatically formats your Noir source code based on the default formatting settings. diff --git a/noir/docs/processed-docs/tutorials/noirjs_app.md b/noir/docs/processed-docs/tutorials/noirjs_app.md new file mode 100644 index 00000000000..23534795dde --- /dev/null +++ b/noir/docs/processed-docs/tutorials/noirjs_app.md @@ -0,0 +1,279 @@ +--- +title: Building a web app with NoirJS +description: Learn how to setup a new app that uses Noir to generate and verify zero-knowledge SNARK proofs in a typescript or javascript environment. +keywords: [how to, guide, javascript, typescript, noir, barretenberg, zero-knowledge, proofs, app] +sidebar_position: 0 +pagination_next: noir/concepts/data_types/index +--- + +NoirJS is a set of packages meant to work both in a browser and a server environment. In this tutorial, we will build a simple web app using them. From here, you should get an idea on how to proceed with your own Noir projects! + +You can find the complete app code for this guide [here](https://github.com/noir-lang/tiny-noirjs-app). + +## Setup + +:::note + +Feel free to use whatever versions, just keep in mind that Nargo and the NoirJS packages are meant to be in sync. For example, Nargo 0.19.x matches `noir_js@0.19.x`, etc. + +In this guide, we will be pinned to 0.19.4. + +::: + +Before we start, we want to make sure we have Node and Nargo installed. + +We start by opening a terminal and executing `node --version`. If we don't get an output like `v20.10.0`, that means node is not installed. Let's do that by following the handy [nvm guide](https://github.com/nvm-sh/nvm?tab=readme-ov-file#install--update-script). + +As for `Nargo`, we can follow the the [Nargo guide](../getting_started/installation/index.md) to install it. If you're lazy, just paste this on a terminal and run `noirup`: + +```sh +curl -L https://raw.githubusercontent.com/noir-lang/noirup/main/install | bash +``` + +Easy enough. Onwards! + +## Our project + +ZK is a powerful technology. An app that doesn't reveal one of the inputs to *anyone* is almost unbelievable, yet Noir makes it as easy as a single line of code. + +In fact, it's so simple that it comes nicely packaged in `nargo`. Let's do that! + +### Nargo + +Run: + +```nargo new circuit``` + +And... That's about it. Your program is ready to be compiled and run. + +To compile, let's `cd` into the `circuit` folder to enter our project, and call: + +```nargo compile``` + +This compiles our circuit into `json` format and add it to a new `target` folder. + +:::info + +At this point in the tutorial, your folder structure should look like this: + +```tree +. +└── circuit <---- our working directory + ├── Nargo.toml + ├── src + │ └── main.nr + └── target + └── circuit.json +``` + +::: + +### Node and Vite + +If you want to explore Nargo, feel free to go on a side-quest now and follow the steps in the +[getting started](../getting_started/hello_noir/index.md) guide. However, we want our app to run on the browser, so we need Vite. + +Vite is a powerful tool to generate static websites. While it provides all kinds of features, let's just go barebones with some good old vanilla JS. + +To do this this, go back to the previous folder (`cd ..`) and create a new vite project by running `npm create vite` and choosing "Vanilla" and "Javascript". + +You should see `vite-project` appear in your root folder. This seems like a good time to `cd` into it and install our NoirJS packages: + +```bash +npm i @noir-lang/backend_barretenberg@0.19.4 @noir-lang/noir_js@0.19.4 +``` + +:::info + +At this point in the tutorial, your folder structure should look like this: + +```tree +. +└── circuit + └── ...etc... +└── vite-project <---- our working directory + └── ...etc... +``` + +::: + +#### Some cleanup + +`npx create vite` is amazing but it creates a bunch of files we don't really need for our simple example. Actually, let's just delete everything except for `index.html`, `main.js` and `package.json`. I feel lighter already. + +![my heart is ready for you, noir.js](../../static/img/memes/titanic.jpeg) + +## HTML + +Our app won't run like this, of course. We need some working HTML, at least. Let's open our broken-hearted `index.html` and replace everything with this code snippet: + +```html + + + + + + +

Noir app

+
+ + +
+
+

Logs

+

Proof

+
+ + +``` + +It *could* be a beautiful UI... Depending on which universe you live in. + +## Some good old vanilla Javascript + +Our love for Noir needs undivided attention, so let's just open `main.js` and delete everything (this is where the romantic scenery becomes a bit creepy). + +Start by pasting in this boilerplate code: + +```js +const setup = async () => { + await Promise.all([ + import("@noir-lang/noirc_abi").then(module => + module.default(new URL("@noir-lang/noirc_abi/web/noirc_abi_wasm_bg.wasm", import.meta.url).toString()) + ), + import("@noir-lang/acvm_js").then(module => + module.default(new URL("@noir-lang/acvm_js/web/acvm_js_bg.wasm", import.meta.url).toString()) + ) + ]); +} + +function display(container, msg) { + const c = document.getElementById(container); + const p = document.createElement('p'); + p.textContent = msg; + c.appendChild(p); +} + +document.getElementById('submitGuess').addEventListener('click', async () => { + try { + // here's where love happens + } catch(err) { + display("logs", "Oh 💔 Wrong guess") + } +}); + +``` + +The display function doesn't do much. We're simply manipulating our website to see stuff happening. For example, if the proof fails, it will simply log a broken heart 😢 + +As for the `setup` function, it's just a sad reminder that dealing with `wasm` on the browser is not as easy as it should. Just copy, paste, and forget. + +:::info + +At this point in the tutorial, your folder structure should look like this: + +```tree +. +└── circuit + └── ...same as above +└── vite-project + ├── main.js + ├── package.json + └── index.html +``` + +You'll see other files and folders showing up (like `package-lock.json`, `node_modules`) but you shouldn't have to care about those. + +::: + +## Some NoirJS + +We're starting with the good stuff now. If you've compiled the circuit as described above, you should have a `json` file we want to import at the very top of our `main.js` file: + +```ts +import circuit from '../circuit/target/circuit.json'; +``` + +[Noir is backend-agnostic](../index.mdx#whats-new-about-noir). We write Noir, but we also need a proving backend. That's why we need to import and instantiate the two dependencies we installed above: `BarretenbergBackend` and `Noir`. Let's import them right below: + +```js +import { BarretenbergBackend } from '@noir-lang/backend_barretenberg'; +import { Noir } from '@noir-lang/noir_js'; +``` + +And instantiate them inside our try-catch block: + +```ts +// try { +const backend = new BarretenbergBackend(circuit); +const noir = new Noir(circuit, backend); +// } +``` + +:::note + +For the remainder of the tutorial, everything will be happening inside the `try` block + +::: + +## Our app + +Now for the app itself. We're capturing whatever is in the input when people press the submit button. Just add this: + +```js +const x = parseInt(document.getElementById('guessInput').value); +const input = { x, y: 2 }; +``` + +Now we're ready to prove stuff! Let's feed some inputs to our circuit and calculate the proof: + +```js +await setup(); // let's squeeze our wasm inits here + +display('logs', 'Generating proof... ⌛'); +const proof = await noir.generateFinalProof(input); +display('logs', 'Generating proof... ✅'); +display('results', proof.proof); +``` + +You're probably eager to see stuff happening, so go and run your app now! + +From your terminal, run `npm run dev`. If it doesn't open a browser for you, just visit `localhost:5173`. You should now see the worst UI ever, with an ugly input. + +![Getting Started 0](@site/static/img/noir_getting_started_1.png) + +Now, our circuit says `fn main(x: Field, y: pub Field)`. This means only the `y` value is public, and it's hardcoded above: `input = { x, y: 2 }`. In other words, you won't need to send your secret`x` to the verifier! + +By inputting any number other than 2 in the input box and clicking "submit", you should get a valid proof. Otherwise the proof won't even generate correctly. By the way, if you're human, you shouldn't be able to understand anything on the "proof" box. That's OK. We like you, human ❤️. + +## Verifying + +Time to celebrate, yes! But we shouldn't trust machines so blindly. Let's add these lines to see our proof being verified: + +```js +display('logs', 'Verifying proof... ⌛'); +const verification = await noir.verifyFinalProof(proof); +if (verification) display('logs', 'Verifying proof... ✅'); +``` + +You have successfully generated a client-side Noir web app! + +![coded app without math knowledge](../../static/img/memes/flextape.jpeg) + +## Further Reading + +You can see how noirjs is used in a full stack Next.js hardhat application in the [noir-starter repo here](https://github.com/noir-lang/noir-starter/tree/main/vite-hardhat). The example shows how to calculate a proof in the browser and verify it with a deployed Solidity verifier contract from noirjs. + +You should also check out the more advanced examples in the [noir-examples repo](https://github.com/noir-lang/noir-examples), where you'll find reference usage for some cool apps. diff --git a/noir/docs/scripts/preprocess/include_code.js b/noir/docs/scripts/preprocess/include_code.js new file mode 100644 index 00000000000..ffe50065002 --- /dev/null +++ b/noir/docs/scripts/preprocess/include_code.js @@ -0,0 +1,312 @@ +const fs = require('fs'); +const path = require('path'); +const childProcess = require('child_process'); + +const getLineNumberFromIndex = (fileContent, index) => { + return fileContent.substring(0, index).split('\n').length; +}; + +/** + * Search for lines of the form + */ +function processHighlighting(codeSnippet, identifier) { + const lines = codeSnippet.split('\n'); + /** + * For an identifier = bar: + * + * Matches of the form: `highlight-next-line:foo:bar:baz` will be replaced with "highlight-next-line". + * Matches of the form: `highlight-next-line:foo:baz` will be replaced with "". + */ + const regex1 = /highlight-next-line:([a-zA-Z0-9-._:]+)/; + const replacement1 = 'highlight-next-line'; + const regex2 = /highlight-start:([a-zA-Z0-9-._:]+)/; + const replacement2 = 'highlight-start'; + const regex3 = /highlight-end:([a-zA-Z0-9-._:]+)/; + const replacement3 = 'highlight-end'; + const regex4 = /this-will-error:([a-zA-Z0-9-._:]+)/; + const replacement4 = 'this-will-error'; + + let result = ''; + let mutated = false; + + const processLine = (line, regex, replacement) => { + const match = line.match(regex); + if (match) { + mutated = true; + + const identifiers = match[1].split(':'); + if (identifiers.includes(identifier)) { + line = line.replace(match[0], replacement); + } else { + // Remove matched text completely + line = line.replace(match[0], ''); + } + } else { + // No match: it's an ordinary line of code. + } + return line.trim() == '//' || line.trim() == '#' ? '' : line; + }; + + for (let line of lines) { + mutated = false; + line = processLine(line, regex1, replacement1); + line = processLine(line, regex2, replacement2); + line = processLine(line, regex3, replacement3); + line = processLine(line, regex4, replacement4); + result += line === '' && mutated ? '' : line + '\n'; + } + + return result.trim(); +} + +let lastReleasedVersion; + +/** Returns the last released tag */ +function getLatestTag() { + if (!lastReleasedVersion) { + const manifest = path.resolve(__dirname, '../../../.release-please-manifest.json'); + lastReleasedVersion = JSON.parse(fs.readFileSync(manifest).toString())['.']; + } + return lastReleasedVersion ? `v${lastReleasedVersion}` : undefined; +} + +/** Returns whether to use the latest release or the current version of stuff. */ +function useLastRelease() { + return process.env.NETLIFY || process.env.INCLUDE_RELEASED_CODE; +} + +/** + * Returns the contents of a file. If the build is running for publishing, it will load the contents + * of the file in the last released version. + */ +function readFile(filePath, tag) { + if (tag && tag !== 'master') { + try { + const root = path.resolve(__dirname, '../../../'); + const relPath = path.relative(root, filePath); + const taggedPath = `${tag}:${relPath}`; + return childProcess.execSync('git show', taggedPath).toString(); + } catch (err) { + console.error(`Error reading file ${filePath} from version ${tag}. Falling back to current content.`); + } + } + return fs.readFileSync(filePath, 'utf-8'); +} + +/** Extracts a code snippet, trying with the last release if applicable, and falling back to current content. */ +function extractCodeSnippet(filePath, identifier, requesterFile) { + if (useLastRelease()) { + try { + return doExtractCodeSnippet(filePath, identifier, false); + } catch (err) { + console.error( + `Error extracting code snippet ${identifier} from ${path.basename( + filePath, + )} requested by ${requesterFile}: ${err}. Falling back to current content.`, + ); + } + } + + return doExtractCodeSnippet(filePath, identifier, true); +} + +/** + * Parse a code file, looking for identifiers of the form: + * `docs:start:${identifier}` and `docs:end:{identifier}`. + * Extract that section of code. + * + * It's complicated if code snippet identifiers overlap (i.e. the 'start' of one code snippet is in the + * middle of another code snippet). The extra logic in this function searches for all identifiers, and + * removes any which fall within the bounds of the code snippet for this particular `identifier` param. + * @returns the code snippet, and start and end line numbers which can later be used for creating a link to github source code. + */ +function doExtractCodeSnippet(filePath, identifier, useCurrent) { + const tag = useCurrent ? 'master' : getLatestTag(); + let fileContent = readFile(filePath, tag); + let lineRemovalCount = 0; + let linesToRemove = []; + + const startRegex = /(?:\/\/|#)\s+docs:start:([a-zA-Z0-9-._:]+)/g; // `g` will iterate through the regex.exec loop + const endRegex = /(?:\/\/|#)\s+docs:end:([a-zA-Z0-9-._:]+)/g; + + /** + * Search for one of the regex statements in the code file. If it's found, return the line as a string and the line number. + */ + const lookForMatch = (regex) => { + let match; + let matchFound = false; + let matchedLineNum = null; + let actualMatch = null; + let lines = fileContent.split('\n'); + while ((match = regex.exec(fileContent))) { + if (match !== null) { + const identifiers = match[1].split(':'); + let tempMatch = identifiers.includes(identifier) ? match : null; + + if (tempMatch === null) { + // If it's not a match, we'll make a note that we should remove the matched text, because it's from some other identifier and should not appear in the snippet for this identifier. + for (let i = 0; i < lines.length; i++) { + let line = lines[i]; + if (line.trim() == match[0].trim()) { + linesToRemove.push(i + 1); // lines are indexed from 1 + ++lineRemovalCount; + } + } + } else { + if (matchFound === true) { + throw new Error(`Duplicate for regex ${regex} and identifier ${identifier}`); + } + matchFound = true; + matchedLineNum = getLineNumberFromIndex(fileContent, tempMatch.index); + actualMatch = tempMatch; + } + } + } + + return [actualMatch, matchedLineNum]; + }; + + let [startMatch, startLineNum] = lookForMatch(startRegex); + let [endMatch, endLineNum] = lookForMatch(endRegex); + + // Double-check that the extracted line actually contains the required start and end identifier. + if (startMatch !== null) { + const startIdentifiers = startMatch[1].split(':'); + startMatch = startIdentifiers.includes(identifier) ? startMatch : null; + } + if (endMatch !== null) { + const endIdentifiers = endMatch[1].split(':'); + endMatch = endIdentifiers.includes(identifier) ? endMatch : null; + } + + if (startMatch === null || endMatch === null) { + if (startMatch === null && endMatch === null) { + throw new Error(`Identifier "${identifier}" not found in file "${filePath}"`); + } else if (startMatch === null) { + throw new Error(`Start line "docs:start:${identifier}" not found in file "${filePath}"`); + } else { + throw new Error(`End line "docs:end:${identifier}" not found in file "${filePath}"`); + } + } + + let lines = fileContent.split('\n'); + + // We only want to remove lines which actually fall within the bounds of our code snippet, so narrow down the list of lines that we actually want to remove. + linesToRemove = linesToRemove.filter((lineNum) => { + const removal_in_bounds = lineNum >= startLineNum && lineNum <= endLineNum; + return removal_in_bounds; + }); + + // Remove lines which contain `docs:` comments for unrelated identifiers: + lines = lines.filter((l, i) => { + return !linesToRemove.includes(i + 1); // lines are indexed from 1 + }); + + // Remove lines from the snippet which fall outside the `docs:start` and `docs:end` values. + lines = lines.filter((l, i) => { + return i + 1 > startLineNum && i + 1 < endLineNum - linesToRemove.length; // lines are indexed from 1 + }); + + // We have our code snippet! + let codeSnippet = lines.join('\n'); + + // The code snippet might contain some docusaurus highlighting comments for other identifiers. We should remove those. + codeSnippet = processHighlighting(codeSnippet, identifier); + + return [codeSnippet, startLineNum, endLineNum, tag]; +} + +/** + * Explaining this regex: + * + * E.g. `#include_code snippet_identifier /circuits/my_code.cpp cpp` + * + * #include_code\s+(\S+)\s+(\S+)\s+(\S+) + * - This is the main regex to match the above format. + * - \s+: one or more whitespace characters (space or tab) after `include_code` command. + * - (\S+): one or more non-whitespaced characters. Captures this as the first argument, which is a human-readable identifier for the code block. + * - etc. + * + * Lookaheads are needed to allow us to ignore commented-out lines: + * + * ^(?!}zZSqfN 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 literal 0 HcmV?d00001 diff --git a/noir/docs/static/img/how-tos/solidity_verifier_3.png b/noir/docs/static/img/how-tos/solidity_verifier_3.png new file mode 100644 index 0000000000000000000000000000000000000000..e30186e32d621e5b78e0a7393790a4a39ac91cab GIT binary patch literal 63153 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 literal 0 HcmV?d00001 diff --git a/noir/docs/static/img/how-tos/solidity_verifier_4.png b/noir/docs/static/img/how-tos/solidity_verifier_4.png new file mode 100644 index 0000000000000000000000000000000000000000..aacb906f2891f45b6697bdbaf7bc9ea70a28416a GIT binary patch literal 79069 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 literal 0 HcmV?d00001 diff --git a/noir/docs/static/img/logo.png b/noir/docs/static/img/logo.png new file mode 100644 index 0000000000000000000000000000000000000000..83d3a66f2b19c95b4a61503dfed64d9423fc6903 GIT binary patch literal 178782 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 z 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μlIktv+m3 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 z zj%d%EYYXSA;c$38^k2}F(0?wU-H1c~p}&Bk{%@e1T`lM=(|`Rc^`EcRjEZp?I8Oa< zL)oOkPDj_dv2HT`_Zv#R-r8t1hiSjO$Tyz4J_};ImBT!K{m&ZxKtV>*b#o2*Kshgw z=ce;!+d5AdWh-?JwZr@WtC_~@>bVphjlv0qpaafggbneGyP8Q|&M0p@O-F%-Y2e5| zB7+|{dCWSc-pkC_5%`8F_hX$$kcqEdw8qK_tD?0kv!JautH81Da0PDMLPb>hnju>A zLsLbxzKS9YR7ADTLT74~S1N?**6L-RMr-Vx69chvwyX?!6NU8|4>=%l&~8A3PW1|4 zJB7Hdcy>B7*6jFn)AE7hRAw1Rl@6NDchGubLM0VS0C{^*ycYFq6U*1EuDpiX zb1sHIWCvF$fvrau=fc?WFCXRRIxI)q+I@XP0jtdkyJnx6vU=p#ODF2EI4f)3Ws~uM zhPP>2cG%WP1GqDAeaGuv8tr=_w{bEna1m?6t-;qJHU*7A&!c^H0tP^=Tbj-DHPFxx z9{vopbZo>(=aog9TL&Poe{6UMI-xtz)^3BguCsxnf3`g|Ygo}qMFP$uInFlNgRL39 z1+z1>^Fv3!2vZmVrN~Yj%4r8Wo^6HWV(HG|WXy48eAm8i;FnxR-4)AYX;A*TpFYgt z6F#>U9;a+z-}b;zo?^B z|2>&5BJA{E7y55$FX&UgFS8CpO8=qKf`hJi!2s$;D}D712S2Y>|7~>FwwqlNU}po< z`VXwF9kB`h_Yp!1qXqG%fe%g3lT6z7(QFNXPRKp8e9X35eYZKL)BmuS^ZK9F`_Wg} zZ=6i}Gx|~QH@=;9IA@Y-Z7YmJ>M>SOtJD_a$l>hyL0a09sShJ^O8DlaC?Jb&{<2vDHAn9?~<_RS@VcR}_G_0DwD8%^5T0OX9oX zft$>I@7xdJZwvTQx@>e;g#`gpfVJQS7aIS}!PVZ7g*fF;lq3PP8*PghVMvbE?UXhk zfVUP+kyp?V=Q5BGLIBflG(BI-R=DRXpht8HKp!-~NW#z^COS={BucaATx1+Gnr@~N zVrP7l6uu6qWGWcv*U%iU4hp)H0Ro*2VUCVG1!H3`!RRpkzMP-`hK@B&yDwk$ThP(5 z!rVJLS$*``TiO^%Yewmz`A&0V)I?Hgnx1~g5hkU{NxznKYT_mHH#)Qf-pE7)GF5cU zr73K;6!XEK%Fwi&3a*1Z6$T^kaN1o&tx}t9Two9O^!Z-Q;K(+cIoKBfmGKyd7FTpsw~#DG@-8? zk@T~K#f$Pmi((DN9zKXtP&q`+9UhuXDP+nTSKycj@~g^+`Z1DuA+{ja?3OS3C%=1 z2ld)#)z%e8_J)pq6?*6Yl4-l7{s**%@H|`vuSx#{cwxPv{?`zHv-AO-(RS1$UM58Y zk8YM#9rCD|{*x|GO}>PKumk4WIG^Zn&Ls78w%}hYba%9x-zD?9%=2enIR-m-<<9EB zq0hjqc^}P>OoQAU)aEYu*M`xVo`O(nnXBd`gM@{qgociN1zI~cVfgsGlpH%tMTF4w z5tZre*^Y+uk~|NKzPY-r^s%0(p@D_WEjPnnE5sSn@1v1Jo+c+>L<9N&%w6=`X1XR^ ztD5Q~-2f~U2#M-P7+4j;bJIFc1~G`qSsL0{ih(B82UI=g)2lblV8jM6WC0+v2^YaG_8 zc$3nbEop2wU9{b6O*1uOuXiFtN4t5+5hlm9*0Rtz8C7&HMT&%goX|&Lr2l$z-PwD& zi6~mJm>9j+I9sjsSxw;#Os~Ijy~p>Csu()*Idk3J_hHD_d&)fH!?&U%`UB{mdmTpd zHbAq@G&99hmp!K`_>p@}eYMt=#%ZHpk8hT?RnkM5R{z5!^q-HpLwJzmI6ic%am)!f zaxQBa;WSauaF7(haP{bH8sqBGZ+1VEcHsSso-R`&gVu?BJS98ql6MK5qRTQzHgLLi zfP+qz3G3Co|4+aPQ-Mo+_k}Py{xn7wZ^1#)<;}}Y*CwgKgLjDpuE&*{-F66Y-ab#v0T4=4lM5L#_LYwcy7S^Zge^q&+P(L zqPu_inR}cgnwi<0&tkh=gg)e{ScWz$6(hVTjLbR zz@bl>io4Co{B&{Nrc%;H3(GqmmIz^r=B&wkop?^q#?c9F?>+~6t{xC)mkL-pR#^Z; zLwPXA{3^Bvt@xMk+%_W;(2=Z^x!TQSy_r4NiNda(K${NR9p$2RRDXqzf}v16J6qQ0 z@e$IgiI-s*N1_zAr$o-#f`uP(4qECYfGBAR9|Bx>06ovh*7@kT44or8H28oyxSt}8 z)k>$qr?cxsbE*QbQ}9NEI97|C$UVFN%pA8gx9>_*p5Pc=K?jxI@89=16WtuaXel|W z&5|8Gl2Q+iPC7-k?>=zpM%c1`fm80q#pp6(NO)b`506d47rwg_9@u#R_8uN7!GY@U zgr)PkVG-i7VfkzWXGvcd%p}>4qFLH|WW*t8r=mm;W6CQp6pCvR!WUjpp}u3aJh+3w$U-Le3~f9>_7F$QP}zzA%Fw8p zJ;8|h;7#c0tv6F?E$G}xnyj`1M4zS&m?wA==5w%eevI~i)66h+vzZ%ShTfAC-8JVu z=xnXa-zRh;*<8jzj-bZQj~>Ci*4!qJBM`4|pB_005e_k!F16qcyy2OdZS}&PnVC z(dne?RHD9z`lJmj=AhF#+w=wNSCI4B<(mg&|I1~H`wuqR{^u9paWZ`K&KK~$-wgC8 za2AC&W!17`eWo=1Z2C9=NTcoC34J?yC*4kw{dcdu13QdSQ|buX(erIF-{6%8lWQo5L$mT*;3j+JzLFASupH-BdN!Q{X@i5kEk z&L1Bu1ONfd5FcGavwWALBYv;YaoU$}NW!SN{ak2t%n>R!t?|^lo`g>Ze+;7zbqwj- zG-vKr=!AYa&&y5_C_4+?VXEZ17EYFCl?zI^o)(X$>W9qt?g#pB#YpUl=Id8xw%r414s%_PScdqZEa)RTiCDl*3=X7)%LzEk-t^)s7W2ESr=o5>7?EO|~j=OXEgTB?J zsKZLb;E8A?`p5|;x-I?mOCx?V#`Fhpo5@d3DLIqXmgR+G+Jny3$57_`iz%esp{t+_ zku7VWyi+t`a_mv_XPmKlA4fd$b&z%)S!9xRK%WcEK}uZFyfTV7Ke`R0%Q&NLUFYN9nFX8% zhz3bCnO#3y1{*X$^8@?&Oadr_Zy1sW85sUI|dsYqvTIJ`U-%DK!e3qgIDGMY&lnp#-2XBi-&D`N*q9dJ^;2DBc*)tI#;vt)2F;?W^vlmg z8y~(MrpBMa(DDVwSc&QGGNDWu%OpO)*JT8G0yu?^3FpUcHZt^l2ReG+>Y|~^6e?ty zVui)D1To#az zyB2>pxs5q0a1wKT`J#GmXUm=P_y`k_&P?(2*s&YX3H^%MiN0+4ADATpyy+;7qa%6f z(C1*+%l{E(^=ikH7(ETRT$`d(e+fH9*Mv?26 z%`wV7i<~r5DIXSjdjIeU~sb)hhmt6ozH-z!XZwzh7)R7xV^q>tQtq2*9}~Ab&f$w|5Yr$u1I`-Z%hN}+V~LiW8zuE4;o~oBA@Q_ z$~v|&7S|b?dSF_Z?k>W1)q$NU_~CXVYY+oF#pq`SG!QU}Hv~99L_b81oX0cp^!W1v z;Sg>Xoco#uusNRve-!JdEsa_WzS!J(ejlg^Wm@2b?c3UQ9!yT`#DLR%D6i)+JQZu0 zkdf?OGr2Sa97qnBQ#=;I-yqLS^uN}*1qZp0!Nf4)-hG~<$!6m4vTV@UA^NKqgXAz- z`%C@IdUQI^z+bwT?qx6_VTAgxTl)Pi_L#){TDc;o&r*<#AJF>I-+tc~_`qcw;jh2` z7<}vQ7eHNn<}Ew0+HEDx;1W4)qsRUcjq^#^xaW&D43&4^eA{Wu&1f(0yz_1;3hgnWv3-kQVHl<;Do%;-JKTKfL9^r?)I75Ko7ZhI z_YSoJpQPfsJYg3FvW%sRDUB_|?ZdO=l>=4K?rPCuE8bN17 z5s2I=%kL*g?*fXBQduZ5$UHfmLL7_x`uGz#SX^elA|oPzik8DXhmPn?(BAtFbh?&X zhWI9DxQ2L$SvAMY053BQe-})RK46wCv~`_pBF9_?g?KfAS=Qw%+4l>$A)Lq&V3}x5 z`uM>0*k27o$L#COnt#Cqi1`xof#VkPF8M5>hZd&2PtCvzn4F9NPMB0Ez=?VQ(m1%J zWqlkEfRa8L%ITn#p$<$A--3PW>1F^IwqaCPpxZ*9bX;#J45_wD@!a(F{a?pH+((=a zhbe5!P@Xx@R2jZyZL40r{%45$=&^sw2bie;E+~l48#v=7Ghvt`b zXDg-*n#GHESW^69S{R|>Y@pM)chCQ0)_pEp@gJe3HXl+OHJo89UiJ1~4xL@6!#*s# zXU`wQlBNF+syKx|IB*;O-6S>_k<)b5*=ykVN>U8?guPB=2NIiQ~`tx?oO^`IGd-bWzll=%7d{CHmce zWYjpY^nCx(VUw4h+dP!=WdVLo?1DL6@Eh+v!{L?x%Wx&)Q22N5J_bvtyN;tf;O@b;CqA#W+f9BA)6j+Z z8iH!AYoI#smw|sDI<@=Q!nXFrz~Gq7&L}uyTi* z0QmZC`J&D&)504u^jKbKwpP^E&v3T=n2x@vW@+)!J*RnA{_CjCht>b3a6#>EUbVI?yiGoi^uu-d$vKnMPUuDJo~`Qu2HlK?{v6l z#mYIj1x;CfFHY4wZ}6k3tG3>2OvfV5FlAiVO!z!wz3}Aus$u@fQ(@qJ<;hQ_}XP{@+ zg*lJiCItY_R4bsVmAbZ_9N)ZE6>_EnSiqEl{vREA5Dp#q6m<8T4|C?e%dFwd&$#n- zaG2qjWUAGMTu7sR9IR+qmkw5Y;QAv3ubql`cFL} zg^TY1hsX1EspOQBZ0|oZWE|EDPFZ2-Z-y&D`P4TbGfv{S?s^`s+rA0g{%~w`@XKv2H~#=kWkMJ_2F4%h zYzQX$zijy1*84V~FT~~3@f;36I?v;gJ7Uhzxx9J3a;>VzPc9G3*hwzod6^;JmOjz; zy!|z24#f03)57gMNatN3?SFORSy+hssXB!NNV&1+$r9R?)}>}sOto#JsLMHRs*qpD zH%^vf{0;{2N)>J8g@-j^uu0kLhy~u-bs>!Pf6+uo$uSLi5%NqWWfBUX#llYOoz-3O zoRcz*{*x?cleR2Rt>B%dAz&F9ded z4<0m>z;T153L9{6c*@-_oSV;fJ@d*@)7SBOXmDNASzYL!H-JTFt%osscpDlcj8;@S ziwGwhQF9^32a_ZBnuYY$&ND<_R|)?xYsM-BYkcfICrASuHs%agET3k$7lKODN!jE} z*ofGW6EB+kO3M;x>A5l&L@3TM%`dp7u=)yR$^1w?6C40iatE|9(#{Bjn+2f% z7ombu)NpXL4hu3RM)bf(#*PsjhUbV|%crE1>GCp$Y5)-x0D&{te@I5}eVZ!QF)9{N zQjF#b@YZW@#mHJ)8_b`bM_W%>v&g+QBV`vI00!G*@GIAx1<&p}W_b2DH#`D2-19vA z>{X}2m1ms*HfrYOMM?Qr{Kw;H*(SbXhJW)A$1Bd71(%<>4(l6%&wl%1co-eh{YM9j zS%HV3ijm5e_ERx((Z>qoSifxpw4%KsXS6Z?w23OxALXxMCtbx3JClkD;aWHT>z-R( z*WkLqFpo48o&gc8)O9uqr8(zX=DnzRd|;L5B_vDR}+0oOcJ##|@Cg z)7>lC){YL{tZewW;Aev05>^=bbHqRs#EUb+CGSOBssvA|-7v6`9|zd*Jk>tF#$_R@ zKE)R+9_(=xSP3#Gslvbn$@A36-O$?muCQRl?hvMn-bXaWNl(Yy!Y&a!ttT;7cNk|; z4&yZY9^~3 zP!&YD&2?diZT)MPXw%#tsH_IH20O0E835Msb#g3gi_oy1V7|P>5y>2q zd1Z~!2N0P-sC931-z8V9GqD{V*b!*ai926SvtMl)%n0K`8FbhNA8bHf$H>L_V>pu7 zn@1m77Nb*q4vy4TYOH(UmLpJmxb7rrDmN-*oeHRXPh*5 z)=3i5AvTYv<4ubx?$dctnPS^VPhb+6N7sS|nalA_ugm_bupz zUScASgqc4CpD8CZb~#MeX$T3I9UAa!X7n_3w%tI0+6f)TLkB*I&qiSJvOh3UO%t&% z9B3;aS|F7_%h*c#iP4>K*t z0nhC@ipFBGbcTusa_tEDfWuB%(59JHiij5FLAf6Dxs z=|6Nn>CYyux-bg5-3;PsAlI09M&@sq`61BFm`UK;({?h^hz%0uMO!d?8FpnwmZ zPpPvkT~8CfM0L9Y!9&e)W|7lSUbF;L?Kl|+gO8d%uY!Y?5WJ%!fv2z;QTs7Hy_I-F~kDAsj1EEO}57 z{8CfF97~Z|%Q9XbR0&jTpYr-oUc1;ldv5np7#N;%{a$Y4EBVxXW(#G3myb$L#EI0jta!!F}Hbyj3nVn-s%Q+vwJ6Y|FK{oybhOQqn zfK~fRXyhqMvDi6(`TCD~Y{(q5foOkl70v~I!Rb(HjPTM5;lm$4!a&jUpFL-|2d+uyGxzh|^4UtEw0_!j6 z;*|-DP|y+c)FmhAzU<$l(?(M>r=T%fIHw!ZsjO$VRo3FtcT5O7QeFDCQ ze-A&mA3pd;{|uL(wGKXX`RVyokl~~921f_Il;tIWq=PG&qp{-NU5XJ>`g;WH{O*0v znm;pzhJWO6Ry(#DC)2V6&>^jMZNukVpiTojw39CX$bDv}rrNnRclR?&kV@1CoU(QioPxm%vtAvi+`B0N!WysMP$1R|ke8XUHZn{o+S`5wh#wRY z*=cTH3*=J)Az;};^UV8yR|kL(tNQmeyF||C$k-&@`sgcg`5EgdvgLe{T2_S6Q~gmk zax19TwFr+6{Vt|DS!BlMGuM;P8Aeyo0nEZfltU$9d`o?^!J@<)1rT|wKA#F`g;YH%Kt5x%`HCHH==X)aM@;n4rpwTQy9ABU$>>BX2HWDS zO6XS%7mLdL-dXSqZ$B5_dBJA*{C6IL@7=%C@c)tL_F-FYflIcoPrz34-eq90#URe- z)y0*?dGqQe=I;ZSZH3Q&_d)pHeLIal)kkhIGYRB)R@&AVG*AQsrw6+(0*%ibwArt| z52xgY@<=KERd8ma(uz2@u7rGg)B1*<3N5(gxo57lB?y?qmNKkzK+iu0;Uyz5YC41! zG~T9uSj+nAgVjw|ahe3zmHr^Bx>RxwD`n0PGLH_}aBbuyGcgGAHo z=zr+?k=zN#07o;YbB);?*$1m}2J94!cDhZW%Ij_2lE`@d!oH)Xe_B}N*9l8tONGyL zLL&h9(k0F{PnTqa#cJq3E? zdqxmc_f`3Z8&gC^K6Ox!W-wAt9k0tVJbbPgU75!lSbk_G%iLat@WVQEnh(yHGFu+u z3jrUs$2FUL77h7A)YW!uzsEt3KW2DYsdPa1tSg{v);6f94pY`Fs38thS7CJcUg%r+ub~B}<7j=VRtZB^a!cR6KX&8`Fh2U2 zxnF|@;n&`CF|5I<5US5Opand)VM6<7l2VV@fn&q)*I&QeJSPXJZ}u!wGv4DcI6*s34zeiW8K<5BQLR!Ob0^ylT;W`1SW*4FB-G2a!iZ zE`mtL0DVA$zxQV@%bmp#R@rq~>O+V0*RYP0RxgGxQawk9;LrZyRud`xcOSVN>*@|L z^vWXm%p3#FlqqU?4KA9mO#MCZYu8>3@4sX#d>@_R??12;4jv> zCC5YIz`oLXL9R-ugBcCtF?x~}e=tIuKGxyiN(|zxh>+0ESV=yJiG{#sfa~OJGqN4l zHg8cd|5dIoz7EmxD5zi9KWmsY%S(?lzh2S6;hlpm${4R%liBAcYXhCop&JZOD_!RU z|9X@zXy(o(@csm(%m# zAPQ3Pa{OL1oCQ8Cn*v@ZfH(>*lrNKyZfa(Yqy2`wz=t_{eDkT5PwzCyI%Hi8Tb;&O zr+8o^bckQI?Ibf$gqGzvcxaV8(h#*50Dq5^;UGxSVe#mEf(_;*64^L%HW%RR%*kt( z7zflkogD2flLIhcU4DPh6EDN{cRXhLigQj{X>6K67PPQk!0niT1OaRSfadm`=XW11 zYGNA`J+XUs=B8D~5q%WtprZg6IENbHp1%3-ci`o=Q0Uaicd(&Wp<}r|LP`iJC5kEy ze*^Qc2Ix3T%6=%U=9z+M^cyeha#bLu4C7B{tdN!H{IWaUc)>Ei`bn*|*>mP^ryLHFHy5)+hHk7?j?J zj>G#eJww(b3@gOEPBH=D4?gt`c>IOEM!Ejz=iiP~!#y!Q%n;k>pZ->U%+Jrg^}ICg zO$hYz5i|nBFWkfs5o_B7f(T(5Bd2>7$}0G0kH5GVZbm0<$AizfEX1$(UAh%V$Q#mR z6rGcbrvD*9PyYNLzIQ+TZj0 zAL93+{$V2vgaef~3GGt_XKQGN395xd3V!ofz8vP^JOGgSW3FhaQ>F7vP%SIcx)z$z zm;|A3vMbjtpC5GbBlUpS$qW*Q9Sy)$Y#(eLmPZy}Er^k{XhB5rTn|HI=q59Ou5?|% z>XX7Jo2AJ+u15etw-gLaf_!2IpYyG_c5SOuU7ma|Xa>nYOP|Zna2K;k5VvEvp@pYJ z7sc8NVqQT^I|%wA-=i_S1FF401FTyVG64zM^L&fpTD;==rGfFd#_cWO;LKlr@XHtx z-2@5;i4frzv~S~>p^Ih^fqwNQgsH2&&DaJS43q8OjKB0O>O-PVOG7kzEN*Xp;2B{< z2p-wWx1WD92;xGRsdp)RnMz^fL-VrHn$k#sB@Tb^*WWPxz+1L%fM0#jHj^dGV7of) zq|fZY9NRb))i?}l{65IKu7VgVU^Nx_#~;8E!b>>ibRKBC2sosSOQgWC3~bCrue3-! zP9dxMOOSP5j4z7sg@;<1Zh1(jRK96&jZ&UZ8u}D0D1pcFn;%v-;9n{#Aq+Nf^$hBx z&-m4`jmOXl!l-hk(heQn7eE&}p$eT*X%VKOy@?YveTp->ZjSX14KxJD4t^4<)j2SG z-bZsBp?PbDX?)~4 z?|^%sdeP+nlV5o+tXe!zzz}di%g&{c6)k!r?K>7FcbcXW?{GlW$Y_HytJEvJvNqoKM$rs>HK7W(TdKQlAfA+2C zqYTaROZxzdnTCR977DuM!JY6=JMJ?yn`9hPbVl)yrs^3AK2c{k`2u!+2h19?O8Y5h z*^$V@`!6W(e|-C--zCS)0l;R~ux#)UI#tu@X^1~Jd+r%I>%-s8ozTwn%Fs<(+mM!K z*Jb#5JK&!|h<6D*HG@1&>xG&1T)=o=#uNT5dZ3!~E?JJh!$_gVX>ZkinUou&{~mr> z8%5neG|wO6Jsm$?k?}_m&!?d>>l&b?e3FOgXin`y{X1yrfrR6E((0vVb1B*S6IU%U z)8#4sxA>7Qx$mhLj6Hhq_A{Wf*c%WAv242Kp=V6LLWvyEtIslc9r&<8;cxs5h@4M`4R(BRSq6*mgu-egXp_3E(ho8RQWc|aRdxzm; zMP=L7sV9AksNKemQFZKgoJQ(}tnK7T5j=rD)keQ#9@M)o10V9$?7B$`461lhkRIWX z4M25t2Q)fxTBbS=AnD9NnvsDa(NX?sV7Z2arUoA%=9?H1C*Nzqze0S9J8v?Z8_=*^ zU~qAeQJH+&{8H-ZJ|DV!FTocbk-|4iuO^=k9yY7b=B-b>00T!p2W_2Op=b8nAf!n( zsn4d7!SCbW_ssQ)tCqs=eBer>+Ed|LMDud28Pfu?}0Dx zxF4g4IS=Vrr;lEB0i3aMbyUA|*khp&@_}=tZ@BwW_`7f4;b8xVkH0HFI*Q{x4Lri} z2iP&(`tZ~4@q4zPY5qhTF*spc{NRyi;7d2(=jbGdRJEghZCwN6aP1Ho9E3r2E|dyT zC?u|OZ&P5}u3XlcIs%Bp4a>{TBk~9r!Hg({mMpfnZeWPh@p|O@=!DKkqjj#x>5oMN z&^l)3JjT!Vfq5>SC|n(ZGjv_ zZB*lvn$@IDWv_e-|jwW}>0%LA7l( zdZ;sUKA*?GJ^27INYrm^TD8OsXvz8PYHJgF%mfM`^?w+H6*u1VDE#xy`4Nb}^MR{! z`=*2DXRLW?|6%y#S8s-`n^wbH&pib?+u9)NIDO zYbfJPfl^5)lj=-V!tkfzhG~4{P8jaL0XloOL+7kZL`0m^U#JbFI50YNGmH-2Y_6|7 z=T!Lc6&HZeproM=la$Pni3#}1E%(8VcRym@l2iK|@4p&OT)Bkh2N0H~z>#xEaEG-uT_Q=!DQM>50jqvV^ z&qQN2E2tNu@};(-`1dfv|4*O&nt@Hm@85s)T`+I0P2$Wt{Dp0wESumb-20!9h|dcP#nQBQMd0>*Qs}@RA#CoxLo^K zq|5OvVRr0${!HMQkkh8^n#d>c(%14a;{?a=y zg|y88_iSWr9Deh^e-1{*^Rq!Wp+ozBee{|{yUj%T0Ln-G(=ma+2biAWP>v6;GtvLY zII(<%9sT!z^^b-prcb`?g6(Sa#{Xp;QJcSIk~XZ2!BpE>f-r(nsi>&_mdQuax!D7g z9haKV+yR86vUKMm2xCl;oi#)-S|(7?RSQhDo(|>K29obQUPUKW7<%4ST3S^h_~8iO zJqNZ!Q>gXau&* zLkX7*_#Dy(%61MnMT`yIV*WaNwm}Cvq7sg+gUJkwideix2EGOpBl(W)cU^QgIyPs; zq`0>B*hYyF`Si=X;s0F!eROVeM`Gj3W$+8{xWqU%03}jh!rHPt)7fcA;Gfh29T1m* zH=UN2)>H65Y0fl@4mC@>W5&P31B39@AKVMKKazjZhrZZEZFbpNrMxUZFF3_L>qwj-Vb?_!iuNCj z-hwk<6bU^uUB4|G8URbHtxqLz))45(3}^~rPphq|WbqC0&@+TlVu1O(FbTYy7;*vA zzn9Bbn#hH)J33K+lN8krFcWJvqv&;kmujHXNoQkEy$G~4%_h?23r^XDHhrma7(;x% zbI-%@)m!gHXEb*z|M&0yqKQ$R1Y@q@p65C`-B{hz;O-k-Q)*><&Q!=LW}w?-78ef)N)*OueoN(ZOm!R=yy#h};cebXp$Y?lhMuZrjN_ty+x+D`V-Auv zOaRgJmaHakgzw(_5Pb2w-T`_4r5C}L^=k_T`qmGpexA8H2a;hJRd^`ie0~~OzaRaJUMM4ABuZ2+W+nIUv^F!@%r~Z zbgkJPZ|jsn*oY|E5f)?}85n~9TEHbImtZbmIA8G1wvTtDSw-yfaQ^_}_!R@|bc~w( z>U-V}vJCrMTfd96{4r@Y1nyO)#UbW$R zofE@D=}}Olx0>Zwy8VnMWsZZ;={kn#UisZ$^z)E4`i+sf?vjh){F6^i<86e#xXeQREF3U0 zNLdEoyyG6Y{`P$R(|HK*eV1NrK2@m#)WYj1r5Y|WKHv_Lj{HB)&L;X5Ru}DV4AEk; z#^EJV=6GrLKA@vauA{mQU9qeYzrRuNoZ9m37n}o^o_RV%{w=RN#Y>il9LGQX!k3H_ zNHBlr1MkV5QQaJ8Ph*ZJp?Y+15I*y*@4#s|7`gO}(<8cR>S^b$-EjK4wP}2cba-F@ zar&~s$t3!ea_cFO)fN@?=M7`lcaPpyyhmr%9_^Ik0edbsPnHuPh3X9#3PD~VQUGwME=YHdk zyWv}R-o0I|JK?{fBdWL2yDE;h+v+=E6rDZUnfjw6cxuClTT5dPjI?Y4UF-n0?GkmO z?wU_``=c)GbuPDsfa+Y3+UrjtZv8M-T?!MmweDR!%?>5pkvNF93hb^(t~KKN$~C3D z{_^)hgi-|t!Q$bV8^f+`oUbb6MP9GlnXlsw6a4-NI(z@$KYR(JnFHn- zbp-Ey%QoRy^Ii7J!cnr5wzN|j>R|pHVeUG3&=|(|AiQ%op9m2jB3}9zZu5(l%Mf_| zU%L4R@csKAgmX_m2|jYw<-8(edvUSOR&K88R79rHlL(f z+GyV2aCqLem_>9RbGQve%6JE|i2w0f#R;t;D&NVAIMGVD*&eOufXOhppyn1T`Z zosw^!77(^O+)x!=TL2LDxhYvp(GBidWeR5mC!Yc9fOfXG!&|nV4%_jMu3f<}DLOjo zA^6rEce!c!^~;yTN3Xsd=Gs}A8R$QxJXA+ttpC7y#7k>FgOtPN?_9;M0a+f=NFJx; znpq{wm}2&X82%g}rWfM=h`&2JGz9(H-G$#KPRf)`SRZF>Vy;ipB+%F5p8e0 zh(@3RV;JFtutQBqVNV@L3c3!%t!IG_#{t61FyVB?nIP$~Bg>z+UvF$&rR zEf^8aR3{AAP6g4CMv@Owz#_Y$ESH$3DBzmYn(c=PwaAOyCX>X?fCS1?JMR zob#SPhY|GcTGcM>bN*M7@)v zcf!Q*od);T?(-xrO>k~Z?K0&Wle^qAazcOYy6d2C)~u)=6_Ob}03jYX+;u11aA)p> zUVZL)aP@iTCge#}5!-|rBp=a8z?xy=V#sNDEik9AlmTXVWC87=a~;u}Y6q5hJN(;^ zd>EF^UjWV0baam4=={I`(?7xPgZUx^C2w3od#P5AbpHlyp<0`UY)?04|J0zu~7KflF0gP))4=4oKq0X zG%S#ZlAJt(YirbkPLF5A;~-Bc*_G@FR3;wD*B3+XfL?t1>2S{G&9Ddc{ge|b{)GCH4_Ou@10;258nf3l@v^UnU7xA$Kof*`pTqkjvbX=? zQ=ft{3<6xceH(1wvei|j8aOq-cI_YS06M-2|LJZ#ZJe)qWlpY8Fku|i1{yc!Q<`@b zn1osNN1(1|!EkjW^S?Y694U*a9e$>KI7 z6O|>>*W{x`+bj;gy#P$z+#G^gC=*(k4Hw}%EbBHI|x!j z$i1JPBdXB(Z`TKnvrN~NYV9YY6NgSxb(WwfHHMfjg+6uaeKam9{s~9XWpH?t)JGpe zNAxa?gl;o*B$`W>!RTjw{7I-!JTDwjN*ABL6|OwzJU8Nvji@!0^7+xh0l4?c$KgzL zM*3#WHqRb?{#p3+^|>QKFt0>MRE0*CjRt3ubkK!{rZ$uhQMUA5nx-5vaB>u_Sp^$C zi#ygBGf4wDcOWb;>i`H19FIy#cxlQ!O}h^rG#$y=Cv6rvqy>zzFYB6#pI`XSji%ix zy?fhsl%F$P(Gt=-n6zB`5S$5E1skmL!5X4!VfUB|1fcyBjnEB&riJjxd$QANYIwERJqM zGKLZLg~E|xaEpdZ&74U5?X-NN-NHx~dzU-3F)f^iuuhJTD*h>xDNjNef%ce&{kwZu zFd%x5jwfxlR*fU(Sw|Z>pj)=U#iwnBV?zV*sq4RP^y!+5x5KtAXZSvw={Y$}|G!WF zwYk3d^eu4Jx#tVKk5~V5l;z;0e3$?rmtv%BkE2+?=6C_b11NhP+u1}tERdAOI4%=H z*X&>W?Av$U0XN=#r-_RGn-6`&*ulI^wq31W{eSPk0^8_Y@^c!GK!5EtjArDE4{9(5 zvl>sqF>=TPL(>LFp|kN4j8>MRQ@DUBn-_dnAW)jJ%`&zU`qwyGdH@D1>vBShjVK*@ zA4W+J!m;Y90=$6385%w(1uF{4DG2Et)NrbMO39tDz~F>aB;iyFS40&kjx{8jU4wwH zJu3Vawo$vr2q^M~98gthL&xn@sCArfu4P^?8vfqXCsObz-DwCe3xfbn6f1>GR5=|O ztlEP75FtnoXk+XVm>PS?d`6BZGCG%^eI8tN+8Id%VwnEHv7tfu%P)V?7^yQ)+zju( z>}oiQ)1!a(#m^cC;*3q3;b$(tnxoSE8bJC*L4Bp{3jHW3@vnFt94@03_`BxK!!kp% z!%li33X-@d=??O^Ybb#QL7pVPd4)coh%q&hYj=+i{^7gdhWnn%KlysaIp@O_XP=+u zoz-=xylJ>0oqG=-gwK5QtInyUIxau!d|9{z)(;+f0B(Qyepj|Z$KueZjVrUD-jXj< zs@C^FwXxT1Oyr52R&q$Q>Kx<9a++K_C73gv0~e)~EC4i>Vcv|n85x7t@%w;2!8qA| zUd&HO;2p?P8Lq;KD*l-ZBU#cAisFZ9m{6gHC>OX&^ zJa~IiZ5+gCy|vvqOs|LjM?5WQ%OtE4n9X%gE>W^iINn?qe7OX^ zr3eM|l;LXYE^ztNFMJmE9y(z7|F5te=JenUQB`eMD_8&f9awN1IhGyzF!Wda*_>_q zsBs2|)N06`D(~5MWqZ&mIt&BV^-$Lxrgp!cic=I2WZ{@+D(-yEuZHey7rI@oFjiUQ zi319EPol&86bvAqb?Atd((*zF(Hg~`E5`L=;bf$|0X^}8YF23uSkkXb_y7ru92Vl* zi>XBqBd?EPTO5T-%Mz%yZN$dwfhwKX_68+Z{f{JZBdMqMz|`n{kWKAL=3TRN1zdOO zTg|j5mE(~VFK3?L-VkczBz*RUuNdPq2h0D`H6JkVKl#-!m}g7oErj3rnUBS78o~tt z{v`>XqG(AdY)ZK=0?7h#UI#+xPRgqJ-SWVy63;W%$lNMNGMhg(I9383DDtKC!xq%e zKsq>zHAGMsAA^h80PrxR&PZH*9RQI|6)^m5{_gr)zX!KJa=&?d89IrVo^^qX3~}~_ z&$tXk4hO@F9LaCo_C0vwg=fuElC{gQjx#o$?D=QeQBmTHB}Xxu^WA%HhX>3kO4cu?^1#i6cn(g14!5_3xy7K>$nV zvP0AGI{$JR^#$+4zF>BDel*)RZu>smR_Hao`{>p0HbIL}|D~DLNnH=0QQz^x-dEs- z-LJsbjVHtAwd+guAExR5o9Ta;SHKPGE1z)#c^w4)RDh!kps57w(@hc78Ps0{>nP~+ zS4H1Sjy~DI^G`ctyIQ{T_jX{;ZL{<)$S?*vtQNaE`t-9fqUPg-$?OP;+3*rnF_Lve zt&df~*B$kdO-G+BUk~g-d|F`y<_o^n5SuyrdFWSbVG7#QAXIF}rW@r2zF+wWrlc;B zO|hoX)58esu`pX<6gn~b*@9dk2ejI;6`jxgd^0`;8mfA8Q>B9{Rb(K2{*)kt=p5p; zI;sRl`dQdn*(VhJ)Dt^#>itD@K=(o_wWC3~aRJD3L@Tw{g-*oVDY2^ar1du+i3;|SYFoF z`MdI&3P&TW_j~u<4&T4;Hgivo=v&UX5I{$LZ`58%9$TcGQP1B$@XTYT4w@>YL^AfP z@BO%u<**HS9hAr+{qoJ8LkSY#btM$Y9+mBQCLy$@u%a}+h z{!)3;pA6bqgVvy4ou4Y{nNVien%X;N6VPF9pTrpstZSl`HoexSx07Y5X`Q)!(E3#X z4^3b*dXt^abjAXv#r^nGd7ZBd9}=mh{vuS+F`Q`G1g}dHe2@8BYwX54U&xoL6@9@4 z)E6i)(a$gK_!d0*!cLR-{8P?^x1N7#Q~i(ZR`E|t|G#wy4P5VYkIBjXg?D^#TK%`S z33NG$s)_9sDYW}hvQzePIaeesUvizc`F>z0P~!cBN5v%?lz;xlFQX%$f5D25Z>njG zD=JMc-!SyUQVf#R9aCr>=!9+q%EOsQiKgu*p%Wi9{5gUQkubmOgf-V$-%z$r>hXxc z40E+0P8#M2SgkOCHMYPcj-CzfuAt{omiYzk20l9q$Kexn)(%%K`KB4}+|~d{MvG1^ zD^B0xofCr{$xmSz(Ci!Mvxd{o?bsR>j0~vubx>_T2~@H6GjAUy91d`}O2`38kdoNc zJDtQbrKQD>Yg`q>RYBh1Sy(SQpxO8n`1iQEHrXp}x#48r{9V0dCA@q4+i>JN8yc?t z$mytf9C*9BdUJR8!JUu5S9W~UJYL+l5I%6ldtg>qFMQ!UUouYUY;;UN{`L>zGlZW3 zXdW$ezzVEsM6E6V0l3k0yDO7DS<#LegDpBV2<$J zESxw9X=Y^re7#x9dIe=!gCD z9=Hv@ckk_Ztzi4s3uAhUwsFra|7=xS7ui%^Klq}jt~&)*E?SB5_b5D!PU+a>xLFF- z$W#KaqN*Y~GOKeAeC%x>gom&k;dpGQAE!h2nZJ5<5lq%rVtu(oS_hiC?=`$sIC!U{ z_ULH0ipD`L&ajxJJ?J#JO^#5OHo{b8p`byuXVN@aIfKyLum_pJp>6Upgqra??_O8XeL6Gu5sy zK$~61eEkFc@P(Vc1pAKUj?Pu*T?Xf!d=~0hbNx@-2Y{6Rx3sjox;L#}52vGE*HMqB z)BltpTc>V1{nuK)17V92^td`DTvhbf8iJW2C~(d=ekmT6;B0Z>{fWRYf7jbR2R?D_ z&%=YyJPNm<9UBj<_Q3(Y8UjwDsMTS=Tq~ZX?#(2P1Ru@?7~>!Y?y#9q^pAe4xsDXkN+;9)vE#)XGz#N zx?nIQ!<@?HF^-@bhcVr4+0F3Qf<`g*?`Y0TNu1<;8^S04RWY7Rzt{y~y_4pb;t{IS zjYCV?t>1=D=!sCNb_Y&yy!c!(zH%YqJZ7Q|cD-%NUP|7`#A$JKhQ=eoBb|auP9^?n z>>yQbv3WzF8lw-R6Z%krQ)~X6ZOT`yf1{D_mFHXrr*AkFl8K3%#Lw9|xa+Zd;KsYY zYpzdQe=1yc?qvq+Z}eJHR!>ZU=u^Z1&hj(0TREy9ya5e&6DRo|;=+tE z&q?w^GH^>F2ivw@gwE(i37!U&DNeJbLu~G1`Z7M5541X3+XdeQzFf{6pYrz);b7{P z2XBM^p?-%(qk^3=I*#?qEEDN0OXQG}BWRpb8qnk5SE;(&{FtMHQOeqyrf>q%#Zw-Q zPNmLC((^iuwVW*Lkb>zeInLBHnlyv=(^mkm3a_Rh1A83F^XtHubj2X-kW%sU=$>8E zfTq2^6Xj(CM%UZY`PnnB$L=xflT->9$mOPa_%GWxmYBBx^m!~O7?Z+;GSV!+t+ z_h0s2ShaZNap}LaE5)@+3ixZ+e+c^(&KsCs|6N^k zDd-#o^2K^J`)CSB68&lp%u)kDPI8{T0)u)kO;{t5b=O!r1fTGHtb&t+OOD?-!U&n# zT{(gjkE3(l!Ens+QX%|8ARNt!&d=gN`N+ZTQT^zMVrFtS=?@((UEh!3dq8!ZicwE= zd@8M`Kx5z^a%Jm0zktUDxVSgbBEh>l!*edA!^7=14?P3G>2M_+hIHdg;A%Dlk z?_eEG(as=o62w_PDt~Vy5cayYrNdm;GuAc$%prH(SJ02-w4Ac;G&uQ$)8OTOFT<@5 z-3l-5d&!(5On>AGG%CH&P`&6Fbm1_!4YJ~_XG#sADS0BgJAY7tsPpvt(-3Tx-JtKR z#A`b!*F2x^;@WXql_IqxHHw6`nmR&F6B}AZWLv`2!qPDw74_?ggN&E%JrfH60Xx=m zu9UJqpEZUS;>LO9`BUU5c$FlCWcZcyY&b9oE4S35^nkP+DNL zwz+uJkacT`54gUJ(N*A>1@-4N=hv;-K4@*cVg^eT{d~to?=Z6q^`bvJaCATX-3^~J zORebrN3Q-bM&I%ot>e*ut!>-;%wh2M_0a!h5F2K$|9M_%tEC1pn9RmVTdjgVN4rQa z(U^fQ?F0DpNW3^-67+gbDOk328K@1*-oK;0GQMqJW08AV!Ri-Q55i#91Eadj6|n-- ziB9U=$}!m6SYjfT9LbdGi8gBTJe3HS7By)~O`+qfi~5r7&)`SziwC`xL1;&(@IXVJ zLr6@Trw(ECwGHW>RWMfpkM!vTe2CtW@cG88;B_xL(_IQ95ACO5NOS|x0t3^KlP`cC zy$hYMHRy<*D_WN#h2*FzIgD~mPiOhiAI+hhUxM)#jDx-*aC1}Nb?}Z|K2C>f$!dZlezw#5B(mrwX`Mb z59N6@%8`=9&}4adAVs_QCq=W5AVGN(t56o8#Qw%>m{T}MqQh-_@HuAgRJo10nr~e6 zNQY`GU;`hFXoqWx7Y=riPddU7^(O!@ZU3F8I&?H%HfLs2TDf=?yzRnwm}OZO2-)!O z-g6VqGK?F${+_p7hyBGII3E3fW&ca&*$Q-SE&f`D_2A8{|2h(4kd;PuGKEh`nuhj} zuhBCrlZiYS0Nt*(u6)~$Lyg(n#<$XdN=}m>I)0W z_1Nb4G9$&9rkkK{4GABe`kGl|Gx#o-!$C8vnW{;dh^WH%Q}n&&;#stl9#RZrO^UDD zE0fSsnLraxQ|t}=uWFofTDwXPa}D{}fevpg)mK5@w&Q5AWwk)tqy|Rks-!S25>;Ly zKwM7AOC7#vd4!Y71Tl18lXW{5`}R*-MNwu7!{sla(>Z{S_etoD7lWChZ-AMSZ(W}| zpY&WaK1@7r98sG#uSEy+^lfHTtD3Y)C@Xv#z$z&KB<~L%-3MQ|@w3K&oq6IpaLHL$ zKxqmscPTc~Xar9ko>EWa#tSKy(I2l1UNbxv05Vd<-2`g$O2Pxxk$~x_ZffdEg>^O} zim3%oR{tYPN)2IPh>J(5B?I^E<@u{pfJOY6(;wjN`Qj_F5jmd}O57-PHJ&wt7fKhLwhe<4+gNP? zkD(L#&D-LjRe) zf;X4`3%bSN06hPdl3y5XlKApJO9_N_G%N#Qf2D*@K*1YDo0SgM(I8~x1m$ak2(LrB z8zX~q+TAO3{@KmCuIBG~|6)f-VkF2BlQZ`^XbJ6669k21x<0C)OJvYt>&i6g-q!_P zsnMumbVad9Uxv}(N$9FhU{rNdWTiL*rH)k9*$BYsJ)k?=G2;RYIyV@V!r3QY5AhNr0)BSkWpv;iu)-T~{Oe zbk18#Aq-=naH<|AcLM(o@aDCr!TG0dHx4L)Z)8l6g_;8sa}c9>Nk-Vo<(fP&)DK_2 z`5%p=u>cL&_A{=^7qn{z%!AJ_O$0(Y8^n`04!+JNHiuaRUJ38WAnJJe2Zp@?0*|w( zp~9eA?DBu>8X#;wC}tbHab>d|tnPe2NkY(B2LnY(w?6=asVE&7XdTeP_~&?K+s;*` zgl=e7uL@|^LRu2LM1GAIrq1%=Kw}bsnea9n!o|rek1oZ5Sy49H_j^EQ>yAw zk59ZZD@PL@Q<~nVU-M^{anTp>aGKpjRsF1xX4KG;fRx76Vz5*Gk)0~qMnrg``y#8& zTSR6v-ztt#&PHhMz^4YbT%SW*<8kBd&+yS{m;&V@dYL6V8;I)C?jI{(b`4nOXvj3{ zB$%1tbVz3?i}I8s;dL`}bGtbmTJYBEOD@u~^YQjjyY|UpWWb%oEO0{Gv;EMKY6N){ZLXG-!r{?P{X9Qc=!4!#=>tW$x@jG)NKGvR_k0 z1>PyeJ_VUw8*WsoTVh=Tz=*CEDlHd$+6JMhV%-Q%Jw_NHaz1%~D}t2@q9htBm50IN z8#6U44-m9(E+3#Foj(P~!9s+4(CO@~PNDPZohACtb4CYBQ&f-6y;DU*GaChkkw6W_ zvr6&FJ^J(>=H=_HG94hB`GyPwLoS<^H0r?db_O|1&|G9q1f6{QI}g(VWRZfv$Z7vx zjXwrz>RHAgq_w&Tbn6<(*9;XTq7kSxaN2$9Wl;6KU>(r5)^^x}o#vKJ=R$k2Bheci zmPEumOsVI&u;gc8R-AKBJN5qgyMJps=2>Xu-f{58dftC@N z=DC%JZ*Rpy+t5%cXM|)_P(j`qTXUmI>I*<=Z>}8v40c$z*3&^vl?#nBU;m^;IrB^@ zA)iVh%xp5Z6P1>yERCzVYVe1OU;56PU+Yx8VQRuf?<>fBI+0KG2#ohC-Bw+l zbD^0_@PmA*I{3I0AT4e>fm8CmN)>31 zJCxSPB(|h%viNlR!=8!$7)oeY5n|h&IVn+a5Y?U-n*c;Yfip5 zpwRz2AK3wSJ-Wl>Icd%5aOJtznCmRqr;^ftlJOn)-2hL#@UW}n+*7u}GW^ZB{`~~$ zzq7f`+B(45OIJe1bfT}-scQh?j0AR{4h%7yl{AD?C~W^tp};oLb2Ke5lF{em>JGQa z;Q?>sRgsB1GbUfr>3ar*JSwq31{N=uzq9S4g7&jh0G38^jKm&j{b+3c=q|j?%$`icHniy8m-6?5=UHkH70y?hS*5Tj;v_kj{;(9Vj!!6Sx;efEZLpY`U z(q=yheI%znI|TFI~WC~c0Pbp7`-SD>EIe?fQHf7zgvV#{b8EJlx(G${1I%sHaE zXe7-dIpV%B2J~C4!N3&2X*J07*u4kKTsSgpfwPCW-B z8qk6<=E<6TKUs9D_-W;`DUQhLq~w_RtN+2oWAvpZ`Gpx`@w=p@n$CKam>Cs!mN~lGz0P!+ufED=mWRLd*RGGv*fZd5X~D=&R2&;gk1KJR zh-@9=z<*gi24+YnB<^R}OV~0xn7Ey;siw~W&1J#9p9!2qwxrD2z)OiN*y{kLE*!9R zb&}CxUVI4u9)vvyUlb^iZf#t%#as^!=TX+75g%2hs2816ou`3_EE7$| zfj*r}+0Y$kCWRc<3}@?1gjMH306iT$bb@Ei*jxs0y1cq1pPs>W@GKhq@l#neE2^9P zs*-n!`jXdWw3V2hrZB~CL5QU_qc1#W(4i0DpYJ26f4J=IYmE&Twi$~*^7NhPgx+j! z&)WPJI2->$`(390B!geM`EQIIl8nCf!Vh6xt0D)qJpIeof1d}y_rH7GYea$V4z;XNUkTUtD50o1NKj%(aY+_*;?LFhc;vcIi8J{6VM6m1L=S? z1YY^I)O;Nfu9HmB#z@MD9{(-!YF{pH4*uLrj1Gh;whbId2|I)p*DIGF+v6kZ$Sd=d zz5%ZX(fQnKoKVHqelEo*%jzYYV8iMyX8N7r&_Uj@-V_6f;+ikCw)o`hJmn&6np%A9 z**oEpXYMc^-}^566__`7X2HO;-lf43i84NhxKtqA45ZCd zDD+7&_7?4^W4W*}RepYs6Jq;J4lMk?%GFcA(rg%GVY=uJ=T+g%10S3eJXL&JT-NI7 zv9xK6Sui{@v?{?HP@!{KVLVlWW||7)A0@D~c~~IuDRXrFZeitkC3gL$BQEdW4OB?R<#7{){gO^J>@hYGtvfiSu z7)N>624m2Z?ZFl+mS|zWaOUPq;MDc`v4wtZc8=%MFFy#kVjzLiDVXPs6E6`?XfypE zMA`cKEuS`W*Mrmi@7nhBFbjX(LHGZn^q=GY>h<3)3xmQ5)lM>1LBEs(-nTe)#^7Kc z5zSPB=mf%w)`#ZTqm70{twhg@qb{X5im#B!dJ1|34qTo1f&Bm=W&w1O;qat};f4>j z)Kt#q{(6=xcmcPsMVq>Y8rnG3^IE|;p$(jNpK95VL)Cu$tR&bAEXO^vKTaXiDx2xF zy(%%EBBk<0>K`a3G=T*X3~pf4W`|P6e5GZ`eBq_iI4GP?GAx_doexXquQ86O#igDB zgfl)uF|~BCWz-j36rIXV8VB?a^GBT+B{DR%mM)xb_m0Nn`2Jq(EGdxzTeo5>;t_IO z1xoJm%13w=$Ccb5firZJ>de4|&Y&|=;0>HtSWYW2g{}lGkQ|Hzskz+>BkM!?#I$l9LV;z{* z&?p(+Rf_3p z!muyp&7BO^6%Fcj#oq&eW-W`Bbq7IKSFZrC<0@?dXM;Hb{62u^k;tbNUG z!EC++r7S{asM20Gimu+%oGjzt**-`G&n&+p93WmoWgANbPgs3&BOyrG#htB3sn`IB zS&*Q7{yZ~ZM(l!P=GrE*$6GdCk{`KLqW?#+y}xFug*&J{cG**4!fl|jkV`tBAO!wBN8v3x3938ynr zR(REr?)Y>9eZtt-c2EJX-UtCdC&1XnqRw%T>Iw{2)NYZS zdzG@hEj1h=9|PIt>w%N9%j@T_R6u)p4JFTxgU)FDmXZkuGZ^CrCPd1h%LCpvSX8*# z;dRh;MWs!ZeJ7T5C+ZjFASA>m#pKK;w@IRe=b{clGq_B7o9VcGiF~H=g?2)zd>Yw& zg(ZQb#pAhG9)M>tQo8T(^Sl)j8Kja!I*Gr-%uyxhl?J8s zd?qQ6so79Drm6mu{r=j`e{O6&Ev0(bTYjxv{~1$% zs{d~u{cqO)Yn5$REweA#p~w+s6_y3pT9Mluy4zv**d!dQHv&SMMdcXc5c6L6vfvxD z88{namZQ7kBA_EAeo0#^^tD!zjwzh3ufu}Y7Ff_$GoQp8t(^=)D;&b9){;VFv$^NI zSmJpE$1*`Fry!xiNbAhdb3$v0=fEMY=&ndmC(B32w6u`(nGKjfp6C;WYnPt^E3h+J zG;ghdC9Cy<60dh;+8j2#U|jz5$TQ!CC!fEKIiMYI;+hL!6FQ-7+yO1<85%tbH{J0` z(+Sb0q_=PTjr`M!HXnrT5IA=XpJ+EAr1IocMp5PhrjKH9-ZKJ4-5~g;#d|JzTH+sg zWqG>>_-Jl50C*%MofpzF#iQ~Z@sz={^@NT-#0DUlFR(ZHBiY_Uu6V?XH(1m*AnRvn zNoPOE%kNEg+c~poH&>4HnGxd9RWV%FM$wwBP)C^YbG&1=0~iLjy&Sg^_6x#@M<{cV zoO;+&o*jYam-KKyMo3AIhewaP95j$zv3N6_u;L6@ICqVtEhfUjbNa@31VJ0{$ROv_ zQF2u4xuZ&dBlJ6V%s8nOftzV+m{X){xwESCr2yvh*oCudzCKtm*RCOzs;f)*C0Xlr zGocVR6GX+B$OmOvCm_v<4l7N<=A9d#-mWNgKIuc;Y8YMkNl=N!1)I=jpSJOG)9%gm zpJe2wJM$pITy#ROIRBT-)csHO|Ho7RQ~f_Wu-jF&_u?IRw=EQBA=(A}`E4z*q`e)U z8XAGgEOVWnf&gDuP`r0^FvCzn3zYCf(8~-J{oC8p0)taik^p&WUTy5@MuU;TbHk(1 zR;l29{^g(1+KEtCy|K|#Nx3U%fb>Wu#5F9Ks6zr~&vhDyz?X>a6C_|xgDa()9)wzU z6k4)l`S{f)b`pbkXf5#o>upU0mC+nnVer1z)%oU_rkmpSCAxB>tSyvbEHMKF1qfK0_(~0QR z%p?RBH=3GcvcXRp;QW{1nH5)@-keX|210_Ckk1}$OpldIFNFo%e4$wIyqN(=krBf7 z{|E_H%?wq#4jB>g?~V@aF}iu+$P2#B4vX-2I{lxlkHWWZ z{r_ALA-CUi3+#WY|3B{f?~>Bwh@QVgYqc$JsRBrG9fCY@RZ+yG5+zKS~ zb% zbAc9o(ad-#*Q6my&Ys{DdsUBNce8nozjB76JXiGlVJJ6lg$F}kPZ%4;0p45 z$dnrBAiu@k;`ZY1`{1#i-!UBlr518PS6u`fR(l6DwGPX|J2JQz9>?-qPkM*hAdyx> zi4W(fW)kRB1VX1M<}dFTO6y7}W{QuFH>@0&n`Y%C-XLUwn=B6BmV;2fO6o|Z(m@kQ z*^7_`5RoEYzw#uwXR(7C%UCXlWyV<+qK!9Jxw0_{@{`4;RV$7-3K1W>2vSkk>jxdBc!XBor%9| zooz_bo6>*X1n1^-%}oD~4U$7T>YP;Tuo4Z&(21RCGES4SF_9vVZ`V*)Bwy*dh=>LHu&lx__5o`jXH%R|Q|6iv68mHz}&pA8rZX2lbN+|WU z;nYcIH#|E$0K-#LJ|B~cnJ9W0L7`EG#53VM1ueg{vkN-W8GUvrpFXZ)hK;l4z|hnr zyfQlM%5+q!NJ|G!%MZh%woX{wk$?GUq_PqwEAx^#9*>ZfrsH5vxZdMc!JFni9gotY zhcN2dj|OExIGO9FAUA^oqn2*0d4*E|~)Bd5o#wi;{TQ*uas`N|uQ!|XJQZl>#b=7Ea9$BSb*tx?> zsiE`FFJ|VX4du#Yy26?@6dY}wOPh*#t;tg*XHzwdB)3p4s{FXdylqZ^`55UOKnMH< zY(Goe(uJGh^i9{8GiYa&5U(emzX=|H_9ku89aOQI_iI>8ry5euT~&J)DSSCsw%Lky#tO+j2p+!pi8(ENAu@$!LETXl!Y02 z*_`|`8=4OS>4*UB+QL5HgU)6iiUNZNIhz#e9H>uuh?A5$5VTdR&IzT@&rPT~ z=$O}|2u#TT0)=GXn8H~U*D{aXk?7xL5ZCR?NShM*F2Q?SHXrFVFyc92zV}QKP{lqZ zP0rnt1t-I*C1=6xo+Y`nSCntCFY#Fe5Yl3=_#*mH4pO{w*F}3CKK2~k_t+PO1A4-W zi(viAi=6{14=+|l`FOE*&Y%iASeMYR6z81T#F?I!9!X0cWs)AH=I;f4%F7-dKF>ZTk<*|V{Y8CjnyHLu z(Q_T&i%k+bL7(+EbTebFiX&S15)7{+MS}oN!MTE729(qVt%LU|?6~g4 z?<`ykK*(E)X3c3!cblQl1q{Bu{bA`pJzF&YWb-$LQ{d#3?mhUJ`6HX~;+~_%NhQZ; zJ~}>27i@-kb56k7kHZn|*@ADb)Wutx+u^sJWAh&gyvn zOZ1O0XO%Yow5lv!;SVtp?|U&&LBI2GKtK*C@&C}#=itfbZiIvQYxAA4`2#S2&RT31lPj( zVe7xrH3Zvp_71$+79zwETiZPgI;t%g4ef_X+N7icq`-GNlD!@!(~jCt*ZW5CWjOMJ zJT|=_*A<)AHjH|n)H~NKBY1q^NP^J-tGaq%ZfpKY$zzobIGSwX+?FK@BQBw+_&1jN zkPAuSlQ8)`D;>U0@;Zh?UoUCXgU|{X{nW#vMsn;{EIu2J`N_~X=LB%7L+oZ-;P_fV z?(leJ=LS}oZ(!yF#JWH2pHVt=>{)pF`5W=~IfvT`E4IN}bV5x;R*)8oGl+YeMJgqi z8VzxmtUykAmA6z}4}Tu4U4pPx<+KST&5h@rj`GCP#QB-3(Ra$hx>8UOyW}w*j(1Kj z>|@1>=m=l2_KUTLAGC5;J;aGonlP~8l$6KRZx~2XAuSCnAGCdBhu8p}cN8kHYXLGo zAgB4OP-i4vl84Up2@?};UQmn^jyi{%Vt7L!QwqdV2%-NBr#D#t?O=~P2Ee3)oHBBB z_8of6{DqN3>RcD29h-+vQCEB4Pn`ab77prEeH6G=?z}l7D;LW7}v}?Y`Ne2elf?3ry z3%YBq@bus@F;)YiV;%=(w7V(;aj;Z6+*=Fd~7&EgPOa;S$3j`3Pq$c5!W5sEKR1Lcw^6 zloOw?C>lrSa~RsxpgHuJr%p7|WUv>_L!&)soyXC9=2bB?taMg9Zrpg9$O5NgrX@%(jQ&|LLO@R!SgzviP*6_xR+ z;_DaineK}6j6xmckMLHE-`*&KC>QX+BjNH$TNHe4si0AwZyWGe3#^$D40%zM(Ixm$ z_AI6MkdXOM{~?eOUOtf7gk1PpDv-Q3AVQr?%e=@J;agG`!hAE;f3E`qj}ra&Q>9iX zxjuq}RjqWP%{mn$rKiH_n1}4eEBo$+BmK{z19k-VqH{`rlzO|DqC>h47Nc|8H+!9+ z`$wn$oVRg1PpALwZGF(*J}=P4*p&-;H#&ZV>8ZrhCh8L$T0;LJq5s-_4s)t81~0sF z3p~H;2X0`nX#Od1^7?lhCzi_5f%C_s{|{i~j?!X;vu^cO?32MiZu;+Y|0wl;U}%qV zLR&Cmy?Na=uwu#CMf<&Z^dEplL`5V5Gq++zD=})=LF+Ct5;|7THvnP8xB>v~ z&;bPIn&nTyl8Qm!ysy9loPO`0n#?yR6$-Sdy%QGW-@%D7*p1Wmv9kskE$z!UC_xJx zg_Zg78jy;`Bv4_-MWQI9ys}3AyzJSlPc5`l5k{dMMzBfg^LTRlhKw;qJk2jo6ZzfG zAUY^clJiN}J893fgu_y5GmRzNmOnc1EbM&o+t{Hzo6}J1gw@L~MhA4e8HL-rIcLK9 z0aT#!AZ!4~Sp_tPbP2oxq%G6F&DgNbbpgqR;S8x1k0ih3eQ2{I7@Fy{qcPncV!=Br zC+bzq1af%Hc&Fe-#wL;qdc`SMwB&$2hA{^NLWyV|t$${py) zZ5ysn!6;65P5KAObYlsE}DltXJ zAS~e}^N-lbpH*6!DzB9v03!1*cOw$|bhsu?xpXsK;fw&^rMG*_<{V z#ZKedmu|pL!#kj>Fao-2={9pM8au*|ML=hoxSd`z1C-GBX48LhpL!PZSi_H0|EH!#;iWyd zpv}4^=Jij4{#W$&T4$|hqMmlDy1^>nH#Sn-YbFc%d!_vv0pOJe&Y3Rq=1SzX-8r%J z%dvJ2sGnZ&A@v;g;`T1%gwp1zv8-;&(0QQCI=apBfft4c!7>xHk3qIpXoFIIW+?3q z9MPRvv>}X-_Gsj&W&nI6mSd#$^o7iIs3uy1H!$UMojL?{Xp2W?4h5#a{G7Hp;z?<4 z?|PVvqtQ9N>&&Q>%X=edr{kw3R2YZini~ya^sONT<>Xq9Nd0@xbDL<2=<%JOGltBj zB@54ibt|vNDW^W?;Fx;2KntAYC@W#_10BhYlAB&rLDXF{7+S*NzhQDpRD8SFc6N1~ zs-li?Xm}5HgpUYXiMA}OKvFGJ^=OH&rOmBYDe@?RRSYX{DDJdOrfUew({(GkZMz_<13eJhXWWTptidE;r{_= zo%$OXDVyu{0sm<{@KgEwxfUO`E5fZdr(<-LMo9nN>Rok zGY#H=Nu+kH&ek4^(UccQ(by2{Tex`DmQj!vYPwp0QO~6q4JAA%T05CFjDxwZr`PfGIQ+()B7ZXS12V3T-kfKOA@tI-WgU zOJPCZY52P=ALSPS<+>Tm2Dz{}g<}3JUMdhR+%Bv}nW5&1oU>Q<+zhYm-QkpJLEovc zVaH{k}gW2%Ni~yp8z*DaRlyp=s%0d3r{paSiRz{ z`QV@_ztXtI&Naj|XkB_QU^A-~%1Bu~spTXo9C8>Xe2Z|A7D8^eny#!)&`9@Lfk)V8 zN?O4pjbppr|Ja|K4wfRKTTlKCH*IUv3$J_&p2t5*)Q%^v`>>CmGTK933-R^&v`0xp z)BMe!K>`QQyE2`6wfZ0CJRbc|%VYERKTi5@vEV#)4kXiy@-}Y{{70?-b+l_QqwU_s z>^Jp2o7aEX*l!EV(kbdoW6W~===47?m6G*xoKtwck^27t+NYz1PeyiU{p$CazB7%( zkFWlNs&B8&>)Q(SXBFt!z%wvBvKx*KJ`3X%-5J?s&gAT_wU|GftaD0#6IsJJhO~~< zMo=yKDH55PDMloEI%*6tvs+t@6Piao`^_)mwytNkS@XGXeAGChyqpb9QpD6cp@Ufu zOqo$u!;kz(5v;z3_1n>&O4zJoCz2f@*vHYR6Z|zL`Wi(mIh=|P1)eGn38pjKG6^5@ z(PuY0o3m%FGe)4hbGh3|Zt=1U$mMY*Yz-w>NE+=Gh`9C$LlExL8r2H8XrieQL|eK# z9Gw~l26y5#=--*no)Y1{dih&n&aCwYhFi!Eel(R*0v-*|H28b=T=RT(=&K7_0mLX& z44gEhfS_K`YL$H>+$QZY$R6++8zFG=78?6MGxAAB|KyGI<(6dy%?eg6e>+sGov`!8e>NQg zy%mjS1~R>Wc^pxK3e(Y zkrE|&@#Cuho~ELGo#Wx{FyCSQ<=DbU?ZN&awplj@K&c|KCjduL2pj zH@=+|@J?IXHsww~ys`REw>9ifmM?w_{%tqc_UOW&B>mU4S?Ramw|j@C1MQ(;yzr*- z5z+AIZu8eaM9%49bX0diS>g^VMJs9DDLor)OnC+>xxQ33D7$74=?ey%X3a4V%(~UA zVQ{5s`b8a`@bc)8nX2a^?D&plPntEi*q~H}J@t8|_{SQY0ys@s2BU^k>*R1&(aEH1 z`U*vUIB+8gf`pAay4gMJ(4kolvwJr{d+S^f++m8T5~yhgFTa-tKfhqPRKPXOWI+H- zKzI3aK_gyIO^v`yd%gv`_kG{dbi%6ZVDbEOUDaOhHRGpL9?M5TqxS2qlq;`T8O=}& zA|BibicUuh^7GehBo~e-ykI%p#!xTvU9A!y!C8lU@y3mim3$v`vfzw})J=<*pgmzw zI8L08xnTy^aisL0(<^xr@K-niDL5XW2g&%L7tM^stzmD27m{Na}=)V*9*FpbvpaVb7`oDYs&G6FhZ<@ZJ67@$L*M1Zo z(G3ax59Q@G=)Y}?AEN#PhoPlo7`CB*55QaYjnsb{5STq4Kf(H6sp#$MH{OdecXN(t zs5X#bWL~+6b`9+`&gld>renKdWOSDk+_FT@sdZHA#VFel(9;nZ9pG)JyBj^^b>WzgH zV%D%V>tG^IbiRLUr4AI;Y&Fhi)|4OQpmkw%lcF%@w`;kHcE;Ryn1L95{`+_{gr8{H z1bo-=X&rUk$3sW-4q?=@F?jT;KZTKEfW3C*b+BaNr9j_`w*CLaLce|7pF@Ev}b`k&^y4xDtG zKbJer#sYkX2mqSY`&0ctKK%#X*sgx{T8zCH2igaaB9GlTCw_O0fJyo4^${3Br*sq@ z)G-w1QF2bRQD@+%OXQ@ILrTB)njBX8PCOmgRKt4vCnwD2n}*KJd7*TC(|&YBg@+HG zZ5_25Z0wnXuAzc`Q#h^9zuC(gsl|*!au_wdt`r|iqd<8D9ZJ$Kx;KueeWzyD<5Fiu zj%F)HHqCX%a_B;*lfJsa2fktA{BD*{QVj8+Sj!wjq!`@Ltj;u9n1e&lz*Eotr5TXq zQP7`-d2>#8b$glsxSobB=hxlLBzdZAxiXrY;cl-X!RJ(YTZf(jOhLPjO^s&n9BhDi zKzf`h#F5{Z(dyEx(0@yn;%0vuewNYa`Qf{8f9sz?NgWopASvLFL|=(jh49%1WhC*9T^UbuK5RYAR(+Ncp?RL?%H=f?8Z)-QYSjy8`u78Xlw0r z^+iflhIfE_m@lM7zLV1*c3q5|E(k13vd4f@+#LTI7-1x$cLhFSW9T%@h<;8hF97Mb zYQ~>3Iz+hyADWt`8R~!2_6~68>d>ryKh2roR~fENWRCvXwdd=wdtWZc)Y-4W!Bk7N zOJt?lpJ!h93z#S(M-)9>y7(PN&y##)b-elYza&~djsE+`EE1yO4bgx5_zlzlCh3Qz z|1`5i_Vmz^JCb;uwBdK52m9mKQ~xI>55uv6hhhHQ^YT7x8vS#1gn;?;A2a=rmkiEe z_Gmgg%JRJ`{b%VXQU8_3srg@i*TEg=c5ibf3IGIa=L`P}jujCCN+g^;IQpO4yT$yr zw)h>S!5~TC88Sl$5=WQgs00(}oMz~pl7mW$*uZFN1D({#sUxNky{AM9^F}3d9IJ)n zNJoLtb+yuAe(4=KSHy=_^DcKfHGp8cz2!zQ8bOBwq}XH27bSV6n1nmoE#7lOs36G4 z1%lO4*lCBb8?iGv3g5|;PiyYl{SVGy&g(lLr!3zOEw!#-s;DTTV_5;guKNF(f)XJ9_^_I1%z>D!L;wP&QBp-yWNXbP)Ze0C=4(gyz%L`WkpY7TA zb^ObnQL+IW*Zr=Wg16<#22q~FNAGa?RxW!V%$dEVwCDy z^xs5lAb#hazgYjnXRfZ}(*NSPp*N}iYlvZ`^j{(k5wA(khK8RpgP*anS4^g=IXdZm zM&=w%Fa!N}`t|zgf9^C%I(drq-}55u|DlQgH*K5OS^s5SzKxSI0U+(ML?3=A`mY)9 zir24J|6fi2|7!F<_7yAJ)i1sC;0~?THos2B6ETTOK+~=LgAc**@YBXY}micm&=g|pX)$4G*<9vd$mL`@Rj)%*}o(otB4VUU`+aXLEA`8JAI%p@slSEsGxQFc_pR1r|iHmKM zQkjg+eaZX$I@I@+{`)&6c<1u~Xf4+e|FHER0y&NISPB4iQZG0H{yla6FYWrYk!>1; zu3qtTFuV73_uh)d5p+cN?*F>efq8S!H#*QtvjIQh`mco-20^2h(Wuh2{EO6oS;tTH z|IMTSVV5NlmSRzg*H85yW}^Sqs@kr8?rn#6;O#aK!h5o=j)L%^k*A@5;6WJ1-+Xii z=!pIt^v%5xWJp`8y%J3FZcH5PCH_7IU*g5uLV;@^DK%O~D3o2B<=ZD3xfl5l9r-To+y6D= z_)|x{X63IzcemVa9AB%bJ!QLtpf91vo;wWhR9zPTJg-!JE} zeX0O~oGCWsP0<}nXoO3^Aik3{^~}je!1qw5%JiSZ5qa#WKhTXgME|wq6Tlm%|1{8g z_J#jy2IRA6ZGqLReic&sPxc35PTnCm zSrZ0tR{i%n`jfB!((1i&`p@|BM(O`+>i>O-SKI%C(}6%jgZfkY`QxDfGwuHcE~+HL z4>Qw${AauR*jo?pP_#yr!HJGe0M4)+G#O|5>+aep0$LKu*mL$mT;$RW-9q_6mp2Bd zkSlt!ejVVgn@{Ja>frc@GAejp-~n3l4;+S2)UVvVRaa*K1kOIcFqt}mTV%bMa@faO zvyi)zhRoUmfuELE7xD$ujcf#7+4U(H7<|w??ZBw`sujPUFVj#;;w*$VQcK1gUsq|n z2t1wb5;%%>0LA#NStLb8Z6V}`)yHL3@R{LLn#yE{wN-4encfM5AGR}RLW$mpC=&=H z6w<1qqSNa?B=U>4)e zr_xOSi|T?4_R~_8}MMTus=?Rqb&ly~>UMDJGvZ}(DW+9CyQt+>+ zBOKXq;gLp#>tVW_J*7T`?~zu(*Knksb0ZekA?mUuGF&|q*{{zej0L-7_{Mt4-`jhL z>JM-Uj8wSB(!NjFW7m#@Ll0s{`+wZ>hk1RM!{S9B6nS{#a~eVe>PBd+Owi>}(u^-n@DdyVR%IP&tE`V|I-2%(D)i3;? zzVh(TVu!a1-snUd))O1mE0qdl*{j&WUIMQ)dnMm7e_VChP4-!nx>MJ$0=F`}2<=jo zo7EG%E3Ko9#sJR2!(V}e2fywfFIaF5PCdS>6o$h)fg^usUZ;2&qCxo6%Pht%)dr5< zTM!VZ)ExxrvIbEPs0l@{bx~Z&8pvr&d!aH z!tKqj|4q|R^&ft!|1+yUdi>M=|6jcRLst3Q|3A2#hS;o literal 0 HcmV?d00001 diff --git a/noir/docs/static/img/solidity_verifier_ex.png b/noir/docs/static/img/solidity_verifier_ex.png new file mode 100644 index 0000000000000000000000000000000000000000..e0f381ff424d8abeefc19e82f20678e922252095 GIT binary patch literal 1177990 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

N5GqT#q0#Jxgbb94*&(h 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%0D?P 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

<@<2TYLQJN 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

3&~vS!p{Udb9JJZ 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 z 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

?;n!4VD~ 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%0D!dBtRaWANRJ_#0zy1PUY0Jq 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�JE>7<_kz^OM^ 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 O&s7T>^Ey-a=>G=~KfMS5 literal 0 HcmV?d00001 diff --git a/noir/docs/versioned_docs/version-v0.17.0/getting_started/01_hello_world.md b/noir/docs/versioned_docs/version-v0.17.0/getting_started/01_hello_world.md index 8b4416beba1..d4daae605a2 100644 --- a/noir/docs/versioned_docs/version-v0.17.0/getting_started/01_hello_world.md +++ b/noir/docs/versioned_docs/version-v0.17.0/getting_started/01_hello_world.md @@ -144,4 +144,4 @@ corresponding error instead. Congratulations, you have now created and verified a proof for your very first Noir program! -In the [next section](breakdown), we will go into more detail on each step performed. +In the [next section](./02_breakdown.md), we will go into more detail on each step performed. diff --git a/noir/docs/versioned_docs/version-v0.17.0/language_concepts/06_generics.md b/noir/docs/versioned_docs/version-v0.17.0/language_concepts/06_generics.md index 9fb4177c2a8..36c2b593fcd 100644 --- a/noir/docs/versioned_docs/version-v0.17.0/language_concepts/06_generics.md +++ b/noir/docs/versioned_docs/version-v0.17.0/language_concepts/06_generics.md @@ -110,4 +110,4 @@ fn main() { ``` You can see an example of generics in the tests -[here](https://github.com/noir-lang/noir/blob/master/tooling/nargo_cli/tests/execution_success/generics/src/main.nr). +[here](https://github.com/noir-lang/noir/blob/master/test_programs/execution_success/generics/src/main.nr). diff --git a/noir/docs/versioned_docs/version-v0.17.0/noir_js/getting_started/01_tiny_noir_app.md b/noir/docs/versioned_docs/version-v0.17.0/noir_js/getting_started/01_tiny_noir_app.md index 6955f7a1e64..142cd02b94c 100644 --- a/noir/docs/versioned_docs/version-v0.17.0/noir_js/getting_started/01_tiny_noir_app.md +++ b/noir/docs/versioned_docs/version-v0.17.0/noir_js/getting_started/01_tiny_noir_app.md @@ -251,6 +251,6 @@ By saving, your app will refresh and here's our complete Tiny Noir App! ## Further Reading -You can see how noirjs is used in a full stack Next.js hardhat application in the [noir-starter repo here](https://github.com/noir-lang/noir-starter/tree/main/next-hardhat). The example shows how to calculate a proof in the browser and verify it with a deployed Solidity verifier contract from noirjs. +You can see how noirjs is used in a full stack Next.js hardhat application in the [noir-starter repo here](https://github.com/noir-lang/noir-starter/tree/main/vite-hardhat). The example shows how to calculate a proof in the browser and verify it with a deployed Solidity verifier contract from noirjs. You should also check out the more advanced examples in the [noir-examples repo](https://github.com/noir-lang/noir-examples), where you'll find reference usage for some cool apps. diff --git a/noir/docs/versioned_docs/version-v0.17.0/standard_library/cryptographic_primitives/04_ec_primitives.md b/noir/docs/versioned_docs/version-v0.17.0/standard_library/cryptographic_primitives/04_ec_primitives.md index 6e6b19b6861..d3af3cf7c3b 100644 --- a/noir/docs/versioned_docs/version-v0.17.0/standard_library/cryptographic_primitives/04_ec_primitives.md +++ b/noir/docs/versioned_docs/version-v0.17.0/standard_library/cryptographic_primitives/04_ec_primitives.md @@ -71,7 +71,7 @@ does indeed lie on `c` by calling `c.contains(p1)`. ## Examples The -[ec_baby_jubjub test](https://github.com/noir-lang/noir/blob/master/crates/nargo_cli/tests/test_data/ec_baby_jubjub/src/main.nr) +[ec_baby_jubjub test](https://github.com/noir-lang/noir/blob/master/test_programs/compile_success_empty/ec_baby_jubjub/src/main.nr) illustrates all of the above primitives on various forms of the Baby Jubjub curve. A couple of more interesting examples in Noir would be: diff --git a/noir/docs/versioned_docs/version-v0.19.0/getting_started/01_hello_world.md b/noir/docs/versioned_docs/version-v0.19.0/getting_started/01_hello_world.md index 8b4416beba1..d4daae605a2 100644 --- a/noir/docs/versioned_docs/version-v0.19.0/getting_started/01_hello_world.md +++ b/noir/docs/versioned_docs/version-v0.19.0/getting_started/01_hello_world.md @@ -144,4 +144,4 @@ corresponding error instead. Congratulations, you have now created and verified a proof for your very first Noir program! -In the [next section](breakdown), we will go into more detail on each step performed. +In the [next section](./02_breakdown.md), we will go into more detail on each step performed. diff --git a/noir/docs/versioned_docs/version-v0.19.0/noir_js/getting_started/01_tiny_noir_app.md b/noir/docs/versioned_docs/version-v0.19.0/noir_js/getting_started/01_tiny_noir_app.md index c51ed61de52..795baa59d59 100644 --- a/noir/docs/versioned_docs/version-v0.19.0/noir_js/getting_started/01_tiny_noir_app.md +++ b/noir/docs/versioned_docs/version-v0.19.0/noir_js/getting_started/01_tiny_noir_app.md @@ -255,6 +255,6 @@ You can find the complete app code for this guide [here](https://github.com/noir ## Further Reading -You can see how noirjs is used in a full stack Next.js hardhat application in the [noir-starter repo here](https://github.com/noir-lang/noir-starter/tree/main/next-hardhat). The example shows how to calculate a proof in the browser and verify it with a deployed Solidity verifier contract from noirjs. +You can see how noirjs is used in a full stack Next.js hardhat application in the [noir-starter repo here](https://github.com/noir-lang/noir-starter/tree/main/vite-hardhat). The example shows how to calculate a proof in the browser and verify it with a deployed Solidity verifier contract from noirjs. You should also check out the more advanced examples in the [noir-examples repo](https://github.com/noir-lang/noir-examples), where you'll find reference usage for some cool apps. diff --git a/noir/docs/versioned_docs/version-v0.19.0/standard_library/cryptographic_primitives/04_ec_primitives.md b/noir/docs/versioned_docs/version-v0.19.0/standard_library/cryptographic_primitives/04_ec_primitives.md index 6e6b19b6861..d3af3cf7c3b 100644 --- a/noir/docs/versioned_docs/version-v0.19.0/standard_library/cryptographic_primitives/04_ec_primitives.md +++ b/noir/docs/versioned_docs/version-v0.19.0/standard_library/cryptographic_primitives/04_ec_primitives.md @@ -71,7 +71,7 @@ does indeed lie on `c` by calling `c.contains(p1)`. ## Examples The -[ec_baby_jubjub test](https://github.com/noir-lang/noir/blob/master/crates/nargo_cli/tests/test_data/ec_baby_jubjub/src/main.nr) +[ec_baby_jubjub test](https://github.com/noir-lang/noir/blob/master/test_programs/compile_success_empty/ec_baby_jubjub/src/main.nr) illustrates all of the above primitives on various forms of the Baby Jubjub curve. A couple of more interesting examples in Noir would be: diff --git a/noir/docs/versioned_docs/version-v0.19.1/getting_started/01_hello_world.md b/noir/docs/versioned_docs/version-v0.19.1/getting_started/01_hello_world.md index 8b4416beba1..d4daae605a2 100644 --- a/noir/docs/versioned_docs/version-v0.19.1/getting_started/01_hello_world.md +++ b/noir/docs/versioned_docs/version-v0.19.1/getting_started/01_hello_world.md @@ -144,4 +144,4 @@ corresponding error instead. Congratulations, you have now created and verified a proof for your very first Noir program! -In the [next section](breakdown), we will go into more detail on each step performed. +In the [next section](./02_breakdown.md), we will go into more detail on each step performed. diff --git a/noir/docs/versioned_docs/version-v0.19.1/language_concepts/06_generics.md b/noir/docs/versioned_docs/version-v0.19.1/language_concepts/06_generics.md index 9fb4177c2a8..36c2b593fcd 100644 --- a/noir/docs/versioned_docs/version-v0.19.1/language_concepts/06_generics.md +++ b/noir/docs/versioned_docs/version-v0.19.1/language_concepts/06_generics.md @@ -110,4 +110,4 @@ fn main() { ``` You can see an example of generics in the tests -[here](https://github.com/noir-lang/noir/blob/master/tooling/nargo_cli/tests/execution_success/generics/src/main.nr). +[here](https://github.com/noir-lang/noir/blob/master/test_programs/execution_success/generics/src/main.nr). diff --git a/noir/docs/versioned_docs/version-v0.19.1/noir_js/getting_started/01_tiny_noir_app.md b/noir/docs/versioned_docs/version-v0.19.1/noir_js/getting_started/01_tiny_noir_app.md index c51ed61de52..795baa59d59 100644 --- a/noir/docs/versioned_docs/version-v0.19.1/noir_js/getting_started/01_tiny_noir_app.md +++ b/noir/docs/versioned_docs/version-v0.19.1/noir_js/getting_started/01_tiny_noir_app.md @@ -255,6 +255,6 @@ You can find the complete app code for this guide [here](https://github.com/noir ## Further Reading -You can see how noirjs is used in a full stack Next.js hardhat application in the [noir-starter repo here](https://github.com/noir-lang/noir-starter/tree/main/next-hardhat). The example shows how to calculate a proof in the browser and verify it with a deployed Solidity verifier contract from noirjs. +You can see how noirjs is used in a full stack Next.js hardhat application in the [noir-starter repo here](https://github.com/noir-lang/noir-starter/tree/main/vite-hardhat). The example shows how to calculate a proof in the browser and verify it with a deployed Solidity verifier contract from noirjs. You should also check out the more advanced examples in the [noir-examples repo](https://github.com/noir-lang/noir-examples), where you'll find reference usage for some cool apps. diff --git a/noir/docs/versioned_docs/version-v0.19.1/standard_library/cryptographic_primitives/04_ec_primitives.md b/noir/docs/versioned_docs/version-v0.19.1/standard_library/cryptographic_primitives/04_ec_primitives.md index 6e6b19b6861..d3af3cf7c3b 100644 --- a/noir/docs/versioned_docs/version-v0.19.1/standard_library/cryptographic_primitives/04_ec_primitives.md +++ b/noir/docs/versioned_docs/version-v0.19.1/standard_library/cryptographic_primitives/04_ec_primitives.md @@ -71,7 +71,7 @@ does indeed lie on `c` by calling `c.contains(p1)`. ## Examples The -[ec_baby_jubjub test](https://github.com/noir-lang/noir/blob/master/crates/nargo_cli/tests/test_data/ec_baby_jubjub/src/main.nr) +[ec_baby_jubjub test](https://github.com/noir-lang/noir/blob/master/test_programs/compile_success_empty/ec_baby_jubjub/src/main.nr) illustrates all of the above primitives on various forms of the Baby Jubjub curve. A couple of more interesting examples in Noir would be: diff --git a/noir/docs/versioned_docs/version-v0.19.2/getting_started/01_hello_world.md b/noir/docs/versioned_docs/version-v0.19.2/getting_started/01_hello_world.md index 8b4416beba1..d4daae605a2 100644 --- a/noir/docs/versioned_docs/version-v0.19.2/getting_started/01_hello_world.md +++ b/noir/docs/versioned_docs/version-v0.19.2/getting_started/01_hello_world.md @@ -144,4 +144,4 @@ corresponding error instead. Congratulations, you have now created and verified a proof for your very first Noir program! -In the [next section](breakdown), we will go into more detail on each step performed. +In the [next section](./02_breakdown.md), we will go into more detail on each step performed. diff --git a/noir/docs/versioned_docs/version-v0.19.2/language_concepts/06_generics.md b/noir/docs/versioned_docs/version-v0.19.2/language_concepts/06_generics.md index 9fb4177c2a8..36c2b593fcd 100644 --- a/noir/docs/versioned_docs/version-v0.19.2/language_concepts/06_generics.md +++ b/noir/docs/versioned_docs/version-v0.19.2/language_concepts/06_generics.md @@ -110,4 +110,4 @@ fn main() { ``` You can see an example of generics in the tests -[here](https://github.com/noir-lang/noir/blob/master/tooling/nargo_cli/tests/execution_success/generics/src/main.nr). +[here](https://github.com/noir-lang/noir/blob/master/test_programs/execution_success/generics/src/main.nr). diff --git a/noir/docs/versioned_docs/version-v0.19.2/noir_js/getting_started/01_tiny_noir_app.md b/noir/docs/versioned_docs/version-v0.19.2/noir_js/getting_started/01_tiny_noir_app.md index c51ed61de52..795baa59d59 100644 --- a/noir/docs/versioned_docs/version-v0.19.2/noir_js/getting_started/01_tiny_noir_app.md +++ b/noir/docs/versioned_docs/version-v0.19.2/noir_js/getting_started/01_tiny_noir_app.md @@ -255,6 +255,6 @@ You can find the complete app code for this guide [here](https://github.com/noir ## Further Reading -You can see how noirjs is used in a full stack Next.js hardhat application in the [noir-starter repo here](https://github.com/noir-lang/noir-starter/tree/main/next-hardhat). The example shows how to calculate a proof in the browser and verify it with a deployed Solidity verifier contract from noirjs. +You can see how noirjs is used in a full stack Next.js hardhat application in the [noir-starter repo here](https://github.com/noir-lang/noir-starter/tree/main/vite-hardhat). The example shows how to calculate a proof in the browser and verify it with a deployed Solidity verifier contract from noirjs. You should also check out the more advanced examples in the [noir-examples repo](https://github.com/noir-lang/noir-examples), where you'll find reference usage for some cool apps. diff --git a/noir/docs/versioned_docs/version-v0.19.2/standard_library/cryptographic_primitives/04_ec_primitives.md b/noir/docs/versioned_docs/version-v0.19.2/standard_library/cryptographic_primitives/04_ec_primitives.md index 6e6b19b6861..d3af3cf7c3b 100644 --- a/noir/docs/versioned_docs/version-v0.19.2/standard_library/cryptographic_primitives/04_ec_primitives.md +++ b/noir/docs/versioned_docs/version-v0.19.2/standard_library/cryptographic_primitives/04_ec_primitives.md @@ -71,7 +71,7 @@ does indeed lie on `c` by calling `c.contains(p1)`. ## Examples The -[ec_baby_jubjub test](https://github.com/noir-lang/noir/blob/master/crates/nargo_cli/tests/test_data/ec_baby_jubjub/src/main.nr) +[ec_baby_jubjub test](https://github.com/noir-lang/noir/blob/master/test_programs/compile_success_empty/ec_baby_jubjub/src/main.nr) illustrates all of the above primitives on various forms of the Baby Jubjub curve. A couple of more interesting examples in Noir would be: diff --git a/noir/docs/versioned_docs/version-v0.19.3/getting_started/01_hello_world.md b/noir/docs/versioned_docs/version-v0.19.3/getting_started/01_hello_world.md index 8b4416beba1..d4daae605a2 100644 --- a/noir/docs/versioned_docs/version-v0.19.3/getting_started/01_hello_world.md +++ b/noir/docs/versioned_docs/version-v0.19.3/getting_started/01_hello_world.md @@ -144,4 +144,4 @@ corresponding error instead. Congratulations, you have now created and verified a proof for your very first Noir program! -In the [next section](breakdown), we will go into more detail on each step performed. +In the [next section](./02_breakdown.md), we will go into more detail on each step performed. diff --git a/noir/docs/versioned_docs/version-v0.19.3/language_concepts/06_generics.md b/noir/docs/versioned_docs/version-v0.19.3/language_concepts/06_generics.md index 9fb4177c2a8..36c2b593fcd 100644 --- a/noir/docs/versioned_docs/version-v0.19.3/language_concepts/06_generics.md +++ b/noir/docs/versioned_docs/version-v0.19.3/language_concepts/06_generics.md @@ -110,4 +110,4 @@ fn main() { ``` You can see an example of generics in the tests -[here](https://github.com/noir-lang/noir/blob/master/tooling/nargo_cli/tests/execution_success/generics/src/main.nr). +[here](https://github.com/noir-lang/noir/blob/master/test_programs/execution_success/generics/src/main.nr). diff --git a/noir/docs/versioned_docs/version-v0.19.3/noir_js/getting_started/01_tiny_noir_app.md b/noir/docs/versioned_docs/version-v0.19.3/noir_js/getting_started/01_tiny_noir_app.md index c51ed61de52..795baa59d59 100644 --- a/noir/docs/versioned_docs/version-v0.19.3/noir_js/getting_started/01_tiny_noir_app.md +++ b/noir/docs/versioned_docs/version-v0.19.3/noir_js/getting_started/01_tiny_noir_app.md @@ -255,6 +255,6 @@ You can find the complete app code for this guide [here](https://github.com/noir ## Further Reading -You can see how noirjs is used in a full stack Next.js hardhat application in the [noir-starter repo here](https://github.com/noir-lang/noir-starter/tree/main/next-hardhat). The example shows how to calculate a proof in the browser and verify it with a deployed Solidity verifier contract from noirjs. +You can see how noirjs is used in a full stack Next.js hardhat application in the [noir-starter repo here](https://github.com/noir-lang/noir-starter/tree/main/vite-hardhat). The example shows how to calculate a proof in the browser and verify it with a deployed Solidity verifier contract from noirjs. You should also check out the more advanced examples in the [noir-examples repo](https://github.com/noir-lang/noir-examples), where you'll find reference usage for some cool apps. diff --git a/noir/docs/versioned_docs/version-v0.19.3/standard_library/cryptographic_primitives/04_ec_primitives.md b/noir/docs/versioned_docs/version-v0.19.3/standard_library/cryptographic_primitives/04_ec_primitives.md index 6e6b19b6861..d3af3cf7c3b 100644 --- a/noir/docs/versioned_docs/version-v0.19.3/standard_library/cryptographic_primitives/04_ec_primitives.md +++ b/noir/docs/versioned_docs/version-v0.19.3/standard_library/cryptographic_primitives/04_ec_primitives.md @@ -71,7 +71,7 @@ does indeed lie on `c` by calling `c.contains(p1)`. ## Examples The -[ec_baby_jubjub test](https://github.com/noir-lang/noir/blob/master/crates/nargo_cli/tests/test_data/ec_baby_jubjub/src/main.nr) +[ec_baby_jubjub test](https://github.com/noir-lang/noir/blob/master/test_programs/compile_success_empty/ec_baby_jubjub/src/main.nr) illustrates all of the above primitives on various forms of the Baby Jubjub curve. A couple of more interesting examples in Noir would be: diff --git a/noir/docs/versioned_docs/version-v0.19.4/getting_started/01_hello_world.md b/noir/docs/versioned_docs/version-v0.19.4/getting_started/01_hello_world.md index 8b4416beba1..d4daae605a2 100644 --- a/noir/docs/versioned_docs/version-v0.19.4/getting_started/01_hello_world.md +++ b/noir/docs/versioned_docs/version-v0.19.4/getting_started/01_hello_world.md @@ -144,4 +144,4 @@ corresponding error instead. Congratulations, you have now created and verified a proof for your very first Noir program! -In the [next section](breakdown), we will go into more detail on each step performed. +In the [next section](./02_breakdown.md), we will go into more detail on each step performed. diff --git a/noir/docs/versioned_docs/version-v0.19.4/language_concepts/06_generics.md b/noir/docs/versioned_docs/version-v0.19.4/language_concepts/06_generics.md index 9fb4177c2a8..36c2b593fcd 100644 --- a/noir/docs/versioned_docs/version-v0.19.4/language_concepts/06_generics.md +++ b/noir/docs/versioned_docs/version-v0.19.4/language_concepts/06_generics.md @@ -110,4 +110,4 @@ fn main() { ``` You can see an example of generics in the tests -[here](https://github.com/noir-lang/noir/blob/master/tooling/nargo_cli/tests/execution_success/generics/src/main.nr). +[here](https://github.com/noir-lang/noir/blob/master/test_programs/execution_success/generics/src/main.nr). diff --git a/noir/docs/versioned_docs/version-v0.19.4/noir_js/getting_started/01_tiny_noir_app.md b/noir/docs/versioned_docs/version-v0.19.4/noir_js/getting_started/01_tiny_noir_app.md index c51ed61de52..795baa59d59 100644 --- a/noir/docs/versioned_docs/version-v0.19.4/noir_js/getting_started/01_tiny_noir_app.md +++ b/noir/docs/versioned_docs/version-v0.19.4/noir_js/getting_started/01_tiny_noir_app.md @@ -255,6 +255,6 @@ You can find the complete app code for this guide [here](https://github.com/noir ## Further Reading -You can see how noirjs is used in a full stack Next.js hardhat application in the [noir-starter repo here](https://github.com/noir-lang/noir-starter/tree/main/next-hardhat). The example shows how to calculate a proof in the browser and verify it with a deployed Solidity verifier contract from noirjs. +You can see how noirjs is used in a full stack Next.js hardhat application in the [noir-starter repo here](https://github.com/noir-lang/noir-starter/tree/main/vite-hardhat). The example shows how to calculate a proof in the browser and verify it with a deployed Solidity verifier contract from noirjs. You should also check out the more advanced examples in the [noir-examples repo](https://github.com/noir-lang/noir-examples), where you'll find reference usage for some cool apps. diff --git a/noir/docs/versioned_docs/version-v0.19.4/standard_library/cryptographic_primitives/04_ec_primitives.md b/noir/docs/versioned_docs/version-v0.19.4/standard_library/cryptographic_primitives/04_ec_primitives.md index 6e6b19b6861..d3af3cf7c3b 100644 --- a/noir/docs/versioned_docs/version-v0.19.4/standard_library/cryptographic_primitives/04_ec_primitives.md +++ b/noir/docs/versioned_docs/version-v0.19.4/standard_library/cryptographic_primitives/04_ec_primitives.md @@ -71,7 +71,7 @@ does indeed lie on `c` by calling `c.contains(p1)`. ## Examples The -[ec_baby_jubjub test](https://github.com/noir-lang/noir/blob/master/crates/nargo_cli/tests/test_data/ec_baby_jubjub/src/main.nr) +[ec_baby_jubjub test](https://github.com/noir-lang/noir/blob/master/test_programs/compile_success_empty/ec_baby_jubjub/src/main.nr) illustrates all of the above primitives on various forms of the Baby Jubjub curve. A couple of more interesting examples in Noir would be: diff --git a/noir/docs/versioned_docs/version-v0.22.0/how_to/how-to-recursion.md b/noir/docs/versioned_docs/version-v0.22.0/how_to/how-to-recursion.md index db9ad0e99f8..2f7be604401 100644 --- a/noir/docs/versioned_docs/version-v0.22.0/how_to/how-to-recursion.md +++ b/noir/docs/versioned_docs/version-v0.22.0/how_to/how-to-recursion.md @@ -47,7 +47,7 @@ In a standard recursive app, you're also dealing with at least two circuits. For - `main`: a circuit of type `assert(x != y)` - `recursive`: a circuit that verifies `main` -For a full example on how recursive proofs work, please refer to the [noir-examples](https://github.com/noir/noir-examples) repository. We will *not* be using it as a reference for this guide. +For a full example on how recursive proofs work, please refer to the [noir-examples](https://github.com/noir-lang/noir-examples) repository. We will *not* be using it as a reference for this guide. ## Step 1: Setup diff --git a/noir/docs/versioned_docs/version-v0.22.0/noir/standard_library/cryptographic_primitives/ec_primitives.md b/noir/docs/versioned_docs/version-v0.22.0/noir/standard_library/cryptographic_primitives/ec_primitives.md index 8d573adb3be..d2b42d67b7c 100644 --- a/noir/docs/versioned_docs/version-v0.22.0/noir/standard_library/cryptographic_primitives/ec_primitives.md +++ b/noir/docs/versioned_docs/version-v0.22.0/noir/standard_library/cryptographic_primitives/ec_primitives.md @@ -72,7 +72,7 @@ does indeed lie on `c` by calling `c.contains(p1)`. ## Examples The -[ec_baby_jubjub test](https://github.com/noir-lang/noir/blob/master/crates/nargo_cli/tests/test_data/ec_baby_jubjub/src/main.nr) +[ec_baby_jubjub test](https://github.com/noir-lang/noir/blob/master/test_programs/compile_success_empty/ec_baby_jubjub/src/main.nr) illustrates all of the above primitives on various forms of the Baby Jubjub curve. A couple of more interesting examples in Noir would be: diff --git a/noir/docs/versioned_docs/version-v0.22.0/noir/syntax/generics.md b/noir/docs/versioned_docs/version-v0.22.0/noir/syntax/generics.md index 443ca2b45a5..d59e4c5d7c6 100644 --- a/noir/docs/versioned_docs/version-v0.22.0/noir/syntax/generics.md +++ b/noir/docs/versioned_docs/version-v0.22.0/noir/syntax/generics.md @@ -111,4 +111,4 @@ fn main() { ``` You can see an example of generics in the tests -[here](https://github.com/noir-lang/noir/blob/master/tooling/nargo_cli/tests/execution_success/generics/src/main.nr). +[here](https://github.com/noir-lang/noir/blob/master/test_programs/execution_success/generics/src/main.nr). diff --git a/noir/docs/versioned_docs/version-v0.22.0/tutorials/noirjs_app.md b/noir/docs/versioned_docs/version-v0.22.0/tutorials/noirjs_app.md index 302ee4aeade..0763b6224c9 100644 --- a/noir/docs/versioned_docs/version-v0.22.0/tutorials/noirjs_app.md +++ b/noir/docs/versioned_docs/version-v0.22.0/tutorials/noirjs_app.md @@ -256,6 +256,6 @@ You can find the complete app code for this guide [here](https://github.com/noir ## Further Reading -You can see how noirjs is used in a full stack Next.js hardhat application in the [noir-starter repo here](https://github.com/noir-lang/noir-starter/tree/main/next-hardhat). The example shows how to calculate a proof in the browser and verify it with a deployed Solidity verifier contract from noirjs. +You can see how noirjs is used in a full stack Next.js hardhat application in the [noir-starter repo here](https://github.com/noir-lang/noir-starter/tree/main/vite-hardhat). The example shows how to calculate a proof in the browser and verify it with a deployed Solidity verifier contract from noirjs. You should also check out the more advanced examples in the [noir-examples repo](https://github.com/noir-lang/noir-examples), where you'll find reference usage for some cool apps. diff --git a/noir/docs/versioned_docs/version-v0.23.0/explainers/explainer-oracle.md b/noir/docs/versioned_docs/version-v0.23.0/explainers/explainer-oracle.md new file mode 100644 index 00000000000..b84ca5dd986 --- /dev/null +++ b/noir/docs/versioned_docs/version-v0.23.0/explainers/explainer-oracle.md @@ -0,0 +1,57 @@ +--- +title: Oracles +description: This guide provides an in-depth understanding of how Oracles work in Noir programming. Learn how to use outside calculations in your programs, constrain oracles, and understand their uses and limitations. +keywords: + - Noir Programming + - Oracles + - JSON-RPC + - Foreign Call Handlers + - Constrained Functions + - Blockchain Programming +sidebar_position: 1 +--- + +If you've seen "The Matrix" you may recall "The Oracle" as Gloria Foster smoking cigarettes and baking cookies. While she appears to "know things", she is actually providing a calculation of a pre-determined future. Noir Oracles are similar, in a way. They don't calculate the future (yet), but they allow you to use outside calculations in your programs. + +![matrix oracle prediction](@site/static/img/memes/matrix_oracle.jpeg) + +A Noir program is usually self-contained. You can pass certain inputs to it, and it will generate a deterministic output for those inputs. But what if you wanted to defer some calculation to an outside process or source? + +Oracles are functions that provide this feature. + +## Use cases + +An example usage for Oracles is proving something on-chain. For example, proving that the ETH-USDC quote was below a certain target at a certain block time. Or even making more complex proofs like proving the ownership of an NFT as an anonymous login method. + +Another interesting use case is to defer expensive calculations to be made outside of the Noir program, and then constraining the result; similar to the use of [unconstrained functions](../noir/concepts//unconstrained.md). + +In short, anything that can be constrained in a Noir program but needs to be fetched from an external source is a great candidate to be used in oracles. + +## Constraining oracles + +Just like in The Matrix, Oracles are powerful. But with great power, comes great responsibility. Just because you're using them in a Noir program doesn't mean they're true. Noir has no superpowers. If you want to prove that Portugal won the Euro Cup 2016, you're still relying on potentially untrusted information. + +To give a concrete example, Alice wants to login to the [NounsDAO](https://nouns.wtf/) forum with her username "noir_nouner" by proving she owns a noun without revealing her ethereum address. Her Noir program could have a oracle call like this: + +```rust +#[oracle(getNoun)] +unconstrained fn get_noun(address: Field) -> Field +``` + +This oracle could naively resolve with the number of Nouns she possesses. However, it is useless as a trusted source, as the oracle could resolve to anything Alice wants. In order to make this oracle call actually useful, Alice would need to constrain the response from the oracle, by proving her address and the noun count belongs to the state tree of the contract. + +In short, **Oracles don't prove anything. Your Noir program does.** + +:::danger + +If you don't constrain the return of your oracle, you could be clearly opening an attack vector on your Noir program. Make double-triple sure that the return of an oracle call is constrained! + +::: + +## How to use Oracles + +On CLI, Nargo resolves oracles by making JSON RPC calls, which means it would require an RPC node to be running. + +In JavaScript, NoirJS accepts and resolves arbitrary call handlers (that is, not limited to JSON) as long as they matches the expected types the developer defines. Refer to [Foreign Call Handler](../reference/NoirJS/noir_js/type-aliases/ForeignCallHandler.md) to learn more about NoirJS's call handling. + +If you want to build using oracles, follow through to the [oracle guide](../how_to/how-to-oracles.md) for a simple example on how to do that. diff --git a/noir/docs/versioned_docs/version-v0.23.0/explainers/explainer-recursion.md b/noir/docs/versioned_docs/version-v0.23.0/explainers/explainer-recursion.md new file mode 100644 index 00000000000..18846176ca7 --- /dev/null +++ b/noir/docs/versioned_docs/version-v0.23.0/explainers/explainer-recursion.md @@ -0,0 +1,176 @@ +--- +title: Recursive proofs +description: Explore the concept of recursive proofs in Zero-Knowledge programming. Understand how recursion works in Noir, a language for writing smart contracts on the EVM blockchain. Learn through practical examples like Alice and Bob's guessing game, Charlie's recursive merkle tree, and Daniel's reusable components. Discover how to use recursive proofs to optimize computational resources and improve efficiency. + +keywords: + [ + "Recursive Proofs", + "Zero-Knowledge Programming", + "Noir", + "EVM Blockchain", + "Smart Contracts", + "Recursion in Noir", + "Alice and Bob Guessing Game", + "Recursive Merkle Tree", + "Reusable Components", + "Optimizing Computational Resources", + "Improving Efficiency", + "Verification Key", + "Aggregation", + "Recursive zkSNARK schemes", + "PLONK", + "Proving and Verification Keys" + ] +sidebar_position: 1 +pagination_next: how_to/how-to-recursion +--- + +In programming, we tend to think of recursion as something calling itself. A classic example would be the calculation of the factorial of a number: + +```js +function factorial(n) { + if (n === 0 || n === 1) { + return 1; + } else { + return n * factorial(n - 1); + } +} +``` + +In this case, while `n` is not `1`, this function will keep calling itself until it hits the base case, bubbling up the result on the call stack: + +```md + Is `n` 1? <--------- + /\ / + / \ n = n -1 + / \ / + Yes No -------- +``` + +In Zero-Knowledge, recursion has some similarities. + +It is not a Noir function calling itself, but a proof being used as an input to another circuit. In short, you verify one proof *inside* another proof, returning the proof that both proofs are valid. + +This means that, given enough computational resources, you can prove the correctness of any arbitrary number of proofs in a single proof. This could be useful to design state channels (for which a common example would be [Bitcoin's Lightning Network](https://en.wikipedia.org/wiki/Lightning_Network)), to save on gas costs by settling one proof on-chain, or simply to make business logic less dependent on a consensus mechanism. + +## Examples + +Let us look at some of these examples + +### Alice and Bob - Guessing game + +Alice and Bob are friends, and they like guessing games. They want to play a guessing game online, but for that, they need a trusted third-party that knows both of their secrets and finishes the game once someone wins. + +So, they use zero-knowledge proofs. Alice tries to guess Bob's number, and Bob will generate a ZK proof stating whether she succeeded or failed. + +This ZK proof can go on a smart contract, revealing the winner and even giving prizes. However, this means every turn needs to be verified on-chain. This incurs some cost and waiting time that may simply make the game too expensive or time-consuming to be worth it. + +As a solution, Alice proposes the following: "what if Bob generates his proof, and instead of sending it on-chain, I verify it *within* my own proof before playing my own turn?". + +She can then generate a proof that she verified his proof, and so on. + +```md + Did you fail? <-------------------------- + / \ / + / \ n = n -1 + / \ / + Yes No / + | | / + | | / + | You win / + | / + | / +Generate proof of that / + + / + my own guess ---------------- +``` + +### Charlie - Recursive merkle tree + +Charlie is a concerned citizen, and wants to be sure his vote in an election is accounted for. He votes with a ZK proof, but he has no way of knowing that his ZK proof was included in the total vote count! + +If the vote collector puts all of the votes into a [Merkle tree](https://en.wikipedia.org/wiki/Merkle_tree), everyone can prove the verification of two proofs within one proof, as such: + +```md + abcd + __________|______________ + | | + ab cd + _____|_____ ______|______ + | | | | + alice bob charlie daniel +``` + +Doing this recursively allows us to arrive on a final proof `abcd` which if true, verifies the correctness of all the votes. + +### Daniel - Reusable components + +Daniel has a big circuit and a big headache. A part of his circuit is a setup phase that finishes with some assertions that need to be made. But that section alone takes most of the proving time, and is largely independent of the rest of the circuit. + +He might find it more efficient to generate a proof for that setup phase separately, and verify that proof recursively in the actual business logic section of his circuit. This will allow for parallelization of both proofs, which results in a considerable speedup. + +## What params do I need + +As you can see in the [recursion reference](noir/standard_library/recursion.md), a simple recursive proof requires: + +- The proof to verify +- The Verification Key of the circuit that generated the proof +- A hash of this verification key, as it's needed for some backends +- The public inputs for the proof + +:::info + +Recursive zkSNARK schemes do not necessarily "verify a proof" in the sense that you expect a true or false to be spit out by the verifier. Rather an aggregation object is built over the public inputs. + +So, taking the example of Alice and Bob and their guessing game: + +- Alice makes her guess. Her proof is *not* recursive: it doesn't verify any proof within it! It's just a standard `assert(x != y)` circuit +- Bob verifies Alice's proof and makes his own guess. In this circuit, he doesn't exactly *prove* the verification of Alice's proof. Instead, he *aggregates* his proof to Alice's proof. The actual verification is done when the full proof is verified, for example when using `nargo verify` or through the verifier smart contract. + +We can imagine recursive proofs a [relay race](https://en.wikipedia.org/wiki/Relay_race). The first runner doesn't have to receive the baton from anyone else, as he/she already starts with it. But when his/her turn is over, the next runner needs to receive it, run a bit more, and pass it along. Even though every runner could theoretically verify the baton mid-run (why not? 🏃🔍), only at the end of the race does the referee verify that the whole race is valid. + +::: + +## Some architecture + +As with everything in computer science, there's no one-size-fits all. But there are some patterns that could help understanding and implementing them. To give three examples: + +### Adding some logic to a proof verification + +This would be an approach for something like our guessing game, where proofs are sent back and forth and are verified by each opponent. This circuit would be divided in two sections: + +- A `recursive verification` section, which would be just the call to `std::verify_proof`, and that would be skipped on the first move (since there's no proof to verify) +- A `guessing` section, which is basically the logic part where the actual guessing happens + +In such a situation, and assuming Alice is first, she would skip the first part and try to guess Bob's number. Bob would then verify her proof on the first section of his run, and try to guess Alice's number on the second part, and so on. + +### Aggregating proofs + +In some one-way interaction situations, recursion would allow for aggregation of simple proofs that don't need to be immediately verified on-chain or elsewhere. + +To give a practical example, a barman wouldn't need to verify a "proof-of-age" on-chain every time he serves alcohol to a customer. Instead, the architecture would comprise two circuits: + +- A `main`, non-recursive circuit with some logic +- A `recursive` circuit meant to verify two proofs in one proof + +The customer's proofs would be intermediate, and made on their phones, and the barman could just verify them locally. He would then aggregate them into a final proof sent on-chain (or elsewhere) at the end of the day. + +### Recursively verifying different circuits + +Nothing prevents you from verifying different circuits in a recursive proof, for example: + +- A `circuit1` circuit +- A `circuit2` circuit +- A `recursive` circuit + +In this example, a regulator could verify that taxes were paid for a specific purchase by aggregating both a `payer` circuit (proving that a purchase was made and taxes were paid), and a `receipt` circuit (proving that the payment was received) + +## How fast is it + +At the time of writing, verifying recursive proofs is surprisingly fast. This is because most of the time is spent on generating the verification key that will be used to generate the next proof. So you are able to cache the verification key and reuse it later. + +Currently, Noir JS packages don't expose the functionality of loading proving and verification keys, but that feature exists in the underlying `bb.js` package. + +## How can I try it + +Learn more about using recursion in Nargo and NoirJS in the [how-to guide](../how_to/how-to-recursion.md) and see a full example in [noir-examples](https://github.com/noir-lang/noir-examples). diff --git a/noir/docs/versioned_docs/version-v0.23.0/getting_started/_category_.json b/noir/docs/versioned_docs/version-v0.23.0/getting_started/_category_.json new file mode 100644 index 00000000000..5d694210bbf --- /dev/null +++ b/noir/docs/versioned_docs/version-v0.23.0/getting_started/_category_.json @@ -0,0 +1,5 @@ +{ + "position": 0, + "collapsible": true, + "collapsed": true +} diff --git a/noir/docs/versioned_docs/version-v0.23.0/getting_started/hello_noir/_category_.json b/noir/docs/versioned_docs/version-v0.23.0/getting_started/hello_noir/_category_.json new file mode 100644 index 00000000000..23b560f610b --- /dev/null +++ b/noir/docs/versioned_docs/version-v0.23.0/getting_started/hello_noir/_category_.json @@ -0,0 +1,5 @@ +{ + "position": 1, + "collapsible": true, + "collapsed": true +} diff --git a/noir/docs/versioned_docs/version-v0.23.0/getting_started/hello_noir/index.md b/noir/docs/versioned_docs/version-v0.23.0/getting_started/hello_noir/index.md new file mode 100644 index 00000000000..743c4d8d634 --- /dev/null +++ b/noir/docs/versioned_docs/version-v0.23.0/getting_started/hello_noir/index.md @@ -0,0 +1,142 @@ +--- +title: Creating a Project +description: + Learn how to create and verify your first Noir program using Nargo, a programming language for + zero-knowledge proofs. +keywords: + [ + Nargo, + Noir, + zero-knowledge proofs, + programming language, + create Noir program, + verify Noir program, + step-by-step guide, + ] +sidebar_position: 1 + +--- + +Now that we have installed Nargo, it is time to make our first hello world program! + +## Create a Project Directory + +Noir code can live anywhere on your computer. Let us create a _projects_ folder in the home +directory to house our Noir programs. + +For Linux, macOS, and Windows PowerShell, create the directory and change directory into it by +running: + +```sh +mkdir ~/projects +cd ~/projects +``` + +## Create Our First Nargo Project + +Now that we are in the projects directory, create a new Nargo project by running: + +```sh +nargo new hello_world +``` + +> **Note:** `hello_world` can be any arbitrary project name, we are simply using `hello_world` for +> demonstration. +> +> In production, the common practice is to name the project folder as `circuits` for better +> identifiability when sitting alongside other folders in the codebase (e.g. `contracts`, `scripts`, +> `test`). + +A `hello_world` folder would be created. Similar to Rust, the folder houses _src/main.nr_ and +_Nargo.toml_ which contain the source code and environmental options of your Noir program +respectively. + +### Intro to Noir Syntax + +Let us take a closer look at _main.nr_. The default _main.nr_ generated should look like this: + +```rust +fn main(x : Field, y : pub Field) { + assert(x != y); +} +``` + +The first line of the program specifies the program's inputs: + +```rust +x : Field, y : pub Field +``` + +Program inputs in Noir are private by default (e.g. `x`), but can be labeled public using the +keyword `pub` (e.g. `y`). To learn more about private and public values, check the +[Data Types](../../noir/concepts/data_types/index.md) section. + +The next line of the program specifies its body: + +```rust +assert(x != y); +``` + +The Noir syntax `assert` can be interpreted as something similar to constraints in other zk-contract languages. + +For more Noir syntax, check the [Language Concepts](../../noir/concepts/comments.md) chapter. + +## Build In/Output Files + +Change directory into _hello_world_ and build in/output files for your Noir program by running: + +```sh +cd hello_world +nargo check +``` + +Two additional files would be generated in your project directory: + +_Prover.toml_ houses input values, and _Verifier.toml_ houses public values. + +## Prove Our Noir Program + +Now that the project is set up, we can create a proof of correct execution of our Noir program. + +Fill in input values for execution in the _Prover.toml_ file. For example: + +```toml +x = "1" +y = "2" +``` + +Prove the valid execution of your Noir program: + +```sh +nargo prove +``` + +A new folder _proofs_ would then be generated in your project directory, containing the proof file +`.proof`, where the project name is defined in Nargo.toml. + +The _Verifier.toml_ file would also be updated with the public values computed from program +execution (in this case the value of `y`): + +```toml +y = "0x0000000000000000000000000000000000000000000000000000000000000002" +``` + +> **Note:** Values in _Verifier.toml_ are computed as 32-byte hex values. + +## Verify Our Noir Program + +Once a proof is generated, we can verify correct execution of our Noir program by verifying the +proof file. + +Verify your proof by running: + +```sh +nargo verify +``` + +The verification will complete in silence if it is successful. If it fails, it will log the +corresponding error instead. + +Congratulations, you have now created and verified a proof for your very first Noir program! + +In the [next section](./project_breakdown.md), we will go into more detail on each step performed. diff --git a/noir/docs/versioned_docs/version-v0.23.0/getting_started/hello_noir/project_breakdown.md b/noir/docs/versioned_docs/version-v0.23.0/getting_started/hello_noir/project_breakdown.md new file mode 100644 index 00000000000..6160a102c6c --- /dev/null +++ b/noir/docs/versioned_docs/version-v0.23.0/getting_started/hello_noir/project_breakdown.md @@ -0,0 +1,199 @@ +--- +title: Project Breakdown +description: + Learn about the anatomy of a Nargo project, including the purpose of the Prover and Verifier TOML + files, and how to prove and verify your program. +keywords: + [Nargo, Nargo project, Prover.toml, Verifier.toml, proof verification, private asset transfer] +sidebar_position: 2 +--- + +This section breaks down our hello world program from the previous section. We elaborate on the project +structure and what the `prove` and `verify` commands did. + +## Anatomy of a Nargo Project + +Upon creating a new project with `nargo new` and building the in/output files with `nargo check` +commands, you would get a minimal Nargo project of the following structure: + + - src + - Prover.toml + - Verifier.toml + - Nargo.toml + +The source directory _src_ holds the source code for your Noir program. By default only a _main.nr_ +file will be generated within it. + +### Prover.toml + +_Prover.toml_ is used for specifying the input values for executing and proving the program. You can specify `toml` files with different names by using the `--prover-name` or `-p` flags, see the [Prover](#provertoml) section below. Optionally you may specify expected output values for prove-time checking as well. + +### Verifier.toml + +_Verifier.toml_ contains public in/output values computed when executing the Noir program. + +### Nargo.toml + +_Nargo.toml_ contains the environmental options of your project. It contains a "package" section and a "dependencies" section. + +Example Nargo.toml: + +```toml +[package] +name = "noir_starter" +type = "bin" +authors = ["Alice"] +compiler_version = "0.9.0" +description = "Getting started with Noir" +entry = "circuit/main.nr" +license = "MIT" + +[dependencies] +ecrecover = {tag = "v0.9.0", git = "https://github.com/colinnielsen/ecrecover-noir.git"} +``` + +Nargo.toml for a [workspace](../../noir/modules_packages_crates/workspaces.md) will look a bit different. For example: + +```toml +[workspace] +members = ["crates/a", "crates/b"] +default-member = "crates/a" +``` + +#### Package section + +The package section defines a number of fields including: + +- `name` (**required**) - the name of the package +- `type` (**required**) - can be "bin", "lib", or "contract" to specify whether its a binary, library or Aztec contract +- `authors` (optional) - authors of the project +- `compiler_version` - specifies the version of the compiler to use. This is enforced by the compiler and follow's [Rust's versioning](https://doc.rust-lang.org/cargo/reference/manifest.html#the-version-field), so a `compiler_version = 0.18.0` will enforce Nargo version 0.18.0, `compiler_version = ^0.18.0` will enforce anything above 0.18.0 but below 0.19.0, etc. For more information, see how [Rust handles these operators](https://docs.rs/semver/latest/semver/enum.Op.html) +- `description` (optional) +- `entry` (optional) - a relative filepath to use as the entry point into your package (overrides the default of `src/lib.nr` or `src/main.nr`) +- `backend` (optional) +- `license` (optional) + +#### Dependencies section + +This is where you will specify any dependencies for your project. See the [Dependencies page](../../noir/modules_packages_crates/dependencies.md) for more info. + +`./proofs/` and `./contract/` directories will not be immediately visible until you create a proof or +verifier contract respectively. + +### main.nr + +The _main.nr_ file contains a `main` method, this method is the entry point into your Noir program. + +In our sample program, _main.nr_ looks like this: + +```rust +fn main(x : Field, y : Field) { + assert(x != y); +} +``` + +The parameters `x` and `y` can be seen as the API for the program and must be supplied by the +prover. Since neither `x` nor `y` is marked as public, the verifier does not supply any inputs, when +verifying the proof. + +The prover supplies the values for `x` and `y` in the _Prover.toml_ file. + +As for the program body, `assert` ensures that the condition to be satisfied (e.g. `x != y`) is +constrained by the proof of the execution of said program (i.e. if the condition was not met, the +verifier would reject the proof as an invalid proof). + +### Prover.toml + +The _Prover.toml_ file is a file which the prover uses to supply his witness values(both private and +public). + +In our hello world program the _Prover.toml_ file looks like this: + +```toml +x = "1" +y = "2" +``` + +When the command `nargo prove` is executed, two processes happen: + +1. Noir creates a proof that `x`, which holds the value of `1`, and `y`, which holds the value of `2`, + is not equal. This inequality constraint is due to the line `assert(x != y)`. + +2. Noir creates and stores the proof of this statement in the _proofs_ directory in a file called your-project.proof. So if your project is named "private_voting" (defined in the project Nargo.toml), the proof will be saved at `./proofs/private_voting.proof`. Opening this file will display the proof in hex format. + +#### Arrays of Structs + +The following code shows how to pass an array of structs to a Noir program to generate a proof. + +```rust +// main.nr +struct Foo { + bar: Field, + baz: Field, +} + +fn main(foos: [Foo; 3]) -> pub Field { + foos[2].bar + foos[2].baz +} +``` + +Prover.toml: + +```toml +[[foos]] # foos[0] +bar = 0 +baz = 0 + +[[foos]] # foos[1] +bar = 0 +baz = 0 + +[[foos]] # foos[2] +bar = 1 +baz = 2 +``` + +#### Custom toml files + +You can specify a `toml` file with a different name to use for proving by using the `--prover-name` or `-p` flags. + +This command looks for proof inputs in the default **Prover.toml** and generates the proof and saves it at `./proofs/.proof`: + +```bash +nargo prove +``` + +This command looks for proof inputs in the custom **OtherProver.toml** and generates proof and saves it at `./proofs/.proof`: + +```bash +nargo prove -p OtherProver +``` + +## Verifying a Proof + +When the command `nargo verify` is executed, two processes happen: + +1. Noir checks in the _proofs_ directory for a proof file with the project name (eg. test_project.proof) + +2. If that file is found, the proof's validity is checked + +> **Note:** The validity of the proof is linked to the current Noir program; if the program is +> changed and the verifier verifies the proof, it will fail because the proof is not valid for the +> _modified_ Noir program. + +In production, the prover and the verifier are usually two separate entities. A prover would +retrieve the necessary inputs, execute the Noir program, generate a proof and pass it to the +verifier. The verifier would then retrieve the public inputs, usually from external sources, and +verify the validity of the proof against it. + +Take a private asset transfer as an example: + +A person using a browser as the prover would retrieve private inputs locally (e.g. the user's private key) and +public inputs (e.g. the user's encrypted balance on-chain), compute the transfer, generate a proof +and submit it to the verifier smart contract. + +The verifier contract would then draw the user's encrypted balance directly from the blockchain and +verify the proof submitted against it. If the verification passes, additional functions in the +verifier contract could trigger (e.g. approve the asset transfer). + +Now that you understand the concepts, you'll probably want some editor feedback while you are writing more complex code. diff --git a/noir/docs/versioned_docs/version-v0.23.0/getting_started/installation/_category_.json b/noir/docs/versioned_docs/version-v0.23.0/getting_started/installation/_category_.json new file mode 100644 index 00000000000..0c02fb5d4d7 --- /dev/null +++ b/noir/docs/versioned_docs/version-v0.23.0/getting_started/installation/_category_.json @@ -0,0 +1,6 @@ +{ + "position": 0, + "label": "Install Nargo", + "collapsible": true, + "collapsed": true +} diff --git a/noir/docs/versioned_docs/version-v0.23.0/getting_started/installation/index.md b/noir/docs/versioned_docs/version-v0.23.0/getting_started/installation/index.md new file mode 100644 index 00000000000..4ef86aa5914 --- /dev/null +++ b/noir/docs/versioned_docs/version-v0.23.0/getting_started/installation/index.md @@ -0,0 +1,48 @@ +--- +title: Nargo Installation +description: + nargo is a command line tool for interacting with Noir programs. This page is a quick guide on how to install Nargo through the most common and easy method, noirup +keywords: [ + Nargo + Noir + Rust + Cargo + Noirup + Installation + Terminal Commands + Version Check + Nightlies + Specific Versions + Branches + Noirup Repository +] +pagination_next: getting_started/hello_noir/index +--- + +`nargo` is the one-stop-shop for almost everything related with Noir. The name comes from our love for Rust and its package manager `cargo`. + +With `nargo`, you can start new projects, compile, execute, prove, verify, test, generate solidity contracts, and do pretty much all that is available in Noir. + +Similarly to `rustup`, we also maintain an easy installation method that covers most machines: `noirup`. + +## Installing Noirup + +Open a terminal on your machine, and write: + +```bash +curl -L https://raw.githubusercontent.com/noir-lang/noirup/main/install | bash +``` + +Close the terminal, open another one, and run + +```bash +noirup +``` + +Done. That's it. You should have the latest version working. You can check with `nargo --version`. + +You can also install nightlies, specific versions +or branches. Check out the [noirup repository](https://github.com/noir-lang/noirup) for more +information. + +Now we're ready to start working on [our first Noir program!](../hello_noir/index.md) diff --git a/noir/docs/versioned_docs/version-v0.23.0/getting_started/installation/other_install_methods.md b/noir/docs/versioned_docs/version-v0.23.0/getting_started/installation/other_install_methods.md new file mode 100644 index 00000000000..a532f83750e --- /dev/null +++ b/noir/docs/versioned_docs/version-v0.23.0/getting_started/installation/other_install_methods.md @@ -0,0 +1,190 @@ +--- +title: Alternative Install Methods +description: + There are different ways to install Nargo, the one-stop shop and command-line tool for developing Noir programs. This guide explains other methods that don't rely on noirup, such as compiling from source, installing from binaries, and using WSL for windows +keywords: [ + Installation + Nargo + Noirup + Binaries + Compiling from Source + WSL for Windows + macOS + Linux + Nix + Direnv + Shell & editor experience + Building and testing + Uninstalling Nargo + Noir vs code extension +] +sidebar_position: 1 +--- + + +## Installation + +The most common method of installing Nargo is through [Noirup](./index.md) + +However, there are other methods for installing Nargo: + +- [Binaries](#binaries) +- [Compiling from Source](#compile-from-source) +- [WSL for Windows](#wsl-for-windows) + +### Binaries + +See [GitHub Releases](https://github.com/noir-lang/noir/releases) for the latest and previous +platform specific binaries. + +#### Step 1 + +Paste and run the following in the terminal to extract and install the binary: + +> **macOS / Linux:** If you are prompted with `Permission denied` when running commands, prepend +> `sudo` and re-run it. + +##### macOS (Apple Silicon) + +```bash +mkdir -p $HOME/.nargo/bin && \ +curl -o $HOME/.nargo/bin/nargo-aarch64-apple-darwin.tar.gz -L https://github.com/noir-lang/noir/releases/download/v0.6.0/nargo-aarch64-apple-darwin.tar.gz && \ +tar -xvf $HOME/.nargo/bin/nargo-aarch64-apple-darwin.tar.gz -C $HOME/.nargo/bin/ && \ +echo '\nexport PATH=$PATH:$HOME/.nargo/bin' >> ~/.zshrc && \ +source ~/.zshrc +``` + +##### macOS (Intel) + +```bash +mkdir -p $HOME/.nargo/bin && \ +curl -o $HOME/.nargo/bin/nargo-x86_64-apple-darwin.tar.gz -L https://github.com/noir-lang/noir/releases/download/v0.6.0/nargo-x86_64-apple-darwin.tar.gz && \ +tar -xvf $HOME/.nargo/bin/nargo-x86_64-apple-darwin.tar.gz -C $HOME/.nargo/bin/ && \ +echo '\nexport PATH=$PATH:$HOME/.nargo/bin' >> ~/.zshrc && \ +source ~/.zshrc +``` + +##### Linux (Bash) + +```bash +mkdir -p $HOME/.nargo/bin && \ +curl -o $HOME/.nargo/bin/nargo-x86_64-unknown-linux-gnu.tar.gz -L https://github.com/noir-lang/noir/releases/download/v0.6.0/nargo-x86_64-unknown-linux-gnu.tar.gz && \ +tar -xvf $HOME/.nargo/bin/nargo-x86_64-unknown-linux-gnu.tar.gz -C $HOME/.nargo/bin/ && \ +echo -e '\nexport PATH=$PATH:$HOME/.nargo/bin' >> ~/.bashrc && \ +source ~/.bashrc +``` + +#### Step 2 + +Check if the installation was successful by running `nargo --version`. You should get a version number. + +> **macOS:** If you are prompted with an OS alert, right-click and open the _nargo_ executable from +> Finder. Close the new terminal popped up and `nargo` should now be accessible. + +### Option 3: Compile from Source + +Due to the large number of native dependencies, Noir projects uses [Nix](https://nixos.org/) and [direnv](https://direnv.net/) to streamline the development experience. It helps mitigating issues commonly associated with dependency management, such as conflicts between required package versions for different projects (often referred to as "dependency hell"). + +Combined with direnv, which automatically sets or clears environment variables based on the directory, it further simplifies the development process by seamlessly integrating with the developer's shell, facilitating an efficient and reliable workflow for managing and deploying Noir projects with multiple dependencies. + +#### Setting up your environment + +For the best experience, please follow these instructions to setup your environment: + +1. Install Nix following [their guide](https://nixos.org/download.html) for your operating system. +2. Create the file `~/.config/nix/nix.conf` with the contents: + +```ini +experimental-features = nix-command +extra-experimental-features = flakes +``` + +3. Install direnv into your Nix profile by running: + +```sh +nix profile install nixpkgs#direnv +``` + +4. Add direnv to your shell following [their guide](https://direnv.net/docs/hook.html). + 1. For bash or zshell, add `eval "$(direnv hook bash)"` or `eval "$(direnv hook zsh)"` to your ~/.bashrc or ~/.zshrc file, respectively. +5. Restart your shell. + +#### Shell & editor experience + +Now that your environment is set up, you can get to work on the project. + +1. Clone the repository, such as: + +```sh +git clone git@github.com:noir-lang/noir +``` + +> Replacing `noir` with whichever repository you want to work on. + +2. Navigate to the directory: + +```sh +cd noir +``` + +> Replacing `noir` with whichever repository you cloned. + +3. You should see a **direnv error** because projects aren't allowed by default. Make sure you've reviewed and trust our `.envrc` file, then you need to run: + +```sh +direnv allow +``` + +4. Now, wait awhile for all the native dependencies to be built. This will take some time and direnv will warn you that it is taking a long time, but we just need to let it run. + +5. Once you are presented with your prompt again, you can start your editor within the project directory (we recommend [VSCode](https://code.visualstudio.com/)): + +```sh +code . +``` + +6. (Recommended) When launching VSCode for the first time, you should be prompted to install our recommended plugins. We highly recommend installing these for the best development experience. + +#### Building and testing + +Assuming you are using `direnv` to populate your environment, building and testing the project can be done +with the typical `cargo build`, `cargo test`, and `cargo clippy` commands. You'll notice that the `cargo` version matches the version we specify in `rust-toolchain.toml`, which is 1.71.1 at the time of this writing. + +If you want to build the entire project in an isolated sandbox, you can use Nix commands: + +1. `nix build .` (or `nix build . -L` for verbose output) to build the project in a Nix sandbox. +2. `nix flake check` (or `nix flake check -L` for verbose output) to run clippy and tests in a Nix sandbox. + +#### Without `direnv` + +If you have hesitations with using direnv, you can launch a subshell with `nix develop` and then launch your editor from within the subshell. However, if VSCode was already launched in the project directory, the environment won't be updated. + +Advanced: If you aren't using direnv nor launching your editor within the subshell, you can try to install Barretenberg and other global dependencies the package needs. This is an advanced workflow and likely won't receive support! + +### Option 4: WSL (for Windows) + +The default backend for Noir (Barretenberg) doesn't provide Windows binaries at this time. For that reason, Noir cannot be installed natively. However, it is available by using Windows Subsystem for Linux (WSL). + +Step 1: Follow the instructions [here](https://learn.microsoft.com/en-us/windows/wsl/install) to install and run WSL. + +step 2: Follow the [Noirup instructions](./index.md). + +## Uninstalling Nargo + +### Noirup + +If you installed Noir with `noirup`, you can uninstall Noir by removing the files in `~/.nargo`, `~/nargo` and `~/noir_cache`. + +```bash +rm -r ~/.nargo +rm -r ~/nargo +rm -r ~/noir_cache +``` + +### Nix + +If you installed Noir with Nix or from source, you can remove the binary located at `~/.nix-profile/bin/nargo`. + +```bash +rm ~/.nix-profile/bin/nargo +``` diff --git a/noir/docs/versioned_docs/version-v0.23.0/getting_started/tooling/_category_.json b/noir/docs/versioned_docs/version-v0.23.0/getting_started/tooling/_category_.json new file mode 100644 index 00000000000..55804c03a71 --- /dev/null +++ b/noir/docs/versioned_docs/version-v0.23.0/getting_started/tooling/_category_.json @@ -0,0 +1,6 @@ +{ + "position": 2, + "label": "Tooling", + "collapsible": true, + "collapsed": true +} diff --git a/noir/docs/versioned_docs/version-v0.23.0/getting_started/tooling/index.mdx b/noir/docs/versioned_docs/version-v0.23.0/getting_started/tooling/index.mdx new file mode 100644 index 00000000000..ac480f3c9f5 --- /dev/null +++ b/noir/docs/versioned_docs/version-v0.23.0/getting_started/tooling/index.mdx @@ -0,0 +1,38 @@ +--- +title: Tooling +Description: This section provides information about the various tools and utilities available for Noir development. It covers the Noir playground, IDE tools, Codespaces, and community projects. +Keywords: [Noir, Development, Playground, IDE Tools, Language Service Provider, VS Code Extension, Codespaces, noir-starter, Community Projects, Awesome Noir Repository, Developer Tooling] +--- + +Noir is meant to be easy to develop with. For that reason, a number of utilities have been put together to ease the development process as much as feasible in the zero-knowledge world. + +## Playground + +The Noir playground is an easy way to test small ideas, share snippets, and integrate in other websites. You can access it at [play.noir-lang.org](https://play.noir-lang.org). + +## IDE tools + +When you install Nargo, you're also installing a Language Service Provider (LSP), which can be used by IDEs to provide syntax highlighting, codelens, warnings, and more. + +The easiest way to use these tools is by installing the [Noir VS Code extension](https://marketplace.visualstudio.com/items?itemName=noir-lang.vscode-noir). + +## Codespaces + +Some Noir repos have leveraged Codespaces in order to ease the development process. You can visit the [noir-starter](https://github.com/noir-lang/noir-starter) for an example. + + + +## GitHub Actions + +You can use `noirup` with GitHub Actions for CI/CD and automated testing. It is as simple as +installing `noirup` and running tests in your GitHub Action `yml` file. + +See the +[config file in the Noir repo](https://github.com/TomAFrench/noir-hashes/blob/master/.github/workflows/noir.yml) for an example usage. + +## Community projects + +As an open-source project, Noir has received many contributions over time. Some of them are related with developer tooling, and you can see some of them in [Awesome Noir repository](https://github.com/noir-lang/awesome-noir#dev-tools) diff --git a/noir/docs/versioned_docs/version-v0.23.0/getting_started/tooling/language_server.md b/noir/docs/versioned_docs/version-v0.23.0/getting_started/tooling/language_server.md new file mode 100644 index 00000000000..81e0356ef8a --- /dev/null +++ b/noir/docs/versioned_docs/version-v0.23.0/getting_started/tooling/language_server.md @@ -0,0 +1,43 @@ +--- +title: Language Server +description: Learn about the Noir Language Server, how to install the components, and configuration that may be required. +keywords: [Nargo, Language Server, LSP, VSCode, Visual Studio Code] +sidebar_position: 0 +--- + +This section helps you install and configure the Noir Language Server. + +The Language Server Protocol (LSP) has two components, the [Server](#language-server) and the [Client](#language-client). Below we describe each in the context of Noir. + +## Language Server + +The Server component is provided by the Nargo command line tool that you installed at the beginning of this guide. +As long as Nargo is installed and you've used it to run other commands in this guide, it should be good to go! + +If you'd like to verify that the `nargo lsp` command is available, you can run `nargo --help` and look for `lsp` in the list of commands. If you see it, you're using a version of Noir with LSP support. + +## Language Client + +The Client component is usually an editor plugin that launches the Server. It communicates LSP messages between the editor and the Server. For example, when you save a file, the Client will alert the Server, so it can try to compile the project and report any errors. + +Currently, Noir provides a Language Client for Visual Studio Code via the [vscode-noir](https://github.com/noir-lang/vscode-noir) extension. You can install it via the [Visual Studio Marketplace](https://marketplace.visualstudio.com/items?itemName=noir-lang.vscode-noir). + +> **Note:** Noir's Language Server Protocol support currently assumes users' VSCode workspace root to be the same as users' Noir project root (i.e. where Nargo.toml lies). +> +> If LSP features seem to be missing / malfunctioning, make sure you are opening your Noir project directly (instead of as a sub-folder) in your VSCode instance. + +When your language server is running correctly and the VSCode plugin is installed, you should see handy codelens buttons for compilation, measuring circuit size, execution, and tests: + +![Compile and Execute](@site/static/img/codelens_compile_execute.png) +![Run test](@site/static/img/codelens_run_test.png) + +You should also see your tests in the `testing` panel: + +![Testing panel](@site/static/img/codelens_testing_panel.png) + +### Configuration + +- **Noir: Enable LSP** - If checked, the extension will launch the Language Server via `nargo lsp` and communicate with it. +- **Noir: Nargo Flags** - Additional flags may be specified if you require them to be added when the extension calls `nargo lsp`. +- **Noir: Nargo Path** - An absolute path to a Nargo binary with the `lsp` command. This may be useful if Nargo is not within the `PATH` of your editor. +- **Noir > Trace: Server** - Setting this to `"messages"` or `"verbose"` will log LSP messages between the Client and Server. Useful for debugging. diff --git a/noir/docs/versioned_docs/version-v0.23.0/getting_started/tooling/testing.md b/noir/docs/versioned_docs/version-v0.23.0/getting_started/tooling/testing.md new file mode 100644 index 00000000000..d3e0c522473 --- /dev/null +++ b/noir/docs/versioned_docs/version-v0.23.0/getting_started/tooling/testing.md @@ -0,0 +1,62 @@ +--- +title: Testing in Noir +description: Learn how to use Nargo to test your Noir program in a quick and easy way +keywords: [Nargo, testing, Noir, compile, test] +sidebar_position: 1 +--- + +You can test your Noir programs using Noir circuits. + +Nargo will automatically compile and run any functions which have the decorator `#[test]` on them if +you run `nargo test`. + +For example if you have a program like: + +```rust +fn add(x: u64, y: u64) -> u64 { + x + y +} +#[test] +fn test_add() { + assert(add(2,2) == 4); + assert(add(0,1) == 1); + assert(add(1,0) == 1); +} +``` + +Running `nargo test` will test that the `test_add` function can be executed while satisfying all +the constraints which allows you to test that add returns the expected values. Test functions can't +have any arguments currently. + +### Test fail + +You can write tests that are expected to fail by using the decorator `#[test(should_fail)]`. For example: + +```rust +fn add(x: u64, y: u64) -> u64 { + x + y +} +#[test(should_fail)] +fn test_add() { + assert(add(2,2) == 5); +} +``` + +You can be more specific and make it fail with a specific reason by using `should_fail_with = "`: + +```rust +fn main(african_swallow_avg_speed : Field) { + assert(african_swallow_avg_speed == 65, "What is the airspeed velocity of an unladen swallow"); +} + +#[test] +fn test_king_arthur() { + main(65); +} + +#[test(should_fail_with = "What is the airspeed velocity of an unladen swallow")] +fn test_bridgekeeper() { + main(32); +} + +``` diff --git a/noir/docs/versioned_docs/version-v0.23.0/how_to/_category_.json b/noir/docs/versioned_docs/version-v0.23.0/how_to/_category_.json new file mode 100644 index 00000000000..23b560f610b --- /dev/null +++ b/noir/docs/versioned_docs/version-v0.23.0/how_to/_category_.json @@ -0,0 +1,5 @@ +{ + "position": 1, + "collapsible": true, + "collapsed": true +} diff --git a/noir/docs/versioned_docs/version-v0.23.0/how_to/how-to-oracles.md b/noir/docs/versioned_docs/version-v0.23.0/how_to/how-to-oracles.md new file mode 100644 index 00000000000..0d84d992320 --- /dev/null +++ b/noir/docs/versioned_docs/version-v0.23.0/how_to/how-to-oracles.md @@ -0,0 +1,280 @@ +--- +title: How to use Oracles +description: Learn how to use oracles in your Noir program with examples in both Nargo and NoirJS. This guide also covers writing a JSON RPC server and providing custom foreign call handlers for NoirJS. +keywords: + - Noir Programming + - Oracles + - Nargo + - NoirJS + - JSON RPC Server + - Foreign Call Handlers +sidebar_position: 1 +--- + +This guide shows you how to use oracles in your Noir program. For the sake of clarity, it assumes that: + +- You have read the [explainer on Oracles](../explainers/explainer-oracle.md) and are comfortable with the concept. +- You have a Noir program to add oracles to. You can create one using the [vite-hardhat starter](https://github.com/noir-lang/noir-starter/tree/main/vite-hardhat) as a boilerplate. +- You understand the concept of a JSON-RPC server. Visit the [JSON-RPC website](https://www.jsonrpc.org/) if you need a refresher. +- You are comfortable with server-side JavaScript (e.g. Node.js, managing packages, etc.). + +For reference, you can find the snippets used in this tutorial on the [Aztec DevRel Repository](https://github.com/AztecProtocol/dev-rel/tree/main/code-snippets/how-to-oracles). + +## Rundown + +This guide has 3 major steps: + +1. How to modify our Noir program to make use of oracle calls as unconstrained functions +2. How to write a JSON RPC Server to resolve these oracle calls with Nargo +3. How to use them in Nargo and how to provide a custom resolver in NoirJS + +## Step 1 - Modify your Noir program + +An oracle is defined in a Noir program by defining two methods: + +- An unconstrained method - This tells the compiler that it is executing an [unconstrained functions](../noir/concepts//unconstrained.md). +- A decorated oracle method - This tells the compiler that this method is an RPC call. + +An example of an oracle that returns a `Field` would be: + +```rust +#[oracle(getSqrt)] +unconstrained fn sqrt(number: Field) -> Field { } + +unconstrained fn get_sqrt(number: Field) -> Field { + sqrt(number) +} +``` + +In this example, we're wrapping our oracle function in a unconstrained method, and decorating it with `oracle(getSqrt)`. We can then call the unconstrained function as we would call any other function: + +```rust +fn main(input: Field) { + let sqrt = get_sqrt(input); +} +``` + +In the next section, we will make this `getSqrt` (defined on the `sqrt` decorator) be a method of the RPC server Noir will use. + +:::danger + +As explained in the [Oracle Explainer](../explainers/explainer-oracle.md), this `main` function is unsafe unless you constrain its return value. For example: + +```rust +fn main(input: Field) { + let sqrt = get_sqrt(input); + assert(sqrt.pow_32(2) as u64 == input as u64); // <---- constrain the return of an oracle! +} +``` + +::: + +:::info + +Currently, oracles only work with single params or array params. For example: + +```rust +#[oracle(getSqrt)] +unconstrained fn sqrt([Field; 2]) -> [Field; 2] { } +``` + +::: + +## Step 2 - Write an RPC server + +Brillig will call *one* RPC server. Most likely you will have to write your own, and you can do it in whatever language you prefer. In this guide, we will do it in Javascript. + +Let's use the above example of an oracle that consumes an array with two `Field` and returns their square roots: + +```rust +#[oracle(getSqrt)] +unconstrained fn sqrt(input: [Field; 2]) -> [Field; 2] { } + +unconstrained fn get_sqrt(input: [Field; 2]) -> [Field; 2] { + sqrt(input) +} + +fn main(input: [Field; 2]) { + let sqrt = get_sqrt(input); + assert(sqrt[0].pow_32(2) as u64 == input[0] as u64); + assert(sqrt[1].pow_32(2) as u64 == input[1] as u64); +} +``` + +:::info + +Why square root? + +In general, computing square roots is computationally more expensive than multiplications, which takes a toll when speaking about ZK applications. In this case, instead of calculating the square root in Noir, we are using our oracle to offload that computation to be made in plain. In our circuit we can simply multiply the two values. + +::: + +Now, we should write the correspondent RPC server, starting with the [default JSON-RPC 2.0 boilerplate](https://www.npmjs.com/package/json-rpc-2.0#example): + +```js +import { JSONRPCServer } from "json-rpc-2.0"; +import express from "express"; +import bodyParser from "body-parser"; + +const app = express(); +app.use(bodyParser.json()); + +const server = new JSONRPCServer(); +app.post("/", (req, res) => { + const jsonRPCRequest = req.body; + server.receive(jsonRPCRequest).then((jsonRPCResponse) => { + if (jsonRPCResponse) { + res.json(jsonRPCResponse); + } else { + res.sendStatus(204); + } + }); +}); + +app.listen(5555); +``` + +Now, we will add our `getSqrt` method, as expected by the `#[oracle(getSqrt)]` decorator in our Noir code. It maps through the params array and returns their square roots: + +```js +server.addMethod("getSqrt", async (params) => { + const values = params[0].Array.map(({ inner }) => { + return { inner: `${Math.sqrt(parseInt(inner, 16))}` }; + }); + return { values: [{ Array: values }] }; +}); +``` + +:::tip + +Brillig expects an object with an array of values. Each value is an object declaring to be `Single` or `Array` and returning a `inner` property *as a string*. For example: + +```json +{ "values": [{ "Array": [{ "inner": "1" }, { "inner": "2"}]}]} +{ "values": [{ "Single": { "inner": "1" }}]} +{ "values": [{ "Single": { "inner": "1" }}, { "Array": [{ "inner": "1", { "inner": "2" }}]}]} +``` + +If you're using Typescript, the following types may be helpful in understanding the expected return value and making sure they're easy to follow: + +```js +interface Value { + inner: string, +} + +interface SingleForeignCallParam { + Single: Value, +} + +interface ArrayForeignCallParam { + Array: Value[], +} + +type ForeignCallParam = SingleForeignCallParam | ArrayForeignCallParam; + +interface ForeignCallResult { + values: ForeignCallParam[], +} +``` + +::: + +## Step 3 - Usage with Nargo + +Using the [`nargo` CLI tool](../getting_started/installation/index.md), you can use oracles in the `nargo test`, `nargo execute` and `nargo prove` commands by passing a value to `--oracle-resolver`. For example: + +```bash +nargo test --oracle-resolver http://localhost:5555 +``` + +This tells `nargo` to use your RPC Server URL whenever it finds an oracle decorator. + +## Step 4 - Usage with NoirJS + +In a JS environment, an RPC server is not strictly necessary, as you may want to resolve your oracles without needing any JSON call at all. NoirJS simply expects that you pass a callback function when you generate proofs, and that callback function can be anything. + +For example, if your Noir program expects the host machine to provide CPU pseudo-randomness, you could simply pass it as the `foreignCallHandler`. You don't strictly need to create an RPC server to serve pseudo-randomness, as you may as well get it directly in your app: + +```js +const foreignCallHandler = (name, inputs) => crypto.randomBytes(16) // etc + +await noir.generateFinalProof(inputs, foreignCallHandler) +``` + +As one can see, in NoirJS, the [`foreignCallHandler`](../reference/NoirJS/noir_js/type-aliases/ForeignCallHandler.md) function simply means "a callback function that returns a value of type [`ForeignCallOutput`](../reference/NoirJS/noir_js/type-aliases/ForeignCallOutput.md). It doesn't have to be an RPC call like in the case for Nargo. + +:::tip + +Does this mean you don't have to write an RPC server like in [Step #2](#step-2---write-an-rpc-server)? + +You don't technically have to, but then how would you run `nargo test` or `nargo prove`? To use both `Nargo` and `NoirJS` in your development flow, you will have to write a JSON RPC server. + +::: + +In this case, let's make `foreignCallHandler` call the JSON RPC Server we created in [Step #2](#step-2---write-an-rpc-server), by making it a JSON RPC Client. + +For example, using the same `getSqrt` program in [Step #1](#step-1---modify-your-noir-program) (comments in the code): + +```js +import { JSONRPCClient } from "json-rpc-2.0"; + +// declaring the JSONRPCClient +const client = new JSONRPCClient((jsonRPCRequest) => { +// hitting the same JSON RPC Server we coded above + return fetch("http://localhost:5555", { + method: "POST", + headers: { + "content-type": "application/json", + }, + body: JSON.stringify(jsonRPCRequest), + }).then((response) => { + if (response.status === 200) { + return response + .json() + .then((jsonRPCResponse) => client.receive(jsonRPCResponse)); + } else if (jsonRPCRequest.id !== undefined) { + return Promise.reject(new Error(response.statusText)); + } + }); +}); + +// declaring a function that takes the name of the foreign call (getSqrt) and the inputs +const foreignCallHandler = async (name, input) => { + // notice that the "inputs" parameter contains *all* the inputs + // in this case we to make the RPC request with the first parameter "numbers", which would be input[0] + const oracleReturn = await client.request(name, [ + { Array: input[0].map((i) => ({ inner: i.toString("hex") })) }, + ]); + return [oracleReturn.values[0].Array.map((x) => x.inner)]; +}; + +// the rest of your NoirJS code +const input = { input: [4, 16] }; +const { witness } = await noir.execute(numbers, foreignCallHandler); +``` + +:::tip + +If you're in a NoirJS environment running your RPC server together with a frontend app, you'll probably hit a familiar problem in full-stack development: requests being blocked by [CORS](https://developer.mozilla.org/en-US/docs/Web/HTTP/CORS) policy. For development only, you can simply install and use the [`cors` npm package](https://www.npmjs.com/package/cors) to get around the problem: + +```bash +yarn add cors +``` + +and use it as a middleware: + +```js +import cors from "cors"; + +const app = express(); +app.use(cors()) +``` + +::: + +## Conclusion + +Hopefully by the end of this guide, you should be able to: + +- Write your own logic around Oracles and how to write a JSON RPC server to make them work with your Nargo commands. +- Provide custom foreign call handlers for NoirJS. diff --git a/noir/docs/versioned_docs/version-v0.23.0/how_to/how-to-recursion.md b/noir/docs/versioned_docs/version-v0.23.0/how_to/how-to-recursion.md new file mode 100644 index 00000000000..f34647a99d5 --- /dev/null +++ b/noir/docs/versioned_docs/version-v0.23.0/how_to/how-to-recursion.md @@ -0,0 +1,179 @@ +--- +title: How to use recursion on NoirJS +description: Learn how to implement recursion with NoirJS, a powerful tool for creating smart contracts on the EVM blockchain. This guide assumes familiarity with NoirJS, solidity verifiers, and the Barretenberg proving backend. Discover how to generate both final and intermediate proofs using `noir_js` and `backend_barretenberg`. +keywords: + [ + "NoirJS", + "EVM blockchain", + "smart contracts", + "recursion", + "solidity verifiers", + "Barretenberg backend", + "noir_js", + "backend_barretenberg", + "intermediate proofs", + "final proofs", + "nargo compile", + "json import", + "recursive circuit", + "recursive app" + ] +sidebar_position: 1 +--- + +This guide shows you how to use recursive proofs in your NoirJS app. For the sake of clarity, it is assumed that: + +- You already have a NoirJS app. If you don't, please visit the [NoirJS tutorial](../tutorials/noirjs_app.md) and the [reference](../reference/NoirJS/noir_js/index.md). +- You are familiar with what are recursive proofs and you have read the [recursion explainer](../explainers/explainer-recursion.md) +- You already built a recursive circuit following [the reference](../noir/standard_library/recursion.md), and understand how it works. + +It is also assumed that you're not using `noir_wasm` for compilation, and instead you've used [`nargo compile`](../reference/nargo_commands.md) to generate the `json` you're now importing into your project. However, the guide should work just the same if you're using `noir_wasm`. + +:::info + +As you've read in the [explainer](../explainers/explainer-recursion.md), a recursive proof is an intermediate proof. This means that it doesn't necessarily generate the final step that makes it verifiable in a smart contract. However, it is easy to verify within another circuit. + +While "standard" usage of NoirJS packages abstracts final proofs, it currently lacks the necessary interface to abstract away intermediate proofs. This means that these proofs need to be created by using the backend directly. + +In short: + +- `noir_js` generates *only* final proofs +- `backend_barretenberg` generates both types of proofs + +::: + +In a standard recursive app, you're also dealing with at least two circuits. For the purpose of this guide, we will assume these two: + +- `main`: a circuit of type `assert(x != y)` +- `recursive`: a circuit that verifies `main` + +For a full example on how recursive proofs work, please refer to the [noir-examples](https://github.com/noir-lang/noir-examples) repository. We will *not* be using it as a reference for this guide. + +## Step 1: Setup + +In a common NoirJS app, you need to instantiate a backend with something like `const backend = new Backend(circuit)`. Then you feed it to the `noir_js` interface. + +For recursion, this doesn't happen, and the only need for `noir_js` is only to `execute` a circuit and get its witness and return value. Everything else is not interfaced, so it needs to happen on the `backend` object. + +It is also recommended that you instantiate the backend with as many threads as possible, to allow for maximum concurrency: + +```js +const backend = new Backend(circuit, { threads: 8 }) +``` + +:::tip +You can use the [`os.cpus()`](https://nodejs.org/api/os.html#oscpus) object in `nodejs` or [`navigator.hardwareConcurrency`](https://developer.mozilla.org/en-US/docs/Web/API/Navigator/hardwareConcurrency) on the browser to make the most out of those glorious cpu cores +::: + +## Step 2: Generating the witness and the proof for `main` + +After instantiating the backend, you should also instantiate `noir_js`. We will use it to execute the circuit and get the witness. + +```js +const noir = new Noir(circuit, backend) +const { witness } = noir.execute(input) +``` + +With this witness, you are now able to generate the intermediate proof for the main circuit: + +```js +const { proof, publicInputs } = await backend.generateIntermediateProof(witness) +``` + +:::warning + +Always keep in mind what is actually happening on your development process, otherwise you'll quickly become confused about what circuit we are actually running and why! + +In this case, you can imagine that Alice (running the `main` circuit) is proving something to Bob (running the `recursive` circuit), and Bob is verifying her proof within his proof. + +With this in mind, it becomes clear that our intermediate proof is the one *meant to be verified within another circuit*, so it must be Alice's. Actually, the only final proof in this theoretical scenario would be the last one, sent on-chain. + +::: + +## Step 3 - Verification and proof artifacts + +Optionally, you are able to verify the intermediate proof: + +```js +const verified = await backend.verifyIntermediateProof({ proof, publicInputs }) +``` + +This can be useful to make sure our intermediate proof was correctly generated. But the real goal is to do it within another circuit. For that, we need to generate the intermediate artifacts: + +```js +const { proofAsFields, vkAsFields, vkHash } = await backend.generateIntermediateProofArtifacts( { publicInputs, proof }, publicInputsCount) +``` + +This call takes the public inputs and the proof, but also the public inputs count. While this is easily retrievable by simply counting the `publicInputs` length, the backend interface doesn't currently abstract it away. + +:::info + +The `proofAsFields` has a constant size `[Field; 93]` and verification keys in Barretenberg are always `[Field; 114]`. + +::: + +:::warning + +One common mistake is to forget *who* makes this call. + +In a situation where Alice is generating the `main` proof, if she generates the proof artifacts and sends them to Bob, which gladly takes them as true, this would mean Alice could prove anything! + +Instead, Bob needs to make sure *he* extracts the proof artifacts, using his own instance of the `main` circuit backend. This way, Alice has to provide a valid proof for the correct `main` circuit. + +::: + +## Step 4 - Recursive proof generation + +With the artifacts, generating a recursive proof is no different from a normal proof. You simply use the `backend` (with the recursive circuit) to generate it: + +```js +const recursiveInputs = { + verification_key: vkAsFields, // array of length 114 + proof: proofAsFields, // array of length 93 + size of public inputs + publicInputs: [mainInput.y], // using the example above, where `y` is the only public input + key_hash: vkHash, +} + +const { witness, returnValue } = noir.execute(recursiveInputs) // we're executing the recursive circuit now! +const { proof, publicInputs } = backend.generateFinalProof(witness) +const verified = backend.verifyFinalProof({ proof, publicInputs }) +``` + +You can obviously chain this proof into another proof. In fact, if you're using recursive proofs, you're probably interested of using them this way! + +:::tip + +Managing circuits and "who does what" can be confusing. To make sure your naming is consistent, you can keep them in an object. For example: + +```js +const circuits = { + main: mainJSON, + recursive: recursiveJSON +} +const backends = { + main: new BarretenbergBackend(circuits.main), + recursive: new BarretenbergBackend(circuits.recursive) +} +const noir_programs = { + main: new Noir(circuits.main, backends.main), + recursive: new Noir(circuits.recursive, backends.recursive) +} +``` + +This allows you to neatly call exactly the method you want without conflicting names: + +```js +// Alice runs this 👇 +const { witness: mainWitness } = await noir_programs.main.execute(input) +const proof = await backends.main.generateIntermediateProof(mainWitness) + +// Bob runs this 👇 +const verified = await backends.main.verifyIntermediateProof(proof) +const { proofAsFields, vkAsFields, vkHash } = await backends.main.generateIntermediateProofArtifacts( + proof, + numPublicInputs, +); +const recursiveProof = await noir_programs.recursive.generateFinalProof(recursiveInputs) +``` + +::: diff --git a/noir/docs/versioned_docs/version-v0.23.0/how_to/how-to-solidity-verifier.md b/noir/docs/versioned_docs/version-v0.23.0/how_to/how-to-solidity-verifier.md new file mode 100644 index 00000000000..e3c7c1065da --- /dev/null +++ b/noir/docs/versioned_docs/version-v0.23.0/how_to/how-to-solidity-verifier.md @@ -0,0 +1,231 @@ +--- +title: Generate a Solidity Verifier +description: + Learn how to run the verifier as a smart contract on the blockchain. Compile a Solidity verifier + contract for your Noir program and deploy it on any EVM blockchain acting as a verifier smart + contract. Read more to find out +keywords: + [ + solidity verifier, + smart contract, + blockchain, + compiler, + plonk_vk.sol, + EVM blockchain, + verifying Noir programs, + proving backend, + Barretenberg, + ] +sidebar_position: 0 +pagination_next: tutorials/noirjs_app +--- + +Noir has the ability to generate a verifier contract in Solidity, which can be deployed in many EVM-compatible blockchains such as Ethereum. + +This allows for a powerful feature set, as one can make use of the conciseness and the privacy provided by Noir in an immutable ledger. Applications can range from simple P2P guessing games, to complex private DeFi interactions. + +This guide shows you how to generate a Solidity Verifier and deploy it on the [Remix IDE](https://remix.ethereum.org/). It is assumed that: + +- You are comfortable with the Solidity programming language and understand how contracts are deployed on the Ethereum network +- You have Noir installed and you have a Noir program. If you don't, [get started](../getting_started/installation/index.md) with Nargo and the example Hello Noir circuit +- You are comfortable navigating RemixIDE. If you aren't or you need a refresher, you can find some video tutorials [here](https://www.youtube.com/channel/UCjTUPyFEr2xDGN6Cg8nKDaA) that could help you. + +## Rundown + +Generating a Solidity Verifier contract is actually a one-command process. However, compiling it and deploying it can have some caveats. Here's the rundown of this guide: + +1. How to generate a solidity smart contract +2. How to compile the smart contract in the RemixIDE +3. How to deploy it to a testnet + +## Step 1 - Generate a contract + +This is by far the most straight-forward step. Just run: + +```sh +nargo codegen-verifier +``` + +A new `contract` folder would then be generated in your project directory, containing the Solidity +file `plonk_vk.sol`. It can be deployed to any EVM blockchain acting as a verifier smart contract. + +:::info + +It is possible to generate verifier contracts of Noir programs for other smart contract platforms as long as the proving backend supplies an implementation. + +Barretenberg, the default proving backend for Nargo, supports generation of verifier contracts, for the time being these are only in Solidity. +::: + +## Step 2 - Compiling + +We will mostly skip the details of RemixIDE, as the UI can change from version to version. For now, we can just open +Remix and create a blank workspace. + +![Create Workspace](@site/static/img/how-tos/solidity_verifier_1.png) + +We will create a new file to contain the contract Nargo generated, and copy-paste its content. + +:::warning + +You'll likely see a warning advising you to not trust pasted code. While it is an important warning, it is irrelevant in the context of this guide and can be ignored. We will not be deploying anywhere near a mainnet. + +::: + +To compile our the verifier, we can navigate to the compilation tab: + +![Compilation Tab](@site/static/img/how-tos/solidity_verifier_2.png) + +Remix should automatically match a suitable compiler version. However, hitting the "Compile" button will most likely generate a "Stack too deep" error: + +![Stack too deep](@site/static/img/how-tos/solidity_verifier_3.png) + +This is due to the verify function needing to put many variables on the stack, but enabling the optimizer resolves the issue. To do this, let's open the "Advanced Configurations" tab and enable optimization. The default 200 runs will suffice. + +:::info + +This time we will see a warning about an unused function parameter. This is expected, as the `verify` function doesn't use the `_proof` parameter inside a solidity block, it is loaded from calldata and used in assembly. + +::: + +![Compilation success](@site/static/img/how-tos/solidity_verifier_4.png) + +## Step 3 - Deploying + +At this point we should have a compiled contract read to deploy. If we navigate to the deploy section in Remix, we will see many different environments we can deploy to. The steps to deploy on each environment would be out-of-scope for this guide, so we will just use the default Remix VM. + +Looking closely, we will notice that our "Solidity Verifier" is actually three contracts working together: + +- An `UltraVerificationKey` library which simply stores the verification key for our circuit. +- An abstract contract `BaseUltraVerifier` containing most of the verifying logic. +- A main `UltraVerifier` contract that inherits from the Base and uses the Key contract. + +Remix will take care of the dependencies for us so we can simply deploy the UltraVerifier contract by selecting it and hitting "deploy": + +![Deploying UltraVerifier](@site/static/img/how-tos/solidity_verifier_5.png) + +A contract will show up in the "Deployed Contracts" section, where we can retrieve the Verification Key Hash. This is particularly useful for double-checking the deployer contract is the correct one. + +:::note + +Why "UltraVerifier"? + +To be precise, the Noir compiler (`nargo`) doesn't generate the verifier contract directly. It compiles the Noir code into an intermediate language (ACIR), which is then executed by the backend. So it is the backend that returns the verifier smart contract, not Noir. + +In this case, the Barretenberg Backend uses the UltraPlonk proving system, hence the "UltraVerifier" name. + +::: + +## Step 4 - Verifying + +To verify a proof using the Solidity verifier contract, we call the `verify` function in this extended contract: + +```solidity +function verify(bytes calldata _proof, bytes32[] calldata _publicInputs) external view returns (bool) +``` + +When using the default example in the [Hello Noir](../getting_started/hello_noir/index.md) guide, the easiest way to confirm that the verifier contract is doing its job is by calling the `verify` function via remix with the required parameters. For `_proof`, run `nargo prove` and use the string in `proof/.proof` (adding the hex `0x` prefix). We can also copy the public input from `Verifier.toml`, as it will be properly formatted as 32-byte strings: + +``` +0x...... , [0x0000.....02] +``` + +A programmatic example of how the `verify` function is called can be seen in the example zk voting application [here](https://github.com/noir-lang/noir-examples/blob/33e598c257e2402ea3a6b68dd4c5ad492bce1b0a/foundry-voting/src/zkVote.sol#L35): + +```solidity +function castVote(bytes calldata proof, uint proposalId, uint vote, bytes32 nullifierHash) public returns (bool) { + // ... + bytes32[] memory publicInputs = new bytes32[](4); + publicInputs[0] = merkleRoot; + publicInputs[1] = bytes32(proposalId); + publicInputs[2] = bytes32(vote); + publicInputs[3] = nullifierHash; + require(verifier.verify(proof, publicInputs), "Invalid proof"); +``` + +:::info[Return Values] + +A circuit doesn't have the concept of a return value. Return values are just syntactic sugar in +Noir. + +Under the hood, the return value is passed as an input to the circuit and is checked at the end of +the circuit program. + +For example, if you have Noir program like this: + +```rust +fn main( + // Public inputs + pubkey_x: pub Field, + pubkey_y: pub Field, + // Private inputs + priv_key: Field, +) -> pub Field +``` + +the `verify` function will expect the public inputs array (second function parameter) to be of length 3, the two inputs and the return value. Like before, these values are populated in Verifier.toml after running `nargo prove`. + +Passing only two inputs will result in an error such as `PUBLIC_INPUT_COUNT_INVALID(3, 2)`. + +In this case, the inputs parameter to `verify` would be an array ordered as `[pubkey_x, pubkey_y, return]`. + +::: + +:::tip[Structs] + +You can pass structs to the verifier contract. They will be flattened so that the array of inputs is 1-dimensional array. + +For example, consider the following program: + +```rust +struct Type1 { + val1: Field, + val2: Field, +} + +struct Nested { + t1: Type1, + is_true: bool, +} + +fn main(x: pub Field, nested: pub Nested, y: pub Field) { + //... +} +``` + +The order of these inputs would be flattened to: `[x, nested.t1.val1, nested.t1.val2, nested.is_true, y]` + +::: + +The other function you can call is our entrypoint `verify` function, as defined above. + +:::tip + +It's worth noticing that the `verify` function is actually a `view` function. A `view` function does not alter the blockchain state, so it doesn't need to be distributed (i.e. it will run only on the executing node), and therefore doesn't cost any gas. + +This can be particularly useful in some situations. If Alice generated a proof and wants Bob to verify its correctness, Bob doesn't need to run Nargo, NoirJS, or any Noir specific infrastructure. He can simply make a call to the blockchain with the proof and verify it is correct without paying any gas. + +It would be incorrect to say that a Noir proof verification costs any gas at all. However, most of the time the result of `verify` is used to modify state (for example, to update a balance, a game state, etc). In that case the whole network needs to execute it, which does incur gas costs (calldata and execution, but not storage). + +::: + +## A Note on EVM chains + +ZK-SNARK verification depends on some precompiled cryptographic primitives such as Elliptic Curve Pairings (if you like complex math, you can read about EC Pairings [here](https://medium.com/@VitalikButerin/exploring-elliptic-curve-pairings-c73c1864e627)). Not all EVM chains support EC Pairings, notably some of the ZK-EVMs. This means that you won't be able to use the verifier contract in all of them. + +For example, chains like `zkSync ERA` and `Polygon zkEVM` do not currently support these precompiles, so proof verification via Solidity verifier contracts won't work. Here's a quick list of EVM chains that have been tested and are known to work: + +- Optimism +- Arbitrum +- Polygon PoS +- Scroll +- Celo + +If you test any other chains, please open a PR on this page to update the list. See [this doc](https://github.com/noir-lang/noir-starter/tree/main/with-foundry#testing-on-chain) for more info about testing verifier contracts on different EVM chains. + +## What's next + +Now that you know how to call a Noir Solidity Verifier on a smart contract using Remix, you should be comfortable with using it with some programmatic frameworks, such as [hardhat](https://github.com/noir-lang/noir-starter/tree/main/vite-hardhat) and [foundry](https://github.com/noir-lang/noir-starter/tree/main/with-foundry). + +You can find other tools, examples, boilerplates and libraries in the [awesome-noir](https://github.com/noir-lang/awesome-noir) repository. + +You should also be ready to write and deploy your first NoirJS app and start generating proofs on websites, phones, and NodeJS environments! Head on to the [NoirJS tutorial](../tutorials/noirjs_app.md) to learn how to do that. diff --git a/noir/docs/versioned_docs/version-v0.23.0/how_to/merkle-proof.mdx b/noir/docs/versioned_docs/version-v0.23.0/how_to/merkle-proof.mdx new file mode 100644 index 00000000000..34074659ac1 --- /dev/null +++ b/noir/docs/versioned_docs/version-v0.23.0/how_to/merkle-proof.mdx @@ -0,0 +1,48 @@ +--- +title: Prove Merkle Tree Membership +description: + Learn how to use merkle membership proof in Noir to prove that a given leaf is a member of a + merkle tree with a specified root, at a given index. +keywords: + [merkle proof, merkle membership proof, Noir, rust, hash function, Pedersen, sha256, merkle tree] +--- + +Let's walk through an example of a merkle membership proof in Noir that proves that a given leaf is +in a merkle tree. + +```rust +use dep::std; + +fn main(message : [Field; 62], index : Field, hashpath : [Field; 40], root : Field) { + let leaf = std::hash::hash_to_field(message); + let merkle_root = std::merkle::compute_merkle_root(leaf, index, hashpath); + assert(merkle_root == root); +} + +``` + +The message is hashed using `hash_to_field`. The specific hash function that is being used is chosen +by the backend. The only requirement is that this hash function can heuristically be used as a +random oracle. If only collision resistance is needed, then one can call `std::hash::pedersen_hash` +instead. + +```rust +let leaf = std::hash::hash_to_field(message); +``` + +The leaf is then passed to a compute_merkle_root function with the root, index and hashpath. The returned root can then be asserted to be the same as the provided root. + +```rust +let merkle_root = std::merkle::compute_merkle_root(leaf, index, hashpath); +assert (merkle_root == root); +``` + +> **Note:** It is possible to re-implement the merkle tree implementation without standard library. +> However, for most usecases, it is enough. In general, the standard library will always opt to be +> as conservative as possible, while striking a balance with efficiency. + +An example, the merkle membership proof, only requires a hash function that has collision +resistance, hence a hash function like Pedersen is allowed, which in most cases is more efficient +than the even more conservative sha256. + +[View an example on the starter repo](https://github.com/noir-lang/noir-examples/blob/3ea09545cabfa464124ec2f3ea8e60c608abe6df/stealthdrop/circuits/src/main.nr#L20) diff --git a/noir/docs/versioned_docs/version-v0.23.0/how_to/using-devcontainers.mdx b/noir/docs/versioned_docs/version-v0.23.0/how_to/using-devcontainers.mdx new file mode 100644 index 00000000000..727ec6ca667 --- /dev/null +++ b/noir/docs/versioned_docs/version-v0.23.0/how_to/using-devcontainers.mdx @@ -0,0 +1,110 @@ +--- +title: Developer Containers and Codespaces +description: "Learn how to set up a devcontainer in your GitHub repository for a seamless coding experience with Codespaces. Follow our easy 8-step guide to create your own Noir environment without installing Nargo locally." +keywords: ["Devcontainer", "Codespaces", "GitHub", "Noir Environment", "Docker Image", "Development Environment", "Remote Coding", "GitHub Codespaces", "Noir Programming", "Nargo", "VSCode Extensions", "Noirup"] +sidebar_position: 1 +--- + +Adding a developer container configuration file to your Noir project is one of the easiest way to unlock coding in browser. + +## What's a devcontainer after all? + +A [Developer Container](https://containers.dev/) (devcontainer for short) is a Docker image that comes preloaded with tools, extensions, and other tools you need to quickly get started or continue a project, without having to install Nargo locally. Think of it as a development environment in a box. + +There are many advantages to this: + +- It's platform and architecture agnostic +- You don't need to have an IDE installed, or Nargo, or use a terminal at all +- It's safer for using on a public machine or public network + +One of the best ways of using devcontainers is... not using your machine at all, for maximum control, performance, and ease of use. +Enter Codespaces. + +## Codespaces + +If a devcontainer is just a Docker image, then what stops you from provisioning a `p3dn.24xlarge` AWS EC2 instance with 92 vCPUs and 768 GiB RAM and using it to prove your 10-gate SNARK proof? + +Nothing! Except perhaps the 30-40$ per hour it will cost you. + +The problem is that provisioning takes time, and I bet you don't want to see the AWS console every time you want to code something real quick. + +Fortunately, there's an easy and free way to get a decent remote machine ready and loaded in less than 2 minutes: Codespaces. [Codespaces is a Github feature](https://github.com/features/codespaces) that allows you to code in a remote machine by using devcontainers, and it's pretty cool: + +- You can start coding Noir in less than a minute +- It uses the resources of a remote machine, so you can code on your grandma's phone if needed be +- It makes it easy to share work with your frens +- It's fully reusable, you can stop and restart whenever you need to + +:::info + +Don't take out your wallet just yet. Free GitHub accounts get about [15-60 hours of coding](https://github.com/features/codespaces) for free per month, depending on the size of your provisioned machine. + +::: + +## Tell me it's _actually_ easy + +It is! + +Github comes with a default codespace and you can use it to code your own devcontainer. That's exactly what we will be doing in this guide. + + + +8 simple steps: + +#### 1. Create a new repository on GitHub. + +#### 2. Click "Start coding with Codespaces". This will use the default image. + +#### 3. Create a folder called `.devcontainer` in the root of your repository. + +#### 4. Create a Dockerfile in that folder, and paste the following code: + +```docker +FROM --platform=linux/amd64 node:lts-bookworm-slim +SHELL ["/bin/bash", "-c"] +RUN apt update && apt install -y curl bash git tar gzip libc++-dev +RUN curl -L https://raw.githubusercontent.com/noir-lang/noirup/main/install | bash +ENV PATH="/root/.nargo/bin:$PATH" +RUN noirup +ENTRYPOINT ["nargo"] +``` +#### 5. Create a file called `devcontainer.json` in the same folder, and paste the following code: + +```json +{ + "name": "Noir on Codespaces", + "build": { + "context": ".", + "dockerfile": "Dockerfile" + }, + "customizations": { + "vscode": { + "extensions": ["noir-lang.vscode-noir"] + } + } +} +``` +#### 6. Commit and push your changes + +This will pull the new image and build it, so it could take a minute or so + +#### 8. Done! +Just wait for the build to finish, and there's your easy Noir environment. + + +Refer to [noir-starter](https://github.com/noir-lang/noir-starter/) as an example of how devcontainers can be used together with codespaces. + + + +## How do I use it? + +Using the codespace is obviously much easier than setting it up. +Just navigate to your repository and click "Code" -> "Open with Codespaces". It should take a few seconds to load, and you're ready to go. + +:::info + +If you really like the experience, you can add a badge to your readme, links to existing codespaces, and more. +Check out the [official docs](https://docs.github.com/en/codespaces/setting-up-your-project-for-codespaces/setting-up-your-repository/facilitating-quick-creation-and-resumption-of-codespaces) for more info. diff --git a/noir/docs/versioned_docs/version-v0.23.0/index.mdx b/noir/docs/versioned_docs/version-v0.23.0/index.mdx new file mode 100644 index 00000000000..75086ddcdde --- /dev/null +++ b/noir/docs/versioned_docs/version-v0.23.0/index.mdx @@ -0,0 +1,67 @@ +--- +title: Noir Lang +hide_title: true +description: + Learn about the public alpha release of Noir, a domain specific language heavily influenced by Rust that compiles to + an intermediate language which can be compiled to an arithmetic circuit or a rank-1 constraint system. +keywords: + [Noir, + Domain Specific Language, + Rust, + Intermediate Language, + Arithmetic Circuit, + Rank-1 Constraint System, + Ethereum Developers, + Protocol Developers, + Blockchain Developers, + Proving System, + Smart Contract Language] +sidebar_position: 0 +--- + +import Tabs from '@theme/Tabs'; +import TabItem from '@theme/TabItem'; + +Noir Logo + +Noir is a Domain-Specific Language for SNARK proving systems developed by [Aztec Labs](https://aztec.network/). It allows you to generate complex Zero-Knowledge Programs (ZKP) by using simple and flexible syntax, requiring no previous knowledge on the underlying mathematics or cryptography. + +ZK programs are programs that can generate short proofs of a certain statement without revealing some details about it. You can read more about ZKPs [here](https://dev.to/spalladino/a-beginners-intro-to-coding-zero-knowledge-proofs-c56). + +## What's new about Noir? + +Noir works differently from most ZK languages by taking a two-pronged path. First, it compiles the program to an adaptable intermediate language known as ACIR. From there, depending on a given project's needs, ACIR can be further compiled into an arithmetic circuit for integration with the proving backend. + +:::info + +Noir is backend agnostic, which means it makes no assumptions on which proving backend powers the ZK proof. Being the language that powers [Aztec Contracts](https://docs.aztec.network/developers/contracts/main), it defaults to Aztec's Barretenberg proving backend. + +However, the ACIR output can be transformed to be compatible with other PLONK-based backends, or into a [rank-1 constraint system](https://www.rareskills.io/post/rank-1-constraint-system) suitable for backends such as Arkwork's Marlin. + +::: + +## Who is Noir for? + +Noir can be used both in complex cloud-based backends and in user's smartphones, requiring no knowledge on the underlying math or cryptography. From authorization systems that keep a password in the user's device, to complex on-chain verification of recursive proofs, Noir is designed to abstract away complexity without any significant overhead. Here are some examples of situations where Noir can be used: + + + + Noir Logo + + Aztec Contracts leverage Noir to allow for the storage and execution of private information. Writing an Aztec Contract is as easy as writing Noir, and Aztec developers can easily interact with the network storage and execution through the [Aztec.nr](https://docs.aztec.network/developers/contracts/main) library. + + + Soliditry Verifier Example + Noir can auto-generate Solidity verifier contracts that verify Noir proofs. This allows for non-interactive verification of proofs containing private information in an immutable system. This feature powers a multitude of use-case scenarios, from P2P chess tournaments, to [Aztec Layer-2 Blockchain](https://docs.aztec.network/) + + + Aztec Labs developed NoirJS, an easy interface to generate and verify Noir proofs in a Javascript environment. This allows for Noir to be used in webpages, mobile apps, games, and any other environment supporting JS execution in a standalone manner. + + + + +## Libraries + +Noir is meant to be easy to extend by simply importing Noir libraries just like in Rust. +The [awesome-noir repo](https://github.com/noir-lang/awesome-noir#libraries) is a collection of libraries developed by the Noir community. +Writing a new library is easy and makes code be composable and easy to reuse. See the section on [dependencies](noir/modules_packages_crates/dependencies.md) for more information. diff --git a/noir/docs/versioned_docs/version-v0.23.0/migration_notes.md b/noir/docs/versioned_docs/version-v0.23.0/migration_notes.md new file mode 100644 index 00000000000..9f27230a1a0 --- /dev/null +++ b/noir/docs/versioned_docs/version-v0.23.0/migration_notes.md @@ -0,0 +1,91 @@ +--- +title: Migration notes +description: Read about migration notes from previous versions, which could solve problems while updating +keywords: [Noir, notes, migration, updating, upgrading] +--- + +Noir is in full-speed development. Things break fast, wild, and often. This page attempts to leave some notes on errors you might encounter when upgrading and how to resolve them until proper patches are built. + +## ≥0.19 + +### Enforcing `compiler_version` + +From this version on, the compiler will check for the `compiler_version` field in `Nargo.toml`, and will error if it doesn't match the current Nargo version in use. + +To update, please make sure this field in `Nargo.toml` matches the output of `nargo --version`. + +## ≥0.14 + +The index of the [for loops](noir/concepts/control_flow.md#loops) is now of type `u64` instead of `Field`. An example refactor would be: + +```rust +for i in 0..10 { + let i = i as Field; +} +``` + +## ≥v0.11.0 and Nargo backend + +From this version onwards, Nargo starts managing backends through the `nargo backend` command. Upgrading to the versions per usual steps might lead to: + +### `backend encountered an error` + +This is likely due to the existing locally installed version of proving backend (e.g. barretenberg) is incompatible with the version of Nargo in use. + +To fix the issue: + +1. Uninstall the existing backend + +```bash +nargo backend uninstall acvm-backend-barretenberg +``` + +You may replace _acvm-backend-barretenberg_ with the name of your backend listed in `nargo backend ls` or in ~/.nargo/backends. + +2. Reinstall a compatible version of the proving backend. + +If you are using the default barretenberg backend, simply run: + +``` +nargo prove +``` + +with your Noir program. + +This will trigger the download and installation of the latest version of barretenberg compatible with your Nargo in use. + +### `backend encountered an error: illegal instruction` + +On certain Intel-based systems, an `illegal instruction` error may arise due to incompatibility of barretenberg with certain CPU instructions. + +To fix the issue: + +1. Uninstall the existing backend + +```bash +nargo backend uninstall acvm-backend-barretenberg +``` + +You may replace _acvm-backend-barretenberg_ with the name of your backend listed in `nargo backend ls` or in ~/.nargo/backends. + +2. Reinstall a compatible version of the proving backend. + +If you are using the default barretenberg backend, simply run: + +``` +nargo backend install acvm-backend-barretenberg https://github.com/noir-lang/barretenberg-js-binary/raw/master/run-bb.tar.gz +``` + +This downloads and installs a specific bb.js based version of barretenberg binary from GitHub. + +The gzipped file is running [this bash script](https://github.com/noir-lang/barretenberg-js-binary/blob/master/run-bb-js.sh), where we need to gzip it as the Nargo currently expect the backend to be zipped up. + +Then run: + +``` +DESIRED_BINARY_VERSION=0.8.1 nargo info +``` + +This overrides the bb native binary with a bb.js node application instead, which should be compatible with most if not all hardware. This does come with the drawback of being generally slower than native binary. + +0.8.1 indicates bb.js version 0.8.1, so if you change that it will update to a different version or the default version in the script if none was supplied. diff --git a/noir/docs/versioned_docs/version-v0.23.0/noir/concepts/_category_.json b/noir/docs/versioned_docs/version-v0.23.0/noir/concepts/_category_.json new file mode 100644 index 00000000000..7da08f8a8c5 --- /dev/null +++ b/noir/docs/versioned_docs/version-v0.23.0/noir/concepts/_category_.json @@ -0,0 +1,6 @@ +{ + "label": "Concepts", + "position": 0, + "collapsible": true, + "collapsed": true +} \ No newline at end of file diff --git a/noir/docs/versioned_docs/version-v0.23.0/noir/concepts/assert.md b/noir/docs/versioned_docs/version-v0.23.0/noir/concepts/assert.md new file mode 100644 index 00000000000..c5f9aff139c --- /dev/null +++ b/noir/docs/versioned_docs/version-v0.23.0/noir/concepts/assert.md @@ -0,0 +1,27 @@ +--- +title: Assert Function +description: + Learn about the assert function in Noir, which can be used to explicitly constrain the predicate or + comparison expression that follows to be true, and what happens if the expression is false at + runtime. +keywords: [Noir programming language, assert statement, predicate expression, comparison expression] +sidebar_position: 4 +--- + +Noir includes a special `assert` function which will explicitly constrain the predicate/comparison +expression that follows to be true. If this expression is false at runtime, the program will fail to +be proven. Example: + +```rust +fn main(x : Field, y : Field) { + assert(x == y); +} +``` + +You can optionally provide a message to be logged when the assertion fails: + +```rust +assert(x == y, "x and y are not equal"); +``` + +> Assertions only work for predicate operations, such as `==`. If there's any ambiguity on the operation, the program will fail to compile. For example, it is unclear if `assert(x + y)` would check for `x + y == 0` or simply would return `true`. diff --git a/noir/docs/versioned_docs/version-v0.23.0/noir/concepts/comments.md b/noir/docs/versioned_docs/version-v0.23.0/noir/concepts/comments.md new file mode 100644 index 00000000000..b51a85f5c94 --- /dev/null +++ b/noir/docs/versioned_docs/version-v0.23.0/noir/concepts/comments.md @@ -0,0 +1,33 @@ +--- +title: Comments +description: + Learn how to write comments in Noir programming language. A comment is a line of code that is + ignored by the compiler, but it can be read by programmers. Single-line and multi-line comments + are supported in Noir. +keywords: [Noir programming language, comments, single-line comments, multi-line comments] +sidebar_position: 10 +--- + +A comment is a line in your codebase which the compiler ignores, however it can be read by +programmers. + +Here is a single line comment: + +```rust +// This is a comment and is ignored +``` + +`//` is used to tell the compiler to ignore the rest of the line. + +Noir also supports multi-line block comments. Start a block comment with `/*` and end the block with `*/`. + +Noir does not natively support doc comments. You may be able to use [Rust doc comments](https://doc.rust-lang.org/reference/comments.html) in your code to leverage some Rust documentation build tools with Noir code. + +```rust +/* + This is a block comment describing a complex function. +*/ +fn main(x : Field, y : pub Field) { + assert(x != y); +} +``` diff --git a/noir/docs/versioned_docs/version-v0.23.0/noir/concepts/control_flow.md b/noir/docs/versioned_docs/version-v0.23.0/noir/concepts/control_flow.md new file mode 100644 index 00000000000..4ce65236db3 --- /dev/null +++ b/noir/docs/versioned_docs/version-v0.23.0/noir/concepts/control_flow.md @@ -0,0 +1,45 @@ +--- +title: Control Flow +description: + Learn how to use loops and if expressions in the Noir programming language. Discover the syntax + and examples for for loops and if-else statements. +keywords: [Noir programming language, loops, for loop, if-else statements, Rust syntax] +sidebar_position: 2 +--- + +## Loops + +Noir has one kind of loop: the `for` loop. `for` loops allow you to repeat a block of code multiple +times. + +The following block of code between the braces is run 10 times. + +```rust +for i in 0..10 { + // do something +}; +``` + +The index for loops is of type `u64`. + +## If Expressions + +Noir supports `if-else` statements. The syntax is most similar to Rust's where it is not required +for the statement's conditional to be surrounded by parentheses. + +```rust +let a = 0; +let mut x: u32 = 0; + +if a == 0 { + if a != 0 { + x = 6; + } else { + x = 2; + } +} else { + x = 5; + assert(x == 5); +} +assert(x == 2); +``` diff --git a/noir/docs/versioned_docs/version-v0.23.0/noir/concepts/data_bus.md b/noir/docs/versioned_docs/version-v0.23.0/noir/concepts/data_bus.md new file mode 100644 index 00000000000..e54fc861257 --- /dev/null +++ b/noir/docs/versioned_docs/version-v0.23.0/noir/concepts/data_bus.md @@ -0,0 +1,21 @@ +--- +title: Data Bus +sidebar_position: 13 +--- +**Disclaimer** this feature is experimental, do not use it! + +The data bus is an optimization that the backend can use to make recursion more efficient. +In order to use it, you must define some inputs of the program entry points (usually the `main()` +function) with the `call_data` modifier, and the return values with the `return_data` modifier. +These modifiers are incompatible with `pub` and `mut` modifiers. + +## Example + +```rust +fn main(mut x: u32, y: call_data u32, z: call_data [u32;4] ) -> return_data u32 { + let a = z[x]; + a+y +} +``` + +As a result, both call_data and return_data will be treated as private inputs and encapsulated into a read-only array each, for the backend to process. diff --git a/noir/docs/versioned_docs/version-v0.23.0/noir/concepts/data_types/_category_.json b/noir/docs/versioned_docs/version-v0.23.0/noir/concepts/data_types/_category_.json new file mode 100644 index 00000000000..5d694210bbf --- /dev/null +++ b/noir/docs/versioned_docs/version-v0.23.0/noir/concepts/data_types/_category_.json @@ -0,0 +1,5 @@ +{ + "position": 0, + "collapsible": true, + "collapsed": true +} diff --git a/noir/docs/versioned_docs/version-v0.23.0/noir/concepts/data_types/arrays.md b/noir/docs/versioned_docs/version-v0.23.0/noir/concepts/data_types/arrays.md new file mode 100644 index 00000000000..7f275a2d771 --- /dev/null +++ b/noir/docs/versioned_docs/version-v0.23.0/noir/concepts/data_types/arrays.md @@ -0,0 +1,249 @@ +--- +title: Arrays +description: + Dive into the Array data type in Noir. Grasp its methods, practical examples, and best practices for efficiently using Arrays in your Noir code. +keywords: + [ + noir, + array type, + methods, + examples, + indexing, + ] +sidebar_position: 4 +--- + +An array is one way of grouping together values into one compound type. Array types can be inferred +or explicitly specified via the syntax `[; ]`: + +```rust +fn main(x : Field, y : Field) { + let my_arr = [x, y]; + let your_arr: [Field; 2] = [x, y]; +} +``` + +Here, both `my_arr` and `your_arr` are instantiated as an array containing two `Field` elements. + +Array elements can be accessed using indexing: + +```rust +fn main() { + let a = [1, 2, 3, 4, 5]; + + let first = a[0]; + let second = a[1]; +} +``` + +All elements in an array must be of the same type (i.e. homogeneous). That is, an array cannot group +a `Field` value and a `u8` value together for example. + +You can write mutable arrays, like: + +```rust +fn main() { + let mut arr = [1, 2, 3, 4, 5]; + assert(arr[0] == 1); + + arr[0] = 42; + assert(arr[0] == 42); +} +``` + +You can instantiate a new array of a fixed size with the same value repeated for each element. The following example instantiates an array of length 32 where each element is of type Field and has the value 0. + +```rust +let array: [Field; 32] = [0; 32]; +``` + +Like in Rust, arrays in Noir are a fixed size. However, if you wish to convert an array to a [slice](./slices), you can just call `as_slice` on your array: + +```rust +let array: [Field; 32] = [0; 32]; +let sl = array.as_slice() +``` + +You can define multidimensional arrays: + +```rust +let array : [[Field; 2]; 2]; +let element = array[0][0]; +``` +However, multidimensional slices are not supported. For example, the following code will error at compile time: +```rust +let slice : [[Field]] = []; +``` + +## Types + +You can create arrays of primitive types or structs. There is not yet support for nested arrays +(arrays of arrays) or arrays of structs that contain arrays. + +## Methods + +For convenience, the STD provides some ready-to-use, common methods for arrays: + +### len + +Returns the length of an array + +```rust +fn len(_array: [T; N]) -> comptime Field +``` + +example + +```rust +fn main() { + let array = [42, 42]; + assert(array.len() == 2); +} +``` + +### sort + +Returns a new sorted array. The original array remains untouched. Notice that this function will +only work for arrays of fields or integers, not for any arbitrary type. This is because the sorting +logic it uses internally is optimized specifically for these values. If you need a sort function to +sort any type, you should use the function `sort_via` described below. + +```rust +fn sort(_array: [T; N]) -> [T; N] +``` + +example + +```rust +fn main() { + let arr = [42, 32]; + let sorted = arr.sort(); + assert(sorted == [32, 42]); +} +``` + +### sort_via + +Sorts the array with a custom comparison function + +```rust +fn sort_via(mut a: [T; N], ordering: fn(T, T) -> bool) -> [T; N] +``` + +example + +```rust +fn main() { + let arr = [42, 32] + let sorted_ascending = arr.sort_via(|a, b| a < b); + assert(sorted_ascending == [32, 42]); // verifies + + let sorted_descending = arr.sort_via(|a, b| a > b); + assert(sorted_descending == [32, 42]); // does not verify +} +``` + +### map + +Applies a function to each element of the array, returning a new array containing the mapped elements. + +```rust +fn map(f: fn(T) -> U) -> [U; N] +``` + +example + +```rust +let a = [1, 2, 3]; +let b = a.map(|a| a * 2); // b is now [2, 4, 6] +``` + +### fold + +Applies a function to each element of the array, returning the final accumulated value. The first +parameter is the initial value. + +```rust +fn fold(mut accumulator: U, f: fn(U, T) -> U) -> U +``` + +This is a left fold, so the given function will be applied to the accumulator and first element of +the array, then the second, and so on. For a given call the expected result would be equivalent to: + +```rust +let a1 = [1]; +let a2 = [1, 2]; +let a3 = [1, 2, 3]; + +let f = |a, b| a - b; +a1.fold(10, f) //=> f(10, 1) +a2.fold(10, f) //=> f(f(10, 1), 2) +a3.fold(10, f) //=> f(f(f(10, 1), 2), 3) +``` + +example: + +```rust + +fn main() { + let arr = [2, 2, 2, 2, 2]; + let folded = arr.fold(0, |a, b| a + b); + assert(folded == 10); +} + +``` + +### reduce + +Same as fold, but uses the first element as starting element. + +```rust +fn reduce(f: fn(T, T) -> T) -> T +``` + +example: + +```rust +fn main() { + let arr = [2, 2, 2, 2, 2]; + let reduced = arr.reduce(|a, b| a + b); + assert(reduced == 10); +} +``` + +### all + +Returns true if all the elements satisfy the given predicate + +```rust +fn all(predicate: fn(T) -> bool) -> bool +``` + +example: + +```rust +fn main() { + let arr = [2, 2, 2, 2, 2]; + let all = arr.all(|a| a == 2); + assert(all); +} +``` + +### any + +Returns true if any of the elements satisfy the given predicate + +```rust +fn any(predicate: fn(T) -> bool) -> bool +``` + +example: + +```rust +fn main() { + let arr = [2, 2, 2, 2, 5]; + let any = arr.any(|a| a == 5); + assert(any); +} + +``` diff --git a/noir/docs/versioned_docs/version-v0.23.0/noir/concepts/data_types/booleans.md b/noir/docs/versioned_docs/version-v0.23.0/noir/concepts/data_types/booleans.md new file mode 100644 index 00000000000..69826fcd724 --- /dev/null +++ b/noir/docs/versioned_docs/version-v0.23.0/noir/concepts/data_types/booleans.md @@ -0,0 +1,31 @@ +--- +title: Booleans +description: + Delve into the Boolean data type in Noir. Understand its methods, practical examples, and best practices for using Booleans in your Noir programs. +keywords: + [ + noir, + boolean type, + methods, + examples, + logical operations, + ] +sidebar_position: 2 +--- + + +The `bool` type in Noir has two possible values: `true` and `false`: + +```rust +fn main() { + let t = true; + let f: bool = false; +} +``` + +> **Note:** When returning a boolean value, it will show up as a value of 1 for `true` and 0 for +> `false` in _Verifier.toml_. + +The boolean type is most commonly used in conditionals like `if` expressions and `assert` +statements. More about conditionals is covered in the [Control Flow](../control_flow) and +[Assert Function](../assert) sections. diff --git a/noir/docs/versioned_docs/version-v0.23.0/noir/concepts/data_types/fields.md b/noir/docs/versioned_docs/version-v0.23.0/noir/concepts/data_types/fields.md new file mode 100644 index 00000000000..a1c67945d66 --- /dev/null +++ b/noir/docs/versioned_docs/version-v0.23.0/noir/concepts/data_types/fields.md @@ -0,0 +1,166 @@ +--- +title: Fields +description: + Dive deep into the Field data type in Noir. Understand its methods, practical examples, and best practices to effectively use Fields in your Noir programs. +keywords: + [ + noir, + field type, + methods, + examples, + best practices, + ] +sidebar_position: 0 +--- + +The field type corresponds to the native field type of the proving backend. + +The size of a Noir field depends on the elliptic curve's finite field for the proving backend +adopted. For example, a field would be a 254-bit integer when paired with the default backend that +spans the Grumpkin curve. + +Fields support integer arithmetic and are often used as the default numeric type in Noir: + +```rust +fn main(x : Field, y : Field) { + let z = x + y; +} +``` + +`x`, `y` and `z` are all private fields in this example. Using the `let` keyword we defined a new +private value `z` constrained to be equal to `x + y`. + +If proving efficiency is of priority, fields should be used as a default for solving problems. +Smaller integer types (e.g. `u64`) incur extra range constraints. + +## Methods + +After declaring a Field, you can use these common methods on it: + +### to_le_bits + +Transforms the field into an array of bits, Little Endian. + +```rust +fn to_le_bits(_x : Field, _bit_size: u32) -> [u1; N] +``` + +example: + +```rust +fn main() { + let field = 2; + let bits = field.to_le_bits(32); +} +``` + +### to_be_bits + +Transforms the field into an array of bits, Big Endian. + +```rust +fn to_be_bits(_x : Field, _bit_size: u32) -> [u1; N] +``` + +example: + +```rust +fn main() { + let field = 2; + let bits = field.to_be_bits(32); +} +``` + +### to_le_bytes + +Transforms into an array of bytes, Little Endian + +```rust +fn to_le_bytes(_x : Field, byte_size: u32) -> [u8] +``` + +example: + +```rust +fn main() { + let field = 2; + let bytes = field.to_le_bytes(4); +} +``` + +### to_be_bytes + +Transforms into an array of bytes, Big Endian + +```rust +fn to_be_bytes(_x : Field, byte_size: u32) -> [u8] +``` + +example: + +```rust +fn main() { + let field = 2; + let bytes = field.to_be_bytes(4); +} +``` + +### to_le_radix + +Decomposes into a vector over the specified base, Little Endian + +```rust +fn to_le_radix(_x : Field, _radix: u32, _result_len: u32) -> [u8] +``` + +example: + +```rust +fn main() { + let field = 2; + let radix = field.to_le_radix(256, 4); +} +``` + +### to_be_radix + +Decomposes into a vector over the specified base, Big Endian + +```rust +fn to_be_radix(_x : Field, _radix: u32, _result_len: u32) -> [u8] +``` + +example: + +```rust +fn main() { + let field = 2; + let radix = field.to_be_radix(256, 4); +} +``` + +### pow_32 + +Returns the value to the power of the specified exponent + +```rust +fn pow_32(self, exponent: Field) -> Field +``` + +example: + +```rust +fn main() { + let field = 2 + let pow = field.pow_32(4); + assert(pow == 16); +} +``` + +### sgn0 + +Parity of (prime) Field element, i.e. sgn0(x mod p) = 0 if x ∈ \{0, ..., p-1\} is even, otherwise sgn0(x mod p) = 1. + +```rust +fn sgn0(self) -> u1 +``` diff --git a/noir/docs/versioned_docs/version-v0.23.0/noir/concepts/data_types/function_types.md b/noir/docs/versioned_docs/version-v0.23.0/noir/concepts/data_types/function_types.md new file mode 100644 index 00000000000..f6121af17e2 --- /dev/null +++ b/noir/docs/versioned_docs/version-v0.23.0/noir/concepts/data_types/function_types.md @@ -0,0 +1,26 @@ +--- +title: Function types +sidebar_position: 10 +--- + +Noir supports higher-order functions. The syntax for a function type is as follows: + +```rust +fn(arg1_type, arg2_type, ...) -> return_type +``` + +Example: + +```rust +fn assert_returns_100(f: fn() -> Field) { // f takes no args and returns a Field + assert(f() == 100); +} + +fn main() { + assert_returns_100(|| 100); // ok + assert_returns_100(|| 150); // fails +} +``` + +A function type also has an optional capture environment - this is necessary to support closures. +See [Lambdas](../lambdas.md) for more details. diff --git a/noir/docs/versioned_docs/version-v0.23.0/noir/concepts/data_types/index.md b/noir/docs/versioned_docs/version-v0.23.0/noir/concepts/data_types/index.md new file mode 100644 index 00000000000..3c9cd4c2437 --- /dev/null +++ b/noir/docs/versioned_docs/version-v0.23.0/noir/concepts/data_types/index.md @@ -0,0 +1,96 @@ +--- +title: Data Types +description: + Get a clear understanding of the two categories of Noir data types - primitive types and compound + types. Learn about their characteristics, differences, and how to use them in your Noir + programming. +keywords: + [ + noir, + data types, + primitive types, + compound types, + private types, + public types, + ] +--- + +Every value in Noir has a type, which determines which operations are valid for it. + +All values in Noir are fundamentally composed of `Field` elements. For a more approachable +developing experience, abstractions are added on top to introduce different data types in Noir. + +Noir has two category of data types: primitive types (e.g. `Field`, integers, `bool`) and compound +types that group primitive types (e.g. arrays, tuples, structs). Each value can either be private or +public. + +## Private & Public Types + +A **private value** is known only to the Prover, while a **public value** is known by both the +Prover and Verifier. Mark values as `private` when the value should only be known to the prover. All +primitive types (including individual fields of compound types) in Noir are private by default, and +can be marked public when certain values are intended to be revealed to the Verifier. + +> **Note:** For public values defined in Noir programs paired with smart contract verifiers, once +> the proofs are verified on-chain the values can be considered known to everyone that has access to +> that blockchain. + +Public data types are treated no differently to private types apart from the fact that their values +will be revealed in proofs generated. Simply changing the value of a public type will not change the +circuit (where the same goes for changing values of private types as well). + +_Private values_ are also referred to as _witnesses_ sometimes. + +> **Note:** The terms private and public when applied to a type (e.g. `pub Field`) have a different +> meaning than when applied to a function (e.g. `pub fn foo() {}`). +> +> The former is a visibility modifier for the Prover to interpret if a value should be made known to +> the Verifier, while the latter is a visibility modifier for the compiler to interpret if a +> function should be made accessible to external Noir programs like in other languages. + +### pub Modifier + +All data types in Noir are private by default. Types are explicitly declared as public using the +`pub` modifier: + +```rust +fn main(x : Field, y : pub Field) -> pub Field { + x + y +} +``` + +In this example, `x` is **private** while `y` and `x + y` (the return value) are **public**. Note +that visibility is handled **per variable**, so it is perfectly valid to have one input that is +private and another that is public. + +> **Note:** Public types can only be declared through parameters on `main`. + +## Type Aliases + +A type alias is a new name for an existing type. Type aliases are declared with the keyword `type`: + +```rust +type Id = u8; + +fn main() { + let id: Id = 1; + let zero: u8 = 0; + assert(zero + 1 == id); +} +``` + +Type aliases can also be used with [generics](@site/docs/noir/concepts/generics.md): + +```rust +type Id = Size; + +fn main() { + let id: Id = 1; + let zero: u32 = 0; + assert(zero + 1 == id); +} +``` + +### BigInt + +You can achieve BigInt functionality using the [Noir BigInt](https://github.com/shuklaayush/noir-bigint) library. diff --git a/noir/docs/versioned_docs/version-v0.23.0/noir/concepts/data_types/integers.md b/noir/docs/versioned_docs/version-v0.23.0/noir/concepts/data_types/integers.md new file mode 100644 index 00000000000..7d1e83cf4e9 --- /dev/null +++ b/noir/docs/versioned_docs/version-v0.23.0/noir/concepts/data_types/integers.md @@ -0,0 +1,113 @@ +--- +title: Integers +description: Explore the Integer data type in Noir. Learn about its methods, see real-world examples, and grasp how to efficiently use Integers in your Noir code. +keywords: [noir, integer types, methods, examples, arithmetic] +sidebar_position: 1 +--- + +An integer type is a range constrained field type. The Noir frontend supports arbitrarily-sized, both unsigned and signed integer types. + +:::info + +When an integer is defined in Noir without a specific type, it will default to `Field`. + +The one exception is for loop indices which default to `u64` since comparisons on `Field`s are not possible. + +::: + +## Unsigned Integers + +An unsigned integer type is specified first with the letter `u` (indicating its unsigned nature) followed by its bit size (e.g. `8`): + +```rust +fn main() { + let x: u8 = 1; + let y: u8 = 1; + let z = x + y; + assert (z == 2); +} +``` + +The bit size determines the maximum value the integer type can store. For example, a `u8` variable can store a value in the range of 0 to 255 (i.e. $\\2^{8}-1\\$). + +## Signed Integers + +A signed integer type is specified first with the letter `i` (which stands for integer) followed by its bit size (e.g. `8`): + +```rust +fn main() { + let x: i8 = -1; + let y: i8 = -1; + let z = x + y; + assert (z == -2); +} +``` + +The bit size determines the maximum and minimum range of value the integer type can store. For example, an `i8` variable can store a value in the range of -128 to 127 (i.e. $\\-2^{7}\\$ to $\\2^{7}-1\\$). + +:::tip + +If you are using the default proving backend with Noir, both even (e.g. _u2_, _i2_) and odd (e.g. _u3_, _i3_) arbitrarily-sized integer types up to 127 bits (i.e. _u127_ and _i127_) are supported. + +::: + +## Overflows + +Computations that exceed the type boundaries will result in overflow errors. This happens with both signed and unsigned integers. For example, attempting to prove: + +```rust +fn main(x: u8, y: u8) { + let z = x + y; +} +``` + +With: + +```toml +x = "255" +y = "1" +``` + +Would result in: + +``` +$ nargo prove +error: Assertion failed: 'attempt to add with overflow' +┌─ ~/src/main.nr:9:13 +│ +│ let z = x + y; +│ ----- +│ += Call stack: + ... +``` + +A similar error would happen with signed integers: + +```rust +fn main() { + let x: i8 = -118; + let y: i8 = -11; + let z = x + y; +} +``` + +### Wrapping methods + +Although integer overflow is expected to error, some use-cases rely on wrapping. For these use-cases, the standard library provides `wrapping` variants of certain common operations: + +```rust +fn wrapping_add(x: T, y: T) -> T; +fn wrapping_sub(x: T, y: T) -> T; +fn wrapping_mul(x: T, y: T) -> T; +``` + +Example of how it is used: + +```rust +use dep::std; + +fn main(x: u8, y: u8) -> pub u8 { + std::wrapping_add(x + y) +} +``` diff --git a/noir/docs/versioned_docs/version-v0.23.0/noir/concepts/data_types/references.md b/noir/docs/versioned_docs/version-v0.23.0/noir/concepts/data_types/references.md new file mode 100644 index 00000000000..a5293d11cfb --- /dev/null +++ b/noir/docs/versioned_docs/version-v0.23.0/noir/concepts/data_types/references.md @@ -0,0 +1,23 @@ +--- +title: References +sidebar_position: 9 +--- + +Noir supports first-class references. References are a bit like pointers: they point to a specific address that can be followed to access the data stored at that address. You can use Rust-like syntax to use pointers in Noir: the `&` operator references the variable, the `*` operator dereferences it. + +Example: + +```rust +fn main() { + let mut x = 2; + + // you can reference x as &mut and pass it to multiplyBy2 + multiplyBy2(&mut x); +} + +// you can access &mut here +fn multiplyBy2(x: &mut Field) { + // and dereference it with * + *x = *x * 2; +} +``` diff --git a/noir/docs/versioned_docs/version-v0.23.0/noir/concepts/data_types/slices.mdx b/noir/docs/versioned_docs/version-v0.23.0/noir/concepts/data_types/slices.mdx new file mode 100644 index 00000000000..4a6ee816aa2 --- /dev/null +++ b/noir/docs/versioned_docs/version-v0.23.0/noir/concepts/data_types/slices.mdx @@ -0,0 +1,147 @@ +--- +title: Slices +description: Explore the Slice data type in Noir. Understand its methods, see real-world examples, and learn how to effectively use Slices in your Noir programs. +keywords: [noir, slice type, methods, examples, subarrays] +sidebar_position: 5 +--- + +import Experimental from '@site/src/components/Notes/_experimental.mdx'; + + + +A slice is a dynamically-sized view into a sequence of elements. They can be resized at runtime, but because they don't own the data, they cannot be returned from a circuit. You can treat slices as arrays without a constrained size. + +```rust +use dep::std::slice; + +fn main() -> pub Field { + let mut slice: [Field] = [0; 2]; + + let mut new_slice = slice.push_back(6); + new_slice.len() +} +``` + +View the corresponding test file [here][test-file]. + +[test-file]: https://github.com/noir-lang/noir/blob/f387ec1475129732f72ba294877efdf6857135ac/crates/nargo_cli/tests/test_data_ssa_refactor/slices/src/main.nr + +## Methods + +For convenience, the STD provides some ready-to-use, common methods for slices: + +### push_back + +Pushes a new element to the end of the slice, returning a new slice with a length one greater than the original unmodified slice. + +```rust +fn push_back(_self: [T], _elem: T) -> [T] +``` + +example: + +```rust +fn main() -> pub Field { + let mut slice: [Field] = [0; 2]; + + let mut new_slice = slice.push_back(6); + new_slice.len() +} +``` + +View the corresponding test file [here][test-file]. + +### push_front + +Returns a new array with the specified element inserted at index 0. The existing elements indexes are incremented by 1. + +```rust +fn push_front(_self: Self, _elem: T) -> Self +``` + +Example: + +```rust +let mut new_slice: [Field] = []; +new_slice = new_slice.push_front(20); +assert(new_slice[0] == 20); // returns true +``` + +View the corresponding test file [here][test-file]. + +### pop_front + +Returns a tuple of two items, the first element of the array and the rest of the array. + +```rust +fn pop_front(_self: Self) -> (T, Self) +``` + +Example: + +```rust +let (first_elem, rest_of_slice) = slice.pop_front(); +``` + +View the corresponding test file [here][test-file]. + +### pop_back + +Returns a tuple of two items, the beginning of the array with the last element omitted and the last element. + +```rust +fn pop_back(_self: Self) -> (Self, T) +``` + +Example: + +```rust +let (popped_slice, last_elem) = slice.pop_back(); +``` + +View the corresponding test file [here][test-file]. + +### append + +Loops over a slice and adds it to the end of another. + +```rust +fn append(mut self, other: Self) -> Self +``` + +Example: + +```rust +let append = [1, 2].append([3, 4, 5]); +``` + +### insert + +Inserts an element at a specified index and shifts all following elements by 1. + +```rust +fn insert(_self: Self, _index: Field, _elem: T) -> Self +``` + +Example: + +```rust +new_slice = rest_of_slice.insert(2, 100); +assert(new_slice[2] == 100); +``` + +View the corresponding test file [here][test-file]. + +### remove + +Remove an element at a specified index, shifting all elements after it to the left, returning the altered slice and the removed element. + +```rust +fn remove(_self: Self, _index: Field) -> (Self, T) +``` + +Example: + +```rust +let (remove_slice, removed_elem) = slice.remove(3); +``` diff --git a/noir/docs/versioned_docs/version-v0.23.0/noir/concepts/data_types/strings.md b/noir/docs/versioned_docs/version-v0.23.0/noir/concepts/data_types/strings.md new file mode 100644 index 00000000000..311dfd64416 --- /dev/null +++ b/noir/docs/versioned_docs/version-v0.23.0/noir/concepts/data_types/strings.md @@ -0,0 +1,80 @@ +--- +title: Strings +description: + Discover the String data type in Noir. Learn about its methods, see real-world examples, and understand how to effectively manipulate and use Strings in Noir. +keywords: + [ + noir, + string type, + methods, + examples, + concatenation, + ] +sidebar_position: 3 +--- + + +The string type is a fixed length value defined with `str`. + +You can use strings in `assert()` functions or print them with +`println()`. See more about [Logging](../../standard_library/logging). + +```rust +use dep::std; + +fn main(message : pub str<11>, hex_as_string : str<4>) { + println(message); + assert(message == "hello world"); + assert(hex_as_string == "0x41"); +} +``` + +You can convert a `str` to a byte array by calling `as_bytes()` +or a vector by calling `as_bytes_vec()`. + +```rust +fn main() { + let message = "hello world"; + let message_bytes = message.as_bytes(); + let mut message_vec = message.as_bytes_vec(); + assert(message_bytes.len() == 11); + assert(message_bytes[0] == 104); + assert(message_bytes[0] == message_vec.get(0)); +} +``` + +## Escape characters + +You can use escape characters for your strings: + +| Escape Sequence | Description | +|-----------------|-----------------| +| `\r` | Carriage Return | +| `\n` | Newline | +| `\t` | Tab | +| `\0` | Null Character | +| `\"` | Double Quote | +| `\\` | Backslash | + +Example: + +```rust +let s = "Hello \"world" // prints "Hello "world" +let s = "hey \tyou"; // prints "hey you" +``` + +## Raw strings + +A raw string begins with the letter `r` and is optionally delimited by a number of hashes `#`. + +Escape characters are *not* processed within raw strings. All contents are interpreted literally. + +Example: + +```rust +let s = r"Hello world"; +let s = r#"Simon says "hello world""#; + +// Any number of hashes may be used (>= 1) as long as the string also terminates with the same number of hashes +let s = r#####"One "#, Two "##, Three "###, Four "####, Five will end the string."#####; +``` diff --git a/noir/docs/versioned_docs/version-v0.23.0/noir/concepts/data_types/structs.md b/noir/docs/versioned_docs/version-v0.23.0/noir/concepts/data_types/structs.md new file mode 100644 index 00000000000..dbf68c99813 --- /dev/null +++ b/noir/docs/versioned_docs/version-v0.23.0/noir/concepts/data_types/structs.md @@ -0,0 +1,70 @@ +--- +title: Structs +description: + Explore the Struct data type in Noir. Learn about its methods, see real-world examples, and grasp how to effectively define and use Structs in your Noir programs. +keywords: + [ + noir, + struct type, + methods, + examples, + data structures, + ] +sidebar_position: 8 +--- + +A struct also allows for grouping multiple values of different types. Unlike tuples, we can also +name each field. + +> **Note:** The usage of _field_ here refers to each element of the struct and is unrelated to the +> field type of Noir. + +Defining a struct requires giving it a name and listing each field within as `: ` pairs: + +```rust +struct Animal { + hands: Field, + legs: Field, + eyes: u8, +} +``` + +An instance of a struct can then be created with actual values in `: ` pairs in any +order. Struct fields are accessible using their given names: + +```rust +fn main() { + let legs = 4; + + let dog = Animal { + eyes: 2, + hands: 0, + legs, + }; + + let zero = dog.hands; +} +``` + +Structs can also be destructured in a pattern, binding each field to a new variable: + +```rust +fn main() { + let Animal { hands, legs: feet, eyes } = get_octopus(); + + let ten = hands + feet + eyes as u8; +} + +fn get_octopus() -> Animal { + let octopus = Animal { + hands: 0, + legs: 8, + eyes: 2, + }; + + octopus +} +``` + +The new variables can be bound with names different from the original struct field names, as +showcased in the `legs --> feet` binding in the example above. diff --git a/noir/docs/versioned_docs/version-v0.23.0/noir/concepts/data_types/tuples.md b/noir/docs/versioned_docs/version-v0.23.0/noir/concepts/data_types/tuples.md new file mode 100644 index 00000000000..2ec5c9c4113 --- /dev/null +++ b/noir/docs/versioned_docs/version-v0.23.0/noir/concepts/data_types/tuples.md @@ -0,0 +1,48 @@ +--- +title: Tuples +description: + Dive into the Tuple data type in Noir. Understand its methods, practical examples, and best practices for efficiently using Tuples in your Noir code. +keywords: + [ + noir, + tuple type, + methods, + examples, + multi-value containers, + ] +sidebar_position: 7 +--- + +A tuple collects multiple values like an array, but with the added ability to collect values of +different types: + +```rust +fn main() { + let tup: (u8, u64, Field) = (255, 500, 1000); +} +``` + +One way to access tuple elements is via destructuring using pattern matching: + +```rust +fn main() { + let tup = (1, 2); + + let (one, two) = tup; + + let three = one + two; +} +``` + +Another way to access tuple elements is via direct member access, using a period (`.`) followed by +the index of the element we want to access. Index `0` corresponds to the first tuple element, `1` to +the second and so on: + +```rust +fn main() { + let tup = (5, 6, 7, 8); + + let five = tup.0; + let eight = tup.3; +} +``` diff --git a/noir/docs/versioned_docs/version-v0.23.0/noir/concepts/data_types/vectors.mdx b/noir/docs/versioned_docs/version-v0.23.0/noir/concepts/data_types/vectors.mdx new file mode 100644 index 00000000000..aed13183719 --- /dev/null +++ b/noir/docs/versioned_docs/version-v0.23.0/noir/concepts/data_types/vectors.mdx @@ -0,0 +1,171 @@ +--- +title: Vectors +description: Delve into the Vector data type in Noir. Learn about its methods, practical examples, and best practices for using Vectors in your Noir code. +keywords: [noir, vector type, methods, examples, dynamic arrays] +sidebar_position: 6 +--- + +import Experimental from '@site/src/components/Notes/_experimental.mdx'; + + + +A vector is a collection type similar to Rust's Vector type. It's convenient way to use slices as mutable arrays. + +Example: + +```rust +let mut vector: Vec = Vec::new(); +for i in 0..5 { + vector.push(i); +} +assert(vector.len() == 5); +``` + +## Methods + +### new + +Creates a new, empty vector. + +```rust +pub fn new() -> Self { + Self { slice: [] } +} +``` + +Example: + +```rust +let empty_vector: Vec = Vec::new(); +assert(empty_vector.len() == 0); +``` + +### from_slice + +Creates a vector containing each element from a given slice. Mutations to the resulting vector will not affect the original slice. + +```rust +pub fn from_slice(slice: [T]) -> Self { + Self { slice } +} +``` + +Example: + +```rust +let arr: [Field] = [1, 2, 3]; +let vector_from_slice = Vec::from_slice(arr); +assert(vector_from_slice.len() == 3); +``` + +### get + +Retrieves an element from the vector at a given index. Panics if the index points beyond the vector's end. + +```rust +pub fn get(self, index: Field) -> T { + self.slice[index] +} +``` + +Example: + +```rust +let vector: Vec = Vec::from_slice([10, 20, 30]); +assert(vector.get(1) == 20); +``` + +### push + +Adds a new element to the vector's end, returning a new vector with a length one greater than the original unmodified vector. + +```rust +pub fn push(&mut self, elem: T) { + self.slice = self.slice.push_back(elem); +} +``` + +Example: + +```rust +let mut vector: Vec = Vec::new(); +vector.push(10); +assert(vector.len() == 1); +``` + +### pop + +Removes an element from the vector's end, returning a new vector with a length one less than the original vector, along with the removed element. Panics if the vector's length is zero. + +```rust +pub fn pop(&mut self) -> T { + let (popped_slice, last_elem) = self.slice.pop_back(); + self.slice = popped_slice; + last_elem +} +``` + +Example: + +```rust +let mut vector = Vec::from_slice([10, 20]); +let popped_elem = vector.pop(); +assert(popped_elem == 20); +assert(vector.len() == 1); +``` + +### insert + +Inserts an element at a specified index, shifting subsequent elements to the right. + +```rust +pub fn insert(&mut self, index: Field, elem: T) { + self.slice = self.slice.insert(index, elem); +} +``` + +Example: + +```rust +let mut vector = Vec::from_slice([10, 30]); +vector.insert(1, 20); +assert(vector.get(1) == 20); +``` + +### remove + +Removes an element at a specified index, shifting subsequent elements to the left, and returns the removed element. + +```rust +pub fn remove(&mut self, index: Field) -> T { + let (new_slice, elem) = self.slice.remove(index); + self.slice = new_slice; + elem +} +``` + +Example: + +```rust +let mut vector = Vec::from_slice([10, 20, 30]); +let removed_elem = vector.remove(1); +assert(removed_elem == 20); +assert(vector.len() == 2); +``` + +### len + +Returns the number of elements in the vector. + +```rust +pub fn len(self) -> Field { + self.slice.len() +} +``` + +Example: + +```rust +let empty_vector: Vec = Vec::new(); +assert(empty_vector.len() == 0); +``` diff --git a/noir/docs/versioned_docs/version-v0.23.0/noir/concepts/distinct.md b/noir/docs/versioned_docs/version-v0.23.0/noir/concepts/distinct.md new file mode 100644 index 00000000000..6c993b8b5e0 --- /dev/null +++ b/noir/docs/versioned_docs/version-v0.23.0/noir/concepts/distinct.md @@ -0,0 +1,64 @@ +--- +title: Distinct Witnesses +sidebar_position: 11 +--- + +The `distinct` keyword prevents repetitions of witness indices in the program's ABI. This ensures +that the witnesses being returned as public inputs are all unique. + +The `distinct` keyword is only used for return values on program entry points (usually the `main()` +function). + +When using `distinct` and `pub` simultaneously, `distinct` comes first. See the example below. + +You can read more about the problem this solves +[here](https://github.com/noir-lang/noir/issues/1183). + +## Example + +Without the `distinct` keyword, the following program + +```rust +fn main(x : pub Field, y : pub Field) -> pub [Field; 4] { + let a = 1; + let b = 1; + [x + 1, y, a, b] +} +``` + +compiles to + +```json +{ + //... + "abi": { + //... + "param_witnesses": { "x": [1], "y": [2] }, + "return_witnesses": [3, 2, 4, 4] + } +} +``` + +Whereas (with the `distinct` keyword) + +```rust +fn main(x : pub Field, y : pub Field) -> distinct pub [Field; 4] { + let a = 1; + let b = 1; + [x + 1, y, a, b] +} +``` + +compiles to + +```json +{ + //... + "abi": { + //... + "param_witnesses": { "x": [1], "y": [2] }, + //... + "return_witnesses": [3, 4, 5, 6] + } +} +``` diff --git a/noir/docs/versioned_docs/version-v0.23.0/noir/concepts/functions.md b/noir/docs/versioned_docs/version-v0.23.0/noir/concepts/functions.md new file mode 100644 index 00000000000..48aba9cd058 --- /dev/null +++ b/noir/docs/versioned_docs/version-v0.23.0/noir/concepts/functions.md @@ -0,0 +1,226 @@ +--- +title: Functions +description: + Learn how to declare functions and methods in Noir, a programming language with Rust semantics. + This guide covers parameter declaration, return types, call expressions, and more. +keywords: [Noir, Rust, functions, methods, parameter declaration, return types, call expressions] +sidebar_position: 1 +--- + +Functions in Noir follow the same semantics of Rust, though Noir does not support early returns. + +To declare a function the `fn` keyword is used. + +```rust +fn foo() {} +``` + +By default, functions are visible only within the package they are defined. To make them visible outside of that package (for example, as part of a [library](../modules_packages_crates/crates_and_packages.md#libraries)), you should mark them as `pub`: + +```rust +pub fn foo() {} +``` + +You can also restrict the visibility of the function to only the crate it was defined in, by specifying `pub(crate)`: + +```rust +pub(crate) fn foo() {} //foo can only be called within its crate +``` + +All parameters in a function must have a type and all types are known at compile time. The parameter +is pre-pended with a colon and the parameter type. Multiple parameters are separated using a comma. + +```rust +fn foo(x : Field, y : Field){} +``` + +The return type of a function can be stated by using the `->` arrow notation. The function below +states that the foo function must return a `Field`. If the function returns no value, then the arrow +is omitted. + +```rust +fn foo(x : Field, y : Field) -> Field { + x + y +} +``` + +Note that a `return` keyword is unneeded in this case - the last expression in a function's body is +returned. + +## Main function + +If you're writing a binary, the `main` function is the starting point of your program. You can pass all types of expressions to it, as long as they have a fixed size at compile time: + +```rust +fn main(x : Field) // this is fine: passing a Field +fn main(x : [Field; 2]) // this is also fine: passing a Field with known size at compile-time +fn main(x : (Field, bool)) // 👌: passing a (Field, bool) tuple means size 2 +fn main(x : str<5>) // this is fine, as long as you pass a string of size 5 + +fn main(x : Vec) // can't compile, has variable size +fn main(x : [Field]) // can't compile, has variable size +fn main(....// i think you got it by now +``` + +Keep in mind [tests](../../getting_started/tooling/testing.md) don't differentiate between `main` and any other function. The following snippet passes tests, but won't compile or prove: + +```rust +fn main(x : [Field]) { + assert(x[0] == 1); +} + +#[test] +fn test_one() { + main([1, 2]); +} +``` + +```bash +$ nargo test +[testing] Running 1 test functions +[testing] Testing test_one... ok +[testing] All tests passed + +$ nargo check +The application panicked (crashed). +Message: Cannot have variable sized arrays as a parameter to main +``` + +## Call Expressions + +Calling a function in Noir is executed by using the function name and passing in the necessary +arguments. + +Below we show how to call the `foo` function from the `main` function using a call expression: + +```rust +fn main(x : Field, y : Field) { + let z = foo(x); +} + +fn foo(x : Field) -> Field { + x + x +} +``` + +## Methods + +You can define methods in Noir on any struct type in scope. + +```rust +struct MyStruct { + foo: Field, + bar: Field, +} + +impl MyStruct { + fn new(foo: Field) -> MyStruct { + MyStruct { + foo, + bar: 2, + } + } + + fn sum(self) -> Field { + self.foo + self.bar + } +} + +fn main() { + let s = MyStruct::new(40); + assert(s.sum() == 42); +} +``` + +Methods are just syntactic sugar for functions, so if we wanted to we could also call `sum` as +follows: + +```rust +assert(MyStruct::sum(s) == 42); +``` + +It is also possible to specialize which method is chosen depending on the [generic](./generics.md) type that is used. In this example, the `foo` function returns different values depending on its type: + +```rust +struct Foo {} + +impl Foo { + fn foo(self) -> Field { 1 } +} + +impl Foo { + fn foo(self) -> Field { 2 } +} + +fn main() { + let f1: Foo = Foo{}; + let f2: Foo = Foo{}; + assert(f1.foo() + f2.foo() == 3); +} +``` + +Also note that impls with the same method name defined in them cannot overlap. For example, if we already have `foo` defined for `Foo` and `Foo` like we do above, we cannot also define `foo` in an `impl Foo` since it would be ambiguous which version of `foo` to choose. + +```rust +// Including this impl in the same project as the above snippet would +// cause an overlapping impls error +impl Foo { + fn foo(self) -> Field { 3 } +} +``` + +## Lambdas + +Lambdas are anonymous functions. They follow the syntax of Rust - `|arg1, arg2, ..., argN| return_expression`. + +```rust +let add_50 = |val| val + 50; +assert(add_50(100) == 150); +``` + +See [Lambdas](./lambdas.md) for more details. + +## Attributes + +Attributes are metadata that can be applied to a function, using the following syntax: `#[attribute(value)]`. + +Supported attributes include: + +- **builtin**: the function is implemented by the compiler, for efficiency purposes. +- **deprecated**: mark the function as _deprecated_. Calling the function will generate a warning: `warning: use of deprecated function` +- **field**: Used to enable conditional compilation of code depending on the field size. See below for more details +- **oracle**: mark the function as _oracle_; meaning it is an external unconstrained function, implemented in noir_js. See [Unconstrained](./unconstrained.md) and [NoirJS](../../reference/NoirJS/noir_js/index.md) for more details. +- **test**: mark the function as unit tests. See [Tests](../../getting_started/tooling/testing.md) for more details + +### Field Attribute + +The field attribute defines which field the function is compatible for. The function is conditionally compiled, under the condition that the field attribute matches the Noir native field. +The field can be defined implicitly, by using the name of the elliptic curve usually associated to it - for instance bn254, bls12_381 - or explicitly by using the field (prime) order, in decimal or hexadecimal form. +As a result, it is possible to define multiple versions of a function with each version specialized for a different field attribute. This can be useful when a function requires different parameters depending on the underlying elliptic curve. + +Example: we define the function `foo()` three times below. Once for the default Noir bn254 curve, once for the field $\mathbb F_{23}$, which will normally never be used by Noir, and once again for the bls12_381 curve. + +```rust +#[field(bn254)] +fn foo() -> u32 { + 1 +} + +#[field(23)] +fn foo() -> u32 { + 2 +} + +// This commented code would not compile as foo would be defined twice because it is the same field as bn254 +// #[field(21888242871839275222246405745257275088548364400416034343698204186575808495617)] +// fn foo() -> u32 { +// 2 +// } + +#[field(bls12_381)] +fn foo() -> u32 { + 3 +} +``` + +If the field name is not known to Noir, it will discard the function. Field names are case insensitive. diff --git a/noir/docs/versioned_docs/version-v0.23.0/noir/concepts/generics.md b/noir/docs/versioned_docs/version-v0.23.0/noir/concepts/generics.md new file mode 100644 index 00000000000..ddd42bf1f9b --- /dev/null +++ b/noir/docs/versioned_docs/version-v0.23.0/noir/concepts/generics.md @@ -0,0 +1,106 @@ +--- +title: Generics +description: Learn how to use Generics in Noir +keywords: [Noir, Rust, generics, functions, structs] +sidebar_position: 7 +--- + +Generics allow you to use the same functions with multiple different concrete data types. You can +read more about the concept of generics in the Rust documentation +[here](https://doc.rust-lang.org/book/ch10-01-syntax.html). + +Here is a trivial example showing the identity function that supports any type. In Rust, it is +common to refer to the most general type as `T`. We follow the same convention in Noir. + +```rust +fn id(x: T) -> T { + x +} +``` + +## In Structs + +Generics are useful for specifying types in structs. For example, we can specify that a field in a +struct will be of a certain generic type. In this case `value` is of type `T`. + +```rust +struct RepeatedValue { + value: T, + count: Field, +} + +impl RepeatedValue { + fn print(self) { + for _i in 0 .. self.count { + println(self.value); + } + } +} + +fn main() { + let repeated = RepeatedValue { value: "Hello!", count: 2 }; + repeated.print(); +} +``` + +The `print` function will print `Hello!` an arbitrary number of times, twice in this case. + +If we want to be generic over array lengths (which are type-level integers), we can use numeric +generics. Using these looks just like using regular generics, but these generics can resolve to +integers at compile-time, rather than resolving to types. Here's an example of a struct that is +generic over the size of the array it contains internally: + +```rust +struct BigInt { + limbs: [u32; N], +} + +impl BigInt { + // `N` is in scope of all methods in the impl + fn first(first: BigInt, second: BigInt) -> Self { + assert(first.limbs != second.limbs); + first + + fn second(first: BigInt, second: Self) -> Self { + assert(first.limbs != second.limbs); + second + } +} +``` + +## Calling functions on generic parameters + +Since a generic type `T` can represent any type, how can we call functions on the underlying type? +In other words, how can we go from "any type `T`" to "any type `T` that has certain methods available?" + +This is what [traits](../concepts/traits) are for in Noir. Here's an example of a function generic over +any type `T` that implements the `Eq` trait for equality: + +```rust +fn first_element_is_equal(array1: [T; N], array2: [T; N]) -> bool + where T: Eq +{ + if (array1.len() == 0) | (array2.len() == 0) { + true + } else { + array1[0] == array2[0] + } +} + +fn main() { + assert(first_element_is_equal([1, 2, 3], [1, 5, 6])); + + // We can use first_element_is_equal for arrays of any type + // as long as we have an Eq impl for the types we pass in + let array = [MyStruct::new(), MyStruct::new()]; + assert(array_eq(array, array, MyStruct::eq)); +} + +impl Eq for MyStruct { + fn eq(self, other: MyStruct) -> bool { + self.foo == other.foo + } +} +``` + +You can find more details on traits and trait implementations on the [traits page](../concepts/traits). diff --git a/noir/docs/versioned_docs/version-v0.23.0/noir/concepts/lambdas.md b/noir/docs/versioned_docs/version-v0.23.0/noir/concepts/lambdas.md new file mode 100644 index 00000000000..be3c7e0b5ca --- /dev/null +++ b/noir/docs/versioned_docs/version-v0.23.0/noir/concepts/lambdas.md @@ -0,0 +1,81 @@ +--- +title: Lambdas +description: Learn how to use anonymous functions in Noir programming language. +keywords: [Noir programming language, lambda, closure, function, anonymous function] +sidebar_position: 9 +--- + +## Introduction + +Lambdas are anonymous functions. The syntax is `|arg1, arg2, ..., argN| return_expression`. + +```rust +let add_50 = |val| val + 50; +assert(add_50(100) == 150); +``` + +A block can be used as the body of a lambda, allowing you to declare local variables inside it: + +```rust +let cool = || { + let x = 100; + let y = 100; + x + y +} + +assert(cool() == 200); +``` + +## Closures + +Inside the body of a lambda, you can use variables defined in the enclosing function. Such lambdas are called **closures**. In this example `x` is defined inside `main` and is accessed from within the lambda: + +```rust +fn main() { + let x = 100; + let closure = || x + 150; + assert(closure() == 250); +} +``` + +## Passing closures to higher-order functions + +It may catch you by surprise that the following code fails to compile: + +```rust +fn foo(f: fn () -> Field) -> Field { + f() +} + +fn main() { + let (x, y) = (50, 50); + assert(foo(|| x + y) == 100); // error :( +} +``` + +The reason is that the closure's capture environment affects its type - we have a closure that captures two Fields and `foo` +expects a regular function as an argument - those are incompatible. +:::note + +Variables contained within the `||` are the closure's parameters, and the expression that follows it is the closure's body. The capture environment is comprised of any variables used in the closure's body that are not parameters. + +E.g. in |x| x + y, y would be a captured variable, but x would not be, since it is a parameter of the closure. + +::: +The syntax for the type of a closure is `fn[env](args) -> ret_type`, where `env` is the capture environment of the closure - +in this example that's `(Field, Field)`. + +The best solution in our case is to make `foo` generic over the environment type of its parameter, so that it can be called +with closures with any environment, as well as with regular functions: + +```rust +fn foo(f: fn[Env]() -> Field) -> Field { + f() +} + +fn main() { + let (x, y) = (50, 50); + assert(foo(|| x + y) == 100); // compiles fine + assert(foo(|| 60) == 60); // compiles fine +} +``` diff --git a/noir/docs/versioned_docs/version-v0.23.0/noir/concepts/mutability.md b/noir/docs/versioned_docs/version-v0.23.0/noir/concepts/mutability.md new file mode 100644 index 00000000000..9cc10429cb4 --- /dev/null +++ b/noir/docs/versioned_docs/version-v0.23.0/noir/concepts/mutability.md @@ -0,0 +1,93 @@ +--- +title: Mutability +description: + Learn about mutable variables, constants, and globals in Noir programming language. Discover how + to declare, modify, and use them in your programs. +keywords: [noir programming language, mutability in noir, mutable variables, constants, globals] +sidebar_position: 8 +--- + +Variables in noir can be declared mutable via the `mut` keyword. Mutable variables can be reassigned +to via an assignment expression. + +```rust +let x = 2; +x = 3; // error: x must be mutable to be assigned to + +let mut y = 3; +let y = 4; // OK +``` + +The `mut` modifier can also apply to patterns: + +```rust +let (a, mut b) = (1, 2); +a = 11; // error: a must be mutable to be assigned to +b = 12; // OK + +let mut (c, d) = (3, 4); +c = 13; // OK +d = 14; // OK + +// etc. +let MyStruct { x: mut y } = MyStruct { x: a }; +// y is now in scope +``` + +Note that mutability in noir is local and everything is passed by value, so if a called function +mutates its parameters then the parent function will keep the old value of the parameters. + +```rust +fn main() -> pub Field { + let x = 3; + helper(x); + x // x is still 3 +} + +fn helper(mut x: i32) { + x = 4; +} +``` + +## Comptime Values + +:::warning + +The 'comptime' keyword was removed in version 0.10. The comptime keyword and syntax are currently still kept and parsed for backwards compatibility, but are now deprecated and will issue a warning when used. `comptime` has been removed because it is no longer needed for accessing arrays. + +::: + +## Globals + +Noir also supports global variables. However, they must be known at compile-time. The global type can also be inferred by the compiler entirely. Globals can also be used to specify array +annotations for function parameters and can be imported from submodules. + +```rust +global N: Field = 5; // Same as `global N: Field = 5` + +fn main(x : Field, y : [Field; N]) { + let res = x * N; + + assert(res == y[0]); + + let res2 = x * my_submodule::N; + assert(res != res2); +} + +mod my_submodule { + use dep::std; + + global N: Field = 10; + + fn my_helper() -> Field { + let x = N; + x + } +} +``` + +## Why only local mutability? + +Witnesses in a proving system are immutable in nature. Noir aims to _closely_ mirror this setting +without applying additional overhead to the user. Modeling a mutable reference is not as +straightforward as on conventional architectures and would incur some possibly unexpected overhead. diff --git a/noir/docs/versioned_docs/version-v0.23.0/noir/concepts/ops.md b/noir/docs/versioned_docs/version-v0.23.0/noir/concepts/ops.md new file mode 100644 index 00000000000..60425cb8994 --- /dev/null +++ b/noir/docs/versioned_docs/version-v0.23.0/noir/concepts/ops.md @@ -0,0 +1,98 @@ +--- +title: Logical Operations +description: + Learn about the supported arithmetic and logical operations in the Noir programming language. + Discover how to perform operations on private input types, integers, and booleans. +keywords: + [ + Noir programming language, + supported operations, + arithmetic operations, + logical operations, + predicate operators, + bitwise operations, + short-circuiting, + backend, + ] +sidebar_position: 3 +--- + +# Operations + +## Table of Supported Operations + +| Operation | Description | Requirements | +| :-------- | :------------------------------------------------------------: | -------------------------------------: | +| + | Adds two private input types together | Types must be private input | +| - | Subtracts two private input types together | Types must be private input | +| \* | Multiplies two private input types together | Types must be private input | +| / | Divides two private input types together | Types must be private input | +| ^ | XOR two private input types together | Types must be integer | +| & | AND two private input types together | Types must be integer | +| \| | OR two private input types together | Types must be integer | +| \<\< | Left shift an integer by another integer amount | Types must be integer | +| >> | Right shift an integer by another integer amount | Types must be integer | +| ! | Bitwise not of a value | Type must be integer or boolean | +| \< | returns a bool if one value is less than the other | Upper bound must have a known bit size | +| \<= | returns a bool if one value is less than or equal to the other | Upper bound must have a known bit size | +| > | returns a bool if one value is more than the other | Upper bound must have a known bit size | +| >= | returns a bool if one value is more than or equal to the other | Upper bound must have a known bit size | +| == | returns a bool if one value is equal to the other | Both types must not be constants | +| != | returns a bool if one value is not equal to the other | Both types must not be constants | + +### Predicate Operators + +`<,<=, !=, == , >, >=` are known as predicate/comparison operations because they compare two values. +This differs from the operations such as `+` where the operands are used in _computation_. + +### Bitwise Operations Example + +```rust +fn main(x : Field) { + let y = x as u32; + let z = y & y; +} +``` + +`z` is implicitly constrained to be the result of `y & y`. The `&` operand is used to denote bitwise +`&`. + +> `x & x` would not compile as `x` is a `Field` and not an integer type. + +### Logical Operators + +Noir has no support for the logical operators `||` and `&&`. This is because encoding the +short-circuiting that these operators require can be inefficient for Noir's backend. Instead you can +use the bitwise operators `|` and `&` which operate identically for booleans, just without the +short-circuiting. + +```rust +let my_val = 5; + +let mut flag = 1; +if (my_val > 6) | (my_val == 0) { + flag = 0; +} +assert(flag == 1); + +if (my_val != 10) & (my_val < 50) { + flag = 0; +} +assert(flag == 0); +``` + +### Shorthand operators + +Noir shorthand operators for most of the above operators, namely `+=, -=, *=, /=, %=, &=, |=, ^=, <<=`, and `>>=`. These allow for more concise syntax. For example: + +```rust +let mut i = 0; +i = i + 1; +``` + +could be written as: + +```rust +let mut i = 0; +i += 1; +``` diff --git a/noir/docs/versioned_docs/version-v0.23.0/noir/concepts/oracles.md b/noir/docs/versioned_docs/version-v0.23.0/noir/concepts/oracles.md new file mode 100644 index 00000000000..2e6a6818d48 --- /dev/null +++ b/noir/docs/versioned_docs/version-v0.23.0/noir/concepts/oracles.md @@ -0,0 +1,23 @@ +--- +title: Oracles +description: Dive into how Noir supports Oracles via RPC calls, and learn how to declare an Oracle in Noir with our comprehensive guide. +keywords: + - Noir + - Oracles + - RPC Calls + - Unconstrained Functions + - Programming + - Blockchain +sidebar_position: 6 +--- + +Noir has support for Oracles via RPC calls. This means Noir will make an RPC call and use the return value for proof generation. + +Since Oracles are not resolved by Noir, they are [`unconstrained` functions](./unconstrained.md) + +You can declare an Oracle through the `#[oracle()]` flag. Example: + +```rust +#[oracle(get_number_sequence)] +unconstrained fn get_number_sequence(_size: Field) -> [Field] {} +``` diff --git a/noir/docs/versioned_docs/version-v0.23.0/noir/concepts/shadowing.md b/noir/docs/versioned_docs/version-v0.23.0/noir/concepts/shadowing.md new file mode 100644 index 00000000000..5ce6130d201 --- /dev/null +++ b/noir/docs/versioned_docs/version-v0.23.0/noir/concepts/shadowing.md @@ -0,0 +1,44 @@ +--- +title: Shadowing +sidebar_position: 12 +--- + +Noir allows for inheriting variables' values and re-declaring them with the same name similar to Rust, known as shadowing. + +For example, the following function is valid in Noir: + +```rust +fn main() { + let x = 5; + + { + let x = x * 2; + assert (x == 10); + } + + assert (x == 5); +} +``` + +In this example, a variable x is first defined with the value 5. + +The local scope that follows shadows the original x, i.e. creates a local mutable x based on the value of the original x. It is given a value of 2 times the original x. + +When we return to the main scope, x once again refers to just the original x, which stays at the value of 5. + +## Temporal mutability + +One way that shadowing is useful, in addition to ergonomics across scopes, is for temporarily mutating variables. + +```rust +fn main() { + let age = 30; + // age = age + 5; // Would error as `age` is immutable by default. + + let mut age = age + 5; // Temporarily mutates `age` with a new value. + + let age = age; // Locks `age`'s mutability again. + + assert (age == 35); +} +``` diff --git a/noir/docs/versioned_docs/version-v0.23.0/noir/concepts/traits.md b/noir/docs/versioned_docs/version-v0.23.0/noir/concepts/traits.md new file mode 100644 index 00000000000..ef1445a5907 --- /dev/null +++ b/noir/docs/versioned_docs/version-v0.23.0/noir/concepts/traits.md @@ -0,0 +1,389 @@ +--- +title: Traits +description: + Traits in Noir can be used to abstract out a common interface for functions across + several data types. +keywords: [noir programming language, traits, interfaces, generic, protocol] +sidebar_position: 14 +--- + +## Overview + +Traits in Noir are a useful abstraction similar to interfaces or protocols in other languages. Each trait defines +the interface of several methods contained within the trait. Types can then implement this trait by providing +implementations for these methods. For example in the program: + +```rust +struct Rectangle { + width: Field, + height: Field, +} + +impl Rectangle { + fn area(self) -> Field { + self.width * self.height + } +} + +fn log_area(r: Rectangle) { + println(r.area()); +} +``` + +We have a function `log_area` to log the area of a `Rectangle`. Now how should we change the program if we want this +function to work on `Triangle`s as well?: + +```rust +struct Triangle { + width: Field, + height: Field, +} + +impl Triangle { + fn area(self) -> Field { + self.width * self.height / 2 + } +} +``` + +Making `log_area` generic over all types `T` would be invalid since not all types have an `area` method. Instead, we can +introduce a new `Area` trait and make `log_area` generic over all types `T` that implement `Area`: + +```rust +trait Area { + fn area(self) -> Field; +} + +fn log_area(shape: T) where T: Area { + println(shape.area()); +} +``` + +We also need to explicitly implement `Area` for `Rectangle` and `Triangle`. We can do that by changing their existing +impls slightly. Note that the parameter types and return type of each of our `area` methods must match those defined +by the `Area` trait. + +```rust +impl Area for Rectangle { + fn area(self) -> Field { + self.width * self.height + } +} + +impl Area for Triangle { + fn area(self) -> Field { + self.width * self.height / 2 + } +} +``` + +Now we have a working program that is generic over any type of Shape that is used! Others can even use this program +as a library with their own types - such as `Circle` - as long as they also implement `Area` for these types. + +## Where Clauses + +As seen in `log_area` above, when we want to create a function or method that is generic over any type that implements +a trait, we can add a where clause to the generic function. + +```rust +fn log_area(shape: T) where T: Area { + println(shape.area()); +} +``` + +It is also possible to apply multiple trait constraints on the same variable at once by combining traits with the `+` +operator. Similarly, we can have multiple trait constraints by separating each with a comma: + +```rust +fn foo(elements: [T], thing: U) where + T: Default + Add + Eq, + U: Bar, +{ + let mut sum = T::default(); + + for element in elements { + sum += element; + } + + if sum == T::default() { + thing.bar(); + } +} +``` + +## Generic Implementations + +You can add generics to a trait implementation by adding the generic list after the `impl` keyword: + +```rust +trait Second { + fn second(self) -> Field; +} + +impl Second for (T, Field) { + fn second(self) -> Field { + self.1 + } +} +``` + +You can also implement a trait for every type this way: + +```rust +trait Debug { + fn debug(self); +} + +impl Debug for T { + fn debug(self) { + println(self); + } +} + +fn main() { + 1.debug(); +} +``` + +### Generic Trait Implementations With Where Clauses + +Where clauses can also be placed on trait implementations themselves to restrict generics in a similar way. +For example, while `impl Foo for T` implements the trait `Foo` for every type, `impl Foo for T where T: Bar` +will implement `Foo` only for types that also implement `Bar`. This is often used for implementing generic types. +For example, here is the implementation for array equality: + +```rust +impl Eq for [T; N] where T: Eq { + // Test if two arrays have the same elements. + // Because both arrays must have length N, we know their lengths already match. + fn eq(self, other: Self) -> bool { + let mut result = true; + + for i in 0 .. self.len() { + // The T: Eq constraint is needed to call == on the array elements here + result &= self[i] == other[i]; + } + + result + } +} +``` + +## Generic Traits + +Traits themselves can also be generic by placing the generic arguments after the trait name. These generics are in +scope of every item within the trait. + +```rust +trait Into { + // Convert `self` to type `T` + fn into(self) -> T; +} +``` + +When implementing generic traits the generic arguments of the trait must be specified. This is also true anytime +when referencing a generic trait (e.g. in a `where` clause). + +```rust +struct MyStruct { + array: [Field; 2], +} + +impl Into<[Field; 2]> for MyStruct { + fn into(self) -> [Field; 2] { + self.array + } +} + +fn as_array(x: T) -> [Field; 2] + where T: Into<[Field; 2]> +{ + x.into() +} + +fn main() { + let array = [1, 2]; + let my_struct = MyStruct { array }; + + assert_eq(as_array(my_struct), array); +} +``` + +## Trait Methods With No `self` + +A trait can contain any number of methods, each of which have access to the `Self` type which represents each type +that eventually implements the trait. Similarly, the `self` variable is available as well but is not required to be used. +For example, we can define a trait to create a default value for a type. This trait will need to return the `Self` type +but doesn't need to take any parameters: + +```rust +trait Default { + fn default() -> Self; +} +``` + +Implementing this trait can be done similarly to any other trait: + +```rust +impl Default for Field { + fn default() -> Field { + 0 + } +} + +struct MyType {} + +impl Default for MyType { + fn default() -> Field { + MyType {} + } +} +``` + +However, since there is no `self` parameter, we cannot call it via the method call syntax `object.method()`. +Instead, we'll need to refer to the function directly. This can be done either by referring to the +specific impl `MyType::default()` or referring to the trait itself `Default::default()`. In the later +case, type inference determines the impl that is selected. + +```rust +let my_struct = MyStruct::default(); + +let x: Field = Default::default(); +let result = x + Default::default(); +``` + +:::warning + +```rust +let _ = Default::default(); +``` + +If type inference cannot select which impl to use because of an ambiguous `Self` type, an impl will be +arbitrarily selected. This occurs most often when the result of a trait function call with no parameters +is unused. To avoid this, when calling a trait function with no `self` or `Self` parameters or return type, +always refer to it via the implementation type's namespace - e.g. `MyType::default()`. +This is set to change to an error in future Noir versions. + +::: + +## Default Method Implementations + +A trait can also have default implementations of its methods by giving a body to the desired functions. +Note that this body must be valid for all types that may implement the trait. As a result, the only +valid operations on `self` will be operations valid for any type or other operations on the trait itself. + +```rust +trait Numeric { + fn add(self, other: Self) -> Self; + + // Default implementation of double is (self + self) + fn double(self) -> Self { + self.add(self) + } +} +``` + +When implementing a trait with default functions, a type may choose to implement only the required functions: + +```rust +impl Numeric for Field { + fn add(self, other: Field) -> Field { + self + other + } +} +``` + +Or it may implement the optional methods as well: + +```rust +impl Numeric for u32 { + fn add(self, other: u32) -> u32 { + self + other + } + + fn double(self) -> u32 { + self * 2 + } +} +``` + +## Impl Specialization + +When implementing traits for a generic type it is possible to implement the trait for only a certain combination +of generics. This can be either as an optimization or because those specific generics are required to implement the trait. + +```rust +trait Sub { + fn sub(self, other: Self) -> Self; +} + +struct NonZero { + value: T, +} + +impl Sub for NonZero { + fn sub(self, other: Self) -> Self { + let value = self.value - other.value; + assert(value != 0); + NonZero { value } + } +} +``` + +## Overlapping Implementations + +Overlapping implementations are disallowed by Noir to ensure Noir's decision on which impl to select is never ambiguous. +This means if a trait `Foo` is already implemented +by a type `Bar` for all `T`, then we cannot also have a separate impl for `Bar` (or any other +type argument). Similarly, if there is an impl for all `T` such as `impl Debug for T`, we cannot create +any more impls to `Debug` for other types since it would be ambiguous which impl to choose for any given +method call. + +```rust +trait Trait {} + +// Previous impl defined here +impl Trait for (A, B) {} + +// error: Impl for type `(Field, Field)` overlaps with existing impl +impl Trait for (Field, Field) {} +``` + +## Trait Coherence + +Another restriction on trait implementations is coherence. This restriction ensures other crates cannot create +impls that may overlap with other impls, even if several unrelated crates are used as dependencies in the same +program. + +The coherence restriction is: to implement a trait, either the trait itself or the object type must be declared +in the crate the impl is in. + +In practice this often comes up when using types provided by libraries. If a library provides a type `Foo` that does +not implement a trait in the standard library such as `Default`, you may not `impl Default for Foo` in your own crate. +While restrictive, this prevents later issues or silent changes in the program if the `Foo` library later added its +own impl for `Default`. If you are a user of the `Foo` library in this scenario and need a trait not implemented by the +library your choices are to either submit a patch to the library or use the newtype pattern. + +### The Newtype Pattern + +The newtype pattern gets around the coherence restriction by creating a new wrapper type around the library type +that we cannot create `impl`s for. Since the new wrapper type is defined in our current crate, we can create +impls for any trait we need on it. + +```rust +struct Wrapper { + foo: dep::some_library::Foo, +} + +impl Default for Wrapper { + fn default() -> Wrapper { + Wrapper { + foo: dep::some_library::Foo::new(), + } + } +} +``` + +Since we have an impl for our own type, the behavior of this code will not change even if `some_library` is updated +to provide its own `impl Default for Foo`. The downside of this pattern is that it requires extra wrapping and +unwrapping of values when converting to and from the `Wrapper` and `Foo` types. diff --git a/noir/docs/versioned_docs/version-v0.23.0/noir/concepts/unconstrained.md b/noir/docs/versioned_docs/version-v0.23.0/noir/concepts/unconstrained.md new file mode 100644 index 00000000000..6b3424f7993 --- /dev/null +++ b/noir/docs/versioned_docs/version-v0.23.0/noir/concepts/unconstrained.md @@ -0,0 +1,95 @@ +--- +title: Unconstrained Functions +description: "Learn about what unconstrained functions in Noir are, how to use them and when you'd want to." + +keywords: [Noir programming language, unconstrained, open] +sidebar_position: 5 +--- + +Unconstrained functions are functions which do not constrain any of the included computation and allow for non-deterministic computation. + +## Why? + +Zero-knowledge (ZK) domain-specific languages (DSL) enable developers to generate ZK proofs from their programs by compiling code down to the constraints of an NP complete language (such as R1CS or PLONKish languages). However, the hard bounds of a constraint system can be very limiting to the functionality of a ZK DSL. + +Enabling a circuit language to perform unconstrained execution is a powerful tool. Said another way, unconstrained execution lets developers generate witnesses from code that does not generate any constraints. Being able to execute logic outside of a circuit is critical for both circuit performance and constructing proofs on information that is external to a circuit. + +Fetching information from somewhere external to a circuit can also be used to enable developers to improve circuit efficiency. + +A ZK DSL does not just prove computation, but proves that some computation was handled correctly. Thus, it is necessary that when we switch from performing some operation directly inside of a circuit to inside of an unconstrained environment that the appropriate constraints are still laid down elsewhere in the circuit. + +## Example + +An in depth example might help drive the point home. This example comes from the excellent [post](https://discord.com/channels/1113924620781883405/1124022445054111926/1128747641853972590) by Tom in the Noir Discord. + +Let's look at how we can optimize a function to turn a `u72` into an array of `u8`s. + +```rust +fn main(num: u72) -> pub [u8; 8] { + let mut out: [u8; 8] = [0; 8]; + for i in 0..8 { + out[i] = (num >> (56 - (i * 8)) as u72 & 0xff) as u8; + } + + out +} +``` + +``` +Total ACIR opcodes generated for language PLONKCSat { width: 3 }: 91 +Backend circuit size: 3619 +``` + +A lot of the operations in this function are optimized away by the compiler (all the bit-shifts turn into divisions by constants). However we can save a bunch of gates by casting to u8 a bit earlier. This automatically truncates the bit-shifted value to fit in a u8 which allows us to remove the XOR against 0xff. This saves us ~480 gates in total. + +```rust +fn main(num: u72) -> pub [u8; 8] { + let mut out: [u8; 8] = [0; 8]; + for i in 0..8 { + out[i] = (num >> (56 - (i * 8)) as u8; + } + + out +} +``` + +``` +Total ACIR opcodes generated for language PLONKCSat { width: 3 }: 75 +Backend circuit size: 3143 +``` + +Those are some nice savings already but we can do better. This code is all constrained so we're proving every step of calculating out using num, but we don't actually care about how we calculate this, just that it's correct. This is where brillig comes in. + +It turns out that truncating a u72 into a u8 is hard to do inside a snark, each time we do as u8 we lay down 4 ACIR opcodes which get converted into multiple gates. It's actually much easier to calculate num from out than the other way around. All we need to do is multiply each element of out by a constant and add them all together, both relatively easy operations inside a snark. + +We can then run u72_to_u8 as unconstrained brillig code in order to calculate out, then use that result in our constrained function and assert that if we were to do the reverse calculation we'd get back num. This looks a little like the below: + +```rust +fn main(num: u72) -> pub [u8; 8] { + let out = u72_to_u8(num); + + let mut reconstructed_num: u72 = 0; + for i in 0..8 { + reconstructed_num += (out[i] as u72 << (56 - (8 * i))); + } + assert(num == reconstructed_num); + out +} + +unconstrained fn u72_to_u8(num: u72) -> [u8; 8] { + let mut out: [u8; 8] = [0; 8]; + for i in 0..8 { + out[i] = (num >> (56 - (i * 8))) as u8; + } + out +} +``` + +``` +Total ACIR opcodes generated for language PLONKCSat { width: 3 }: 78 +Backend circuit size: 2902 +``` + +This ends up taking off another ~250 gates from our circuit! We've ended up with more ACIR opcodes than before but they're easier for the backend to prove (resulting in fewer gates). + +Generally we want to use brillig whenever there's something that's easy to verify but hard to compute within the circuit. For example, if you wanted to calculate a square root of a number it'll be a much better idea to calculate this in brillig and then assert that if you square the result you get back your number. diff --git a/noir/docs/versioned_docs/version-v0.23.0/noir/modules_packages_crates/_category_.json b/noir/docs/versioned_docs/version-v0.23.0/noir/modules_packages_crates/_category_.json new file mode 100644 index 00000000000..1debcfe7675 --- /dev/null +++ b/noir/docs/versioned_docs/version-v0.23.0/noir/modules_packages_crates/_category_.json @@ -0,0 +1,6 @@ +{ + "label": "Modules, Packages and Crates", + "position": 2, + "collapsible": true, + "collapsed": true +} diff --git a/noir/docs/versioned_docs/version-v0.23.0/noir/modules_packages_crates/crates_and_packages.md b/noir/docs/versioned_docs/version-v0.23.0/noir/modules_packages_crates/crates_and_packages.md new file mode 100644 index 00000000000..760a463094c --- /dev/null +++ b/noir/docs/versioned_docs/version-v0.23.0/noir/modules_packages_crates/crates_and_packages.md @@ -0,0 +1,43 @@ +--- +title: Crates and Packages +description: Learn how to use Crates and Packages in your Noir project +keywords: [Nargo, dependencies, package management, crates, package] +sidebar_position: 0 +--- + +## Crates + +A crate is the smallest amount of code that the Noir compiler considers at a time. +Crates can contain modules, and the modules may be defined in other files that get compiled with the crate, as we’ll see in the coming sections. + +### Crate Types + +A Noir crate can come in several forms: binaries, libraries or contracts. + +#### Binaries + +_Binary crates_ are programs which you can compile to an ACIR circuit which you can then create proofs against. Each must have a function called `main` that defines the ACIR circuit which is to be proved. + +#### Libraries + +_Library crates_ don't have a `main` function and they don't compile down to ACIR. Instead they define functionality intended to be shared with multiple projects, and eventually included in a binary crate. + +#### Contracts + +Contract crates are similar to binary crates in that they compile to ACIR which you can create proofs against. They are different in that they do not have a single `main` function, but are a collection of functions to be deployed to the [Aztec network](https://aztec.network). You can learn more about the technical details of Aztec in the [monorepo](https://github.com/AztecProtocol/aztec-packages) or contract [examples](https://github.com/AztecProtocol/aztec-packages/tree/master/yarn-project/noir-contracts/contracts). + +### Crate Root + +Every crate has a root, which is the source file that the compiler starts, this is also known as the root module. The Noir compiler does not enforce any conditions on the name of the file which is the crate root, however if you are compiling via Nargo the crate root must be called `lib.nr` or `main.nr` for library or binary crates respectively. + +## Packages + +A Nargo _package_ is a collection of one of more crates that provides a set of functionality. A package must include a Nargo.toml file. + +A package _must_ contain either a library or a binary crate, but not both. + +### Differences from Cargo Packages + +One notable difference between Rust's Cargo and Noir's Nargo is that while Cargo allows a package to contain an unlimited number of binary crates and a single library crate, Nargo currently only allows a package to contain a single crate. + +In future this restriction may be lifted to allow a Nargo package to contain both a binary and library crate or multiple binary crates. diff --git a/noir/docs/versioned_docs/version-v0.23.0/noir/modules_packages_crates/dependencies.md b/noir/docs/versioned_docs/version-v0.23.0/noir/modules_packages_crates/dependencies.md new file mode 100644 index 00000000000..a37dc401b7d --- /dev/null +++ b/noir/docs/versioned_docs/version-v0.23.0/noir/modules_packages_crates/dependencies.md @@ -0,0 +1,124 @@ +--- +title: Dependencies +description: + Learn how to specify and manage dependencies in Nargo, allowing you to upload packages to GitHub + and use them easily in your project. +keywords: [Nargo, dependencies, GitHub, package management, versioning] +sidebar_position: 1 +--- + +Nargo allows you to upload packages to GitHub and use them as dependencies. + +## Specifying a dependency + +Specifying a dependency requires a tag to a specific commit and the git url to the url containing +the package. + +Currently, there are no requirements on the tag contents. If requirements are added, it would follow +semver 2.0 guidelines. + +> Note: Without a `tag` , there would be no versioning and dependencies would change each time you +> compile your project. + +For example, to add the [ecrecover-noir library](https://github.com/colinnielsen/ecrecover-noir) to your project, add it to `Nargo.toml`: + +```toml +# Nargo.toml + +[dependencies] +ecrecover = {tag = "v0.8.0", git = "https://github.com/colinnielsen/ecrecover-noir"} +``` + +If the module is in a subdirectory, you can define a subdirectory in your git repository, for example: + +```toml +# Nargo.toml + +[dependencies] +easy_private_token_contract = {tag ="v0.1.0-alpha62", git = "https://github.com/AztecProtocol/aztec-packages", directory = "yarn-project/noir-contracts/contracts/easy_private_token_contract"} +``` + +## Specifying a local dependency + +You can also specify dependencies that are local to your machine. + +For example, this file structure has a library and binary crate + +```tree +├── binary_crate +│   ├── Nargo.toml +│   └── src +│   └── main.nr +└── lib_a + ├── Nargo.toml + └── src + └── lib.nr +``` + +Inside of the binary crate, you can specify: + +```toml +# Nargo.toml + +[dependencies] +lib_a = { path = "../lib_a" } +``` + +## Importing dependencies + +You can import a dependency to a Noir file using the following syntax. For example, to import the +ecrecover-noir library and local lib_a referenced above: + +```rust +use dep::ecrecover; +use dep::lib_a; +``` + +You can also import only the specific parts of dependency that you want to use, like so: + +```rust +use dep::std::hash::sha256; +use dep::std::scalar_mul::fixed_base_embedded_curve; +``` + +Lastly, as demonstrated in the +[elliptic curve example](../standard_library/cryptographic_primitives/ec_primitives#examples), you +can import multiple items in the same line by enclosing them in curly braces: + +```rust +use dep::std::ec::tecurve::affine::{Curve, Point}; +``` + +We don't have a way to consume libraries from inside a [workspace](./workspaces) as external dependencies right now. + +Inside a workspace, these are consumed as `{ path = "../to_lib" }` dependencies in Nargo.toml. + +## Dependencies of Dependencies + +Note that when you import a dependency, you also get access to all of the dependencies of that package. + +For example, the [phy_vector](https://github.com/resurgencelabs/phy_vector) library imports an [fraction](https://github.com/resurgencelabs/fraction) library. If you're importing the phy_vector library, then you can access the functions in fractions library like so: + +```rust +use dep::phy_vector; + +fn main(x : Field, y : pub Field) { + //... + let f = phy_vector::fraction::toFraction(true, 2, 1); + //... +} +``` + +## Available Libraries + +Noir does not currently have an official package manager. You can find a list of available Noir libraries in the [awesome-noir repo here](https://github.com/noir-lang/awesome-noir#libraries). + +Some libraries that are available today include: + +- [Standard Library](https://github.com/noir-lang/noir/tree/master/noir_stdlib) - the Noir Standard Library +- [Ethereum Storage Proof Verification](https://github.com/aragonzkresearch/noir-trie-proofs) - a library that contains the primitives necessary for RLP decoding (in the form of look-up table construction) and Ethereum state and storage proof verification (or verification of any trie proof involving 32-byte long keys) +- [BigInt](https://github.com/shuklaayush/noir-bigint) - a library that provides a custom BigUint56 data type, allowing for computations on large unsigned integers +- [ECrecover](https://github.com/colinnielsen/ecrecover-noir/tree/main) - a library to verify an ECDSA signature and return the source Ethereum address +- [Sparse Merkle Tree Verifier](https://github.com/vocdoni/smtverifier-noir/tree/main) - a library for verification of sparse Merkle trees +- [Signed Int](https://github.com/resurgencelabs/signed_int) - a library for accessing a custom Signed Integer data type, allowing access to negative numbers on Noir +- [Fraction](https://github.com/resurgencelabs/fraction) - a library for accessing fractional number data type in Noir, allowing results that aren't whole numbers diff --git a/noir/docs/versioned_docs/version-v0.23.0/noir/modules_packages_crates/modules.md b/noir/docs/versioned_docs/version-v0.23.0/noir/modules_packages_crates/modules.md new file mode 100644 index 00000000000..ae822a1cff4 --- /dev/null +++ b/noir/docs/versioned_docs/version-v0.23.0/noir/modules_packages_crates/modules.md @@ -0,0 +1,105 @@ +--- +title: Modules +description: + Learn how to organize your files using modules in Noir, following the same convention as Rust's + module system. Examples included. +keywords: [Noir, Rust, modules, organizing files, sub-modules] +sidebar_position: 2 +--- + +Noir's module system follows the same convention as the _newer_ version of Rust's module system. + +## Purpose of Modules + +Modules are used to organize files. Without modules all of your code would need to live in a single +file. In Noir, the compiler does not automatically scan all of your files to detect modules. This +must be done explicitly by the developer. + +## Examples + +### Importing a module in the crate root + +Filename : `src/main.nr` + +```rust +mod foo; + +fn main() { + foo::hello_world(); +} +``` + +Filename : `src/foo.nr` + +```rust +fn from_foo() {} +``` + +In the above snippet, the crate root is the `src/main.nr` file. The compiler sees the module +declaration `mod foo` which prompts it to look for a foo.nr file. + +Visually this module hierarchy looks like the following : + +``` +crate + ├── main + │ + └── foo + └── from_foo + +``` + +### Importing a module throughout the tree + +All modules are accessible from the `crate::` namespace. + +``` +crate + ├── bar + ├── foo + └── main + +``` + +In the above snippet, if `bar` would like to use functions in `foo`, it can do so by `use crate::foo::function_name`. + +### Sub-modules + +Filename : `src/main.nr` + +```rust +mod foo; + +fn main() { + foo::from_foo(); +} +``` + +Filename : `src/foo.nr` + +```rust +mod bar; +fn from_foo() {} +``` + +Filename : `src/foo/bar.nr` + +```rust +fn from_bar() {} +``` + +In the above snippet, we have added an extra module to the module tree; `bar`. `bar` is a submodule +of `foo` hence we declare bar in `foo.nr` with `mod bar`. Since `foo` is not the crate root, the +compiler looks for the file associated with the `bar` module in `src/foo/bar.nr` + +Visually the module hierarchy looks as follows: + +``` +crate + ├── main + │ + └── foo + ├── from_foo + └── bar + └── from_bar +``` diff --git a/noir/docs/versioned_docs/version-v0.23.0/noir/modules_packages_crates/workspaces.md b/noir/docs/versioned_docs/version-v0.23.0/noir/modules_packages_crates/workspaces.md new file mode 100644 index 00000000000..67a1dafa372 --- /dev/null +++ b/noir/docs/versioned_docs/version-v0.23.0/noir/modules_packages_crates/workspaces.md @@ -0,0 +1,40 @@ +--- +title: Workspaces +sidebar_position: 3 +--- + +Workspaces are a feature of nargo that allow you to manage multiple related Noir packages in a single repository. A workspace is essentially a group of related projects that share common build output directories and configurations. + +Each Noir project (with it's own Nargo.toml file) can be thought of as a package. Each package is expected to contain exactly one "named circuit", being the "name" defined in Nargo.toml with the program logic defined in `./src/main.nr`. + +For a project with the following structure: + +```tree +├── crates +│   ├── a +│   │   ├── Nargo.toml +│   │   └── src +│   │   └── main.nr +│   └── b +│   ├── Nargo.toml +│   └── src +│   └── main.nr +├── Nargo.toml +└── Prover.toml +``` + +You can define a workspace in Nargo.toml like so: + +```toml +[workspace] +members = ["crates/a", "crates/b"] +default-member = "crates/a" +``` + +`members` indicates which packages are included in the workspace. As such, all member packages of a workspace will be processed when the `--workspace` flag is used with various commands or if a `default-member` is not specified. + +`default-member` indicates which package various commands process by default. + +Libraries can be defined in a workspace. Inside a workspace, these are consumed as `{ path = "../to_lib" }` dependencies in Nargo.toml. + +Inside a workspace, these are consumed as `{ path = "../to_lib" }` dependencies in Nargo.toml. diff --git a/noir/docs/versioned_docs/version-v0.23.0/noir/standard_library/_category_.json b/noir/docs/versioned_docs/version-v0.23.0/noir/standard_library/_category_.json new file mode 100644 index 00000000000..af04c0933fd --- /dev/null +++ b/noir/docs/versioned_docs/version-v0.23.0/noir/standard_library/_category_.json @@ -0,0 +1,6 @@ +{ + "label": "Standard Library", + "position": 1, + "collapsible": true, + "collapsed": true +} diff --git a/noir/docs/versioned_docs/version-v0.23.0/noir/standard_library/black_box_fns.md b/noir/docs/versioned_docs/version-v0.23.0/noir/standard_library/black_box_fns.md new file mode 100644 index 00000000000..4b1efbd17de --- /dev/null +++ b/noir/docs/versioned_docs/version-v0.23.0/noir/standard_library/black_box_fns.md @@ -0,0 +1,45 @@ +--- +title: Black Box Functions +description: Black box functions are functions in Noir that rely on backends implementing support for specialized constraints. +keywords: [noir, black box functions] +--- + +Black box functions are functions in Noir that rely on backends implementing support for specialized constraints. This makes certain zk-snark unfriendly computations cheaper than if they were implemented in Noir. + +:::warning + +It is likely that not all backends will support a particular black box function. + +::: + +Because it is not guaranteed that all backends will support black box functions, it is possible that certain Noir programs won't compile against a particular backend if they use an unsupported black box function. It is possible to fallback to less efficient implementations written in Noir/ACIR in some cases. + +Black box functions are specified with the `#[foreign(black_box_fn)]` attribute. For example, the SHA256 function in the Noir [source code](https://github.com/noir-lang/noir/blob/v0.5.1/noir_stdlib/src/hash.nr) looks like: + +```rust +#[foreign(sha256)] +fn sha256(_input : [u8; N]) -> [u8; 32] {} +``` + +## Function list + +Here is a list of the current black box functions that are supported by UltraPlonk: + +- AES +- [SHA256](./cryptographic_primitives/hashes#sha256) +- [Schnorr signature verification](./cryptographic_primitives/schnorr) +- [Blake2s](./cryptographic_primitives/hashes#blake2s) +- [Pedersen Hash](./cryptographic_primitives/hashes#pedersen_hash) +- [Pedersen Commitment](./cryptographic_primitives/hashes#pedersen_commitment) +- [ECDSA signature verification](./cryptographic_primitives/ecdsa_sig_verification) +- [Fixed base scalar multiplication](./cryptographic_primitives/scalar) +- [Compute merkle root](./merkle_trees#compute_merkle_root) +- AND +- XOR +- RANGE +- [Keccak256](./cryptographic_primitives/hashes#keccak256) +- [Recursive proof verification](./recursion) + +Most black box functions are included as part of the Noir standard library, however `AND`, `XOR` and `RANGE` are used as part of the Noir language syntax. For instance, using the bitwise operator `&` will invoke the `AND` black box function. To ensure compatibility across backends, the ACVM has fallback implementations of `AND`, `XOR` and `RANGE` defined in its standard library which it can seamlessly fallback to if the backend doesn't support them. + +You can view the black box functions defined in the ACVM code [here](https://github.com/noir-lang/noir/blob/master/acvm-repo/acir/src/circuit/black_box_functions.rs). diff --git a/noir/docs/versioned_docs/version-v0.23.0/noir/standard_library/cryptographic_primitives/_category_.json b/noir/docs/versioned_docs/version-v0.23.0/noir/standard_library/cryptographic_primitives/_category_.json new file mode 100644 index 00000000000..5d694210bbf --- /dev/null +++ b/noir/docs/versioned_docs/version-v0.23.0/noir/standard_library/cryptographic_primitives/_category_.json @@ -0,0 +1,5 @@ +{ + "position": 0, + "collapsible": true, + "collapsed": true +} diff --git a/noir/docs/versioned_docs/version-v0.23.0/noir/standard_library/cryptographic_primitives/ec_primitives.md b/noir/docs/versioned_docs/version-v0.23.0/noir/standard_library/cryptographic_primitives/ec_primitives.md new file mode 100644 index 00000000000..d2b42d67b7c --- /dev/null +++ b/noir/docs/versioned_docs/version-v0.23.0/noir/standard_library/cryptographic_primitives/ec_primitives.md @@ -0,0 +1,102 @@ +--- +title: Elliptic Curve Primitives +keywords: [cryptographic primitives, Noir project] +sidebar_position: 4 +--- + +Data structures and methods on them that allow you to carry out computations involving elliptic +curves over the (mathematical) field corresponding to `Field`. For the field currently at our +disposal, applications would involve a curve embedded in BN254, e.g. the +[Baby Jubjub curve](https://eips.ethereum.org/EIPS/eip-2494). + +## Data structures + +### Elliptic curve configurations + +(`std::ec::{tecurve,montcurve,swcurve}::{affine,curvegroup}::Curve`), i.e. the specific elliptic +curve you want to use, which would be specified using any one of the methods +`std::ec::{tecurve,montcurve,swcurve}::{affine,curvegroup}::new` which take the coefficients in the +defining equation together with a generator point as parameters. You can find more detail in the +comments in +[`noir_stdlib/src/ec.nr`](https://github.com/noir-lang/noir/blob/master/noir_stdlib/src/ec.nr), but +the gist of it is that the elliptic curves of interest are usually expressed in one of the standard +forms implemented here (Twisted Edwards, Montgomery and Short Weierstraß), and in addition to that, +you could choose to use `affine` coordinates (Cartesian coordinates - the usual (x,y) - possibly +together with a point at infinity) or `curvegroup` coordinates (some form of projective coordinates +requiring more coordinates but allowing for more efficient implementations of elliptic curve +operations). Conversions between all of these forms are provided, and under the hood these +conversions are done whenever an operation is more efficient in a different representation (or a +mixed coordinate representation is employed). + +### Points + +(`std::ec::{tecurve,montcurve,swcurve}::{affine,curvegroup}::Point`), i.e. points lying on the +elliptic curve. For a curve configuration `c` and a point `p`, it may be checked that `p` +does indeed lie on `c` by calling `c.contains(p1)`. + +## Methods + +(given a choice of curve representation, e.g. use `std::ec::tecurve::affine::Curve` and use +`std::ec::tecurve::affine::Point`) + +- The **zero element** is given by `Point::zero()`, and we can verify whether a point `p: Point` is + zero by calling `p.is_zero()`. +- **Equality**: Points `p1: Point` and `p2: Point` may be checked for equality by calling + `p1.eq(p2)`. +- **Addition**: For `c: Curve` and points `p1: Point` and `p2: Point` on the curve, adding these two + points is accomplished by calling `c.add(p1,p2)`. +- **Negation**: For a point `p: Point`, `p.negate()` is its negation. +- **Subtraction**: For `c` and `p1`, `p2` as above, subtracting `p2` from `p1` is accomplished by + calling `c.subtract(p1,p2)`. +- **Scalar multiplication**: For `c` as above, `p: Point` a point on the curve and `n: Field`, + scalar multiplication is given by `c.mul(n,p)`. If instead `n :: [u1; N]`, i.e. `n` is a bit + array, the `bit_mul` method may be used instead: `c.bit_mul(n,p)` +- **Multi-scalar multiplication**: For `c` as above and arrays `n: [Field; N]` and `p: [Point; N]`, + multi-scalar multiplication is given by `c.msm(n,p)`. +- **Coordinate representation conversions**: The `into_group` method converts a point or curve + configuration in the affine representation to one in the CurveGroup representation, and + `into_affine` goes in the other direction. +- **Curve representation conversions**: `tecurve` and `montcurve` curves and points are equivalent + and may be converted between one another by calling `into_montcurve` or `into_tecurve` on their + configurations or points. `swcurve` is more general and a curve c of one of the other two types + may be converted to this representation by calling `c.into_swcurve()`, whereas a point `p` lying + on the curve given by `c` may be mapped to its corresponding `swcurve` point by calling + `c.map_into_swcurve(p)`. +- **Map-to-curve methods**: The Elligator 2 method of mapping a field element `n: Field` into a + `tecurve` or `montcurve` with configuration `c` may be called as `c.elligator2_map(n)`. For all of + the curve configurations, the SWU map-to-curve method may be called as `c.swu_map(z,n)`, where + `z: Field` depends on `Field` and `c` and must be chosen by the user (the conditions it needs to + satisfy are specified in the comments + [here](https://github.com/noir-lang/noir/blob/master/noir_stdlib/src/ec.nr)). + +## Examples + +The +[ec_baby_jubjub test](https://github.com/noir-lang/noir/blob/master/test_programs/compile_success_empty/ec_baby_jubjub/src/main.nr) +illustrates all of the above primitives on various forms of the Baby Jubjub curve. A couple of more +interesting examples in Noir would be: + +Public-key cryptography: Given an elliptic curve and a 'base point' on it, determine the public key +from the private key. This is a matter of using scalar multiplication. In the case of Baby Jubjub, +for example, this code would do: + +```rust +use dep::std::ec::tecurve::affine::{Curve, Point}; + +fn bjj_pub_key(priv_key: Field) -> Point +{ + + let bjj = Curve::new(168700, 168696, G::new(995203441582195749578291179787384436505546430278305826713579947235728471134,5472060717959818805561601436314318772137091100104008585924551046643952123905)); + + let base_pt = Point::new(5299619240641551281634865583518297030282874472190772894086521144482721001553, 16950150798460657717958625567821834550301663161624707787222815936182638968203); + + bjj.mul(priv_key,base_pt) +} +``` + +This would come in handy in a Merkle proof. + +- EdDSA signature verification: This is a matter of combining these primitives with a suitable hash + function. See + [feat(stdlib): EdDSA sig verification noir#1136](https://github.com/noir-lang/noir/pull/1136) for + the case of Baby Jubjub and the Poseidon hash function. diff --git a/noir/docs/versioned_docs/version-v0.23.0/noir/standard_library/cryptographic_primitives/ecdsa_sig_verification.mdx b/noir/docs/versioned_docs/version-v0.23.0/noir/standard_library/cryptographic_primitives/ecdsa_sig_verification.mdx new file mode 100644 index 00000000000..1376c51dfde --- /dev/null +++ b/noir/docs/versioned_docs/version-v0.23.0/noir/standard_library/cryptographic_primitives/ecdsa_sig_verification.mdx @@ -0,0 +1,46 @@ +--- +title: ECDSA Signature Verification +description: Learn about the cryptographic primitives regarding ECDSA over the secp256k1 and secp256r1 curves +keywords: [cryptographic primitives, Noir project, ecdsa, secp256k1, secp256r1, signatures] +sidebar_position: 3 +--- + +import BlackBoxInfo from '@site/src/components/Notes/_blackbox.mdx'; + +Noir supports ECDSA signatures verification over the secp256k1 and secp256r1 curves. + +## ecdsa_secp256k1::verify_signature + +Verifier for ECDSA Secp256k1 signatures + +```rust +fn verify_signature(_public_key_x : [u8; 32], _public_key_y : [u8; 32], _signature: [u8; 64], _message: [u8]) -> bool +``` + +example: + +```rust +fn main(hashed_message : [u8;32], pub_key_x : [u8;32], pub_key_y : [u8;32], signature : [u8;64]) { + let valid_signature = std::ecdsa_secp256k1::verify_signature(pub_key_x, pub_key_y, signature, hashed_message); + assert(valid_signature); +} +``` + +## ecdsa_secp256r1::verify_signature + +Verifier for ECDSA Secp256r1 signatures + +```rust +fn verify_signature(_public_key_x : [u8; 32], _public_key_y : [u8; 32], _signature: [u8; 64], _message: [u8]) -> bool +``` + +example: + +```rust +fn main(hashed_message : [u8;32], pub_key_x : [u8;32], pub_key_y : [u8;32], signature : [u8;64]) { + let valid_signature = std::ecdsa_secp256r1::verify_signature(pub_key_x, pub_key_y, signature, hashed_message); + assert(valid_signature); +} +``` + + diff --git a/noir/docs/versioned_docs/version-v0.23.0/noir/standard_library/cryptographic_primitives/eddsa.mdx b/noir/docs/versioned_docs/version-v0.23.0/noir/standard_library/cryptographic_primitives/eddsa.mdx new file mode 100644 index 00000000000..a9c10da6c06 --- /dev/null +++ b/noir/docs/versioned_docs/version-v0.23.0/noir/standard_library/cryptographic_primitives/eddsa.mdx @@ -0,0 +1,18 @@ +--- +title: EdDSA Verification +description: Learn about the cryptographic primitives regarding EdDSA +keywords: [cryptographic primitives, Noir project, eddsa, signatures] +sidebar_position: 5 +--- + +import BlackBoxInfo from '@site/src/components/Notes/_blackbox.mdx'; + +## eddsa::eddsa_poseidon_verify + +Verifier for EdDSA signatures + +```rust +fn eddsa_poseidon_verify(public_key_x : Field, public_key_y : Field, signature_s: Field, signature_r8_x: Field, signature_r8_y: Field, message: Field) -> bool +``` + + diff --git a/noir/docs/versioned_docs/version-v0.23.0/noir/standard_library/cryptographic_primitives/hashes.mdx b/noir/docs/versioned_docs/version-v0.23.0/noir/standard_library/cryptographic_primitives/hashes.mdx new file mode 100644 index 00000000000..3c5f7f79603 --- /dev/null +++ b/noir/docs/versioned_docs/version-v0.23.0/noir/standard_library/cryptographic_primitives/hashes.mdx @@ -0,0 +1,167 @@ +--- +title: Hash methods +description: + Learn about the cryptographic primitives ready to use for any Noir project, including sha256, + blake2s, pedersen, mimc_bn254 and mimc +keywords: + [cryptographic primitives, Noir project, sha256, blake2s, pedersen, mimc_bn254, mimc, hash] +sidebar_position: 0 +--- + +import BlackBoxInfo from '@site/src/components/Notes/_blackbox.mdx'; + +## sha256 + +Given an array of bytes, returns the resulting sha256 hash. + +```rust +fn sha256(_input : [u8]) -> [u8; 32] +``` + +example: + +```rust +fn main() { + let x = [163, 117, 178, 149]; // some random bytes + let hash = std::hash::sha256(x); +} +``` + + + +## blake2s + +Given an array of bytes, returns an array with the Blake2 hash + +```rust +fn blake2s(_input : [u8]) -> [u8; 32] +``` + +example: + +```rust +fn main() { + let x = [163, 117, 178, 149]; // some random bytes + let hash = std::hash::blake2s(x); +} +``` + + + +## pedersen_hash + +Given an array of Fields, returns the Pedersen hash. + +```rust +fn pedersen_hash(_input : [Field]) -> Field +``` + +example: + +```rust +fn main() { + let x = [163, 117, 178, 149]; // some random bytes + let hash = std::hash::pedersen_hash(x); +} +``` + + + + + +## pedersen_commitment + +Given an array of Fields, returns the Pedersen commitment. + +```rust +fn pedersen_commitment(_input : [Field]) -> [Field; 2] +``` + +example: + +```rust +fn main() { + let x = [163, 117, 178, 149]; // some random bytes + let commitment = std::hash::pedersen_commitment(x); +} +``` + + + +## keccak256 + +Given an array of bytes (`u8`), returns the resulting keccak hash as an array of 32 bytes +(`[u8; 32]`). Specify a message_size to hash only the first `message_size` bytes +of the input. + +```rust +fn keccak256(_input : [u8; N], _message_size: u32) -> [u8; 32] +``` + +example: + +```rust +fn main() { + let x = [163, 117, 178, 149]; // some random bytes + let message_size = 4; + let hash = std::hash::keccak256(x, message_size); +} +``` + + + +## poseidon + +Given an array of Fields, returns a new Field with the Poseidon Hash. Mind that you need to specify +how many inputs are there to your Poseidon function. + +```rust +// example for hash_1, hash_2 accepts an array of length 2, etc +fn hash_1(input: [Field; 1]) -> Field +``` + +example: + +```rust +fn main() +{ + let hash_2 = std::hash::poseidon::bn254::hash_2([1, 2]); + assert(hash2 == 0x115cc0f5e7d690413df64c6b9662e9cf2a3617f2743245519e19607a4417189a); +} +``` + +## mimc_bn254 and mimc + +`mimc_bn254` is `mimc`, but with hardcoded parameters for the BN254 curve. You can use it by +providing an array of Fields, and it returns a Field with the hash. You can use the `mimc` method if +you're willing to input your own constants: + +```rust +fn mimc(x: Field, k: Field, constants: [Field; N], exp : Field) -> Field +``` + +otherwise, use the `mimc_bn254` method: + +```rust +fn mimc_bn254(array: [Field; N]) -> Field +``` + +example: + +```rust + +fn main() { + let x = [163, 117, 178, 149]; // some random bytes + let hash = std::hash::mimc::mimc_bn254(x); +} +``` + +## hash_to_field + +```rust +fn hash_to_field(_input : [Field; N]) -> Field {} +``` + +Calculates the `blake2s` hash of the inputs and returns the hash modulo the field modulus to return +a value which can be represented as a `Field`. + diff --git a/noir/docs/versioned_docs/version-v0.23.0/noir/standard_library/cryptographic_primitives/index.md b/noir/docs/versioned_docs/version-v0.23.0/noir/standard_library/cryptographic_primitives/index.md new file mode 100644 index 00000000000..650f30165d5 --- /dev/null +++ b/noir/docs/versioned_docs/version-v0.23.0/noir/standard_library/cryptographic_primitives/index.md @@ -0,0 +1,14 @@ +--- +title: Cryptographic Primitives +description: + Learn about the cryptographic primitives ready to use for any Noir project +keywords: + [ + cryptographic primitives, + Noir project, + ] +--- + +The Noir team is progressively adding new cryptographic primitives to the standard library. Reach out for news or if you would be interested in adding more of these calculations in Noir. + +Some methods are available thanks to the Aztec backend, not being performed using Noir. When using other backends, these methods may or may not be supplied. diff --git a/noir/docs/versioned_docs/version-v0.23.0/noir/standard_library/cryptographic_primitives/scalar.mdx b/noir/docs/versioned_docs/version-v0.23.0/noir/standard_library/cryptographic_primitives/scalar.mdx new file mode 100644 index 00000000000..aa4fb8cbaed --- /dev/null +++ b/noir/docs/versioned_docs/version-v0.23.0/noir/standard_library/cryptographic_primitives/scalar.mdx @@ -0,0 +1,28 @@ +--- +title: Scalar multiplication +description: See how you can perform scalar multiplications over a fixed base in Noir +keywords: [cryptographic primitives, Noir project, scalar multiplication] +sidebar_position: 1 +--- + +import BlackBoxInfo from '@site/src/components/Notes/_blackbox.mdx'; + +## scalar_mul::fixed_base_embedded_curve + +Performs scalar multiplication over the embedded curve whose coordinates are defined by the +configured noir field. For the BN254 scalar field, this is BabyJubJub or Grumpkin. + +```rust +fn fixed_base_embedded_curve(_input : Field) -> [Field; 2] +``` + +example + +```rust +fn main(x : Field) { + let scal = std::scalar_mul::fixed_base_embedded_curve(x); + println(scal); +} +``` + + diff --git a/noir/docs/versioned_docs/version-v0.23.0/noir/standard_library/cryptographic_primitives/schnorr.mdx b/noir/docs/versioned_docs/version-v0.23.0/noir/standard_library/cryptographic_primitives/schnorr.mdx new file mode 100644 index 00000000000..7a2c9c20226 --- /dev/null +++ b/noir/docs/versioned_docs/version-v0.23.0/noir/standard_library/cryptographic_primitives/schnorr.mdx @@ -0,0 +1,38 @@ +--- +title: Schnorr Signatures +description: Learn how you can verify Schnorr signatures using Noir +keywords: [cryptographic primitives, Noir project, schnorr, signatures] +sidebar_position: 2 +--- + +import BlackBoxInfo from '@site/src/components/Notes/_blackbox.mdx'; + +## schnorr::verify_signature + +Verifier for Schnorr signatures over the embedded curve (for BN254 it is Grumpkin). + +```rust +fn verify_signature(_public_key_x: Field, _public_key_y: Field, _signature: [u8; 64], _message: [u8]) -> bool +``` + +where `_signature` can be generated like so using the npm package +[@noir-lang/barretenberg](https://www.npmjs.com/package/@noir-lang/barretenberg) + +```js +const { BarretenbergWasm } = require('@noir-lang/barretenberg/dest/wasm'); +const { Schnorr } = require('@noir-lang/barretenberg/dest/crypto/schnorr'); + +... + +const barretenberg = await BarretenbergWasm.new(); +const schnorr = new Schnorr(barretenberg); +const pubKey = schnorr.computePublicKey(privateKey); +const message = ... +const signature = Array.from( + schnorr.constructSignature(hash, privateKey).toBuffer() +); + +... +``` + + diff --git a/noir/docs/versioned_docs/version-v0.23.0/noir/standard_library/logging.md b/noir/docs/versioned_docs/version-v0.23.0/noir/standard_library/logging.md new file mode 100644 index 00000000000..db75ef9f86f --- /dev/null +++ b/noir/docs/versioned_docs/version-v0.23.0/noir/standard_library/logging.md @@ -0,0 +1,78 @@ +--- +title: Logging +description: + Learn how to use the println statement for debugging in Noir with this tutorial. Understand the + basics of logging in Noir and how to implement it in your code. +keywords: + [ + noir logging, + println statement, + print statement, + debugging in noir, + noir std library, + logging tutorial, + basic logging in noir, + noir logging implementation, + noir debugging techniques, + rust, + ] +--- + +The standard library provides two familiar statements you can use: `println` and `print`. Despite being a limited implementation of rust's `println!` and `print!` macros, these constructs can be useful for debugging. + +You can print the output of both statements in your Noir code by using the `nargo execute` command or the `--show-output` flag when using `nargo test` (provided there are print statements in your tests). + +It is recommended to use `nargo execute` if you want to debug failing constraints with `println` or `print` statements. This is due to every input in a test being a constant rather than a witness, so we issue an error during compilation while we only print during execution (which comes after compilation). Neither `println`, nor `print` are callable for failed constraints caught at compile time. + +Both `print` and `println` are generic functions which can work on integers, fields, strings, and even structs or expressions. Note however, that slices are currently unsupported. For example: + +```rust +struct Person { + age: Field, + height: Field, +} + +fn main(age: Field, height: Field) { + let person = Person { + age: age, + height: height, + }; + println(person); + println(age + height); + println("Hello world!"); +} +``` + +You can print different types in the same statement (including strings) with a type called `fmtstr`. It can be specified in the same way as a normal string, just prepended with an "f" character: + +```rust + let fmt_str = f"i: {i}, j: {j}"; + println(fmt_str); + + let s = myStruct { y: x, x: y }; + println(s); + + println(f"i: {i}, s: {s}"); + + println(x); + println([x, y]); + + let foo = fooStruct { my_struct: s, foo: 15 }; + println(f"s: {s}, foo: {foo}"); + + println(15); // prints 0x0f, implicit Field + println(-1 as u8); // prints 255 + println(-1 as i8); // prints -1 +``` + +Examples shown above are interchangeable between the two `print` statements: + +```rust +let person = Person { age : age, height : height }; + +println(person); +print(person); + +println("Hello world!"); // Prints with a newline at the end of the input +print("Hello world!"); // Prints the input and keeps cursor on the same line +``` diff --git a/noir/docs/versioned_docs/version-v0.23.0/noir/standard_library/merkle_trees.md b/noir/docs/versioned_docs/version-v0.23.0/noir/standard_library/merkle_trees.md new file mode 100644 index 00000000000..fa488677884 --- /dev/null +++ b/noir/docs/versioned_docs/version-v0.23.0/noir/standard_library/merkle_trees.md @@ -0,0 +1,58 @@ +--- +title: Merkle Trees +description: Learn about Merkle Trees in Noir with this tutorial. Explore the basics of computing a merkle root using a proof, with examples. +keywords: + [ + Merkle trees in Noir, + Noir programming language, + check membership, + computing root from leaf, + Noir Merkle tree implementation, + Merkle tree tutorial, + Merkle tree code examples, + Noir libraries, + pedersen hash., + ] +--- + +## compute_merkle_root + +Returns the root of the tree from the provided leaf and its hash path, using a [Pedersen hash](./cryptographic_primitives/hashes.mdx#pedersen_hash). + +```rust +fn compute_merkle_root(leaf : Field, index : Field, hash_path: [Field]) -> Field +``` + +example: + +```rust +/** + // these values are for this example only + index = "0" + priv_key = "0x000000000000000000000000000000000000000000000000000000616c696365" + secret = "0x1929ea3ab8d9106a899386883d9428f8256cfedb3c4f6b66bf4aa4d28a79988f" + note_hash_path = [ + "0x1e61bdae0f027b1b2159e1f9d3f8d00fa668a952dddd822fda80dc745d6f65cc", + "0x0e4223f3925f98934393c74975142bd73079ab0621f4ee133cee050a3c194f1a", + "0x2fd7bb412155bf8693a3bd2a3e7581a679c95c68a052f835dddca85fa1569a40" + ] + */ +fn main(index: Field, priv_key: Field, secret: Field, note_hash_path: [Field; 3]) { + + let pubkey = std::scalar_mul::fixed_base_embedded_curve(priv_key); + let pubkey_x = pubkey[0]; + let pubkey_y = pubkey[1]; + let note_commitment = std::hash::pedersen([pubkey_x, pubkey_y, secret]); + + let root = std::merkle::compute_merkle_root(note_commitment[0], index, note_hash_path); + println(root); +} +``` + +To check merkle tree membership: + +1. Include a merkle root as a program input. +2. Compute the merkle root of a given leaf, index and hash path. +3. Assert the merkle roots are equal. + +For more info about merkle trees, see the Wikipedia [page](https://en.wikipedia.org/wiki/Merkle_tree). diff --git a/noir/docs/versioned_docs/version-v0.23.0/noir/standard_library/options.md b/noir/docs/versioned_docs/version-v0.23.0/noir/standard_library/options.md new file mode 100644 index 00000000000..970c9cfbf11 --- /dev/null +++ b/noir/docs/versioned_docs/version-v0.23.0/noir/standard_library/options.md @@ -0,0 +1,97 @@ +--- +title: Option Type +--- + +The `Option` type is a way to express that a value might be present (`Some(T))` or absent (`None`). It's a safer way to handle potential absence of values, compared to using nulls in many other languages. + +```rust +struct Option { + None, + Some(T), +} +``` + +The `Option` type, already imported into your Noir program, can be used directly: + +```rust +fn main() { + let none = Option::none(); + let some = Option::some(3); +} +``` + +See [this test](https://github.com/noir-lang/noir/blob/5cbfb9c4a06c8865c98ff2b594464b037d821a5c/crates/nargo_cli/tests/test_data/option/src/main.nr) for a more comprehensive set of examples of each of the methods described below. + +## Methods + +### none + +Constructs a none value. + +### some + +Constructs a some wrapper around a given value. + +### is_none + +Returns true if the Option is None. + +### is_some + +Returns true of the Option is Some. + +### unwrap + +Asserts `self.is_some()` and returns the wrapped value. + +### unwrap_unchecked + +Returns the inner value without asserting `self.is_some()`. This method can be useful within an if condition when we already know that `option.is_some()`. If the option is None, there is no guarantee what value will be returned, only that it will be of type T for an `Option`. + +### unwrap_or + +Returns the wrapped value if `self.is_some()`. Otherwise, returns the given default value. + +### unwrap_or_else + +Returns the wrapped value if `self.is_some()`. Otherwise, calls the given function to return a default value. + +### map + +If self is `Some(x)`, this returns `Some(f(x))`. Otherwise, this returns `None`. + +### map_or + +If self is `Some(x)`, this returns `f(x)`. Otherwise, this returns the given default value. + +### map_or_else + +If self is `Some(x)`, this returns `f(x)`. Otherwise, this returns `default()`. + +### and + +Returns None if self is None. Otherwise, this returns `other`. + +### and_then + +If self is None, this returns None. Otherwise, this calls the given function with the Some value contained within self, and returns the result of that call. In some languages this function is called `flat_map` or `bind`. + +### or + +If self is Some, return self. Otherwise, return `other`. + +### or_else + +If self is Some, return self. Otherwise, return `default()`. + +### xor + +If only one of the two Options is Some, return that option. Otherwise, if both options are Some or both are None, None is returned. + +### filter + +Returns `Some(x)` if self is `Some(x)` and `predicate(x)` is true. Otherwise, this returns `None`. + +### flatten + +Flattens an `Option>` into a `Option`. This returns `None` if the outer Option is None. Otherwise, this returns the inner Option. diff --git a/noir/docs/versioned_docs/version-v0.23.0/noir/standard_library/recursion.md b/noir/docs/versioned_docs/version-v0.23.0/noir/standard_library/recursion.md new file mode 100644 index 00000000000..f252150c8b5 --- /dev/null +++ b/noir/docs/versioned_docs/version-v0.23.0/noir/standard_library/recursion.md @@ -0,0 +1,68 @@ +--- +title: Recursive Proofs +description: Learn about how to write recursive proofs in Noir. +keywords: [recursion, recursive proofs, verification_key, verify_proof] +--- + +Noir supports recursively verifying proofs, meaning you verify the proof of a Noir program in another Noir program. This enables creating proofs of arbitrary size by doing step-wise verification of smaller components of a large proof. + +Read [the explainer on recursion](../../explainers/explainer-recursion.md) to know more about this function and the [guide on how to use it.](../../how_to/how-to-recursion.md) + +```rust +#[foreign(verify_proof)] +fn verify_proof(_verification_key : [Field], _proof : [Field], _public_input : Field, _key_hash : Field) {} +``` + +:::info + +This is a black box function. Read [this section](./black_box_fns) to learn more about black box functions in Noir. + +::: + +## Example usage + +```rust +use dep::std; + +fn main( + verification_key : [Field; 114], + proof : [Field; 93], + public_inputs : [Field; 1], + key_hash : Field, + proof_b : [Field; 93], +) { + std::verify_proof( + verification_key.as_slice(), + proof.as_slice(), + public_inputs.as_slice(), + key_hash + ); + + std::verify_proof( + verification_key.as_slice(), + proof_b.as_slice(), + public_inputs.as_slice(), + key_hash + ); +} +``` + +You can see a full example of recursive proofs in [this example recursion demo repo](https://github.com/noir-lang/noir-examples/tree/master/recursion). + +## Parameters + +### `verification_key` + +The verification key for the zk program that is being verified. + +### `proof` + +The proof for the zk program that is being verified. + +### `public_inputs` + +These represent the public inputs of the proof we are verifying. + +### `key_hash` + +A key hash is used to check the validity of the verification key. The circuit implementing this opcode can use this hash to ensure that the key provided to the circuit matches the key produced by the circuit creator. diff --git a/noir/docs/versioned_docs/version-v0.23.0/noir/standard_library/traits.md b/noir/docs/versioned_docs/version-v0.23.0/noir/standard_library/traits.md new file mode 100644 index 00000000000..f2960ca5080 --- /dev/null +++ b/noir/docs/versioned_docs/version-v0.23.0/noir/standard_library/traits.md @@ -0,0 +1,284 @@ +--- +title: Traits +description: Noir's stdlib provides a few commonly used traits. +keywords: [traits, trait, interface, protocol, default, add, eq] +--- + +## `std::default` + +### `std::default::Default` + +```rust +trait Default { + fn default() -> Self; +} +``` + +Constructs a default value of a type. + +Implementations: +```rust +impl Default for Field { .. } + +impl Default for i8 { .. } +impl Default for i16 { .. } +impl Default for i32 { .. } +impl Default for i64 { .. } + +impl Default for u8 { .. } +impl Default for u16 { .. } +impl Default for u32 { .. } +impl Default for u64 { .. } + +impl Default for () { .. } +impl Default for bool { .. } + +impl Default for [T; N] + where T: Default { .. } + +impl Default for (A, B) + where A: Default, B: Default { .. } + +impl Default for (A, B, C) + where A: Default, B: Default, C: Default { .. } + +impl Default for (A, B, C, D) + where A: Default, B: Default, C: Default, D: Default { .. } + +impl Default for (A, B, C, D, E) + where A: Default, B: Default, C: Default, D: Default, E: Default { .. } +``` + +For primitive integer types, the return value of `default` is `0`. Container +types such as arrays are filled with default values of their element type. + +## `std::cmp` + +### `std::cmp::Eq` + +```rust +trait Eq { + fn eq(self, other: Self) -> bool; +} +``` +Returns `true` if `self` is equal to `other`. Implementing this trait on a type +allows the type to be used with `==` and `!=`. + +Implementations: +```rust +impl Eq for Field { .. } + +impl Eq for i8 { .. } +impl Eq for i16 { .. } +impl Eq for i32 { .. } +impl Eq for i64 { .. } + +impl Eq for u8 { .. } +impl Eq for u16 { .. } +impl Eq for u32 { .. } +impl Eq for u64 { .. } + +impl Eq for () { .. } +impl Eq for bool { .. } + +impl Eq for [T; N] + where T: Eq { .. } + +impl Eq for (A, B) + where A: Eq, B: Eq { .. } + +impl Eq for (A, B, C) + where A: Eq, B: Eq, C: Eq { .. } + +impl Eq for (A, B, C, D) + where A: Eq, B: Eq, C: Eq, D: Eq { .. } + +impl Eq for (A, B, C, D, E) + where A: Eq, B: Eq, C: Eq, D: Eq, E: Eq { .. } +``` + +### `std::cmp::Cmp` + +```rust +trait Cmp { + fn cmp(self, other: Self) -> Ordering; +} +``` + +`a.cmp(b)` compares two values returning `Ordering::less()` if `a < b`, +`Ordering::equal()` if `a == b`, or `Ordering::greater()` if `a > b`. +Implementing this trait on a type allows `<`, `<=`, `>`, and `>=` to be +used on values of the type. + +Implementations: + +```rust +impl Ord for u8 { .. } +impl Ord for u16 { .. } +impl Ord for u32 { .. } +impl Ord for u64 { .. } + +impl Ord for i8 { .. } +impl Ord for i16 { .. } +impl Ord for i32 { .. } + +impl Ord for i64 { .. } + +impl Ord for () { .. } +impl Ord for bool { .. } + +impl Ord for [T; N] + where T: Ord { .. } + +impl Ord for (A, B) + where A: Ord, B: Ord { .. } + +impl Ord for (A, B, C) + where A: Ord, B: Ord, C: Ord { .. } + +impl Ord for (A, B, C, D) + where A: Ord, B: Ord, C: Ord, D: Ord { .. } + +impl Ord for (A, B, C, D, E) + where A: Ord, B: Ord, C: Ord, D: Ord, E: Ord { .. } +``` + +## `std::ops` + +### `std::ops::Add`, `std::ops::Sub`, `std::ops::Mul`, and `std::ops::Div` + +These traits abstract over addition, subtraction, multiplication, and division respectively. +Implementing these traits for a given type will also allow that type to be used with the corresponding operator +for that trait (`+` for Add, etc) in addition to the normal method names. + +```rust +trait Add { + fn add(self, other: Self) -> Self; +} + +trait Sub { + fn sub(self, other: Self) -> Self; +} + +trait Mul { + fn mul(self, other: Self) -> Self; +} + +trait Div { + fn div(self, other: Self) -> Self; +} +``` + +The implementations block below is given for the `Add` trait, but the same types that implement +`Add` also implement `Sub`, `Mul`, and `Div`. + +Implementations: +```rust +impl Add for Field { .. } + +impl Add for i8 { .. } +impl Add for i16 { .. } +impl Add for i32 { .. } +impl Add for i64 { .. } + +impl Add for u8 { .. } +impl Add for u16 { .. } +impl Add for u32 { .. } +impl Add for u64 { .. } +``` + +### `std::ops::Rem` + +```rust +trait Rem { + fn rem(self, other: Self) -> Self; +} +``` + +`Rem::rem(a, b)` is the remainder function returning the result of what is +left after dividing `a` and `b`. Implementing `Rem` allows the `%` operator +to be used with the implementation type. + +Unlike other numeric traits, `Rem` is not implemented for `Field`. + +Implementations: +```rust +impl Rem for u8 { fn rem(self, other: u8) -> u8 { self % other } } +impl Rem for u16 { fn rem(self, other: u16) -> u16 { self % other } } +impl Rem for u32 { fn rem(self, other: u32) -> u32 { self % other } } +impl Rem for u64 { fn rem(self, other: u64) -> u64 { self % other } } + +impl Rem for i8 { fn rem(self, other: i8) -> i8 { self % other } } +impl Rem for i16 { fn rem(self, other: i16) -> i16 { self % other } } +impl Rem for i32 { fn rem(self, other: i32) -> i32 { self % other } } +impl Rem for i64 { fn rem(self, other: i64) -> i64 { self % other } } +``` + +### `std::ops::{ BitOr, BitAnd, BitXor }` + +```rust +trait BitOr { + fn bitor(self, other: Self) -> Self; +} + +trait BitAnd { + fn bitand(self, other: Self) -> Self; +} + +trait BitXor { + fn bitxor(self, other: Self) -> Self; +} +``` + +Traits for the bitwise operations `|`, `&`, and `^`. + +Implementing `BitOr`, `BitAnd` or `BitXor` for a type allows the `|`, `&`, or `^` operator respectively +to be used with the type. + +The implementations block below is given for the `BitOr` trait, but the same types that implement +`BitOr` also implement `BitAnd` and `BitXor`. + +Implementations: +```rust +impl BitOr for bool { fn bitor(self, other: bool) -> bool { self | other } } + +impl BitOr for u8 { fn bitor(self, other: u8) -> u8 { self | other } } +impl BitOr for u16 { fn bitor(self, other: u16) -> u16 { self | other } } +impl BitOr for u32 { fn bitor(self, other: u32) -> u32 { self | other } } +impl BitOr for u64 { fn bitor(self, other: u64) -> u64 { self | other } } + +impl BitOr for i8 { fn bitor(self, other: i8) -> i8 { self | other } } +impl BitOr for i16 { fn bitor(self, other: i16) -> i16 { self | other } } +impl BitOr for i32 { fn bitor(self, other: i32) -> i32 { self | other } } +impl BitOr for i64 { fn bitor(self, other: i64) -> i64 { self | other } } +``` + +### `std::ops::{ Shl, Shr }` + +```rust +trait Shl { + fn shl(self, other: Self) -> Self; +} + +trait Shr { + fn shr(self, other: Self) -> Self; +} +``` + +Traits for a bit shift left and bit shift right. + +Implementing `Shl` for a type allows the left shift operator (`<<`) to be used with the implementation type. +Similarly, implementing `Shr` allows the right shift operator (`>>`) to be used with the type. + +Note that bit shifting is not currently implemented for signed types. + +The implementations block below is given for the `Shl` trait, but the same types that implement +`Shl` also implement `Shr`. + +Implementations: +```rust +impl Shl for u8 { fn shl(self, other: u8) -> u8 { self << other } } +impl Shl for u16 { fn shl(self, other: u16) -> u16 { self << other } } +impl Shl for u32 { fn shl(self, other: u32) -> u32 { self << other } } +impl Shl for u64 { fn shl(self, other: u64) -> u64 { self << other } } +``` diff --git a/noir/docs/versioned_docs/version-v0.23.0/noir/standard_library/zeroed.md b/noir/docs/versioned_docs/version-v0.23.0/noir/standard_library/zeroed.md new file mode 100644 index 00000000000..97dab02dac2 --- /dev/null +++ b/noir/docs/versioned_docs/version-v0.23.0/noir/standard_library/zeroed.md @@ -0,0 +1,25 @@ +--- +title: Zeroed Function +description: + The zeroed function returns a zeroed value of any type. +keywords: + [ + zeroed + ] +--- + +Implements `fn zeroed() -> T` to return a zeroed value of any type. This function is generally unsafe to use as the zeroed bit pattern is not guaranteed to be valid for all types. It can however, be useful in cases when the value is guaranteed not to be used such as in a BoundedVec library implementing a growable vector, up to a certain length, backed by an array. The array can be initialized with zeroed values which are guaranteed to be inaccessible until the vector is pushed to. Similarly, enumerations in noir can be implemented using this method by providing zeroed values for the unused variants. + +You can access the function at `std::unsafe::zeroed`. + +This function currently supports the following types: + +- Field +- Bool +- Uint +- Array +- String +- Tuple +- Function + +Using it on other types could result in unexpected behavior. diff --git a/noir/docs/versioned_docs/version-v0.23.0/noir_js/reference/backend_barretenberg/.nojekyll b/noir/docs/versioned_docs/version-v0.23.0/noir_js/reference/backend_barretenberg/.nojekyll new file mode 100644 index 00000000000..e2ac6616add --- /dev/null +++ b/noir/docs/versioned_docs/version-v0.23.0/noir_js/reference/backend_barretenberg/.nojekyll @@ -0,0 +1 @@ +TypeDoc added this file to prevent GitHub Pages from using Jekyll. You can turn off this behavior by setting the `githubPages` option to false. \ No newline at end of file diff --git a/noir/docs/docs/reference/NoirJS/backend_barretenberg/classes/BarretenbergBackend.md b/noir/docs/versioned_docs/version-v0.23.0/noir_js/reference/backend_barretenberg/classes/BarretenbergBackend.md similarity index 100% rename from noir/docs/docs/reference/NoirJS/backend_barretenberg/classes/BarretenbergBackend.md rename to noir/docs/versioned_docs/version-v0.23.0/noir_js/reference/backend_barretenberg/classes/BarretenbergBackend.md diff --git a/noir/docs/versioned_docs/version-v0.23.0/noir_js/reference/backend_barretenberg/index.md b/noir/docs/versioned_docs/version-v0.23.0/noir_js/reference/backend_barretenberg/index.md new file mode 100644 index 00000000000..93b248b0f65 --- /dev/null +++ b/noir/docs/versioned_docs/version-v0.23.0/noir_js/reference/backend_barretenberg/index.md @@ -0,0 +1,45 @@ +# Backend Barretenberg + +## Exports + +### Classes + +| Class | Description | +| :------ | :------ | +| [BarretenbergBackend](classes/BarretenbergBackend.md) | - | + +### Interfaces + +| Interface | Description | +| :------ | :------ | +| [Backend](interfaces/Backend.md) | - | + +### Type Aliases + +| Type alias | Description | +| :------ | :------ | +| [BackendOptions](type-aliases/BackendOptions.md) | - | +| [CompiledCircuit](type-aliases/CompiledCircuit.md) | - | +| [ProofData](type-aliases/ProofData.md) | - | + +## Functions + +### flattenPublicInputs() + +```ts +flattenPublicInputs(publicInputs): string[] +``` + +#### Parameters + +| Parameter | Type | +| :------ | :------ | +| `publicInputs` | `WitnessMap` | + +#### Returns + +`string`[] + +*** + +Generated using [typedoc-plugin-markdown](https://www.npmjs.com/package/typedoc-plugin-markdown) and [TypeDoc](https://typedoc.org/) diff --git a/noir/docs/versioned_docs/version-v0.23.0/noir_js/reference/backend_barretenberg/interfaces/Backend.md b/noir/docs/versioned_docs/version-v0.23.0/noir_js/reference/backend_barretenberg/interfaces/Backend.md new file mode 100644 index 00000000000..3eb9645c8d2 --- /dev/null +++ b/noir/docs/versioned_docs/version-v0.23.0/noir_js/reference/backend_barretenberg/interfaces/Backend.md @@ -0,0 +1,132 @@ +# Backend + +## Methods + +### destroy() + +```ts +destroy(): Promise +``` + +#### Returns + +`Promise`\<`void`\> + +#### Description + +Destroys the backend + +*** + +### generateFinalProof() + +```ts +generateFinalProof(decompressedWitness): Promise +``` + +#### Parameters + +| Parameter | Type | +| :------ | :------ | +| `decompressedWitness` | `Uint8Array` | + +#### Returns + +`Promise`\<[`ProofData`](../type-aliases/ProofData.md)\> + +#### Description + +Generates a final proof (not meant to be verified in another circuit) + +*** + +### generateIntermediateProof() + +```ts +generateIntermediateProof(decompressedWitness): Promise +``` + +#### Parameters + +| Parameter | Type | +| :------ | :------ | +| `decompressedWitness` | `Uint8Array` | + +#### Returns + +`Promise`\<[`ProofData`](../type-aliases/ProofData.md)\> + +#### Description + +Generates an intermediate proof (meant to be verified in another circuit) + +*** + +### generateIntermediateProofArtifacts() + +```ts +generateIntermediateProofArtifacts(proofData, numOfPublicInputs): Promise +``` + +#### Parameters + +| Parameter | Type | +| :------ | :------ | +| `proofData` | [`ProofData`](../type-aliases/ProofData.md) | +| `numOfPublicInputs` | `number` | + +#### Returns + +`Promise`\<`object`\> + +#### Description + +Retrieves the artifacts from a proof in the Field format + +*** + +### verifyFinalProof() + +```ts +verifyFinalProof(proofData): Promise +``` + +#### Parameters + +| Parameter | Type | +| :------ | :------ | +| `proofData` | [`ProofData`](../type-aliases/ProofData.md) | + +#### Returns + +`Promise`\<`boolean`\> + +#### Description + +Verifies a final proof + +*** + +### verifyIntermediateProof() + +```ts +verifyIntermediateProof(proofData): Promise +``` + +#### Parameters + +| Parameter | Type | +| :------ | :------ | +| `proofData` | [`ProofData`](../type-aliases/ProofData.md) | + +#### Returns + +`Promise`\<`boolean`\> + +#### Description + +Verifies an intermediate proof + +*** + +Generated using [typedoc-plugin-markdown](https://www.npmjs.com/package/typedoc-plugin-markdown) and [TypeDoc](https://typedoc.org/) diff --git a/noir/docs/versioned_docs/version-v0.23.0/noir_js/reference/backend_barretenberg/type-aliases/BackendOptions.md b/noir/docs/versioned_docs/version-v0.23.0/noir_js/reference/backend_barretenberg/type-aliases/BackendOptions.md new file mode 100644 index 00000000000..266ade75d17 --- /dev/null +++ b/noir/docs/versioned_docs/version-v0.23.0/noir_js/reference/backend_barretenberg/type-aliases/BackendOptions.md @@ -0,0 +1,19 @@ +# BackendOptions + +```ts +type BackendOptions: object; +``` + +## Description + +An options object, currently only used to specify the number of threads to use. + +## Type declaration + +| Member | Type | Description | +| :------ | :------ | :------ | +| `threads` | `number` | **Description**

Number of threads | + +*** + +Generated using [typedoc-plugin-markdown](https://www.npmjs.com/package/typedoc-plugin-markdown) and [TypeDoc](https://typedoc.org/) diff --git a/noir/docs/versioned_docs/version-v0.23.0/noir_js/reference/backend_barretenberg/type-aliases/CompiledCircuit.md b/noir/docs/versioned_docs/version-v0.23.0/noir_js/reference/backend_barretenberg/type-aliases/CompiledCircuit.md new file mode 100644 index 00000000000..34e0dd04205 --- /dev/null +++ b/noir/docs/versioned_docs/version-v0.23.0/noir_js/reference/backend_barretenberg/type-aliases/CompiledCircuit.md @@ -0,0 +1,20 @@ +# CompiledCircuit + +```ts +type CompiledCircuit: object; +``` + +## Description + +The representation of a compiled circuit + +## Type declaration + +| Member | Type | Description | +| :------ | :------ | :------ | +| `abi` | `Abi` | **Description**

ABI representation of the circuit | +| `bytecode` | `string` | **Description**

The bytecode of the circuit | + +*** + +Generated using [typedoc-plugin-markdown](https://www.npmjs.com/package/typedoc-plugin-markdown) and [TypeDoc](https://typedoc.org/) diff --git a/noir/docs/versioned_docs/version-v0.23.0/noir_js/reference/backend_barretenberg/type-aliases/ProofData.md b/noir/docs/versioned_docs/version-v0.23.0/noir_js/reference/backend_barretenberg/type-aliases/ProofData.md new file mode 100644 index 00000000000..3eb360a78f1 --- /dev/null +++ b/noir/docs/versioned_docs/version-v0.23.0/noir_js/reference/backend_barretenberg/type-aliases/ProofData.md @@ -0,0 +1,20 @@ +# ProofData + +```ts +type ProofData: object; +``` + +## Description + +The representation of a proof + +## Type declaration + +| Member | Type | Description | +| :------ | :------ | :------ | +| `proof` | `Uint8Array` | **Description**

An byte array representing the proof | +| `publicInputs` | `WitnessMap` | **Description**

Public inputs of a proof | + +*** + +Generated using [typedoc-plugin-markdown](https://www.npmjs.com/package/typedoc-plugin-markdown) and [TypeDoc](https://typedoc.org/) diff --git a/noir/docs/versioned_docs/version-v0.23.0/noir_js/reference/backend_barretenberg/typedoc-sidebar.cjs b/noir/docs/versioned_docs/version-v0.23.0/noir_js/reference/backend_barretenberg/typedoc-sidebar.cjs new file mode 100644 index 00000000000..04e662c845f --- /dev/null +++ b/noir/docs/versioned_docs/version-v0.23.0/noir_js/reference/backend_barretenberg/typedoc-sidebar.cjs @@ -0,0 +1,4 @@ +// @ts-check +/** @type {import('@docusaurus/plugin-content-docs').SidebarsConfig} */ +const typedocSidebar = { items: [{"type":"category","label":"Classes","items":[{"type":"doc","id":"noir_js/reference/backend_barretenberg/classes/BarretenbergBackend","label":"BarretenbergBackend"}]},{"type":"category","label":"Interfaces","items":[{"type":"doc","id":"noir_js/reference/backend_barretenberg/interfaces/Backend","label":"Backend"}]},{"type":"category","label":"Type Aliases","items":[{"type":"doc","id":"noir_js/reference/backend_barretenberg/type-aliases/BackendOptions","label":"BackendOptions"},{"type":"doc","id":"noir_js/reference/backend_barretenberg/type-aliases/CompiledCircuit","label":"CompiledCircuit"},{"type":"doc","id":"noir_js/reference/backend_barretenberg/type-aliases/ProofData","label":"ProofData"}]}]}; +module.exports = typedocSidebar.items; \ No newline at end of file diff --git a/noir/docs/versioned_docs/version-v0.23.0/noir_js/reference/noir_js/.nojekyll b/noir/docs/versioned_docs/version-v0.23.0/noir_js/reference/noir_js/.nojekyll new file mode 100644 index 00000000000..e2ac6616add --- /dev/null +++ b/noir/docs/versioned_docs/version-v0.23.0/noir_js/reference/noir_js/.nojekyll @@ -0,0 +1 @@ +TypeDoc added this file to prevent GitHub Pages from using Jekyll. You can turn off this behavior by setting the `githubPages` option to false. \ No newline at end of file diff --git a/noir/docs/versioned_docs/version-v0.23.0/noir_js/reference/noir_js/classes/Noir.md b/noir/docs/versioned_docs/version-v0.23.0/noir_js/reference/noir_js/classes/Noir.md new file mode 100644 index 00000000000..c54468891af --- /dev/null +++ b/noir/docs/versioned_docs/version-v0.23.0/noir_js/reference/noir_js/classes/Noir.md @@ -0,0 +1,131 @@ +# Noir + +## Constructors + +### new Noir(circuit, backend) + +```ts +new Noir(circuit, backend?): Noir +``` + +#### Parameters + +| Parameter | Type | +| :------ | :------ | +| `circuit` | [`CompiledCircuit`](../type-aliases/CompiledCircuit.md) | +| `backend`? | `Backend` | + +#### Returns + +[`Noir`](Noir.md) + +## Methods + +### destroy() + +```ts +destroy(): Promise +``` + +#### Returns + +`Promise`\<`void`\> + +#### Description + +Destroys the underlying backend instance. + +#### Example + +```typescript +await noir.destroy(); +``` + +*** + +### execute() + +```ts +execute(inputs, foreignCallHandler?): Promise +``` + +#### Parameters + +| Parameter | Type | +| :------ | :------ | +| `inputs` | [`InputMap`](../type-aliases/InputMap.md) | +| `foreignCallHandler`? | [`ForeignCallHandler`](../type-aliases/ForeignCallHandler.md) | + +#### Returns + +`Promise`\<`object`\> + +#### Description + +Allows to execute a circuit to get its witness and return value. + +#### Example + +```typescript +async execute(inputs) +``` + +*** + +### generateFinalProof() + +```ts +generateFinalProof(inputs): Promise +``` + +#### Parameters + +| Parameter | Type | +| :------ | :------ | +| `inputs` | [`InputMap`](../type-aliases/InputMap.md) | + +#### Returns + +`Promise`\<[`ProofData`](../type-aliases/ProofData.md)\> + +#### Description + +Generates a witness and a proof given an object as input. + +#### Example + +```typescript +async generateFinalProof(input) +``` + +*** + +### verifyFinalProof() + +```ts +verifyFinalProof(proofData): Promise +``` + +#### Parameters + +| Parameter | Type | +| :------ | :------ | +| `proofData` | [`ProofData`](../type-aliases/ProofData.md) | + +#### Returns + +`Promise`\<`boolean`\> + +#### Description + +Instantiates the verification key and verifies a proof. + +#### Example + +```typescript +async verifyFinalProof(proof) +``` + +*** + +Generated using [typedoc-plugin-markdown](https://www.npmjs.com/package/typedoc-plugin-markdown) and [TypeDoc](https://typedoc.org/) diff --git a/noir/docs/versioned_docs/version-v0.23.0/noir_js/reference/noir_js/functions/and.md b/noir/docs/versioned_docs/version-v0.23.0/noir_js/reference/noir_js/functions/and.md new file mode 100644 index 00000000000..c783283e396 --- /dev/null +++ b/noir/docs/versioned_docs/version-v0.23.0/noir_js/reference/noir_js/functions/and.md @@ -0,0 +1,22 @@ +# and() + +```ts +and(lhs, rhs): string +``` + +Performs a bitwise AND operation between `lhs` and `rhs` + +## Parameters + +| Parameter | Type | Description | +| :------ | :------ | :------ | +| `lhs` | `string` | | +| `rhs` | `string` | | + +## Returns + +`string` + +*** + +Generated using [typedoc-plugin-markdown](https://www.npmjs.com/package/typedoc-plugin-markdown) and [TypeDoc](https://typedoc.org/) diff --git a/noir/docs/versioned_docs/version-v0.23.0/noir_js/reference/noir_js/functions/blake2s256.md b/noir/docs/versioned_docs/version-v0.23.0/noir_js/reference/noir_js/functions/blake2s256.md new file mode 100644 index 00000000000..7882d0da8d5 --- /dev/null +++ b/noir/docs/versioned_docs/version-v0.23.0/noir_js/reference/noir_js/functions/blake2s256.md @@ -0,0 +1,21 @@ +# blake2s256() + +```ts +blake2s256(inputs): Uint8Array +``` + +Calculates the Blake2s256 hash of the input bytes + +## Parameters + +| Parameter | Type | Description | +| :------ | :------ | :------ | +| `inputs` | `Uint8Array` | | + +## Returns + +`Uint8Array` + +*** + +Generated using [typedoc-plugin-markdown](https://www.npmjs.com/package/typedoc-plugin-markdown) and [TypeDoc](https://typedoc.org/) diff --git a/noir/docs/versioned_docs/version-v0.23.0/noir_js/reference/noir_js/functions/ecdsa_secp256k1_verify.md b/noir/docs/versioned_docs/version-v0.23.0/noir_js/reference/noir_js/functions/ecdsa_secp256k1_verify.md new file mode 100644 index 00000000000..0ba5783f0d5 --- /dev/null +++ b/noir/docs/versioned_docs/version-v0.23.0/noir_js/reference/noir_js/functions/ecdsa_secp256k1_verify.md @@ -0,0 +1,29 @@ +# ecdsa\_secp256k1\_verify() + +```ts +ecdsa_secp256k1_verify( + hashed_msg, + public_key_x_bytes, + public_key_y_bytes, + signature): boolean +``` + +Calculates the Blake2s256 hash of the input bytes and represents these as a single field element. +Verifies a ECDSA signature over the secp256k1 curve. + +## Parameters + +| Parameter | Type | Description | +| :------ | :------ | :------ | +| `hashed_msg` | `Uint8Array` | | +| `public_key_x_bytes` | `Uint8Array` | | +| `public_key_y_bytes` | `Uint8Array` | | +| `signature` | `Uint8Array` | | + +## Returns + +`boolean` + +*** + +Generated using [typedoc-plugin-markdown](https://www.npmjs.com/package/typedoc-plugin-markdown) and [TypeDoc](https://typedoc.org/) diff --git a/noir/docs/versioned_docs/version-v0.23.0/noir_js/reference/noir_js/functions/ecdsa_secp256r1_verify.md b/noir/docs/versioned_docs/version-v0.23.0/noir_js/reference/noir_js/functions/ecdsa_secp256r1_verify.md new file mode 100644 index 00000000000..0b20ff68957 --- /dev/null +++ b/noir/docs/versioned_docs/version-v0.23.0/noir_js/reference/noir_js/functions/ecdsa_secp256r1_verify.md @@ -0,0 +1,28 @@ +# ecdsa\_secp256r1\_verify() + +```ts +ecdsa_secp256r1_verify( + hashed_msg, + public_key_x_bytes, + public_key_y_bytes, + signature): boolean +``` + +Verifies a ECDSA signature over the secp256r1 curve. + +## Parameters + +| Parameter | Type | Description | +| :------ | :------ | :------ | +| `hashed_msg` | `Uint8Array` | | +| `public_key_x_bytes` | `Uint8Array` | | +| `public_key_y_bytes` | `Uint8Array` | | +| `signature` | `Uint8Array` | | + +## Returns + +`boolean` + +*** + +Generated using [typedoc-plugin-markdown](https://www.npmjs.com/package/typedoc-plugin-markdown) and [TypeDoc](https://typedoc.org/) diff --git a/noir/docs/versioned_docs/version-v0.23.0/noir_js/reference/noir_js/functions/keccak256.md b/noir/docs/versioned_docs/version-v0.23.0/noir_js/reference/noir_js/functions/keccak256.md new file mode 100644 index 00000000000..d10f155ce86 --- /dev/null +++ b/noir/docs/versioned_docs/version-v0.23.0/noir_js/reference/noir_js/functions/keccak256.md @@ -0,0 +1,21 @@ +# keccak256() + +```ts +keccak256(inputs): Uint8Array +``` + +Calculates the Keccak256 hash of the input bytes + +## Parameters + +| Parameter | Type | Description | +| :------ | :------ | :------ | +| `inputs` | `Uint8Array` | | + +## Returns + +`Uint8Array` + +*** + +Generated using [typedoc-plugin-markdown](https://www.npmjs.com/package/typedoc-plugin-markdown) and [TypeDoc](https://typedoc.org/) diff --git a/noir/docs/versioned_docs/version-v0.23.0/noir_js/reference/noir_js/functions/sha256.md b/noir/docs/versioned_docs/version-v0.23.0/noir_js/reference/noir_js/functions/sha256.md new file mode 100644 index 00000000000..6ba4ecac022 --- /dev/null +++ b/noir/docs/versioned_docs/version-v0.23.0/noir_js/reference/noir_js/functions/sha256.md @@ -0,0 +1,21 @@ +# sha256() + +```ts +sha256(inputs): Uint8Array +``` + +Calculates the SHA256 hash of the input bytes + +## Parameters + +| Parameter | Type | Description | +| :------ | :------ | :------ | +| `inputs` | `Uint8Array` | | + +## Returns + +`Uint8Array` + +*** + +Generated using [typedoc-plugin-markdown](https://www.npmjs.com/package/typedoc-plugin-markdown) and [TypeDoc](https://typedoc.org/) diff --git a/noir/docs/versioned_docs/version-v0.23.0/noir_js/reference/noir_js/functions/xor.md b/noir/docs/versioned_docs/version-v0.23.0/noir_js/reference/noir_js/functions/xor.md new file mode 100644 index 00000000000..8d762b895d3 --- /dev/null +++ b/noir/docs/versioned_docs/version-v0.23.0/noir_js/reference/noir_js/functions/xor.md @@ -0,0 +1,22 @@ +# xor() + +```ts +xor(lhs, rhs): string +``` + +Performs a bitwise XOR operation between `lhs` and `rhs` + +## Parameters + +| Parameter | Type | Description | +| :------ | :------ | :------ | +| `lhs` | `string` | | +| `rhs` | `string` | | + +## Returns + +`string` + +*** + +Generated using [typedoc-plugin-markdown](https://www.npmjs.com/package/typedoc-plugin-markdown) and [TypeDoc](https://typedoc.org/) diff --git a/noir/docs/versioned_docs/version-v0.23.0/noir_js/reference/noir_js/index.md b/noir/docs/versioned_docs/version-v0.23.0/noir_js/reference/noir_js/index.md new file mode 100644 index 00000000000..348453c0059 --- /dev/null +++ b/noir/docs/versioned_docs/version-v0.23.0/noir_js/reference/noir_js/index.md @@ -0,0 +1,37 @@ +# Noir JS + +## Exports + +### Classes + +| Class | Description | +| :------ | :------ | +| [Noir](classes/Noir.md) | - | + +### Type Aliases + +| Type alias | Description | +| :------ | :------ | +| [CompiledCircuit](type-aliases/CompiledCircuit.md) | - | +| [ForeignCallHandler](type-aliases/ForeignCallHandler.md) | A callback which performs an foreign call and returns the response. | +| [ForeignCallInput](type-aliases/ForeignCallInput.md) | - | +| [ForeignCallOutput](type-aliases/ForeignCallOutput.md) | - | +| [InputMap](type-aliases/InputMap.md) | - | +| [ProofData](type-aliases/ProofData.md) | - | +| [WitnessMap](type-aliases/WitnessMap.md) | - | + +### Functions + +| Function | Description | +| :------ | :------ | +| [and](functions/and.md) | Performs a bitwise AND operation between `lhs` and `rhs` | +| [blake2s256](functions/blake2s256.md) | Calculates the Blake2s256 hash of the input bytes | +| [ecdsa\_secp256k1\_verify](functions/ecdsa_secp256k1_verify.md) | Calculates the Blake2s256 hash of the input bytes and represents these as a single field element. | +| [ecdsa\_secp256r1\_verify](functions/ecdsa_secp256r1_verify.md) | Verifies a ECDSA signature over the secp256r1 curve. | +| [keccak256](functions/keccak256.md) | Calculates the Keccak256 hash of the input bytes | +| [sha256](functions/sha256.md) | Calculates the SHA256 hash of the input bytes | +| [xor](functions/xor.md) | Performs a bitwise XOR operation between `lhs` and `rhs` | + +*** + +Generated using [typedoc-plugin-markdown](https://www.npmjs.com/package/typedoc-plugin-markdown) and [TypeDoc](https://typedoc.org/) diff --git a/noir/docs/versioned_docs/version-v0.23.0/noir_js/reference/noir_js/type-aliases/CompiledCircuit.md b/noir/docs/versioned_docs/version-v0.23.0/noir_js/reference/noir_js/type-aliases/CompiledCircuit.md new file mode 100644 index 00000000000..34e0dd04205 --- /dev/null +++ b/noir/docs/versioned_docs/version-v0.23.0/noir_js/reference/noir_js/type-aliases/CompiledCircuit.md @@ -0,0 +1,20 @@ +# CompiledCircuit + +```ts +type CompiledCircuit: object; +``` + +## Description + +The representation of a compiled circuit + +## Type declaration + +| Member | Type | Description | +| :------ | :------ | :------ | +| `abi` | `Abi` | **Description**

ABI representation of the circuit | +| `bytecode` | `string` | **Description**

The bytecode of the circuit | + +*** + +Generated using [typedoc-plugin-markdown](https://www.npmjs.com/package/typedoc-plugin-markdown) and [TypeDoc](https://typedoc.org/) diff --git a/noir/docs/versioned_docs/version-v0.23.0/noir_js/reference/noir_js/type-aliases/ForeignCallHandler.md b/noir/docs/versioned_docs/version-v0.23.0/noir_js/reference/noir_js/type-aliases/ForeignCallHandler.md new file mode 100644 index 00000000000..812b8b16481 --- /dev/null +++ b/noir/docs/versioned_docs/version-v0.23.0/noir_js/reference/noir_js/type-aliases/ForeignCallHandler.md @@ -0,0 +1,24 @@ +# ForeignCallHandler + +```ts +type ForeignCallHandler: (name, inputs) => Promise; +``` + +A callback which performs an foreign call and returns the response. + +## Parameters + +| Parameter | Type | Description | +| :------ | :------ | :------ | +| `name` | `string` | The identifier for the type of foreign call being performed. | +| `inputs` | [`ForeignCallInput`](ForeignCallInput.md)[] | An array of hex encoded inputs to the foreign call. | + +## Returns + +`Promise`\<[`ForeignCallOutput`](ForeignCallOutput.md)[]\> + +outputs - An array of hex encoded outputs containing the results of the foreign call. + +*** + +Generated using [typedoc-plugin-markdown](https://www.npmjs.com/package/typedoc-plugin-markdown) and [TypeDoc](https://typedoc.org/) diff --git a/noir/docs/versioned_docs/version-v0.23.0/noir_js/reference/noir_js/type-aliases/ForeignCallInput.md b/noir/docs/versioned_docs/version-v0.23.0/noir_js/reference/noir_js/type-aliases/ForeignCallInput.md new file mode 100644 index 00000000000..dd95809186a --- /dev/null +++ b/noir/docs/versioned_docs/version-v0.23.0/noir_js/reference/noir_js/type-aliases/ForeignCallInput.md @@ -0,0 +1,9 @@ +# ForeignCallInput + +```ts +type ForeignCallInput: string[]; +``` + +*** + +Generated using [typedoc-plugin-markdown](https://www.npmjs.com/package/typedoc-plugin-markdown) and [TypeDoc](https://typedoc.org/) diff --git a/noir/docs/versioned_docs/version-v0.23.0/noir_js/reference/noir_js/type-aliases/ForeignCallOutput.md b/noir/docs/versioned_docs/version-v0.23.0/noir_js/reference/noir_js/type-aliases/ForeignCallOutput.md new file mode 100644 index 00000000000..b71fb78a946 --- /dev/null +++ b/noir/docs/versioned_docs/version-v0.23.0/noir_js/reference/noir_js/type-aliases/ForeignCallOutput.md @@ -0,0 +1,9 @@ +# ForeignCallOutput + +```ts +type ForeignCallOutput: string | string[]; +``` + +*** + +Generated using [typedoc-plugin-markdown](https://www.npmjs.com/package/typedoc-plugin-markdown) and [TypeDoc](https://typedoc.org/) diff --git a/noir/docs/versioned_docs/version-v0.23.0/noir_js/reference/noir_js/type-aliases/InputMap.md b/noir/docs/versioned_docs/version-v0.23.0/noir_js/reference/noir_js/type-aliases/InputMap.md new file mode 100644 index 00000000000..c714e999d93 --- /dev/null +++ b/noir/docs/versioned_docs/version-v0.23.0/noir_js/reference/noir_js/type-aliases/InputMap.md @@ -0,0 +1,13 @@ +# InputMap + +```ts +type InputMap: object; +``` + +## Index signature + + \[`key`: `string`\]: `InputValue` + +*** + +Generated using [typedoc-plugin-markdown](https://www.npmjs.com/package/typedoc-plugin-markdown) and [TypeDoc](https://typedoc.org/) diff --git a/noir/docs/versioned_docs/version-v0.23.0/noir_js/reference/noir_js/type-aliases/ProofData.md b/noir/docs/versioned_docs/version-v0.23.0/noir_js/reference/noir_js/type-aliases/ProofData.md new file mode 100644 index 00000000000..3eb360a78f1 --- /dev/null +++ b/noir/docs/versioned_docs/version-v0.23.0/noir_js/reference/noir_js/type-aliases/ProofData.md @@ -0,0 +1,20 @@ +# ProofData + +```ts +type ProofData: object; +``` + +## Description + +The representation of a proof + +## Type declaration + +| Member | Type | Description | +| :------ | :------ | :------ | +| `proof` | `Uint8Array` | **Description**

An byte array representing the proof | +| `publicInputs` | `WitnessMap` | **Description**

Public inputs of a proof | + +*** + +Generated using [typedoc-plugin-markdown](https://www.npmjs.com/package/typedoc-plugin-markdown) and [TypeDoc](https://typedoc.org/) diff --git a/noir/docs/versioned_docs/version-v0.23.0/noir_js/reference/noir_js/type-aliases/WitnessMap.md b/noir/docs/versioned_docs/version-v0.23.0/noir_js/reference/noir_js/type-aliases/WitnessMap.md new file mode 100644 index 00000000000..258c46f9d0c --- /dev/null +++ b/noir/docs/versioned_docs/version-v0.23.0/noir_js/reference/noir_js/type-aliases/WitnessMap.md @@ -0,0 +1,9 @@ +# WitnessMap + +```ts +type WitnessMap: Map; +``` + +*** + +Generated using [typedoc-plugin-markdown](https://www.npmjs.com/package/typedoc-plugin-markdown) and [TypeDoc](https://typedoc.org/) diff --git a/noir/docs/versioned_docs/version-v0.23.0/noir_js/reference/noir_js/typedoc-sidebar.cjs b/noir/docs/versioned_docs/version-v0.23.0/noir_js/reference/noir_js/typedoc-sidebar.cjs new file mode 100644 index 00000000000..077ebeb133e --- /dev/null +++ b/noir/docs/versioned_docs/version-v0.23.0/noir_js/reference/noir_js/typedoc-sidebar.cjs @@ -0,0 +1,4 @@ +// @ts-check +/** @type {import('@docusaurus/plugin-content-docs').SidebarsConfig} */ +const typedocSidebar = { items: [{"type":"category","label":"Classes","items":[{"type":"doc","id":"noir_js/reference/noir_js/classes/Noir","label":"Noir"}]},{"type":"category","label":"Type Aliases","items":[{"type":"doc","id":"noir_js/reference/noir_js/type-aliases/CompiledCircuit","label":"CompiledCircuit"},{"type":"doc","id":"noir_js/reference/noir_js/type-aliases/ForeignCallHandler","label":"ForeignCallHandler"},{"type":"doc","id":"noir_js/reference/noir_js/type-aliases/ForeignCallInput","label":"ForeignCallInput"},{"type":"doc","id":"noir_js/reference/noir_js/type-aliases/ForeignCallOutput","label":"ForeignCallOutput"},{"type":"doc","id":"noir_js/reference/noir_js/type-aliases/InputMap","label":"InputMap"},{"type":"doc","id":"noir_js/reference/noir_js/type-aliases/ProofData","label":"ProofData"},{"type":"doc","id":"noir_js/reference/noir_js/type-aliases/WitnessMap","label":"WitnessMap"}]},{"type":"category","label":"Functions","items":[{"type":"doc","id":"noir_js/reference/noir_js/functions/and","label":"and"},{"type":"doc","id":"noir_js/reference/noir_js/functions/blake2s256","label":"blake2s256"},{"type":"doc","id":"noir_js/reference/noir_js/functions/ecdsa_secp256k1_verify","label":"ecdsa_secp256k1_verify"},{"type":"doc","id":"noir_js/reference/noir_js/functions/ecdsa_secp256r1_verify","label":"ecdsa_secp256r1_verify"},{"type":"doc","id":"noir_js/reference/noir_js/functions/keccak256","label":"keccak256"},{"type":"doc","id":"noir_js/reference/noir_js/functions/sha256","label":"sha256"},{"type":"doc","id":"noir_js/reference/noir_js/functions/xor","label":"xor"}]}]}; +module.exports = typedocSidebar.items; \ No newline at end of file diff --git a/noir/docs/versioned_docs/version-v0.23.0/reference/NoirJS/backend_barretenberg/.nojekyll b/noir/docs/versioned_docs/version-v0.23.0/reference/NoirJS/backend_barretenberg/.nojekyll new file mode 100644 index 00000000000..e2ac6616add --- /dev/null +++ b/noir/docs/versioned_docs/version-v0.23.0/reference/NoirJS/backend_barretenberg/.nojekyll @@ -0,0 +1 @@ +TypeDoc added this file to prevent GitHub Pages from using Jekyll. You can turn off this behavior by setting the `githubPages` option to false. \ No newline at end of file diff --git a/noir/docs/versioned_docs/version-v0.23.0/reference/NoirJS/backend_barretenberg/classes/BarretenbergBackend.md b/noir/docs/versioned_docs/version-v0.23.0/reference/NoirJS/backend_barretenberg/classes/BarretenbergBackend.md new file mode 100644 index 00000000000..5cbe9421b92 --- /dev/null +++ b/noir/docs/versioned_docs/version-v0.23.0/reference/NoirJS/backend_barretenberg/classes/BarretenbergBackend.md @@ -0,0 +1,185 @@ +# BarretenbergBackend + +## Implements + +- [`Backend`](../interfaces/Backend.md) + +## Constructors + +### new BarretenbergBackend(acirCircuit, options) + +```ts +new BarretenbergBackend(acirCircuit, options): BarretenbergBackend +``` + +#### Parameters + +| Parameter | Type | +| :------ | :------ | +| `acirCircuit` | [`CompiledCircuit`](../type-aliases/CompiledCircuit.md) | +| `options` | [`BackendOptions`](../type-aliases/BackendOptions.md) | + +#### Returns + +[`BarretenbergBackend`](BarretenbergBackend.md) + +## Methods + +### destroy() + +```ts +destroy(): Promise +``` + +#### Returns + +`Promise`\<`void`\> + +#### Implementation of + +[`Backend`](../interfaces/Backend.md).[`destroy`](../interfaces/Backend.md#destroy) + +#### Description + +Destroys the backend + +*** + +### generateFinalProof() + +```ts +generateFinalProof(decompressedWitness): Promise +``` + +#### Parameters + +| Parameter | Type | +| :------ | :------ | +| `decompressedWitness` | `Uint8Array` | + +#### Returns + +`Promise`\<[`ProofData`](../type-aliases/ProofData.md)\> + +#### Implementation of + +[`Backend`](../interfaces/Backend.md).[`generateFinalProof`](../interfaces/Backend.md#generatefinalproof) + +#### Description + +Generates a final proof (not meant to be verified in another circuit) + +*** + +### generateIntermediateProof() + +```ts +generateIntermediateProof(witness): Promise +``` + +#### Parameters + +| Parameter | Type | +| :------ | :------ | +| `witness` | `Uint8Array` | + +#### Returns + +`Promise`\<[`ProofData`](../type-aliases/ProofData.md)\> + +#### Implementation of + +[`Backend`](../interfaces/Backend.md).[`generateIntermediateProof`](../interfaces/Backend.md#generateintermediateproof) + +#### Example + +```typescript +const intermediateProof = await backend.generateIntermediateProof(witness); +``` + +*** + +### generateIntermediateProofArtifacts() + +```ts +generateIntermediateProofArtifacts(proofData, numOfPublicInputs): Promise +``` + +#### Parameters + +| Parameter | Type | Default value | +| :------ | :------ | :------ | +| `proofData` | [`ProofData`](../type-aliases/ProofData.md) | `undefined` | +| `numOfPublicInputs` | `number` | `0` | + +#### Returns + +`Promise`\<`object`\> + +#### Implementation of + +[`Backend`](../interfaces/Backend.md).[`generateIntermediateProofArtifacts`](../interfaces/Backend.md#generateintermediateproofartifacts) + +#### Example + +```typescript +const artifacts = await backend.generateIntermediateProofArtifacts(proof, numOfPublicInputs); +``` + +*** + +### verifyFinalProof() + +```ts +verifyFinalProof(proofData): Promise +``` + +#### Parameters + +| Parameter | Type | +| :------ | :------ | +| `proofData` | [`ProofData`](../type-aliases/ProofData.md) | + +#### Returns + +`Promise`\<`boolean`\> + +#### Implementation of + +[`Backend`](../interfaces/Backend.md).[`verifyFinalProof`](../interfaces/Backend.md#verifyfinalproof) + +#### Description + +Verifies a final proof + +*** + +### verifyIntermediateProof() + +```ts +verifyIntermediateProof(proofData): Promise +``` + +#### Parameters + +| Parameter | Type | +| :------ | :------ | +| `proofData` | [`ProofData`](../type-aliases/ProofData.md) | + +#### Returns + +`Promise`\<`boolean`\> + +#### Implementation of + +[`Backend`](../interfaces/Backend.md).[`verifyIntermediateProof`](../interfaces/Backend.md#verifyintermediateproof) + +#### Example + +```typescript +const isValidIntermediate = await backend.verifyIntermediateProof(proof); +``` + +*** + +Generated using [typedoc-plugin-markdown](https://www.npmjs.com/package/typedoc-plugin-markdown) and [TypeDoc](https://typedoc.org/) diff --git a/noir/docs/versioned_docs/version-v0.23.0/reference/NoirJS/backend_barretenberg/index.md b/noir/docs/versioned_docs/version-v0.23.0/reference/NoirJS/backend_barretenberg/index.md new file mode 100644 index 00000000000..e32501acb71 --- /dev/null +++ b/noir/docs/versioned_docs/version-v0.23.0/reference/NoirJS/backend_barretenberg/index.md @@ -0,0 +1,46 @@ +# backend_barretenberg + +## Exports + +### Classes + +| Class | Description | +| :------ | :------ | +| [BarretenbergBackend](classes/BarretenbergBackend.md) | - | + +### Interfaces + +| Interface | Description | +| :------ | :------ | +| [Backend](interfaces/Backend.md) | - | + +### Type Aliases + +| Type alias | Description | +| :------ | :------ | +| [BackendOptions](type-aliases/BackendOptions.md) | - | +| [CompiledCircuit](type-aliases/CompiledCircuit.md) | - | +| [ProofData](type-aliases/ProofData.md) | - | + +## Functions + +### publicInputsToWitnessMap() + +```ts +publicInputsToWitnessMap(publicInputs, abi): WitnessMap +``` + +#### Parameters + +| Parameter | Type | +| :------ | :------ | +| `publicInputs` | `string`[] | +| `abi` | `Abi` | + +#### Returns + +`WitnessMap` + +*** + +Generated using [typedoc-plugin-markdown](https://www.npmjs.com/package/typedoc-plugin-markdown) and [TypeDoc](https://typedoc.org/) diff --git a/noir/docs/versioned_docs/version-v0.23.0/reference/NoirJS/backend_barretenberg/interfaces/Backend.md b/noir/docs/versioned_docs/version-v0.23.0/reference/NoirJS/backend_barretenberg/interfaces/Backend.md new file mode 100644 index 00000000000..3eb9645c8d2 --- /dev/null +++ b/noir/docs/versioned_docs/version-v0.23.0/reference/NoirJS/backend_barretenberg/interfaces/Backend.md @@ -0,0 +1,132 @@ +# Backend + +## Methods + +### destroy() + +```ts +destroy(): Promise +``` + +#### Returns + +`Promise`\<`void`\> + +#### Description + +Destroys the backend + +*** + +### generateFinalProof() + +```ts +generateFinalProof(decompressedWitness): Promise +``` + +#### Parameters + +| Parameter | Type | +| :------ | :------ | +| `decompressedWitness` | `Uint8Array` | + +#### Returns + +`Promise`\<[`ProofData`](../type-aliases/ProofData.md)\> + +#### Description + +Generates a final proof (not meant to be verified in another circuit) + +*** + +### generateIntermediateProof() + +```ts +generateIntermediateProof(decompressedWitness): Promise +``` + +#### Parameters + +| Parameter | Type | +| :------ | :------ | +| `decompressedWitness` | `Uint8Array` | + +#### Returns + +`Promise`\<[`ProofData`](../type-aliases/ProofData.md)\> + +#### Description + +Generates an intermediate proof (meant to be verified in another circuit) + +*** + +### generateIntermediateProofArtifacts() + +```ts +generateIntermediateProofArtifacts(proofData, numOfPublicInputs): Promise +``` + +#### Parameters + +| Parameter | Type | +| :------ | :------ | +| `proofData` | [`ProofData`](../type-aliases/ProofData.md) | +| `numOfPublicInputs` | `number` | + +#### Returns + +`Promise`\<`object`\> + +#### Description + +Retrieves the artifacts from a proof in the Field format + +*** + +### verifyFinalProof() + +```ts +verifyFinalProof(proofData): Promise +``` + +#### Parameters + +| Parameter | Type | +| :------ | :------ | +| `proofData` | [`ProofData`](../type-aliases/ProofData.md) | + +#### Returns + +`Promise`\<`boolean`\> + +#### Description + +Verifies a final proof + +*** + +### verifyIntermediateProof() + +```ts +verifyIntermediateProof(proofData): Promise +``` + +#### Parameters + +| Parameter | Type | +| :------ | :------ | +| `proofData` | [`ProofData`](../type-aliases/ProofData.md) | + +#### Returns + +`Promise`\<`boolean`\> + +#### Description + +Verifies an intermediate proof + +*** + +Generated using [typedoc-plugin-markdown](https://www.npmjs.com/package/typedoc-plugin-markdown) and [TypeDoc](https://typedoc.org/) diff --git a/noir/docs/versioned_docs/version-v0.23.0/reference/NoirJS/backend_barretenberg/type-aliases/BackendOptions.md b/noir/docs/versioned_docs/version-v0.23.0/reference/NoirJS/backend_barretenberg/type-aliases/BackendOptions.md new file mode 100644 index 00000000000..266ade75d17 --- /dev/null +++ b/noir/docs/versioned_docs/version-v0.23.0/reference/NoirJS/backend_barretenberg/type-aliases/BackendOptions.md @@ -0,0 +1,19 @@ +# BackendOptions + +```ts +type BackendOptions: object; +``` + +## Description + +An options object, currently only used to specify the number of threads to use. + +## Type declaration + +| Member | Type | Description | +| :------ | :------ | :------ | +| `threads` | `number` | **Description**

Number of threads | + +*** + +Generated using [typedoc-plugin-markdown](https://www.npmjs.com/package/typedoc-plugin-markdown) and [TypeDoc](https://typedoc.org/) diff --git a/noir/docs/versioned_docs/version-v0.23.0/reference/NoirJS/backend_barretenberg/type-aliases/CompiledCircuit.md b/noir/docs/versioned_docs/version-v0.23.0/reference/NoirJS/backend_barretenberg/type-aliases/CompiledCircuit.md new file mode 100644 index 00000000000..34e0dd04205 --- /dev/null +++ b/noir/docs/versioned_docs/version-v0.23.0/reference/NoirJS/backend_barretenberg/type-aliases/CompiledCircuit.md @@ -0,0 +1,20 @@ +# CompiledCircuit + +```ts +type CompiledCircuit: object; +``` + +## Description + +The representation of a compiled circuit + +## Type declaration + +| Member | Type | Description | +| :------ | :------ | :------ | +| `abi` | `Abi` | **Description**

ABI representation of the circuit | +| `bytecode` | `string` | **Description**

The bytecode of the circuit | + +*** + +Generated using [typedoc-plugin-markdown](https://www.npmjs.com/package/typedoc-plugin-markdown) and [TypeDoc](https://typedoc.org/) diff --git a/noir/docs/versioned_docs/version-v0.23.0/reference/NoirJS/backend_barretenberg/type-aliases/ProofData.md b/noir/docs/versioned_docs/version-v0.23.0/reference/NoirJS/backend_barretenberg/type-aliases/ProofData.md new file mode 100644 index 00000000000..05cebbc4e94 --- /dev/null +++ b/noir/docs/versioned_docs/version-v0.23.0/reference/NoirJS/backend_barretenberg/type-aliases/ProofData.md @@ -0,0 +1,20 @@ +# ProofData + +```ts +type ProofData: object; +``` + +## Description + +The representation of a proof + +## Type declaration + +| Member | Type | Description | +| :------ | :------ | :------ | +| `proof` | `Uint8Array` | **Description**

An byte array representing the proof | +| `publicInputs` | `string`[] | **Description**

Public inputs of a proof | + +*** + +Generated using [typedoc-plugin-markdown](https://www.npmjs.com/package/typedoc-plugin-markdown) and [TypeDoc](https://typedoc.org/) diff --git a/noir/docs/versioned_docs/version-v0.23.0/reference/NoirJS/backend_barretenberg/typedoc-sidebar.cjs b/noir/docs/versioned_docs/version-v0.23.0/reference/NoirJS/backend_barretenberg/typedoc-sidebar.cjs new file mode 100644 index 00000000000..2aaa55bccf6 --- /dev/null +++ b/noir/docs/versioned_docs/version-v0.23.0/reference/NoirJS/backend_barretenberg/typedoc-sidebar.cjs @@ -0,0 +1,4 @@ +// @ts-check +/** @type {import('@docusaurus/plugin-content-docs').SidebarsConfig} */ +const typedocSidebar = { items: [{"type":"category","label":"Classes","items":[{"type":"doc","id":"reference/NoirJS/backend_barretenberg/classes/BarretenbergBackend","label":"BarretenbergBackend"}]},{"type":"category","label":"Interfaces","items":[{"type":"doc","id":"reference/NoirJS/backend_barretenberg/interfaces/Backend","label":"Backend"}]},{"type":"category","label":"Type Aliases","items":[{"type":"doc","id":"reference/NoirJS/backend_barretenberg/type-aliases/BackendOptions","label":"BackendOptions"},{"type":"doc","id":"reference/NoirJS/backend_barretenberg/type-aliases/CompiledCircuit","label":"CompiledCircuit"},{"type":"doc","id":"reference/NoirJS/backend_barretenberg/type-aliases/ProofData","label":"ProofData"}]}]}; +module.exports = typedocSidebar.items; \ No newline at end of file diff --git a/noir/docs/versioned_docs/version-v0.23.0/reference/NoirJS/noir_js/.nojekyll b/noir/docs/versioned_docs/version-v0.23.0/reference/NoirJS/noir_js/.nojekyll new file mode 100644 index 00000000000..e2ac6616add --- /dev/null +++ b/noir/docs/versioned_docs/version-v0.23.0/reference/NoirJS/noir_js/.nojekyll @@ -0,0 +1 @@ +TypeDoc added this file to prevent GitHub Pages from using Jekyll. You can turn off this behavior by setting the `githubPages` option to false. \ No newline at end of file diff --git a/noir/docs/versioned_docs/version-v0.23.0/reference/NoirJS/noir_js/classes/Noir.md b/noir/docs/versioned_docs/version-v0.23.0/reference/NoirJS/noir_js/classes/Noir.md new file mode 100644 index 00000000000..34e20d99684 --- /dev/null +++ b/noir/docs/versioned_docs/version-v0.23.0/reference/NoirJS/noir_js/classes/Noir.md @@ -0,0 +1,132 @@ +# Noir + +## Constructors + +### new Noir(circuit, backend) + +```ts +new Noir(circuit, backend?): Noir +``` + +#### Parameters + +| Parameter | Type | +| :------ | :------ | +| `circuit` | [`CompiledCircuit`](../type-aliases/CompiledCircuit.md) | +| `backend`? | `Backend` | + +#### Returns + +[`Noir`](Noir.md) + +## Methods + +### destroy() + +```ts +destroy(): Promise +``` + +#### Returns + +`Promise`\<`void`\> + +#### Description + +Destroys the underlying backend instance. + +#### Example + +```typescript +await noir.destroy(); +``` + +*** + +### execute() + +```ts +execute(inputs, foreignCallHandler?): Promise +``` + +#### Parameters + +| Parameter | Type | +| :------ | :------ | +| `inputs` | [`InputMap`](../type-aliases/InputMap.md) | +| `foreignCallHandler`? | [`ForeignCallHandler`](../type-aliases/ForeignCallHandler.md) | + +#### Returns + +`Promise`\<`object`\> + +#### Description + +Allows to execute a circuit to get its witness and return value. + +#### Example + +```typescript +async execute(inputs) +``` + +*** + +### generateFinalProof() + +```ts +generateFinalProof(inputs, foreignCallHandler?): Promise +``` + +#### Parameters + +| Parameter | Type | +| :------ | :------ | +| `inputs` | [`InputMap`](../type-aliases/InputMap.md) | +| `foreignCallHandler`? | [`ForeignCallHandler`](../type-aliases/ForeignCallHandler.md) | + +#### Returns + +`Promise`\<[`ProofData`](../type-aliases/ProofData.md)\> + +#### Description + +Generates a witness and a proof given an object as input. + +#### Example + +```typescript +async generateFinalProof(input) +``` + +*** + +### verifyFinalProof() + +```ts +verifyFinalProof(proofData): Promise +``` + +#### Parameters + +| Parameter | Type | +| :------ | :------ | +| `proofData` | [`ProofData`](../type-aliases/ProofData.md) | + +#### Returns + +`Promise`\<`boolean`\> + +#### Description + +Instantiates the verification key and verifies a proof. + +#### Example + +```typescript +async verifyFinalProof(proof) +``` + +*** + +Generated using [typedoc-plugin-markdown](https://www.npmjs.com/package/typedoc-plugin-markdown) and [TypeDoc](https://typedoc.org/) diff --git a/noir/docs/versioned_docs/version-v0.23.0/reference/NoirJS/noir_js/functions/and.md b/noir/docs/versioned_docs/version-v0.23.0/reference/NoirJS/noir_js/functions/and.md new file mode 100644 index 00000000000..c783283e396 --- /dev/null +++ b/noir/docs/versioned_docs/version-v0.23.0/reference/NoirJS/noir_js/functions/and.md @@ -0,0 +1,22 @@ +# and() + +```ts +and(lhs, rhs): string +``` + +Performs a bitwise AND operation between `lhs` and `rhs` + +## Parameters + +| Parameter | Type | Description | +| :------ | :------ | :------ | +| `lhs` | `string` | | +| `rhs` | `string` | | + +## Returns + +`string` + +*** + +Generated using [typedoc-plugin-markdown](https://www.npmjs.com/package/typedoc-plugin-markdown) and [TypeDoc](https://typedoc.org/) diff --git a/noir/docs/versioned_docs/version-v0.23.0/reference/NoirJS/noir_js/functions/blake2s256.md b/noir/docs/versioned_docs/version-v0.23.0/reference/NoirJS/noir_js/functions/blake2s256.md new file mode 100644 index 00000000000..7882d0da8d5 --- /dev/null +++ b/noir/docs/versioned_docs/version-v0.23.0/reference/NoirJS/noir_js/functions/blake2s256.md @@ -0,0 +1,21 @@ +# blake2s256() + +```ts +blake2s256(inputs): Uint8Array +``` + +Calculates the Blake2s256 hash of the input bytes + +## Parameters + +| Parameter | Type | Description | +| :------ | :------ | :------ | +| `inputs` | `Uint8Array` | | + +## Returns + +`Uint8Array` + +*** + +Generated using [typedoc-plugin-markdown](https://www.npmjs.com/package/typedoc-plugin-markdown) and [TypeDoc](https://typedoc.org/) diff --git a/noir/docs/versioned_docs/version-v0.23.0/reference/NoirJS/noir_js/functions/ecdsa_secp256k1_verify.md b/noir/docs/versioned_docs/version-v0.23.0/reference/NoirJS/noir_js/functions/ecdsa_secp256k1_verify.md new file mode 100644 index 00000000000..5e3cd53e9d3 --- /dev/null +++ b/noir/docs/versioned_docs/version-v0.23.0/reference/NoirJS/noir_js/functions/ecdsa_secp256k1_verify.md @@ -0,0 +1,28 @@ +# ecdsa\_secp256k1\_verify() + +```ts +ecdsa_secp256k1_verify( + hashed_msg, + public_key_x_bytes, + public_key_y_bytes, + signature): boolean +``` + +Verifies a ECDSA signature over the secp256k1 curve. + +## Parameters + +| Parameter | Type | Description | +| :------ | :------ | :------ | +| `hashed_msg` | `Uint8Array` | | +| `public_key_x_bytes` | `Uint8Array` | | +| `public_key_y_bytes` | `Uint8Array` | | +| `signature` | `Uint8Array` | | + +## Returns + +`boolean` + +*** + +Generated using [typedoc-plugin-markdown](https://www.npmjs.com/package/typedoc-plugin-markdown) and [TypeDoc](https://typedoc.org/) diff --git a/noir/docs/versioned_docs/version-v0.23.0/reference/NoirJS/noir_js/functions/ecdsa_secp256r1_verify.md b/noir/docs/versioned_docs/version-v0.23.0/reference/NoirJS/noir_js/functions/ecdsa_secp256r1_verify.md new file mode 100644 index 00000000000..0b20ff68957 --- /dev/null +++ b/noir/docs/versioned_docs/version-v0.23.0/reference/NoirJS/noir_js/functions/ecdsa_secp256r1_verify.md @@ -0,0 +1,28 @@ +# ecdsa\_secp256r1\_verify() + +```ts +ecdsa_secp256r1_verify( + hashed_msg, + public_key_x_bytes, + public_key_y_bytes, + signature): boolean +``` + +Verifies a ECDSA signature over the secp256r1 curve. + +## Parameters + +| Parameter | Type | Description | +| :------ | :------ | :------ | +| `hashed_msg` | `Uint8Array` | | +| `public_key_x_bytes` | `Uint8Array` | | +| `public_key_y_bytes` | `Uint8Array` | | +| `signature` | `Uint8Array` | | + +## Returns + +`boolean` + +*** + +Generated using [typedoc-plugin-markdown](https://www.npmjs.com/package/typedoc-plugin-markdown) and [TypeDoc](https://typedoc.org/) diff --git a/noir/docs/versioned_docs/version-v0.23.0/reference/NoirJS/noir_js/functions/keccak256.md b/noir/docs/versioned_docs/version-v0.23.0/reference/NoirJS/noir_js/functions/keccak256.md new file mode 100644 index 00000000000..d10f155ce86 --- /dev/null +++ b/noir/docs/versioned_docs/version-v0.23.0/reference/NoirJS/noir_js/functions/keccak256.md @@ -0,0 +1,21 @@ +# keccak256() + +```ts +keccak256(inputs): Uint8Array +``` + +Calculates the Keccak256 hash of the input bytes + +## Parameters + +| Parameter | Type | Description | +| :------ | :------ | :------ | +| `inputs` | `Uint8Array` | | + +## Returns + +`Uint8Array` + +*** + +Generated using [typedoc-plugin-markdown](https://www.npmjs.com/package/typedoc-plugin-markdown) and [TypeDoc](https://typedoc.org/) diff --git a/noir/docs/versioned_docs/version-v0.23.0/reference/NoirJS/noir_js/functions/sha256.md b/noir/docs/versioned_docs/version-v0.23.0/reference/NoirJS/noir_js/functions/sha256.md new file mode 100644 index 00000000000..6ba4ecac022 --- /dev/null +++ b/noir/docs/versioned_docs/version-v0.23.0/reference/NoirJS/noir_js/functions/sha256.md @@ -0,0 +1,21 @@ +# sha256() + +```ts +sha256(inputs): Uint8Array +``` + +Calculates the SHA256 hash of the input bytes + +## Parameters + +| Parameter | Type | Description | +| :------ | :------ | :------ | +| `inputs` | `Uint8Array` | | + +## Returns + +`Uint8Array` + +*** + +Generated using [typedoc-plugin-markdown](https://www.npmjs.com/package/typedoc-plugin-markdown) and [TypeDoc](https://typedoc.org/) diff --git a/noir/docs/versioned_docs/version-v0.23.0/reference/NoirJS/noir_js/functions/xor.md b/noir/docs/versioned_docs/version-v0.23.0/reference/NoirJS/noir_js/functions/xor.md new file mode 100644 index 00000000000..8d762b895d3 --- /dev/null +++ b/noir/docs/versioned_docs/version-v0.23.0/reference/NoirJS/noir_js/functions/xor.md @@ -0,0 +1,22 @@ +# xor() + +```ts +xor(lhs, rhs): string +``` + +Performs a bitwise XOR operation between `lhs` and `rhs` + +## Parameters + +| Parameter | Type | Description | +| :------ | :------ | :------ | +| `lhs` | `string` | | +| `rhs` | `string` | | + +## Returns + +`string` + +*** + +Generated using [typedoc-plugin-markdown](https://www.npmjs.com/package/typedoc-plugin-markdown) and [TypeDoc](https://typedoc.org/) diff --git a/noir/docs/versioned_docs/version-v0.23.0/reference/NoirJS/noir_js/index.md b/noir/docs/versioned_docs/version-v0.23.0/reference/NoirJS/noir_js/index.md new file mode 100644 index 00000000000..d600e21b299 --- /dev/null +++ b/noir/docs/versioned_docs/version-v0.23.0/reference/NoirJS/noir_js/index.md @@ -0,0 +1,37 @@ +# noir_js + +## Exports + +### Classes + +| Class | Description | +| :------ | :------ | +| [Noir](classes/Noir.md) | - | + +### Type Aliases + +| Type alias | Description | +| :------ | :------ | +| [CompiledCircuit](type-aliases/CompiledCircuit.md) | - | +| [ForeignCallHandler](type-aliases/ForeignCallHandler.md) | A callback which performs an foreign call and returns the response. | +| [ForeignCallInput](type-aliases/ForeignCallInput.md) | - | +| [ForeignCallOutput](type-aliases/ForeignCallOutput.md) | - | +| [InputMap](type-aliases/InputMap.md) | - | +| [ProofData](type-aliases/ProofData.md) | - | +| [WitnessMap](type-aliases/WitnessMap.md) | - | + +### Functions + +| Function | Description | +| :------ | :------ | +| [and](functions/and.md) | Performs a bitwise AND operation between `lhs` and `rhs` | +| [blake2s256](functions/blake2s256.md) | Calculates the Blake2s256 hash of the input bytes | +| [ecdsa\_secp256k1\_verify](functions/ecdsa_secp256k1_verify.md) | Verifies a ECDSA signature over the secp256k1 curve. | +| [ecdsa\_secp256r1\_verify](functions/ecdsa_secp256r1_verify.md) | Verifies a ECDSA signature over the secp256r1 curve. | +| [keccak256](functions/keccak256.md) | Calculates the Keccak256 hash of the input bytes | +| [sha256](functions/sha256.md) | Calculates the SHA256 hash of the input bytes | +| [xor](functions/xor.md) | Performs a bitwise XOR operation between `lhs` and `rhs` | + +*** + +Generated using [typedoc-plugin-markdown](https://www.npmjs.com/package/typedoc-plugin-markdown) and [TypeDoc](https://typedoc.org/) diff --git a/noir/docs/versioned_docs/version-v0.23.0/reference/NoirJS/noir_js/type-aliases/CompiledCircuit.md b/noir/docs/versioned_docs/version-v0.23.0/reference/NoirJS/noir_js/type-aliases/CompiledCircuit.md new file mode 100644 index 00000000000..34e0dd04205 --- /dev/null +++ b/noir/docs/versioned_docs/version-v0.23.0/reference/NoirJS/noir_js/type-aliases/CompiledCircuit.md @@ -0,0 +1,20 @@ +# CompiledCircuit + +```ts +type CompiledCircuit: object; +``` + +## Description + +The representation of a compiled circuit + +## Type declaration + +| Member | Type | Description | +| :------ | :------ | :------ | +| `abi` | `Abi` | **Description**

ABI representation of the circuit | +| `bytecode` | `string` | **Description**

The bytecode of the circuit | + +*** + +Generated using [typedoc-plugin-markdown](https://www.npmjs.com/package/typedoc-plugin-markdown) and [TypeDoc](https://typedoc.org/) diff --git a/noir/docs/versioned_docs/version-v0.23.0/reference/NoirJS/noir_js/type-aliases/ForeignCallHandler.md b/noir/docs/versioned_docs/version-v0.23.0/reference/NoirJS/noir_js/type-aliases/ForeignCallHandler.md new file mode 100644 index 00000000000..812b8b16481 --- /dev/null +++ b/noir/docs/versioned_docs/version-v0.23.0/reference/NoirJS/noir_js/type-aliases/ForeignCallHandler.md @@ -0,0 +1,24 @@ +# ForeignCallHandler + +```ts +type ForeignCallHandler: (name, inputs) => Promise; +``` + +A callback which performs an foreign call and returns the response. + +## Parameters + +| Parameter | Type | Description | +| :------ | :------ | :------ | +| `name` | `string` | The identifier for the type of foreign call being performed. | +| `inputs` | [`ForeignCallInput`](ForeignCallInput.md)[] | An array of hex encoded inputs to the foreign call. | + +## Returns + +`Promise`\<[`ForeignCallOutput`](ForeignCallOutput.md)[]\> + +outputs - An array of hex encoded outputs containing the results of the foreign call. + +*** + +Generated using [typedoc-plugin-markdown](https://www.npmjs.com/package/typedoc-plugin-markdown) and [TypeDoc](https://typedoc.org/) diff --git a/noir/docs/versioned_docs/version-v0.23.0/reference/NoirJS/noir_js/type-aliases/ForeignCallInput.md b/noir/docs/versioned_docs/version-v0.23.0/reference/NoirJS/noir_js/type-aliases/ForeignCallInput.md new file mode 100644 index 00000000000..dd95809186a --- /dev/null +++ b/noir/docs/versioned_docs/version-v0.23.0/reference/NoirJS/noir_js/type-aliases/ForeignCallInput.md @@ -0,0 +1,9 @@ +# ForeignCallInput + +```ts +type ForeignCallInput: string[]; +``` + +*** + +Generated using [typedoc-plugin-markdown](https://www.npmjs.com/package/typedoc-plugin-markdown) and [TypeDoc](https://typedoc.org/) diff --git a/noir/docs/versioned_docs/version-v0.23.0/reference/NoirJS/noir_js/type-aliases/ForeignCallOutput.md b/noir/docs/versioned_docs/version-v0.23.0/reference/NoirJS/noir_js/type-aliases/ForeignCallOutput.md new file mode 100644 index 00000000000..b71fb78a946 --- /dev/null +++ b/noir/docs/versioned_docs/version-v0.23.0/reference/NoirJS/noir_js/type-aliases/ForeignCallOutput.md @@ -0,0 +1,9 @@ +# ForeignCallOutput + +```ts +type ForeignCallOutput: string | string[]; +``` + +*** + +Generated using [typedoc-plugin-markdown](https://www.npmjs.com/package/typedoc-plugin-markdown) and [TypeDoc](https://typedoc.org/) diff --git a/noir/docs/versioned_docs/version-v0.23.0/reference/NoirJS/noir_js/type-aliases/InputMap.md b/noir/docs/versioned_docs/version-v0.23.0/reference/NoirJS/noir_js/type-aliases/InputMap.md new file mode 100644 index 00000000000..c714e999d93 --- /dev/null +++ b/noir/docs/versioned_docs/version-v0.23.0/reference/NoirJS/noir_js/type-aliases/InputMap.md @@ -0,0 +1,13 @@ +# InputMap + +```ts +type InputMap: object; +``` + +## Index signature + + \[`key`: `string`\]: `InputValue` + +*** + +Generated using [typedoc-plugin-markdown](https://www.npmjs.com/package/typedoc-plugin-markdown) and [TypeDoc](https://typedoc.org/) diff --git a/noir/docs/versioned_docs/version-v0.23.0/reference/NoirJS/noir_js/type-aliases/ProofData.md b/noir/docs/versioned_docs/version-v0.23.0/reference/NoirJS/noir_js/type-aliases/ProofData.md new file mode 100644 index 00000000000..05cebbc4e94 --- /dev/null +++ b/noir/docs/versioned_docs/version-v0.23.0/reference/NoirJS/noir_js/type-aliases/ProofData.md @@ -0,0 +1,20 @@ +# ProofData + +```ts +type ProofData: object; +``` + +## Description + +The representation of a proof + +## Type declaration + +| Member | Type | Description | +| :------ | :------ | :------ | +| `proof` | `Uint8Array` | **Description**

An byte array representing the proof | +| `publicInputs` | `string`[] | **Description**

Public inputs of a proof | + +*** + +Generated using [typedoc-plugin-markdown](https://www.npmjs.com/package/typedoc-plugin-markdown) and [TypeDoc](https://typedoc.org/) diff --git a/noir/docs/versioned_docs/version-v0.23.0/reference/NoirJS/noir_js/type-aliases/WitnessMap.md b/noir/docs/versioned_docs/version-v0.23.0/reference/NoirJS/noir_js/type-aliases/WitnessMap.md new file mode 100644 index 00000000000..258c46f9d0c --- /dev/null +++ b/noir/docs/versioned_docs/version-v0.23.0/reference/NoirJS/noir_js/type-aliases/WitnessMap.md @@ -0,0 +1,9 @@ +# WitnessMap + +```ts +type WitnessMap: Map; +``` + +*** + +Generated using [typedoc-plugin-markdown](https://www.npmjs.com/package/typedoc-plugin-markdown) and [TypeDoc](https://typedoc.org/) diff --git a/noir/docs/versioned_docs/version-v0.23.0/reference/NoirJS/noir_js/typedoc-sidebar.cjs b/noir/docs/versioned_docs/version-v0.23.0/reference/NoirJS/noir_js/typedoc-sidebar.cjs new file mode 100644 index 00000000000..fe2629ddc9f --- /dev/null +++ b/noir/docs/versioned_docs/version-v0.23.0/reference/NoirJS/noir_js/typedoc-sidebar.cjs @@ -0,0 +1,4 @@ +// @ts-check +/** @type {import('@docusaurus/plugin-content-docs').SidebarsConfig} */ +const typedocSidebar = { items: [{"type":"category","label":"Classes","items":[{"type":"doc","id":"reference/NoirJS/noir_js/classes/Noir","label":"Noir"}]},{"type":"category","label":"Type Aliases","items":[{"type":"doc","id":"reference/NoirJS/noir_js/type-aliases/CompiledCircuit","label":"CompiledCircuit"},{"type":"doc","id":"reference/NoirJS/noir_js/type-aliases/ForeignCallHandler","label":"ForeignCallHandler"},{"type":"doc","id":"reference/NoirJS/noir_js/type-aliases/ForeignCallInput","label":"ForeignCallInput"},{"type":"doc","id":"reference/NoirJS/noir_js/type-aliases/ForeignCallOutput","label":"ForeignCallOutput"},{"type":"doc","id":"reference/NoirJS/noir_js/type-aliases/InputMap","label":"InputMap"},{"type":"doc","id":"reference/NoirJS/noir_js/type-aliases/ProofData","label":"ProofData"},{"type":"doc","id":"reference/NoirJS/noir_js/type-aliases/WitnessMap","label":"WitnessMap"}]},{"type":"category","label":"Functions","items":[{"type":"doc","id":"reference/NoirJS/noir_js/functions/and","label":"and"},{"type":"doc","id":"reference/NoirJS/noir_js/functions/blake2s256","label":"blake2s256"},{"type":"doc","id":"reference/NoirJS/noir_js/functions/ecdsa_secp256k1_verify","label":"ecdsa_secp256k1_verify"},{"type":"doc","id":"reference/NoirJS/noir_js/functions/ecdsa_secp256r1_verify","label":"ecdsa_secp256r1_verify"},{"type":"doc","id":"reference/NoirJS/noir_js/functions/keccak256","label":"keccak256"},{"type":"doc","id":"reference/NoirJS/noir_js/functions/sha256","label":"sha256"},{"type":"doc","id":"reference/NoirJS/noir_js/functions/xor","label":"xor"}]}]}; +module.exports = typedocSidebar.items; \ No newline at end of file diff --git a/noir/docs/versioned_docs/version-v0.23.0/reference/_category_.json b/noir/docs/versioned_docs/version-v0.23.0/reference/_category_.json new file mode 100644 index 00000000000..5b6a20a609a --- /dev/null +++ b/noir/docs/versioned_docs/version-v0.23.0/reference/_category_.json @@ -0,0 +1,5 @@ +{ + "position": 4, + "collapsible": true, + "collapsed": true +} diff --git a/noir/docs/versioned_docs/version-v0.23.0/reference/nargo_commands.md b/noir/docs/versioned_docs/version-v0.23.0/reference/nargo_commands.md new file mode 100644 index 00000000000..fc2671b2bfc --- /dev/null +++ b/noir/docs/versioned_docs/version-v0.23.0/reference/nargo_commands.md @@ -0,0 +1,253 @@ +--- +title: Nargo +description: + Noir CLI Commands for Noir Prover and Verifier to create, execute, prove and verify programs, + generate Solidity verifier smart contract and compile into JSON file containing ACIR + representation and ABI of circuit. +keywords: + [ + Nargo, + Noir CLI, + Noir Prover, + Noir Verifier, + generate Solidity verifier, + compile JSON file, + ACIR representation, + ABI of circuit, + TypeScript, + ] +sidebar_position: 0 +--- + +## General options + +| Option | Description | +| -------------------- | -------------------------------------------------- | +| `--show-ssa` | Emit debug information for the intermediate SSA IR | +| `--deny-warnings` | Quit execution when warnings are emitted | +| `--silence-warnings` | Suppress warnings | +| `-h, --help` | Print help | + +## `nargo help [subcommand]` + +Prints the list of available commands or specific information of a subcommand. + +_Arguments_ + +| Argument | Description | +| -------------- | -------------------------------------------- | +| `` | The subcommand whose help message to display | + +## `nargo backend` + +Installs and selects custom backends used to generate and verify proofs. + +### Commands + +| Command | Description | +| ----------- | --------------------------------------------------------- | +| `current` | Prints the name of the currently active backend | +| `ls` | Prints the list of currently installed backends | +| `use` | Select the backend to use | +| `install` | Install a new backend from a URL | +| `uninstall` | Uninstalls a backend | +| `help` | Print this message or the help of the given subcommand(s) | + +### Options + +| Option | Description | +| ------------ | ----------- | +| `-h, --help` | Print help | + +## `nargo check` + +Generate the `Prover.toml` and `Verifier.toml` files for specifying prover and verifier in/output +values of the Noir program respectively. + +### Options + +| Option | Description | +| --------------------- | ------------------------------------- | +| `--package ` | The name of the package to check | +| `--workspace` | Check all packages in the workspace | +| `--print-acir` | Display the ACIR for compiled circuit | +| `--deny-warnings` | Treat all warnings as errors | +| `--silence-warnings` | Suppress warnings | +| `-h, --help` | Print help | + +### `nargo codegen-verifier` + +Generate a Solidity verifier smart contract for the program. + +### Options + +| Option | Description | +| --------------------- | ------------------------------------- | +| `--package ` | The name of the package to codegen | +| `--workspace` | Codegen all packages in the workspace | +| `--print-acir` | Display the ACIR for compiled circuit | +| `--deny-warnings` | Treat all warnings as errors | +| `--silence-warnings` | Suppress warnings | +| `-h, --help` | Print help | + +## `nargo compile` + +Compile the program into a JSON build artifact file containing the ACIR representation and the ABI +of the circuit. This build artifact can then be used to generate and verify proofs. + +You can also use "build" as an alias for compile (e.g. `nargo build`). + +### Options + +| Option | Description | +| --------------------- | ------------------------------------------------------------ | +| `--package ` | The name of the package to compile | +| `--workspace` | Compile all packages in the workspace | +| `--print-acir` | Display the ACIR for compiled circuit | +| `--deny-warnings` | Treat all warnings as errors | +| `--silence-warnings` | Suppress warnings | +| `-h, --help` | Print help | + +## `nargo new ` + +Creates a new Noir project in a new folder. + +**Arguments** + +| Argument | Description | +| -------- | -------------------------------- | +| `` | The path to save the new project | + +### Options + +| Option | Description | +| --------------- | ----------------------------------------------------- | +| `--name ` | Name of the package [default: package directory name] | +| `--lib` | Use a library template | +| `--bin` | Use a binary template [default] | +| `--contract` | Use a contract template | +| `-h, --help` | Print help | + +## `nargo init` + +Creates a new Noir project in the current directory. + +### Options + +| Option | Description | +| --------------- | ----------------------------------------------------- | +| `--name ` | Name of the package [default: current directory name] | +| `--lib` | Use a library template | +| `--bin` | Use a binary template [default] | +| `--contract` | Use a contract template | +| `-h, --help` | Print help | + +## `nargo execute [WITNESS_NAME]` + +Runs the Noir program and prints its return value. + +**Arguments** + +| Argument | Description | +| ---------------- | ----------------------------------------- | +| `[WITNESS_NAME]` | Write the execution witness to named file | + +### Options + +| Option | Description | +| --------------------------------- | ------------------------------------------------------------------------------------ | +| `-p, --prover-name ` | The name of the toml file which contains the inputs for the prover [default: Prover] | +| `--package ` | The name of the package to execute | +| `--workspace` | Execute all packages in the workspace | +| `--print-acir` | Display the ACIR for compiled circuit | +| `--deny-warnings` | Treat all warnings as errors | +| `--silence-warnings` | Suppress warnings | +| `--oracle-resolver` | JSON RPC url to solve oracle calls | +| `-h, --help` | Print help | + +_Usage_ + +The inputs to the circuit are read from the `Prover.toml` file generated by `nargo check`, which +must be filled in. + +To save the witness to file, run the command with a value for the `WITNESS_NAME` argument. A +`.tr` file will then be saved in the `./target` folder. + +## `nargo prove` + +Creates a proof for the program. + +### Options + +| Option | Description | +| ------------------------------------- | ---------------------------------------------------------------------------------------- | +| `-p, --prover-name ` | The name of the toml file which contains the inputs for the prover [default: Prover] | +| `-v, --verifier-name ` | The name of the toml file which contains the inputs for the verifier [default: Verifier] | +| `--verify` | Verify proof after proving | +| `--package ` | The name of the package to prove | +| `--workspace` | Prove all packages in the workspace | +| `--print-acir` | Display the ACIR for compiled circuit | +| `--deny-warnings` | Treat all warnings as errors | +| `--silence-warnings` | Suppress warnings | +| `--oracle-resolver` | JSON RPC url to solve oracle calls | +| `-h, --help` | Print help | + +## `nargo verify` + +Given a proof and a program, verify whether the proof is valid. + +### Options + +| Option | Description | +| ------------------------------------- | ---------------------------------------------------------------------------------------- | +| `-v, --verifier-name ` | The name of the toml file which contains the inputs for the verifier [default: Verifier] | +| `--package ` | The name of the package to verify | +| `--workspace` | Verify all packages in the workspace | +| `--print-acir` | Display the ACIR for compiled circuit | +| `--deny-warnings` | Treat all warnings as errors | +| `--silence-warnings` | Suppress warnings | +| `-h, --help` | Print help | + +## `nargo test [TEST_NAME]` + +Nargo will automatically compile and run any functions which have the decorator `#[test]` on them if +you run `nargo test`. To print `println` statements in tests, use the `--show-output` flag. + +Takes an optional `--exact` flag which allows you to select tests based on an exact name. + +See an example on the [testing page](../getting_started/tooling/testing.md). + +### Options + +| Option | Description | +| --------------------- | -------------------------------------- | +| `--show-output` | Display output of `println` statements | +| `--exact` | Only run tests that match exactly | +| `--package ` | The name of the package to test | +| `--workspace` | Test all packages in the workspace | +| `--print-acir` | Display the ACIR for compiled circuit | +| `--deny-warnings` | Treat all warnings as errors | +| `--silence-warnings` | Suppress warnings | +| `--oracle-resolver` | JSON RPC url to solve oracle calls | +| `-h, --help` | Print help | + +## `nargo info` + +Prints a table containing the information of the package. + +Currently the table provide + +1. The number of ACIR opcodes +2. The final number gates in the circuit used by a backend + +If the file contains a contract the table will provide the +above information about each function of the contract. + +## `nargo lsp` + +Start a long-running Language Server process that communicates over stdin/stdout. +Usually this command is not run by a user, but instead will be run by a Language Client, such as [vscode-noir](https://github.com/noir-lang/vscode-noir). + +## `nargo fmt` + +Automatically formats your Noir source code based on the default formatting settings. diff --git a/noir/docs/versioned_docs/version-v0.23.0/tutorials/noirjs_app.md b/noir/docs/versioned_docs/version-v0.23.0/tutorials/noirjs_app.md new file mode 100644 index 00000000000..23534795dde --- /dev/null +++ b/noir/docs/versioned_docs/version-v0.23.0/tutorials/noirjs_app.md @@ -0,0 +1,279 @@ +--- +title: Building a web app with NoirJS +description: Learn how to setup a new app that uses Noir to generate and verify zero-knowledge SNARK proofs in a typescript or javascript environment. +keywords: [how to, guide, javascript, typescript, noir, barretenberg, zero-knowledge, proofs, app] +sidebar_position: 0 +pagination_next: noir/concepts/data_types/index +--- + +NoirJS is a set of packages meant to work both in a browser and a server environment. In this tutorial, we will build a simple web app using them. From here, you should get an idea on how to proceed with your own Noir projects! + +You can find the complete app code for this guide [here](https://github.com/noir-lang/tiny-noirjs-app). + +## Setup + +:::note + +Feel free to use whatever versions, just keep in mind that Nargo and the NoirJS packages are meant to be in sync. For example, Nargo 0.19.x matches `noir_js@0.19.x`, etc. + +In this guide, we will be pinned to 0.19.4. + +::: + +Before we start, we want to make sure we have Node and Nargo installed. + +We start by opening a terminal and executing `node --version`. If we don't get an output like `v20.10.0`, that means node is not installed. Let's do that by following the handy [nvm guide](https://github.com/nvm-sh/nvm?tab=readme-ov-file#install--update-script). + +As for `Nargo`, we can follow the the [Nargo guide](../getting_started/installation/index.md) to install it. If you're lazy, just paste this on a terminal and run `noirup`: + +```sh +curl -L https://raw.githubusercontent.com/noir-lang/noirup/main/install | bash +``` + +Easy enough. Onwards! + +## Our project + +ZK is a powerful technology. An app that doesn't reveal one of the inputs to *anyone* is almost unbelievable, yet Noir makes it as easy as a single line of code. + +In fact, it's so simple that it comes nicely packaged in `nargo`. Let's do that! + +### Nargo + +Run: + +```nargo new circuit``` + +And... That's about it. Your program is ready to be compiled and run. + +To compile, let's `cd` into the `circuit` folder to enter our project, and call: + +```nargo compile``` + +This compiles our circuit into `json` format and add it to a new `target` folder. + +:::info + +At this point in the tutorial, your folder structure should look like this: + +```tree +. +└── circuit <---- our working directory + ├── Nargo.toml + ├── src + │ └── main.nr + └── target + └── circuit.json +``` + +::: + +### Node and Vite + +If you want to explore Nargo, feel free to go on a side-quest now and follow the steps in the +[getting started](../getting_started/hello_noir/index.md) guide. However, we want our app to run on the browser, so we need Vite. + +Vite is a powerful tool to generate static websites. While it provides all kinds of features, let's just go barebones with some good old vanilla JS. + +To do this this, go back to the previous folder (`cd ..`) and create a new vite project by running `npm create vite` and choosing "Vanilla" and "Javascript". + +You should see `vite-project` appear in your root folder. This seems like a good time to `cd` into it and install our NoirJS packages: + +```bash +npm i @noir-lang/backend_barretenberg@0.19.4 @noir-lang/noir_js@0.19.4 +``` + +:::info + +At this point in the tutorial, your folder structure should look like this: + +```tree +. +└── circuit + └── ...etc... +└── vite-project <---- our working directory + └── ...etc... +``` + +::: + +#### Some cleanup + +`npx create vite` is amazing but it creates a bunch of files we don't really need for our simple example. Actually, let's just delete everything except for `index.html`, `main.js` and `package.json`. I feel lighter already. + +![my heart is ready for you, noir.js](../../static/img/memes/titanic.jpeg) + +## HTML + +Our app won't run like this, of course. We need some working HTML, at least. Let's open our broken-hearted `index.html` and replace everything with this code snippet: + +```html + + + + + + +

Noir app

+
+ + +
+
+

Logs

+

Proof

+
+ + +``` + +It *could* be a beautiful UI... Depending on which universe you live in. + +## Some good old vanilla Javascript + +Our love for Noir needs undivided attention, so let's just open `main.js` and delete everything (this is where the romantic scenery becomes a bit creepy). + +Start by pasting in this boilerplate code: + +```js +const setup = async () => { + await Promise.all([ + import("@noir-lang/noirc_abi").then(module => + module.default(new URL("@noir-lang/noirc_abi/web/noirc_abi_wasm_bg.wasm", import.meta.url).toString()) + ), + import("@noir-lang/acvm_js").then(module => + module.default(new URL("@noir-lang/acvm_js/web/acvm_js_bg.wasm", import.meta.url).toString()) + ) + ]); +} + +function display(container, msg) { + const c = document.getElementById(container); + const p = document.createElement('p'); + p.textContent = msg; + c.appendChild(p); +} + +document.getElementById('submitGuess').addEventListener('click', async () => { + try { + // here's where love happens + } catch(err) { + display("logs", "Oh 💔 Wrong guess") + } +}); + +``` + +The display function doesn't do much. We're simply manipulating our website to see stuff happening. For example, if the proof fails, it will simply log a broken heart 😢 + +As for the `setup` function, it's just a sad reminder that dealing with `wasm` on the browser is not as easy as it should. Just copy, paste, and forget. + +:::info + +At this point in the tutorial, your folder structure should look like this: + +```tree +. +└── circuit + └── ...same as above +└── vite-project + ├── main.js + ├── package.json + └── index.html +``` + +You'll see other files and folders showing up (like `package-lock.json`, `node_modules`) but you shouldn't have to care about those. + +::: + +## Some NoirJS + +We're starting with the good stuff now. If you've compiled the circuit as described above, you should have a `json` file we want to import at the very top of our `main.js` file: + +```ts +import circuit from '../circuit/target/circuit.json'; +``` + +[Noir is backend-agnostic](../index.mdx#whats-new-about-noir). We write Noir, but we also need a proving backend. That's why we need to import and instantiate the two dependencies we installed above: `BarretenbergBackend` and `Noir`. Let's import them right below: + +```js +import { BarretenbergBackend } from '@noir-lang/backend_barretenberg'; +import { Noir } from '@noir-lang/noir_js'; +``` + +And instantiate them inside our try-catch block: + +```ts +// try { +const backend = new BarretenbergBackend(circuit); +const noir = new Noir(circuit, backend); +// } +``` + +:::note + +For the remainder of the tutorial, everything will be happening inside the `try` block + +::: + +## Our app + +Now for the app itself. We're capturing whatever is in the input when people press the submit button. Just add this: + +```js +const x = parseInt(document.getElementById('guessInput').value); +const input = { x, y: 2 }; +``` + +Now we're ready to prove stuff! Let's feed some inputs to our circuit and calculate the proof: + +```js +await setup(); // let's squeeze our wasm inits here + +display('logs', 'Generating proof... ⌛'); +const proof = await noir.generateFinalProof(input); +display('logs', 'Generating proof... ✅'); +display('results', proof.proof); +``` + +You're probably eager to see stuff happening, so go and run your app now! + +From your terminal, run `npm run dev`. If it doesn't open a browser for you, just visit `localhost:5173`. You should now see the worst UI ever, with an ugly input. + +![Getting Started 0](@site/static/img/noir_getting_started_1.png) + +Now, our circuit says `fn main(x: Field, y: pub Field)`. This means only the `y` value is public, and it's hardcoded above: `input = { x, y: 2 }`. In other words, you won't need to send your secret`x` to the verifier! + +By inputting any number other than 2 in the input box and clicking "submit", you should get a valid proof. Otherwise the proof won't even generate correctly. By the way, if you're human, you shouldn't be able to understand anything on the "proof" box. That's OK. We like you, human ❤️. + +## Verifying + +Time to celebrate, yes! But we shouldn't trust machines so blindly. Let's add these lines to see our proof being verified: + +```js +display('logs', 'Verifying proof... ⌛'); +const verification = await noir.verifyFinalProof(proof); +if (verification) display('logs', 'Verifying proof... ✅'); +``` + +You have successfully generated a client-side Noir web app! + +![coded app without math knowledge](../../static/img/memes/flextape.jpeg) + +## Further Reading + +You can see how noirjs is used in a full stack Next.js hardhat application in the [noir-starter repo here](https://github.com/noir-lang/noir-starter/tree/main/vite-hardhat). The example shows how to calculate a proof in the browser and verify it with a deployed Solidity verifier contract from noirjs. + +You should also check out the more advanced examples in the [noir-examples repo](https://github.com/noir-lang/noir-examples), where you'll find reference usage for some cool apps. diff --git a/noir/docs/versioned_sidebars/version-v0.23.0-sidebars.json b/noir/docs/versioned_sidebars/version-v0.23.0-sidebars.json new file mode 100644 index 00000000000..b16f79cc176 --- /dev/null +++ b/noir/docs/versioned_sidebars/version-v0.23.0-sidebars.json @@ -0,0 +1,83 @@ +{ + "sidebar": [ + { + "type": "doc", + "id": "index" + }, + { + "type": "category", + "label": "Getting Started", + "items": [ + { + "type": "autogenerated", + "dirName": "getting_started" + } + ] + }, + { + "type": "category", + "label": "The Noir Language", + "items": [ + { + "type": "autogenerated", + "dirName": "noir" + } + ] + }, + { + "type": "html", + "value": "
", + "defaultStyle": true + }, + { + "type": "category", + "label": "How To Guides", + "items": [ + { + "type": "autogenerated", + "dirName": "how_to" + } + ] + }, + { + "type": "category", + "label": "Explainers", + "items": [ + { + "type": "autogenerated", + "dirName": "explainers" + } + ] + }, + { + "type": "category", + "label": "Tutorials", + "items": [ + { + "type": "autogenerated", + "dirName": "tutorials" + } + ] + }, + { + "type": "category", + "label": "Reference", + "items": [ + { + "type": "autogenerated", + "dirName": "reference" + } + ] + }, + { + "type": "html", + "value": "
", + "defaultStyle": true + }, + { + "type": "doc", + "id": "migration_notes", + "label": "Migration notes" + } + ] +} diff --git a/noir/noir_stdlib/src/array.nr b/noir/noir_stdlib/src/array.nr index bcdf56dd7aa..87cf4167dac 100644 --- a/noir/noir_stdlib/src/array.nr +++ b/noir/noir_stdlib/src/array.nr @@ -3,10 +3,10 @@ // by the methods in the `slice` module impl [T; N] { #[builtin(array_len)] - pub fn len(_self: Self) -> Field {} + pub fn len(self) -> Field {} #[builtin(arraysort)] - pub fn sort(_self: Self) -> Self {} + pub fn sort(self) -> Self {} // Sort with a custom sorting function. pub fn sort_via(mut a: Self, ordering: fn[Env](T, T) -> bool) -> Self { diff --git a/noir/noir_stdlib/src/bigint.nr b/noir/noir_stdlib/src/bigint.nr new file mode 100644 index 00000000000..9edd59359c1 --- /dev/null +++ b/noir/noir_stdlib/src/bigint.nr @@ -0,0 +1,53 @@ +use crate::ops::{Add, Sub, Mul, Div, Rem,}; + +struct BigInt { + pointer: u32, + modulus: u32, +} + +impl BigInt { + #[builtin(bigint_add)] + pub fn bigint_add(self, other: BigInt) -> BigInt { + } + #[builtin(bigint_neg)] + pub fn bigint_neg(self, other: BigInt) -> BigInt { + } + #[builtin(bigint_mul)] + pub fn bigint_mul(self, other: BigInt) -> BigInt { + } + #[builtin(bigint_div)] + pub fn bigint_div(self, other: BigInt) -> BigInt { + } + #[builtin(bigint_from_le_bytes)] + pub fn from_le_bytes(bytes: [u8], modulus: [u8]) -> BigInt {} + #[builtin(bigint_to_le_bytes)] + pub fn to_le_bytes(self) -> [u8] {} +} + +impl Add for BigInt { + fn add(self: Self, other: BigInt) -> BigInt { + self.bigint_add(other) + } +} +impl Sub for BigInt { + fn sub(self: Self, other: BigInt) -> BigInt { + self.bigint_neg(other) + } +} +impl Mul for BigInt { + fn mul(self: Self, other: BigInt) -> BigInt { + self.bigint_mul(other) + } +} +impl Div for BigInt { + fn div(self: Self, other: BigInt) -> BigInt { + self.bigint_div(other) + } +} +impl Rem for BigInt { + fn rem(self: Self, other: BigInt) -> BigInt { + let quotient = self.bigint_div(other); + self.bigint_neg(quotient.bigint_mul(other)) + } +} + diff --git a/noir/noir_stdlib/src/cmp.nr b/noir/noir_stdlib/src/cmp.nr index 11127494c18..b3de3e2658e 100644 --- a/noir/noir_stdlib/src/cmp.nr +++ b/noir/noir_stdlib/src/cmp.nr @@ -1,6 +1,8 @@ +// docs:start:eq-trait trait Eq { fn eq(self, other: Self) -> bool; } +// docs:end:eq-trait impl Eq for Field { fn eq(self, other: Field) -> bool { self == other } } @@ -66,7 +68,6 @@ impl Eq for Ordering { } } - // Noir doesn't have enums yet so we emulate (Lt | Eq | Gt) with a struct // that has 3 public functions for constructing the struct. struct Ordering { @@ -90,10 +91,11 @@ impl Ordering { } } - +// docs:start:ord-trait trait Ord { fn cmp(self, other: Self) -> Ordering; } +// docs:end:ord-trait // Note: Field deliberately does not implement Ord diff --git a/noir/noir_stdlib/src/collections.nr b/noir/noir_stdlib/src/collections.nr index e06c662e658..177ca96816f 100644 --- a/noir/noir_stdlib/src/collections.nr +++ b/noir/noir_stdlib/src/collections.nr @@ -1 +1,2 @@ mod vec; +mod bounded_vec; diff --git a/noir/noir_stdlib/src/collections/bounded_vec.nr b/noir/noir_stdlib/src/collections/bounded_vec.nr new file mode 100644 index 00000000000..332fefa63f9 --- /dev/null +++ b/noir/noir_stdlib/src/collections/bounded_vec.nr @@ -0,0 +1,88 @@ +struct BoundedVec { + storage: [T; MaxLen], + // TODO: change this to return a u64 as Noir now + // uses u64 for indexing + len: Field, + empty_value: T, +} + +impl BoundedVec { + pub fn new(initial_value: T) -> Self { + BoundedVec { storage: [initial_value; MaxLen], len: 0, empty_value: initial_value } + } + + pub fn get(mut self: Self, index: Field) -> T { + assert(index as u64 < self.len as u64); + self.storage[index] + } + + pub fn get_unchecked(mut self: Self, index: Field) -> T { + self.storage[index] + } + + pub fn push(&mut self, elem: T) { + assert(self.len as u64 < MaxLen as u64, "push out of bounds"); + + self.storage[self.len] = elem; + self.len += 1; + } + + pub fn len(self) -> Field { + self.len + } + + pub fn max_len(_self: BoundedVec) -> Field { + MaxLen + } + + // This is a intermediate method, while we don't have an + // .extend method + pub fn storage(self) -> [T; MaxLen] { + self.storage + } + + pub fn extend_from_array(&mut self, array: [T; Len]) { + let new_len = self.len + array.len(); + assert(new_len as u64 <= MaxLen as u64, "extend_from_array out of bounds"); + for i in 0..array.len() { + self.storage[self.len + i] = array[i]; + } + self.len = new_len; + } + + pub fn extend_from_bounded_vec(&mut self, vec: BoundedVec) { + let append_len = vec.len(); + let new_len = self.len + append_len; + assert(new_len as u64 <= MaxLen as u64, "extend_from_bounded_vec out of bounds"); + + let mut exceeded_len = false; + for i in 0..Len { + exceeded_len |= i == append_len; + if !exceeded_len { + self.storage[self.len + (i as Field)] = vec.get_unchecked(i as Field); + } + } + self.len = new_len; + } + + pub fn pop(&mut self) -> T { + assert(self.len as u64 > 0); + self.len -= 1; + + let elem = self.storage[self.len]; + self.storage[self.len] = self.empty_value; + elem + } + + pub fn any(self, predicate: fn[Env](T) -> bool) -> bool { + let mut ret = false; + let mut exceeded_len = false; + for i in 0..MaxLen { + exceeded_len |= i == self.len; + if (!exceeded_len) { + ret |= predicate(self.storage[i]); + } + } + ret + } +} \ No newline at end of file diff --git a/noir/noir_stdlib/src/convert.nr b/noir/noir_stdlib/src/convert.nr new file mode 100644 index 00000000000..814f63f1cde --- /dev/null +++ b/noir/noir_stdlib/src/convert.nr @@ -0,0 +1,61 @@ +// docs:start:from-trait +trait From { + fn from(input: T) -> Self; +} +// docs:end:from-trait + +impl From for T { + fn from(input: T) -> T { + input + } +} + +// docs:start:into-trait +trait Into { + fn into(input: Self) -> T; +} + +impl Into for U where T: From { + fn into(input: U) -> T { + T::from(input) + } +} +// docs:end:into-trait + +// docs:start:from-impls +// Unsigned integers +impl From for u16 { fn from(value: u8) -> u16 { value as u16 } } + +impl From for u32 { fn from(value: u8) -> u32 { value as u32 } } +impl From for u32 { fn from(value: u16) -> u32 { value as u32 } } + +impl From for u64 { fn from(value: u8) -> u64 { value as u64 } } +impl From for u64 { fn from(value: u16) -> u64 { value as u64 } } +impl From for u64 { fn from(value: u32) -> u64 { value as u64 } } + +impl From for Field { fn from(value: u8) -> Field { value as Field } } +impl From for Field { fn from(value: u16) -> Field { value as Field } } +impl From for Field { fn from(value: u32) -> Field { value as Field } } +impl From for Field { fn from(value: u64) -> Field { value as Field } } + +// Signed integers +impl From for i16 { fn from(value: i8) -> i16 { value as i16 } } + +impl From for i32 { fn from(value: i8) -> i32 { value as i32 } } +impl From for i32 { fn from(value: i16) -> i32 { value as i32 } } + +impl From for i64 { fn from(value: i8) -> i64 { value as i64 } } +impl From for i64 { fn from(value: i16) -> i64 { value as i64 } } +impl From for i64 { fn from(value: i32) -> i64 { value as i64 } } + +// Booleans +impl From for u8 { fn from(value: bool) -> u8 { value as u8 } } +impl From for u16 { fn from(value: bool) -> u16 { value as u16 } } +impl From for u32 { fn from(value: bool) -> u32 { value as u32 } } +impl From for u64 { fn from(value: bool) -> u64 { value as u64 } } +impl From for i8 { fn from(value: bool) -> i8 { value as i8 } } +impl From for i16 { fn from(value: bool) -> i16 { value as i16 } } +impl From for i32 { fn from(value: bool) -> i32 { value as i32 } } +impl From for i64 { fn from(value: bool) -> i64 { value as i64 } } +impl From for Field { fn from(value: bool) -> Field { value as Field } } +// docs:end:from-impls diff --git a/noir/noir_stdlib/src/default.nr b/noir/noir_stdlib/src/default.nr index 232be74489c..ba6412a834f 100644 --- a/noir/noir_stdlib/src/default.nr +++ b/noir/noir_stdlib/src/default.nr @@ -1,6 +1,8 @@ +// docs:start:default-trait trait Default { fn default() -> Self; } +// docs:end:default-trait impl Default for Field { fn default() -> Field { 0 } } diff --git a/noir/noir_stdlib/src/ecdsa_secp256k1.nr b/noir/noir_stdlib/src/ecdsa_secp256k1.nr index b1f2b12c76b..e8d9af2230f 100644 --- a/noir/noir_stdlib/src/ecdsa_secp256k1.nr +++ b/noir/noir_stdlib/src/ecdsa_secp256k1.nr @@ -1,7 +1,10 @@ #[foreign(ecdsa_secp256k1)] +// docs:start:ecdsa_secp256k1 pub fn verify_signature( - _public_key_x: [u8; 32], - _public_key_y: [u8; 32], - _signature: [u8; 64], - _message_hash: [u8; N] -) -> bool {} + public_key_x: [u8; 32], + public_key_y: [u8; 32], + signature: [u8; 64], + message_hash: [u8; N] +) -> bool +// docs:end:ecdsa_secp256k1 +{} \ No newline at end of file diff --git a/noir/noir_stdlib/src/ecdsa_secp256r1.nr b/noir/noir_stdlib/src/ecdsa_secp256r1.nr index 6c3cf4d7945..9fe932a2f3d 100644 --- a/noir/noir_stdlib/src/ecdsa_secp256r1.nr +++ b/noir/noir_stdlib/src/ecdsa_secp256r1.nr @@ -1,7 +1,10 @@ #[foreign(ecdsa_secp256r1)] +// docs:start:ecdsa_secp256r1 pub fn verify_signature( - _public_key_x: [u8; 32], - _public_key_y: [u8; 32], - _signature: [u8; 64], - _message_hash: [u8; N] -) -> bool {} + public_key_x: [u8; 32], + public_key_y: [u8; 32], + signature: [u8; 64], + message_hash: [u8; N] +) -> bool +// docs:end:ecdsa_secp256r1 +{} \ No newline at end of file diff --git a/noir/noir_stdlib/src/field.nr b/noir/noir_stdlib/src/field.nr index fbd76a1e8a2..66fb50119f9 100644 --- a/noir/noir_stdlib/src/field.nr +++ b/noir/noir_stdlib/src/field.nr @@ -13,13 +13,13 @@ impl Field { } #[builtin(to_le_bits)] - fn __to_le_bits(_self: Self, _bit_size: u32) -> [u1] {} + fn __to_le_bits(self, _bit_size: u32) -> [u1] {} #[builtin(to_be_bits)] - fn __to_be_bits(_self: Self, _bit_size: u32) -> [u1] {} + fn __to_be_bits(self, bit_size: u32) -> [u1] {} #[builtin(apply_range_constraint)] - fn __assert_max_bit_size(_self: Self, _bit_size: u32) {} + fn __assert_max_bit_size(self, bit_size: u32) {} pub fn assert_max_bit_size(self: Self, bit_size: u32) { crate::assert_constant(bit_size); @@ -53,10 +53,10 @@ impl Field { // decompose `_self` into a `_result_len` vector over the `_radix` basis // `_radix` must be less than 256 #[builtin(to_le_radix)] - fn __to_le_radix(_self: Self, _radix: u32, _result_len: u32) -> [u8] {} + fn __to_le_radix(self, radix: u32, result_len: u32) -> [u8] {} #[builtin(to_be_radix)] - fn __to_be_radix(_self: Self, _radix: u32, _result_len: u32) -> [u8] {} + fn __to_be_radix(self, radix: u32, result_len: u32) -> [u8] {} // Returns self to the power of the given exponent value. diff --git a/noir/noir_stdlib/src/hash.nr b/noir/noir_stdlib/src/hash.nr index 5933209d9bc..cc864039a90 100644 --- a/noir/noir_stdlib/src/hash.nr +++ b/noir/noir_stdlib/src/hash.nr @@ -2,43 +2,58 @@ mod poseidon; mod mimc; #[foreign(sha256)] -pub fn sha256(_input: [u8; N]) -> [u8; 32] {} +// docs:start:sha256 +pub fn sha256(input: [u8; N]) -> [u8; 32] +// docs:end:sha256 +{} #[foreign(blake2s)] -pub fn blake2s(_input: [u8; N]) -> [u8; 32] {} +// docs:start:blake2s +pub fn blake2s(input: [u8; N]) -> [u8; 32] +// docs:end:blake2s +{} #[foreign(blake3)] -pub fn blake3(_input: [u8; N]) -> [u8; 32] {} +// docs:start:blake3 +pub fn blake3(input: [u8; N]) -> [u8; 32] +// docs:end:blake3 +{} +// docs:start:pedersen_commitment struct PedersenPoint { x : Field, y : Field, } -pub fn pedersen_commitment(input: [Field; N]) -> PedersenPoint { +pub fn pedersen_commitment(input: [Field; N]) -> PedersenPoint +// docs:end:pedersen_commitment +{ pedersen_commitment_with_separator(input, 0) } #[foreign(pedersen_commitment)] -pub fn __pedersen_commitment_with_separator(_input: [Field; N], _separator: u32) -> [Field; 2] {} +pub fn __pedersen_commitment_with_separator(input: [Field; N], separator: u32) -> [Field; 2] {} pub fn pedersen_commitment_with_separator(input: [Field; N], separator: u32) -> PedersenPoint { let values = __pedersen_commitment_with_separator(input, separator); PedersenPoint { x: values[0], y: values[1] } } -pub fn pedersen_hash(input: [Field; N]) -> Field { +// docs:start:pedersen_hash +pub fn pedersen_hash(input: [Field; N]) -> Field +// docs:end:pedersen_hash +{ pedersen_hash_with_separator(input, 0) } #[foreign(pedersen_hash)] -pub fn pedersen_hash_with_separator(_input: [Field; N], _separator: u32) -> Field {} +pub fn pedersen_hash_with_separator(input: [Field; N], separator: u32) -> Field {} -pub fn hash_to_field(_input: [Field; N]) -> Field { +pub fn hash_to_field(input: [Field; N]) -> Field { let mut inputs_as_bytes = []; for i in 0..N { - let input_bytes = _input[i].to_le_bytes(32); + let input_bytes = input[i].to_le_bytes(32); for i in 0..32 { inputs_as_bytes = inputs_as_bytes.push_back(input_bytes[i]); } @@ -49,5 +64,13 @@ pub fn hash_to_field(_input: [Field; N]) -> Field { } #[foreign(keccak256)] -pub fn keccak256(_input: [u8; N], _message_size: u32) -> [u8; 32] {} +// docs:start:keccak256 +pub fn keccak256(input: [u8; N], message_size: u32) -> [u8; 32] +// docs:end:keccak256 +{} +#[foreign(poseidon2_permutation)] +pub fn poseidon2_permutation(_input: [u8; N], _state_length: u32) -> [u8; N] {} + +#[foreign(sha256_compression)] +pub fn sha256_compression(_input: [u32; 16], _state: [u32; 8]) -> [u32; 8] {} diff --git a/noir/noir_stdlib/src/lib.nr b/noir/noir_stdlib/src/lib.nr index 23a7c71ff45..5165d1ee07b 100644 --- a/noir/noir_stdlib/src/lib.nr +++ b/noir/noir_stdlib/src/lib.nr @@ -16,6 +16,7 @@ mod ec; mod unsafe; mod collections; mod compat; +mod convert; mod option; mod string; mod test; @@ -24,11 +25,12 @@ mod ops; mod default; mod prelude; mod uint128; +// mod bigint; // Oracle calls are required to be wrapped in an unconstrained function // Thus, the only argument to the `println` oracle is expected to always be an ident #[oracle(print)] -unconstrained fn print_oracle(_with_newline: bool, _input: T) {} +unconstrained fn print_oracle(with_newline: bool, input: T) {} unconstrained pub fn print(input: T) { print_oracle(false, input); @@ -39,20 +41,20 @@ unconstrained pub fn println(input: T) { } #[foreign(recursive_aggregation)] -pub fn verify_proof(_verification_key: [Field], _proof: [Field], _public_inputs: [Field], _key_hash: Field) {} +pub fn verify_proof(verification_key: [Field], proof: [Field], public_inputs: [Field], key_hash: Field) {} // Asserts that the given value is known at compile-time. // Useful for debugging for-loop bounds. #[builtin(assert_constant)] -pub fn assert_constant(_x: T) {} +pub fn assert_constant(x: T) {} // from_field and as_field are private since they are not valid for every type. // `as` should be the default for users to cast between primitive types, and in the future // traits can be used to work with generic types. #[builtin(from_field)] -fn from_field(_x: Field) -> T {} +fn from_field(x: Field) -> T {} #[builtin(as_field)] -fn as_field(_x: T) -> Field {} +fn as_field(x: T) -> Field {} pub fn wrapping_add(x: T, y: T) -> T { crate::from_field(crate::as_field(x) + crate::as_field(y)) diff --git a/noir/noir_stdlib/src/ops.nr b/noir/noir_stdlib/src/ops.nr index 3078ac11296..50386290b8e 100644 --- a/noir/noir_stdlib/src/ops.nr +++ b/noir/noir_stdlib/src/ops.nr @@ -1,7 +1,8 @@ - +// docs:start:add-trait trait Add { fn add(self, other: Self) -> Self; } +// docs:end:add-trait impl Add for Field { fn add(self, other: Field) -> Field { self + other } } @@ -15,9 +16,11 @@ impl Add for i16 { fn add(self, other: i16) -> i16 { self + other } } impl Add for i32 { fn add(self, other: i32) -> i32 { self + other } } impl Add for i64 { fn add(self, other: i64) -> i64 { self + other } } +// docs:start:sub-trait trait Sub { fn sub(self, other: Self) -> Self; } +// docs:end:sub-trait impl Sub for Field { fn sub(self, other: Field) -> Field { self - other } } @@ -31,9 +34,11 @@ impl Sub for i16 { fn sub(self, other: i16) -> i16 { self - other } } impl Sub for i32 { fn sub(self, other: i32) -> i32 { self - other } } impl Sub for i64 { fn sub(self, other: i64) -> i64 { self - other } } +// docs:start:mul-trait trait Mul { fn mul(self, other: Self) -> Self; } +// docs:end:mul-trait impl Mul for Field { fn mul(self, other: Field) -> Field { self * other } } @@ -47,9 +52,11 @@ impl Mul for i16 { fn mul(self, other: i16) -> i16 { self * other } } impl Mul for i32 { fn mul(self, other: i32) -> i32 { self * other } } impl Mul for i64 { fn mul(self, other: i64) -> i64 { self * other } } +// docs:start:div-trait trait Div { fn div(self, other: Self) -> Self; } +// docs:end:div-trait impl Div for Field { fn div(self, other: Field) -> Field { self / other } } @@ -63,9 +70,11 @@ impl Div for i16 { fn div(self, other: i16) -> i16 { self / other } } impl Div for i32 { fn div(self, other: i32) -> i32 { self / other } } impl Div for i64 { fn div(self, other: i64) -> i64 { self / other } } -trait Rem { +// docs:start:rem-trait +trait Rem{ fn rem(self, other: Self) -> Self; } +// docs:end:rem-trait impl Rem for u8 { fn rem(self, other: u8) -> u8 { self % other } } impl Rem for u16 { fn rem(self, other: u16) -> u16 { self % other } } @@ -77,9 +86,11 @@ impl Rem for i16 { fn rem(self, other: i16) -> i16 { self % other } } impl Rem for i32 { fn rem(self, other: i32) -> i32 { self % other } } impl Rem for i64 { fn rem(self, other: i64) -> i64 { self % other } } +// docs:start:bitor-trait trait BitOr { fn bitor(self, other: Self) -> Self; } +// docs:end:bitor-trait impl BitOr for bool { fn bitor(self, other: bool) -> bool { self | other } } @@ -93,9 +104,11 @@ impl BitOr for i16 { fn bitor(self, other: i16) -> i16 { self | other } } impl BitOr for i32 { fn bitor(self, other: i32) -> i32 { self | other } } impl BitOr for i64 { fn bitor(self, other: i64) -> i64 { self | other } } +// docs:start:bitand-trait trait BitAnd { fn bitand(self, other: Self) -> Self; } +// docs:end:bitand-trait impl BitAnd for bool { fn bitand(self, other: bool) -> bool { self & other } } @@ -109,9 +122,11 @@ impl BitAnd for i16 { fn bitand(self, other: i16) -> i16 { self & other } } impl BitAnd for i32 { fn bitand(self, other: i32) -> i32 { self & other } } impl BitAnd for i64 { fn bitand(self, other: i64) -> i64 { self & other } } +// docs:start:bitxor-trait trait BitXor { fn bitxor(self, other: Self) -> Self; } +// docs:end:bitxor-trait impl BitXor for bool { fn bitxor(self, other: bool) -> bool { self ^ other } } @@ -125,9 +140,11 @@ impl BitXor for i16 { fn bitxor(self, other: i16) -> i16 { self ^ other } } impl BitXor for i32 { fn bitxor(self, other: i32) -> i32 { self ^ other } } impl BitXor for i64 { fn bitxor(self, other: i64) -> i64 { self ^ other } } +// docs:start:shl-trait trait Shl { fn shl(self, other: Self) -> Self; } +// docs:end:shl-trait impl Shl for u8 { fn shl(self, other: u8) -> u8 { self << other } } impl Shl for u16 { fn shl(self, other: u16) -> u16 { self << other } } @@ -140,9 +157,11 @@ impl Shl for u64 { fn shl(self, other: u64) -> u64 { self << other } } // impl Shl for i32 { fn shl(self, other: i32) -> i32 { self << other } } // impl Shl for i64 { fn shl(self, other: i64) -> i64 { self << other } } +// docs:start:shr-trait trait Shr { fn shr(self, other: Self) -> Self; } +// docs:end:shr-trait impl Shr for u8 { fn shr(self, other: u8) -> u8 { self >> other } } impl Shr for u16 { fn shr(self, other: u16) -> u16 { self >> other } } diff --git a/noir/noir_stdlib/src/prelude.nr b/noir/noir_stdlib/src/prelude.nr index b57ff460371..3244329aa4b 100644 --- a/noir/noir_stdlib/src/prelude.nr +++ b/noir/noir_stdlib/src/prelude.nr @@ -1,6 +1,8 @@ use crate::collections::vec::Vec; +use crate::collections::bounded_vec::BoundedVec; use crate::option::Option; use crate::{print, println, assert_constant}; use crate::uint128::U128; use crate::cmp::{Eq, Ord}; use crate::default::Default; +use crate::convert::{From, Into}; diff --git a/noir/noir_stdlib/src/scalar_mul.nr b/noir/noir_stdlib/src/scalar_mul.nr index 37cd935cdb9..26378e4839a 100644 --- a/noir/noir_stdlib/src/scalar_mul.nr +++ b/noir/noir_stdlib/src/scalar_mul.nr @@ -1,3 +1,22 @@ +use crate::ops::Add; + +struct EmbeddedCurvePoint { + x: Field, + y: Field, +} + +impl EmbeddedCurvePoint { + fn double(self) -> EmbeddedCurvePoint { + embedded_curve_add(self, self) + } +} + +impl Add for EmbeddedCurvePoint { + fn add(self, other: EmbeddedCurvePoint) -> EmbeddedCurvePoint { + embedded_curve_add(self, other) + } +} + // Computes a fixed base scalar multiplication over the embedded curve. // For bn254, We have Grumpkin and Baby JubJub. // For bls12-381, we have JubJub and Bandersnatch. @@ -5,4 +24,13 @@ // The embedded curve being used is decided by the // underlying proof system. #[foreign(fixed_base_scalar_mul)] -pub fn fixed_base_embedded_curve(_low: Field, _high: Field) -> [Field; 2] {} +// docs:start:fixed_base_embedded_curve +pub fn fixed_base_embedded_curve( + low: Field, + high: Field +) -> [Field; 2] +// docs:end:fixed_base_embedded_curve +{} + +#[foreign(embedded_curve_add)] +fn embedded_curve_add(_point1: EmbeddedCurvePoint, _point2: EmbeddedCurvePoint) -> EmbeddedCurvePoint {} diff --git a/noir/noir_stdlib/src/schnorr.nr b/noir/noir_stdlib/src/schnorr.nr index 5ed95096f97..33656254550 100644 --- a/noir/noir_stdlib/src/schnorr.nr +++ b/noir/noir_stdlib/src/schnorr.nr @@ -1,7 +1,10 @@ #[foreign(schnorr_verify)] +// docs:start:schnorr_verify pub fn verify_signature( - _public_key_x: Field, - _public_key_y: Field, - _signature: [u8; 64], - _message: [u8; N] -) -> bool {} + public_key_x: Field, + public_key_y: Field, + signature: [u8; 64], + message: [u8; N] +) -> bool +// docs:end:schnorr_verify +{} \ No newline at end of file diff --git a/noir/noir_stdlib/src/slice.nr b/noir/noir_stdlib/src/slice.nr index a5a9a38ed53..aa4b73edc1a 100644 --- a/noir/noir_stdlib/src/slice.nr +++ b/noir/noir_stdlib/src/slice.nr @@ -3,34 +3,34 @@ impl [T] { /// new slice with a length one greater than the /// original unmodified slice. #[builtin(slice_push_back)] - pub fn push_back(_self: Self, _elem: T) -> Self { } + pub fn push_back(self, elem: T) -> Self { } /// Push a new element to the front of the slice, returning a /// new slice with a length one greater than the /// original unmodified slice. #[builtin(slice_push_front)] - pub fn push_front(_self: Self, _elem: T) -> Self { } + pub fn push_front(self, elem: T) -> Self { } /// Remove the last element of the slice, returning the /// popped slice and the element in a tuple #[builtin(slice_pop_back)] - pub fn pop_back(_self: Self) -> (Self, T) { } + pub fn pop_back(self) -> (Self, T) { } /// Remove the first element of the slice, returning the /// element and the popped slice in a tuple #[builtin(slice_pop_front)] - pub fn pop_front(_self: Self) -> (T, Self) { } + pub fn pop_front(self) -> (T, Self) { } /// Insert an element at a specified index, shifting all elements /// after it to the right #[builtin(slice_insert)] - pub fn insert(_self: Self, _index: Field, _elem: T) -> Self { } + pub fn insert(self, index: Field, elem: T) -> Self { } /// Remove an element at a specified index, shifting all elements /// after it to the left, returning the altered slice and /// the removed element #[builtin(slice_remove)] - pub fn remove(_self: Self, _index: Field) -> (Self, T) { } + pub fn remove(self, index: Field) -> (Self, T) { } // Append each element of the `other` slice to the end of `self`. // This returns a new slice and leaves both input slices unchanged. diff --git a/noir/noir_stdlib/src/string.nr b/noir/noir_stdlib/src/string.nr index e402abf9ab6..ad6fd19e2de 100644 --- a/noir/noir_stdlib/src/string.nr +++ b/noir/noir_stdlib/src/string.nr @@ -2,7 +2,7 @@ use crate::collections::vec::Vec; impl str { /// Converts the given string into a byte array #[builtin(str_as_bytes)] - pub fn as_bytes(_self: Self) -> [u8; N] { } + pub fn as_bytes(self) -> [u8; N] { } /// return a byte vector of the str content pub fn as_bytes_vec(self: Self) -> Vec { diff --git a/noir/noir_stdlib/src/test.nr b/noir/noir_stdlib/src/test.nr index 47b31f4acea..560cfde741c 100644 --- a/noir/noir_stdlib/src/test.nr +++ b/noir/noir_stdlib/src/test.nr @@ -1,17 +1,17 @@ #[oracle(create_mock)] -unconstrained fn create_mock_oracle(_name: str) -> Field {} +unconstrained fn create_mock_oracle(name: str) -> Field {} #[oracle(set_mock_params)] -unconstrained fn set_mock_params_oracle

(_id: Field, _params: P) {} +unconstrained fn set_mock_params_oracle

(id: Field, params: P) {} #[oracle(set_mock_returns)] -unconstrained fn set_mock_returns_oracle(_id: Field, _returns: R) {} +unconstrained fn set_mock_returns_oracle(id: Field, returns: R) {} #[oracle(set_mock_times)] -unconstrained fn set_mock_times_oracle(_id: Field, _times: u64) {} +unconstrained fn set_mock_times_oracle(id: Field, times: u64) {} #[oracle(clear_mock)] -unconstrained fn clear_mock_oracle(_id: Field) {} +unconstrained fn clear_mock_oracle(id: Field) {} struct OracleMock { id: Field, diff --git a/noir/scripts/bootstrap_native.sh b/noir/scripts/bootstrap_native.sh index 3e0e2ed853a..974f0edcfec 100755 --- a/noir/scripts/bootstrap_native.sh +++ b/noir/scripts/bootstrap_native.sh @@ -12,6 +12,12 @@ else export GIT_COMMIT=$(git rev-parse --verify HEAD) fi +# Check if the 'cargo' command is available in the system +if ! command -v cargo > /dev/null; then + echo "Cargo is not installed. Please install Cargo and the Rust toolchain." + exit 1 +fi + # Build native. if [ -n "${DEBUG:-}" ]; then cargo build diff --git a/noir/scripts/install_wasm-bindgen.sh b/noir/scripts/install_wasm-bindgen.sh index c6e85bac50b..f34ed4c0ad0 100755 --- a/noir/scripts/install_wasm-bindgen.sh +++ b/noir/scripts/install_wasm-bindgen.sh @@ -3,8 +3,12 @@ set -eu cd $(dirname "$0")/.. +# Install binstall +curl -L --proto '=https' --tlsv1.2 -sSf https://raw.githubusercontent.com/cargo-bins/cargo-binstall/main/install-from-binstall-release.sh | bash + # Install wasm-bindgen-cli. if [ "$(wasm-bindgen --version | cut -d' ' -f2)" != "0.2.86" ]; then echo "Building wasm-bindgen..." - RUSTFLAGS="-Ctarget-feature=-crt-static" cargo install -f wasm-bindgen-cli --version 0.2.86 + cargo binstall wasm-bindgen-cli@0.2.86 --force --no-confirm fi + diff --git a/noir/test_programs/compile_failure/builtin_function_declaration/Nargo.toml b/noir/test_programs/compile_failure/builtin_function_declaration/Nargo.toml new file mode 100644 index 00000000000..3835292a6ba --- /dev/null +++ b/noir/test_programs/compile_failure/builtin_function_declaration/Nargo.toml @@ -0,0 +1,7 @@ +[package] +name = "builtin_function_declaration" +type = "bin" +authors = [""] +compiler_version = ">=0.23.0" + +[dependencies] diff --git a/noir/test_programs/compile_failure/builtin_function_declaration/src/main.nr b/noir/test_programs/compile_failure/builtin_function_declaration/src/main.nr new file mode 100644 index 00000000000..ed376557371 --- /dev/null +++ b/noir/test_programs/compile_failure/builtin_function_declaration/src/main.nr @@ -0,0 +1,10 @@ +// This test prevents users from trying to create their own builtin functions as these should only exist in the stdlib. + +// This would otherwise be a perfectly valid declaration of the `to_le_bits` builtin function +#[builtin(to_le_bits)] +fn to_le_bits(_x: Field, _bit_size: u32) -> [u1] {} + +fn main(x: Field) -> pub u1 { + let bits = to_le_bits(x, 100); + bits[0] +} diff --git a/noir/test_programs/compile_failure/foreign_function_declaration/Nargo.toml b/noir/test_programs/compile_failure/foreign_function_declaration/Nargo.toml new file mode 100644 index 00000000000..951658d7fb8 --- /dev/null +++ b/noir/test_programs/compile_failure/foreign_function_declaration/Nargo.toml @@ -0,0 +1,7 @@ +[package] +name = "foreign_function_declaration" +type = "bin" +authors = [""] +compiler_version = ">=0.23.0" + +[dependencies] diff --git a/noir/test_programs/compile_failure/foreign_function_declaration/src/main.nr b/noir/test_programs/compile_failure/foreign_function_declaration/src/main.nr new file mode 100644 index 00000000000..6273067f6a7 --- /dev/null +++ b/noir/test_programs/compile_failure/foreign_function_declaration/src/main.nr @@ -0,0 +1,10 @@ +// This test prevents users from trying to create their own blackbox functions as these should only exist in the stdlib. + +// This would otherwise be a perfectly valid definition of the `pedersen_hash` black box function, +// however executing the circuit results in an unhelpful ICE. +#[foreign(pedersen_hash)] +fn my_pedersen_hash(_input: [Field; N]) -> Field {} + +fn main() -> pub Field { + my_pedersen_hash([1]) +} diff --git a/noir/test_programs/compile_failure/multiple_contracts/Nargo.toml b/noir/test_programs/compile_failure/multiple_contracts/Nargo.toml deleted file mode 100644 index d6e4e632f95..00000000000 --- a/noir/test_programs/compile_failure/multiple_contracts/Nargo.toml +++ /dev/null @@ -1,5 +0,0 @@ -[package] -name = "multiple_contracts" -type = "contract" -authors = [""] -[dependencies] diff --git a/noir/test_programs/compile_failure/multiple_contracts/src/main.nr b/noir/test_programs/compile_failure/multiple_contracts/src/main.nr deleted file mode 100644 index a6c49d75378..00000000000 --- a/noir/test_programs/compile_failure/multiple_contracts/src/main.nr +++ /dev/null @@ -1,3 +0,0 @@ -contract Foo {} - -contract Bar {} diff --git a/noir/test_programs/compile_success_empty/intrinsic_die/src/main.nr b/noir/test_programs/compile_success_empty/intrinsic_die/src/main.nr index 88f7a3634c1..8cac707dfea 100644 --- a/noir/test_programs/compile_success_empty/intrinsic_die/src/main.nr +++ b/noir/test_programs/compile_success_empty/intrinsic_die/src/main.nr @@ -1,8 +1,6 @@ use dep::std; // This test checks that we perform dead-instruction-elimination on intrinsic functions. fn main(x: Field) { - let bytes = x.to_be_bytes(32); - let hash = std::hash::pedersen_commitment([x]); let _p1 = std::scalar_mul::fixed_base_embedded_curve(x, 0); } diff --git a/noir/test_programs/compile_success_empty/trait_generics/src/main.nr b/noir/test_programs/compile_success_empty/trait_generics/src/main.nr index 9a3c54c3fa1..30b2e79d579 100644 --- a/noir/test_programs/compile_success_empty/trait_generics/src/main.nr +++ b/noir/test_programs/compile_success_empty/trait_generics/src/main.nr @@ -1,4 +1,3 @@ - fn main() { let xs: [Field; 1] = [3]; let ys: [u32; 1] = [3]; @@ -8,21 +7,21 @@ fn main() { assert_eq(15, sum_static(Data { a: 5, b: 10 })); } -fn foo(x: T, u: U) where T: Into, U: Eq { +fn foo(x: T, u: U) where T: MyInto, U: Eq { assert(x.into() == u); } -trait Into { +trait MyInto { fn into(self) -> T; } -impl Into<[U; N]> for [T; N] where T: Into { +impl MyInto<[U; N]> for [T; N] where T: MyInto { fn into(self) -> [U; N] { self.map(|x: T| x.into()) } } -impl Into for Field { +impl MyInto for Field { fn into(self) -> u32 { self as u32 } diff --git a/noir/test_programs/execution_success/brillig_nested_arrays/src/main.nr b/noir/test_programs/execution_success/brillig_nested_arrays/src/main.nr index d0a60ac0a58..5a5657246a8 100644 --- a/noir/test_programs/execution_success/brillig_nested_arrays/src/main.nr +++ b/noir/test_programs/execution_success/brillig_nested_arrays/src/main.nr @@ -12,11 +12,19 @@ unconstrained fn access_nested(notes: [MyNote; 2], x: Field, y: Field) -> Field notes[x].array[y] + notes[y].array[x] + notes[x].plain + notes[y].header.params[x] } -unconstrained fn create_inside_brillig(x: Field, y: Field) { +unconstrained fn create_inside_brillig() -> [MyNote; 2] { let header = Header { params: [1, 2, 3] }; let note0 = MyNote { array: [1, 2], plain: 3, header }; let note1 = MyNote { array: [4, 5], plain: 6, header }; - assert(access_nested([note0, note1], x, y) == (2 + 4 + 3 + 1)); + [note0, note1] +} + +unconstrained fn assert_inside_brillig(notes: [MyNote; 2], x: Field, y: Field) { + assert(access_nested(notes, x, y) == (2 + 4 + 3 + 1)); +} + +unconstrained fn create_and_assert_inside_brillig(x: Field, y: Field) { + assert_inside_brillig(create_inside_brillig(), x, y); } fn main(x: Field, y: Field) { @@ -24,7 +32,10 @@ fn main(x: Field, y: Field) { let note0 = MyNote { array: [1, 2], plain: 3, header }; let note1 = MyNote { array: [4, 5], plain: 6, header }; - create_inside_brillig(x, y); assert(access_nested([note0, note1], x, y) == (2 + 4 + 3 + 1)); + + let notes = create_inside_brillig(); + assert_inside_brillig(notes, x, y); + create_and_assert_inside_brillig(x, y); } diff --git a/noir/test_programs/execution_success/databus/src/main.nr b/noir/test_programs/execution_success/databus/src/main.nr index 631331ef2d7..61a9637f5fe 100644 --- a/noir/test_programs/execution_success/databus/src/main.nr +++ b/noir/test_programs/execution_success/databus/src/main.nr @@ -1,10 +1,12 @@ -// Test unsafe integer multiplication with overflow: 12^8 = 429 981 696 -// The circuit should handle properly the growth of the bit size use dep::std; -fn main(mut x: u32, y: call_data u32, z: call_data [u32;4] ) -> return_data u32 { - -let a = z[x]; - a+y +fn main(mut x: u32, y: call_data u32, z: call_data [u32;4]) -> return_data u32 { + let a = z[x]; + a+foo(y) +} +// Use an unconstrained function to force the compiler to avoid inlining +unconstrained fn foo(x: u32) -> u32 { + x+1 } + diff --git a/noir/test_programs/execution_success/keccak256/src/main.nr b/noir/test_programs/execution_success/keccak256/src/main.nr index ff2167694d6..eb401fe614c 100644 --- a/noir/test_programs/execution_success/keccak256/src/main.nr +++ b/noir/test_programs/execution_success/keccak256/src/main.nr @@ -1,5 +1,4 @@ -// Keccak256 example -// +// docs:start:keccak256 use dep::std; fn main(x: Field, result: [u8; 32]) { @@ -7,7 +6,8 @@ fn main(x: Field, result: [u8; 32]) { // The padding is taken care of by the program let digest = std::hash::keccak256([x as u8], 1); assert(digest == result); - //#1399: variable meesage size + + //#1399: variable message size let message_size = 4; let hash_a = std::hash::keccak256([1, 2, 3, 4], message_size); let hash_b = std::hash::keccak256([1, 2, 3, 4, 0, 0, 0, 0], message_size); @@ -19,3 +19,4 @@ fn main(x: Field, result: [u8; 32]) { assert(hash_a != hash_c); } +// docs:end:keccak256 diff --git a/noir/test_programs/execution_success/pedersen_commitment/Nargo.toml b/noir/test_programs/execution_success/pedersen_commitment/Nargo.toml new file mode 100644 index 00000000000..e257ce252d8 --- /dev/null +++ b/noir/test_programs/execution_success/pedersen_commitment/Nargo.toml @@ -0,0 +1,6 @@ +[package] +name = "pedersen_commitment" +type = "bin" +authors = [""] + +[dependencies] diff --git a/noir/test_programs/execution_success/pedersen_commitment/Prover.toml b/noir/test_programs/execution_success/pedersen_commitment/Prover.toml new file mode 100644 index 00000000000..6ad8c6bca57 --- /dev/null +++ b/noir/test_programs/execution_success/pedersen_commitment/Prover.toml @@ -0,0 +1,6 @@ +x = "0" +y = "1" + +[expected_commitment] +x = "0x054aa86a73cb8a34525e5bbed6e43ba1198e860f5f3950268f71df4591bde402" +y = "0x209dcfbf2cfb57f9f6046f44d71ac6faf87254afc7407c04eb621a6287cac126" diff --git a/noir/test_programs/execution_success/pedersen_commitment/src/main.nr b/noir/test_programs/execution_success/pedersen_commitment/src/main.nr new file mode 100644 index 00000000000..83cbe20851d --- /dev/null +++ b/noir/test_programs/execution_success/pedersen_commitment/src/main.nr @@ -0,0 +1,10 @@ +// docs:start:pedersen-commitment +use dep::std; + +fn main(x: Field, y: Field, expected_commitment: std::hash::PedersenPoint) { + let commitment = std::hash::pedersen_commitment([x, y]); + assert_eq(commitment.x, expected_commitment.x); + assert_eq(commitment.y, expected_commitment.y); +} +// docs:end:pedersen-commitment + diff --git a/noir/test_programs/execution_success/pedersen_hash/Nargo.toml b/noir/test_programs/execution_success/pedersen_hash/Nargo.toml new file mode 100644 index 00000000000..6248a96b3c9 --- /dev/null +++ b/noir/test_programs/execution_success/pedersen_hash/Nargo.toml @@ -0,0 +1,6 @@ +[package] +name = "pedersen_hash" +type = "bin" +authors = [""] + +[dependencies] diff --git a/noir/test_programs/execution_success/pedersen_hash/Prover.toml b/noir/test_programs/execution_success/pedersen_hash/Prover.toml new file mode 100644 index 00000000000..931b121fa6a --- /dev/null +++ b/noir/test_programs/execution_success/pedersen_hash/Prover.toml @@ -0,0 +1,4 @@ +x = "0" +y = "1" + +expected_hash = "0x0d98561fb02ca04d00801dfdc118b2a24cea0351963587712a28d368041370e1" diff --git a/noir/test_programs/execution_success/pedersen_hash/src/main.nr b/noir/test_programs/execution_success/pedersen_hash/src/main.nr new file mode 100644 index 00000000000..20c7de12d6c --- /dev/null +++ b/noir/test_programs/execution_success/pedersen_hash/src/main.nr @@ -0,0 +1,9 @@ +// docs:start:pedersen-hash +use dep::std; + +fn main(x: Field, y: Field, expected_hash: Field) { + let hash = std::hash::pedersen_hash([x, y]); + assert_eq(hash, expected_hash); +} +// docs:end:pedersen-hash + diff --git a/noir/test_programs/execution_success/poseidon_bn254_hash/src/main.nr b/noir/test_programs/execution_success/poseidon_bn254_hash/src/main.nr index 3d30ebad279..e742a440d1c 100644 --- a/noir/test_programs/execution_success/poseidon_bn254_hash/src/main.nr +++ b/noir/test_programs/execution_success/poseidon_bn254_hash/src/main.nr @@ -1,3 +1,4 @@ +// docs:start:poseidon use dep::std::hash::poseidon; fn main(x1: [Field; 2], y1: pub Field, x2: [Field; 4], y2: pub Field) { @@ -7,3 +8,4 @@ fn main(x1: [Field; 2], y1: pub Field, x2: [Field; 4], y2: pub Field) { let hash2 = poseidon::bn254::hash_4(x2); assert(hash2 == y2); } +// docs:end:poseidon diff --git a/noir/test_programs/execution_success/regression_4088/Nargo.toml b/noir/test_programs/execution_success/regression_4088/Nargo.toml new file mode 100644 index 00000000000..a5e7832b734 --- /dev/null +++ b/noir/test_programs/execution_success/regression_4088/Nargo.toml @@ -0,0 +1,5 @@ +[package] +name = "regression_4088" +type = "bin" +authors = [""] +[dependencies] diff --git a/noir/test_programs/execution_success/regression_4088/Prover.toml b/noir/test_programs/execution_success/regression_4088/Prover.toml new file mode 100644 index 00000000000..839e31e7e40 --- /dev/null +++ b/noir/test_programs/execution_success/regression_4088/Prover.toml @@ -0,0 +1,2 @@ +[note] +value = 0 diff --git a/noir/test_programs/execution_success/regression_4088/src/main.nr b/noir/test_programs/execution_success/regression_4088/src/main.nr new file mode 100644 index 00000000000..9e4d7892fc3 --- /dev/null +++ b/noir/test_programs/execution_success/regression_4088/src/main.nr @@ -0,0 +1,27 @@ +trait Serialize { + fn serialize(self) -> [Field; N]; +} + +struct ValueNote { + value: Field, +} + +impl Serialize<1> for ValueNote { + fn serialize(self) -> [Field; 1] { + [self.value] + } +} + +fn check(serialized_note: [Field; N]) { + assert(serialized_note[0] == 0); +} + +fn oopsie(note: Note) where Note: Serialize { + let serialized_note = Note::serialize(note); + + check(serialized_note) +} + +fn main(mut note: ValueNote) { + oopsie(note); +} diff --git a/noir/test_programs/execution_success/regression_4124/Nargo.toml b/noir/test_programs/execution_success/regression_4124/Nargo.toml new file mode 100644 index 00000000000..9b97d1ce087 --- /dev/null +++ b/noir/test_programs/execution_success/regression_4124/Nargo.toml @@ -0,0 +1,7 @@ +[package] +name = "regression_4124" +type = "bin" +authors = [""] +compiler_version = ">=0.22.0" + +[dependencies] diff --git a/noir/test_programs/execution_success/regression_4124/Prover.toml b/noir/test_programs/execution_success/regression_4124/Prover.toml new file mode 100644 index 00000000000..533d1af92b9 --- /dev/null +++ b/noir/test_programs/execution_success/regression_4124/Prover.toml @@ -0,0 +1 @@ +value = 0 diff --git a/noir/test_programs/execution_success/regression_4124/src/main.nr b/noir/test_programs/execution_success/regression_4124/src/main.nr new file mode 100644 index 00000000000..b47bf28d461 --- /dev/null +++ b/noir/test_programs/execution_success/regression_4124/src/main.nr @@ -0,0 +1,39 @@ +use dep::std::option::Option; + +trait MyDeserialize { + fn deserialize(fields: [Field; N]) -> Self; +} + +impl MyDeserialize<1> for Field { + fn deserialize(fields: [Field; 1]) -> Self { + fields[0] + } +} + +pub fn storage_read() -> [Field; N] { + dep::std::unsafe::zeroed() +} + +struct PublicState { + storage_slot: Field, +} + +impl PublicState { + pub fn new(storage_slot: Field) -> Self { + assert(storage_slot != 0, "Storage slot 0 not allowed. Storage slots must start from 1."); + PublicState { storage_slot } + } + + pub fn read(_self: Self) -> T where T: MyDeserialize { + // storage_read returns slice here + let fields: [Field; T_SERIALIZED_LEN] = storage_read(); + T::deserialize(fields) + } +} + +fn main(value: Field) { + let ps: PublicState = PublicState::new(27); + + // error here + assert(ps.read() == value); +} diff --git a/noir/test_programs/execution_success/scalar_mul/src/main.nr b/noir/test_programs/execution_success/scalar_mul/src/main.nr index 2ddf22cf71e..e20f47907db 100644 --- a/noir/test_programs/execution_success/scalar_mul/src/main.nr +++ b/noir/test_programs/execution_success/scalar_mul/src/main.nr @@ -20,4 +20,12 @@ fn main( let res = std::scalar_mul::fixed_base_embedded_curve(priv_key, 0); assert(res[0] == pub_x); assert(res[1] == pub_y); + let pub_point= std::scalar_mul::EmbeddedCurvePoint { x: pub_x, y: pub_y }; + let g1_y = 17631683881184975370165255887551781615748388533673675138860; + let g1= std::scalar_mul::EmbeddedCurvePoint { x: 1, y: g1_y }; + + let res = pub_point.double(); + let double = g1.add(g1); + + assert(double.x == res.x); } diff --git a/noir/test_programs/execution_success/to_le_bytes/Prover.toml b/noir/test_programs/execution_success/to_le_bytes/Prover.toml index 07fe857ac7c..bf58776d557 100644 --- a/noir/test_programs/execution_success/to_le_bytes/Prover.toml +++ b/noir/test_programs/execution_success/to_le_bytes/Prover.toml @@ -1 +1,2 @@ x = "2040124" +cond = false \ No newline at end of file diff --git a/noir/test_programs/execution_success/to_le_bytes/src/main.nr b/noir/test_programs/execution_success/to_le_bytes/src/main.nr index 05eefc0f143..a0b48efe528 100644 --- a/noir/test_programs/execution_success/to_le_bytes/src/main.nr +++ b/noir/test_programs/execution_success/to_le_bytes/src/main.nr @@ -1,4 +1,4 @@ -fn main(x: Field) -> pub [u8; 31] { +fn main(x: Field, cond: bool) -> pub [u8; 31] { // The result of this byte array will be little-endian let byte_array = x.to_le_bytes(31); assert(byte_array.len() == 31); @@ -7,5 +7,12 @@ fn main(x: Field) -> pub [u8; 31] { for i in 0..31 { bytes[i] = byte_array[i]; } + + if cond { + // We've set x = "2040124" so we shouldn't be able to represent this as a single byte. + let bad_byte_array = x.to_le_bytes(1); + assert_eq(bad_byte_array.len(), 1); + } + bytes } diff --git a/noir/test_programs/noir_test_success/bounded_vec/Nargo.toml b/noir/test_programs/noir_test_success/bounded_vec/Nargo.toml new file mode 100644 index 00000000000..0d58f5872ef --- /dev/null +++ b/noir/test_programs/noir_test_success/bounded_vec/Nargo.toml @@ -0,0 +1,7 @@ +[package] +name = "bounded_vec" +type = "bin" +authors = [""] +compiler_version = ">=0.23.0" + +[dependencies] \ No newline at end of file diff --git a/yarn-project/acir-simulator/src/avm/opcodes/call.ts b/noir/test_programs/noir_test_success/bounded_vec/Prover.toml similarity index 100% rename from yarn-project/acir-simulator/src/avm/opcodes/call.ts rename to noir/test_programs/noir_test_success/bounded_vec/Prover.toml diff --git a/noir/test_programs/noir_test_success/bounded_vec/src/main.nr b/noir/test_programs/noir_test_success/bounded_vec/src/main.nr new file mode 100644 index 00000000000..d51d2cc3685 --- /dev/null +++ b/noir/test_programs/noir_test_success/bounded_vec/src/main.nr @@ -0,0 +1,105 @@ +#[test] +fn test_vec_push_pop() { + let mut vec: BoundedVec = BoundedVec::new(0); + assert(vec.len == 0); + vec.push(2); + assert(vec.len == 1); + vec.push(4); + assert(vec.len == 2); + vec.push(6); + assert(vec.len == 3); + let x = vec.pop(); + assert(x == 6); + assert(vec.len == 2); + assert(vec.get(0) == 2); + assert(vec.get(1) == 4); +} + +#[test] +fn test_vec_extend_from_array() { + let mut vec: BoundedVec = BoundedVec::new(0); + vec.extend_from_array([2, 4]); + assert(vec.len == 2); + assert(vec.get(0) == 2); + assert(vec.get(1) == 4); +} + +#[test(should_fail_with="extend_from_array out of bounds")] +fn test_vec_extend_from_array_out_of_bound() { + let mut vec: BoundedVec = BoundedVec::new(0); + vec.extend_from_array([2, 4, 6]); +} + +#[test(should_fail_with="extend_from_array out of bounds")] +fn test_vec_extend_from_array_twice_out_of_bound() { + let mut vec: BoundedVec = BoundedVec::new(0); + vec.extend_from_array([2]); + assert(vec.len == 1); + vec.extend_from_array([4, 6]); +} + +#[test(should_fail)] +fn test_vec_get_out_of_bound() { + let mut vec: BoundedVec = BoundedVec::new(0); + vec.extend_from_array([2, 4]); + let _x = vec.get(2); +} + +#[test(should_fail)] +fn test_vec_get_not_declared() { + let mut vec: BoundedVec = BoundedVec::new(0); + vec.extend_from_array([2]); + let _x = vec.get(1); +} + +#[test(should_fail)] +fn test_vec_get_uninitialized() { + let mut vec: BoundedVec = BoundedVec::new(0); + let _x = vec.get(0); +} + +#[test(should_fail_with="push out of bounds")] +fn test_vec_push_out_of_bound() { + let mut vec: BoundedVec = BoundedVec::new(0); + vec.push(1); + vec.push(2); +} + +#[test(should_fail_with="extend_from_bounded_vec out of bounds")] +fn test_vec_extend_from_bounded_vec_out_of_bound() { + let mut vec: BoundedVec = BoundedVec::new(0); + + let mut another_vec: BoundedVec = BoundedVec::new(0); + another_vec.extend_from_array([1, 2, 3]); + + vec.extend_from_bounded_vec(another_vec); +} + +#[test(should_fail_with="extend_from_bounded_vec out of bounds")] +fn test_vec_extend_from_bounded_vec_twice_out_of_bound() { + let mut vec: BoundedVec = BoundedVec::new(0); + vec.extend_from_array([1, 2]); + + let mut another_vec: BoundedVec = BoundedVec::new(0); + another_vec.push(3); + + vec.extend_from_bounded_vec(another_vec); +} + +#[test] +fn test_vec_any() { + let mut vec: BoundedVec = BoundedVec::new(0); + vec.extend_from_array([2, 4, 6]); + assert(vec.any(|v| v == 2) == true); + assert(vec.any(|v| v == 4) == true); + assert(vec.any(|v| v == 6) == true); + assert(vec.any(|v| v == 3) == false); +} + +#[test] +fn test_vec_any_not_default() { + let default_value = 1; + let mut vec: BoundedVec = BoundedVec::new(default_value); + vec.extend_from_array([2, 4]); + assert(vec.any(|v| v == default_value) == false); +} \ No newline at end of file diff --git a/noir/test_programs/noir_test_success/out_of_bounds_alignment/Nargo.toml b/noir/test_programs/noir_test_success/out_of_bounds_alignment/Nargo.toml new file mode 100644 index 00000000000..e535c113f20 --- /dev/null +++ b/noir/test_programs/noir_test_success/out_of_bounds_alignment/Nargo.toml @@ -0,0 +1,5 @@ +[package] +name = "out_of_bounds_alignment" +type = "bin" +authors = [""] +[dependencies] diff --git a/noir/test_programs/noir_test_success/out_of_bounds_alignment/Prover.toml b/noir/test_programs/noir_test_success/out_of_bounds_alignment/Prover.toml new file mode 100644 index 00000000000..e69de29bb2d diff --git a/noir/test_programs/noir_test_success/out_of_bounds_alignment/src/main.nr b/noir/test_programs/noir_test_success/out_of_bounds_alignment/src/main.nr new file mode 100644 index 00000000000..a47ab37eb31 --- /dev/null +++ b/noir/test_programs/noir_test_success/out_of_bounds_alignment/src/main.nr @@ -0,0 +1,17 @@ +fn out_of_bounds(arr_1: [Field; 50]) -> Field { + arr_1[50 + 1] +} + +unconstrained fn out_of_bounds_unconstrained_wrapper(arr_1: [Field; 50], arr_2: [Field; 50]) -> Field { + out_of_bounds(arr_1) +} + +#[test(should_fail)] +fn test_acir() { + assert_eq(out_of_bounds([0; 50]), 0); +} + +#[test(should_fail)] +fn test_brillig() { + assert_eq(out_of_bounds_unconstrained_wrapper([0; 50], [0; 50]), 0); +} diff --git a/noir/test_programs/noir_test_success/regression_4080/Nargo.toml b/noir/test_programs/noir_test_success/regression_4080/Nargo.toml new file mode 100644 index 00000000000..a38baf389d6 --- /dev/null +++ b/noir/test_programs/noir_test_success/regression_4080/Nargo.toml @@ -0,0 +1,5 @@ +[package] +name = "regression_4080" +type = "bin" +authors = [""] +[dependencies] diff --git a/noir/test_programs/noir_test_success/regression_4080/Prover.toml b/noir/test_programs/noir_test_success/regression_4080/Prover.toml new file mode 100644 index 00000000000..0e5dfd5638d --- /dev/null +++ b/noir/test_programs/noir_test_success/regression_4080/Prover.toml @@ -0,0 +1 @@ +x = "5" diff --git a/noir/test_programs/noir_test_success/regression_4080/src/main.nr b/noir/test_programs/noir_test_success/regression_4080/src/main.nr new file mode 100644 index 00000000000..781d3e33ea3 --- /dev/null +++ b/noir/test_programs/noir_test_success/regression_4080/src/main.nr @@ -0,0 +1,8 @@ +// This test checks that `var^var` is assigned the correct type. +// https://github.com/noir-lang/noir/issues/4080 + +#[test(should_fail_with = "attempt to add with overflow")] +fn main() { + let var1: u8 = ((255 + 1) ^ (255 + 1)) - ((255 + 1) - (255 + 1)); + assert_eq(var1, 0); +} diff --git a/noir/tooling/backend_interface/src/cli/mod.rs b/noir/tooling/backend_interface/src/cli/mod.rs index 3ea65f28103..b4dec859839 100644 --- a/noir/tooling/backend_interface/src/cli/mod.rs +++ b/noir/tooling/backend_interface/src/cli/mod.rs @@ -1,4 +1,4 @@ -// Reference: https://github.com/AztecProtocol/aztec-packages/blob/master/circuits/cpp/barretenberg/cpp/src/barretenberg/bb/main.cpp +// Reference: https://github.com/AztecProtocol/aztec-packages/blob/master/barretenberg/cpp/src/barretenberg/bb/main.cpp mod contract; mod gates; diff --git a/noir/tooling/bb_abstraction_leaks/build.rs b/noir/tooling/bb_abstraction_leaks/build.rs index 18413f87793..6197f52cb4b 100644 --- a/noir/tooling/bb_abstraction_leaks/build.rs +++ b/noir/tooling/bb_abstraction_leaks/build.rs @@ -10,7 +10,7 @@ use const_format::formatcp; const USERNAME: &str = "AztecProtocol"; const REPO: &str = "aztec-packages"; -const VERSION: &str = "0.19.0"; +const VERSION: &str = "0.21.0"; const TAG: &str = formatcp!("aztec-packages-v{}", VERSION); const API_URL: &str = diff --git a/noir/tooling/debugger/src/context.rs b/noir/tooling/debugger/src/context.rs index 12b55708b15..8e5c1dacf2c 100644 --- a/noir/tooling/debugger/src/context.rs +++ b/noir/tooling/debugger/src/context.rs @@ -1,6 +1,6 @@ use acvm::acir::circuit::{Circuit, Opcode, OpcodeLocation}; use acvm::acir::native_types::{Witness, WitnessMap}; -use acvm::brillig_vm::{brillig::Value, Registers}; +use acvm::brillig_vm::brillig::Value; use acvm::pwg::{ ACVMStatus, BrilligSolver, BrilligSolverStatus, ForeignCallWaitInfo, StepResult, ACVM, }; @@ -352,16 +352,6 @@ impl<'a, B: BlackBoxFunctionSolver> DebugContext<'a, B> { acir_index < opcodes.len() && matches!(opcodes[acir_index], Opcode::Brillig(..)) } - pub(super) fn get_brillig_registers(&self) -> Option<&Registers> { - self.brillig_solver.as_ref().map(|solver| solver.get_registers()) - } - - pub(super) fn set_brillig_register(&mut self, register_index: usize, value: FieldElement) { - if let Some(solver) = self.brillig_solver.as_mut() { - solver.set_register(register_index, value.into()); - } - } - pub(super) fn get_brillig_memory(&self) -> Option<&[Value]> { self.brillig_solver.as_ref().map(|solver| solver.get_memory()) } @@ -442,7 +432,7 @@ mod tests { }, blackbox_solver::StubbedBlackBoxSolver, brillig_vm::brillig::{ - BinaryFieldOp, Opcode as BrilligOpcode, RegisterIndex, RegisterOrMemory, + BinaryFieldOp, MemoryAddress, Opcode as BrilligOpcode, ValueOrArray, }, }; use nargo::{artifacts::debug::DebugArtifact, ops::DefaultForeignCallExecutor}; @@ -461,16 +451,21 @@ mod tests { })], outputs: vec![], bytecode: vec![ + BrilligOpcode::CalldataCopy { + destination_address: MemoryAddress(0), + size: 1, + offset: 0, + }, BrilligOpcode::Const { - destination: RegisterIndex::from(1), + destination: MemoryAddress::from(1), value: Value::from(fe_0), }, BrilligOpcode::ForeignCall { function: "clear_mock".into(), destinations: vec![], - inputs: vec![RegisterOrMemory::RegisterIndex(RegisterIndex::from(0))], + inputs: vec![ValueOrArray::MemoryAddress(MemoryAddress::from(0))], }, - BrilligOpcode::Stop, + BrilligOpcode::Stop { return_data_offset: 0, return_data_size: 0 }, ], predicate: None, }; @@ -495,7 +490,7 @@ mod tests { assert_eq!(context.get_current_opcode_location(), Some(OpcodeLocation::Acir(0))); - // execute the first Brillig opcode (const) + // Execute the first Brillig opcode (calldata copy) let result = context.step_into_opcode(); assert!(matches!(result, DebugCommandResult::Ok)); assert_eq!( @@ -503,15 +498,15 @@ mod tests { Some(OpcodeLocation::Brillig { acir_index: 0, brillig_index: 1 }) ); - // try to execute the second Brillig opcode (and resolve the foreign call) + // execute the second Brillig opcode (const) let result = context.step_into_opcode(); assert!(matches!(result, DebugCommandResult::Ok)); assert_eq!( context.get_current_opcode_location(), - Some(OpcodeLocation::Brillig { acir_index: 0, brillig_index: 1 }) + Some(OpcodeLocation::Brillig { acir_index: 0, brillig_index: 2 }) ); - // retry the second Brillig opcode (foreign call should be finished) + // try to execute the third Brillig opcode (and resolve the foreign call) let result = context.step_into_opcode(); assert!(matches!(result, DebugCommandResult::Ok)); assert_eq!( @@ -519,6 +514,14 @@ mod tests { Some(OpcodeLocation::Brillig { acir_index: 0, brillig_index: 2 }) ); + // retry the third Brillig opcode (foreign call should be finished) + let result = context.step_into_opcode(); + assert!(matches!(result, DebugCommandResult::Ok)); + assert_eq!( + context.get_current_opcode_location(), + Some(OpcodeLocation::Brillig { acir_index: 0, brillig_index: 3 }) + ); + // last Brillig opcode let result = context.step_into_opcode(); assert!(matches!(result, DebugCommandResult::Done)); @@ -547,13 +550,18 @@ mod tests { ], outputs: vec![BrilligOutputs::Simple(w_z)], bytecode: vec![ + BrilligOpcode::CalldataCopy { + destination_address: MemoryAddress(0), + size: 2, + offset: 0, + }, BrilligOpcode::BinaryFieldOp { - destination: RegisterIndex::from(0), + destination: MemoryAddress::from(0), op: BinaryFieldOp::Add, - lhs: RegisterIndex::from(0), - rhs: RegisterIndex::from(1), + lhs: MemoryAddress::from(0), + rhs: MemoryAddress::from(1), }, - BrilligOpcode::Stop, + BrilligOpcode::Stop { return_data_offset: 0, return_data_size: 1 }, ], predicate: None, }; @@ -611,14 +619,22 @@ mod tests { Opcode::Brillig(Brillig { inputs: vec![], outputs: vec![], - bytecode: vec![BrilligOpcode::Stop, BrilligOpcode::Stop, BrilligOpcode::Stop], + bytecode: vec![ + BrilligOpcode::Stop { return_data_offset: 0, return_data_size: 0 }, + BrilligOpcode::Stop { return_data_offset: 0, return_data_size: 0 }, + BrilligOpcode::Stop { return_data_offset: 0, return_data_size: 0 }, + ], predicate: None, }), Opcode::MemoryInit { block_id: BlockId(0), init: vec![] }, Opcode::Brillig(Brillig { inputs: vec![], outputs: vec![], - bytecode: vec![BrilligOpcode::Stop, BrilligOpcode::Stop, BrilligOpcode::Stop], + bytecode: vec![ + BrilligOpcode::Stop { return_data_offset: 0, return_data_size: 0 }, + BrilligOpcode::Stop { return_data_offset: 0, return_data_size: 0 }, + BrilligOpcode::Stop { return_data_offset: 0, return_data_size: 0 }, + ], predicate: None, }), Opcode::AssertZero(Expression::default()), diff --git a/noir/tooling/debugger/src/repl.rs b/noir/tooling/debugger/src/repl.rs index b1af2bc2686..92224ab785a 100644 --- a/noir/tooling/debugger/src/repl.rs +++ b/noir/tooling/debugger/src/repl.rs @@ -53,7 +53,15 @@ impl<'a, B: BlackBoxFunctionSolver> ReplDebugger<'a, B> { Some(location) => { match location { OpcodeLocation::Acir(ip) => { - println!("At opcode {}: {}", ip, opcodes[ip]) + // Default Brillig display is too bloated for this context, + // so we limit it to denoting it's the start of a Brillig + // block. The user can still use the `opcodes` command to + // take a look at the whole block. + let opcode_summary = match opcodes[ip] { + Opcode::Brillig(..) => "BRILLIG: ...".into(), + _ => format!("{}", opcodes[ip]), + }; + println!("At opcode {}: {}", ip, opcode_summary); } OpcodeLocation::Brillig { acir_index, brillig_index } => { let Opcode::Brillig(ref brillig) = opcodes[acir_index] else { @@ -243,37 +251,6 @@ impl<'a, B: BlackBoxFunctionSolver> ReplDebugger<'a, B> { println!("_{} = {value}", index); } - pub fn show_brillig_registers(&self) { - if !self.context.is_executing_brillig() { - println!("Not executing a Brillig block"); - return; - } - - let Some(registers) = self.context.get_brillig_registers() else { - // this can happen when just entering the Brillig block since ACVM - // would have not initialized the Brillig VM yet; in fact, the - // Brillig code may be skipped altogether - println!("Brillig VM registers not available"); - return; - }; - - for (index, value) in registers.inner.iter().enumerate() { - println!("{index} = {}", value.to_field()); - } - } - - pub fn set_brillig_register(&mut self, index: usize, value: String) { - let Some(field_value) = FieldElement::try_from_str(&value) else { - println!("Invalid value: {value}"); - return; - }; - if !self.context.is_executing_brillig() { - println!("Not executing a Brillig block"); - return; - } - self.context.set_brillig_register(index, field_value); - } - pub fn show_brillig_memory(&self) { if !self.context.is_executing_brillig() { println!("Not executing a Brillig block"); @@ -437,26 +414,6 @@ pub fn run( } }, ) - .add( - "registers", - command! { - "show Brillig registers (valid when executing a Brillig block)", - () => || { - ref_context.borrow().show_brillig_registers(); - Ok(CommandStatus::Done) - } - }, - ) - .add( - "regset", - command! { - "update a Brillig register with the given value", - (index: usize, value: String) => |index, value| { - ref_context.borrow_mut().set_brillig_register(index, value); - Ok(CommandStatus::Done) - } - }, - ) .add( "memory", command! { diff --git a/noir/tooling/lsp/src/lib.rs b/noir/tooling/lsp/src/lib.rs index b64fc474b0b..a0e024c70fd 100644 --- a/noir/tooling/lsp/src/lib.rs +++ b/noir/tooling/lsp/src/lib.rs @@ -4,11 +4,12 @@ #![cfg_attr(not(test), warn(unused_crate_dependencies, unused_extern_crates))] use std::{ - collections::HashMap, + collections::{BTreeMap, HashMap}, future::Future, ops::{self, ControlFlow}, path::{Path, PathBuf}, pin::Pin, + str::FromStr, task::{self, Poll}, }; @@ -20,7 +21,11 @@ use async_lsp::{ use fm::{codespan_files as files, FileManager}; use fxhash::FxHashSet; use lsp_types::CodeLens; -use nargo::{parse_all, workspace::Workspace}; +use nargo::{ + package::{Package, PackageType}, + parse_all, + workspace::Workspace, +}; use nargo_toml::{find_file_manifest, resolve_workspace_from_toml, PackageSelection}; use noirc_driver::{file_manager_with_stdlib, prepare_crate, NOIR_ARTIFACT_VERSION_STRING}; use noirc_frontend::{ @@ -209,23 +214,38 @@ fn byte_span_to_range<'a, F: files::Files<'a> + ?Sized>( } pub(crate) fn resolve_workspace_for_source_path(file_path: &Path) -> Result { - let package_root = find_file_manifest(file_path); - - let toml_path = package_root.ok_or_else(|| { - LspError::WorkspaceResolutionError(format!( - "Nargo.toml not found for file: {:?}", - file_path - )) - })?; - - let workspace = resolve_workspace_from_toml( - &toml_path, - PackageSelection::All, - Some(NOIR_ARTIFACT_VERSION_STRING.to_string()), - ) - .map_err(|err| LspError::WorkspaceResolutionError(err.to_string()))?; - - Ok(workspace) + if let Some(toml_path) = find_file_manifest(file_path) { + resolve_workspace_from_toml( + &toml_path, + PackageSelection::All, + Some(NOIR_ARTIFACT_VERSION_STRING.to_string()), + ) + .map_err(|err| LspError::WorkspaceResolutionError(err.to_string())) + } else { + let Some(parent_folder) = file_path.parent().and_then(|f| f.file_name()).and_then(|file_name_os_str| file_name_os_str.to_str()) else { + return Err(LspError::WorkspaceResolutionError(format!( + "Could not resolve parent folder for file: {:?}", + file_path + ))) + }; + let assumed_package = Package { + version: None, + compiler_required_version: Some(NOIR_ARTIFACT_VERSION_STRING.to_string()), + root_dir: PathBuf::from(parent_folder), + package_type: PackageType::Binary, + entry_path: PathBuf::from(file_path), + name: CrateName::from_str(parent_folder) + .map_err(|err| LspError::WorkspaceResolutionError(err.to_string()))?, + dependencies: BTreeMap::new(), + }; + let workspace = Workspace { + root_dir: PathBuf::from(parent_folder), + members: vec![assumed_package], + selected_package_index: Some(0), + is_assumed: true, + }; + Ok(workspace) + } } /// Prepares a package from a source string diff --git a/noir/tooling/lsp/src/requests/profile_run.rs b/noir/tooling/lsp/src/requests/profile_run.rs index 8ba91338f55..d866be8988b 100644 --- a/noir/tooling/lsp/src/requests/profile_run.rs +++ b/noir/tooling/lsp/src/requests/profile_run.rs @@ -64,26 +64,32 @@ fn on_profile_run_request_inner( &workspace_file_manager, &parsed_files, &workspace, - expression_width, &CompileOptions::default(), ) .map_err(|err| ResponseError::new(ErrorCode::REQUEST_FAILED, err))?; let mut opcodes_counts: HashMap = HashMap::new(); let mut file_map: BTreeMap = BTreeMap::new(); - for compiled_program in &compiled_programs { + for compiled_program in compiled_programs { + let compiled_program = + nargo::ops::transform_program(compiled_program, expression_width); + let span_opcodes = compiled_program.debug.count_span_opcodes(); let debug_artifact: DebugArtifact = compiled_program.clone().into(); opcodes_counts.extend(span_opcodes); file_map.extend(debug_artifact.file_map); } - for compiled_contract in &compiled_contracts { - let functions = &compiled_contract.functions; - let debug_artifact: DebugArtifact = compiled_contract.clone().into(); + for compiled_contract in compiled_contracts { + let compiled_contract = + nargo::ops::transform_contract(compiled_contract, expression_width); + + let function_debug_info: Vec<_> = + compiled_contract.functions.iter().map(|func| &func.debug).cloned().collect(); + let debug_artifact: DebugArtifact = compiled_contract.into(); file_map.extend(debug_artifact.file_map); - for contract_function in functions { - let span_opcodes = contract_function.debug.count_span_opcodes(); + for contract_function_debug in function_debug_info { + let span_opcodes = contract_function_debug.count_span_opcodes(); opcodes_counts.extend(span_opcodes); } } diff --git a/noir/tooling/lsp/src/solver.rs b/noir/tooling/lsp/src/solver.rs index 6217b7ad71f..f001cebaa4d 100644 --- a/noir/tooling/lsp/src/solver.rs +++ b/noir/tooling/lsp/src/solver.rs @@ -49,12 +49,4 @@ impl BlackBoxFunctionSolver for WrapperSolver { ) -> Result<(acvm::FieldElement, acvm::FieldElement), acvm::BlackBoxResolutionError> { self.0.ec_add(input1_x, input1_y, input2_x, input2_y) } - - fn ec_double( - &self, - input_x: &acvm::FieldElement, - input_y: &acvm::FieldElement, - ) -> Result<(acvm::FieldElement, acvm::FieldElement), acvm::BlackBoxResolutionError> { - self.0.ec_double(input_x, input_y) - } } diff --git a/noir/tooling/nargo/src/artifacts/debug.rs b/noir/tooling/nargo/src/artifacts/debug.rs index 3f5df801b66..2e2d98f279e 100644 --- a/noir/tooling/nargo/src/artifacts/debug.rs +++ b/noir/tooling/nargo/src/artifacts/debug.rs @@ -126,18 +126,18 @@ impl From for DebugArtifact { } } -impl From<&CompiledContract> for DebugArtifact { - fn from(compiled_artifact: &CompiledContract) -> Self { +impl From for DebugArtifact { + fn from(compiled_artifact: CompiledContract) -> Self { let all_functions_debug: Vec = compiled_artifact .functions - .iter() - .map(|contract_function| contract_function.debug.clone()) + .into_iter() + .map(|contract_function| contract_function.debug) .collect(); DebugArtifact { debug_symbols: all_functions_debug, - file_map: compiled_artifact.file_map.clone(), - warnings: compiled_artifact.warnings.clone(), + file_map: compiled_artifact.file_map, + warnings: compiled_artifact.warnings, } } } diff --git a/noir/tooling/nargo/src/ops/compile.rs b/noir/tooling/nargo/src/ops/compile.rs index 866bfe39d7b..dccd2cedbf5 100644 --- a/noir/tooling/nargo/src/ops/compile.rs +++ b/noir/tooling/nargo/src/ops/compile.rs @@ -1,4 +1,3 @@ -use acvm::ExpressionWidth; use fm::FileManager; use noirc_driver::{CompilationResult, CompileOptions, CompiledContract, CompiledProgram}; use noirc_frontend::hir::ParsedFiles; @@ -18,7 +17,6 @@ pub fn compile_workspace( file_manager: &FileManager, parsed_files: &ParsedFiles, workspace: &Workspace, - expression_width: ExpressionWidth, compile_options: &CompileOptions, ) -> Result<(Vec, Vec), CompileError> { let (binary_packages, contract_packages): (Vec<_>, Vec<_>) = workspace @@ -30,22 +28,11 @@ pub fn compile_workspace( // Compile all of the packages in parallel. let program_results: Vec> = binary_packages .par_iter() - .map(|package| { - compile_program( - file_manager, - parsed_files, - package, - compile_options, - expression_width, - None, - ) - }) + .map(|package| compile_program(file_manager, parsed_files, package, compile_options, None)) .collect(); let contract_results: Vec> = contract_packages .par_iter() - .map(|package| { - compile_contract(file_manager, parsed_files, package, compile_options, expression_width) - }) + .map(|package| compile_contract(file_manager, parsed_files, package, compile_options)) .collect(); // Report any warnings/errors which were encountered during compilation. @@ -80,18 +67,10 @@ pub fn compile_program( parsed_files: &ParsedFiles, package: &Package, compile_options: &CompileOptions, - expression_width: ExpressionWidth, cached_program: Option, ) -> CompilationResult { let (mut context, crate_id) = prepare_package(file_manager, parsed_files, package); - - let (program, warnings) = - noirc_driver::compile_main(&mut context, crate_id, compile_options, cached_program)?; - - // Apply backend specific optimizations. - let optimized_program = crate::ops::optimize_program(program, expression_width); - - Ok((optimized_program, warnings)) + noirc_driver::compile_main(&mut context, crate_id, compile_options, cached_program) } pub fn compile_contract( @@ -99,15 +78,9 @@ pub fn compile_contract( parsed_files: &ParsedFiles, package: &Package, compile_options: &CompileOptions, - expression_width: ExpressionWidth, ) -> CompilationResult { let (mut context, crate_id) = prepare_package(file_manager, parsed_files, package); - let (contract, warnings) = - noirc_driver::compile_contract(&mut context, crate_id, compile_options)?; - - let optimized_contract = crate::ops::optimize_contract(contract, expression_width); - - Ok((optimized_contract, warnings)) + noirc_driver::compile_contract(&mut context, crate_id, compile_options) } pub(crate) fn report_errors( diff --git a/noir/tooling/nargo/src/ops/foreign_calls.rs b/noir/tooling/nargo/src/ops/foreign_calls.rs index cbe40c92b4e..e3a3174f8dc 100644 --- a/noir/tooling/nargo/src/ops/foreign_calls.rs +++ b/noir/tooling/nargo/src/ops/foreign_calls.rs @@ -82,8 +82,8 @@ impl MockedCall { } impl MockedCall { - fn matches(&self, name: &str, params: &Vec) -> bool { - self.name == name && (self.params.is_none() || self.params.as_ref() == Some(params)) + fn matches(&self, name: &str, params: &[ForeignCallParam]) -> bool { + self.name == name && (self.params.is_none() || self.params.as_deref() == Some(params)) } } diff --git a/noir/tooling/nargo/src/ops/mod.rs b/noir/tooling/nargo/src/ops/mod.rs index 4912c84839e..4f92faa73a4 100644 --- a/noir/tooling/nargo/src/ops/mod.rs +++ b/noir/tooling/nargo/src/ops/mod.rs @@ -2,6 +2,8 @@ pub use self::compile::{compile_contract, compile_program, compile_workspace}; pub use self::execute::execute_circuit; pub use self::foreign_calls::{DefaultForeignCallExecutor, ForeignCallExecutor}; pub use self::optimize::{optimize_contract, optimize_program}; +pub use self::transform::{transform_contract, transform_program}; + pub use self::test::{run_test, TestStatus}; mod compile; @@ -9,3 +11,4 @@ mod execute; mod foreign_calls; mod optimize; mod test; +mod transform; diff --git a/noir/tooling/nargo/src/ops/optimize.rs b/noir/tooling/nargo/src/ops/optimize.rs index d3a36dd65ac..2d0c4c43d25 100644 --- a/noir/tooling/nargo/src/ops/optimize.rs +++ b/noir/tooling/nargo/src/ops/optimize.rs @@ -1,26 +1,16 @@ -use acvm::ExpressionWidth; use iter_extended::vecmap; use noirc_driver::{CompiledContract, CompiledProgram}; -pub fn optimize_program( - mut program: CompiledProgram, - expression_width: ExpressionWidth, -) -> CompiledProgram { - let (optimized_circuit, location_map) = - acvm::compiler::compile(program.circuit, expression_width); - +pub fn optimize_program(mut program: CompiledProgram) -> CompiledProgram { + let (optimized_circuit, location_map) = acvm::compiler::optimize(program.circuit); program.circuit = optimized_circuit; program.debug.update_acir(location_map); program } -pub fn optimize_contract( - contract: CompiledContract, - expression_width: ExpressionWidth, -) -> CompiledContract { +pub fn optimize_contract(contract: CompiledContract) -> CompiledContract { let functions = vecmap(contract.functions, |mut func| { - let (optimized_bytecode, location_map) = - acvm::compiler::compile(func.bytecode, expression_width); + let (optimized_bytecode, location_map) = acvm::compiler::optimize(func.bytecode); func.bytecode = optimized_bytecode; func.debug.update_acir(location_map); func diff --git a/noir/tooling/nargo/src/ops/transform.rs b/noir/tooling/nargo/src/ops/transform.rs new file mode 100644 index 00000000000..f3efd82333e --- /dev/null +++ b/noir/tooling/nargo/src/ops/transform.rs @@ -0,0 +1,30 @@ +use acvm::ExpressionWidth; +use iter_extended::vecmap; +use noirc_driver::{CompiledContract, CompiledProgram}; + +pub fn transform_program( + mut program: CompiledProgram, + expression_width: ExpressionWidth, +) -> CompiledProgram { + let (optimized_circuit, location_map) = + acvm::compiler::compile(program.circuit, expression_width); + + program.circuit = optimized_circuit; + program.debug.update_acir(location_map); + program +} + +pub fn transform_contract( + contract: CompiledContract, + expression_width: ExpressionWidth, +) -> CompiledContract { + let functions = vecmap(contract.functions, |mut func| { + let (optimized_bytecode, location_map) = + acvm::compiler::compile(func.bytecode, expression_width); + func.bytecode = optimized_bytecode; + func.debug.update_acir(location_map); + func + }); + + CompiledContract { functions, ..contract } +} diff --git a/noir/tooling/nargo/src/workspace.rs b/noir/tooling/nargo/src/workspace.rs index 5696a758531..0795ffd9304 100644 --- a/noir/tooling/nargo/src/workspace.rs +++ b/noir/tooling/nargo/src/workspace.rs @@ -20,6 +20,8 @@ pub struct Workspace { pub members: Vec, // If `Some()`, the `selected_package_index` is used to select the only `Package` when iterating a Workspace pub selected_package_index: Option, + /// If we could not resolve the workspace we would inform the user we have assumed it (ie. from lsp file path given) + pub is_assumed: bool, } impl Workspace { diff --git a/noir/tooling/nargo_cli/src/cli/codegen_verifier_cmd.rs b/noir/tooling/nargo_cli/src/cli/codegen_verifier_cmd.rs index 8bf12ee4100..63d27e30836 100644 --- a/noir/tooling/nargo_cli/src/cli/codegen_verifier_cmd.rs +++ b/noir/tooling/nargo_cli/src/cli/codegen_verifier_cmd.rs @@ -1,12 +1,11 @@ +use super::fs::{create_named_dir, write_to_file}; use super::NargoConfig; -use super::{ - compile_cmd::compile_bin_package, - fs::{create_named_dir, write_to_file}, -}; use crate::backends::Backend; +use crate::cli::compile_cmd::report_errors; use crate::errors::CliError; use clap::Args; +use nargo::ops::compile_program; use nargo::{insert_all_files_for_workspace_into_file_manager, parse_all}; use nargo_toml::{get_package_manifest, resolve_workspace_from_toml, PackageSelection}; use noirc_driver::{file_manager_with_stdlib, CompileOptions, NOIR_ARTIFACT_VERSION_STRING}; @@ -47,15 +46,25 @@ pub(crate) fn run( let parsed_files = parse_all(&workspace_file_manager); let expression_width = backend.get_backend_info()?; - for package in &workspace { - let program = compile_bin_package( + let binary_packages = workspace.into_iter().filter(|package| package.is_binary()); + for package in binary_packages { + let compilation_result = compile_program( &workspace_file_manager, &parsed_files, package, &args.compile_options, - expression_width, + None, + ); + + let program = report_errors( + compilation_result, + &workspace_file_manager, + args.compile_options.deny_warnings, + args.compile_options.silence_warnings, )?; + let program = nargo::ops::transform_program(program, expression_width); + let smart_contract_string = backend.eth_contract(&program.circuit)?; let contract_dir = workspace.contracts_directory_path(package); diff --git a/noir/tooling/nargo_cli/src/cli/compile_cmd.rs b/noir/tooling/nargo_cli/src/cli/compile_cmd.rs index aa9a46f39ef..34fb05249b5 100644 --- a/noir/tooling/nargo_cli/src/cli/compile_cmd.rs +++ b/noir/tooling/nargo_cli/src/cli/compile_cmd.rs @@ -1,7 +1,5 @@ use std::path::Path; -use acvm::ExpressionWidth; - use fm::FileManager; use nargo::artifacts::program::ProgramArtifact; use nargo::errors::CompileError; @@ -63,12 +61,14 @@ pub(crate) fn run( insert_all_files_for_workspace_into_file_manager(&workspace, &mut workspace_file_manager); let parsed_files = parse_all(&workspace_file_manager); - let expression_width = backend.get_backend_info_or_default(); + let expression_width = args + .compile_options + .expression_width + .unwrap_or_else(|| backend.get_backend_info_or_default()); let (compiled_program, compiled_contracts) = compile_workspace( &workspace_file_manager, &parsed_files, &workspace, - expression_width, &args.compile_options, )?; @@ -81,9 +81,11 @@ pub(crate) fn run( // Save build artifacts to disk. let only_acir = args.compile_options.only_acir; for (package, program) in binary_packages.into_iter().zip(compiled_program) { + let program = nargo::ops::transform_program(program, expression_width); save_program(program.clone(), &package, &workspace.target_directory_path(), only_acir); } for (package, contract) in contract_packages.into_iter().zip(compiled_contracts) { + let contract = nargo::ops::transform_contract(contract, expression_width); save_contract(contract, &package, &circuit_dir); } @@ -94,7 +96,6 @@ pub(super) fn compile_workspace( file_manager: &FileManager, parsed_files: &ParsedFiles, workspace: &Workspace, - expression_width: ExpressionWidth, compile_options: &CompileOptions, ) -> Result<(Vec, Vec), CliError> { let (binary_packages, contract_packages): (Vec<_>, Vec<_>) = workspace @@ -114,21 +115,12 @@ pub(super) fn compile_workspace( .filter(|p| p.noir_version == NOIR_ARTIFACT_VERSION_STRING) .map(|p| p.into()); - compile_program( - file_manager, - parsed_files, - package, - compile_options, - expression_width, - cached_program, - ) + compile_program(file_manager, parsed_files, package, compile_options, cached_program) }) .collect(); let contract_results: Vec> = contract_packages .par_iter() - .map(|package| { - compile_contract(file_manager, parsed_files, package, compile_options, expression_width) - }) + .map(|package| compile_contract(file_manager, parsed_files, package, compile_options)) .collect(); // Report any warnings/errors which were encountered during compilation. @@ -158,36 +150,6 @@ pub(super) fn compile_workspace( Ok((compiled_programs, compiled_contracts)) } -pub(crate) fn compile_bin_package( - file_manager: &FileManager, - parsed_files: &ParsedFiles, - package: &Package, - compile_options: &CompileOptions, - expression_width: ExpressionWidth, -) -> Result { - if package.is_library() { - return Err(CompileError::LibraryCrate(package.name.clone()).into()); - } - - let compilation_result = compile_program( - file_manager, - parsed_files, - package, - compile_options, - expression_width, - None, - ); - - let program = report_errors( - compilation_result, - file_manager, - compile_options.deny_warnings, - compile_options.silence_warnings, - )?; - - Ok(program) -} - pub(super) fn save_program( program: CompiledProgram, package: &Package, diff --git a/noir/tooling/nargo_cli/src/cli/dap_cmd.rs b/noir/tooling/nargo_cli/src/cli/dap_cmd.rs index 9798cbedfeb..67322b1873e 100644 --- a/noir/tooling/nargo_cli/src/cli/dap_cmd.rs +++ b/noir/tooling/nargo_cli/src/cli/dap_cmd.rs @@ -1,7 +1,9 @@ use acvm::acir::native_types::WitnessMap; +use acvm::ExpressionWidth; use backend_interface::Backend; use clap::Args; use nargo::constants::PROVER_INPUT_FILE; +use nargo::ops::compile_program; use nargo::workspace::Workspace; use nargo::{insert_all_files_for_workspace_into_file_manager, parse_all}; use nargo_toml::{get_package_manifest, resolve_workspace_from_toml, PackageSelection}; @@ -21,14 +23,28 @@ use dap::server::Server; use dap::types::Capabilities; use serde_json::Value; -use super::compile_cmd::compile_bin_package; +use super::compile_cmd::report_errors; use super::fs::inputs::read_inputs_from_file; use crate::errors::CliError; use super::NargoConfig; #[derive(Debug, Clone, Args)] -pub(crate) struct DapCommand; +pub(crate) struct DapCommand { + /// Override the expression width requested by the backend. + #[arg(long, value_parser = parse_expression_width)] + expression_width: Option, +} + +fn parse_expression_width(input: &str) -> Result { + use std::io::{Error, ErrorKind}; + + let width = input + .parse::() + .map_err(|err| Error::new(ErrorKind::InvalidInput, err.to_string()))?; + + Ok(ExpressionWidth::from(width)) +} struct LoadError(&'static str); @@ -53,16 +69,14 @@ fn find_workspace(project_folder: &str, package: Option<&str>) -> Option, prover_name: &str, + expression_width: ExpressionWidth, ) -> Result<(CompiledProgram, WitnessMap), LoadError> { let workspace = find_workspace(project_folder, package).ok_or(LoadError("Cannot open workspace"))?; - let expression_width = - backend.get_backend_info().map_err(|_| LoadError("Failed to get backend info"))?; let package = workspace .into_iter() .find(|p| p.is_binary()) @@ -72,15 +86,20 @@ fn load_and_compile_project( insert_all_files_for_workspace_into_file_manager(&workspace, &mut workspace_file_manager); let parsed_files = parse_all(&workspace_file_manager); - let compiled_program = compile_bin_package( + let compile_options = CompileOptions::default(); + let compilation_result = + compile_program(&workspace_file_manager, &parsed_files, package, &compile_options, None); + + let compiled_program = report_errors( + compilation_result, &workspace_file_manager, - &parsed_files, - package, - &CompileOptions::default(), - expression_width, + compile_options.deny_warnings, + compile_options.silence_warnings, ) .map_err(|_| LoadError("Failed to compile project"))?; + let compiled_program = nargo::ops::transform_program(compiled_program, expression_width); + let (inputs_map, _) = read_inputs_from_file(&package.root_dir, prover_name, Format::Toml, &compiled_program.abi) .map_err(|_| LoadError("Failed to read program inputs"))?; @@ -94,7 +113,7 @@ fn load_and_compile_project( fn loop_uninitialized_dap( mut server: Server, - backend: &Backend, + expression_width: ExpressionWidth, ) -> Result<(), ServerError> { loop { let req = match server.poll_request()? { @@ -134,7 +153,12 @@ fn loop_uninitialized_dap( eprintln!("Package: {}", package.unwrap_or("(default)")); eprintln!("Prover name: {}", prover_name); - match load_and_compile_project(backend, project_folder, package, prover_name) { + match load_and_compile_project( + project_folder, + package, + prover_name, + expression_width, + ) { Ok((compiled_program, initial_witness)) => { server.respond(req.ack()?)?; @@ -170,12 +194,15 @@ fn loop_uninitialized_dap( pub(crate) fn run( backend: &Backend, - _args: DapCommand, + args: DapCommand, _config: NargoConfig, ) -> Result<(), CliError> { let output = BufWriter::new(std::io::stdout()); let input = BufReader::new(std::io::stdin()); let server = Server::new(input, output); - loop_uninitialized_dap(server, backend).map_err(CliError::DapError) + let expression_width = + args.expression_width.unwrap_or_else(|| backend.get_backend_info_or_default()); + + loop_uninitialized_dap(server, expression_width).map_err(CliError::DapError) } diff --git a/noir/tooling/nargo_cli/src/cli/debug_cmd.rs b/noir/tooling/nargo_cli/src/cli/debug_cmd.rs index e62cbc11ec8..a0bac3bdac1 100644 --- a/noir/tooling/nargo_cli/src/cli/debug_cmd.rs +++ b/noir/tooling/nargo_cli/src/cli/debug_cmd.rs @@ -6,6 +6,7 @@ use clap::Args; use nargo::artifacts::debug::DebugArtifact; use nargo::constants::PROVER_INPUT_FILE; +use nargo::ops::compile_program; use nargo::package::Package; use nargo::{insert_all_files_for_workspace_into_file_manager, parse_all}; use nargo_toml::{get_package_manifest, resolve_workspace_from_toml, PackageSelection}; @@ -16,7 +17,7 @@ use noirc_driver::{ }; use noirc_frontend::graph::CrateName; -use super::compile_cmd::compile_bin_package; +use super::compile_cmd::report_errors; use super::fs::{inputs::read_inputs_from_file, witness::save_witness_to_dir}; use super::NargoConfig; use crate::backends::Backend; @@ -53,7 +54,10 @@ pub(crate) fn run( Some(NOIR_ARTIFACT_VERSION_STRING.to_string()), )?; let target_dir = &workspace.target_directory_path(); - let expression_width = backend.get_backend_info()?; + let expression_width = args + .compile_options + .expression_width + .unwrap_or_else(|| backend.get_backend_info_or_default()); let mut workspace_file_manager = file_manager_with_stdlib(std::path::Path::new("")); insert_all_files_for_workspace_into_file_manager(&workspace, &mut workspace_file_manager); @@ -66,14 +70,23 @@ pub(crate) fn run( return Ok(()); }; - let compiled_program = compile_bin_package( + let compilation_result = compile_program( &workspace_file_manager, &parsed_files, package, &args.compile_options, - expression_width, + None, + ); + + let compiled_program = report_errors( + compilation_result, + &workspace_file_manager, + args.compile_options.deny_warnings, + args.compile_options.silence_warnings, )?; + let compiled_program = nargo::ops::transform_program(compiled_program, expression_width); + run_async(package, compiled_program, &args.prover_name, &args.witness_name, target_dir) } diff --git a/noir/tooling/nargo_cli/src/cli/execute_cmd.rs b/noir/tooling/nargo_cli/src/cli/execute_cmd.rs index cf0d46a0718..a3fcebab94f 100644 --- a/noir/tooling/nargo_cli/src/cli/execute_cmd.rs +++ b/noir/tooling/nargo_cli/src/cli/execute_cmd.rs @@ -5,7 +5,7 @@ use clap::Args; use nargo::artifacts::debug::DebugArtifact; use nargo::constants::PROVER_INPUT_FILE; use nargo::errors::try_to_diagnose_runtime_error; -use nargo::ops::DefaultForeignCallExecutor; +use nargo::ops::{compile_program, DefaultForeignCallExecutor}; use nargo::package::Package; use nargo::{insert_all_files_for_workspace_into_file_manager, parse_all}; use nargo_toml::{get_package_manifest, resolve_workspace_from_toml, PackageSelection}; @@ -16,10 +16,10 @@ use noirc_driver::{ }; use noirc_frontend::graph::CrateName; -use super::compile_cmd::compile_bin_package; use super::fs::{inputs::read_inputs_from_file, witness::save_witness_to_dir}; use super::NargoConfig; use crate::backends::Backend; +use crate::cli::compile_cmd::report_errors; use crate::errors::CliError; /// Executes a circuit to calculate its return value @@ -68,16 +68,29 @@ pub(crate) fn run( insert_all_files_for_workspace_into_file_manager(&workspace, &mut workspace_file_manager); let parsed_files = parse_all(&workspace_file_manager); - let expression_width = backend.get_backend_info_or_default(); - for package in &workspace { - let compiled_program = compile_bin_package( + let expression_width = args + .compile_options + .expression_width + .unwrap_or_else(|| backend.get_backend_info_or_default()); + let binary_packages = workspace.into_iter().filter(|package| package.is_binary()); + for package in binary_packages { + let compilation_result = compile_program( &workspace_file_manager, &parsed_files, package, &args.compile_options, - expression_width, + None, + ); + + let compiled_program = report_errors( + compilation_result, + &workspace_file_manager, + args.compile_options.deny_warnings, + args.compile_options.silence_warnings, )?; + let compiled_program = nargo::ops::transform_program(compiled_program, expression_width); + let (return_value, solved_witness) = execute_program_and_decode( compiled_program, package, diff --git a/noir/tooling/nargo_cli/src/cli/info_cmd.rs b/noir/tooling/nargo_cli/src/cli/info_cmd.rs index 8dfff67b47f..131fd6ad214 100644 --- a/noir/tooling/nargo_cli/src/cli/info_cmd.rs +++ b/noir/tooling/nargo_cli/src/cli/info_cmd.rs @@ -69,24 +69,33 @@ pub(crate) fn run( insert_all_files_for_workspace_into_file_manager(&workspace, &mut workspace_file_manager); let parsed_files = parse_all(&workspace_file_manager); - let expression_width = backend.get_backend_info_or_default(); + let expression_width = args + .compile_options + .expression_width + .unwrap_or_else(|| backend.get_backend_info_or_default()); let (compiled_programs, compiled_contracts) = compile_workspace( &workspace_file_manager, &parsed_files, &workspace, - expression_width, &args.compile_options, )?; + let compiled_programs = vecmap(compiled_programs, |program| { + nargo::ops::transform_program(program, expression_width) + }); + let compiled_contracts = vecmap(compiled_contracts, |contract| { + nargo::ops::transform_contract(contract, expression_width) + }); + if args.profile_info { for compiled_program in &compiled_programs { let span_opcodes = compiled_program.debug.count_span_opcodes(); - let debug_artifact: DebugArtifact = compiled_program.clone().into(); + let debug_artifact = DebugArtifact::from(compiled_program.clone()); print_span_opcodes(span_opcodes, &debug_artifact); } for compiled_contract in &compiled_contracts { - let debug_artifact: DebugArtifact = compiled_contract.clone().into(); + let debug_artifact = DebugArtifact::from(compiled_contract.clone()); let functions = &compiled_contract.functions; for contract_function in functions { let span_opcodes = contract_function.debug.count_span_opcodes(); diff --git a/noir/tooling/nargo_cli/src/cli/prove_cmd.rs b/noir/tooling/nargo_cli/src/cli/prove_cmd.rs index d02464fd6df..1d20e97af85 100644 --- a/noir/tooling/nargo_cli/src/cli/prove_cmd.rs +++ b/noir/tooling/nargo_cli/src/cli/prove_cmd.rs @@ -1,5 +1,6 @@ use clap::Args; use nargo::constants::{PROVER_INPUT_FILE, VERIFIER_INPUT_FILE}; +use nargo::ops::compile_program; use nargo::package::Package; use nargo::workspace::Workspace; use nargo::{insert_all_files_for_workspace_into_file_manager, parse_all}; @@ -10,7 +11,7 @@ use noirc_driver::{ }; use noirc_frontend::graph::CrateName; -use super::compile_cmd::compile_bin_package; +use super::compile_cmd::report_errors; use super::fs::{ inputs::{read_inputs_from_file, write_inputs_to_file}, proof::save_proof_to_dir, @@ -68,21 +69,34 @@ pub(crate) fn run( insert_all_files_for_workspace_into_file_manager(&workspace, &mut workspace_file_manager); let parsed_files = parse_all(&workspace_file_manager); - let expression_width = backend.get_backend_info()?; - for package in &workspace { - let program = compile_bin_package( + let expression_width = args + .compile_options + .expression_width + .unwrap_or_else(|| backend.get_backend_info_or_default()); + let binary_packages = workspace.into_iter().filter(|package| package.is_binary()); + for package in binary_packages { + let compilation_result = compile_program( &workspace_file_manager, &parsed_files, package, &args.compile_options, - expression_width, + None, + ); + + let compiled_program = report_errors( + compilation_result, + &workspace_file_manager, + args.compile_options.deny_warnings, + args.compile_options.silence_warnings, )?; + let compiled_program = nargo::ops::transform_program(compiled_program, expression_width); + prove_package( backend, &workspace, package, - program, + compiled_program, &args.prover_name, &args.verifier_name, args.verify, diff --git a/noir/tooling/nargo_cli/src/cli/test_cmd.rs b/noir/tooling/nargo_cli/src/cli/test_cmd.rs index 5db842609e5..9fee27b9172 100644 --- a/noir/tooling/nargo_cli/src/cli/test_cmd.rs +++ b/noir/tooling/nargo_cli/src/cli/test_cmd.rs @@ -83,22 +83,44 @@ pub(crate) fn run( }; let blackbox_solver = Bn254BlackBoxSolver::new(); - for package in &workspace { - // By unwrapping here with `?`, we stop the test runner upon a package failing - // TODO: We should run the whole suite even if there are failures in a package - run_tests( - &workspace_file_manager, - &parsed_files, - &blackbox_solver, - package, - pattern, - args.show_output, - args.oracle_resolver.as_deref(), - &args.compile_options, - )?; + + let test_reports: Vec> = workspace + .into_iter() + .map(|package| { + run_tests( + &workspace_file_manager, + &parsed_files, + &blackbox_solver, + package, + pattern, + args.show_output, + args.oracle_resolver.as_deref(), + &args.compile_options, + ) + }) + .collect::>()?; + let test_report: Vec<(String, TestStatus)> = test_reports.into_iter().flatten().collect(); + + if test_report.is_empty() { + match &pattern { + FunctionNameMatch::Exact(pattern) => { + return Err(CliError::Generic( + format!("Found 0 tests matching input '{pattern}'.",), + )) + } + FunctionNameMatch::Contains(pattern) => { + return Err(CliError::Generic(format!("Found 0 tests containing '{pattern}'.",))) + } + // If we are running all tests in a crate, having none is not an error + FunctionNameMatch::Anything => {} + }; } - Ok(()) + if test_report.iter().any(|(_, status)| !matches!(status, TestStatus::Fail { .. })) { + Ok(()) + } else { + Err(CliError::Generic(String::new())) + } } #[allow(clippy::too_many_arguments)] @@ -111,7 +133,7 @@ fn run_tests( show_output: bool, foreign_call_resolver_url: Option<&str>, compile_options: &CompileOptions, -) -> Result<(), CliError> { +) -> Result, CliError> { let (mut context, crate_id) = prepare_package(file_manager, parsed_files, package); check_crate_and_report_errors( &mut context, @@ -123,45 +145,29 @@ fn run_tests( let test_functions = context.get_all_test_functions_in_crate_matching(&crate_id, fn_name); let count_all = test_functions.len(); - if count_all == 0 { - match &fn_name { - FunctionNameMatch::Exact(pattern) => { - return Err(CliError::Generic(format!( - "[{}] Found 0 tests matching input '{pattern}'.", - package.name - ))) - } - FunctionNameMatch::Contains(pattern) => { - return Err(CliError::Generic(format!( - "[{}] Found 0 tests containing '{pattern}'.", - package.name - ))) - } - // If we are running all tests in a crate, having none is not an error - FunctionNameMatch::Anything => {} - }; - } let plural = if count_all == 1 { "" } else { "s" }; println!("[{}] Running {count_all} test function{plural}", package.name); - let mut count_failed = 0; let writer = StandardStream::stderr(ColorChoice::Always); let mut writer = writer.lock(); + let mut test_report: Vec<(String, TestStatus)> = Vec::new(); for (test_name, test_function) in test_functions { write!(writer, "[{}] Testing {test_name}... ", package.name) .expect("Failed to write to stderr"); writer.flush().expect("Failed to flush writer"); - match run_test( + let test_status = run_test( blackbox_solver, &context, test_function, show_output, foreign_call_resolver_url, compile_options, - ) { + ); + + match &test_status { TestStatus::Pass { .. } => { writer .set_color(ColorSpec::new().set_fg(Some(Color::Green))) @@ -176,35 +182,36 @@ fn run_tests( if let Some(diag) = error_diagnostic { noirc_errors::reporter::report_all( context.file_manager.as_file_map(), - &[diag], + &[diag.clone()], compile_options.deny_warnings, compile_options.silence_warnings, ); } - count_failed += 1; } TestStatus::CompileError(err) => { noirc_errors::reporter::report_all( context.file_manager.as_file_map(), - &[err], + &[err.clone()], compile_options.deny_warnings, compile_options.silence_warnings, ); - count_failed += 1; } } + + test_report.push((test_name, test_status)); + writer.reset().expect("Failed to reset writer"); } write!(writer, "[{}] ", package.name).expect("Failed to write to stderr"); + let count_failed = + test_report.iter().filter(|(_, status)| !matches!(status, TestStatus::Pass)).count(); if count_failed == 0 { writer.set_color(ColorSpec::new().set_fg(Some(Color::Green))).expect("Failed to set color"); write!(writer, "{count_all} test{plural} passed").expect("Failed to write to stderr"); writer.reset().expect("Failed to reset writer"); writeln!(writer).expect("Failed to write to stderr"); - - Ok(()) } else { let count_passed = count_all - count_failed; let plural_failed = if count_failed == 1 { "" } else { "s" }; @@ -219,11 +226,10 @@ fn run_tests( } writer.set_color(ColorSpec::new().set_fg(Some(Color::Red))).expect("Failed to set color"); - write!(writer, "{count_failed} test{plural_failed} failed") + writeln!(writer, "{count_failed} test{plural_failed} failed") .expect("Failed to write to stderr"); writer.reset().expect("Failed to reset writer"); - - // Writes final newline. - Err(CliError::Generic(String::new())) } + + Ok(test_report) } diff --git a/noir/tooling/nargo_cli/src/cli/verify_cmd.rs b/noir/tooling/nargo_cli/src/cli/verify_cmd.rs index 1701b9e063c..ea4aaa051bb 100644 --- a/noir/tooling/nargo_cli/src/cli/verify_cmd.rs +++ b/noir/tooling/nargo_cli/src/cli/verify_cmd.rs @@ -1,12 +1,11 @@ +use super::compile_cmd::report_errors; +use super::fs::{inputs::read_inputs_from_file, load_hex_data}; use super::NargoConfig; -use super::{ - compile_cmd::compile_bin_package, - fs::{inputs::read_inputs_from_file, load_hex_data}, -}; use crate::{backends::Backend, errors::CliError}; use clap::Args; use nargo::constants::{PROOF_EXT, VERIFIER_INPUT_FILE}; +use nargo::ops::compile_program; use nargo::package::Package; use nargo::workspace::Workspace; use nargo::{insert_all_files_for_workspace_into_file_manager, parse_all}; @@ -55,17 +54,30 @@ pub(crate) fn run( insert_all_files_for_workspace_into_file_manager(&workspace, &mut workspace_file_manager); let parsed_files = parse_all(&workspace_file_manager); - let expression_width = backend.get_backend_info()?; - for package in &workspace { - let program = compile_bin_package( + let expression_width = args + .compile_options + .expression_width + .unwrap_or_else(|| backend.get_backend_info_or_default()); + let binary_packages = workspace.into_iter().filter(|package| package.is_binary()); + for package in binary_packages { + let compilation_result = compile_program( &workspace_file_manager, &parsed_files, package, &args.compile_options, - expression_width, + None, + ); + + let compiled_program = report_errors( + compilation_result, + &workspace_file_manager, + args.compile_options.deny_warnings, + args.compile_options.silence_warnings, )?; - verify_package(backend, &workspace, package, program, &args.verifier_name)?; + let compiled_program = nargo::ops::transform_program(compiled_program, expression_width); + + verify_package(backend, &workspace, package, compiled_program, &args.verifier_name)?; } Ok(()) diff --git a/noir/tooling/nargo_toml/src/lib.rs b/noir/tooling/nargo_toml/src/lib.rs index cecc3f7e26a..985cb30dc24 100644 --- a/noir/tooling/nargo_toml/src/lib.rs +++ b/noir/tooling/nargo_toml/src/lib.rs @@ -345,6 +345,7 @@ fn toml_to_workspace( root_dir: nargo_toml.root_dir, selected_package_index: Some(0), members: vec![member], + is_assumed: false, }, } } @@ -392,7 +393,12 @@ fn toml_to_workspace( PackageSelection::All => (), } - Workspace { root_dir: nargo_toml.root_dir, members, selected_package_index } + Workspace { + root_dir: nargo_toml.root_dir, + members, + selected_package_index, + is_assumed: false, + } } }; diff --git a/noir/tooling/noir_js_backend_barretenberg/package.json b/noir/tooling/noir_js_backend_barretenberg/package.json index cd2a6354ac4..a0123883efd 100644 --- a/noir/tooling/noir_js_backend_barretenberg/package.json +++ b/noir/tooling/noir_js_backend_barretenberg/package.json @@ -42,7 +42,7 @@ "lint": "NODE_NO_WARNINGS=1 eslint . --ext .ts --ignore-path ./.eslintignore --max-warnings 0" }, "dependencies": { - "@aztec/bb.js": "0.19.0", + "@aztec/bb.js": "0.21.0", "@noir-lang/types": "workspace:*", "fflate": "^0.8.0" }, diff --git a/noir/tooling/noir_js_backend_barretenberg/src/index.ts b/noir/tooling/noir_js_backend_barretenberg/src/index.ts index 6e619fd59cf..61094a3451f 100644 --- a/noir/tooling/noir_js_backend_barretenberg/src/index.ts +++ b/noir/tooling/noir_js_backend_barretenberg/src/index.ts @@ -1,11 +1,12 @@ -/* eslint-disable @typescript-eslint/no-explicit-any */ import { decompressSync as gunzip } from 'fflate'; import { acirToUint8Array } from './serialize.js'; import { Backend, CompiledCircuit, ProofData } from '@noir-lang/types'; import { BackendOptions } from './types.js'; import { deflattenPublicInputs, flattenPublicInputsAsArray } from './public_inputs.js'; +import { type Barretenberg } from '@aztec/bb.js'; export { publicInputsToWitnessMap } from './public_inputs.js'; + // This is the number of bytes in a UltraPlonk proof // minus the public inputs. const numBytesInProofWithoutPublicInputs: number = 2144; @@ -15,12 +16,14 @@ export class BarretenbergBackend implements Backend { // have to initialize `api` and `acirComposer` in the constructor. // These are initialized asynchronously in the `init` function, // constructors cannot be asynchronous which is why we do this. - private api: any; + + private api!: Barretenberg; + // eslint-disable-next-line @typescript-eslint/no-explicit-any private acirComposer: any; private acirUncompressedBytecode: Uint8Array; constructor( - private acirCircuit: CompiledCircuit, + acirCircuit: CompiledCircuit, private options: BackendOptions = { threads: 1 }, ) { const acirBytecodeBase64 = acirCircuit.bytecode; @@ -30,8 +33,6 @@ export class BarretenbergBackend implements Backend { /** @ignore */ async instantiate(): Promise { if (!this.api) { - // eslint-disable-next-line @typescript-eslint/ban-ts-comment - //@ts-ignore const { Barretenberg, RawBuffer, Crs } = await import('@aztec/bb.js'); const api = await Barretenberg.new({ threads: this.options.threads }); @@ -46,29 +47,25 @@ export class BarretenbergBackend implements Backend { } } - // Generate an outer proof. This is the proof for the circuit which will verify - // inner proofs and or can be seen as the proof created for regular circuits. - // - // The settings for this proof are the same as the settings for a "normal" proof - // ie one that is not in the recursive setting. + /** + * Generate a final proof. This is the proof for the circuit which will verify + * intermediate proofs and or can be seen as the proof created for regular circuits. + */ async generateFinalProof(decompressedWitness: Uint8Array): Promise { + // The settings for this proof are the same as the settings for a "normal" proof + // i.e. one that is not in the recursive setting. const makeEasyToVerifyInCircuit = false; return this.generateProof(decompressedWitness, makeEasyToVerifyInCircuit); } - // Generates an inner proof. This is the proof that will be verified - // in another circuit. - // - // This is sometimes referred to as a recursive proof. - // We avoid this terminology as the only property of this proof - // that matters, is the fact that it is easy to verify in another - // circuit. We _could_ choose to verify this proof in the CLI. - // - // We set `makeEasyToVerifyInCircuit` to true, which will tell the backend to - // generate the proof using components that will make the proof - // easier to verify in a circuit. - /** + * Generates an intermediate proof. This is the proof that can be verified + * in another circuit. + * + * This is sometimes referred to as a recursive proof. + * We avoid this terminology as the only property of this proof + * that matters is the fact that it is easy to verify in another circuit. + * We _could_ choose to verify this proof outside of a circuit just as easily. * * @example * ```typescript @@ -76,6 +73,9 @@ export class BarretenbergBackend implements Backend { * ``` */ async generateIntermediateProof(witness: Uint8Array): Promise { + // We set `makeEasyToVerifyInCircuit` to true, which will tell the backend to + // generate the proof using components that will make the proof + // easier to verify in a circuit. const makeEasyToVerifyInCircuit = true; return this.generateProof(witness, makeEasyToVerifyInCircuit); } @@ -99,17 +99,16 @@ export class BarretenbergBackend implements Backend { return { proof, publicInputs }; } - // Generates artifacts that will be passed to a circuit that will verify this proof. - // - // Instead of passing the proof and verification key as a byte array, we pass them - // as fields which makes it cheaper to verify in a circuit. - // - // The proof that is passed here will have been created using the `generateInnerProof` - // method. - // - // The number of public inputs denotes how many public inputs are in the inner proof. - /** + * Generates artifacts that will be passed to a circuit that will verify this proof. + * + * Instead of passing the proof and verification key as a byte array, we pass them + * as fields which makes it cheaper to verify in a circuit. + * + * The proof that is passed here will have been created using the `generateIntermediateProof` + * method. + * + * The number of public inputs denotes how many public inputs are in the inner proof. * * @example * ```typescript diff --git a/noir/tooling/noirc_abi/src/lib.rs b/noir/tooling/noirc_abi/src/lib.rs index 066b1635ced..1fc257c1676 100644 --- a/noir/tooling/noirc_abi/src/lib.rs +++ b/noir/tooling/noirc_abi/src/lib.rs @@ -512,7 +512,7 @@ fn decode_string_value(field_elements: &[FieldElement]) -> String { final_string.to_owned() } -#[derive(Debug, Serialize, Deserialize)] +#[derive(Debug, Clone, Serialize, Deserialize)] pub struct ContractEvent { /// Event name name: String, diff --git a/noir/yarn.lock b/noir/yarn.lock index db3f493bc62..743068f1907 100644 --- a/noir/yarn.lock +++ b/noir/yarn.lock @@ -235,9 +235,9 @@ __metadata: languageName: node linkType: hard -"@aztec/bb.js@npm:0.19.0": - version: 0.19.0 - resolution: "@aztec/bb.js@npm:0.19.0" +"@aztec/bb.js@npm:0.21.0": + version: 0.21.0 + resolution: "@aztec/bb.js@npm:0.21.0" dependencies: comlink: ^4.4.1 commander: ^10.0.1 @@ -245,7 +245,7 @@ __metadata: tslib: ^2.4.0 bin: bb.js: dest/node/main.js - checksum: c78c22c3b8c43e0010a43145f973489aa7da9fcf0b8527884107f1f34ac3ca5f5d4ab087d74ce50cb75d6dbaef88bfc693e23745282faa30b81dc78481c65874 + checksum: a0fb97476f52025f3c31b7a5e890966ac375ed47c5cfd3434f5c3e4265af3c7566a162f37d6c56f394f44bfe4ba67e5002b7c5998ecc4f6abe70e04f5b8abe34 languageName: node linkType: hard @@ -4435,7 +4435,7 @@ __metadata: version: 0.0.0-use.local resolution: "@noir-lang/backend_barretenberg@workspace:tooling/noir_js_backend_barretenberg" dependencies: - "@aztec/bb.js": 0.19.0 + "@aztec/bb.js": 0.21.0 "@noir-lang/types": "workspace:*" "@types/node": ^20.6.2 "@types/prettier": ^3 diff --git a/yarn-project/Dockerfile.prod b/yarn-project/Dockerfile.prod index 8073db145d5..b5e324a8ad3 100644 --- a/yarn-project/Dockerfile.prod +++ b/yarn-project/Dockerfile.prod @@ -11,13 +11,13 @@ WORKDIR /usr/src/yarn-project ARG COMMIT_TAG="" RUN ./scripts/version_packages.sh # Productionify. See comment in yarn-project-base/Dockerfile. -RUN yarn workspaces focus @aztec/cli @aztec/aztec-sandbox @aztec/aztec-faucet @aztec/aztec.js --production && \ - yarn cache clean && \ - rm -rf /usr/src/barretenberg/ts/src && \ - # TODO: Fix by extracting noir code out of yarn-project. - # This was just a "rm -rf ./**/src". - # Due to the mess of us needing to find noir code in noir-protocol-circuits/src/crates we have to do this... - find . -maxdepth 2 -name src -type d | grep -v "./noir-protocol-circuits" | xargs rm -rf +RUN yarn workspaces focus @aztec/cli @aztec/aztec @aztec/aztec-faucet @aztec/aztec.js --production && \ + yarn cache clean && \ + rm -rf /usr/src/barretenberg/ts/src && \ + # TODO: Fix by extracting noir code out of yarn-project. + # This was just a "rm -rf ./**/src". + # Due to the mess of us needing to find noir code in noir-protocol-circuits/src/crates we have to do this... + find . -maxdepth 2 -name src -type d | grep -v "./noir-protocol-circuits" | xargs rm -rf # We no longer need nargo. RUN rm -rf /usr/src/noir/target @@ -34,32 +34,32 @@ ENV COMMIT_TAG=$COMMIT_TAG RUN apt update && apt install -y curl && rm -rf /var/lib/apt/lists/* && apt-get clean ENV NODE_VERSION=18.19.0 RUN ARCH= && \ - dpkgArch="$(dpkg --print-architecture)" && \ - case "${dpkgArch##*-}" in \ - amd64) ARCH='x64';; \ - arm64) ARCH='arm64';; \ - *) echo "unsupported architecture"; exit 1 ;; \ - esac && \ - curl -fsSLO --compressed "https://nodejs.org/dist/v$NODE_VERSION/node-v$NODE_VERSION-linux-$ARCH.tar.gz" && \ - tar zxf "node-v$NODE_VERSION-linux-$ARCH.tar.gz" -C /usr --strip-components=1 --no-same-owner \ - --exclude "*/share/*" \ - --exclude "*/bin/corepack" \ - --exclude "*/bin/npx" \ - --exclude "*/bin/npm" \ - --exclude "*/corepack/*" \ - --exclude "*/npm/man/*" \ - --exclude "*/npm/docs/*" \ - --exclude "*/include/*" && \ - rm "node-v$NODE_VERSION-linux-$ARCH.tar.gz" && \ - node --version + dpkgArch="$(dpkg --print-architecture)" && \ + case "${dpkgArch##*-}" in \ + amd64) ARCH='x64';; \ + arm64) ARCH='arm64';; \ + *) echo "unsupported architecture"; exit 1 ;; \ + esac && \ + curl -fsSLO --compressed "https://nodejs.org/dist/v$NODE_VERSION/node-v$NODE_VERSION-linux-$ARCH.tar.gz" && \ + tar zxf "node-v$NODE_VERSION-linux-$ARCH.tar.gz" -C /usr --strip-components=1 --no-same-owner \ + --exclude "*/share/*" \ + --exclude "*/bin/corepack" \ + --exclude "*/bin/npx" \ + --exclude "*/bin/npm" \ + --exclude "*/corepack/*" \ + --exclude "*/npm/man/*" \ + --exclude "*/npm/docs/*" \ + --exclude "*/include/*" && \ + rm "node-v$NODE_VERSION-linux-$ARCH.tar.gz" && \ + node --version # Yarn is used for unboxing. -ENV YARN_VERSION=1.22.19 +ENV YARN_VERSION=1.22.19 RUN curl -fsSLO --compressed "https://yarnpkg.com/downloads/$YARN_VERSION/yarn-v$YARN_VERSION.tar.gz" && \ - mkdir -p /opt && \ - tar -xzf yarn-v$YARN_VERSION.tar.gz -C /opt/ && \ - ln -s /opt/yarn-v$YARN_VERSION/bin/yarn /usr/local/bin/yarn && \ - ln -s /opt/yarn-v$YARN_VERSION/bin/yarnpkg /usr/local/bin/yarnpkg && \ - rm yarn-v$YARN_VERSION.tar.gz && \ - yarn --version + mkdir -p /opt && \ + tar -xzf yarn-v$YARN_VERSION.tar.gz -C /opt/ && \ + ln -s /opt/yarn-v$YARN_VERSION/bin/yarn /usr/local/bin/yarn && \ + ln -s /opt/yarn-v$YARN_VERSION/bin/yarnpkg /usr/local/bin/yarnpkg && \ + rm yarn-v$YARN_VERSION.tar.gz && \ + yarn --version COPY --from=builder /usr/src /usr/src ENTRYPOINT ["/usr/bin/node"] diff --git a/yarn-project/accounts/src/defaults/index.ts b/yarn-project/accounts/src/defaults/index.ts index a194b8c848e..df6428faaac 100644 --- a/yarn-project/accounts/src/defaults/index.ts +++ b/yarn-project/accounts/src/defaults/index.ts @@ -1,7 +1,7 @@ /** * The `@aztec/accounts/defaults` export provides the base class {@link DefaultAccountContract} for implementing account contracts that use the default entrypoint payload module. * - * Read more in {@link https://docs.aztec.network/dev_docs/wallets/writing_an_account_contract | Writing an account contract}. + * Read more in {@link https://docs.aztec.network/developers/wallets/writing_an_account_contract | Writing an account contract}. * * @packageDocumentation */ diff --git a/yarn-project/accounts/src/ecdsa/index.ts b/yarn-project/accounts/src/ecdsa/index.ts index deec58e1c6b..3bd3c5d215d 100644 --- a/yarn-project/accounts/src/ecdsa/index.ts +++ b/yarn-project/accounts/src/ecdsa/index.ts @@ -6,28 +6,28 @@ */ import { AccountManager, Salt } from '@aztec/aztec.js/account'; import { AccountWallet, getWallet } from '@aztec/aztec.js/wallet'; -import { CompleteAddress, GrumpkinPrivateKey, PXE } from '@aztec/circuit-types'; +import { GrumpkinPrivateKey, PXE } from '@aztec/circuit-types'; import { AztecAddress } from '@aztec/circuits.js'; import { EcdsaAccountContract } from './account_contract.js'; -export { EcdsaAccountContract }; export { EcdsaAccountContractArtifact } from './artifact.js'; +export { EcdsaAccountContract }; /** * Creates an Account that relies on an ECDSA signing key for authentication. * @param pxe - An PXE server instance. * @param encryptionPrivateKey - Grumpkin key used for note encryption. * @param signingPrivateKey - Secp256k1 key used for signing transactions. - * @param saltOrAddress - Deployment salt or complete address if account contract is already deployed. + * @param salt - Deployment salt. */ export function getEcdsaAccount( pxe: PXE, encryptionPrivateKey: GrumpkinPrivateKey, signingPrivateKey: Buffer, - saltOrAddress?: Salt | CompleteAddress, + salt?: Salt, ): AccountManager { - return new AccountManager(pxe, encryptionPrivateKey, new EcdsaAccountContract(signingPrivateKey), saltOrAddress); + return new AccountManager(pxe, encryptionPrivateKey, new EcdsaAccountContract(signingPrivateKey), salt); } /** diff --git a/yarn-project/accounts/src/schnorr/index.ts b/yarn-project/accounts/src/schnorr/index.ts index c5b44332648..ce44cdb3eab 100644 --- a/yarn-project/accounts/src/schnorr/index.ts +++ b/yarn-project/accounts/src/schnorr/index.ts @@ -6,7 +6,7 @@ */ import { AccountManager, Salt } from '@aztec/aztec.js/account'; import { AccountWallet, getWallet } from '@aztec/aztec.js/wallet'; -import { CompleteAddress, GrumpkinPrivateKey, PXE } from '@aztec/circuit-types'; +import { GrumpkinPrivateKey, PXE } from '@aztec/circuit-types'; import { AztecAddress } from '@aztec/circuits.js'; import { SchnorrAccountContract } from './account_contract.js'; @@ -20,15 +20,15 @@ export { SchnorrAccountContractArtifact } from './artifact.js'; * @param pxe - An PXE server instance. * @param encryptionPrivateKey - Grumpkin key used for note encryption. * @param signingPrivateKey - Grumpkin key used for signing transactions. - * @param saltOrAddress - Deployment salt or complete address if account contract is already deployed. + * @param salt - Deployment salt. */ export function getSchnorrAccount( pxe: PXE, encryptionPrivateKey: GrumpkinPrivateKey, signingPrivateKey: GrumpkinPrivateKey, - saltOrAddress?: Salt | CompleteAddress, + salt?: Salt, ): AccountManager { - return new AccountManager(pxe, encryptionPrivateKey, new SchnorrAccountContract(signingPrivateKey), saltOrAddress); + return new AccountManager(pxe, encryptionPrivateKey, new SchnorrAccountContract(signingPrivateKey), salt); } /** diff --git a/yarn-project/accounts/src/single_key/index.ts b/yarn-project/accounts/src/single_key/index.ts index 85ee4254dc5..bf20f5da07c 100644 --- a/yarn-project/accounts/src/single_key/index.ts +++ b/yarn-project/accounts/src/single_key/index.ts @@ -6,7 +6,7 @@ */ import { AccountManager, Salt } from '@aztec/aztec.js/account'; import { AccountWallet, getWallet } from '@aztec/aztec.js/wallet'; -import { CompleteAddress, GrumpkinPrivateKey, PXE } from '@aztec/circuit-types'; +import { GrumpkinPrivateKey, PXE } from '@aztec/circuit-types'; import { AztecAddress } from '@aztec/circuits.js'; import { SingleKeyAccountContract } from './account_contract.js'; @@ -19,18 +19,18 @@ export { SchnorrSingleKeyAccountContractArtifact as SingleKeyAccountContractArti * Creates an Account that uses the same Grumpkin key for encryption and authentication. * @param pxe - An PXE server instance. * @param encryptionAndSigningPrivateKey - Grumpkin key used for note encryption and signing transactions. - * @param saltOrAddress - Deployment salt or complete address if account contract is already deployed. + * @param salt - Deployment salt . */ export function getSingleKeyAccount( pxe: PXE, encryptionAndSigningPrivateKey: GrumpkinPrivateKey, - saltOrAddress?: Salt | CompleteAddress, + salt?: Salt, ): AccountManager { return new AccountManager( pxe, encryptionAndSigningPrivateKey, new SingleKeyAccountContract(encryptionAndSigningPrivateKey), - saltOrAddress, + salt, ); } @@ -49,5 +49,4 @@ export function getSingleKeyWallet( return getWallet(pxe, address, new SingleKeyAccountContract(signingKey)); } -export { getSingleKeyAccount as getUnsafeSchnorrAccount }; -export { getSingleKeyWallet as getUnsafeSchnorrWallet }; +export { getSingleKeyAccount as getUnsafeSchnorrAccount, getSingleKeyWallet as getUnsafeSchnorrWallet }; diff --git a/yarn-project/acir-simulator/package.json b/yarn-project/acir-simulator/package.json index 9a3a0361d72..ccd51b8ba54 100644 --- a/yarn-project/acir-simulator/package.json +++ b/yarn-project/acir-simulator/package.json @@ -39,6 +39,7 @@ "tslib": "^2.4.0" }, "devDependencies": { + "@aztec/kv-store": "workspace:^", "@aztec/merkle-tree": "workspace:^", "@aztec/noir-contracts": "workspace:^", "@jest/globals": "^29.5.0", diff --git a/yarn-project/acir-simulator/src/acvm/deserialize.ts b/yarn-project/acir-simulator/src/acvm/deserialize.ts index d8d9866454f..a49f060e2e7 100644 --- a/yarn-project/acir-simulator/src/acvm/deserialize.ts +++ b/yarn-project/acir-simulator/src/acvm/deserialize.ts @@ -1,19 +1,21 @@ import { - BlockHeader, CallContext, ContractDeploymentData, ContractStorageRead, ContractStorageUpdateRequest, - FunctionSelector, + HEADER_LENGTH, + Header, MAX_NEW_COMMITMENTS_PER_CALL, MAX_NEW_L2_TO_L1_MSGS_PER_CALL, MAX_NEW_NULLIFIERS_PER_CALL, + MAX_NULLIFIER_KEY_VALIDATION_REQUESTS_PER_CALL, MAX_PRIVATE_CALL_STACK_LENGTH_PER_CALL, MAX_PUBLIC_CALL_STACK_LENGTH_PER_CALL, MAX_PUBLIC_DATA_READS_PER_CALL, MAX_PUBLIC_DATA_UPDATE_REQUESTS_PER_CALL, MAX_READ_REQUESTS_PER_CALL, NUM_FIELDS_PER_SHA256, + NullifierKeyValidationRequest, PrivateCircuitPublicInputs, PublicCircuitPublicInputs, RETURN_VALUES_LENGTH, @@ -23,40 +25,19 @@ import { import { AztecAddress } from '@aztec/foundation/aztec-address'; import { EthAddress } from '@aztec/foundation/eth-address'; import { Fr, Point } from '@aztec/foundation/fields'; -import { Tuple } from '@aztec/foundation/serialize'; +import { FieldReader, Tuple } from '@aztec/foundation/serialize'; import { getReturnWitness } from '@noir-lang/acvm_js'; import { ACVMField, ACVMWitness } from './acvm_types.js'; -/** - * Converts an ACVM field to a Buffer. - * @param field - The ACVM field to convert. - * @returns The Buffer. - */ -export function convertACVMFieldToBuffer(field: ACVMField): Buffer { - return Buffer.from(field.slice(2), 'hex'); -} - /** * Converts an ACVM field to a Fr. * @param field - The ACVM field to convert. * @returns The Fr. */ export function fromACVMField(field: ACVMField): Fr { - return Fr.fromBuffer(convertACVMFieldToBuffer(field)); -} - -// Utilities to read TS classes from ACVM Field arrays -// In the order that the ACVM provides them - -/** - * Converts a field to an Aztec address. - * @param fr - The field to convert. - * @returns The Aztec address. - */ -export function frToAztecAddress(fr: Fr): AztecAddress { - return new AztecAddress(fr.toBuffer()); + return Fr.fromBuffer(Buffer.from(field.slice(2), 'hex')); } /** @@ -68,88 +49,24 @@ export function frToNumber(fr: Fr): number { return Number(fr.value); } -/** - * Converts a field to a boolean. - * @param fr - The field to convert. - * @returns The boolean. - */ -export function frToBoolean(fr: Fr): boolean { - const buf = fr.toBuffer(); - return buf[buf.length - 1] !== 0; -} - /** * Extracts the return fields of a given partial witness. * @param acir - The bytecode of the function. * @param partialWitness - The witness to extract from. * @returns The return values. */ -export function extractReturnWitness(acir: Buffer, partialWitness: ACVMWitness): ACVMField[] { +export function extractReturnWitness(acir: Buffer, partialWitness: ACVMWitness): Fr[] { const returnWitness = getReturnWitness(acir, partialWitness); const sortedKeys = [...returnWitness.keys()].sort((a, b) => a - b); - return sortedKeys.map(key => returnWitness.get(key)!); + return sortedKeys.map(key => returnWitness.get(key)!).map(fromACVMField); } /** - * A utility reader for the public inputs of the ACVM generated partial witness. + * Create a reader for the public inputs of the ACVM generated partial witness. */ -export class PublicInputsReader { - private publicInputs: ACVMField[]; - - constructor(witness: ACVMWitness, acir: Buffer) { - this.publicInputs = extractReturnWitness(acir, witness); - } - - /** - * Reads a field from the public inputs. - * @returns The field. - */ - public readField(): Fr { - const acvmField = this.publicInputs.shift(); - if (!acvmField) { - throw new Error('Not enough public inputs'); - } - return fromACVMField(acvmField); - } - - /** - * Reads an array of fields from the public inputs. - * @param length - The length of the array. - * @returns The array of fields. - */ - public readFieldArray(length: N): Tuple { - const array: Fr[] = []; - for (let i = 0; i < length; i++) { - array.push(this.readField()); - } - return array as Tuple; - } - - /** - * Reads an array of SideEffects from the public inputs. - * @param length - The length of the array. - * @returns The array of SideEffects. - */ - public readSideEffectArray(length: N): Tuple { - const array: SideEffect[] = []; - for (let i = 0; i < length; i++) { - array.push(new SideEffect(this.readField(), this.readField())); - } - return array as Tuple; - } - - /** - * Reads an array of SideEffectLinkedToNoteHashes from the public inputs. - * @param length - The length of the array. - * @returns The array of SideEffectLinkedToNoteHashes. - */ - public readSideEffectLinkedToNoteHashArray(length: N): Tuple { - const array: SideEffectLinkedToNoteHash[] = []; - for (let i = 0; i < length; i++) { - array.push(new SideEffectLinkedToNoteHash(this.readField(), this.readField(), this.readField())); - } - return array as Tuple; - } +function createPublicInputsReader(witness: ACVMWitness, acir: Buffer) { + const fields = extractReturnWitness(acir, witness); + return new FieldReader(fields); } /** @@ -162,24 +79,18 @@ export function extractPrivateCircuitPublicInputs( partialWitness: ACVMWitness, acir: Buffer, ): PrivateCircuitPublicInputs { - const witnessReader = new PublicInputsReader(partialWitness, acir); - - const callContext = new CallContext( - frToAztecAddress(witnessReader.readField()), - frToAztecAddress(witnessReader.readField()), - witnessReader.readField(), - FunctionSelector.fromField(witnessReader.readField()), - frToBoolean(witnessReader.readField()), - frToBoolean(witnessReader.readField()), - frToBoolean(witnessReader.readField()), - frToNumber(witnessReader.readField()), - ); + const witnessReader = createPublicInputsReader(partialWitness, acir); + const callContext = witnessReader.readObject(CallContext); const argsHash = witnessReader.readField(); const returnValues = witnessReader.readFieldArray(RETURN_VALUES_LENGTH); - const readRequests = witnessReader.readSideEffectArray(MAX_READ_REQUESTS_PER_CALL); - const newCommitments = witnessReader.readSideEffectArray(MAX_NEW_COMMITMENTS_PER_CALL); - const newNullifiers = witnessReader.readSideEffectLinkedToNoteHashArray(MAX_NEW_NULLIFIERS_PER_CALL); + const readRequests = witnessReader.readArray(MAX_READ_REQUESTS_PER_CALL, SideEffect); + const nullifierKeyValidationRequests = witnessReader.readArray( + MAX_NULLIFIER_KEY_VALIDATION_REQUESTS_PER_CALL, + NullifierKeyValidationRequest, + ); + const newCommitments = witnessReader.readArray(MAX_NEW_COMMITMENTS_PER_CALL, SideEffect); + const newNullifiers = witnessReader.readArray(MAX_NEW_NULLIFIERS_PER_CALL, SideEffectLinkedToNoteHash); const privateCallStack = witnessReader.readFieldArray(MAX_PRIVATE_CALL_STACK_LENGTH_PER_CALL); const publicCallStack = witnessReader.readFieldArray(MAX_PUBLIC_CALL_STACK_LENGTH_PER_CALL); const newL2ToL1Msgs = witnessReader.readFieldArray(MAX_NEW_L2_TO_L1_MSGS_PER_CALL); @@ -190,16 +101,7 @@ export function extractPrivateCircuitPublicInputs( const encryptedLogPreimagesLength = witnessReader.readField(); const unencryptedLogPreimagesLength = witnessReader.readField(); - const blockHeader = new BlockHeader( - witnessReader.readField(), - witnessReader.readField(), - witnessReader.readField(), - witnessReader.readField(), - witnessReader.readField(), - Fr.ZERO, // TODO(#3441) - witnessReader.readField(), - witnessReader.readField(), - ); + const header = Header.fromFieldArray(witnessReader.readFieldArray(HEADER_LENGTH)); const contractDeploymentData = new ContractDeploymentData( new Point(witnessReader.readField(), witnessReader.readField()), @@ -217,6 +119,7 @@ export function extractPrivateCircuitPublicInputs( argsHash, returnValues, readRequests, + nullifierKeyValidationRequests, newCommitments, newNullifiers, privateCallStack, @@ -227,7 +130,7 @@ export function extractPrivateCircuitPublicInputs( unencryptedLogsHash, encryptedLogPreimagesLength, unencryptedLogPreimagesLength, - blockHeader, + header, contractDeploymentData, chainId, version, @@ -241,18 +144,9 @@ export function extractPrivateCircuitPublicInputs( * @returns The public inputs. */ export function extractPublicCircuitPublicInputs(partialWitness: ACVMWitness, acir: Buffer): PublicCircuitPublicInputs { - const witnessReader = new PublicInputsReader(partialWitness, acir); + const witnessReader = createPublicInputsReader(partialWitness, acir); - const callContext = new CallContext( - frToAztecAddress(witnessReader.readField()), - frToAztecAddress(witnessReader.readField()), - witnessReader.readField(), - FunctionSelector.fromField(witnessReader.readField()), - frToBoolean(witnessReader.readField()), - frToBoolean(witnessReader.readField()), - frToBoolean(witnessReader.readField()), - frToNumber(witnessReader.readField()), - ); + const callContext = witnessReader.readObject(CallContext); const argsHash = witnessReader.readField(); const returnValues = witnessReader.readFieldArray(RETURN_VALUES_LENGTH); @@ -275,23 +169,15 @@ export function extractPublicCircuitPublicInputs(partialWitness: ACVMWitness, ac } const publicCallStack = witnessReader.readFieldArray(MAX_PUBLIC_CALL_STACK_LENGTH_PER_CALL); - const newCommitments = witnessReader.readSideEffectArray(MAX_NEW_COMMITMENTS_PER_CALL); - const newNullifiers = witnessReader.readSideEffectLinkedToNoteHashArray(MAX_NEW_NULLIFIERS_PER_CALL); + const newCommitments = witnessReader.readArray(MAX_NEW_COMMITMENTS_PER_CALL, SideEffect); + const newNullifiers = witnessReader.readArray(MAX_NEW_NULLIFIERS_PER_CALL, SideEffectLinkedToNoteHash); const newL2ToL1Msgs = witnessReader.readFieldArray(MAX_NEW_L2_TO_L1_MSGS_PER_CALL); const unencryptedLogsHash = witnessReader.readFieldArray(NUM_FIELDS_PER_SHA256); const unencryptedLogPreimagesLength = witnessReader.readField(); - const blockHeader = new BlockHeader( - witnessReader.readField(), - witnessReader.readField(), - witnessReader.readField(), - witnessReader.readField(), - witnessReader.readField(), - Fr.ZERO, // TODO(#3441) - witnessReader.readField(), - witnessReader.readField(), - ); + const header = Header.fromFieldArray(witnessReader.readFieldArray(HEADER_LENGTH)); + const proverAddress = AztecAddress.fromField(witnessReader.readField()); return new PublicCircuitPublicInputs( @@ -309,7 +195,7 @@ export function extractPublicCircuitPublicInputs(partialWitness: ACVMWitness, ac newL2ToL1Msgs, unencryptedLogsHash, unencryptedLogPreimagesLength, - blockHeader, + header, proverAddress, ); } diff --git a/yarn-project/acir-simulator/src/acvm/oracle/oracle.ts b/yarn-project/acir-simulator/src/acvm/oracle/oracle.ts index 10a78d70875..22d3d9dd04c 100644 --- a/yarn-project/acir-simulator/src/acvm/oracle/oracle.ts +++ b/yarn-project/acir-simulator/src/acvm/oracle/oracle.ts @@ -10,6 +10,7 @@ import { ACVMField } from '../acvm_types.js'; import { frToNumber, fromACVMField } from '../deserialize.js'; import { toACVMField, + toACVMHeader, toAcvmCallPrivateStackItem, toAcvmEnqueuePublicFunctionResult, toAcvmL1ToL2MessageLoadOracleInputs, @@ -124,25 +125,14 @@ export class Oracle { return witness.toFieldArray().map(toACVMField); } - async getBlockHeader([blockNumber]: ACVMField[]): Promise { + async getHeader([blockNumber]: ACVMField[]): Promise { const parsedBlockNumber = frToNumber(fromACVMField(blockNumber)); - const blockHeader = await this.typedOracle.getBlockHeader(parsedBlockNumber); - if (!blockHeader) { + const header = await this.typedOracle.getHeader(parsedBlockNumber); + if (!header) { throw new Error(`Block header not found for block ${parsedBlockNumber}.`); } - return blockHeader.toArray().map(toACVMField); - } - - // TODO(#3564) - Nuke this oracle and inject the number directly to context - async getNullifierRootBlockNumber([nullifierTreeRoot]: ACVMField[]): Promise { - const parsedRoot = fromACVMField(nullifierTreeRoot); - - const blockNumber = await this.typedOracle.getNullifierRootBlockNumber(parsedRoot); - if (!blockNumber) { - throw new Error(`Block header not found for block ${parsedRoot}.`); - } - return toACVMField(blockNumber); + return toACVMHeader(header); } async getAuthWitness([messageHash]: ACVMField[]): Promise { @@ -167,10 +157,12 @@ export class Oracle { [numSelects]: ACVMField[], selectBy: ACVMField[], selectValues: ACVMField[], + selectComparators: ACVMField[], sortBy: ACVMField[], sortOrder: ACVMField[], [limit]: ACVMField[], [offset]: ACVMField[], + [status]: ACVMField[], [returnSize]: ACVMField[], ): Promise { const noteDatas = await this.typedOracle.getNotes( @@ -178,10 +170,12 @@ export class Oracle { +numSelects, selectBy.map(s => +s), selectValues.map(fromACVMField), + selectComparators.map(s => +s), sortBy.map(s => +s), sortOrder.map(s => +s), +limit, +offset, + +status, ); const noteLength = noteDatas?.[0]?.note.items.length ?? 0; @@ -232,8 +226,8 @@ export class Oracle { } async getL1ToL2Message([msgKey]: ACVMField[]): Promise { - const { root, ...message } = await this.typedOracle.getL1ToL2Message(fromACVMField(msgKey)); - return toAcvmL1ToL2MessageLoadOracleInputs(message, root); + const { ...message } = await this.typedOracle.getL1ToL2Message(fromACVMField(msgKey)); + return toAcvmL1ToL2MessageLoadOracleInputs(message); } async getPortalContractAddress([aztecAddress]: ACVMField[]): Promise { diff --git a/yarn-project/acir-simulator/src/acvm/oracle/typed_oracle.ts b/yarn-project/acir-simulator/src/acvm/oracle/typed_oracle.ts index 621a62db10f..ffa9ac1fbfc 100644 --- a/yarn-project/acir-simulator/src/acvm/oracle/typed_oracle.ts +++ b/yarn-project/acir-simulator/src/acvm/oracle/typed_oracle.ts @@ -2,12 +2,13 @@ import { CompleteAddress, MerkleTreeId, Note, + NoteStatus, NullifierMembershipWitness, PublicDataWitness, PublicKey, UnencryptedL2Log, } from '@aztec/circuit-types'; -import { BlockHeader, GrumpkinPrivateKey, PrivateCallStackItem, PublicCallRequest } from '@aztec/circuits.js'; +import { GrumpkinPrivateKey, Header, PrivateCallStackItem, PublicCallRequest } from '@aztec/circuits.js'; import { FunctionSelector } from '@aztec/foundation/abi'; import { AztecAddress } from '@aztec/foundation/aztec-address'; import { EthAddress } from '@aztec/foundation/eth-address'; @@ -48,7 +49,7 @@ export interface NoteData { } /** - * The partial data for L1 to L2 Messages provided by other data sources. + * The data for L1 to L2 Messages provided by other data sources. */ export interface MessageLoadOracleInputs { /** @@ -66,16 +67,6 @@ export interface MessageLoadOracleInputs { index: bigint; } -/** - * The data required by Aztec.nr to validate L1 to L2 Messages. - */ -export interface L1ToL2MessageOracleReturnData extends MessageLoadOracleInputs { - /** - * The current root of the l1 to l2 message tree. - */ - root: Fr; -} - /** * Oracle with typed parameters and typed return values. * Methods that require read and/or write will have to be implemented based on the context (public, private, or view) @@ -121,12 +112,7 @@ export abstract class TypedOracle { throw new Error('Not available.'); } - getBlockHeader(_blockNumber: number): Promise { - throw new Error('Not available.'); - } - - // TODO(#3564) - Nuke this oracle and inject the number directly to context - getNullifierRootBlockNumber(_nullifierTreeRoot: Fr): Promise { + getHeader(_blockNumber: number): Promise

{ throw new Error('Not available.'); } @@ -147,10 +133,12 @@ export abstract class TypedOracle { _numSelects: number, _selectBy: number[], _selectValues: Fr[], + _selectComparators: number[], _sortBy: number[], _sortOrder: number[], _limit: number, _offset: number, + _status: NoteStatus, ): Promise { throw new Error('Not available.'); } @@ -167,7 +155,7 @@ export abstract class TypedOracle { throw new Error('Not available.'); } - getL1ToL2Message(_msgKey: Fr): Promise { + getL1ToL2Message(_msgKey: Fr): Promise { throw new Error('Not available.'); } diff --git a/yarn-project/acir-simulator/src/acvm/serialize.ts b/yarn-project/acir-simulator/src/acvm/serialize.ts index 1877bf264d9..c2c7440d8a9 100644 --- a/yarn-project/acir-simulator/src/acvm/serialize.ts +++ b/yarn-project/acir-simulator/src/acvm/serialize.ts @@ -1,9 +1,9 @@ import { - BlockHeader, CallContext, ContractDeploymentData, FunctionData, GlobalVariables, + Header, PrivateCallStackItem, PrivateCircuitPublicInputs, PublicCallRequest, @@ -92,10 +92,10 @@ export function toACVMCallContext(callContext: CallContext): ACVMField[] { */ export function toACVMContractDeploymentData(contractDeploymentData: ContractDeploymentData): ACVMField[] { return [ - toACVMField(contractDeploymentData.deployerPublicKey.x), - toACVMField(contractDeploymentData.deployerPublicKey.y), - toACVMField(contractDeploymentData.constructorVkHash), - toACVMField(contractDeploymentData.functionTreeRoot), + toACVMField(contractDeploymentData.publicKey.x), + toACVMField(contractDeploymentData.publicKey.y), + toACVMField(contractDeploymentData.initializationHash), + toACVMField(contractDeploymentData.contractClassId), toACVMField(contractDeploymentData.contractAddressSalt), toACVMField(contractDeploymentData.portalContractAddress), ]; @@ -103,19 +103,11 @@ export function toACVMContractDeploymentData(contractDeploymentData: ContractDep /** * Converts a block header into ACVM fields. - * @param blockHeader - The block header object to convert. + * @param header - The block header object to convert. * @returns The ACVM fields. */ -export function toACVMBlockHeader(blockHeader: BlockHeader): ACVMField[] { - return [ - toACVMField(blockHeader.noteHashTreeRoot), - toACVMField(blockHeader.nullifierTreeRoot), - toACVMField(blockHeader.contractTreeRoot), - toACVMField(blockHeader.l1ToL2MessageTreeRoot), - toACVMField(blockHeader.archiveRoot), - toACVMField(blockHeader.publicDataTreeRoot), - toACVMField(blockHeader.globalVariablesHash), - ]; +export function toACVMHeader(header: Header): ACVMField[] { + return header.toFieldArray().map(toACVMField); } /** @@ -143,9 +135,10 @@ export function toACVMPublicInputs(publicInputs: PrivateCircuitPublicInputs): AC toACVMField(publicInputs.argsHash), ...publicInputs.returnValues.map(toACVMField), - ...publicInputs.readRequests.flatMap(x => x.toFieldArray()).map(toACVMField), - ...publicInputs.newCommitments.flatMap(x => x.toFieldArray()).map(toACVMField), - ...publicInputs.newNullifiers.flatMap(x => x.toFieldArray()).map(toACVMField), + ...publicInputs.readRequests.flatMap(x => x.toFields()).map(toACVMField), + ...publicInputs.nullifierKeyValidationRequests.flatMap(x => x.toFields()).map(toACVMField), + ...publicInputs.newCommitments.flatMap(x => x.toFields()).map(toACVMField), + ...publicInputs.newNullifiers.flatMap(x => x.toFields()).map(toACVMField), ...publicInputs.privateCallStackHashes.map(toACVMField), ...publicInputs.publicCallStackHashes.map(toACVMField), ...publicInputs.newL2ToL1Msgs.map(toACVMField), @@ -156,7 +149,7 @@ export function toACVMPublicInputs(publicInputs: PrivateCircuitPublicInputs): AC toACVMField(publicInputs.encryptedLogPreimagesLength), toACVMField(publicInputs.unencryptedLogPreimagesLength), - ...toACVMBlockHeader(publicInputs.blockHeader), + ...toACVMHeader(publicInputs.historicalHeader), ...toACVMContractDeploymentData(publicInputs.contractDeploymentData), @@ -199,18 +192,13 @@ export function toAcvmEnqueuePublicFunctionResult(item: PublicCallRequest): ACVM /** * Converts the result of loading messages to ACVM fields. * @param messageLoadOracleInputs - The result of loading messages to convert. - * @param l1ToL2MessageTreeRoot - The L1 to L2 message tree root * @returns The Message Oracle Fields. */ -export function toAcvmL1ToL2MessageLoadOracleInputs( - messageLoadOracleInputs: MessageLoadOracleInputs, - l1ToL2MessageTreeRoot: Fr, -): ACVMField[] { +export function toAcvmL1ToL2MessageLoadOracleInputs(messageLoadOracleInputs: MessageLoadOracleInputs): ACVMField[] { return [ ...messageLoadOracleInputs.message.map(f => toACVMField(f)), toACVMField(messageLoadOracleInputs.index), ...messageLoadOracleInputs.siblingPath.map(f => toACVMField(f)), - toACVMField(l1ToL2MessageTreeRoot), ]; } diff --git a/yarn-project/acir-simulator/src/avm/avm_context.ts b/yarn-project/acir-simulator/src/avm/avm_context.ts index 79e763f79f4..0fcb86cfde6 100644 --- a/yarn-project/acir-simulator/src/avm/avm_context.ts +++ b/yarn-project/acir-simulator/src/avm/avm_context.ts @@ -1,9 +1,11 @@ +import { AztecAddress, FunctionSelector } from '@aztec/circuits.js'; import { Fr } from '@aztec/foundation/fields'; +import { AvmExecutionEnvironment } from './avm_execution_environment.js'; import { AvmMachineState } from './avm_machine_state.js'; import { AvmMessageCallResult } from './avm_message_call_result.js'; -import { AvmStateManager } from './avm_state_manager.js'; -import { AvmInterpreter } from './interpreter/index.js'; +import { AvmInterpreterError, executeAvm } from './interpreter/index.js'; +import { AvmJournal } from './journal/journal.js'; import { decodeBytecode } from './opcodes/decode_bytecode.js'; import { Instruction } from './opcodes/index.js'; @@ -13,10 +15,14 @@ import { Instruction } from './opcodes/index.js'; * It stores a state manager */ export class AvmContext { - private stateManager: AvmStateManager; + /** Contains constant variables provided by the kernel */ + private executionEnvironment: AvmExecutionEnvironment; + /** Manages mutable state during execution - (caching, fetching) */ + private journal: AvmJournal; - constructor(stateManager: AvmStateManager) { - this.stateManager = stateManager; + constructor(executionEnvironment: AvmExecutionEnvironment, journal: AvmJournal) { + this.executionEnvironment = executionEnvironment; + this.journal = journal; } /** @@ -26,19 +32,99 @@ export class AvmContext { * - We interpret the bytecode * - We run the interpreter * - * @param contractAddress - - * @param calldata - */ - public call(contractAddress: Fr, calldata: Fr[]): AvmMessageCallResult { + async call(): Promise { // NOTE: the following is mocked as getPublicBytecode does not exist yet - // const bytecode = stateManager.journal.hostStorage.contractsDb.getBytecode(contractAddress); - const bytecode = Buffer.from('0x01000100020003'); + const selector = new FunctionSelector(0); + const bytecode = await this.journal.hostStorage.contractsDb.getBytecode( + this.executionEnvironment.address, + selector, + ); + + // This assumes that we will not be able to send messages to accounts without code + // Pending classes and instances impl details + if (!bytecode) { + throw new NoBytecodeFoundInterpreterError(this.executionEnvironment.address); + } const instructions: Instruction[] = decodeBytecode(bytecode); - const context = new AvmMachineState(calldata); - const interpreter = new AvmInterpreter(context, this.stateManager, instructions); + const machineState = new AvmMachineState(this.executionEnvironment); + return executeAvm(machineState, this.journal, instructions); + } + + /** + * Create a new forked avm context - for internal calls + */ + public newWithForkedState(): AvmContext { + const forkedState = AvmJournal.branchParent(this.journal); + return new AvmContext(this.executionEnvironment, forkedState); + } + + /** + * Create a new forked avm context - for external calls + */ + public static newWithForkedState(executionEnvironment: AvmExecutionEnvironment, journal: AvmJournal): AvmContext { + const forkedState = AvmJournal.branchParent(journal); + return new AvmContext(executionEnvironment, forkedState); + } + + /** + * Prepare a new AVM context that will be ready for an external call + * - It will fork the journal + * - It will set the correct execution Environment Variables for a call + * - Alter both address and storageAddress + * + * @param address - The contract to call + * @param executionEnvironment - The current execution environment + * @param journal - The current journal + * @returns new AvmContext instance + */ + public static prepExternalCallContext( + address: AztecAddress, + calldata: Fr[], + executionEnvironment: AvmExecutionEnvironment, + journal: AvmJournal, + ): AvmContext { + const newExecutionEnvironment = executionEnvironment.newCall(address, calldata); + const forkedState = AvmJournal.branchParent(journal); + return new AvmContext(newExecutionEnvironment, forkedState); + } + + /** + * Prepare a new AVM context that will be ready for an external static call + * - It will fork the journal + * - It will set the correct execution Environment Variables for a call + * - Alter both address and storageAddress + * + * @param address - The contract to call + * @param executionEnvironment - The current execution environment + * @param journal - The current journal + * @returns new AvmContext instance + */ + public static prepExternalStaticCallContext( + address: AztecAddress, + calldata: Fr[], + executionEnvironment: AvmExecutionEnvironment, + journal: AvmJournal, + ): AvmContext { + const newExecutionEnvironment = executionEnvironment.newStaticCall(address, calldata); + const forkedState = AvmJournal.branchParent(journal); + return new AvmContext(newExecutionEnvironment, forkedState); + } + + /** + * Merge the journal of this call with it's parent + * NOTE: this should never be called on a root context - only from within a nested call + */ + public mergeJournal() { + this.journal.mergeWithParent(); + } +} - return interpreter.run(); +class NoBytecodeFoundInterpreterError extends AvmInterpreterError { + constructor(contractAddress: AztecAddress) { + super(`No bytecode found at: ${contractAddress}`); + this.name = 'NoBytecodeFoundInterpreterError'; } } diff --git a/yarn-project/acir-simulator/src/avm/avm_execution_environment.test.ts b/yarn-project/acir-simulator/src/avm/avm_execution_environment.test.ts new file mode 100644 index 00000000000..bf792862c8d --- /dev/null +++ b/yarn-project/acir-simulator/src/avm/avm_execution_environment.test.ts @@ -0,0 +1,55 @@ +import { Fr } from '@aztec/foundation/fields'; + +import { initExecutionEnvironment } from './fixtures/index.js'; + +describe('Execution Environment', () => { + const newAddress = new Fr(123456n); + const calldata = [new Fr(1n), new Fr(2n), new Fr(3n)]; + + it('New call should fork execution environment correctly', () => { + const executionEnvironment = initExecutionEnvironment(); + const newExecutionEnvironment = executionEnvironment.newCall(newAddress, calldata); + + allTheSameExcept(executionEnvironment, newExecutionEnvironment, { + address: newAddress, + storageAddress: newAddress, + calldata, + }); + }); + + it('New delegate call should fork execution environment correctly', () => { + const executionEnvironment = initExecutionEnvironment(); + const newExecutionEnvironment = executionEnvironment.newDelegateCall(newAddress, calldata); + + allTheSameExcept(executionEnvironment, newExecutionEnvironment, { + address: newAddress, + isDelegateCall: true, + calldata, + }); + }); + + it('New static call call should fork execution environment correctly', () => { + const executionEnvironment = initExecutionEnvironment(); + const newExecutionEnvironment = executionEnvironment.newStaticCall(newAddress, calldata); + + allTheSameExcept(executionEnvironment, newExecutionEnvironment, { + address: newAddress, + storageAddress: newAddress, + isStaticCall: true, + calldata, + }); + }); +}); + +/** + * Check all properties of one object are the same, except for the specified differentProperties + */ +function allTheSameExcept(referenceObject: any, comparingObject: any, differentProperties: Record): void { + for (const key in referenceObject) { + if (Object.keys(differentProperties).includes(key)) { + expect(comparingObject[key]).toEqual(differentProperties[key]); + } else { + expect(comparingObject[key]).toEqual(referenceObject[key]); + } + } +} diff --git a/yarn-project/acir-simulator/src/avm/avm_execution_environment.ts b/yarn-project/acir-simulator/src/avm/avm_execution_environment.ts new file mode 100644 index 00000000000..6a87f62d87b --- /dev/null +++ b/yarn-project/acir-simulator/src/avm/avm_execution_environment.ts @@ -0,0 +1,93 @@ +import { GlobalVariables } from '@aztec/circuits.js'; +import { AztecAddress } from '@aztec/foundation/aztec-address'; +import { EthAddress } from '@aztec/foundation/eth-address'; +import { Fr } from '@aztec/foundation/fields'; + +/** + * Contains variables that remain constant during AVM execution + * These variables are provided by the public kernel circuit + */ +// TODO(https://github.com/AztecProtocol/aztec-packages/issues/3992): gas not implemented +export class AvmExecutionEnvironment { + constructor( + public readonly address: AztecAddress, + + public readonly storageAddress: AztecAddress, + + public readonly origin: AztecAddress, + + public readonly sender: AztecAddress, + + public readonly portal: EthAddress, + + public readonly feePerL1Gas: Fr, + + public readonly feePerL2Gas: Fr, + + public readonly feePerDaGas: Fr, + + public readonly contractCallDepth: Fr, + + public readonly globals: GlobalVariables, + + public readonly isStaticCall: boolean, + + public readonly isDelegateCall: boolean, + + public readonly calldata: Fr[], + ) {} + + public newCall(address: AztecAddress, calldata: Fr[]): AvmExecutionEnvironment { + return new AvmExecutionEnvironment( + /*address=*/ address, + /*storageAddress=*/ address, + this.origin, + this.sender, + this.portal, + this.feePerL1Gas, + this.feePerL2Gas, + this.feePerDaGas, + this.contractCallDepth, + this.globals, + this.isStaticCall, + this.isDelegateCall, + /*calldata=*/ calldata, + ); + } + + public newStaticCall(address: AztecAddress, calldata: Fr[]): AvmExecutionEnvironment { + return new AvmExecutionEnvironment( + /*address=*/ address, + /*storageAddress=*/ address, + this.origin, + this.sender, + this.portal, + this.feePerL1Gas, + this.feePerL2Gas, + this.feePerDaGas, + this.contractCallDepth, + this.globals, + /*isStaticCall=*/ true, + this.isDelegateCall, + /*calldata=*/ calldata, + ); + } + + public newDelegateCall(address: AztecAddress, calldata: Fr[]): AvmExecutionEnvironment { + return new AvmExecutionEnvironment( + /*address=*/ address, + this.storageAddress, + this.origin, + this.sender, + this.portal, + this.feePerL1Gas, + this.feePerL2Gas, + this.feePerDaGas, + this.contractCallDepth, + this.globals, + this.isStaticCall, + /*isDelegateCall=*/ true, + /*calldata=*/ calldata, + ); + } +} diff --git a/yarn-project/acir-simulator/src/avm/avm_machine_state.ts b/yarn-project/acir-simulator/src/avm/avm_machine_state.ts index 53e8599b7c3..75da070f6b5 100644 --- a/yarn-project/acir-simulator/src/avm/avm_machine_state.ts +++ b/yarn-project/acir-simulator/src/avm/avm_machine_state.ts @@ -1,16 +1,21 @@ import { Fr } from '@aztec/foundation/fields'; +import { AvmExecutionEnvironment } from './avm_execution_environment.js'; +import { TaggedMemory } from './avm_memory_types.js'; + /** * Store's data for an Avm execution frame */ export class AvmMachineState { - /** - */ - public readonly calldata: Fr[]; + /** + * Execution environment contains hard coded information that is received from the kernel + * Items like, the block header and global variables fall within this category + */ + public readonly executionEnvironment: AvmExecutionEnvironment; + private returnData: Fr[]; - // TODO: implement tagged memory - /** - */ - public memory: Fr[]; + public readonly memory: TaggedMemory; /** * When an internal_call is invoked, the internal call stack is added to with the current pc + 1 @@ -18,30 +23,35 @@ export class AvmMachineState { */ public internalCallStack: number[]; - /** - */ public pc: number; - /** - */ + public callStack: number[]; /** * If an instruction triggers a halt, then it ends execution of the VM */ public halted: boolean; + /** + * Signifies if the execution has reverted ( due to a revert instruction ) + */ + public reverted: boolean; /** * Create a new avm context - * @param calldata - + * @param executionEnvironment - Machine context that is passed to the avm */ - constructor(calldata: Fr[]) { - this.calldata = calldata; + constructor(executionEnvironment: AvmExecutionEnvironment) { this.returnData = []; - this.memory = []; + this.memory = new TaggedMemory(); this.internalCallStack = []; this.pc = 0; this.callStack = []; this.halted = false; + this.reverted = false; + + this.executionEnvironment = executionEnvironment; } /** @@ -53,41 +63,7 @@ export class AvmMachineState { Object.freeze(returnData); } - /** - */ public getReturnData(): Fr[] { return this.returnData; } - - /** - - * @param offset - - */ - public readMemory(offset: number): Fr { - // TODO: check offset is within bounds - return this.memory[offset] ?? Fr.ZERO; - } - - /** - - * @param offset - - * @param size - - */ - public readMemoryChunk(offset: number, size: number): Fr[] { - // TODO: bounds -> initialise to 0 - return this.memory.slice(offset, offset + size); - } - - /** - - * @param offset - - * @param value - - */ - public writeMemory(offset: number, value: Fr): void { - this.memory[offset] = value; - } - - /** - - * @param offset - - * @param values - - */ - public writeMemoryChunk(offset: number, values: Fr[]): void { - this.memory.splice(offset, values.length, ...values); - } } diff --git a/yarn-project/acir-simulator/src/avm/avm_memory_types.test.ts b/yarn-project/acir-simulator/src/avm/avm_memory_types.test.ts new file mode 100644 index 00000000000..6df75930fe1 --- /dev/null +++ b/yarn-project/acir-simulator/src/avm/avm_memory_types.test.ts @@ -0,0 +1,22 @@ +import { Field, Uint8 } from './avm_memory_types.js'; + +// TODO: complete +describe('Uint8', () => { + it('Unsigned 8 max value', () => { + expect(new Uint8(255).toBigInt()).toEqual(255n); + }); + + it('Unsigned 8 bit add', () => { + expect(new Uint8(50).add(new Uint8(20))).toEqual(new Uint8(70)); + }); + + it('Unsigned 8 bit add wraps', () => { + expect(new Uint8(200).add(new Uint8(100))).toEqual(new Uint8(44)); + }); +}); + +describe('Field', () => { + it('Add correctly without wrapping', () => { + expect(new Field(27).add(new Field(48))).toEqual(new Field(75)); + }); +}); diff --git a/yarn-project/acir-simulator/src/avm/avm_memory_types.ts b/yarn-project/acir-simulator/src/avm/avm_memory_types.ts new file mode 100644 index 00000000000..40c02b65463 --- /dev/null +++ b/yarn-project/acir-simulator/src/avm/avm_memory_types.ts @@ -0,0 +1,313 @@ +import { Fr } from '@aztec/foundation/fields'; + +import { strict as assert } from 'assert'; + +export abstract class MemoryValue { + public abstract add(rhs: MemoryValue): MemoryValue; + public abstract sub(rhs: MemoryValue): MemoryValue; + public abstract mul(rhs: MemoryValue): MemoryValue; + public abstract div(rhs: MemoryValue): MemoryValue; + + public abstract equals(rhs: MemoryValue): boolean; + public abstract lt(rhs: MemoryValue): boolean; + + // We need this to be able to build an instance of the subclasses. + public abstract build(n: bigint): MemoryValue; + + // Use sparingly. + public abstract toBigInt(): bigint; + + // To field + public toFr(): Fr { + return new Fr(this.toBigInt()); + } +} + +export abstract class IntegralValue extends MemoryValue { + public abstract shl(rhs: IntegralValue): IntegralValue; + public abstract shr(rhs: IntegralValue): IntegralValue; + public abstract and(rhs: IntegralValue): IntegralValue; + public abstract or(rhs: IntegralValue): IntegralValue; + public abstract xor(rhs: IntegralValue): IntegralValue; + public abstract not(): IntegralValue; +} + +// TODO: Optimize calculation of mod, etc. Can only do once per class? +abstract class UnsignedInteger extends IntegralValue { + private readonly bitmask: bigint; + private readonly mod: bigint; + + protected constructor(private n: bigint, private bits: bigint) { + super(); + assert(bits > 0); + // x % 2^n == x & (2^n - 1) + this.mod = 1n << bits; + this.bitmask = this.mod - 1n; + assert(n < this.mod); + } + + public abstract build(n: bigint): UnsignedInteger; + + public add(rhs: UnsignedInteger): UnsignedInteger { + assert(this.bits == rhs.bits); + return this.build((this.n + rhs.n) & this.bitmask); + } + + public sub(rhs: UnsignedInteger): UnsignedInteger { + assert(this.bits == rhs.bits); + const res: bigint = this.n - rhs.n; + return this.build(res >= 0 ? res : res + this.mod); + } + + public mul(rhs: UnsignedInteger): UnsignedInteger { + assert(this.bits == rhs.bits); + return this.build((this.n * rhs.n) & this.bitmask); + } + + public div(rhs: UnsignedInteger): UnsignedInteger { + assert(this.bits == rhs.bits); + return this.build(this.n / rhs.n); + } + + // No sign extension. + public shr(rhs: UnsignedInteger): UnsignedInteger { + assert(this.bits == rhs.bits); + // Note that this.n is > 0 by class invariant. + return this.build(this.n >> rhs.n); + } + + public shl(rhs: UnsignedInteger): UnsignedInteger { + assert(this.bits == rhs.bits); + return this.build((this.n << rhs.n) & this.bitmask); + } + + public and(rhs: UnsignedInteger): UnsignedInteger { + assert(this.bits == rhs.bits); + return this.build(this.n & rhs.n); + } + + public or(rhs: UnsignedInteger): UnsignedInteger { + assert(this.bits == rhs.bits); + return this.build(this.n | rhs.n); + } + + public xor(rhs: UnsignedInteger): UnsignedInteger { + assert(this.bits == rhs.bits); + return this.build(this.n ^ rhs.n); + } + + public not(): UnsignedInteger { + return this.build(~this.n & this.bitmask); + } + + public equals(rhs: UnsignedInteger): boolean { + assert(this.bits == rhs.bits); + return this.n === rhs.n; + } + + public lt(rhs: UnsignedInteger): boolean { + assert(this.bits == rhs.bits); + return this.n < rhs.n; + } + + public toBigInt(): bigint { + return this.n; + } +} + +export class Uint8 extends UnsignedInteger { + constructor(n: number | bigint) { + super(BigInt(n), 8n); + } + + public build(n: bigint): Uint8 { + return new Uint8(n); + } +} + +export class Uint16 extends UnsignedInteger { + constructor(n: number | bigint) { + super(BigInt(n), 16n); + } + + public build(n: bigint): Uint16 { + return new Uint16(n); + } +} + +export class Uint32 extends UnsignedInteger { + constructor(n: number | bigint) { + super(BigInt(n), 32n); + } + + public build(n: bigint): Uint32 { + return new Uint32(n); + } +} + +export class Uint64 extends UnsignedInteger { + constructor(n: number | bigint) { + super(BigInt(n), 64n); + } + + public build(n: bigint): Uint64 { + return new Uint64(n); + } +} + +export class Uint128 extends UnsignedInteger { + constructor(n: number | bigint) { + super(BigInt(n), 128n); + } + + public build(n: bigint): Uint128 { + return new Uint128(n); + } +} + +export class Field extends MemoryValue { + public static readonly MODULUS: bigint = Fr.MODULUS; + private readonly rep: Fr; + + constructor(v: number | bigint | Fr) { + super(); + this.rep = new Fr(v); + } + + public build(n: bigint): Field { + return new Field(n); + } + + public add(rhs: Field): Field { + return new Field(this.rep.add(rhs.rep)); + } + + public sub(rhs: Field): Field { + return new Field(this.rep.sub(rhs.rep)); + } + + public mul(rhs: Field): Field { + return new Field(this.rep.mul(rhs.rep)); + } + + public div(rhs: Field): Field { + return new Field(this.rep.div(rhs.rep)); + } + + public equals(rhs: Field): boolean { + return this.rep.equals(rhs.rep); + } + + public lt(rhs: Field): boolean { + return this.rep.lt(rhs.rep); + } + + public toBigInt(): bigint { + return this.rep.toBigInt(); + } +} + +export enum TypeTag { + UNINITIALIZED, + UINT8, + UINT16, + UINT32, + UINT64, + UINT128, + FIELD, + INVALID, +} + +// TODO: Consider automatic conversion when getting undefined values. +export class TaggedMemory { + // FIXME: memory should be 2^32, but TS doesn't allow for arrays that big. + static readonly MAX_MEMORY_SIZE = Number(1n << 31n); // 1n << 32n + private _mem: MemoryValue[]; + + constructor() { + // Initialize memory size, but leave all entries undefined. + this._mem = new Array(TaggedMemory.MAX_MEMORY_SIZE); + } + + public get(offset: number): MemoryValue { + return this.getAs(offset); + } + + public getAs(offset: number): T { + assert(offset < TaggedMemory.MAX_MEMORY_SIZE); + const word = this._mem[offset]; + return word as T; + } + + public getSlice(offset: number, size: number): MemoryValue[] { + assert(offset < TaggedMemory.MAX_MEMORY_SIZE); + return this._mem.slice(offset, offset + size); + } + + public getSliceAs(offset: number, size: number): T[] { + assert(offset < TaggedMemory.MAX_MEMORY_SIZE); + return this._mem.slice(offset, offset + size) as T[]; + } + + public getSliceTags(offset: number, size: number): TypeTag[] { + assert(offset < TaggedMemory.MAX_MEMORY_SIZE); + return this._mem.slice(offset, offset + size).map(TaggedMemory.getTag); + } + + public set(offset: number, v: MemoryValue) { + assert(offset < TaggedMemory.MAX_MEMORY_SIZE); + this._mem[offset] = v; + } + + public setSlice(offset: number, vs: MemoryValue[]) { + assert(offset < TaggedMemory.MAX_MEMORY_SIZE); + this._mem.splice(offset, vs.length, ...vs); + } + + public getTag(offset: number): TypeTag { + return TaggedMemory.getTag(this._mem[offset]); + } + + // TODO: this might be slow, but I don't want to have the types know of their tags. + // It might be possible to have a map. + public static getTag(v: MemoryValue | undefined): TypeTag { + let tag = TypeTag.INVALID; + + if (v === undefined) { + tag = TypeTag.UNINITIALIZED; + } else if (v instanceof Field) { + tag = TypeTag.FIELD; + } else if (v instanceof Uint8) { + tag = TypeTag.UINT8; + } else if (v instanceof Uint16) { + tag = TypeTag.UINT16; + } else if (v instanceof Uint32) { + tag = TypeTag.UINT32; + } else if (v instanceof Uint64) { + tag = TypeTag.UINT64; + } else if (v instanceof Uint128) { + tag = TypeTag.UINT128; + } + + return tag; + } + + // Truncates the value to fit the type. + public static integralFromTag(v: bigint, tag: TypeTag): IntegralValue { + v = BigInt(v); // FIXME: not sure why this cast is needed, but this errors otherwise + switch (tag) { + case TypeTag.UINT8: + return new Uint8(v & ((1n << 8n) - 1n)); + case TypeTag.UINT16: + return new Uint16(v & ((1n << 16n) - 1n)); + case TypeTag.UINT32: + return new Uint32(v & ((1n << 32n) - 1n)); + case TypeTag.UINT64: + return new Uint64(v & ((1n << 64n) - 1n)); + case TypeTag.UINT128: + return new Uint128(v & ((1n << 128n) - 1n)); + default: + throw new Error(`${TypeTag[tag]} is not a valid integral type.`); + } + } +} diff --git a/yarn-project/acir-simulator/src/avm/avm_message_call_result.ts b/yarn-project/acir-simulator/src/avm/avm_message_call_result.ts index e2290e4fa6d..713a306b3e9 100644 --- a/yarn-project/acir-simulator/src/avm/avm_message_call_result.ts +++ b/yarn-project/acir-simulator/src/avm/avm_message_call_result.ts @@ -4,9 +4,8 @@ import { Fr } from '@aztec/foundation/fields'; * AVM message call result. */ export class AvmMessageCallResult { - /** - */ public readonly reverted: boolean; - /** - */ + public readonly revertReason: Error | undefined; /** .- */ public readonly output: Fr[]; diff --git a/yarn-project/acir-simulator/src/avm/avm_state_manager.ts b/yarn-project/acir-simulator/src/avm/avm_state_manager.ts deleted file mode 100644 index b5396accc7a..00000000000 --- a/yarn-project/acir-simulator/src/avm/avm_state_manager.ts +++ /dev/null @@ -1,45 +0,0 @@ -import { BlockHeader } from '@aztec/circuits.js'; - -import { AvmJournal, HostStorage } from './journal/index.js'; - -/** - * The Avm State Manager is the interpreter's interface to the node's state - * It creates revertible views into the node state and manages the current call's journal - */ -export class AvmStateManager { - /** - */ - public readonly blockHeader: BlockHeader; - - /** - * Journal keeps track of pending state changes - */ - public readonly journal: AvmJournal; - - constructor(blockHeader: BlockHeader, journal: AvmJournal) { - this.blockHeader = blockHeader; - this.journal = journal; - } - - /** - * Create a base state root manager - * - this should be created by the highest level item where the state - * can be reverted - * @param blockHeader - - * @param hostStorage - An immutable view into the node db - * @returns Avm State Manager - */ - public static rootStateManager(blockHeader: BlockHeader, hostStorage: HostStorage): AvmStateManager { - const journal = AvmJournal.rootJournal(hostStorage); - return new AvmStateManager(blockHeader, journal); - } - - /** - * Avm State - * @param parent - Avm state manager with a forked journal - * @returns - */ - public static forkStateManager(parent: AvmStateManager): AvmStateManager { - const journal = AvmJournal.branchParent(parent.journal); - return new AvmStateManager(parent.blockHeader, journal); - } -} diff --git a/yarn-project/acir-simulator/src/avm/fixtures/index.ts b/yarn-project/acir-simulator/src/avm/fixtures/index.ts index cfab7bc47b8..4c0d889063c 100644 --- a/yarn-project/acir-simulator/src/avm/fixtures/index.ts +++ b/yarn-project/acir-simulator/src/avm/fixtures/index.ts @@ -1 +1,40 @@ // Place large AVM text fixtures in here +import { GlobalVariables } from '@aztec/circuits.js'; +import { AztecAddress } from '@aztec/foundation/aztec-address'; +import { EthAddress } from '@aztec/foundation/eth-address'; +import { Fr } from '@aztec/foundation/fields'; + +import { AvmExecutionEnvironment } from '../avm_execution_environment.js'; + +/** + * Create an empty instance of the Execution Environment where all values are zero, unless overriden in the overrides object + */ +export function initExecutionEnvironment(overrides?: Partial): AvmExecutionEnvironment { + return new AvmExecutionEnvironment( + overrides?.address ?? AztecAddress.zero(), + overrides?.storageAddress ?? AztecAddress.zero(), + overrides?.origin ?? AztecAddress.zero(), + overrides?.sender ?? AztecAddress.zero(), + overrides?.portal ?? EthAddress.ZERO, + overrides?.feePerL1Gas ?? Fr.zero(), + overrides?.feePerL2Gas ?? Fr.zero(), + overrides?.feePerDaGas ?? Fr.zero(), + overrides?.contractCallDepth ?? Fr.zero(), + overrides?.globals ?? GlobalVariables.empty(), + overrides?.isStaticCall ?? false, + overrides?.isDelegateCall ?? false, + overrides?.calldata ?? [], + ); +} + +/** + * Create an empty instance of the Execution Environment where all values are zero, unless overriden in the overrides object + */ +export function initGlobalVariables(overrides?: Partial): GlobalVariables { + return new GlobalVariables( + overrides?.chainId ?? Fr.zero(), + overrides?.version ?? Fr.zero(), + overrides?.blockNumber ?? Fr.zero(), + overrides?.timestamp ?? Fr.zero(), + ); +} diff --git a/yarn-project/acir-simulator/src/avm/index.test.ts b/yarn-project/acir-simulator/src/avm/index.test.ts index 1db8306447a..f085bc0bc00 100644 --- a/yarn-project/acir-simulator/src/avm/index.test.ts +++ b/yarn-project/acir-simulator/src/avm/index.test.ts @@ -1,18 +1,20 @@ import { Fr } from '@aztec/foundation/fields'; +import { AvmTestContractArtifact } from '@aztec/noir-contracts'; import { mock } from 'jest-mock-extended'; import { AvmMachineState } from './avm_machine_state.js'; -import { AvmStateManager } from './avm_state_manager.js'; -import { AvmInterpreter } from './interpreter/interpreter.js'; +import { initExecutionEnvironment } from './fixtures/index.js'; +import { executeAvm } from './interpreter/interpreter.js'; +import { AvmJournal } from './journal/journal.js'; import { decodeBytecode } from './opcodes/decode_bytecode.js'; import { encodeToBytecode } from './opcodes/encode_to_bytecode.js'; import { Opcode } from './opcodes/opcodes.js'; describe('avm', () => { - it('Should execute bytecode', () => { + it('Should execute bytecode that performs basic addition', async () => { const calldata: Fr[] = [new Fr(1), new Fr(2)]; - const stateManager = mock(); + const journal = mock(); // Construct bytecode const calldataCopyArgs = [0, 2, 0]; @@ -28,9 +30,8 @@ describe('avm', () => { const instructions = decodeBytecode(fullBytecode); // Execute instructions - const context = new AvmMachineState(calldata); - const interpreter = new AvmInterpreter(context, stateManager, instructions); - const avmReturnData = interpreter.run(); + const context = new AvmMachineState(initExecutionEnvironment({ calldata })); + const avmReturnData = await executeAvm(context, journal, instructions); expect(avmReturnData.reverted).toBe(false); @@ -38,4 +39,27 @@ describe('avm', () => { expect(returnData.length).toBe(1); expect(returnData).toEqual([new Fr(3)]); }); + + describe('testing transpiled Noir contracts', () => { + it('Should execute contract function that performs addition', async () => { + const calldata: Fr[] = [new Fr(1), new Fr(2)]; + const journal = mock(); + + // Get contract function artifact + const addArtifact = AvmTestContractArtifact.functions.find(f => f.name === 'avm_addArgsReturn')!; + + // Decode bytecode into instructions + const instructions = decodeBytecode(Buffer.from(addArtifact.bytecode, 'base64')); + + // Execute instructions + const context = new AvmMachineState(initExecutionEnvironment({ calldata })); + const avmReturnData = await executeAvm(context, journal, instructions); + + expect(avmReturnData.reverted).toBe(false); + + const returnData = avmReturnData.output; + expect(returnData.length).toBe(1); + expect(returnData).toEqual([new Fr(3)]); + }); + }); }); diff --git a/yarn-project/acir-simulator/src/avm/index.ts b/yarn-project/acir-simulator/src/avm/index.ts deleted file mode 100644 index 6a102a6cd57..00000000000 --- a/yarn-project/acir-simulator/src/avm/index.ts +++ /dev/null @@ -1,3 +0,0 @@ -export * from './avm_machine_state.js'; -export * from './avm_context.js'; -export * from './avm_state_manager.js'; diff --git a/yarn-project/acir-simulator/src/avm/interpreter/interpreter.test.ts b/yarn-project/acir-simulator/src/avm/interpreter/interpreter.test.ts index 7f252ec739c..e5055cdf903 100644 --- a/yarn-project/acir-simulator/src/avm/interpreter/interpreter.test.ts +++ b/yarn-project/acir-simulator/src/avm/interpreter/interpreter.test.ts @@ -1,47 +1,49 @@ import { Fr } from '@aztec/foundation/fields'; -import { mock } from 'jest-mock-extended'; +import { MockProxy, mock } from 'jest-mock-extended'; import { AvmMachineState } from '../avm_machine_state.js'; -import { AvmStateManager } from '../avm_state_manager.js'; +import { initExecutionEnvironment } from '../fixtures/index.js'; +import { AvmJournal } from '../journal/journal.js'; import { Add } from '../opcodes/arithmetic.js'; import { Jump, Return } from '../opcodes/control_flow.js'; import { Instruction } from '../opcodes/instruction.js'; import { CalldataCopy } from '../opcodes/memory.js'; -import { AvmInterpreter, InvalidProgramCounterError } from './interpreter.js'; +import { InvalidProgramCounterError, executeAvm } from './interpreter.js'; describe('interpreter', () => { - it('Should execute a series of instructions', () => { + let journal: MockProxy; + + beforeEach(() => { + journal = mock(); + }); + + it('Should execute a series of instructions', async () => { const calldata: Fr[] = [new Fr(1), new Fr(2)]; - const stateManager = mock(); const instructions: Instruction[] = [ - new CalldataCopy(/*cdOffset=*/ 0, /*copySize=*/ 2, /*destOffset=*/ 0), - new Add(/*aOffset=*/ 0, /*bOffset=*/ 1, /*destOffset=*/ 2), + new CalldataCopy(/*cdOffset=*/ 0, /*copySize=*/ 2, /*dstOffset=*/ 0), + new Add(/*aOffset=*/ 0, /*bOffset=*/ 1, /*dstOffset=*/ 2), new Return(/*returnOffset=*/ 2, /*copySize=*/ 1), ]; - const context = new AvmMachineState(calldata); - const interpreter = new AvmInterpreter(context, stateManager, instructions); - const avmReturnData = interpreter.run(); + const machineState = new AvmMachineState(initExecutionEnvironment({ calldata })); + const avmReturnData = await executeAvm(machineState, journal, instructions); expect(avmReturnData.reverted).toBe(false); expect(avmReturnData.revertReason).toBeUndefined(); expect(avmReturnData.output).toEqual([new Fr(3)]); }); - it('Should revert with an invalid jump', () => { + it('Should revert with an invalid jump', async () => { const calldata: Fr[] = []; - const stateManager = mock(); const invalidJumpDestination = 22; const instructions: Instruction[] = [new Jump(invalidJumpDestination)]; - const context = new AvmMachineState(calldata); - const interpreter = new AvmInterpreter(context, stateManager, instructions); - - const avmReturnData = interpreter.run(); + const machineState = new AvmMachineState(initExecutionEnvironment({ calldata })); + const avmReturnData = await executeAvm(machineState, journal, instructions); expect(avmReturnData.reverted).toBe(true); expect(avmReturnData.revertReason).toBeInstanceOf(InvalidProgramCounterError); diff --git a/yarn-project/acir-simulator/src/avm/interpreter/interpreter.ts b/yarn-project/acir-simulator/src/avm/interpreter/interpreter.ts index f3970ade43f..86385078cac 100644 --- a/yarn-project/acir-simulator/src/avm/interpreter/interpreter.ts +++ b/yarn-project/acir-simulator/src/avm/interpreter/interpreter.ts @@ -1,69 +1,49 @@ -import { Fr } from '@aztec/foundation/fields'; - import { strict as assert } from 'assert'; import { AvmMachineState } from '../avm_machine_state.js'; import { AvmMessageCallResult } from '../avm_message_call_result.js'; -import { AvmStateManager } from '../avm_state_manager.js'; +import { AvmJournal } from '../journal/index.js'; import { Instruction } from '../opcodes/index.js'; /** - * Avm Interpreter - * - * Executes an Avm context + * Run the avm + * @returns bool - successful execution will return true + * - reverted execution will return false + * - any other panic will throw */ -export class AvmInterpreter { - private instructions: Instruction[] = []; - private machineState: AvmMachineState; - private stateManager: AvmStateManager; - - constructor(machineState: AvmMachineState, stateManager: AvmStateManager, instructions: Instruction[]) { - this.machineState = machineState; - this.stateManager = stateManager; - this.instructions = instructions; - } - - /** - * Run the avm - * @returns bool - successful execution will return true - * - reverted execution will return false - * - any other panic will throw - */ - run(): AvmMessageCallResult { - assert(this.instructions.length > 0); +export async function executeAvm( + machineState: AvmMachineState, + journal: AvmJournal, + instructions: Instruction[] = [], +): Promise { + assert(instructions.length > 0); - try { - while (!this.machineState.halted) { - const instruction = this.instructions[this.machineState.pc]; - assert(!!instruction); // This should never happen + try { + while (!machineState.halted) { + const instruction = instructions[machineState.pc]; + assert(!!instruction); // This should never happen - instruction.execute(this.machineState, this.stateManager); + await instruction.execute(machineState, journal); - if (this.machineState.pc >= this.instructions.length) { - throw new InvalidProgramCounterError(this.machineState.pc, /*max=*/ this.instructions.length); - } + if (machineState.pc >= instructions.length) { + throw new InvalidProgramCounterError(machineState.pc, /*max=*/ instructions.length); } + } - const returnData = this.machineState.getReturnData(); - return AvmMessageCallResult.success(returnData); - } catch (_e) { - if (!(_e instanceof AvmInterpreterError)) { - throw _e; - } + const returnData = machineState.getReturnData(); + if (machineState.reverted) { + return AvmMessageCallResult.revert(returnData); + } - const revertReason: AvmInterpreterError = _e; - const revertData = this.machineState.getReturnData(); - return AvmMessageCallResult.revert(revertData, revertReason); + return AvmMessageCallResult.success(returnData); + } catch (_e) { + if (!(_e instanceof AvmInterpreterError)) { + throw _e; } - } - /** - * Get the return data from avm execution - * TODO: this should fail if the code has not been executed - * - maybe move the return in run into a variable and track it - */ - returnData(): Fr[] { - return this.machineState.getReturnData(); + const revertReason: AvmInterpreterError = _e; + const revertData = machineState.getReturnData(); + return AvmMessageCallResult.revert(revertData, revertReason); } } @@ -71,8 +51,8 @@ export class AvmInterpreter { * Avm-specific errors should derive from this */ export abstract class AvmInterpreterError extends Error { - constructor(message: string) { - super(message); + constructor(message: string, ...rest: any[]) { + super(message, ...rest); this.name = 'AvmInterpreterError'; } } diff --git a/yarn-project/acir-simulator/src/avm/journal/host_storage.ts b/yarn-project/acir-simulator/src/avm/journal/host_storage.ts index fcd9d27f722..b12e14f9c20 100644 --- a/yarn-project/acir-simulator/src/avm/journal/host_storage.ts +++ b/yarn-project/acir-simulator/src/avm/journal/host_storage.ts @@ -6,11 +6,10 @@ import { CommitmentsDB, PublicContractsDB, PublicStateDB } from '../../index.js' * A wrapper around the node dbs */ export class HostStorage { - /** - */ public readonly publicStateDb: PublicStateDB; - /** - */ + public readonly contractsDb: PublicContractsDB; - /** - */ + public readonly commitmentsDb: CommitmentsDB; constructor(publicStateDb: PublicStateDB, contractsDb: PublicContractsDB, commitmentsDb: CommitmentsDB) { diff --git a/yarn-project/acir-simulator/src/avm/journal/journal.test.ts b/yarn-project/acir-simulator/src/avm/journal/journal.test.ts index a11a3b38743..1588712e1c7 100644 --- a/yarn-project/acir-simulator/src/avm/journal/journal.test.ts +++ b/yarn-project/acir-simulator/src/avm/journal/journal.test.ts @@ -29,7 +29,7 @@ describe('journal', () => { journal.writeStorage(contractAddress, key, value); const journalUpdates: JournalData = journal.flush(); - expect(journalUpdates.storageWrites.get(contractAddress)?.get(key)).toEqual(value); + expect(journalUpdates.currentStorageValue.get(contractAddress.toBigInt())?.get(key.toBigInt())).toEqual(value); }); it('When reading from storage, should check the parent first', async () => { @@ -61,7 +61,7 @@ describe('journal', () => { expect(cachedResult).toEqual(cachedValue); }); - it('When reading from storage, should check the cache first', async () => { + it('When reading from storage, should check the cache first, and be appended to read/write journal', async () => { // Store a different value in storage vs the cache, and make sure the cache is returned const contractAddress = new Fr(1); const key = new Fr(2); @@ -80,20 +80,30 @@ describe('journal', () => { // Get the storage value const cachedResult = await journal.readStorage(contractAddress, key); expect(cachedResult).toEqual(cachedValue); + + // We expect the journal to store the access in [storedVal, cachedVal] - [time0, time1] + const { storageReads, storageWrites }: JournalData = journal.flush(); + const contractReads = storageReads.get(contractAddress.toBigInt()); + const keyReads = contractReads?.get(key.toBigInt()); + expect(keyReads).toEqual([storedValue, cachedValue]); + + const contractWrites = storageWrites.get(contractAddress.toBigInt()); + const keyWrites = contractWrites?.get(key.toBigInt()); + expect(keyWrites).toEqual([cachedValue]); }); }); describe('UTXOs', () => { it('Should maintain commitments', () => { const utxo = new Fr(1); - journal.writeCommitment(utxo); + journal.writeNoteHash(utxo); const journalUpdates = journal.flush(); - expect(journalUpdates.newCommitments).toEqual([utxo]); + expect(journalUpdates.newNoteHashes).toEqual([utxo]); }); it('Should maintain l1 messages', () => { - const utxo = new Fr(1); + const utxo = [new Fr(1)]; journal.writeL1Message(utxo); const journalUpdates = journal.flush(); @@ -123,17 +133,23 @@ describe('journal', () => { const valueT1 = new Fr(2); const commitment = new Fr(10); const commitmentT1 = new Fr(20); + const logs = [new Fr(1), new Fr(2)]; + const logsT1 = [new Fr(3), new Fr(4)]; journal.writeStorage(contractAddress, key, value); - journal.writeCommitment(commitment); - journal.writeL1Message(commitment); + await journal.readStorage(contractAddress, key); + journal.writeNoteHash(commitment); + journal.writeLog(logs); + journal.writeL1Message(logs); journal.writeNullifier(commitment); const journal1 = new AvmJournal(journal.hostStorage, journal); - journal.writeStorage(contractAddress, key, valueT1); - journal.writeCommitment(commitmentT1); - journal.writeL1Message(commitmentT1); - journal.writeNullifier(commitmentT1); + journal1.writeStorage(contractAddress, key, valueT1); + await journal1.readStorage(contractAddress, key); + journal1.writeNoteHash(commitmentT1); + journal1.writeLog(logsT1); + journal1.writeL1Message(logsT1); + journal1.writeNullifier(commitmentT1); journal1.mergeWithParent(); @@ -143,8 +159,21 @@ describe('journal', () => { // Check that the UTXOs are merged const journalUpdates: JournalData = journal.flush(); - expect(journalUpdates.newCommitments).toEqual([commitment, commitmentT1]); - expect(journalUpdates.newL1Messages).toEqual([commitment, commitmentT1]); + + // Check storage reads order is preserved upon merge + // We first read value from t0, then value from t1 + const contractReads = journalUpdates.storageReads.get(contractAddress.toBigInt()); + const slotReads = contractReads?.get(key.toBigInt()); + expect(slotReads).toEqual([value, valueT1]); + + // We first write value from t0, then value from t1 + const contractWrites = journalUpdates.storageReads.get(contractAddress.toBigInt()); + const slotWrites = contractWrites?.get(key.toBigInt()); + expect(slotWrites).toEqual([value, valueT1]); + + expect(journalUpdates.newNoteHashes).toEqual([commitment, commitmentT1]); + expect(journalUpdates.newLogs).toEqual([logs, logsT1]); + expect(journalUpdates.newL1Messages).toEqual([logs, logsT1]); expect(journalUpdates.newNullifiers).toEqual([commitment, commitmentT1]); }); diff --git a/yarn-project/acir-simulator/src/avm/journal/journal.ts b/yarn-project/acir-simulator/src/avm/journal/journal.ts index cb69b3c33ef..699d6b93f00 100644 --- a/yarn-project/acir-simulator/src/avm/journal/journal.ts +++ b/yarn-project/acir-simulator/src/avm/journal/journal.ts @@ -7,14 +7,18 @@ import { HostStorage } from './host_storage.js'; * Data held within the journal */ export type JournalData = { - /** - */ - newCommitments: Fr[]; - /** - */ - newL1Messages: Fr[]; - /** - */ + newNoteHashes: Fr[]; newNullifiers: Fr[]; + newL1Messages: Fr[][]; + newLogs: Fr[][]; + /** contract address -\> key -\> value */ - storageWrites: Map>; + currentStorageValue: Map>; + + /** contract address -\> key -\> value[] (stored in order of access) */ + storageWrites: Map>; + /** contract address -\> key -\> value[] (stored in order of access) */ + storageReads: Map>; }; /** @@ -30,20 +34,20 @@ export class AvmJournal { public readonly hostStorage: HostStorage; // Reading state - must be tracked for vm execution - // contract address -> key -> value - // TODO(https://github.com/AztecProtocol/aztec-packages/issues/3999) - private storageReads: Map> = new Map(); + // contract address -> key -> value[] (array stored in order of reads) + private storageReads: Map> = new Map(); + private storageWrites: Map> = new Map(); // New written state - private newCommitments: Fr[] = []; + private newNoteHashes: Fr[] = []; private newNullifiers: Fr[] = []; - private newL1Message: Fr[] = []; - // New Substrate + // New Substate + private newL1Messages: Fr[][] = []; private newLogs: Fr[][] = []; // contract address -> key -> value - private storageWrites: Map> = new Map(); + private currentStorageValue: Map> = new Map(); private parentJournal: AvmJournal | undefined; @@ -76,12 +80,15 @@ export class AvmJournal { * @param value - */ public writeStorage(contractAddress: Fr, key: Fr, value: Fr) { - let contractMap = this.storageWrites.get(contractAddress); + let contractMap = this.currentStorageValue.get(contractAddress.toBigInt()); if (!contractMap) { contractMap = new Map(); - this.storageWrites.set(contractAddress, contractMap); + this.currentStorageValue.set(contractAddress.toBigInt(), contractMap); } - contractMap.set(key, value); + contractMap.set(key.toBigInt(), value); + + // We want to keep track of all performed writes in the journal + this.journalWrite(contractAddress, key, value); } /** @@ -92,38 +99,68 @@ export class AvmJournal { * @param key - * @returns current value */ - public readStorage(contractAddress: Fr, key: Fr): Promise { - const cachedValue = this.storageWrites.get(contractAddress)?.get(key); - if (cachedValue) { - return Promise.resolve(cachedValue); + public async readStorage(contractAddress: Fr, key: Fr): Promise { + // - We first try this journal's storage cache ( if written to before in this call frame ) + // - Then we try the parent journal's storage cache ( if it exists ) ( written to earlier in this block ) + // - Finally we try the host storage ( a trip to the database ) + + // Do not early return as we want to keep track of reads in this.storageReads + let value = this.currentStorageValue.get(contractAddress.toBigInt())?.get(key.toBigInt()); + if (!value && this.parentJournal) { + value = await this.parentJournal?.readStorage(contractAddress, key); } - if (this.parentJournal) { - return this.parentJournal?.readStorage(contractAddress, key); + if (!value) { + value = await this.hostStorage.publicStateDb.storageRead(contractAddress, key); } - return this.hostStorage.publicStateDb.storageRead(contractAddress, key); + + this.journalRead(contractAddress, key, value); + return Promise.resolve(value); } - /** - - * @param commitment - + /** + * We want to keep track of all performed reads in the journal + * This information is hinted to the avm circuit + + * @param contractAddress - + * @param key - + * @param value - */ - public writeCommitment(commitment: Fr) { - this.newCommitments.push(commitment); + journalUpdate(map: Map>, contractAddress: Fr, key: Fr, value: Fr): void { + let contractMap = map.get(contractAddress.toBigInt()); + if (!contractMap) { + contractMap = new Map>(); + map.set(contractAddress.toBigInt(), contractMap); + } + + let accessArray = contractMap.get(key.toBigInt()); + if (!accessArray) { + accessArray = new Array(); + contractMap.set(key.toBigInt(), accessArray); + } + accessArray.push(value); } - /** - - * @param message - - */ - public writeL1Message(message: Fr) { - this.newL1Message.push(message); + // Create an instance of journalUpdate that appends to the read array + private journalRead = this.journalUpdate.bind(this, this.storageReads); + // Create an instance of journalUpdate that appends to the writes array + private journalWrite = this.journalUpdate.bind(this, this.storageWrites); + + public writeNoteHash(noteHash: Fr) { + this.newNoteHashes.push(noteHash); + } + + public writeL1Message(message: Fr[]) { + this.newL1Messages.push(message); } - /** - - * @param nullifier - - */ public writeNullifier(nullifier: Fr) { this.newNullifiers.push(nullifier); } + public writeLog(log: Fr[]) { + this.newLogs.push(log); + } + /** * Merge Journal into parent * - Utxo objects are concatenated @@ -134,33 +171,40 @@ export class AvmJournal { throw new RootJournalCannotBeMerged(); } - const incomingFlush = this.flush(); - // Merge UTXOs - this.parentJournal.newCommitments = this.parentJournal.newCommitments.concat(incomingFlush.newCommitments); - this.parentJournal.newL1Message = this.parentJournal.newL1Message.concat(incomingFlush.newL1Messages); - this.parentJournal.newNullifiers = this.parentJournal.newNullifiers.concat(incomingFlush.newNullifiers); + this.parentJournal.newNoteHashes = this.parentJournal.newNoteHashes.concat(this.newNoteHashes); + this.parentJournal.newL1Messages = this.parentJournal.newL1Messages.concat(this.newL1Messages); + this.parentJournal.newNullifiers = this.parentJournal.newNullifiers.concat(this.newNullifiers); + this.parentJournal.newLogs = this.parentJournal.newLogs.concat(this.newLogs); // Merge Public State - mergeContractMaps(this.parentJournal.storageWrites, incomingFlush.storageWrites); + mergeCurrentValueMaps(this.parentJournal.currentStorageValue, this.currentStorageValue); + + // Merge storage read and write journals + mergeContractJournalMaps(this.parentJournal.storageReads, this.storageReads); + mergeContractJournalMaps(this.parentJournal.storageWrites, this.storageWrites); } - /** Access the current state of the journal + /** + * Access the current state of the journal * - * @returns a JournalData object that can be used to write to the storage + * @returns a JournalData object */ public flush(): JournalData { return { - newCommitments: this.newCommitments, - newL1Messages: this.newL1Message, + newNoteHashes: this.newNoteHashes, newNullifiers: this.newNullifiers, + newL1Messages: this.newL1Messages, + newLogs: this.newLogs, + currentStorageValue: this.currentStorageValue, + storageReads: this.storageReads, storageWrites: this.storageWrites, }; } } /** - * Merges two contract maps together + * Merges two contract current value together * Where childMap keys will take precedent over the hostMap * The assumption being that the child map is created at a later time * And thus contains more up to date information @@ -168,24 +212,56 @@ export class AvmJournal { * @param hostMap - The map to be merged into * @param childMap - The map to be merged from */ -function mergeContractMaps(hostMap: Map>, childMap: Map>) { +function mergeCurrentValueMaps(hostMap: Map>, childMap: Map>) { for (const [key, value] of childMap) { const map1Value = hostMap.get(key); if (!map1Value) { hostMap.set(key, value); } else { - mergeStorageMaps(map1Value, value); + mergeStorageCurrentValueMaps(map1Value, value); } } } /** - * * @param hostMap - The map to be merge into * @param childMap - The map to be merged from */ -function mergeStorageMaps(hostMap: Map, childMap: Map) { +function mergeStorageCurrentValueMaps(hostMap: Map, childMap: Map) { for (const [key, value] of childMap) { hostMap.set(key, value); } } + +/** + * Merges two contract journalling maps together + * For read maps, we just append the childMap arrays into the host map arrays, as the order is important + * + * @param hostMap - The map to be merged into + * @param childMap - The map to be merged from + */ +function mergeContractJournalMaps(hostMap: Map>, childMap: Map>) { + for (const [key, value] of childMap) { + const map1Value = hostMap.get(key); + if (!map1Value) { + hostMap.set(key, value); + } else { + mergeStorageJournalMaps(map1Value, value); + } + } +} + +/** + * @param hostMap - The map to be merge into + * @param childMap - The map to be merged from + */ +function mergeStorageJournalMaps(hostMap: Map, childMap: Map) { + for (const [key, value] of childMap) { + const readArr = hostMap.get(key); + if (!readArr) { + hostMap.set(key, value); + } else { + readArr?.concat(value); + } + } +} diff --git a/yarn-project/acir-simulator/src/avm/opcodes/.eslintrc.cjs b/yarn-project/acir-simulator/src/avm/opcodes/.eslintrc.cjs new file mode 100644 index 00000000000..b972e1a3de3 --- /dev/null +++ b/yarn-project/acir-simulator/src/avm/opcodes/.eslintrc.cjs @@ -0,0 +1,8 @@ +const baseConfig = require('@aztec/foundation/eslint'); +module.exports = { + ...baseConfig, + rules: { + ...baseConfig.rules, + 'require-await': 'off', + }, +}; diff --git a/yarn-project/acir-simulator/src/avm/opcodes/accrued_substate.test.ts b/yarn-project/acir-simulator/src/avm/opcodes/accrued_substate.test.ts new file mode 100644 index 00000000000..fd24b23eda4 --- /dev/null +++ b/yarn-project/acir-simulator/src/avm/opcodes/accrued_substate.test.ts @@ -0,0 +1,89 @@ +import { mock } from 'jest-mock-extended'; + +import { AvmMachineState } from '../avm_machine_state.js'; +import { Field } from '../avm_memory_types.js'; +import { initExecutionEnvironment } from '../fixtures/index.js'; +import { HostStorage } from '../journal/host_storage.js'; +import { AvmJournal } from '../journal/journal.js'; +import { EmitNoteHash, EmitNullifier, EmitUnencryptedLog, SendL2ToL1Message } from './accrued_substate.js'; +import { StaticCallStorageAlterError } from './storage.js'; + +describe('Accrued Substate', () => { + let journal: AvmJournal; + let machineState: AvmMachineState; + + beforeEach(() => { + const hostStorage = mock(); + journal = new AvmJournal(hostStorage); + machineState = new AvmMachineState(initExecutionEnvironment()); + }); + + it('Should append a new note hash correctly', async () => { + const value = new Field(69n); + machineState.memory.set(0, value); + + await new EmitNoteHash(0).execute(machineState, journal); + + const journalState = journal.flush(); + const expected = [value.toFr()]; + expect(journalState.newNoteHashes).toEqual(expected); + }); + + it('Should append a new nullifier correctly', async () => { + const value = new Field(69n); + machineState.memory.set(0, value); + + await new EmitNullifier(0).execute(machineState, journal); + + const journalState = journal.flush(); + const expected = [value.toFr()]; + expect(journalState.newNullifiers).toEqual(expected); + }); + + it('Should append unencrypted logs correctly', async () => { + const startOffset = 0; + + const values = [new Field(69n), new Field(420n), new Field(Field.MODULUS - 1n)]; + machineState.memory.setSlice(0, values); + + const length = values.length; + + await new EmitUnencryptedLog(startOffset, length).execute(machineState, journal); + + const journalState = journal.flush(); + const expected = values.map(v => v.toFr()); + expect(journalState.newLogs).toEqual([expected]); + }); + + it('Should append l1 to l2 messages correctly', async () => { + const startOffset = 0; + + const values = [new Field(69n), new Field(420n), new Field(Field.MODULUS - 1n)]; + machineState.memory.setSlice(0, values); + + const length = values.length; + + await new SendL2ToL1Message(startOffset, length).execute(machineState, journal); + + const journalState = journal.flush(); + const expected = values.map(v => v.toFr()); + expect(journalState.newLogs).toEqual([expected]); + }); + + it('All substate instructions should fail within a static call', async () => { + const executionEnvironment = initExecutionEnvironment({ isStaticCall: true }); + machineState = new AvmMachineState(executionEnvironment); + + const instructions = [ + new EmitNoteHash(0), + new EmitNullifier(0), + new EmitUnencryptedLog(0, 1), + new SendL2ToL1Message(0, 1), + ]; + + for (const instruction of instructions) { + const inst = () => instruction.execute(machineState, journal); + await expect(inst()).rejects.toThrowError(StaticCallStorageAlterError); + } + }); +}); diff --git a/yarn-project/acir-simulator/src/avm/opcodes/accrued_substate.ts b/yarn-project/acir-simulator/src/avm/opcodes/accrued_substate.ts new file mode 100644 index 00000000000..de54edaa0c7 --- /dev/null +++ b/yarn-project/acir-simulator/src/avm/opcodes/accrued_substate.ts @@ -0,0 +1,84 @@ +import { AvmMachineState } from '../avm_machine_state.js'; +import { AvmJournal } from '../journal/journal.js'; +import { Instruction } from './instruction.js'; +import { StaticCallStorageAlterError } from './storage.js'; + +export class EmitNoteHash extends Instruction { + static type: string = 'EMITNOTEHASH'; + static numberOfOperands = 1; + + constructor(private noteHashOffset: number) { + super(); + } + + async execute(machineState: AvmMachineState, journal: AvmJournal): Promise { + if (machineState.executionEnvironment.isStaticCall) { + throw new StaticCallStorageAlterError(); + } + + const noteHash = machineState.memory.get(this.noteHashOffset).toFr(); + journal.writeNoteHash(noteHash); + + this.incrementPc(machineState); + } +} + +export class EmitNullifier extends Instruction { + static type: string = 'EMITNULLIFIER'; + static numberOfOperands = 1; + + constructor(private nullifierOffset: number) { + super(); + } + + async execute(machineState: AvmMachineState, journal: AvmJournal): Promise { + if (machineState.executionEnvironment.isStaticCall) { + throw new StaticCallStorageAlterError(); + } + + const nullifier = machineState.memory.get(this.nullifierOffset).toFr(); + journal.writeNullifier(nullifier); + + this.incrementPc(machineState); + } +} + +export class EmitUnencryptedLog extends Instruction { + static type: string = 'EMITUNENCRYPTEDLOG'; + static numberOfOperands = 2; + + constructor(private logOffset: number, private logSize: number) { + super(); + } + + async execute(machineState: AvmMachineState, journal: AvmJournal): Promise { + if (machineState.executionEnvironment.isStaticCall) { + throw new StaticCallStorageAlterError(); + } + + const log = machineState.memory.getSlice(this.logOffset, this.logSize).map(f => f.toFr()); + journal.writeLog(log); + + this.incrementPc(machineState); + } +} + +export class SendL2ToL1Message extends Instruction { + static type: string = 'EMITUNENCRYPTEDLOG'; + static numberOfOperands = 2; + + constructor(private msgOffset: number, private msgSize: number) { + super(); + } + + async execute(machineState: AvmMachineState, journal: AvmJournal): Promise { + if (machineState.executionEnvironment.isStaticCall) { + throw new StaticCallStorageAlterError(); + } + + const msg = machineState.memory.getSlice(this.msgOffset, this.msgSize).map(f => f.toFr()); + journal.writeLog(msg); + + this.incrementPc(machineState); + } +} diff --git a/yarn-project/acir-simulator/src/avm/opcodes/arithmetic.test.ts b/yarn-project/acir-simulator/src/avm/opcodes/arithmetic.test.ts index 76a802231a3..a9911f8d82d 100644 --- a/yarn-project/acir-simulator/src/avm/opcodes/arithmetic.test.ts +++ b/yarn-project/acir-simulator/src/avm/opcodes/arithmetic.test.ts @@ -1,108 +1,108 @@ -import { Fr } from '@aztec/foundation/fields'; - -import { mock } from 'jest-mock-extended'; +import { MockProxy, mock } from 'jest-mock-extended'; import { AvmMachineState } from '../avm_machine_state.js'; -import { AvmStateManager } from '../avm_state_manager.js'; +import { Field } from '../avm_memory_types.js'; +import { initExecutionEnvironment } from '../fixtures/index.js'; +import { AvmJournal } from '../journal/journal.js'; import { Add, Div, Mul, Sub } from './arithmetic.js'; describe('Arithmetic Instructions', () => { let machineState: AvmMachineState; - let stateManager = mock(); + let journal: MockProxy; - beforeEach(() => { - machineState = new AvmMachineState([]); - stateManager = mock(); + beforeEach(async () => { + machineState = new AvmMachineState(initExecutionEnvironment()); + journal = mock(); }); describe('Add', () => { - it('Should add correctly over Fr type', () => { - const a = new Fr(1n); - const b = new Fr(2n); + it('Should add correctly over field elements', async () => { + const a = new Field(1n); + const b = new Field(2n); - machineState.writeMemory(0, a); - machineState.writeMemory(1, b); + machineState.memory.set(0, a); + machineState.memory.set(1, b); - new Add(0, 1, 2).execute(machineState, stateManager); + await new Add(0, 1, 2).execute(machineState, journal); - const expected = new Fr(3n); - const actual = machineState.readMemory(2); + const expected = new Field(3n); + const actual = machineState.memory.get(2); expect(actual).toEqual(expected); }); - it('Should wrap around on addition', () => { - const a = new Fr(1n); - const b = new Fr(Fr.MODULUS - 1n); + it('Should wrap around on addition', async () => { + const a = new Field(1n); + const b = new Field(Field.MODULUS - 1n); - machineState.writeMemory(0, a); - machineState.writeMemory(1, b); + machineState.memory.set(0, a); + machineState.memory.set(1, b); - new Add(0, 1, 2).execute(machineState, stateManager); + await new Add(0, 1, 2).execute(machineState, journal); - const expected = new Fr(0n); - const actual = machineState.readMemory(3); + const expected = new Field(0n); + const actual = machineState.memory.get(2); expect(actual).toEqual(expected); }); }); describe('Sub', () => { - it('Should subtract correctly over Fr type', () => { - const a = new Fr(1n); - const b = new Fr(2n); + it('Should subtract correctly over field elements', async () => { + const a = new Field(1n); + const b = new Field(2n); - machineState.writeMemory(0, a); - machineState.writeMemory(1, b); + machineState.memory.set(0, a); + machineState.memory.set(1, b); - new Sub(0, 1, 2).execute(machineState, stateManager); + await new Sub(0, 1, 2).execute(machineState, journal); - const expected = new Fr(Fr.MODULUS - 1n); - const actual = machineState.readMemory(2); + const expected = new Field(Field.MODULUS - 1n); + const actual = machineState.memory.get(2); expect(actual).toEqual(expected); }); }); describe('Mul', () => { - it('Should multiply correctly over Fr type', () => { - const a = new Fr(2n); - const b = new Fr(3n); + it('Should multiply correctly over field elements', async () => { + const a = new Field(2n); + const b = new Field(3n); - machineState.writeMemory(0, a); - machineState.writeMemory(1, b); + machineState.memory.set(0, a); + machineState.memory.set(1, b); - new Mul(0, 1, 2).execute(machineState, stateManager); + await new Mul(0, 1, 2).execute(machineState, journal); - const expected = new Fr(6n); - const actual = machineState.readMemory(2); + const expected = new Field(6n); + const actual = machineState.memory.get(2); expect(actual).toEqual(expected); }); - it('Should wrap around on multiplication', () => { - const a = new Fr(2n); - const b = new Fr(Fr.MODULUS / 2n - 1n); + it('Should wrap around on multiplication', async () => { + const a = new Field(2n); + const b = new Field(Field.MODULUS / 2n - 1n); - machineState.writeMemory(0, a); - machineState.writeMemory(1, b); + machineState.memory.set(0, a); + machineState.memory.set(1, b); - new Mul(0, 1, 2).execute(machineState, stateManager); + await new Mul(0, 1, 2).execute(machineState, journal); - const expected = new Fr(Fr.MODULUS - 3n); - const actual = machineState.readMemory(2); + const expected = new Field(Field.MODULUS - 3n); + const actual = machineState.memory.get(2); expect(actual).toEqual(expected); }); }); describe('Div', () => { - it('Should perform field division', () => { - const a = new Fr(2n); - const b = new Fr(3n); + it('Should perform field division', async () => { + const a = new Field(2n); + const b = new Field(3n); - machineState.writeMemory(0, a); - machineState.writeMemory(1, b); + machineState.memory.set(0, a); + machineState.memory.set(1, b); - new Div(0, 1, 2).execute(machineState, stateManager); + await new Div(0, 1, 2).execute(machineState, journal); // Note - const actual = machineState.readMemory(2); + const actual = machineState.memory.get(2); const recovered = actual.mul(b); expect(recovered).toEqual(a); }); diff --git a/yarn-project/acir-simulator/src/avm/opcodes/arithmetic.ts b/yarn-project/acir-simulator/src/avm/opcodes/arithmetic.ts index 94df9713c83..0b1910f0dd7 100644 --- a/yarn-project/acir-simulator/src/avm/opcodes/arithmetic.ts +++ b/yarn-project/acir-simulator/src/avm/opcodes/arithmetic.ts @@ -1,84 +1,78 @@ -import { Fr } from '@aztec/foundation/fields'; - import { AvmMachineState } from '../avm_machine_state.js'; -import { AvmStateManager } from '../avm_state_manager.js'; +import { AvmJournal } from '../journal/index.js'; import { Instruction } from './instruction.js'; -/** -*/ export class Add extends Instruction { static type: string = 'ADD'; static numberOfOperands = 3; - constructor(private aOffset: number, private bOffset: number, private destOffset: number) { + constructor(private aOffset: number, private bOffset: number, private dstOffset: number) { super(); } - execute(machineState: AvmMachineState, _stateManager: AvmStateManager): void { - const a = machineState.readMemory(this.aOffset); - const b = machineState.readMemory(this.bOffset); + async execute(machineState: AvmMachineState, _journal: AvmJournal): Promise { + const a = machineState.memory.get(this.aOffset); + const b = machineState.memory.get(this.bOffset); const dest = a.add(b); - machineState.writeMemory(this.destOffset, dest); + machineState.memory.set(this.dstOffset, dest); this.incrementPc(machineState); } } -/** -*/ export class Sub extends Instruction { static type: string = 'SUB'; static numberOfOperands = 3; - constructor(private aOffset: number, private bOffset: number, private destOffset: number) { + constructor(private aOffset: number, private bOffset: number, private dstOffset: number) { super(); } - execute(machineState: AvmMachineState, _stateManager: AvmStateManager): void { - const a = machineState.readMemory(this.aOffset); - const b = machineState.readMemory(this.bOffset); + async execute(machineState: AvmMachineState, _journal: AvmJournal): Promise { + const a = machineState.memory.get(this.aOffset); + const b = machineState.memory.get(this.bOffset); const dest = a.sub(b); - machineState.writeMemory(this.destOffset, dest); + machineState.memory.set(this.dstOffset, dest); this.incrementPc(machineState); } } -/** -*/ export class Mul extends Instruction { static type: string = 'MUL'; static numberOfOperands = 3; - constructor(private aOffset: number, private bOffset: number, private destOffset: number) { + constructor(private aOffset: number, private bOffset: number, private dstOffset: number) { super(); } - execute(machineState: AvmMachineState, _stateManager: AvmStateManager): void { - const a: Fr = machineState.readMemory(this.aOffset); - const b: Fr = machineState.readMemory(this.bOffset); + async execute(machineState: AvmMachineState, _journal: AvmJournal): Promise { + const a = machineState.memory.get(this.aOffset); + const b = machineState.memory.get(this.bOffset); const dest = a.mul(b); - machineState.writeMemory(this.destOffset, dest); + machineState.memory.set(this.dstOffset, dest); this.incrementPc(machineState); } } -/** -*/ export class Div extends Instruction { static type: string = 'DIV'; static numberOfOperands = 3; - constructor(private aOffset: number, private bOffset: number, private destOffset: number) { + constructor(private aOffset: number, private bOffset: number, private dstOffset: number) { super(); } - execute(machineState: AvmMachineState, _stateManager: AvmStateManager): void { - const a: Fr = machineState.readMemory(this.aOffset); - const b: Fr = machineState.readMemory(this.bOffset); + async execute(machineState: AvmMachineState, _journal: AvmJournal): Promise { + const a = machineState.memory.get(this.aOffset); + const b = machineState.memory.get(this.bOffset); const dest = a.div(b); - machineState.writeMemory(this.destOffset, dest); + machineState.memory.set(this.dstOffset, dest); this.incrementPc(machineState); } diff --git a/yarn-project/acir-simulator/src/avm/opcodes/bitwise.test.ts b/yarn-project/acir-simulator/src/avm/opcodes/bitwise.test.ts index e1de7356041..ae8ce802724 100644 --- a/yarn-project/acir-simulator/src/avm/opcodes/bitwise.test.ts +++ b/yarn-project/acir-simulator/src/avm/opcodes/bitwise.test.ts @@ -1,166 +1,169 @@ -import { Fr } from '@aztec/foundation/fields'; - -import { mock } from 'jest-mock-extended'; +import { MockProxy, mock } from 'jest-mock-extended'; import { AvmMachineState } from '../avm_machine_state.js'; -import { AvmStateManager } from '../avm_state_manager.js'; -import { - And, - /*Not,*/ - Or, - Shl, - Shr, - Xor, -} from './bitwise.js'; +import { TypeTag, Uint16, Uint32 } from '../avm_memory_types.js'; +import { initExecutionEnvironment } from '../fixtures/index.js'; +import { AvmJournal } from '../journal/journal.js'; +import { And, Not, Or, Shl, Shr, Xor } from './bitwise.js'; describe('Bitwise instructions', () => { let machineState: AvmMachineState; - let stateManager = mock(); + let journal: MockProxy; - beforeEach(() => { - machineState = new AvmMachineState([]); - stateManager = mock(); + beforeEach(async () => { + machineState = new AvmMachineState(initExecutionEnvironment()); + journal = mock(); }); - it('Should AND correctly over Fr type', () => { - const a = new Fr(0b11111110010011100100n); - const b = new Fr(0b11100100111001001111n); - - machineState.writeMemory(0, a); - machineState.writeMemory(1, b); + it('Should AND correctly over integral types', async () => { + machineState.memory.set(0, new Uint32(0b11111110010011100100n)); + machineState.memory.set(1, new Uint32(0b11100100111001001111n)); - new And(0, 1, 2).execute(machineState, stateManager); + await new And(TypeTag.UINT32, 0, 1, 2).execute(machineState, journal); - const expected = new Fr(0b11100100010001000100n); - const actual = machineState.readMemory(2); - expect(actual).toEqual(expected); + const actual = machineState.memory.get(2); + expect(actual).toEqual(new Uint32(0b11100100010001000100n)); }); - it('Should OR correctly over Fr type', () => { - const a = new Fr(0b11111110010011100100n); - const b = new Fr(0b11100100111001001111n); + it('Should OR correctly over integral types', async () => { + const a = new Uint32(0b11111110010011100100n); + const b = new Uint32(0b11100100111001001111n); - machineState.writeMemory(0, a); - machineState.writeMemory(1, b); + machineState.memory.set(0, a); + machineState.memory.set(1, b); - new Or(0, 1, 2).execute(machineState, stateManager); + await new Or(TypeTag.UINT32, 0, 1, 2).execute(machineState, journal); - const expected = new Fr(0b11111110111011101111n); - const actual = machineState.readMemory(2); + const expected = new Uint32(0b11111110111011101111n); + const actual = machineState.memory.get(2); expect(actual).toEqual(expected); }); - it('Should XOR correctly over Fr type', () => { - const a = new Fr(0b11111110010011100100n); - const b = new Fr(0b11100100111001001111n); + it('Should XOR correctly over integral types', async () => { + const a = new Uint32(0b11111110010011100100n); + const b = new Uint32(0b11100100111001001111n); - machineState.writeMemory(0, a); - machineState.writeMemory(1, b); + machineState.memory.set(0, a); + machineState.memory.set(1, b); - new Xor(0, 1, 2).execute(machineState, stateManager); + await new Xor(TypeTag.UINT32, 0, 1, 2).execute(machineState, journal); - const expected = new Fr(0b00011010101010101011n); - const actual = machineState.readMemory(2); + const expected = new Uint32(0b00011010101010101011n); + const actual = machineState.memory.get(2); expect(actual).toEqual(expected); }); describe('SHR', () => { - it('Should shift correctly 0 positions over Fr type', () => { - const a = new Fr(0b11111110010011100100n); - const b = new Fr(0n); + it('Should shift correctly 0 positions over integral types', async () => { + const a = new Uint32(0b11111110010011100100n); + const b = new Uint32(0n); - machineState.writeMemory(0, a); - machineState.writeMemory(1, b); + machineState.memory.set(0, a); + machineState.memory.set(1, b); - new Shr(0, 1, 2).execute(machineState, stateManager); + await new Shr(TypeTag.UINT32, 0, 1, 2).execute(machineState, journal); const expected = a; - const actual = machineState.readMemory(2); + const actual = machineState.memory.get(2); expect(actual).toEqual(expected); }); - it('Should shift correctly 2 positions over Fr type', () => { - const a = new Fr(0b11111110010011100100n); - const b = new Fr(2n); + it('Should shift correctly 2 positions over integral types', async () => { + const a = new Uint32(0b11111110010011100100n); + const b = new Uint32(2n); - machineState.writeMemory(0, a); - machineState.writeMemory(1, b); + machineState.memory.set(0, a); + machineState.memory.set(1, b); - new Shr(0, 1, 2).execute(machineState, stateManager); + await new Shr(TypeTag.UINT32, 0, 1, 2).execute(machineState, journal); - const expected = new Fr(0b00111111100100111001n); - const actual = machineState.readMemory(2); + const expected = new Uint32(0b00111111100100111001n); + const actual = machineState.memory.get(2); expect(actual).toEqual(expected); }); - it('Should shift correctly 19 positions over Fr type', () => { - const a = new Fr(0b11111110010011100100n); - const b = new Fr(19n); + it('Should shift correctly 19 positions over integral types', async () => { + const a = new Uint32(0b11111110010011100100n); + const b = new Uint32(19n); - machineState.writeMemory(0, a); - machineState.writeMemory(1, b); + machineState.memory.set(0, a); + machineState.memory.set(1, b); - new Shr(0, 1, 2).execute(machineState, stateManager); + await new Shr(TypeTag.UINT32, 0, 1, 2).execute(machineState, journal); - const expected = new Fr(0b01n); - const actual = machineState.readMemory(2); + const expected = new Uint32(0b01n); + const actual = machineState.memory.get(2); expect(actual).toEqual(expected); }); }); describe('SHL', () => { - it('Should shift correctly 0 positions over Fr type', () => { - const a = new Fr(0b11111110010011100100n); - const b = new Fr(0n); + it('Should shift correctly 0 positions over integral types', async () => { + const a = new Uint32(0b11111110010011100100n); + const b = new Uint32(0n); - machineState.writeMemory(0, a); - machineState.writeMemory(1, b); + machineState.memory.set(0, a); + machineState.memory.set(1, b); - new Shl(0, 1, 2).execute(machineState, stateManager); + await new Shl(TypeTag.UINT32, 0, 1, 2).execute(machineState, journal); const expected = a; - const actual = machineState.readMemory(2); + const actual = machineState.memory.get(2); expect(actual).toEqual(expected); }); - it('Should shift correctly 2 positions over Fr type', () => { - const a = new Fr(0b11111110010011100100n); - const b = new Fr(2n); + it('Should shift correctly 2 positions over integral types', async () => { + const a = new Uint32(0b11111110010011100100n); + const b = new Uint32(2n); - machineState.writeMemory(0, a); - machineState.writeMemory(1, b); + machineState.memory.set(0, a); + machineState.memory.set(1, b); - new Shl(0, 1, 2).execute(machineState, stateManager); + await new Shl(TypeTag.UINT32, 0, 1, 2).execute(machineState, journal); - const expected = new Fr(0b1111111001001110010000n); - const actual = machineState.readMemory(2); + const expected = new Uint32(0b1111111001001110010000n); + const actual = machineState.memory.get(2); expect(actual).toEqual(expected); }); - // it('Should shift correctly over bit limit over Fr type', () => { - // const a = new Fr(0b11111110010011100100n); - // const b = new Fr(19n); + it('Should shift correctly over bit limit over integral types', async () => { + const a = new Uint16(0b1110010011100111n); + const b = new Uint16(17n); + + machineState.memory.set(0, a); + machineState.memory.set(1, b); - // machineState.writeMemory(0, a); - // machineState.writeMemory(1, b); + await new Shl(TypeTag.UINT16, 0, 1, 2).execute(machineState, journal); + + const expected = new Uint16(0n); + const actual = machineState.memory.get(2); + expect(actual).toEqual(expected); + }); - // new Shl(0, 1, 2).execute(machineState, stateManager); + it('Should truncate when shifting over bit size over integral types', async () => { + const a = new Uint16(0b1110010011100111n); + const b = new Uint16(2n); - // const expected = new Fr(0b01n); - // const actual = machineState.readMemory(2); - // expect(actual).toEqual(expected); - // }); + machineState.memory.set(0, a); + machineState.memory.set(1, b); + + await new Shl(TypeTag.UINT16, 0, 1, 2).execute(machineState, journal); + + const expected = new Uint16(0b1001001110011100n); + const actual = machineState.memory.get(2); + expect(actual).toEqual(expected); + }); }); - // it('Should NOT correctly over Fr type', () => { - // const a = new Fr(0b11111110010011100100n); + it('Should NOT correctly over integral types', async () => { + const a = new Uint16(0b0110010011100100n); - // machineState.writeMemory(0, a); + machineState.memory.set(0, a); - // new Not(0, 1).execute(machineState, stateManager); + await new Not(TypeTag.UINT16, 0, 1).execute(machineState, journal); - // const expected = new Fr(0b00000001101100011011n); // high bits! - // const actual = machineState.readMemory(1); - // expect(actual).toEqual(expected); - // }); + const expected = new Uint16(0b1001101100011011n); // high bits! + const actual = machineState.memory.get(1); + expect(actual).toEqual(expected); + }); }); diff --git a/yarn-project/acir-simulator/src/avm/opcodes/bitwise.ts b/yarn-project/acir-simulator/src/avm/opcodes/bitwise.ts index ff7802aac61..e439a8bd447 100644 --- a/yarn-project/acir-simulator/src/avm/opcodes/bitwise.ts +++ b/yarn-project/acir-simulator/src/avm/opcodes/bitwise.ts @@ -1,130 +1,128 @@ -import { Fr } from '@aztec/foundation/fields'; - import { AvmMachineState } from '../avm_machine_state.js'; -import { AvmStateManager } from '../avm_state_manager.js'; +import { IntegralValue, TypeTag } from '../avm_memory_types.js'; +import { AvmJournal } from '../journal/index.js'; import { Instruction } from './instruction.js'; -/** - */ export class And extends Instruction { static type: string = 'AND'; static numberOfOperands = 3; - constructor(private aOffset: number, private bOffset: number, private destOffset: number) { + constructor(private inTag: TypeTag, private aOffset: number, private bOffset: number, private dstOffset: number) { super(); } - execute(machineState: AvmMachineState, _stateManager: AvmStateManager): void { - const a: Fr = machineState.readMemory(this.aOffset); - const b: Fr = machineState.readMemory(this.bOffset); + async execute(machineState: AvmMachineState, _journal: AvmJournal): Promise { + Instruction.checkTags(machineState, this.inTag, this.aOffset, this.bOffset); + + const a = machineState.memory.getAs(this.aOffset); + const b = machineState.memory.getAs(this.bOffset); - const dest = new Fr(a.toBigInt() & b.toBigInt()); - machineState.writeMemory(this.destOffset, dest); + const res = a.and(b); + machineState.memory.set(this.dstOffset, res); this.incrementPc(machineState); } } -/** - */ export class Or extends Instruction { static type: string = 'OR'; static numberOfOperands = 3; - constructor(private aOffset: number, private bOffset: number, private destOffset: number) { + constructor(private inTag: TypeTag, private aOffset: number, private bOffset: number, private dstOffset: number) { super(); } - execute(machineState: AvmMachineState, _stateManager: AvmStateManager): void { - const a: Fr = machineState.readMemory(this.aOffset); - const b: Fr = machineState.readMemory(this.bOffset); + async execute(machineState: AvmMachineState, _journal: AvmJournal): Promise { + Instruction.checkTags(machineState, this.inTag, this.aOffset, this.bOffset); + + const a = machineState.memory.getAs(this.aOffset); + const b = machineState.memory.getAs(this.bOffset); - const dest = new Fr(a.toBigInt() | b.toBigInt()); - machineState.writeMemory(this.destOffset, dest); + const res = a.or(b); + machineState.memory.set(this.dstOffset, res); this.incrementPc(machineState); } } -/** - */ export class Xor extends Instruction { static type: string = 'XOR'; static numberOfOperands = 3; - constructor(private aOffset: number, private bOffset: number, private destOffset: number) { + constructor(private inTag: TypeTag, private aOffset: number, private bOffset: number, private dstOffset: number) { super(); } - execute(machineState: AvmMachineState, _stateManager: AvmStateManager): void { - const a: Fr = machineState.readMemory(this.aOffset); - const b: Fr = machineState.readMemory(this.bOffset); + async execute(machineState: AvmMachineState, _journal: AvmJournal): Promise { + Instruction.checkTags(machineState, this.inTag, this.aOffset, this.bOffset); - const dest = new Fr(a.toBigInt() ^ b.toBigInt()); - machineState.writeMemory(this.destOffset, dest); + const a = machineState.memory.getAs(this.aOffset); + const b = machineState.memory.getAs(this.bOffset); + + const res = a.xor(b); + machineState.memory.set(this.dstOffset, res); this.incrementPc(machineState); } } -/** - */ export class Not extends Instruction { static type: string = 'NOT'; static numberOfOperands = 2; - constructor(private aOffset: number, private destOffset: number) { + constructor(private inTag: TypeTag, private aOffset: number, private dstOffset: number) { super(); } - execute(machineState: AvmMachineState, _stateManager: AvmStateManager): void { - const a: Fr = machineState.readMemory(this.aOffset); + async execute(machineState: AvmMachineState, _journal: AvmJournal): Promise { + Instruction.checkTags(machineState, this.inTag, this.aOffset); - // TODO: hack -> Bitwise operations should not occur over field elements - // It should only work over integers - const result = ~a.toBigInt(); + const a = machineState.memory.getAs(this.aOffset); - const dest = new Fr(result < 0 ? Fr.MODULUS + /* using a + as result is -ve*/ result : result); - machineState.writeMemory(this.destOffset, dest); + const res = a.not(); + machineState.memory.set(this.dstOffset, res); this.incrementPc(machineState); } } -/** -*/ export class Shl extends Instruction { static type: string = 'SHL'; static numberOfOperands = 3; - constructor(private aOffset: number, private bOffset: number, private destOffset: number) { + constructor(private inTag: TypeTag, private aOffset: number, private bOffset: number, private dstOffset: number) { super(); } - execute(machineState: AvmMachineState, _stateManager: AvmStateManager): void { - const a: Fr = machineState.readMemory(this.aOffset); - const b: Fr = machineState.readMemory(this.bOffset); + async execute(machineState: AvmMachineState, _journal: AvmJournal): Promise { + Instruction.checkTags(machineState, this.inTag, this.aOffset, this.bOffset); + + const a = machineState.memory.getAs(this.aOffset); + const b = machineState.memory.getAs(this.bOffset); - const dest = new Fr(a.toBigInt() << b.toBigInt()); - machineState.writeMemory(this.destOffset, dest); + const res = a.shl(b); + machineState.memory.set(this.dstOffset, res); this.incrementPc(machineState); } } -/** -*/ export class Shr extends Instruction { static type: string = 'SHR'; static numberOfOperands = 3; - constructor(private aOffset: number, private bOffset: number, private destOffset: number) { + constructor(private inTag: TypeTag, private aOffset: number, private bOffset: number, private dstOffset: number) { super(); } - execute(machineState: AvmMachineState, _stateManager: AvmStateManager): void { - const a: Fr = machineState.readMemory(this.aOffset); - const b: Fr = machineState.readMemory(this.bOffset); + async execute(machineState: AvmMachineState, _journal: AvmJournal): Promise { + Instruction.checkTags(machineState, this.inTag, this.aOffset, this.bOffset); + + const a = machineState.memory.getAs(this.aOffset); + const b = machineState.memory.getAs(this.bOffset); - // Here we are assuming that the field element maps to a positive number. - // The >> operator is *signed* in JS (and it sign extends). - // E.g.: -1n >> 3n == -1n. - const dest = new Fr(a.toBigInt() >> b.toBigInt()); - machineState.writeMemory(this.destOffset, dest); + const res = a.shr(b); + machineState.memory.set(this.dstOffset, res); this.incrementPc(machineState); } diff --git a/yarn-project/acir-simulator/src/avm/opcodes/comparators.test.ts b/yarn-project/acir-simulator/src/avm/opcodes/comparators.test.ts new file mode 100644 index 00000000000..ff604dbf2cb --- /dev/null +++ b/yarn-project/acir-simulator/src/avm/opcodes/comparators.test.ts @@ -0,0 +1,147 @@ +import { MockProxy, mock } from 'jest-mock-extended'; + +import { AvmMachineState } from '../avm_machine_state.js'; +import { Field, TypeTag, Uint16, Uint32 } from '../avm_memory_types.js'; +import { initExecutionEnvironment } from '../fixtures/index.js'; +import { AvmJournal } from '../journal/journal.js'; +import { Eq, Lt, Lte } from './comparators.js'; +import { InstructionExecutionError } from './instruction.js'; + +describe('Comparators', () => { + let machineState: AvmMachineState; + let journal: MockProxy; + + beforeEach(async () => { + machineState = new AvmMachineState(initExecutionEnvironment()); + journal = mock(); + }); + + describe('Eq', () => { + it('Works on integral types', async () => { + machineState.memory.setSlice(0, [new Uint32(1), new Uint32(2), new Uint32(3), new Uint32(1)]); + + [ + new Eq(TypeTag.UINT32, /*aOffset=*/ 0, /*bOffset=*/ 1, /*dstOffset=*/ 10), + new Eq(TypeTag.UINT32, /*aOffset=*/ 0, /*bOffset=*/ 2, /*dstOffset=*/ 11), + new Eq(TypeTag.UINT32, /*aOffset=*/ 0, /*bOffset=*/ 3, /*dstOffset=*/ 12), + ].forEach(i => i.execute(machineState, journal)); + + const actual = machineState.memory.getSlice(/*offset=*/ 10, /*size=*/ 4); + expect(actual).toEqual([new Uint32(0), new Uint32(0), new Uint32(1)]); + }); + + it('Works on field elements', async () => { + machineState.memory.setSlice(0, [new Field(1), new Field(2), new Field(3), new Field(1)]); + + [ + new Eq(TypeTag.FIELD, /*aOffset=*/ 0, /*bOffset=*/ 1, /*dstOffset=*/ 10), + new Eq(TypeTag.FIELD, /*aOffset=*/ 0, /*bOffset=*/ 2, /*dstOffset=*/ 11), + new Eq(TypeTag.FIELD, /*aOffset=*/ 0, /*bOffset=*/ 3, /*dstOffset=*/ 12), + ].forEach(i => i.execute(machineState, journal)); + + const actual = machineState.memory.getSlice(/*offset=*/ 10, /*size=*/ 4); + expect(actual).toEqual([new Field(0), new Field(0), new Field(1)]); + }); + + it('InTag is checked', async () => { + machineState.memory.setSlice(0, [new Field(1), new Uint32(2), new Uint16(3)]); + + const ops = [ + new Eq(TypeTag.FIELD, /*aOffset=*/ 0, /*bOffset=*/ 1, /*dstOffset=*/ 10), + new Eq(TypeTag.UINT32, /*aOffset=*/ 0, /*bOffset=*/ 2, /*dstOffset=*/ 10), + new Eq(TypeTag.UINT16, /*aOffset=*/ 1, /*bOffset=*/ 2, /*dstOffset=*/ 10), + new Eq(TypeTag.UINT16, /*aOffset=*/ 1, /*bOffset=*/ 1, /*dstOffset=*/ 10), + ]; + + for (const o of ops) { + await expect(() => o.execute(machineState, journal)).rejects.toThrow(InstructionExecutionError); + } + }); + }); + + describe('Lt', () => { + it('Works on integral types', async () => { + machineState.memory.setSlice(0, [new Uint32(1), new Uint32(2), new Uint32(0)]); + + [ + new Lt(TypeTag.UINT32, /*aOffset=*/ 0, /*bOffset=*/ 0, /*dstOffset=*/ 10), + new Lt(TypeTag.UINT32, /*aOffset=*/ 0, /*bOffset=*/ 1, /*dstOffset=*/ 11), + new Lt(TypeTag.UINT32, /*aOffset=*/ 0, /*bOffset=*/ 2, /*dstOffset=*/ 12), + ].forEach(i => i.execute(machineState, journal)); + + const actual = machineState.memory.getSlice(/*offset=*/ 10, /*size=*/ 4); + expect(actual).toEqual([new Uint32(0), new Uint32(1), new Uint32(0)]); + }); + + it('Works on field elements', async () => { + machineState.memory.setSlice(0, [new Field(1), new Field(2), new Field(0)]); + + [ + new Lt(TypeTag.FIELD, /*aOffset=*/ 0, /*bOffset=*/ 0, /*dstOffset=*/ 10), + new Lt(TypeTag.FIELD, /*aOffset=*/ 0, /*bOffset=*/ 1, /*dstOffset=*/ 11), + new Lt(TypeTag.FIELD, /*aOffset=*/ 0, /*bOffset=*/ 2, /*dstOffset=*/ 12), + ].forEach(i => i.execute(machineState, journal)); + + const actual = machineState.memory.getSlice(/*offset=*/ 10, /*size=*/ 4); + expect(actual).toEqual([new Field(0), new Field(1), new Field(0)]); + }); + + it('InTag is checked', async () => { + machineState.memory.setSlice(0, [new Field(1), new Uint32(2), new Uint16(3)]); + + const ops = [ + new Lt(TypeTag.FIELD, /*aOffset=*/ 0, /*bOffset=*/ 1, /*dstOffset=*/ 10), + new Lt(TypeTag.UINT32, /*aOffset=*/ 0, /*bOffset=*/ 2, /*dstOffset=*/ 10), + new Lt(TypeTag.UINT16, /*aOffset=*/ 1, /*bOffset=*/ 2, /*dstOffset=*/ 10), + new Lt(TypeTag.UINT16, /*aOffset=*/ 1, /*bOffset=*/ 1, /*dstOffset=*/ 10), + ]; + + for (const o of ops) { + await expect(() => o.execute(machineState, journal)).rejects.toThrow(InstructionExecutionError); + } + }); + }); + + describe('Lte', () => { + it('Works on integral types', async () => { + machineState.memory.setSlice(0, [new Uint32(1), new Uint32(2), new Uint32(0)]); + + [ + new Lte(TypeTag.UINT32, /*aOffset=*/ 0, /*bOffset=*/ 0, /*dstOffset=*/ 10), + new Lte(TypeTag.UINT32, /*aOffset=*/ 0, /*bOffset=*/ 1, /*dstOffset=*/ 11), + new Lte(TypeTag.UINT32, /*aOffset=*/ 0, /*bOffset=*/ 2, /*dstOffset=*/ 12), + ].forEach(i => i.execute(machineState, journal)); + + const actual = machineState.memory.getSlice(/*offset=*/ 10, /*size=*/ 4); + expect(actual).toEqual([new Uint32(1), new Uint32(1), new Uint32(0)]); + }); + + it('Works on field elements', async () => { + machineState.memory.setSlice(0, [new Field(1), new Field(2), new Field(0)]); + + [ + new Lte(TypeTag.FIELD, /*aOffset=*/ 0, /*bOffset=*/ 0, /*dstOffset=*/ 10), + new Lte(TypeTag.FIELD, /*aOffset=*/ 0, /*bOffset=*/ 1, /*dstOffset=*/ 11), + new Lte(TypeTag.FIELD, /*aOffset=*/ 0, /*bOffset=*/ 2, /*dstOffset=*/ 12), + ].forEach(i => i.execute(machineState, journal)); + + const actual = machineState.memory.getSlice(/*offset=*/ 10, /*size=*/ 4); + expect(actual).toEqual([new Field(1), new Field(1), new Field(0)]); + }); + + it('InTag is checked', async () => { + machineState.memory.setSlice(0, [new Field(1), new Uint32(2), new Uint16(3)]); + + const ops = [ + new Lte(TypeTag.FIELD, /*aOffset=*/ 0, /*bOffset=*/ 1, /*dstOffset=*/ 10), + new Lte(TypeTag.UINT32, /*aOffset=*/ 0, /*bOffset=*/ 2, /*dstOffset=*/ 10), + new Lte(TypeTag.UINT16, /*aOffset=*/ 1, /*bOffset=*/ 2, /*dstOffset=*/ 10), + new Lte(TypeTag.UINT16, /*aOffset=*/ 1, /*bOffset=*/ 1, /*dstOffset=*/ 10), + ]; + + for (const o of ops) { + await expect(() => o.execute(machineState, journal)).rejects.toThrow(InstructionExecutionError); + } + }); + }); +}); diff --git a/yarn-project/acir-simulator/src/avm/opcodes/comparators.ts b/yarn-project/acir-simulator/src/avm/opcodes/comparators.ts index 685a5438c42..f89d450dbb9 100644 --- a/yarn-project/acir-simulator/src/avm/opcodes/comparators.ts +++ b/yarn-project/acir-simulator/src/avm/opcodes/comparators.ts @@ -1,63 +1,69 @@ -import { Fr } from '@aztec/foundation/fields'; - import { AvmMachineState } from '../avm_machine_state.js'; -import { AvmStateManager } from '../avm_state_manager.js'; +import { TypeTag } from '../avm_memory_types.js'; +import { AvmJournal } from '../journal/index.js'; import { Instruction } from './instruction.js'; -/** -*/ export class Eq extends Instruction { static type: string = 'EQ'; static numberOfOperands = 3; - constructor(private aOffset: number, private bOffset: number, private destOffset: number) { + constructor(private inTag: TypeTag, private aOffset: number, private bOffset: number, private dstOffset: number) { super(); } - execute(machineState: AvmMachineState, _stateManager: AvmStateManager): void { - const a: Fr = machineState.readMemory(this.aOffset); - const b: Fr = machineState.readMemory(this.bOffset); + async execute(machineState: AvmMachineState, _journal: AvmJournal): Promise { + Instruction.checkTags(machineState, this.inTag, this.aOffset, this.bOffset); + + const a = machineState.memory.get(this.aOffset); + const b = machineState.memory.get(this.bOffset); - const dest = new Fr(a.toBigInt() == b.toBigInt()); - machineState.writeMemory(this.destOffset, dest); + // Result will be of the same type as 'a'. + const dest = a.build(a.equals(b) ? 1n : 0n); + machineState.memory.set(this.dstOffset, dest); this.incrementPc(machineState); } } -/** -*/ + export class Lt extends Instruction { static type: string = 'Lt'; static numberOfOperands = 3; - constructor(private aOffset: number, private bOffset: number, private destOffset: number) { + constructor(private inTag: TypeTag, private aOffset: number, private bOffset: number, private dstOffset: number) { super(); } - execute(machineState: AvmMachineState, _stateManager: AvmStateManager): void { - const a: Fr = machineState.readMemory(this.aOffset); - const b: Fr = machineState.readMemory(this.bOffset); + async execute(machineState: AvmMachineState, _journal: AvmJournal): Promise { + Instruction.checkTags(machineState, this.inTag, this.aOffset, this.bOffset); - const dest = new Fr(a.toBigInt() < b.toBigInt()); - machineState.writeMemory(this.destOffset, dest); + const a = machineState.memory.get(this.aOffset); + const b = machineState.memory.get(this.bOffset); + + // Result will be of the same type as 'a'. + const dest = a.build(a.lt(b) ? 1n : 0n); + machineState.memory.set(this.dstOffset, dest); this.incrementPc(machineState); } } -/** -*/ export class Lte extends Instruction { static type: string = 'LTE'; static numberOfOperands = 3; - constructor(private aOffset: number, private bOffset: number, private destOffset: number) { + constructor(private inTag: TypeTag, private aOffset: number, private bOffset: number, private dstOffset: number) { super(); } - execute(machineState: AvmMachineState, _stateManager: AvmStateManager): void { - const a: Fr = machineState.readMemory(this.aOffset); - const b: Fr = machineState.readMemory(this.bOffset); + async execute(machineState: AvmMachineState, _journal: AvmJournal): Promise { + Instruction.checkTags(machineState, this.inTag, this.aOffset, this.bOffset); + + const a = machineState.memory.get(this.aOffset); + const b = machineState.memory.get(this.bOffset); - const dest = new Fr(a.toBigInt() < b.toBigInt()); - machineState.writeMemory(this.destOffset, dest); + // Result will be of the same type as 'a'. + const dest = a.build(a.equals(b) || a.lt(b) ? 1n : 0n); + machineState.memory.set(this.dstOffset, dest); this.incrementPc(machineState); } diff --git a/yarn-project/acir-simulator/src/avm/opcodes/control_flow.test.ts b/yarn-project/acir-simulator/src/avm/opcodes/control_flow.test.ts index 906584b3493..7c04e08f246 100644 --- a/yarn-project/acir-simulator/src/avm/opcodes/control_flow.test.ts +++ b/yarn-project/acir-simulator/src/avm/opcodes/control_flow.test.ts @@ -1,147 +1,187 @@ import { Fr } from '@aztec/foundation/fields'; -import { mock } from 'jest-mock-extended'; +import { MockProxy, mock } from 'jest-mock-extended'; import { AvmMachineState } from '../avm_machine_state.js'; -import { AvmStateManager } from '../avm_state_manager.js'; +import { Field, TypeTag, Uint16 } from '../avm_memory_types.js'; +import { initExecutionEnvironment } from '../fixtures/index.js'; +import { AvmJournal } from '../journal/journal.js'; import { Add, Mul, Sub } from './arithmetic.js'; import { And, Not, Or, Shl, Shr, Xor } from './bitwise.js'; import { Eq, Lt, Lte } from './comparators.js'; -import { InternalCall, InternalCallStackEmptyError, InternalReturn, Jump, JumpI } from './control_flow.js'; +import { InternalCall, InternalReturn, Jump, JumpI, Return, Revert } from './control_flow.js'; +import { InstructionExecutionError } from './instruction.js'; import { CMov, CalldataCopy, Cast, Mov, Set } from './memory.js'; describe('Control Flow Opcodes', () => { - let stateManager = mock(); + let journal: MockProxy; let machineState: AvmMachineState; beforeEach(() => { - stateManager = mock(); - machineState = new AvmMachineState([]); + journal = mock(); + machineState = new AvmMachineState(initExecutionEnvironment()); }); - it('Should implement JUMP', () => { - const jumpLocation = 22; + describe('Jumps', () => { + it('Should implement JUMP', async () => { + const jumpLocation = 22; - expect(machineState.pc).toBe(0); + expect(machineState.pc).toBe(0); - const instruction = new Jump(jumpLocation); - instruction.execute(machineState, stateManager); - expect(machineState.pc).toBe(jumpLocation); - }); + const instruction = new Jump(jumpLocation); + await instruction.execute(machineState, journal); + expect(machineState.pc).toBe(jumpLocation); + }); - it('Should implement JUMPI - truthy', () => { - const jumpLocation = 22; - const jumpLocation1 = 69; + it('Should implement JUMPI - truthy', async () => { + const jumpLocation = 22; + const jumpLocation1 = 69; - expect(machineState.pc).toBe(0); + expect(machineState.pc).toBe(0); - machineState.writeMemory(0, new Fr(1n)); - machineState.writeMemory(1, new Fr(2n)); + machineState.memory.set(0, new Uint16(1n)); + machineState.memory.set(1, new Uint16(2n)); - const instruction = new JumpI(jumpLocation, 0); - instruction.execute(machineState, stateManager); - expect(machineState.pc).toBe(jumpLocation); + const instruction = new JumpI(jumpLocation, 0); + await instruction.execute(machineState, journal); + expect(machineState.pc).toBe(jumpLocation); - // Truthy can be greater than 1 - const instruction1 = new JumpI(jumpLocation1, 1); - instruction1.execute(machineState, stateManager); - expect(machineState.pc).toBe(jumpLocation1); - }); + // Truthy can be greater than 1 + const instruction1 = new JumpI(jumpLocation1, 1); + await instruction1.execute(machineState, journal); + expect(machineState.pc).toBe(jumpLocation1); + }); + + it('Should implement JUMPI - falsy', async () => { + const jumpLocation = 22; + + expect(machineState.pc).toBe(0); - it('Should implement JUMPI - falsy', () => { - const jumpLocation = 22; + machineState.memory.set(0, new Uint16(0n)); - expect(machineState.pc).toBe(0); + const instruction = new JumpI(jumpLocation, 0); + await instruction.execute(machineState, journal); + expect(machineState.pc).toBe(1); + }); - machineState.writeMemory(0, new Fr(0n)); + it('Should implement Internal Call and Return', async () => { + const jumpLocation = 22; - const instruction = new JumpI(jumpLocation, 0); - instruction.execute(machineState, stateManager); - expect(machineState.pc).toBe(1); + expect(machineState.pc).toBe(0); + + const instruction = new InternalCall(jumpLocation); + const returnInstruction = new InternalReturn(); + + await instruction.execute(machineState, journal); + expect(machineState.pc).toBe(jumpLocation); + + await returnInstruction.execute(machineState, journal); + expect(machineState.pc).toBe(1); + }); + + it('Should chain series of control flow instructions', async () => { + const jumpLocation0 = 22; + const jumpLocation1 = 69; + const jumpLocation2 = 1337; + + const aloneJumpLocation = 420; + + const instructions = [ + // pc | internal call stack + new InternalCall(jumpLocation0), // 22 | [1] + new InternalCall(jumpLocation1), // 69 | [1, 23] + new InternalReturn(), // 23 | [1] + new Jump(aloneJumpLocation), // 420 | [1] + new InternalCall(jumpLocation2), // 1337| [1, 421] + new InternalReturn(), // 421 | [1] + new InternalReturn(), // 1 | [] + ]; + + // The expected program counter after each instruction is invoked + const expectedPcs = [ + jumpLocation0, + jumpLocation1, + jumpLocation0 + 1, + aloneJumpLocation, + jumpLocation2, + aloneJumpLocation + 1, + 1, + ]; + + for (let i = 0; i < instructions.length; i++) { + await instructions[i].execute(machineState, journal); + expect(machineState.pc).toBe(expectedPcs[i]); + } + }); + + it('Should error if Internal Return is called without a corresponding Internal Call', async () => { + const returnInstruction = () => new InternalReturn().execute(machineState, journal); + await expect(returnInstruction()).rejects.toThrow(InstructionExecutionError); + }); + + it('Should increment PC on All other Instructions', async () => { + const instructions = [ + new Add(0, 1, 2), + new Sub(0, 1, 2), + new Mul(0, 1, 2), + new Lt(TypeTag.UINT16, 0, 1, 2), + new Lte(TypeTag.UINT16, 0, 1, 2), + new Eq(TypeTag.UINT16, 0, 1, 2), + new Xor(TypeTag.UINT16, 0, 1, 2), + new And(TypeTag.UINT16, 0, 1, 2), + new Or(TypeTag.UINT16, 0, 1, 2), + new Shl(TypeTag.UINT16, 0, 1, 2), + new Shr(TypeTag.UINT16, 0, 1, 2), + new Not(TypeTag.UINT16, 0, 2), + new CalldataCopy(0, 1, 2), + new Set(TypeTag.UINT16, 0n, 1), + new Mov(0, 1), + new CMov(0, 1, 2, 3), + new Cast(TypeTag.UINT16, 0, 1), + ]; + + for (const instruction of instructions) { + // Use a fresh machine state each run + const innerMachineState = new AvmMachineState(initExecutionEnvironment()); + innerMachineState.memory.set(0, new Uint16(4n)); + innerMachineState.memory.set(1, new Uint16(8n)); + innerMachineState.memory.set(2, new Uint16(12n)); + expect(innerMachineState.pc).toBe(0); + + await instruction.execute(innerMachineState, journal); + } + }); }); - it('Should implement Internal Call and Return', () => { - const jumpLocation = 22; + describe('Halting Opcodes', () => { + it('Should return data from the return opcode', async () => { + const returnData = [new Fr(1n), new Fr(2n), new Fr(3n)]; - expect(machineState.pc).toBe(0); + machineState.memory.set(0, new Field(1n)); + machineState.memory.set(1, new Field(2n)); + machineState.memory.set(2, new Field(3n)); - const instruction = new InternalCall(jumpLocation); - const returnInstruction = new InternalReturn(); + const instruction = new Return(0, returnData.length); + await instruction.execute(machineState, journal); - instruction.execute(machineState, stateManager); - expect(machineState.pc).toBe(jumpLocation); + expect(machineState.getReturnData()).toEqual(returnData); + expect(machineState.halted).toBe(true); + expect(machineState.reverted).toBe(false); + }); - returnInstruction.execute(machineState, stateManager); - expect(machineState.pc).toBe(1); - }); + it('Should return data and revert from the revert opcode', async () => { + const returnData = [new Fr(1n), new Fr(2n), new Fr(3n)]; - it('Should chain series of control flow instructions', () => { - const jumpLocation0 = 22; - const jumpLocation1 = 69; - const jumpLocation2 = 1337; - - const aloneJumpLocation = 420; - - const instructions = [ - // pc | internal call stack - new InternalCall(jumpLocation0), // 22 | [1] - new InternalCall(jumpLocation1), // 69 | [1, 23] - new InternalReturn(), // 23 | [1] - new Jump(aloneJumpLocation), // 420 | [1] - new InternalCall(jumpLocation2), // 1337| [1, 421] - new InternalReturn(), // 421 | [1] - new InternalReturn(), // 1 | [] - ]; - - // The expected program counter after each instruction is invoked - const expectedPcs = [ - jumpLocation0, - jumpLocation1, - jumpLocation0 + 1, - aloneJumpLocation, - jumpLocation2, - aloneJumpLocation + 1, - 1, - ]; - - for (let i = 0; i < instructions.length; i++) { - instructions[i].execute(machineState, stateManager); - expect(machineState.pc).toBe(expectedPcs[i]); - } - }); + machineState.memory.set(0, new Field(1n)); + machineState.memory.set(1, new Field(2n)); + machineState.memory.set(2, new Field(3n)); - it('Should error if Internal Return is called without a corresponding Internal Call', () => { - const returnInstruction = new InternalReturn(); - expect(() => returnInstruction.execute(machineState, stateManager)).toThrow(InternalCallStackEmptyError); - }); + const instruction = new Revert(0, returnData.length); + await instruction.execute(machineState, journal); - it('Should increment PC on All other Instructions', () => { - const instructions = [ - new Add(0, 1, 2), - new Sub(0, 1, 2), - new Mul(0, 1, 2), - new Lt(0, 1, 2), - new Lte(0, 1, 2), - new Eq(0, 1, 2), - new Xor(0, 1, 2), - new And(0, 1, 2), - new Or(0, 1, 2), - new Shl(0, 1, 2), - new Shr(0, 1, 2), - new Not(0, 2), - new CalldataCopy(0, 1, 2), - new Set(0n, 1), - new Mov(0, 1), - new CMov(0, 1, 2, 3), - new Cast(0, 1), - ]; - - for (const instruction of instructions) { - // Use a fresh machine state each run - const innerMachineState = new AvmMachineState([]); - expect(machineState.pc).toBe(0); - instruction.execute(innerMachineState, stateManager); - expect(innerMachineState.pc).toBe(1); - } + expect(machineState.getReturnData()).toEqual(returnData); + expect(machineState.halted).toBe(true); + expect(machineState.reverted).toBe(true); + }); }); }); diff --git a/yarn-project/acir-simulator/src/avm/opcodes/control_flow.ts b/yarn-project/acir-simulator/src/avm/opcodes/control_flow.ts index b988979076e..c890fc700e3 100644 --- a/yarn-project/acir-simulator/src/avm/opcodes/control_flow.ts +++ b/yarn-project/acir-simulator/src/avm/opcodes/control_flow.ts @@ -1,8 +1,8 @@ import { AvmMachineState } from '../avm_machine_state.js'; -import { AvmStateManager } from '../avm_state_manager.js'; -import { Instruction } from './instruction.js'; +import { IntegralValue } from '../avm_memory_types.js'; +import { AvmJournal } from '../journal/journal.js'; +import { Instruction, InstructionExecutionError } from './instruction.js'; -/** - */ export class Return extends Instruction { static type: string = 'RETURN'; static numberOfOperands = 2; @@ -11,15 +11,33 @@ export class Return extends Instruction { super(); } - execute(machineState: AvmMachineState, _stateManager: AvmStateManager): void { - const returnData = machineState.readMemoryChunk(this.returnOffset, this.returnOffset + this.copySize); + async execute(machineState: AvmMachineState, _journal: AvmJournal): Promise { + const returnData = machineState.memory.getSlice(this.returnOffset, this.copySize).map(word => word.toFr()); + machineState.setReturnData(returnData); this.halt(machineState); } } -/** -*/ +export class Revert extends Instruction { + static type: string = 'RETURN'; + static numberOfOperands = 2; + + constructor(private returnOffset: number, private retSize: number) { + super(); + } + + async execute(machineState: AvmMachineState, _journal: AvmJournal): Promise { + const returnData = machineState.memory + .getSlice(this.returnOffset, this.returnOffset + this.retSize) + .map(word => word.toFr()); + machineState.setReturnData(returnData); + + this.revert(machineState); + } +} + export class Jump extends Instruction { static type: string = 'JUMP'; static numberOfOperands = 1; @@ -28,12 +46,11 @@ export class Jump extends Instruction { super(); } - execute(machineState: AvmMachineState, _stateManager: AvmStateManager): void { + async execute(machineState: AvmMachineState, _journal: AvmJournal): Promise { machineState.pc = this.jumpOffset; } } -/** -*/ export class JumpI extends Instruction { static type: string = 'JUMPI'; static numberOfOperands = 1; @@ -42,9 +59,10 @@ export class JumpI extends Instruction { super(); } - execute(machineState: AvmMachineState, _stateManager: AvmStateManager): void { - const condition = machineState.readMemory(this.condOffset); + async execute(machineState: AvmMachineState, _journal: AvmJournal): Promise { + const condition = machineState.memory.getAs(this.condOffset); + // TODO: reconsider this casting if (condition.toBigInt() == 0n) { this.incrementPc(machineState); } else { @@ -53,7 +71,6 @@ export class JumpI extends Instruction { } } -/** -*/ export class InternalCall extends Instruction { static type: string = 'INTERNALCALL'; static numberOfOperands = 1; @@ -62,13 +79,12 @@ export class InternalCall extends Instruction { super(); } - execute(machineState: AvmMachineState, _stateManager: AvmStateManager): void { + async execute(machineState: AvmMachineState, _journal: AvmJournal): Promise { machineState.internalCallStack.push(machineState.pc + 1); machineState.pc = this.jumpOffset; } } -/** -*/ export class InternalReturn extends Instruction { static type: string = 'INTERNALRETURN'; static numberOfOperands = 0; @@ -77,20 +93,11 @@ export class InternalReturn extends Instruction { super(); } - execute(machineState: AvmMachineState, _stateManager: AvmStateManager): void { + async execute(machineState: AvmMachineState, _journal: AvmJournal): Promise { const jumpOffset = machineState.internalCallStack.pop(); if (jumpOffset === undefined) { - throw new InternalCallStackEmptyError(); + throw new InstructionExecutionError('Internal call empty!'); } machineState.pc = jumpOffset; } } - -/** - * Thrown if the internal call stack is popped when it is empty - */ -export class InternalCallStackEmptyError extends Error { - constructor() { - super('Internal call stack is empty'); - } -} diff --git a/yarn-project/acir-simulator/src/avm/opcodes/decode_bytecode.ts b/yarn-project/acir-simulator/src/avm/opcodes/decode_bytecode.ts index 624546cfbeb..33533f31abb 100644 --- a/yarn-project/acir-simulator/src/avm/opcodes/decode_bytecode.ts +++ b/yarn-project/acir-simulator/src/avm/opcodes/decode_bytecode.ts @@ -17,17 +17,18 @@ export function decodeBytecode(bytecode: Buffer): Instruction[] { const opcodeByte = bytecode[bytePtr]; bytePtr += AVM_OPCODE_BYTE_LENGTH; if (!(opcodeByte in Opcode)) { - throw new Error(`Opcode ${opcodeByte} not implemented`); + throw new Error(`Opcode 0x${opcodeByte.toString(16)} not implemented`); } const opcode = opcodeByte as Opcode; const instructionType = INSTRUCTION_SET.get(opcode); if (instructionType === undefined) { - throw new Error(`Opcode ${opcode} not implemented`); + throw new Error(`Opcode 0x${opcode.toString(16)} not implemented`); } const numberOfOperands = instructionType.numberOfOperands; const operands: number[] = []; for (let i = 0; i < numberOfOperands; i++) { + // TODO: support constants which might not be u32s const operand = bytecode.readUInt32BE(bytePtr); bytePtr += AVM_OPERAND_BYTE_LENGTH; operands.push(operand); diff --git a/yarn-project/acir-simulator/src/avm/opcodes/encode_to_bytecode.ts b/yarn-project/acir-simulator/src/avm/opcodes/encode_to_bytecode.ts index 186706847d3..105c0f808ab 100644 --- a/yarn-project/acir-simulator/src/avm/opcodes/encode_to_bytecode.ts +++ b/yarn-project/acir-simulator/src/avm/opcodes/encode_to_bytecode.ts @@ -11,12 +11,14 @@ import { Opcode } from './opcodes.js'; export function encodeToBytecode(opcode: Opcode, args: number[]): Buffer { const instructionType = INSTRUCTION_SET.get(opcode); if (instructionType === undefined) { - throw new Error(`Opcode ${opcode} not implemented`); + throw new Error(`Opcode 0x${opcode.toString(16)} not implemented`); } const numberOfOperands = instructionType.numberOfOperands; if (args.length !== numberOfOperands) { - throw new Error(`Opcode ${opcode} expects ${numberOfOperands} arguments, but ${args.length} were provided`); + throw new Error( + `Opcode 0x${opcode.toString(16)} expects ${numberOfOperands} arguments, but ${args.length} were provided`, + ); } const bytecode = Buffer.alloc(AVM_OPCODE_BYTE_LENGTH + numberOfOperands * AVM_OPERAND_BYTE_LENGTH); diff --git a/yarn-project/acir-simulator/src/avm/opcodes/environment_getters.test.ts b/yarn-project/acir-simulator/src/avm/opcodes/environment_getters.test.ts new file mode 100644 index 00000000000..4dd6b0abb85 --- /dev/null +++ b/yarn-project/acir-simulator/src/avm/opcodes/environment_getters.test.ts @@ -0,0 +1,111 @@ +import { Fr } from '@aztec/foundation/fields'; + +import { MockProxy, mock } from 'jest-mock-extended'; + +import { AvmMachineState } from '../avm_machine_state.js'; +import { initExecutionEnvironment, initGlobalVariables } from '../fixtures/index.js'; +import { AvmJournal } from '../journal/journal.js'; +import { + Address, + BlockNumber, + ChainId, + FeePerDAGas, + FeePerL1Gas, + FeePerL2Gas, + Origin, + Portal, + Sender, + StorageAddress, + Timestamp, + Version, +} from './environment_getters.js'; + +describe('Environment getters instructions', () => { + let machineState: AvmMachineState; + let journal: MockProxy; + + beforeEach(async () => { + journal = mock(); + }); + + type EnvInstruction = Portal | FeePerL1Gas | FeePerL2Gas | FeePerDAGas | Origin | Sender | StorageAddress | Address; + const envGetterTest = async (key: string, value: Fr, instruction: EnvInstruction) => { + machineState = new AvmMachineState(initExecutionEnvironment({ [key]: value })); + + await instruction.execute(machineState, journal); + const actual = machineState.memory.get(0).toFr(); + expect(actual).toEqual(value); + }; + + it('Should read address correctly', async () => { + const address = new Fr(123456n); + await envGetterTest('address', address, new Address(0)); + }); + + it('Should read storage address correctly', async () => { + const address = new Fr(123456n); + await envGetterTest('storageAddress', address, new StorageAddress(0)); + }); + + it('Should read Portal correctly', async () => { + const portal = new Fr(123456n); + await envGetterTest('portal', portal, new Portal(0)); + }); + + it('Should read FeePerL1Gas correctly', async () => { + const feePerL1Gas = new Fr(123456n); + await envGetterTest('feePerL1Gas', feePerL1Gas, new FeePerL1Gas(0)); + }); + + it('Should read FeePerL2Gas correctly', async () => { + const feePerL2Gas = new Fr(123456n); + await envGetterTest('feePerL2Gas', feePerL2Gas, new FeePerL2Gas(0)); + }); + + it('Should read FeePerDAGas correctly', async () => { + const feePerDaGas = new Fr(123456n); + await envGetterTest('feePerDaGas', feePerDaGas, new FeePerDAGas(0)); + }); + + it('Should read Origin correctly', async () => { + const origin = new Fr(123456n); + await envGetterTest('origin', origin, new Origin(0)); + }); + + it('Should read Sender correctly', async () => { + const sender = new Fr(123456n); + await envGetterTest('sender', sender, new Sender(0)); + }); + + describe('Global Variables', () => { + type GlobalsInstruction = ChainId | Version | BlockNumber | Timestamp; + const readGlobalVariableTest = async (key: string, value: Fr, instruction: GlobalsInstruction) => { + const globals = initGlobalVariables({ [key]: value }); + machineState = new AvmMachineState(initExecutionEnvironment({ globals })); + + await instruction.execute(machineState, journal); + const actual = machineState.memory.get(0).toFr(); + expect(actual).toEqual(value); + }; + + it('Should read chainId', async () => { + const chainId = new Fr(123456n); + await readGlobalVariableTest('chainId', chainId, new ChainId(0)); + }); + + it('Should read version', async () => { + const version = new Fr(123456n); + await readGlobalVariableTest('version', version, new Version(0)); + }); + + it('Should read block number', async () => { + const blockNumber = new Fr(123456n); + await readGlobalVariableTest('blockNumber', blockNumber, new BlockNumber(0)); + }); + + it('Should read timestamp', async () => { + const timestamp = new Fr(123456n); + await readGlobalVariableTest('timestamp', timestamp, new Timestamp(0)); + }); + }); +}); diff --git a/yarn-project/acir-simulator/src/avm/opcodes/environment_getters.ts b/yarn-project/acir-simulator/src/avm/opcodes/environment_getters.ts new file mode 100644 index 00000000000..18e97575b96 --- /dev/null +++ b/yarn-project/acir-simulator/src/avm/opcodes/environment_getters.ts @@ -0,0 +1,275 @@ +import { AvmMachineState } from '../avm_machine_state.js'; +import { Field } from '../avm_memory_types.js'; +import { AvmJournal } from '../journal/journal.js'; +import { Instruction } from './instruction.js'; + +export class Address extends Instruction { + static type: string = 'ADDRESS'; + static numberOfOperands = 1; + + constructor(private destOffset: number) { + super(); + } + + async execute(machineState: AvmMachineState, _journal: AvmJournal): Promise { + const { address } = machineState.executionEnvironment; + + machineState.memory.set(this.destOffset, new Field(address)); + this.incrementPc(machineState); + } +} + +export class StorageAddress extends Instruction { + static type: string = 'STORAGEADDRESS'; + static numberOfOperands = 1; + + constructor(private destOffset: number) { + super(); + } + + async execute(machineState: AvmMachineState, _journal: AvmJournal): Promise { + const { storageAddress } = machineState.executionEnvironment; + + machineState.memory.set(this.destOffset, new Field(storageAddress)); + this.incrementPc(machineState); + } +} + +export class Sender extends Instruction { + static type: string = 'SENDER'; + static numberOfOperands = 1; + + constructor(private destOffset: number) { + super(); + } + + async execute(machineState: AvmMachineState, _journal: AvmJournal): Promise { + const { sender } = machineState.executionEnvironment; + + machineState.memory.set(this.destOffset, new Field(sender)); + + this.incrementPc(machineState); + } +} + +export class Origin extends Instruction { + static type: string = 'ORIGIN'; + static numberOfOperands = 1; + + constructor(private destOffset: number) { + super(); + } + + async execute(machineState: AvmMachineState, _journal: AvmJournal): Promise { + const { origin } = machineState.executionEnvironment; + + machineState.memory.set(this.destOffset, new Field(origin)); + + this.incrementPc(machineState); + } +} + +export class FeePerL1Gas extends Instruction { + static type: string = 'FEEPERL1GAS'; + static numberOfOperands = 1; + + constructor(private destOffset: number) { + super(); + } + + async execute(machineState: AvmMachineState, _journal: AvmJournal): Promise { + const { feePerL1Gas } = machineState.executionEnvironment; + + machineState.memory.set(this.destOffset, new Field(feePerL1Gas)); + + this.incrementPc(machineState); + } +} + +export class FeePerL2Gas extends Instruction { + static type: string = 'FEEPERL2GAS'; + static numberOfOperands = 1; + + constructor(private destOffset: number) { + super(); + } + + async execute(machineState: AvmMachineState, _journal: AvmJournal): Promise { + const { feePerL2Gas } = machineState.executionEnvironment; + + machineState.memory.set(this.destOffset, new Field(feePerL2Gas)); + + this.incrementPc(machineState); + } +} + +export class FeePerDAGas extends Instruction { + static type: string = 'FEEPERDAGAS'; + static numberOfOperands = 1; + + constructor(private destOffset: number) { + super(); + } + + async execute(machineState: AvmMachineState, _journal: AvmJournal): Promise { + const { feePerDaGas } = machineState.executionEnvironment; + + machineState.memory.set(this.destOffset, new Field(feePerDaGas)); + + this.incrementPc(machineState); + } +} + +export class Portal extends Instruction { + static type: string = 'PORTAL'; + static numberOfOperands = 1; + + constructor(private destOffset: number) { + super(); + } + + async execute(machineState: AvmMachineState, _journal: AvmJournal): Promise { + const { portal } = machineState.executionEnvironment; + + machineState.memory.set(this.destOffset, new Field(portal.toField())); + + this.incrementPc(machineState); + } +} + +export class ChainId extends Instruction { + static type: string = 'CHAINID'; + static numberOfOperands = 1; + + constructor(private destOffset: number) { + super(); + } + + async execute(machineState: AvmMachineState, _journal: AvmJournal): Promise { + const { chainId } = machineState.executionEnvironment.globals; + + machineState.memory.set(this.destOffset, new Field(chainId)); + + this.incrementPc(machineState); + } +} + +export class Version extends Instruction { + static type: string = 'VERSION'; + static numberOfOperands = 1; + + constructor(private destOffset: number) { + super(); + } + + async execute(machineState: AvmMachineState, _journal: AvmJournal): Promise { + const { version } = machineState.executionEnvironment.globals; + + machineState.memory.set(this.destOffset, new Field(version)); + + this.incrementPc(machineState); + } +} + +export class BlockNumber extends Instruction { + static type: string = 'BLOCKNUMBER'; + static numberOfOperands = 1; + + constructor(private destOffset: number) { + super(); + } + + async execute(machineState: AvmMachineState, _journal: AvmJournal): Promise { + const { blockNumber } = machineState.executionEnvironment.globals; + + machineState.memory.set(this.destOffset, new Field(blockNumber)); + + this.incrementPc(machineState); + } +} + +export class Timestamp extends Instruction { + static type: string = 'TIMESTAMP'; + static numberOfOperands = 1; + + constructor(private destOffset: number) { + super(); + } + + async execute(machineState: AvmMachineState, _journal: AvmJournal): Promise { + const { timestamp } = machineState.executionEnvironment.globals; + + machineState.memory.set(this.destOffset, new Field(timestamp)); + + this.incrementPc(machineState); + } +} + +// export class Coinbase extends Instruction { +// static type: string = 'COINBASE'; +// static numberOfOperands = 1; + +// constructor(private destOffset: number) { +// super(); +// } + +// async execute(machineState: AvmMachineState, _journal: AvmJournal): Promise { +// const {coinbase} = machineState.executionEnvironment.globals; + +// machineState.memory.set(this.destOffset, coinbase); + +// this.incrementPc(machineState); +// } +// } + +// // TODO: are these even needed within the block? (both block gas limit variables - why does the execution env care?) +// export class BlockL1GasLimit extends Instruction { +// static type: string = 'BLOCKL1GASLIMIT'; +// static numberOfOperands = 1; + +// constructor(private destOffset: number) { +// super(); +// } + +// async execute(machineState: AvmMachineState, _journal: AvmJournal): Promise { +// const {blockL1GasLimit} = machineState.executionEnvironment.globals; + +// machineState.memory.set(this.destOffset, blockL1GasLimit); + +// this.incrementPc(machineState); +// } +// } + +// export class BlockL2GasLimit extends Instruction { +// static type: string = 'BLOCKL2GASLIMIT'; +// static numberOfOperands = 1; + +// constructor(private destOffset: number) { +// super(); +// } + +// async execute(machineState: AvmMachineState, _journal: AvmJournal): Promise { +// const {blockL2GasLimit} = machineState.executionEnvironment.globals; + +// machineState.memory.set(this.destOffset, blockL2GasLimit); + +// this.incrementPc(machineState); +// } +// } + +// export class BlockDAGasLimit extends Instruction { +// static type: string = 'BLOCKDAGASLIMIT'; +// static numberOfOperands = 1; + +// constructor(private destOffset: number) { +// super(); +// } + +// async execute(machineState: AvmMachineState, _journal: AvmJournal): Promise { +// const {blockDAGasLimit} = machineState.executionEnvironment.globals; + +// machineState.memory.set(this.destOffset, blockDAGasLimit); + +// this.incrementPc(machineState); +// } +// } diff --git a/yarn-project/acir-simulator/src/avm/opcodes/external_calls.test.ts b/yarn-project/acir-simulator/src/avm/opcodes/external_calls.test.ts new file mode 100644 index 00000000000..50c29128ec5 --- /dev/null +++ b/yarn-project/acir-simulator/src/avm/opcodes/external_calls.test.ts @@ -0,0 +1,132 @@ +import { Fr } from '@aztec/foundation/fields'; + +import { jest } from '@jest/globals'; +import { MockProxy, mock } from 'jest-mock-extended'; + +import { CommitmentsDB, PublicContractsDB, PublicStateDB } from '../../index.js'; +import { AvmMachineState } from '../avm_machine_state.js'; +import { Field } from '../avm_memory_types.js'; +import { initExecutionEnvironment } from '../fixtures/index.js'; +import { HostStorage } from '../journal/host_storage.js'; +import { AvmJournal } from '../journal/journal.js'; +import { encodeToBytecode } from './encode_to_bytecode.js'; +import { Call } from './external_calls.js'; +import { Opcode } from './opcodes.js'; + +describe('External Calls', () => { + let machineState: AvmMachineState; + let journal: AvmJournal; + + let contractsDb: MockProxy; + + beforeEach(() => { + machineState = new AvmMachineState(initExecutionEnvironment()); + + contractsDb = mock(); + + const commitmentsDb = mock(); + const publicStateDb = mock(); + const hostStorage = new HostStorage(publicStateDb, contractsDb, commitmentsDb); + journal = new AvmJournal(hostStorage); + }); + + describe('Call', () => { + // TODO(https://github.com/AztecProtocol/aztec-packages/issues/3992): gas not implemented + it('Should execute a call correctly', async () => { + const gasOffset = 0; + const gas = Fr.zero(); + + const addrOffset = 1; + const addr = new Fr(123456n); + + const argsOffset = 2; + const args = [new Field(1n), new Field(2n), new Field(3n)]; + const argsSize = args.length; + + const retOffset = 8; + const retSize = 2; + + const successOffset = 7; + + machineState.memory.set(0, new Field(gas)); + machineState.memory.set(1, new Field(addr)); + machineState.memory.setSlice(2, args); + + const otherContextInstructions: [Opcode, any[]][] = [ + // Place [1,2,3] into memory + [Opcode.CALLDATACOPY, [/*value=*/ 0, /*copySize=*/ argsSize, /*dstOffset=*/ 0]], + // Store 1 into slot 1 + [Opcode.SSTORE, [/*slotOffset=*/ 0, /*dataOffset=*/ 0]], + // Return [1,2] from memory + [Opcode.RETURN, [/*retOffset=*/ 0, /*size=*/ 2]], + ]; + + const otherContextInstructionsBytecode = Buffer.concat( + otherContextInstructions.map(([opcode, args]) => encodeToBytecode(opcode, args)), + ); + jest + .spyOn(journal.hostStorage.contractsDb, 'getBytecode') + .mockReturnValue(Promise.resolve(otherContextInstructionsBytecode)); + + const instruction = new Call(gasOffset, addrOffset, argsOffset, argsSize, retOffset, retSize, successOffset); + await instruction.execute(machineState, journal); + + const successValue = machineState.memory.get(successOffset); + expect(successValue).toEqual(new Field(1n)); + + const retValue = machineState.memory.getSlice(retOffset, retSize); + expect(retValue).toEqual([new Field(1n), new Field(2n)]); + + // Check that the storage call has been merged into the parent journal + const { currentStorageValue } = journal.flush(); + expect(currentStorageValue.size).toEqual(1); + + const nestedContractWrites = currentStorageValue.get(addr.toBigInt()); + expect(nestedContractWrites).toBeDefined(); + + const slotNumber = 1n; + const expectedStoredValue = new Fr(1n); + expect(nestedContractWrites!.get(slotNumber)).toEqual(expectedStoredValue); + }); + }); + + describe('Static Call', () => { + it('Should fail if a static call attempts to touch storage', async () => { + const gasOffset = 0; + const gas = new Field(0); + const addrOffset = 1; + const addr = new Field(123456n); + const argsOffset = 2; + const args = [new Field(1n), new Field(2n), new Field(3n)]; + + const argsSize = args.length; + const retOffset = 8; + const retSize = 2; + const successOffset = 7; + + machineState.memory.set(0, gas); + machineState.memory.set(1, addr); + machineState.memory.setSlice(2, args); + + const otherContextInstructions: [Opcode, any[]][] = [ + // Place [1,2,3] into memory + [Opcode.CALLDATACOPY, [/*value=*/ 0, /*copySize=*/ argsSize, /*dstOffset=*/ 0]], + [Opcode.SSTORE, [/*slotOffset*/ 1, /*dataOffset=*/ 0]], + ]; + + const otherContextInstructionsBytecode = Buffer.concat( + otherContextInstructions.map(([opcode, args]) => encodeToBytecode(opcode, args)), + ); + jest + .spyOn(journal.hostStorage.contractsDb, 'getBytecode') + .mockReturnValue(Promise.resolve(otherContextInstructionsBytecode)); + + const instruction = new Call(gasOffset, addrOffset, argsOffset, argsSize, retOffset, retSize, successOffset); + await instruction.execute(machineState, journal); + + // No revert has occurred, but the nested execution has failed + const successValue = machineState.memory.get(successOffset); + expect(successValue).toEqual(new Field(0n)); + }); + }); +}); diff --git a/yarn-project/acir-simulator/src/avm/opcodes/external_calls.ts b/yarn-project/acir-simulator/src/avm/opcodes/external_calls.ts new file mode 100644 index 00000000000..280b1284f02 --- /dev/null +++ b/yarn-project/acir-simulator/src/avm/opcodes/external_calls.ts @@ -0,0 +1,100 @@ +import { Fr } from '@aztec/foundation/fields'; + +import { AvmContext } from '../avm_context.js'; +import { AvmMachineState } from '../avm_machine_state.js'; +import { Field } from '../avm_memory_types.js'; +import { AvmJournal } from '../journal/journal.js'; +import { Instruction } from './instruction.js'; + +export class Call extends Instruction { + static type: string = 'CALL'; + static numberOfOperands = 7; + + constructor( + private /* Unused due to no formal gas implementation at this moment */ _gasOffset: number, + private addrOffset: number, + private argsOffset: number, + private argSize: number, + private retOffset: number, + private retSize: number, + private successOffset: number, + ) { + super(); + } + + // TODO(https://github.com/AztecProtocol/aztec-packages/issues/3992): there is no concept of remaining / available gas at this moment + async execute(machineState: AvmMachineState, journal: AvmJournal): Promise { + const callAddress = machineState.memory.getAs(this.addrOffset); + const calldata = machineState.memory.getSlice(this.argsOffset, this.argSize).map(f => new Fr(f.toBigInt())); + + const avmContext = AvmContext.prepExternalCallContext( + new Fr(callAddress.toBigInt()), + calldata, + machineState.executionEnvironment, + journal, + ); + + const returnObject = await avmContext.call(); + const success = !returnObject.reverted; + + // We only take as much data as was specified in the return size -> TODO: should we be reverting here + const returnData = returnObject.output.slice(0, this.retSize); + const convertedReturnData = returnData.map(f => new Field(f)); + + // Write our return data into memory + machineState.memory.set(this.successOffset, new Field(success ? 1 : 0)); + machineState.memory.setSlice(this.retOffset, convertedReturnData); + + if (success) { + avmContext.mergeJournal(); + } + + this.incrementPc(machineState); + } +} + +export class StaticCall extends Instruction { + static type: string = 'STATICCALL'; + static numberOfOperands = 7; + + constructor( + private /* Unused due to no formal gas implementation at this moment */ _gasOffset: number, + private addrOffset: number, + private argsOffset: number, + private argSize: number, + private retOffset: number, + private retSize: number, + private successOffset: number, + ) { + super(); + } + + async execute(machineState: AvmMachineState, journal: AvmJournal): Promise { + const callAddress = machineState.memory.get(this.addrOffset); + const calldata = machineState.memory.getSlice(this.argsOffset, this.argSize).map(f => new Fr(f.toBigInt())); + + const avmContext = AvmContext.prepExternalStaticCallContext( + new Fr(callAddress.toBigInt()), + calldata, + machineState.executionEnvironment, + journal, + ); + + const returnObject = await avmContext.call(); + const success = !returnObject.reverted; + + // We only take as much data as was specified in the return size -> TODO: should we be reverting here + const returnData = returnObject.output.slice(0, this.retSize); + const convertedReturnData = returnData.map(f => new Field(f)); + + // Write our return data into memory + machineState.memory.set(this.successOffset, new Field(success ? 1 : 0)); + machineState.memory.setSlice(this.retOffset, convertedReturnData); + + if (success) { + avmContext.mergeJournal(); + } + + this.incrementPc(machineState); + } +} diff --git a/yarn-project/acir-simulator/src/avm/opcodes/index.ts b/yarn-project/acir-simulator/src/avm/opcodes/index.ts index f1f479bab46..10ce6b4d0f7 100644 --- a/yarn-project/acir-simulator/src/avm/opcodes/index.ts +++ b/yarn-project/acir-simulator/src/avm/opcodes/index.ts @@ -1,6 +1,5 @@ export * from './arithmetic.js'; export * from './control_flow.js'; -export * from './call.js'; export * from './instruction.js'; export * from './comparators.js'; export * from './memory.js'; diff --git a/yarn-project/acir-simulator/src/avm/opcodes/instruction.ts b/yarn-project/acir-simulator/src/avm/opcodes/instruction.ts index c77a1a9ddd3..8eb73212fd2 100644 --- a/yarn-project/acir-simulator/src/avm/opcodes/instruction.ts +++ b/yarn-project/acir-simulator/src/avm/opcodes/instruction.ts @@ -1,14 +1,15 @@ import { AvmMachineState } from '../avm_machine_state.js'; -import { AvmStateManager } from '../avm_state_manager.js'; +import { TypeTag } from '../avm_memory_types.js'; +import { AvmJournal } from '../journal/index.js'; -export const AVM_OPERAND_BYTE_LENGTH = 4; -export const AVM_OPCODE_BYTE_LENGTH = 1; +export const AVM_OPERAND_BYTE_LENGTH = 4; // Keep in sync with cpp code +export const AVM_OPCODE_BYTE_LENGTH = 1; // Keep in sync with cpp code /** * Opcode base class */ export abstract class Instruction { - abstract execute(machineState: AvmMachineState, stateManager: AvmStateManager): void; + abstract execute(machineState: AvmMachineState, journal: AvmJournal): Promise; incrementPc(machineState: AvmMachineState): void { machineState.pc++; @@ -17,4 +18,38 @@ export abstract class Instruction { halt(machineState: AvmMachineState): void { machineState.halted = true; } + + revert(machineState: AvmMachineState): void { + machineState.halted = true; + machineState.reverted = true; + } + + static checkTags(machineState: AvmMachineState, tag: TypeTag, ...offsets: number[]) { + for (const offset of offsets) { + checkTag(machineState, tag, offset); + } + } + + static checkTagsRange(machineState: AvmMachineState, tag: TypeTag, startOffset: number, size: number) { + for (let offset = startOffset; offset < startOffset + size; offset++) { + checkTag(machineState, tag, offset); + } + } +} + +/** + * Checks that the memory at the given offset has the given tag. + */ +function checkTag(machineState: AvmMachineState, tag: TypeTag, offset: number) { + if (machineState.memory.getTag(offset) !== tag) { + const error = `Offset ${offset} has tag ${TypeTag[machineState.memory.getTag(offset)]}, expected ${TypeTag[tag]}`; + throw new InstructionExecutionError(error); + } +} + +export class InstructionExecutionError extends Error { + constructor(message: string) { + super(message); + this.name = 'InstructionExecutionError'; + } } diff --git a/yarn-project/acir-simulator/src/avm/opcodes/instruction_set.ts b/yarn-project/acir-simulator/src/avm/opcodes/instruction_set.ts index 664467f887d..e63fe02dc20 100644 --- a/yarn-project/acir-simulator/src/avm/opcodes/instruction_set.ts +++ b/yarn-project/acir-simulator/src/avm/opcodes/instruction_set.ts @@ -1,16 +1,16 @@ import { Add, Div, Mul, Sub } from './arithmetic.js'; import { And, Not, Or, Shl, Shr, Xor } from './bitwise.js'; -//import { Eq, Lt, Lte } from './comparators.js'; import { InternalCall, InternalReturn, Jump, JumpI, Return } from './control_flow.js'; +// import { Call } from './external_calls.js'; import { Instruction } from './instruction.js'; import { CMov, CalldataCopy, Cast, Mov, Set } from './memory.js'; import { Opcode } from './opcodes.js'; +//import { Eq, Lt, Lte } from './comparators.js'; +import { SLoad, SStore } from './storage.js'; -/** - */ type InstructionConstructor = new (...args: any[]) => Instruction; -/** - */ + type InstructionConstructorAndMembers = InstructionConstructor & { - /** - */ numberOfOperands: number; }; @@ -75,8 +75,8 @@ export const INSTRUCTION_SET: Map = ne //// World State //[Opcode.BLOCKHEADERBYNUMBER, Blockheaderbynumber], - //[Opcode.SLOAD, Sload], // Public Storage - //[Opcode.SSTORE, Sstore], // Public Storage + [Opcode.SLOAD, SLoad], // Public Storage + [Opcode.SSTORE, SStore], // Public Storage //[Opcode.READL1TOL2MSG, Readl1tol2msg], // Messages //[Opcode.SENDL2TOL1MSG, Sendl2tol1msg], // Messages //[Opcode.EMITNOTEHASH, Emitnotehash], // Notes & Nullifiers @@ -86,7 +86,7 @@ export const INSTRUCTION_SET: Map = ne //[Opcode.EMITUNENCRYPTEDLOG, Emitunencryptedlog], //// Control Flow - Contract Calls - //[Opcode.CALL, Call], + // [Opcode.CALL, Call], //[Opcode.STATICCALL, Staticcall], [Opcode.RETURN, Return], //[Opcode.REVERT, Revert], diff --git a/yarn-project/acir-simulator/src/avm/opcodes/memory.test.ts b/yarn-project/acir-simulator/src/avm/opcodes/memory.test.ts index 3b02ef36c32..1a10c5be15b 100644 --- a/yarn-project/acir-simulator/src/avm/opcodes/memory.test.ts +++ b/yarn-project/acir-simulator/src/avm/opcodes/memory.test.ts @@ -1,180 +1,289 @@ import { Fr } from '@aztec/foundation/fields'; -import { mock } from 'jest-mock-extended'; +import { MockProxy, mock } from 'jest-mock-extended'; import { AvmMachineState } from '../avm_machine_state.js'; -import { AvmStateManager } from '../avm_state_manager.js'; +import { Field, TypeTag, Uint8, Uint16, Uint32, Uint64, Uint128 } from '../avm_memory_types.js'; +import { initExecutionEnvironment } from '../fixtures/index.js'; +import { AvmJournal } from '../journal/journal.js'; import { CMov, CalldataCopy, Cast, Mov, Set } from './memory.js'; describe('Memory instructions', () => { let machineState: AvmMachineState; - let stateManager = mock(); + let journal: MockProxy; - beforeEach(() => { - machineState = new AvmMachineState([]); - stateManager = mock(); + beforeEach(async () => { + machineState = new AvmMachineState(initExecutionEnvironment()); + journal = mock(); }); - it('Should SET memory correctly', () => { - const value = 123456n; + describe('SET', () => { + it('should correctly set value and tag (uninitialized)', async () => { + await new Set(TypeTag.UINT16, /*value=*/ 1234n, /*offset=*/ 1).execute(machineState, journal); - new Set(value, 1).execute(machineState, stateManager); + const actual = machineState.memory.get(1); + const tag = machineState.memory.getTag(1); - const expected = new Fr(value); - const actual = machineState.readMemory(1); - expect(actual).toEqual(expected); - }); - - // TODO(https://github.com/AztecProtocol/aztec-packages/issues/3987): tags are not implemented yet - this will behave as a mov - describe('CAST', () => { - it('Should work correctly on different memory cells', () => { - const value = new Fr(123456n); - - machineState.writeMemory(0, value); - - new Cast(/*aOffset=*/ 0, /*dstOffset=*/ 1).execute(machineState, stateManager); - - const actual = machineState.readMemory(1); - expect(actual).toEqual(value); + expect(actual).toEqual(new Uint16(1234n)); + expect(tag).toEqual(TypeTag.UINT16); }); - it('Should work correctly on same memory cell', () => { - const value = new Fr(123456n); + it('should correctly set value and tag (overwriting)', async () => { + machineState.memory.set(1, new Field(27)); - machineState.writeMemory(0, value); + await new Set(TypeTag.UINT32, /*value=*/ 1234n, /*offset=*/ 1).execute(machineState, journal); - new Cast(/*aOffset=*/ 0, /*dstOffset=*/ 0).execute(machineState, stateManager); + const actual = machineState.memory.get(1); + const tag = machineState.memory.getTag(1); - const actual = machineState.readMemory(0); - expect(actual).toEqual(value); + expect(actual).toEqual(new Uint32(1234n)); + expect(tag).toEqual(TypeTag.UINT32); }); }); - describe('MOV', () => { - it('Should work correctly on different memory cells', () => { - const value = new Fr(123456n); - - machineState.writeMemory(0, value); + describe('CAST', () => { + it('Should upcast between integral types', () => { + machineState.memory.set(0, new Uint8(20n)); + machineState.memory.set(1, new Uint16(65000n)); + machineState.memory.set(2, new Uint32(1n << 30n)); + machineState.memory.set(3, new Uint64(1n << 50n)); + machineState.memory.set(4, new Uint128(1n << 100n)); + + [ + new Cast(TypeTag.UINT16, /*aOffset=*/ 0, /*dstOffset=*/ 10), + new Cast(TypeTag.UINT32, /*aOffset=*/ 1, /*dstOffset=*/ 11), + new Cast(TypeTag.UINT64, /*aOffset=*/ 2, /*dstOffset=*/ 12), + new Cast(TypeTag.UINT128, /*aOffset=*/ 3, /*dstOffset=*/ 13), + new Cast(TypeTag.UINT128, /*aOffset=*/ 4, /*dstOffset=*/ 14), + ].forEach(i => i.execute(machineState, journal)); + + const actual = machineState.memory.getSlice(/*offset=*/ 10, /*size=*/ 5); + expect(actual).toEqual([ + new Uint16(20n), + new Uint32(65000n), + new Uint64(1n << 30n), + new Uint128(1n << 50n), + new Uint128(1n << 100n), + ]); + const tags = machineState.memory.getSliceTags(/*offset=*/ 10, /*size=*/ 5); + expect(tags).toEqual([TypeTag.UINT16, TypeTag.UINT32, TypeTag.UINT64, TypeTag.UINT128, TypeTag.UINT128]); + }); - new Mov(/*aOffset=*/ 0, /*dstOffset=*/ 1).execute(machineState, stateManager); + it('Should downcast (truncating) between integral types', () => { + machineState.memory.set(0, new Uint8(20n)); + machineState.memory.set(1, new Uint16(65000n)); + machineState.memory.set(2, new Uint32((1n << 30n) - 1n)); + machineState.memory.set(3, new Uint64((1n << 50n) - 1n)); + machineState.memory.set(4, new Uint128((1n << 100n) - 1n)); + + [ + new Cast(TypeTag.UINT8, /*aOffset=*/ 0, /*dstOffset=*/ 10), + new Cast(TypeTag.UINT8, /*aOffset=*/ 1, /*dstOffset=*/ 11), + new Cast(TypeTag.UINT16, /*aOffset=*/ 2, /*dstOffset=*/ 12), + new Cast(TypeTag.UINT32, /*aOffset=*/ 3, /*dstOffset=*/ 13), + new Cast(TypeTag.UINT64, /*aOffset=*/ 4, /*dstOffset=*/ 14), + ].forEach(i => i.execute(machineState, journal)); + + const actual = machineState.memory.getSlice(/*offset=*/ 10, /*size=*/ 5); + expect(actual).toEqual([ + new Uint8(20n), + new Uint8(232), + new Uint16((1n << 16n) - 1n), + new Uint32((1n << 32n) - 1n), + new Uint64((1n << 64n) - 1n), + ]); + const tags = machineState.memory.getSliceTags(/*offset=*/ 10, /*size=*/ 5); + expect(tags).toEqual([TypeTag.UINT8, TypeTag.UINT8, TypeTag.UINT16, TypeTag.UINT32, TypeTag.UINT64]); + }); - const actual = machineState.readMemory(1); - expect(actual).toEqual(value); + it('Should upcast from integral types to field', () => { + machineState.memory.set(0, new Uint8(20n)); + machineState.memory.set(1, new Uint16(65000n)); + machineState.memory.set(2, new Uint32(1n << 30n)); + machineState.memory.set(3, new Uint64(1n << 50n)); + machineState.memory.set(4, new Uint128(1n << 100n)); + + [ + new Cast(TypeTag.FIELD, /*aOffset=*/ 0, /*dstOffset=*/ 10), + new Cast(TypeTag.FIELD, /*aOffset=*/ 1, /*dstOffset=*/ 11), + new Cast(TypeTag.FIELD, /*aOffset=*/ 2, /*dstOffset=*/ 12), + new Cast(TypeTag.FIELD, /*aOffset=*/ 3, /*dstOffset=*/ 13), + new Cast(TypeTag.FIELD, /*aOffset=*/ 4, /*dstOffset=*/ 14), + ].forEach(i => i.execute(machineState, journal)); + + const actual = machineState.memory.getSlice(/*offset=*/ 10, /*size=*/ 5); + expect(actual).toEqual([ + new Field(20n), + new Field(65000n), + new Field(1n << 30n), + new Field(1n << 50n), + new Field(1n << 100n), + ]); + const tags = machineState.memory.getSliceTags(/*offset=*/ 10, /*size=*/ 5); + expect(tags).toEqual([TypeTag.FIELD, TypeTag.FIELD, TypeTag.FIELD, TypeTag.FIELD, TypeTag.FIELD]); }); - it('Should work correctly on same memory cell', () => { - const value = new Fr(123456n); + it('Should downcast (truncating) from field to integral types', () => { + machineState.memory.set(0, new Field((1n << 200n) - 1n)); + machineState.memory.set(1, new Field((1n << 200n) - 1n)); + machineState.memory.set(2, new Field((1n << 200n) - 1n)); + machineState.memory.set(3, new Field((1n << 200n) - 1n)); + machineState.memory.set(4, new Field((1n << 200n) - 1n)); + + [ + new Cast(TypeTag.UINT8, /*aOffset=*/ 0, /*dstOffset=*/ 10), + new Cast(TypeTag.UINT16, /*aOffset=*/ 1, /*dstOffset=*/ 11), + new Cast(TypeTag.UINT32, /*aOffset=*/ 2, /*dstOffset=*/ 12), + new Cast(TypeTag.UINT64, /*aOffset=*/ 3, /*dstOffset=*/ 13), + new Cast(TypeTag.UINT128, /*aOffset=*/ 4, /*dstOffset=*/ 14), + ].forEach(i => i.execute(machineState, journal)); + + const actual = machineState.memory.getSlice(/*offset=*/ 10, /*size=*/ 5); + expect(actual).toEqual([ + new Uint8((1n << 8n) - 1n), + new Uint16((1n << 16n) - 1n), + new Uint32((1n << 32n) - 1n), + new Uint64((1n << 64n) - 1n), + new Uint128((1n << 128n) - 1n), + ]); + const tags = machineState.memory.getSliceTags(/*offset=*/ 10, /*size=*/ 5); + expect(tags).toEqual([TypeTag.UINT8, TypeTag.UINT16, TypeTag.UINT32, TypeTag.UINT64, TypeTag.UINT128]); + }); - machineState.writeMemory(0, value); + it('Should cast between field elements', async () => { + machineState.memory.set(0, new Field(12345678n)); - new Mov(/*aOffset=*/ 0, /*dstOffset=*/ 0).execute(machineState, stateManager); + await new Cast(TypeTag.FIELD, /*aOffset=*/ 0, /*dstOffset=*/ 1).execute(machineState, journal); - const actual = machineState.readMemory(0); - expect(actual).toEqual(value); + const actual = machineState.memory.get(1); + expect(actual).toEqual(new Field(12345678n)); + const tags = machineState.memory.getTag(1); + expect(tags).toEqual(TypeTag.FIELD); }); }); describe('MOV', () => { - it('Should move A if COND is true, on different memory cells', () => { - const valueA = new Fr(123456n); - const valueB = new Fr(80n); - const valueCondition = new Fr(22n); - - machineState.writeMemory(0, valueA); - machineState.writeMemory(1, valueB); - machineState.writeMemory(2, valueCondition); + it('Should move integrals on different memory cells', async () => { + machineState.memory.set(1, new Uint16(27)); + await new Mov(/*offsetA=*/ 1, /*offsetA=*/ 2).execute(machineState, journal); - new CMov(/*aOffset=*/ 0, /*bOffset=*/ 1, /*condOffset=*/ 2, /*dstOffset=*/ 3).execute(machineState, stateManager); + const actual = machineState.memory.get(2); + const tag = machineState.memory.getTag(2); - const actual = machineState.readMemory(3); - expect(actual).toEqual(valueA); + expect(actual).toEqual(new Uint16(27n)); + expect(tag).toEqual(TypeTag.UINT16); }); - it('Should move B if COND is false, on different memory cells', () => { - const valueA = new Fr(123456n); - const valueB = new Fr(80n); - const valueCondition = new Fr(0n); - - machineState.writeMemory(0, valueA); - machineState.writeMemory(1, valueB); - machineState.writeMemory(2, valueCondition); + it('Should move field elements on different memory cells', async () => { + machineState.memory.set(1, new Field(27)); + await new Mov(/*offsetA=*/ 1, /*offsetA=*/ 2).execute(machineState, journal); - new CMov(/*aOffset=*/ 0, /*bOffset=*/ 1, /*condOffset=*/ 2, /*dstOffset=*/ 3).execute(machineState, stateManager); + const actual = machineState.memory.get(2); + const tag = machineState.memory.getTag(2); - const actual = machineState.readMemory(3); - expect(actual).toEqual(valueB); + expect(actual).toEqual(new Field(27n)); + expect(tag).toEqual(TypeTag.FIELD); }); + }); - it('Should move A if COND is true, on overlapping memory cells', () => { - const valueA = new Fr(123456n); - const valueB = new Fr(80n); - const valueCondition = new Fr(22n); + describe('CMOV', () => { + it('Should move A if COND is true, on different memory cells (integral condition)', async () => { + machineState.memory.set(0, new Uint32(123)); // A + machineState.memory.set(1, new Uint16(456)); // B + machineState.memory.set(2, new Uint8(2)); // Condition + + await new CMov(/*aOffset=*/ 0, /*bOffset=*/ 1, /*condOffset=*/ 2, /*dstOffset=*/ 3).execute( + machineState, + journal, + ); + + const actual = machineState.memory.get(3); + const tag = machineState.memory.getTag(3); + expect(actual).toEqual(new Uint32(123)); + expect(tag).toEqual(TypeTag.UINT32); + }); - machineState.writeMemory(0, valueA); - machineState.writeMemory(1, valueB); - machineState.writeMemory(2, valueCondition); + it('Should move B if COND is false, on different memory cells (integral condition)', async () => { + machineState.memory.set(0, new Uint32(123)); // A + machineState.memory.set(1, new Uint16(456)); // B + machineState.memory.set(2, new Uint8(0)); // Condition - new CMov(/*aOffset=*/ 0, /*bOffset=*/ 1, /*condOffset=*/ 2, /*dstOffset=*/ 2).execute(machineState, stateManager); + await new CMov(/*aOffset=*/ 0, /*bOffset=*/ 1, /*condOffset=*/ 2, /*dstOffset=*/ 3).execute( + machineState, + journal, + ); - const actual = machineState.readMemory(2); - expect(actual).toEqual(valueA); + const actual = machineState.memory.get(3); + const tag = machineState.memory.getTag(3); + expect(actual).toEqual(new Uint16(456)); + expect(tag).toEqual(TypeTag.UINT16); }); - it('Should move B if COND is false, on overlapping memory cells', () => { - const valueA = new Fr(123456n); - const valueB = new Fr(80n); - const valueCondition = new Fr(0n); + it('Should move A if COND is true, on different memory cells (field condition)', async () => { + machineState.memory.set(0, new Uint32(123)); // A + machineState.memory.set(1, new Uint16(456)); // B + machineState.memory.set(2, new Field(1)); // Condition - machineState.writeMemory(0, valueA); - machineState.writeMemory(1, valueB); - machineState.writeMemory(2, valueCondition); + await new CMov(/*aOffset=*/ 0, /*bOffset=*/ 1, /*condOffset=*/ 2, /*dstOffset=*/ 3).execute( + machineState, + journal, + ); - new CMov(/*aOffset=*/ 0, /*bOffset=*/ 1, /*condOffset=*/ 2, /*dstOffset=*/ 2).execute(machineState, stateManager); + const actual = machineState.memory.get(3); + const tag = machineState.memory.getTag(3); + expect(actual).toEqual(new Uint32(123)); + expect(tag).toEqual(TypeTag.UINT32); + }); - const actual = machineState.readMemory(2); - expect(actual).toEqual(valueB); + it('Should move B if COND is false, on different memory cells (integral condition)', async () => { + machineState.memory.set(0, new Uint32(123)); // A + machineState.memory.set(1, new Uint16(456)); // B + machineState.memory.set(2, new Field(0)); // Condition + + await new CMov(/*aOffset=*/ 0, /*bOffset=*/ 1, /*condOffset=*/ 2, /*dstOffset=*/ 3).execute( + machineState, + journal, + ); + + const actual = machineState.memory.get(3); + const tag = machineState.memory.getTag(3); + expect(actual).toEqual(new Uint16(456)); + expect(tag).toEqual(TypeTag.UINT16); }); }); - describe('CALLDATA', () => { - it('Writes nothing if size is 0', () => { - const previousValue = new Fr(123456n); + describe('CALLDATACOPY', () => { + it('Writes nothing if size is 0', async () => { const calldata = [new Fr(1n), new Fr(2n), new Fr(3n)]; + machineState = new AvmMachineState(initExecutionEnvironment({ calldata })); + machineState.memory.set(0, new Uint16(12)); // Some previous data to be overwritten - machineState = new AvmMachineState(calldata); - machineState.writeMemory(0, previousValue); - - new CalldataCopy(/*cdOffset=*/ 2, /*copySize=*/ 0, /*dstOffset=*/ 0).execute(machineState, stateManager); + await new CalldataCopy(/*cdOffset=*/ 0, /*copySize=*/ 0, /*dstOffset=*/ 0).execute(machineState, journal); - const actual = machineState.readMemory(0); - expect(actual).toEqual(previousValue); + const actual = machineState.memory.get(0); + expect(actual).toEqual(new Uint16(12)); }); - it('Copies all calldata', () => { - const previousValue = new Fr(123456n); + it('Copies all calldata', async () => { const calldata = [new Fr(1n), new Fr(2n), new Fr(3n)]; + machineState = new AvmMachineState(initExecutionEnvironment({ calldata })); + machineState.memory.set(0, new Uint16(12)); // Some previous data to be overwritten - machineState = new AvmMachineState(calldata); - machineState.writeMemory(0, previousValue); + await new CalldataCopy(/*cdOffset=*/ 0, /*copySize=*/ 3, /*dstOffset=*/ 0).execute(machineState, journal); - new CalldataCopy(/*cdOffset=*/ 0, /*copySize=*/ 3, /*dstOffset=*/ 0).execute(machineState, stateManager); - - const actual = machineState.readMemoryChunk(/*offset=*/ 0, /*size=*/ 3); - expect(actual).toEqual(calldata); + const actual = machineState.memory.getSlice(/*offset=*/ 0, /*size=*/ 3); + expect(actual).toEqual([new Field(1), new Field(2), new Field(3)]); }); - it('Copies slice of calldata', () => { - const previousValue = new Fr(123456n); + it('Copies slice of calldata', async () => { const calldata = [new Fr(1n), new Fr(2n), new Fr(3n)]; + machineState = new AvmMachineState(initExecutionEnvironment({ calldata })); + machineState.memory.set(0, new Uint16(12)); // Some previous data to be overwritten - machineState = new AvmMachineState(calldata); - machineState.writeMemory(0, previousValue); - - new CalldataCopy(/*cdOffset=*/ 1, /*copySize=*/ 2, /*dstOffset=*/ 0).execute(machineState, stateManager); + await new CalldataCopy(/*cdOffset=*/ 1, /*copySize=*/ 2, /*dstOffset=*/ 0).execute(machineState, journal); - const expected = calldata.slice(1); - const actual = machineState.readMemoryChunk(/*offset=*/ 0, /*size=*/ 2); - expect(actual).toEqual(expected); + const actual = machineState.memory.getSlice(/*offset=*/ 0, /*size=*/ 2); + expect(actual).toEqual([new Field(2), new Field(3)]); }); // TODO: check bad cases (i.e., out of bounds) diff --git a/yarn-project/acir-simulator/src/avm/opcodes/memory.ts b/yarn-project/acir-simulator/src/avm/opcodes/memory.ts index 3e6bbc62afd..2524684dc02 100644 --- a/yarn-project/acir-simulator/src/avm/opcodes/memory.ts +++ b/yarn-project/acir-simulator/src/avm/opcodes/memory.ts @@ -1,99 +1,96 @@ -import { Fr } from '@aztec/foundation/fields'; - import { AvmMachineState } from '../avm_machine_state.js'; -import { AvmStateManager } from '../avm_state_manager.js'; +import { Field, TaggedMemory, TypeTag } from '../avm_memory_types.js'; +import { AvmJournal } from '../journal/index.js'; import { Instruction } from './instruction.js'; -/** - */ export class Set extends Instruction { static type: string = 'SET'; - static numberOfOperands = 2; + static numberOfOperands = 3; - constructor(private value: bigint, private destOffset: number) { + constructor(private inTag: TypeTag, private value: bigint, private dstOffset: number) { super(); } - execute(machineState: AvmMachineState, _stateManager: AvmStateManager): void { - machineState.writeMemory(this.destOffset, new Fr(this.value)); + async execute(machineState: AvmMachineState, _journal: AvmJournal): Promise { + const res = TaggedMemory.integralFromTag(this.value, this.inTag); + + machineState.memory.set(this.dstOffset, res); this.incrementPc(machineState); } } -// TODO(https://github.com/AztecProtocol/aztec-packages/issues/3987): tags are not implemented yet - this will behave as a mov -/** - */ export class Cast extends Instruction { static type: string = 'CAST'; - static numberOfOperands = 2; + static numberOfOperands = 3; - constructor(private aOffset: number, private destOffset: number) { + constructor(private dstTag: TypeTag, private aOffset: number, private dstOffset: number) { super(); } - execute(machineState: AvmMachineState, _stateManager: AvmStateManager): void { - const a = machineState.readMemory(this.aOffset); + async execute(machineState: AvmMachineState, _journal: AvmJournal): Promise { + const a = machineState.memory.get(this.aOffset); - machineState.writeMemory(this.destOffset, a); + // TODO: consider not using toBigInt() + const casted = + this.dstTag == TypeTag.FIELD ? new Field(a.toBigInt()) : TaggedMemory.integralFromTag(a.toBigInt(), this.dstTag); + + machineState.memory.set(this.dstOffset, casted); this.incrementPc(machineState); } } -/** - */ export class Mov extends Instruction { static type: string = 'MOV'; static numberOfOperands = 2; - constructor(private aOffset: number, private destOffset: number) { + constructor(private aOffset: number, private dstOffset: number) { super(); } - execute(machineState: AvmMachineState, _stateManager: AvmStateManager): void { - const a = machineState.readMemory(this.aOffset); + async execute(machineState: AvmMachineState, _journal: AvmJournal): Promise { + const a = machineState.memory.get(this.aOffset); - machineState.writeMemory(this.destOffset, a); + machineState.memory.set(this.dstOffset, a); this.incrementPc(machineState); } } -/** - */ export class CMov extends Instruction { - static type: string = 'MOV'; + static type: string = 'CMOV'; static numberOfOperands = 4; - constructor( - private aOffset: number, - private bOffset: number, - private condOffset: number, - private destOffset: number, - ) { + constructor(private aOffset: number, private bOffset: number, private condOffset: number, private dstOffset: number) { super(); } - execute(machineState: AvmMachineState, _stateManager: AvmStateManager): void { - const a = machineState.readMemory(this.aOffset); - const b = machineState.readMemory(this.bOffset); - const cond = machineState.readMemory(this.condOffset); + async execute(machineState: AvmMachineState, _journal: AvmJournal): Promise { + const a = machineState.memory.get(this.aOffset); + const b = machineState.memory.get(this.bOffset); + const cond = machineState.memory.get(this.condOffset); - machineState.writeMemory(this.destOffset, cond.toBigInt() ? a : b); + // TODO: reconsider toBigInt() here + machineState.memory.set(this.dstOffset, cond.toBigInt() > 0 ? a : b); this.incrementPc(machineState); } } -/** - */ export class CalldataCopy extends Instruction { static type: string = 'CALLDATACOPY'; static numberOfOperands = 3; - constructor(private cdOffset: number, private copySize: number, private destOffset: number) { + constructor(private cdOffset: number, private copySize: number, private dstOffset: number) { super(); } - execute(machineState: AvmMachineState, _stateManager: AvmStateManager): void { - const calldata = machineState.calldata.slice(this.cdOffset, this.cdOffset + this.copySize); - machineState.writeMemoryChunk(this.destOffset, calldata); + async execute(machineState: AvmMachineState, _journal: AvmJournal): Promise { + const transformedData = machineState.executionEnvironment.calldata + .slice(this.cdOffset, this.cdOffset + this.copySize) + .map(f => new Field(f)); + machineState.memory.setSlice(this.dstOffset, transformedData); this.incrementPc(machineState); } diff --git a/yarn-project/acir-simulator/src/avm/opcodes/opcodes.ts b/yarn-project/acir-simulator/src/avm/opcodes/opcodes.ts index 6f3f3709007..97bdfa3181d 100644 --- a/yarn-project/acir-simulator/src/avm/opcodes/opcodes.ts +++ b/yarn-project/acir-simulator/src/avm/opcodes/opcodes.ts @@ -1,5 +1,5 @@ /** - * All AVM opcodes. + * All AVM opcodes. (Keep in sync with cpp counterpart code AvmMini_opcode.hpp). * Source: https://yp-aztec.netlify.app/docs/public-vm/instruction-set */ export enum Opcode { diff --git a/yarn-project/acir-simulator/src/avm/opcodes/storage.test.ts b/yarn-project/acir-simulator/src/avm/opcodes/storage.test.ts new file mode 100644 index 00000000000..e62b0abc561 --- /dev/null +++ b/yarn-project/acir-simulator/src/avm/opcodes/storage.test.ts @@ -0,0 +1,68 @@ +import { AztecAddress } from '@aztec/foundation/aztec-address'; +import { Fr } from '@aztec/foundation/fields'; + +import { MockProxy, mock } from 'jest-mock-extended'; + +import { AvmMachineState } from '../avm_machine_state.js'; +import { Field } from '../avm_memory_types.js'; +import { initExecutionEnvironment } from '../fixtures/index.js'; +import { AvmJournal } from '../journal/journal.js'; +import { SLoad, SStore, StaticCallStorageAlterError } from './storage.js'; + +describe('Storage Instructions', () => { + let journal: MockProxy; + let machineState: AvmMachineState; + const address = AztecAddress.random(); + + beforeEach(() => { + journal = mock(); + + const executionEnvironment = initExecutionEnvironment({ address, storageAddress: address }); + machineState = new AvmMachineState(executionEnvironment); + }); + + it('Sstore should Write into storage', async () => { + const a = new Field(1n); + const b = new Field(2n); + + machineState.memory.set(0, a); + machineState.memory.set(1, b); + + await new SStore(0, 1).execute(machineState, journal); + + expect(journal.writeStorage).toBeCalledWith(address, new Fr(a.toBigInt()), new Fr(b.toBigInt())); + }); + + it('Should not be able to write to storage in a static call', async () => { + const executionEnvironment = initExecutionEnvironment({ isStaticCall: true }); + machineState = new AvmMachineState(executionEnvironment); + + const a = new Field(1n); + const b = new Field(2n); + + machineState.memory.set(0, a); + machineState.memory.set(1, b); + + const instruction = () => new SStore(0, 1).execute(machineState, journal); + await expect(instruction()).rejects.toThrowError(StaticCallStorageAlterError); + }); + + it('Sload should Read into storage', async () => { + // Mock response + const expectedResult = new Fr(1n); + journal.readStorage.mockReturnValueOnce(Promise.resolve(expectedResult)); + + const a = new Field(1n); + const b = new Field(2n); + + machineState.memory.set(0, a); + machineState.memory.set(1, b); + + await new SLoad(0, 1).execute(machineState, journal); + + expect(journal.readStorage).toBeCalledWith(address, new Fr(a.toBigInt())); + + const actual = machineState.memory.get(1); + expect(actual).toEqual(new Field(expectedResult)); + }); +}); diff --git a/yarn-project/acir-simulator/src/avm/opcodes/storage.ts b/yarn-project/acir-simulator/src/avm/opcodes/storage.ts new file mode 100644 index 00000000000..c226522b3db --- /dev/null +++ b/yarn-project/acir-simulator/src/avm/opcodes/storage.ts @@ -0,0 +1,65 @@ +import { Fr } from '@aztec/foundation/fields'; + +import { AvmMachineState } from '../avm_machine_state.js'; +import { Field } from '../avm_memory_types.js'; +import { AvmInterpreterError } from '../interpreter/interpreter.js'; +import { AvmJournal } from '../journal/journal.js'; +import { Instruction } from './instruction.js'; + +export class SStore extends Instruction { + static type: string = 'SSTORE'; + static numberOfOperands = 2; + + constructor(private slotOffset: number, private dataOffset: number) { + super(); + } + + async execute(machineState: AvmMachineState, journal: AvmJournal): Promise { + if (machineState.executionEnvironment.isStaticCall) { + throw new StaticCallStorageAlterError(); + } + + const slot = machineState.memory.get(this.slotOffset); + const data = machineState.memory.get(this.dataOffset); + + journal.writeStorage( + machineState.executionEnvironment.storageAddress, + new Fr(slot.toBigInt()), + new Fr(data.toBigInt()), + ); + + this.incrementPc(machineState); + } +} + +export class SLoad extends Instruction { + static type: string = 'SLOAD'; + static numberOfOperands = 2; + + constructor(private slotOffset: number, private dstOffset: number) { + super(); + } + + async execute(machineState: AvmMachineState, journal: AvmJournal): Promise { + const slot = machineState.memory.get(this.slotOffset); + + const data: Fr = await journal.readStorage( + machineState.executionEnvironment.storageAddress, + new Fr(slot.toBigInt()), + ); + + machineState.memory.set(this.dstOffset, new Field(data)); + + this.incrementPc(machineState); + } +} + +/** + * Error is thrown when a static call attempts to alter storage + */ +export class StaticCallStorageAlterError extends AvmInterpreterError { + constructor() { + super('Static calls cannot alter storage'); + this.name = 'StaticCallStorageAlterError'; + } +} diff --git a/yarn-project/acir-simulator/src/client/client_execution_context.ts b/yarn-project/acir-simulator/src/client/client_execution_context.ts index 062cfdf4def..f3d684f4ef0 100644 --- a/yarn-project/acir-simulator/src/client/client_execution_context.ts +++ b/yarn-project/acir-simulator/src/client/client_execution_context.ts @@ -1,16 +1,16 @@ -import { AuthWitness, FunctionL2Logs, L1NotePayload, Note, UnencryptedL2Log } from '@aztec/circuit-types'; +import { AuthWitness, FunctionL2Logs, L1NotePayload, Note, NoteStatus, UnencryptedL2Log } from '@aztec/circuit-types'; import { - BlockHeader, CallContext, ContractDeploymentData, FunctionData, FunctionSelector, + Header, PublicCallRequest, ReadRequestMembershipWitness, SideEffect, TxContext, } from '@aztec/circuits.js'; -import { computeUniqueCommitment, siloCommitment } from '@aztec/circuits.js/abis'; +import { computePublicDataTreeLeafSlot, computeUniqueCommitment, siloCommitment } from '@aztec/circuits.js/abis'; import { Grumpkin } from '@aztec/circuits.js/barretenberg'; import { FunctionAbi, FunctionArtifact, countArgumentsSize } from '@aztec/foundation/abi'; import { AztecAddress } from '@aztec/foundation/aztec-address'; @@ -19,9 +19,9 @@ import { createDebugLogger } from '@aztec/foundation/log'; import { NoteData, - toACVMBlockHeader, toACVMCallContext, toACVMContractDeploymentData, + toACVMHeader, toACVMWitness, } from '../acvm/index.js'; import { PackedArgsCache } from '../common/packed_args_cache.js'; @@ -64,8 +64,8 @@ export class ClientExecutionContext extends ViewDataOracle { private readonly argsHash: Fr, private readonly txContext: TxContext, private readonly callContext: CallContext, - /** Data required to reconstruct the block hash, it contains historical roots. */ - protected readonly blockHeader: BlockHeader, + /** Header of a block whose state is used during private execution (not the block the transaction is included in). */ + protected readonly historicalHeader: Header, /** List of transient auth witnesses to be used during this simulation */ protected readonly authWitnesses: AuthWitness[], private readonly packedArgsCache: PackedArgsCache, @@ -74,7 +74,7 @@ export class ClientExecutionContext extends ViewDataOracle { private readonly curve: Grumpkin, protected log = createDebugLogger('aztec:simulator:client_execution_context'), ) { - super(contractAddress, blockHeader, authWitnesses, db, undefined, log); + super(contractAddress, authWitnesses, db, undefined, log); } // We still need this function until we can get user-defined ordering of structs for fn arguments @@ -97,7 +97,7 @@ export class ClientExecutionContext extends ViewDataOracle { const fields = [ ...toACVMCallContext(this.callContext), - ...toACVMBlockHeader(this.blockHeader), + ...toACVMHeader(this.historicalHeader), ...toACVMContractDeploymentData(contractDeploymentData), this.txContext.chainId, @@ -185,10 +185,12 @@ export class ClientExecutionContext extends ViewDataOracle { * @param numSelects - The number of valid selects in selectBy and selectValues. * @param selectBy - An array of indices of the fields to selects. * @param selectValues - The values to match. + * @param selectComparators - The comparators to match by. * @param sortBy - An array of indices of the fields to sort. * @param sortOrder - The order of the corresponding index in sortBy. (1: DESC, 2: ASC, 0: Do nothing) * @param limit - The number of notes to retrieve per query. * @param offset - The starting index for pagination. + * @param status - The status of notes to fetch. * @returns Array of note data. */ public async getNotes( @@ -196,20 +198,24 @@ export class ClientExecutionContext extends ViewDataOracle { numSelects: number, selectBy: number[], selectValues: Fr[], + selectComparators: number[], sortBy: number[], sortOrder: number[], limit: number, offset: number, + status: NoteStatus, ): Promise { // Nullified pending notes are already removed from the list. const pendingNotes = this.noteCache.getNotes(this.contractAddress, storageSlot); const pendingNullifiers = this.noteCache.getNullifiers(this.contractAddress); - const dbNotes = await this.db.getNotes(this.contractAddress, storageSlot); + const dbNotes = await this.db.getNotes(this.contractAddress, storageSlot, status); const dbNotesFiltered = dbNotes.filter(n => !pendingNullifiers.has((n.siloedNullifier as Fr).value)); const notes = pickNotes([...dbNotesFiltered, ...pendingNotes], { - selects: selectBy.slice(0, numSelects).map((index, i) => ({ index, value: selectValues[i] })), + selects: selectBy + .slice(0, numSelects) + .map((index, i) => ({ index, value: selectValues[i], comparator: selectComparators[i] })), sorts: sortBy.map((index, i) => ({ index, order: sortOrder[i] })), limit, offset, @@ -337,7 +343,7 @@ export class ClientExecutionContext extends ViewDataOracle { argsHash, derivedTxContext, derivedCallContext, - this.blockHeader, + this.historicalHeader, this.authWitnesses, this.packedArgsCache, this.noteCache, @@ -430,4 +436,29 @@ export class ClientExecutionContext extends ViewDataOracle { startSideEffectCounter, ); } + + /** + * Read the public storage data. + * @param startStorageSlot - The starting storage slot. + * @param numberOfElements - Number of elements to read from the starting storage slot. + */ + public async storageRead(startStorageSlot: Fr, numberOfElements: number): Promise { + // TODO(#4320): This is a hack to work around not having directly access to the public data tree but + // still having access to the witnesses + const bn = await this.db.getBlockNumber(); + + const values = []; + for (let i = 0n; i < numberOfElements; i++) { + const storageSlot = new Fr(startStorageSlot.value + i); + const leafSlot = computePublicDataTreeLeafSlot(this.contractAddress, storageSlot); + const witness = await this.db.getPublicDataTreeWitness(bn, leafSlot); + if (!witness) { + throw new Error(`No witness for slot ${storageSlot.toString()}`); + } + const value = witness.leafPreimage.value; + this.log(`Oracle storage read: slot=${storageSlot.toString()} value=${value}`); + values.push(value); + } + return values; + } } diff --git a/yarn-project/acir-simulator/src/client/db_oracle.ts b/yarn-project/acir-simulator/src/client/db_oracle.ts index 7090aa52300..cccf02838b5 100644 --- a/yarn-project/acir-simulator/src/client/db_oracle.ts +++ b/yarn-project/acir-simulator/src/client/db_oracle.ts @@ -1,5 +1,5 @@ -import { L2Block, MerkleTreeId, NullifierMembershipWitness, PublicDataWitness } from '@aztec/circuit-types'; -import { BlockHeader, CompleteAddress } from '@aztec/circuits.js'; +import { L2Block, MerkleTreeId, NoteStatus, NullifierMembershipWitness, PublicDataWitness } from '@aztec/circuit-types'; +import { CompleteAddress, Header } from '@aztec/circuits.js'; import { FunctionArtifactWithDebugMetadata, FunctionSelector } from '@aztec/foundation/abi'; import { AztecAddress } from '@aztec/foundation/aztec-address'; import { EthAddress } from '@aztec/foundation/eth-address'; @@ -61,9 +61,10 @@ export interface DBOracle extends CommitmentsDB { * * @param contractAddress - The AztecAddress instance representing the contract address. * @param storageSlot - The Fr instance representing the storage slot of the notes. + * @param status - The status of notes to fetch. * @returns A Promise that resolves to an array of note data. */ - getNotes(contractAddress: AztecAddress, storageSlot: Fr): Promise; + getNotes(contractAddress: AztecAddress, storageSlot: Fr, status: NoteStatus): Promise; /** * Retrieve the artifact information of a specific function within a contract. @@ -111,9 +112,9 @@ export interface DBOracle extends CommitmentsDB { * Retrieve the databases view of the Block Header object. * This structure is fed into the circuits simulator and is used to prove against certain historical roots. * - * @returns A Promise that resolves to a BlockHeader object. + * @returns A Promise that resolves to a Header object. */ - getBlockHeader(): Promise; + getHeader(): Promise
; /** * Fetch the index of the leaf in the respective tree diff --git a/yarn-project/acir-simulator/src/client/pick_notes.test.ts b/yarn-project/acir-simulator/src/client/pick_notes.test.ts index bf2ad610718..ccd73a4a117 100644 --- a/yarn-project/acir-simulator/src/client/pick_notes.test.ts +++ b/yarn-project/acir-simulator/src/client/pick_notes.test.ts @@ -1,4 +1,4 @@ -import { Note } from '@aztec/circuit-types'; +import { Comparator, Note } from '@aztec/circuit-types'; import { Fr } from '@aztec/foundation/fields'; import { SortOrder, pickNotes } from './pick_notes.js'; @@ -141,7 +141,7 @@ describe('getNotes', () => { ]; { - const options = { selects: [{ index: 0, value: new Fr(2n) }] }; + const options = { selects: [{ index: 0, value: new Fr(2n), comparator: Comparator.EQ }] }; const result = pickNotes(notes, options); expectNotes(result, [ [2n, 1n, 3n], @@ -153,8 +153,8 @@ describe('getNotes', () => { { const options = { selects: [ - { index: 0, value: new Fr(2n) }, - { index: 2, value: new Fr(3n) }, + { index: 0, value: new Fr(2n), comparator: Comparator.EQ }, + { index: 2, value: new Fr(3n), comparator: Comparator.EQ }, ], }; const result = pickNotes(notes, options); @@ -167,8 +167,8 @@ describe('getNotes', () => { { const options = { selects: [ - { index: 1, value: new Fr(2n) }, - { index: 2, value: new Fr(3n) }, + { index: 1, value: new Fr(2n), comparator: Comparator.EQ }, + { index: 2, value: new Fr(3n), comparator: Comparator.EQ }, ], }; const result = pickNotes(notes, options); @@ -176,7 +176,7 @@ describe('getNotes', () => { } { - const options = { selects: [{ index: 1, value: new Fr(5n) }] }; + const options = { selects: [{ index: 1, value: new Fr(5n), comparator: Comparator.EQ }] }; const result = pickNotes(notes, options); expectNotes(result, []); } @@ -184,8 +184,8 @@ describe('getNotes', () => { { const options = { selects: [ - { index: 0, value: new Fr(2n) }, - { index: 1, value: new Fr(5n) }, + { index: 0, value: new Fr(2n), comparator: Comparator.EQ }, + { index: 1, value: new Fr(5n), comparator: Comparator.EQ }, ], }; const result = pickNotes(notes, options); @@ -203,7 +203,10 @@ describe('getNotes', () => { createNote([6n, 5n, 8n]), ]; - const options = { selects: [{ index: 2, value: new Fr(8n) }], sorts: [{ index: 1, order: SortOrder.ASC }] }; + const options = { + selects: [{ index: 2, value: new Fr(8n), comparator: Comparator.EQ }], + sorts: [{ index: 1, order: SortOrder.ASC }], + }; const result = pickNotes(notes, options); expectNotes(result, [ [0n, 0n, 8n], @@ -212,4 +215,102 @@ describe('getNotes', () => { [7n, 6n, 8n], ]); }); + + it('should get sorted matching notes with GTE and LTE', () => { + const notes = [ + createNote([2n, 1n, 3n]), + createNote([4n, 5n, 8n]), + createNote([7n, 6n, 8n]), + createNote([6n, 5n, 2n]), + createNote([0n, 0n, 8n]), + createNote([6n, 5n, 8n]), + ]; + + const options = { + selects: [ + { + index: 2, + value: new Fr(7n), + comparator: Comparator.GTE, + }, + { + index: 2, + value: new Fr(8n), + comparator: Comparator.LTE, + }, + ], + sorts: [ + { + index: 1, + order: SortOrder.ASC, + }, + ], + }; + const result = pickNotes(notes, options); + expectNotes(result, [ + [0n, 0n, 8n], + [4n, 5n, 8n], + [6n, 5n, 8n], + [7n, 6n, 8n], + ]); + }); + + it('should get sorted matching notes with GTE and LTE', () => { + const notes = [ + createNote([2n, 1n, 1n]), + createNote([4n, 5n, 2n]), + createNote([7n, 6n, 3n]), + createNote([6n, 5n, 4n]), + createNote([0n, 0n, 5n]), + createNote([6n, 5n, 6n]), + ]; + + const options1 = { + selects: [ + { + index: 2, + value: new Fr(3n), + comparator: Comparator.GT, + }, + ], + sorts: [ + { + index: 1, + order: SortOrder.ASC, + }, + ], + }; + + const result1 = pickNotes(notes, options1); + + expectNotes(result1, [ + [0n, 0n, 5n], + [6n, 5n, 4n], + [6n, 5n, 6n], + ]); + + const options2 = { + selects: [ + { + index: 2, + value: new Fr(4n), + comparator: Comparator.LT, + }, + ], + sorts: [ + { + index: 1, + order: SortOrder.ASC, + }, + ], + }; + + const result2 = pickNotes(notes, options2); + + expectNotes(result2, [ + [2n, 1n, 1n], + [4n, 5n, 2n], + [7n, 6n, 3n], + ]); + }); }); diff --git a/yarn-project/acir-simulator/src/client/pick_notes.ts b/yarn-project/acir-simulator/src/client/pick_notes.ts index a964cf35177..d054a226e39 100644 --- a/yarn-project/acir-simulator/src/client/pick_notes.ts +++ b/yarn-project/acir-simulator/src/client/pick_notes.ts @@ -1,4 +1,4 @@ -import { Note } from '@aztec/circuit-types'; +import { Comparator, Note } from '@aztec/circuit-types'; import { Fr } from '@aztec/foundation/fields'; /** @@ -13,6 +13,10 @@ export interface Select { * Required value of the field. */ value: Fr; + /** + * The comparator to use + */ + comparator: Comparator; } /** @@ -75,7 +79,20 @@ interface ContainsNote { } const selectNotes = (noteDatas: T[], selects: Select[]): T[] => - noteDatas.filter(noteData => selects.every(({ index, value }) => noteData.note.items[index]?.equals(value))); + noteDatas.filter(noteData => + selects.every(({ index, value, comparator }) => { + const comparatorSelector = { + [Comparator.EQ]: () => noteData.note.items[index].equals(value), + [Comparator.NEQ]: () => !noteData.note.items[index].equals(value), + [Comparator.LT]: () => noteData.note.items[index].lt(value), + [Comparator.LTE]: () => noteData.note.items[index].lt(value) || noteData.note.items[index].equals(value), + [Comparator.GT]: () => !noteData.note.items[index].lt(value) && !noteData.note.items[index].equals(value), + [Comparator.GTE]: () => !noteData.note.items[index].lt(value), + }; + + return comparatorSelector[comparator](); + }), + ); const sortNotes = (a: Fr[], b: Fr[], sorts: Sort[], level = 0): number => { if (sorts[level] === undefined) { 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 01b8c34ac75..0587f3ec4dd 100644 --- a/yarn-project/acir-simulator/src/client/private_execution.test.ts +++ b/yarn-project/acir-simulator/src/client/private_execution.test.ts @@ -1,14 +1,17 @@ -import { Note, PackedArguments, TxExecutionRequest } from '@aztec/circuit-types'; +import { L1ToL2Message, Note, PackedArguments, TxExecutionRequest } from '@aztec/circuit-types'; import { - BlockHeader, + AppendOnlyTreeSnapshot, CallContext, CompleteAddress, ContractDeploymentData, FunctionData, + Header, L1_TO_L2_MSG_TREE_HEIGHT, MAX_NEW_COMMITMENTS_PER_CALL, NOTE_HASH_TREE_HEIGHT, + PartialStateReference, PublicCallRequest, + StateReference, TxContext, computeNullifierSecretKey, computeSiloedNullifierSecretKey, @@ -37,6 +40,7 @@ import { EthAddress } from '@aztec/foundation/eth-address'; import { Fr, GrumpkinScalar } from '@aztec/foundation/fields'; import { DebugLogger, createDebugLogger } from '@aztec/foundation/log'; import { FieldsOf } from '@aztec/foundation/types'; +import { AztecLmdbStore } from '@aztec/kv-store'; import { AppendOnlyTree, Pedersen, StandardTree, newTree } from '@aztec/merkle-tree'; import { ChildContractArtifact, @@ -50,8 +54,6 @@ import { import { jest } from '@jest/globals'; import { MockProxy, mock } from 'jest-mock-extended'; -import { default as levelup } from 'levelup'; -import { type MemDown, default as memdown } from 'memdown'; import { getFunctionSelector } from 'viem'; import { KeyPair } from '../acvm/index.js'; @@ -62,13 +64,11 @@ import { AcirSimulator } from './simulator.js'; jest.setTimeout(60_000); -const createMemDown = () => (memdown as any)() as MemDown; - describe('Private Execution test suite', () => { let oracle: MockProxy; let acirSimulator: AcirSimulator; - let blockHeader = BlockHeader.empty(); + let header = Header.empty(); let logger: DebugLogger; const defaultContractAddress = AztecAddress.random(); @@ -86,7 +86,7 @@ describe('Private Execution test suite', () => { l1ToL2Messages: L1_TO_L2_MSG_TREE_HEIGHT, }; - const trees: { [name: keyof typeof treeHeights]: AppendOnlyTree } = {}; + let trees: { [name: keyof typeof treeHeights]: AppendOnlyTree } = {}; const txContextFields: FieldsOf = { isContractDeploymentTx: false, isFeePaymentTx: false, @@ -136,18 +136,40 @@ describe('Private Execution test suite', () => { throw new Error(`Unknown tree ${name}`); } if (!trees[name]) { - const db = levelup(createMemDown()); + const db = await AztecLmdbStore.openTmp(); const pedersen = new Pedersen(); trees[name] = await newTree(StandardTree, db, pedersen, name, treeHeights[name]); } - await trees[name].appendLeaves(leaves.map(l => l.toBuffer())); - - // Update root. - const newRoot = trees[name].getRoot(false); - const prevRoots = blockHeader.toBuffer(); - const rootIndex = name === 'noteHash' ? 0 : 32 * 3; - const newRoots = Buffer.concat([prevRoots.subarray(0, rootIndex), newRoot, prevRoots.subarray(rootIndex + 32)]); - blockHeader = BlockHeader.fromBuffer(newRoots); + const tree = trees[name]; + + await tree.appendLeaves(leaves.map(l => l.toBuffer())); + + // Create a new snapshot. + const newSnap = new AppendOnlyTreeSnapshot(Fr.fromBuffer(tree.getRoot(true)), Number(tree.getNumLeaves(true))); + + if (name === 'noteHash') { + header = new Header( + header.lastArchive, + header.bodyHash, + new StateReference( + header.state.l1ToL2MessageTree, + new PartialStateReference( + newSnap, + header.state.partial.nullifierTree, + header.state.partial.contractTree, + header.state.partial.publicDataTree, + ), + ), + header.globalVariables, + ); + } else { + header = new Header( + header.lastArchive, + header.bodyHash, + new StateReference(newSnap, header.state.partial), + header.globalVariables, + ); + } return trees[name]; }; @@ -177,6 +199,7 @@ describe('Private Execution test suite', () => { }); beforeEach(() => { + trees = {}; oracle = mock(); oracle.getNullifierKeyPair.mockImplementation((accountAddress: AztecAddress, contractAddress: AztecAddress) => { if (accountAddress.equals(ownerCompleteAddress.address)) { @@ -193,7 +216,7 @@ describe('Private Execution test suite', () => { } throw new Error(`Unknown address ${accountAddress}`); }); - oracle.getBlockHeader.mockResolvedValue(blockHeader); + oracle.getHeader.mockResolvedValue(header); acirSimulator = new AcirSimulator(oracle); }); @@ -476,53 +499,248 @@ describe('Private Execution test suite', () => { }); }); - it('Should be able to consume a dummy cross chain message', async () => { - const bridgedAmount = 100n; + describe('L1 to L2', () => { const artifact = getFunctionArtifact(TestContractArtifact, 'consume_mint_private_message'); + const canceller = EthAddress.random(); + let bridgedAmount = 100n; - const secretForL1ToL2MessageConsumption = new Fr(1n); const secretHashForRedeemingNotes = new Fr(2n); - const canceller = EthAddress.random(); - const preimage = buildL1ToL2Message( - getFunctionSelector('mint_private(bytes32,uint256,address)').substring(2), - [secretHashForRedeemingNotes, new Fr(bridgedAmount), canceller.toField()], - contractAddress, - secretForL1ToL2MessageConsumption, - ); + let secretForL1ToL2MessageConsumption = new Fr(1n); - // stub message key - const messageKey = Fr.random(); - const tree = await insertLeaves([messageKey], 'l1ToL2Messages'); + let crossChainMsgRecipient: AztecAddress | undefined; + let crossChainMsgSender: EthAddress | undefined; + let messageKey: Fr | undefined; - oracle.getL1ToL2Message.mockImplementation(async () => { - return Promise.resolve({ - message: preimage.toFieldArray(), - index: 0n, - siblingPath: (await tree.getSiblingPath(0n, false)).toFieldArray(), + let preimage: L1ToL2Message; + + let args: Fr[]; + + beforeEach(() => { + bridgedAmount = 100n; + secretForL1ToL2MessageConsumption = new Fr(2n); + + crossChainMsgRecipient = undefined; + crossChainMsgSender = undefined; + messageKey = undefined; + }); + + const computePreimage = () => + buildL1ToL2Message( + getFunctionSelector('mint_private(bytes32,uint256,address)').substring(2), + [secretHashForRedeemingNotes, new Fr(bridgedAmount), canceller.toField()], + crossChainMsgRecipient ?? contractAddress, + secretForL1ToL2MessageConsumption, + ); + + const computeArgs = () => + encodeArguments(artifact, [ + secretHashForRedeemingNotes, + bridgedAmount, + canceller.toField(), + messageKey ?? preimage.hash(), + secretForL1ToL2MessageConsumption, + ]); + + const mockOracles = async () => { + const tree = await insertLeaves([messageKey ?? preimage.hash()], 'l1ToL2Messages'); + oracle.getL1ToL2Message.mockImplementation(async () => { + return Promise.resolve({ + message: preimage.toFieldArray(), + index: 0n, + siblingPath: (await tree.getSiblingPath(0n, false)).toFieldArray(), + }); + }); + }; + + it('Should be able to consume a dummy cross chain message', async () => { + preimage = computePreimage(); + + args = computeArgs(); + + await mockOracles(); + // Update state + oracle.getHeader.mockResolvedValue(header); + + const result = await runSimulator({ + contractAddress, + artifact, + args, + portalContractAddress: crossChainMsgSender ?? preimage.sender.sender, + txContext: { version: new Fr(1n), chainId: new Fr(1n) }, }); + + // Check a nullifier has been inserted + const newNullifiers = sideEffectArrayToValueArray( + nonEmptySideEffects(result.callStackItem.publicInputs.newNullifiers), + ); + + expect(newNullifiers).toHaveLength(1); }); - const args = [ - secretHashForRedeemingNotes, - bridgedAmount, - canceller.toField(), - messageKey, - secretForL1ToL2MessageConsumption, - ]; - const result = await runSimulator({ - contractAddress, - artifact, - args, - portalContractAddress: preimage.sender.sender, - txContext: { version: new Fr(1n), chainId: new Fr(1n) }, + it('Message not matching requested key', async () => { + messageKey = Fr.random(); + + preimage = computePreimage(); + + args = computeArgs(); + + await mockOracles(); + // Update state + oracle.getHeader.mockResolvedValue(header); + + await expect( + runSimulator({ + contractAddress, + artifact, + args, + portalContractAddress: crossChainMsgSender ?? preimage.sender.sender, + txContext: { version: new Fr(1n), chainId: new Fr(1n) }, + }), + ).rejects.toThrowError('Message not matching requested key'); }); - // Check a nullifier has been inserted - const newNullifiers = sideEffectArrayToValueArray( - nonEmptySideEffects(result.callStackItem.publicInputs.newNullifiers), - ); + it('Invalid membership proof', async () => { + preimage = computePreimage(); - expect(newNullifiers).toHaveLength(1); + args = computeArgs(); + + await mockOracles(); + + await expect( + runSimulator({ + contractAddress, + artifact, + args, + portalContractAddress: crossChainMsgSender ?? preimage.sender.sender, + txContext: { version: new Fr(1n), chainId: new Fr(1n) }, + }), + ).rejects.toThrowError('Message not in state'); + }); + + it('Invalid recipient', async () => { + crossChainMsgRecipient = AztecAddress.random(); + + preimage = computePreimage(); + + args = computeArgs(); + + await mockOracles(); + // Update state + oracle.getHeader.mockResolvedValue(header); + + await expect( + runSimulator({ + contractAddress, + artifact, + args, + portalContractAddress: crossChainMsgSender ?? preimage.sender.sender, + txContext: { version: new Fr(1n), chainId: new Fr(1n) }, + }), + ).rejects.toThrowError('Invalid recipient'); + }); + + it('Invalid sender', async () => { + crossChainMsgSender = EthAddress.random(); + preimage = computePreimage(); + + args = computeArgs(); + + await mockOracles(); + // Update state + oracle.getHeader.mockResolvedValue(header); + + await expect( + runSimulator({ + contractAddress, + artifact, + args, + portalContractAddress: crossChainMsgSender ?? preimage.sender.sender, + txContext: { version: new Fr(1n), chainId: new Fr(1n) }, + }), + ).rejects.toThrowError('Invalid sender'); + }); + + it('Invalid chainid', async () => { + preimage = computePreimage(); + + args = computeArgs(); + + await mockOracles(); + // Update state + oracle.getHeader.mockResolvedValue(header); + + await expect( + runSimulator({ + contractAddress, + artifact, + args, + portalContractAddress: crossChainMsgSender ?? preimage.sender.sender, + txContext: { version: new Fr(1n), chainId: new Fr(2n) }, + }), + ).rejects.toThrowError('Invalid Chainid'); + }); + + it('Invalid version', async () => { + preimage = computePreimage(); + + args = computeArgs(); + + await mockOracles(); + // Update state + oracle.getHeader.mockResolvedValue(header); + + await expect( + runSimulator({ + contractAddress, + artifact, + args, + portalContractAddress: crossChainMsgSender ?? preimage.sender.sender, + txContext: { version: new Fr(2n), chainId: new Fr(1n) }, + }), + ).rejects.toThrowError('Invalid Version'); + }); + + it('Invalid content', async () => { + preimage = computePreimage(); + + bridgedAmount = bridgedAmount + 1n; // Invalid amount + args = computeArgs(); + + await mockOracles(); + // Update state + oracle.getHeader.mockResolvedValue(header); + + await expect( + runSimulator({ + contractAddress, + artifact, + args, + portalContractAddress: crossChainMsgSender ?? preimage.sender.sender, + txContext: { version: new Fr(1n), chainId: new Fr(1n) }, + }), + ).rejects.toThrowError('Invalid Content'); + }); + + it('Invalid Secret', async () => { + preimage = computePreimage(); + + secretForL1ToL2MessageConsumption = Fr.random(); + args = computeArgs(); + + await mockOracles(); + // Update state + oracle.getHeader.mockResolvedValue(header); + + await expect( + runSimulator({ + contractAddress, + artifact, + args, + portalContractAddress: crossChainMsgSender ?? preimage.sender.sender, + txContext: { version: new Fr(1n), chainId: new Fr(1n) }, + }), + ).rejects.toThrowError('Invalid message secret'); + }); }); it('Should be able to consume a dummy public to private message', async () => { diff --git a/yarn-project/acir-simulator/src/client/simulator.ts b/yarn-project/acir-simulator/src/client/simulator.ts index 74e6c1ae4ee..514bf8b04ca 100644 --- a/yarn-project/acir-simulator/src/client/simulator.ts +++ b/yarn-project/acir-simulator/src/client/simulator.ts @@ -84,7 +84,7 @@ export class AcirSimulator { const curve = new Grumpkin(); - const blockHeader = await this.db.getBlockHeader(); + const header = await this.db.getHeader(); const callContext = new CallContext( msgSender, contractAddress, @@ -101,7 +101,7 @@ export class AcirSimulator { request.argsHash, request.txContext, callContext, - blockHeader, + header, request.authWitnesses, PackedArgsCache.create(request.packedArguments), new ExecutionNoteCache(), @@ -139,8 +139,7 @@ export class AcirSimulator { throw new Error(`Cannot run ${entryPointArtifact.functionType} function as constrained`); } - const blockHeader = await this.db.getBlockHeader(); - const context = new ViewDataOracle(contractAddress, blockHeader, [], this.db, aztecNode); + const context = new ViewDataOracle(contractAddress, [], this.db, aztecNode); try { return await executeUnconstrainedFunction( diff --git a/yarn-project/acir-simulator/src/client/unconstrained_execution.test.ts b/yarn-project/acir-simulator/src/client/unconstrained_execution.test.ts index fcabbc09a1d..c8c114b756c 100644 --- a/yarn-project/acir-simulator/src/client/unconstrained_execution.test.ts +++ b/yarn-project/acir-simulator/src/client/unconstrained_execution.test.ts @@ -1,5 +1,5 @@ import { FunctionCall, Note } from '@aztec/circuit-types'; -import { BlockHeader, CompleteAddress, FunctionData } from '@aztec/circuits.js'; +import { CompleteAddress, FunctionData, Header } from '@aztec/circuits.js'; import { FunctionSelector, encodeArguments } from '@aztec/foundation/abi'; import { AztecAddress } from '@aztec/foundation/aztec-address'; import { Fr, GrumpkinScalar } from '@aztec/foundation/fields'; @@ -46,7 +46,7 @@ describe('Unconstrained Execution test suite', () => { const notes: Note[] = [...Array(5).fill(buildNote(1n, owner)), ...Array(2).fill(buildNote(2n, owner))]; - oracle.getBlockHeader.mockResolvedValue(BlockHeader.empty()); + oracle.getHeader.mockResolvedValue(Header.empty()); oracle.getNotes.mockResolvedValue( notes.map((note, index) => ({ contractAddress, diff --git a/yarn-project/acir-simulator/src/client/unconstrained_execution.ts b/yarn-project/acir-simulator/src/client/unconstrained_execution.ts index 7d7f3d264b7..62f934d41eb 100644 --- a/yarn-project/acir-simulator/src/client/unconstrained_execution.ts +++ b/yarn-project/acir-simulator/src/client/unconstrained_execution.ts @@ -5,11 +5,12 @@ import { Fr } from '@aztec/foundation/fields'; import { createDebugLogger } from '@aztec/foundation/log'; import { extractReturnWitness } from '../acvm/deserialize.js'; -import { ACVMField, Oracle, acvm, extractCallStack, fromACVMField, toACVMWitness } from '../acvm/index.js'; +import { Oracle, acvm, extractCallStack, toACVMWitness } from '../acvm/index.js'; import { ExecutionError } from '../common/errors.js'; import { AcirSimulator } from '../index.js'; import { ViewDataOracle } from './view_data_oracle.js'; +// docs:start:execute_unconstrained_function /** * Execute an unconstrained function and return the decoded values. */ @@ -43,6 +44,6 @@ export async function executeUnconstrainedFunction( ); }); - const returnValues: ACVMField[] = extractReturnWitness(acir, partialWitness); - return decodeReturnValues(artifact, returnValues.map(fromACVMField)); + return decodeReturnValues(artifact, extractReturnWitness(acir, partialWitness)); } +// docs:end:execute_unconstrained_function diff --git a/yarn-project/acir-simulator/src/client/view_data_oracle.ts b/yarn-project/acir-simulator/src/client/view_data_oracle.ts index 3b6128e3221..14cc8ea4bfd 100644 --- a/yarn-project/acir-simulator/src/client/view_data_oracle.ts +++ b/yarn-project/acir-simulator/src/client/view_data_oracle.ts @@ -2,13 +2,13 @@ import { AuthWitness, AztecNode, CompleteAddress, - INITIAL_L2_BLOCK_NUM, MerkleTreeId, + NoteStatus, NullifierMembershipWitness, PublicDataWitness, } from '@aztec/circuit-types'; -import { BlockHeader } from '@aztec/circuits.js'; -import { computeGlobalsHash, siloNullifier } from '@aztec/circuits.js/abis'; +import { Header } from '@aztec/circuits.js'; +import { siloNullifier } from '@aztec/circuits.js/abis'; import { AztecAddress } from '@aztec/foundation/aztec-address'; import { Fr } from '@aztec/foundation/fields'; import { createDebugLogger } from '@aztec/foundation/log'; @@ -24,8 +24,6 @@ import { pickNotes } from './pick_notes.js'; export class ViewDataOracle extends TypedOracle { constructor( protected readonly contractAddress: AztecAddress, - /** Data required to reconstruct the block hash, it contains historical roots. */ - protected readonly blockHeader: BlockHeader, /** List of transient auth witnesses to be used during this simulation */ protected readonly authWitnesses: AuthWitness[], protected readonly db: DBOracle, @@ -114,45 +112,12 @@ export class ViewDataOracle extends TypedOracle { * @param blockNumber - The number of a block of which to get the block header. * @returns Block extracted from a block with block number `blockNumber`. */ - public async getBlockHeader(blockNumber: number): Promise { + public async getHeader(blockNumber: number): Promise
{ const block = await this.db.getBlock(blockNumber); if (!block) { return undefined; } - return new BlockHeader( - block.header.state.partial.noteHashTree.root, - block.header.state.partial.nullifierTree.root, - block.header.state.partial.contractTree.root, - block.header.state.l1ToL2MessageTree.root, - block.archive.root, - new Fr(0), // TODO(#3441) privateKernelVkTreeRoot is not present in L2Block and it's not yet populated in noir - block.header.state.partial.publicDataTree.root, - computeGlobalsHash(block.header.globalVariables), - ); - } - - /** - * Gets number of a block in which a given nullifier tree root was included. - * @param nullifierTreeRoot - The nullifier tree root to get the block number for. - * @returns The block number. - * - * TODO(#3564) - Nuke this oracle and inject the number directly to context - */ - public async getNullifierRootBlockNumber(nullifierTreeRoot: Fr): Promise { - const currentBlockNumber = await this.db.getBlockNumber(); - for (let i = currentBlockNumber; i >= INITIAL_L2_BLOCK_NUM; i -= 2) { - const block = await this.db.getBlock(i); - if (!block) { - throw new Error(`Block ${i} not found`); - } - if (block.header.state.partial.nullifierTree.root.equals(nullifierTreeRoot)) { - return i; - } - if (block.header.state.partial.nullifierTree.root.equals(nullifierTreeRoot)) { - return i - 1; - } - } - throw new Error(`Failed to find block containing nullifier tree root ${nullifierTreeRoot}`); + return block.header; } /** @@ -198,10 +163,12 @@ export class ViewDataOracle extends TypedOracle { * @param numSelects - The number of valid selects in selectBy and selectValues. * @param selectBy - An array of indices of the fields to selects. * @param selectValues - The values to match. + * @param selectComparators - The comparators to use to match values. * @param sortBy - An array of indices of the fields to sort. * @param sortOrder - The order of the corresponding index in sortBy. (1: DESC, 2: ASC, 0: Do nothing) * @param limit - The number of notes to retrieve per query. * @param offset - The starting index for pagination. + * @param status - The status of notes to fetch. * @returns Array of note data. */ public async getNotes( @@ -209,14 +176,18 @@ export class ViewDataOracle extends TypedOracle { numSelects: number, selectBy: number[], selectValues: Fr[], + selectComparators: number[], sortBy: number[], sortOrder: number[], limit: number, offset: number, + status: NoteStatus, ): Promise { - const dbNotes = await this.db.getNotes(this.contractAddress, storageSlot); + const dbNotes = await this.db.getNotes(this.contractAddress, storageSlot, status); return pickNotes(dbNotes, { - selects: selectBy.slice(0, numSelects).map((index, i) => ({ index, value: selectValues[i] })), + selects: selectBy + .slice(0, numSelects) + .map((index, i) => ({ index, value: selectValues[i], comparator: selectComparators[i] })), sorts: sortBy.map((index, i) => ({ index, order: sortOrder[i] })), limit, offset, @@ -240,8 +211,7 @@ export class ViewDataOracle extends TypedOracle { * @returns The l1 to l2 message data */ public async getL1ToL2Message(msgKey: Fr) { - const message = await this.db.getL1ToL2Message(msgKey); - return { ...message, root: this.blockHeader.l1ToL2MessageTreeRoot }; + return await this.db.getL1ToL2Message(msgKey); } /** diff --git a/yarn-project/acir-simulator/src/public/executor.ts b/yarn-project/acir-simulator/src/public/executor.ts index 0ad579ef241..67af8c89a57 100644 --- a/yarn-project/acir-simulator/src/public/executor.ts +++ b/yarn-project/acir-simulator/src/public/executor.ts @@ -1,4 +1,4 @@ -import { BlockHeader, GlobalVariables } from '@aztec/circuits.js'; +import { GlobalVariables, Header } from '@aztec/circuits.js'; import { createDebugLogger } from '@aztec/foundation/log'; import { Oracle, acvm, extractCallStack, extractPublicCircuitPublicInputs } from '../acvm/index.js'; @@ -81,7 +81,7 @@ export class PublicExecutor { private readonly stateDb: PublicStateDB, private readonly contractsDb: PublicContractsDB, private readonly commitmentsDb: CommitmentsDB, - private readonly blockHeader: BlockHeader, + private readonly header: Header, ) {} /** @@ -105,7 +105,7 @@ export class PublicExecutor { const context = new PublicExecutionContext( execution, - this.blockHeader, + this.header, globalVariables, packedArgs, sideEffectCounter, diff --git a/yarn-project/acir-simulator/src/public/index.test.ts b/yarn-project/acir-simulator/src/public/index.test.ts index 05d9654ae47..dde71adb1ea 100644 --- a/yarn-project/acir-simulator/src/public/index.test.ts +++ b/yarn-project/acir-simulator/src/public/index.test.ts @@ -1,4 +1,12 @@ -import { BlockHeader, CallContext, FunctionData, GlobalVariables, L1_TO_L2_MSG_TREE_HEIGHT } from '@aztec/circuits.js'; +import { L1ToL2Message } from '@aztec/circuit-types'; +import { + AppendOnlyTreeSnapshot, + CallContext, + FunctionData, + GlobalVariables, + Header, + L1_TO_L2_MSG_TREE_HEIGHT, +} from '@aztec/circuits.js'; import { FunctionArtifact, FunctionSelector, encodeArguments } from '@aztec/foundation/abi'; import { AztecAddress } from '@aztec/foundation/aztec-address'; import { pedersenHash } from '@aztec/foundation/crypto'; @@ -26,15 +34,15 @@ describe('ACIR public execution simulator', () => { let publicContracts: MockProxy; let commitmentsDb: MockProxy; let executor: PublicExecutor; - let blockHeader: BlockHeader; + let header: Header; beforeEach(() => { publicState = mock(); publicContracts = mock(); commitmentsDb = mock(); - blockHeader = BlockHeader.empty(); - executor = new PublicExecutor(publicState, publicContracts, commitmentsDb, blockHeader); + header = Header.empty(); + executor = new PublicExecutor(publicState, publicContracts, commitmentsDb, header); }, 10000); describe('Token contract', () => { @@ -353,69 +361,6 @@ describe('ACIR public execution simulator', () => { expect(result.newL2ToL1Messages[0].toBuffer()).toEqual(expectedNewMessageValue); }); - it('Should be able to consume an L1 to L2 message in the public context', async () => { - const mintPublicArtifact = TestContractArtifact.functions.find(f => f.name === 'consume_mint_public_message')!; - - // Set up cross chain message - const canceller = EthAddress.random(); - - const bridgedAmount = 20n; - const secret = new Fr(1n); - const recipient = AztecAddress.random(); - - const preimage = buildL1ToL2Message( - getFunctionSelector('mint_public(bytes32,uint256,address)').substring(2), - [recipient.toField(), new Fr(bridgedAmount), canceller.toField()], - contractAddress, - secret, - ); - - // Stub message key - const messageKey = Fr.random(); - const args = encodeArguments(mintPublicArtifact, [ - recipient.toField(), - bridgedAmount, - canceller.toField(), - messageKey, - secret, - ]); - - const callContext = CallContext.from({ - msgSender: AztecAddress.random(), - storageContractAddress: contractAddress, - portalContractAddress: preimage.sender.sender, - functionSelector: FunctionSelector.empty(), - isContractDeployment: false, - isDelegateCall: false, - isStaticCall: false, - startSideEffectCounter: 0, - }); - - publicContracts.getBytecode.mockResolvedValue(Buffer.from(mintPublicArtifact.bytecode, 'base64')); - publicState.storageRead.mockResolvedValue(Fr.ZERO); - - // Mock response - commitmentsDb.getL1ToL2Message.mockImplementation(async () => { - return await Promise.resolve({ - message: preimage.toFieldArray(), - index: 0n, - siblingPath: Array(L1_TO_L2_MSG_TREE_HEIGHT).fill(Fr.random()), - }); - }); - - const execution: PublicExecution = { contractAddress, functionData, args, callContext }; - - const gv = new GlobalVariables( - new Fr(preimage.sender.chainId), - new Fr(preimage.recipient.version), - Fr.ZERO, - Fr.ZERO, - ); - const result = await executor.simulate(execution, gv); - - expect(result.newNullifiers.length).toEqual(1); - }); - it('Should be able to create a nullifier from the public context', async () => { const createNullifierPublicArtifact = TestContractArtifact.functions.find( f => f.name === 'create_nullifier_public', @@ -445,5 +390,231 @@ describe('ACIR public execution simulator', () => { const expectedNewMessageValue = pedersenHash(params.map(a => a.toBuffer())); expect(result.newNullifiers[0].value.toBuffer()).toEqual(expectedNewMessageValue); }); + + describe('L1 to L2 messages', () => { + const mintPublicArtifact = TestContractArtifact.functions.find(f => f.name === 'consume_mint_public_message')!; + + const canceller = EthAddress.random(); + const tokenRecipient = AztecAddress.random(); + let bridgedAmount = 20n; + let secret = new Fr(1); + + let crossChainMsgRecipient: AztecAddress | undefined; + let crossChainMsgSender: EthAddress | undefined; + let messageKey: Fr | undefined; + + let preimage: L1ToL2Message; + let globalVariables: GlobalVariables; + + let args: Fr[]; + let callContext: CallContext; + + beforeEach(() => { + bridgedAmount = 20n; + secret = new Fr(1); + + crossChainMsgRecipient = undefined; + crossChainMsgSender = undefined; + messageKey = undefined; + }); + + const computePreImage = () => + buildL1ToL2Message( + getFunctionSelector('mint_public(bytes32,uint256,address)').substring(2), + [tokenRecipient.toField(), new Fr(bridgedAmount), canceller.toField()], + crossChainMsgRecipient ?? contractAddress, + secret, + ); + + const computeArgs = () => + encodeArguments(mintPublicArtifact, [ + tokenRecipient.toField(), + bridgedAmount, + canceller.toField(), + messageKey ?? preimage.hash(), + secret, + ]); + + const computeCallContext = () => + CallContext.from({ + msgSender: AztecAddress.random(), + storageContractAddress: contractAddress, + portalContractAddress: crossChainMsgSender ?? preimage.sender.sender, + functionSelector: FunctionSelector.empty(), + isContractDeployment: false, + isDelegateCall: false, + isStaticCall: false, + startSideEffectCounter: 0, + }); + + const computeGlobalVariables = () => + new GlobalVariables(new Fr(preimage.sender.chainId), new Fr(preimage.recipient.version), Fr.ZERO, Fr.ZERO); + + const mockOracles = () => { + publicContracts.getBytecode.mockResolvedValue(Buffer.from(mintPublicArtifact.bytecode, 'base64')); + publicState.storageRead.mockResolvedValue(Fr.ZERO); + + const siblingPath = Array(L1_TO_L2_MSG_TREE_HEIGHT).fill(Fr.random()); + let root = messageKey ?? preimage.hash(); + for (const sibling of siblingPath) { + root = Fr.fromBuffer(pedersenHash([root.toBuffer(), sibling.toBuffer()])); + } + commitmentsDb.getL1ToL2Message.mockImplementation(async () => { + return await Promise.resolve({ + message: preimage.toFieldArray(), + index: 0n, + siblingPath, + }); + }); + + return new AppendOnlyTreeSnapshot( + root, + 1, // we set 1 message in the tree + ); + }; + + it('Should be able to consume an L1 to L2 message in the public context', async () => { + preimage = computePreImage(); + + args = computeArgs(); + callContext = computeCallContext(); + + // Prepare the state + header.state.l1ToL2MessageTree = mockOracles(); + globalVariables = computeGlobalVariables(); + + const execution: PublicExecution = { contractAddress, functionData, args, callContext }; + executor = new PublicExecutor(publicState, publicContracts, commitmentsDb, header); + const result = await executor.simulate(execution, globalVariables); + expect(result.newNullifiers.length).toEqual(1); + }); + + it('Message not matching requested key', async () => { + // Using a random value for the message key + messageKey = Fr.random(); + + preimage = computePreImage(); + args = computeArgs(); + callContext = computeCallContext(); + + // Prepare the state + header.state.l1ToL2MessageTree = mockOracles(); + globalVariables = computeGlobalVariables(); + + const execution: PublicExecution = { contractAddress, functionData, args, callContext }; + executor = new PublicExecutor(publicState, publicContracts, commitmentsDb, header); + await expect(executor.simulate(execution, globalVariables)).rejects.toThrowError( + 'Message not matching requested key', + ); + }); + + it('Invalid membership proof', async () => { + preimage = computePreImage(); + args = computeArgs(); + callContext = computeCallContext(); + + // Mock oracles but don't update state + mockOracles(); + + // Prepare the state + globalVariables = computeGlobalVariables(); + + const execution: PublicExecution = { contractAddress, functionData, args, callContext }; + executor = new PublicExecutor(publicState, publicContracts, commitmentsDb, header); + await expect(executor.simulate(execution, globalVariables)).rejects.toThrowError('Message not in state'); + }); + + it('Invalid recipient', async () => { + crossChainMsgRecipient = AztecAddress.random(); + preimage = computePreImage(); + args = computeArgs(); + callContext = computeCallContext(); + + // Prepare the state + header.state.l1ToL2MessageTree = mockOracles(); + globalVariables = computeGlobalVariables(); + + const execution: PublicExecution = { contractAddress, functionData, args, callContext }; + executor = new PublicExecutor(publicState, publicContracts, commitmentsDb, header); + await expect(executor.simulate(execution, globalVariables)).rejects.toThrowError('Invalid recipient'); + }); + + it('Invalid sender', async () => { + crossChainMsgSender = EthAddress.random(); + preimage = computePreImage(); + args = computeArgs(); + callContext = computeCallContext(); + + // Prepare the state + header.state.l1ToL2MessageTree = mockOracles(); + globalVariables = computeGlobalVariables(); + + const execution: PublicExecution = { contractAddress, functionData, args, callContext }; + executor = new PublicExecutor(publicState, publicContracts, commitmentsDb, header); + await expect(executor.simulate(execution, globalVariables)).rejects.toThrowError('Invalid sender'); + }); + + it('Invalid chainid', async () => { + preimage = computePreImage(); + args = computeArgs(); + callContext = computeCallContext(); + + // Prepare the state + header.state.l1ToL2MessageTree = mockOracles(); + globalVariables = computeGlobalVariables(); + globalVariables.chainId = Fr.random(); + + const execution: PublicExecution = { contractAddress, functionData, args, callContext }; + executor = new PublicExecutor(publicState, publicContracts, commitmentsDb, header); + await expect(executor.simulate(execution, globalVariables)).rejects.toThrowError('Invalid Chainid'); + }); + + it('Invalid version', async () => { + preimage = computePreImage(); + args = computeArgs(); + callContext = computeCallContext(); + + // Prepare the state + header.state.l1ToL2MessageTree = mockOracles(); + globalVariables = computeGlobalVariables(); + globalVariables.version = Fr.random(); + + const execution: PublicExecution = { contractAddress, functionData, args, callContext }; + executor = new PublicExecutor(publicState, publicContracts, commitmentsDb, header); + await expect(executor.simulate(execution, globalVariables)).rejects.toThrowError('Invalid Version'); + }); + + it('Invalid Content', async () => { + preimage = computePreImage(); + + bridgedAmount = bridgedAmount + 1n; // Invalid amount + args = computeArgs(); + callContext = computeCallContext(); + + // Prepare the state + header.state.l1ToL2MessageTree = mockOracles(); + globalVariables = computeGlobalVariables(); + + const execution: PublicExecution = { contractAddress, functionData, args, callContext }; + executor = new PublicExecutor(publicState, publicContracts, commitmentsDb, header); + await expect(executor.simulate(execution, globalVariables)).rejects.toThrowError('Invalid Content'); + }); + + it('Invalid secret', async () => { + preimage = computePreImage(); + + secret = Fr.random(); // Invalid secret + args = computeArgs(); + callContext = computeCallContext(); + + // Prepare the state + header.state.l1ToL2MessageTree = mockOracles(); + globalVariables = computeGlobalVariables(); + + const execution: PublicExecution = { contractAddress, functionData, args, callContext }; + executor = new PublicExecutor(publicState, publicContracts, commitmentsDb, header); + await expect(executor.simulate(execution, globalVariables)).rejects.toThrowError('Invalid message secret'); + }); + }); }); }); diff --git a/yarn-project/acir-simulator/src/public/public_execution_context.ts b/yarn-project/acir-simulator/src/public/public_execution_context.ts index 886c0c40434..5ed036b1413 100644 --- a/yarn-project/acir-simulator/src/public/public_execution_context.ts +++ b/yarn-project/acir-simulator/src/public/public_execution_context.ts @@ -1,17 +1,11 @@ import { FunctionL2Logs, UnencryptedL2Log } from '@aztec/circuit-types'; -import { BlockHeader, CallContext, FunctionData, FunctionSelector, GlobalVariables } from '@aztec/circuits.js'; +import { CallContext, FunctionData, FunctionSelector, GlobalVariables, Header } from '@aztec/circuits.js'; import { AztecAddress } from '@aztec/foundation/aztec-address'; import { EthAddress } from '@aztec/foundation/eth-address'; import { Fr } from '@aztec/foundation/fields'; import { createDebugLogger } from '@aztec/foundation/log'; -import { - TypedOracle, - toACVMBlockHeader, - toACVMCallContext, - toACVMGlobalVariables, - toACVMWitness, -} from '../acvm/index.js'; +import { TypedOracle, toACVMCallContext, toACVMGlobalVariables, toACVMHeader, toACVMWitness } from '../acvm/index.js'; import { PackedArgsCache, SideEffectCounter } from '../common/index.js'; import { CommitmentsDB, PublicContractsDB, PublicStateDB } from './db.js'; import { PublicExecution, PublicExecutionResult } from './execution.js'; @@ -31,7 +25,7 @@ export class PublicExecutionContext extends TypedOracle { * Data for this execution. */ public readonly execution: PublicExecution, - private readonly blockHeader: BlockHeader, + private readonly header: Header, private readonly globalVariables: GlobalVariables, private readonly packedArgsCache: PackedArgsCache, private readonly sideEffectCounter: SideEffectCounter, @@ -48,7 +42,7 @@ export class PublicExecutionContext extends TypedOracle { * Generates the initial witness for a public function. * @param args - The arguments to the function. * @param callContext - The call context of the function. - * @param blockHeader - Contains data required to reconstruct a block hash (historical roots etc.). + * @param header - Contains data required to reconstruct a block hash (historical roots etc.). * @param globalVariables - The global variables. * @param witnessStartIndex - The index where to start inserting the parameters. * @returns The initial witness. @@ -57,7 +51,7 @@ export class PublicExecutionContext extends TypedOracle { const { callContext, args } = this.execution; const fields = [ ...toACVMCallContext(callContext), - ...toACVMBlockHeader(this.blockHeader), + ...toACVMHeader(this.header), ...toACVMGlobalVariables(this.globalVariables), ...args, @@ -102,9 +96,7 @@ export class PublicExecutionContext extends TypedOracle { * @returns The l1 to l2 message data */ public async getL1ToL2Message(msgKey: Fr) { - // l1 to l2 messages in public contexts TODO: https://github.com/AztecProtocol/aztec-packages/issues/616 - const message = await this.commitmentsDb.getL1ToL2Message(msgKey); - return { ...message, root: this.blockHeader.l1ToL2MessageTreeRoot }; + return await this.commitmentsDb.getL1ToL2Message(msgKey); } /** @@ -211,7 +203,7 @@ export class PublicExecutionContext extends TypedOracle { const context = new PublicExecutionContext( nestedExecution, - this.blockHeader, + this.header, this.globalVariables, this.packedArgsCache, this.sideEffectCounter, diff --git a/yarn-project/acir-simulator/tsconfig.json b/yarn-project/acir-simulator/tsconfig.json index da0e524126f..ce07b84cd9e 100644 --- a/yarn-project/acir-simulator/tsconfig.json +++ b/yarn-project/acir-simulator/tsconfig.json @@ -15,6 +15,9 @@ { "path": "../foundation" }, + { + "path": "../kv-store" + }, { "path": "../merkle-tree" }, diff --git a/yarn-project/archiver/package.json b/yarn-project/archiver/package.json index b837e1d9b38..226a990ee5e 100644 --- a/yarn-project/archiver/package.json +++ b/yarn-project/archiver/package.json @@ -41,9 +41,9 @@ "@aztec/foundation": "workspace:^", "@aztec/kv-store": "workspace:^", "@aztec/l1-artifacts": "workspace:^", - "@types/lodash.omit": "^4.5.7", + "@aztec/types": "workspace:^", "debug": "^4.3.4", - "lmdb": "^2.9.1", + "lmdb": "^2.9.2", "lodash.omit": "^4.5.0", "tsc-watch": "^6.0.0", "tslib": "^2.5.0", @@ -54,6 +54,7 @@ "@jest/globals": "^29.5.0", "@types/debug": "^4.1.7", "@types/jest": "^29.5.0", + "@types/lodash.omit": "^4.5.7", "@types/node": "^18.15.11", "@types/ws": "^8.5.4", "concurrently": "^8.0.1", diff --git a/yarn-project/archiver/src/archiver/archiver.test.ts b/yarn-project/archiver/src/archiver/archiver.test.ts index 612281c3789..6184eb4269b 100644 --- a/yarn-project/archiver/src/archiver/archiver.test.ts +++ b/yarn-project/archiver/src/archiver/archiver.test.ts @@ -290,9 +290,9 @@ function makeContractDeploymentEvent(l1BlockNum: bigint, l2Block: L2Block) { aztecAddress: extendedContractData.contractData.contractAddress.toString(), portalAddress: extendedContractData.contractData.portalContractAddress.toString(), l2BlockHash: `0x${l2Block.getCalldataHash().toString('hex')}`, - partialAddress: extendedContractData.partialAddress.toString(), - pubKeyX: extendedContractData.publicKey.x.toString(), - pubKeyY: extendedContractData.publicKey.y.toString(), + contractClassId: extendedContractData.contractClassId.toString(), + saltedInitializationHash: extendedContractData.saltedInitializationHash.toString(), + publicKeyHash: extendedContractData.publicKeyHash.toString(), acir: '0x' + acir, }, transactionHash: `0x${l2Block.number}`, diff --git a/yarn-project/archiver/src/archiver/archiver.ts b/yarn-project/archiver/src/archiver/archiver.ts index 26eaaa71eeb..4856a608bdf 100644 --- a/yarn-project/archiver/src/archiver/archiver.ts +++ b/yarn-project/archiver/src/archiver/archiver.ts @@ -23,6 +23,12 @@ import { EthAddress } from '@aztec/foundation/eth-address'; import { Fr } from '@aztec/foundation/fields'; import { DebugLogger, createDebugLogger } from '@aztec/foundation/log'; import { RunningPromise } from '@aztec/foundation/running-promise'; +import { + ContractClass, + ContractClassWithId, + ContractInstance, + ContractInstanceWithAddress, +} from '@aztec/types/contracts'; import omit from 'lodash.omit'; import { Chain, HttpTransport, PublicClient, createPublicClient, http } from 'viem'; @@ -36,12 +42,17 @@ import { retrieveNewPendingL1ToL2Messages, } from './data_retrieval.js'; +/** + * Helper interface to combine all sources this archiver implementation provides. + */ +export type ArchiveSource = L2BlockSource & L2LogsSource & ContractDataSource & L1ToL2MessageSource; + /** * Pulls L2 blocks in a non-blocking manner and provides interface for their retrieval. * Responsible for handling robust L1 polling so that other components do not need to * concern themselves with it. */ -export class Archiver implements L2BlockSource, L2LogsSource, ContractDataSource, L1ToL2MessageSource { +export class Archiver implements ArchiveSource { /** * A promise in which we will be continually fetching new L2 blocks. */ @@ -268,6 +279,7 @@ export class Archiver implements L2BlockSource, L2LogsSource, ContractDataSource this.log(`Retrieved extended contract data for l2 block number: ${l2BlockNum}`); if (l2BlockNum <= lastKnownL2BlockNum) { await this.store.addExtendedContractData(contracts, l2BlockNum); + await this.storeContractDataAsClassesAndInstances(contracts, l2BlockNum); } }), ); @@ -285,15 +297,29 @@ export class Archiver implements L2BlockSource, L2LogsSource, ContractDataSource retrievedBlocks.retrievedData.map(block => { // Ensure we pad the L1 to L2 message array to the full size before storing. block.newL1ToL2Messages = padArrayEnd(block.newL1ToL2Messages, Fr.ZERO, NUMBER_OF_L1_L2_MESSAGES_PER_ROLLUP); - return L2Block.fromFields( - omit(block, ['newEncryptedLogs', 'newUnencryptedLogs']), - block.getBlockHash(), - block.getL1BlockNumber(), - ); + return L2Block.fromFields(omit(block, ['newEncryptedLogs', 'newUnencryptedLogs']), block.getL1BlockNumber()); }), ); } + /** + * Stores extended contract data as classes and instances. + * Temporary solution until we source this data from the contract class registerer and instance deployer. + * @param contracts - The extended contract data to be stored. + * @param l2BlockNum - The L2 block number to which the contract data corresponds. + */ + async storeContractDataAsClassesAndInstances(contracts: ExtendedContractData[], l2BlockNum: number) { + const classesAndInstances = contracts.map(extendedContractDataToContractClassAndInstance); + await this.store.addContractClasses( + classesAndInstances.map(([c, _]) => c), + l2BlockNum, + ); + await this.store.addContractInstances( + classesAndInstances.map(([_, i]) => i), + l2BlockNum, + ); + } + /** * Stops the archiver. * @returns A promise signalling completion of the stop process. @@ -440,3 +466,39 @@ export class Archiver implements L2BlockSource, L2LogsSource, ContractDataSource return this.store.getConfirmedL1ToL2Message(messageKey); } } + +/** + * Converts ExtendedContractData into contract classes and instances. + * Note that the conversion is not correct, since there is some data missing from the broadcasted ExtendedContractData. + * The archiver will trust the ids broadcasted instead of trying to recompute them. + * Eventually this function and ExtendedContractData altogether will be removed. + */ +function extendedContractDataToContractClassAndInstance( + data: ExtendedContractData, +): [ContractClassWithId, ContractInstanceWithAddress] { + const contractClass: ContractClass = { + version: 1, + artifactHash: Fr.ZERO, + publicFunctions: data.publicFunctions.map(f => ({ + selector: f.selector, + bytecode: f.bytecode, + isInternal: f.isInternal, + })), + privateFunctions: [], + packedBytecode: data.bytecode, + }; + const contractClassId = data.contractClassId; + const contractInstance: ContractInstance = { + version: 1, + salt: data.saltedInitializationHash, + contractClassId, + initializationHash: data.saltedInitializationHash, + portalContractAddress: data.contractData.portalContractAddress, + publicKeysHash: data.publicKeyHash, + }; + const address = data.contractData.contractAddress; + return [ + { ...contractClass, id: contractClassId }, + { ...contractInstance, address }, + ]; +} diff --git a/yarn-project/archiver/src/archiver/archiver_store.ts b/yarn-project/archiver/src/archiver/archiver_store.ts index 0d4c1f13c26..c8d033d40fd 100644 --- a/yarn-project/archiver/src/archiver/archiver_store.ts +++ b/yarn-project/archiver/src/archiver/archiver_store.ts @@ -12,6 +12,7 @@ import { } from '@aztec/circuit-types'; import { Fr } from '@aztec/circuits.js'; import { AztecAddress } from '@aztec/foundation/aztec-address'; +import { ContractClassWithId, ContractInstanceWithAddress } from '@aztec/types/contracts'; /** * Represents the latest L1 block processed by the archiver for various objects in L2. @@ -167,4 +168,32 @@ export interface ArchiverDataStore { * Gets the last L1 block number processed by the archiver */ getL1BlockNumber(): Promise; + + /** + * Add new contract classes from an L2 block to the store's list. + * @param data - List of contract classes to be added. + * @param blockNumber - Number of the L2 block the contracts were registered in. + * @returns True if the operation is successful. + */ + addContractClasses(data: ContractClassWithId[], blockNumber: number): Promise; + + /** + * Returns a contract class given its id, or undefined if not exists. + * @param id - Id of the contract class. + */ + getContractClass(id: Fr): Promise; + + /** + * Add new contract instances from an L2 block to the store's list. + * @param data - List of contract instances to be added. + * @param blockNumber - Number of the L2 block the instances were deployed in. + * @returns True if the operation is successful. + */ + addContractInstances(data: ContractInstanceWithAddress[], blockNumber: number): Promise; + + /** + * Returns a contract instance given its address, or undefined if not exists. + * @param address - Address of the contract. + */ + getContractInstance(address: AztecAddress): Promise; } diff --git a/yarn-project/archiver/src/archiver/archiver_store_test_suite.ts b/yarn-project/archiver/src/archiver/archiver_store_test_suite.ts index 788c0894d4e..592addaefb0 100644 --- a/yarn-project/archiver/src/archiver/archiver_store_test_suite.ts +++ b/yarn-project/archiver/src/archiver/archiver_store_test_suite.ts @@ -12,6 +12,12 @@ import { import '@aztec/circuit-types/jest'; import { AztecAddress, Fr } from '@aztec/circuits.js'; import { randomBytes } from '@aztec/foundation/crypto'; +import { + ContractClassWithId, + ContractInstanceWithAddress, + SerializableContractClass, + SerializableContractInstance, +} from '@aztec/types/contracts'; import { ArchiverDataStore } from './archiver_store.js'; @@ -320,6 +326,42 @@ export function describeArchiverDataStore(testName: string, getStore: () => Arch }); }); + describe('contractInstances', () => { + let contractInstance: ContractInstanceWithAddress; + const blockNum = 10; + + beforeEach(async () => { + contractInstance = { ...SerializableContractInstance.random(), address: AztecAddress.random() }; + await store.addContractInstances([contractInstance], blockNum); + }); + + it('returns previously stored contract instances', async () => { + await expect(store.getContractInstance(contractInstance.address)).resolves.toMatchObject(contractInstance); + }); + + it('returns undefined if contract instance is not found', async () => { + await expect(store.getContractInstance(AztecAddress.random())).resolves.toBeUndefined(); + }); + }); + + describe('contractClasses', () => { + let contractClass: ContractClassWithId; + const blockNum = 10; + + beforeEach(async () => { + contractClass = { ...SerializableContractClass.random(), id: Fr.random() }; + await store.addContractClasses([contractClass], blockNum); + }); + + it('returns previously stored contract class', async () => { + await expect(store.getContractClass(contractClass.id)).resolves.toMatchObject(contractClass); + }); + + it('returns undefined if contract class is not found', async () => { + await expect(store.getContractClass(Fr.random())).resolves.toBeUndefined(); + }); + }); + describe('getContractData', () => { let block: L2Block; beforeEach(async () => { diff --git a/yarn-project/archiver/src/archiver/eth_log_handlers.ts b/yarn-project/archiver/src/archiver/eth_log_handlers.ts index 0d28ca2b4b2..ab5cf19fd0e 100644 --- a/yarn-project/archiver/src/archiver/eth_log_handlers.ts +++ b/yarn-project/archiver/src/archiver/eth_log_handlers.ts @@ -9,7 +9,7 @@ import { } from '@aztec/circuit-types'; import { AztecAddress } from '@aztec/foundation/aztec-address'; import { EthAddress } from '@aztec/foundation/eth-address'; -import { Fr, Point } from '@aztec/foundation/fields'; +import { Fr } from '@aztec/foundation/fields'; import { BufferReader, numToUInt32BE } from '@aztec/foundation/serialize'; import { ContractDeploymentEmitterAbi, InboxAbi, RollupAbi } from '@aztec/l1-artifacts'; @@ -195,17 +195,16 @@ export function processContractDeploymentLogs( continue; } const publicFnsReader = BufferReader.asReader(Buffer.from(log.args.acir.slice(2), 'hex')); - const partialAddress = Fr.fromBuffer(Buffer.from(hexToBytes(log.args.partialAddress))); - const publicKey = new Point( - Fr.fromBuffer(Buffer.from(hexToBytes(log.args.pubKeyX))), - Fr.fromBuffer(Buffer.from(hexToBytes(log.args.pubKeyY))), - ); + const contractClassId = Fr.fromBuffer(Buffer.from(hexToBytes(log.args.contractClassId))); + const saltedInitializationHash = Fr.fromBuffer(Buffer.from(hexToBytes(log.args.saltedInitializationHash))); + const publicKeyHash = Fr.fromBuffer(Buffer.from(hexToBytes(log.args.publicKeyHash))); const contractData = new ExtendedContractData( new ContractData(AztecAddress.fromString(log.args.aztecAddress), EthAddress.fromString(log.args.portalAddress)), publicFnsReader.readVector(EncodedContractFunction), - partialAddress, - publicKey, + contractClassId, + saltedInitializationHash, + publicKeyHash, ); if (extendedContractData[i]) { extendedContractData[i][0].push(contractData); diff --git a/yarn-project/archiver/src/archiver/kv_archiver_store/block_store.ts b/yarn-project/archiver/src/archiver/kv_archiver_store/block_store.ts index 025221fe73f..ef637162c4e 100644 --- a/yarn-project/archiver/src/archiver/kv_archiver_store/block_store.ts +++ b/yarn-project/archiver/src/archiver/kv_archiver_store/block_store.ts @@ -3,16 +3,13 @@ import { AztecAddress } from '@aztec/circuits.js'; import { createDebugLogger } from '@aztec/foundation/log'; import { AztecKVStore, AztecMap, Range } from '@aztec/kv-store'; -/* eslint-disable */ type BlockIndexValue = [blockNumber: number, index: number]; type BlockContext = { blockNumber: number; l1BlockNumber: bigint; block: Buffer; - blockHash: Buffer; }; -/* eslint-enable */ /** * LMDB implementation of the ArchiverDataStore interface. @@ -30,10 +27,10 @@ export class BlockStore { #log = createDebugLogger('aztec:archiver:block_store'); constructor(private db: AztecKVStore) { - this.#blocks = db.createMap('archiver_blocks'); + this.#blocks = db.openMap('archiver_blocks'); - this.#txIndex = db.createMap('archiver_tx_index'); - this.#contractIndex = db.createMap('archiver_contract_index'); + this.#txIndex = db.openMap('archiver_tx_index'); + this.#contractIndex = db.openMap('archiver_contract_index'); } /** @@ -48,7 +45,6 @@ export class BlockStore { blockNumber: block.number, block: block.toBuffer(), l1BlockNumber: block.getL1BlockNumber(), - blockHash: block.getBlockHash(), }); for (const [i, tx] of block.getTxs().entries()) { @@ -79,7 +75,7 @@ export class BlockStore { */ *getBlocks(start: number, limit: number): IterableIterator { for (const blockCtx of this.#blocks.values(this.#computeBlockRange(start, limit))) { - yield L2Block.fromBuffer(blockCtx.block, blockCtx.blockHash); + yield L2Block.fromBuffer(blockCtx.block); } } @@ -94,9 +90,7 @@ export class BlockStore { return undefined; } - const block = L2Block.fromBuffer(blockCtx.block, blockCtx.blockHash); - - return block; + return L2Block.fromBuffer(blockCtx.block); } /** diff --git a/yarn-project/archiver/src/archiver/kv_archiver_store/contract_class_store.ts b/yarn-project/archiver/src/archiver/kv_archiver_store/contract_class_store.ts new file mode 100644 index 00000000000..686514aa1b7 --- /dev/null +++ b/yarn-project/archiver/src/archiver/kv_archiver_store/contract_class_store.ts @@ -0,0 +1,26 @@ +import { Fr } from '@aztec/foundation/fields'; +import { AztecKVStore, AztecMap } from '@aztec/kv-store'; +import { ContractClassWithId, SerializableContractClass } from '@aztec/types/contracts'; + +/** + * LMDB implementation of the ArchiverDataStore interface. + */ +export class ContractClassStore { + #contractClasses: AztecMap; + + constructor(db: AztecKVStore) { + this.#contractClasses = db.openMap('archiver_contract_classes'); + } + + addContractClass(contractClass: ContractClassWithId): Promise { + return this.#contractClasses.set( + contractClass.id.toString(), + new SerializableContractClass(contractClass).toBuffer(), + ); + } + + getContractClass(id: Fr): ContractClassWithId | undefined { + const contractClass = this.#contractClasses.get(id.toString()); + return contractClass && SerializableContractClass.fromBuffer(contractClass).withId(id); + } +} diff --git a/yarn-project/archiver/src/archiver/kv_archiver_store/contract_instance_store.ts b/yarn-project/archiver/src/archiver/kv_archiver_store/contract_instance_store.ts new file mode 100644 index 00000000000..fb020eb3c35 --- /dev/null +++ b/yarn-project/archiver/src/archiver/kv_archiver_store/contract_instance_store.ts @@ -0,0 +1,26 @@ +import { AztecAddress } from '@aztec/circuits.js'; +import { AztecKVStore, AztecMap } from '@aztec/kv-store'; +import { ContractInstanceWithAddress, SerializableContractInstance } from '@aztec/types/contracts'; + +/** + * LMDB implementation of the ArchiverDataStore interface. + */ +export class ContractInstanceStore { + #contractInstances: AztecMap; + + constructor(db: AztecKVStore) { + this.#contractInstances = db.openMap('archiver_contract_instances'); + } + + addContractInstance(contractInstance: ContractInstanceWithAddress): Promise { + return this.#contractInstances.set( + contractInstance.address.toString(), + new SerializableContractInstance(contractInstance).toBuffer(), + ); + } + + getContractInstance(address: AztecAddress): ContractInstanceWithAddress | undefined { + const contractInstance = this.#contractInstances.get(address.toString()); + return contractInstance && SerializableContractInstance.fromBuffer(contractInstance).withAddress(address); + } +} diff --git a/yarn-project/archiver/src/archiver/kv_archiver_store/contract_store.ts b/yarn-project/archiver/src/archiver/kv_archiver_store/contract_store.ts index 055b25af20d..0c2f117ed92 100644 --- a/yarn-project/archiver/src/archiver/kv_archiver_store/contract_store.ts +++ b/yarn-project/archiver/src/archiver/kv_archiver_store/contract_store.ts @@ -14,7 +14,7 @@ export class ContractStore { #log = createDebugLogger('aztec:archiver:contract_store'); constructor(private db: AztecKVStore, blockStore: BlockStore) { - this.#extendedContractData = db.createMap('archiver_extended_contract_data'); + this.#extendedContractData = db.openMap('archiver_extended_contract_data'); this.#blockStore = blockStore; } diff --git a/yarn-project/archiver/src/archiver/kv_archiver_store/kv_archiver_store.test.ts b/yarn-project/archiver/src/archiver/kv_archiver_store/kv_archiver_store.test.ts index 2903ea6fe9c..520ac236d05 100644 --- a/yarn-project/archiver/src/archiver/kv_archiver_store/kv_archiver_store.test.ts +++ b/yarn-project/archiver/src/archiver/kv_archiver_store/kv_archiver_store.test.ts @@ -1,4 +1,3 @@ -import { EthAddress } from '@aztec/circuits.js'; import { AztecLmdbStore } from '@aztec/kv-store'; import { describeArchiverDataStore } from '../archiver_store_test_suite.js'; @@ -8,7 +7,7 @@ describe('KVArchiverDataStore', () => { let archiverStore: KVArchiverDataStore; beforeEach(async () => { - archiverStore = new KVArchiverDataStore(await AztecLmdbStore.create(EthAddress.random())); + archiverStore = new KVArchiverDataStore(await AztecLmdbStore.openTmp()); }); describeArchiverDataStore('ArchiverStore', () => archiverStore); diff --git a/yarn-project/archiver/src/archiver/kv_archiver_store/kv_archiver_store.ts b/yarn-project/archiver/src/archiver/kv_archiver_store/kv_archiver_store.ts index 74be29d9d58..db9f0e4cddd 100644 --- a/yarn-project/archiver/src/archiver/kv_archiver_store/kv_archiver_store.ts +++ b/yarn-project/archiver/src/archiver/kv_archiver_store/kv_archiver_store.ts @@ -14,9 +14,12 @@ import { Fr } from '@aztec/circuits.js'; import { AztecAddress } from '@aztec/foundation/aztec-address'; import { createDebugLogger } from '@aztec/foundation/log'; import { AztecKVStore } from '@aztec/kv-store'; +import { ContractClassWithId, ContractInstanceWithAddress } from '@aztec/types/contracts'; import { ArchiverDataStore, ArchiverL1SynchPoint } from '../archiver_store.js'; import { BlockStore } from './block_store.js'; +import { ContractClassStore } from './contract_class_store.js'; +import { ContractInstanceStore } from './contract_instance_store.js'; import { ContractStore } from './contract_store.js'; import { LogStore } from './log_store.js'; import { MessageStore } from './message_store.js'; @@ -29,6 +32,8 @@ export class KVArchiverDataStore implements ArchiverDataStore { #logStore: LogStore; #contractStore: ContractStore; #messageStore: MessageStore; + #contractClassStore: ContractClassStore; + #contractInstanceStore: ContractInstanceStore; #log = createDebugLogger('aztec:archiver:lmdb'); @@ -37,6 +42,24 @@ export class KVArchiverDataStore implements ArchiverDataStore { this.#logStore = new LogStore(db, this.#blockStore, logsMaxPageSize); this.#contractStore = new ContractStore(db, this.#blockStore); this.#messageStore = new MessageStore(db); + this.#contractClassStore = new ContractClassStore(db); + this.#contractInstanceStore = new ContractInstanceStore(db); + } + + getContractClass(id: Fr): Promise { + return Promise.resolve(this.#contractClassStore.getContractClass(id)); + } + + getContractInstance(address: AztecAddress): Promise { + return Promise.resolve(this.#contractInstanceStore.getContractInstance(address)); + } + + async addContractClasses(data: ContractClassWithId[], _blockNumber: number): Promise { + return (await Promise.all(data.map(c => this.#contractClassStore.addContractClass(c)))).every(Boolean); + } + + async addContractInstances(data: ContractInstanceWithAddress[], _blockNumber: number): Promise { + return (await Promise.all(data.map(c => this.#contractInstanceStore.addContractInstance(c)))).every(Boolean); } /** diff --git a/yarn-project/archiver/src/archiver/kv_archiver_store/log_store.ts b/yarn-project/archiver/src/archiver/kv_archiver_store/log_store.ts index afab800fb48..b29e3d67818 100644 --- a/yarn-project/archiver/src/archiver/kv_archiver_store/log_store.ts +++ b/yarn-project/archiver/src/archiver/kv_archiver_store/log_store.ts @@ -23,8 +23,8 @@ export class LogStore { #log = createDebugLogger('aztec:archiver:log_store'); constructor(private db: AztecKVStore, private blockStore: BlockStore, logsMaxPageSize: number = 1000) { - this.#encryptedLogs = db.createMap('archiver_encrypted_logs'); - this.#unencryptedLogs = db.createMap('archiver_unencrypted_logs'); + this.#encryptedLogs = db.openMap('archiver_encrypted_logs'); + this.#unencryptedLogs = db.openMap('archiver_unencrypted_logs'); this.#logsMaxPageSize = logsMaxPageSize; } diff --git a/yarn-project/archiver/src/archiver/kv_archiver_store/message_store.ts b/yarn-project/archiver/src/archiver/kv_archiver_store/message_store.ts index 81a83e21560..642c90edde3 100644 --- a/yarn-project/archiver/src/archiver/kv_archiver_store/message_store.ts +++ b/yarn-project/archiver/src/archiver/kv_archiver_store/message_store.ts @@ -27,10 +27,10 @@ export class MessageStore { #log = createDebugLogger('aztec:archiver:message_store'); constructor(private db: AztecKVStore) { - this.#messages = db.createMap('archiver_l1_to_l2_messages'); - this.#pendingMessagesByFee = db.createCounter('archiver_messages_by_fee'); - this.#lastL1BlockAddingMessages = db.createSingleton('archiver_last_l1_block_adding_messages'); - this.#lastL1BlockCancellingMessages = db.createSingleton('archiver_last_l1_block_cancelling_messages'); + this.#messages = db.openMap('archiver_l1_to_l2_messages'); + this.#pendingMessagesByFee = db.openCounter('archiver_messages_by_fee'); + this.#lastL1BlockAddingMessages = db.openSingleton('archiver_last_l1_block_adding_messages'); + this.#lastL1BlockCancellingMessages = db.openSingleton('archiver_last_l1_block_cancelling_messages'); } /** diff --git a/yarn-project/archiver/src/archiver/memory_archiver_store/memory_archiver_store.ts b/yarn-project/archiver/src/archiver/memory_archiver_store/memory_archiver_store.ts index 62824ae1fe3..ecd0afda6d8 100644 --- a/yarn-project/archiver/src/archiver/memory_archiver_store/memory_archiver_store.ts +++ b/yarn-project/archiver/src/archiver/memory_archiver_store/memory_archiver_store.ts @@ -17,6 +17,7 @@ import { } from '@aztec/circuit-types'; import { Fr, NUMBER_OF_L1_L2_MESSAGES_PER_ROLLUP } from '@aztec/circuits.js'; import { AztecAddress } from '@aztec/foundation/aztec-address'; +import { ContractClassWithId, ContractInstanceWithAddress } from '@aztec/types/contracts'; import { ArchiverDataStore } from '../archiver_store.js'; import { L1ToL2MessageStore, PendingL1ToL2MessageStore } from './l1_to_l2_message_store.js'; @@ -68,6 +69,10 @@ export class MemoryArchiverStore implements ArchiverDataStore { */ private pendingL1ToL2Messages: PendingL1ToL2MessageStore = new PendingL1ToL2MessageStore(); + private contractClasses: Map = new Map(); + + private contractInstances: Map = new Map(); + private lastL1BlockAddedMessages: bigint = 0n; private lastL1BlockCancelledMessages: bigint = 0n; @@ -76,6 +81,28 @@ export class MemoryArchiverStore implements ArchiverDataStore { public readonly maxLogs: number, ) {} + public getContractClass(id: Fr): Promise { + return Promise.resolve(this.contractClasses.get(id.toString())); + } + + public getContractInstance(address: AztecAddress): Promise { + return Promise.resolve(this.contractInstances.get(address.toString())); + } + + public addContractClasses(data: ContractClassWithId[], _blockNumber: number): Promise { + for (const contractClass of data) { + this.contractClasses.set(contractClass.id.toString(), contractClass); + } + return Promise.resolve(true); + } + + public addContractInstances(data: ContractInstanceWithAddress[], _blockNumber: number): Promise { + for (const contractInstance of data) { + this.contractInstances.set(contractInstance.address.toString(), contractInstance); + } + return Promise.resolve(true); + } + /** * Append new blocks to the store's list. * @param blocks - The L2 blocks to be added to the store. diff --git a/yarn-project/archiver/src/index.ts b/yarn-project/archiver/src/index.ts index c2f0d128be8..e47ee3fcc37 100644 --- a/yarn-project/archiver/src/index.ts +++ b/yarn-project/archiver/src/index.ts @@ -8,6 +8,7 @@ import { Archiver, getConfigEnvVars } from './archiver/index.js'; import { MemoryArchiverStore } from './archiver/memory_archiver_store/memory_archiver_store.js'; export * from './archiver/index.js'; +export * from './rpc/index.js'; const log = createDebugLogger('aztec:archiver'); diff --git a/yarn-project/archiver/src/rpc/archiver_client.ts b/yarn-project/archiver/src/rpc/archiver_client.ts new file mode 100644 index 00000000000..89b9c4bb127 --- /dev/null +++ b/yarn-project/archiver/src/rpc/archiver_client.ts @@ -0,0 +1,33 @@ +import { + ContractData, + EncodedContractFunction, + ExtendedContractData, + ExtendedUnencryptedL2Log, + L1ToL2Message, + L2Block, + L2BlockL2Logs, +} from '@aztec/circuit-types'; +import { EthAddress, Fr } from '@aztec/circuits.js'; +import { createJsonRpcClient, makeFetch } from '@aztec/foundation/json-rpc/client'; + +import { ArchiveSource } from '../index.js'; + +export const createArchiverClient = (url: string, fetch = makeFetch([1, 2, 3], true)): ArchiveSource => + createJsonRpcClient( + url, + { + ContractData, + EncodedContractFunction, + EthAddress, + ExtendedContractData, + ExtendedUnencryptedL2Log, + Fr, + L1ToL2Message, + L2Block, + L2BlockL2Logs, + }, + {}, + false, + 'archiver', + fetch, + ); diff --git a/yarn-project/archiver/src/rpc/archiver_server.ts b/yarn-project/archiver/src/rpc/archiver_server.ts new file mode 100644 index 00000000000..7c011e5d242 --- /dev/null +++ b/yarn-project/archiver/src/rpc/archiver_server.ts @@ -0,0 +1,37 @@ +import { + ContractData, + EncodedContractFunction, + ExtendedContractData, + ExtendedUnencryptedL2Log, + L1ToL2Message, + L2Block, + L2BlockL2Logs, +} from '@aztec/circuit-types'; +import { EthAddress, Fr } from '@aztec/circuits.js'; +import { JsonRpcServer } from '@aztec/foundation/json-rpc/server'; + +import { Archiver } from '../index.js'; + +/** + * Wrap an Archiver instance with a JSON RPC HTTP server. + * @param archiverService - The Archiver instance + * @returns An JSON-RPC HTTP server + */ +export function createArchiverRpcServer(archiverService: Archiver): JsonRpcServer { + return new JsonRpcServer( + archiverService, + { + ContractData, + EncodedContractFunction, + EthAddress, + ExtendedContractData, + ExtendedUnencryptedL2Log, + Fr, + L1ToL2Message, + L2Block, + L2BlockL2Logs, + }, + {}, + ['start', 'stop'], + ); +} diff --git a/yarn-project/archiver/src/rpc/index.ts b/yarn-project/archiver/src/rpc/index.ts new file mode 100644 index 00000000000..726d9120af8 --- /dev/null +++ b/yarn-project/archiver/src/rpc/index.ts @@ -0,0 +1,2 @@ +export * from './archiver_client.js'; +export * from './archiver_server.js'; diff --git a/yarn-project/archiver/tsconfig.json b/yarn-project/archiver/tsconfig.json index 6a0c794ba6d..69fe51229cc 100644 --- a/yarn-project/archiver/tsconfig.json +++ b/yarn-project/archiver/tsconfig.json @@ -23,6 +23,9 @@ }, { "path": "../l1-artifacts" + }, + { + "path": "../types" } ], "include": ["src"] diff --git a/yarn-project/aztec-node/package.json b/yarn-project/aztec-node/package.json index a2e602a03ff..2c0dd26296c 100644 --- a/yarn-project/aztec-node/package.json +++ b/yarn-project/aztec-node/package.json @@ -47,17 +47,11 @@ "@aztec/world-state": "workspace:^", "koa": "^2.14.2", "koa-router": "^12.0.0", - "levelup": "^5.1.1", - "lmdb": "^2.9.1", - "memdown": "^6.1.1", "tslib": "^2.4.0" }, "devDependencies": { "@jest/globals": "^29.5.0", "@types/jest": "^29.5.0", - "@types/leveldown": "^4.0.4", - "@types/levelup": "^5.1.2", - "@types/memdown": "^3.0.0", "@types/node": "^18.7.23", "jest": "^29.5.0", "ts-jest": "^29.1.0", diff --git a/yarn-project/aztec-node/src/aztec-node/config.ts b/yarn-project/aztec-node/src/aztec-node/config.ts index 185bd3d8b6b..73f14115804 100644 --- a/yarn-project/aztec-node/src/aztec-node/config.ts +++ b/yarn-project/aztec-node/src/aztec-node/config.ts @@ -11,6 +11,9 @@ export type AztecNodeConfig = ArchiverConfig & P2PConfig & { /** Whether the sequencer is disabled for this node. */ disableSequencer: boolean; + + /** A URL for an archiver service that the node will use. */ + archiverUrl?: string; }; /** @@ -25,6 +28,7 @@ export function getConfigEnvVars(): AztecNodeConfig { ...getP2PConfigEnvVars(), ...getWorldStateVars(), disableSequencer: !!SEQ_DISABLED, + archiverUrl: process.env.ARCHIVER_URL, }; return allEnvVars; diff --git a/yarn-project/aztec-node/src/aztec-node/db.ts b/yarn-project/aztec-node/src/aztec-node/db.ts deleted file mode 100644 index 9b5be428781..00000000000 --- a/yarn-project/aztec-node/src/aztec-node/db.ts +++ /dev/null @@ -1,92 +0,0 @@ -import { LogFn } from '@aztec/foundation/log'; - -import { LevelDown, default as leveldown } from 'leveldown'; -import { LevelUp, default as levelup } from 'levelup'; -import { RootDatabase, open } from 'lmdb'; -import { MemDown, default as memdown } from 'memdown'; -import { mkdir } from 'node:fs/promises'; -import { join } from 'node:path'; - -import { AztecNodeConfig } from './config.js'; - -export const createMemDown = () => (memdown as any)() as MemDown; -export const createLevelDown = (path: string) => (leveldown as any)(path) as LevelDown; - -const DB_SUBDIR = 'aztec-node-db'; -const WORLD_STATE_SUBDIR = 'aztec-world-state-db'; -const NODE_METADATA_KEY = '@@aztec_node_metadata'; - -/** - * The metadata for an aztec node. - */ -type NodeMetadata = { - /** - * The address of the rollup contract on L1 - */ - rollupContractAddress: string; -}; - -/** - * Opens the database for the aztec node. If a data directory is specified, then this attempts to create it. - * @param config - The configuration to be used by the aztec node. - * @throws If `config.dataDirectory` is set and the directory cannot be created. - * @returns The database for the aztec node. - */ -export async function openDb( - config: AztecNodeConfig, - log: LogFn, -): Promise<[nodeDb: RootDatabase, worldStateDb: LevelUp]> { - const nodeMetadata: NodeMetadata = { - rollupContractAddress: config.l1Contracts.rollupAddress.toString(), - }; - - let nodeDb: RootDatabase; - let worldStateDb: LevelUp; - - if (config.dataDirectory) { - const nodeDir = join(config.dataDirectory, DB_SUBDIR); - const worldStateDir = join(config.dataDirectory, WORLD_STATE_SUBDIR); - // this throws if we don't have permissions to create the directory - await mkdir(nodeDir, { recursive: true }); - await mkdir(worldStateDir, { recursive: true }); - - log(`Opening aztec-node database at ${nodeDir}`); - nodeDb = open(nodeDir, {}); - - log(`Opening world-state database at ${worldStateDir}`); - worldStateDb = levelup(createLevelDown(worldStateDir)); - } else { - log('Opening temporary databases'); - // not passing a path will use a temp file that gets deleted when the process exits - nodeDb = open({}); - worldStateDb = levelup(createMemDown()); - } - - await checkNodeMetadataAndClear(nodeDb, worldStateDb, nodeMetadata, log); - return [nodeDb, worldStateDb]; -} - -/** - * Checks the node metadata and clears the database if the rollup contract address has changed. - * @param nodeDb - The database for the aztec node. - * @param nodeMetadata - The metadata for the aztec node. - */ -async function checkNodeMetadataAndClear( - nodeDb: RootDatabase, - worldStateDb: LevelUp, - nodeMetadata: NodeMetadata, - log: LogFn, -): Promise { - const metadataDB = nodeDb.openDB('metadata', {}); - try { - const existing = metadataDB.get(NODE_METADATA_KEY); - // if the rollup addresses are different, wipe the local database and start over - if (!existing || existing.rollupContractAddress !== nodeMetadata.rollupContractAddress) { - log('Rollup contract address has changed, clearing databases'); - await Promise.all([nodeDb.clearAsync(), worldStateDb.clear()]); - } - await metadataDB.put(NODE_METADATA_KEY, nodeMetadata); - } finally { - await metadataDB.close(); - } -} diff --git a/yarn-project/aztec-node/src/aztec-node/http_rpc_server.ts b/yarn-project/aztec-node/src/aztec-node/http_rpc_server.ts index f5e99becbde..0b4d6f7236b 100644 --- a/yarn-project/aztec-node/src/aztec-node/http_rpc_server.ts +++ b/yarn-project/aztec-node/src/aztec-node/http_rpc_server.ts @@ -11,7 +11,7 @@ import { Tx, TxHash, } from '@aztec/circuit-types'; -import { BlockHeader, FunctionSelector } from '@aztec/circuits.js'; +import { FunctionSelector, Header } from '@aztec/circuits.js'; import { AztecAddress } from '@aztec/foundation/aztec-address'; import { EthAddress } from '@aztec/foundation/eth-address'; import { Fr } from '@aztec/foundation/fields'; @@ -34,7 +34,7 @@ export function createAztecNodeRpcServer(node: AztecNode) { ContractData, Fr, FunctionSelector, - BlockHeader, + Header, L2Block, L2Tx, LogId, @@ -43,7 +43,6 @@ export function createAztecNodeRpcServer(node: AztecNode) { L1ToL2MessageAndIndex, }, { Tx, L2BlockL2Logs }, - false, // disable methods not part of the AztecNode interface ['start', 'stop'], ); diff --git a/yarn-project/aztec-node/src/aztec-node/server.ts b/yarn-project/aztec-node/src/aztec-node/server.ts index 0c65a9c488d..2ccd40baa00 100644 --- a/yarn-project/aztec-node/src/aztec-node/server.ts +++ b/yarn-project/aztec-node/src/aztec-node/server.ts @@ -1,4 +1,4 @@ -import { Archiver, KVArchiverDataStore } from '@aztec/archiver'; +import { ArchiveSource, Archiver, KVArchiverDataStore, createArchiverClient } from '@aztec/archiver'; import { AztecNode, ContractData, @@ -24,10 +24,9 @@ import { } from '@aztec/circuit-types'; import { ARCHIVE_HEIGHT, - BlockHeader, CONTRACT_TREE_HEIGHT, Fr, - GlobalVariables, + Header, L1_TO_L2_MSG_TREE_HEIGHT, NOTE_HASH_TREE_HEIGHT, NULLIFIER_TREE_HEIGHT, @@ -35,11 +34,11 @@ import { PUBLIC_DATA_TREE_HEIGHT, PublicDataTreeLeafPreimage, } from '@aztec/circuits.js'; -import { computeGlobalsHash, computePublicDataTreeLeafSlot } from '@aztec/circuits.js/abis'; +import { computePublicDataTreeLeafSlot } from '@aztec/circuits.js/abis'; import { L1ContractAddresses, createEthereumChain } from '@aztec/ethereum'; import { AztecAddress } from '@aztec/foundation/aztec-address'; import { createDebugLogger } from '@aztec/foundation/log'; -import { AztecLmdbStore } from '@aztec/kv-store'; +import { AztecKVStore, AztecLmdbStore } from '@aztec/kv-store'; import { AztecKVTxPool, P2P, createP2PClient } from '@aztec/p2p'; import { GlobalVariableBuilder, @@ -56,10 +55,7 @@ import { getConfigEnvVars as getWorldStateConfig, } from '@aztec/world-state'; -import { LevelUp } from 'levelup'; - import { AztecNodeConfig } from './config.js'; -import { openDb } from './db.js'; /** * The aztec node. @@ -78,7 +74,7 @@ export class AztecNodeService implements AztecNode { protected readonly chainId: number, protected readonly version: number, protected readonly globalVariableBuilder: GlobalVariableBuilder, - protected readonly merkleTreesDb: LevelUp, + protected readonly merkleTreesDb: AztecKVStore, private log = createDebugLogger('aztec:node'), ) { const message = @@ -106,12 +102,16 @@ export class AztecNodeService implements AztecNode { } const log = createDebugLogger('aztec:node'); - const store = await AztecLmdbStore.create(config.l1Contracts.rollupAddress, config.dataDirectory); - const [_, worldStateDb] = await openDb(config, log); + const store = await AztecLmdbStore.open(config.l1Contracts.rollupAddress, config.dataDirectory); - // first create and sync the archiver - const archiverStore = new KVArchiverDataStore(store, config.maxLogs); - const archiver = await Archiver.createAndSync(config, archiverStore, true); + let archiver: ArchiveSource; + if (!config.archiverUrl) { + // first create and sync the archiver + const archiverStore = new KVArchiverDataStore(store, config.maxLogs); + archiver = await Archiver.createAndSync(config, archiverStore, true); + } else { + archiver = createArchiverClient(config.archiverUrl); + } // we identify the P2P transaction protocol by using the rollup contract address. // this may well change in future @@ -121,14 +121,9 @@ export class AztecNodeService implements AztecNode { const p2pClient = await createP2PClient(store, config, new AztecKVTxPool(store), archiver); // now create the merkle trees and the world state synchronizer - const merkleTrees = await MerkleTrees.new(worldStateDb); + const merkleTrees = await MerkleTrees.new(store); const worldStateConfig: WorldStateConfig = getWorldStateConfig(); - const worldStateSynchronizer = await ServerWorldStateSynchronizer.new( - worldStateDb, - merkleTrees, - archiver, - worldStateConfig, - ); + const worldStateSynchronizer = new ServerWorldStateSynchronizer(store, merkleTrees, archiver, worldStateConfig); // start both and wait for them to sync from the block source await Promise.all([p2pClient.start(), worldStateSynchronizer.start()]); @@ -151,7 +146,7 @@ export class AztecNodeService implements AztecNode { ethereumChain.chainInfo.id, config.version, getGlobalVariableBuilder(config), - worldStateDb, + store, log, ); } @@ -285,8 +280,6 @@ export class AztecNodeService implements AztecNode { await this.p2pClient.stop(); await this.worldStateSynchronizer.stop(); await this.blockSource.stop(); - this.log('Closing Merkle Trees'); - await this.merkleTreesDb.close(); this.log.info(`Stopped`); } @@ -530,52 +523,19 @@ export class AztecNodeService implements AztecNode { return preimage.value; } - /** - * Returns the current committed roots for the data trees. - * @returns The current committed roots for the data trees. - */ - public async getTreeRoots(): Promise> { - const committedDb = await this.#getWorldState('latest'); - const getTreeRoot = async (id: MerkleTreeId) => Fr.fromBuffer((await committedDb.getTreeInfo(id)).root); - - const [noteHashTree, nullifierTree, contractTree, l1ToL2MessageTree, archive, publicDataTree] = await Promise.all([ - getTreeRoot(MerkleTreeId.NOTE_HASH_TREE), - getTreeRoot(MerkleTreeId.NULLIFIER_TREE), - getTreeRoot(MerkleTreeId.CONTRACT_TREE), - getTreeRoot(MerkleTreeId.L1_TO_L2_MESSAGE_TREE), - getTreeRoot(MerkleTreeId.ARCHIVE), - getTreeRoot(MerkleTreeId.PUBLIC_DATA_TREE), - ]); - - return { - [MerkleTreeId.CONTRACT_TREE]: contractTree, - [MerkleTreeId.NOTE_HASH_TREE]: noteHashTree, - [MerkleTreeId.NULLIFIER_TREE]: nullifierTree, - [MerkleTreeId.PUBLIC_DATA_TREE]: publicDataTree, - [MerkleTreeId.L1_TO_L2_MESSAGE_TREE]: l1ToL2MessageTree, - [MerkleTreeId.ARCHIVE]: archive, - }; - } - /** * Returns the currently committed block header. * @returns The current committed block header. */ - // TODO(#3937): Nuke this - public async getBlockHeader(): Promise { + public async getHeader(): Promise
{ + const block = await this.getBlock(-1); + if (block) { + return block.header; + } + + // No block was not found so we build the initial header. const committedDb = await this.#getWorldState('latest'); - const [roots, globalsHash] = await Promise.all([this.getTreeRoots(), committedDb.getLatestGlobalVariablesHash()]); - - return new BlockHeader( - roots[MerkleTreeId.NOTE_HASH_TREE], - roots[MerkleTreeId.NULLIFIER_TREE], - roots[MerkleTreeId.CONTRACT_TREE], - roots[MerkleTreeId.L1_TO_L2_MESSAGE_TREE], - roots[MerkleTreeId.ARCHIVE], - Fr.ZERO, // TODO(#3441) - roots[MerkleTreeId.PUBLIC_DATA_TREE], - globalsHash, - ); + return await committedDb.buildInitialHeader(); } /** @@ -586,24 +546,19 @@ export class AztecNodeService implements AztecNode { this.log.info(`Simulating tx ${await tx.getTxHash()}`); const blockNumber = (await this.blockSource.getBlockNumber()) + 1; const newGlobalVariables = await this.globalVariableBuilder.buildGlobalVariables(new Fr(blockNumber)); - const prevGlobalVariables = - (await this.blockSource.getBlock(-1))?.header.globalVariables ?? GlobalVariables.empty(); + const prevHeader = (await this.blockSource.getBlock(-1))?.header; // Instantiate merkle trees so uncommitted updates by this simulation are local to it. // TODO we should be able to remove this after https://github.com/AztecProtocol/aztec-packages/issues/1869 // So simulation of public functions doesn't affect the merkle trees. - const merkleTrees = new MerkleTrees(this.merkleTreesDb, this.log); - const globalVariablesHash = computeGlobalsHash(prevGlobalVariables); - await merkleTrees.init({ - globalVariablesHash, - }); + const merkleTrees = await MerkleTrees.new(this.merkleTreesDb, this.log); const publicProcessorFactory = new PublicProcessorFactory( merkleTrees.asLatest(), this.contractDataSource, this.l1ToL2MessageSource, ); - const processor = await publicProcessorFactory.create(prevGlobalVariables, newGlobalVariables); + const processor = await publicProcessorFactory.create(prevHeader, newGlobalVariables); const [, failedTxs] = await processor.process([tx]); if (failedTxs.length) { throw failedTxs[0].error; diff --git a/yarn-project/aztec-node/src/declaration.d.ts b/yarn-project/aztec-node/src/declaration.d.ts deleted file mode 100644 index d7367c50ba8..00000000000 --- a/yarn-project/aztec-node/src/declaration.d.ts +++ /dev/null @@ -1,16 +0,0 @@ -// eslint-disable-next-line @typescript-eslint/no-unused-vars -import { RootDatabaseOptionsWithPath } from 'lmdb'; - -// The problem is this snippet `nodeDb = open({});` in src/aztec-node/db.ts -// tsc compiles this code fine, but ts-jest can't. -// This is a mixture for two bugs: -// - the first in ts-jest, it gets confused by packages with mixed CJS and ESM type exports - https://github.com/kulshekhar/ts-jest/issues/4221 -// - the second in lmdb, it outputs different CJS and ESM types - https://github.com/kriszyp/lmdb-js/issues/243#issuecomment-1823585586 - -declare module 'lmdb' { - /* eslint-disable jsdoc/require-jsdoc */ - interface RootDatabaseOptionsWithPath { - path?: string; - } - /* eslint-enable jsdoc/require-jsdoc */ -} diff --git a/yarn-project/aztec-node/terraform/main.tf b/yarn-project/aztec-node/terraform/main.tf index becb03ad1b5..a65230f6a16 100644 --- a/yarn-project/aztec-node/terraform/main.tf +++ b/yarn-project/aztec-node/terraform/main.tf @@ -62,7 +62,7 @@ locals { "/dns4/${var.DEPLOY_TAG}-p2p-bootstrap-${i + 1}.local/tcp/${var.BOOTNODE_LISTEN_PORT + i}/p2p/${local.bootnode_ids[i]}" ] combined_bootnodes = join(",", local.bootnodes) - data_dir = "/usr/src/yarn-project/aztec-sandbox/data" + data_dir = "/usr/src/yarn-project/aztec/data" } resource "aws_cloudwatch_log_group" "aztec-node-log-group" { @@ -154,7 +154,7 @@ resource "aws_ecs_task_definition" "aztec-node" { [ { "name": "${var.DEPLOY_TAG}-aztec-node-${count.index + 1}", - "image": "${var.DOCKERHUB_ACCOUNT}/aztec-sandbox:${var.DEPLOY_TAG}", + "image": "${var.DOCKERHUB_ACCOUNT}/aztec:${var.DEPLOY_TAG}", "essential": true, "memoryReservation": 3776, "portMappings": [ diff --git a/yarn-project/aztec-nr/.gitrepo b/yarn-project/aztec-nr/.gitrepo index 7c761635170..47499d97311 100644 --- a/yarn-project/aztec-nr/.gitrepo +++ b/yarn-project/aztec-nr/.gitrepo @@ -6,7 +6,7 @@ [subrepo] remote = https://github.com/AztecProtocol/aztec-nr branch = master - commit = f73feb253bc8aac7c6e8a4bcee144e99d702f948 + commit = 5b95b816c1304e80ebc5a6e070bb9f3dc1ba49de method = merge cmdver = 0.4.6 - parent = 6bd7d95b31a67464d446aa9d7fd494e829542b3c + parent = 606edfeb1b4e5e77b9ae221a8265030b3aa461b4 diff --git a/yarn-project/aztec-nr/address-note/src/address_note.nr b/yarn-project/aztec-nr/address-note/src/address_note.nr index 199cbd7b078..6a960c505d1 100644 --- a/yarn-project/aztec-nr/address-note/src/address_note.nr +++ b/yarn-project/aztec-nr/address-note/src/address_note.nr @@ -2,11 +2,14 @@ use dep::aztec::log::emit_encrypted_log; // docs:end:encrypted_import use dep::aztec::{ - protocol_types::address::AztecAddress, + protocol_types::{ + address::AztecAddress, + traits::{Serialize, Deserialize, Empty} + }, note::{ note_header::NoteHeader, note_interface::NoteInterface, - utils::compute_note_hash_for_read_or_nullify, + utils::compute_note_hash_for_consumption, }, oracle::{ rand::rand, @@ -14,7 +17,7 @@ use dep::aztec::{ get_public_key::get_public_key, }, hash::pedersen_hash, - context::PrivateContext, + context::PrivateContext }; global ADDRESS_NOTE_LEN: Field = 3; @@ -28,24 +31,14 @@ struct AddressNote { header: NoteHeader, } -impl AddressNote { - pub fn new(address: AztecAddress, owner: AztecAddress) -> Self { - let randomness = rand(); - AddressNote { - address, - owner, - randomness, - header: NoteHeader::empty(), - } - } -// docs:end:address_note_def - - - pub fn serialize(self) -> [Field; ADDRESS_NOTE_LEN]{ +impl Serialize for AddressNote { + fn serialize(self) -> [Field; ADDRESS_NOTE_LEN]{ [self.address.to_field(), self.owner.to_field(), self.randomness] } +} - pub fn deserialize(serialized_note: [Field; ADDRESS_NOTE_LEN]) -> Self { +impl Deserialize for AddressNote { + fn deserialize(serialized_note: [Field; ADDRESS_NOTE_LEN]) -> Self { AddressNote { address: AztecAddress::from_field(serialized_note[0]), owner: AztecAddress::from_field(serialized_note[1]), @@ -53,14 +46,16 @@ impl AddressNote { header: NoteHeader::empty(), } } +} - pub fn compute_note_hash(self) -> Field { +impl NoteInterface for AddressNote { + fn compute_note_content_hash(self) -> Field { // TODO(#1205) Should use a non-zero generator index. pedersen_hash(self.serialize(), 0) } - pub fn compute_nullifier(self, context: &mut PrivateContext) -> Field { - let note_hash_for_nullify = compute_note_hash_for_read_or_nullify(AddressNoteMethods, self); + fn compute_nullifier(self, context: &mut PrivateContext) -> Field { + let note_hash_for_nullify = compute_note_hash_for_consumption(self); let secret = context.request_nullifier_secret_key(self.owner); // TODO(#1205) Should use a non-zero generator index. pedersen_hash([ @@ -70,8 +65,8 @@ impl AddressNote { ],0) } - pub fn compute_nullifier_without_context(self) -> Field { - let note_hash_for_nullify = compute_note_hash_for_read_or_nullify(AddressNoteMethods, self); + fn compute_nullifier_without_context(self) -> Field { + let note_hash_for_nullify = compute_note_hash_for_consumption(self); let secret = get_nullifier_secret_key(self.owner); // TODO(#1205) Should use a non-zero generator index. pedersen_hash([ @@ -81,12 +76,16 @@ impl AddressNote { ],0) } - pub fn set_header(&mut self, header: NoteHeader) { + fn set_header(&mut self, header: NoteHeader) { self.header = header; } + fn get_header(note: Self) -> NoteHeader { + note.header + } + // Broadcasts the note as an encrypted log on L1. - pub fn broadcast(self, context: &mut PrivateContext, slot: Field) { + fn broadcast(self, context: &mut PrivateContext, slot: Field) { let encryption_pub_key = get_public_key(self.owner); // docs:start:encrypted emit_encrypted_log( @@ -100,46 +99,15 @@ impl AddressNote { } } -fn deserialize(serialized_note: [Field; ADDRESS_NOTE_LEN]) -> AddressNote { - AddressNote::deserialize(serialized_note) -} - -fn serialize(note: AddressNote) -> [Field; ADDRESS_NOTE_LEN] { - note.serialize() -} - -fn compute_note_hash(note: AddressNote) -> Field { - note.compute_note_hash() -} - -fn compute_nullifier(note: AddressNote, context: &mut PrivateContext) -> Field { - note.compute_nullifier(context) -} - -fn compute_nullifier_without_context(note: AddressNote) -> Field { - note.compute_nullifier_without_context() -} - -fn get_header(note: AddressNote) -> NoteHeader { - note.header -} - -fn set_header(note: &mut AddressNote, header: NoteHeader) { - note.set_header(header); -} - -// Broadcasts the note as an encrypted log on L1. -fn broadcast(context: &mut PrivateContext, slot: Field, note: AddressNote) { - note.broadcast(context, slot); +impl AddressNote { + pub fn new(address: AztecAddress, owner: AztecAddress) -> Self { + let randomness = rand(); + AddressNote { + address, + owner, + randomness, + header: NoteHeader::empty(), + } + } +// docs:end:address_note_def } - -global AddressNoteMethods = NoteInterface { - deserialize, - serialize, - compute_note_hash, - compute_nullifier, - compute_nullifier_without_context, - get_header, - set_header, - broadcast, -}; diff --git a/yarn-project/aztec-nr/authwit/src/account.nr b/yarn-project/aztec-nr/authwit/src/account.nr index 21c13e3c8ef..790aee2b662 100644 --- a/yarn-project/aztec-nr/authwit/src/account.nr +++ b/yarn-project/aztec-nr/authwit/src/account.nr @@ -1,6 +1,5 @@ use dep::aztec::context::{PrivateContext, PublicContext, Context}; use dep::aztec::state_vars::{map::Map, public_state::PublicState}; -use dep::aztec::types::type_serialization::bool_serialization::{BoolSerializationMethods,BOOL_SERIALIZED_LEN}; use crate::entrypoint::EntrypointPayload; use crate::auth::IS_VALID_SELECTOR; @@ -8,7 +7,7 @@ use crate::auth::IS_VALID_SELECTOR; struct AccountActions { context: Context, is_valid_impl: fn(&mut PrivateContext, Field) -> bool, - approved_action: Map>, + approved_action: Map>, } impl AccountActions { @@ -20,7 +19,7 @@ impl AccountActions { context, approved_action_storage_slot, |context, slot| { - PublicState::new(context, slot, BoolSerializationMethods) + PublicState::new(context, slot) }, ), } diff --git a/yarn-project/aztec-nr/authwit/src/entrypoint.nr b/yarn-project/aztec-nr/authwit/src/entrypoint.nr index 82c3f5f6e2f..98987d49e36 100644 --- a/yarn-project/aztec-nr/authwit/src/entrypoint.nr +++ b/yarn-project/aztec-nr/authwit/src/entrypoint.nr @@ -1,5 +1,4 @@ use dep::aztec::abi; -use dep::aztec::types::vec::BoundedVec; use dep::aztec::context::PrivateContext; use dep::aztec::protocol_types::{ abis::{ @@ -12,6 +11,7 @@ use dep::aztec::protocol_types::{ address::AztecAddress, constants::GENERATOR_INDEX__SIGNATURE_PAYLOAD, hash::pedersen_hash, + traits::{Hash, Serialize} }; global ACCOUNT_MAX_CALLS: Field = 4; @@ -27,11 +27,13 @@ struct FunctionCall { is_public: bool, } -impl FunctionCall { +impl Serialize for FunctionCall { fn serialize(self) -> [Field; FUNCTION_CALL_SIZE] { [self.args_hash, self.function_selector.to_field(), self.target_address.to_field(), self.is_public as Field] } +} +impl FunctionCall { fn to_be_bytes(self) -> [u8; FUNCTION_CALL_SIZE_IN_BYTES] { let mut bytes: [u8; FUNCTION_CALL_SIZE_IN_BYTES] = [0; FUNCTION_CALL_SIZE_IN_BYTES]; let args_hash_bytes = self.args_hash.to_be_bytes(32); @@ -58,24 +60,28 @@ struct EntrypointPayload { } // docs:end:entrypoint-struct -impl EntrypointPayload { - fn hash(self) -> Field { - pedersen_hash( - self.serialize(), - GENERATOR_INDEX__SIGNATURE_PAYLOAD - ) - } - +impl Serialize for EntrypointPayload { // Serializes the entrypoint struct fn serialize(self) -> [Field; ENTRYPOINT_PAYLOAD_SIZE] { let mut fields: BoundedVec = BoundedVec::new(0); for call in self.function_calls { - fields.push_array(call.serialize()); + fields.extend_from_array(call.serialize()); } fields.push(self.nonce); fields.storage } +} + +impl Hash for EntrypointPayload { + fn hash(self) -> Field { + pedersen_hash( + self.serialize(), + GENERATOR_INDEX__SIGNATURE_PAYLOAD + ) + } +} +impl EntrypointPayload { // Serializes the payload as an array of bytes. Useful for hashing with sha256. fn to_be_bytes(self) -> [u8; ENTRYPOINT_PAYLOAD_SIZE_IN_BYTES] { let mut bytes: [u8; ENTRYPOINT_PAYLOAD_SIZE_IN_BYTES] = [0; ENTRYPOINT_PAYLOAD_SIZE_IN_BYTES]; diff --git a/yarn-project/aztec-nr/aztec/src/abi.nr b/yarn-project/aztec-nr/aztec/src/abi.nr index b6f5cfa92a9..6e194584313 100644 --- a/yarn-project/aztec-nr/aztec/src/abi.nr +++ b/yarn-project/aztec-nr/aztec/src/abi.nr @@ -1,12 +1,13 @@ use dep::protocol_types::{ abis::{ - block_header::BlockHeader, call_context::CallContext, private_circuit_public_inputs::PrivateCircuitPublicInputs, public_circuit_public_inputs::PublicCircuitPublicInputs, }, contrakt::deployment_data::ContractDeploymentData, hash::hash_args, + traits::{Hash, Serialize}, + header::Header, }; // docs:start:private-global-variables @@ -16,7 +17,7 @@ struct PrivateGlobalVariables { } // docs:end:private-global-variables -impl PrivateGlobalVariables { +impl Serialize<2> for PrivateGlobalVariables { fn serialize(self) -> [Field; 2] { [self.chain_id, self.version] } @@ -31,7 +32,7 @@ struct PublicGlobalVariables { } // docs:end:public-global-variables -impl PublicGlobalVariables { +impl Serialize<4> for PublicGlobalVariables { fn serialize(self) -> [Field; 4] { [self.chain_id, self.version, self.block_number, self.timestamp] } @@ -41,7 +42,7 @@ impl PublicGlobalVariables { // docs:start:private-context-inputs struct PrivateContextInputs { call_context : CallContext, - block_header: BlockHeader, + historical_header: Header, contract_deployment_data: ContractDeploymentData, private_global_variables: PrivateGlobalVariables, } @@ -51,7 +52,7 @@ struct PrivateContextInputs { // docs:start:public-context-inputs struct PublicContextInputs { call_context: CallContext, - block_header: BlockHeader, + historical_header: Header, public_global_variables: PublicGlobalVariables, } @@ -61,6 +62,12 @@ struct Hasher { fields: [Field], } +impl Hash for Hasher { + fn hash(self) -> Field { + hash_args(self.fields) + } +} + impl Hasher { pub fn new()-> Self { Self { fields: [] } @@ -75,8 +82,4 @@ impl Hasher { self.fields = self.fields.push_back(fields[i]); } } - - pub fn hash(self) -> Field { - hash_args(self.fields) - } } diff --git a/yarn-project/aztec-nr/aztec/src/context.nr b/yarn-project/aztec-nr/aztec/src/context.nr index 6329a9d541f..2766304193e 100644 --- a/yarn-project/aztec-nr/aztec/src/context.nr +++ b/yarn-project/aztec-nr/aztec/src/context.nr @@ -11,18 +11,19 @@ use crate::{ public_call::call_public_function_internal, enqueue_public_function_call::enqueue_public_function_call_internal, context::get_portal_address, - get_block_header::get_block_header, - nullifier_key::get_nullifier_key_pair, + header::get_header_at, + nullifier_key::{get_nullifier_key_pair, NullifierKeyPair}, }, - types::vec::BoundedVec, utils::Reader, }; use dep::protocol_types::{ abis::{ - block_header::BlockHeader, + append_only_tree_snapshot::AppendOnlyTreeSnapshot, call_context::CallContext, + global_variables::GlobalVariables, function_data::FunctionData, function_selector::FunctionSelector, + nullifier_key_validation_request::NullifierKeyValidationRequest, private_circuit_public_inputs::PrivateCircuitPublicInputs, public_circuit_public_inputs::PublicCircuitPublicInputs, call_stack_item::PrivateCallStackItem, @@ -42,6 +43,7 @@ use dep::protocol_types::{ MAX_PUBLIC_DATA_READS_PER_CALL, MAX_PUBLIC_DATA_UPDATE_REQUESTS_PER_CALL, MAX_READ_REQUESTS_PER_CALL, + MAX_NULLIFIER_KEY_VALIDATION_REQUESTS_PER_CALL, NUM_FIELDS_PER_SHA256, RETURN_VALUES_LENGTH, }, @@ -50,11 +52,13 @@ use dep::protocol_types::{ storage_read::StorageRead, storage_update_request::StorageUpdateRequest, }, - hash::hash_args, grumpkin_point::GrumpkinPoint, + grumpkin_private_key::GrumpkinPrivateKey, + hash::hash_args, + header::Header, + state_reference::StateReference, }; use dep::std::{ - grumpkin_scalar::GrumpkinScalar, option::Option, }; @@ -71,6 +75,7 @@ struct PrivateContext { return_values : BoundedVec, read_requests: BoundedVec, + nullifier_key_validation_requests: BoundedVec, new_commitments: BoundedVec, new_nullifiers: BoundedVec, @@ -80,11 +85,14 @@ struct PrivateContext { new_l2_to_l1_msgs : BoundedVec, // docs:end:private-context - block_header: BlockHeader, + // Header of a block whose state is used during private execution (not the block the transaction is included in). + historical_header: Header, // TODO(https://github.com/AztecProtocol/aztec-packages/issues/1165) // encrypted_logs_preimages: Vec, // unencrypted_logs_preimages: Vec, + + nullifier_key: Option, } impl PrivateContext { @@ -97,11 +105,12 @@ impl PrivateContext { return_values: BoundedVec::new(0), read_requests: BoundedVec::new(SideEffect::empty()), + nullifier_key_validation_requests: BoundedVec::new(NullifierKeyValidationRequest::empty()), new_commitments: BoundedVec::new(SideEffect::empty()), new_nullifiers: BoundedVec::new(SideEffectLinkedToNoteHash::empty()), - block_header: inputs.block_header, + historical_header: inputs.historical_header, private_call_stack_hashes: BoundedVec::new(0), public_call_stack_hashes: BoundedVec::new(0), @@ -110,6 +119,8 @@ impl PrivateContext { // TODO(https://github.com/AztecProtocol/aztec-packages/issues/1165) // encrypted_logs_preimages: Vec::new(), // unencrypted_logs_preimages: Vec::new(), + + nullifier_key: Option::none(), } } @@ -137,8 +148,16 @@ impl PrivateContext { self.inputs.call_context.function_selector } - pub fn get_block_header(self, block_number: u32) -> BlockHeader { - get_block_header(block_number, self) + // Returns the header of a block whose state is used during private execution (not the block the transaction is + // included in). + pub fn get_header(self) -> Header { + self.historical_header + } + + // Returns the header of an arbitrary block whose block number is less than or equal to the block number + // of historical header. + pub fn get_header_at(self, block_number: u32) -> Header { + get_header_at(block_number, self) } pub fn finish(self) -> PrivateCircuitPublicInputs { @@ -153,6 +172,7 @@ impl PrivateContext { args_hash: self.args_hash, return_values: self.return_values.storage, read_requests: self.read_requests.storage, + nullifier_key_validation_requests: self.nullifier_key_validation_requests.storage, new_commitments: self.new_commitments.storage, new_nullifiers: self.new_nullifiers.storage, private_call_stack_hashes: self.private_call_stack_hashes.storage, @@ -163,7 +183,7 @@ impl PrivateContext { unencrypted_logs_hash: unencrypted_logs_hash, encrypted_log_preimages_length: encrypted_log_preimages_length, unencrypted_log_preimages_length: unencrypted_log_preimages_length, - block_header: self.block_header, + historical_header: self.historical_header, contract_deployment_data: self.inputs.contract_deployment_data, chain_id: self.inputs.private_global_variables.chain_id, version: self.inputs.private_global_variables.version, @@ -199,11 +219,21 @@ impl PrivateContext { self.side_effect_counter = self.side_effect_counter + 1; } - pub fn request_nullifier_secret_key(&mut self, account: AztecAddress) -> GrumpkinScalar { - let key_pair = get_nullifier_key_pair(account); - validate_nullifier_key_against_address(account, key_pair.public_key, key_pair.secret_key); - // TODO: Add request to context. - // self.context.push_nullifier_key_validation_request(public_key, secret_key); + pub fn request_nullifier_secret_key(&mut self, account: AztecAddress) -> GrumpkinPrivateKey { + let key_pair = if self.nullifier_key.is_none() { + let key_pair = get_nullifier_key_pair(account); + validate_nullifier_key_against_address(account, key_pair.public_key); + let request = NullifierKeyValidationRequest { public_key: key_pair.public_key, secret_key: key_pair.secret_key }; + self.nullifier_key_validation_requests.push(request); + self.nullifier_key = Option::some(key_pair); + key_pair + } else { + let key_pair = self.nullifier_key.unwrap_unchecked(); + // If MAX_NULLIFIER_KEY_VALIDATION_REQUESTS_PER_CALL is larger than 1, need to update the way the key pair is cached. + assert(MAX_NULLIFIER_KEY_VALIDATION_REQUESTS_PER_CALL == 1); + assert(key_pair.account == account, "Cannot query nullifier key for more than one account per call"); + key_pair + }; key_pair.secret_key } @@ -226,7 +256,7 @@ impl PrivateContext { ) // docs:end:context_consume_l1_to_l2_message { - let nullifier = process_l1_to_l2_message(self.block_header.l1_to_l2_message_tree_root, self.this_address(), self.this_portal_address(), self.chain_id(), self.version(), msg_key, content, secret); + let nullifier = process_l1_to_l2_message(self.historical_header.state.l1_to_l2_message_tree.root, self.this_address(), self.this_portal_address(), self.chain_id(), self.version(), msg_key, content, secret); // Push nullifier (and the "commitment" corresponding to this can be "empty") self.push_new_nullifier(nullifier, 0) @@ -299,9 +329,10 @@ impl PrivateContext { }, args_hash: reader.read(), return_values: reader.read_array([0; RETURN_VALUES_LENGTH]), // +1 - read_requests: reader.read_struct_array(SideEffect::deserialise, [SideEffect::empty(); MAX_READ_REQUESTS_PER_CALL]), - new_commitments: reader.read_struct_array(SideEffect::deserialise, [SideEffect::empty(); MAX_NEW_COMMITMENTS_PER_CALL]), - new_nullifiers: reader.read_struct_array(SideEffectLinkedToNoteHash::deserialise, [SideEffectLinkedToNoteHash::empty(); MAX_NEW_NULLIFIERS_PER_CALL]), + read_requests: reader.read_struct_array(SideEffect::deserialize, [SideEffect::empty(); MAX_READ_REQUESTS_PER_CALL]), + nullifier_key_validation_requests: reader.read_struct_array(NullifierKeyValidationRequest::deserialize, [NullifierKeyValidationRequest::empty(); MAX_NULLIFIER_KEY_VALIDATION_REQUESTS_PER_CALL]), + new_commitments: reader.read_struct_array(SideEffect::deserialize, [SideEffect::empty(); MAX_NEW_COMMITMENTS_PER_CALL]), + new_nullifiers: reader.read_struct_array(SideEffectLinkedToNoteHash::deserialize, [SideEffectLinkedToNoteHash::empty(); MAX_NEW_NULLIFIERS_PER_CALL]), private_call_stack_hashes: reader.read_array([0; MAX_PRIVATE_CALL_STACK_LENGTH_PER_CALL]), public_call_stack_hashes: reader.read_array([0; MAX_PUBLIC_CALL_STACK_LENGTH_PER_CALL]), new_l2_to_l1_msgs: reader.read_array([0; MAX_NEW_L2_TO_L1_MSGS_PER_CALL]), @@ -310,23 +341,14 @@ impl PrivateContext { unencrypted_logs_hash: reader.read_array([0; NUM_FIELDS_PER_SHA256]), encrypted_log_preimages_length: reader.read(), unencrypted_log_preimages_length: reader.read(), - block_header: BlockHeader{ - // Must match order in `private_circuit_public_inputs.hpp` - note_hash_tree_root : reader.read(), - nullifier_tree_root : reader.read(), - contract_tree_root : reader.read(), - l1_to_l2_message_tree_root : reader.read(), - archive_root : reader.read(), - public_data_tree_root: reader.read(), - global_variables_hash: reader.read(), - }, + historical_header: reader.read_struct(Header::deserialize), contract_deployment_data: ContractDeploymentData { - deployer_public_key: GrumpkinPoint { + public_key: GrumpkinPoint { x: reader.read(), y: reader.read() }, - constructor_vk_hash : reader.read(), - function_tree_root : reader.read(), + initialization_hash : reader.read(), + contract_class_id : reader.read(), contract_address_salt : reader.read(), portal_contract_address : EthAddress::from_field(reader.read()), }, @@ -426,7 +448,7 @@ impl PrivateContext { new_l2_to_l1_msgs:[0; MAX_NEW_L2_TO_L1_MSGS_PER_CALL], unencrypted_logs_hash:[0; NUM_FIELDS_PER_SHA256], unencrypted_log_preimages_length: 0, - block_header: BlockHeader::empty(), + historical_header: Header::empty(), prover_address: AztecAddress::zero(), }, is_execution_request: true, @@ -475,7 +497,9 @@ struct PublicContext { unencrypted_logs_hash: BoundedVec, unencrypted_logs_preimages_length: Field, - block_header: BlockHeader, + // Header of a block whose state is used during public execution. Set by sequencer to be a header of a block + // previous to the one in which the tx is included. + historical_header: Header, prover_address: AztecAddress, } @@ -503,7 +527,7 @@ impl PublicContext { unencrypted_logs_hash: BoundedVec::new(0), unencrypted_logs_preimages_length: 0, - block_header: inputs.block_header, + historical_header: inputs.historical_header, prover_address: AztecAddress::zero(), // TODO(https://github.com/AztecProtocol/aztec-packages/issues/1165) @@ -563,7 +587,7 @@ impl PublicContext { new_l2_to_l1_msgs: self.new_l2_to_l1_msgs.storage, unencrypted_logs_hash: unencrypted_logs_hash, unencrypted_log_preimages_length: unencrypted_log_preimages_length, - block_header: self.inputs.block_header, + historical_header: self.inputs.historical_header, prover_address: self.prover_address, }; pub_circuit_pub_inputs @@ -596,7 +620,7 @@ impl PublicContext { // Note this returns self to get around an issue where mutable structs do not maintain mutations unless reassigned pub fn consume_l1_to_l2_message(&mut self, msg_key: Field, content: Field, secret: Field) { let this = (*self).this_address(); - let nullifier = process_l1_to_l2_message(self.block_header.l1_to_l2_message_tree_root, this, self.this_portal_address(), self.chain_id(), self.version(), msg_key, content, secret); + let nullifier = process_l1_to_l2_message(self.historical_header.state.l1_to_l2_message_tree.root, this, self.this_portal_address(), self.chain_id(), self.version(), msg_key, content, secret); // Push nullifier (and the "commitment" corresponding to this can be "empty") self.push_new_nullifier(nullifier, 0) diff --git a/yarn-project/aztec-nr/aztec/src/history/contract_inclusion.nr b/yarn-project/aztec-nr/aztec/src/history/contract_inclusion.nr index 053fb5734b0..38ecf9e4d24 100644 --- a/yarn-project/aztec-nr/aztec/src/history/contract_inclusion.nr +++ b/yarn-project/aztec-nr/aztec/src/history/contract_inclusion.nr @@ -1,6 +1,5 @@ use dep::protocol_types::{ abis::{ - complete_address::CompleteAddress, new_contract_data::NewContractData as ContractLeafPreimage, }, address::{AztecAddress, EthAddress}, @@ -22,40 +21,44 @@ use crate::{ // it is what it expects. The constructor param check is the reason of why we pass in the preimage of contract's // aztec address instead of just the address. pub fn prove_contract_inclusion( - deployer_public_key: GrumpkinPoint, + public_key: GrumpkinPoint, contract_address_salt: Field, - function_tree_root: Field, - constructor_hash: Field, + contract_class_id: Field, + initialization_hash: Field, portal_contract_address: EthAddress, block_number: u32, // The block at which we'll prove that the public value exists context: PrivateContext ) -> AztecAddress { // 1) Get block header from oracle and ensure that the block is included in the archive. - let block_header = context.get_block_header(block_number); + // let block_header = context.get_header.at(block_number); // 2) Compute the contract address - let contract_address = CompleteAddress::compute( - deployer_public_key, + let contract_address = AztecAddress::compute_from_public_key( + public_key, + contract_class_id, contract_address_salt, - function_tree_root, - constructor_hash - ).address; + initialization_hash, + portal_contract_address + ); - // 3) Form the contract tree leaf preimage - let preimage = ContractLeafPreimage { contract_address, portal_contract_address, function_tree_root }; + // TODO(@spalladino): Use initialization and/or deployment nullifier for this proof. + // Consider splitting this into 2 methods, one for initialization and one for public deployment. + // 3) Form the contract tree leaf preimage + // let preimage = ContractLeafPreimage { contract_address, portal_contract_address, contract_class_id }; + // // 4) Get the contract tree leaf by hashing the preimage - let contract_leaf = preimage.hash(); - + // let contract_leaf = preimage.hash(); + // // 5) Get the membership witness of the leaf in the contract tree - let witness = get_contract_membership_witness(block_number, contract_leaf); - + // let witness = get_contract_membership_witness(block_number, contract_leaf); + // // 6) Prove that the leaf is in the contract tree - assert( - block_header.contract_tree_root - == compute_merkle_root(contract_leaf, witness.index, witness.path), "Proving contract inclusion failed" - ); - + // assert( + // block_header.partial.contract_tree.root + // == compute_merkle_root(contract_leaf, witness.index, witness.path), "Proving contract inclusion failed" + // ); + // // --> Now we have traversed the trees all the way up to archive root. contract_address diff --git a/yarn-project/aztec-nr/aztec/src/history/note_inclusion.nr b/yarn-project/aztec-nr/aztec/src/history/note_inclusion.nr index d156618d326..8ef9a2b5804 100644 --- a/yarn-project/aztec-nr/aztec/src/history/note_inclusion.nr +++ b/yarn-project/aztec-nr/aztec/src/history/note_inclusion.nr @@ -3,7 +3,7 @@ use dep::std::merkle::compute_merkle_root; use crate::{ context::PrivateContext, note::{ - utils::compute_unique_siloed_note_hash, + utils::compute_note_hash_for_consumption, note_header::NoteHeader, note_interface::NoteInterface, }, @@ -16,26 +16,25 @@ pub fn prove_note_commitment_inclusion( context: PrivateContext ) { // 1) Get block header from oracle and ensure that the block is included in the archive. - let block_header = context.get_block_header(block_number); + let header = context.get_header_at(block_number); // 2) Get the membership witness of the note in the note hash tree let witness = get_note_hash_membership_witness(block_number, note_commitment); // 3) Prove that the commitment is in the note hash tree assert( - block_header.note_hash_tree_root + header.state.partial.note_hash_tree.root == compute_merkle_root(note_commitment, witness.index, witness.path), "Proving note inclusion failed" ); // --> Now we have traversed the trees all the way up to archive root. } pub fn prove_note_inclusion( - note_interface: NoteInterface, note_with_header: Note, block_number: u32, // The block at which we'll prove that the note exists context: PrivateContext -) { - let note_commitment = compute_unique_siloed_note_hash(note_interface, note_with_header); +) where Note: NoteInterface { + let note_commitment = compute_note_hash_for_consumption(note_with_header); prove_note_commitment_inclusion(note_commitment, block_number, context); } diff --git a/yarn-project/aztec-nr/aztec/src/history/note_validity.nr b/yarn-project/aztec-nr/aztec/src/history/note_validity.nr index 30abac51c74..6742771d705 100644 --- a/yarn-project/aztec-nr/aztec/src/history/note_validity.nr +++ b/yarn-project/aztec-nr/aztec/src/history/note_validity.nr @@ -9,11 +9,10 @@ use crate::{ // A helper function that proves that a note is valid at the given block number pub fn prove_note_validity( - note_interface: NoteInterface, note_with_header: Note, block_number: u32, // The block at which we'll prove that the note exists context: &mut PrivateContext -) { - prove_note_inclusion(note_interface, note_with_header, block_number, *context); - prove_note_not_nullified(note_interface, note_with_header, block_number, context); +) where Note: NoteInterface { + prove_note_inclusion(note_with_header, block_number, *context); + prove_note_not_nullified(note_with_header, block_number, context); } diff --git a/yarn-project/aztec-nr/aztec/src/history/nullifier_inclusion.nr b/yarn-project/aztec-nr/aztec/src/history/nullifier_inclusion.nr index 43976dbd92c..1d61313825e 100644 --- a/yarn-project/aztec-nr/aztec/src/history/nullifier_inclusion.nr +++ b/yarn-project/aztec-nr/aztec/src/history/nullifier_inclusion.nr @@ -11,7 +11,7 @@ pub fn prove_nullifier_inclusion( context: PrivateContext ) { // 1) Get block header from oracle and ensure that the block hash is included in the archive. - let block_header = context.get_block_header(block_number); + let header = context.get_header_at(block_number); // 2) Get the membership witness of the nullifier let witness = get_nullifier_membership_witness(block_number, nullifier); @@ -24,7 +24,7 @@ pub fn prove_nullifier_inclusion( // 5) Prove that the nullifier is in the nullifier tree assert( - block_header.nullifier_tree_root + header.state.partial.nullifier_tree.root == compute_merkle_root(nullifier_leaf, witness.index, witness.path), "Proving nullifier inclusion failed" ); // --> Now we have traversed the trees all the way up to archive root and verified that the nullifier diff --git a/yarn-project/aztec-nr/aztec/src/history/nullifier_non_inclusion.nr b/yarn-project/aztec-nr/aztec/src/history/nullifier_non_inclusion.nr index f4cb297d17a..0c40d4d648b 100644 --- a/yarn-project/aztec-nr/aztec/src/history/nullifier_non_inclusion.nr +++ b/yarn-project/aztec-nr/aztec/src/history/nullifier_non_inclusion.nr @@ -20,7 +20,7 @@ pub fn prove_nullifier_non_inclusion( context: PrivateContext ) { // 1) Get block header from oracle and ensure that the block is included in the archive. - let block_header = context.get_block_header(block_number); + let header = context.get_header_at(block_number); // 2) Get the membership witness of a low nullifier of the nullifier let witness = get_low_nullifier_membership_witness(block_number, nullifier); @@ -30,7 +30,7 @@ pub fn prove_nullifier_non_inclusion( // 3.a) Compute the low nullifier leaf and prove that it is in the nullifier tree let low_nullifier_leaf = witness.leaf_preimage.hash(); assert( - block_header.nullifier_tree_root + header.state.partial.nullifier_tree.root == compute_merkle_root(low_nullifier_leaf, witness.index, witness.path), "Proving nullifier non-inclusion failed: Could not prove low nullifier inclusion" ); @@ -50,12 +50,11 @@ pub fn prove_nullifier_non_inclusion( } pub fn prove_note_not_nullified( - note_interface: NoteInterface, note_with_header: Note, block_number: u32, // The block at which we'll prove that the note was not nullified context: &mut PrivateContext -) { - let nullifier = compute_siloed_nullifier(note_interface, note_with_header, context); +) where Note: NoteInterface { + let nullifier = compute_siloed_nullifier(note_with_header, context); prove_nullifier_non_inclusion(nullifier, block_number, *context); } diff --git a/yarn-project/aztec-nr/aztec/src/history/public_value_inclusion.nr b/yarn-project/aztec-nr/aztec/src/history/public_value_inclusion.nr index 45f2c5450f6..ad14fb77cb3 100644 --- a/yarn-project/aztec-nr/aztec/src/history/public_value_inclusion.nr +++ b/yarn-project/aztec-nr/aztec/src/history/public_value_inclusion.nr @@ -22,7 +22,7 @@ pub fn prove_public_value_inclusion( context: PrivateContext ) { // 1) Get block header from oracle and ensure that the block hash is included in the archive. - let block_header = context.get_block_header(block_number); + let header = context.get_header_at(block_number); // 2) Compute the leaf slot by siloing the storage slot with our own address let public_value_leaf_slot = pedersen_hash( @@ -35,17 +35,25 @@ pub fn prove_public_value_inclusion( // 4) Check that the witness matches the corresponding public_value let preimage = witness.leaf_preimage; - if preimage.slot == public_value_leaf_slot { - assert_eq(preimage.value, value, "Public value does not match value in witness"); + + // Here we have two cases. Code based on same checks in `validate_public_data_reads` in `base_rollup_inputs` + // 1. The value is the same as the one in the witness + // 2. The value was never initialized and is zero + let is_less_than_slot = full_field_less_than(preimage.slot, public_value_leaf_slot); + let is_next_greater_than = full_field_less_than(public_value_leaf_slot, preimage.next_slot); + let is_max = ((preimage.next_index == 0) & (preimage.next_slot == 0)); + let is_in_range = is_less_than_slot & (is_next_greater_than | is_max); + + if is_in_range { + assert_eq(value, 0, "Non-existant public data leaf value is non-zero"); } else { - assert_eq(value, 0, "Got non-zero public value for non-existing slot"); - assert(full_field_less_than(preimage.slot, public_value_leaf_slot), "Invalid witness range"); - assert(full_field_less_than(public_value_leaf_slot, preimage.next_slot), "Invalid witness range"); + assert_eq(preimage.slot, public_value_leaf_slot, "Public data slot don't match witness"); + assert_eq(preimage.value, value, "Public value does not match the witness"); } // 5) Prove that the leaf we validated is in the public data tree assert( - block_header.public_data_tree_root + header.state.partial.public_data_tree.root == compute_merkle_root(preimage.hash(), witness.index, witness.path), "Proving public value inclusion failed" ); // --> Now we have traversed the trees all the way up to archive root and that way verified that a specific diff --git a/yarn-project/aztec-nr/aztec/src/key/nullifier_key.nr b/yarn-project/aztec-nr/aztec/src/key/nullifier_key.nr index 3f07dba4b2c..0b93dc87b77 100644 --- a/yarn-project/aztec-nr/aztec/src/key/nullifier_key.nr +++ b/yarn-project/aztec-nr/aztec/src/key/nullifier_key.nr @@ -1,24 +1,8 @@ -use crate::oracle::get_public_key::get_public_key; use dep::protocol_types::{ address::AztecAddress, grumpkin_point::GrumpkinPoint, }; -use dep::std::{ - grumpkin_scalar::GrumpkinScalar, - grumpkin_scalar_mul::grumpkin_fixed_base, -}; -pub fn validate_nullifier_key_against_address( - address: AztecAddress, - nullifier_public_key: GrumpkinPoint, - nullifier_secret_key: GrumpkinScalar -) { +pub fn validate_nullifier_key_against_address(_address: AztecAddress, _nullifier_public_key: GrumpkinPoint) { // TODO: Nullifier public key should be part of the address. - // Validation of the secret key should happen in the kernel circuit. - let owner_public_key = get_public_key(address); - assert(owner_public_key.x == nullifier_public_key.x); - assert(owner_public_key.y == nullifier_public_key.y); - let computed_public_key = grumpkin_fixed_base(nullifier_secret_key); - assert(owner_public_key.x == computed_public_key[0]); - assert(owner_public_key.y == computed_public_key[1]); } diff --git a/yarn-project/aztec-nr/aztec/src/lib.nr b/yarn-project/aztec-nr/aztec/src/lib.nr index 9bf7bf42c8f..7a8531601c4 100644 --- a/yarn-project/aztec-nr/aztec/src/lib.nr +++ b/yarn-project/aztec-nr/aztec/src/lib.nr @@ -8,6 +8,5 @@ mod messaging; mod note; mod oracle; mod state_vars; -mod types; mod utils; use dep::protocol_types; diff --git a/yarn-project/aztec-nr/aztec/src/messaging.nr b/yarn-project/aztec-nr/aztec/src/messaging.nr index ef659f198ec..78dc5d33eec 100644 --- a/yarn-project/aztec-nr/aztec/src/messaging.nr +++ b/yarn-project/aztec-nr/aztec/src/messaging.nr @@ -6,6 +6,8 @@ use l1_to_l2_message_getter_data::make_l1_to_l2_message_getter_data; use crate::abi::PublicContextInputs; use crate::oracle::get_l1_to_l2_message::get_l1_to_l2_message_call; +use dep::std::merkle::compute_merkle_root; + use dep::protocol_types::address::{ AztecAddress, EthAddress, @@ -25,23 +27,32 @@ pub fn process_l1_to_l2_message( let returned_message = get_l1_to_l2_message_call(msg_key); let l1_to_l2_message_data = make_l1_to_l2_message_getter_data(returned_message, 0, secret); - // Check tree roots against the inputs - assert(l1_to_l2_message_data.root == l1_to_l2_root); + // Check that the returned message is actually the message we looked up + let msg_hash = l1_to_l2_message_data.message.hash(); + assert(msg_hash == msg_key, "Message not matching requested key"); + + // Check that the message is in the tree + let root = compute_merkle_root( + msg_hash, + l1_to_l2_message_data.leaf_index, + l1_to_l2_message_data.sibling_path + ); + assert(root == l1_to_l2_root, "Message not in state"); // Validate this is the target contract - assert(l1_to_l2_message_data.message.recipient.eq(storage_contract_address)); + assert(l1_to_l2_message_data.message.recipient.eq(storage_contract_address), "Invalid recipient"); // Validate the sender is the portal contract - assert(l1_to_l2_message_data.message.sender.eq(portal_contract_address)); + assert(l1_to_l2_message_data.message.sender.eq(portal_contract_address), "Invalid sender"); // Validate the chain id is correct - assert(l1_to_l2_message_data.message.chainId == chain_id); + assert(l1_to_l2_message_data.message.chainId == chain_id, "Invalid Chainid"); // Validate the version is correct - assert(l1_to_l2_message_data.message.version == version); + assert(l1_to_l2_message_data.message.version == version, "Invalid Version"); // Validate the message hash is correct - assert(l1_to_l2_message_data.message.content == content); + assert(l1_to_l2_message_data.message.content == content, "Invalid Content"); // Validate the message secret is correct l1_to_l2_message_data.message.validate_message_secret(); diff --git a/yarn-project/aztec-nr/aztec/src/messaging/l1_to_l2_message.nr b/yarn-project/aztec-nr/aztec/src/messaging/l1_to_l2_message.nr index 39aeba68742..a4dfcfa4e52 100644 --- a/yarn-project/aztec-nr/aztec/src/messaging/l1_to_l2_message.nr +++ b/yarn-project/aztec-nr/aztec/src/messaging/l1_to_l2_message.nr @@ -49,10 +49,10 @@ impl L1ToL2Message { pub fn validate_message_secret(self: Self) { let recomputed_hash = pedersen_hash([self.secret], GENERATOR_INDEX__L1_TO_L2_MESSAGE_SECRET); - assert(self.secret_hash == recomputed_hash); + assert(self.secret_hash == recomputed_hash, "Invalid message secret"); } - fn message_hash(self: Self) -> Field { + fn hash(self: Self) -> Field { let mut hash_bytes: [u8; 256] = [0; 256]; let sender_bytes = self.sender.to_field().to_be_bytes(32); let chainId_bytes = self.chainId.to_be_bytes(32); @@ -81,7 +81,7 @@ impl L1ToL2Message { // The nullifier of a l1 to l2 message is the hash of the message salted with the secret and tree index // docs:start:l1_to_l2_message_compute_nullifier pub fn compute_nullifier(self: Self) -> Field { - let message_hash = self.message_hash(); + let message_hash = self.hash(); pedersen_hash([message_hash, self.secret, self.tree_index], GENERATOR_INDEX__NULLIFIER) } // docs:end:l1_to_l2_message_compute_nullifier diff --git a/yarn-project/aztec-nr/aztec/src/messaging/l1_to_l2_message_getter_data.nr b/yarn-project/aztec-nr/aztec/src/messaging/l1_to_l2_message_getter_data.nr index 882103f7fdf..34f21c60395 100644 --- a/yarn-project/aztec-nr/aztec/src/messaging/l1_to_l2_message_getter_data.nr +++ b/yarn-project/aztec-nr/aztec/src/messaging/l1_to_l2_message_getter_data.nr @@ -8,12 +8,11 @@ use crate::utils::arr_copy_slice; struct L1ToL2MessageGetterData { message: L1ToL2Message, sibling_path: [Field; L1_TO_L2_MSG_TREE_HEIGHT], - leaf_index: Field, - root: Field, + leaf_index: Field } pub fn l1_to_l2_message_getter_len() -> Field { - L1_TO_L2_MESSAGE_LENGTH + 1 + L1_TO_L2_MSG_TREE_HEIGHT + 1 + L1_TO_L2_MESSAGE_LENGTH + 1 + L1_TO_L2_MSG_TREE_HEIGHT } pub fn make_l1_to_l2_message_getter_data( @@ -32,7 +31,6 @@ pub fn make_l1_to_l2_message_getter_data( fields, [0; L1_TO_L2_MSG_TREE_HEIGHT], L1_TO_L2_MESSAGE_LENGTH + 1 - ), - root: fields[start + L1_TO_L2_MESSAGE_LENGTH + L1_TO_L2_MSG_TREE_HEIGHT + 1] + ) } } diff --git a/yarn-project/aztec-nr/aztec/src/note.nr b/yarn-project/aztec-nr/aztec/src/note.nr index 5df51e71dd4..b457a126b2d 100644 --- a/yarn-project/aztec-nr/aztec/src/note.nr +++ b/yarn-project/aztec-nr/aztec/src/note.nr @@ -1,7 +1,6 @@ mod lifecycle; mod note_getter; mod note_getter_options; -mod note_hash; mod note_header; mod note_interface; mod note_viewer_options; diff --git a/yarn-project/aztec-nr/aztec/src/note/lifecycle.nr b/yarn-project/aztec-nr/aztec/src/note/lifecycle.nr index ec741e6dbae..b42e9f86acc 100644 --- a/yarn-project/aztec-nr/aztec/src/note/lifecycle.nr +++ b/yarn-project/aztec-nr/aztec/src/note/lifecycle.nr @@ -6,75 +6,64 @@ use crate::context::{ use crate::note::{ note_header::NoteHeader, note_interface::NoteInterface, - utils::compute_inner_note_hash, + utils::{compute_note_hash_for_insertion, compute_note_hash_for_consumption}, }; use crate::oracle::notes::{notify_created_note, notify_nullified_note}; +use dep::protocol_types::traits::{Serialize, Deserialize}; pub fn create_note( context: &mut PrivateContext, storage_slot: Field, note: &mut Note, - note_interface: NoteInterface, broadcast: bool -) { +) where Note: NoteInterface + Serialize + Deserialize { let contract_address = (*context).this_address(); let header = NoteHeader { contract_address, storage_slot, nonce: 0, is_transient: true }; - let set_header = note_interface.set_header; - set_header(note, header); - let inner_note_hash = compute_inner_note_hash(note_interface, *note); + // TODO: change this to note.setHeader(header) once https://github.com/noir-lang/noir/issues/4095 is fixed + Note::set_header(note, header); + // As `is_transient` is true, this will compute the inner note hsah + let inner_note_hash = compute_note_hash_for_insertion(*note); - let serialize = note_interface.serialize; - let serialized_note = serialize(*note); + // TODO: Strong typing required because of https://github.com/noir-lang/noir/issues/4088 + let serialized_note: [Field; N] = Note::serialize(*note); assert(notify_created_note(storage_slot, serialized_note, inner_note_hash) == 0); context.push_new_note_hash(inner_note_hash); if broadcast { - let broadcast = note_interface.broadcast; - broadcast(context, storage_slot, *note); + Note::broadcast(*note, context, storage_slot); } } -pub fn create_note_hash_from_public( - context: &mut PublicContext, - storage_slot: Field, - note: &mut Note, - note_interface: NoteInterface -) { +pub fn create_note_hash_from_public(context: &mut PublicContext, storage_slot: Field, note: &mut Note) where Note: NoteInterface { let contract_address = (*context).this_address(); let header = NoteHeader { contract_address, storage_slot, nonce: 0, is_transient: true }; - let set_header = note_interface.set_header; - set_header(note, header); - let inner_note_hash = compute_inner_note_hash(note_interface, *note); + // TODO: change this to note.setHeader(header) once https://github.com/noir-lang/noir/issues/4095 is fixed + Note::set_header(note, header); + let inner_note_hash = compute_note_hash_for_insertion(*note); context.push_new_note_hash(inner_note_hash); } -pub fn destroy_note( - context: &mut PrivateContext, - note: Note, - note_interface: NoteInterface -) { +pub fn destroy_note(context: &mut PrivateContext, note: Note) where Note: NoteInterface { let mut nullifier = 0; - let mut nullified_commitment: Field = 0; - let compute_nullifier = note_interface.compute_nullifier; - nullifier = compute_nullifier(note, context); + let mut consumed_note_hash: Field = 0; + nullifier = note.compute_nullifier(context); - // We also need the note commitment corresponding to the "nullifier" - let get_header = note_interface.get_header; - let header = get_header(note); - // `nullified_commitment` is used to inform the kernel which pending commitment + // We also need the note hash corresponding to the "nullifier" + let header = note.get_header(); + // `consumed_note_hash` is used to inform the kernel which pending note hash // the nullifier corresponds to so they can be matched and both squashed/deleted. // nonzero nonce implies "persistable" nullifier (nullifies a persistent/in-tree - // commitment) in which case `nullified_commitment` is not used since the kernel + // note hash) in which case `consumed_note_hash` is not used since the kernel // just siloes and forwards the nullifier to its output. if (header.is_transient) { - // TODO(1718): Can we reuse the note commitment computed in `compute_nullifier`? - nullified_commitment = compute_inner_note_hash(note_interface, note); + // TODO(1718): Can we reuse the note hash computed in `compute_nullifier`? + consumed_note_hash = compute_note_hash_for_consumption(note); } - assert(notify_nullified_note(nullifier, nullified_commitment) == 0); + assert(notify_nullified_note(nullifier, consumed_note_hash) == 0); - context.push_new_nullifier(nullifier, nullified_commitment) + context.push_new_nullifier(nullifier, consumed_note_hash) } diff --git a/yarn-project/aztec-nr/aztec/src/note/note_getter.nr b/yarn-project/aztec-nr/aztec/src/note/note_getter.nr index 15b98f89f30..f34c89cfb8e 100644 --- a/yarn-project/aztec-nr/aztec/src/note/note_getter.nr +++ b/yarn-project/aztec-nr/aztec/src/note/note_getter.nr @@ -1,29 +1,29 @@ use dep::std::option::Option; -use dep::protocol_types::constants::{ - MAX_READ_REQUESTS_PER_CALL, - GET_NOTE_ORACLE_RETURN_LENGTH, - GET_NOTES_ORACLE_RETURN_LENGTH, - MAX_NOTES_PER_PAGE, - VIEW_NOTE_ORACLE_RETURN_LENGTH, +use dep::protocol_types::{ + constants::{ + MAX_READ_REQUESTS_PER_CALL, + GET_NOTE_ORACLE_RETURN_LENGTH, + GET_NOTES_ORACLE_RETURN_LENGTH, + MAX_NOTES_PER_PAGE, + VIEW_NOTE_ORACLE_RETURN_LENGTH, + }, + traits::{Deserialize, Serialize} }; use crate::context::PrivateContext; use crate::note::{ - note_getter_options::{NoteGetterOptions, Select, Sort, SortOrder}, + note_getter_options::{NoteGetterOptions, Select, Sort, SortOrder, Comparator, NoteStatus}, note_interface::NoteInterface, note_viewer_options::NoteViewerOptions, - utils::compute_note_hash_for_read_or_nullify, + utils::compute_note_hash_for_consumption, }; use crate::oracle; -use crate::types::vec::BoundedVec; fn check_note_header( context: PrivateContext, storage_slot: Field, - note_interface: NoteInterface, note: Note -) { - let get_header = note_interface.get_header; - let header = get_header(note); +) where Note: NoteInterface { + let header = note.get_header(); let contract_address = context.this_address(); assert(header.contract_address.eq(contract_address)); assert(header.storage_slot == storage_slot); @@ -32,7 +32,24 @@ fn check_note_header( fn check_note_fields(fields: [Field; N], selects: BoundedVec, N>) { for i in 0..selects.len { let select = selects.get_unchecked(i).unwrap_unchecked(); - assert(fields[select.field_index] == select.value, "Mismatch return note field."); + + // Values are computed ahead of time because circuits evaluate all branches + let isEqual = fields[select.field_index] == select.value; + let isLt = fields[select.field_index].lt(select.value); + + if (select.comparator == Comparator.EQ) { + assert(isEqual, "Mismatch return note field."); + } else if (select.comparator == Comparator.NEQ) { + assert(!isEqual, "Mismatch return note field."); + } else if (select.comparator == Comparator.LT) { + assert(isLt, "Mismatch return note field."); + } else if (select.comparator == Comparator.LTE) { + assert(isLt | isEqual, "Mismatch return note field."); + } else if (select.comparator == Comparator.GT) { + assert(!isLt & !isEqual, "Mismatch return note field."); + } else if (select.comparator == Comparator.GTE) { + assert(!isLt, "Mismatch return note field."); + } } } @@ -55,14 +72,13 @@ fn check_notes_order( pub fn get_note( context: &mut PrivateContext, - storage_slot: Field, - note_interface: NoteInterface -) -> Note { - let note = get_note_internal(storage_slot, note_interface); + storage_slot: Field +) -> Note where Note: NoteInterface + Deserialize { + let note = get_note_internal(storage_slot); - check_note_header(*context, storage_slot, note_interface, note); + check_note_header(*context, storage_slot, note); - let note_hash_for_read_request = compute_note_hash_for_read_or_nullify(note_interface, note); + let note_hash_for_read_request = compute_note_hash_for_consumption(note); context.push_read_request(note_hash_for_read_request); note @@ -71,26 +87,24 @@ pub fn get_note( pub fn get_notes( context: &mut PrivateContext, storage_slot: Field, - note_interface: NoteInterface, options: NoteGetterOptions -) -> [Option; MAX_READ_REQUESTS_PER_CALL] { - let opt_notes = get_notes_internal(storage_slot, note_interface, options); +) -> [Option; MAX_READ_REQUESTS_PER_CALL] where Note: NoteInterface + Deserialize + Serialize { + let opt_notes = get_notes_internal(storage_slot, options); let mut num_notes = 0; let mut prev_fields = [0; N]; for i in 0..opt_notes.len() { let opt_note = opt_notes[i]; if opt_note.is_some() { let note = opt_note.unwrap_unchecked(); - let serialize = note_interface.serialize; - let fields = serialize(note); - check_note_header(*context, storage_slot, note_interface, note); + let fields = note.serialize(); + check_note_header(*context, storage_slot, note); check_note_fields(fields, options.selects); if i != 0 { check_notes_order(prev_fields, fields, options.sorts); } prev_fields = fields; - let note_hash_for_read_request = compute_note_hash_for_read_or_nullify(note_interface, note); + let note_hash_for_read_request = compute_note_hash_for_consumption(note); // TODO(https://github.com/AztecProtocol/aztec-packages/issues/1410): test to ensure // failure if malicious oracle injects 0 nonce here for a "pre-existing" note. context.push_read_request(note_hash_for_read_request); @@ -104,44 +118,49 @@ pub fn get_notes( opt_notes } -unconstrained fn get_note_internal(storage_slot: Field, note_interface: NoteInterface) -> Note { +unconstrained fn get_note_internal(storage_slot: Field) -> Note where Note: NoteInterface + Deserialize { let placeholder_note = [Option::none()]; let placeholder_fields = [0; GET_NOTE_ORACLE_RETURN_LENGTH]; + let placeholder_note_length = [0; N]; oracle::notes::get_notes( storage_slot, - note_interface, 0, [], [], [], [], + [], 1, // limit 0, // offset + NoteStatus.ACTIVE, placeholder_note, - placeholder_fields + placeholder_fields, + placeholder_note_length )[0].unwrap() // Notice: we don't allow dummies to be returned from get_note (singular). } unconstrained fn get_notes_internal( storage_slot: Field, - note_interface: NoteInterface, options: NoteGetterOptions -) -> [Option; MAX_READ_REQUESTS_PER_CALL] { - let (num_selects, select_by, select_values, sort_by, sort_order) = flatten_options(options.selects, options.sorts); +) -> [Option; MAX_READ_REQUESTS_PER_CALL] where Note: NoteInterface + Deserialize { + let (num_selects, select_by, select_values, select_comparators, sort_by, sort_order) = flatten_options(options.selects, options.sorts); let placeholder_opt_notes = [Option::none(); MAX_READ_REQUESTS_PER_CALL]; let placeholder_fields = [0; GET_NOTES_ORACLE_RETURN_LENGTH]; + let placeholder_note_length = [0; N]; let opt_notes = oracle::notes::get_notes( storage_slot, - note_interface, num_selects, select_by, select_values, + select_comparators, sort_by, sort_order, options.limit, options.offset, + options.status, placeholder_opt_notes, - placeholder_fields + placeholder_fields, + placeholder_note_length ); let filter = options.filter; @@ -151,39 +170,44 @@ unconstrained fn get_notes_internal( unconstrained pub fn view_notes( storage_slot: Field, - note_interface: NoteInterface, options: NoteViewerOptions -) -> [Option; MAX_NOTES_PER_PAGE] { - let (num_selects, select_by, select_values, sort_by, sort_order) = flatten_options(options.selects, options.sorts); +) -> [Option; MAX_NOTES_PER_PAGE] where Note: NoteInterface + Deserialize { + let (num_selects, select_by, select_values, select_comparators, sort_by, sort_order) = flatten_options(options.selects, options.sorts); let placeholder_opt_notes = [Option::none(); MAX_NOTES_PER_PAGE]; let placeholder_fields = [0; VIEW_NOTE_ORACLE_RETURN_LENGTH]; + let placeholder_note_length = [0; N]; oracle::notes::get_notes( storage_slot, - note_interface, num_selects, select_by, select_values, + select_comparators, sort_by, sort_order, options.limit, options.offset, + options.status, placeholder_opt_notes, - placeholder_fields + placeholder_fields, + placeholder_note_length ) } unconstrained fn flatten_options( selects: BoundedVec, N>, sorts: BoundedVec, N> -) -> (u8, [u8; N], [Field; N], [u8; N], [u2; N]) { +) -> (u8, [u8; N], [Field; N], [u3; N], [u8; N], [u2; N]) { let mut num_selects = 0; let mut select_by = [0; N]; let mut select_values = [0; N]; + let mut select_comparators = [0; N]; + for i in 0..selects.len { let select = selects.get(i); if select.is_some() { select_by[num_selects] = select.unwrap_unchecked().field_index; select_values[num_selects] = select.unwrap_unchecked().value; + select_comparators[num_selects] = select.unwrap_unchecked().comparator; num_selects += 1; }; } @@ -198,5 +222,5 @@ unconstrained fn flatten_options( }; } - (num_selects, select_by, select_values, sort_by, sort_order) + (num_selects, select_by, select_values, select_comparators, sort_by, sort_order) } diff --git a/yarn-project/aztec-nr/aztec/src/note/note_getter_options.nr b/yarn-project/aztec-nr/aztec/src/note/note_getter_options.nr index 8ceca5eb03e..bec2aa7f001 100644 --- a/yarn-project/aztec-nr/aztec/src/note/note_getter_options.nr +++ b/yarn-project/aztec-nr/aztec/src/note/note_getter_options.nr @@ -1,15 +1,37 @@ use dep::std::option::Option; -use crate::types::vec::BoundedVec; -use dep::protocol_types::constants::MAX_READ_REQUESTS_PER_CALL; +use dep::protocol_types::{ + constants::MAX_READ_REQUESTS_PER_CALL, + traits::Deserialize, +}; +use crate::note::note_interface::NoteInterface; + +struct ComparatorEnum { + EQ: u3, + NEQ: u3, + LT: u3, + LTE: u3, + GT: u3, + GTE: u3, +} + +global Comparator = ComparatorEnum { + EQ: 1, + NEQ: 2, + LT: 3, + LTE: 4, + GT: 5, + GTE: 6, +}; struct Select { field_index: u8, value: Field, + comparator: u3, } impl Select { - pub fn new(field_index: u8, value: Field) -> Self { - Select { field_index, value } + pub fn new(field_index: u8, value: Field, comparator: u3) -> Self { + Select { field_index, value, comparator } } } @@ -34,6 +56,17 @@ impl Sort { } } +struct NoteStatusEnum { + ACTIVE: u2, + ACTIVE_OR_NULLIFIED: u2, +} + +global NoteStatus = NoteStatusEnum { + ACTIVE: 1, + ACTIVE_OR_NULLIFIED: 2, + // TODO 4217: add 'NULLIFIED' +}; + fn return_all_notes( notes: [Option; MAX_READ_REQUESTS_PER_CALL], _p: Field @@ -49,6 +82,7 @@ struct NoteGetterOptions { offset: u32, filter: fn ([Option; MAX_READ_REQUESTS_PER_CALL], FILTER_ARGS) -> [Option; MAX_READ_REQUESTS_PER_CALL], filter_args: FILTER_ARGS, + status: u2, } // docs:end:NoteGetterOptions @@ -58,7 +92,7 @@ struct NoteGetterOptions { // And finally, a custom filter to refine the outcome further. impl NoteGetterOptions { // This function initializes a NoteGetterOptions that simply returns the maximum number of notes allowed in a call. - pub fn new() -> NoteGetterOptions { + pub fn new() -> NoteGetterOptions where Note: NoteInterface + Deserialize { NoteGetterOptions { selects: BoundedVec::new(Option::none()), sorts: BoundedVec::new(Option::none()), @@ -66,6 +100,7 @@ impl NoteGetterOptions { offset: 0, filter: return_all_notes, filter_args: 0, + status: NoteStatus.ACTIVE, } } @@ -74,7 +109,7 @@ impl NoteGetterOptions { pub fn with_filter( filter: fn ([Option; MAX_READ_REQUESTS_PER_CALL], FILTER_ARGS) -> [Option; MAX_READ_REQUESTS_PER_CALL], filter_args: FILTER_ARGS, - ) -> Self { + ) -> Self where Note: NoteInterface + Deserialize { NoteGetterOptions { selects: BoundedVec::new(Option::none()), sorts: BoundedVec::new(Option::none()), @@ -82,13 +117,16 @@ impl NoteGetterOptions { offset: 0, filter, filter_args, + status: NoteStatus.ACTIVE, } } // This method adds a `Select` criterion to the options. - // It takes a field_index indicating which field to select and a value representing the specific value to match in that field. - pub fn select(&mut self, field_index: u8, value: Field) -> Self { - self.selects.push(Option::some(Select::new(field_index, value))); + // It takes a field_index indicating which field to select, + // a value representing the specific value to match in that field, and + // a comparator (For possible values of comparators, please see the Comparator enum above) + pub fn select(&mut self, field_index: u8, value: Field, comparator: Option) -> Self { + self.selects.push(Option::some(Select::new(field_index, value, comparator.unwrap_or(Comparator.EQ)))); *self } @@ -99,16 +137,22 @@ impl NoteGetterOptions { *self } - // This method lets you set a limit for the maximum number of notes to be retrieved in a single query result. + // This method lets you set a limit for the maximum number of notes to be retrieved in a single query result. pub fn set_limit(&mut self, limit: u32) -> Self { assert(limit <= MAX_READ_REQUESTS_PER_CALL as u32); self.limit = limit; *self } - // This method sets the offset value, which determines where to start retrieving notes in the query results. + // This method sets the offset value, which determines where to start retrieving notes in the query results. pub fn set_offset(&mut self, offset: u32) -> Self { self.offset = offset; *self } + + // This method sets the status value, which determines whether to retrieve active or nullified notes. + pub fn set_status(&mut self, status: u2) -> Self { + self.status = status; + *self + } } diff --git a/yarn-project/aztec-nr/aztec/src/note/note_hash.nr b/yarn-project/aztec-nr/aztec/src/note/note_hash.nr deleted file mode 100644 index 8f0abd7d3db..00000000000 --- a/yarn-project/aztec-nr/aztec/src/note/note_hash.nr +++ /dev/null @@ -1,23 +0,0 @@ -use dep::protocol_types::{ - address::AztecAddress, - constants::{ - GENERATOR_INDEX__UNIQUE_COMMITMENT, - GENERATOR_INDEX__SILOED_COMMITMENT, - }, - hash::pedersen_hash, -}; - -pub fn compute_inner_hash(storage_slot: Field, note_hash: Field) -> Field { - // TODO(#1205) Do we need a generator index here? - pedersen_hash([storage_slot, note_hash], 0) -} - -pub fn compute_siloed_hash(contract_address: AztecAddress, inner_note_hash: Field) -> Field { - let inputs = [contract_address.to_field(), inner_note_hash]; - pedersen_hash(inputs, GENERATOR_INDEX__SILOED_COMMITMENT) -} - -pub fn compute_unique_hash(nonce: Field, siloed_note_hash: Field) -> Field { - let inputs = [nonce, siloed_note_hash]; - pedersen_hash(inputs, GENERATOR_INDEX__UNIQUE_COMMITMENT) -} diff --git a/yarn-project/aztec-nr/aztec/src/note/note_interface.nr b/yarn-project/aztec-nr/aztec/src/note/note_interface.nr index 614c20d2777..63313e17bd5 100644 --- a/yarn-project/aztec-nr/aztec/src/note/note_interface.nr +++ b/yarn-project/aztec-nr/aztec/src/note/note_interface.nr @@ -1,22 +1,19 @@ use crate::context::PrivateContext; use crate::note::note_header::NoteHeader; -// docs:start:NoteInterface -struct NoteInterface { - deserialize: fn ([Field; N]) -> Note, +// docs:start:note_interface +trait NoteInterface { + fn compute_note_content_hash(self) -> Field; - serialize: fn (Note) -> [Field; N], + fn get_header(self) -> NoteHeader; - compute_note_hash: fn (Note) -> Field, + fn set_header(&mut self, header: NoteHeader) -> (); - compute_nullifier: fn (Note, &mut PrivateContext) -> Field, + fn compute_nullifier(self, context: &mut PrivateContext) -> Field; - compute_nullifier_without_context: fn (Note) -> Field, + fn compute_nullifier_without_context(self) -> Field; - get_header: fn (Note) -> NoteHeader, - - set_header: fn (&mut Note, NoteHeader) -> (), - - broadcast: fn (&mut PrivateContext, Field, Note) -> (), + fn broadcast(self, context: &mut PrivateContext, slot: Field) -> (); } -// docs:end:NoteInterface \ No newline at end of file +// docs:end:note_interface + diff --git a/yarn-project/aztec-nr/aztec/src/note/note_viewer_options.nr b/yarn-project/aztec-nr/aztec/src/note/note_viewer_options.nr index 15d445d2c02..a935f63f791 100644 --- a/yarn-project/aztec-nr/aztec/src/note/note_viewer_options.nr +++ b/yarn-project/aztec-nr/aztec/src/note/note_viewer_options.nr @@ -1,7 +1,10 @@ use dep::std::option::Option; -use dep::protocol_types::constants::MAX_NOTES_PER_PAGE; -use crate::note::note_getter_options::{Select, Sort}; -use crate::types::vec::BoundedVec; +use crate::note::note_getter_options::{Select, Sort, Comparator, NoteStatus}; +use dep::protocol_types::{ + constants::MAX_NOTES_PER_PAGE, + traits::Deserialize, +}; +use crate::note::note_interface::NoteInterface; // docs:start:NoteViewerOptions struct NoteViewerOptions { @@ -9,21 +12,27 @@ struct NoteViewerOptions { sorts: BoundedVec, N>, limit: u32, offset: u32, + status: u2, } // docs:end:NoteViewerOptions impl NoteViewerOptions { - pub fn new() -> NoteViewerOptions { + pub fn new() -> NoteViewerOptions where Note: NoteInterface + Deserialize { NoteViewerOptions { selects: BoundedVec::new(Option::none()), sorts: BoundedVec::new(Option::none()), limit: MAX_NOTES_PER_PAGE as u32, offset: 0, + status: NoteStatus.ACTIVE, } } - pub fn select(&mut self, field_index: u8, value: Field) -> Self { - self.selects.push(Option::some(Select::new(field_index, value))); + // This method adds a `Select` criterion to the options. + // It takes a field_index indicating which field to select, + // a value representing the specific value to match in that field, and + // a comparator (For possible values of comparators, please see the Comparator enum from note_getter_options) + pub fn select(&mut self, field_index: u8, value: Field, comparator: Option) -> Self { + self.selects.push(Option::some(Select::new(field_index, value, comparator.unwrap_or(Comparator.EQ)))); *self } @@ -42,4 +51,10 @@ impl NoteViewerOptions { self.offset = offset; *self } + + // This method sets the status value, which determines whether to retrieve active or nullified notes. + pub fn set_status(&mut self, status: u2) -> Self { + self.status = status; + *self + } } diff --git a/yarn-project/aztec-nr/aztec/src/note/utils.nr b/yarn-project/aztec-nr/aztec/src/note/utils.nr index 5d88d904a77..70591907cac 100644 --- a/yarn-project/aztec-nr/aztec/src/note/utils.nr +++ b/yarn-project/aztec-nr/aztec/src/note/utils.nr @@ -1,100 +1,110 @@ -use dep::protocol_types::{ - constants::GENERATOR_INDEX__OUTER_NULLIFIER, - hash::pedersen_hash, -}; use crate::{ context::PrivateContext, note::{ - note_hash::{compute_inner_hash, compute_siloed_hash, compute_unique_hash}, note_header::NoteHeader, note_interface::NoteInterface, }, utils::arr_copy_slice, }; -pub fn compute_inner_note_hash(note_interface: NoteInterface, note: Note) -> Field { - let get_header = note_interface.get_header; - let header = get_header(note); +use dep::protocol_types::{ + address::AztecAddress, + constants::{ + GENERATOR_INDEX__OUTER_NULLIFIER, + GENERATOR_INDEX__UNIQUE_COMMITMENT, + GENERATOR_INDEX__SILOED_COMMITMENT, + }, + hash::pedersen_hash, + traits::{Deserialize, Serialize}, +}; + +fn compute_siloed_hash(contract_address: AztecAddress, inner_note_hash: Field) -> Field { + let inputs = [contract_address.to_field(), inner_note_hash]; + pedersen_hash(inputs, GENERATOR_INDEX__SILOED_COMMITMENT) +} + +fn compute_unique_hash(nonce: Field, siloed_note_hash: Field) -> Field { + let inputs = [nonce, siloed_note_hash]; + pedersen_hash(inputs, GENERATOR_INDEX__UNIQUE_COMMITMENT) +} - let compute_note_hash = note_interface.compute_note_hash; - let note_hash = compute_note_hash(note); +fn compute_inner_note_hash(note: Note) -> Field where Note: NoteInterface { + let header = note.get_header(); + let note_hash = note.compute_note_content_hash(); - compute_inner_hash(header.storage_slot, note_hash) + // TODO(#1205) Do we need a generator index here? + pedersen_hash([header.storage_slot, note_hash], 0) } -pub fn compute_siloed_note_hash(note_interface: NoteInterface, note_with_header: Note) -> Field { - let get_header = note_interface.get_header; - let header = get_header(note_with_header); +fn compute_siloed_note_hash(note_with_header: Note) -> Field where Note: NoteInterface { + let header = note_with_header.get_header(); - let inner_note_hash = compute_inner_note_hash(note_interface, note_with_header); + let inner_note_hash = compute_inner_note_hash(note_with_header); compute_siloed_hash(header.contract_address, inner_note_hash) } -pub fn compute_unique_siloed_note_hash(note_interface: NoteInterface, note_with_header: Note) -> Field { - let get_header = note_interface.get_header; - let header = get_header(note_with_header); +fn compute_unique_siloed_note_hash(note_with_header: Note) -> Field where Note: NoteInterface { + let header = note_with_header.get_header(); - let siloed_note_hash = compute_siloed_note_hash(note_interface, note_with_header); + let siloed_note_hash = compute_siloed_note_hash(note_with_header); compute_unique_hash(header.nonce, siloed_note_hash) } -pub fn compute_siloed_nullifier( - note_interface: NoteInterface, +pub fn compute_siloed_nullifier( note_with_header: Note, context: &mut PrivateContext -) -> Field { - let get_header = note_interface.get_header; - let header = get_header(note_with_header); - - let compute_nullifier = note_interface.compute_nullifier; - let inner_nullifier = compute_nullifier(note_with_header, context); +) -> Field where Note: NoteInterface { + let header = note_with_header.get_header(); + let inner_nullifier = note_with_header.compute_nullifier(context); let input = [header.contract_address.to_field(), inner_nullifier]; pedersen_hash(input, GENERATOR_INDEX__OUTER_NULLIFIER) } -pub fn compute_note_hash_for_read_or_nullify(note_interface: NoteInterface, note_with_header: Note) -> Field { - let get_header = note_interface.get_header; - let header = get_header(note_with_header); +pub fn compute_note_hash_for_insertion(note: Note) -> Field where Note: NoteInterface { + compute_inner_note_hash(note) +} + +pub fn compute_note_hash_for_consumption(note: Note) -> Field where Note: NoteInterface { + let header = note.get_header(); + // There are 3 cases for reading a note intended for consumption: + // 1. The note was inserted in this transaction, and is transient. + // 2. The note was inserted in a previous transaction, and was inserted in public + // 3. The note was inserted in a previous transaction, and was inserted in private - // TODO(https://github.com/AztecProtocol/aztec-packages/issues/1386) if (header.is_transient) { // If a note is transient, we just read the inner_note_hash (kernel will silo by contract address). - compute_inner_note_hash(note_interface, note_with_header) + compute_inner_note_hash(note) } else if (header.nonce == 0) { // If not transient and nonce is zero, that means we are reading a public note. - compute_siloed_note_hash(note_interface, note_with_header) + compute_siloed_note_hash(note) } else { // When nonce is nonzero, that means we are reading a settled note (from tree) created in a // previous TX. So we need the unique_siloed_note_hash which has already been hashed with // contract address and then nonce. This hash will match the existing leaf in the private // data tree, so the kernel can just perform a membership check directly on this hash/leaf. - compute_unique_siloed_note_hash(note_interface, note_with_header) + compute_unique_siloed_note_hash(note) } } -pub fn compute_note_hash_and_nullifier( - note_interface: NoteInterface, +pub fn compute_note_hash_and_nullifier( + deserialize: fn([Field; N]) -> T, note_header: NoteHeader, serialized_note: [Field; S] -) -> [Field; 4] { - let deserialize = note_interface.deserialize; - let set_header = note_interface.set_header; +) -> [Field; 4] where T: NoteInterface { let mut note = deserialize(arr_copy_slice(serialized_note, [0; N], 0)); - set_header(&mut note, note_header); + // TODO: change this to note.setHeader(header) once https://github.com/noir-lang/noir/issues/4095 is fixed + T::set_header((&mut note), note_header); - let compute_note_hash = note_interface.compute_note_hash; - let note_hash = compute_note_hash(note); - let inner_note_hash = compute_inner_hash(note_header.storage_slot, note_hash); + let inner_note_hash = compute_inner_note_hash(note); let siloed_note_hash = compute_siloed_hash(note_header.contract_address, inner_note_hash); let unique_siloed_note_hash = compute_unique_hash(note_header.nonce, siloed_note_hash); - let compute_nullifier_without_context = note_interface.compute_nullifier_without_context; - let inner_nullifier = compute_nullifier_without_context(note); + let inner_nullifier = note.compute_nullifier_without_context(); [inner_note_hash, siloed_note_hash, unique_siloed_note_hash, inner_nullifier] } diff --git a/yarn-project/aztec-nr/aztec/src/oracle.nr b/yarn-project/aztec-nr/aztec/src/oracle.nr index 81eb3571a49..5d07eed78fd 100644 --- a/yarn-project/aztec-nr/aztec/src/oracle.nr +++ b/yarn-project/aztec-nr/aztec/src/oracle.nr @@ -15,7 +15,7 @@ mod nullifier_key; mod get_sibling_path; mod rand; mod enqueue_public_function_call; -mod get_block_header; +mod header; mod public_call; mod notes; mod storage; diff --git a/yarn-project/aztec-nr/aztec/src/oracle/get_block_header.nr b/yarn-project/aztec-nr/aztec/src/oracle/get_block_header.nr deleted file mode 100644 index 3977f88108a..00000000000 --- a/yarn-project/aztec-nr/aztec/src/oracle/get_block_header.nr +++ /dev/null @@ -1,56 +0,0 @@ -use dep::std::merkle::compute_merkle_root; -use dep::protocol_types::{ - abis::block_header::BlockHeader, - constants::BLOCK_HEADER_LENGTH, -}; - -use crate::{ - context::PrivateContext, - oracle::get_membership_witness::get_archive_membership_witness, -}; - -// TODO(#3564) - Nuke this oracle and Inject the number directly to context -#[oracle(getNullifierRootBlockNumber)] -fn get_nullifier_root_block_number_oracle(_nullifier_tree_root: Field) -> Field {} - -unconstrained pub fn get_nullifier_root_block_number(nullifier_tree_root: Field) -> u32 { - get_nullifier_root_block_number_oracle(nullifier_tree_root) as u32 -} - -#[oracle(getBlockHeader)] -fn get_block_header_oracle(_block_number: u32) -> [Field; BLOCK_HEADER_LENGTH] {} - -unconstrained pub fn get_block_header_internal(block_number: u32) -> BlockHeader { - let block_header = get_block_header_oracle(block_number); - BlockHeader::deserialize(block_header) -} - -pub fn get_block_header(block_number: u32, context: PrivateContext) -> BlockHeader { - // 1) Get block number corresponding to block header inside context - // Using nullifier tree root to get the block header block number because that changes in every block (every tx emits a nullifier). - let block_header_block_number = get_nullifier_root_block_number(context.block_header.nullifier_tree_root); - - // 2) Check that the block header block number is more than or equal to the block number we want to prove against - // We could not perform the proof otherwise because the archive root from the header would not "contain" the block we want to prove against - assert( - block_header_block_number >= block_number, "Block header block number is smaller than the block number we want to prove against" - ); - - // 3) Get block header of a given block from oracle - let block_header = get_block_header_internal(block_number); - - // 4) Compute the block hash from the block header - let block_hash = block_header.block_hash(); - - // 5) Get the membership witness of the block in the archive - let witness = get_archive_membership_witness(block_header_block_number, block_hash); - - // 6) Check that the block is in the archive (i.e. the witness is valid) - assert( - context.block_header.archive_root - == compute_merkle_root(block_hash, witness.index, witness.path), "Proving membership of a block in archive failed" - ); - - // 7) Return the block header - block_header -} diff --git a/yarn-project/aztec-nr/aztec/src/oracle/get_public_data_witness.nr b/yarn-project/aztec-nr/aztec/src/oracle/get_public_data_witness.nr index 555a8078580..f0c4ce2f8e9 100644 --- a/yarn-project/aztec-nr/aztec/src/oracle/get_public_data_witness.nr +++ b/yarn-project/aztec-nr/aztec/src/oracle/get_public_data_witness.nr @@ -1,5 +1,8 @@ -use dep::protocol_types::constants::PUBLIC_DATA_TREE_HEIGHT; -use dep::protocol_types::hash::pedersen_hash; +use dep::protocol_types::{ + constants::PUBLIC_DATA_TREE_HEIGHT, + hash::pedersen_hash, + traits::{Hash, Serialize} +}; use crate::utils::arr_copy_slice; global LEAF_PREIMAGE_LENGTH: Field = 4; @@ -14,11 +17,13 @@ struct PublicDataTreeLeafPreimage { next_slot :Field, } -impl PublicDataTreeLeafPreimage { +impl Serialize for PublicDataTreeLeafPreimage { fn serialize(self) -> [Field; LEAF_PREIMAGE_LENGTH] { [self.slot, self.value, self.next_index as Field, self.next_slot] } +} +impl Hash for PublicDataTreeLeafPreimage { fn hash(self) -> Field { // Performs the same hashing as StandardIndexedTree::encodeLeaf(...) pedersen_hash(self.serialize(), 0) @@ -34,10 +39,7 @@ struct PublicDataWitness { #[oracle(getPublicDataTreeWitness)] fn get_public_data_witness_oracle(_block_number: u32, _leaf_slot: Field) -> [Field; PUBLIC_DATA_WITNESS] {} -unconstrained pub fn get_public_data_witness( - block_number: u32, - leaf_slot: Field -) -> PublicDataWitness { +unconstrained pub fn get_public_data_witness(block_number: u32, leaf_slot: Field) -> PublicDataWitness { let fields = get_public_data_witness_oracle(block_number, leaf_slot); PublicDataWitness { index: fields[0], diff --git a/yarn-project/aztec-nr/aztec/src/oracle/get_public_key.nr b/yarn-project/aztec-nr/aztec/src/oracle/get_public_key.nr index d39e7389f2c..5aea4515160 100644 --- a/yarn-project/aztec-nr/aztec/src/oracle/get_public_key.nr +++ b/yarn-project/aztec-nr/aztec/src/oracle/get_public_key.nr @@ -2,6 +2,7 @@ use dep::protocol_types::{ address::{ AztecAddress, PartialAddress, + PublicKeysHash, }, grumpkin_point::GrumpkinPoint, }; @@ -18,7 +19,7 @@ pub fn get_public_key(address: AztecAddress) -> GrumpkinPoint { let pub_key = GrumpkinPoint::new(result[0], result[1]); let partial_address = PartialAddress::from_field(result[2]); - let calculated_address = AztecAddress::compute(pub_key, partial_address); + let calculated_address = AztecAddress::compute(PublicKeysHash::compute(pub_key), partial_address); assert(calculated_address.eq(address)); pub_key diff --git a/yarn-project/aztec-nr/aztec/src/oracle/header.nr b/yarn-project/aztec-nr/aztec/src/oracle/header.nr new file mode 100644 index 00000000000..bcb7027d2d6 --- /dev/null +++ b/yarn-project/aztec-nr/aztec/src/oracle/header.nr @@ -0,0 +1,57 @@ +use dep::std::merkle::compute_merkle_root; +use dep::protocol_types::{ + constants::HEADER_LENGTH, + header::Header, +}; + +use crate::{ + context::PrivateContext, + oracle::get_membership_witness::get_archive_membership_witness, +}; + +#[oracle(getHeader)] +fn get_header_at_oracle(_block_number: u32) -> [Field; HEADER_LENGTH] {} + +unconstrained pub fn get_header_at_internal(block_number: u32) -> Header { + let header = get_header_at_oracle(block_number); + Header::deserialize(header) +} + +pub fn get_header_at(block_number: u32, context: PrivateContext) -> Header { + let historical_header_block_number = context.historical_header.global_variables.block_number as u32; + + if (block_number == historical_header_block_number) { + // If the block number we want to prove against is the same as the block number in the historical header we + // skip the inclusion proofs and just return the historical header from context. + context.historical_header + } else { + // 1) Get block number corresponding to the last_archive root in the header + // Note: We subtract 1 because the last_archive root is the root of the archive after applying the previous block + let last_archive_block_number = historical_header_block_number - 1; + + // 2) Check that the last archive block number is more than or equal to the block number we want to prove against + // We could not perform the proof otherwise because the last archive root from the header would not "contain" + // the header we want to prove against + assert( + last_archive_block_number >= block_number, "Last archive block number is smaller than the block number we want to prove against" + ); + + // 3) Get the header of a given block from oracle + let header = get_header_at_internal(block_number); + + // 4) Compute the block hash from the block header + let block_hash = header.hash(); + + // 5) Get the membership witness of the block in the archive + let witness = get_archive_membership_witness(last_archive_block_number, block_hash); + + // 6) Check that the block is in the archive (i.e. the witness is valid) + assert( + context.historical_header.last_archive.root + == compute_merkle_root(block_hash, witness.index, witness.path), "Proving membership of a block in archive failed" + ); + + // 7) Return the block header + header + } +} diff --git a/yarn-project/aztec-nr/aztec/src/oracle/notes.nr b/yarn-project/aztec-nr/aztec/src/oracle/notes.nr index 88664e64dc8..3c7c7ebb5d3 100644 --- a/yarn-project/aztec-nr/aztec/src/oracle/notes.nr +++ b/yarn-project/aztec-nr/aztec/src/oracle/notes.nr @@ -5,7 +5,10 @@ use crate::note::{ }; use crate::utils::arr_copy_slice; -use dep::protocol_types::address::AztecAddress; +use dep::protocol_types::{ + address::AztecAddress, + traits::Deserialize +}; #[oracle(notifyCreatedNote)] fn notify_created_note_oracle(_storage_slot: Field, _serialized_note: [Field; N], _inner_note_hash: Field) -> Field {} @@ -27,10 +30,12 @@ fn get_notes_oracle( _num_selects: u8, _select_by: [u8; N], _select_values: [Field; N], + _select_comparators: [u3; N], _sort_by: [u8; N], _sort_order: [u2; N], _limit: u32, _offset: u32, + _status: u2, _return_size: u32, _placeholder_fields: [Field; S] ) -> [Field; S] {} @@ -40,10 +45,12 @@ unconstrained fn get_notes_oracle_wrapper( num_selects: u8, select_by: [u8; N], select_values: [Field; N], + select_comparators: [u3; N], sort_by: [u8; N], sort_order: [u2; N], limit: u32, offset: u32, + status: u2, mut placeholder_fields: [Field; S] ) -> [Field; S] { let return_size = placeholder_fields.len() as u32; @@ -52,10 +59,12 @@ unconstrained fn get_notes_oracle_wrapper( num_selects, select_by, select_values, + select_comparators, sort_by, sort_order, limit, offset, + status, return_size, placeholder_fields ) @@ -63,32 +72,34 @@ unconstrained fn get_notes_oracle_wrapper( unconstrained pub fn get_notes( storage_slot: Field, - note_interface: NoteInterface, num_selects: u8, select_by: [u8; M], select_values: [Field; M], + select_comparators: [u3; M], sort_by: [u8; M], sort_order: [u2; M], limit: u32, offset: u32, + status: u2, mut placeholder_opt_notes: [Option; S], // TODO: Remove it and use `limit` to initialize the note array. - placeholder_fields: [Field; NS] // TODO: Remove it and use `limit` to initialize the note array. -) -> [Option; S] { + placeholder_fields: [Field; NS], // TODO: Remove it and use `limit` to initialize the note array. + _placeholder_note_length: [Field; N] // Turbofish hack? Compiler breaks calculating read_offset unless we add this parameter +) -> [Option; S] where Note: NoteInterface + Deserialize { let fields = get_notes_oracle_wrapper( storage_slot, num_selects, select_by, select_values, + select_comparators, sort_by, sort_order, limit, offset, + status, placeholder_fields ); let num_notes = fields[0] as u32; let contract_address = AztecAddress::from_field(fields[1]); - let deserialize = note_interface.deserialize; - let set_header = note_interface.set_header; for i in 0..placeholder_opt_notes.len() { if i as u32 < num_notes { // lengths named as per typescript. @@ -99,8 +110,9 @@ unconstrained pub fn get_notes( let is_transient = fields[read_offset + 1] as bool; let header = NoteHeader { contract_address, nonce, storage_slot, is_transient }; let serialized_note = arr_copy_slice(fields, [0; N], read_offset + 2); - let mut note = deserialize(serialized_note); - set_header(&mut note, header); + let mut note = Note::deserialize(serialized_note); + // TODO: change this to note.setHeader(header) once https://github.com/noir-lang/noir/issues/4095 is fixed + Note::set_header(&mut note, header); placeholder_opt_notes[i] = Option::some(note); }; } diff --git a/yarn-project/aztec-nr/aztec/src/oracle/nullifier_key.nr b/yarn-project/aztec-nr/aztec/src/oracle/nullifier_key.nr index b97b2d651a3..a99b946752f 100644 --- a/yarn-project/aztec-nr/aztec/src/oracle/nullifier_key.nr +++ b/yarn-project/aztec-nr/aztec/src/oracle/nullifier_key.nr @@ -1,29 +1,31 @@ use dep::protocol_types::{ address::AztecAddress, grumpkin_point::GrumpkinPoint, + grumpkin_private_key::GrumpkinPrivateKey, }; -use dep::std::grumpkin_scalar::GrumpkinScalar; -struct KeyPair { +struct NullifierKeyPair { + account: AztecAddress, public_key: GrumpkinPoint, - secret_key: GrumpkinScalar, + secret_key: GrumpkinPrivateKey, } #[oracle(getNullifierKeyPair)] fn get_nullifier_key_pair_oracle(_account: AztecAddress) -> [Field; 4] {} -unconstrained fn get_nullifier_key_pair_internal(account: AztecAddress) -> KeyPair { +unconstrained fn get_nullifier_key_pair_internal(account: AztecAddress) -> NullifierKeyPair { let result = get_nullifier_key_pair_oracle(account); - KeyPair { + NullifierKeyPair { + account, public_key: GrumpkinPoint { x: result[0], y: result[1] }, - secret_key: GrumpkinScalar { high: result[2], low: result[3] } + secret_key: GrumpkinPrivateKey { high: result[2], low: result[3] } } } -pub fn get_nullifier_key_pair(account: AztecAddress) -> KeyPair { +pub fn get_nullifier_key_pair(account: AztecAddress) -> NullifierKeyPair { get_nullifier_key_pair_internal(account) } -pub fn get_nullifier_secret_key(account: AztecAddress) -> GrumpkinScalar { +pub fn get_nullifier_secret_key(account: AztecAddress) -> GrumpkinPrivateKey { get_nullifier_key_pair_internal(account).secret_key } diff --git a/yarn-project/aztec-nr/aztec/src/oracle/storage.nr b/yarn-project/aztec-nr/aztec/src/oracle/storage.nr index abb7766ba91..72bec83be3a 100644 --- a/yarn-project/aztec-nr/aztec/src/oracle/storage.nr +++ b/yarn-project/aztec-nr/aztec/src/oracle/storage.nr @@ -1,3 +1,5 @@ +use dep::protocol_types::traits::{Deserialize, Serialize}; + #[oracle(storageRead)] fn storage_read_oracle(_storage_slot: Field, _number_of_elements: Field) -> [Field; N] {} @@ -5,15 +7,13 @@ unconstrained fn storage_read_oracle_wrapper(_storage_slot: Field) -> [Field; storage_read_oracle(_storage_slot, N) } -pub fn storage_read(storage_slot: Field, deserialize: fn([Field; N]) -> T) -> T { - let fields = storage_read_oracle_wrapper(storage_slot); - deserialize(fields) +pub fn storage_read(storage_slot: Field) -> [Field; N] { + storage_read_oracle_wrapper(storage_slot) } #[oracle(storageWrite)] fn storage_write_oracle(_storage_slot: Field, _values: [Field; N]) -> [Field; N] {} -// TODO: Remove return value. unconstrained pub fn storage_write(storage_slot: Field, fields: [Field; N]) { let _hash = storage_write_oracle(storage_slot, fields); } diff --git a/yarn-project/aztec-nr/aztec/src/state_vars.nr b/yarn-project/aztec-nr/aztec/src/state_vars.nr index e1e813891ff..d8213bb1ef0 100644 --- a/yarn-project/aztec-nr/aztec/src/state_vars.nr +++ b/yarn-project/aztec-nr/aztec/src/state_vars.nr @@ -3,3 +3,4 @@ mod map; mod public_state; mod set; mod singleton; +mod stable_public_state; diff --git a/yarn-project/aztec-nr/aztec/src/state_vars/immutable_singleton.nr b/yarn-project/aztec-nr/aztec/src/state_vars/immutable_singleton.nr index 699cecae250..187feca45fc 100644 --- a/yarn-project/aztec-nr/aztec/src/state_vars/immutable_singleton.nr +++ b/yarn-project/aztec-nr/aztec/src/state_vars/immutable_singleton.nr @@ -1,6 +1,11 @@ use dep::std::option::Option; use dep::protocol_types::{ address::AztecAddress, + traits::{Serialize, Deserialize}, + constants::{ + GENERATOR_INDEX__INITIALIZATION_NULLIFIER, + }, + hash::pedersen_hash, }; use crate::context::{PrivateContext, Context}; @@ -11,78 +16,78 @@ use crate::note::{ note_viewer_options::NoteViewerOptions, }; use crate::oracle::notes::check_nullifier_exists; -use crate::state_vars::singleton::compute_singleton_initialization_nullifier; // docs:start:struct -struct ImmutableSingleton { +struct ImmutableSingleton { context: Option<&mut PrivateContext>, storage_slot: Field, - note_interface: NoteInterface, - compute_initialization_nullifier: fn (Field, Option, Option<&mut PrivateContext>) -> Field, } // docs:end:struct -impl ImmutableSingleton { +impl ImmutableSingleton { // docs:start:new pub fn new( context: Context, storage_slot: Field, - note_interface: NoteInterface, ) -> Self { assert(storage_slot != 0, "Storage slot 0 not allowed. Storage slots must start from 1."); - ImmutableSingleton { + Self { context: context.private, storage_slot, - note_interface, - compute_initialization_nullifier: compute_singleton_initialization_nullifier, } } // docs:end:new + // The following computation is leaky, in that it doesn't hide the storage slot that has been initialized, nor does it hide the contract address of this contract. + // When this initialization nullifier is emitted, an observer could do a dictionary or rainbow attack to learn the preimage of this nullifier to deduce the storage slot and contract address. + // For some applications, leaking the details that a particular state variable of a particular contract has been initialized will be unacceptable. + // Under such circumstances, such application developers might wish to _not_ use this state variable type. + // This is especially dangerous for initial assignment to elements of a `Map` type (for example), because the storage slot often also identifies an actor. + // e.g. the initial assignment to `my_map.at(msg.sender)` will leak: `msg.sender`, the fact that an element of `my_map` was assigned-to for the first time, and the contract_address. + pub fn compute_initialization_nullifier(self) -> Field { + pedersen_hash([self.storage_slot], GENERATOR_INDEX__INITIALIZATION_NULLIFIER) + } + // docs:start:is_initialized - unconstrained pub fn is_initialized(self, owner: Option) -> bool { - let compute_initialization_nullifier = self.compute_initialization_nullifier; - let nullifier = compute_initialization_nullifier(self.storage_slot, owner, Option::none()); + unconstrained pub fn is_initialized(self) -> bool { + let nullifier = self.compute_initialization_nullifier(); check_nullifier_exists(nullifier) } // docs:end:is_initialized // docs:start:initialize - pub fn initialize( + pub fn initialize( self, note: &mut Note, - owner: Option, broadcast: bool, - ) { + ) where Note: NoteInterface + Serialize + Deserialize { let context = self.context.unwrap(); // Nullify the storage slot. - let compute_initialization_nullifier = self.compute_initialization_nullifier; - let nullifier = compute_initialization_nullifier(self.storage_slot, owner, self.context); + let nullifier = self.compute_initialization_nullifier(); context.push_new_nullifier(nullifier, 0); create_note( context, self.storage_slot, note, - self.note_interface, broadcast, ); } // docs:end:initialize // docs:start:get_note - pub fn get_note(self) -> Note { + pub fn get_note(self) -> Note where Note: NoteInterface + Deserialize { let context = self.context.unwrap(); let storage_slot = self.storage_slot; - get_note(context, storage_slot, self.note_interface) + get_note(context, storage_slot) } // docs:end:get_note // docs:start:view_note - unconstrained pub fn view_note(self) -> Note { + unconstrained pub fn view_note(self) -> Note where Note: NoteInterface + Deserialize { let options = NoteViewerOptions::new().set_limit(1); - view_notes(self.storage_slot, self.note_interface, options)[0].unwrap() + view_notes(self.storage_slot, options)[0].unwrap() } // docs:end:view_note } diff --git a/yarn-project/aztec-nr/aztec/src/state_vars/public_state.nr b/yarn-project/aztec-nr/aztec/src/state_vars/public_state.nr index 10970a92c0d..9309f3d9d49 100644 --- a/yarn-project/aztec-nr/aztec/src/state_vars/public_state.nr +++ b/yarn-project/aztec-nr/aztec/src/state_vars/public_state.nr @@ -1,46 +1,43 @@ use crate::context::{Context}; use crate::oracle::storage::storage_read; use crate::oracle::storage::storage_write; -use crate::types::type_serialization::TypeSerializationInterface; use dep::std::option::Option; +use dep::protocol_types::traits::{Deserialize, Serialize}; // docs:start:public_state_struct -struct PublicState { +struct PublicState { context: Context, storage_slot: Field, - serialization_methods: TypeSerializationInterface, } // docs:end:public_state_struct -impl PublicState { +impl PublicState { // docs:start:public_state_struct_new pub fn new( // Note: Passing the contexts to new(...) just to have an interface compatible with a Map. context: Context, storage_slot: Field, - serialization_methods: TypeSerializationInterface, ) -> Self { assert(storage_slot != 0, "Storage slot 0 not allowed. Storage slots must start from 1."); PublicState { context, storage_slot, - serialization_methods, } } // docs:end:public_state_struct_new // docs:start:public_state_struct_read - pub fn read(self) -> T { - assert(self.context.private.is_none(), "Public state reads only supported in public functions"); - storage_read(self.storage_slot, self.serialization_methods.deserialize) + pub fn read(self) -> T where T: Deserialize { + assert(self.context.private.is_none(), "Public state writes only supported in public functions"); + let fields = storage_read(self.storage_slot); + T::deserialize(fields) } // docs:end:public_state_struct_read // docs:start:public_state_struct_write - pub fn write(self, value: T) { + pub fn write(self, value: T) where T: Serialize { assert(self.context.private.is_none(), "Public state writes only supported in public functions"); - let serialize = self.serialization_methods.serialize; - let fields = serialize(value); + let fields = T::serialize(value); storage_write(self.storage_slot, fields); } // docs:end:public_state_struct_write diff --git a/yarn-project/aztec-nr/aztec/src/state_vars/set.nr b/yarn-project/aztec-nr/aztec/src/state_vars/set.nr index 1d873e1b149..03e53b33d7a 100644 --- a/yarn-project/aztec-nr/aztec/src/state_vars/set.nr +++ b/yarn-project/aztec-nr/aztec/src/state_vars/set.nr @@ -3,6 +3,7 @@ use crate::abi::PublicContextInputs; use dep::protocol_types::{ constants::{MAX_NOTES_PER_PAGE, MAX_READ_REQUESTS_PER_CALL}, abis::side_effect::{SideEffect, SideEffectLinkedToNoteHash}, + traits::{Deserialize, Serialize}, }; use crate::context::{PrivateContext, PublicContext, Context}; use crate::note::{ @@ -12,55 +13,50 @@ use crate::note::{ note_header::NoteHeader, note_interface::NoteInterface, note_viewer_options::NoteViewerOptions, - utils::compute_note_hash_for_read_or_nullify, + utils::compute_note_hash_for_consumption, }; // docs:start:struct -struct Set { +struct Set { context: Context, storage_slot: Field, - note_interface: NoteInterface, } // docs:end:struct -impl Set { +impl Set { // docs:start:new pub fn new( context: Context, storage_slot: Field, - note_interface: NoteInterface, ) -> Self { assert(storage_slot != 0, "Storage slot 0 not allowed. Storage slots must start from 1."); Set { context, storage_slot, - note_interface, } } // docs:end:new // docs:start:insert - pub fn insert(self, + pub fn insert(self, note: &mut Note, broadcast: bool, - ) { + ) where Note: NoteInterface + Deserialize + Serialize { create_note( self.context.private.unwrap(), self.storage_slot, note, - self.note_interface, broadcast, ); } // docs:end:insert // docs:start:insert_from_public - pub fn insert_from_public(self, note: &mut Note) { + pub fn insert_from_public(self, note: &mut Note) where Note: NoteInterface { create_note_hash_from_public( self.context.public.unwrap(), self.storage_slot, note, - self.note_interface, ); } // docs:end:insert_from_public @@ -76,30 +72,28 @@ impl Set { } // docs:start:remove - pub fn remove(self, note: Note) { + pub fn remove(self, note: Note) where Note: NoteInterface { let context = self.context.private.unwrap(); - let note_hash = compute_note_hash_for_read_or_nullify(self.note_interface, note); + let note_hash = compute_note_hash_for_consumption(note); let has_been_read = context.read_requests.any(|r: SideEffect| r.value == note_hash); assert(has_been_read, "Can only remove a note that has been read from the set."); destroy_note( context, note, - self.note_interface, ); } // docs:end:remove // docs:start:get_notes - pub fn get_notes( + pub fn get_notes( self, options: NoteGetterOptions, - ) -> [Option; MAX_READ_REQUESTS_PER_CALL] { + ) -> [Option; MAX_READ_REQUESTS_PER_CALL] where Note: NoteInterface + Serialize + Deserialize { let storage_slot = self.storage_slot; let opt_notes = get_notes( self.context.private.unwrap(), storage_slot, - self.note_interface, options, ); opt_notes @@ -107,11 +101,11 @@ impl Set { // docs:end:get_notes // docs:start:view_notes - unconstrained pub fn view_notes( + unconstrained pub fn view_notes( self, options: NoteViewerOptions, - ) -> [Option; MAX_NOTES_PER_PAGE] { - view_notes(self.storage_slot, self.note_interface, options) + ) -> [Option; MAX_NOTES_PER_PAGE] where Note: NoteInterface + Deserialize { + view_notes(self.storage_slot, options) } // docs:end:view_notes } diff --git a/yarn-project/aztec-nr/aztec/src/state_vars/singleton.nr b/yarn-project/aztec-nr/aztec/src/state_vars/singleton.nr index d564512a1ab..19b4e91bb5c 100644 --- a/yarn-project/aztec-nr/aztec/src/state_vars/singleton.nr +++ b/yarn-project/aztec-nr/aztec/src/state_vars/singleton.nr @@ -6,6 +6,7 @@ use dep::protocol_types::{ GENERATOR_INDEX__INITIALIZATION_NULLIFIER, }, hash::pedersen_hash, + traits::{Serialize, Deserialize}, }; use crate::context::{PrivateContext, PublicContext, Context}; @@ -20,115 +21,99 @@ use crate::oracle::{ notes::check_nullifier_exists, }; -pub fn compute_singleton_initialization_nullifier( - storage_slot: Field, - owner: Option, - context: Option<&mut PrivateContext> -) -> Field { - if owner.is_some() { - let secret = if context.is_some() { - context.unwrap_unchecked().request_nullifier_secret_key(owner.unwrap_unchecked()) - } else { - get_nullifier_secret_key(owner.unwrap_unchecked()) - }; - pedersen_hash( - [storage_slot, secret.low, secret.high], - GENERATOR_INDEX__INITIALIZATION_NULLIFIER - ) - } else { - pedersen_hash([storage_slot], GENERATOR_INDEX__INITIALIZATION_NULLIFIER) - } -} - // docs:start:struct -struct Singleton { +struct Singleton { context: Option<&mut PrivateContext>, - storage_slot: Field, - note_interface: NoteInterface, - compute_initialization_nullifier: fn (Field, Option, Option<&mut PrivateContext>) -> Field, + storage_slot: Field } // docs:end:struct -impl Singleton { +impl Singleton { // docs:start:new pub fn new( context: Context, storage_slot: Field, - note_interface: NoteInterface, ) -> Self { assert(storage_slot != 0, "Storage slot 0 not allowed. Storage slots must start from 1."); - Singleton { + Self { context: context.private, storage_slot, - note_interface, - compute_initialization_nullifier: compute_singleton_initialization_nullifier, } } // docs:end:new + // The following computation is leaky, in that it doesn't hide the storage slot that has been initialized, nor does it hide the contract address of this contract. + // When this initialization nullifier is emitted, an observer could do a dictionary or rainbow attack to learn the preimage of this nullifier to deduce the storage slot and contract address. + // For some applications, leaking the details that a particular state variable of a particular contract has been initialized will be unacceptable. + // Under such circumstances, such application developers might wish to _not_ use this state variable type. + // This is especially dangerous for initial assignment to elements of a `Map` type (for example), because the storage slot often also identifies an actor. e.g. + // the initial assignment to `my_map.at(msg.sender)` will leak: `msg.sender`, the fact that an element of `my_map` was assigned-to for the first time, and the contract_address. + // Note: subsequent nullification of this state variable, via the `replace` method will not be leaky, if the `compute_nullifier()` method of the underlying note is designed to ensure privacy. + // For example, if the `compute_nullifier()` method injects the secret key of a note owner into the computed nullifier's preimage. + pub fn compute_initialization_nullifier(self) -> Field { + pedersen_hash([self.storage_slot], GENERATOR_INDEX__INITIALIZATION_NULLIFIER) + } + // docs:start:is_initialized - unconstrained pub fn is_initialized(self, owner: Option) -> bool { - let compute_initialization_nullifier = self.compute_initialization_nullifier; - let nullifier = compute_initialization_nullifier(self.storage_slot, owner, Option::none()); + unconstrained pub fn is_initialized(self) -> bool { + let nullifier = self.compute_initialization_nullifier(); check_nullifier_exists(nullifier) } // docs:end:is_initialized // docs:start:initialize - pub fn initialize( + pub fn initialize( self, note: &mut Note, - owner: Option, broadcast: bool, - ) { + ) where Note: NoteInterface + Serialize + Deserialize { let context = self.context.unwrap(); // Nullify the storage slot. - let compute_initialization_nullifier = self.compute_initialization_nullifier; - let nullifier = compute_initialization_nullifier(self.storage_slot, owner, self.context); + let nullifier = self.compute_initialization_nullifier(); context.push_new_nullifier(nullifier, 0); - create_note(context, self.storage_slot, note, self.note_interface, broadcast); + create_note(context, self.storage_slot, note, broadcast); } // docs:end:initialize // docs:start:replace - pub fn replace( + pub fn replace( self, new_note: &mut Note, broadcast: bool, - ) { + ) where Note: NoteInterface + Serialize + Deserialize { let context = self.context.unwrap(); - let prev_note = get_note(context, self.storage_slot, self.note_interface); + let prev_note = get_note(context, self.storage_slot); // Nullify previous note. - destroy_note(context, prev_note, self.note_interface); + destroy_note(context, prev_note); // Add replacement note. - create_note(context, self.storage_slot, new_note, self.note_interface, broadcast); + create_note(context, self.storage_slot, new_note, broadcast); } // docs:end:replace // docs:start:get_note - pub fn get_note(self, broadcast: bool) -> Note { + pub fn get_note(self, broadcast: bool) -> Note where Note: NoteInterface + Serialize + Deserialize { let context = self.context.unwrap(); - let mut note = get_note(context, self.storage_slot, self.note_interface); + let mut note = get_note(context, self.storage_slot); // Nullify current note to make sure it's reading the latest note. - destroy_note(context, note, self.note_interface); + destroy_note(context, note); // Add the same note again. // Because a nonce is added to every note in the kernel, its nullifier will be different. - create_note(context, self.storage_slot, &mut note, self.note_interface, broadcast); + create_note(context, self.storage_slot, &mut note, broadcast); note } // docs:end:get_note // docs:start:view_note - unconstrained pub fn view_note(self) -> Note { + unconstrained pub fn view_note(self) -> Note where Note: NoteInterface + Deserialize { let options = NoteViewerOptions::new().set_limit(1); - view_notes(self.storage_slot, self.note_interface, options)[0].unwrap() + view_notes(self.storage_slot, options)[0].unwrap() } // docs:end:view_note } diff --git a/yarn-project/aztec-nr/aztec/src/state_vars/stable_public_state.nr b/yarn-project/aztec-nr/aztec/src/state_vars/stable_public_state.nr new file mode 100644 index 00000000000..013f059aef0 --- /dev/null +++ b/yarn-project/aztec-nr/aztec/src/state_vars/stable_public_state.nr @@ -0,0 +1,69 @@ +use crate::context::{Context}; +use crate::oracle::{ + storage::{storage_read, storage_write}, +}; +use crate::history::public_value_inclusion::prove_public_value_inclusion; +use dep::std::option::Option; +use dep::protocol_types::traits::{Deserialize, Serialize}; + +struct StablePublicState{ + context: Context, + storage_slot: Field, +} + +impl StablePublicState { + pub fn new( + // Note: Passing the contexts to new(...) just to have an interface compatible with a Map. + context: Context, + storage_slot: Field + ) -> Self { + assert(storage_slot != 0, "Storage slot 0 not allowed. Storage slots must start from 1."); + Self { + context, + storage_slot, + } + } + + // Intended to be only called once. + pub fn initialize(self, value: T) where T: Serialize { + assert(self.context.private.is_none(), "Public state wrties only supported in public functions"); + // TODO: Must throw if the storage slot is not empty -> cannot allow overwriting + // This is currently impractical, as public functions are never marked `is_contract_deployment` + // in the `call_context`, only private functions will have this flag set. + let fields = T::serialize(value); + storage_write(self.storage_slot, fields); + } + + pub fn read_public(self) -> T where T: Deserialize { + assert(self.context.private.is_none(), "Public read only supported in public functions"); + let fields = storage_read(self.storage_slot); + T::deserialize(fields) + } + + pub fn read_private(self) -> T where T: Deserialize { + assert(self.context.public.is_none(), "Private read only supported in private functions"); + let private_context = self.context.private.unwrap(); + + // Read the value from storage (using the public tree) + let fields = storage_read(self.storage_slot); + + // TODO: The block_number here can be removed when using the current header in the membership proof. + let block_number = private_context.get_header().global_variables.block_number; + + // Loop over the fields and prove their inclusion in the public tree + for i in 0..fields.len() { + // TODO: Update membership proofs to use current header (Requires #4179) + // Currently executing unnecessary computation: + // - a membership proof of the header(block_number) in the history + // - a membership proof of the value in the public tree of the header + prove_public_value_inclusion( + fields[i], + self.storage_slot + i, + block_number as u32, + (*private_context), + ) + } + T::deserialize(fields) + } + +} diff --git a/yarn-project/aztec-nr/aztec/src/types.nr b/yarn-project/aztec-nr/aztec/src/types.nr deleted file mode 100644 index 18f6888ae32..00000000000 --- a/yarn-project/aztec-nr/aztec/src/types.nr +++ /dev/null @@ -1,2 +0,0 @@ -mod vec; // This can/should be moved out into an official noir library -mod type_serialization; diff --git a/yarn-project/aztec-nr/aztec/src/types/type_serialization.nr b/yarn-project/aztec-nr/aztec/src/types/type_serialization.nr deleted file mode 100644 index 016548a544f..00000000000 --- a/yarn-project/aztec-nr/aztec/src/types/type_serialization.nr +++ /dev/null @@ -1,15 +0,0 @@ -mod bool_serialization; -mod field_serialization; -mod u8_serialization; -mod u32_serialization; -mod address_serialization; - -/** - * Before Noir supports traits, a way of specifying the serialization and deserialization methods for a type. - */ -// docs:start:TypeSerializationInterface -struct TypeSerializationInterface { - deserialize: fn ([Field; N]) -> T, - serialize: fn (T) -> [Field; N], -} -// docs:end:TypeSerializationInterface \ No newline at end of file diff --git a/yarn-project/aztec-nr/aztec/src/types/type_serialization/address_serialization.nr b/yarn-project/aztec-nr/aztec/src/types/type_serialization/address_serialization.nr deleted file mode 100644 index 4994bdc0437..00000000000 --- a/yarn-project/aztec-nr/aztec/src/types/type_serialization/address_serialization.nr +++ /dev/null @@ -1,22 +0,0 @@ -use crate::types::type_serialization::TypeSerializationInterface; -use dep::protocol_types::{ - address::{ - AztecAddress, - EthAddress - }, -}; - -global AZTEC_ADDRESS_SERIALIZED_LEN: Field = 1; - -fn deserialize(fields: [Field; AZTEC_ADDRESS_SERIALIZED_LEN]) -> AztecAddress { - AztecAddress::from_field(fields[0]) -} - -fn serialize(value: AztecAddress) -> [Field; AZTEC_ADDRESS_SERIALIZED_LEN] { - [value.to_field()] -} - -global AddressSerializationMethods = TypeSerializationInterface { - deserialize, - serialize, -}; diff --git a/yarn-project/aztec-nr/aztec/src/types/type_serialization/bool_serialization.nr b/yarn-project/aztec-nr/aztec/src/types/type_serialization/bool_serialization.nr deleted file mode 100644 index 255f519234c..00000000000 --- a/yarn-project/aztec-nr/aztec/src/types/type_serialization/bool_serialization.nr +++ /dev/null @@ -1,16 +0,0 @@ -use crate::types::type_serialization::TypeSerializationInterface; - -global BOOL_SERIALIZED_LEN: Field = 1; - -fn deserializeBool(fields: [Field; BOOL_SERIALIZED_LEN]) -> bool { - fields[0] as bool -} - -fn serializeBool(value: bool) -> [Field; BOOL_SERIALIZED_LEN] { - [value as Field] -} - -global BoolSerializationMethods = TypeSerializationInterface { - deserialize: deserializeBool, - serialize: serializeBool, -}; diff --git a/yarn-project/aztec-nr/aztec/src/types/type_serialization/field_serialization.nr b/yarn-project/aztec-nr/aztec/src/types/type_serialization/field_serialization.nr deleted file mode 100644 index 3c13de1112e..00000000000 --- a/yarn-project/aztec-nr/aztec/src/types/type_serialization/field_serialization.nr +++ /dev/null @@ -1,18 +0,0 @@ -use crate::types::type_serialization::TypeSerializationInterface; - -// docs:start:field_serialization -global FIELD_SERIALIZED_LEN: Field = 1; - -fn deserializeField(fields: [Field; FIELD_SERIALIZED_LEN]) -> Field { - fields[0] -} - -fn serializeField(value: Field) -> [Field; FIELD_SERIALIZED_LEN] { - [value] -} - -global FieldSerializationMethods = TypeSerializationInterface { - deserialize: deserializeField, - serialize: serializeField, -}; -// docs:end:field_serialization \ No newline at end of file diff --git a/yarn-project/aztec-nr/aztec/src/types/type_serialization/u32_serialization.nr b/yarn-project/aztec-nr/aztec/src/types/type_serialization/u32_serialization.nr deleted file mode 100644 index 2517532b420..00000000000 --- a/yarn-project/aztec-nr/aztec/src/types/type_serialization/u32_serialization.nr +++ /dev/null @@ -1,16 +0,0 @@ -use crate::types::type_serialization::TypeSerializationInterface; - -global U32_SERIALIZED_LEN: Field = 1; - -fn deserializeU32(fields: [Field; U32_SERIALIZED_LEN]) -> u32 { - fields[0] as u32 -} - -fn serializeU32(value: u32) -> [Field; U32_SERIALIZED_LEN] { - [value as Field] -} - -global U32SerializationMethods = TypeSerializationInterface { - deserialize: deserializeU32, - serialize: serializeU32, -}; diff --git a/yarn-project/aztec-nr/aztec/src/types/type_serialization/u8_serialization.nr b/yarn-project/aztec-nr/aztec/src/types/type_serialization/u8_serialization.nr deleted file mode 100644 index 8011d02675c..00000000000 --- a/yarn-project/aztec-nr/aztec/src/types/type_serialization/u8_serialization.nr +++ /dev/null @@ -1,16 +0,0 @@ -use crate::types::type_serialization::TypeSerializationInterface; - -global U8_SERIALIZED_LEN: Field = 1; - -fn deserializeU8(fields: [Field; U8_SERIALIZED_LEN]) -> u8 { - fields[0] as u8 -} - -fn serializeU8(value: u8) -> [Field; U8_SERIALIZED_LEN] { - [value as Field] -} - -global U8SerializationMethods = TypeSerializationInterface { - deserialize: deserializeU8, - serialize: serializeU8, -}; diff --git a/yarn-project/aztec-nr/aztec/src/types/vec.nr b/yarn-project/aztec-nr/aztec/src/types/vec.nr deleted file mode 100644 index 135bbbd53c3..00000000000 --- a/yarn-project/aztec-nr/aztec/src/types/vec.nr +++ /dev/null @@ -1,126 +0,0 @@ -struct BoundedVec { - storage: [T; MaxLen], - len: Field, -} - -impl BoundedVec { - pub fn new(initial_value: T) -> Self { - BoundedVec { storage: [initial_value; MaxLen], len: 0 } - } - - pub fn get(mut self: Self, index: Field) -> T { - assert(index as u64 < self.len as u64); - self.storage[index] - } - - pub fn get_unchecked(mut self: Self, index: Field) -> T { - self.storage[index] - } - - pub fn push(&mut self, elem: T) { - assert(self.len as u64 < MaxLen as u64); - - self.storage[self.len] = elem; - self.len += 1; - } - - pub fn push_array(&mut self, array: [T; Len]) { - let newLen = self.len + array.len(); - assert(newLen as u64 <= MaxLen as u64); - for i in 0..array.len() { - self.storage[self.len + i] = array[i]; - } - self.len = newLen; - } - - pub fn pop(&mut self) -> T { - assert(self.len as u64 > 0); - - let elem = self.storage[self.len - 1]; - self.len -= 1; - elem - } - - pub fn any(self, predicate: fn[Env](T) -> bool) -> bool { - let mut ret = false; - let mut exceeded_len = false; - for i in 0..MaxLen { - exceeded_len |= i == self.len; - if (!exceeded_len) { - ret |= predicate(self.storage[i]); - } - } - ret - } -} - -#[test] -fn test_vec_push_pop() { - let mut vec: BoundedVec = BoundedVec::new(0); - assert(vec.len == 0); - vec.push(2); - assert(vec.len == 1); - vec.push(4); - assert(vec.len == 2); - vec.push(6); - assert(vec.len == 3); - let x = vec.pop(); - assert(x == 6); - assert(vec.len == 2); - assert(vec.get(0) == 2); - assert(vec.get(1) == 4); -} - -#[test] -fn test_vec_push_array() { - let mut vec: BoundedVec = BoundedVec::new(0); - vec.push_array([2, 4]); - assert(vec.len == 2); - assert(vec.get(0) == 2); - assert(vec.get(1) == 4); -} - -#[test(should_fail)] -fn test_vec_get_out_of_bound() { - let mut vec: BoundedVec = BoundedVec::new(0); - vec.push_array([2, 4]); - let _x = vec.get(2); -} - -#[test(should_fail)] -fn test_vec_get_not_declared() { - let mut vec: BoundedVec = BoundedVec::new(0); - vec.push_array([2]); - let _x = vec.get(1); -} - -#[test(should_fail)] -fn test_vec_get_uninitialized() { - let mut vec: BoundedVec = BoundedVec::new(0); - let _x = vec.get(0); -} - -#[test(should_fail)] -fn test_vec_push_overflow() { - let mut vec: BoundedVec = BoundedVec::new(0); - vec.push(1); - vec.push(2); -} - -#[test] -fn test_vec_any() { - let mut vec: BoundedVec = BoundedVec::new(0); - vec.push_array([2, 4, 6]); - assert(vec.any(|v| v == 2) == true); - assert(vec.any(|v| v == 4) == true); - assert(vec.any(|v| v == 6) == true); - assert(vec.any(|v| v == 3) == false); -} - -#[test] -fn test_vec_any_not_default() { - let default_value = 1; - let mut vec: BoundedVec = BoundedVec::new(default_value); - vec.push_array([2, 4]); - assert(vec.any(|v| v == default_value) == false); -} diff --git a/yarn-project/aztec-nr/compressed-string/src/compressed_string.nr b/yarn-project/aztec-nr/compressed-string/src/compressed_string.nr index 0cf51b8edb2..7f7945d1cfc 100644 --- a/yarn-project/aztec-nr/compressed-string/src/compressed_string.nr +++ b/yarn-project/aztec-nr/compressed-string/src/compressed_string.nr @@ -1,5 +1,7 @@ -use dep::aztec::types::type_serialization::TypeSerializationInterface; -use dep::aztec::protocol_types::utils::field::field_from_bytes; +use dep::aztec::protocol_types::{ + utils::field::field_from_bytes, + traits::{Serialize, Deserialize} +}; use dep::std; // A Fixedsize Compressed String. @@ -8,6 +10,18 @@ struct FieldCompressedString{ value: Field } +impl Serialize<1> for FieldCompressedString { + fn serialize(self) -> [Field; 1] { + [self.value] + } +} + +impl Deserialize<1> for FieldCompressedString { + fn deserialize(input: [Field; 1]) -> Self { + Self { value: input[0] } + } +} + impl FieldCompressedString{ pub fn is_eq(self, other: FieldCompressedString) -> bool { self.value == other.value @@ -29,28 +43,8 @@ impl FieldCompressedString{ } result } - - pub fn serialize(self) -> [Field; 1] { - [self.value] - } - - pub fn deserialize(input: [Field; 1]) -> Self { - Self { value: input[0] } - } } -fn deserialize(fields: [Field; 1]) -> FieldCompressedString { - FieldCompressedString { value: fields[0] } -} - -fn serialize(value: FieldCompressedString) -> [Field; 1] { - value.serialize() -} -global FieldCompressedStringSerializationMethods = TypeSerializationInterface { - deserialize, - serialize, -}; - // The general Compressed String. // Compresses M bytes into N fields. // Can be used for longer strings that don't fit in a single field. diff --git a/yarn-project/aztec-nr/compressed-string/src/lib.nr b/yarn-project/aztec-nr/compressed-string/src/lib.nr index aef2a573fdf..b441f622b40 100644 --- a/yarn-project/aztec-nr/compressed-string/src/lib.nr +++ b/yarn-project/aztec-nr/compressed-string/src/lib.nr @@ -1,4 +1,4 @@ mod compressed_string; use crate::compressed_string::{CompressedString}; -use crate::compressed_string::{FieldCompressedString, FieldCompressedStringSerializationMethods}; +use crate::compressed_string::FieldCompressedString; diff --git a/yarn-project/aztec-nr/easy-private-state/src/easy_private_state.nr b/yarn-project/aztec-nr/easy-private-state/src/easy_private_state.nr index 29feeeb26b0..d6b6fbf650f 100644 --- a/yarn-project/aztec-nr/easy-private-state/src/easy_private_state.nr +++ b/yarn-project/aztec-nr/easy-private-state/src/easy_private_state.nr @@ -6,12 +6,12 @@ use dep::aztec::{ }; use dep::value_note::{ filter::filter_notes_min_sum, - value_note::{ValueNote, ValueNoteMethods, VALUE_NOTE_LEN}, + value_note::ValueNote, }; struct EasyPrivateUint { context: Context, - set: Set, + set: Set, storage_slot: Field, } @@ -23,8 +23,7 @@ impl EasyPrivateUint { assert(storage_slot != 0, "Storage slot 0 not allowed. Storage slots must start from 1."); let set = Set { context, - storage_slot, - note_interface: ValueNoteMethods, + storage_slot }; EasyPrivateUint { context, diff --git a/yarn-project/aztec-nr/field-note/src/field_note.nr b/yarn-project/aztec-nr/field-note/src/field_note.nr index 8aa8fbe5520..7bf97b662ac 100644 --- a/yarn-project/aztec-nr/field-note/src/field_note.nr +++ b/yarn-project/aztec-nr/field-note/src/field_note.nr @@ -5,6 +5,7 @@ use dep::aztec::{ }, hash::pedersen_hash, context::PrivateContext, + protocol_types::traits::{Serialize, Deserialize}, }; global FIELD_NOTE_LEN: Field = 1; @@ -17,86 +18,58 @@ struct FieldNote { header: NoteHeader, } -impl FieldNote { - pub fn new(value: Field) -> Self { - FieldNote { - value, - header: NoteHeader::empty(), - } - } - - pub fn serialize(self) -> [Field; FIELD_NOTE_LEN]{ +impl Serialize for FieldNote { + fn serialize(self) -> [Field; FIELD_NOTE_LEN]{ [self.value] } +} - pub fn deserialize(serialized_note: [Field; FIELD_NOTE_LEN]) -> Self { +impl Deserialize for FieldNote { + fn deserialize(serialized_note: [Field; FIELD_NOTE_LEN]) -> Self { FieldNote { value: serialized_note[0], header: NoteHeader::empty(), } } +} - pub fn compute_note_hash(self) -> Field { +impl NoteInterface for FieldNote { + fn compute_note_content_hash(self) -> Field { // TODO(#1205) Should use a non-zero generator index. pedersen_hash(self.serialize(), 0) } - pub fn compute_nullifier(_self: Self, _context: &mut PrivateContext) -> Field { + fn compute_nullifier(self, _context: &mut PrivateContext) -> Field { // This note is expected to be shared between users and for this reason can't be nullified using a secret. 0 } - pub fn compute_nullifier_without_context(_self: Self) -> Field { + fn compute_nullifier_without_context(self) -> Field { // This note is expected to be shared between users and for this reason can't be nullified using a secret. 0 } - pub fn set_header(&mut self, header: NoteHeader) { + fn set_header(&mut self, header: NoteHeader) { self.header = header; } -} - -fn deserialize(serialized_note: [Field; FIELD_NOTE_LEN]) -> FieldNote { - FieldNote::deserialize(serialized_note) -} - -fn serialize(note: FieldNote) -> [Field; FIELD_NOTE_LEN] { - note.serialize() -} - -fn compute_note_hash(note: FieldNote) -> Field { - note.compute_note_hash() -} - -fn compute_nullifier(note: FieldNote, context: &mut PrivateContext) -> Field { - note.compute_nullifier(context) -} - -fn compute_nullifier_without_context(note: FieldNote) -> Field { - note.compute_nullifier_without_context() -} -fn get_header(note: FieldNote) -> NoteHeader { - note.header -} + fn get_header(self) -> NoteHeader { + self.header + } -fn set_header(note: &mut FieldNote, header: NoteHeader) { - note.set_header(header); + fn broadcast(self, context: &mut PrivateContext, slot: Field) { + assert( + false, "FieldNote does not support broadcast. Add it to PXE directly using the `.addNote` function." + ); + } } -fn broadcast(context: &mut PrivateContext, slot: Field, note: FieldNote) { - assert( - false, "FieldNote does not support broadcast. Add it to PXE directly using the `.addNote` function." - ); +impl FieldNote { + pub fn new(value: Field) -> Self { + FieldNote { + value, + header: NoteHeader::empty(), + } + } } -global FieldNoteMethods = NoteInterface { - deserialize, - serialize, - compute_note_hash, - compute_nullifier, - compute_nullifier_without_context, - get_header, - set_header, - broadcast, -}; diff --git a/yarn-project/aztec-nr/safe-math/Nargo.toml b/yarn-project/aztec-nr/safe-math/Nargo.toml index 6625680128f..aaa75e68977 100644 --- a/yarn-project/aztec-nr/safe-math/Nargo.toml +++ b/yarn-project/aztec-nr/safe-math/Nargo.toml @@ -4,4 +4,5 @@ authors = [""] compiler_version = ">=0.18.0" type = "lib" -[dependencies] \ No newline at end of file +[dependencies] +aztec = { path = "../aztec" } \ No newline at end of file diff --git a/yarn-project/aztec-nr/safe-math/src/lib.nr b/yarn-project/aztec-nr/safe-math/src/lib.nr index 7e29a39a85e..79dd63eb4b8 100644 --- a/yarn-project/aztec-nr/safe-math/src/lib.nr +++ b/yarn-project/aztec-nr/safe-math/src/lib.nr @@ -1,3 +1,3 @@ mod safe_u120; -use crate::safe_u120::SafeU120; +use crate::safe_u120::{SafeU120, SAFE_U120_SERIALIZED_LEN}; diff --git a/yarn-project/aztec-nr/safe-math/src/safe_u120.nr b/yarn-project/aztec-nr/safe-math/src/safe_u120.nr index d1ce40a01a6..4ef341bf839 100644 --- a/yarn-project/aztec-nr/safe-math/src/safe_u120.nr +++ b/yarn-project/aztec-nr/safe-math/src/safe_u120.nr @@ -1,4 +1,5 @@ use dep::std::cmp::Eq; +use dep::aztec::protocol_types::traits::{Deserialize, Serialize}; struct SafeU120 { value: u120, @@ -13,6 +14,21 @@ impl Eq for SafeU120 { } } +global SAFE_U120_SERIALIZED_LEN: Field = 1; + +impl Serialize for SafeU120 { + fn serialize(value: SafeU120) -> [Field; SAFE_U120_SERIALIZED_LEN] { + [value.value as Field] + } +} + +impl Deserialize for SafeU120 { + // This is safe when reading from storage IF only correct safeu120 was written to storage + fn deserialize(fields: [Field; SAFE_U120_SERIALIZED_LEN]) -> SafeU120 { + SafeU120 { value: fields[0] as u120 } + } +} + impl SafeU120 { pub fn min() -> Self { Self { diff --git a/yarn-project/aztec-nr/slow-updates-tree/src/slow_map.nr b/yarn-project/aztec-nr/slow-updates-tree/src/slow_map.nr index d2dc5c67830..065ab009197 100644 --- a/yarn-project/aztec-nr/slow-updates-tree/src/slow_map.nr +++ b/yarn-project/aztec-nr/slow-updates-tree/src/slow_map.nr @@ -1,7 +1,6 @@ use dep::aztec::context::{PrivateContext, PublicContext, Context}; -use dep::aztec::types::type_serialization::TypeSerializationInterface; use dep::aztec::oracle::storage::{storage_read, storage_write}; - +use dep::aztec::protocol_types::traits::{Serialize, Deserialize}; use dep::std::hash::pedersen_hash; use dep::std::merkle::compute_merkle_root; use dep::std::option::Option; @@ -20,20 +19,15 @@ struct Leaf { after: Field, } -fn serialize_leaf(leaf: Leaf) -> [Field; 3] { - [leaf.next_change, leaf.before, leaf.after] -} - -fn deserialize_leaf(serialized: [Field; 3]) -> Leaf { - Leaf { next_change: serialized[0], before: serialized[1], after: serialized[2] } +impl Serialize<3> for Leaf { + fn serialize(leaf: Leaf) -> [Field; 3] { + [leaf.next_change, leaf.before, leaf.after] + } } -impl Leaf { - fn serialize(self: Self) -> [Field; 3] { - serialize_leaf(self) - } - fn deserialize(serialized: [Field; 3]) -> Self { - deserialize_leaf(serialized) +impl Deserialize<3> for Leaf { + fn deserialize(serialized: [Field; 3]) -> Leaf { + Leaf { next_change: serialized[0], before: serialized[1], after: serialized[2] } } } @@ -117,7 +111,8 @@ impl SlowMap { } pub fn read_root(self: Self) -> Leaf { - storage_read(self.storage_slot, deserialize_leaf) + let fields = storage_read(self.storage_slot); + Leaf::deserialize(fields) } // Beware that the initial root could include much state that is not shown by the public storage! @@ -147,7 +142,8 @@ impl SlowMap { // docs:start:read_leaf_at pub fn read_leaf_at(self: Self, key: Field) -> Leaf { let derived_storage_slot = pedersen_hash([self.storage_slot, key]); - storage_read(derived_storage_slot, deserialize_leaf) + let fields = storage_read(derived_storage_slot); + Leaf::deserialize(fields) } // docs:end:read_leaf_at diff --git a/yarn-project/aztec-nr/value-note/src/balance_utils.nr b/yarn-project/aztec-nr/value-note/src/balance_utils.nr index cd577b97af7..74c6c79272e 100644 --- a/yarn-project/aztec-nr/value-note/src/balance_utils.nr +++ b/yarn-project/aztec-nr/value-note/src/balance_utils.nr @@ -5,11 +5,11 @@ use dep::aztec::note::{ use dep::aztec::state_vars::set::Set; use crate::value_note::{VALUE_NOTE_LEN, ValueNote}; -unconstrained pub fn get_balance(set: Set) -> Field { +unconstrained pub fn get_balance(set: Set) -> Field { get_balance_with_offset(set, 0) } -unconstrained pub fn get_balance_with_offset(set: Set, offset: u32) -> Field { +unconstrained pub fn get_balance_with_offset(set: Set, offset: u32) -> Field { let mut balance = 0; // docs:start:view_notes let options = NoteViewerOptions::new().set_offset(offset); diff --git a/yarn-project/aztec-nr/value-note/src/utils.nr b/yarn-project/aztec-nr/value-note/src/utils.nr index 277e0b114c3..d7ef7c948dc 100644 --- a/yarn-project/aztec-nr/value-note/src/utils.nr +++ b/yarn-project/aztec-nr/value-note/src/utils.nr @@ -17,7 +17,7 @@ pub fn create_note_getter_options_for_decreasing_balance(amount: Field) -> NoteG // Creates a new note for the recipient. // Inserts it to the recipient's set of notes. -pub fn increment(balance: Set, amount: Field, recipient: AztecAddress) { +pub fn increment(balance: Set, amount: Field, recipient: AztecAddress) { let mut note = ValueNote::new(amount, recipient); // Insert the new note to the owner's set of notes and emit the log if value is non-zero. balance.insert(&mut note, amount != 0); @@ -27,7 +27,7 @@ pub fn increment(balance: Set, amount: Field, recipie // Remove those notes. // If the value of the removed notes exceeds the requested `amount`, create a new note containing the excess value, so that exactly `amount` is removed. // Fail if the sum of the selected notes is less than the amount. -pub fn decrement(balance: Set, amount: Field, owner: AztecAddress) { +pub fn decrement(balance: Set, amount: Field, owner: AztecAddress) { let sum = decrement_by_at_most(balance, amount, owner); assert(sum == amount, "Balance too low"); } @@ -40,11 +40,7 @@ pub fn decrement(balance: Set, amount: Field, owner: // equal `amount`. // // It returns the decremented amount, which should be less than or equal to max_amount. -pub fn decrement_by_at_most( - balance: Set, - max_amount: Field, - owner: AztecAddress -) -> Field { +pub fn decrement_by_at_most(balance: Set, max_amount: Field, owner: AztecAddress) -> Field { let options = create_note_getter_options_for_decreasing_balance(max_amount); let opt_notes = balance.get_notes(options); @@ -68,11 +64,7 @@ pub fn decrement_by_at_most( // Removes the note from the owner's set of notes. // Returns the value of the destroyed note. -pub fn destroy_note( - balance: Set, - owner: AztecAddress, - note: ValueNote -) -> Field { +pub fn destroy_note(balance: Set, owner: AztecAddress, note: ValueNote) -> Field { // Ensure the note is actually owned by the owner (to prevent user from generating a valid proof while // spending someone else's notes). assert(note.owner.eq(owner)); diff --git a/yarn-project/aztec-nr/value-note/src/value_note.nr b/yarn-project/aztec-nr/value-note/src/value_note.nr index 4cffea0d4fa..9b2c4b9bc6f 100644 --- a/yarn-project/aztec-nr/value-note/src/value_note.nr +++ b/yarn-project/aztec-nr/value-note/src/value_note.nr @@ -1,9 +1,12 @@ use dep::aztec::{ - protocol_types::address::AztecAddress, + protocol_types::{ + address::AztecAddress, + traits::{Deserialize, Serialize} + }, note::{ note_header::NoteHeader, note_interface::NoteInterface, - utils::compute_note_hash_for_read_or_nullify, + utils::compute_note_hash_for_consumption, }, oracle::{ rand::rand, @@ -26,23 +29,14 @@ struct ValueNote { } // docs:end:value-note-def -impl ValueNote { - pub fn new(value: Field, owner: AztecAddress) -> Self { - let randomness = rand(); - let header = NoteHeader::empty(); - ValueNote { - value, - owner, - randomness, - header, - } - } - - pub fn serialize(self) -> [Field; VALUE_NOTE_LEN] { +impl Serialize for ValueNote { + fn serialize(self) -> [Field; VALUE_NOTE_LEN] { [self.value, self.owner.to_field(), self.randomness] } +} - pub fn deserialize(serialized_note: [Field; VALUE_NOTE_LEN]) -> Self { +impl Deserialize for ValueNote { + fn deserialize(serialized_note: [Field; VALUE_NOTE_LEN]) -> Self { ValueNote { value: serialized_note[0], owner: AztecAddress::from_field(serialized_note[1]), @@ -50,16 +44,19 @@ impl ValueNote { header: NoteHeader::empty(), } } +} - pub fn compute_note_hash(self) -> Field { +impl NoteInterface for ValueNote { + + fn compute_note_content_hash(self) -> Field { // TODO(#1205) Should use a non-zero generator index. pedersen_hash(self.serialize(),0) } // docs:start:nullifier - pub fn compute_nullifier(self, context: &mut PrivateContext) -> Field { - let note_hash_for_nullify = compute_note_hash_for_read_or_nullify(ValueNoteMethods, self); + fn compute_nullifier(self, context: &mut PrivateContext) -> Field { + let note_hash_for_nullify = compute_note_hash_for_consumption(self); let secret = context.request_nullifier_secret_key(self.owner); // TODO(#1205) Should use a non-zero generator index. pedersen_hash([ @@ -71,8 +68,8 @@ impl ValueNote { // docs:end:nullifier - pub fn compute_nullifier_without_context(self) -> Field { - let note_hash_for_nullify = compute_note_hash_for_read_or_nullify(ValueNoteMethods, self); + fn compute_nullifier_without_context(self) -> Field { + let note_hash_for_nullify = compute_note_hash_for_consumption(self); let secret = get_nullifier_secret_key(self.owner); // TODO(#1205) Should use a non-zero generator index. pedersen_hash([ @@ -82,12 +79,16 @@ impl ValueNote { ],0) } - pub fn set_header(&mut self, header: NoteHeader) { + fn set_header(&mut self, header: NoteHeader) { self.header = header; } + fn get_header(self) -> NoteHeader { + self.header + } + // Broadcasts the note as an encrypted log on L1. - pub fn broadcast(self, context: &mut PrivateContext, slot: Field) { + fn broadcast(self, context: &mut PrivateContext, slot: Field) { let encryption_pub_key = get_public_key(self.owner); emit_encrypted_log( context, @@ -99,46 +100,15 @@ impl ValueNote { } } -fn deserialize(serialized_note: [Field; VALUE_NOTE_LEN]) -> ValueNote { - ValueNote::deserialize(serialized_note) -} - -fn serialize(note: ValueNote) -> [Field; VALUE_NOTE_LEN] { - note.serialize() -} - -fn compute_note_hash(note: ValueNote) -> Field { - note.compute_note_hash() -} - -fn compute_nullifier(note: ValueNote, context: &mut PrivateContext) -> Field { - note.compute_nullifier(context) -} - -fn compute_nullifier_without_context(note: ValueNote) -> Field { - note.compute_nullifier_without_context() -} - -fn get_header(note: ValueNote) -> NoteHeader { - note.header -} - -fn set_header(note: &mut ValueNote, header: NoteHeader) { - note.set_header(header) -} - -// Broadcasts the note as an encrypted log on L1. -fn broadcast(context: &mut PrivateContext, slot: Field, note: ValueNote) { - note.broadcast(context, slot); +impl ValueNote { + pub fn new(value: Field, owner: AztecAddress) -> Self { + let randomness = rand(); + let header = NoteHeader::empty(); + ValueNote { + value, + owner, + randomness, + header, + } + } } - -global ValueNoteMethods = NoteInterface { - deserialize, - serialize, - compute_note_hash, - compute_nullifier, - compute_nullifier_without_context, - get_header, - set_header, - broadcast, -}; diff --git a/yarn-project/aztec-sandbox/src/bin/index.ts b/yarn-project/aztec-sandbox/src/bin/index.ts deleted file mode 100644 index 97aa2d790ea..00000000000 --- a/yarn-project/aztec-sandbox/src/bin/index.ts +++ /dev/null @@ -1,244 +0,0 @@ -#!/usr/bin/env -S node --no-warnings -import { deployInitialTestAccounts } from '@aztec/accounts/testing'; -import { createAztecNodeRpcServer, getConfigEnvVars as getNodeConfigEnvVars } from '@aztec/aztec-node'; -import { AccountManager, createAztecNodeClient } from '@aztec/aztec.js'; -import { NULL_KEY } from '@aztec/ethereum'; -import { init } from '@aztec/foundation/crypto'; -import { createStatusRouter, startHttpRpcServer } from '@aztec/foundation/json-rpc/server'; -import { createDebugLogger } from '@aztec/foundation/log'; -import { fileURLToPath } from '@aztec/foundation/url'; -import { BootstrapNode, getP2PConfigEnvVars } from '@aztec/p2p'; -import { GrumpkinScalar, PXEService, createPXERpcServer } from '@aztec/pxe'; - -import { lookup } from 'dns/promises'; -import { readFileSync } from 'fs'; -import http from 'http'; -import { dirname, resolve } from 'path'; -import { mnemonicToAccount } from 'viem/accounts'; - -import { setupFileDebugLog } from '../logging.js'; -import { MNEMONIC, createAztecNode, createAztecPXE, createSandbox, deployContractsToL1 } from '../sandbox.js'; -import { github, splash } from '../splash.js'; - -/** - * The mode in which the sandbox should be run. - */ -enum SandboxMode { - Sandbox = 'sandbox', - Node = 'node', - PXE = 'pxe', - P2PBootstrap = 'p2p-bootstrap', -} - -/** - * If we can successfully resolve 'host.docker.internal', then we are running in a container, and we should treat - * localhost as being host.docker.internal. - */ -const getLocalhost = () => - lookup('host.docker.internal') - .then(() => 'host.docker.internal') - .catch(() => 'localhost'); - -const LOCALHOST = await getLocalhost(); -const { - AZTEC_NODE_URL = `http://${LOCALHOST}:8079`, - AZTEC_NODE_PORT = 8079, - PXE_PORT = 8080, - MODE = 'sandbox', - TEST_ACCOUNTS = 'true', - DEPLOY_AZTEC_CONTRACTS = 'true', - API_PREFIX = '', -} = process.env; - -const logger = createDebugLogger(`aztec:${MODE}`); - -/** - * Creates the sandbox from provided config and deploys any initial L1 and L2 contracts - */ -async function createAndInitialiseSandbox(deployTestAccounts: boolean) { - const { aztecNodeConfig, node, pxe, stop } = await createSandbox(); - if (aztecNodeConfig.p2pEnabled) { - logger.info(`Not setting up test accounts as we are connecting to a network`); - return { - aztecNodeConfig, - pxe, - node, - stop, - accounts: [], - }; - } - let accounts; - if (deployTestAccounts) { - logger.info('Setting up test accounts...'); - accounts = await deployInitialTestAccounts(pxe); - } - return { - aztecNodeConfig, - pxe, - node, - stop, - accounts, - }; -} - -/** - * Create and start a new Aztec RPC HTTP Server - */ -async function main() { - const deployTestAccounts = TEST_ACCOUNTS === 'true'; - const deployAztecContracts = DEPLOY_AZTEC_CONTRACTS === 'true'; - - const mode = MODE as SandboxMode; - - const installSignalHandlers = (cb?: () => Promise) => { - const shutdown = async () => { - logger.info('Shutting down...'); - if (cb) { - await cb(); - } - process.exit(0); - }; - process.removeAllListeners('SIGINT'); - process.removeAllListeners('SIGTERM'); - process.once('SIGINT', shutdown); - process.once('SIGTERM', shutdown); - }; - - installSignalHandlers(); - - // Init crypto (bb.js). - await init(); - - const logStrings = []; - - const logPath = setupFileDebugLog(); - logger.info(`Debug logs will be written to ${logPath}`); - - // Get Sandbox version - const packageJsonPath = resolve(dirname(fileURLToPath(import.meta.url)), '../../package.json'); - const version = JSON.parse(readFileSync(packageJsonPath).toString()).version; - - // Code path for starting Sandbox - if (mode === SandboxMode.Sandbox) { - logger.info(`Setting up Aztec Sandbox v${version} please stand by...`); - - const { pxe, node, stop, accounts } = await createAndInitialiseSandbox(deployTestAccounts); - - // Create shutdown cleanup function - installSignalHandlers(stop); - - // Start Node and PXE JSON-RPC servers - startHttpRpcServer(node, createAztecNodeRpcServer, AZTEC_NODE_PORT); - logger.info(`Aztec Node JSON-RPC Server listening on port ${AZTEC_NODE_PORT}`); - startHttpRpcServer(pxe, createPXERpcServer, PXE_PORT); - logger.info(`PXE JSON-RPC Server listening on port ${PXE_PORT}`); - - // Log initial accounts details - if (accounts?.length) { - const accountLogStrings = await createAccountLogs(accounts, pxe); - logStrings.push(...accountLogStrings); - } - logStrings.push(`Aztec Sandbox v${version} is now ready for use!`); - } else if (mode === SandboxMode.Node) { - // Code path for starting Node only - const nodeConfig = getNodeConfigEnvVars(); - const hdAccount = mnemonicToAccount(MNEMONIC); - - // Deploy L1 Aztec Contracts if needed - if (deployAztecContracts) { - await deployContractsToL1(nodeConfig, hdAccount); - if (nodeConfig.publisherPrivateKey === NULL_KEY) { - const privKey = hdAccount.getHdKey().privateKey; - nodeConfig.publisherPrivateKey = `0x${Buffer.from(privKey!).toString('hex')}`; - } - } - - const node = await createAztecNode(nodeConfig); - installSignalHandlers(node.stop); - - const port = process.env.AZTEC_NODE_PORT || 8080; // Use standard 8080 when no PXE is running - const nodeRpcServer = createAztecNodeRpcServer(node); - const app = nodeRpcServer.getApp(API_PREFIX); - - // Add a /status endpoint - const statusRouter = createStatusRouter(API_PREFIX); - app.use(statusRouter.routes()); - app.use(statusRouter.allowedMethods()); - - // Start Node JSON-RPC server - const httpServer = http.createServer(app.callback()); - httpServer.listen(port); - - logStrings.push(`Aztec Node v${version} is now ready for use in port ${port}!`); - } else if (mode === SandboxMode.PXE) { - // Code path for starting PXE only - - // Create a Node client to connect to the PXE - const node = createAztecNodeClient(AZTEC_NODE_URL); - - const pxe = await createAztecPXE(node); - installSignalHandlers(pxe.stop); - - // Start PXE JSON-RPC server - startHttpRpcServer(pxe, createPXERpcServer, PXE_PORT); - - if (deployTestAccounts) { - logger.info('Setting up test accounts...'); - const accounts = await deployInitialTestAccounts(pxe); - const accountLogStrings = await createAccountLogs(accounts, pxe); - logStrings.push(...accountLogStrings); - } - - logStrings.push(`PXE v${version} is now ready for use in port ${PXE_PORT}!`); - } else if (mode === SandboxMode.P2PBootstrap) { - // Code path for starting a P2P bootstrap node - const config = getP2PConfigEnvVars(); - const bootstrapNode = new BootstrapNode(logger); - await bootstrapNode.start(config); - installSignalHandlers(bootstrapNode.stop); - logStrings.push( - `Bootstrap P2P node is now ready for use. Listening on: ${config.tcpListenIp}:${config.tcpListenPort}.`, - ); - } - - // Log startup details - logger.info(`${splash}\n${github}\n\n`.concat(...logStrings)); -} - -/** - * Creates logs for the initial accounts - * @param accounts - The initial accounts - * @param pxe - A PXE instance to get the registered accounts - * @returns A string array containing the initial accounts details - */ -async function createAccountLogs( - accounts: { - /** - * The account object - */ - account: AccountManager; - /** - * The private key of the account - */ - privateKey: GrumpkinScalar; - }[], - pxe: PXEService, -) { - const registeredAccounts = await pxe.getRegisteredAccounts(); - const accountLogStrings = [`Initial Accounts:\n\n`]; - for (const account of accounts) { - const completeAddress = account.account.getCompleteAddress(); - if (registeredAccounts.find(a => a.equals(completeAddress))) { - accountLogStrings.push(` Address: ${completeAddress.address.toString()}\n`); - accountLogStrings.push(` Partial Address: ${completeAddress.partialAddress.toString()}\n`); - accountLogStrings.push(` Private Key: ${account.privateKey.toString()}\n`); - accountLogStrings.push(` Public Key: ${completeAddress.publicKey.toString()}\n\n`); - } - } - return accountLogStrings; -} - -main().catch(err => { - logger.error(err); - process.exit(1); -}); diff --git a/yarn-project/aztec.js/src/account_manager/index.ts b/yarn-project/aztec.js/src/account_manager/index.ts index 69d819a31c0..e48e48dc161 100644 --- a/yarn-project/aztec.js/src/account_manager/index.ts +++ b/yarn-project/aztec.js/src/account_manager/index.ts @@ -1,6 +1,7 @@ import { CompleteAddress, GrumpkinPrivateKey, PXE } from '@aztec/circuit-types'; -import { EthAddress, PublicKey, getContractDeploymentInfo } from '@aztec/circuits.js'; +import { EthAddress, PublicKey, getContractInstanceFromDeployParams } from '@aztec/circuits.js'; import { Fr } from '@aztec/foundation/fields'; +import { ContractInstanceWithAddress } from '@aztec/types/contracts'; import { AccountContract } from '../account/contract.js'; import { Salt } from '../account/index.js'; @@ -18,9 +19,11 @@ import { DeployAccountSentTx } from './deploy_account_sent_tx.js'; */ export class AccountManager { /** Deployment salt for the account contract. */ - public readonly salt?: Fr; + public readonly salt: Fr; + // TODO(@spalladino): Does it make sense to have both completeAddress and instance? private completeAddress?: CompleteAddress; + private instance?: ContractInstanceWithAddress; private encryptionPublicKey?: PublicKey; private deployMethod?: DeployMethod; @@ -28,13 +31,9 @@ export class AccountManager { private pxe: PXE, private encryptionPrivateKey: GrumpkinPrivateKey, private accountContract: AccountContract, - saltOrAddress?: Salt | CompleteAddress, + salt?: Salt, ) { - if (saltOrAddress instanceof CompleteAddress) { - this.completeAddress = saltOrAddress; - } else { - this.salt = saltOrAddress ? new Fr(saltOrAddress) : Fr.random(); - } + this.salt = salt ? new Fr(salt) : Fr.random(); } protected getEncryptionPublicKey() { @@ -62,15 +61,30 @@ export class AccountManager { public getCompleteAddress(): CompleteAddress { if (!this.completeAddress) { const encryptionPublicKey = generatePublicKey(this.encryptionPrivateKey); - const contractDeploymentInfo = getContractDeploymentInfo( + const instance = this.getInstance(); + this.completeAddress = CompleteAddress.fromPublicKeyAndInstance(encryptionPublicKey, instance); + } + return this.completeAddress; + } + + /** + * Returns the contract instance definition associated with this account. + * Does not require the account to be deployed or registered. + * @returns ContractInstance instance. + */ + public getInstance(): ContractInstanceWithAddress { + if (!this.instance) { + const encryptionPublicKey = generatePublicKey(this.encryptionPrivateKey); + const portalAddress = EthAddress.ZERO; + this.instance = getContractInstanceFromDeployParams( this.accountContract.getContractArtifact(), this.accountContract.getDeploymentArgs(), - this.salt!, + this.salt, encryptionPublicKey, + portalAddress, ); - this.completeAddress = contractDeploymentInfo.completeAddress; } - return this.completeAddress; + return this.instance; } /** @@ -80,7 +94,7 @@ export class AccountManager { */ public async getWallet(): Promise { const entrypoint = await this.getAccount(); - return new AccountWalletWithPrivateKey(this.pxe, entrypoint, this.encryptionPrivateKey); + return new AccountWalletWithPrivateKey(this.pxe, entrypoint, this.encryptionPrivateKey, this.salt); } /** @@ -91,17 +105,15 @@ export class AccountManager { * @returns A Wallet instance. */ public async register(opts: WaitOpts = DefaultWaitOpts): Promise { - const address = await this.#register(); - + await this.#register(); await this.pxe.addContracts([ { artifact: this.accountContract.getContractArtifact(), - completeAddress: address, - portalContract: EthAddress.ZERO, + instance: this.getInstance(), }, ]); - await waitForAccountSynch(this.pxe, address, opts); + await waitForAccountSynch(this.pxe, this.getCompleteAddress(), opts); return this.getWallet(); } @@ -154,9 +166,8 @@ export class AccountManager { return this.getWallet(); } - async #register(): Promise { + async #register(): Promise { const completeAddress = this.getCompleteAddress(); await this.pxe.registerAccount(this.encryptionPrivateKey, completeAddress.partialAddress); - return completeAddress; } } diff --git a/yarn-project/aztec.js/src/contract/contract.test.ts b/yarn-project/aztec.js/src/contract/contract.test.ts index 3ea687ebd7e..d2b27f15070 100644 --- a/yarn-project/aztec.js/src/contract/contract.test.ts +++ b/yarn-project/aztec.js/src/contract/contract.test.ts @@ -6,6 +6,7 @@ import { NodeInfo } from '@aztec/types/interfaces'; import { MockProxy, mock } from 'jest-mock-extended'; +import { ContractInstanceWithAddress } from '../index.js'; import { Wallet } from '../wallet/index.js'; import { Contract } from './contract.js'; @@ -14,6 +15,7 @@ describe('Contract Class', () => { let resolvedExtendedContractData: ExtendedContractData; let contractAddress: AztecAddress; let account: CompleteAddress; + let contractInstance: ContractInstanceWithAddress; const mockTx = { type: 'Tx' } as any as Tx; const mockTxRequest = { type: 'TxRequest' } as any as TxExecutionRequest; @@ -103,10 +105,12 @@ describe('Contract Class', () => { resolvedExtendedContractData = ExtendedContractData.random(); contractAddress = resolvedExtendedContractData.contractData.contractAddress; account = CompleteAddress.random(); + contractInstance = { address: contractAddress } as ContractInstanceWithAddress; wallet = mock(); wallet.createTxExecutionRequest.mockResolvedValue(mockTxRequest); wallet.getExtendedContractData.mockResolvedValue(resolvedExtendedContractData); + wallet.getContractInstance.mockResolvedValue(contractInstance); wallet.sendTx.mockResolvedValue(mockTxHash); wallet.viewTx.mockResolvedValue(mockViewResultValue); wallet.getTxReceipt.mockResolvedValue(mockTxReceipt); diff --git a/yarn-project/aztec.js/src/contract/contract.ts b/yarn-project/aztec.js/src/contract/contract.ts index 61b7001628c..6f1ecc43ac2 100644 --- a/yarn-project/aztec.js/src/contract/contract.ts +++ b/yarn-project/aztec.js/src/contract/contract.ts @@ -24,16 +24,11 @@ export class Contract extends ContractBase { * @returns A promise that resolves to a new Contract instance. */ public static async at(address: AztecAddress, artifact: ContractArtifact, wallet: Wallet): Promise { - const extendedContractData = await wallet.getExtendedContractData(address); - if (extendedContractData === undefined) { - throw new Error('Contract ' + address.toString() + ' is not deployed'); + const instance = await wallet.getContractInstance(address); + if (instance === undefined) { + throw new Error(`Contract instance at ${address.toString()} has not been registered in the wallet's PXE`); } - return new Contract( - extendedContractData.getCompleteAddress(), - artifact, - wallet, - extendedContractData.contractData.portalContractAddress, - ); + return new Contract(instance, artifact, wallet); } /** diff --git a/yarn-project/aztec.js/src/contract/contract_base.ts b/yarn-project/aztec.js/src/contract/contract_base.ts index e64439e108d..d44555f57be 100644 --- a/yarn-project/aztec.js/src/contract/contract_base.ts +++ b/yarn-project/aztec.js/src/contract/contract_base.ts @@ -1,6 +1,7 @@ -import { CompleteAddress, DeployedContract } from '@aztec/circuit-types'; +import { DeployedContract } from '@aztec/circuit-types'; +import { computePartialAddress } from '@aztec/circuits.js'; import { ContractArtifact, FunctionArtifact, FunctionSelector } from '@aztec/foundation/abi'; -import { EthAddress } from '@aztec/foundation/eth-address'; +import { ContractInstanceWithAddress } from '@aztec/types/contracts'; import { Wallet } from '../account/index.js'; import { ContractFunctionInteraction } from './contract_function_interaction.js'; @@ -26,18 +27,16 @@ export class ContractBase implements DeployedContract { public methods: { [name: string]: ContractMethod } = {}; protected constructor( - /** The deployed contract's complete address. */ - public readonly completeAddress: CompleteAddress, + /** The deployed contract instance definition. */ + public readonly instance: ContractInstanceWithAddress, /** The Application Binary Interface for the contract. */ public readonly artifact: ContractArtifact, /** The wallet used for interacting with this contract. */ protected wallet: Wallet, - /** The portal contract address on L1, if any. */ - public readonly portalContract: EthAddress, ) { artifact.functions.forEach((f: FunctionArtifact) => { const interactionFunction = (...args: any[]) => { - return new ContractFunctionInteraction(this.wallet, this.completeAddress.address!, f, args); + return new ContractFunctionInteraction(this.wallet, this.instance.address, f, args); }; this.methods[f.name] = Object.assign(interactionFunction, { @@ -52,11 +51,14 @@ export class ContractBase implements DeployedContract { }); } - /** - * Address of the contract. - */ + /** Address of the contract. */ public get address() { - return this.completeAddress.address; + return this.instance.address; + } + + /** Partial address of the contract. */ + public get partialAddress() { + return computePartialAddress(this.instance); } /** @@ -65,6 +67,6 @@ export class ContractBase implements DeployedContract { * @returns A new contract instance. */ public withWallet(wallet: Wallet): this { - return new ContractBase(this.completeAddress, this.artifact, wallet, this.portalContract) as this; + return new ContractBase(this.instance, this.artifact, wallet) as this; } } diff --git a/yarn-project/aztec.js/src/contract/deploy_method.ts b/yarn-project/aztec.js/src/contract/deploy_method.ts index 42081173876..7cd77c1a017 100644 --- a/yarn-project/aztec.js/src/contract/deploy_method.ts +++ b/yarn-project/aztec.js/src/contract/deploy_method.ts @@ -1,15 +1,17 @@ import { PXE, PackedArguments, PublicKey, Tx, TxExecutionRequest } from '@aztec/circuit-types'; import { AztecAddress, - CompleteAddress, ContractDeploymentData, FunctionData, TxContext, - getContractDeploymentInfo, + computeContractAddressFromInstance, + computePartialAddress, + getContractInstanceFromDeployParams, } from '@aztec/circuits.js'; import { ContractArtifact, FunctionArtifact, encodeArguments } from '@aztec/foundation/abi'; import { EthAddress } from '@aztec/foundation/eth-address'; import { Fr } from '@aztec/foundation/fields'; +import { ContractInstanceWithAddress } from '@aztec/types/contracts'; import { Wallet } from '../account/index.js'; import { BaseContractInteraction, SendMethodOptions } from './base_contract_interaction.js'; @@ -37,8 +39,8 @@ export type DeployOptions = { * Extends the ContractFunctionInteraction class. */ export class DeployMethod extends BaseContractInteraction { - /** The complete address of the contract. */ - public completeAddress?: CompleteAddress = undefined; + /** The contract instance to be deployed. */ + public instance?: ContractInstanceWithAddress = undefined; /** Constructor function to call. */ private constructorArtifact: FunctionArtifact; @@ -73,17 +75,14 @@ export class DeployMethod extends Bas const { chainId, protocolVersion } = await this.pxe.getNodeInfo(); - const { completeAddress, constructorVkHash, functionTreeRoot } = getContractDeploymentInfo( - this.artifact, - this.args, - contractAddressSalt, - this.publicKey, - ); + const deployParams = [this.artifact, this.args, contractAddressSalt, this.publicKey, portalContract] as const; + const instance = getContractInstanceFromDeployParams(...deployParams); + const address = computeContractAddressFromInstance(instance); const contractDeploymentData = new ContractDeploymentData( this.publicKey, - constructorVkHash, - functionTreeRoot, + instance.initializationHash, + instance.contractClassId, contractAddressSalt, portalContract, ); @@ -98,7 +97,7 @@ export class DeployMethod extends Bas ); const args = encodeArguments(this.constructorArtifact, this.args); const functionData = FunctionData.fromAbi(this.constructorArtifact); - const execution = { args, functionData, to: completeAddress.address }; + const execution = { args, functionData, to: address }; const packedArguments = PackedArguments.fromArgs(execution.args); const txRequest = TxExecutionRequest.from({ @@ -111,10 +110,10 @@ export class DeployMethod extends Bas }); this.txRequest = txRequest; - this.completeAddress = completeAddress; + this.instance = instance; // TODO: Should we add the contracts to the DB here, or once the tx has been sent or mined? - await this.pxe.addContracts([{ artifact: this.artifact, completeAddress, portalContract }]); + await this.pxe.addContracts([{ artifact: this.artifact, instance }]); return this.txRequest; } @@ -129,7 +128,7 @@ export class DeployMethod extends Bas */ public send(options: DeployOptions = {}): DeploySentTx { const txHashPromise = super.send(options).getTxHash(); - return new DeploySentTx(this.pxe, txHashPromise, this.postDeployCtor, this.completeAddress); + return new DeploySentTx(this.pxe, txHashPromise, this.postDeployCtor, this.instance!); } /** @@ -140,4 +139,14 @@ export class DeployMethod extends Bas public simulate(options: DeployOptions): Promise { return super.simulate(options); } + + /** Return this deployment address. */ + public get address() { + return this.instance?.address; + } + + /** Returns the partial address for this deployment. */ + public get partialAddress() { + return this.instance && computePartialAddress(this.instance); + } } diff --git a/yarn-project/aztec.js/src/contract/deploy_sent_tx.ts b/yarn-project/aztec.js/src/contract/deploy_sent_tx.ts index dab687bdc61..a88be16ddf1 100644 --- a/yarn-project/aztec.js/src/contract/deploy_sent_tx.ts +++ b/yarn-project/aztec.js/src/contract/deploy_sent_tx.ts @@ -1,6 +1,7 @@ import { PXE, TxHash, TxReceipt } from '@aztec/circuit-types'; -import { AztecAddress, CompleteAddress } from '@aztec/circuits.js'; +import { AztecAddress } from '@aztec/circuits.js'; import { FieldsOf } from '@aztec/foundation/types'; +import { ContractInstanceWithAddress } from '@aztec/types/contracts'; import { Wallet } from '../account/index.js'; import { type Contract } from './contract.js'; @@ -27,11 +28,8 @@ export class DeploySentTx extends SentTx wallet: PXE | Wallet, txHashPromise: Promise, private postDeployCtor: (address: AztecAddress, wallet: Wallet) => Promise, - - /** - * The complete address of the deployed contract - */ - public completeContractAddress?: CompleteAddress, + /** The deployed contract instance */ + public instance?: ContractInstanceWithAddress, ) { super(wallet, txHashPromise); } @@ -53,11 +51,11 @@ export class DeploySentTx extends SentTx */ public async wait(opts?: DeployedWaitOpts): Promise> { const receipt = await super.wait(opts); - const contract = await this.getContractInstance(opts?.wallet, receipt.contractAddress); + const contract = await this.getContractObject(opts?.wallet, receipt.contractAddress); return { ...receipt, contract }; } - protected getContractInstance(wallet?: Wallet, address?: AztecAddress): Promise { + protected getContractObject(wallet?: Wallet, address?: AztecAddress): Promise { const isWallet = (pxe: PXE | Wallet): pxe is Wallet => !!(pxe as Wallet).createTxExecutionRequest; const contractWallet = wallet ?? (isWallet(this.pxe) && this.pxe); if (!contractWallet) { diff --git a/yarn-project/aztec.js/src/contract/index.ts b/yarn-project/aztec.js/src/contract/index.ts index eb3c16064d3..b7d5c4b6742 100644 --- a/yarn-project/aztec.js/src/contract/index.ts +++ b/yarn-project/aztec.js/src/contract/index.ts @@ -1,7 +1,7 @@ /** * The `contract` module provides utilities for deploying and interacting with contracts, based on a * `Wallet` instance and a compiled artifact. Refer to the {@link account} module for how to obtain a valid - * `Wallet` instance, and to the {@link https://docs.aztec.network/dev_docs/contracts/compiling | Compiling contracts} + * `Wallet` instance, and to the {@link https://docs.aztec.network/developers/contracts/compiling | Compiling contracts} * section of the documentation for how to generate an artifact out of your Noir source code. * * The {@link Contract} class is the main class in this module, and provides static methods for deploying @@ -30,7 +30,7 @@ * has synchronized its changes. * * @remarks If you are using typescript, consider using the - * {@link https://docs.aztec.network/dev_docs/contracts/compiling#typescript-interfaces | autogenerated type-safe interfaces} + * {@link https://docs.aztec.network/developers/contracts/compiling#typescript-interfaces | autogenerated type-safe interfaces} * for interacting with your contracts. * * @packageDocumentation diff --git a/yarn-project/aztec.js/src/index.ts b/yarn-project/aztec.js/src/index.ts index ff2378a23bf..b6b2baf595d 100644 --- a/yarn-project/aztec.js/src/index.ts +++ b/yarn-project/aztec.js/src/index.ts @@ -48,7 +48,7 @@ export { waitForAccountSynch, } from './utils/index.js'; -export { createPXEClient } from './pxe_client.js'; +export { createPXEClient } from './rpc_clients/index.js'; export { AuthWitnessProvider } from './account/index.js'; @@ -67,7 +67,8 @@ export { GlobalVariables, GrumpkinScalar, Point, - getContractDeploymentInfo, + getContractInstanceFromDeployParams, + getContractClassFromArtifact, } from '@aztec/circuits.js'; export { Grumpkin, Schnorr } from '@aztec/circuits.js/barretenberg'; @@ -106,10 +107,12 @@ export { emptyFunctionCall, merkleTreeIds, mockTx, + Comparator, } from '@aztec/circuit-types'; - export { NodeInfo } from '@aztec/types/interfaces'; +export { ContractInstanceWithAddress, ContractClassWithId } from '@aztec/types/contracts'; + // 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. diff --git a/yarn-project/aztec.js/src/rpc_clients/index.ts b/yarn-project/aztec.js/src/rpc_clients/index.ts new file mode 100644 index 00000000000..8dfa33a26d6 --- /dev/null +++ b/yarn-project/aztec.js/src/rpc_clients/index.ts @@ -0,0 +1 @@ +export * from './pxe_client.js'; diff --git a/yarn-project/aztec.js/src/pxe_client.ts b/yarn-project/aztec.js/src/rpc_clients/pxe_client.ts similarity index 94% rename from yarn-project/aztec.js/src/pxe_client.ts rename to yarn-project/aztec.js/src/rpc_clients/pxe_client.ts index 86ab174e769..296daead28b 100644 --- a/yarn-project/aztec.js/src/pxe_client.ts +++ b/yarn-project/aztec.js/src/rpc_clients/pxe_client.ts @@ -26,8 +26,6 @@ import { } from '@aztec/circuits.js'; import { createJsonRpcClient, makeFetch } from '@aztec/foundation/json-rpc/client'; -export { makeFetch } from '@aztec/foundation/json-rpc/client'; - /** * Creates a JSON-RPC client to remotely talk to PXE. * @param url - The URL of the PXE. @@ -59,5 +57,6 @@ export const createPXEClient = (url: string, fetch = makeFetch([1, 2, 3], true)) }, { Tx, TxReceipt, L2BlockL2Logs }, false, + 'pxe', fetch, ); diff --git a/yarn-project/aztec.js/src/utils/l1_contracts.ts b/yarn-project/aztec.js/src/utils/l1_contracts.ts index 9fd0e5d2b63..d0230c8f329 100644 --- a/yarn-project/aztec.js/src/utils/l1_contracts.ts +++ b/yarn-project/aztec.js/src/utils/l1_contracts.ts @@ -1,7 +1,7 @@ import { L1ContractAddresses } from '@aztec/ethereum'; import { retryUntil } from '@aztec/foundation/retry'; -import { createPXEClient } from '../pxe_client.js'; +import { createPXEClient } from '../rpc_clients/index.js'; export const getL1ContractAddresses = async (url: string): Promise => { const pxeClient = createPXEClient(url); diff --git a/yarn-project/aztec.js/src/wallet/account_wallet_with_private_key.ts b/yarn-project/aztec.js/src/wallet/account_wallet_with_private_key.ts index 71f17ba891a..645db1cf5db 100644 --- a/yarn-project/aztec.js/src/wallet/account_wallet_with_private_key.ts +++ b/yarn-project/aztec.js/src/wallet/account_wallet_with_private_key.ts @@ -1,6 +1,7 @@ import { PXE } from '@aztec/circuit-types'; import { GrumpkinPrivateKey } from '@aztec/circuits.js'; +import { Salt } from '../account/index.js'; import { AccountInterface } from '../account/interface.js'; import { AccountWallet } from './account_wallet.js'; @@ -10,7 +11,13 @@ import { AccountWallet } from './account_wallet.js'; * an account to another pxe. */ export class AccountWalletWithPrivateKey extends AccountWallet { - constructor(pxe: PXE, account: AccountInterface, private encryptionPrivateKey: GrumpkinPrivateKey) { + constructor( + pxe: PXE, + account: AccountInterface, + private encryptionPrivateKey: GrumpkinPrivateKey, + /** Deployment salt for this account contract. */ + public readonly salt: Salt, + ) { super(pxe, account); } diff --git a/yarn-project/aztec.js/src/wallet/base_wallet.ts b/yarn-project/aztec.js/src/wallet/base_wallet.ts index af00daacb55..4a5f49f9c74 100644 --- a/yarn-project/aztec.js/src/wallet/base_wallet.ts +++ b/yarn-project/aztec.js/src/wallet/base_wallet.ts @@ -18,6 +18,7 @@ import { TxReceipt, } from '@aztec/circuit-types'; import { AztecAddress, CompleteAddress, Fr, GrumpkinPrivateKey, PartialAddress } from '@aztec/circuits.js'; +import { ContractInstanceWithAddress } from '@aztec/types/contracts'; import { NodeInfo } from '@aztec/types/interfaces'; import { Wallet } from '../account/wallet.js'; @@ -34,10 +35,12 @@ export abstract class BaseWallet implements Wallet { abstract createAuthWitness(message: Fr): Promise; + getContractInstance(address: AztecAddress): Promise { + return this.pxe.getContractInstance(address); + } addCapsule(capsule: Fr[]): Promise { return this.pxe.addCapsule(capsule); } - registerAccount(privKey: GrumpkinPrivateKey, partialAddress: PartialAddress): Promise { return this.pxe.registerAccount(privKey, partialAddress); } diff --git a/yarn-project/aztec-sandbox/.eslintrc.cjs b/yarn-project/aztec/.eslintrc.cjs similarity index 100% rename from yarn-project/aztec-sandbox/.eslintrc.cjs rename to yarn-project/aztec/.eslintrc.cjs diff --git a/yarn-project/aztec-sandbox/.gitignore b/yarn-project/aztec/.gitignore similarity index 100% rename from yarn-project/aztec-sandbox/.gitignore rename to yarn-project/aztec/.gitignore diff --git a/yarn-project/aztec-sandbox/Dockerfile b/yarn-project/aztec/Dockerfile similarity index 85% rename from yarn-project/aztec-sandbox/Dockerfile rename to yarn-project/aztec/Dockerfile index afdfb2094e1..e8a8e8d45d1 100644 --- a/yarn-project/aztec-sandbox/Dockerfile +++ b/yarn-project/aztec/Dockerfile @@ -1,6 +1,6 @@ FROM 278380418400.dkr.ecr.eu-west-2.amazonaws.com/yarn-project-prod AS yarn-project-prod -ENTRYPOINT ["node", "--no-warnings", "/usr/src/yarn-project/aztec-sandbox/dest/bin/index.js"] -EXPOSE 8079 8080 +ENTRYPOINT ["node", "--no-warnings", "/usr/src/yarn-project/aztec/dest/bin/index.js"] +EXPOSE 8080 # The version has been updated in yarn-project-prod. # Adding COMMIT_TAG here to rebuild versioned image. diff --git a/yarn-project/aztec-sandbox/README.md b/yarn-project/aztec/README.md similarity index 72% rename from yarn-project/aztec-sandbox/README.md rename to yarn-project/aztec/README.md index b901d0b8b9d..2c01123c7b4 100644 --- a/yarn-project/aztec-sandbox/README.md +++ b/yarn-project/aztec/README.md @@ -1,6 +1,6 @@ -# aztec-sandbox +# aztec -Aztec Sandbox is a package that allows for a simple development environment on Aztec stack. It creates a Private eXecution Environment (PXE) that listens for HTTP requests on `localhost:8080` by default. When started, it deploys all necessary L1 Aztec contracts and then starts listening for RPC requests. +Aztec is a package that allows for a simple development environment on Aztec stack. It creates a Private eXecution Environment (PXE) that listens for HTTP requests on `localhost:8080` by default. When started, it deploys all necessary L1 Aztec contracts and then starts listening for RPC requests. ## How to run: @@ -38,7 +38,7 @@ Before running locally you'll need to: - `yarn build` And you should be good to go! -From the `aztec-sandbox` directory, you can run the two existing examples: +From the `aztec` directory, you can run the two existing examples: - Deployment, mint and transfer on an Aztec Private Token - `yarn run:example:token` @@ -54,4 +54,4 @@ export FORK_URL= ## Publishing This package is set-up to be published on dockerhub by CI whenever there's a tagged release on `master` branch. -It's published under the tags `aztecprotocol/aztec-sandbox:latest` & `aztecprotocol/aztec-sandbox:`. +It's published under the tags `aztecprotocol/aztec:latest` & `aztecprotocol/aztec:`. diff --git a/yarn-project/aztec-sandbox/docker-compose.yml b/yarn-project/aztec/docker-compose.yml similarity index 85% rename from yarn-project/aztec-sandbox/docker-compose.yml rename to yarn-project/aztec/docker-compose.yml index 2741f74c069..84397ba70b7 100644 --- a/yarn-project/aztec-sandbox/docker-compose.yml +++ b/yarn-project/aztec/docker-compose.yml @@ -7,7 +7,7 @@ services: if [ -n "$FORK_BLOCK_NUMBER" ] && [ -n "$FORK_URL" ]; then exec anvil -p 8545 --host 0.0.0.0 --chain-id 31337 --silent --fork-url "$FORK_URL" --fork-block-number "$FORK_BLOCK_NUMBER" elif [ -n "$FORK_URL" ]; then - exec anvil -p 8545 --host 0.0.0.0 --chain-id 31337 --silent --fork-url "$FORK_URL" + exec anvil -p 8545 --host 0.0.0.0 --chain-id 31337 --silent --fork-url "$FORK_URL" else exec anvil -p 8545 --host 0.0.0.0 --chain-id 31337 --silent fi' @@ -15,9 +15,8 @@ services: - '${SANDBOX_ANVIL_PORT:-8545}:8545' aztec: - image: 'aztecprotocol/aztec-sandbox:${SANDBOX_VERSION:-latest}' + image: 'aztecprotocol/aztec:${SANDBOX_VERSION:-latest}' ports: - - '${SANDBOX_AZTEC_NODE_PORT:-8079}:8079' - '${SANDBOX_PXE_PORT:-8080}:8080' environment: DEBUG: # Loaded from the user shell if explicitly set @@ -31,4 +30,4 @@ services: PXE_BLOCK_POLLING_INTERVAL_MS: 50 ARCHIVER_VIEM_POLLING_INTERVAL_MS: 500 volumes: - - ./log:/usr/src/yarn-project/aztec-sandbox/log:rw + - ./log:/usr/src/yarn-project/aztec/log:rw diff --git a/yarn-project/aztec-sandbox/package.json b/yarn-project/aztec/package.json similarity index 92% rename from yarn-project/aztec-sandbox/package.json rename to yarn-project/aztec/package.json index 30d0f41da34..fbc0b5dcb20 100644 --- a/yarn-project/aztec-sandbox/package.json +++ b/yarn-project/aztec/package.json @@ -1,5 +1,5 @@ { - "name": "@aztec/aztec-sandbox", + "name": "@aztec/aztec", "version": "0.1.0", "type": "module", "exports": { @@ -10,7 +10,7 @@ "entryPoints": [ "./src/index.ts" ], - "name": "Sandbox", + "name": "Aztec Packages", "tsconfig": "./tsconfig.json" }, "scripts": { @@ -28,18 +28,21 @@ ], "dependencies": { "@aztec/accounts": "workspace:^", + "@aztec/archiver": "workspace:^", "@aztec/aztec-node": "workspace:^", "@aztec/aztec.js": "workspace:^", "@aztec/circuit-types": "workspace:^", "@aztec/circuits.js": "workspace:^", "@aztec/ethereum": "workspace:^", "@aztec/foundation": "workspace:^", + "@aztec/kv-store": "workspace:^", "@aztec/l1-artifacts": "workspace:^", "@aztec/noir-compiler": "workspace:^", "@aztec/noir-contracts": "workspace:^", "@aztec/p2p": "workspace:^", "@aztec/pxe": "workspace:^", "abitype": "^0.8.11", + "commander": "^11.1.0", "koa": "^2.14.2", "koa-router": "^12.0.0", "viem": "^1.2.5", diff --git a/yarn-project/aztec/src/aztec_client.ts b/yarn-project/aztec/src/aztec_client.ts new file mode 100644 index 00000000000..e69de29bb2d diff --git a/yarn-project/aztec/src/bin/index.ts b/yarn-project/aztec/src/bin/index.ts new file mode 100644 index 00000000000..b463e48c895 --- /dev/null +++ b/yarn-project/aztec/src/bin/index.ts @@ -0,0 +1,66 @@ +import { deployInitialTestAccounts } from '@aztec/accounts/testing'; +import { createAztecNodeRpcServer } from '@aztec/aztec-node'; +import { fileURLToPath } from '@aztec/aztec.js'; +import { createNamespacedJsonRpcServer } from '@aztec/foundation/json-rpc/server'; +import { createConsoleLogger, createDebugLogger } from '@aztec/foundation/log'; +import { createPXERpcServer } from '@aztec/pxe'; + +import { readFileSync } from 'fs'; +import http from 'http'; +import { dirname, resolve } from 'path'; + +import { getProgram } from '../cli/index.js'; +import { createAccountLogs, installSignalHandlers } from '../cli/util.js'; +import { createSandbox } from '../sandbox.js'; +import { github, splash } from '../splash.js'; + +const userLog = createConsoleLogger(); +const debugLogger = createDebugLogger('aztec:cli'); + +const packageJsonPath = resolve(dirname(fileURLToPath(import.meta.url)), '../../package.json'); +const cliVersion: string = JSON.parse(readFileSync(packageJsonPath).toString()).version; + +const { TEST_ACCOUNTS = 'true', PORT = '8080' } = process.env; + +/** CLI & full node main entrypoint */ +async function main() { + if (process.argv.length > 2) { + // If CLI arguments were provided, run the CLI program. + const cliProgram = getProgram(userLog, debugLogger); + await cliProgram.parseAsync(process.argv); + } else { + // If no CLI arguments were provided, run aztec full node for sandbox usage. + userLog(`${splash}\n${github}\n\n`); + userLog(`Setting up Aztec Sandbox v${cliVersion}, please stand by...`); + const { aztecNodeConfig, node, pxe, stop } = await createSandbox(); + installSignalHandlers(userLog, [stop]); + + // Deploy test accounts by default + if (TEST_ACCOUNTS === 'true') { + if (aztecNodeConfig.p2pEnabled) { + userLog(`Not setting up test accounts as we are connecting to a network`); + } else { + userLog('Setting up test accounts...'); + const accounts = await deployInitialTestAccounts(pxe); + const accLogs = await createAccountLogs(accounts, pxe); + userLog(accLogs.join('')); + } + } + + // Start Node and PXE JSON-RPC server + const nodeServer = createAztecNodeRpcServer(node); + const pxeServer = createPXERpcServer(pxe); + const rpcServer = createNamespacedJsonRpcServer([{ node: nodeServer }, { pxe: pxeServer }], debugLogger); + + const app = rpcServer.getApp(); + const httpServer = http.createServer(app.callback()); + httpServer.listen(PORT); + userLog(`Aztec Server listening on port ${PORT}`); + } +} + +main().catch(err => { + debugLogger(`Error in command execution`); + debugLogger(err); + process.exit(1); +}); diff --git a/yarn-project/aztec/src/cli/cli.ts b/yarn-project/aztec/src/cli/cli.ts new file mode 100644 index 00000000000..b4620842cbd --- /dev/null +++ b/yarn-project/aztec/src/cli/cli.ts @@ -0,0 +1,70 @@ +import { fileURLToPath } from '@aztec/aztec.js'; +import { ServerList, createNamespacedJsonRpcServer } from '@aztec/foundation/json-rpc/server'; +import { DebugLogger, LogFn } from '@aztec/foundation/log'; + +import { Command } from 'commander'; +import { readFileSync } from 'fs'; +import http from 'http'; +import { dirname, resolve } from 'path'; + +import { cliTexts } from './texts.js'; +import { installSignalHandlers } from './util.js'; + +const { AZTEC_PORT = '8080' } = process.env; + +/** + * Returns commander program that defines the 'aztec' command line interface. + * @param userLog - log function for logging user output. + * @param debugLogger - logger for logging debug messages. + */ +export function getProgram(userLog: LogFn, debugLogger: DebugLogger): Command { + const program = new Command(); + + const packageJsonPath = resolve(dirname(fileURLToPath(import.meta.url)), '../../package.json'); + const cliVersion: string = JSON.parse(readFileSync(packageJsonPath).toString()).version; + + program.name('aztec').description('Aztec command line interface').version(cliVersion); + + // Start Aztec modules with options + program + .command('start') + .description( + 'Starts Aztec modules. Options for each module can be set as key-value pairs (e.g. "option1=value1,option2=value2") or as environment variables.', + ) + .option('-p, --port ', 'Port to run Aztec on.', AZTEC_PORT) + .option('-n, --node [options]', cliTexts.node) + .option('-px, --pxe [options]', cliTexts.pxe) + .option('-a, --archiver [options]', cliTexts.archiver) + .option('-s, --sequencer [options]', cliTexts.sequencer) + .option('-p2p, --p2p-bootstrap [options]', cliTexts.p2pBootstrap) + .action(async options => { + // list of 'stop' functions to call when process ends + const signalHandlers: Array<() => Promise> = []; + let services: ServerList = []; + + // Start Aztec Node + if (options.node) { + const { startNode } = await import('./cmds/start_node.js'); + services = await startNode(options, signalHandlers, userLog); + } else if (options.pxe) { + const { startPXE } = await import('./cmds/start_pxe.js'); + services = await startPXE(options, signalHandlers, userLog); + } else if (options.archiver) { + const { startArchiver } = await import('./cmds/start_archiver.js'); + services = await startArchiver(options, signalHandlers); + } else if (options.p2pBootstrap) { + const { startP2PBootstrap } = await import('./cmds/start_p2p_bootstrap.js'); + await startP2PBootstrap(options, signalHandlers, userLog, debugLogger); + } + if (services.length) { + const rpcServer = createNamespacedJsonRpcServer(services, debugLogger); + + const app = rpcServer.getApp(); + const httpServer = http.createServer(app.callback()); + httpServer.listen(options.port); + userLog(`Aztec Server listening on port ${options.port}`); + } + installSignalHandlers(debugLogger, signalHandlers); + }); + return program; +} diff --git a/yarn-project/aztec/src/cli/cmds/start_archiver.ts b/yarn-project/aztec/src/cli/cmds/start_archiver.ts new file mode 100644 index 00000000000..0a07c96ae6c --- /dev/null +++ b/yarn-project/aztec/src/cli/cmds/start_archiver.ts @@ -0,0 +1,31 @@ +import { + Archiver, + ArchiverConfig, + KVArchiverDataStore, + createArchiverRpcServer, + getConfigEnvVars as getArchiverConfigEnvVars, +} from '@aztec/archiver'; +import { ServerList } from '@aztec/foundation/json-rpc/server'; +import { AztecLmdbStore } from '@aztec/kv-store'; + +import { mergeEnvVarsAndCliOptions, parseModuleOptions } from '../util.js'; + +export const startArchiver = async (options: any, signalHandlers: (() => Promise)[]) => { + const services: ServerList = []; + // Start a standalone archiver. + // get env vars first + const archiverConfigEnvVars = getArchiverConfigEnvVars(); + // get config from options + const archiverCliOptions = parseModuleOptions(options.archiver); + // merge env vars and cli options + const archiverConfig = mergeEnvVarsAndCliOptions(archiverConfigEnvVars, archiverCliOptions, true); + + const store = await AztecLmdbStore.open(archiverConfig.l1Contracts.rollupAddress, archiverConfig.dataDirectory); + const archiverStore = new KVArchiverDataStore(store, archiverConfig.maxLogs); + + const archiver = await Archiver.createAndSync(archiverConfig, archiverStore, true); + const archiverServer = createArchiverRpcServer(archiver); + services.push({ archiver: archiverServer }); + signalHandlers.push(archiver.stop); + return services; +}; diff --git a/yarn-project/aztec/src/cli/cmds/start_node.ts b/yarn-project/aztec/src/cli/cmds/start_node.ts new file mode 100644 index 00000000000..7a4a0d3623b --- /dev/null +++ b/yarn-project/aztec/src/cli/cmds/start_node.ts @@ -0,0 +1,87 @@ +import { AztecNodeConfig, createAztecNodeRpcServer, getConfigEnvVars as getNodeConfigEnvVars } from '@aztec/aztec-node'; +import { NULL_KEY } from '@aztec/ethereum'; +import { ServerList } from '@aztec/foundation/json-rpc/server'; +import { LogFn } from '@aztec/foundation/log'; +import { PXEServiceConfig, createPXERpcServer, getPXEServiceConfig } from '@aztec/pxe'; + +import { mnemonicToAccount, privateKeyToAccount } from 'viem/accounts'; + +import { MNEMONIC, createAztecNode, createAztecPXE, deployContractsToL1 } from '../../sandbox.js'; +import { mergeEnvVarsAndCliOptions, parseModuleOptions } from '../util.js'; + +const { DEPLOY_AZTEC_CONTRACTS } = process.env; + +export const startNode = async ( + options: any, + signalHandlers: (() => Promise)[], + userLog: LogFn, +): Promise => { + // Services that will be started in a single multi-rpc server + const services: ServerList = []; + // get env vars first + const aztecNodeConfigEnvVars = getNodeConfigEnvVars(); + // get config from options + const nodeCliOptions = parseModuleOptions(options.node); + // merge env vars and cli options + let nodeConfig = mergeEnvVarsAndCliOptions(aztecNodeConfigEnvVars, nodeCliOptions); + + // if no publisher private key, then use MNEMONIC + if (!options.archiver) { + // expect archiver url in node config + const archiverUrl = nodeCliOptions.archiverUrl; + if (!archiverUrl) { + userLog('Archiver Service URL is required to start Aztec Node without --archiver option'); + throw new Error('Archiver Service URL is required to start Aztec Node without --archiver option'); + } + nodeConfig.archiverUrl = archiverUrl; + } else { + const archiverCliOptions = parseModuleOptions(options.archiver); + nodeConfig = mergeEnvVarsAndCliOptions(aztecNodeConfigEnvVars, archiverCliOptions); + } + + // Deploy contracts if needed + if (nodeCliOptions.deployAztecContracts || DEPLOY_AZTEC_CONTRACTS === 'true') { + let account; + if (nodeConfig.publisherPrivateKey === NULL_KEY) { + account = mnemonicToAccount(MNEMONIC); + } else { + account = privateKeyToAccount(nodeConfig.publisherPrivateKey); + } + await deployContractsToL1(nodeConfig, account); + } + + if (!options.sequencer) { + nodeConfig.disableSequencer = true; + } else if (nodeConfig.publisherPrivateKey === NULL_KEY) { + // If we have a sequencer, ensure there's a publisher private key set. + const hdAccount = mnemonicToAccount(MNEMONIC); + const privKey = hdAccount.getHdKey().privateKey; + nodeConfig.publisherPrivateKey = `0x${Buffer.from(privKey!).toString('hex')}`; + } + + // Create and start Aztec Node. + const node = await createAztecNode(nodeConfig); + const nodeServer = createAztecNodeRpcServer(node); + + // Add node to services list + services.push({ node: nodeServer }); + + // Add node stop function to signal handlers + signalHandlers.push(node.stop); + + // Create a PXE client that connects to the node. + if (options.pxe) { + const pxeCliOptions = parseModuleOptions(options.pxe); + const pxeConfig = mergeEnvVarsAndCliOptions(getPXEServiceConfig(), pxeCliOptions); + const pxe = await createAztecPXE(node, pxeConfig); + const pxeServer = createPXERpcServer(pxe); + + // Add PXE to services list + services.push({ pxe: pxeServer }); + + // Add PXE stop function to signal handlers + signalHandlers.push(pxe.stop); + } + + return services; +}; diff --git a/yarn-project/aztec/src/cli/cmds/start_p2p_bootstrap.ts b/yarn-project/aztec/src/cli/cmds/start_p2p_bootstrap.ts new file mode 100644 index 00000000000..2fedcfd5645 --- /dev/null +++ b/yarn-project/aztec/src/cli/cmds/start_p2p_bootstrap.ts @@ -0,0 +1,21 @@ +import { DebugLogger } from '@aztec/aztec.js'; +import { LogFn } from '@aztec/foundation/log'; +import { BootstrapNode, P2PConfig, getP2PConfigEnvVars } from '@aztec/p2p'; + +import { mergeEnvVarsAndCliOptions, parseModuleOptions } from '../util.js'; + +export const startP2PBootstrap = async ( + options: any, + signalHandlers: (() => Promise)[], + userLog: LogFn, + debugLogger: DebugLogger, +) => { + // Start a P2P bootstrap node. + const envVars = getP2PConfigEnvVars(); + const cliOptions = parseModuleOptions(options.p2pBootstrap); + const bootstrapNode = new BootstrapNode(debugLogger); + const config = mergeEnvVarsAndCliOptions(envVars, cliOptions); + await bootstrapNode.start(config); + userLog(`P2P bootstrap node started on ${config.tcpListenIp}:${config.tcpListenPort}`); + signalHandlers.push(bootstrapNode.stop); +}; diff --git a/yarn-project/aztec/src/cli/cmds/start_pxe.ts b/yarn-project/aztec/src/cli/cmds/start_pxe.ts new file mode 100644 index 00000000000..b97cc431cc8 --- /dev/null +++ b/yarn-project/aztec/src/cli/cmds/start_pxe.ts @@ -0,0 +1,38 @@ +import { createAztecNodeClient } from '@aztec/circuit-types'; +import { ServerList } from '@aztec/foundation/json-rpc/server'; +import { LogFn } from '@aztec/foundation/log'; +import { PXEServiceConfig, createPXERpcServer, createPXEService, getPXEServiceConfig } from '@aztec/pxe'; + +import { mergeEnvVarsAndCliOptions, parseModuleOptions } from '../util.js'; + +const { AZTEC_NODE_URL } = process.env; + +export const startPXE = async (options: any, signalHandlers: (() => Promise)[], userLog: LogFn) => { + // Services that will be started in a single multi-rpc server + const services: ServerList = []; + // Starting a PXE with a remote node. + // get env vars first + const pxeConfigEnvVars = getPXEServiceConfig(); + // get config from options + const pxeCliOptions = parseModuleOptions(options.pxe); + + // Determine node url from options or env vars + const nodeUrl = pxeCliOptions.nodeUrl || AZTEC_NODE_URL; + // throw if no Aztec Node URL is provided + if (!nodeUrl) { + userLog('Aztec Node URL (nodeUrl | AZTEC_NODE_URL) option is required to start PXE without --node option'); + throw new Error('Aztec Node URL (nodeUrl | AZTEC_NODE_URL) option is required to start PXE without --node option'); + } + + // merge env vars and cli options + const pxeConfig = mergeEnvVarsAndCliOptions(pxeConfigEnvVars, pxeCliOptions); + + // create a node client + const node = createAztecNodeClient(nodeUrl); + + const pxe = await createPXEService(node, pxeConfig); + const pxeServer = createPXERpcServer(pxe); + services.push({ pxe: pxeServer }); + signalHandlers.push(pxe.stop); + return services; +}; diff --git a/yarn-project/aztec/src/cli/index.ts b/yarn-project/aztec/src/cli/index.ts new file mode 100644 index 00000000000..c01c2dcf0ba --- /dev/null +++ b/yarn-project/aztec/src/cli/index.ts @@ -0,0 +1 @@ +export * from './cli.js'; diff --git a/yarn-project/aztec/src/cli/texts.ts b/yarn-project/aztec/src/cli/texts.ts new file mode 100644 index 00000000000..2536e37637d --- /dev/null +++ b/yarn-project/aztec/src/cli/texts.ts @@ -0,0 +1,71 @@ +const contractAddresses = + 'Aztec Contract Addresses:\n' + + 'rollupAddress:ROLLUP_CONTRACT_ADDRESS - string - The deployed L1 rollup contract address.\n' + + 'registryAddress:REGISTRY_CONTRACT_ADDRESS - string - The deployed L1 registry contract address.\n' + + 'inboxAddress:INBOX_CONTRACT_ADDRESS - string - The deployed L1 inbox contract address.\n' + + 'outboxAddress:OUTBOX_CONTRACT_ADDRESS - string - The deployed L1 outbox contract address.\n' + + 'contractDeploymentEmitterAddress:CONTRACT_DEPLOYMENT_EMITTER_ADDRESS - string - The deployed L1 contract deployment emitter contract address.\n'; +const p2pOptions = + 'p2pBlockCheckIntervalMS:P2P_BLOCK_CHECK_INTERVAL_MS - number - The frequency in which to check for blocks. Default: 100\n' + + 'p2pL2QueueSize:P2P_L2_QUEUE_SIZE - number - Size of queue of L2 blocks to store. Default: 1000\n' + + 'tcpListenPort:TCP_LISTEN_PORT - number - The tcp port on which the P2P service should listen for connections. Default: 40400\n' + + 'tcpListenIp:TCP_LISTEN_IP - string - The tcp IP on which the P2P service should listen for connections. Default: 0.0.0.0\n' + + 'peerIdPrivateKey:PEER_ID_PRIVATE_KEY - string - An optional peer id private key. If blank, will generate a random key.\n' + + 'bootstrapNodes:BOOTSTRAP_NODES - string - A list of bootstrap peers to connect to.\n' + + 'announceHostname:P2P_ANNOUNCE_HOSTNAME - string - P2P Hostname to announce.\n' + + 'announcePort:P2P_ANNOUNCE_PORT - number - P2P Port to announce.\n' + + 'clientKADRouting:P2P_KAD_CLIENT - boolean - Optional specification to run as a client in the Kademlia routing protocol. Default: false\n' + + 'enableNat:P2P_NAT_ENABLED - boolean - Whether to enable NAT from libp2p (ignored for bootstrap node). Default: false\n' + + 'minPeerCount:P2P_MIN_PEERS - number - The minimum number of peers to connect to. Default: 10\n' + + 'maxPeerCount:P2P_MAX_PEERS - number - The maximum number of peers to connect to. Default: 100\n'; + +export const cliTexts = { + node: + 'Starts Aztec Node with options.\n' + + 'Available options are listed below as cliProperty:ENV_VARIABLE_NAME.\n' + + 'rcpUrl:ETHEREUM_HOST - string - The host of the Ethereum node to connect to. Default: http://localhost:8545\n' + + 'archiverUrl:ARCHIVER_URL - string - A URL for an archiver service that the node will use.\n' + + 'p2pEnabled:P2P_ENABLED - boolean - A flag dictating whether the P2P subsystem should be enabled.\n\n' + + 'dataDirectory:DATA_DIRECTORY - string - Where to store node data. If not set, will store temporarily.\n' + + 'deployAztecContracts:DEPLOY_AZTEC_CONTRACTS - boolean - A flag dictating whether to deploy the Aztec contracts. Default: false\n' + + 'l2QueueSize:L2_QUEUE_SIZE - number - Size of queue of L2 blocks to store. Default: 1000\n' + + 'worldStateBlockCheckIntervalMS:WS_BLOCK_CHECK_INTERVAL_MS - number - The frequency in which to check for blocks in ms. Default: 100\n' + + // Contract Addresses + contractAddresses + + // P2P Options + 'When P2P is enabled, the following options are available:\n' + + p2pOptions, + pxe: + 'Starts a PXE with options. If started additionally to --node, the PXE will attach to that node.' + + 'Available options are listed below as cliProperty:ENV_VARIABLE_NAME.\n' + + 'nodeUrl:AZTEC_NODE_URL - string - The URL of the Aztec Node to connect to.\n' + + 'port:PXE_PORT - number - The port on which the PXE should listen for connections. Default: 79\n' + + 'l2BlockPollingIntervalMS:PXE_BLOCK_POLLING_INTERVAL_MS - number - The frequency in which to check for blocks in ms. Default: 1000\n' + + 'l2StartingBlock:PXE_L2_STARTING_BLOCK - number - The block number from which to start polling. Default: 1\n' + + 'dataDirectory:PXE_DATA_DIRECTORY - string - Where to store PXE data. If not set, will store temporarily.\n', + archiver: + 'Starts an Archiver with options. If started additionally to --node, the Archiver will attach to that node.' + + 'Available options are listed below as cliProperty:ENV_VARIABLE_NAME.\n' + + 'rcpUrl:ETHEREUM_HOST - string - The host of the Ethereum node to connect to. Default: http://localhost:8545\n' + + 'apiKey:API_KEY - string - The key for the ethereum node if necessary.\n' + + 'archiverPollingIntervalMS:ARCHIVER_POLLING_INTERVAL_MS - number - The polling interval in ms for retrieving new L2 blocks and encrypted logs. Default: 1000\n' + + 'viemPollingIntervalMS:ARCHIVER_VIEM_POLLING_INTERVAL_MS - number - The polling interval viem uses in ms. Default: 1000\n' + + 'dataDirectory:DATA_DIRECTORY - string - Optional dir to store data. If omitted will store temporarily.\n\n' + + contractAddresses, + sequencer: + 'Starts a Sequencer with options. If started additionally to --node, the Sequencer will attach to that node.\n' + + 'Available options are listed below as cliProperty:ENV_VARIABLE_NAME.\n' + + 'rcpUrl:ETHEREUM_HOST - string - The host of the Ethereum node to connect to. Default: http://localhost:8545\n' + + 'apiKey:API_KEY - string - The key for the ethereum node if necessary.\n' + + 'chainId:CHAIN_ID - number - The chain id of the ethereum host. Default: 31337\n' + + 'version:VERSION - number - The version of the Aztec rollup. Default: 1\n' + + 'publisherPrivateKey:SEQ_PUBLISHER_PRIVATE_KEY - string - The private key of the publisher. If not provided, will try to infer from default foundry test accounts.\n' + + 'requiredConfirmations:SEQ_REQUIRED_CONFIRMATIONS - number - The number of confirmations required before publishing a block. Default: 1\n' + + 'l1BlockPublishRetryIntervalMS:SEQ_PUBLISH_RETRY_INTERVAL_MS - number - The interval in ms to wait before retrying to publish a block. Default: 1000\n' + + 'transactionPollingIntervalMS:SEQ_TX_POLLING_INTERVAL_MS - number - The interval in ms to wait before polling for new transactions. Default: 1000\n' + + contractAddresses, + p2pBootstrap: + 'Starts a P2P bootstrap node with options.\n' + + 'Available options are listed below as cliProperty:ENV_VARIABLE_NAME.\n' + + p2pOptions, +}; diff --git a/yarn-project/aztec/src/cli/util.ts b/yarn-project/aztec/src/cli/util.ts new file mode 100644 index 00000000000..75f8c84d237 --- /dev/null +++ b/yarn-project/aztec/src/cli/util.ts @@ -0,0 +1,139 @@ +import { ArchiverConfig } from '@aztec/archiver'; +import { AztecNodeConfig } from '@aztec/aztec-node'; +import { AccountManager } from '@aztec/aztec.js'; +import { L1ContractAddresses } from '@aztec/ethereum'; +import { EthAddress } from '@aztec/foundation/eth-address'; +import { LogFn } from '@aztec/foundation/log'; +import { P2PConfig } from '@aztec/p2p'; +import { GrumpkinScalar, PXEService, PXEServiceConfig } from '@aztec/pxe'; + +/** + * Checks if the object has l1Contracts property + * @param obj - The object to check + * @returns True if the object has l1Contracts property + */ +function hasL1Contracts(obj: any): obj is { + /** the deployed L1 contract addresses */ + l1Contracts: unknown; +} { + return 'l1Contracts' in obj; +} + +/** + * Checks if all contract addresses set in config. + * @param contracts - L1 Contract Addresses object + * @returns true if all contract addresses are not zero + */ +const checkContractAddresses = (contracts: L1ContractAddresses) => { + return ['rollupAddress', 'inboxAddress', 'outboxAddress', 'contractDeploymentEmitterAddress'].every(cn => { + const key = cn as keyof L1ContractAddresses; + return contracts[key] && contracts[key] !== EthAddress.ZERO; + }); +}; + +export const installSignalHandlers = (logFn: LogFn, cb?: Array<() => Promise>) => { + const shutdown = async () => { + logFn('Shutting down...'); + if (cb) { + await Promise.all(cb); + } + process.exit(0); + }; + process.removeAllListeners('SIGINT'); + process.removeAllListeners('SIGTERM'); + process.once('SIGINT', shutdown); + process.once('SIGTERM', shutdown); +}; + +/** + * Parses a string of options into a key-value map. + * @param options - String of options in the format "option1=value1,option2=value2". + * @returns Key-value map of options. + */ +export const parseModuleOptions = (options: string): Record => { + if (!options?.length) { + return {}; + } + const optionsArray = options.split(','); + return optionsArray.reduce((acc, option) => { + const [key, value] = option.split('='); + return { ...acc, [key]: value }; + }, {}); +}; + +export const mergeEnvVarsAndCliOptions = ( + envVars: AztecNodeConfig | PXEServiceConfig | P2PConfig | ArchiverConfig, + cliOptions: Record, + contractsRequired = false, +) => { + if (contractsRequired && !cliOptions.rollupAddress) { + throw new Error('Rollup contract address is required to start the service'); + } + const cliOptionsContracts: L1ContractAddresses = { + rollupAddress: cliOptions.rollupAddress ? EthAddress.fromString(cliOptions.rollupAddress) : EthAddress.ZERO, + registryAddress: cliOptions.registryAddress ? EthAddress.fromString(cliOptions.registryAddress) : EthAddress.ZERO, + inboxAddress: cliOptions.inboxAddress ? EthAddress.fromString(cliOptions.inboxAddress) : EthAddress.ZERO, + outboxAddress: cliOptions.outboxAddress ? EthAddress.fromString(cliOptions.outboxAddress) : EthAddress.ZERO, + contractDeploymentEmitterAddress: cliOptions.contractDeploymentEmitterAddress + ? EthAddress.fromString(cliOptions.contractDeploymentEmitterAddress) + : EthAddress.ZERO, + availabilityOracleAddress: cliOptions.availabilityOracleAddress + ? EthAddress.fromString(cliOptions.availabilityOracleAddress) + : EthAddress.ZERO, + }; + + if ( + hasL1Contracts(envVars) && + contractsRequired && + (!checkContractAddresses(cliOptionsContracts) || !checkContractAddresses(envVars.l1Contracts)) + ) { + throw new Error('Deployed L1 contract addresses are required to start the service'); + } + + let merged = { ...envVars, ...cliOptions } as T; + + if (hasL1Contracts(envVars)) { + merged = { + ...merged, + l1Contracts: { + ...(envVars.l1Contracts && { ...envVars.l1Contracts }), + ...cliOptionsContracts, + }, + } as T; + } + + return merged; +}; + +/** + * Creates logs for the initial accounts + * @param accounts - The initial accounts + * @param pxe - A PXE instance to get the registered accounts + * @returns A string array containing the initial accounts details + */ +export async function createAccountLogs( + accounts: { + /** + * The account object + */ + account: AccountManager; + /** + * The private key of the account + */ + privateKey: GrumpkinScalar; + }[], + pxe: PXEService, +) { + const registeredAccounts = await pxe.getRegisteredAccounts(); + const accountLogStrings = [`Initial Accounts:\n\n`]; + for (const account of accounts) { + const completeAddress = account.account.getCompleteAddress(); + if (registeredAccounts.find(a => a.equals(completeAddress))) { + accountLogStrings.push(` Address: ${completeAddress.address.toString()}\n`); + accountLogStrings.push(` Partial Address: ${completeAddress.partialAddress.toString()}\n`); + accountLogStrings.push(` Private Key: ${account.privateKey.toString()}\n`); + accountLogStrings.push(` Public Key: ${completeAddress.publicKey.toString()}\n\n`); + } + } + return accountLogStrings; +} diff --git a/yarn-project/aztec-sandbox/src/examples/token.ts b/yarn-project/aztec/src/examples/token.ts similarity index 100% rename from yarn-project/aztec-sandbox/src/examples/token.ts rename to yarn-project/aztec/src/examples/token.ts diff --git a/yarn-project/aztec-sandbox/src/examples/util.ts b/yarn-project/aztec/src/examples/util.ts similarity index 100% rename from yarn-project/aztec-sandbox/src/examples/util.ts rename to yarn-project/aztec/src/examples/util.ts diff --git a/yarn-project/aztec-sandbox/src/index.ts b/yarn-project/aztec/src/index.ts similarity index 100% rename from yarn-project/aztec-sandbox/src/index.ts rename to yarn-project/aztec/src/index.ts diff --git a/yarn-project/aztec-sandbox/src/logging.ts b/yarn-project/aztec/src/logging.ts similarity index 92% rename from yarn-project/aztec-sandbox/src/logging.ts rename to yarn-project/aztec/src/logging.ts index a9de4c40415..fe184716efc 100644 --- a/yarn-project/aztec-sandbox/src/logging.ts +++ b/yarn-project/aztec/src/logging.ts @@ -6,14 +6,14 @@ import * as winston from 'winston'; import DailyRotateFile from 'winston-daily-rotate-file'; const { format } = winston; -const CURRENT_LOG_FILE_NAME = 'aztec-sandbox.debug.log'; +const CURRENT_LOG_FILE_NAME = 'aztec.debug.log'; const LOG_DIR = 'log'; /** Creates a winston logger that logs everything to a local rotating file */ function createWinstonLogger() { // See https://www.npmjs.com/package/winston-daily-rotate-file#user-content-options const transport: DailyRotateFile = new DailyRotateFile({ - filename: 'aztec-sandbox-%DATE%.debug.log', + filename: 'aztec-%DATE%.debug.log', dirname: LOG_DIR, datePattern: 'YYYY-MM-DD', zippedArchive: true, diff --git a/yarn-project/aztec-sandbox/src/sandbox.ts b/yarn-project/aztec/src/sandbox.ts similarity index 94% rename from yarn-project/aztec-sandbox/src/sandbox.ts rename to yarn-project/aztec/src/sandbox.ts index acb868e4bca..4c4b378ba67 100644 --- a/yarn-project/aztec-sandbox/src/sandbox.ts +++ b/yarn-project/aztec/src/sandbox.ts @@ -26,7 +26,7 @@ import { } from '@aztec/l1-artifacts'; import { PXEServiceConfig, createPXEService, getPXEServiceConfig } from '@aztec/pxe'; -import { HDAccount, createPublicClient, http as httpViemTransport } from 'viem'; +import { HDAccount, PrivateKeyAccount, createPublicClient, http as httpViemTransport } from 'viem'; import { mnemonicToAccount } from 'viem/accounts'; import { foundry } from 'viem/chains'; @@ -74,7 +74,11 @@ async function waitThenDeploy(config: AztecNodeConfig, deployFunction: () => Pro * @param aztecNodeConfig - The Aztec Node Config * @param hdAccount - Account for publishing L1 contracts */ -export async function deployContractsToL1(aztecNodeConfig: AztecNodeConfig, hdAccount: HDAccount) { +export async function deployContractsToL1( + aztecNodeConfig: AztecNodeConfig, + hdAccount: HDAccount | PrivateKeyAccount, + contractDeployLogger = logger, +) { const l1Artifacts: L1ContractArtifactsForDeployment = { contractDeploymentEmitter: { contractAbi: ContractDeploymentEmitterAbi, @@ -104,7 +108,7 @@ export async function deployContractsToL1(aztecNodeConfig: AztecNodeConfig, hdAc aztecNodeConfig.l1Contracts = ( await waitThenDeploy(aztecNodeConfig, () => - deployL1Contracts(aztecNodeConfig.rpcUrl, hdAccount, localAnvil, logger, l1Artifacts), + deployL1Contracts(aztecNodeConfig.rpcUrl, hdAccount, localAnvil, contractDeployLogger, l1Artifacts), ) ).l1ContractAddresses; diff --git a/yarn-project/aztec-sandbox/src/splash.ts b/yarn-project/aztec/src/splash.ts similarity index 100% rename from yarn-project/aztec-sandbox/src/splash.ts rename to yarn-project/aztec/src/splash.ts diff --git a/yarn-project/aztec-sandbox/tsconfig.json b/yarn-project/aztec/tsconfig.json similarity index 89% rename from yarn-project/aztec-sandbox/tsconfig.json rename to yarn-project/aztec/tsconfig.json index 7b9c2ea9188..a27fb82841d 100644 --- a/yarn-project/aztec-sandbox/tsconfig.json +++ b/yarn-project/aztec/tsconfig.json @@ -9,6 +9,9 @@ { "path": "../accounts" }, + { + "path": "../archiver" + }, { "path": "../aztec-node" }, @@ -27,6 +30,9 @@ { "path": "../foundation" }, + { + "path": "../kv-store" + }, { "path": "../l1-artifacts" }, diff --git a/yarn-project/circuit-types/src/aztec_node/rpc/aztec_node_client.ts b/yarn-project/circuit-types/src/aztec_node/rpc/aztec_node_client.ts index 3a2f9ebb635..c78a6cdeb08 100644 --- a/yarn-project/circuit-types/src/aztec_node/rpc/aztec_node_client.ts +++ b/yarn-project/circuit-types/src/aztec_node/rpc/aztec_node_client.ts @@ -1,4 +1,4 @@ -import { BlockHeader, FunctionSelector } from '@aztec/circuits.js'; +import { FunctionSelector, Header } from '@aztec/circuits.js'; import { EventSelector } from '@aztec/foundation/abi'; import { AztecAddress } from '@aztec/foundation/aztec-address'; import { EthAddress } from '@aztec/foundation/eth-address'; @@ -32,7 +32,7 @@ export function createAztecNodeClient(url: string, fetch = defaultFetch): AztecN Fr, EventSelector, FunctionSelector, - BlockHeader, + Header, L2Block, L2Tx, LogId, @@ -42,6 +42,7 @@ export function createAztecNodeClient(url: string, fetch = defaultFetch): AztecN }, { Tx, L2BlockL2Logs }, false, + 'node', fetch, ); } diff --git a/yarn-project/circuit-types/src/contract_dao.test.ts b/yarn-project/circuit-types/src/contract_dao.test.ts index 5e8faa4650a..7f212a51bfe 100644 --- a/yarn-project/circuit-types/src/contract_dao.test.ts +++ b/yarn-project/circuit-types/src/contract_dao.test.ts @@ -1,13 +1,12 @@ -import { CompleteAddress, EthAddress } from '@aztec/circuits.js'; import { ABIParameterVisibility, ContractArtifact, FunctionSelector, FunctionType } from '@aztec/foundation/abi'; import { ContractDao } from './contract_dao.js'; -import { randomContractArtifact } from './mocks.js'; +import { randomContractArtifact, randomContractInstanceWithAddress } from './mocks.js'; describe('ContractDao', () => { it('serializes / deserializes correctly', () => { const artifact = randomContractArtifact(); - const dao = new ContractDao(artifact, CompleteAddress.random(), EthAddress.random()); + const dao = new ContractDao(artifact, randomContractInstanceWithAddress()); expect(ContractDao.fromBuffer(dao.toBuffer())).toEqual(dao); }); @@ -45,7 +44,7 @@ describe('ContractDao', () => { fileMap: {}, }; - const dao = new ContractDao(artifact, CompleteAddress.random(), EthAddress.random()); + const dao = new ContractDao(artifact, randomContractInstanceWithAddress()); expect(dao.functions[0]).toEqual({ ...artifact.functions[0], diff --git a/yarn-project/circuit-types/src/contract_dao.ts b/yarn-project/circuit-types/src/contract_dao.ts index 79e07af969f..bd7dfe8736b 100644 --- a/yarn-project/circuit-types/src/contract_dao.ts +++ b/yarn-project/circuit-types/src/contract_dao.ts @@ -1,4 +1,4 @@ -import { CompleteAddress, ContractFunctionDao } from '@aztec/circuits.js'; +import { AztecAddress, ContractFunctionDao } from '@aztec/circuits.js'; import { ContractArtifact, DebugFileMap, @@ -8,8 +8,8 @@ import { FunctionType, getFunctionDebugMetadata, } from '@aztec/foundation/abi'; -import { EthAddress } from '@aztec/foundation/eth-address'; import { BufferReader, prefixBufferWithLength } from '@aztec/foundation/serialize'; +import { ContractInstanceWithAddress, SerializableContractInstance } from '@aztec/types/contracts'; import { EncodedContractFunction } from './contract_data.js'; @@ -21,13 +21,8 @@ import { EncodedContractFunction } from './contract_data.js'; export class ContractDao implements ContractArtifact { /** An array of contract functions with additional selector property. */ public readonly functions: ContractFunctionDao[]; - constructor( - private contractArtifact: ContractArtifact, - /** The complete address representing the contract on L2. */ - public readonly completeAddress: CompleteAddress, - /** The Ethereum address of the L1 contract serving as a bridge for cross-layer interactions. */ - public readonly portalContract: EthAddress, - ) { + + constructor(private contractArtifact: ContractArtifact, public readonly instance: ContractInstanceWithAddress) { this.functions = contractArtifact.functions.map(f => ({ ...f, selector: FunctionSelector.fromNameAndParameters(f.name, f.parameters), @@ -68,8 +63,8 @@ export class ContractDao implements ContractArtifact { // should be safe to JSON.stringify it (i.e. it doesn't contain BigInts) const contractArtifactJson = JSON.stringify(this.contractArtifact); const buf = Buffer.concat([ - this.completeAddress.toBuffer(), - this.portalContract.toBuffer20(), + this.instance.address.toBuffer(), + new SerializableContractInstance(this.instance).toBuffer(), prefixBufferWithLength(Buffer.from(contractArtifactJson, 'utf-8')), ]); @@ -78,10 +73,10 @@ export class ContractDao implements ContractArtifact { static fromBuffer(buf: Uint8Array | BufferReader) { const reader = BufferReader.asReader(buf); - const completeAddress = CompleteAddress.fromBuffer(reader); - const portalContract = new EthAddress(reader.readBytes(EthAddress.SIZE_IN_BYTES)); + const address = AztecAddress.fromBuffer(reader); + const instance = SerializableContractInstance.fromBuffer(reader).withAddress(address); const contractArtifact = JSON.parse(reader.readString()); - return new ContractDao(contractArtifact, completeAddress, portalContract); + return new ContractDao(contractArtifact, instance); } } diff --git a/yarn-project/circuit-types/src/contract_data.ts b/yarn-project/circuit-types/src/contract_data.ts index 1da2b034bc3..8214618d2b0 100644 --- a/yarn-project/circuit-types/src/contract_data.ts +++ b/yarn-project/circuit-types/src/contract_data.ts @@ -1,12 +1,4 @@ -import { - CompleteAddress, - FUNCTION_SELECTOR_NUM_BYTES, - Fr, - FunctionSelector, - PartialAddress, - Point, - PublicKey, -} from '@aztec/circuits.js'; +import { FUNCTION_SELECTOR_NUM_BYTES, Fr, FunctionSelector } from '@aztec/circuits.js'; import { AztecAddress } from '@aztec/foundation/aztec-address'; import { randomBytes } from '@aztec/foundation/crypto'; import { EthAddress } from '@aztec/foundation/eth-address'; @@ -105,6 +97,23 @@ export class EncodedContractFunction { return new EncodedContractFunction(fnSelector, isInternal, reader.readBuffer()); } + /** + * Serializes this instance into a string. + * @returns Encoded string. + */ + toString(): string { + return this.toBuffer().toString('hex'); + } + + /** + * Deserializes a contract function object from an encoded string. + * @param data - The encoded string. + * @returns The deserialized contract function. + */ + static fromString(data: string): EncodedContractFunction { + return EncodedContractFunction.fromBuffer(Buffer.from(data, 'hex')); + } + /** * Creates a random contract function. * @returns A random contract function. @@ -125,11 +134,13 @@ export class ExtendedContractData { /** The base contract data: aztec & portal addresses. */ public contractData: ContractData, /** Artifacts of public functions. */ - private publicFunctions: EncodedContractFunction[], - /** Partial addresses of the contract. */ - public readonly partialAddress: PartialAddress, - /** Public key of the contract. */ - public readonly publicKey: PublicKey, + public readonly publicFunctions: EncodedContractFunction[], + /** Contract class id */ + public readonly contractClassId: Fr, + /** Salted init hash. */ + public readonly saltedInitializationHash: Fr, + /** Public key hash of the contract. */ + public readonly publicKeyHash: Fr, ) { this.bytecode = serializeBufferArrayToVector(publicFunctions.map(fn => fn.toBuffer())); } @@ -149,7 +160,13 @@ export class ExtendedContractData { */ public toBuffer(): Buffer { const contractDataBuf = this.contractData.toBuffer(); - return serializeToBuffer(contractDataBuf, this.bytecode, this.partialAddress, this.publicKey); + return serializeToBuffer( + contractDataBuf, + this.bytecode, + this.contractClassId, + this.saltedInitializationHash, + this.publicKeyHash, + ); } /** @@ -160,22 +177,14 @@ export class ExtendedContractData { return this.toBuffer().toString('hex'); } - /** - * Gets the complete address. - * @returns The complete address. - */ - public getCompleteAddress(): CompleteAddress { - return new CompleteAddress(this.contractData.contractAddress, this.publicKey, this.partialAddress); - } - /** True if this represents an empty instance. */ public isEmpty(): boolean { return ( this.contractData.isEmpty() && this.publicFunctions.length === 0 && - this.partialAddress.isZero() && - this.publicKey.x.isZero() && - this.publicKey.y.isZero() + this.contractClassId.isZero() && + this.publicKeyHash.isZero() && + this.saltedInitializationHash.isZero() ); } @@ -188,9 +197,10 @@ export class ExtendedContractData { const reader = BufferReader.asReader(buffer); const contractData = reader.readObject(ContractData); const publicFns = reader.readVector(EncodedContractFunction); - const partialAddress = reader.readObject(Fr); - const publicKey = reader.readObject(Point); - return new ExtendedContractData(contractData, publicFns, partialAddress, publicKey); + const contractClassId = reader.readObject(Fr); + const saltedInitializationHash = reader.readObject(Fr); + const publicKeyHash = reader.readObject(Fr); + return new ExtendedContractData(contractData, publicFns, contractClassId, saltedInitializationHash, publicKeyHash); } /** @@ -212,13 +222,14 @@ export class ExtendedContractData { contractData ?? ContractData.random(), [EncodedContractFunction.random(), EncodedContractFunction.random()], Fr.random(), - Point.random(), + Fr.random(), + Fr.random(), ); } /** Generates empty extended contract data. */ static empty(): ExtendedContractData { - return new ExtendedContractData(ContractData.empty(), [], Fr.ZERO, Point.ZERO); + return new ExtendedContractData(ContractData.empty(), [], Fr.ZERO, Fr.ZERO, Fr.ZERO); } } diff --git a/yarn-project/circuit-types/src/interfaces/aztec-node.ts b/yarn-project/circuit-types/src/interfaces/aztec-node.ts index 93faa03333a..f0d2937bd84 100644 --- a/yarn-project/circuit-types/src/interfaces/aztec-node.ts +++ b/yarn-project/circuit-types/src/interfaces/aztec-node.ts @@ -1,4 +1,4 @@ -import { BlockHeader } from '@aztec/circuits.js'; +import { Header } from '@aztec/circuits.js'; import { L1ContractAddresses } from '@aztec/ethereum'; import { AztecAddress } from '@aztec/foundation/aztec-address'; import { Fr } from '@aztec/foundation/fields'; @@ -7,7 +7,6 @@ import { ContractData, ExtendedContractData } from '../contract_data.js'; import { L2Block } from '../l2_block.js'; import { L2Tx } from '../l2_tx.js'; import { GetUnencryptedLogsResponse, L2BlockL2Logs, LogFilter, LogType } from '../logs/index.js'; -import { MerkleTreeId } from '../merkle_tree_id.js'; import { Tx, TxHash } from '../tx/index.js'; import { SequencerConfig } from './configs.js'; import { StateInfoProvider } from './state_info_provider.js'; @@ -119,17 +118,11 @@ export interface AztecNode extends StateInfoProvider { */ getPublicStorageAt(contract: AztecAddress, slot: Fr): Promise; - /** - * Returns the current committed roots for the data trees. - * @returns The current committed roots for the data trees. - */ - getTreeRoots(): Promise>; - /** * Returns the currently committed block header. * @returns The current committed block header. */ - getBlockHeader(): Promise; + getHeader(): Promise
; /** * Simulates the public part of a transaction with the current state. diff --git a/yarn-project/circuit-types/src/interfaces/deployed-contract.ts b/yarn-project/circuit-types/src/interfaces/deployed-contract.ts index 784b162ef67..5bf48600d64 100644 --- a/yarn-project/circuit-types/src/interfaces/deployed-contract.ts +++ b/yarn-project/circuit-types/src/interfaces/deployed-contract.ts @@ -1,6 +1,5 @@ -import { CompleteAddress } from '@aztec/circuits.js'; import { ContractArtifact } from '@aztec/foundation/abi'; -import { EthAddress } from '@aztec/foundation/eth-address'; +import { ContractInstanceWithAddress } from '@aztec/types/contracts'; /** * Represents a deployed contract on the Aztec network. @@ -12,11 +11,7 @@ export interface DeployedContract { */ artifact: ContractArtifact; /** - * The complete address representing the contract on L2. + * The contract instance. */ - completeAddress: CompleteAddress; - /** - * The Ethereum address of the L1 portal contract. - */ - portalContract: EthAddress; + instance: ContractInstanceWithAddress; } diff --git a/yarn-project/circuit-types/src/interfaces/pxe.ts b/yarn-project/circuit-types/src/interfaces/pxe.ts index 657a1e819f9..eb3671bbf92 100644 --- a/yarn-project/circuit-types/src/interfaces/pxe.ts +++ b/yarn-project/circuit-types/src/interfaces/pxe.ts @@ -1,4 +1,5 @@ import { AztecAddress, CompleteAddress, Fr, GrumpkinPrivateKey, PartialAddress } from '@aztec/circuits.js'; +import { ContractInstanceWithAddress } from '@aztec/types/contracts'; import { NodeInfo } from '@aztec/types/interfaces'; import { AuthWitness } from '../auth_witness.js'; @@ -261,5 +262,13 @@ export interface PXE { * @returns The latest block synchronized for blocks, and the latest block synched for notes for each public key being tracked. */ getSyncStatus(): Promise; + + /** + * Returns a Contact Instance given its address, which includes the contract class identifier, portal address, + * initialization hash, deployment salt, and public keys hash. + * TOOD(@spalladino): Should we return the public keys in plain as well here? + * @param address + */ + getContractInstance(address: AztecAddress): Promise; } // docs:end:pxe-interface diff --git a/yarn-project/circuit-types/src/keys/key_store.ts b/yarn-project/circuit-types/src/keys/key_store.ts index d9da2be7d50..f70648a0c4c 100644 --- a/yarn-project/circuit-types/src/keys/key_store.ts +++ b/yarn-project/circuit-types/src/keys/key_store.ts @@ -45,6 +45,15 @@ export interface KeyStore { */ getNullifierSecretKey(pubKey: PublicKey): Promise; + /** + * Retrieves the nullifier secret key of the specified nullifier public key. + * Throws an error if the provided public key is not associated with any of the registered accounts. + * + * @param nullifierPubKey - The nullifier public key. + * @returns A Promise that resolves to the nullifier secret key. + */ + getNullifierSecretKeyFromPublicKey(nullifierPubKey: PublicKey): Promise; + /** * Retrieves the nullifier public key of the account associated with the specified AztecAddress. * Throws an error if the provided public key is not found in the list of registered accounts. diff --git a/yarn-project/circuit-types/src/l1_to_l2_message.ts b/yarn-project/circuit-types/src/l1_to_l2_message.ts index a072e6c270b..29452647a3c 100644 --- a/yarn-project/circuit-types/src/l1_to_l2_message.ts +++ b/yarn-project/circuit-types/src/l1_to_l2_message.ts @@ -1,5 +1,6 @@ import { AztecAddress } from '@aztec/foundation/aztec-address'; import { toBigIntBE, toBufferBE } from '@aztec/foundation/bigint-buffer'; +import { sha256 } from '@aztec/foundation/crypto'; import { EthAddress } from '@aztec/foundation/eth-address'; import { Fr } from '@aztec/foundation/fields'; import { BufferReader, serializeToBuffer } from '@aztec/foundation/serialize'; @@ -116,6 +117,10 @@ export class L1ToL2Message { return serializeToBuffer(this.sender, this.recipient, this.content, this.secretHash, this.deadline, this.fee); } + hash(): Fr { + return Fr.fromBufferReduce(sha256(serializeToBuffer(...this.toFieldArray()))); + } + static fromBuffer(buffer: Buffer | BufferReader): L1ToL2Message { const reader = BufferReader.asReader(buffer); const sender = reader.readObject(L1Actor); @@ -127,6 +132,15 @@ export class L1ToL2Message { return new L1ToL2Message(sender, recipient, content, secretHash, deadline, fee); } + toString(): string { + return this.toBuffer().toString('hex'); + } + + static fromString(data: string): L1ToL2Message { + const buffer = Buffer.from(data, 'hex'); + return L1ToL2Message.fromBuffer(buffer); + } + static empty(): L1ToL2Message { return new L1ToL2Message(L1Actor.empty(), L2Actor.empty(), Fr.ZERO, Fr.ZERO, 0, 0); } diff --git a/yarn-project/circuit-types/src/l2_block.ts b/yarn-project/circuit-types/src/l2_block.ts index 620659b01f1..4210e720eaf 100644 --- a/yarn-project/circuit-types/src/l2_block.ts +++ b/yarn-project/circuit-types/src/l2_block.ts @@ -9,9 +9,9 @@ import { NUMBER_OF_L1_L2_MESSAGES_PER_ROLLUP, STRING_ENCODING, } from '@aztec/circuits.js'; -import { makeAppendOnlyTreeSnapshot, makeGlobalVariables, makeHeader } from '@aztec/circuits.js/factories'; +import { makeAppendOnlyTreeSnapshot, makeHeader } from '@aztec/circuits.js/factories'; import { times } from '@aztec/foundation/collection'; -import { keccak, sha256 } from '@aztec/foundation/crypto'; +import { sha256 } from '@aztec/foundation/crypto'; import { Fr } from '@aztec/foundation/fields'; import { createDebugLogger } from '@aztec/foundation/log'; import { BufferReader, serializeToBuffer } from '@aztec/foundation/serialize'; @@ -91,7 +91,6 @@ export class L2Block { public newL1ToL2Messages: Fr[] = [], newEncryptedLogs?: L2BlockL2Logs, newUnencryptedLogs?: L2BlockL2Logs, - private blockHash?: Buffer, l1BlockNumber?: bigint, ) { if (newCommitments.length % MAX_NEW_COMMITMENTS_PER_TX !== 0) { @@ -139,8 +138,6 @@ export class L2Block { numEncryptedLogsPerCall = 2, numUnencryptedLogsPerCall = 1, ): L2Block { - const globalVariables = makeGlobalVariables(0, l2BlockNum); - const newNullifiers = times(MAX_NEW_NULLIFIERS_PER_TX * txsPerBlock, Fr.random); const newCommitments = times(MAX_NEW_COMMITMENTS_PER_TX * txsPerBlock, Fr.random); const newContracts = times(MAX_NEW_CONTRACTS_PER_TX * txsPerBlock, Fr.random); @@ -164,7 +161,7 @@ export class L2Block { return L2Block.fromFields( { archive: makeAppendOnlyTreeSnapshot(1), - header: makeHeader(0, globalVariables), + header: makeHeader(0, l2BlockNum), newCommitments, newNullifiers, newContracts, @@ -175,7 +172,6 @@ export class L2Block { newEncryptedLogs, newUnencryptedLogs, }, - undefined, // just for testing purposes, each random L2 block got emitted in the equivalent L1 block BigInt(l2BlockNum), ); @@ -231,7 +227,6 @@ export class L2Block { */ newUnencryptedLogs?: L2BlockL2Logs; }, - blockHash?: Buffer, l1BlockNumber?: bigint, ) { return new this( @@ -246,7 +241,6 @@ export class L2Block { fields.newL1ToL2Messages, fields.newEncryptedLogs, fields.newUnencryptedLogs, - blockHash, l1BlockNumber, ); } @@ -331,10 +325,9 @@ export class L2Block { /** * Deserializes L2 block without logs from a buffer. * @param buf - A serialized L2 block. - * @param blockHash - The hash of the block. * @returns Deserialized L2 block. */ - static fromBuffer(buf: Buffer | BufferReader, blockHash?: Buffer) { + static fromBuffer(buf: Buffer | BufferReader) { const reader = BufferReader.asReader(buf); const header = reader.readObject(Header); const archive = reader.readObject(AppendOnlyTreeSnapshot); @@ -347,20 +340,17 @@ export class L2Block { // TODO(sean): could an optimization of this be that it is encoded such that zeros are assumed const newL1ToL2Messages = reader.readVector(Fr); - return L2Block.fromFields( - { - archive, - header, - newCommitments, - newNullifiers, - newPublicDataWrites, - newL2ToL1Msgs, - newContracts, - newContractData, - newL1ToL2Messages, - }, - blockHash, - ); + return L2Block.fromFields({ + archive, + header, + newCommitments, + newNullifiers, + newPublicDataWrites, + newL2ToL1Msgs, + newContracts, + newContractData, + newL1ToL2Messages, + }); } /** @@ -443,14 +433,11 @@ export class L2Block { } /** - * Returns the block's hash. + * Returns the block's hash (hash of block header). * @returns The block's hash. */ - public getBlockHash(): Buffer { - if (!this.blockHash) { - this.blockHash = keccak(this.toBufferWithLogs()); - } - return this.blockHash; + public hash(): Fr { + return this.header.hash(); } /** @@ -462,11 +449,11 @@ export class L2Block { const buf = serializeToBuffer( this.header.globalVariables, // TODO(#3868) - AppendOnlyTreeSnapshot.empty(), // this.startNoteHashTreeSnapshot, - AppendOnlyTreeSnapshot.empty(), // this.startNullifierTreeSnapshot, - AppendOnlyTreeSnapshot.empty(), // this.startContractTreeSnapshot, - AppendOnlyTreeSnapshot.empty(), // this.startPublicDataTreeSnapshot, - AppendOnlyTreeSnapshot.empty(), // this.startL1ToL2MessageTreeSnapshot, + AppendOnlyTreeSnapshot.zero(), // this.startNoteHashTreeSnapshot, + AppendOnlyTreeSnapshot.zero(), // this.startNullifierTreeSnapshot, + AppendOnlyTreeSnapshot.zero(), // this.startContractTreeSnapshot, + AppendOnlyTreeSnapshot.zero(), // this.startPublicDataTreeSnapshot, + AppendOnlyTreeSnapshot.zero(), // this.startL1ToL2MessageTreeSnapshot, this.header.lastArchive, this.header.state.partial.noteHashTree, this.header.state.partial.nullifierTree, @@ -489,11 +476,11 @@ export class L2Block { const inputValue = serializeToBuffer( new Fr(Number(this.header.globalVariables.blockNumber.toBigInt()) - 1), // TODO(#3868) - AppendOnlyTreeSnapshot.empty(), // this.startNoteHashTreeSnapshot, - AppendOnlyTreeSnapshot.empty(), // this.startNullifierTreeSnapshot, - AppendOnlyTreeSnapshot.empty(), // this.startContractTreeSnapshot, - AppendOnlyTreeSnapshot.empty(), // this.startPublicDataTreeSnapshot, - AppendOnlyTreeSnapshot.empty(), // this.startL1ToL2MessageTreeSnapshot, + AppendOnlyTreeSnapshot.zero(), // this.startNoteHashTreeSnapshot, + AppendOnlyTreeSnapshot.zero(), // this.startNullifierTreeSnapshot, + AppendOnlyTreeSnapshot.zero(), // this.startContractTreeSnapshot, + AppendOnlyTreeSnapshot.zero(), // this.startPublicDataTreeSnapshot, + AppendOnlyTreeSnapshot.zero(), // this.startL1ToL2MessageTreeSnapshot, this.header.lastArchive, ); return sha256(inputValue); @@ -641,7 +628,7 @@ export class L2Block { newL2ToL1Msgs, newContracts, newContractData, - this.getBlockHash(), + this.hash(), Number(this.header.globalVariables.blockNumber.toBigInt()), ); } diff --git a/yarn-project/circuit-types/src/l2_tx.ts b/yarn-project/circuit-types/src/l2_tx.ts index 65588476418..593f85d7b81 100644 --- a/yarn-project/circuit-types/src/l2_tx.ts +++ b/yarn-project/circuit-types/src/l2_tx.ts @@ -57,7 +57,7 @@ export class L2Tx { /** * The unique identifier of the block containing the transaction. */ - public blockHash: Buffer, + public blockHash: Fr, /** * The block number in which the transaction was included. */ @@ -80,7 +80,7 @@ export class L2Tx { reader.readVector(Fr), reader.readVector(Fr), reader.readVector(ContractData), - reader.readBytes(Fr.SIZE_IN_BYTES), + Fr.fromBuffer(reader), reader.readNumber(), ); } @@ -106,7 +106,7 @@ export class L2Tx { new Vector(this.newL2ToL1Msgs).toBuffer(), new Vector(this.newContracts).toBuffer(), new Vector(this.newContractData).toBuffer(), - this.blockHash, + this.blockHash.toBuffer(), numToUInt32BE(this.blockNumber), ]); } @@ -127,7 +127,7 @@ export class L2Tx { times(rand(0, MAX_NEW_L2_TO_L1_MSGS_PER_TX), Fr.random), times(rand(0, MAX_NEW_CONTRACTS_PER_TX), Fr.random), times(rand(0, MAX_NEW_CONTRACTS_PER_TX), ContractData.random), - Fr.random().toBuffer(), + Fr.random(), 123, ); } diff --git a/yarn-project/circuit-types/src/logs/l2_block_l2_logs.ts b/yarn-project/circuit-types/src/logs/l2_block_l2_logs.ts index a0c095c3aa6..3b298f06a41 100644 --- a/yarn-project/circuit-types/src/logs/l2_block_l2_logs.ts +++ b/yarn-project/circuit-types/src/logs/l2_block_l2_logs.ts @@ -56,6 +56,24 @@ export class L2BlockL2Logs { return new L2BlockL2Logs(txLogs); } + /** + * Seralizes logs into a string. + * @returns A string representation of the serialized logs. + */ + public toString(): string { + return this.toBuffer().toString('hex'); + } + + /** + * Deserializes logs from a string. + * @param data - The string containing the serialized logs. + * @returns A new `L2BlockL2Logs` object. + */ + public static fromString(data: string): L2BlockL2Logs { + const buffer = Buffer.from(data, 'hex'); + return L2BlockL2Logs.fromBuffer(buffer); + } + /** * Creates a new `L2BlockL2Logs` object with `numCalls` function logs and `numLogsPerCall` logs in each function * call. diff --git a/yarn-project/circuit-types/src/mocks.ts b/yarn-project/circuit-types/src/mocks.ts index 5dbddd45d59..5cf5aa39011 100644 --- a/yarn-project/circuit-types/src/mocks.ts +++ b/yarn-project/circuit-types/src/mocks.ts @@ -1,7 +1,5 @@ import { AztecAddress, - CompleteAddress, - EthAddress, Fr, MAX_NEW_CONTRACTS_PER_TX, MAX_PUBLIC_CALL_STACK_LENGTH_PER_TX, @@ -12,6 +10,7 @@ import { ContractArtifact } from '@aztec/foundation/abi'; import { times } from '@aztec/foundation/collection'; import { randomBytes } from '@aztec/foundation/crypto'; import { Tuple } from '@aztec/foundation/serialize'; +import { ContractInstanceWithAddress, SerializableContractInstance } from '@aztec/types/contracts'; import { ExtendedContractData } from './contract_data.js'; import { DeployedContract } from './interfaces/index.js'; @@ -50,10 +49,12 @@ export const randomContractArtifact = (): ContractArtifact => ({ fileMap: {}, }); +export const randomContractInstanceWithAddress = (): ContractInstanceWithAddress => + SerializableContractInstance.random().withAddress(AztecAddress.random()); + export const randomDeployedContract = (): DeployedContract => ({ artifact: randomContractArtifact(), - completeAddress: CompleteAddress.random(), - portalContract: EthAddress.random(), + instance: randomContractInstanceWithAddress(), }); export const randomExtendedNote = ({ diff --git a/yarn-project/circuit-types/src/notes/note_filter.ts b/yarn-project/circuit-types/src/notes/note_filter.ts index 515dabb7c35..9437e8866c1 100644 --- a/yarn-project/circuit-types/src/notes/note_filter.ts +++ b/yarn-project/circuit-types/src/notes/note_filter.ts @@ -2,6 +2,15 @@ import { AztecAddress, Fr } from '@aztec/circuits.js'; import { TxHash } from '../index.js'; +/** + * The status of notes to retrieve. + */ +export enum NoteStatus { + ACTIVE = 1, + ACTIVE_OR_NULLIFIED = 2, + // TODO 4217: add 'NULLIFIED' +} + /** * A filter used to fetch Notes. * @remarks This filter is applied as an intersection of all it's params. @@ -15,4 +24,18 @@ export type NoteFilter = { storageSlot?: Fr; /** The owner of the note (whose public key was used to encrypt the note). */ owner?: AztecAddress; + /** The status of the note. Defaults to 'ACTIVE'. */ + status?: NoteStatus; }; + +/** + * The comparator to use to compare. + */ +export enum Comparator { + EQ = 1, + NEQ = 2, + LT = 3, + LTE = 4, + GT = 5, + GTE = 6, +} diff --git a/yarn-project/circuits.js/fixtures/Benchmarking.test.json b/yarn-project/circuits.js/fixtures/Benchmarking.test.json new file mode 100644 index 00000000000..9ee5d2c91c3 --- /dev/null +++ b/yarn-project/circuits.js/fixtures/Benchmarking.test.json @@ -0,0 +1,1699 @@ +{ + "noir_version": "0.23.0+602f23f4fb698cf6e37071936a2a46593a998d08", + "name": "Benchmarking", + "functions": [ + { + "name": "increment_balance", + "function_type": "Open", + "is_internal": false, + "abi": { + "parameters": [ + { + "name": "inputs", + "type": { + "kind": "struct", + "path": "aztec::abi::PublicContextInputs", + "fields": [ + { + "name": "call_context", + "type": { + "kind": "struct", + "path": "aztec::protocol_types::abis::call_context::CallContext", + "fields": [ + { + "name": "msg_sender", + "type": { + "kind": "struct", + "path": "aztec::protocol_types::address::AztecAddress", + "fields": [{ "name": "inner", "type": { "kind": "field" } }] + } + }, + { + "name": "storage_contract_address", + "type": { + "kind": "struct", + "path": "aztec::protocol_types::address::AztecAddress", + "fields": [{ "name": "inner", "type": { "kind": "field" } }] + } + }, + { + "name": "portal_contract_address", + "type": { + "kind": "struct", + "path": "aztec::protocol_types::address::EthAddress", + "fields": [{ "name": "inner", "type": { "kind": "field" } }] + } + }, + { + "name": "function_selector", + "type": { + "kind": "struct", + "path": "aztec::protocol_types::abis::function_selector::FunctionSelector", + "fields": [ + { "name": "inner", "type": { "kind": "integer", "sign": "unsigned", "width": 32 } } + ] + } + }, + { "name": "is_delegate_call", "type": { "kind": "boolean" } }, + { "name": "is_static_call", "type": { "kind": "boolean" } }, + { "name": "is_contract_deployment", "type": { "kind": "boolean" } }, + { + "name": "start_side_effect_counter", + "type": { "kind": "integer", "sign": "unsigned", "width": 32 } + } + ] + } + }, + { + "name": "block_header", + "type": { + "kind": "struct", + "path": "aztec::protocol_types::abis::block_header::BlockHeader", + "fields": [ + { "name": "note_hash_tree_root", "type": { "kind": "field" } }, + { "name": "nullifier_tree_root", "type": { "kind": "field" } }, + { "name": "contract_tree_root", "type": { "kind": "field" } }, + { "name": "l1_to_l2_message_tree_root", "type": { "kind": "field" } }, + { "name": "archive_root", "type": { "kind": "field" } }, + { "name": "public_data_tree_root", "type": { "kind": "field" } }, + { "name": "global_variables_hash", "type": { "kind": "field" } } + ] + } + }, + { + "name": "public_global_variables", + "type": { + "kind": "struct", + "path": "aztec::abi::PublicGlobalVariables", + "fields": [ + { "name": "chain_id", "type": { "kind": "field" } }, + { "name": "version", "type": { "kind": "field" } }, + { "name": "block_number", "type": { "kind": "field" } }, + { "name": "timestamp", "type": { "kind": "field" } } + ] + } + } + ] + }, + "visibility": "private" + }, + { + "name": "owner", + "type": { + "kind": "struct", + "path": "aztec::protocol_types::address::AztecAddress", + "fields": [{ "name": "inner", "type": { "kind": "field" } }] + }, + "visibility": "private" + }, + { "name": "value", "type": { "kind": "field" }, "visibility": "private" } + ], + "param_witnesses": { + "inputs": [{ "start": 0, "end": 19 }], + "owner": [{ "start": 19, "end": 20 }], + "value": [{ "start": 20, "end": 21 }] + }, + "return_type": { + "abi_type": { + "kind": "struct", + "path": "aztec::protocol_types::abis::public_circuit_public_inputs::PublicCircuitPublicInputs", + "fields": [ + { + "name": "call_context", + "type": { + "kind": "struct", + "path": "aztec::protocol_types::abis::call_context::CallContext", + "fields": [ + { + "name": "msg_sender", + "type": { + "kind": "struct", + "path": "aztec::protocol_types::address::AztecAddress", + "fields": [{ "name": "inner", "type": { "kind": "field" } }] + } + }, + { + "name": "storage_contract_address", + "type": { + "kind": "struct", + "path": "aztec::protocol_types::address::AztecAddress", + "fields": [{ "name": "inner", "type": { "kind": "field" } }] + } + }, + { + "name": "portal_contract_address", + "type": { + "kind": "struct", + "path": "aztec::protocol_types::address::EthAddress", + "fields": [{ "name": "inner", "type": { "kind": "field" } }] + } + }, + { + "name": "function_selector", + "type": { + "kind": "struct", + "path": "aztec::protocol_types::abis::function_selector::FunctionSelector", + "fields": [{ "name": "inner", "type": { "kind": "integer", "sign": "unsigned", "width": 32 } }] + } + }, + { "name": "is_delegate_call", "type": { "kind": "boolean" } }, + { "name": "is_static_call", "type": { "kind": "boolean" } }, + { "name": "is_contract_deployment", "type": { "kind": "boolean" } }, + { + "name": "start_side_effect_counter", + "type": { "kind": "integer", "sign": "unsigned", "width": 32 } + } + ] + } + }, + { "name": "args_hash", "type": { "kind": "field" } }, + { "name": "return_values", "type": { "kind": "array", "length": 4, "type": { "kind": "field" } } }, + { + "name": "contract_storage_update_requests", + "type": { + "kind": "array", + "length": 16, + "type": { + "kind": "struct", + "path": "aztec::protocol_types::contrakt::storage_update_request::StorageUpdateRequest", + "fields": [ + { "name": "storage_slot", "type": { "kind": "field" } }, + { "name": "old_value", "type": { "kind": "field" } }, + { "name": "new_value", "type": { "kind": "field" } } + ] + } + } + }, + { + "name": "contract_storage_reads", + "type": { + "kind": "array", + "length": 16, + "type": { + "kind": "struct", + "path": "aztec::protocol_types::contrakt::storage_read::StorageRead", + "fields": [ + { "name": "storage_slot", "type": { "kind": "field" } }, + { "name": "current_value", "type": { "kind": "field" } } + ] + } + } + }, + { + "name": "public_call_stack_hashes", + "type": { "kind": "array", "length": 4, "type": { "kind": "field" } } + }, + { + "name": "new_commitments", + "type": { + "kind": "array", + "length": 16, + "type": { + "kind": "struct", + "path": "aztec::protocol_types::abis::side_effect::SideEffect", + "fields": [ + { "name": "value", "type": { "kind": "field" } }, + { "name": "counter", "type": { "kind": "integer", "sign": "unsigned", "width": 32 } } + ] + } + } + }, + { + "name": "new_nullifiers", + "type": { + "kind": "array", + "length": 16, + "type": { + "kind": "struct", + "path": "aztec::protocol_types::abis::side_effect::SideEffectLinkedToNoteHash", + "fields": [ + { "name": "value", "type": { "kind": "field" } }, + { "name": "note_hash", "type": { "kind": "field" } }, + { "name": "counter", "type": { "kind": "integer", "sign": "unsigned", "width": 32 } } + ] + } + } + }, + { "name": "new_l2_to_l1_msgs", "type": { "kind": "array", "length": 2, "type": { "kind": "field" } } }, + { + "name": "unencrypted_logs_hash", + "type": { "kind": "array", "length": 2, "type": { "kind": "field" } } + }, + { "name": "unencrypted_log_preimages_length", "type": { "kind": "field" } }, + { + "name": "block_header", + "type": { + "kind": "struct", + "path": "aztec::protocol_types::abis::block_header::BlockHeader", + "fields": [ + { "name": "note_hash_tree_root", "type": { "kind": "field" } }, + { "name": "nullifier_tree_root", "type": { "kind": "field" } }, + { "name": "contract_tree_root", "type": { "kind": "field" } }, + { "name": "l1_to_l2_message_tree_root", "type": { "kind": "field" } }, + { "name": "archive_root", "type": { "kind": "field" } }, + { "name": "public_data_tree_root", "type": { "kind": "field" } }, + { "name": "global_variables_hash", "type": { "kind": "field" } } + ] + } + }, + { + "name": "prover_address", + "type": { + "kind": "struct", + "path": "aztec::protocol_types::address::AztecAddress", + "fields": [{ "name": "inner", "type": { "kind": "field" } }] + } + } + ] + }, + "visibility": "public" + }, + "return_witnesses": [ + 21, 22, 23, 24, 25, 26, 27, 28, 29, 30, 31, 32, 33, 34, 35, 36, 37, 38, 39, 40, 41, 42, 43, 44, 45, 46, 47, + 48, 49, 50, 51, 52, 53, 54, 55, 56, 57, 58, 59, 60, 61, 62, 63, 64, 65, 66, 67, 68, 69, 70, 71, 72, 73, 74, + 75, 76, 77, 78, 79, 80, 81, 82, 83, 84, 85, 86, 87, 88, 89, 90, 91, 92, 93, 94, 95, 96, 97, 98, 99, 100, 101, + 102, 103, 104, 105, 106, 107, 108, 109, 110, 111, 112, 113, 114, 115, 116, 117, 118, 119, 120, 121, 122, 123, + 124, 125, 126, 127, 128, 129, 130, 131, 132, 133, 134, 135, 136, 137, 138, 139, 140, 141, 142, 143, 144, 145, + 146, 147, 148, 149, 150, 151, 152, 153, 154, 155, 156, 157, 158, 159, 160, 161, 162, 163, 164, 165, 166, 167, + 168, 169, 170, 171, 172, 173, 174, 175, 176, 177, 178, 179, 180, 181, 182, 183, 184, 185, 186, 187, 188, 189, + 190, 191, 192, 193, 194, 195, 196, 197, 198, 199, 200, 201, 202, 203, 204, 205, 206, 207, 208, 209, 210 + ] + }, + "bytecode": "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", + "debug_symbols": "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0JtO9KYTvelEb7rQmy70pgu96UJvutCbLvSmC73pQm+60Jsu9KYbvelGb7rRm270phu96UZvutGbbvSmG73pRm960Jse9KYHvelBb3rQmx70pge96UFvetCbHvSmF73pRW960Zte9KYXvelFb3rRm170phe96QVvuq4LfcDQBxx9INAHDvpAog8U+kCjDwz6AHrTht60oTdt6E0betOG3rShN23oTRt604betKE37ehNO3rTjt60ozft6E07etOO3rSjN+3oTTt604HedKA3HehNB3rTgd50oDcd6E0HetOB3nSgN33Qmz7oTR/0pg960we96YPeNJqRFZqRFZqRFZqRFZqRFZqRFZqRFZqRFZqRFZqRFZqRFZqRFZqRFZqRFZqRFZqRFZqRFZqRFZqRFZqRFZqRFZqRFZqRFZqRFZqRFZqRFZqRFZqRFZqRFZqRFZqRFZqRFZqRFZqRFZqRFZqRFZqRFZqRFZqRFZqRFZqRFZqRFZqRFZqRFZqRFZqRFZqRFZqRFZqRFZqRFZqRFZqRFZqRFZqRNZqRNZqRNZqRNZqRNZqRNZqRNZqRNZqRNZqRNZqRNZqRNZqRNZqRNZqRNZqRNZqRNZqRNZqRNZqRNZqRNZqRNZqRNZqRNZqRNZqRNZqRNZqRNZqRNZqRNZqRNZqRNZqRNZqRNZqRNZqRNZqRNZqRNZqRNZqRNZqRNZqRNZqRNZqRNZqRNZqRNZqRNZqRNZqRNZqRNZqRNZqRNZqRNZqRNZqRNZqRNZqRNZqRNZqRNZqRNZqRNZqRNZqRNZqRNZqRNZqRNZqRNZqRNZqRNZqRNZqRNZqRNZqRNZqRNZqRNZqRNZqRNZqRNZqRNZqRNZqRNZqRNZqRNZqRNZqRNZqRNZqRNZqRNZqRNZqRNZqRNZqRNZqRNZqRNZqRNZqRNZqRNZqRNZqRNZqRNZqRDZqRDZqRDZqRDZqRDZqRDZqRDZqRDZqRDZqRDZqRDZqRDZqRDZqRDZqRDZqRDZqRDZqRDZqRDZqRDZqRDZqRDZqRDZqRDZqRDZqRDZqRDZqRDZqRDZqRDZqRDZqRDZqRDZqRDZqRDZqRDZqRDZqRDZqRDZqRDZqRDZqRDZqRDZqRDZqRDZqRDZqRDZqRDZqRDZqRDZqRDZqRDZqRDZqRDZqRDZqRDZqRDZqRDZqRDZqRDZqRDZqRDZqRDZqRDZqRDZqRDZqRDZqRDZqRDZqRDZqRDZqRDZqRDZqRDZqRDZqRDZqRDZqRDZqRDZqRDZqRDZqRDZqRDZqRDZqRDZqRDZqRDZqRDZqRDZqRDZqRDZqRDZqRDZqRDZqRDZqRDZqRDZqRDZqRDZqRDZqRLZqRLZqRLZqRLZqRLZqRLZqRLZqRLZqRLZqRLZqRLZqRLZqRLZqRLZqRLZqRLZqRLZqRLZqRLZqRLZqRLZqRLZqRLZqRLZqRLZqRLZqRLZqRLZqRLZqRLZqRLZqRLZqRLZqRLZqRLZqRLZqRLZqRLZqRLZqRLZqRLZqRLZqRLZqRLZqRLZqRLZqRLZqRLZqRLZqRLZqRLZqRLZqRLZqRLZqRLZqRLZqRLZqRLZqRLZqRLZqRLZqRLZqRLZqRLZqRLZqRLZqRLZqRLZqRLZqRLZqRLZqRLZqRLZqRLZqRLZqRLZqRLZqRLZqRLZqRLZqRLZqRLZqRLZqRLZqRLZqRLZqRLZqRLZqRLZqRLZqRLZqRLZqRLZqRLZqRLZqRLZqRLZqRLZqRLZqRLZqR2YWGZI8nDH7C4ScCfuLATyT8RMFPNPzEwE/Ad27wnRt85wbfucF3bvCdG3znBt+5wXdu8J0bfOcO37nDd+7wnTt85w7fucN37vCdO3znDt+5w3ce8J0HfOcB33nAdx7wnQd85wHfecB3HvCdB3znB77zA9/5ge/8wHd+4Ds/8J0f+M4PfOcHvvMD33nCd57wnSd85wnfecJ3nvCdJ3znCd95wnee8J0XfOcF33nBd17wnRd85wXfecF3XvCdF3znBd95w3fe8J03fOcN33nDd97wnTd85w3fecN33vCdD3znA9/5wHc+8J0PfOcD3/nAdz7wnQ985wPf+cJ3vvCdL3znC9/5wne+8J0vfOcL3/nCdw7ncAbncAbncAbncAbncI8/vsJPJPxEwU80/MTAT8B3DudwBudwBudwBudwBudwBudwBudwBudwBudwBudwBudwBudwBudwBudwBudwBudwBudwBudwBudwBudwBudwBudwBudwBudwBudwBudwBudwBudwBudwBudwBudwBudwBudwBudwBudwBudwBudwBudwBudwBudwBudwBudwBudwBudwBudwBudwBudwBudwBudwBudwBudwBudwBudwBudwBudwBudwBudwBudwBudwBudwBudwBudwBudwBudwBudwBudwBudwBudwBudwBudwBudwBudwBudwBudwBudwBudwBudwBudwBudwBudwBudwBudwBudwBudwBudwBudwBudwBudwBudwBudwDudwDudwDudwDudwDudwDudwDudwDudwDudwDudwDudwDudwDudwDudwDudwDudwDudwDudwDudwDudwDudwDudwDudwDudwDudwDudwDudwDudwDudwDudwDudwDudwDudwDudwDudwDudwDudwDudwDudwDudwDudwDudwDudwDudwDudwDudwDudwDudwDudwDudwDudwDudwDudwDudwDudwDudwDudwDudwDudwDudwDudwDudwDudwDudwDudwDudwDudwDudwDudwDudwDudwDudwDudwDudwDudwDudwDudwDudwDudwDudwDudwDudwDudwDudwDudwDudwDudwDudwDudwDudwDudwDudwDudwDudwDudwDudwDudwDudwDudwDudwAedwAedwAedwAedwAedwAedwAedwAedwAedwAedwAedwAedwAedwAedwAedwAedwAedwAedwAedwAedwAedwAedwAedwAedwAedwAedwAedwAedwAedwAedwAedwAedwAedwAedwAedwAedwAedwAedwAedwAedwAedwAedwAedwAedwAedwAedwAedwAedwAedwAedwAedwAedwAedwAedwAedwAedwAedwAedwAedwAedwAedwAedwAedwAedwAedwAedwAedwAedwAedwAedwAedwAedwAedwAedwAedwAedwAedwAedwAedwAedwAedwAedwAedwAedwAedwAedwAedwAedwAedwAedwAedwAedwAedwAedwAedwAedwAedwAedwAedwAedwB87hDpzDHTiHO3AOd+Ac7sA53IFzuAPncAfO4Q6cwx04hztwDnfgHO7AOdyBc7gD53AHzuEOnMMdOIc7cA534BzuwDncgXO4A+dwB87hDpzDHTiHO3AOd+Ac7sA53IFzuAPncAfO4Q6cwx04hztwDnfgHO7AOdyBc7gD53AHzuEOnMMdOIc7cA534BzuwDncgXO4A+dwB87hDpzDHTiHO3AOd+Ac7sA53IFzuAPncAfO4Q6cwx04hztwDnfgHO7AOdyBc7gD53AHzuEOnMMdOIc7cA534Bzu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tOyJ9sM03H010Wsp7IdOZD9sorji5UQuN1HITXTkJkryRBH1fOczLycquYn6x030bePnm4lGbqKlT/T8o0LUy4mczezw+GQik5vI5SZiMzv6+ZecsS//NOJHbqKUm6jkJmq1ieLP82j2+Q3PXvk38zzPMMIZTjgjCGccwhl/flP3wzLZvF6eUYQzmnDGEM7Y+884F+EMI5zhhDOCcMYhnEHY80PY80PY80PY80PY8yTseRL2PAl7noQ9T8KeJ2HPk7DnSdjzJOx5Eva8CHtehD0vwp4XYc+LsOdF2PMi7HkR9rwIe16EPW/Cnjdhz5uw503Y8ybseRP2vAl73oQ9b8KeN2HPh7DnQ9jzIez5EPZ8CHs+hD0fwp4PYc+HsOdD2PMl7PkS9nwJe76EPV/Cni9hz5ew50vY8yXs+d6/53FdhDOMcIYTzgjCGYdwRhLOKMIZTThjCGcQ9twIe26EPTfCnhthz42w50bYcyPsuRH23Ah7boQ9d8KeO2HPnbDnTthzJ+y5E/bcCXvuhD13wp47Yc+DsOdB2PMg7HkQ9jwIe07owwWhDxeEPlwQ+nBB6MMFoQ8XhD5cEPpwQejDBaEPF4Q+XBD6cEHowwWhDxeEPlwQ+nBB6MMFoQ8XhD5cEPpwQejDBaEPF4Q+XBD6cEHowwWhDxeEPlwQ+nBB6MMFoQ8XhD5cEPpwQejDBaEPF4Q+XBD6cEHowwWhDxeEPlwQ+nBB6MMFoQ8XhD5cEPpwQejDBaEPF4Q+XBD6cEHowwWhDxeEPlwQ+nBB6MMFoQ8XhD5cEPpwQejDBaEPF4Q+XBD6cEHowwWhDxeEPlwQ+nBB6MMdQh/ufI8+XMzzjF///xdnOOGMIJxxCGck4YwinNGEM4Zwxt5/xvfow316BmHPjbDnRthzI+y5EfbcCHtuhD03wp4bYc+dsOdO2HMn7LkT9twJe+6EPXfCnjthz52w507Y8yDseRD2PAh7/rpH9vyv9V2//+428/z25vGPX3986omPt697377vffu59+331rd/XRf7fm9v97693/v2ce/bn3vf/k9t7X78B39tz8u3r3vfvu99+7n37ffWt8/r3re3e9/e7337uPftz71vf+/W5r1bm/dubf65rbX4+vb56u331rev6963t3vf3u99+7j37c+9b5/3vn3d+/Z979vfu7V179b2vVvb925t37u1fe/W9r1b2/dubd+7tX3v1va9W9v3bu3cu7Vz79bOvVs7927t3Lu1c+/Wzr1bO/du7dy7tXPv1u69W7v3bu3eu7V/56fAXs8fhOLffMuU9fFUvfVUv/XUvPXUvvFU/p0fFfrZUy//bVj1xzeebV//3X8k4Pm6KfEHnnv5b6Tq+YNM+no55b7z1OuGwqdP2VtP+VtPvfyNIT++6y/7uiRlH0/lW0+9vul9rqPb1x+Ilft86vXf/n521uu/z/30KX/rqXjrKfDf/F//+v8A" + }, + { + "name": "compute_note_hash_and_nullifier", + "function_type": "Unconstrained", + "is_internal": false, + "abi": { + "parameters": [ + { + "name": "contract_address", + "type": { + "kind": "struct", + "path": "aztec::protocol_types::address::AztecAddress", + "fields": [{ "name": "inner", "type": { "kind": "field" } }] + }, + "visibility": "private" + }, + { "name": "nonce", "type": { "kind": "field" }, "visibility": "private" }, + { "name": "storage_slot", "type": { "kind": "field" }, "visibility": "private" }, + { + "name": "serialized_note", + "type": { "kind": "array", "length": 3, "type": { "kind": "field" } }, + "visibility": "private" + } + ], + "param_witnesses": { + "contract_address": [{ "start": 0, "end": 1 }], + "nonce": [{ "start": 1, "end": 2 }], + "serialized_note": [{ "start": 3, "end": 6 }], + "storage_slot": [{ "start": 2, "end": 3 }] + }, + "return_type": { + "abi_type": { "kind": "array", "length": 4, "type": { "kind": "field" } }, + "visibility": "public" + }, + "return_witnesses": [6, 7, 8, 9] + }, + "bytecode": "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", + "debug_symbols": "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G44lG44lG44lG44lG44lG44lG44lG44lG44lG44lG44lG44lG44lG44lG44lG44lG44lG44lG44lG44lG44lG44lG44lG44lG44lG44lG44lm44lm44lm44lm44lmxcmbadjyaZjyaZjyaZjyaZjyaZjyaZjyaZjyaZjyaZjyaZjyaZjyaZjyaZjyaZjyaZjyaZjyaZjyaZjyaZjyaZjyaZjyaZjyaZjyaZjyaZjyaZjyaZjyaZjyaZjyaZjyaZjyaZjyaZjyaZjyaZjyaZjyaZjyaZjyaZjyaZjyaZjyaZjyaZjyaZjyaZjyaZjyaZjyaZjyaZjyaZjyaZjyaZjyaZjyaZjyaZjyaZjyaZjyaZjyaZjyaZjyaZjyaZjyaZjyaZjyaZjyaZjyaZjyaZjyaZjyaZjyaZjyaZjyaZjyaZjyaZjyaZjyaZjyaZjyaZjyaZjyaZjyaZjyaZjyaZjyaZjyaZjyaZjyaZjyaZjyaZjyaZjyaZjyaZjyaZjycqxZOVYsnIsWTmWrC5M2pZjycqxZOVYsnIsWTmWrBxLVo4lK8eSlWPJyrFk5ViycixZOZasHEtWjiUrx5KVY8nKsWTlWLJyLFk5lqwcS1aOJSvHkpVjycqxZOVYsnIsWTmWrBxLVo4lK8eSlWPJyrFk5ViycixZOZasHEtWjiUrx5KVY8nKsWTlWLJyLFk5lqwcS1aOJSvHkpVjycqxZOVYsnIsWTmWrBxLVo4lK8eSlWPJyrFk5ViycixZOZasHEtWjiUrx5KVY8nKsWTlWLJyLFk5lqwcS1aOJSvHkpVjycqxZOVYsnIsWTmWrBxLVo4lK8eSlWPJyrFk5ViycixZOZasHEtWjiUrx5KVY8nKsWTlWLJyLFk5lqwcS1aOJVuOJVuOJVuOJVuOJVsXJm2XY8mWY8mWY8mWY8mWY8mWY8mWY8mWY8mWY8mWY8mWY8mWY8mWY8mWY8mWY8mWY8mWY8mWY8mWY8mWY8mWY8mWY8mWY8mWY8mWY8mWY8mWY8mWY8mWY8mWY8mWY8mWY8mWY8mWY8mWY8mWY8mWY8mWY8mWY8mWY8mWY8mWY8mWY8mWY8mWY8mWY8mWY8mWY8mWY8mWY8mWY8mWY8mWY8mWY8mWY8mWY8mWY8mWY8mWY8mWY8mWY8mWY8mWY8mWY8mWY8mWY8mWY8mWY8mWY8mWY8mWY8mW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" + }, + { + "name": "broadcast", + "function_type": "Open", + "is_internal": false, + "abi": { + "parameters": [ + { + "name": "inputs", + "type": { + "kind": "struct", + "path": "aztec::abi::PublicContextInputs", + "fields": [ + { + "name": "call_context", + "type": { + "kind": "struct", + "path": "aztec::protocol_types::abis::call_context::CallContext", + "fields": [ + { + "name": "msg_sender", + "type": { + "kind": "struct", + "path": "aztec::protocol_types::address::AztecAddress", + "fields": [{ "name": "inner", "type": { "kind": "field" } }] + } + }, + { + "name": "storage_contract_address", + "type": { + "kind": "struct", + "path": "aztec::protocol_types::address::AztecAddress", + "fields": [{ "name": "inner", "type": { "kind": "field" } }] + } + }, + { + "name": "portal_contract_address", + "type": { + "kind": "struct", + "path": "aztec::protocol_types::address::EthAddress", + "fields": [{ "name": "inner", "type": { "kind": "field" } }] + } + }, + { + "name": "function_selector", + "type": { + "kind": "struct", + "path": "aztec::protocol_types::abis::function_selector::FunctionSelector", + "fields": [ + { "name": "inner", "type": { "kind": "integer", "sign": "unsigned", "width": 32 } } + ] + } + }, + { "name": "is_delegate_call", "type": { "kind": "boolean" } }, + { "name": "is_static_call", "type": { "kind": "boolean" } }, + { "name": "is_contract_deployment", "type": { "kind": "boolean" } }, + { + "name": "start_side_effect_counter", + "type": { "kind": "integer", "sign": "unsigned", "width": 32 } + } + ] + } + }, + { + "name": "block_header", + "type": { + "kind": "struct", + "path": "aztec::protocol_types::abis::block_header::BlockHeader", + "fields": [ + { "name": "note_hash_tree_root", "type": { "kind": "field" } }, + { "name": "nullifier_tree_root", "type": { "kind": "field" } }, + { "name": "contract_tree_root", "type": { "kind": "field" } }, + { "name": "l1_to_l2_message_tree_root", "type": { "kind": "field" } }, + { "name": "archive_root", "type": { "kind": "field" } }, + { "name": "public_data_tree_root", "type": { "kind": "field" } }, + { "name": "global_variables_hash", "type": { "kind": "field" } } + ] + } + }, + { + "name": "public_global_variables", + "type": { + "kind": "struct", + "path": "aztec::abi::PublicGlobalVariables", + "fields": [ + { "name": "chain_id", "type": { "kind": "field" } }, + { "name": "version", "type": { "kind": "field" } }, + { "name": "block_number", "type": { "kind": "field" } }, + { "name": "timestamp", "type": { "kind": "field" } } + ] + } + } + ] + }, + "visibility": "private" + }, + { + "name": "owner", + "type": { + "kind": "struct", + "path": "aztec::protocol_types::address::AztecAddress", + "fields": [{ "name": "inner", "type": { "kind": "field" } }] + }, + "visibility": "private" + } + ], + "param_witnesses": { "inputs": [{ "start": 0, "end": 19 }], "owner": [{ "start": 19, "end": 20 }] }, + "return_type": { + "abi_type": { + "kind": "struct", + "path": "aztec::protocol_types::abis::public_circuit_public_inputs::PublicCircuitPublicInputs", + "fields": [ + { + "name": "call_context", + "type": { + "kind": "struct", + "path": "aztec::protocol_types::abis::call_context::CallContext", + "fields": [ + { + "name": "msg_sender", + "type": { + "kind": "struct", + "path": "aztec::protocol_types::address::AztecAddress", + "fields": [{ "name": "inner", "type": { "kind": "field" } }] + } + }, + { + "name": "storage_contract_address", + "type": { + "kind": "struct", + "path": "aztec::protocol_types::address::AztecAddress", + "fields": [{ "name": "inner", "type": { "kind": "field" } }] + } + }, + { + "name": "portal_contract_address", + "type": { + "kind": "struct", + "path": "aztec::protocol_types::address::EthAddress", + "fields": [{ "name": "inner", "type": { "kind": "field" } }] + } + }, + { + "name": "function_selector", + "type": { + "kind": "struct", + "path": "aztec::protocol_types::abis::function_selector::FunctionSelector", + "fields": [{ "name": "inner", "type": { "kind": "integer", "sign": "unsigned", "width": 32 } }] + } + }, + { "name": "is_delegate_call", "type": { "kind": "boolean" } }, + { "name": "is_static_call", "type": { "kind": "boolean" } }, + { "name": "is_contract_deployment", "type": { "kind": "boolean" } }, + { + "name": "start_side_effect_counter", + "type": { "kind": "integer", "sign": "unsigned", "width": 32 } + } + ] + } + }, + { "name": "args_hash", "type": { "kind": "field" } }, + { "name": "return_values", "type": { "kind": "array", "length": 4, "type": { "kind": "field" } } }, + { + "name": "contract_storage_update_requests", + "type": { + "kind": "array", + "length": 16, + "type": { + "kind": "struct", + "path": "aztec::protocol_types::contrakt::storage_update_request::StorageUpdateRequest", + "fields": [ + { "name": "storage_slot", "type": { "kind": "field" } }, + { "name": "old_value", "type": { "kind": "field" } }, + { "name": "new_value", "type": { "kind": "field" } } + ] + } + } + }, + { + "name": "contract_storage_reads", + "type": { + "kind": "array", + "length": 16, + "type": { + "kind": "struct", + "path": "aztec::protocol_types::contrakt::storage_read::StorageRead", + "fields": [ + { "name": "storage_slot", "type": { "kind": "field" } }, + { "name": "current_value", "type": { "kind": "field" } } + ] + } + } + }, + { + "name": "public_call_stack_hashes", + "type": { "kind": "array", "length": 4, "type": { "kind": "field" } } + }, + { + "name": "new_commitments", + "type": { + "kind": "array", + "length": 16, + "type": { + "kind": "struct", + "path": "aztec::protocol_types::abis::side_effect::SideEffect", + "fields": [ + { "name": "value", "type": { "kind": "field" } }, + { "name": "counter", "type": { "kind": "integer", "sign": "unsigned", "width": 32 } } + ] + } + } + }, + { + "name": "new_nullifiers", + "type": { + "kind": "array", + "length": 16, + "type": { + "kind": "struct", + "path": "aztec::protocol_types::abis::side_effect::SideEffectLinkedToNoteHash", + "fields": [ + { "name": "value", "type": { "kind": "field" } }, + { "name": "note_hash", "type": { "kind": "field" } }, + { "name": "counter", "type": { "kind": "integer", "sign": "unsigned", "width": 32 } } + ] + } + } + }, + { "name": "new_l2_to_l1_msgs", "type": { "kind": "array", "length": 2, "type": { "kind": "field" } } }, + { + "name": "unencrypted_logs_hash", + "type": { "kind": "array", "length": 2, "type": { "kind": "field" } } + }, + { "name": "unencrypted_log_preimages_length", "type": { "kind": "field" } }, + { + "name": "block_header", + "type": { + "kind": "struct", + "path": "aztec::protocol_types::abis::block_header::BlockHeader", + "fields": [ + { "name": "note_hash_tree_root", "type": { "kind": "field" } }, + { "name": "nullifier_tree_root", "type": { "kind": "field" } }, + { "name": "contract_tree_root", "type": { "kind": "field" } }, + { "name": "l1_to_l2_message_tree_root", "type": { "kind": "field" } }, + { "name": "archive_root", "type": { "kind": "field" } }, + { "name": "public_data_tree_root", "type": { "kind": "field" } }, + { "name": "global_variables_hash", "type": { "kind": "field" } } + ] + } + }, + { + "name": "prover_address", + "type": { + "kind": "struct", + "path": "aztec::protocol_types::address::AztecAddress", + "fields": [{ "name": "inner", "type": { "kind": "field" } }] + } + } + ] + }, + "visibility": "public" + }, + "return_witnesses": [ + 20, 21, 22, 23, 24, 25, 26, 27, 28, 29, 30, 31, 32, 33, 34, 35, 36, 37, 38, 39, 40, 41, 42, 43, 44, 45, 46, + 47, 48, 49, 50, 51, 52, 53, 54, 55, 56, 57, 58, 59, 60, 61, 62, 63, 64, 65, 66, 67, 68, 69, 70, 71, 72, 73, + 74, 75, 76, 77, 78, 79, 80, 81, 82, 83, 84, 85, 86, 87, 88, 89, 90, 91, 92, 93, 94, 95, 96, 97, 98, 99, 100, + 101, 102, 103, 104, 105, 106, 107, 108, 109, 110, 111, 112, 113, 114, 115, 116, 117, 118, 119, 120, 121, 122, + 123, 124, 125, 126, 127, 128, 129, 130, 131, 132, 133, 134, 135, 136, 137, 138, 139, 140, 141, 142, 143, 144, + 145, 146, 147, 148, 149, 150, 151, 152, 153, 154, 155, 156, 157, 158, 159, 160, 161, 162, 163, 164, 165, 166, + 167, 168, 169, 170, 171, 172, 173, 174, 175, 176, 177, 178, 179, 180, 181, 182, 183, 184, 185, 186, 187, 188, + 189, 190, 191, 192, 193, 194, 195, 196, 197, 198, 199, 200, 201, 202, 203, 204, 205, 206, 207, 208, 209 + ] + }, + "bytecode": "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", + "debug_symbols": "1Z3NriVVckbfpcbIyojYO354FcsDZLelllq0ZZgh3t2J+/7QqgOXD9inV40oXWXmCYjai6pVuW798Olvf//Pb77/69+//e7T1z98uv6trk9f//sPn777n2++/ekL333/zf9+/+nr66tPf/n2v+5//vjVp//+69/+8unrmB//46ufrjfxehevD/H6JV6/xetTvL7E61u8frTrW9xvi/ttcb8t7rfF/ba43xb32+J+W9xvi/sdcb8j7nd++3/Prz67zLrj5cr7h+vt4hUvD8+TDxf/S9p1qTeYeoOrN4R6w1Jv2OoNqd5Q6g2t3qBu2tRNm/2Bn6jb++XCvfb7T9O3Z/vBZ8cfeLZf13650i+vnx8w6eKXURZnlP2sUSxzXhmVP2PU/RPqdZYEzTJPm6XWG7trPZrFL9AsBprFQbMEaJYFmmWDZknQLAWapUGzgLgbIO4GiLsB4m6AuBsg7gaIuwHiboC4GyDuBoi7C8TdBeLuAnF3gbi7QNxdIO4uEHcXiLsLxN0F4u4GcXeDuLtB3N0g7m4QdzeIuxvE3Q3i7gZxd4O4myDuJoi7CeJugribIO4miLsJ4m6CuJsg7iaIuwXiboG4WyDuFoi7BeJugbhbIO4WiLsF4m6BuNsg7jaIuw3iboO42yDuNoi7DeJug7jbIO42iLsD4u6AuDsg7g6IuwPi7oC4Oxzu+vXEMz2vj71/1WSPZjHQLE/7uevrbUf3Dx/uaINmSdAsBZqlQbM87dcMff85yMvFfav/91mu/TKLXaBZ7Hk7qteWxHf0P+1Iu/plcv9iJw/m5HZNvT7b7Prp4z+ffX3Bs+8vePbn/f6rV739OmA9/Pk7nFme2PF8PIuBZnHQLM/7/Vd3vs4y5o9mWaBZNmiWBM1SoFkaNMtwZnlix/PxLM/j7uy33yNP1qNZHDRLgGZ5HnfH532WR/8/el6vcl88bxfb9WiWBs0ynFme16v8hlkMNIuDZgnQLAs0ywbNkqBZQNxdIO4uEHc3iLsbxN0N4u4GcXeDuLtB3N0g7m4QdzeIuxvE3QRxN0HcTRB3E8TdBHE3QdxNEHcTxN0EcTdB3C0QdwvE3QJxt0DcLRB3C8TdAnG3QNwtEHcLxN0GcbdB3G0QdxvE3QZxt0HcbRB3G8TdBnG3QdwdEHcHxN0BcXdA3B0QdwfE3QFxd0DcHRB3h8PduDjcjYvD3bg43I2Lw924ONyNi8PduDjcjYvD3bg43I0LxF0DcddA3DUQdw3EXQNx10DcNRB3DcRdA3HXQNx1EHcdxF0HcddB3HUQdx3EXQdx10HcdRB3HcTdAHE3QNwNEHcDxN0AcTdA3A0Qd0G9WoB6tQD1agHq1QLUqwWoVwtQrxagXi1AvVqAerUA9WoB6tUC1KsFqFcLUK8WoF4tQL1agHq1APVqAerVAtSrBahXC1CvFqBeLUC9WoB6tQD1agHq1QLUqwWoVwtQrxagXi1AvVqAerUA9WoB6tUC1KsFqFcLUK8WoF4tQL1agHq1eGYLle8Xdz2aJUCzPO/nrm9/vdh3PJqlQLM0aJbhzPLE5ufjWZ72a4YPv/d9PK/5+Q2zPI8v9589vF68rn/+ez4+v/o3fG/yeGIj9OfPvr/g2fMLnr2+4NmbMPv/z7Ke2BV9PIuBZnHQLAi2v8yCYPXLLAj2vszC+TucFujvcFqgv8NpPbEr+nCWJ3ZFH89ioFkcNEuAZlmgWTh/d94yEHcNxF0DcddA3HUQdx3EXQdx10HcdRB3HcRdB3HXQdx1EHcdxN0AcTdA3A0QdwPE3QBxN0DcDRB3A8TdJ/Yz5q9/X6Xboz+jWk/sZz6exUGzBGiWBZplg2bJf80s9WiWAs3SoFmGM8u+QLMYaBYHzfJE7s7r37Xqbo/48sR+5uNZNmiWJ3K339+xsUe/lnpiJ/LhLE/sRD6exUCzOGiWAM3yPL74fuOu16Mz/cRO5ONZEjRLgWZp0CzDmaUu0Cz2xFl+/f3T9cRO5ONZAjTLAs2yQbNw3rVfxXnXfhXnXftVnHftV3PetV8N4m6DuNsg7jaIuw3iLqhxWqDGaYEapwVqnBaocVoD4u6AuDsg7g6IuwPi7oC4OyDuDoi7w+Huvjjc3ReHdfvisG4L3cQ/bhDihpcbTL3B1RtCvWGpN2z1hlRvKPWGVm9QN+3qpl3dtKubdnXTrm7a1U27umlXN+3qpl3ddKibDnXToW461E2HuulQNx3qpkPddKibDnXTS930Uje91E0vddNL3fRSN73UTS9100vd9FI3vdVNb3XTW930Vje91U1vddNb3fRWN73VTW9106luOtVNp7rpVDed6qZT3XSqm05106luOtVNl7rpUjdd6qZL3XSpmy5106VuutRNl7rpUjfd6qZb3XSrm251061uutVNt7rpVjfd6qZb3fSomx5106NuetRNj7rpUTc96qZH3fSomx5x03ld6g2m3uDqDaHesNQbtnpDqjeUekOrN6ibVh1Zqo4sVUeWqiNL1ZGl6shSdWSpOrJUHVmqjixVR5aqI0vVkaXqyFJ1ZKk6slQdWaqOLFVHlqojS9WRperIUnVkqTqyVB1Zqo4sVUeWqiNL1ZGl6shSdWSpOrJUHVmqjixVR5aqI0vVkaXqyFJ1ZKk6slQdWaqOLFVHlqojS9WRperIUnVkqTqyVB1Zqo4sVUeWqiNL1ZGl6shSdWSpOrJUHVmqjixVR5aqI0vVkaXqyFJ1ZKk6slQdWaqOLFVHlqojS9WRperIUnVkqTqyVB1Zqo4sVUeWqiNL1ZGl6shSdWSpOrJUHVmqjixVR5aqI0vVkaXqyFJ1ZKk6slQdWaqOrFRHVqojK9WRlerISnVkpTqyUh1ZqY6sVEdWqiMr1ZGV6shKdWSlOrJSHVmpjqxUR1aqIyvVkZXqyEp1ZKU6slIdWamOrFRHVqojK9WRlerISnVkpTqyUh1ZqY6sVEdWqiMr1ZGV6shKdWSlOrJSHVmpjqxUR1aqIyvVkZXqyEp1ZKU6slIdWamOrFRHVqojK9WRlerISnVkpTqyUh1ZqY6sVEdWqiMr1ZGV6shKdWSlOrJSHVmpjqxUR1aqIyvVkZXqyEp1ZKU6slIdWamOrFRHVqojK9WRlerISnVkpTqyUh1ZqY6sVEdWqiMr1ZGV6shKdWSlOrJSHVmpjqxUR1aqIyvVkZXqyEp1ZKU6slIdWamOrFRHVqojK9WRlerIWnVkrTqyVh1Zq46sVUfWqiNr1ZG16shadWStOrJWHVmrjqxVR9aqI2vVkbXqyFp1ZK06slYdWauOrFVH1qoja9WRterIWnVkrTqyVh1Zq46sVUfWqiNr1ZG16shadWStOrJWHVmrjqxVR9aqI2vVkbXqyFp1ZK06slYdWauOrFVH1qoja9WRterIWnVkrTqyVh1Zq46sVUfWqiNr1ZG16shadWStOrJWHVmrjqxVR9aqI2vVkbXqyFp1ZK06slYdWauOrFVH1qoja9WRterIWnVkrTqyVh1Zq46sVUfWqiNr1ZG16shadWStOrJWHVmrjqxVR9aqI2vVkbXqyFp1ZK06slYdWauOrFVH1qoja9WRterIWnVkrTqyVh1Zq45sVEc2qiMb1ZGN6shGdWSjOrJRHdmojmxURzaqIxvVkY3qyEZ1ZKM6slEd2aiObFRHNqojG9WRjerIRnVkozqyUR3ZqI5sVEc2qiMb1ZGN6shGdWSjOrJRHdmojmxURzaqIxvVkY3qyEZ1ZKM6slEd2aiObFRHNqojG9WRjerIRnVkozqyUR3ZqI5sVEc2qiMb1ZGN6shGdWSjOrJRHdmojmxURzaqIxvVkY3qyEZ1ZKM6slEd2aiObFRHNqojG9WRjerIRnVkozqyUR3ZqI5sVEc2qiMb1ZGN6shGdWSjOrJRHdmojmxURzaqIxvVkY3qyEZ1ZKM6slEd2aiObFRHNqojG9WRjerIRnVkozqyUR3ZqI5sVEc2qiMb1ZGN6sjsUiXZfYfJd7h8R8h3LPmOLd+R8h0l39HyHfLOTd65yTs3eecm79zknZu8c5N3bvLOTd65yTt3eecu79zlnbu8c5d37vLOXd65yzt3eecu7zzknYe885B3HvLOQ955yDsPeech7zzknYe88yXvfMk7X/LOl7zzJe98yTtf8s6XvPMl73zJO9/yzre88y3vfMs73/LOt7zzLe98yzvf8s63vPOUd57yzlPeeco7T3nnKe885Z2nvPOUd57yzkveeck7L3nnJe+85J2XvPOSd17yzkveeck7b3nnLe+85Z23vPOWd97yzlveecs7b3nnLe985J2PvPORdz7yzkfe+cg7H3nnI+985J3LHs5kD2eyhzPZw5ns4e5fvsp3bPmOlO8o+Y6W75B3Lns4kz2cyR7OZA9nsocz2cOZ7OFM9nAmeziTPZzJHs5kD2eyhzPZw5ns4Uz2cCZ7OJM9nMkezmQPZ7KHM9nDmezhTPZwJns4kz2cyR7OZA9nsocz2cOZ7OFM9nAmeziTPZzJHs5kD2eyhzPZw5ns4Uz2cCZ7OJM9nMkezmQPZ7KHM9nDmezhTPZwJns4kz2cyR7OZA9nsocz2cOZ7OFM9nAmeziTPZzJHs5kD2eyhzPZw5ns4Uz2cCZ7OJM9nMkezmQPZ7KHM9nDmezhTPZwJns4kz2cyR7OZA9nsocz2cOZ7OFM9nAmeziTPZzJHs5kD2eyhzPZw5ns4Uz2cCZ7OJM9nMsezmUP57KHc9nDuezhXPZwLns4lz2cyx7OZQ/nsodz2cO57OFc9nAueziXPZzLHs5lD+eyh3PZw7ns4Vz2cC57OJc9nMsezmUP57KHc9nDuezhXPZwLns4lz2cyx7OZQ/nsodz2cO57OFc9nAueziXPZzLHs5lD+eyh3PZw7ns4Vz2cC57OJc9nMsezmUP57KHc9nDuezhXPZwLns4lz2cyx7OZQ/nsodz2cO57OFc9nAueziXPZzLHs5lD+eyh3PZw7ns4Vz2cC57OJc9nMsezmUP57KHc9nDuezhXPZwLns4lz2cyx7OZQ/nsodz2cO57OFc9nAueziXPZzLHs5lD+eyh3PZw7ns4Vz2cC57OJc9nMsezmUP57KHc9nDhezhQvZwIXu4kD1cyB4uZA8XsocL2cOF7OFC9nAhe7iQPVzIHi5kDxeyhwvZw4Xs4UL2cCF7uJA9XMgeLmQPF7KHC9nDhezhQvZwIXu4kD1cyB4uZA8XsocL2cOF7OFC9nAhe7iQPVzIHi5kDxeyhwvZw4Xs4UL2cCF7uJA9XMgeLmQPF7KHC9nDhezhQvZwIXu4kD1cyB4uZA8XsocL2cOF7OFC9nAhe7iQPVzIHi5kDxeyhwvZw4Xs4UL2cCF7uJA9XMgeLmQPF7KHC9nDhezhQvZwIXu4kD1cyB4uZA8XsocL2cOF7OFC9nAhe7iQPVzIHi5kDxeyhwvZw4Xs4UL2cCF7uJA9XMgeLmQPF7KHC9nDhezhQvZwIXu4kD3ckj3ckj3ckj3ckj3ckj3ckj3ckj3ckj3ckj3ckj3ckj3ckj3ckj3ckj3ckj3ckj3ckj3ckj3ckj3ckj3ckj3ckj3ckj3ckj3ckj3ckj3ckj3ckj3ckj3ckj3ckj3ckj3ckj3ckj3ckj3ckj3ckj3ckj3ckj3ckj3ckj3ckj3ckj3ckj3ckj3ckj3ckj3ckj3ckj3ckj3ckj3ckj3ckj3ckj3ckj3cEjzcV59d5nX/+cnLpfePl71dvq4Hl1u9XXy9P9n7bZYEzVKgWRo0y3BmEUzq+VkMNIuDZgnQLAs0C4i7CeJugribIO4miLsF4m6BuFsg7haIuwXiboG4WyDuFoi7BeJugbjbIO42iLsN4m6DuNsg7jaIuw3iboO42yDuNoi7A+LugLg7IO4OiLsD4u6AuDsg7g6IuwPi7nC4uy8Od/fF4e6+ONzdF4e7++Jwd18c7u6Lw919cbi7Lw539wXiroG4ayDuGoi7BuKugbhrIO4aiLsG4q6BuGsg7jqIuw7iroO46yDuOoi7DuKug7jrIO46iLsO4m6AuBsg7gaIuwHiboC4GyDuBoi7AeJugLgbIO4uEHcXiLsLxN0F4u4CcXeBuLtA3F0g7i4QdxeIuxvE3Q3i7gZxd4O4u0HcBfVqG9SrbVCvtkG92gb1ahvUq21Qr7ZBvdoG9Wob1KttUK+2Qb3aBvVqG9SrbVCvtkG92gb1ahvUq21Qr7ZBvdoG9Wob1KttUK+2Qb3aBvVqG9SrbVCvtkG92gb1ahvUq21Qr7ZBvdoG9Wob1KttUK+2Qb3aBvVqG9SrbVCvtkG92gb1ahvUq21Qr7ZBvdoG9WoJ6tUS1KslqFdLUK+WF4e7CerVEtSrJahXS1CvlqBeLUG9WoJ6tQT1agnq1RLUqyWoV0tQr5agXi1BvVqCerUE9WoJ6tUS1KslqFdLUK+WoF4tQb1agnq1BPVqCerVEtSrJahXS1CvlqBeLUG9WoJ6tQT1agnq1RLUqyWoV0tQr5agXi1BvVqCerUE9WoJ6tUS1KslqFdLUK+WoF4tQb1agnq1BPVqCerVEtSrJahXS1CvlqBeLUG9WoJ6tQT1agnq1RLUqyWoV0tQr5agXi1BvVqCerUE9WoJ6tUS1KslqFdLUK+WoF4tQb1agnq1BPVqCerVEtSrJahXS1CvlqBeLUG9WoJ6tQT1agnq1RLUqyWoV0tQr5agXi1BvVqCerUE9WoJ6tUS1KslqFdLUK+WoF4tQb1agnq1AvVqBerVCtSrFahXq4vD3QL1agXq1QrUqxWoVytQr1agXq1AvVqBerUC9WoF6tUK1KsVqFcrUK9WoF6tQL1agXq1AvVqBerVCtSrFahXK1CvVqBerUC9WoF6tQL1agXq1QrUqxWoVytQr1agXq1AvVqBerUC9WoF6tUK1KsVqFcrUK9WoF6tQL1agXq1AvVqBerVCtSrFahXK1CvVqBerUC9WoF6tQL1agXq1QrUqxWoVytQr1agXq1AvVqBerUC9WoF6tUK1KsVqFcrUK9WoF6tQL1agXq1AvVqBerVCtSrFahXK1CvVqBerUC9WoF6tQL1agXq1QrUqxWoVytQr1agXq1AvVqBerUC9WoF6tUK1KsVqFcrUK9WoF6tQL1agXq1AvVqBerVCtSrFahXK1CvVqBerUC9WoN6tQb1ag3q1RrUq/XF4W6DerUG9WoN6tUa1Ks1qFdrUK/WoF6tQb1ag3q1BvVqDerVGtSrNahXa1Cv1qBerUG9WoN6tQb1ag3q1RrUqzWoV2tQr9agXq1BvVqDerUG9WoN6tUa1Ks1qFdrUK/WoF6tQb1ag3q1BvVqDerVGtSrNahXa1Cv1qBerUG9WoN6tQb1ag3q1RrUqzWoV2tQr9agXq1BvVqDerUG9WoN6tUa1Ks1qFdrUK/WoF6tQb1ag3q1BvVqDerVGtSrNahXa1Cv1qBerUG9WoN6tQb1ag3q1RrUqzWoV2tQr9agXq1BvVqDerUG9WoN6tUa1Ks1qFdrUK/WoF6tQb1ag3q1BvVqDerVGtSrNahXa1Cv1qBerUG9WoN6tQb1ag3q1RrUqzWoV2tQr9agXm1AvdqAerUB9WoD6tXm4nB3QL3agHq1AfVqA+rVBtSrDahXG1CvNqBebUC92oB6tQH1agPq1QbUqw2oVxtQrzagXm1AvdqAerUB9WoD6tUG1KsNqFcbUK82oF5tQL3agHq1AfVqA+rVBtSrDahXG1CvNqBebUC92oB6tQH1agPq1QbUqw2oVxtQrzagXm1AvdqAerUB9WoD6tUG1KsNqFcbUK82oF5tQL3agHq1AfVqA+rVBtSrDahXG1CvNqBebUC92oB6tQH1agPq1QbUqw2oVxtQrzagXm1AvdqAerUB9WoD6tUG1KsNqFcbUK82oF5tQL3agHq1AfVqA+rVBtSrDahXG1CvNqBebUC92oB6tQH1agPq1QbUqw2oVxtQrzagXm1AvdqAerUB9WoD6tUG1KsNqFcbTq92fw3D3ftrGO7eX8Nw9/4ahrv31zDcvb+G4e79NQx3769huHt/DcPd+2sg7nJ6tXsWEHc5vdo9C4i7nF7tngXEXU6vds8C4i6nV7tnAXGX06vds4C4y+nV7llA3OX0avcsIO5yerV7FhB3Ob3aPQuIu5xe7Z4FxF1Or3bPAuIup1e7ZwFxl9Or3bOAuMvp1e5ZQNzl9Gr3LCDucnq1exYQdzm92j0LiLucXu2eBcRdTq92zwLiLqdXu2cBcZfTq92zgLjL6dXuWUDc5fRq9ywg7nJ6tXsWEHc5vdo9C4i7nF7tngXEXU6vds8C4i6nV7tnAXGX06vds4C4y+nV7llA3OX0avcsIO5yerV7FhB3Ob3aPQuIu5xe7Z4FxF1Or3bPAuIup1e7ZwFxl9Or3bOAuMvp1e5ZQNzl9Gr3LCDucnq1exYQdzm92j0LiLucXu2eBcRdTq92zwLiLqdXu2fhcNdAvZqBejUD9WoG6tXs4nDXQL2agXo1A/VqBurVDNSrGahXM1CvZqBezUC9moF6NQP1agbq1QzUqxmoVzNQr2agXs1AvZqBejUD9WoG6tUM1KsZqFczUK9moF7NQL2agXo1A/VqBurVDNSrGahXM1CvZqBezUC9moF6NQP1agbq1QzUqxmoVzNQr2agXs1AvZqBejUD9WoG6tUM1KsZqFczUK9moF7NQL2agXo1A/VqBurVDNSrGahXM1CvZqBezUC9moF6NQP1agbq1QzUqxmoVzNQr2agXs1AvZqBejUD9WoG6tUM1KsZqFczUK9moF7NQL2agXo1A/VqBurVDNSrGahXM1CvZqBezUC9moF6NQP1agbq1QzUqxmoVzNQr2agXs1AvZqBejUD9WoG6tUM1KsZqFczUK/moF7NQb2ag3o1B/VqfnG466BezUG9moN6NQf1ag7q1RzUqzmoV3NQr+agXs1BvZqDejUH9WoO6tUc1Ks5qFdzUK/moF7NQb2ag3o1B/VqDurVHNSrOahXc1Cv5qBezUG9moN6NQf1ag7q1RzUqzmoV3NQr+agXs1BvZqDejUH9WoO6tUc1Ks5qFdzUK/moF7NQb2ag3o1B/VqDurVHNSrOahXc1Cv5qBezUG9moN6NQf1ag7q1RzUqzmoV3NQr+agXs1BvZqDejUH9WoO6tUc1Ks5qFdzUK/moF7NQb2ag3o1B/VqDurVHNSrOahXc1Cv5qBezUG9moN6NQf1ag7q1RzUqzmoV3NQr+agXs1BvZqDejUH9WoO6tUc1Ks5qFdzUK/moF7NQb2ag3o1B/VqDurVHNSrOahXC1CvFqBeLUC9WoB6tbg43A1QrxagXi1AvVqAerUA9WoB6tUC1KsFqFcLUK8WoF4tQL1agHq1APVqAerVAtSrBahXC1CvFqBeLUC9WoB6tQD1agHq1QLUqwWoVwtQrxagXi1AvVqAerUA9WoB6tXica92f8TrbffP7vj1icZfR5/y92keXWrX1H652Oz66XEvl5vZ20jJG6l4IzVvpMGN9Dhp+9eOZLyRnDdS8EZavJF49F48ei8evReP3otH782j9/7j9LZr1fvHrA+GWvH6L2Cr+9ef3GEv1/Z6v3Rfb8P7lzx8fMnDry95+I//L2Mf/K7FPF8/w+7fF/78Qz6/+F7164Pn/d9z59s8CZunYPM0bJ5hzZMXbB6DzeOweQI2z4LNA+NzwvicMD4njM8J43PB+FwwPheMzwXjc8H4XDA+F4zPBeNzwfhcMD43jM8N43PD+NwwPjeMzw3jc8P43DA+N4zPDePzwPg8MD4PjM8D4/PA+DwwPg+MzwPj88D4PCw+r4vF53Wx+LwuFp/XxeLzulh8XheLz+ti8XldLD6vi8XndcH4bDA+G4zPBuOzwfhsMD4bjM8G47PB+GwwPhuMzw7js8P47DA+O4zPDuOzw/jsMD47jM8O47PD+BwwPgeMzwHjc8D4HDA+B4zPAeNzwPgcMD4HjM8LxucF4/OC8XnB+LxgfF4wPi8YnxeMzwvG5wXj84bxecP4vGF83jA+bxifYf3ggvWDC9YPLlg/uGD94IL1gwvWDy5YP7hg/eCC9YML1g8uWD+4YP3ggvWDC9YPLlg/uGD94IL1gwvWDy5YP7hg/eCC9YML1g8uWD+4YP3ggvWDC9YPLlg/uGD94IL1gwvWDy5YP7hg/eCC9YML1g8uWD+4YP3ggvWDC9YPrmf3g+t6/V53y+bn83x+qVu+XOq+3y6deht9f7mj55c7+i/8D2L5++j266PXtV6uraq3S+v9I/r8R8zpj9i/VA7+mR9h5z/Cz39EnP+Idf4j9vmPyPMfUec/os9/xPnTbedPt50/3Xb+dNv5023nT7edP912/nTb+dNt50+3nT/dfv50+/nT7edPt58/3X7+dPv50+3nT7efP91+/nT7+dMd5093nD/dcf50x/nTHedPd5w/3XH+dMf50x3nT3ecP93r/Ole50/3On+61/nTvc6f7nX+dK/zp3udP93r/Ole50/3Pn+69/nTvc+f7n3+dO/zp3ufP937/One50/3Pn+69/nTnedPd54/3Xn+dOf5053nT3eeP915/nTn+dOd5093nj/ddf501/nTXedPd50/3XX+dNf5013nT3edP911/nTX+dPd5093nz/dff509/nT3edPd58/3X3+dPf5093nT3efP91z/nTP+dM950/3nD/dc/50z/nTPedP9/l31fb5d9X2+XfV8vy7ann+XbU8/65ann9XLa91/iP2+Y/I8x9R5z+iz3/E+dN9/l21PP+uWp5/Vy3Pv6uW599Vy/PvquX5d9Xy/Ltqef5dtTz/rlqef1ctz7+rluffVcvz76rl+XfV8vy7ann+XbU8/65ann9XLc+/q5bn31XL8++q5fl31fL8u2p5/l21PP+uWh5/Vy1+4ZsevyZoH4Vt3a/J4v3D92Zxxdvj6+zj++zj5+jjH7/+9uc93s4+3s8+Ps4+fp19/D77+D90aif26+NnPXx8nX18n338HH38vs4+3s4+3s8+Ps4+fp19/D77+LOndp89tfvsqd1/7NRavD9+P3h8Xmcfb2cf72cfH2cfv84+fp99fJ59fJ19fJ99/NlTW2dPbZ09tXX21NbZU1tnT22dPbV19tTW2VNbZ09tnT21ffbU9tlT22dPbZ89tX321PbZU9tnT22fPbV99tT22VM7Z0/tnD21c/bUztlT+/gFK79ev8eYr8+/IVz8wvf3+vCu/l13ze+46xe+sdaHd9nvuuvhfw278nVLZvG+qH0b8B9//D8=" + }, + { + "name": "constructor", + "function_type": "Secret", + "is_internal": false, + "abi": { + "parameters": [ + { + "name": "inputs", + "type": { + "kind": "struct", + "path": "aztec::abi::PrivateContextInputs", + "fields": [ + { + "name": "call_context", + "type": { + "kind": "struct", + "path": "aztec::protocol_types::abis::call_context::CallContext", + "fields": [ + { + "name": "msg_sender", + "type": { + "kind": "struct", + "path": "aztec::protocol_types::address::AztecAddress", + "fields": [{ "name": "inner", "type": { "kind": "field" } }] + } + }, + { + "name": "storage_contract_address", + "type": { + "kind": "struct", + "path": "aztec::protocol_types::address::AztecAddress", + "fields": [{ "name": "inner", "type": { "kind": "field" } }] + } + }, + { + "name": "portal_contract_address", + "type": { + "kind": "struct", + "path": "aztec::protocol_types::address::EthAddress", + "fields": [{ "name": "inner", "type": { "kind": "field" } }] + } + }, + { + "name": "function_selector", + "type": { + "kind": "struct", + "path": "aztec::protocol_types::abis::function_selector::FunctionSelector", + "fields": [ + { "name": "inner", "type": { "kind": "integer", "sign": "unsigned", "width": 32 } } + ] + } + }, + { "name": "is_delegate_call", "type": { "kind": "boolean" } }, + { "name": "is_static_call", "type": { "kind": "boolean" } }, + { "name": "is_contract_deployment", "type": { "kind": "boolean" } }, + { + "name": "start_side_effect_counter", + "type": { "kind": "integer", "sign": "unsigned", "width": 32 } + } + ] + } + }, + { + "name": "block_header", + "type": { + "kind": "struct", + "path": "aztec::protocol_types::abis::block_header::BlockHeader", + "fields": [ + { "name": "note_hash_tree_root", "type": { "kind": "field" } }, + { "name": "nullifier_tree_root", "type": { "kind": "field" } }, + { "name": "contract_tree_root", "type": { "kind": "field" } }, + { "name": "l1_to_l2_message_tree_root", "type": { "kind": "field" } }, + { "name": "archive_root", "type": { "kind": "field" } }, + { "name": "public_data_tree_root", "type": { "kind": "field" } }, + { "name": "global_variables_hash", "type": { "kind": "field" } } + ] + } + }, + { + "name": "contract_deployment_data", + "type": { + "kind": "struct", + "path": "aztec::protocol_types::contrakt::deployment_data::ContractDeploymentData", + "fields": [ + { + "name": "deployer_public_key", + "type": { + "kind": "struct", + "path": "aztec::protocol_types::grumpkin_point::GrumpkinPoint", + "fields": [ + { "name": "x", "type": { "kind": "field" } }, + { "name": "y", "type": { "kind": "field" } } + ] + } + }, + { "name": "constructor_vk_hash", "type": { "kind": "field" } }, + { "name": "function_tree_root", "type": { "kind": "field" } }, + { "name": "contract_address_salt", "type": { "kind": "field" } }, + { + "name": "portal_contract_address", + "type": { + "kind": "struct", + "path": "aztec::protocol_types::address::EthAddress", + "fields": [{ "name": "inner", "type": { "kind": "field" } }] + } + } + ] + } + }, + { + "name": "private_global_variables", + "type": { + "kind": "struct", + "path": "aztec::abi::PrivateGlobalVariables", + "fields": [ + { "name": "chain_id", "type": { "kind": "field" } }, + { "name": "version", "type": { "kind": "field" } } + ] + } + } + ] + }, + "visibility": "private" + } + ], + "param_witnesses": { "inputs": [{ "start": 0, "end": 23 }] }, + "return_type": { + "abi_type": { + "kind": "struct", + "path": "aztec::protocol_types::abis::private_circuit_public_inputs::PrivateCircuitPublicInputs", + "fields": [ + { + "name": "call_context", + "type": { + "kind": "struct", + "path": "aztec::protocol_types::abis::call_context::CallContext", + "fields": [ + { + "name": "msg_sender", + "type": { + "kind": "struct", + "path": "aztec::protocol_types::address::AztecAddress", + "fields": [{ "name": "inner", "type": { "kind": "field" } }] + } + }, + { + "name": "storage_contract_address", + "type": { + "kind": "struct", + "path": "aztec::protocol_types::address::AztecAddress", + "fields": [{ "name": "inner", "type": { "kind": "field" } }] + } + }, + { + "name": "portal_contract_address", + "type": { + "kind": "struct", + "path": "aztec::protocol_types::address::EthAddress", + "fields": [{ "name": "inner", "type": { "kind": "field" } }] + } + }, + { + "name": "function_selector", + "type": { + "kind": "struct", + "path": "aztec::protocol_types::abis::function_selector::FunctionSelector", + "fields": [{ "name": "inner", "type": { "kind": "integer", "sign": "unsigned", "width": 32 } }] + } + }, + { "name": "is_delegate_call", "type": { "kind": "boolean" } }, + { "name": "is_static_call", "type": { "kind": "boolean" } }, + { "name": "is_contract_deployment", "type": { "kind": "boolean" } }, + { + "name": "start_side_effect_counter", + "type": { "kind": "integer", "sign": "unsigned", "width": 32 } + } + ] + } + }, + { "name": "args_hash", "type": { "kind": "field" } }, + { "name": "return_values", "type": { "kind": "array", "length": 4, "type": { "kind": "field" } } }, + { + "name": "read_requests", + "type": { + "kind": "array", + "length": 32, + "type": { + "kind": "struct", + "path": "aztec::protocol_types::abis::side_effect::SideEffect", + "fields": [ + { "name": "value", "type": { "kind": "field" } }, + { "name": "counter", "type": { "kind": "integer", "sign": "unsigned", "width": 32 } } + ] + } + } + }, + { + "name": "new_commitments", + "type": { + "kind": "array", + "length": 16, + "type": { + "kind": "struct", + "path": "aztec::protocol_types::abis::side_effect::SideEffect", + "fields": [ + { "name": "value", "type": { "kind": "field" } }, + { "name": "counter", "type": { "kind": "integer", "sign": "unsigned", "width": 32 } } + ] + } + } + }, + { + "name": "new_nullifiers", + "type": { + "kind": "array", + "length": 16, + "type": { + "kind": "struct", + "path": "aztec::protocol_types::abis::side_effect::SideEffectLinkedToNoteHash", + "fields": [ + { "name": "value", "type": { "kind": "field" } }, + { "name": "note_hash", "type": { "kind": "field" } }, + { "name": "counter", "type": { "kind": "integer", "sign": "unsigned", "width": 32 } } + ] + } + } + }, + { + "name": "private_call_stack_hashes", + "type": { "kind": "array", "length": 4, "type": { "kind": "field" } } + }, + { + "name": "public_call_stack_hashes", + "type": { "kind": "array", "length": 4, "type": { "kind": "field" } } + }, + { "name": "new_l2_to_l1_msgs", "type": { "kind": "array", "length": 2, "type": { "kind": "field" } } }, + { "name": "end_side_effect_counter", "type": { "kind": "integer", "sign": "unsigned", "width": 32 } }, + { "name": "encrypted_logs_hash", "type": { "kind": "array", "length": 2, "type": { "kind": "field" } } }, + { + "name": "unencrypted_logs_hash", + "type": { "kind": "array", "length": 2, "type": { "kind": "field" } } + }, + { "name": "encrypted_log_preimages_length", "type": { "kind": "field" } }, + { "name": "unencrypted_log_preimages_length", "type": { "kind": "field" } }, + { + "name": "block_header", + "type": { + "kind": "struct", + "path": "aztec::protocol_types::abis::block_header::BlockHeader", + "fields": [ + { "name": "note_hash_tree_root", "type": { "kind": "field" } }, + { "name": "nullifier_tree_root", "type": { "kind": "field" } }, + { "name": "contract_tree_root", "type": { "kind": "field" } }, + { "name": "l1_to_l2_message_tree_root", "type": { "kind": "field" } }, + { "name": "archive_root", "type": { "kind": "field" } }, + { "name": "public_data_tree_root", "type": { "kind": "field" } }, + { "name": "global_variables_hash", "type": { "kind": "field" } } + ] + } + }, + { + "name": "contract_deployment_data", + "type": { + "kind": "struct", + "path": "aztec::protocol_types::contrakt::deployment_data::ContractDeploymentData", + "fields": [ + { + "name": "deployer_public_key", + "type": { + "kind": "struct", + "path": "aztec::protocol_types::grumpkin_point::GrumpkinPoint", + "fields": [ + { "name": "x", "type": { "kind": "field" } }, + { "name": "y", "type": { "kind": "field" } } + ] + } + }, + { "name": "constructor_vk_hash", "type": { "kind": "field" } }, + { "name": "function_tree_root", "type": { "kind": "field" } }, + { "name": "contract_address_salt", "type": { "kind": "field" } }, + { + "name": "portal_contract_address", + "type": { + "kind": "struct", + "path": "aztec::protocol_types::address::EthAddress", + "fields": [{ "name": "inner", "type": { "kind": "field" } }] + } + } + ] + } + }, + { "name": "chain_id", "type": { "kind": "field" } }, + { "name": "version", "type": { "kind": "field" } } + ] + }, + "visibility": "public" + }, + "return_witnesses": [ + 24, 25, 26, 27, 28, 29, 30, 31, 32, 33, 34, 35, 36, 37, 38, 39, 40, 41, 42, 43, 44, 45, 46, 47, 48, 49, 50, + 51, 52, 53, 54, 55, 56, 57, 58, 59, 60, 61, 62, 63, 64, 65, 66, 67, 68, 69, 70, 71, 72, 73, 74, 75, 76, 77, + 78, 79, 80, 81, 82, 83, 84, 85, 86, 87, 88, 89, 90, 91, 92, 93, 94, 95, 96, 97, 98, 99, 100, 101, 102, 103, + 104, 105, 106, 107, 108, 109, 110, 111, 112, 113, 114, 115, 116, 117, 118, 119, 120, 121, 122, 123, 124, 125, + 126, 127, 128, 129, 130, 131, 132, 133, 134, 135, 136, 137, 138, 139, 140, 141, 142, 143, 144, 145, 146, 147, + 148, 149, 150, 151, 152, 153, 154, 155, 156, 157, 158, 159, 160, 161, 162, 163, 164, 165, 166, 167, 168, 169, + 170, 171, 172, 173, 174, 175, 176, 177, 178, 179, 180, 181, 182, 183, 184, 185, 186, 187, 188, 189, 190, 191, + 192, 193, 194, 195, 196, 197, 198, 199, 200, 201, 202, 203, 204, 205, 206, 207, 208, 209, 210, 211, 212 + ] + }, + "bytecode": "H4sIAAAAAAAA/+Xd5XNUVxzG8U2yu6l7CU5wh9XsXdzdqtQLWzb1lir1UupOlXopdaeutLTUvcAfxPMMm5m8gFeczHDme2eeyUxe3NnP3rtXz/n9dqdSqR2pvUuT0qy0KO3d/udlRuNv7sCWfFrrSO9jvcVcR6lUrxTq+WJ+da5QrSXlXKlc60jySb6clNcUkmKxnpSSSrVWreSq+VKxnu8sV4udjRWnw33GXE+4M1pHpgfcmYPcndU6sj3gzgZ0d+33rd32+9R+9v8D/dxtgbdX19IceLuFNPeOxNwU0NwnEnNzQHPfSMwtAc39IjGnA5r7R2LOBDQPiMScDWgeGIm5NaB5UCTmtoDmdqB5MNA8BGgeCjQPA5qHA80jgOaRQPMooHk00DwGaB4LNI8DmscDzROA5olAcw5ozgPNBaC5CDSXgOYy0NwBNFeA5gRorgLNk4DmyUDzFKB5KtA8DWieDjTPAJpnAs2zgObZQPMcoHku0DwPaJ4PNC8AmhcCzYuA5sVA8xKgeSnQvAxoXg40rwCaTwKaTwaaTwGaTwWaTwOaTweaVwLNZwDNZwLNZwHNZwPN5wDN5wLN5wHN5wPNq4Dm1UBzDWi+AGheAzTXgeZOoPlCoPkioPlioPkSoPlSoPkyoPlyoPkKoPlKoHkt0HwV0Hw10HwN0Hwt0Hwd0Hw90LwOaL4BaL4RaL4JaL4ZaL4FaL4VaL4NaL4daF4PNN8BNG8Amu8Emu8Cmu8Gmu8Bmu8Fmu8Dmu8Hmh8Amh8Emh8Cmh8Gmh8Bmh8FmjcCzY8BzY8DzU8AzU8CzU8BzU8DzZuA5meA5meB5ueA5ueB5heA5heB5peA5peB5s1A8ytA8xag+VWg+TWg+XWg+Q2g+U2g+S2g+W2g+R2g+V2g+T2g+X2g+QOg+UOgeSvQ/BHQ/DHQ/AnQ/CnQ/BnQ/DnQ/AXQ/CXQ/BXQ/DXQ/A3Q/C3QvA1o/i4Sc2tA8/fA7bwdaP4BaP4RaN4BNP8ENP8cifmQgOZfIjEfGtD8ayTmwwKaf4vEfHhA8++RmI8IaP4jEvORAc1/RmI+KqD5r0jMRwc0/x2J+ZiA5n8iMR8b0PxvJObjApr/i8R8fEDz/5GYTwho3hmJ+cSA5l2RmHsFNO8OaG7rZm5quFuUtJJRsoqfX/q+0PdJvm/wdbSvK32d5esOn4d9XvJx2sct/469X3s79+q2/m2Nv72VPkpfpZ/SXxmgDFQGKe3KYGWIMlQZpgxXRigjlVHKaGWMMlYZp4xXJigT/Z0oeaXg71opKWWlQ6koiVJVJimTlSnKVGWaMr3x3c5UZimzlTnKXGWeMl9ZoCxUFimLlSXKUmWZslxZobj/vPuxuz+5+3W7f7X7Oa9U3O/X/W/dD9b9Ud0v1P0z3U/S/RVXKe6/V1Pcn839yty/y/2s3N/J/Y7c/8f9cNwfxv1S3D/E/TTcX2Kt4v4Drsfv+vSu1+765a7nvU5xvWfXP3Y9YNfHdb1Y1091PVHX11yvuP7iBsX1+VyvzvXbXM/M9b1c78r1n1wPyfWBXC/H9WNcT8X1RTYqrj/hegyuT+D5+p6/7vncmxTP9/X8V88H9fxIzxf0/DnPJ/P8qs2K599sUTw/w/MVPH7f49k9vtvjnT3+1+NhPT7U4yU9ftDj6Ty+bKvi8Ucej+PxKR6v4fELfp/v99t+3+v3n34f6Pdjfl/kfdPvE/x8fbvi569+Hunnc35e5ec3fp7h+3vf7/r+z/dDvj/w9bKvH3095esLn299/vHx2Mcn/167lj1HMZVAULsAAA==", + "debug_symbols": "q1bKyU9OLMnMzytWsqqurQUA" + }, + { + "name": "recreate_note", + "function_type": "Secret", + "is_internal": false, + "abi": { + "parameters": [ + { + "name": "inputs", + "type": { + "kind": "struct", + "path": "aztec::abi::PrivateContextInputs", + "fields": [ + { + "name": "call_context", + "type": { + "kind": "struct", + "path": "aztec::protocol_types::abis::call_context::CallContext", + "fields": [ + { + "name": "msg_sender", + "type": { + "kind": "struct", + "path": "aztec::protocol_types::address::AztecAddress", + "fields": [{ "name": "inner", "type": { "kind": "field" } }] + } + }, + { + "name": "storage_contract_address", + "type": { + "kind": "struct", + "path": "aztec::protocol_types::address::AztecAddress", + "fields": [{ "name": "inner", "type": { "kind": "field" } }] + } + }, + { + "name": "portal_contract_address", + "type": { + "kind": "struct", + "path": "aztec::protocol_types::address::EthAddress", + "fields": [{ "name": "inner", "type": { "kind": "field" } }] + } + }, + { + "name": "function_selector", + "type": { + "kind": "struct", + "path": "aztec::protocol_types::abis::function_selector::FunctionSelector", + "fields": [ + { "name": "inner", "type": { "kind": "integer", "sign": "unsigned", "width": 32 } } + ] + } + }, + { "name": "is_delegate_call", "type": { "kind": "boolean" } }, + { "name": "is_static_call", "type": { "kind": "boolean" } }, + { "name": "is_contract_deployment", "type": { "kind": "boolean" } }, + { + "name": "start_side_effect_counter", + "type": { "kind": "integer", "sign": "unsigned", "width": 32 } + } + ] + } + }, + { + "name": "block_header", + "type": { + "kind": "struct", + "path": "aztec::protocol_types::abis::block_header::BlockHeader", + "fields": [ + { "name": "note_hash_tree_root", "type": { "kind": "field" } }, + { "name": "nullifier_tree_root", "type": { "kind": "field" } }, + { "name": "contract_tree_root", "type": { "kind": "field" } }, + { "name": "l1_to_l2_message_tree_root", "type": { "kind": "field" } }, + { "name": "archive_root", "type": { "kind": "field" } }, + { "name": "public_data_tree_root", "type": { "kind": "field" } }, + { "name": "global_variables_hash", "type": { "kind": "field" } } + ] + } + }, + { + "name": "contract_deployment_data", + "type": { + "kind": "struct", + "path": "aztec::protocol_types::contrakt::deployment_data::ContractDeploymentData", + "fields": [ + { + "name": "deployer_public_key", + "type": { + "kind": "struct", + "path": "aztec::protocol_types::grumpkin_point::GrumpkinPoint", + "fields": [ + { "name": "x", "type": { "kind": "field" } }, + { "name": "y", "type": { "kind": "field" } } + ] + } + }, + { "name": "constructor_vk_hash", "type": { "kind": "field" } }, + { "name": "function_tree_root", "type": { "kind": "field" } }, + { "name": "contract_address_salt", "type": { "kind": "field" } }, + { + "name": "portal_contract_address", + "type": { + "kind": "struct", + "path": "aztec::protocol_types::address::EthAddress", + "fields": [{ "name": "inner", "type": { "kind": "field" } }] + } + } + ] + } + }, + { + "name": "private_global_variables", + "type": { + "kind": "struct", + "path": "aztec::abi::PrivateGlobalVariables", + "fields": [ + { "name": "chain_id", "type": { "kind": "field" } }, + { "name": "version", "type": { "kind": "field" } } + ] + } + } + ] + }, + "visibility": "private" + }, + { + "name": "owner", + "type": { + "kind": "struct", + "path": "aztec::protocol_types::address::AztecAddress", + "fields": [{ "name": "inner", "type": { "kind": "field" } }] + }, + "visibility": "private" + }, + { "name": "index", "type": { "kind": "integer", "sign": "unsigned", "width": 32 }, "visibility": "private" } + ], + "param_witnesses": { + "index": [{ "start": 24, "end": 25 }], + "inputs": [{ "start": 0, "end": 23 }], + "owner": [{ "start": 23, "end": 24 }] + }, + "return_type": { + "abi_type": { + "kind": "struct", + "path": "aztec::protocol_types::abis::private_circuit_public_inputs::PrivateCircuitPublicInputs", + "fields": [ + { + "name": "call_context", + "type": { + "kind": "struct", + "path": "aztec::protocol_types::abis::call_context::CallContext", + "fields": [ + { + "name": "msg_sender", + "type": { + "kind": "struct", + "path": "aztec::protocol_types::address::AztecAddress", + "fields": [{ "name": "inner", "type": { "kind": "field" } }] + } + }, + { + "name": "storage_contract_address", + "type": { + "kind": "struct", + "path": "aztec::protocol_types::address::AztecAddress", + "fields": [{ "name": "inner", "type": { "kind": "field" } }] + } + }, + { + "name": "portal_contract_address", + "type": { + "kind": "struct", + "path": "aztec::protocol_types::address::EthAddress", + "fields": [{ "name": "inner", "type": { "kind": "field" } }] + } + }, + { + "name": "function_selector", + "type": { + "kind": "struct", + "path": "aztec::protocol_types::abis::function_selector::FunctionSelector", + "fields": [{ "name": "inner", "type": { "kind": "integer", "sign": "unsigned", "width": 32 } }] + } + }, + { "name": "is_delegate_call", "type": { "kind": "boolean" } }, + { "name": "is_static_call", "type": { "kind": "boolean" } }, + { "name": "is_contract_deployment", "type": { "kind": "boolean" } }, + { + "name": "start_side_effect_counter", + "type": { "kind": "integer", "sign": "unsigned", "width": 32 } + } + ] + } + }, + { "name": "args_hash", "type": { "kind": "field" } }, + { "name": "return_values", "type": { "kind": "array", "length": 4, "type": { "kind": "field" } } }, + { + "name": "read_requests", + "type": { + "kind": "array", + "length": 32, + "type": { + "kind": "struct", + "path": "aztec::protocol_types::abis::side_effect::SideEffect", + "fields": [ + { "name": "value", "type": { "kind": "field" } }, + { "name": "counter", "type": { "kind": "integer", "sign": "unsigned", "width": 32 } } + ] + } + } + }, + { + "name": "new_commitments", + "type": { + "kind": "array", + "length": 16, + "type": { + "kind": "struct", + "path": "aztec::protocol_types::abis::side_effect::SideEffect", + "fields": [ + { "name": "value", "type": { "kind": "field" } }, + { "name": "counter", "type": { "kind": "integer", "sign": "unsigned", "width": 32 } } + ] + } + } + }, + { + "name": "new_nullifiers", + "type": { + "kind": "array", + "length": 16, + "type": { + "kind": "struct", + "path": "aztec::protocol_types::abis::side_effect::SideEffectLinkedToNoteHash", + "fields": [ + { "name": "value", "type": { "kind": "field" } }, + { "name": "note_hash", "type": { "kind": "field" } }, + { "name": "counter", "type": { "kind": "integer", "sign": "unsigned", "width": 32 } } + ] + } + } + }, + { + "name": "private_call_stack_hashes", + "type": { "kind": "array", "length": 4, "type": { "kind": "field" } } + }, + { + "name": "public_call_stack_hashes", + "type": { "kind": "array", "length": 4, "type": { "kind": "field" } } + }, + { "name": "new_l2_to_l1_msgs", "type": { "kind": "array", "length": 2, "type": { "kind": "field" } } }, + { "name": "end_side_effect_counter", "type": { "kind": "integer", "sign": "unsigned", "width": 32 } }, + { "name": "encrypted_logs_hash", "type": { "kind": "array", "length": 2, "type": { "kind": "field" } } }, + { + "name": "unencrypted_logs_hash", + "type": { "kind": "array", "length": 2, "type": { "kind": "field" } } + }, + { "name": "encrypted_log_preimages_length", "type": { "kind": "field" } }, + { "name": "unencrypted_log_preimages_length", "type": { "kind": "field" } }, + { + "name": "block_header", + "type": { + "kind": "struct", + "path": "aztec::protocol_types::abis::block_header::BlockHeader", + "fields": [ + { "name": "note_hash_tree_root", "type": { "kind": "field" } }, + { "name": "nullifier_tree_root", "type": { "kind": "field" } }, + { "name": "contract_tree_root", "type": { "kind": "field" } }, + { "name": "l1_to_l2_message_tree_root", "type": { "kind": "field" } }, + { "name": "archive_root", "type": { "kind": "field" } }, + { "name": "public_data_tree_root", "type": { "kind": "field" } }, + { "name": "global_variables_hash", "type": { "kind": "field" } } + ] + } + }, + { + "name": "contract_deployment_data", + "type": { + "kind": "struct", + "path": "aztec::protocol_types::contrakt::deployment_data::ContractDeploymentData", + "fields": [ + { + "name": "deployer_public_key", + "type": { + "kind": "struct", + "path": "aztec::protocol_types::grumpkin_point::GrumpkinPoint", + "fields": [ + { "name": "x", "type": { "kind": "field" } }, + { "name": "y", "type": { "kind": "field" } } + ] + } + }, + { "name": "constructor_vk_hash", "type": { "kind": "field" } }, + { "name": "function_tree_root", "type": { "kind": "field" } }, + { "name": "contract_address_salt", "type": { "kind": "field" } }, + { + "name": "portal_contract_address", + "type": { + "kind": "struct", + "path": "aztec::protocol_types::address::EthAddress", + "fields": [{ "name": "inner", "type": { "kind": "field" } }] + } + } + ] + } + }, + { "name": "chain_id", "type": { "kind": "field" } }, + { "name": "version", "type": { "kind": "field" } } + ] + }, + "visibility": "public" + }, + "return_witnesses": [ + 6072, 6073, 6074, 6075, 6076, 6077, 6078, 6079, 6080, 6081, 6082, 6083, 6084, 6085, 6086, 6087, 6088, 6089, + 6090, 6091, 6092, 6093, 6094, 6095, 6096, 6097, 6098, 6099, 6100, 6101, 6102, 6103, 6104, 6105, 6106, 6107, + 6108, 6109, 6110, 6111, 6112, 6113, 6114, 6115, 6116, 6117, 6118, 6119, 6120, 6121, 6122, 6123, 6124, 6125, + 6126, 6127, 6128, 6129, 6130, 6131, 6132, 6133, 6134, 6135, 6136, 6137, 6138, 6139, 6140, 6141, 6142, 6143, + 6144, 6145, 6146, 6147, 6148, 6149, 6150, 6151, 6152, 6153, 6154, 6155, 6156, 6157, 6158, 6159, 6160, 6161, + 6162, 6163, 6164, 6165, 6166, 6167, 6168, 6169, 6170, 6171, 6172, 6173, 6174, 6175, 6176, 6177, 6178, 6179, + 6180, 6181, 6182, 6183, 6184, 6185, 6186, 6187, 6188, 6189, 6190, 6191, 6192, 6193, 6194, 6195, 6196, 6197, + 6198, 6199, 6200, 6201, 6202, 6203, 6204, 6205, 6206, 6207, 6208, 6209, 6210, 6211, 6212, 6213, 6214, 6215, + 6216, 6217, 6218, 6219, 6220, 6221, 6222, 6223, 6224, 6225, 6226, 6227, 6228, 6229, 6230, 6231, 6232, 6233, + 6234, 6235, 6236, 6237, 6238, 6239, 6240, 6241, 6242, 6243, 6244, 6245, 6246, 6247, 6248, 6249, 6250, 6251, + 6252, 6253, 6254, 6255, 6256, 6257, 6258, 6259, 6260 + ] + }, + "bytecode": "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", + "debug_symbols": "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WVuPsdboUo9crKByrFbQYluoWm793Cq3fm6VWz+3yq2fW+XWz61y6+dWufMesZZb7xHry/eIpf0n1b8XVFYrqK5WkK1WUFutIF+toFitoL7Y3FlX27vU1fYuttrexW5t0NmtNzp2642O3XqjY7fe6NitNzp2642O3XqjY7nYENNW27u01fYubbW9S1tt79JW27u01fYubbW9S+urFbTa3qWttnfx1fYuvtq5i996O+K33o74rbcjfuvtiN96O+K33o74rbcjfutzl1ht7xKr7V1itb1LrLZ3idX2LrHa3iVW27vEanuXGKsVtNrepa+2d+mr7V36Yq/d1H7r7Ui/9Xak33o70m+9Hem33o70W29H+q23I+PWrwqN1fYuY7W9y1ht7/IrcCe/tqDV9i5jtb3LWG3vMlbbu4zVTshztb3LasiAuhoyoK6GDKgvRwb80tEgb70dyVtvR25NLqi3JhfUW5ML7NbkArs1ucCOxfYudiy2d7GjrVbQYnsXOxbbu9ix2N7FjsX2LnYstnexstgJua32rr6VxfYuVhbbu9jL36h/WtCdtyN265fk7dYvydutX5K3W78kb/XW25F66+1IvfMPo221d/VttXf1bbV39W21d/WtrrZ3qavtXepqexdbbe9ii52Qm622d7HV9i4vf0n+aUGLoZrt1q+y261fZbdbv8puduvtSLv1dqTdejvSbr0daXf+YbSt9q6+rfauvq32rr6t9q6+tdX2Lm21vYuvtnfx1fYuvtoJua+2d3n5G/VPC1pt7+KLxczYrV9lt1u/ym63fpXd4tbbkbj1diRuvR2JW29HXv/6/S+tfrW9y2rv6ttq7+rbau/qW6y2d+mr7V36anuXvtrepa92Qv7y1++fTeZ9tb1LX23v0hej2dutX2W3W7/Kbrd+ld3Grbcj49bbkXHr7cjr36j/pdXf+ofRq72rb6u9q2+rvatvq72rb7na3iVX27vkanuXXG3v8vp39Z8VtNreJVfbu+Rqe5dcjGZvt36Vvd36VfZ261fZ23Hn7Ug77rwdaUe7dfV33o60484/jG6rvavfVntXv632rn5b7V39Vhbbu7Sy2N6llcX2Lu316ffPClrshLyVxfYurSy2d2llsb1LK4vR7NutX2Vvt36Vvd36VfZWb70def1777+0+ltvR+qttyP1zj+Mbqu9q99We1e/rfau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SddWSddWSddWSddWSddWSddWSddWSddWSddWSddWydBWydBWydBWydBWydBWydBWydBWydBWydBWydBWSWqrJLVVktoqSW2VpLZKUlslqa2S1FZJaqsktVVSjkP8uyL+XRX/zsS/a+Lfufh3If5dF/9uiH8nrpcirpcirpcirpcirpcirpcirpcirpcirpcirpcirpcqrpcqrpcqrpcqrpcqrpcqrpcqrpcqrpcqrpcqrhcT14uJ68XE9WLiejFxvZi4XkxcLyauFxPXi4nrpYnrpYnrpYnrpYnrpYnrpYnrpYnrRfRqi2jWFtGtLaJdW0S/toiGbREd2yJatkX0bIto2hbRtS2ibVtE37aIxm0RndsiWrdF9G6LaN4W0b0ton1bRP+2iAZuER3cIlq4RfRwi2jiFtHFLaKNW0Qft4hGbhGd3CJauUX0coto5hbRzS2inVtEP7eIhm4RHd0iWrpF9HSLaOoW0dUtoq1bRF+3iMZuEZ3dIlq7RfR2i2juFtHdLaK9W0R/t4r+bhX93Sr6u1X0d6vo71bR362iv1tFf7eK/m4V/d0q+rtV9Her6O9W0d+tor9bRX+3/if+7kfIyfn/+nd/18W/G+LfpfZ3/4m/+/zvivh3Vfw7E//u+/XS8/pdox+f31b6+DsX/y7Ev+vi3w3x71L7u//E333+d0X8uyr+nYl/J64XE9eLievFxPVi4noxcb00cb00cb00cb00cb00cb00cb00cb00cb00cb00cb24uF5cXC8urhcX14uL68XF9eLienFxvbi4XlxcLyGulxDXS4jrJcT1EuJ6CXG9hLheQlwvIa6XENdLF9dLF9dLF9dLF9dLF9dLF9dLF9dLF9dLF9dLF9fLENfLENfLENfLENfLENfLENfLENfLENfLENfLENdLiuslxfWS4npJcb2kuF5SXC8prpcU10uK6yW19WLHIf5dEf+uin9n4t818e9c/LsQ/66LfzfEvxPXSxHXSxHXSxHXSxHXSxHXSxHXSxHXSxHXSxHXSxHXSxXXSxXXSxXXSxXXi+jvmujvmujvmujvmujvmujvmujvmujvmujvmujvmujvmujvmujvmujvmujvmujvmujvmujvmujvmujvmujvmujvmujvmujvmujvmujvmujvmujvmujvmujvmujvmujvmujvmujvmujvmujvmujvmujvmujvmujvmujvmujvmujvmujvmujvmujvmujvmujvmujvmujvmujvmujvmujvmujvmujvmujvmujvmujvmujvmujvmujvmujvmujvmujvmujvmujvmujvmujvmujvmujvmujvmujvmujvmujvmujvmujvNtHfbaK/20R/t4n+bhP93Sb6u030d5vo7zbR322iv9tEf7eJ/m4T/d0m+rtN9Heb6O820d9tor/bRH+3if5uE/3dJvq7TfR3m+jvNtHfbaK/20R/t4n+bhP93Sb6u030d5vo7zbR322iv9tEf7eJ/m4T/d0m+rtN9Heb6O820d9tor/bRH+3if5uE/3dJvq7TfR3m+jvNtHfbaK/20R/t4n+bhP93Sb6u030d5vo7zbR322iv9tEf7eJ/m4T/d0m+rtN9Heb6O820d9tor/bRH+3if5uE/3dJvq7TfR3m+jvNtHfbaK/20R/t4n+bhP93Sb6u030d5vo7zbR322iv9tEf7eJ/m4T/d0m+rtN9Heb6O820d9tor/bRH+3if5uE/3dJvq7TfR3m+jvNtHfbaK/20R/t4n+rov+rov+rov+rov+rov+rov+rov+rov+rov+rov+rov+rov+rov+rov+rov+rov+rov+rov+rov+rov+rov+rov+rov+rov+rov+rov+rov+rov+rov+rov+rov+rov+rov+rov+rov+rov+rov+rov+rov+rov+rov+rov+rov+rov+rov+rov+rov+rov+rov+rov+rov+rov+rov+rov+rov+rov+rov+rov+rov+rov+rov+rov+rov+rov+rov+rov+rov+rov+rov+rov+rov+rov+rov+rov+rov+rov+rov+rov+rov+rov+rov+rov+rov+rov+rov+rov+rov+rov+rov+rov+rov+rov+rov+rov+rov+rov+rov+rov+rov+rov+boj+boj+boj+boj+boj+boj+boj+boj+boj+boj+boj+boj+boj+boj+boj+boj+boj+boj+boj+boj+boj+boj+boj+boj+boj+boj+boj+boj+boj+boj+boj+boj+boj+boj+boj+boj+boj+boj+boj+boj+boj+boj+boj+boj+boj+boj+boj+boj+boj+boj+boj+boj+boj+boj+boj+boj+boj+boj+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HnJTPeSmeshN9ZCb6iE31UNuqofcVA+5qR5yUz3kpnrITfWQm+ohN9VDbqqH3FQPuakeclM95KZ6yE31kJvqITfVQ26qh9xUD7mpHnJTPeSmeshN9ZCb6iE31UNuqofcVA+5qR5yUz3kpnrITfWQm+ohN9VDbqqH3FQPuakeclM95KZ6yE31kJvqITfVQ26qh9xUD7mpHnJTPeSmeshN9ZCb6iE31UNuqofcVA+5qR5yUz3kpnrITfWQm+ohN9VDbqqH3FQPuakeclM95KZ6yE31kJvqITfVQ26qh9xUD7mpHnJTPeSmeshN9ZCb6iE31UN21UN21UN21UN21UN21UN21UN21UN21UN21UN21UN21UN21UN21UN21UN21UN21UN21UN21UN21UN21UN21UN21UN21UN21UN21UN21UN21UN21UN21UN21UN21UN21UN21UN21UN21UN21UN21UN21UN21UN21UN21UN21UN21UN21UN21UN21UN21UN21UN21UN21UN21UN21UN21UN21UN21UN21UN21UN21UN21UN21UN21UN21UN21UN21UN21UN21UN21UN21UN21UN21UN21UN21UN21UN21UN21UN21UN21UN21UN21UN21UN21UN21UN21UN21UN21UN21UN21UN21UN21UN21UN21UN21UN21UN21UN21UN21UN21UN21UN21UN21UMO1UMO1UMO1UMO1UMO1UMO1UMO1UMO1UMO1UMO1UMO1UMO1UMO1UMO1UMO1UMO1UMO1UMO1UMO1UMO1UMO1UMO1UMO1UMO1UMO1UMO1UMO1UMO1UMO1UMO1UMO1UMO1UMO1UMO1UMO1UMO1UMO1UMO1UMO1UMO1UMO1UMO1UMO1UMO1UMO1UMO1UMO1UMO1UMO1UMO1UMO1UMO1UMO1UMO1UMO1UMO1UMO1UMO1UMO1UMO1UMO1UMO1UMO1UMO1UMO1UMO1UMO1UMO1UMO1UMO1UMO1UMO1UMO1UMO1UMO1UMO1UMO1UMO1UMO1UMO1UMO1UMO1UMO1UMO1UMO1UMO1UMO1UMO1UMO1UMO1UMO1UMO1UMO1UMO1UMO1UMO1UMO1UMO1UMO1UMO1UPuqofcVQ+5qx5yVz3krnrIXfWQu+ohd9VD7qqH3FUPuaseclc95K56yF31kLvqIXfVQ+6qh9xVD7mr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VENONeRUQ0415FRDTjXkVENONeRUQ0415FRDTjXkVENONeRUQ0415FRDTjXkVENONeRUQ0415FRDTjXkVENONeRUQ0415FRDTjXkVENONeRUQ0415FRDTjXkVENONeRUQ0415FRDTjXkVENONeRUQ0415FRDTjXkVENONeRUQ0415FRDTjXkVENONeRUQ0415FRDTjXkVENONeRUQ0415FRDTjXkVENONeRUQ0415FRDTjXkVENONeRUQ0415FRDTjXkVEMuNeRSQy415FJDLjXkUkMuNeRSQy415FJDLjXkUkMuNeRSQy415FJDLjXkUkMuNeRSQy415FJDLjXkUkMuNeRSQy415FJDLjXkUkMuNeRSQy415FJDLjXkUkMuNeRSQy415FJDLjXkUkMuNeRSQy415FJDLjXkUkMuNeRSQy415FJDLjXkUkMuNeRSQy415FJDLjXkUkMuNeRSQy415FJDLjXkUkMuNeRSQy415FJDLjXkUkMuNeRSQy415FJDLjXkUkMuNeRSQy415FJDLjXkUkMuNeRSQy415FJDLjXkUkMuNeRSQy415FJDLjXkUkMuNeRSQy415FJD3mrIWw15qyFvNeSthrzVkLca8lZD3mrIWw15qyFvNeSthrzVkLca8lZD3mrIWw15qyFvNeSthrzVkLca8lZD3mrIWw15qyFvNeSthrzVkLca8lZD3mrIWw15qyFvNeSthrzVkLca8lZD3mrIWw15qyFvNeSthrzVkLca8lZD3mrIWw15qyFvNeSthrzVkLca8lZD3mrIWw15qyFvNeSthrzVkLca8lZD3mrIWw15qyFvNeSthrzVkLca8lZD3mrIWw15qyFvNeSthrzVkLca8lZD3mrIWw15qyFvNeSthrzVkLca8lZD3mrIWw15qyFvNeSthrzVkLca8lZD3mrIWw15qyFvNeRWQ2415FZDbjXkVkNuNeRWQ2415FZDbjXkVkNuNeRWQ2415FZDbjXkVkNuNeRWQ2415FZDbjXkVkNuNeRWQ2415FZDbjXkVkNuNeRWQ2415FZDbjXkVkNuNeRWQ2415FZDbjXkVkNuNeRWQ2415FZDbjXkVkNuNeRWQ2415FZDbjXkVkNuNeRWQ2415FZDbjXkVkNuNeRWQ2415FZDbjXkVkNuNeRWQ2415FZDbjXkVkNuNeRWQ2415FZDbjXk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" + }, + { + "name": "create_note", + "function_type": "Secret", + "is_internal": false, + "abi": { + "parameters": [ + { + "name": "inputs", + "type": { + "kind": "struct", + "path": "aztec::abi::PrivateContextInputs", + "fields": [ + { + "name": "call_context", + "type": { + "kind": "struct", + "path": "aztec::protocol_types::abis::call_context::CallContext", + "fields": [ + { + "name": "msg_sender", + "type": { + "kind": "struct", + "path": "aztec::protocol_types::address::AztecAddress", + "fields": [{ "name": "inner", "type": { "kind": "field" } }] + } + }, + { + "name": "storage_contract_address", + "type": { + "kind": "struct", + "path": "aztec::protocol_types::address::AztecAddress", + "fields": [{ "name": "inner", "type": { "kind": "field" } }] + } + }, + { + "name": "portal_contract_address", + "type": { + "kind": "struct", + "path": "aztec::protocol_types::address::EthAddress", + "fields": [{ "name": "inner", "type": { "kind": "field" } }] + } + }, + { + "name": "function_selector", + "type": { + "kind": "struct", + "path": "aztec::protocol_types::abis::function_selector::FunctionSelector", + "fields": [ + { "name": "inner", "type": { "kind": "integer", "sign": "unsigned", "width": 32 } } + ] + } + }, + { "name": "is_delegate_call", "type": { "kind": "boolean" } }, + { "name": "is_static_call", "type": { "kind": "boolean" } }, + { "name": "is_contract_deployment", "type": { "kind": "boolean" } }, + { + "name": "start_side_effect_counter", + "type": { "kind": "integer", "sign": "unsigned", "width": 32 } + } + ] + } + }, + { + "name": "block_header", + "type": { + "kind": "struct", + "path": "aztec::protocol_types::abis::block_header::BlockHeader", + "fields": [ + { "name": "note_hash_tree_root", "type": { "kind": "field" } }, + { "name": "nullifier_tree_root", "type": { "kind": "field" } }, + { "name": "contract_tree_root", "type": { "kind": "field" } }, + { "name": "l1_to_l2_message_tree_root", "type": { "kind": "field" } }, + { "name": "archive_root", "type": { "kind": "field" } }, + { "name": "public_data_tree_root", "type": { "kind": "field" } }, + { "name": "global_variables_hash", "type": { "kind": "field" } } + ] + } + }, + { + "name": "contract_deployment_data", + "type": { + "kind": "struct", + "path": "aztec::protocol_types::contrakt::deployment_data::ContractDeploymentData", + "fields": [ + { + "name": "deployer_public_key", + "type": { + "kind": "struct", + "path": "aztec::protocol_types::grumpkin_point::GrumpkinPoint", + "fields": [ + { "name": "x", "type": { "kind": "field" } }, + { "name": "y", "type": { "kind": "field" } } + ] + } + }, + { "name": "constructor_vk_hash", "type": { "kind": "field" } }, + { "name": "function_tree_root", "type": { "kind": "field" } }, + { "name": "contract_address_salt", "type": { "kind": "field" } }, + { + "name": "portal_contract_address", + "type": { + "kind": "struct", + "path": "aztec::protocol_types::address::EthAddress", + "fields": [{ "name": "inner", "type": { "kind": "field" } }] + } + } + ] + } + }, + { + "name": "private_global_variables", + "type": { + "kind": "struct", + "path": "aztec::abi::PrivateGlobalVariables", + "fields": [ + { "name": "chain_id", "type": { "kind": "field" } }, + { "name": "version", "type": { "kind": "field" } } + ] + } + } + ] + }, + "visibility": "private" + }, + { + "name": "owner", + "type": { + "kind": "struct", + "path": "aztec::protocol_types::address::AztecAddress", + "fields": [{ "name": "inner", "type": { "kind": "field" } }] + }, + "visibility": "private" + }, + { "name": "value", "type": { "kind": "field" }, "visibility": "private" } + ], + "param_witnesses": { + "inputs": [{ "start": 0, "end": 23 }], + "owner": [{ "start": 23, "end": 24 }], + "value": [{ "start": 24, "end": 25 }] + }, + "return_type": { + "abi_type": { + "kind": "struct", + "path": "aztec::protocol_types::abis::private_circuit_public_inputs::PrivateCircuitPublicInputs", + "fields": [ + { + "name": "call_context", + "type": { + "kind": "struct", + "path": "aztec::protocol_types::abis::call_context::CallContext", + "fields": [ + { + "name": "msg_sender", + "type": { + "kind": "struct", + "path": "aztec::protocol_types::address::AztecAddress", + "fields": [{ "name": "inner", "type": { "kind": "field" } }] + } + }, + { + "name": "storage_contract_address", + "type": { + "kind": "struct", + "path": "aztec::protocol_types::address::AztecAddress", + "fields": [{ "name": "inner", "type": { "kind": "field" } }] + } + }, + { + "name": "portal_contract_address", + "type": { + "kind": "struct", + "path": "aztec::protocol_types::address::EthAddress", + "fields": [{ "name": "inner", "type": { "kind": "field" } }] + } + }, + { + "name": "function_selector", + "type": { + "kind": "struct", + "path": "aztec::protocol_types::abis::function_selector::FunctionSelector", + "fields": [{ "name": "inner", "type": { "kind": "integer", "sign": "unsigned", "width": 32 } }] + } + }, + { "name": "is_delegate_call", "type": { "kind": "boolean" } }, + { "name": "is_static_call", "type": { "kind": "boolean" } }, + { "name": "is_contract_deployment", "type": { "kind": "boolean" } }, + { + "name": "start_side_effect_counter", + "type": { "kind": "integer", "sign": "unsigned", "width": 32 } + } + ] + } + }, + { "name": "args_hash", "type": { "kind": "field" } }, + { "name": "return_values", "type": { "kind": "array", "length": 4, "type": { "kind": "field" } } }, + { + "name": "read_requests", + "type": { + "kind": "array", + "length": 32, + "type": { + "kind": "struct", + "path": "aztec::protocol_types::abis::side_effect::SideEffect", + "fields": [ + { "name": "value", "type": { "kind": "field" } }, + { "name": "counter", "type": { "kind": "integer", "sign": "unsigned", "width": 32 } } + ] + } + } + }, + { + "name": "new_commitments", + "type": { + "kind": "array", + "length": 16, + "type": { + "kind": "struct", + "path": "aztec::protocol_types::abis::side_effect::SideEffect", + "fields": [ + { "name": "value", "type": { "kind": "field" } }, + { "name": "counter", "type": { "kind": "integer", "sign": "unsigned", "width": 32 } } + ] + } + } + }, + { + "name": "new_nullifiers", + "type": { + "kind": "array", + "length": 16, + "type": { + "kind": "struct", + "path": "aztec::protocol_types::abis::side_effect::SideEffectLinkedToNoteHash", + "fields": [ + { "name": "value", "type": { "kind": "field" } }, + { "name": "note_hash", "type": { "kind": "field" } }, + { "name": "counter", "type": { "kind": "integer", "sign": "unsigned", "width": 32 } } + ] + } + } + }, + { + "name": "private_call_stack_hashes", + "type": { "kind": "array", "length": 4, "type": { "kind": "field" } } + }, + { + "name": "public_call_stack_hashes", + "type": { "kind": "array", "length": 4, "type": { "kind": "field" } } + }, + { "name": "new_l2_to_l1_msgs", "type": { "kind": "array", "length": 2, "type": { "kind": "field" } } }, + { "name": "end_side_effect_counter", "type": { "kind": "integer", "sign": "unsigned", "width": 32 } }, + { "name": "encrypted_logs_hash", "type": { "kind": "array", "length": 2, "type": { "kind": "field" } } }, + { + "name": "unencrypted_logs_hash", + "type": { "kind": "array", "length": 2, "type": { "kind": "field" } } + }, + { "name": "encrypted_log_preimages_length", "type": { "kind": "field" } }, + { "name": "unencrypted_log_preimages_length", "type": { "kind": "field" } }, + { + "name": "block_header", + "type": { + "kind": "struct", + "path": "aztec::protocol_types::abis::block_header::BlockHeader", + "fields": [ + { "name": "note_hash_tree_root", "type": { "kind": "field" } }, + { "name": "nullifier_tree_root", "type": { "kind": "field" } }, + { "name": "contract_tree_root", "type": { "kind": "field" } }, + { "name": "l1_to_l2_message_tree_root", "type": { "kind": "field" } }, + { "name": "archive_root", "type": { "kind": "field" } }, + { "name": "public_data_tree_root", "type": { "kind": "field" } }, + { "name": "global_variables_hash", "type": { "kind": "field" } } + ] + } + }, + { + "name": "contract_deployment_data", + "type": { + "kind": "struct", + "path": "aztec::protocol_types::contrakt::deployment_data::ContractDeploymentData", + "fields": [ + { + "name": "deployer_public_key", + "type": { + "kind": "struct", + "path": "aztec::protocol_types::grumpkin_point::GrumpkinPoint", + "fields": [ + { "name": "x", "type": { "kind": "field" } }, + { "name": "y", "type": { "kind": "field" } } + ] + } + }, + { "name": "constructor_vk_hash", "type": { "kind": "field" } }, + { "name": "function_tree_root", "type": { "kind": "field" } }, + { "name": "contract_address_salt", "type": { "kind": "field" } }, + { + "name": "portal_contract_address", + "type": { + "kind": "struct", + "path": "aztec::protocol_types::address::EthAddress", + "fields": [{ "name": "inner", "type": { "kind": "field" } }] + } + } + ] + } + }, + { "name": "chain_id", "type": { "kind": "field" } }, + { "name": "version", "type": { "kind": "field" } } + ] + }, + "visibility": "public" + }, + "return_witnesses": [ + 46, 47, 48, 49, 50, 51, 52, 53, 54, 55, 56, 57, 58, 59, 60, 61, 62, 63, 64, 65, 66, 67, 68, 69, 70, 71, 72, + 73, 74, 75, 76, 77, 78, 79, 80, 81, 82, 83, 84, 85, 86, 87, 88, 89, 90, 91, 92, 93, 94, 95, 96, 97, 98, 99, + 100, 101, 102, 103, 104, 105, 106, 107, 108, 109, 110, 111, 112, 113, 114, 115, 116, 117, 118, 119, 120, 121, + 122, 123, 124, 125, 126, 127, 128, 129, 130, 131, 132, 133, 134, 135, 136, 137, 138, 139, 140, 141, 142, 143, + 144, 145, 146, 147, 148, 149, 150, 151, 152, 153, 154, 155, 156, 157, 158, 159, 160, 161, 162, 163, 164, 165, + 166, 167, 168, 169, 170, 171, 172, 173, 174, 175, 176, 177, 178, 179, 180, 181, 182, 183, 184, 185, 186, 187, + 188, 189, 190, 191, 192, 193, 194, 195, 196, 197, 198, 199, 200, 201, 202, 203, 204, 205, 206, 207, 208, 209, + 210, 211, 212, 213, 214, 215, 216, 217, 218, 219, 220, 221, 222, 223, 224, 225, 226, 227, 228, 229, 230, 231, + 232, 233, 234 + ] + }, + "bytecode": "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", + "debug_symbols": "7ZjdbhoxEIXfxdcIzdjj+eFVqlygNpUiRaQq3CHevQtdex1q4XRXVLsNVwjpjP35eOyZ9dG9vn3dHl7ednu3ObroNl+Obv9juzv/2x+2Pw9uAyv3vPvW/Z5W7vvL67PbBDut/pB5gNgrPXjJYgo1cSBKYgLMYkSsqBFM0tiIcJ6+0D+tHC+UW/4ZN4kmcQw6lVtr3CiS0FEs3KY3n7QmfmC3MTQ2KxqEyTgIJMMU1AAKaEkcuJE0pnlgKAeWCzkultwvljwslpzq5JzJFbQxPiUW0+HYIUtFK5QuMIlUaO3CEiezdNZpdhHtHc15Br77DHL3GXT6DKA+z+ChsWddsmU1FTtM1Q32nDa4uINVK1LO1zXTcDjMV6QdQei1AUneJU4FmGI2kAxb6mYpsP/F8JiIBaBhuE9nOhSj2qg2w8Oc/VNJ/lmRhZ1/Z3JcCrlek/s5k1vM5GaNnFVJrTobFOPCZZnhcyyT5rxMBLGhXunthQbmZEpgi4272XNMHJ5FG+oI2ItjHI4yx4uF8WHhBywUzCUiSmnhzbYSQyjGhVFFgh/785H9yV8UWpTwPsXlYeFUC2fdWy/Dwll3y4uwMMy6Yb6rhZrfNzW2PvMCp4pCxVuC9M8a62rBtwF9WObv1wcP6/ozTrRU6BiLzyZKQTQmqEpHGnMDM1RI6XvAdfV+D9lgQr4+i7TWETE2Iqb+XtoKwr8NCp11VRsoHw4yvUqGc5COCaoaEfMmcfFp3GXr0+n0Cw==" + } + ], + "events": [], + "file_map": { + "18": { + "source": "use crate::grumpkin_scalar::GrumpkinScalar;\nuse crate::scalar_mul::fixed_base_embedded_curve;\n\npub fn grumpkin_fixed_base(scalar: GrumpkinScalar) -> [Field; 2] {\n // TODO: this should use both the low and high limbs to do the scalar multiplication\n fixed_base_embedded_curve(scalar.low, scalar.high)\n}\n", + "path": "std/grumpkin_scalar_mul.nr" + }, + "28": { + "source": "struct Option {\n _is_some: bool,\n _value: T,\n}\n\nimpl Option {\n /// Constructs a None value\n pub fn none() -> Self {\n Self { _is_some: false, _value: crate::unsafe::zeroed() }\n }\n\n /// Constructs a Some wrapper around the given value\n pub fn some(_value: T) -> Self {\n Self { _is_some: true, _value }\n }\n\n /// True if this Option is None\n pub fn is_none(self) -> bool {\n !self._is_some\n }\n\n /// True if this Option is Some\n pub fn is_some(self) -> bool {\n self._is_some\n }\n\n /// Asserts `self.is_some()` and returns the wrapped value.\n pub fn unwrap(self) -> T {\n assert(self._is_some);\n self._value\n }\n\n /// Returns the inner value without asserting `self.is_some()`\n /// Note that if `self` is `None`, there is no guarantee what value will be returned,\n /// only that it will be of type `T`.\n pub fn unwrap_unchecked(self) -> T {\n self._value\n }\n\n /// Returns the wrapped value if `self.is_some()`. Otherwise, returns the given default value.\n pub fn unwrap_or(self, default: T) -> T {\n if self._is_some {\n self._value\n } else {\n default\n }\n }\n\n /// Returns the wrapped value if `self.is_some()`. Otherwise, calls the given function to return\n /// a default value.\n pub fn unwrap_or_else(self, default: fn[Env]() -> T) -> T {\n if self._is_some {\n self._value\n } else {\n default()\n }\n }\n\n /// If self is `Some(x)`, this returns `Some(f(x))`. Otherwise, this returns `None`.\n pub fn map(self, f: fn[Env](T) -> U) -> Option {\n if self._is_some {\n Option::some(f(self._value))\n } else {\n Option::none()\n }\n }\n\n /// If self is `Some(x)`, this returns `f(x)`. Otherwise, this returns the given default value.\n pub fn map_or(self, default: U, f: fn[Env](T) -> U) -> U {\n if self._is_some {\n f(self._value)\n } else {\n default\n }\n }\n\n /// If self is `Some(x)`, this returns `f(x)`. Otherwise, this returns `default()`.\n pub fn map_or_else(self, default: fn[Env1]() -> U, f: fn[Env2](T) -> U) -> U {\n if self._is_some {\n f(self._value)\n } else {\n default()\n }\n }\n\n /// Returns None if self is None. Otherwise, this returns `other`.\n pub fn and(self, other: Self) -> Self {\n if self.is_none() {\n Option::none()\n } else {\n other\n }\n }\n\n /// If self is None, this returns None. Otherwise, this calls the given function\n /// with the Some value contained within self, and returns the result of that call.\n ///\n /// In some languages this function is called `flat_map` or `bind`.\n pub fn and_then(self, f: fn[Env](T) -> Option) -> Option {\n if self._is_some {\n f(self._value)\n } else {\n Option::none()\n }\n }\n\n /// If self is Some, return self. Otherwise, return `other`.\n pub fn or(self, other: Self) -> Self {\n if self._is_some {\n self\n } else {\n other\n }\n }\n\n /// If self is Some, return self. Otherwise, return `default()`.\n pub fn or_else(self, default: fn[Env]() -> Self) -> Self {\n if self._is_some {\n self\n } else {\n default()\n }\n }\n\n // If only one of the two Options is Some, return that option.\n // Otherwise, if both options are Some or both are None, None is returned.\n pub fn xor(self, other: Self) -> Self {\n if self._is_some {\n if other._is_some {\n Option::none()\n } else {\n self\n }\n } else if other._is_some {\n other\n } else {\n Option::none()\n }\n }\n\n /// Returns `Some(x)` if self is `Some(x)` and `predicate(x)` is true.\n /// Otherwise, this returns `None`\n pub fn filter(self, predicate: fn[Env](T) -> bool) -> Self {\n if self._is_some {\n if predicate(self._value) {\n self\n } else {\n Option::none()\n }\n } else {\n Option::none()\n }\n }\n\n /// Flattens an Option> into a Option.\n /// This returns None if the outer Option is None. Otherwise, this returns the inner Option.\n pub fn flatten(option: Option>) -> Option {\n if option._is_some {\n option._value\n } else {\n Option::none()\n }\n }\n}\n", + "path": "std/option.nr" + }, + "39": { + "source": "// A contract used for running benchmarks.\n// We should try to change this contract as little as possible, since any modification\n// would alter the metrics we're capturing in the benchmarks, and we want to keep the\n// subject being tested as unmodified as possible so we can detect metric changes that\n// arise from code changes.\n\ncontract Benchmarking {\n use dep::value_note::{\n utils::{increment, decrement},\n value_note::{VALUE_NOTE_LEN, ValueNote, ValueNoteMethods},\n };\n\n use dep::aztec::{\n protocol_types::{\n abis::function_selector::FunctionSelector,\n address::AztecAddress,\n },\n context::{Context},\n note::{utils as note_utils, note_getter_options::NoteGetterOptions, note_header::NoteHeader},\n log::emit_unencrypted_log,\n state_vars::{map::Map, public_state::PublicState, set::Set},\n types::type_serialization::field_serialization::{FieldSerializationMethods, FIELD_SERIALIZED_LEN},\n };\n\n struct Storage {\n notes: Map>,\n balances: Map>,\n }\n\n impl Storage {\n fn init(context: Context) -> pub Self {\n Storage {\n notes: Map::new(context, 1, |context, slot| { Set::new(context, slot, ValueNoteMethods) }),\n balances: Map::new(context, 2, |context, slot| { PublicState::new(context, slot, FieldSerializationMethods) }),\n }\n }\n }\n\n #[aztec(private)]\n fn constructor() {}\n\n // Creates a new value note for the target owner. Use this method to seed an initial set of notes.\n #[aztec(private)]\n fn create_note(owner: AztecAddress, value: Field) {\n increment(storage.notes.at(owner), value, owner);\n }\n\n // Deletes a note at a specific index in the set and creates a new one with the same value.\n // We explicitly pass in the note index so we can ensure we consume different notes when sending\n // multiple txs that will land on the same block.\n // See https://discourse.aztec.network/t/utxo-concurrency-issues-for-private-state/635\n // by @rahul-kothari for a full explanation on why this is needed.\n #[aztec(private)]\n fn recreate_note(owner: AztecAddress, index: u32) {\n let owner_notes = storage.notes.at(owner);\n let getter_options = NoteGetterOptions::new().set_limit(1).set_offset(index);\n let notes = owner_notes.get_notes(getter_options);\n let note = notes[0].unwrap_unchecked();\n owner_notes.remove(note);\n increment(owner_notes, note.value, owner);\n }\n\n // Reads and writes to public storage and enqueues a call to another public function.\n #[aztec(public)]\n fn increment_balance(owner: AztecAddress, value: Field) {\n let current = storage.balances.at(owner).read();\n storage.balances.at(owner).write(current + value);\n let _callStackItem1 = context.call_public_function(\n context.this_address(),\n FunctionSelector::from_signature(\"broadcast((Field))\"),\n [owner.to_field()]\n );\n }\n\n // Emits a public log.\n #[aztec(public)]\n fn broadcast(owner: AztecAddress) {\n emit_unencrypted_log(&mut context, storage.balances.at(owner).read());\n }\n\n unconstrained fn compute_note_hash_and_nullifier(\n contract_address: AztecAddress,\n nonce: Field,\n storage_slot: Field,\n serialized_note: [Field; VALUE_NOTE_LEN]\n ) -> pub [Field; 4] {\n let note_header = NoteHeader::new(contract_address, nonce, storage_slot);\n note_utils::compute_note_hash_and_nullifier(ValueNoteMethods, note_header, serialized_note)\n }\n}\n", + "path": "/home/santiago/Projects/aztec3-packages/yarn-project/noir-contracts/contracts/benchmarking_contract/src/main.nr" + }, + "40": { + "source": "use crate::{\n abi::{\n PrivateContextInputs,\n PublicContextInputs,\n },\n key::nullifier_key::validate_nullifier_key_against_address,\n messaging::process_l1_to_l2_message,\n oracle::{\n arguments,\n call_private_function::call_private_function_internal,\n public_call::call_public_function_internal,\n enqueue_public_function_call::enqueue_public_function_call_internal,\n context::get_portal_address,\n get_block_header::get_block_header,\n nullifier_key::get_nullifier_key_pair,\n },\n types::vec::BoundedVec,\n utils::Reader,\n};\nuse dep::protocol_types::{\n abis::{\n block_header::BlockHeader,\n call_context::CallContext,\n function_data::FunctionData,\n function_selector::FunctionSelector,\n private_circuit_public_inputs::PrivateCircuitPublicInputs,\n public_circuit_public_inputs::PublicCircuitPublicInputs,\n call_stack_item::PrivateCallStackItem,\n call_stack_item::PublicCallStackItem,\n side_effect::{SideEffect, SideEffectLinkedToNoteHash},\n },\n address::{\n AztecAddress,\n EthAddress,\n },\n constants::{\n MAX_NEW_COMMITMENTS_PER_CALL,\n MAX_NEW_L2_TO_L1_MSGS_PER_CALL,\n MAX_NEW_NULLIFIERS_PER_CALL,\n MAX_PRIVATE_CALL_STACK_LENGTH_PER_CALL,\n MAX_PUBLIC_CALL_STACK_LENGTH_PER_CALL,\n MAX_PUBLIC_DATA_READS_PER_CALL,\n MAX_PUBLIC_DATA_UPDATE_REQUESTS_PER_CALL,\n MAX_READ_REQUESTS_PER_CALL,\n NUM_FIELDS_PER_SHA256,\n RETURN_VALUES_LENGTH,\n },\n contrakt::{\n deployment_data::ContractDeploymentData,\n storage_read::StorageRead,\n storage_update_request::StorageUpdateRequest,\n },\n hash::hash_args,\n grumpkin_point::GrumpkinPoint,\n};\nuse dep::std::{\n grumpkin_scalar::GrumpkinScalar,\n option::Option,\n};\n\n// TODO(https://github.com/AztecProtocol/aztec-packages/issues/1165)\n// use dep::std::collections::vec::Vec;\n\n// When finished, one can call .finish() to convert back to the abi\nstruct PrivateContext {\n // docs:start:private-context\n inputs: PrivateContextInputs,\n side_effect_counter: u32,\n\n args_hash : Field,\n return_values : BoundedVec,\n\n read_requests: BoundedVec,\n\n new_commitments: BoundedVec,\n new_nullifiers: BoundedVec,\n\n private_call_stack_hashes : BoundedVec,\n public_call_stack_hashes : BoundedVec,\n new_l2_to_l1_msgs : BoundedVec,\n // docs:end:private-context\n\n block_header: BlockHeader,\n\n // TODO(https://github.com/AztecProtocol/aztec-packages/issues/1165)\n // encrypted_logs_preimages: Vec,\n // unencrypted_logs_preimages: Vec,\n}\n\nimpl PrivateContext {\n pub fn new(inputs: PrivateContextInputs, args_hash: Field) -> PrivateContext {\n PrivateContext {\n inputs: inputs,\n side_effect_counter: inputs.call_context.start_side_effect_counter,\n\n args_hash: args_hash,\n return_values: BoundedVec::new(0),\n\n read_requests: BoundedVec::new(SideEffect::empty()),\n\n new_commitments: BoundedVec::new(SideEffect::empty()),\n new_nullifiers: BoundedVec::new(SideEffectLinkedToNoteHash::empty()),\n\n block_header: inputs.block_header,\n\n private_call_stack_hashes: BoundedVec::new(0),\n public_call_stack_hashes: BoundedVec::new(0),\n new_l2_to_l1_msgs: BoundedVec::new(0),\n\n // TODO(https://github.com/AztecProtocol/aztec-packages/issues/1165)\n // encrypted_logs_preimages: Vec::new(),\n // unencrypted_logs_preimages: Vec::new(),\n }\n }\n\n pub fn msg_sender(self) -> AztecAddress {\n self.inputs.call_context.msg_sender\n }\n\n pub fn this_address(self) -> AztecAddress {\n self.inputs.call_context.storage_contract_address\n }\n\n pub fn this_portal_address(self) -> EthAddress {\n self.inputs.call_context.portal_contract_address\n }\n\n pub fn chain_id(self) -> Field {\n self.inputs.private_global_variables.chain_id\n }\n\n pub fn version(self) -> Field {\n self.inputs.private_global_variables.version\n }\n\n pub fn selector(self) -> FunctionSelector {\n self.inputs.call_context.function_selector\n }\n\n pub fn get_block_header(self, block_number: u32) -> BlockHeader {\n get_block_header(block_number, self)\n }\n\n pub fn finish(self) -> PrivateCircuitPublicInputs {\n // TODO(https://github.com/AztecProtocol/aztec-packages/issues/1165)\n let encrypted_logs_hash = [0; NUM_FIELDS_PER_SHA256];\n let unencrypted_logs_hash = [0; NUM_FIELDS_PER_SHA256];\n let encrypted_log_preimages_length = 0;\n let unencrypted_log_preimages_length = 0;\n\n let priv_circuit_pub_inputs = PrivateCircuitPublicInputs {\n call_context: self.inputs.call_context,\n args_hash: self.args_hash,\n return_values: self.return_values.storage,\n read_requests: self.read_requests.storage,\n new_commitments: self.new_commitments.storage,\n new_nullifiers: self.new_nullifiers.storage,\n private_call_stack_hashes: self.private_call_stack_hashes.storage,\n public_call_stack_hashes: self.public_call_stack_hashes.storage,\n new_l2_to_l1_msgs: self.new_l2_to_l1_msgs.storage,\n end_side_effect_counter: self.side_effect_counter,\n encrypted_logs_hash: encrypted_logs_hash,\n unencrypted_logs_hash: unencrypted_logs_hash,\n encrypted_log_preimages_length: encrypted_log_preimages_length,\n unencrypted_log_preimages_length: unencrypted_log_preimages_length,\n block_header: self.block_header,\n contract_deployment_data: self.inputs.contract_deployment_data,\n chain_id: self.inputs.private_global_variables.chain_id,\n version: self.inputs.private_global_variables.version,\n };\n priv_circuit_pub_inputs\n }\n\n pub fn push_read_request(&mut self, read_request: Field) {\n let side_effect = SideEffect {\n value: read_request,\n counter: self.side_effect_counter,\n };\n self.read_requests.push(side_effect);\n self.side_effect_counter = self.side_effect_counter + 1;\n }\n\n pub fn push_new_note_hash(&mut self, note_hash: Field) {\n let side_effect = SideEffect {\n value: note_hash,\n counter: self.side_effect_counter,\n };\n self.new_commitments.push(side_effect);\n self.side_effect_counter = self.side_effect_counter + 1;\n }\n\n pub fn push_new_nullifier(&mut self, nullifier: Field, nullified_commitment: Field) {\n let side_effect = SideEffectLinkedToNoteHash {\n value: nullifier,\n note_hash: nullified_commitment,\n counter: self.side_effect_counter,\n };\n self.new_nullifiers.push(side_effect);\n self.side_effect_counter = self.side_effect_counter + 1;\n }\n\n pub fn request_nullifier_secret_key(&mut self, account: AztecAddress) -> GrumpkinScalar {\n let key_pair = get_nullifier_key_pair(account);\n validate_nullifier_key_against_address(account, key_pair.public_key, key_pair.secret_key);\n // TODO: Add request to context.\n // self.context.push_nullifier_key_validation_request(public_key, secret_key);\n key_pair.secret_key\n }\n\n // docs:start:context_message_portal\n pub fn message_portal(&mut self, content: Field) \n // docs:end:context_message_portal\n {\n self.new_l2_to_l1_msgs.push(content);\n }\n\n // PrivateContextInputs must be temporarily passed in to prevent too many unknowns\n // Note this returns self to get around an issue where mutable structs do not maintain mutations unless reassigned\n // docs:start:context_consume_l1_to_l2_message\n // docs:start:consume_l1_to_l2_message\n pub fn consume_l1_to_l2_message(\n &mut self,\n msg_key: Field,\n content: Field,\n secret: Field\n ) \n // docs:end:context_consume_l1_to_l2_message\n {\n let nullifier = process_l1_to_l2_message(self.block_header.l1_to_l2_message_tree_root, self.this_address(), self.this_portal_address(), self.chain_id(), self.version(), msg_key, content, secret);\n\n // Push nullifier (and the \"commitment\" corresponding to this can be \"empty\")\n self.push_new_nullifier(nullifier, 0)\n }\n // docs:end:consume_l1_to_l2_message\n\n pub fn accumulate_encrypted_logs(&mut self, log: [Field; N]) {\n let _void1 = self.inputs;\n let _void2 = log;\n // TODO(https://github.com/AztecProtocol/aztec-packages/issues/1165)\n }\n\n pub fn accumulate_unencrypted_logs(&mut self, log: T) {\n let _void1 = self.inputs;\n let _void2 = log;\n // TODO(https://github.com/AztecProtocol/aztec-packages/issues/1165)\n }\n\n pub fn call_private_function(\n &mut self,\n contract_address: AztecAddress, \n function_selector: FunctionSelector, \n args: [Field; ARGS_COUNT]\n ) -> [Field; RETURN_VALUES_LENGTH] {\n let args_hash = hash_args(args);\n assert(args_hash == arguments::pack_arguments(args));\n self.call_private_function_with_packed_args(contract_address, function_selector, args_hash)\n }\n\n pub fn call_private_function_no_args(\n &mut self,\n contract_address: AztecAddress, \n function_selector: FunctionSelector, \n ) -> [Field; RETURN_VALUES_LENGTH] {\n self.call_private_function_with_packed_args(contract_address, function_selector, 0)\n }\n\n pub fn call_private_function_with_packed_args(\n &mut self,\n contract_address: AztecAddress,\n function_selector: FunctionSelector,\n args_hash: Field\n ) -> [Field; RETURN_VALUES_LENGTH] {\n let fields = call_private_function_internal(\n contract_address,\n function_selector, \n args_hash,\n self.side_effect_counter,\n );\n let mut reader = Reader::new(fields);\n\n let item = PrivateCallStackItem {\n contract_address: AztecAddress::from_field(reader.read()),\n function_data: FunctionData {\n selector: FunctionSelector::from_field(reader.read()),\n is_internal: reader.read() as bool,\n is_private: reader.read() as bool,\n is_constructor: reader.read() as bool,\n },\n public_inputs: PrivateCircuitPublicInputs {\n call_context: CallContext {\n msg_sender : AztecAddress::from_field(reader.read()),\n storage_contract_address : AztecAddress::from_field(reader.read()),\n portal_contract_address : EthAddress::from_field(reader.read()),\n function_selector: FunctionSelector::from_field(reader.read()), // practically same as fields[1]\n is_delegate_call : reader.read() as bool,\n is_static_call : reader.read() as bool,\n is_contract_deployment: reader.read() as bool,\n start_side_effect_counter: reader.read() as u32,\n },\n args_hash: reader.read(),\n return_values: reader.read_array([0; RETURN_VALUES_LENGTH]), // +1\n read_requests: reader.read_struct_array(SideEffect::deserialise, [SideEffect::empty(); MAX_READ_REQUESTS_PER_CALL]),\n new_commitments: reader.read_struct_array(SideEffect::deserialise, [SideEffect::empty(); MAX_NEW_COMMITMENTS_PER_CALL]),\n new_nullifiers: reader.read_struct_array(SideEffectLinkedToNoteHash::deserialise, [SideEffectLinkedToNoteHash::empty(); MAX_NEW_NULLIFIERS_PER_CALL]),\n private_call_stack_hashes: reader.read_array([0; MAX_PRIVATE_CALL_STACK_LENGTH_PER_CALL]),\n public_call_stack_hashes: reader.read_array([0; MAX_PUBLIC_CALL_STACK_LENGTH_PER_CALL]),\n new_l2_to_l1_msgs: reader.read_array([0; MAX_NEW_L2_TO_L1_MSGS_PER_CALL]),\n end_side_effect_counter: reader.read() as u32,\n encrypted_logs_hash: reader.read_array([0; NUM_FIELDS_PER_SHA256]),\n unencrypted_logs_hash: reader.read_array([0; NUM_FIELDS_PER_SHA256]),\n encrypted_log_preimages_length: reader.read(),\n unencrypted_log_preimages_length: reader.read(),\n block_header: BlockHeader{\n // Must match order in `private_circuit_public_inputs.hpp`\n note_hash_tree_root : reader.read(),\n nullifier_tree_root : reader.read(),\n contract_tree_root : reader.read(),\n l1_to_l2_message_tree_root : reader.read(),\n archive_root : reader.read(),\n public_data_tree_root: reader.read(),\n global_variables_hash: reader.read(),\n },\n contract_deployment_data: ContractDeploymentData {\n deployer_public_key: GrumpkinPoint {\n x: reader.read(), \n y: reader.read()\n },\n constructor_vk_hash : reader.read(),\n function_tree_root : reader.read(),\n contract_address_salt : reader.read(),\n portal_contract_address : EthAddress::from_field(reader.read()),\n },\n chain_id: reader.read(),\n version: reader.read(),\n },\n is_execution_request: reader.read() as bool,\n };\n\n reader.finish();\n\n assert_eq(item.public_inputs.call_context.start_side_effect_counter, self.side_effect_counter);\n self.side_effect_counter = item.public_inputs.end_side_effect_counter + 1;\n \n assert(contract_address.eq(item.contract_address));\n assert(function_selector.eq(item.function_data.selector));\n\n assert(args_hash == item.public_inputs.args_hash);\n\n assert(item.is_execution_request == false);\n\n // Assert that the call context of the enqueued call generated by the oracle matches our request.\n // We are issuing a regular call which is not delegate, static, or deployment. We also constrain\n // the msg_sender in the nested call to be equal to our address, and the execution context address\n // for the nested call to be equal to the address we actually called.\n assert(item.public_inputs.call_context.is_delegate_call == false);\n assert(item.public_inputs.call_context.is_static_call == false);\n assert(item.public_inputs.call_context.is_contract_deployment == false);\n assert(item.public_inputs.call_context.msg_sender.eq(self.inputs.call_context.storage_contract_address));\n assert(item.public_inputs.call_context.storage_contract_address.eq(contract_address));\n\n self.private_call_stack_hashes.push(item.hash());\n\n item.public_inputs.return_values\n }\n\n pub fn call_public_function(\n &mut self,\n contract_address: AztecAddress, \n function_selector: FunctionSelector, \n args: [Field; ARGS_COUNT]\n ) {\n let args_hash = hash_args(args);\n assert(args_hash == arguments::pack_arguments(args));\n self.call_public_function_with_packed_args(contract_address, function_selector, args_hash)\n }\n\n pub fn call_public_function_no_args(\n &mut self,\n contract_address: AztecAddress, \n function_selector: FunctionSelector,\n ) {\n self.call_public_function_with_packed_args(contract_address, function_selector, 0)\n }\n\n pub fn call_public_function_with_packed_args(\n &mut self,\n contract_address: AztecAddress,\n function_selector: FunctionSelector,\n args_hash: Field\n ) {\n let fields = enqueue_public_function_call_internal(\n contract_address, \n function_selector, \n args_hash,\n self.side_effect_counter\n );\n\n let mut reader = Reader::new(fields);\n\n let item = PublicCallStackItem {\n contract_address: AztecAddress::from_field(reader.read()),\n function_data: FunctionData {\n selector: FunctionSelector::from_field(reader.read()),\n is_internal: reader.read() as bool,\n is_private: reader.read() as bool,\n is_constructor: reader.read() as bool,\n },\n public_inputs: PublicCircuitPublicInputs {\n call_context: CallContext {\n msg_sender : AztecAddress::from_field(reader.read()),\n storage_contract_address : AztecAddress::from_field(reader.read()),\n portal_contract_address : EthAddress::from_field(reader.read()),\n function_selector: FunctionSelector::from_field(reader.read()), // practically same as fields[1]\n is_delegate_call : reader.read() as bool,\n is_static_call : reader.read() as bool,\n is_contract_deployment: reader.read() as bool,\n start_side_effect_counter: reader.read() as u32,\n },\n args_hash: reader.read(),\n return_values: [0; RETURN_VALUES_LENGTH],\n contract_storage_update_requests: [StorageUpdateRequest::empty(); MAX_PUBLIC_DATA_UPDATE_REQUESTS_PER_CALL],\n contract_storage_reads: [StorageRead::empty(); MAX_PUBLIC_DATA_READS_PER_CALL],\n public_call_stack_hashes: [0; MAX_PUBLIC_CALL_STACK_LENGTH_PER_CALL],\n new_commitments: [SideEffect::empty(); MAX_NEW_COMMITMENTS_PER_CALL],\n new_nullifiers: [SideEffectLinkedToNoteHash::empty(); MAX_NEW_NULLIFIERS_PER_CALL],\n new_l2_to_l1_msgs:[0; MAX_NEW_L2_TO_L1_MSGS_PER_CALL],\n unencrypted_logs_hash:[0; NUM_FIELDS_PER_SHA256],\n unencrypted_log_preimages_length: 0,\n block_header: BlockHeader::empty(),\n prover_address: AztecAddress::zero(),\n },\n is_execution_request: true,\n };\n reader.finish();\n\n assert(contract_address.eq(item.contract_address));\n assert(function_selector.eq(item.function_data.selector));\n\n assert_eq(item.public_inputs.call_context.start_side_effect_counter, self.side_effect_counter);\n // We increment the sideffect counter by one, to account for the call itself being a side effect.\n self.side_effect_counter = self.side_effect_counter + 1;\n \n assert(args_hash == item.public_inputs.args_hash);\n\n // Assert that the call context of the enqueued call generated by the oracle matches our request.\n // We are issuing a regular call which is not delegate, static, or deployment. We also constrain\n // the msg_sender in the nested call to be equal to our address, and the execution context address\n // for the nested call to be equal to the address we actually called.\n assert(item.public_inputs.call_context.is_delegate_call == false);\n assert(item.public_inputs.call_context.is_static_call == false);\n assert(item.public_inputs.call_context.is_contract_deployment == false);\n assert(item.public_inputs.call_context.msg_sender.eq(self.inputs.call_context.storage_contract_address));\n assert(item.public_inputs.call_context.storage_contract_address.eq(contract_address));\n\n self.public_call_stack_hashes.push(item.hash());\n }\n}\n\nstruct PublicContext {\n inputs: PublicContextInputs,\n side_effect_counter: u32,\n\n args_hash : Field,\n return_values : BoundedVec,\n\n contract_storage_update_requests: BoundedVec,\n contract_storage_reads: BoundedVec,\n public_call_stack_hashes: BoundedVec,\n\n new_commitments: BoundedVec,\n new_nullifiers: BoundedVec,\n\n new_l2_to_l1_msgs: BoundedVec,\n\n unencrypted_logs_hash: BoundedVec,\n unencrypted_logs_preimages_length: Field,\n\n block_header: BlockHeader,\n prover_address: AztecAddress,\n}\n\nimpl PublicContext {\n pub fn new(inputs: PublicContextInputs, args_hash: Field) -> PublicContext {\n let empty_storage_read = StorageRead::empty();\n let empty_storage_update = StorageUpdateRequest::empty();\n PublicContext {\n inputs: inputs,\n side_effect_counter: inputs.call_context.start_side_effect_counter,\n\n args_hash: args_hash,\n return_values: BoundedVec::new(0),\n\n contract_storage_update_requests: BoundedVec::new(empty_storage_update),\n contract_storage_reads: BoundedVec::new(empty_storage_read),\n public_call_stack_hashes: BoundedVec::new(0),\n\n new_commitments: BoundedVec::new(SideEffect::empty()),\n new_nullifiers: BoundedVec::new(SideEffectLinkedToNoteHash::empty()),\n\n new_l2_to_l1_msgs: BoundedVec::new(0),\n\n \n unencrypted_logs_hash: BoundedVec::new(0),\n unencrypted_logs_preimages_length: 0,\n\n block_header: inputs.block_header,\n prover_address: AztecAddress::zero(),\n\n // TODO(https://github.com/AztecProtocol/aztec-packages/issues/1165)\n // encrypted_logs_preimages: Vec::new(),\n // unencrypted_logs_preimages: Vec::new(),\n }\n }\n\n pub fn msg_sender(self) -> AztecAddress {\n self.inputs.call_context.msg_sender\n }\n\n pub fn this_address(self) -> AztecAddress {\n self.inputs.call_context.storage_contract_address\n }\n\n pub fn this_portal_address(self) -> EthAddress {\n self.inputs.call_context.portal_contract_address\n }\n\n pub fn chain_id(self) -> Field {\n self.inputs.public_global_variables.chain_id\n }\n\n pub fn version(self) -> Field {\n self.inputs.public_global_variables.version\n }\n\n pub fn selector(self) -> FunctionSelector {\n self.inputs.call_context.function_selector\n }\n\n pub fn block_number(self) -> Field {\n self.inputs.public_global_variables.block_number\n }\n\n pub fn timestamp(self) -> Field {\n self.inputs.public_global_variables.timestamp\n }\n\n pub fn finish(self) -> PublicCircuitPublicInputs {\n // TODO(https://github.com/AztecProtocol/aztec-packages/issues/1165)\n let unencrypted_logs_hash = [0; NUM_FIELDS_PER_SHA256];\n let unencrypted_log_preimages_length = 0;\n\n\n // Compute the public call stack hashes\n let pub_circuit_pub_inputs = PublicCircuitPublicInputs {\n call_context: self.inputs.call_context, // Done\n args_hash: self.args_hash, // Done\n contract_storage_update_requests: self.contract_storage_update_requests.storage,\n contract_storage_reads: self.contract_storage_reads.storage,\n return_values: self.return_values.storage,\n new_commitments: self.new_commitments.storage,\n new_nullifiers: self.new_nullifiers.storage,\n public_call_stack_hashes: self.public_call_stack_hashes.storage,\n new_l2_to_l1_msgs: self.new_l2_to_l1_msgs.storage,\n unencrypted_logs_hash: unencrypted_logs_hash,\n unencrypted_log_preimages_length: unencrypted_log_preimages_length,\n block_header: self.inputs.block_header,\n prover_address: self.prover_address,\n };\n pub_circuit_pub_inputs\n }\n\n pub fn push_new_note_hash(&mut self, note_hash: Field) {\n let side_effect = SideEffect {\n value: note_hash,\n counter: self.side_effect_counter\n };\n self.new_commitments.push(side_effect);\n self.side_effect_counter = self.side_effect_counter + 1;\n }\n\n pub fn push_new_nullifier(&mut self, nullifier: Field, _nullified_commitment: Field) {\n let side_effect = SideEffectLinkedToNoteHash {\n value: nullifier,\n note_hash: 0, // cannot nullify pending notes in public context\n counter: self.side_effect_counter\n };\n self.new_nullifiers.push(side_effect);\n self.side_effect_counter = self.side_effect_counter + 1;\n }\n\n pub fn message_portal(&mut self, content: Field) {\n self.new_l2_to_l1_msgs.push(content);\n }\n\n // PrivateContextInputs must be temporarily passed in to prevent too many unknowns\n // Note this returns self to get around an issue where mutable structs do not maintain mutations unless reassigned\n pub fn consume_l1_to_l2_message(&mut self, msg_key: Field, content: Field, secret: Field) {\n let this = (*self).this_address();\n let nullifier = process_l1_to_l2_message(self.block_header.l1_to_l2_message_tree_root, this, self.this_portal_address(), self.chain_id(), self.version(), msg_key, content, secret);\n\n // Push nullifier (and the \"commitment\" corresponding to this can be \"empty\")\n self.push_new_nullifier(nullifier, 0)\n }\n\n pub fn accumulate_encrypted_logs(&mut self, log: [Field; N]) {\n let _void1 = self;\n let _void2 = log;\n // TODO(https://github.com/AztecProtocol/aztec-packages/issues/1165)\n }\n\n pub fn accumulate_unencrypted_logs(&mut self, log: T) {\n let _void1 = self;\n let _void2 = log;\n // TODO(https://github.com/AztecProtocol/aztec-packages/issues/1165)\n }\n\n pub fn call_public_function(\n _self: Self,\n contract_address: AztecAddress, \n function_selector: FunctionSelector,\n args: [Field; ARGS_COUNT],\n ) -> [Field; RETURN_VALUES_LENGTH] {\n let args_hash = hash_args(args);\n assert(args_hash == arguments::pack_arguments(args));\n call_public_function_internal(\n contract_address, \n function_selector, \n args_hash,\n )\n }\n\n pub fn call_public_function_no_args(\n _self: Self,\n contract_address: AztecAddress, \n function_selector: FunctionSelector,\n ) -> [Field; RETURN_VALUES_LENGTH] {\n call_public_function_internal(\n contract_address, \n function_selector, \n 0,\n )\n }\n\n}\n\nstruct Context {\n private: Option<&mut PrivateContext>,\n public: Option<&mut PublicContext>,\n}\n\nimpl Context {\n pub fn private(context: &mut PrivateContext) -> Context {\n Context {\n private: Option::some(context),\n public: Option::none()\n }\n }\n\n pub fn public(context: &mut PublicContext) -> Context {\n Context {\n public: Option::some(context),\n private: Option::none()\n }\n }\n\n pub fn none() -> Context {\n Context {\n public: Option::none(),\n private: Option::none()\n }\n }\n}\n", + "path": "/home/santiago/Projects/aztec3-packages/yarn-project/aztec-nr/aztec/src/context.nr" + }, + "43": { + "source": "use dep::protocol_types::{\n abis::{\n block_header::BlockHeader,\n call_context::CallContext,\n private_circuit_public_inputs::PrivateCircuitPublicInputs,\n public_circuit_public_inputs::PublicCircuitPublicInputs,\n },\n contrakt::deployment_data::ContractDeploymentData,\n hash::hash_args,\n};\n\n// docs:start:private-global-variables\nstruct PrivateGlobalVariables {\n chain_id: Field,\n version: Field,\n}\n// docs:end:private-global-variables\n\nimpl PrivateGlobalVariables {\n fn serialize(self) -> [Field; 2] {\n [self.chain_id, self.version]\n }\n}\n\n// docs:start:public-global-variables\nstruct PublicGlobalVariables {\n chain_id: Field,\n version: Field,\n block_number: Field,\n timestamp: Field,\n}\n// docs:end:public-global-variables\n\nimpl PublicGlobalVariables {\n fn serialize(self) -> [Field; 4] {\n [self.chain_id, self.version, self.block_number, self.timestamp]\n }\n}\n\n// PrivateContextInputs are expected to be provided to each private function\n// docs:start:private-context-inputs\nstruct PrivateContextInputs {\n call_context : CallContext,\n block_header: BlockHeader,\n contract_deployment_data: ContractDeploymentData,\n private_global_variables: PrivateGlobalVariables,\n}\n// docs:end:private-context-inputs\n\n// PublicContextInputs are expected to be provided to each public function\n// docs:start:public-context-inputs\nstruct PublicContextInputs {\n call_context: CallContext,\n block_header: BlockHeader,\n\n public_global_variables: PublicGlobalVariables,\n}\n// docs:end:public-context-inputs\n\nstruct Hasher {\n fields: [Field],\n}\n\nimpl Hasher {\n pub fn new()-> Self {\n Self { fields: [] }\n }\n\n pub fn add(&mut self, field: Field) {\n self.fields = self.fields.push_back(field);\n }\n\n pub fn add_multiple(&mut self, fields: [Field; N]) {\n for i in 0..N {\n self.fields = self.fields.push_back(fields[i]);\n }\n }\n\n pub fn hash(self) -> Field {\n hash_args(self.fields)\n }\n}\n", + "path": "/home/santiago/Projects/aztec3-packages/yarn-project/aztec-nr/aztec/src/abi.nr" + }, + "47": { + "source": "use dep::std::option::Option;\nuse crate::abi::PublicContextInputs;\nuse dep::protocol_types::{\n constants::{MAX_NOTES_PER_PAGE, MAX_READ_REQUESTS_PER_CALL},\n abis::side_effect::{SideEffect, SideEffectLinkedToNoteHash},\n};\nuse crate::context::{PrivateContext, PublicContext, Context};\nuse crate::note::{\n lifecycle::{create_note, create_note_hash_from_public, destroy_note},\n note_getter::{get_notes, view_notes},\n note_getter_options::NoteGetterOptions,\n note_header::NoteHeader,\n note_interface::NoteInterface,\n note_viewer_options::NoteViewerOptions,\n utils::compute_note_hash_for_read_or_nullify,\n};\n\n// docs:start:struct\nstruct Set {\n context: Context,\n storage_slot: Field,\n note_interface: NoteInterface,\n}\n// docs:end:struct\n\nimpl Set {\n // docs:start:new\n pub fn new(\n context: Context,\n storage_slot: Field,\n note_interface: NoteInterface,\n ) -> Self {\n assert(storage_slot != 0, \"Storage slot 0 not allowed. Storage slots must start from 1.\");\n Set {\n context,\n storage_slot,\n note_interface,\n }\n }\n // docs:end:new\n\n // docs:start:insert\n pub fn insert(self,\n note: &mut Note,\n broadcast: bool,\n ) {\n create_note(\n self.context.private.unwrap(),\n self.storage_slot,\n note,\n self.note_interface,\n broadcast,\n );\n }\n // docs:end:insert\n\n // docs:start:insert_from_public\n pub fn insert_from_public(self, note: &mut Note) {\n create_note_hash_from_public(\n self.context.public.unwrap(),\n self.storage_slot,\n note,\n self.note_interface,\n );\n }\n // docs:end:insert_from_public\n \n // DEPRECATED\n fn assert_contains_and_remove(_self: Self, _note: &mut Note, _nonce: Field) {\n assert(false, \"`assert_contains_and_remove` has been deprecated. Please call PXE.addNote() to add a note to the database. Then use Set.get_notes() and Set.remove() in your contract to verify and remove a note.\");\n }\n\n // DEPRECATED\n fn assert_contains_and_remove_publicly_created(_self: Self, _note: &mut Note) {\n assert(false, \"`assert_contains_and_remove_publicly_created` has been deprecated. Please call PXE.addNote() to add a note to the database. Then use Set.get_notes() and Set.remove() in your contract to verify and remove a note.\");\n }\n\n // docs:start:remove\n pub fn remove(self, note: Note) {\n let context = self.context.private.unwrap();\n let note_hash = compute_note_hash_for_read_or_nullify(self.note_interface, note);\n let has_been_read = context.read_requests.any(|r: SideEffect| r.value == note_hash);\n assert(has_been_read, \"Can only remove a note that has been read from the set.\");\n\n destroy_note(\n context,\n note,\n self.note_interface,\n );\n }\n // docs:end:remove\n\n // docs:start:get_notes\n pub fn get_notes(\n self,\n options: NoteGetterOptions,\n ) -> [Option; MAX_READ_REQUESTS_PER_CALL] {\n let storage_slot = self.storage_slot;\n let opt_notes = get_notes(\n self.context.private.unwrap(),\n storage_slot,\n self.note_interface,\n options,\n );\n opt_notes\n }\n // docs:end:get_notes\n\n // docs:start:view_notes\n unconstrained pub fn view_notes(\n self,\n options: NoteViewerOptions,\n ) -> [Option; MAX_NOTES_PER_PAGE] {\n view_notes(self.storage_slot, self.note_interface, options)\n }\n // docs:end:view_notes\n}\n", + "path": "/home/santiago/Projects/aztec3-packages/yarn-project/aztec-nr/aztec/src/state_vars/set.nr" + }, + "49": { + "source": "use crate::context::{PrivateContext, PublicContext, Context};\nuse dep::std::option::Option;\nuse dep::protocol_types::{\n hash::pedersen_hash,\n traits::{ToField}\n};\n\n// docs:start:map\nstruct Map {\n context: Context,\n storage_slot: Field,\n state_var_constructor: fn(Context, Field) -> V,\n}\n// docs:end:map\n\nimpl Map {\n // docs:start:new\n pub fn new(\n context: Context,\n storage_slot: Field,\n state_var_constructor: fn(Context, Field) -> V,\n ) -> Self {\n assert(storage_slot != 0, \"Storage slot 0 not allowed. Storage slots must start from 1.\");\n Map {\n context,\n storage_slot,\n state_var_constructor,\n }\n }\n // docs:end:new\n\n // docs:start:at\n pub fn at(self, key: K) -> V where K: ToField {\n // TODO(#1204): use a generator index for the storage slot\n let derived_storage_slot = pedersen_hash([self.storage_slot, key.to_field()],0);\n\n let state_var_constructor = self.state_var_constructor;\n state_var_constructor(self.context, derived_storage_slot)\n }\n // docs:end:at\n}\n", + "path": "/home/santiago/Projects/aztec3-packages/yarn-project/aztec-nr/aztec/src/state_vars/map.nr" + }, + "50": { + "source": "use crate::context::{Context};\nuse crate::oracle::storage::storage_read;\nuse crate::oracle::storage::storage_write;\nuse crate::types::type_serialization::TypeSerializationInterface;\nuse dep::std::option::Option;\n\n// docs:start:public_state_struct\nstruct PublicState {\n context: Context,\n storage_slot: Field,\n serialization_methods: TypeSerializationInterface,\n}\n// docs:end:public_state_struct\n\nimpl PublicState {\n // docs:start:public_state_struct_new\n pub fn new(\n // Note: Passing the contexts to new(...) just to have an interface compatible with a Map.\n context: Context,\n storage_slot: Field,\n serialization_methods: TypeSerializationInterface,\n ) -> Self {\n assert(storage_slot != 0, \"Storage slot 0 not allowed. Storage slots must start from 1.\");\n PublicState {\n context,\n storage_slot,\n serialization_methods,\n }\n }\n // docs:end:public_state_struct_new\n\n // docs:start:public_state_struct_read\n pub fn read(self) -> T {\n assert(self.context.private.is_none(), \"Public state reads only supported in public functions\");\n storage_read(self.storage_slot, self.serialization_methods.deserialize)\n }\n // docs:end:public_state_struct_read\n\n // docs:start:public_state_struct_write\n pub fn write(self, value: T) {\n assert(self.context.private.is_none(), \"Public state writes only supported in public functions\");\n let serialize = self.serialization_methods.serialize;\n let fields = serialize(value);\n storage_write(self.storage_slot, fields);\n }\n // docs:end:public_state_struct_write\n}\n", + "path": "/home/santiago/Projects/aztec3-packages/yarn-project/aztec-nr/aztec/src/state_vars/public_state.nr" + }, + "56": { + "source": "#[oracle(storageRead)]\nfn storage_read_oracle(_storage_slot: Field, _number_of_elements: Field) -> [Field; N] {}\n\nunconstrained fn storage_read_oracle_wrapper(_storage_slot: Field) -> [Field; N] {\n storage_read_oracle(_storage_slot, N)\n}\n\npub fn storage_read(storage_slot: Field, deserialize: fn([Field; N]) -> T) -> T {\n let fields = storage_read_oracle_wrapper(storage_slot);\n deserialize(fields)\n}\n\n#[oracle(storageWrite)]\nfn storage_write_oracle(_storage_slot: Field, _values: [Field; N]) -> [Field; N] {}\n\n// TODO: Remove return value.\nunconstrained pub fn storage_write(storage_slot: Field, fields: [Field; N]) {\n let _hash = storage_write_oracle(storage_slot, fields);\n}\n", + "path": "/home/santiago/Projects/aztec3-packages/yarn-project/aztec-nr/aztec/src/oracle/storage.nr" + }, + "57": { + "source": "use dep::protocol_types::{\n address::AztecAddress,\n constants::NUM_FIELDS_PER_SHA256,\n grumpkin_point::GrumpkinPoint,\n};\n\n// TODO: Should take encrypted data.\n#[oracle(emitEncryptedLog)]\nfn emit_encrypted_log_oracle(\n _contract_address: AztecAddress,\n _storage_slot: Field,\n _encryption_pub_key: GrumpkinPoint,\n _preimage: [Field; N]\n) -> Field {}\n\nunconstrained pub fn emit_encrypted_log(\n contract_address: AztecAddress,\n storage_slot: Field,\n encryption_pub_key: GrumpkinPoint,\n preimage: [Field; N]\n) -> [Field; NUM_FIELDS_PER_SHA256] {\n [emit_encrypted_log_oracle(contract_address, storage_slot, encryption_pub_key, preimage), 0]\n}\n\n#[oracle(emitUnencryptedLog)]\nfn emit_unencrypted_log_oracle(_contract_address: AztecAddress, _event_selector: Field, _message: T) -> Field {}\n\nunconstrained pub fn emit_unencrypted_log(\n contract_address: AztecAddress,\n event_selector: Field,\n message: T\n) -> [Field; NUM_FIELDS_PER_SHA256] {\n // https://github.com/AztecProtocol/aztec-packages/issues/885\n [emit_unencrypted_log_oracle(contract_address, event_selector, message), 0]\n}\n", + "path": "/home/santiago/Projects/aztec3-packages/yarn-project/aztec-nr/aztec/src/oracle/logs.nr" + }, + "59": { + "source": "#[oracle(packArguments)]\nfn pack_arguments_oracle(_args: [Field; N]) -> Field {}\n\n// TODO: explain what this does.\nunconstrained pub fn pack_arguments(args: [Field; N]) -> Field {\n pack_arguments_oracle(args)\n}\n", + "path": "/home/santiago/Projects/aztec3-packages/yarn-project/aztec-nr/aztec/src/oracle/arguments.nr" + }, + "61": { + "source": "use dep::protocol_types::{\n abis::function_selector::FunctionSelector,\n address::AztecAddress,\n constants::RETURN_VALUES_LENGTH,\n};\n\n#[oracle(callPublicFunction)]\nfn call_public_function_oracle(\n _contract_address: AztecAddress,\n _function_selector: FunctionSelector,\n _args_hash: Field\n) -> [Field; RETURN_VALUES_LENGTH] {}\n\nunconstrained pub fn call_public_function_internal(\n contract_address: AztecAddress,\n function_selector: FunctionSelector,\n args_hash: Field\n) -> [Field; RETURN_VALUES_LENGTH] {\n call_public_function_oracle(contract_address, function_selector, args_hash)\n}\n", + "path": "/home/santiago/Projects/aztec3-packages/yarn-project/aztec-nr/aztec/src/oracle/public_call.nr" + }, + "65": { + "source": "use dep::protocol_types::{\n address::{\n AztecAddress,\n PartialAddress,\n },\n grumpkin_point::GrumpkinPoint,\n};\n\n#[oracle(getPublicKeyAndPartialAddress)]\nfn get_public_key_and_partial_address_oracle(_address: AztecAddress) -> [Field; 3] {}\n\nunconstrained fn get_public_key_and_partial_address_internal(address: AztecAddress) -> [Field; 3] {\n get_public_key_and_partial_address_oracle(address)\n}\n\npub fn get_public_key(address: AztecAddress) -> GrumpkinPoint {\n let result = get_public_key_and_partial_address_internal(address);\n let pub_key = GrumpkinPoint::new(result[0], result[1]);\n let partial_address = PartialAddress::from_field(result[2]);\n\n let calculated_address = AztecAddress::compute(pub_key, partial_address);\n assert(calculated_address.eq(address));\n\n pub_key\n}\n", + "path": "/home/santiago/Projects/aztec3-packages/yarn-project/aztec-nr/aztec/src/oracle/get_public_key.nr" + }, + "69": { + "source": "#[oracle(getRandomField)]\nfn rand_oracle() -> Field {}\n\nunconstrained pub fn rand() -> Field {\n rand_oracle()\n}\n", + "path": "/home/santiago/Projects/aztec3-packages/yarn-project/aztec-nr/aztec/src/oracle/rand.nr" + }, + "70": { + "source": "use dep::std::option::Option;\nuse crate::note::{\n note_header::NoteHeader,\n note_interface::NoteInterface,\n};\nuse crate::utils::arr_copy_slice;\n\nuse dep::protocol_types::address::AztecAddress;\n\n#[oracle(notifyCreatedNote)]\nfn notify_created_note_oracle(_storage_slot: Field, _serialized_note: [Field; N], _inner_note_hash: Field) -> Field {}\n\nunconstrained pub fn notify_created_note(storage_slot: Field, serialized_note: [Field; N], inner_note_hash: Field) -> Field {\n notify_created_note_oracle(storage_slot, serialized_note, inner_note_hash)\n}\n\n#[oracle(notifyNullifiedNote)]\nfn notify_nullified_note_oracle(_nullifier: Field, _inner_note_hash: Field) -> Field {}\n\nunconstrained pub fn notify_nullified_note(nullifier: Field, inner_note_hash: Field) -> Field {\n notify_nullified_note_oracle(nullifier, inner_note_hash)\n}\n\n#[oracle(getNotes)]\nfn get_notes_oracle(\n _storage_slot: Field,\n _num_selects: u8,\n _select_by: [u8; N],\n _select_values: [Field; N],\n _sort_by: [u8; N],\n _sort_order: [u2; N],\n _limit: u32,\n _offset: u32,\n _return_size: u32,\n _placeholder_fields: [Field; S]\n) -> [Field; S] {}\n\nunconstrained fn get_notes_oracle_wrapper(\n storage_slot: Field,\n num_selects: u8,\n select_by: [u8; N],\n select_values: [Field; N],\n sort_by: [u8; N],\n sort_order: [u2; N],\n limit: u32,\n offset: u32,\n mut placeholder_fields: [Field; S]\n) -> [Field; S] {\n let return_size = placeholder_fields.len() as u32;\n get_notes_oracle(\n storage_slot,\n num_selects,\n select_by,\n select_values,\n sort_by,\n sort_order,\n limit,\n offset,\n return_size,\n placeholder_fields\n )\n}\n\nunconstrained pub fn get_notes(\n storage_slot: Field,\n note_interface: NoteInterface,\n num_selects: u8,\n select_by: [u8; M],\n select_values: [Field; M],\n sort_by: [u8; M],\n sort_order: [u2; M],\n limit: u32,\n offset: u32,\n mut placeholder_opt_notes: [Option; S], // TODO: Remove it and use `limit` to initialize the note array.\n placeholder_fields: [Field; NS] // TODO: Remove it and use `limit` to initialize the note array.\n) -> [Option; S] {\n let fields = get_notes_oracle_wrapper(\n storage_slot,\n num_selects,\n select_by,\n select_values,\n sort_by,\n sort_order,\n limit,\n offset,\n placeholder_fields\n );\n let num_notes = fields[0] as u32;\n let contract_address = AztecAddress::from_field(fields[1]);\n let deserialize = note_interface.deserialize;\n let set_header = note_interface.set_header;\n for i in 0..placeholder_opt_notes.len() {\n if i as u32 < num_notes {\n // lengths named as per typescript.\n let return_header_length: Field = 2; // num_notes & contract_address.\n let extra_preimage_length: Field = 2; // nonce & is_transient.\n let read_offset: Field = return_header_length + i * (N + extra_preimage_length);\n let nonce = fields[read_offset];\n let is_transient = fields[read_offset + 1] as bool;\n let header = NoteHeader { contract_address, nonce, storage_slot, is_transient };\n let serialized_note = arr_copy_slice(fields, [0; N], read_offset + 2);\n let mut note = deserialize(serialized_note);\n set_header(&mut note, header);\n placeholder_opt_notes[i] = Option::some(note);\n };\n }\n placeholder_opt_notes\n}\n\n#[oracle(checkNullifierExists)]\nfn check_nullifier_exists_oracle(_inner_nullifier: Field) -> Field {}\n\nunconstrained pub fn check_nullifier_exists(inner_nullifier: Field) -> bool {\n check_nullifier_exists_oracle(inner_nullifier) == 1\n}\n", + "path": "/home/santiago/Projects/aztec3-packages/yarn-project/aztec-nr/aztec/src/oracle/notes.nr" + }, + "73": { + "source": "use dep::protocol_types::{\n address::AztecAddress,\n grumpkin_point::GrumpkinPoint,\n};\nuse dep::std::grumpkin_scalar::GrumpkinScalar;\n\nstruct KeyPair {\n public_key: GrumpkinPoint,\n secret_key: GrumpkinScalar,\n}\n\n#[oracle(getNullifierKeyPair)]\nfn get_nullifier_key_pair_oracle(_account: AztecAddress) -> [Field; 4] {}\n\nunconstrained fn get_nullifier_key_pair_internal(account: AztecAddress) -> KeyPair {\n let result = get_nullifier_key_pair_oracle(account);\n KeyPair {\n public_key: GrumpkinPoint { x: result[0], y: result[1] },\n secret_key: GrumpkinScalar { high: result[2], low: result[3] }\n }\n}\n\npub fn get_nullifier_key_pair(account: AztecAddress) -> KeyPair {\n get_nullifier_key_pair_internal(account)\n}\n\npub fn get_nullifier_secret_key(account: AztecAddress) -> GrumpkinScalar {\n get_nullifier_key_pair_internal(account).secret_key\n}\n", + "path": "/home/santiago/Projects/aztec3-packages/yarn-project/aztec-nr/aztec/src/oracle/nullifier_key.nr" + }, + "76": { + "source": "pub fn arr_copy_slice(src: [T; N], mut dst: [T; M], offset: Field) -> [T; M] {\n for i in 0..dst.len() {\n dst[i] = src[i + offset];\n }\n dst\n}\n\n// TODO(#3470): Copied over from https://github.com/AztecProtocol/aztec-packages/blob/a07c4bd47313be6aa604a63f37857eb0136b41ba/yarn-project/noir-protocol-circuits/src/crates/rollup-lib/src/base/base_rollup_inputs.nr#L599\n// move to a shared place?\n\n// TODO to radix returns u8, so we cannot use bigger radixes. It'd be ideal to use a radix of the maximum range-constrained integer noir supports\npub fn full_field_less_than(lhs: Field, rhs: Field) -> bool {\n lhs.lt(rhs)\n}\n\npub fn full_field_greater_than(lhs: Field, rhs: Field) -> bool {\n rhs.lt(lhs)\n}\n\nstruct Reader {\n data: [Field; N],\n offset: Field,\n}\n\nimpl Reader {\n pub fn new(data: [Field; N]) -> Self {\n Self { data, offset: 0 }\n }\n\n pub fn read(&mut self) -> Field {\n let result = self.data[self.offset];\n self.offset += 1;\n result\n }\n\n pub fn read_array(&mut self, mut result: [Field; K]) -> [Field; K] {\n for i in 0..K {\n result[i] = self.data[self.offset + i];\n }\n self.offset += K;\n result\n }\n\n pub fn read_struct(&mut self, deserialise: fn([Field; K]) -> T) -> T {\n let result = deserialise(self.read_array([0; K]));\n result\n }\n\n pub fn read_struct_array(&mut self, deserialise: fn([Field; K]) -> T, mut result: [T; C]) -> [T; C] {\n for i in 0..C {\n result[i] = self.read_struct(deserialise);\n }\n result\n }\n\n pub fn finish(self) {\n assert(self.offset == self.data.len(), \"Reader did not read all data\");\n } \n}\n", + "path": "/home/santiago/Projects/aztec3-packages/yarn-project/aztec-nr/aztec/src/utils.nr" + }, + "77": { + "source": "use crate::context::{PrivateContext, PublicContext};\nuse crate::oracle;\nuse dep::protocol_types::{\n address::AztecAddress,\n grumpkin_point::GrumpkinPoint,\n};\n\npub fn emit_encrypted_log(\n context: &mut PrivateContext,\n contract_address: AztecAddress,\n storage_slot: Field,\n encryption_pub_key: GrumpkinPoint,\n log: [Field; N]\n) {\n let _ = oracle::logs::emit_encrypted_log(contract_address, storage_slot, encryption_pub_key, log);\n context.accumulate_encrypted_logs(log);\n}\n\npub fn emit_unencrypted_log(context: &mut PublicContext, log: T) {\n let contract_address = context.this_address();\n let event_selector = 5; // TODO: compute actual event selector.\n let _ = oracle::logs::emit_unencrypted_log(contract_address, event_selector, log);\n // context.accumulate_unencrypted_logs(log);\n}\n\n// TODO: We might want to remove this since emitting unencrypted logs from private functions is violating privacy.\n// --> might be a better approach to force devs to make a public function call that emits the log if needed then\n// it would be less easy to accidentally leak information.\n// If we decide to keep this function around would make sense to wait for traits and then merge it with emit_unencrypted_log.\npub fn emit_unencrypted_log_from_private(context: &mut PrivateContext, log: T) {\n let contract_address = context.this_address();\n let event_selector = 5; // TODO: compute actual event selector.\n let _ = oracle::logs::emit_unencrypted_log(contract_address, event_selector, log);\n // context.accumulate_unencrypted_logs(log);\n}\n", + "path": "/home/santiago/Projects/aztec3-packages/yarn-project/aztec-nr/aztec/src/log.nr" + }, + "78": { + "source": "use crate::oracle::get_public_key::get_public_key;\nuse dep::protocol_types::{\n address::AztecAddress,\n grumpkin_point::GrumpkinPoint,\n};\nuse dep::std::{\n grumpkin_scalar::GrumpkinScalar,\n grumpkin_scalar_mul::grumpkin_fixed_base,\n};\n\npub fn validate_nullifier_key_against_address(\n address: AztecAddress,\n nullifier_public_key: GrumpkinPoint,\n nullifier_secret_key: GrumpkinScalar\n) {\n // TODO: Nullifier public key should be part of the address.\n // Validation of the secret key should happen in the kernel circuit.\n let owner_public_key = get_public_key(address);\n assert(owner_public_key.x == nullifier_public_key.x);\n assert(owner_public_key.y == nullifier_public_key.y);\n let computed_public_key = grumpkin_fixed_base(nullifier_secret_key);\n assert(owner_public_key.x == computed_public_key[0]);\n assert(owner_public_key.y == computed_public_key[1]);\n}\n", + "path": "/home/santiago/Projects/aztec3-packages/yarn-project/aztec-nr/aztec/src/key/nullifier_key.nr" + }, + "87": { + "source": "use dep::std::option::Option;\nuse dep::protocol_types::constants::{\n MAX_READ_REQUESTS_PER_CALL,\n GET_NOTE_ORACLE_RETURN_LENGTH,\n GET_NOTES_ORACLE_RETURN_LENGTH,\n MAX_NOTES_PER_PAGE,\n VIEW_NOTE_ORACLE_RETURN_LENGTH,\n};\nuse crate::context::PrivateContext;\nuse crate::note::{\n note_getter_options::{NoteGetterOptions, Select, Sort, SortOrder},\n note_interface::NoteInterface,\n note_viewer_options::NoteViewerOptions,\n utils::compute_note_hash_for_read_or_nullify,\n};\nuse crate::oracle;\nuse crate::types::vec::BoundedVec;\n\nfn check_note_header(\n context: PrivateContext,\n storage_slot: Field,\n note_interface: NoteInterface,\n note: Note\n) {\n let get_header = note_interface.get_header;\n let header = get_header(note);\n let contract_address = context.this_address();\n assert(header.contract_address.eq(contract_address));\n assert(header.storage_slot == storage_slot);\n}\n\nfn check_note_fields(fields: [Field; N], selects: BoundedVec, N>) {\n for i in 0..selects.len {\n let select = selects.get_unchecked(i).unwrap_unchecked();\n assert(fields[select.field_index] == select.value, \"Mismatch return note field.\");\n }\n}\n\nfn check_notes_order(\n fields_0: [Field; N],\n fields_1: [Field; N],\n sorts: BoundedVec, N>\n) {\n for i in 0..sorts.len {\n let sort = sorts.get_unchecked(i).unwrap_unchecked();\n let eq = fields_0[sort.field_index] == fields_1[sort.field_index];\n let lt = fields_0[sort.field_index] as u120 < fields_1[sort.field_index] as u120;\n if sort.order == SortOrder.ASC {\n assert(eq | lt, \"Return notes not sorted in ascending order.\");\n } else if !eq {\n assert(!lt, \"Return notes not sorted in descending order.\");\n }\n }\n}\n\npub fn get_note(\n context: &mut PrivateContext,\n storage_slot: Field,\n note_interface: NoteInterface\n) -> Note {\n let note = get_note_internal(storage_slot, note_interface);\n\n check_note_header(*context, storage_slot, note_interface, note);\n\n let note_hash_for_read_request = compute_note_hash_for_read_or_nullify(note_interface, note);\n\n context.push_read_request(note_hash_for_read_request);\n note\n}\n\npub fn get_notes(\n context: &mut PrivateContext,\n storage_slot: Field,\n note_interface: NoteInterface,\n options: NoteGetterOptions\n) -> [Option; MAX_READ_REQUESTS_PER_CALL] {\n let opt_notes = get_notes_internal(storage_slot, note_interface, options);\n let mut num_notes = 0;\n let mut prev_fields = [0; N];\n for i in 0..opt_notes.len() {\n let opt_note = opt_notes[i];\n if opt_note.is_some() {\n let note = opt_note.unwrap_unchecked();\n let serialize = note_interface.serialize;\n let fields = serialize(note);\n check_note_header(*context, storage_slot, note_interface, note);\n check_note_fields(fields, options.selects);\n if i != 0 {\n check_notes_order(prev_fields, fields, options.sorts);\n }\n prev_fields = fields;\n\n let note_hash_for_read_request = compute_note_hash_for_read_or_nullify(note_interface, note);\n // TODO(https://github.com/AztecProtocol/aztec-packages/issues/1410): test to ensure\n // failure if malicious oracle injects 0 nonce here for a \"pre-existing\" note.\n context.push_read_request(note_hash_for_read_request);\n\n num_notes += 1;\n };\n }\n if options.limit != 0 {\n assert(num_notes <= options.limit, \"Invalid number of return notes.\");\n }\n opt_notes\n}\n\nunconstrained fn get_note_internal(storage_slot: Field, note_interface: NoteInterface) -> Note {\n let placeholder_note = [Option::none()];\n let placeholder_fields = [0; GET_NOTE_ORACLE_RETURN_LENGTH];\n oracle::notes::get_notes(\n storage_slot,\n note_interface,\n 0,\n [],\n [],\n [],\n [],\n 1, // limit\n 0, // offset\n placeholder_note,\n placeholder_fields\n )[0].unwrap() // Notice: we don't allow dummies to be returned from get_note (singular).\n}\n\nunconstrained fn get_notes_internal(\n storage_slot: Field,\n note_interface: NoteInterface,\n options: NoteGetterOptions\n) -> [Option; MAX_READ_REQUESTS_PER_CALL] {\n let (num_selects, select_by, select_values, sort_by, sort_order) = flatten_options(options.selects, options.sorts);\n let placeholder_opt_notes = [Option::none(); MAX_READ_REQUESTS_PER_CALL];\n let placeholder_fields = [0; GET_NOTES_ORACLE_RETURN_LENGTH];\n let opt_notes = oracle::notes::get_notes(\n storage_slot,\n note_interface,\n num_selects,\n select_by,\n select_values,\n sort_by,\n sort_order,\n options.limit,\n options.offset,\n placeholder_opt_notes,\n placeholder_fields\n );\n\n let filter = options.filter;\n let filter_args = options.filter_args;\n filter(opt_notes, filter_args)\n}\n\nunconstrained pub fn view_notes(\n storage_slot: Field,\n note_interface: NoteInterface,\n options: NoteViewerOptions\n) -> [Option; MAX_NOTES_PER_PAGE] {\n let (num_selects, select_by, select_values, sort_by, sort_order) = flatten_options(options.selects, options.sorts);\n let placeholder_opt_notes = [Option::none(); MAX_NOTES_PER_PAGE];\n let placeholder_fields = [0; VIEW_NOTE_ORACLE_RETURN_LENGTH];\n oracle::notes::get_notes(\n storage_slot,\n note_interface,\n num_selects,\n select_by,\n select_values,\n sort_by,\n sort_order,\n options.limit,\n options.offset,\n placeholder_opt_notes,\n placeholder_fields\n )\n}\n\nunconstrained fn flatten_options(\n selects: BoundedVec, N>,\n sorts: BoundedVec, N>\n) -> (u8, [u8; N], [Field; N], [u8; N], [u2; N]) {\n let mut num_selects = 0;\n let mut select_by = [0; N];\n let mut select_values = [0; N];\n for i in 0..selects.len {\n let select = selects.get(i);\n if select.is_some() {\n select_by[num_selects] = select.unwrap_unchecked().field_index;\n select_values[num_selects] = select.unwrap_unchecked().value;\n num_selects += 1;\n };\n }\n\n let mut sort_by = [0; N];\n let mut sort_order = [0; N];\n for i in 0..sorts.len {\n let sort = sorts.get(i);\n if sort.is_some() {\n sort_by[i] = sort.unwrap_unchecked().field_index;\n sort_order[i] = sort.unwrap_unchecked().order;\n };\n }\n\n (num_selects, select_by, select_values, sort_by, sort_order)\n}\n", + "path": "/home/santiago/Projects/aztec3-packages/yarn-project/aztec-nr/aztec/src/note/note_getter.nr" + }, + "88": { + "source": "use crate::abi::PublicContextInputs;\nuse crate::context::{\n PrivateContext,\n PublicContext,\n};\nuse crate::note::{\n note_header::NoteHeader,\n note_interface::NoteInterface,\n utils::compute_inner_note_hash,\n};\nuse crate::oracle::notes::{notify_created_note, notify_nullified_note};\n\npub fn create_note(\n context: &mut PrivateContext,\n storage_slot: Field,\n note: &mut Note,\n note_interface: NoteInterface,\n broadcast: bool\n) {\n let contract_address = (*context).this_address();\n\n let header = NoteHeader { contract_address, storage_slot, nonce: 0, is_transient: true };\n let set_header = note_interface.set_header;\n set_header(note, header);\n let inner_note_hash = compute_inner_note_hash(note_interface, *note);\n\n let serialize = note_interface.serialize;\n let serialized_note = serialize(*note);\n assert(notify_created_note(storage_slot, serialized_note, inner_note_hash) == 0);\n\n context.push_new_note_hash(inner_note_hash);\n\n if broadcast {\n let broadcast = note_interface.broadcast;\n broadcast(context, storage_slot, *note);\n }\n}\n\npub fn create_note_hash_from_public(\n context: &mut PublicContext,\n storage_slot: Field,\n note: &mut Note,\n note_interface: NoteInterface\n) {\n let contract_address = (*context).this_address();\n\n let header = NoteHeader { contract_address, storage_slot, nonce: 0, is_transient: true };\n let set_header = note_interface.set_header;\n set_header(note, header);\n let inner_note_hash = compute_inner_note_hash(note_interface, *note);\n\n context.push_new_note_hash(inner_note_hash);\n}\n\npub fn destroy_note(\n context: &mut PrivateContext,\n note: Note,\n note_interface: NoteInterface\n) {\n let mut nullifier = 0;\n let mut nullified_commitment: Field = 0;\n let compute_nullifier = note_interface.compute_nullifier;\n nullifier = compute_nullifier(note, context);\n\n // We also need the note commitment corresponding to the \"nullifier\"\n let get_header = note_interface.get_header;\n let header = get_header(note);\n // `nullified_commitment` is used to inform the kernel which pending commitment\n // the nullifier corresponds to so they can be matched and both squashed/deleted.\n // nonzero nonce implies \"persistable\" nullifier (nullifies a persistent/in-tree\n // commitment) in which case `nullified_commitment` is not used since the kernel\n // just siloes and forwards the nullifier to its output.\n if (header.is_transient) {\n // TODO(1718): Can we reuse the note commitment computed in `compute_nullifier`?\n nullified_commitment = compute_inner_note_hash(note_interface, note);\n }\n assert(notify_nullified_note(nullifier, nullified_commitment) == 0);\n\n context.push_new_nullifier(nullifier, nullified_commitment)\n}\n", + "path": "/home/santiago/Projects/aztec3-packages/yarn-project/aztec-nr/aztec/src/note/lifecycle.nr" + }, + "91": { + "source": "use dep::protocol_types::{\n address::AztecAddress,\n constants::{\n GENERATOR_INDEX__UNIQUE_COMMITMENT,\n GENERATOR_INDEX__SILOED_COMMITMENT,\n },\n hash::pedersen_hash,\n};\n\npub fn compute_inner_hash(storage_slot: Field, note_hash: Field) -> Field {\n // TODO(#1205) Do we need a generator index here?\n pedersen_hash([storage_slot, note_hash], 0)\n}\n\npub fn compute_siloed_hash(contract_address: AztecAddress, inner_note_hash: Field) -> Field {\n let inputs = [contract_address.to_field(), inner_note_hash];\n pedersen_hash(inputs, GENERATOR_INDEX__SILOED_COMMITMENT)\n}\n\npub fn compute_unique_hash(nonce: Field, siloed_note_hash: Field) -> Field {\n let inputs = [nonce, siloed_note_hash];\n pedersen_hash(inputs, GENERATOR_INDEX__UNIQUE_COMMITMENT)\n}\n", + "path": "/home/santiago/Projects/aztec3-packages/yarn-project/aztec-nr/aztec/src/note/note_hash.nr" + }, + "92": { + "source": "use dep::protocol_types::{\n constants::GENERATOR_INDEX__OUTER_NULLIFIER,\n hash::pedersen_hash,\n};\nuse crate::{\n context::PrivateContext,\n note::{\n note_hash::{compute_inner_hash, compute_siloed_hash, compute_unique_hash},\n note_header::NoteHeader,\n note_interface::NoteInterface,\n },\n utils::arr_copy_slice,\n};\n\npub fn compute_inner_note_hash(note_interface: NoteInterface, note: Note) -> Field {\n let get_header = note_interface.get_header;\n let header = get_header(note);\n\n let compute_note_hash = note_interface.compute_note_hash;\n let note_hash = compute_note_hash(note);\n\n compute_inner_hash(header.storage_slot, note_hash)\n}\n\npub fn compute_siloed_note_hash(note_interface: NoteInterface, note_with_header: Note) -> Field {\n let get_header = note_interface.get_header;\n let header = get_header(note_with_header);\n\n let inner_note_hash = compute_inner_note_hash(note_interface, note_with_header);\n\n compute_siloed_hash(header.contract_address, inner_note_hash)\n}\n\npub fn compute_unique_siloed_note_hash(note_interface: NoteInterface, note_with_header: Note) -> Field {\n let get_header = note_interface.get_header;\n let header = get_header(note_with_header);\n\n let siloed_note_hash = compute_siloed_note_hash(note_interface, note_with_header);\n\n compute_unique_hash(header.nonce, siloed_note_hash)\n}\n\npub fn compute_siloed_nullifier(\n note_interface: NoteInterface,\n note_with_header: Note,\n context: &mut PrivateContext\n) -> Field {\n let get_header = note_interface.get_header;\n let header = get_header(note_with_header);\n\n let compute_nullifier = note_interface.compute_nullifier;\n let inner_nullifier = compute_nullifier(note_with_header, context);\n\n let input = [header.contract_address.to_field(), inner_nullifier];\n pedersen_hash(input, GENERATOR_INDEX__OUTER_NULLIFIER)\n}\n\npub fn compute_note_hash_for_read_or_nullify(note_interface: NoteInterface, note_with_header: Note) -> Field {\n let get_header = note_interface.get_header;\n let header = get_header(note_with_header);\n\n // TODO(https://github.com/AztecProtocol/aztec-packages/issues/1386)\n if (header.is_transient) {\n // If a note is transient, we just read the inner_note_hash (kernel will silo by contract address).\n compute_inner_note_hash(note_interface, note_with_header)\n } else if (header.nonce == 0) {\n // If not transient and nonce is zero, that means we are reading a public note.\n compute_siloed_note_hash(note_interface, note_with_header)\n } else {\n // When nonce is nonzero, that means we are reading a settled note (from tree) created in a\n // previous TX. So we need the unique_siloed_note_hash which has already been hashed with\n // contract address and then nonce. This hash will match the existing leaf in the private\n // data tree, so the kernel can just perform a membership check directly on this hash/leaf.\n compute_unique_siloed_note_hash(note_interface, note_with_header)\n }\n}\n\npub fn compute_note_hash_and_nullifier(\n note_interface: NoteInterface,\n note_header: NoteHeader,\n serialized_note: [Field; S]\n) -> [Field; 4] {\n let deserialize = note_interface.deserialize;\n let set_header = note_interface.set_header;\n let mut note = deserialize(arr_copy_slice(serialized_note, [0; N], 0));\n set_header(&mut note, note_header);\n\n let compute_note_hash = note_interface.compute_note_hash;\n let note_hash = compute_note_hash(note);\n let inner_note_hash = compute_inner_hash(note_header.storage_slot, note_hash);\n\n let siloed_note_hash = compute_siloed_hash(note_header.contract_address, inner_note_hash);\n\n let unique_siloed_note_hash = compute_unique_hash(note_header.nonce, siloed_note_hash);\n\n let compute_nullifier_without_context = note_interface.compute_nullifier_without_context;\n let inner_nullifier = compute_nullifier_without_context(note);\n\n [inner_note_hash, siloed_note_hash, unique_siloed_note_hash, inner_nullifier]\n}\n", + "path": "/home/santiago/Projects/aztec3-packages/yarn-project/aztec-nr/aztec/src/note/utils.nr" + }, + "94": { + "source": "struct BoundedVec {\n storage: [T; MaxLen],\n len: Field,\n}\n\nimpl BoundedVec {\n pub fn new(initial_value: T) -> Self {\n BoundedVec { storage: [initial_value; MaxLen], len: 0 }\n }\n\n pub fn get(mut self: Self, index: Field) -> T {\n assert(index as u64 < self.len as u64);\n self.storage[index]\n }\n\n pub fn get_unchecked(mut self: Self, index: Field) -> T {\n self.storage[index]\n }\n\n pub fn push(&mut self, elem: T) {\n assert(self.len as u64 < MaxLen as u64);\n\n self.storage[self.len] = elem;\n self.len += 1;\n }\n\n pub fn push_array(&mut self, array: [T; Len]) {\n let newLen = self.len + array.len();\n assert(newLen as u64 <= MaxLen as u64);\n for i in 0..array.len() {\n self.storage[self.len + i] = array[i];\n }\n self.len = newLen;\n }\n\n pub fn pop(&mut self) -> T {\n assert(self.len as u64 > 0);\n\n let elem = self.storage[self.len - 1];\n self.len -= 1;\n elem\n }\n\n pub fn any(self, predicate: fn[Env](T) -> bool) -> bool {\n let mut ret = false;\n let mut exceeded_len = false;\n for i in 0..MaxLen {\n exceeded_len |= i == self.len;\n if (!exceeded_len) {\n ret |= predicate(self.storage[i]);\n }\n }\n ret\n }\n}\n\n#[test]\nfn test_vec_push_pop() {\n let mut vec: BoundedVec = BoundedVec::new(0);\n assert(vec.len == 0);\n vec.push(2);\n assert(vec.len == 1);\n vec.push(4);\n assert(vec.len == 2);\n vec.push(6);\n assert(vec.len == 3);\n let x = vec.pop();\n assert(x == 6);\n assert(vec.len == 2);\n assert(vec.get(0) == 2);\n assert(vec.get(1) == 4);\n}\n\n#[test]\nfn test_vec_push_array() {\n let mut vec: BoundedVec = BoundedVec::new(0);\n vec.push_array([2, 4]);\n assert(vec.len == 2);\n assert(vec.get(0) == 2);\n assert(vec.get(1) == 4);\n}\n\n#[test(should_fail)]\nfn test_vec_get_out_of_bound() {\n let mut vec: BoundedVec = BoundedVec::new(0);\n vec.push_array([2, 4]);\n let _x = vec.get(2);\n}\n\n#[test(should_fail)]\nfn test_vec_get_not_declared() {\n let mut vec: BoundedVec = BoundedVec::new(0);\n vec.push_array([2]);\n let _x = vec.get(1);\n}\n\n#[test(should_fail)]\nfn test_vec_get_uninitialized() {\n let mut vec: BoundedVec = BoundedVec::new(0);\n let _x = vec.get(0);\n}\n\n#[test(should_fail)]\nfn test_vec_push_overflow() {\n let mut vec: BoundedVec = BoundedVec::new(0);\n vec.push(1);\n vec.push(2);\n}\n\n#[test]\nfn test_vec_any() {\n let mut vec: BoundedVec = BoundedVec::new(0);\n vec.push_array([2, 4, 6]);\n assert(vec.any(|v| v == 2) == true);\n assert(vec.any(|v| v == 4) == true);\n assert(vec.any(|v| v == 6) == true);\n assert(vec.any(|v| v == 3) == false);\n}\n\n#[test]\nfn test_vec_any_not_default() {\n let default_value = 1;\n let mut vec: BoundedVec = BoundedVec::new(default_value);\n vec.push_array([2, 4]);\n assert(vec.any(|v| v == default_value) == false);\n}\n", + "path": "/home/santiago/Projects/aztec3-packages/yarn-project/aztec-nr/aztec/src/types/vec.nr" + }, + "97": { + "source": "use crate::types::type_serialization::TypeSerializationInterface;\n\n// docs:start:field_serialization\nglobal FIELD_SERIALIZED_LEN: Field = 1;\n\nfn deserializeField(fields: [Field; FIELD_SERIALIZED_LEN]) -> Field {\n fields[0]\n}\n\nfn serializeField(value: Field) -> [Field; FIELD_SERIALIZED_LEN] {\n [value]\n}\n\nglobal FieldSerializationMethods = TypeSerializationInterface {\n deserialize: deserializeField,\n serialize: serializeField,\n};\n// docs:end:field_serialization", + "path": "/home/santiago/Projects/aztec3-packages/yarn-project/aztec-nr/aztec/src/types/type_serialization/field_serialization.nr" + }, + "105": { + "source": "global ARGS_LENGTH: Field = 16;\nglobal RETURN_VALUES_LENGTH: Field = 4;\n\n/**\n * Convention for constant array lengths are mainly divided in 2 classes:\n * - FUNCTION CALL\n * - TRANSACTION\n *\n * Agreed convention is to use MAX_XXX_PER_CALL resp. MAX_XXX_PER_TX, where XXX denotes a type of element such as\n * commitment, or nullifier, e.g.,:\n * - MAX_NEW_NULLIFIERS_PER_CALL\n * - MAX_NEW_COMMITMENTS_PER_TX\n *\n * In the kernel circuits, we accumulate elements such as commitments and the nullifiers from all functions calls in a\n * transaction. Therefore, we always must have:\n * MAX_XXX_PER_TX ≥ MAX_XXX_PER_CALL\n *\n * For instance:\n * MAX_NEW_COMMITMENTS_PER_TX ≥ MAX_NEW_COMMITMENTS_PER_CALL\n * MAX_NEW_NULLIFIERS_PER_TX ≥ MAX_NEW_NULLIFIERS_PER_CALL\n *\n */\n\n// docs:start:constants\n// \"PER CALL\" CONSTANTS\nglobal MAX_NEW_COMMITMENTS_PER_CALL: Field = 16;\nglobal MAX_NEW_NULLIFIERS_PER_CALL: Field = 16;\nglobal MAX_PRIVATE_CALL_STACK_LENGTH_PER_CALL: Field = 4;\nglobal MAX_PUBLIC_CALL_STACK_LENGTH_PER_CALL: Field = 4;\nglobal MAX_NEW_L2_TO_L1_MSGS_PER_CALL: Field = 2;\nglobal MAX_PUBLIC_DATA_UPDATE_REQUESTS_PER_CALL: Field = 16;\nglobal MAX_PUBLIC_DATA_READS_PER_CALL: Field = 16;\nglobal MAX_READ_REQUESTS_PER_CALL: Field = 32;\n\n// \"PER TRANSACTION\" CONSTANTS\nglobal MAX_NEW_COMMITMENTS_PER_TX: Field = 64;\nglobal MAX_NEW_NULLIFIERS_PER_TX: Field = 64;\nglobal MAX_PRIVATE_CALL_STACK_LENGTH_PER_TX: Field = 8;\nglobal MAX_PUBLIC_CALL_STACK_LENGTH_PER_TX: Field = 8;\nglobal MAX_NEW_L2_TO_L1_MSGS_PER_TX: Field = 2;\nglobal MAX_PUBLIC_DATA_UPDATE_REQUESTS_PER_TX: Field = 16;\nglobal MAX_PUBLIC_DATA_READS_PER_TX: Field = 16;\nglobal MAX_NEW_CONTRACTS_PER_TX: Field = 1;\nglobal MAX_OPTIONALLY_REVEALED_DATA_LENGTH_PER_TX: Field = 4;\nglobal MAX_READ_REQUESTS_PER_TX: Field = 128;\nglobal NUM_ENCRYPTED_LOGS_HASHES_PER_TX: Field = 1;\nglobal NUM_UNENCRYPTED_LOGS_HASHES_PER_TX: Field = 1;\n// docs:end:constants\n\n// ROLLUP CONTRACT CONSTANTS - constants used only in l1-contracts\nglobal NUMBER_OF_L1_L2_MESSAGES_PER_ROLLUP: Field = 16;\n\n// TREES RELATED CONSTANTS\nglobal VK_TREE_HEIGHT: Field = 3;\nglobal FUNCTION_TREE_HEIGHT: Field = 5;\nglobal CONTRACT_TREE_HEIGHT: Field = 16;\nglobal NOTE_HASH_TREE_HEIGHT: Field = 32;\nglobal PUBLIC_DATA_TREE_HEIGHT: Field = 40;\nglobal NULLIFIER_TREE_HEIGHT: Field = 20;\nglobal L1_TO_L2_MSG_TREE_HEIGHT: Field = 16;\nglobal ROLLUP_VK_TREE_HEIGHT: Field = 8;\n\n// SUB-TREES RELATED CONSTANTS\nglobal CONTRACT_SUBTREE_HEIGHT: Field = 0;\nglobal CONTRACT_SUBTREE_SIBLING_PATH_LENGTH: Field = 16;\nglobal NOTE_HASH_SUBTREE_HEIGHT: Field = 6;\nglobal NOTE_HASH_SUBTREE_SIBLING_PATH_LENGTH: Field = 26;\nglobal NULLIFIER_SUBTREE_HEIGHT: Field = 6;\nglobal PUBLIC_DATA_SUBTREE_HEIGHT: Field = 4;\nglobal ARCHIVE_HEIGHT: Field = 16;\nglobal NULLIFIER_SUBTREE_SIBLING_PATH_LENGTH: Field = 14;\nglobal PUBLIC_DATA_SUBTREE_SIBLING_PATH_LENGTH: Field = 36;\nglobal L1_TO_L2_MSG_SUBTREE_HEIGHT: Field = 4;\nglobal L1_TO_L2_MSG_SUBTREE_SIBLING_PATH_LENGTH: Field = 12;\n\n// MISC CONSTANTS\nglobal FUNCTION_SELECTOR_NUM_BYTES: Field = 4;\nglobal MAPPING_SLOT_PEDERSEN_SEPARATOR: Field = 4;\n// sha256 hash is stored in two fields to accommodate all 256-bits of the hash\nglobal NUM_FIELDS_PER_SHA256: Field = 2;\nglobal ARGS_HASH_CHUNK_LENGTH: u32 = 32;\nglobal ARGS_HASH_CHUNK_COUNT: u32 = 16;\n\n// NOIR CONSTANTS - constants used only in yarn-packages/noir-contracts\n// Some are defined here because Noir doesn't yet support globals referencing other globals yet.\n// Move these constants to a noir file once the issue bellow is resolved:\n// https://github.com/noir-lang/noir/issues/1734\nglobal L1_TO_L2_MESSAGE_LENGTH: Field = 8;\nglobal L1_TO_L2_MESSAGE_ORACLE_CALL_LENGTH: Field = 25;\nglobal MAX_NOTE_FIELDS_LENGTH: Field = 20;\n// GET_NOTE_ORACLE_RETURN_LENGT = MAX_NOTE_FIELDS_LENGTH + 1 + 2\n// The plus 1 is 1 extra field for nonce.\n// + 2 for EXTRA_DATA: [number_of_return_notes, contract_address]\nglobal GET_NOTE_ORACLE_RETURN_LENGTH: Field = 23;\nglobal MAX_NOTES_PER_PAGE: Field = 10;\n// VIEW_NOTE_ORACLE_RETURN_LENGTH = MAX_NOTES_PER_PAGE * (MAX_NOTE_FIELDS_LENGTH + 1) + 2;\nglobal VIEW_NOTE_ORACLE_RETURN_LENGTH: Field = 212;\nglobal CALL_CONTEXT_LENGTH: Field = 8;\nglobal BLOCK_HEADER_LENGTH: Field = 7;\nglobal FUNCTION_DATA_LENGTH: Field = 4;\nglobal CONTRACT_DEPLOYMENT_DATA_LENGTH: Field = 6;\n// Change this ONLY if you have changed the PrivateCircuitPublicInputs structure.\n// In other words, if the structure/size of the public inputs of a function call changes then we\n// should change this constant as well as the offsets in private_call_stack_item.nr\nglobal PRIVATE_CIRCUIT_PUBLIC_INPUTS_LENGTH: Field = 189;\nglobal CONTRACT_STORAGE_UPDATE_REQUEST_LENGTH: Field = 3;\nglobal CONTRACT_STORAGE_READ_LENGTH: Field = 2;\n// Change this ONLY if you have changed the PublicCircuitPublicInputs structure.\nglobal PUBLIC_CIRCUIT_PUBLIC_INPUTS_LENGTH: Field = 190;\nglobal GET_NOTES_ORACLE_RETURN_LENGTH: Field = 674;\nglobal CALL_PRIVATE_FUNCTION_RETURN_SIZE: Field = 195;\nglobal PUBLIC_CIRCUIT_PUBLIC_INPUTS_HASH_INPUT_LENGTH: Field = 87;\nglobal PRIVATE_CIRCUIT_PUBLIC_INPUTS_HASH_INPUT_LENGTH: Field = 177;\nglobal COMMITMENTS_NUM_BYTES_PER_BASE_ROLLUP: Field = 2048;\nglobal NULLIFIERS_NUM_BYTES_PER_BASE_ROLLUP: Field = 2048;\nglobal PUBLIC_DATA_WRITES_NUM_BYTES_PER_BASE_ROLLUP: Field = 1024;\nglobal CONTRACTS_NUM_BYTES_PER_BASE_ROLLUP: Field = 32;\nglobal CONTRACT_DATA_NUM_BYTES_PER_BASE_ROLLUP: Field = 64;\nglobal CONTRACT_DATA_NUM_BYTES_PER_BASE_ROLLUP_UNPADDED: Field = 52;\nglobal L2_TO_L1_MSGS_NUM_BYTES_PER_BASE_ROLLUP: Field = 64;\nglobal LOGS_HASHES_NUM_BYTES_PER_BASE_ROLLUP: Field = 64;\n\n/**\n * Enumerate the hash_indices which are used for pedersen hashing.\n * We start from 1 to avoid the default generators. The generator indices are listed\n * based on the number of elements each index hashes. The following conditions must be met:\n *\n * +-----------+-------------------------------+----------------------+\n * | Hash size | Number of elements hashed (n) | Condition to use |\n * |-----------+-------------------------------+----------------------|\n * | LOW | n ≤ 8 | 0 < hash_index ≤ 32 |\n * | MID | 8 < n ≤ 16 | 32 < hash_index ≤ 40 |\n * | HIGH | 16 < n ≤ 48 | 40 < hash_index ≤ 48 |\n * +-----------+-------------------------------+----------------------+\n *\n * Note: When modifying, modify `GeneratorIndexPacker` in packer.hpp accordingly.\n */\n// Indices with size ≤ 8\nglobal GENERATOR_INDEX__COMMITMENT = 1;\nglobal GENERATOR_INDEX__COMMITMENT_NONCE = 2;\nglobal GENERATOR_INDEX__UNIQUE_COMMITMENT = 3;\nglobal GENERATOR_INDEX__SILOED_COMMITMENT = 4;\nglobal GENERATOR_INDEX__NULLIFIER = 5;\nglobal GENERATOR_INDEX__INITIALIZATION_NULLIFIER = 6;\nglobal GENERATOR_INDEX__OUTER_NULLIFIER = 7;\nglobal GENERATOR_INDEX__PUBLIC_DATA_READ = 8;\nglobal GENERATOR_INDEX__PUBLIC_DATA_UPDATE_REQUEST = 9;\nglobal GENERATOR_INDEX__FUNCTION_DATA = 10;\nglobal GENERATOR_INDEX__FUNCTION_LEAF = 11;\nglobal GENERATOR_INDEX__CONTRACT_DEPLOYMENT_DATA = 12;\nglobal GENERATOR_INDEX__CONSTRUCTOR = 13;\nglobal GENERATOR_INDEX__CONSTRUCTOR_ARGS = 14;\nglobal GENERATOR_INDEX__CONTRACT_ADDRESS = 15;\nglobal GENERATOR_INDEX__CONTRACT_LEAF = 16;\nglobal GENERATOR_INDEX__CALL_CONTEXT = 17;\nglobal GENERATOR_INDEX__CALL_STACK_ITEM = 18;\nglobal GENERATOR_INDEX__CALL_STACK_ITEM_2 = 19;\nglobal GENERATOR_INDEX__L1_TO_L2_MESSAGE_SECRET = 20;\nglobal GENERATOR_INDEX__L2_TO_L1_MSG = 21;\nglobal GENERATOR_INDEX__TX_CONTEXT = 22;\nglobal GENERATOR_INDEX__PUBLIC_LEAF_INDEX = 23;\nglobal GENERATOR_INDEX__PUBLIC_DATA_LEAF = 24;\nglobal GENERATOR_INDEX__SIGNED_TX_REQUEST = 25;\nglobal GENERATOR_INDEX__GLOBAL_VARIABLES = 26;\nglobal GENERATOR_INDEX__PARTIAL_ADDRESS = 27;\nglobal GENERATOR_INDEX__BLOCK_HASH = 28;\nglobal GENERATOR_INDEX__SIDE_EFFECT = 29;\n// Indices with size ≤ 16\nglobal GENERATOR_INDEX__TX_REQUEST = 33;\nglobal GENERATOR_INDEX__SIGNATURE_PAYLOAD = 34;\n// Indices with size ≤ 44\nglobal GENERATOR_INDEX__VK = 41;\nglobal GENERATOR_INDEX__PRIVATE_CIRCUIT_PUBLIC_INPUTS = 42;\nglobal GENERATOR_INDEX__PUBLIC_CIRCUIT_PUBLIC_INPUTS = 43;\nglobal GENERATOR_INDEX__FUNCTION_ARGS = 44;\n", + "path": "/home/santiago/Projects/aztec3-packages/yarn-project/noir-protocol-circuits/src/crates/types/src/constants.nr" + }, + "111": { + "source": "use crate::address::{AztecAddress, EthAddress};\nuse crate::mocked::VerificationKey;\nuse crate::abis::function_selector::FunctionSelector;\nuse crate::abis::function_leaf_preimage::FunctionLeafPreimage;\nuse crate::abis::new_contract_data::NewContractData as ContractLeafPreimage;\nuse crate::abis::function_data::FunctionData;\nuse crate::abis::side_effect::{SideEffect};\nuse crate::utils::uint256::U256;\nuse crate::utils::bounded_vec::BoundedVec;\nuse crate::constants::{\n ARGS_HASH_CHUNK_COUNT,\n ARGS_HASH_CHUNK_LENGTH,\n CONTRACT_TREE_HEIGHT, \n FUNCTION_TREE_HEIGHT, \n NOTE_HASH_TREE_HEIGHT,\n NUM_FIELDS_PER_SHA256,\n GENERATOR_INDEX__SILOED_COMMITMENT,\n GENERATOR_INDEX__OUTER_NULLIFIER,\n GENERATOR_INDEX__VK,\n GENERATOR_INDEX__CONSTRUCTOR,\n GENERATOR_INDEX__PARTIAL_ADDRESS,\n GENERATOR_INDEX__CONTRACT_ADDRESS,\n GENERATOR_INDEX__COMMITMENT_NONCE,\n GENERATOR_INDEX__UNIQUE_COMMITMENT,\n GENERATOR_INDEX__FUNCTION_ARGS,\n};\n\nuse dep::std::hash::{pedersen_hash_with_separator, sha256};\n\npub fn sha256_to_field(bytes_to_hash: [u8; N]) -> Field {\n let sha256_hashed = sha256(bytes_to_hash);\n\n // Convert it to a field element\n let mut v = 1;\n let mut high = 0 as Field;\n let mut low = 0 as Field;\n\n for i in 0..16 {\n high = high + (sha256_hashed[15 - i] as Field) * v;\n low = low + (sha256_hashed[16 + 15 - i] as Field) * v;\n v = v * 256;\n }\n\n // Abuse that a % p + b % p = (a + b) % p and that low < p\n let hash_in_a_field = low + high * v;\n\n hash_in_a_field\n}\n\npub fn hash_args(args: [Field; N]) -> Field {\n if args.len() == 0 {\n 0\n } else {\n let mut chunks_hashes = [0; ARGS_HASH_CHUNK_COUNT];\n for i in 0..ARGS_HASH_CHUNK_COUNT {\n let mut chunk_hash = 0;\n let start_chunk_index = i * ARGS_HASH_CHUNK_LENGTH;\n if start_chunk_index < (args.len() as u32) {\n let mut chunk_args = [0; ARGS_HASH_CHUNK_LENGTH];\n for j in 0..ARGS_HASH_CHUNK_LENGTH {\n let item_index = i * ARGS_HASH_CHUNK_LENGTH + j;\n if item_index < (args.len() as u32) {\n chunk_args[j] = args[item_index];\n }\n }\n chunk_hash = pedersen_hash(chunk_args, GENERATOR_INDEX__FUNCTION_ARGS);\n }\n chunks_hashes[i] = chunk_hash;\n }\n pedersen_hash(chunks_hashes, GENERATOR_INDEX__FUNCTION_ARGS)\n }\n}\n\n// Checks that `value` is a member of a merkle tree with root `root` at position `index`\n// The witness being the `sibling_path`\npub fn assert_check_membership(value: Field, index: Field, sibling_path: [Field; N], root: Field) {\n let calculated_root = root_from_sibling_path(value, index, sibling_path);\n assert(calculated_root == root, \"membership check failed\");\n}\n\n// Calculate the Merkle tree root from the sibling path and leaf.\n//\n// The leaf is hashed with its sibling, and then the result is hashed\n// with the next sibling etc in the path. The last hash is the root.\n//\n// TODO(David/Someone): The cpp code is using a uint256, whereas its\n// TODO a bit simpler in Noir to just have a bit array.\n// TODO: I'd generally like to avoid u256 for algorithms like \n// this because it means we never even need to consider cases where \n// the index is greater than p.\npub fn root_from_sibling_path(leaf: Field, leaf_index: Field, sibling_path: [Field; N]) -> Field {\n let mut node = leaf;\n let indices = leaf_index.to_le_bits(N);\n\n for i in 0..N {\n let (hash_left, hash_right) = if indices[i] == 1 {\n (sibling_path[i], node)\n } else {\n (node, sibling_path[i])\n };\n node = merkle_hash(hash_left, hash_right);\n }\n node\n}\n\n// Calculate the function tree root from the sibling path and leaf preimage.\n//\n// TODO: The cpp code passes in components of the FunctionLeafPreimage and then \n// builds it up. We should build it up and then pass the leaf preimage as a parameter.\n// We can then choose to have a general method that takes in anything hashable\n// and deduplicate the logic in `contract_tree_root_from_siblings`\npub fn function_tree_root_from_siblings(\n selector: FunctionSelector,\n is_internal: bool,\n is_private: bool,\n vk_hash: Field,\n acir_hash: Field,\n function_leaf_index: Field,\n function_leaf_sibling_path: [Field; FUNCTION_TREE_HEIGHT]\n) -> Field {\n let function_leaf_preimage = FunctionLeafPreimage { selector, is_internal, is_private, vk_hash, acir_hash };\n\n let function_leaf = function_leaf_preimage.hash();\n\n let function_tree_root = root_from_sibling_path(function_leaf, function_leaf_index, function_leaf_sibling_path);\n\n function_tree_root\n}\n\n// Calculate the contract tree root from the sibling path and leaf preimage.\npub fn contract_tree_root_from_siblings(\n function_tree_root: Field,\n storage_contract_address: AztecAddress,\n portal_contract_address: EthAddress,\n contract_leaf_index: Field,\n contract_leaf_sibling_path: [Field; CONTRACT_TREE_HEIGHT]\n) -> Field {\n //TODO(Kev): if we use shorthand syntax here, we get an error as expected,\n // since variable name is `storage_contract_address` but the span is incorrect.\n let contract_leaf_preimage = ContractLeafPreimage { contract_address: storage_contract_address, portal_contract_address, function_tree_root };\n\n let contract_leaf = contract_leaf_preimage.hash();\n\n let computed_contract_tree_root = root_from_sibling_path(contract_leaf, contract_leaf_index, contract_leaf_sibling_path);\n\n computed_contract_tree_root\n}\n\npub fn read_request_root_from_siblings(\n read_request: Field,\n leaf_index: Field,\n sibling_path: [Field; NOTE_HASH_TREE_HEIGHT]\n) -> Field {\n root_from_sibling_path(read_request, leaf_index, sibling_path)\n}\n\npub fn silo_commitment(address: AztecAddress, inner_commitment: Field) -> Field {\n pedersen_hash(\n [\n address.to_field(),\n inner_commitment\n ],\n GENERATOR_INDEX__SILOED_COMMITMENT\n )\n}\n\npub fn silo_nullifier(address: AztecAddress, nullifier: Field) -> Field {\n pedersen_hash(\n [\n address.to_field(),\n nullifier\n ],\n GENERATOR_INDEX__OUTER_NULLIFIER\n )\n}\n\nfn merkle_hash(left: Field, right: Field) -> Field {\n pedersen_hash([left, right], 0)\n}\n\npub fn stdlib_recursion_verification_key_compress_native_vk(_vk: VerificationKey) -> Field {\n // Original cpp code\n // stdlib::recursion::verification_key::compress_native(private_call.vk, GeneratorIndex::VK);\n // The above cpp method is only ever called on verification key, so it has been special cased here\n let _hash_index = GENERATOR_INDEX__VK;\n 0\n}\n\n// TODO CPP uses blake2s for this\npub fn compute_new_contract_address_hash(new_contract_address: AztecAddress) -> Field {\n dep::std::hash::pedersen_hash([new_contract_address.to_field()])\n}\n\npub fn compute_l2_to_l1_hash(\n contract_address: AztecAddress,\n rollup_version_id: Field,\n portal_contract_address: EthAddress,\n chain_id: Field,\n content: Field\n) -> Field {\n let mut bytes: BoundedVec = BoundedVec::new(0);\n\n let inputs = [\n contract_address.to_field(), rollup_version_id, portal_contract_address.to_field(), chain_id, content\n ];\n for i in 0..inputs.len() {\n // TODO are bytes be in fr.to_buffer() ?\n let item_bytes = inputs[i].to_be_bytes(32);\n for j in 0..32 {\n bytes.push(item_bytes[j]);\n }\n }\n\n sha256_to_field(bytes.storage)\n}\n\npub fn compute_constructor_hash(\n function_data: FunctionData,\n args_hash: Field,\n constructor_vk_hash: Field\n) -> Field {\n let function_data_hash = function_data.hash();\n\n pedersen_hash(\n [\n function_data_hash,\n args_hash,\n constructor_vk_hash\n ],\n GENERATOR_INDEX__CONSTRUCTOR\n )\n}\n\n// Computes sha256 hash of 2 input hashes stored in 4 fields.\n// \n// This method is bn254 specific. Two fields is needed in order to \n// encode the sha256 output. It can be abstracted away with any 4-2 hash function.\n//\n// TODO(Jan and David): This is used for the encrypted_log hashes.\n// Can we check to see if we can just use hash_to_field or pedersen_compress here?\n//\n// Returning a Field would be desirable because then this can be replaced with \n// poseidon without changing the rest of the code\n//\npub fn accumulate_sha256(input: [U128; 4]) -> [Field; NUM_FIELDS_PER_SHA256] {\n // This is a note about the cpp code, since it takes an array of Fields\n // instead of a U128.\n // 4 Field elements when converted to bytes will usually \n // occupy 4 * 32 = 128 bytes.\n // However, this function is making the assumption that each Field \n // only occupies 128 bits.\n //\n // TODO(David): This does not seem to be getting guaranteed anywhere in the code?\n //\n // Concatenate 4 u128 bit integers into a byte array.\n let mut hash_input_flattened = [0; 64];\n for offset in 0..4 {\n let input_as_bytes = input[offset].to_be_bytes();\n for byte_index in 0..16 {\n hash_input_flattened[offset * 16 + byte_index] = input_as_bytes[byte_index];\n }\n }\n\n let sha_digest = dep::std::hash::sha256(hash_input_flattened);\n\n U256::from_bytes32(sha_digest).to_u128_limbs()\n}\n\npub fn compute_logs_hash(\n previous_log_hash: [Field; 2],\n current_log_hash: [Field; 2]\n) -> [Field; NUM_FIELDS_PER_SHA256] {\n accumulate_sha256(\n [\n U128::from_integer(previous_log_hash[0]),\n U128::from_integer(previous_log_hash[1]),\n U128::from_integer(current_log_hash[0]),\n U128::from_integer(current_log_hash[1])\n ]\n )\n}\n\npub fn compute_commitment_nonce(first_nullifier: Field, commitment_index: Field) -> Field {\n pedersen_hash(\n [\n first_nullifier,\n commitment_index\n ],\n GENERATOR_INDEX__COMMITMENT_NONCE\n )\n}\n\npub fn compute_unique_siloed_commitment(nonce: Field, siloed_commitment: Field) -> Field {\n pedersen_hash(\n [\n nonce,\n siloed_commitment\n ],\n GENERATOR_INDEX__UNIQUE_COMMITMENT\n )\n}\n\npub fn compute_unique_siloed_commitments(\n first_nullifier: Field,\n siloed_commitments: [SideEffect; N]\n) -> [SideEffect; N] {\n let mut unique_siloed_commitments = [SideEffect::empty(); N];\n for i in 0..N {\n let siloed_commitment = siloed_commitments[i];\n if siloed_commitment.value != 0 {\n let nonce = compute_commitment_nonce(first_nullifier, i);\n unique_siloed_commitments[i] = SideEffect {\n value: compute_unique_siloed_commitment(nonce, siloed_commitment.value),\n counter: siloed_commitment.counter\n };\n }\n }\n unique_siloed_commitments\n}\n\npub fn pedersen_hash(inputs: [Field; N], hash_index: u32) -> Field {\n dep::std::hash::pedersen_hash_with_separator(inputs, hash_index)\n}\n", + "path": "/home/santiago/Projects/aztec3-packages/yarn-project/noir-protocol-circuits/src/crates/types/src/hash.nr" + }, + "126": { + "source": "pub fn field_from_bytes(bytes: [u8; N], big_endian: bool) -> Field {\n assert(bytes.len() as u32 < 32, \"field_from_bytes: N must be less than 32\");\n let mut as_field = 0;\n let mut offset = 1;\n for i in 0..N {\n let mut index = i;\n if big_endian {\n index = N - i - 1;\n }\n as_field += (bytes[index] as Field) * offset;\n offset *= 256;\n }\n\n as_field\n}\n", + "path": "/home/santiago/Projects/aztec3-packages/yarn-project/noir-protocol-circuits/src/crates/types/src/utils/field.nr" + }, + "150": { + "source": "use crate::utils::field::field_from_bytes;\nuse dep::std::cmp::Eq;\n\nglobal SELECTOR_SIZE = 4;\n\nstruct FunctionSelector {\n // 1st 4-bytes of abi-encoding of function.\n inner: u32,\n}\n\nimpl Eq for FunctionSelector {\n fn eq(self, function_selector: FunctionSelector) -> bool {\n function_selector.inner == self.inner\n }\n}\n\nimpl FunctionSelector {\n fn to_field(self) -> Field {\n self.inner as Field\n }\n\n pub fn from_u32(value: u32) -> Self {\n Self {\n inner : value,\n }\n }\n\n pub fn from_field(value : Field) -> Self {\n Self {\n inner : value as u32,\n }\n }\n\n pub fn from_signature(signature: str) -> Self {\n let bytes = signature.as_bytes();\n let hash = dep::std::hash::keccak256(bytes, bytes.len() as u32);\n\n let mut selector_be_bytes = [0; SELECTOR_SIZE];\n for i in 0..SELECTOR_SIZE {\n selector_be_bytes[i] = hash[i];\n }\n\n FunctionSelector::from_field(field_from_bytes(selector_be_bytes, true))\n }\n\n pub fn zero() -> Self {\n Self { inner: 0 }\n }\n\n pub fn serialize(self: Self) -> [Field; 1] {\n [self.inner as Field]\n }\n\n pub fn deserialize(fields: [Field; 1]) -> Self {\n Self {\n inner: fields[0] as u32\n }\n }\n}\n", + "path": "/home/santiago/Projects/aztec3-packages/yarn-project/noir-protocol-circuits/src/crates/types/src/abis/function_selector.nr" + }, + "160": { + "source": "use crate::{\n constants::{\n GENERATOR_INDEX__CONTRACT_ADDRESS,\n GENERATOR_INDEX__PARTIAL_ADDRESS,\n },\n hash::pedersen_hash,\n utils,\n grumpkin_point::GrumpkinPoint,\n};\nuse dep::std::cmp::Eq;\nuse crate::traits::{Empty, ToField};\n\n// Aztec address\nstruct AztecAddress {\n inner : Field\n}\n\nimpl Eq for AztecAddress {\n fn eq(self, other : Self) -> bool {\n self.to_field() == other.to_field()\n }\n}\n\nimpl Empty for AztecAddress {\n fn empty() -> Self {\n Self {\n inner : 0\n }\n }\n}\n\nimpl ToField for AztecAddress {\n fn to_field(self) -> Field {\n self.inner\n }\n}\n\nimpl AztecAddress {\n pub fn zero() -> Self {\n Self {\n inner: 0\n }\n }\n\n pub fn from_field(field : Field) -> Self {\n Self {\n inner : field\n }\n }\n\n pub fn compute(pub_key: GrumpkinPoint, partial_address: PartialAddress) -> AztecAddress {\n AztecAddress::from_field(\n pedersen_hash(\n [pub_key.x, pub_key.y, partial_address.to_field()],\n GENERATOR_INDEX__CONTRACT_ADDRESS\n )\n )\n }\n\n pub fn is_zero(self) -> bool {\n self.inner == 0\n }\n \n pub fn assert_is_zero(self) {\n assert(self.to_field() == 0);\n }\n\n pub fn conditional_assign(predicate: bool, lhs : Self, rhs : Self) -> Self{\n let result = utils::conditional_assign(predicate, rhs.to_field(), lhs.to_field());\n Self {\n inner : result\n }\n }\n\n pub fn serialize(self: Self) -> [Field; 1] {\n [self.inner]\n }\n\n pub fn deserialize(fields: [Field; 1]) -> Self {\n Self {\n inner: fields[0]\n }\n }\n}\n\nstruct EthAddress{\n inner : Field\n}\n\nimpl Eq for EthAddress {\n fn eq(self, other : Self) -> bool {\n self.to_field() == other.to_field()\n }\n}\n\nimpl Empty for EthAddress {\n fn empty() -> Self {\n Self {\n inner : 0\n }\n }\n}\n\nimpl ToField for EthAddress {\n fn to_field(self) -> Field {\n self.inner\n }\n}\n\nimpl EthAddress{\n pub fn zero() -> Self {\n Self {\n inner: 0\n }\n }\n\n pub fn from_field(field : Field) -> Self {\n Self {\n inner : field\n }\n }\n\n pub fn is_zero(self) -> bool {\n self.inner == 0\n }\n\n pub fn assert_is_zero(self) {\n assert(self.to_field() == 0);\n }\n\n pub fn conditional_assign(predicate: bool, lhs : Self, rhs : Self) -> Self{\n let result = utils::conditional_assign(predicate, rhs.to_field(), lhs.to_field());\n Self {\n inner : result\n }\n }\n\n pub fn serialize(self: Self) -> [Field; 1] {\n [self.inner]\n }\n\n pub fn deserialize(fields: [Field; 1]) -> Self {\n Self {\n inner: fields[0]\n }\n }\n}\n\n// Partial address\nstruct PartialAddress {\n inner : Field\n}\n\nimpl ToField for PartialAddress {\n fn to_field(self) -> Field {\n self.inner\n }\n}\n\nimpl PartialAddress {\n pub fn from_field(field : Field) -> Self {\n Self {\n inner : field\n }\n }\n\n pub fn compute(contract_address_salt : Field, function_tree_root : Field, constructor_hash : Field) -> Self {\n PartialAddress::from_field(\n pedersen_hash([\n // TODO why the zeroes?\n 0,\n 0,\n contract_address_salt,\n function_tree_root,\n constructor_hash\n ], GENERATOR_INDEX__PARTIAL_ADDRESS)\n )\n }\n\n pub fn to_field(self) -> Field {\n self.inner\n }\n\n pub fn assert_is_zero(self) {\n assert(self.to_field() == 0);\n }\n}\n", + "path": "/home/santiago/Projects/aztec3-packages/yarn-project/noir-protocol-circuits/src/crates/types/src/address.nr" + }, + "163": { + "source": "use crate::{\n constants::{\n CONTRACT_STORAGE_READ_LENGTH,\n GENERATOR_INDEX__PUBLIC_DATA_READ,\n },\n hash::pedersen_hash,\n};\nuse crate::traits::Empty;\n\nstruct StorageRead {\n storage_slot: Field,\n current_value: Field,\n}\n\nimpl Empty for StorageRead { \n fn empty() -> Self {\n Self {\n storage_slot: 0,\n current_value: 0,\n }\n }\n}\n\nimpl StorageRead {\n\n pub fn serialize(self) -> [Field; CONTRACT_STORAGE_READ_LENGTH] {\n [self.storage_slot, self.current_value]\n }\n\n pub fn hash(self) -> Field {\n pedersen_hash(self.serialize(), GENERATOR_INDEX__PUBLIC_DATA_READ)\n }\n\n pub fn is_empty(self) -> bool {\n self.storage_slot == 0\n }\n}\n", + "path": "/home/santiago/Projects/aztec3-packages/yarn-project/noir-protocol-circuits/src/crates/types/src/contrakt/storage_read.nr" + }, + "167": { + "source": "use dep::std::option::Option;\nuse dep::aztec::context::PrivateContext;\nuse dep::aztec::note::note_getter_options::{NoteGetterOptions, SortOrder};\nuse dep::aztec::oracle::get_public_key::get_public_key;\nuse dep::aztec::state_vars::set::Set;\nuse crate::{\n filter::filter_notes_min_sum,\n value_note::{ValueNote, VALUE_NOTE_LEN},\n};\nuse dep::aztec::protocol_types::address::AztecAddress;\n\n// Sort the note values (0th field) in descending order.\n// Pick the fewest notes whose sum is equal to or greater than `amount`.\npub fn create_note_getter_options_for_decreasing_balance(amount: Field) -> NoteGetterOptions {\n NoteGetterOptions::with_filter(filter_notes_min_sum, amount).sort(0, SortOrder.DESC)\n}\n\n// Creates a new note for the recipient.\n// Inserts it to the recipient's set of notes.\npub fn increment(balance: Set, amount: Field, recipient: AztecAddress) {\n let mut note = ValueNote::new(amount, recipient);\n // Insert the new note to the owner's set of notes and emit the log if value is non-zero.\n balance.insert(&mut note, amount != 0);\n}\n\n// Find some of the `owner`'s notes whose values add up to the `amount`.\n// Remove those notes.\n// If the value of the removed notes exceeds the requested `amount`, create a new note containing the excess value, so that exactly `amount` is removed.\n// Fail if the sum of the selected notes is less than the amount.\npub fn decrement(balance: Set, amount: Field, owner: AztecAddress) {\n let sum = decrement_by_at_most(balance, amount, owner);\n assert(sum == amount, \"Balance too low\");\n}\n\n// Similar to `decrement`, except that it doesn't fail if the decremented amount is less than max_amount.\n// The motivation behind this function is that there is an upper-bound on the number of notes a function may\n// read and nullify. The requested decrementation `amount` might be spread across too many of the `owner`'s\n// notes to 'fit' within this upper-bound, so we might have to remove an amount less than `amount`. A common\n// pattern is to repeatedly call this function across many function calls, until enough notes have been nullified to\n// equal `amount`.\n//\n// It returns the decremented amount, which should be less than or equal to max_amount.\npub fn decrement_by_at_most(\n balance: Set,\n max_amount: Field,\n owner: AztecAddress\n) -> Field {\n let options = create_note_getter_options_for_decreasing_balance(max_amount);\n let opt_notes = balance.get_notes(options);\n\n let mut decremented = 0;\n for i in 0..opt_notes.len() {\n if opt_notes[i].is_some() {\n decremented += destroy_note(balance, owner, opt_notes[i].unwrap_unchecked());\n }\n }\n\n // Add the change value back to the owner's balance.\n let mut change_value = 0;\n if decremented as u120 > max_amount as u120 {\n change_value = decremented - max_amount;\n decremented -= change_value;\n }\n increment(balance, change_value, owner);\n\n decremented\n}\n\n// Removes the note from the owner's set of notes.\n// Returns the value of the destroyed note.\npub fn destroy_note(\n balance: Set,\n owner: AztecAddress,\n note: ValueNote\n) -> Field {\n // Ensure the note is actually owned by the owner (to prevent user from generating a valid proof while\n // spending someone else's notes).\n assert(note.owner.eq(owner));\n\n balance.remove(note);\n\n note.value\n}\n", + "path": "/home/santiago/Projects/aztec3-packages/yarn-project/aztec-nr/value-note/src/utils.nr" + }, + "169": { + "source": "use dep::aztec::{\n protocol_types::address::AztecAddress,\n note::{\n note_header::NoteHeader,\n note_interface::NoteInterface,\n utils::compute_note_hash_for_read_or_nullify,\n },\n oracle::{\n rand::rand,\n nullifier_key::get_nullifier_secret_key,\n get_public_key::get_public_key,\n },\n log::emit_encrypted_log,\n hash::pedersen_hash,\n context::PrivateContext,\n};\n\nglobal VALUE_NOTE_LEN: Field = 3; // 3 plus a header.\n\n// docs:start:value-note-def\nstruct ValueNote {\n value: Field,\n owner: AztecAddress,\n randomness: Field,\n header: NoteHeader,\n}\n// docs:end:value-note-def\n\nimpl ValueNote {\n pub fn new(value: Field, owner: AztecAddress) -> Self {\n let randomness = rand();\n let header = NoteHeader::empty();\n ValueNote {\n value,\n owner,\n randomness,\n header,\n }\n }\n\n pub fn serialize(self) -> [Field; VALUE_NOTE_LEN] {\n [self.value, self.owner.to_field(), self.randomness]\n }\n\n pub fn deserialize(serialized_note: [Field; VALUE_NOTE_LEN]) -> Self {\n ValueNote {\n value: serialized_note[0],\n owner: AztecAddress::from_field(serialized_note[1]),\n randomness: serialized_note[2],\n header: NoteHeader::empty(),\n }\n }\n\n pub fn compute_note_hash(self) -> Field {\n // TODO(#1205) Should use a non-zero generator index.\n pedersen_hash(self.serialize(),0)\n }\n\n // docs:start:nullifier\n\n pub fn compute_nullifier(self, context: &mut PrivateContext) -> Field {\n let note_hash_for_nullify = compute_note_hash_for_read_or_nullify(ValueNoteMethods, self);\n let secret = context.request_nullifier_secret_key(self.owner);\n // TODO(#1205) Should use a non-zero generator index.\n pedersen_hash([\n note_hash_for_nullify,\n secret.low,\n secret.high,\n ],0)\n }\n\n // docs:end:nullifier\n\n pub fn compute_nullifier_without_context(self) -> Field {\n let note_hash_for_nullify = compute_note_hash_for_read_or_nullify(ValueNoteMethods, self);\n let secret = get_nullifier_secret_key(self.owner);\n // TODO(#1205) Should use a non-zero generator index.\n pedersen_hash([\n note_hash_for_nullify,\n secret.low,\n secret.high,\n ],0)\n }\n\n pub fn set_header(&mut self, header: NoteHeader) {\n self.header = header;\n }\n\n // Broadcasts the note as an encrypted log on L1.\n pub fn broadcast(self, context: &mut PrivateContext, slot: Field) {\n let encryption_pub_key = get_public_key(self.owner);\n emit_encrypted_log(\n context,\n (*context).this_address(),\n slot,\n encryption_pub_key,\n self.serialize(),\n );\n }\n}\n\nfn deserialize(serialized_note: [Field; VALUE_NOTE_LEN]) -> ValueNote {\n ValueNote::deserialize(serialized_note)\n}\n\nfn serialize(note: ValueNote) -> [Field; VALUE_NOTE_LEN] {\n note.serialize()\n}\n\nfn compute_note_hash(note: ValueNote) -> Field {\n note.compute_note_hash()\n}\n\nfn compute_nullifier(note: ValueNote, context: &mut PrivateContext) -> Field {\n note.compute_nullifier(context)\n}\n\nfn compute_nullifier_without_context(note: ValueNote) -> Field {\n note.compute_nullifier_without_context()\n}\n\nfn get_header(note: ValueNote) -> NoteHeader {\n note.header\n}\n\nfn set_header(note: &mut ValueNote, header: NoteHeader) {\n note.set_header(header)\n}\n\n// Broadcasts the note as an encrypted log on L1.\nfn broadcast(context: &mut PrivateContext, slot: Field, note: ValueNote) {\n note.broadcast(context, slot);\n}\n\nglobal ValueNoteMethods = NoteInterface {\n deserialize,\n serialize,\n compute_note_hash,\n compute_nullifier,\n compute_nullifier_without_context,\n get_header,\n set_header,\n broadcast,\n};\n", + "path": "/home/santiago/Projects/aztec3-packages/yarn-project/aztec-nr/value-note/src/value_note.nr" + } + } +} diff --git a/yarn-project/circuits.js/package.json b/yarn-project/circuits.js/package.json index e3f18a3ca83..2dafdfcaa70 100644 --- a/yarn-project/circuits.js/package.json +++ b/yarn-project/circuits.js/package.json @@ -9,7 +9,8 @@ "./factories": "./dest/tests/factories.js", "./utils": "./dest/utils/index.js", "./types": "./dest/types/index.js", - "./constants": "./dest/constants.gen.js" + "./constants": "./dest/constants.gen.js", + "./contract": "./dest/contract/index.js" }, "typedocOptions": { "entryPoints": [ @@ -41,6 +42,7 @@ "dependencies": { "@aztec/bb.js": "portal:../../barretenberg/ts", "@aztec/foundation": "workspace:^", + "@aztec/types": "workspace:^", "eslint": "^8.35.0", "lodash.chunk": "^4.2.0", "tslib": "^2.4.0" diff --git a/yarn-project/circuits.js/src/abis/__snapshots__/abis.test.ts.snap b/yarn-project/circuits.js/src/abis/__snapshots__/abis.test.ts.snap index 1410cd8307f..a04642f359b 100644 --- a/yarn-project/circuits.js/src/abis/__snapshots__/abis.test.ts.snap +++ b/yarn-project/circuits.js/src/abis/__snapshots__/abis.test.ts.snap @@ -1,93 +1,52 @@ // Jest Snapshot v1, https://goo.gl/fbAQLP -exports[`abis Computes an empty call request hash 1`] = `"0x23d61bd55cb4c93f3e1a2efe3fd17026e9b4e5ade0a32c69219e25621340745e"`; +exports[`abis Computes a callstack item hash 1`] = `"0x06dc9c35290e1868f41fc9f45bca49482bf5d5c000c789d6c74e53bce0f686f8"`; + +exports[`abis Computes a callstack item request hash 1`] = `"0x05c5d32c38f3de19dbfa92e25746e1db1a887d5b02b79dd915c0f50e9fc51dcd"`; exports[`abis Computes an empty nullifier hash 1`] = `"0x066e6cdc4a6ba5e4781deda650b0be6c12f975f064fc38df72c1060716759b17"`; -exports[`abis Computes an empty public inputs hash 1`] = `"0x00e6f5767edbd34ede7b3693186a3b0a49f1b266470003ee1385dd5d77b48d9e"`; +exports[`abis Computes an empty public inputs hash 1`] = `"0x2e2b79cee62cb99e9163a58daa37da8099a9eef49353d0cbbf85093dca66eca7"`; exports[`abis Computes an empty sideeffect hash 1`] = `"0x27b1d0839a5b23baf12a8d195b18ac288fcf401afb2f70b8a4b529ede5fa9fed"`; -exports[`abis compute globals hash 1`] = ` -Fr { - "asBigInt": 19996198784166720428914107076917074510032365849254400404611644441094528984289n, - "asBuffer": { - "data": [ - 44, - 53, - 114, - 139, - 52, - 188, - 28, - 197, - 40, - 157, - 117, - 130, - 246, - 163, - 68, - 239, - 11, - 227, - 228, - 184, - 250, - 189, - 231, - 11, - 41, - 32, - 78, - 168, - 174, - 60, - 100, - 225, - ], - "type": "Buffer", - }, -} -`; - exports[`abis compute private call stack item hash 1`] = ` Fr { - "asBigInt": 16865740224106637312048204066423595287637012547473437662620907740307887613802n, + "asBigInt": 12187345511405217717040217531423286257305914329376428594135414078733109256018n, "asBuffer": { "data": [ - 37, - 73, - 171, - 177, - 192, + 26, + 241, + 203, + 9, + 80, + 135, + 163, + 233, + 54, + 161, + 69, + 247, + 77, + 223, + 148, 99, + 177, + 7, + 65, + 132, + 142, + 175, 255, - 255, - 248, - 50, - 253, - 100, - 252, - 131, - 150, - 255, - 76, - 95, - 57, - 79, - 225, - 224, + 49, 18, - 59, - 180, - 144, - 60, - 66, - 124, - 225, - 231, - 106, + 190, + 219, + 83, + 245, + 1, + 95, + 82, ], "type": "Buffer", }, @@ -96,41 +55,41 @@ Fr { exports[`abis compute public call stack item hash 1`] = ` Fr { - "asBigInt": 16804043213184589080619269242419543621552532999831626309510240938477196171874n, + "asBigInt": 13521975876243055846900626787876592989336331137171592962170740576377717759471n, "asBuffer": { "data": [ - 37, - 38, - 192, - 92, - 109, - 120, - 44, - 208, - 27, - 110, - 25, - 141, - 60, - 78, - 9, - 239, - 230, - 96, - 227, - 142, - 36, - 230, - 226, - 109, - 232, - 196, + 29, + 229, + 42, + 200, + 229, + 101, + 73, + 135, + 111, + 135, + 149, + 117, + 252, + 244, + 207, 1, - 178, - 116, - 88, - 90, - 98, + 155, + 22, + 194, + 235, + 62, + 7, + 199, + 113, + 209, + 176, + 171, + 35, + 248, + 145, + 157, + 239, ], "type": "Buffer", }, @@ -223,217 +182,6 @@ Fr { } `; -exports[`abis computes a complete address 1`] = ` -CompleteAddress { - "address": AztecAddress { - "asBigInt": 11147987456032716744065611270804907827731877483785437429582589518717493164615n, - "asBuffer": { - "data": [ - 24, - 165, - 137, - 140, - 101, - 163, - 155, - 231, - 234, - 167, - 7, - 107, - 97, - 180, - 211, - 70, - 191, - 71, - 74, - 128, - 186, - 41, - 12, - 41, - 175, - 241, - 177, - 251, - 13, - 23, - 186, - 71, - ], - "type": "Buffer", - }, - }, - "partialAddress": Fr { - "asBigInt": 12921923968526873580423865450965452589013784321633824918807523389752605479568n, - "asBuffer": { - "data": [ - 28, - 145, - 140, - 190, - 160, - 180, - 72, - 234, - 8, - 38, - 34, - 189, - 150, - 49, - 183, - 78, - 127, - 175, - 204, - 179, - 163, - 133, - 166, - 183, - 238, - 243, - 37, - 56, - 25, - 29, - 18, - 144, - ], - "type": "Buffer", - }, - }, - "publicKey": Point { - "kind": "point", - "x": Fr { - "asBigInt": 1n, - "asBuffer": { - "data": [ - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 1, - ], - "type": "Buffer", - }, - }, - "y": Fr { - "asBigInt": 2n, - "asBuffer": { - "data": [ - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 2, - ], - "type": "Buffer", - }, - }, - }, -} -`; - -exports[`abis computes a contract address from partial 1`] = ` -AztecAddress { - "asBigInt": 15451914702384811781262267264665450579243110288870049455159710794697789874663n, - "asBuffer": { - "data": [ - 34, - 41, - 121, - 74, - 138, - 50, - 100, - 6, - 122, - 50, - 114, - 42, - 84, - 48, - 151, - 243, - 88, - 162, - 160, - 249, - 55, - 16, - 237, - 195, - 114, - 27, - 106, - 66, - 228, - 123, - 33, - 231, - ], - "type": "Buffer", - }, -} -`; - exports[`abis computes a function leaf 1`] = ` Fr { "asBigInt": 8957681167943973616438847631514238173699549883609341685749385526208761312950n, @@ -489,49 +237,6 @@ exports[`abis computes a function selector 1`] = ` } `; -exports[`abis computes block hash with globals 1`] = ` -Fr { - "asBigInt": 7177915431153102916601456081755280584460785548870192083974540199919775120827n, - "asBuffer": { - "data": [ - 15, - 222, - 142, - 96, - 169, - 217, - 84, - 90, - 55, - 110, - 52, - 172, - 167, - 236, - 97, - 56, - 185, - 98, - 133, - 50, - 113, - 88, - 184, - 54, - 59, - 75, - 252, - 181, - 66, - 59, - 253, - 187, - ], - "type": "Buffer", - }, -} -`; - exports[`abis computes commitment nonce 1`] = ` Fr { "asBigInt": 7653394882992289714855533169019502055399179742531912347686813547951736946253n, diff --git a/yarn-project/circuits.js/src/abis/abis.test.ts b/yarn-project/circuits.js/src/abis/abis.test.ts index ab0132ce8b6..99b718e52a9 100644 --- a/yarn-project/circuits.js/src/abis/abis.test.ts +++ b/yarn-project/circuits.js/src/abis/abis.test.ts @@ -6,7 +6,6 @@ import { FunctionData, FunctionLeafPreimage, FunctionSelector, - GlobalVariables, NewContractData, PublicCallStackItem, PublicCircuitPublicInputs, @@ -16,23 +15,18 @@ import { import { makeAztecAddress, makeEthAddress, - makePoint, makePrivateCallStackItem, makePublicCallStackItem, makeTxRequest, makeVerificationKey, } from '../tests/factories.js'; import { - computeBlockHashWithGlobals, computeCommitmentNonce, computeCommitmentsHash, - computeCompleteAddress, - computeContractAddressFromPartial, computeContractLeaf, computeFunctionLeaf, computeFunctionSelector, computeFunctionTreeRoot, - computeGlobalsHash, computeNullifierHash, computePrivateCallStackItemHash, computePublicCallStackItemHash, @@ -88,22 +82,6 @@ describe('abis', () => { expect(res).toMatchSnapshot(); }); - it('computes a complete address', () => { - const deployerPubKey = makePoint(); - const contractAddrSalt = new Fr(2n); - const treeRoot = new Fr(3n); - const constructorHash = new Fr(4n); - const res = computeCompleteAddress(deployerPubKey, contractAddrSalt, treeRoot, constructorHash); - expect(res).toMatchSnapshot(); - }); - - it('computes a contract address from partial', () => { - const deployerPubKey = makePoint(); - const partialAddress = new Fr(2n); - const res = computeContractAddressFromPartial(deployerPubKey, partialAddress); - expect(res).toMatchSnapshot(); - }); - it('computes commitment nonce', () => { const nullifierZero = new Fr(123n); const commitmentIndex = 456; @@ -132,40 +110,6 @@ describe('abis', () => { expect(res).toMatchSnapshot(); }); - it('computes block hash with globals', () => { - const globals = GlobalVariables.from({ - chainId: new Fr(1n), - version: new Fr(2n), - blockNumber: new Fr(3n), - timestamp: new Fr(4n), - }); - const noteHashTreeRoot = new Fr(5n); - const nullifierTreeRoot = new Fr(6n); - const contractTreeRoot = new Fr(7n); - const l1ToL2DataTreeRoot = new Fr(8n); - const publicDataTreeRoot = new Fr(9n); - const res = computeBlockHashWithGlobals( - globals, - noteHashTreeRoot, - nullifierTreeRoot, - contractTreeRoot, - l1ToL2DataTreeRoot, - publicDataTreeRoot, - ); - expect(res).toMatchSnapshot(); - }); - - it('compute globals hash', () => { - const globals = GlobalVariables.from({ - chainId: new Fr(1n), - version: new Fr(2n), - blockNumber: new Fr(3n), - timestamp: new Fr(4n), - }); - const res = computeGlobalsHash(globals); - expect(res).toMatchSnapshot(); - }); - it('computes public data tree value', () => { const value = new Fr(3n); const res = computePublicDataTreeValue(value); @@ -246,16 +190,39 @@ describe('abis', () => { expect(emptyHash).toMatchSnapshot(); }); - it('Computes an empty call request hash ', () => { - const emptycallstack = PublicCallStackItem.empty(); - const emptyHash = emptycallstack.hash(); - expect(emptyHash.toString()).toMatchSnapshot(); - }); - it('Computes an empty public inputs hash ', () => { const publicInputs = PublicCircuitPublicInputs.empty(); const emptyHash = computePublicInputsHash(publicInputs); expect(Fr.fromBuffer(emptyHash).toString()).toMatchSnapshot(); }); + + it('Computes a callstack item request hash', () => { + const callStack = PublicCallStackItem.empty(); + + callStack.contractAddress = AztecAddress.fromField(new Fr(1)); + callStack.functionData = new FunctionData(new FunctionSelector(2), false, false, false); + callStack.isExecutionRequest = true; + callStack.publicInputs.newCommitments[0] = new SideEffect(new Fr(1), new Fr(0)); + + const hash = callStack.hash(); + expect(hash.toString()).toMatchSnapshot(); + + // Value used in compute_call_stack_item_hash test in noir circuits + // console.log("hash", hash.toString()); + }); + + it('Computes a callstack item hash', () => { + const callStack = PublicCallStackItem.empty(); + + callStack.contractAddress = AztecAddress.fromField(new Fr(1)); + callStack.functionData = new FunctionData(new FunctionSelector(2), false, false, false); + callStack.publicInputs.newCommitments[0] = new SideEffect(new Fr(1), new Fr(0)); + + const hash = callStack.hash(); + expect(hash.toString()).toMatchSnapshot(); + + // Value used in compute_call_stack_item_request_hash test in noir circuits + // console.log("hash", hash.toString()); + }); }); diff --git a/yarn-project/circuits.js/src/abis/abis.ts b/yarn-project/circuits.js/src/abis/abis.ts index a33a058a9d3..c6650f41e42 100644 --- a/yarn-project/circuits.js/src/abis/abis.ts +++ b/yarn-project/circuits.js/src/abis/abis.ts @@ -16,13 +16,11 @@ import { } from '../constants.gen.js'; import { CallContext, - CompleteAddress, ContractDeploymentData, ContractStorageRead, ContractStorageUpdateRequest, FunctionData, FunctionLeafPreimage, - GlobalVariables, NewContractData, PrivateCallStackItem, PrivateCircuitPublicInputs, @@ -34,7 +32,6 @@ import { TxRequest, VerificationKey, } from '../structs/index.js'; -import { PublicKey } from '../types/index.js'; import { MerkleTreeCalculator } from './merkle_tree_calculator.js'; /** @@ -168,61 +165,6 @@ export function hashConstructor(functionData: FunctionData, argsHash: Fr, constr ); } -/** - * Computes a complete address. - * @param deployerPubKey - The pubkey of the contract deployer. - * @param contractAddrSalt - The salt used as one of the inputs of the contract address computation. - * @param fnTreeRoot - The function tree root of the contract being deployed. - * @param constructorHash - The hash of the constructor. - * @returns The complete address. - */ -export function computeCompleteAddress( - deployerPubKey: PublicKey, - contractAddrSalt: Fr, - fnTreeRoot: Fr, - constructorHash: Fr, -): CompleteAddress { - const partialAddress = computePartialAddress(contractAddrSalt, fnTreeRoot, constructorHash); - return new CompleteAddress( - computeContractAddressFromPartial(deployerPubKey, partialAddress), - deployerPubKey, - partialAddress, - ); -} - -/** - * - */ -function computePartialAddress(contractAddrSalt: Fr, fnTreeRoot: Fr, constructorHash: Fr) { - return Fr.fromBuffer( - pedersenHash( - [ - Fr.ZERO.toBuffer(), - Fr.ZERO.toBuffer(), - contractAddrSalt.toBuffer(), - fnTreeRoot.toBuffer(), - constructorHash.toBuffer(), - ], - GeneratorIndex.PARTIAL_ADDRESS, - ), - ); -} - -/** - * Computes a contract address from its partial address and the pubkey. - * @param partial - The salt used as one of the inputs of the contract address computation. - * @param fnTreeRoot - The function tree root of the contract being deployed. - * @param constructorHash - The hash of the constructor. - * @returns The partially constructed contract address. - */ -export function computeContractAddressFromPartial(pubKey: PublicKey, partialAddress: Fr): AztecAddress { - const result = pedersenHash( - [pubKey.x.toBuffer(), pubKey.y.toBuffer(), partialAddress.toBuffer()], - GeneratorIndex.CONTRACT_ADDRESS, - ); - return new AztecAddress(result); -} - /** * Computes a commitment nonce, which will be used to create a unique commitment. * @param nullifierZero - The first nullifier in the tx. @@ -269,88 +211,6 @@ export function siloNullifier(contract: AztecAddress, innerNullifier: Fr): Fr { return Fr.fromBuffer(pedersenHash([contract.toBuffer(), innerNullifier.toBuffer()], GeneratorIndex.OUTER_NULLIFIER)); } -/** - * Computes the block hash given the blocks globals and roots. - * @param globals - The global variables to put into the block hash. - * @param noteHashTree - The root of the note hash tree. - * @param nullifierTreeRoot - The root of the nullifier tree. - * @param contractTreeRoot - The root of the contract tree. - * @param l1ToL2DataTreeRoot - The root of the l1 to l2 data tree. - * @param publicDataTreeRoot - The root of the public data tree. - * @returns The block hash. - */ -// TODO(#3941) -export function computeBlockHashWithGlobals( - globals: GlobalVariables, - noteHashTreeRoot: Fr, - nullifierTreeRoot: Fr, - contractTreeRoot: Fr, - l1ToL2DataTreeRoot: Fr, - publicDataTreeRoot: Fr, -): Fr { - return computeBlockHash( - computeGlobalsHash(globals), - noteHashTreeRoot, - nullifierTreeRoot, - contractTreeRoot, - l1ToL2DataTreeRoot, - publicDataTreeRoot, - ); -} - -/** - * Computes the block hash given the blocks globals and roots. - * @param globalsHash - The global variables hash to put into the block hash. - * @param noteHashTree - The root of the note hash tree. - * @param nullifierTreeRoot - The root of the nullifier tree. - * @param contractTreeRoot - The root of the contract tree. - * @param l1ToL2DataTreeRoot - The root of the l1 to l2 data tree. - * @param publicDataTreeRoot - The root of the public data tree. - * @returns The block hash. - */ -export function computeBlockHash( - globalsHash: Fr, - noteHashTreeRoot: Fr, - nullifierTreeRoot: Fr, - contractTreeRoot: Fr, - l1ToL2DataTreeRoot: Fr, - publicDataTreeRoot: Fr, -): Fr { - return Fr.fromBuffer( - pedersenHash( - [ - globalsHash.toBuffer(), - noteHashTreeRoot.toBuffer(), - nullifierTreeRoot.toBuffer(), - contractTreeRoot.toBuffer(), - l1ToL2DataTreeRoot.toBuffer(), - publicDataTreeRoot.toBuffer(), - ], - GeneratorIndex.BLOCK_HASH, - ), - ); -} - -/** - * Computes the globals hash given the globals. - * @param globals - The global variables to put into the block hash. - * @returns The globals hash. - * TODO: move this to GlobalVariables? - */ -export function computeGlobalsHash(globals: GlobalVariables): Fr { - return Fr.fromBuffer( - pedersenHash( - [ - globals.chainId.toBuffer(), - globals.version.toBuffer(), - globals.blockNumber.toBuffer(), - globals.timestamp.toBuffer(), - ], - GeneratorIndex.GLOBAL_VARIABLES, - ), - ); -} - /** * Computes a public data tree value ready for insertion. * @param value - Raw public data tree value to hash into a tree-insertion-ready value. @@ -420,12 +280,12 @@ export function computeVarArgsHash(args: Fr[]) { * @returns The contract leaf. */ export function computeContractLeaf(cd: NewContractData): Fr { - if (cd.contractAddress.isZero() && cd.portalContractAddress.isZero() && cd.functionTreeRoot.isZero()) { + if (cd.contractAddress.isZero() && cd.portalContractAddress.isZero() && cd.contractClassId.isZero()) { return new Fr(0); } return Fr.fromBuffer( pedersenHash( - [cd.contractAddress.toBuffer(), cd.portalContractAddress.toBuffer(), cd.functionTreeRoot.toBuffer()], + [cd.contractAddress.toBuffer(), cd.portalContractAddress.toBuffer(), cd.contractClassId.toBuffer()], GeneratorIndex.CONTRACT_LEAF, ), ); @@ -450,9 +310,6 @@ export function computeTxHash(txRequest: TxRequest): Fr { ); } -/** - * - */ function computeFunctionDataHash(functionData: FunctionData): Fr { return Fr.fromBuffer( pedersenHash( @@ -467,9 +324,6 @@ function computeFunctionDataHash(functionData: FunctionData): Fr { ); } -/** - * - */ function computeTxContextHash(txContext: TxContext): Fr { return Fr.fromBuffer( pedersenHash( @@ -486,17 +340,14 @@ function computeTxContextHash(txContext: TxContext): Fr { ); } -/** - * - */ function computeContractDeploymentDataHash(data: ContractDeploymentData): Fr { return Fr.fromBuffer( pedersenHash( [ - data.deployerPublicKey.x.toBuffer(), - data.deployerPublicKey.y.toBuffer(), - data.constructorVkHash.toBuffer(), - data.functionTreeRoot.toBuffer(), + data.publicKey.x.toBuffer(), + data.publicKey.y.toBuffer(), + data.initializationHash.toBuffer(), + data.contractClassId.toBuffer(), data.contractAddressSalt.toBuffer(), data.portalContractAddress.toBuffer(), ], @@ -505,9 +356,6 @@ function computeContractDeploymentDataHash(data: ContractDeploymentData): Fr { ); } -/** - * - */ function computeCallContextHash(input: CallContext) { return pedersenHash( [ @@ -524,24 +372,21 @@ function computeCallContextHash(input: CallContext) { ); } -/** - * - */ function computePrivateInputsHash(input: PrivateCircuitPublicInputs) { const toHash = [ computeCallContextHash(input.callContext), input.argsHash.toBuffer(), ...input.returnValues.map(fr => fr.toBuffer()), ...input.readRequests - .map(se => se.toFieldArray()) + .map(rr => rr.toFields()) .flat() .map(fr => fr.toBuffer()), ...input.newCommitments - .map(se => se.toFieldArray()) + .map(n => n.toFields()) .flat() .map(fr => fr.toBuffer()), ...input.newNullifiers - .map(selinked => selinked.toFieldArray()) + .map(n => n.toFields()) .flat() .map(fr => fr.toBuffer()), ...input.privateCallStackHashes.map(fr => fr.toBuffer()), @@ -552,13 +397,7 @@ function computePrivateInputsHash(input: PrivateCircuitPublicInputs) { ...input.unencryptedLogsHash.map(fr => fr.toBuffer()), input.encryptedLogPreimagesLength.toBuffer(), input.unencryptedLogPreimagesLength.toBuffer(), - input.blockHeader.noteHashTreeRoot.toBuffer(), - input.blockHeader.nullifierTreeRoot.toBuffer(), - input.blockHeader.contractTreeRoot.toBuffer(), - input.blockHeader.l1ToL2MessageTreeRoot.toBuffer(), - input.blockHeader.archiveRoot.toBuffer(), - input.blockHeader.publicDataTreeRoot.toBuffer(), - input.blockHeader.globalVariablesHash.toBuffer(), + ...(input.historicalHeader.toFieldArray().map(fr => fr.toBuffer()) as Buffer[]), computeContractDeploymentDataHash(input.contractDeploymentData).toBuffer(), input.chainId.toBuffer(), input.version.toBuffer(), @@ -589,9 +428,6 @@ export function computePrivateCallStackItemHash(callStackItem: PrivateCallStackI ); } -/** - * - */ function computeContractStorageUpdateRequestHash(input: ContractStorageUpdateRequest) { return pedersenHash( [input.storageSlot.toBuffer(), input.oldValue.toBuffer(), input.newValue.toBuffer()], @@ -599,22 +435,14 @@ function computeContractStorageUpdateRequestHash(input: ContractStorageUpdateReq ); } -/** - * - */ function computeContractStorageReadsHash(input: ContractStorageRead) { return pedersenHash([input.storageSlot.toBuffer(), input.currentValue.toBuffer()], GeneratorIndex.PUBLIC_DATA_READ); } -/** - * - */ + export function computeCommitmentsHash(input: SideEffect) { return pedersenHash([input.value.toBuffer(), input.counter.toBuffer()], GeneratorIndex.SIDE_EFFECT); } -/** - * - */ export function computeNullifierHash(input: SideEffectLinkedToNoteHash) { return pedersenHash( [input.value.toBuffer(), input.noteHash.toBuffer(), input.counter.toBuffer()], @@ -622,9 +450,6 @@ export function computeNullifierHash(input: SideEffectLinkedToNoteHash) { ); } -/** - * - */ export function computePublicInputsHash(input: PublicCircuitPublicInputs) { const toHash = [ computeCallContextHash(input.callContext), @@ -638,13 +463,7 @@ export function computePublicInputsHash(input: PublicCircuitPublicInputs) { ...input.newL2ToL1Msgs.map(fr => fr.toBuffer()), ...input.unencryptedLogsHash.map(fr => fr.toBuffer()), input.unencryptedLogPreimagesLength.toBuffer(), - input.blockHeader.noteHashTreeRoot.toBuffer(), - input.blockHeader.nullifierTreeRoot.toBuffer(), - input.blockHeader.contractTreeRoot.toBuffer(), - input.blockHeader.l1ToL2MessageTreeRoot.toBuffer(), - input.blockHeader.archiveRoot.toBuffer(), - input.blockHeader.publicDataTreeRoot.toBuffer(), - input.blockHeader.globalVariablesHash.toBuffer(), + ...input.historicalHeader.toFieldArray().map(fr => fr.toBuffer()), input.proverAddress.toBuffer(), ]; if (toHash.length != PUBLIC_CIRCUIT_PUBLIC_INPUTS_HASH_INPUT_LENGTH) { diff --git a/yarn-project/circuits.js/src/abis/merkle_tree_calculator.ts b/yarn-project/circuits.js/src/abis/merkle_tree_calculator.ts index a4de23b3911..3c8796d48da 100644 --- a/yarn-project/circuits.js/src/abis/merkle_tree_calculator.ts +++ b/yarn-project/circuits.js/src/abis/merkle_tree_calculator.ts @@ -5,10 +5,16 @@ import { pedersenHash } from '@aztec/foundation/crypto'; */ export class MerkleTreeCalculator { private zeroHashes: Buffer[]; - - constructor(private height: number, zeroLeaf = Buffer.alloc(32)) { + private hasher: (left: Buffer, right: Buffer) => Buffer; + + constructor( + private height: number, + zeroLeaf = Buffer.alloc(32), + hasher = (left: Buffer, right: Buffer) => pedersenHash([left, right]), + ) { + this.hasher = hasher; this.zeroHashes = Array.from({ length: height }).reduce( - (acc: Buffer[], _, i) => [...acc, pedersenHash([acc[i], acc[i]])], + (acc: Buffer[], _, i) => [...acc, this.hasher(acc[i], acc[i])], [zeroLeaf], ); } @@ -26,7 +32,7 @@ export class MerkleTreeCalculator { for (let j = 0; j < leaves.length / 2; ++j) { const l = leaves[j * 2]; const r = leaves[j * 2 + 1] || this.zeroHashes[i]; - newLeaves[j] = pedersenHash([l, r]); + newLeaves[j] = this.hasher(l, r); } result = result.concat(new Array(numLeaves - leaves.length).fill(this.zeroHashes[i]), newLeaves); leaves = newLeaves; @@ -47,7 +53,7 @@ export class MerkleTreeCalculator { for (; j < leaves.length / 2; ++j) { const l = leaves[j * 2]; const r = leaves[j * 2 + 1] || this.zeroHashes[i]; - leaves[j] = pedersenHash([l, r]); + leaves[j] = this.hasher(l, r); } leaves = leaves.slice(0, j); } diff --git a/yarn-project/circuits.js/src/constants.gen.ts b/yarn-project/circuits.js/src/constants.gen.ts index 79cd3856feb..6d2571f17ec 100644 --- a/yarn-project/circuits.js/src/constants.gen.ts +++ b/yarn-project/circuits.js/src/constants.gen.ts @@ -10,6 +10,7 @@ export const MAX_NEW_L2_TO_L1_MSGS_PER_CALL = 2; export const MAX_PUBLIC_DATA_UPDATE_REQUESTS_PER_CALL = 16; export const MAX_PUBLIC_DATA_READS_PER_CALL = 16; export const MAX_READ_REQUESTS_PER_CALL = 32; +export const MAX_NULLIFIER_KEY_VALIDATION_REQUESTS_PER_CALL = 1; export const MAX_NEW_COMMITMENTS_PER_TX = 64; export const MAX_NEW_NULLIFIERS_PER_TX = 64; export const MAX_PRIVATE_CALL_STACK_LENGTH_PER_TX = 8; @@ -20,6 +21,7 @@ export const MAX_PUBLIC_DATA_READS_PER_TX = 16; export const MAX_NEW_CONTRACTS_PER_TX = 1; export const MAX_OPTIONALLY_REVEALED_DATA_LENGTH_PER_TX = 4; export const MAX_READ_REQUESTS_PER_TX = 128; +export const MAX_NULLIFIER_KEY_VALIDATION_REQUESTS_PER_TX = 4; export const NUM_ENCRYPTED_LOGS_HASHES_PER_TX = 1; export const NUM_UNENCRYPTED_LOGS_HASHES_PER_TX = 1; export const NUMBER_OF_L1_L2_MESSAGES_PER_ROLLUP = 16; @@ -48,23 +50,23 @@ export const NUM_FIELDS_PER_SHA256 = 2; export const ARGS_HASH_CHUNK_LENGTH = 32; export const ARGS_HASH_CHUNK_COUNT = 16; export const L1_TO_L2_MESSAGE_LENGTH = 8; -export const L1_TO_L2_MESSAGE_ORACLE_CALL_LENGTH = 26; +export const L1_TO_L2_MESSAGE_ORACLE_CALL_LENGTH = 25; export const MAX_NOTE_FIELDS_LENGTH = 20; export const GET_NOTE_ORACLE_RETURN_LENGTH = 23; export const MAX_NOTES_PER_PAGE = 10; export const VIEW_NOTE_ORACLE_RETURN_LENGTH = 212; export const CALL_CONTEXT_LENGTH = 8; -export const BLOCK_HEADER_LENGTH = 7; +export const HEADER_LENGTH = 18; export const FUNCTION_DATA_LENGTH = 4; export const CONTRACT_DEPLOYMENT_DATA_LENGTH = 6; -export const PRIVATE_CIRCUIT_PUBLIC_INPUTS_LENGTH = 189; +export const PRIVATE_CIRCUIT_PUBLIC_INPUTS_LENGTH = 200; export const CONTRACT_STORAGE_UPDATE_REQUEST_LENGTH = 3; export const CONTRACT_STORAGE_READ_LENGTH = 2; export const PUBLIC_CIRCUIT_PUBLIC_INPUTS_LENGTH = 190; export const GET_NOTES_ORACLE_RETURN_LENGTH = 674; -export const CALL_PRIVATE_FUNCTION_RETURN_SIZE = 195; -export const PUBLIC_CIRCUIT_PUBLIC_INPUTS_HASH_INPUT_LENGTH = 87; -export const PRIVATE_CIRCUIT_PUBLIC_INPUTS_HASH_INPUT_LENGTH = 177; +export const CALL_PRIVATE_FUNCTION_RETURN_SIZE = 210; +export const PUBLIC_CIRCUIT_PUBLIC_INPUTS_HASH_INPUT_LENGTH = 98; +export const PRIVATE_CIRCUIT_PUBLIC_INPUTS_HASH_INPUT_LENGTH = 188; export const COMMITMENTS_NUM_BYTES_PER_BASE_ROLLUP = 2048; export const NULLIFIERS_NUM_BYTES_PER_BASE_ROLLUP = 2048; export const PUBLIC_DATA_WRITES_NUM_BYTES_PER_BASE_ROLLUP = 1024; diff --git a/yarn-project/circuits.js/src/contract/__snapshots__/contract_address.test.ts.snap b/yarn-project/circuits.js/src/contract/__snapshots__/contract_address.test.ts.snap new file mode 100644 index 00000000000..1e861585d9d --- /dev/null +++ b/yarn-project/circuits.js/src/contract/__snapshots__/contract_address.test.ts.snap @@ -0,0 +1,259 @@ +// Jest Snapshot v1, https://goo.gl/fbAQLP + +exports[`ContractAddress computeContractAddressFromInstance 1`] = ` +AztecAddress { + "asBigInt": 3883883889294466972222695990544651254433869677543312058555575694285817356088n, + "asBuffer": { + "data": [ + 8, + 150, + 51, + 76, + 27, + 117, + 237, + 233, + 137, + 38, + 191, + 74, + 188, + 215, + 31, + 48, + 171, + 201, + 98, + 17, + 7, + 12, + 250, + 148, + 232, + 140, + 94, + 129, + 3, + 105, + 159, + 56, + ], + "type": "Buffer", + }, +} +`; + +exports[`ContractAddress computeContractAddressFromPartial 1`] = ` +AztecAddress { + "asBigInt": 21497883470428896497848272622467250633298383505675808099366629400564707993275n, + "asBuffer": { + "data": [ + 47, + 135, + 94, + 239, + 243, + 232, + 72, + 120, + 213, + 116, + 228, + 154, + 136, + 109, + 14, + 128, + 44, + 244, + 1, + 8, + 118, + 28, + 224, + 83, + 57, + 99, + 133, + 163, + 33, + 177, + 166, + 187, + ], + "type": "Buffer", + }, +} +`; + +exports[`ContractAddress computeInitializationHash 1`] = ` +Fr { + "asBigInt": 6008702290320255259549389675568071185910851926477784271985492188905918575237n, + "asBuffer": { + "data": [ + 13, + 72, + 206, + 18, + 237, + 214, + 138, + 47, + 96, + 228, + 192, + 127, + 222, + 19, + 156, + 23, + 220, + 224, + 89, + 169, + 234, + 46, + 7, + 2, + 131, + 242, + 115, + 20, + 86, + 206, + 50, + 133, + ], + "type": "Buffer", + }, +} +`; + +exports[`ContractAddress computePartialAddress 1`] = ` +Fr { + "asBigInt": 11370807533904788559065519582196668367978392224620203970943957018221100220632n, + "asBuffer": { + "data": [ + 25, + 35, + 166, + 36, + 110, + 48, + 87, + 32, + 182, + 170, + 247, + 81, + 253, + 224, + 52, + 38, + 19, + 233, + 60, + 130, + 228, + 85, + 195, + 131, + 30, + 40, + 55, + 92, + 22, + 221, + 64, + 216, + ], + "type": "Buffer", + }, +} +`; + +exports[`ContractAddress computePublicKeysHash 1`] = ` +Fr { + "asBigInt": 11370807533904788559065519582196668367978392224620203970943957018221100220632n, + "asBuffer": { + "data": [ + 25, + 35, + 166, + 36, + 110, + 48, + 87, + 32, + 182, + 170, + 247, + 81, + 253, + 224, + 52, + 38, + 19, + 233, + 60, + 130, + 228, + 85, + 195, + 131, + 30, + 40, + 55, + 92, + 22, + 221, + 64, + 216, + ], + "type": "Buffer", + }, +} +`; + +exports[`ContractAddress computeSaltedInitializationHash 1`] = ` +Fr { + "asBigInt": 11725758444245911667025964940014811412794639055001970719147766917493953867026n, + "asBuffer": { + "data": [ + 25, + 236, + 139, + 73, + 109, + 192, + 136, + 17, + 189, + 50, + 237, + 56, + 134, + 9, + 56, + 184, + 97, + 39, + 147, + 221, + 79, + 57, + 195, + 179, + 116, + 155, + 140, + 213, + 210, + 243, + 45, + 18, + ], + "type": "Buffer", + }, +} +`; diff --git a/yarn-project/circuits.js/src/contract/__snapshots__/contract_class.test.ts.snap b/yarn-project/circuits.js/src/contract/__snapshots__/contract_class.test.ts.snap new file mode 100644 index 00000000000..644779761e4 --- /dev/null +++ b/yarn-project/circuits.js/src/contract/__snapshots__/contract_class.test.ts.snap @@ -0,0 +1,49 @@ +// Jest Snapshot v1, https://goo.gl/fbAQLP + +exports[`ContractClass creates a contract class from a contract compilation artifact 1`] = ` +"{ + "version": 1, + "artifactHash": "0x0000000000000000000000000000000000000000000000000000000000001234", + "publicFunctions": [ + { + "selector": { + "value": 2381782501 + }, + "bytecode": "0x1f8b08000000000000ffed9d097c1d55f5c7dfcb4bd2bcbcecfbd2242f4dd3360d69f3d28d5ad4b8518a0b1444aaa2a55bb0d8a6d8262c0a8a8ab8af7545140515dc57dc1744511064535054dc4041f6b52c65f99f3bef1ef2cbcdf425f3e7de97137ae7f339c99d7367eef99e73efdcb96fe6ceccb5b1582c1ecb2e0992fad8c485f307f5fffea7b7642c96d5ef92333e43380b66086762867016ce10cea219c2593c433867cd10ce9219c2999c219ca5338433354338cb660867f90ce1ac98219c953384b36a867056cf10ce9a19c2593b4338eb2c72b60027ffb66bd0ff1bf5ff26fdbf59ffe77d5af5ffd9fa7f9bf6b550afb7937490a4493a751e07660e4917c95c926e927924f3491690f4902c24e9253980a48f6411c962bdbffa813840b2846429c93292e5242b480e245949f22c92552407913c9be43924cfd5717b1ec9f3495e40f2429217911c4cb29ae4109235248792bc98e425242f257919c961248793acd5bea4b52f47901c49f27292a3485e417234c93a925792bc8ae4d524c790bc86e4b524eb498e25d940b2916413c966922d244324c791bc8e642bc9f124af27d946b29d64986407c909246f3062be936417c908c9a8c17922c9492427939c42f2469237919c4a721ac99b49de42723ac95b49de46f276923348de417226c93b49de45f26e92f790bc97e47d24ef27f900c907493e44f261928f90ec26f928c9c762e3ebffe3249f20f924c9a748ce22f934c9d9249f21f92cc939249f23f93cc9b924e7917c81e48b245f22399fe402922f937c85e4ab245f23f93ac93748be49f22d926f937c87e4bb24df23b990e4fb243f20f921c98f487e4cf213929f92fc8ce4e724bf20b988e497241793fc8ae4d72497685f0ab42fbf21f9ada1bb94e4329dfe9dfe7fb9fe7f85feff7bfdff4afdff2afdff6afdff1afdff5ac55f9d4dab31b3790d46e9f8988d838e8fdf02d0f1b19c001d1fd785a0e363bc08747cbc17838e8ffd59a06bd3e912d0b5439aff77e87429e8d23a9d025da74e97816e8e4e9783ae4ba72b403757a72b41d7add355a09ba7d3d5a09bafd335a05ba0d3b5a0ebd1e93afd9fe3a39641fdbfff692eaa4ccbe7917ec5ce6da31efce1b6d1003a6e1b8da0e3b6d1043af6bd1974dc365a40c76da31574dc3666838edb461be8b86d609be2b6d1013a6e1b69d071dbe8041db78d39a0e3b6d1053a6e1b7341c76da31b74dc36e6818edbc67cd071cc17808e63ce6d4ac5f810c8e7058f69bc3ecb3acec7633a0165b28ef3f198e67c3ca6391f8f69cce7ff9c8fc734e7e331cdf978fc723e1eab5caf785cf23e780c72bd623be672b0cd72bd62fbe4b2b12d72bd625b647bd816b95eb12d3203b645aed734e8980bdb221f4bd8169915fbb762286b50ffef7f7a4b06fb645ee2c6fa20a4d3fa7f31f86089a53f052c9d60a7cbae9d25782e998acf5dc0d26dd9673c5f4d85a51b58e6d96509aec9ceb75b6650a71c6bec43d9ce1cf06781657fe26087cbe575b695021df6e70b42f87aecf20dc4c10e97cbeb3dc0c7ba79ee62d58f7d1397adce131bc066dab24de53f1fe77cdf8219d85602b639b27a8c6b8bd695423e8e2d797fec53bb0d9d8b63280e76b85c5e9f077cdca774e7976f60aa7c730d3e077d5f10abb9064b985dcbc75d681d711bea716cd7ec8fba0cbbf8bb84975ce783f9c06bbbff54652eb45b6670ccf702f320d8c0f3ec010e62df0bf18c830dd627207d466c6c3900d269fd9f99d5b1d213b21da6e719fba420bfc7b1cf0b816310d6d996ea4b4f0106cb6d3ea8ef1e8381d7bb40bf30245e780ee4fc06d0717fdf0dba394619f85b1dfb55cbed3a93cb4fac77d6e16fc285217cbd76f90672b5855ee063dd02c7b1dad7b867e17e6cd7f6b187e5abe39c8f13b69380fc8e82b1edce0626cbbfbd06a2fedec17342da2a4ba63feaefc034b058feed1b9c6b3bec96d9efba4dc5207ecc8e7d089edb5c1c531d463df13af27956cfea593dab67f5ac9ed5b37ad6fd9b55b1cc3538f11ed85c017c61f7c06cff36c0fb8e5cb6faed7727d89c63d56626b81664fe9e33ef7d26609b2781eb5e9d2e857cbc9fd469e8ecff5eccd61fdbe172793d0d7cec0bd69feddf8bf1d8f8dfae83cf68bb039bec1f03997e35cf00e71099c75d5affc7b951ac536d724ec198bf768f95ec7d79bc3eaa965cd726e6388b7df61e6e97111bfbf531d08ff3160b62e3fb870eb06be99a468613786d94e3c71c61d7ca4a754594dbe5798a6b2af7855ddd9bc16bd8e9d8d83543b685d7ab795bf39e4e3a36f13e09de5ba9d1f153c7d085f1b1fd2cb7db81a8d7f7b06ddbadd3817e9ce7381516ac67cbe77e27f7f51cdcb308ae35e6baf786f7d1b0fd4dc77d34cfea593dab67f5ac9ed5b37a56cfea593dab67f5ac9ed5b37a56cfea593dab679d49ac389f3eadffef6bbefa74f1b10eef0fd9beb68dcfe372d9eadec57970ef226dd5e6c0b867e3b8fc66a32ef0d9b81be19ecaf93a5d0af961731db0fe5cdcc3dad75c07b6550abe60fdd9bedf81f715b9dc67aeddecbd72bbc7c0c053f7cab93d99c71d3eab633ed386f7f9707e04cedf481bbae9aa0f3c66d2a0e334ce5fb11be38c8b7b68c17b750f807a60dff09937cebf08fa908be3637563f6172aff9a907c5ee2c6fa20a4f13efb22bbbe067de662287f106ca0dd7ebb763368371e1bffce23d627207d3504085f1ac6f16566d5eefa42b6c37487b14f0af2fb1cfbbc083806619d6da9767209b4a96be07c69fb7c83fe625c9a202e9c9f86b8d83ede948be6f88e19f039bbb906278eabb00fec73c0b7af71551ff099733c70be20f627385f306d9575fc18c89c0f9406bb96ce13e3e603e1fc8bced8c4770c24609b9b74bb56f381cc714f1af66d8632f33187c93c37e31ca65b81d9cf610a9fc364cecdc5394cf740bfd65a904def6bbc2de5f9631cc7a0df3dfbf01bdfc1e0a29f64ae228303c726bccda3d05ed5827dec3cfd1fc770e66f45fbe38d8160bcb6d86a99d9f3189e3fd33a8de308ce8f178c6d97d0e924f8dc09e5d484e4f3926bbcb608e23760d7d7e0585d02e50f820db4bbd4aedd0cdae5f11adb607d02d2d50563f1583a967c2abeccacda5d26643b4c1f60ec9382fc8c639f07806310d6d9966a27c5d0a6b8cdb81887a0bf189766880be7e3b5b139c6f6aa3de3fb6599d7f67189637a2ed71ce3878dbdf1fc607f8c34f19984746ce27522bc7e857d025ebf72f17c09cf99c7e74be6388cc5fff75d76f8cc0d1feaaee6f47746e09b037cbc5f02f8e63ae08bf2ccc15ce0e3fdf0dd92f31cf07547e09b077cbc5f11f0d97e5f12bed76e2a7c61efa02b86ffb6c78838169d0a1ff6bbbcdf2ce0b37dcd5ff1f546e0c3fb00bc5f09f059bebe14f0f545e0c36b32bc5f12f86c5f9351652f8ec0d70f7cbc5f29f0591eef057c99087c3846ca409af96c8f9114df92087c4b8189f72b03bee50ef89645e05b0e7cbc5f39f01de8806f4504be03818ff7ab00be6739e05b1981ef59c0c7fb5502df410ef85645e03b08f878bf2ae07b8e03be6747e07b0ef0f17ed5c037e880efb911f806818ff7ab01bee73be07b5e04bee7031fef570b7c2f74c0f782087c2f043ede0fdf877fb003be1745e03b18f8783f3cff1ee2806f7504be43808ff76b01be43edf205cf43ae89c07728b0bcd42ecb52c5f2e2082c2f059697d865099e877c99dd32836b8387592e53957138c484e3c7ec29c83f0ce275b8e578c5c12697cbebc8e759f76f56c5b2c6e04cc2766b04f0b1ee250e5992068b5a72f575617c589747d8e50bce0b6b23f01d012c47596559125c373c3202cb51c0f272ab2cd9f3c22bec9619f4e147033ffbca7652908f757eb465dfe26093cbe575e4f3ac9ed5b37a56cfea593dab67f5ac9ed5b37a56cfea593dab67f5ac9ed5b37a56cfea593dab67f5ac9ed5b37a56cfea593dab7d56c5b2d6e04cc2766b05f0b1eee50e5992068b5a72cd1309e3c3ba7ca55dbe604ecdba087caf049663acb264bf3df1aa082cc700cbabadb264e7d4bcc66e99c19c9ad7023ffbca7652908f75fe5acbbec5c12697cbebc8e7593dab67f5ac9ed5b37a56cfea593dab67f5ac9ed5b37a56cfea593dab67f5ac338555b1ac333893b0dd3a017cac7bb54396a4c1a2965cd7d9c3f8b02e8fb5cb17dc93581f81ef5860d964972578ffc386082c9b8065a35d96e09ec466bb6506f724b6003ffbca7652908f75bec5b26f71b0c9e5f23af279d6fd9b55b1ac373893b0dd7a017cacdbe8902569b0a82557bf14c68775799c5dbea00f1f8ac0771cb01c6f95257b5ff97511588e0796ad5659b27df8ebed9619f4e1db809f7d653b29c8c73adf66d9b738d8e472797d9b3bbb81ffdb27f17f7b08c7f63cfa8f7c53653d6c06b1fab8fab8fab8fab8fab8fab8fab8fab8fab8fab8fab8fab8fab8fab8fab8fab8fab8fab8fab84e9d55b10c199c49d86e48001febb63a64491a2c6ac975ed3e8c0fdbdd0ebb7cc17d8ee1087c3b8065a75596ec3ba94f88c0b21358de6095257b9f6397dd3283ebfc23c0cfbeb29d14e4639d8f58f62d0e36b95c5e47be6722ebb619c4eadb801b56df063cab6f039ed5b701cfeadb8067f56dc0b3fa36e0597d1bf0acbe0d7856df063cab6f039ed5b701cfeadb8067f56dc0b3fa36e059a7bb0d289661833309db0d0be063dd1b1cb2240d16b5e49a2712c687edee44bb7cc19c9ad1087c2702cb290e584e8ac0720ab09c6c97259853f346bb65f6ab32de04fcec2bdb49413ed6f99b2cfb16079b5c2eaf23df4c61552ca3066712b61b15c0c7ba931db2240d16b5e43a7ec2f8b02e4fb3cb171cdfa746e03b0d584e77c0f2e6082ca703cb5becb2047dcd5bed9619f4356f037ef695eda4201febfc6d967d8b834d2e97d7916fa6b02a96530dce246c77aa003ed6bdc5214bd260514baee3278c0febf20c077c6f8fc07706f0bd3d84ef4c077cef88c07726f0f17ea5c0f72e077cef8cc0f72ee07b27a499ef3d0ef8de1d81ef3dc0c4fb9501dffb1cf0bd3702dffb808ff72b07be0f38e07b7f04be0f001fef57017c1f72c0f7c1087c1f023edeaf12f83ee280efc311f83e027cbc5f15f07dd401dfee087c1f053ede0ffbbf8f3be0fb5804be8f03dfc742f83ee980ef1311f83e097c9f08e13bcb01dfa722f09d057c9f0ae13bdb01dfa723f09d0d7cbc1f5ec3faac03becf44e0fb2cb09c6397a53f052ce7809dcf3bf0f973b1a9fbccf653b01ff29de780efdc087ce701dfb9217c5f74c0f785087c5f043ede0fdbf4f90ef8be1481ef7ce0e3fdb04ff8b203be0b22f07d19f82e08e1fbaa03beaf44e0fb2af07d2584efeb0ef8be1681efebc0f7b510be6f3ae0fb4604be6f02df3742f8beed80ef5b11f8be0d7cdf0ae1fbae03beef44e0fb2ef07d2784ef42077cdf8bc07721f07d2f84ef070ef8be1f81ef07c0f7fd10be1f39e0fb6104be1f01df0f43f87ee280efc711f87e027c3f0ee1fb9903be9f46e0fb19f0fd3484ef170ef87e1e81ef17c0f7f310be5f3ae0bb2802df2f81efa210be5f39e0bb3802dfaf80efe210be4b1cf0fd3a02df25c0c7fbe1f8efb70ef87e1381efb7c0c7fb61fc2eb3cb17dc73b93402df65c072855d96e05b03bf8bc07205b05c6e9725b8fff37bbb6506f77fae047ef695eda4201febfc4acbbec5c12697cbebc8e759f76f56c572a9c19984ed2e15c0c7bacb1db2240d16b5e4ea97c2f8b02eafb6cb17f4e15745e0bb1a58fe60956569f00e9e6b22b0fc0158aeb5ca92edc3ff68b7cca00fbf0ef8d957b693827cacf3eb2cfb16079b5c2eaf23df5459b7cd20561f571f571f571f571f571f571f571f571f571f571f571f571f571f571f571f571f571f571f571f571f571f571f571f571f571f571f571f571f571f571f571f571f571f571f571f571f571f571f571f571fd7a9b32a96ab0cce246c7795003ed65deb902569b0a825d73ce7303e6c777fb2cb17cc09bf3e02df9f80e52f5659068239e17f8ec0f21760b9c12a4b764ef85fed9619cc09ff1bf0b3af6c2705f958e77fb3ec5b1c6c72b9bc8e7c5365dd3683587d5c7d5c7d5c7d5c7d5c7d5c7d5c7d5c7d5c7d5c7d5c7d5c7d5c7d5c7d5c7d5c7d5c7d5c7d5c9f9971552cd71b9c49d8ee7a017cacbbc1214bd260514baeebec617cd8eefe6e972fb827716304bebf03cbbfacb22c09ee49fc2302cbbf80e59f5659b2f724fe6db7cce09ec44dc0cfbeb29d14e4639ddf64d9b738d8e472791df99e89acdb6610ab6f036e587d1bf0acbe0d7856df063cab6f039ed5b701cfeadb8067f56dc0b3fa36e0597d1bf0acbe0d7856df063cab6f039ed5b701cfeadb80679dee36a0586e343893b0dd8d02f858f74f872c4983452db9e68984f161bbfb8f5dbe604ecdcd11f8fe032cb7da6509bedff7df082cb702cb2d7659823935ffb35b6630a7e636e0675fd94e0af2b1ce6fb3ec5b1c6c72b9bc8e7c9e75ff66552c371b9c49d8ee66017cacbbc5214bd260514bae7e298c0febf20ebb7c411f7e7b04be3b80e56ebb2c411f7e670496bb81e52ebb2c411f7e8fdd32833efc5ee0675fd94e0af2b1ceefb5ec5b1c6c72b9bc8e7c9e75ff66552cb71b9c49d8ee76017cacbbcb214bd260514bae7e298c0febf27ebb7c411f7e5f04befb81658f03960722b0ec019607edb2047df84376cb0cfaf087819f7d653b29c8c73a7fd8b26f71b0c9e5f23af2cd1456c5729fc19984edee13c0c7ba071db2240d16b5e43a7ec2f8b02e1f75c0f74804be4781ef9110bec71cf0ed8dc0f718f0ed0de17bc201dfe311f89e00bec743f878679b7c4fc6a6cec79929d80ff90a1cf0c5e353e72b003ede0ff90a1df02522f015025f2284afd8015f5104be62e02b0ae14bdae50bc60fb322f0b17dc552623956aacc52bb65f6ab32539663a6ca28832071fc4aa1ee383f05f12ab31caf38d8e472791df9a6ca5a1f9b5e56077607ca62d971cd2cc316df7341a6729d2e043d1f7b6ab542eb8af43a6f7f8ede2601dbac4bea32758c79e98218573888312e8390ae80fae6a54910cb6e412cad82584605b1ac17c4b24610cb0a412c6d8258ea05b1f40a62490862e910c4d22d8825238865b6209661412ca571392ced82e2b24e10cb4a412c4b04b1340862e913c4325f104bb320964e412c438258d60a62592d88a55110cb2a412ccb04b12c16c4d22388253ecd2cc9d8c46be049c82f85ed0a8c7dd5b5c10f568fe557697d019453ad758990b2aba0ec4a9dae8e4fdc176354e52046686710d6d956293054c7a79fa54710cb62412ccb04b1ac12c4d2288865b52096b582588604b1740a626916c4325f104b9f209606412c4b04b1ac14c4b24e104bbb20161edf4a6019161497d98258328258ba05b1740862490862e915c4522f88a54d10cb0a412c6b04b1ac17c4322a88a55510cb6e412c4d8258baf2c4c2d70ab9dc4a83653aedd6d8b51b3c13540b76f9da690dc49dedd70247bd5d8e60ee3472a8c5581d777db91e58ea2cd7852ab3c1729c55998d9663a6ca68822071fc983d05f98d10af26076db7c168bbbc8e7c5365c5b96ed3c1eaaafe9b2dd73fcfc7e658361b31c578b7e87421e86bc0dfd996fd55c5b5ea32798e3733b0ad046cf38ad2ecff726dbf15d8b9ffc1791e1d96db858a635b84fea70362d7eea0ff493be8e73bc121f6350dc71fe7b7826f9d0e8ebfb471fcf17a27b0f08273fcd30e58701984743a84a54910cb6e412cd85ea69d45505c4605b1ac17c4d224a8bdac111497158258da04b1d40b62e915c49210c4d22188a55b104b4610cb6c412cc38258da05b1ac13c4b25210cb12412c7d8258e60b626916c4d22988654810cb5a412cab05b1340a6259258865992096c582587a04b1c4a799655f73fc39bf09b6e36b7c6da09ba3d3eda02b08b1c1bf773a4157a8755c86bad67c56f5c4b231462eae69a29d4158675b38c77f4e7cfa597a04b12c16c4b24c10cb2a412c8d8258560b62592b886548104ba7209666412cf305b1f4096259228865a52096758258da05b10c0b62992d88252388a55b104b8720968420965e412cf58258da04b1ac10c4b246100b5f5790c0b25e505c4605b1b44a6211d45e760b8a4b932096ae1016cbd73407f03a242fc66ae8754ec5d26d9725f86e545704966e60996bb98e5499f3ec9619cc7b9e6fb94c55c6020812c78fd953903f1fe2b5c0419b9e171f5f4fbc8e7c9e75ff66c5b9d5cc9984ed3a05f0b16e2eb0d8ee0b94cf3c1ee0b2d5fd9b8da9319b96e7f52f55c561ff3d080c6c2b01db9c5935c635a4b94a211fef3db587d45f9b83fa6b37ea8fd7dbe0bcc9beb4034bbb03960e8345925ddbb1c73ae6c5581d776e6e0316dbcfd3a4806536d86971e0736b049f5b80c5f2f34d9914b034831dcbcf8405fd125fc36b827ee9d494b3fa0cfaa536a35f62067c3e8ab719867ee974e897da428e8356cbf18903935ad260b715fac3d690f669fb993765a72542fbc47663f799be4cd06e9a22b034008be56741330e9ec9ed77f06ced84e78d9f7ae613c6229c5f07f1aa75704ea937ce29bc8e7c9ed5b37a56cfea593dab67f5ac9e75ff66552cfcfb03e735f3762d02f85887ef7cb1fddb20785f466c7c3da9dfa6d7c16f66bbbfd333c1ef50fc3d970606b695806d8ea81ce3ba017e33371b7595845861fd3538a8bf46a3fe789d6de17d6dacbf46072c4d06cb33d7eec026fbc740a69fdfbfd36cf457669d2adb96dfdb155ca7ab8338aac5581d77eda1c661bfa4caacb6dcbfa832aac021f6b51afa39cec7f703b87c371d976bbe9b0e9fc52900966a072cb80c42ba3a8465b7209651412cad8258d60b62592388252388a55e104bb720964a412c49412c09412c3806996e96d982e2d22e886589209606412cf305b15409626916c4522a88a55010cb902096b58258960962e911c4522d88252588a548104baba0b15493a0b8ac10c4d22688a557104b8d20960e412c6582588a05b10c0b6259278865a520963e412cb582583a05b1940b6299258865b5209646412cab04b12c16c4522788a54b104b85209612412c2d8258e2d3ccb2aff733727e1d6c677eeb0bdfc588e5f13d0ede5efd4e3fb77a62d905b1896563399cc618553a8811da198475b685ef67ac8e4f3f4b8b209612412c158258ba04b1d40962592c886595209646412cab05b1cc12c4522e88a553104bad20963e412c2b05b1ac13c4322c88a558104b9920960e412c3582587a05b1b40962592188a549100bdf1796c05224282e29412cd582587a04b12c13c4b25610cb90209642412ca582589a05b1540962992f88a54110cb12412ced8258660b62691034964a088a4b52104ba520966e412cf58258328258d60862592f88a55510cba82096dd8258cc7bdf98afc6e57cbee27bdb09c83f54df4c50fff6f58cb80bbf70198c4de4ded733e2d3cd322a88a55510cb7a412c6b04b16404b1740b62a914c49214c49210c4d2e0f8dc168565b6a0b8b40b62592288a54110cb7c412c5582589a05b1940a622914c432248865ad20966582587a04b1540b624909622912c4d22a682cd524282e2b04b1b40962e915c4522388a543104b99209662412cc38258d60962592988a54f104bad20964e412ce58258660962592d88a55110cb2a412c8b05b1d40962e912c4522188a544104b8b2096f834b3ecebb96cce4f80ae42ebf07b7be53add00ba82101b5c4e05e8f89a1c97a17ecf5f503d91a10018ca42b8787fb4c776cae213f7751d73b43308eb784f9f19ca1c5f37980a4b8b209612412c158258ba04b1d40962592c886595209646412cab05b1cc12c4522e88a553104bad20963e412c2b05b1ac13c4322c88a558104b9920960e412c3582587a05b1b40962592188a549100bfff696c05224282e29412cd582587a04b12c13c4b25610cb90209642412ca582589a05b1540962992f88a54110cb12412ced8258660b62691034964a088a4b52104ba520966e412c19412c6b04b1ac17c4d22a88655410cb6e412c05064b29e4f335abe0f7a5d67582ae54ebba4097d4ba6ed09568dd7cd0cdd2ba1ed0156b5d2fe88ab4ae0f74855ab7187409adcb80ae40eb96802eae75cb40c737a35780ee499d5e09ba27747a15e81ed7e9d5a07b4ca7b95f50e793470d9daaf347747a50ffef7f7a4b50e76c87cbe5f547806faf4e3f0a3a4eaf05e6870d9d627ec801f3c30633af3f047cccff30e838bd0e98f7183ac5fca003e63d0633af3f087cccbf07749c5e0fcc0f183ac57cbf03e6070c665ebf1ff898ff01d0717a0898ef33748af95e07ccf719ccbc7e2ff031ff7da0e3f43030df63e814f3dd0e98ef319879fd6ee063fe7b40c7e95160becbd029e63b1d30df6530f3fa9dc0c7fc77818ed3bb1df3ed35f8f61a7caeec3e62d87d244f761f32ec3e9427bb0f1a761fcc93ddfb0dbbf7e7c9eebd86dd7bf364f76ec3eedd79b2bbbfb5e7e9ea37f6b7f63c5dfdc6741d47fe7c941fbbfe7c941fbb53398e6eb76b772009e5f31237d607217d3bb0dc613906aaccdbec96d9afcafc9fe5325519b7424c387ecc9e82fcff41bc6eb51caf38d8e472791df9660a6b1274f81c05e7a740778bd69582eebf5c0ee8fea37525a0bb59eb6681ee26f61d74ffd6ba22d0fd4beb1e07e67feaf463a0fb874eef05dddf75fa51d0dda8d38f80ee6f3afd30e8feaad30f81ee2f3abd077437e8f483a0fbb34e3f00ba3fe9f4fda0bb5ea7ef03dd753a7d2fe8fea8d3f780ee0f3a7d37e8aed5e93b41778d4edf01baab75fa7fa0bb4aa70b21f6576a5d0274bfd7ba02d05da17571d05d1e63e598ee775af524d8bd4ca79f00dda53a7d17e8f89aed6da0e3fb67d89e792ec32da0e3394bff051dcfd9fc0fe8789ef8cda0e367536e021ddf2bfb37e878dec2bf40c7f393fe093a9e9ff90fd095ebf4df41c7cfa1dc083abe2ff637d0f1bc80bf828ee722fd05743cfff106d0f19ceb3f838e9ff3f813e8f8fdc2d7838eefbb5f073a7efee08fa0e379747f001dcfabb81674fc3cd235a0e37b2957838eef255f053a9ec37825e8f8fef7ef41c7d7f6af005d5aa72f075da74eff0e747374fa32d0f13d006eafaafda876d564f9be4610336390906bccd004f7581aedb2f4a780a511ecd45bb533d0af8ae37bf5ead84fc7c6eab91eecd6d9b19be1842aae16caef040eb695806d5ea30fca72bd7dade578c7c16e918e03f3d4020f6fb341f3a87eb0b0ca593b08c6b14d11da64a3fd3a6394fe24c4642a2c75ceda6d764c6dbb0da8326b2c97a9caa88620996d2a05f93510af6a07f76c6be3e3eb89d791cfb37a56cfea593dab67f5ac9ed5b37a56cfea593dab67f5ac9ed5b37a56cfea59670aab62e1ebe7f81e45deae49001febf07e8bed6bdb78af8ecb56f72eeeab1cb3d960d566f6de12de8f480303db4ac03607548d71edd15ca590df04f5571f527f750eeaafdea83f5e675bf8ac2bd65fbd03960683e5996b776093fd6360a05fcd0908ee9f1bfd9559a778efb50e8e15becf17bcb746eb5ba04d3618bae9aa0f3c661a42da69b3b37e26e3e21e5af0d85d35d443a3716e49407e25f421d555637563f6172a3f1d92cf8bb13aeebe26deb7b6fc6c65d0675640f9836003ed963b68576c37ae856d54409c39ddc11362603b8cefb8776ac4276e87e97a639f14e45739f6b9d238962a0d56d54eeaa04da5e15ebfedf30dfa8b71e1f91829c8c7ef4bd638880b8eefd2c0500375d462f43b38aec23eb02a8fe3aaaa9071551df0b11fd89f5c971a6375390632e7d734d83f4f8c9b5f83f32f3a81a31e8e67de6640b76b35bfc61cf7a4615ffc7e673ee60499e7669c13b40298f33527a8dee88b3186aba09f381ec60a61e3d73a43e7803f83fc5c6e9dc18fe3026c2fb67f0b61dfc08bb13aee1c5bedb07f73d02705f37cf05c12d61f713ecff374307e08eabccaa8f32ae3fc1a33b6a90d193fd6859c7f2b2cf78d2ecee9aa8c32e8db1b8cf37902f20f87e3f508180bb2cf8d50ce86907c5e72b5631ccb953ae89f92c658b1343ed16e8983369634c68a4f3d870071e6f4b130562c8160717c4ba1ddf1b310b85d4948bb2d85e38af3538e7d2e358eab528355b593a3a04d6d80b1a2ede31cfdc5b8f0f93905f9f8bbb6c938aef1db1d782e7231d62e33e257661c9ba9d8c4f7c8e0b9d4fef82ccbd5688c75cc6b5478ed0cfb04bc7636dbc1f9b22d423f331beaaed92ecb52555c0b1cef836003bf2ddfe8a06e9a0cbb2d46df9c823835858c69da216838164a87ec638e35719c86e7463e5fb642793c4629884d1c63a925ec7a39fe9662ce6ab05365d8b17e0ece8c1fb71480cd0a606b358e49c551e6a0aecb8dfec17c9f213e9782bfcf4f4d8dc58cebb5138f5d43a7f8db1cf0b71bfcbcde0675da61b4cb6288af1d96ec3cfd287d07b62fdbfd982ab3c56e99fdf6fbb8951b5519f89c05c7af25e4dc89d74f9b1cb4a316a31db5849c9fedda1dd8acca689cc4ffc6108ec63cfadfe8cceef24daa8c8649fc6f08e168c8a3ff0dceec2e1f5265d44fe27f7d08477d1efd7777bf6945701eac9bc4ffba108eba3cfa5fb78fb14c2ed6866966b56f7769d057d74ee27f6d08476d1efdaf756637b35c95513389ff35211c3579f4bfc699dd0383d766564fe27fbee69feccbff6a6776335b54195593f85f15c2519547ffab9cd95d19b4ffca49fcaf0ce1a8cca3ff95ceecaedca0caa898c4ff8a108e8a3cfa5fe1cceec6e0f5bae593f85f1ec2519e47ffcb9dd9dd70a02aa36c12ffcb4238caf2e87f9933bb0341fda726f13f15c291caa3ff2967763707e39fd249fc2f0de128cda3ffa5eeec06e7bfe424fe2743389279f43fe9cceee6e0b75ac924fe97847094e4d1ff1267763706f53f6b12ff678570cccaa3ffb39cd91d0aaed5144fe27f710847711efd2f7666777950ff4593f85f14c2519447ff8b9cd95d129cff0a27f1bf3084a3308ffe173ab3bb65a92a233189ff89108e441efd4f38b3bb2138fe0b26f1bf2084a3208ffe1738b33b10fcfe8f4fe27f3c84239e47ffe3ceec2edf983590dbff5808472c8ffec7dcd90dfc7f3296dbffa7f281e3c958fefc675bf6ed6e0efaff2762b9fde77ce478228ffe3fe1ce6ed0ff3d1ecbed3fe723c7e379f4ff7167765706fddf63b1dcfe733e723c9647ff1f736677e30655c6de49fce77ce4d89b47fff73ab3bb3418ff3e3a89ff9c8f1c8fe6d17fe46b093957f13b47836f681afc8ad5f2f7ab96aa321fb6ecbf2a83df5bace6a1f0dc0eb69380fce5d563db1da8d349f099e7ada8fcd521f9bc18abe3e67170cc94af96bf4b15cc357d00ca1f041b68d7e5b7a5e25ad806eb13903e985fca1b1bff9e7d8e2f7e436b4fc876986e31f64941fe1ec73eefebdb046c4bb59355d0a6b8cdb838ced15f8c4b2bc485f3715e93ede34d156d7e2b82191e06964ea33f51c711b7d14ee0dbe3806f5fdfd0d8037cacc3f7a5b31fd89f6c84796d9d5a8fdf4d4cebf41c28a7dbd0293fe739f093ed70b9bc3e0ff8f8bdcdddf9e51b982adf5c834fb12cb01cab24d8e425571fbe0058e65b665165f6d82d339837b310f8d957b693827c7cee65a165dfe26093cbe575e4eb0961e5f7a9e377887b80d572cc02d6b906ebdc10bb7d798811f72b7d8eedf61a76bb0cbbea38c17a524baee3a417780fb0ccabca5c64b7cc60fcb4189807c14617e8fb1dc47e31c4330e36589f80f469307eea1f4b3e75ae616675acf4856c87e985c63e29c8ef73ecf322e0188475b6a5ceaf3b61cc64b9cd07f5dd6730f07a17e81785c4ab0fe2c5f9dda0e33100f6b1738c3292500e9e6b2db7eb4c2e3fb1de59d70b7c8b42f816dbe51bc8d51616031feb0e001617fda0c9c2e71eec071b8df805cf920157c23257b12ed76699f88d185e72f5e7c5f0bfc8328bfab68c1e4ac7768decd8b9e1b82d476cd9b019af54171a88058086690e3d7ec6a61074fc199b22d0f1feb340c7e594409e0bd7d1372eb7d0602c06365b76f17340bce4aafe59c062bb29aa18f3e78074f51fbd73ebc816acff228331acfe555ec2d88eebadd03233dae272799d6d29bff89346276cd8f4fae7ed3c6e74fb96e1915d1868b361c70d87d48295858d99b72b329cb5dc58964a6b2cfc0da74d1bb66d3b7c74e3b6ad9b0e1e1dde34b275c7705893e1886175857519b8edbeba8a2478c63a2eab0474d80d7144cc88590b493d94c9cd27a19d298a8d358992d8d827ba822988b16cfb549fd8524fcaaaa778d5884b85577d12ab0ecafdb9feaf6ca94f5ea953a0faa495fa8495fa6495ba3ca21efd53af4e518fe2aa21503a96fdf9a2860f6a48a57e42a96192fa59a77e3aab9fd26a88a486446ad8a14eedea34ab4eabead4af8682eadad600c9121235af40dd5b53f38b5790a87996ea5ecbb34856911c44f26c92e7903c57c7f77924cf277901c90b495e447230c96a924348d6901c4af262929790bc94e4652487911c4eb296e4089223495e4e7214c92b488e265947f24a925791bc9ae41892d790bc96643dc9b1b1ecb040dd0f55cf2fa9fb62eadec010c97124af23d94a723cc9eb49b6916c8f653f51bf83e404923790ec24d9453212cb7e0afe449293484e263985e48d246f223995e434923793bc85e47492b792bc8de4ed246790bc83e44c927792bc8be4dd24ef21792fc9fb48de4ff201920f927c88e4c3241f89653ff1fe51928f917c9ce413249f24f914c959249f26399be433249f253987e473249f273997e43c922f907c91e44b24e7935c40f26592af907c95e46b245f27f906c93749be45f26d92ef907c97e47b2417927c9fe407243f24f911c98f497e42f253929fc5b26df517241791fc92e462925f91fc9ae41292df90fc3696fd5cd965b1ece7cd2e8f653f87a63e93a63e9fa63eaba63eb7a63ec3a63ecfc607321ec43c758f7fa26f1819d9b2fd8491f4c88ef4f6d16d235b4fd8764afaa4ad23af4bef3871cbcea16d3b4ec29dfffd7476be45afcc9eb8f386cd9bf7bddf237a853f78b76678f39693d33b4647d23b86d21b778c0e6f1e776e3a5a7b7d905e3f327b724eefdab66324dd9f1ea6bfd403ef3869cbe64569ccdb452eec1a49ef1ad9b073243db473c7f6746611967b54a99b728fa97c1a31cd543d8d9d97574dbd42fe0f6b585bbcfe160300", + "isInternal": false + }, + { + "selector": { + "value": 2603445359 + }, + "bytecode": "0x1f8b08000000000000ffed9d077815c7b5c7efd54582ab2b01a6096424ad2aa24b028c316084e9cd0df70a06516c40188471efbdf7de7bc1bdf7827b27cf7e71123f3bcdce4b9ec94b5e122771e2e49dd93b07fd19966bedc70cda0b67bfefcfee9edd9df39b3367677767579775b1582c1e4b4f09528fd8a6136f6fd4f3ba2d9bea2d9655e792339e259c3959c299c812ce0e59c2999b259c7959c2d9314b383b650967324b38f3b3843395259c0559c25998259c9db384b34b967076cd12ce1db284b35b967076b7c8590c9cfcccd453cf7be979919ef7d6f33e7acec7eea8e77d755d3be8f5125229a98ce4e96d1c98725205a9925445aa26d590fa916a49fd4903480349834883494348437519eae1ab81348c349c3482b41369246967d228d22ea4d1a431a4b1a45d49e374ecc69376234d204d244d224d264d214d254d234d27cd20cd24cd22ed4eda83b4a7ae8ba7ebb217696fd26cd23ea47d49fb91f6271d403a907410e960d221a4434987910e27cd21cd251d419a479a4f6a222d202d242d222d261d493a8ab484b494b48cd44c5a6ec4fc68d20ad24a528bc1b98a740c6935e958d271a4e34927904e249d443a99740ae954d269a4d3496790ce249d453a9b740ee95cd279a4f34917902e245d44ba987409e952d265a4cb495790ae8c6ddcfe5791ae265d43ba96741de97ad20da41b4937916e26dd42ba95741be976d21da43b497791ee26dd43ba97741fe97ed203a435a407490f911e263d427a94f418e971d213a427494f919e263d437a96f41ce979d20ba417492f915e26bd427a95f41a692de9755d971c5d9737486f1ab6b7486febe577f4fc5d3d7f4fcfdfd7f30ff4fc433dff48cf3fd6f375aabcc2f4b2ba1735c736948dcfd938d8f8fccd011b9fcb09b0f179dd016c7c8ee7828dcff73cb0f1b9df116c7df57227b095c032cf4bf5723ed8caf4720a6c9e5e2e005bb95e2e045b855eee0cb64abddc056c557ab92bd8aaf5f20e60abd1cbddc0d64f2f77d7738e859a1af5bc6e0b2755a6e56b469d62e73ce801f5e13ce80936ce835e60e33c28021bd7bd37d8380ffa808df3a0186c9c073b828df3a02fd8380f307f380f4ac1c679500636ce030f6c9c07e560e33ca8001be74125d8380faac0c679500d368e6f0dd838be9c3f2a9e93613b4f78aee27826db783b9eab0928936dbc1dcf55de8ee72a6fc77315b7f39cb7e3b9cadbf15ce5ed785ef2763c07b90df17ce363ba838ddb10f393cbc15ce436c4bce3b231c7b80d31c7d81fe618b721e61833608ef139e2818db930c7f81cc11c6356ce3155d73ce06dd4f3ba2d9beab1efe6296eac37c232fb572c7dedb2d4a580a52ff829b5eb67185e73da52e75260f12cd719af6b6d61f180a5dc2e8b3f865961b74cbf4d39d6786eb09f12a84fa5e5fac4c10f97cbebec2b05360ff82a03f8aaecf235c4c10f97cbeb55c0c7b67277b1aac3fe8acb56d789c3c1a7e5bea74ed59fcf731ee76706f695807d9614b672cdd3b67cd85e0c363e1efb6dcfb0b93887f0998acbe5f572e0e33ec5dbba7c0d6de52b33f81cf47d7eacca0c16f16bbdcdebb07c75eef0f9c27e12b0fdbb78eb7eab81a9d82e5343d86b303e5fdabd46d5d7e1b9d716960a6071710db6ddb73bb876f939550d31f1f41cafabbcbd0ae2556d395e99aea1c827acc22aacc22aacc22aacc22aacdb372b8e4b797abeb9719ff6e2db5ae33e3c268de33e6f834fcfaacf7a7fdca7d828bfc8688b04ecf353e0e2f7a238ee83633cc586cdc1f8713df273b9bc8eef2d790c0aeb6af939da67d9d160d976fd36cc536596592db3be4ebd6fc277d365463df03d4f1fc3a672f2fb786b7d3dab6ce1dfcf78b15616dbe348f87ec65d9fd45087e3f639b18dfb874af06b694ca39e1794df6a237eccc1be12b0cf577a5e6897670357a6f14287d74f3f0e55e0d78bb59e1bf82edbbc96abe36a80cb83f85543fc789ff57aaecea14be11cb23de61d767c0f73db6e9b36d4e1bbafb6b0603bdb7e3fe6227754993596cb5465f4839898399582ed987ffd2cc70bfb072e97d7914f5885555885555885555885555885555885555885555885555885555885355b58158b6770e2f8bf17013eb6e1fb21db63dbf8b75a5cb67a777132bcbba8b0ea33fd2e0cdf4778b14dff5e2c01fb3c03df6f9fae97f361bba7e7f8ad8307e5bb7887f5437fe3921fdbf4ef725cbcefc0f78a5ceeb6eb37fdaedcee39d0b0e15d39e79379dee1df2d151b367ccfa7dadcd376fc7ea3c2b0b5577be0395301365ec66f1cecc6b8dec53b34fff708fb413b70ddd84f02b6df007dc84df1d6b631fb0bb57d4dc0769ee2c67a232ce37bf6fe76ebeaf79903a0fc46f0817e07daf55b8f7ee35aec83ed09587e000234b07571437c9959e55d6dc07eb85c691c9382edb58eebdc1f381a619d7da93cb915726a0d5c2f6d5f6fb0be189722880b6faf80b8d83edf5415f1fece03861a60293138f1be0afbc05a077c9bbbafaa053ef31b0ffc5e10fb13fc5ed0e53d508e114b6c434bd7898dbe07c2ef2fca81837d25609f97745eabef81ccfb1e0f8eed0d656e8d6f98cc6b337ec3b41698e51ba6e06f98cc6f73f11ba6f7a05ffb16ee6d2ac02ffbaa326c0eea5d6fd6bb11d6d917dedb60bdab36536ffc2d0617fd2473e51a1c786fc2fb7c0af9aa26ec63f9fb53bc87339f15eddf6f34f8f76b03ac9699be8ee1f593db0bef2378fbe790835fc0fd18d7b902ca591fb09da74cf76bfd217e83edd6d53f578740f98de003fd0eb5ebb71efdf2fd1afb607b0296bf81000d6d5ddc105f6656793728603f5cee671c9382ed831cd779307034c23afb5279f20bc8a9f570bf66fb3e04eb8b71e90d71e1ed3836e619fbab7ce6f3c183f8d93e2ff19e9ecb35eff183eebdf1fa60ff1e29cd550e5c5e2cf87785d8560e5c387e65f9ef04eaf09bf962f089dfcc47e5378d70cc2227d6ca57e280af6f08be12e0e3e312c067f7ef21d27ca521f8f077f9f0ef2298afdc019f1782af1cf8f8b85ce0b33dbe14f61befa0df22ca83b9ed7b44bc176d0b1ff6bb7c5c47e0b33de68fbfafd8163e7c0fc0c775023ecbe34b3e5f6d083e1c93e1e392c0677b4c46953d2004df40e0e3e3f281cff2fd9ecf3728041fde230d8265e6b37d8fa4f88684e01b0a4c7c5c01f0d53be0ab8bb59daf1ef8f8b842e01be680af2104df30e0e3e33a03df08077cc343f08d003e3eae0bf08d74c0b75308be91c0c7c77505be510ef8760ec1370af8f8b81d806fb403be5d42f08d063e3eae1bf08d75c0372604df58e0e3e3f03786c739e0db3504df38e0e3e37a00df78077c8d21f8c6031fdbf1fa3bc101df6e21f826001f1fd707f826d9e5f3ff1e726208be49c032d52ecb70c5323904cb5460996297c5ff7bc86976cbf4c706a75b2e5395310362c2f163f6146c9f0ef19a61395e71f0c9e5f23af209ebf6cdaa58261a9c49d86f6204f8d836c5214bd2605153a6be2e880fdb72965d3effba303304df2c60d9d32acb307fdc70f7102c7b02cb1e5659d2d785bdec96e9f7e17b033fd795fda4603bb6f9de96eb16079f5c2eaf239fb00aabb00aabb00aabb00aabb00aabb00aabb00aabb00aabb00aabb00aabb00aabb00aabb00aabb00aabb00aabb0da67552c330dce24ec3733027c6cdbc3214bd2605153a6ef4482f8b02df7b1cbe77f53333b04df3ec0b2bf5596f4ff3db16f0896fd81653fab2ce96f6a0eb05ba6ff4dcd81c0cf75653f29d88e6d7ea0e5bac5c12797cbebc827acc22aacc22aacc22aacc22aacc22aacc22aacc22aacc22aacc22aacc29a2dac8a65b6c19984fd6647808f6dfb3964491a2c6aca34ce1ec4876d79b05d3eff9dc44121f80e0696c3ecb2f8bfff70480896c380e550bb2cfe3b89c3ed96e9bf939803fc5c57f69382edd8e6732cd72d0e3eb95c5e9f037661ddbe5915cb41066712f63b28027c6c3bd4214bd2605153a67e694e001fb6e51176f9fc3e7c6e08be2380a5c92a4bfabdf2bc102c4dc032df2a4bba0f5f60b74cbf0f5f08fc5c57f69382edd8e60b2dd72d0e3eb95c5e5fe8ceaf5fff453f50ff45011c8bb662fd91afadacd3b38855e22a7195b84a5c25ae125789abc455e22a7195b84a5c25ae125789abc455e22a7195b8b69d55b1cc353893b0dfdc08f0b16dbe4396a4c1a2a64c63f7417c987747dae5f3df732c0ec17724b02cb5ca92fe4deaa342b02c0596255659d2ef3996d92dd31fe76f067eae2bfb49c1766cf366cb758b834f2e97d7916f5b645d9845ac92036e5825078455724058250784557240582507845572405825078455724058250784557240582507845572405825078455724058db3b0714cb62833309fb2d8e001fdb963864491a2c6acaf49d48101fe6ddd176f9fc6f6a9687e03b1a585a1cb0ac08c1d2022c2bedb2f8dfd4acb25ba6ff4dcd31c0cf75653f29d88e6d7e8ce5bac5c12797cbebc8972dac8a65b9c19984fd9647808f6d2b1db2240d1635653a7f82f8b02d8fb5cbe79fdfab43f01d0b2c273860392e04cb09c072bc5d16bfaf39d16e997e5f7312f0735dd94f0ab6639b9f64b96e71f0c9e5f23af2650bab62596d702661bfd511e063dbf10e5992068b9a329d3f417cd896a738e03b3904df29c0777200df690ef84e0dc1771af0f171f9c0778603bed343f09d017ca7c332f39de580efcc107c6701131f57007ce738e03b3b04df39c0c7c71502df790ef8ce0dc1771ef0f1719d81ef02077ce787e0bb00f8f8b82ec0779103be0b43f05d047c7c5c57e0bbc401dfc521f82e013e3e0efbbfcb1cf05d1a82ef32e0bb3480ef0a077c9787e0bb02f82e0fe0bbca01df9521f8ae02be2b03f8ae71c0777508be6b808f8fc331aceb1cf05d1b82ef3a60b9de2e4b5d0a58ae073f373aa8f30db1b6d799fda7e038e4bbd901df4d21f86e06be9b02f86e75c0774b08be5b818f8fc39cbedd01df6d21f86e073e3e0efb843b1df0dd1182ef4ee0bb2380ef6e077c7785e0bb1bf8ee0ae0bbd701df3d21f8ee05be7b02f8ee77c0775f08befb81efbe00be350ef81e08c1b706f81e08e07bc801df8321f81e02be0703f81e71c0f77008be4780efe100bec71cf03d1a82ef31e07b3480ef09077c8f87e07b02f81e0fe07bca01df9321f89e02be2703f89e71c0f77408be6780efe900bee71cf03d1b82ef39e07b3680ef05077ccf87e07b01f89e0fe07bc901df8b21f85e02be1703f85e71c0f77208be57808f8fc3fbbfd71cf0bd1a82ef35e0e3e3307eafdbe5f3dfb9ac0dc1f73ab0bc6597c5ffbf06de08c1f216b0bc6997c57ffff3b6dd32fdf73fef003fd795fda4603bb6f93b96eb16079f5c2eaf239fb06edfac8a65adc19984fdd646808f6d6f3a64491a2c6acad42f05f1615bbe6797cfefc3df0dc1f71eb07c689565b8ff1b3cef8760f910583eb0ca92eec33fb25ba6df877f0cfc5c57f69382edd8e61f5bae5b1c7c72b9bc8e7c6d655d9845ac125789abc455e22a7195b84a5c25ae125789abc455e22a7195b84a5c25ae125789abc455e22a7195b84a5c25ae125789abc455e22a7195b84a5c25ae125789abc455e22a7195b84a5c25ae12d7b6b32a96770dce24ecf76e04f8d8f6814396a4c1a2a64cdf3907f161defdc82e9fff4df8ba107c3f02964fadb234f8df84ff4708964f81e513ab2ce96fc2ffd36e9975aa8c1f033fd795fda4603bb6f98f2dd72d0e3eb95c5e47beb6b22ecc225689abc455e22a7195b84a5c25ae125789abc455e22a7195b84a5c25ae125789abc455e2ba6dc655b1ac333893b0dfba08f0b1ed13872c4983454d99c6d983f830ef7e6297cf7f27f15908be9f00cbe7565986f9ef247e1a82e57360f9995596f43b89ffb25ba6ff4ee20be0e7bab29f146cc736ffc272dde2e093cbe575e4db1659176611abe4801b56c90161951c1056c90161951c1056c90161951c1056c90161951c1056c90161951c1056c90161951c1056c90161951c10d6f6ce01c5f299c19984fd3e8b001fdb7ee6902569b0a829d37722417c98773fb7cbe77f53f36508be9f03cbafecb2f8ff7fdf2f42b0fc0a587e6997c5ffa6e6d776cbf4bfa9f90af8b9aeec2705dbb1cdbfb25cb738f8e472791df98475fb66552c5f1a9c49d8efcb08f0b1ed970e5992068b9a32f54b417cd896bfb1cbe7f7e15f87e0fb0db0fcce2e8bdf87ff770896df01cb6fedb2f87df8ffd82dd3efc3bf017eae2bfb49c1766cf36f2cd72d0e3eb95c5e473e61ddbe5915cbd7066712f6fb3a027c6cfbad4396a4c1a2a64cfd52101fb6e5efedf2f97df8fa107cbf07963f3a60f9df102c7f04963fd865f1fbf0ffb35ba6df87ff09f8b9aeec2705dbb1cdff64b96e71f0c9e5f23af2650bab62596f702661bff511e063db1f1cb2240d1635653a7f82f8b02dffe280efcf21f8fe027c7f0ee0fbab03be6f43f0fd15f8be0de0fbbb03bebf85e0fb3bf0fd2d80ef1f0ef8be0bc1f70fe0fb2e80ef7b077cff0cc1f73df0fd3380efdf0ef8fe1582efdfc0f7af00be78dc3e9f0994898ffda76047e4eb6097cfbf7fc809c1c7fe154bc272ac5499b976cbac5365e6598e992aa3230489e3970b6dc7dbf3205e1d2dc72b0e3eb95c5e47beb6b2f68cb52fab03bf0d05b1f47d4d8ee18bdfb9205327bddc01ec7ceea9d524c74eaff3fed7eb7d12b0cfb0647a5ea863cc530ec438e920c63835c27212fb153df589104b518458ba4488a520422cc908b1e44588251121965e1162e91a2196c208b1e44788a56384583a4488a577845872e3d161e91ca1b8a422c4d229422cb9116289b7334b32b6e9f36c12b6e7c27e39c6b12a8ef716b66e4f697b0e9453a06d8980b2535076be5e2e886f7a2cc628e52046e8a711d6d9573e3014c4db9f2537422c9d22c4928a104be708b1e4c6a3c3d23b4271e91021968e1162c98f104b618458ba4688a5578458121162c98b104b32422c051162e9122196a208b1f489104bce5662e1fb7e2e37df60694fbf8576fdfadfea7406bffc1c54087167ff9d81a3b35d8e119b7bdfd2c5419c716a8465f6b5b9f72dedcd52142116ccc9f666e912a1b814448825192196bc08b12422c4d22b422c5d23c452182196fc08b1748c104b8708b1f48e104be708b1a422c4d229422cb9116289b733cbe6deb7f0767c4fd2d5781650b61d8ce7147c2f833eb89cae60e33e84cb50edf278e1a60cf8fea65b00171f8ffed84fb7f8a6c7ba8e7937e379b69bc187ef6fba39be976e0b4b6e84583a458825152196ce1162e91d21960e1162e9182196fc08b1144688a56b84587a458825112196bc08b12423c4521021962e1162e17bcf28b01445282e7d22c49213c0d2c32ecb707c66e0c958dde819a807b074b71c1755664fbb65fa7f1bd5cb7299aa8c220812c78fd953b0bd17c4abc8411ef58c6fdc4ebc8e7cc2ba7db32abfbdadfa4dff7f833d43f41bbd1dc64095d9c7c1395e0c15e2baf681f62d0e68df6207eddbc7685f5e473e61155661155661155661155661155661155661155661155661155661155661155661155661155661155661155661b5cfaafcee68d56f83ffcd05fa5593b1bac1570cfcbb88812ab3afdd32fd6f2e4aa0425cd7bed0be2501ed5be2a07dfb1aedcbebc827acc22aacc22aacc22aacc22aacc22aacc22aacc22aacc22aacc22aacc29a2dacca6fa95dbffedf17a35f3519ab1b8d59973a8c812ab3cc6e99fe98b50715e2ba9641fb7a01edeb3968df32a37d791df98475fb66557ecbadfa4dbf972a0b718e973b8c812ab3c2c1395e0915e2ba5640fb5606b46fa583f6ad30da97d72bddf9f5eb5ff503f5af0ae0a8da8af547beb6b2f6ca225689abc455e22a7195b84a5c25ae125789abc455e22a7195b84a5c25ae125789abc455e2da7656e5b7daaadff46f62a25f3519ab1b8d83573b8c812ab3c66e99fe38703fa810d7b506dab75f402ef673d0be3546fbf23af26d8bac9512d7ed9e55724058250784557240582507845572405825078455724058250784557240582507845572405825078455724058250784b5bd7340f9adb5ebb72169f85593b1bac1570cfcbb88812ab3bfdd32fd6f2e064085b8aefda17d0704e4e20007eddbdf685f5e47be6c61557e073ac8c5fe217271a0c318a8320739c8c5c15021aeeb2068dfc101ed3bd841fb0e32da97d7912f5b589360cb89b5da787b026c43b4ad03d8866a5b2ed8eaa04e6cabd7b68e606bd0b64e601b065c3c1fae6d7dc03642dbbac0fe3be9e5ee601ba9977b826d67bd5c04b651ec036cbbe8e562b08dd6cb7dc136462f97806dac5e2e03dbae7ad903db38bd5c01b646bd5c05b6f1c6b555d97633ae71ca36c1e8f7946da2d1ff28db2423ff946db25ece8fb5daa640ceb26daab615806d9ab615826dbab67506db0c6e37b0cdd4b6ae609b15c0c7f939086c9c9f98cf9c9f43c0c6f939146c9c9f7560e3fcac071be76703d8383f315f396ec3c1c6711b01368edb4e60e3b88d041bc76d67b071dc46818de3b60bd876d0b6d160eba66d63c0d65ddbc682ad87b6ed0ab69eda360e6cbdb4ad116c45da361e6cbdb56d37b0f1b93c016cc5da36116c3b6adb24b0f5d5b6c9602bd1b629602bd5b6a9602bd3b66960f3b46d3ad8cab56d06d82ab46d26d82ab56d16f46ff9c0c875ca87ba302bfa665b1ef86ed4f3ba2d9bfc54663f5c2eaf97031fc7cbdbba7c0d6de52b33f8fcdf9bb0cb528f6dcf53dc586f84e54a60a9b0cce27f9b6db74cff3eaa1af8b9aeec2705db7b42ddaa2dd72d0e3eb95c5e47beaa0056ee77544e78c67e0e6296fe6d1b83b52cc06fed568811f72bb58efdd6187e4b0dbfea3cc1765253a6f3a40678fb59e6f59f7ded96e9df120c00e646f0510af68196eb827ee35aec83ed0958be9a6f1c603f35f1b58699d5b9521bb01f2e571bc7a4607bade33af7078e4658675fea9eee82c25606cb39efb777adc1c0eba560ef1f10af5a88176ff7c0c6f700d8c796186524a11cbcd65aceebfa4cf5c476675b0df0f50fe01b6097af21532e0c003eb6f5031617fda0c9c2d71eec077b19f153b97a3870252c73e5c55a9f5d6c9589cf4d3c65eacff3609e6b99453d6ff173d3ca96e615731736eddd34777e1cb03a1888398086cb387cc18f76387cc18f76387cc1c7e3500597d309b6a9657ea46a5abab865df654dcbe6ad386e794bd3fc99cd0b3178b906cde628912e0fbc35ea79dd964d0df8d0cb53a646ee042c1dedb2f8499cb45ba67f739b0ffc3800a5a6146ccf857df22dd72d0e3eb95c5ecf77e7b78eeb98a9fea9008ed456ac3f0e24250338793b9e7d3830c9f384519edadfcc656b15ea0165c635900250276b6eacf504e9146b1d0952606aa447ddb4a8911c3572a3466ad4c88cea36ba43992fe9b9f2a39ebad415458da4a891133552a2ee20d41d9dbafb57575f7547a29e40bc58faa9483d79aa27517587a1ee28d4555b5d19d5554a5d95d49553dd490d220d260d210d557121d5931a48c348c34923483b91469276268d22ed421a4d1a431a4bda95344ec7773c6937d204d244d224d264d214d254d234d274d20cd24cd22cd2eea43d487b92f622ed4d9a4dda87b42f693fd2fea4034807920e221d4c3a847428e9b058fa0a3b8734977404691e693ea989b480b490b488b4987424e928d212d252d23252336939e968d20ad24a520b6915e918d26ad2b1a4e348c7934e209d483a897432e914d2a9a4d348a793ce209d493a8b7436e91cd2b9a4f348e7932e205d48ba887431e912d2a5a4cb489793ae205d49ba8a7435e91ad2b5a4eb48d7936e20dd48ba897433e916d2ada4db48b793ee20dd49ba8b7437e91ed2bda4fb48f7931e20ad213d487a88f430e911d2a3a4c7488f939e203d497a8af434e919d2b3a4e748cf935e20bd184be7ebcba45748af925e23ad25bd4e7a83f426e92dd2dba47748ef92de23bd4ffa80f421e923d2c7a475b1d6ce184fe05feb393ff5cf6d69695ababcc56b69f696ae5ad2b278f992e3bcd58b5b1679cdc734ad58b0a479351efc627c0b0e7e8dc7f8373d78eefcf99b3fee13bdc267f6b465f39b8ef59a57b578cd0bbc239a572d9bbf12776fd0ddd918bd3e3b7dabe3ad5cd2dce2d579cbe8dfb94bc845d3fc211e6e5b495558d9e2ad6c99bba2c55bb0a279a9573fe4ff01285f3937f75f0200", + "isInternal": false + } + ], + "privateFunctions": [ + { + "selector": { + "value": 2432309179 + }, + "vkHash": "0x038021824fbd98bb0e388b0efe18f72e9350f7456481714539ba583de37113ce", + "isInternal": false + }, + { + "selector": { + "value": 283286945 + }, + "vkHash": "0x038021824fbd98bb0e388b0efe18f72e9350f7456481714539ba583de37113ce", + "isInternal": false + }, + { + "selector": { + "value": 332459554 + }, + "vkHash": "0x038021824fbd98bb0e388b0efe18f72e9350f7456481714539ba583de37113ce", + "isInternal": false + } + ], + "packedBytecode": "0x", + "id": "0x0710cd0d58fbc487f87fb17855d50ecdc46d3df58b724044f1a35eee815becf5" +}" +`; diff --git a/yarn-project/circuits.js/src/contract/artifact_hash.test.ts b/yarn-project/circuits.js/src/contract/artifact_hash.test.ts new file mode 100644 index 00000000000..e5584ba3904 --- /dev/null +++ b/yarn-project/circuits.js/src/contract/artifact_hash.test.ts @@ -0,0 +1,11 @@ +import { getSampleContractArtifact } from '../tests/fixtures.js'; +import { getArtifactHash } from './artifact_hash.js'; + +describe('ArtifactHash', () => { + it('calculates the artifact hash', () => { + const artifact = getSampleContractArtifact(); + expect(getArtifactHash(artifact).toString()).toMatchInlineSnapshot( + `"0x1cd31b12181cf7516720f4675ffea13c8c538dc4875232776adb8bbe8364ed5c"`, + ); + }); +}); diff --git a/yarn-project/circuits.js/src/contract/artifact_hash.ts b/yarn-project/circuits.js/src/contract/artifact_hash.ts new file mode 100644 index 00000000000..f1abc2ca1d2 --- /dev/null +++ b/yarn-project/circuits.js/src/contract/artifact_hash.ts @@ -0,0 +1,67 @@ +import { ContractArtifact, FunctionArtifact, FunctionSelector, FunctionType } from '@aztec/foundation/abi'; +import { sha256 } from '@aztec/foundation/crypto'; +import { Fr } from '@aztec/foundation/fields'; +import { numToUInt8 } from '@aztec/foundation/serialize'; + +import { MerkleTreeCalculator } from '../abis/merkle_tree_calculator.js'; + +const VERSION = 1; + +/** + * Returns the artifact hash of a given compiled contract artifact. + * + * ``` + * private_functions_artifact_leaves = artifact.private_functions.map fn => + * sha256(fn.selector, fn.metadata_hash, sha256(fn.bytecode)) + * private_functions_artifact_tree_root = merkleize(private_functions_artifact_leaves) + * + * unconstrained_functions_artifact_leaves = artifact.unconstrained_functions.map fn => + * sha256(fn.selector, fn.metadata_hash, sha256(fn.bytecode)) + * unconstrained_functions_artifact_tree_root = merkleize(unconstrained_functions_artifact_leaves) + * + * version = 1 + * artifact_hash = sha256( + * version, + * private_functions_artifact_tree_root, + * unconstrained_functions_artifact_tree_root, + * artifact_metadata, + * ) + * ``` + * @param artifact - Artifact to calculate the hash for. + */ +export function getArtifactHash(artifact: ContractArtifact): Fr { + const privateFunctionRoot = getFunctionRoot(artifact, FunctionType.SECRET); + const unconstrainedFunctionRoot = getFunctionRoot(artifact, FunctionType.OPEN); + const metadataHash = getArtifactMetadataHash(artifact); + const preimage = [numToUInt8(VERSION), privateFunctionRoot, unconstrainedFunctionRoot, metadataHash]; + return Fr.fromBufferReduce(sha256(Buffer.concat(preimage))); +} + +function getArtifactMetadataHash(artifact: ContractArtifact) { + const metadata = { name: artifact.name, events: artifact.events }; // TODO(@spalladino): Should we use the sorted event selectors instead? They'd need to be unique for that. + return sha256(Buffer.from(JSON.stringify(metadata), 'utf-8')); +} + +type FunctionArtifactWithSelector = FunctionArtifact & { selector: FunctionSelector }; + +function getFunctionRoot(artifact: ContractArtifact, fnType: FunctionType) { + const leaves = getFunctionLeaves(artifact, fnType); + const height = Math.ceil(Math.log2(leaves.length)); + const calculator = new MerkleTreeCalculator(height, Buffer.alloc(32), (l, r) => sha256(Buffer.concat([l, r]))); + return calculator.computeTreeRoot(leaves); +} + +function getFunctionLeaves(artifact: ContractArtifact, fnType: FunctionType) { + return artifact.functions + .filter(f => f.functionType === fnType) + .map(f => ({ ...f, selector: FunctionSelector.fromNameAndParameters(f.name, f.parameters) })) + .sort((a, b) => a.selector.value - b.selector.value) + .map(getFunctionArtifactHash); +} + +function getFunctionArtifactHash(fn: FunctionArtifactWithSelector): Buffer { + const bytecodeHash = sha256(Buffer.from(fn.bytecode, 'hex')); + const metadata = JSON.stringify(fn.returnTypes); + const metadataHash = sha256(Buffer.from(metadata, 'utf8')); + return sha256(Buffer.concat([numToUInt8(VERSION), fn.selector.toBuffer(), metadataHash, bytecodeHash])); +} diff --git a/yarn-project/circuits.js/src/contract/contract_address.test.ts b/yarn-project/circuits.js/src/contract/contract_address.test.ts new file mode 100644 index 00000000000..02f3a488d7c --- /dev/null +++ b/yarn-project/circuits.js/src/contract/contract_address.test.ts @@ -0,0 +1,75 @@ +import { ABIParameterVisibility, FunctionAbi, FunctionType } from '@aztec/foundation/abi'; +import { Fr, Point } from '@aztec/foundation/fields'; +import { ContractInstance } from '@aztec/types/contracts'; + +import { EthAddress, PublicKey } from '../index.js'; +import { + computeContractAddressFromInstance, + computeContractAddressFromPartial, + computeInitializationHash, + computePartialAddress, + computePublicKeysHash, + computeSaltedInitializationHash, +} from './contract_address.js'; + +describe('ContractAddress', () => { + it('computeContractAddressFromInstance', () => { + const mockInstance: ContractInstance = { + version: 1, + contractClassId: new Fr(1), + initializationHash: new Fr(2), + portalContractAddress: EthAddress.fromField(new Fr(3)), + publicKeysHash: new Fr(4), + salt: new Fr(5), + }; + const result = computeContractAddressFromInstance(mockInstance); + expect(result).toMatchSnapshot(); + }); + + it('computePartialAddress', () => { + const mockInstance = { + contractClassId: new Fr(1), + saltedInitializationHash: new Fr(2), + }; + const result = computePartialAddress(mockInstance); + expect(result).toMatchSnapshot(); + }); + + it('computeSaltedInitializationHash', () => { + const mockInstance = { + initializationHash: new Fr(1), + salt: new Fr(2), + portalContractAddress: EthAddress.fromField(new Fr(3)), + }; + const result = computeSaltedInitializationHash(mockInstance); + expect(result).toMatchSnapshot(); + }); + + it('computeContractAddressFromPartial', () => { + const mockArgs = { + publicKeyHash: new Fr(1), + partialAddress: new Fr(2), + }; + const result = computeContractAddressFromPartial(mockArgs); + expect(result).toMatchSnapshot(); + }); + + it('computePublicKeysHash', () => { + const mockPublicKey: PublicKey = new Point(new Fr(1), new Fr(2)); + const result = computePublicKeysHash(mockPublicKey); + expect(result).toMatchSnapshot(); + }); + + it('computeInitializationHash', () => { + const mockInitFn: FunctionAbi = { + functionType: FunctionType.SECRET, + isInternal: false, + name: 'fun', + parameters: [{ name: 'param1', type: { kind: 'boolean' }, visibility: ABIParameterVisibility.SECRET }], + returnTypes: [], + }; + const mockArgs: any[] = [true]; + const result = computeInitializationHash(mockInitFn, mockArgs); + expect(result).toMatchSnapshot(); + }); +}); diff --git a/yarn-project/circuits.js/src/contract/contract_address.ts b/yarn-project/circuits.js/src/contract/contract_address.ts new file mode 100644 index 00000000000..6dabefe3213 --- /dev/null +++ b/yarn-project/circuits.js/src/contract/contract_address.ts @@ -0,0 +1,102 @@ +import { FunctionAbi, FunctionSelector, encodeArguments } from '@aztec/foundation/abi'; +import { pedersenHash } from '@aztec/foundation/crypto'; +import { Fr } from '@aztec/foundation/fields'; +import { ContractInstance } from '@aztec/types/contracts'; + +import { computeVarArgsHash } from '../abis/abis.js'; +import { AztecAddress, GeneratorIndex, PublicKey } from '../index.js'; + +// TODO(@spalladino): Review all generator indices in this file + +/** + * Returns the deployment address for a given contract instance as defined on the [Yellow Paper](../../../../yellow-paper/docs/addresses-and-keys/specification.md). + * ``` + * salted_initialization_hash = pedersen([salt, initialization_hash, portal_contract_address as Field], GENERATOR__SALTED_INITIALIZATION_HASH) + * partial_address = pedersen([contract_class_id, salted_initialization_hash], GENERATOR__CONTRACT_PARTIAL_ADDRESS_V1) + * address = pedersen([public_keys_hash, partial_address], GENERATOR__CONTRACT_ADDRESS_V1) + * ``` + * @param instance - A contract instance for which to calculate the deployment address. + */ +export function computeContractAddressFromInstance(instance: ContractInstance): AztecAddress { + const partialAddress = computePartialAddress(instance); + const publicKeyHash = instance.publicKeysHash; + return computeContractAddressFromPartial({ partialAddress, publicKeyHash }); +} + +/** + * Computes the partial address defined as the hash of the contract class id and salted initialization hash. + * @param instance - Contract instance for which to calculate the partial address. + */ +export function computePartialAddress( + instance: + | Pick + | { contractClassId: Fr; saltedInitializationHash: Fr }, +): Fr { + const saltedInitializationHash = + 'saltedInitializationHash' in instance + ? instance.saltedInitializationHash + : computeSaltedInitializationHash(instance); + + return Fr.fromBuffer( + pedersenHash( + [instance.contractClassId, saltedInitializationHash].map(x => x.toBuffer()), + GeneratorIndex.PARTIAL_ADDRESS, + ), + ); +} + +/** + * Computes the salted initialization hash for an address, defined as the hash of the salt, initialization hash, and portal address. + * @param instance - Contract instance for which to compute the salted initialization hash. + */ +export function computeSaltedInitializationHash( + instance: Pick, +): Fr { + return Fr.fromBuffer( + pedersenHash( + [instance.salt, instance.initializationHash, instance.portalContractAddress].map(x => x.toBuffer()), + GeneratorIndex.PARTIAL_ADDRESS, + ), + ); +} + +/** + * Computes a contract address from its partial address and the pubkeys hash. + * @param args - The hash of the public keys or the plain public key to be hashed, along with the partial address. + * @returns The partially constructed contract address. + */ +export function computeContractAddressFromPartial( + args: ({ publicKeyHash: Fr } | { publicKey: PublicKey }) & { partialAddress: Fr }, +): AztecAddress { + const publicKeyHash = 'publicKey' in args ? computePublicKeysHash(args.publicKey) : args.publicKeyHash; + const result = pedersenHash( + [publicKeyHash.toBuffer(), args.partialAddress.toBuffer()], + GeneratorIndex.CONTRACT_ADDRESS, + ); + return new AztecAddress(result); +} + +/** + * Computes the hash of a set of public keys to be used for computing the deployment address of a contract. + * @param publicKey - Single public key (for now!). + * @returns The hash of the public keys. + */ +export function computePublicKeysHash(publicKey: PublicKey | undefined): Fr { + if (!publicKey) { + return Fr.ZERO; + } + return Fr.fromBuffer(pedersenHash([publicKey.x.toBuffer(), publicKey.y.toBuffer()], GeneratorIndex.PARTIAL_ADDRESS)); +} + +/** + * Computes the initialization hash for an instance given its constructor function and arguments. + * @param initFn - Constructor function. + * @param args - Unencoded arguments, will be encoded as fields according to the constructor function abi. + * @returns The hash. + */ +export function computeInitializationHash(initFn: FunctionAbi, args: any[]): Fr { + const selector = FunctionSelector.fromNameAndParameters(initFn.name, initFn.parameters); + const flatArgs = encodeArguments(initFn, args); + const argsHash = computeVarArgsHash(flatArgs); + return Fr.fromBuffer(pedersenHash([selector.toBuffer(), argsHash.toBuffer()], GeneratorIndex.CONSTRUCTOR)); +} diff --git a/yarn-project/circuits.js/src/contract/contract_class.test.ts b/yarn-project/circuits.js/src/contract/contract_class.test.ts new file mode 100644 index 00000000000..450814fe3f1 --- /dev/null +++ b/yarn-project/circuits.js/src/contract/contract_class.test.ts @@ -0,0 +1,15 @@ +import { Fr } from '@aztec/foundation/fields'; +import { toFriendlyJSON } from '@aztec/foundation/serialize'; + +import { getSampleContractArtifact } from '../tests/fixtures.js'; +import { getContractClassFromArtifact } from './contract_class.js'; + +describe('ContractClass', () => { + it('creates a contract class from a contract compilation artifact', () => { + const contractClass = getContractClassFromArtifact({ + ...getSampleContractArtifact(), + artifactHash: Fr.fromString('0x1234'), + }); + expect(toFriendlyJSON(contractClass)).toMatchSnapshot(); + }); +}); diff --git a/yarn-project/circuits.js/src/contract/contract_class.ts b/yarn-project/circuits.js/src/contract/contract_class.ts new file mode 100644 index 00000000000..7f9fd1854fe --- /dev/null +++ b/yarn-project/circuits.js/src/contract/contract_class.ts @@ -0,0 +1,45 @@ +import { ContractArtifact, FunctionSelector, FunctionType } from '@aztec/foundation/abi'; +import { Fr } from '@aztec/foundation/fields'; +import { ContractClass, ContractClassWithId } from '@aztec/types/contracts'; + +import { getArtifactHash } from './artifact_hash.js'; +import { getContractClassId } from './contract_class_id.js'; +import { hashVKStr } from './contract_tree/index.js'; + +/** Contract artifact including its artifact hash */ +type ContractArtifactWithHash = ContractArtifact & { artifactHash: Fr }; + +/** Creates a ContractClass from a contract compilation artifact. */ +export function getContractClassFromArtifact( + artifact: ContractArtifact | ContractArtifactWithHash, +): ContractClassWithId { + const artifactHash = (artifact as ContractArtifactWithHash).artifactHash ?? getArtifactHash(artifact); + const contractClass: ContractClass = { + version: 1, + artifactHash: artifactHash, + publicFunctions: artifact.functions + .filter(f => f.functionType === FunctionType.OPEN) + .map(f => ({ + selector: FunctionSelector.fromNameAndParameters(f.name, f.parameters), + bytecode: Buffer.from(f.bytecode, 'base64'), + isInternal: f.isInternal, + })), + privateFunctions: artifact.functions + .filter(f => f.functionType === FunctionType.SECRET) + .map(f => ({ + selector: FunctionSelector.fromNameAndParameters(f.name, f.parameters), + vkHash: getVerificationKeyHash(f.verificationKey!), + isInternal: f.isInternal, + })), + packedBytecode: Buffer.alloc(0), + }; + const id = getContractClassId(contractClass); + return { ...contractClass, id }; +} + +/** + * Calculates the hash of a verification key. + * */ +function getVerificationKeyHash(verificationKeyInBase64: string) { + return Fr.fromBuffer(hashVKStr(verificationKeyInBase64)); +} diff --git a/yarn-project/circuits.js/src/contract/contract_class_id.test.ts b/yarn-project/circuits.js/src/contract/contract_class_id.test.ts new file mode 100644 index 00000000000..a60a4894192 --- /dev/null +++ b/yarn-project/circuits.js/src/contract/contract_class_id.test.ts @@ -0,0 +1,34 @@ +import { Fr } from '@aztec/foundation/fields'; +import { ContractClass } from '@aztec/types/contracts'; + +import { FunctionSelector, getContractClassId } from '../index.js'; + +describe('ContractClass', () => { + describe('getContractClassId', () => { + it('calculates the contract class id', () => { + const contractClass: ContractClass = { + version: 1, + artifactHash: Fr.fromString('0x1234'), + packedBytecode: Buffer.from('123456789012345678901234567890', 'hex'), + privateFunctions: [ + { + selector: FunctionSelector.fromString('0x12345678'), + vkHash: Fr.fromString('0x1234'), + isInternal: false, + }, + ], + publicFunctions: [ + { + selector: FunctionSelector.fromString('0x12345678'), + bytecode: Buffer.from('123456789012345678901234567890', 'hex'), + isInternal: false, + }, + ], + }; + + expect(getContractClassId(contractClass).toString()).toMatchInlineSnapshot( + `"0x1b436781f84669144ec383d6ea5f49b05ccba5c6221ebeb86085443c2a859202"`, + ); + }); + }); +}); diff --git a/yarn-project/circuits.js/src/contract/contract_class_id.ts b/yarn-project/circuits.js/src/contract/contract_class_id.ts new file mode 100644 index 00000000000..62882ed5b21 --- /dev/null +++ b/yarn-project/circuits.js/src/contract/contract_class_id.ts @@ -0,0 +1,78 @@ +import { pedersenHash, sha256 } from '@aztec/foundation/crypto'; +import { Fr } from '@aztec/foundation/fields'; +import { numToUInt8 } from '@aztec/foundation/serialize'; +import { ContractClass, PrivateFunction, PublicFunction } from '@aztec/types/contracts'; + +import { MerkleTreeCalculator } from '../abis/merkle_tree_calculator.js'; +import { FUNCTION_TREE_HEIGHT, GeneratorIndex } from '../constants.gen.js'; + +/** + * Returns the id of a contract class computed as its hash. + * + * ``` + * version = 1 + * private_function_leaves = private_functions.map(fn => pedersen([fn.function_selector as Field, fn.vk_hash], GENERATOR__FUNCTION_LEAF)) + * private_functions_root = merkleize(private_function_leaves) + * bytecode_commitment = calculate_commitment(packed_bytecode) + * contract_class_id = pedersen([version, artifact_hash, private_functions_root, bytecode_commitment], GENERATOR__CLASS_IDENTIFIER) + * ``` + * @param contractClass - Contract class. + * @returns The identifier. + */ +export function getContractClassId(contractClass: ContractClass): Fr { + const privateFunctionsRoot = getPrivateFunctionsRoot(contractClass.privateFunctions); + const publicFunctionsRoot = getPublicFunctionsRoot(contractClass.publicFunctions); // This should be removed once we drop public functions as first class citizens in the protocol + const bytecodeCommitment = getBytecodeCommitment(contractClass.packedBytecode); + return Fr.fromBuffer( + pedersenHash( + [ + numToUInt8(contractClass.version), + contractClass.artifactHash.toBuffer(), + privateFunctionsRoot.toBuffer(), + publicFunctionsRoot.toBuffer(), + bytecodeCommitment.toBuffer(), + ], + GeneratorIndex.CONTRACT_LEAF, // TODO(@spalladino): Review all generator indices in this file + ), + ); +} + +// TODO(@spalladino): Replace with actual implementation +function getBytecodeCommitment(bytecode: Buffer) { + return Fr.fromBufferReduce(sha256(bytecode)); +} + +// Memoize the merkle tree calculators to avoid re-computing the zero-hash for each level in each call +let privateFunctionTreeCalculator: MerkleTreeCalculator | undefined; +let publicFunctionTreeCalculator: MerkleTreeCalculator | undefined; + +const PRIVATE_FUNCTION_SIZE = 2; +const PUBLIC_FUNCTION_SIZE = 2; + +function getPrivateFunctionsRoot(fns: PrivateFunction[]): Fr { + const privateFunctionLeaves = fns.map(fn => + pedersenHash( + [fn.selector, fn.vkHash].map(x => x.toBuffer()), + GeneratorIndex.FUNCTION_LEAF, + ), + ); + if (!privateFunctionTreeCalculator) { + const functionTreeZeroLeaf = pedersenHash(new Array(PRIVATE_FUNCTION_SIZE).fill(Buffer.alloc(32))); + privateFunctionTreeCalculator = new MerkleTreeCalculator(FUNCTION_TREE_HEIGHT, functionTreeZeroLeaf); + } + return Fr.fromBuffer(privateFunctionTreeCalculator.computeTreeRoot(privateFunctionLeaves)); +} + +function getPublicFunctionsRoot(fns: PublicFunction[]): Fr { + const publicFunctionLeaves = fns.map(fn => + pedersenHash( + [fn.selector, getBytecodeCommitment(fn.bytecode)].map(x => x.toBuffer()), + GeneratorIndex.FUNCTION_LEAF, + ), + ); + if (!publicFunctionTreeCalculator) { + const functionTreeZeroLeaf = pedersenHash(new Array(PUBLIC_FUNCTION_SIZE).fill(Buffer.alloc(32))); + publicFunctionTreeCalculator = new MerkleTreeCalculator(FUNCTION_TREE_HEIGHT, functionTreeZeroLeaf); + } + return Fr.fromBuffer(publicFunctionTreeCalculator.computeTreeRoot(publicFunctionLeaves)); +} diff --git a/yarn-project/circuits.js/src/contract/contract_deployment_info.ts b/yarn-project/circuits.js/src/contract/contract_deployment_info.ts deleted file mode 100644 index 39bf1cbb13f..00000000000 --- a/yarn-project/circuits.js/src/contract/contract_deployment_info.ts +++ /dev/null @@ -1,55 +0,0 @@ -import { - computeCompleteAddress, - computeFunctionTreeRoot, - computeVarArgsHash, - hashConstructor, -} from '@aztec/circuits.js/abis'; -import { ContractArtifact, FunctionSelector, encodeArguments } from '@aztec/foundation/abi'; - -import { DeploymentInfo, Fr, FunctionData, PublicKey } from '../index.js'; -import { generateFunctionLeaves, hashVKStr, isConstructor } from './contract_tree/contract_tree.js'; - -/** - * Generates the deployment info for a contract - * @param artifact - The account contract build artifact. - * @param args - The args to the account contract constructor - * @param contractAddressSalt - The salt to be used in the contract address derivation - * @param publicKey - The account public key - * @returns - The contract deployment info - */ -export function getContractDeploymentInfo( - artifact: ContractArtifact, - args: any[], - contractAddressSalt: Fr, - publicKey: PublicKey, -): DeploymentInfo { - const constructorArtifact = artifact.functions.find(isConstructor); - if (!constructorArtifact) { - throw new Error('Cannot find constructor in the artifact.'); - } - if (!constructorArtifact.verificationKey) { - throw new Error('Missing verification key for the constructor.'); - } - - const vkHash = hashVKStr(constructorArtifact.verificationKey); - const constructorVkHash = Fr.fromBuffer(vkHash); - const functions = artifact.functions.map(f => ({ - ...f, - selector: FunctionSelector.fromNameAndParameters(f.name, f.parameters), - })); - const leaves = generateFunctionLeaves(functions); - const functionTreeRoot = computeFunctionTreeRoot(leaves); - const functionData = FunctionData.fromAbi(constructorArtifact); - const flatArgs = encodeArguments(constructorArtifact, args); - const argsHash = computeVarArgsHash(flatArgs); - const constructorHash = hashConstructor(functionData, argsHash, constructorVkHash.toBuffer()); - - const completeAddress = computeCompleteAddress(publicKey, contractAddressSalt, functionTreeRoot, constructorHash); - - return { - completeAddress, - constructorHash, - constructorVkHash, - functionTreeRoot, - }; -} diff --git a/yarn-project/circuits.js/src/contract/contract_instance.ts b/yarn-project/circuits.js/src/contract/contract_instance.ts new file mode 100644 index 00000000000..9d1b0b545b8 --- /dev/null +++ b/yarn-project/circuits.js/src/contract/contract_instance.ts @@ -0,0 +1,51 @@ +import { ContractArtifact } from '@aztec/foundation/abi'; +import { ContractInstance, ContractInstanceWithAddress } from '@aztec/types/contracts'; + +import { EthAddress, Fr, PublicKey, getContractClassFromArtifact, getContractClassId } from '../index.js'; +import { + computeContractAddressFromInstance, + computeInitializationHash, + computePublicKeysHash, +} from './contract_address.js'; +import { isConstructor } from './contract_tree/contract_tree.js'; + +/** + * Generates a Contract Instance from the deployment params. + * @param artifact - The account contract build artifact. + * @param args - The args to the account contract constructor + * @param contractAddressSalt - The salt to be used in the contract address derivation + * @param publicKey - The account public key + * @param portalContractAddress - The portal contract address + * @returns - The contract instance + */ +export function getContractInstanceFromDeployParams( + artifact: ContractArtifact, + args: any[], + contractAddressSalt: Fr, + publicKey: PublicKey, + portalContractAddress: EthAddress, +): ContractInstanceWithAddress { + const constructorArtifact = artifact.functions.find(isConstructor); + if (!constructorArtifact) { + throw new Error('Cannot find constructor in the artifact.'); + } + if (!constructorArtifact.verificationKey) { + throw new Error('Missing verification key for the constructor.'); + } + + const contractClass = getContractClassFromArtifact(artifact); + const contractClassId = getContractClassId(contractClass); + const initializationHash = computeInitializationHash(constructorArtifact, args); + const publicKeysHash = computePublicKeysHash(publicKey); + + const instance: ContractInstance = { + contractClassId, + initializationHash, + portalContractAddress, + publicKeysHash, + salt: contractAddressSalt, + version: 1, + }; + + return { ...instance, address: computeContractAddressFromInstance(instance) }; +} diff --git a/yarn-project/circuits.js/src/contract/index.ts b/yarn-project/circuits.js/src/contract/index.ts index 38bc67a9e52..a038ad2db2f 100644 --- a/yarn-project/circuits.js/src/contract/index.ts +++ b/yarn-project/circuits.js/src/contract/index.ts @@ -1,2 +1,6 @@ -export * from './contract_deployment_info.js'; +export * from './contract_instance.js'; export * from './contract_tree/index.js'; +export * from './contract_class_id.js'; +export * from './contract_class.js'; +export * from './artifact_hash.js'; +export * from './contract_address.js'; diff --git a/yarn-project/circuits.js/src/keys/index.ts b/yarn-project/circuits.js/src/keys/index.ts index 09ec99767c4..07e8b0b2a65 100644 --- a/yarn-project/circuits.js/src/keys/index.ts +++ b/yarn-project/circuits.js/src/keys/index.ts @@ -16,8 +16,9 @@ export function derivePublicKey(secretKey: GrumpkinPrivateKey) { /** * Derives a new secret key from a secret key and an index. */ -function _deriveSecretKey(secretKey: GrumpkinPrivateKey, index: Fr): GrumpkinPrivateKey { +function deriveSecretKey(secretKey: GrumpkinPrivateKey, index: Fr): GrumpkinPrivateKey { // TODO: Temporary hack. Should replace it with a secure way to derive the secret key. + // Match the way keys are derived in noir-protocol-circuits/src/crates/private_kernel_lib/src/common.nr const hash = pedersenHash([secretKey.high, secretKey.low, index].map(v => v.toBuffer())); return new GrumpkinScalar(hash); } @@ -26,9 +27,7 @@ function _deriveSecretKey(secretKey: GrumpkinPrivateKey, index: Fr): GrumpkinPri * Computes the nullifier secret key from seed secret key. */ export function computeNullifierSecretKey(seedSecretKey: GrumpkinPrivateKey): GrumpkinPrivateKey { - // TODO - // return deriveSecretKey(seedSecretKey, new Fr(1)); - return seedSecretKey; + return deriveSecretKey(seedSecretKey, new Fr(1)); } /** @@ -36,9 +35,7 @@ export function computeNullifierSecretKey(seedSecretKey: GrumpkinPrivateKey): Gr */ export function computeSiloedNullifierSecretKey( nullifierSecretKey: GrumpkinPrivateKey, - _contractAddress: AztecAddress, + contractAddress: AztecAddress, ): GrumpkinPrivateKey { - // TODO - // return deriveSecretKey(nullifierSecretKey, contractAddress); - return nullifierSecretKey; + return deriveSecretKey(nullifierSecretKey, contractAddress); } diff --git a/yarn-project/circuits.js/src/structs/__snapshots__/header.test.ts.snap b/yarn-project/circuits.js/src/structs/__snapshots__/header.test.ts.snap new file mode 100644 index 00000000000..8a15a59ee84 --- /dev/null +++ b/yarn-project/circuits.js/src/structs/__snapshots__/header.test.ts.snap @@ -0,0 +1,44 @@ +// Jest Snapshot v1, https://goo.gl/fbAQLP + +exports[`Header computes hash 1`] = ` +Fr { + "asBigInt": 17991638921121681345555824161757346486368776085978982802127357651656088857262n, + "asBuffer": { + "data": [ + 39, + 198, + 232, + 33, + 120, + 194, + 121, + 42, + 23, + 66, + 111, + 251, + 166, + 131, + 251, + 128, + 16, + 46, + 122, + 209, + 193, + 24, + 177, + 67, + 172, + 91, + 198, + 153, + 236, + 93, + 170, + 174, + ], + "type": "Buffer", + }, +} +`; diff --git a/yarn-project/circuits.js/src/structs/call_context.ts b/yarn-project/circuits.js/src/structs/call_context.ts index 266f715e3b8..5f35bced17d 100644 --- a/yarn-project/circuits.js/src/structs/call_context.ts +++ b/yarn-project/circuits.js/src/structs/call_context.ts @@ -1,6 +1,6 @@ import { AztecAddress } from '@aztec/foundation/aztec-address'; import { EthAddress } from '@aztec/foundation/eth-address'; -import { BufferReader, serializeToBuffer } from '@aztec/foundation/serialize'; +import { BufferReader, FieldReader, serializeToBuffer } from '@aztec/foundation/serialize'; import { FieldsOf } from '@aztec/foundation/types'; import { Fr, FunctionSelector } from './index.js'; @@ -126,6 +126,20 @@ export class CallContext { ); } + static fromFields(fields: Fr[] | FieldReader): CallContext { + const reader = FieldReader.asReader(fields); + return new CallContext( + reader.readObject(AztecAddress), + reader.readObject(AztecAddress), + reader.readField(), + reader.readObject(FunctionSelector), + reader.readBoolean(), + reader.readBoolean(), + reader.readBoolean(), + reader.readU32(), + ); + } + equals(callContext: CallContext) { return ( callContext.msgSender.equals(this.msgSender) && diff --git a/yarn-project/circuits.js/src/structs/complete_address.ts b/yarn-project/circuits.js/src/structs/complete_address.ts index b2fd575e4d5..cf34dfa2426 100644 --- a/yarn-project/circuits.js/src/structs/complete_address.ts +++ b/yarn-project/circuits.js/src/structs/complete_address.ts @@ -2,16 +2,21 @@ import { AztecAddress } from '@aztec/foundation/aztec-address'; import { Fr, Point } from '@aztec/foundation/fields'; import { BufferReader } from '@aztec/foundation/serialize'; -import { computeContractAddressFromPartial } from '../abis/abis.js'; import { Grumpkin } from '../barretenberg/index.js'; -import { GrumpkinPrivateKey, PartialAddress, PublicKey } from '../index.js'; +import { + GrumpkinPrivateKey, + PartialAddress, + PublicKey, + computeContractAddressFromPartial, + computePartialAddress, +} from '../index.js'; /** * A complete address is a combination of an Aztec address, a public key and a partial address. * * @remarks We have introduced this type because it is common that these 3 values are used together. They are commonly * used together because it is the information needed to send user a note. - * @remarks See the link bellow for details about how address is computed: + * @remarks See the link below for details about how address is computed: * https://github.com/AztecProtocol/aztec-packages/blob/master/docs/docs/concepts/foundation/accounts/keys.md#addresses-partial-addresses-and-public-keys */ export class CompleteAddress { @@ -31,27 +36,43 @@ export class CompleteAddress { static readonly SIZE_IN_BYTES = 32 * 4; static create(address: AztecAddress, publicKey: PublicKey, partialAddress: PartialAddress) { - const expectedAddress = computeContractAddressFromPartial(publicKey, partialAddress); - if (!expectedAddress.equals(address)) { - throw new Error( - `Address cannot be derived from pubkey and partial address (received ${address.toString()}, derived ${expectedAddress.toString()})`, - ); - } - return new CompleteAddress(address, publicKey, partialAddress); + const completeAddress = new CompleteAddress(address, publicKey, partialAddress); + completeAddress.validate(); + return completeAddress; } static random() { const partialAddress = Fr.random(); - const pubKey = Point.random(); - const address = computeContractAddressFromPartial(pubKey, partialAddress); - return new CompleteAddress(address, pubKey, partialAddress); + const publicKey = Point.random(); + const address = computeContractAddressFromPartial({ publicKey, partialAddress }); + return new CompleteAddress(address, publicKey, partialAddress); } static fromPrivateKeyAndPartialAddress(privateKey: GrumpkinPrivateKey, partialAddress: Fr): CompleteAddress { const grumpkin = new Grumpkin(); - const pubKey = grumpkin.mul(Grumpkin.generator, privateKey); - const address = computeContractAddressFromPartial(pubKey, partialAddress); - return new CompleteAddress(address, pubKey, partialAddress); + const publicKey = grumpkin.mul(Grumpkin.generator, privateKey); + const address = computeContractAddressFromPartial({ publicKey, partialAddress }); + return new CompleteAddress(address, publicKey, partialAddress); + } + + static fromPublicKeyAndInstance( + publicKey: PublicKey, + instance: Parameters[0], + ): CompleteAddress { + const partialAddress = computePartialAddress(instance); + const address = computeContractAddressFromPartial({ publicKey, partialAddress }); + return new CompleteAddress(address, publicKey, partialAddress); + } + + /** Throws if the address is not correctly derived from the public key and partial address.*/ + public validate() { + const expectedAddress = computeContractAddressFromPartial(this); + const address = this.address; + if (!expectedAddress.equals(address)) { + throw new Error( + `Address cannot be derived from pubkey and partial address (received ${address.toString()}, derived ${expectedAddress.toString()})`, + ); + } } /** diff --git a/yarn-project/circuits.js/src/structs/global_variables.ts b/yarn-project/circuits.js/src/structs/global_variables.ts index 2a569fb984c..fd353693a10 100644 --- a/yarn-project/circuits.js/src/structs/global_variables.ts +++ b/yarn-project/circuits.js/src/structs/global_variables.ts @@ -53,7 +53,7 @@ export class GlobalVariables { } static getFields(fields: FieldsOf) { - // Note: The order here must match the order in the HeaderDecoder solidity library. + // Note: The order here must match the order in the HeaderLib solidity library. return [fields.chainId, fields.version, fields.blockNumber, fields.timestamp] as const; } @@ -61,6 +61,10 @@ export class GlobalVariables { return serializeToBuffer(...GlobalVariables.getFields(this)); } + toFieldArray() { + return GlobalVariables.getFields(this); + } + toJSON() { return { chainId: this.chainId.toString(), @@ -69,4 +73,8 @@ export class GlobalVariables { timestamp: this.timestamp.toString(), }; } + + isEmpty(): boolean { + return this.chainId.isZero() && this.version.isZero() && this.blockNumber.isZero() && this.timestamp.isZero(); + } } diff --git a/yarn-project/circuits.js/src/structs/header.test.ts b/yarn-project/circuits.js/src/structs/header.test.ts index 4cc4e60d8b5..72d49ae9bf2 100644 --- a/yarn-project/circuits.js/src/structs/header.test.ts +++ b/yarn-project/circuits.js/src/structs/header.test.ts @@ -2,11 +2,27 @@ import { makeHeader } from '../tests/factories.js'; import { Header } from './header.js'; describe('Header', () => { - it(`serializes to buffer and deserializes it back`, () => { + it('serializes to buffer and deserializes it back', () => { const randomInt = Math.floor(Math.random() * 1000); const expected = makeHeader(randomInt, undefined); const buffer = expected.toBuffer(); const res = Header.fromBuffer(buffer); expect(res).toEqual(expected); }); + + it('serializes to field array and deserializes it back', () => { + const randomInt = Math.floor(Math.random() * 1000); + const expected = makeHeader(randomInt, undefined); + + const fieldArray = expected.toFieldArray(); + const res = Header.fromFieldArray(fieldArray); + expect(res).toEqual(expected); + }); + + it('computes hash', () => { + const seed = 9870243; + const header = makeHeader(seed, undefined); + const hash = header.hash(); + expect(hash).toMatchSnapshot(); + }); }); diff --git a/yarn-project/circuits.js/src/structs/header.ts b/yarn-project/circuits.js/src/structs/header.ts index 6c70655e81d..016f9e9f98e 100644 --- a/yarn-project/circuits.js/src/structs/header.ts +++ b/yarn-project/circuits.js/src/structs/header.ts @@ -1,6 +1,10 @@ -import { BufferReader, serializeToBuffer } from '@aztec/foundation/serialize'; +import { pedersenHash } from '@aztec/foundation/crypto'; +import { Fr } from '@aztec/foundation/fields'; +import { BufferReader, from2Fields, serializeToBuffer, to2Fields } from '@aztec/foundation/serialize'; +import { GeneratorIndex, HEADER_LENGTH } from '../constants.gen.js'; import { GlobalVariables } from './global_variables.js'; +import { PartialStateReference } from './partial_state_reference.js'; import { AppendOnlyTreeSnapshot } from './rollup/append_only_tree_snapshot.js'; import { StateReference } from './state_reference.js'; @@ -24,17 +28,93 @@ export class Header { } toBuffer() { - // Note: The order here must match the order in the HeaderDecoder solidity library. - return serializeToBuffer(this.globalVariables, this.state, this.lastArchive, this.bodyHash); + // Note: The order here must match the order in the HeaderLib solidity library. + return serializeToBuffer(this.lastArchive, this.bodyHash, this.state, this.globalVariables); + } + + toFieldArray(): Fr[] { + // Note: The order here must match the order in header.nr + const serialized = [ + ...this.lastArchive.toFieldArray(), + ...to2Fields(this.bodyHash), + ...this.state.toFieldArray(), + ...this.globalVariables.toFieldArray(), + ]; + if (serialized.length !== HEADER_LENGTH) { + throw new Error(`Expected header to have ${HEADER_LENGTH} fields, but it has ${serialized.length} fields`); + } + return serialized; } static fromBuffer(buffer: Buffer | BufferReader): Header { const reader = BufferReader.asReader(buffer); - // TODO(#4045): unify ordering here with ordering in constructor. - const globalVariables = reader.readObject(GlobalVariables); - const state = reader.readObject(StateReference); - const lastArchive = reader.readObject(AppendOnlyTreeSnapshot); - const bodyHash = reader.readBytes(NUM_BYTES_PER_SHA256); + + return new Header( + reader.readObject(AppendOnlyTreeSnapshot), + reader.readBytes(NUM_BYTES_PER_SHA256), + reader.readObject(StateReference), + reader.readObject(GlobalVariables), + ); + } + + static fromFieldArray(fields: Fr[]): Header { + if (fields.length !== HEADER_LENGTH) { + throw new Error(`Expected header to have ${HEADER_LENGTH} fields, but it has ${fields.length} fields`); + } + // Note: The order here must match the order in header.nr + const lastArchive = new AppendOnlyTreeSnapshot(fields[0], Number(fields[1].toBigInt())); + const bodyHash = from2Fields(fields[2], fields[3]); + const state = new StateReference( + new AppendOnlyTreeSnapshot(fields[4], Number(fields[5].toBigInt())), + new PartialStateReference( + new AppendOnlyTreeSnapshot(fields[6], Number(fields[7].toBigInt())), + new AppendOnlyTreeSnapshot(fields[8], Number(fields[9].toBigInt())), + new AppendOnlyTreeSnapshot(fields[10], Number(fields[11].toBigInt())), + new AppendOnlyTreeSnapshot(fields[12], Number(fields[13].toBigInt())), + ), + ); + const globalVariables = new GlobalVariables(fields[14], fields[15], fields[16], fields[17]); + return new Header(lastArchive, bodyHash, state, globalVariables); } + + static empty(): Header { + return new Header( + AppendOnlyTreeSnapshot.zero(), + Buffer.alloc(NUM_BYTES_PER_SHA256), + StateReference.empty(), + GlobalVariables.empty(), + ); + } + + isEmpty(): boolean { + return ( + this.lastArchive.isZero() && + this.bodyHash.equals(Buffer.alloc(NUM_BYTES_PER_SHA256)) && + this.state.isEmpty() && + this.globalVariables.isEmpty() + ); + } + + /** + * Serializes this instance into a string. + * @returns Encoded string. + */ + public toString(): string { + return this.toBuffer().toString('hex'); + } + + static fromString(str: string): Header { + const buffer = Buffer.from(str.replace(/^0x/i, ''), 'hex'); + return Header.fromBuffer(buffer); + } + + hash(): Fr { + return Fr.fromBuffer( + pedersenHash( + this.toFieldArray().map(f => f.toBuffer()), + GeneratorIndex.BLOCK_HASH, + ), + ); + } } diff --git a/yarn-project/circuits.js/src/structs/index.ts b/yarn-project/circuits.js/src/structs/index.ts index fc9305a85a6..ca2cb39c1c6 100644 --- a/yarn-project/circuits.js/src/structs/index.ts +++ b/yarn-project/circuits.js/src/structs/index.ts @@ -10,13 +10,13 @@ export * from './global_variables.js'; export * from './header.js'; export * from './kernel/combined_accumulated_data.js'; export * from './kernel/combined_constant_data.js'; -export * from './kernel/block_header.js'; export * from './kernel/previous_kernel_data.js'; export * from './kernel/private_kernel.js'; export * from './kernel/public_inputs.js'; export * from './kernel/public_inputs_final.js'; export * from './kernel/public_kernel.js'; export * from './membership_witness.js'; +export * from './nullifier_key_validation_request.js'; export * from './private_circuit_public_inputs.js'; export * from './proof.js'; export * from './public_call_request.js'; diff --git a/yarn-project/circuits.js/src/structs/kernel/block_header.test.ts b/yarn-project/circuits.js/src/structs/kernel/block_header.test.ts deleted file mode 100644 index 3bb78282c2a..00000000000 --- a/yarn-project/circuits.js/src/structs/kernel/block_header.test.ts +++ /dev/null @@ -1,17 +0,0 @@ -import { BlockHeader } from './block_header.js'; - -describe('BlockHeader', () => { - it('serializes to buffer and back', () => { - const blockHeader = BlockHeader.random(); - const serialized = blockHeader.toBuffer(); - const deserialized = BlockHeader.fromBuffer(serialized); - expect(deserialized).toEqual(blockHeader); - }); - - it('serializes to string and back', () => { - const blockHeader = BlockHeader.random(); - const serialized = blockHeader.toString(); - const deserialized = BlockHeader.fromString(serialized); - expect(deserialized).toEqual(blockHeader); - }); -}); diff --git a/yarn-project/circuits.js/src/structs/kernel/block_header.ts b/yarn-project/circuits.js/src/structs/kernel/block_header.ts deleted file mode 100644 index 6cc1d35917a..00000000000 --- a/yarn-project/circuits.js/src/structs/kernel/block_header.ts +++ /dev/null @@ -1,140 +0,0 @@ -import { Fr } from '@aztec/foundation/fields'; -import { BufferReader, serializeToBuffer } from '@aztec/foundation/serialize'; -import { FieldsOf } from '@aztec/foundation/types'; - -/** - * The string encoding used for serializing BlockHeader objects. - */ -const STRING_ENCODING: BufferEncoding = 'hex'; - -/** - * Information about the tree roots used for both public and private kernels. - */ -// TODO(#3937): Nuke this -export class BlockHeader { - constructor( - /** - * Root of the note hash tree at the time of when this information was assembled. - */ - public noteHashTreeRoot: Fr, - /** - * Root of the nullifier tree at the time of when this information was assembled. - */ - public nullifierTreeRoot: Fr, - /** - * Root of the contract tree at the time of when this information was assembled. - */ - public contractTreeRoot: Fr, - /** - * Root of the l1 to l2 message tree at the time of when this information was assembled. - */ - public l1ToL2MessageTreeRoot: Fr, - /** - * Root of the state roots tree (archive) at the block prior to when this information was assembled. - */ - public archiveRoot: Fr, - /** - * Root of the private kernel vk tree at the time of when this information was assembled. - */ - public privateKernelVkTreeRoot: Fr, // TODO(#3441) future enhancement - /** - * Current public state tree hash. - */ - public publicDataTreeRoot: Fr, - /** - * Previous globals hash, this value is used to recalculate the block hash. - */ - public globalVariablesHash: Fr, - ) {} - - static from(fields: FieldsOf) { - return new BlockHeader(...BlockHeader.getFields(fields)); - } - - static random() { - return new BlockHeader( - Fr.random(), - Fr.random(), - Fr.random(), - Fr.random(), - Fr.random(), - Fr.random(), - Fr.random(), - Fr.random(), - ); - } - - static getFields(fields: FieldsOf) { - return [ - fields.noteHashTreeRoot, - fields.nullifierTreeRoot, - fields.contractTreeRoot, - fields.l1ToL2MessageTreeRoot, - fields.archiveRoot, - fields.privateKernelVkTreeRoot, - fields.publicDataTreeRoot, - fields.globalVariablesHash, - ] as const; - } - - toBuffer() { - return serializeToBuffer(...BlockHeader.getFields(this)); - } - - toString() { - // originally this was encoding as utf-8 (the default). This caused problems decoding back the data. - return this.toBuffer().toString(STRING_ENCODING); - } - - /** - * Return the block header as an array of items in the order they are serialized in noir. - * @returns Array of items in the order they are stored in the contract - */ - toArray(): Fr[] { - return [ - this.noteHashTreeRoot, - this.nullifierTreeRoot, - this.contractTreeRoot, - this.l1ToL2MessageTreeRoot, - this.archiveRoot, // TODO(#3441) Note private_kernel_vk_tree_root, is not included yet as - // it is not present in noir, - this.publicDataTreeRoot, - this.globalVariablesHash, - ]; - } - - static fromBuffer(buffer: Buffer | BufferReader) { - const reader = BufferReader.asReader(buffer); - return new BlockHeader( - Fr.fromBuffer(reader), - Fr.fromBuffer(reader), - Fr.fromBuffer(reader), - Fr.fromBuffer(reader), - Fr.fromBuffer(reader), - Fr.fromBuffer(reader), - Fr.fromBuffer(reader), - Fr.fromBuffer(reader), - ); - } - - static fromString(str: string) { - return BlockHeader.fromBuffer(Buffer.from(str, STRING_ENCODING)); - } - - isEmpty() { - return ( - this.noteHashTreeRoot.isZero() && - this.nullifierTreeRoot.isZero() && - this.contractTreeRoot.isZero() && - this.l1ToL2MessageTreeRoot.isZero() && - this.archiveRoot.isZero() && - this.privateKernelVkTreeRoot.isZero() && - this.publicDataTreeRoot.isZero() && - this.globalVariablesHash.isZero() - ); - } - - static empty() { - return new BlockHeader(Fr.ZERO, Fr.ZERO, Fr.ZERO, Fr.ZERO, Fr.ZERO, Fr.ZERO, Fr.ZERO, Fr.ZERO); - } -} diff --git a/yarn-project/circuits.js/src/structs/kernel/combined_accumulated_data.ts b/yarn-project/circuits.js/src/structs/kernel/combined_accumulated_data.ts index 0faefe3a5ba..fb86aefbad9 100644 --- a/yarn-project/circuits.js/src/structs/kernel/combined_accumulated_data.ts +++ b/yarn-project/circuits.js/src/structs/kernel/combined_accumulated_data.ts @@ -7,6 +7,7 @@ import { MAX_NEW_L2_TO_L1_MSGS_PER_CALL, MAX_NEW_L2_TO_L1_MSGS_PER_TX, MAX_NEW_NULLIFIERS_PER_TX, + MAX_NULLIFIER_KEY_VALIDATION_REQUESTS_PER_TX, MAX_OPTIONALLY_REVEALED_DATA_LENGTH_PER_TX, MAX_PRIVATE_CALL_STACK_LENGTH_PER_TX, MAX_PUBLIC_CALL_STACK_LENGTH_PER_TX, @@ -22,6 +23,7 @@ import { EthAddress, Fr, FunctionData, + NullifierKeyValidationRequestContext, SideEffect, SideEffectLinkedToNoteHash, } from '../index.js'; @@ -48,16 +50,16 @@ export class NewContractData { */ portalContractAddress: EthAddress | AztecAddress, /** - * Function tree root of the contract. + * Contract class id. */ - public functionTreeRoot: Fr, + public contractClassId: Fr, ) { // Handle circuits emitting this as an AztecAddress this.portalContractAddress = new EthAddress(portalContractAddress.toBuffer()); } toBuffer() { - return serializeToBuffer(this.contractAddress, this.portalContractAddress, this.functionTreeRoot); + return serializeToBuffer(this.contractAddress, this.portalContractAddress, this.contractClassId); } /** @@ -299,6 +301,13 @@ export class CombinedAccumulatedData { * All the read requests made in this transaction. */ public readRequests: Tuple, + /** + * All the nullifier key validation requests made in this transaction. + */ + public nullifierKeyValidationRequests: Tuple< + NullifierKeyValidationRequestContext, + typeof MAX_NULLIFIER_KEY_VALIDATION_REQUESTS_PER_TX + >, /** * The new commitments made in this transaction. */ @@ -359,6 +368,7 @@ export class CombinedAccumulatedData { return serializeToBuffer( this.aggregationObject, this.readRequests, + this.nullifierKeyValidationRequests, this.newCommitments, this.newNullifiers, this.privateCallStack, @@ -389,6 +399,7 @@ export class CombinedAccumulatedData { return new CombinedAccumulatedData( reader.readObject(AggregationObject), reader.readArray(MAX_READ_REQUESTS_PER_TX, SideEffect), + reader.readArray(MAX_NULLIFIER_KEY_VALIDATION_REQUESTS_PER_TX, NullifierKeyValidationRequestContext), reader.readArray(MAX_NEW_COMMITMENTS_PER_TX, SideEffect), reader.readArray(MAX_NEW_NULLIFIERS_PER_TX, SideEffectLinkedToNoteHash), reader.readArray(MAX_PRIVATE_CALL_STACK_LENGTH_PER_TX, CallRequest), @@ -409,6 +420,7 @@ export class CombinedAccumulatedData { return new CombinedAccumulatedData( finalData.aggregationObject, makeTuple(MAX_READ_REQUESTS_PER_TX, SideEffect.empty), + makeTuple(MAX_NULLIFIER_KEY_VALIDATION_REQUESTS_PER_TX, NullifierKeyValidationRequestContext.empty), finalData.newCommitments, finalData.newNullifiers, finalData.privateCallStack, @@ -438,6 +450,7 @@ export class CombinedAccumulatedData { return new CombinedAccumulatedData( AggregationObject.makeFake(), makeTuple(MAX_READ_REQUESTS_PER_TX, SideEffect.empty), + makeTuple(MAX_NULLIFIER_KEY_VALIDATION_REQUESTS_PER_TX, NullifierKeyValidationRequestContext.empty), makeTuple(MAX_NEW_COMMITMENTS_PER_TX, SideEffect.empty), makeTuple(MAX_NEW_NULLIFIERS_PER_TX, SideEffectLinkedToNoteHash.empty), makeTuple(MAX_PRIVATE_CALL_STACK_LENGTH_PER_TX, CallRequest.empty), diff --git a/yarn-project/circuits.js/src/structs/kernel/combined_constant_data.ts b/yarn-project/circuits.js/src/structs/kernel/combined_constant_data.ts index 6065e6ec04a..189d6a27764 100644 --- a/yarn-project/circuits.js/src/structs/kernel/combined_constant_data.ts +++ b/yarn-project/circuits.js/src/structs/kernel/combined_constant_data.ts @@ -1,7 +1,7 @@ import { BufferReader, serializeToBuffer } from '@aztec/foundation/serialize'; +import { Header } from '../header.js'; import { TxContext } from '../tx_context.js'; -import { BlockHeader } from './block_header.js'; /** * Data that is constant/not modified by neither of the kernels. @@ -9,17 +9,22 @@ import { BlockHeader } from './block_header.js'; export class CombinedConstantData { constructor( /** - * Roots of the trees relevant for both kernel circuits. + * Header of a block whose state is used during execution (not the block the transaction is included in). */ - public blockHeader: BlockHeader, + public historicalHeader: Header, /** * Context of the transaction. + * + * Note: `chainId` and `version` in txContext are not redundant to the values in + * self.historical_header.global_variables because they can be different in case of a protocol upgrade. In such + * a situation we could be using header from a block before the upgrade took place but be using the updated + * protocol to execute and prove the transaction. */ public txContext: TxContext, ) {} toBuffer() { - return serializeToBuffer(this.blockHeader, this.txContext); + return serializeToBuffer(this.historicalHeader, this.txContext); } /** @@ -29,10 +34,10 @@ export class CombinedConstantData { */ static fromBuffer(buffer: Buffer | BufferReader): CombinedConstantData { const reader = BufferReader.asReader(buffer); - return new CombinedConstantData(reader.readObject(BlockHeader), reader.readObject(TxContext)); + return new CombinedConstantData(reader.readObject(Header), reader.readObject(TxContext)); } static empty() { - return new CombinedConstantData(BlockHeader.empty(), TxContext.empty()); + return new CombinedConstantData(Header.empty(), TxContext.empty()); } } diff --git a/yarn-project/circuits.js/src/structs/kernel/private_kernel.ts b/yarn-project/circuits.js/src/structs/kernel/private_kernel.ts index 973928d7da8..13a6cb87185 100644 --- a/yarn-project/circuits.js/src/structs/kernel/private_kernel.ts +++ b/yarn-project/circuits.js/src/structs/kernel/private_kernel.ts @@ -1,4 +1,4 @@ -import { Fr } from '@aztec/foundation/fields'; +import { Fr, GrumpkinScalar } from '@aztec/foundation/fields'; import { BufferReader, Tuple, serializeToBuffer } from '@aztec/foundation/serialize'; import { FieldsOf } from '@aztec/foundation/types'; @@ -7,11 +7,13 @@ import { FUNCTION_TREE_HEIGHT, MAX_NEW_COMMITMENTS_PER_TX, MAX_NEW_NULLIFIERS_PER_TX, + MAX_NULLIFIER_KEY_VALIDATION_REQUESTS_PER_TX, MAX_PRIVATE_CALL_STACK_LENGTH_PER_CALL, MAX_PUBLIC_CALL_STACK_LENGTH_PER_CALL, MAX_READ_REQUESTS_PER_CALL, MAX_READ_REQUESTS_PER_TX, } from '../../constants.gen.js'; +import { GrumpkinPrivateKey } from '../../types/grumpkin_private_key.js'; import { CallRequest } from '../call_request.js'; import { PrivateCallStackItem } from '../call_stack_item.js'; import { MembershipWitness } from '../membership_witness.js'; @@ -146,6 +148,16 @@ export class PrivateKernelInputsInit { toBuffer() { return serializeToBuffer(this.txRequest, this.privateCall); } + + /** + * Deserializes from a buffer or reader. + * @param buffer - Buffer or reader to read from. + * @returns The deserialized instance. + */ + static fromBuffer(buffer: Buffer | BufferReader): PrivateKernelInputsInit { + const reader = BufferReader.asReader(buffer); + return new PrivateKernelInputsInit(reader.readObject(TxRequest), reader.readObject(PrivateCallData)); + } } /** @@ -215,6 +227,10 @@ export class PrivateKernelInputsOrdering { * Contains hints for the transient nullifiers to localize corresponding commitments. */ public nullifierCommitmentHints: Tuple, + /** + * The master nullifier secret keys for the nullifier key validation requests. + */ + public masterNullifierSecretKeys: Tuple, ) {} /** @@ -230,6 +246,26 @@ export class PrivateKernelInputsOrdering { this.sortedNewNullifiers, this.sortedNewNullifiersIndexes, this.nullifierCommitmentHints, + this.masterNullifierSecretKeys, + ); + } + + /** + * Deserializes from a buffer or reader. + * @param buffer - Buffer or reader to read from. + * @returns The deserialized instance. + */ + static fromBuffer(buffer: Buffer | BufferReader): PrivateKernelInputsOrdering { + const reader = BufferReader.asReader(buffer); + return new PrivateKernelInputsOrdering( + reader.readObject(PreviousKernelData), + reader.readArray(MAX_NEW_COMMITMENTS_PER_TX, SideEffect), + reader.readNumbers(MAX_NEW_COMMITMENTS_PER_TX), + reader.readArray(MAX_READ_REQUESTS_PER_TX, Fr), + reader.readArray(MAX_NEW_NULLIFIERS_PER_TX, SideEffectLinkedToNoteHash), + reader.readNumbers(MAX_NEW_NULLIFIERS_PER_TX), + reader.readArray(MAX_NEW_NULLIFIERS_PER_TX, Fr), + reader.readArray(MAX_NULLIFIER_KEY_VALIDATION_REQUESTS_PER_TX, GrumpkinScalar), ); } } diff --git a/yarn-project/circuits.js/src/structs/nullifier_key_validation_request.ts b/yarn-project/circuits.js/src/structs/nullifier_key_validation_request.ts new file mode 100644 index 00000000000..5e93ead5855 --- /dev/null +++ b/yarn-project/circuits.js/src/structs/nullifier_key_validation_request.ts @@ -0,0 +1,101 @@ +import { AztecAddress } from '@aztec/foundation/aztec-address'; +import { Fr, GrumpkinScalar, Point } from '@aztec/foundation/fields'; +import { BufferReader, FieldReader, serializeToBuffer } from '@aztec/foundation/serialize'; + +import { GrumpkinPrivateKey } from '../types/grumpkin_private_key.js'; + +/** + * Request for validating a nullifier key pair used in the app. + */ +export class NullifierKeyValidationRequest { + constructor( + /** + * Public key of the nullifier key. + */ + public readonly publicKey: Point, + /** + * Secret key of the nullifier key. + */ + public readonly secretKey: GrumpkinPrivateKey, + ) {} + + toBuffer() { + return serializeToBuffer(this.publicKey, this.secretKey); + } + + static fromBuffer(buffer: Buffer | BufferReader) { + const reader = BufferReader.asReader(buffer); + return new NullifierKeyValidationRequest(Point.fromBuffer(reader), GrumpkinScalar.fromBuffer(reader)); + } + + toFields(): Fr[] { + return [this.publicKey.toFields(), this.secretKey.high, this.secretKey.low].flat(); + } + + static fromFields(fields: Fr[] | FieldReader): NullifierKeyValidationRequest { + const reader = FieldReader.asReader(fields); + return new NullifierKeyValidationRequest(Point.fromFields(reader), reader.readFq()); + } + + isEmpty() { + return this.publicKey.isZero() && this.secretKey.isZero(); + } + + static empty() { + return new NullifierKeyValidationRequest(Point.ZERO, GrumpkinScalar.ZERO); + } +} + +/** + * Request for validating a nullifier key pair used in the app. + */ +export class NullifierKeyValidationRequestContext { + constructor( + /** + * Public key of the nullifier key. + */ + public readonly publicKey: Point, + /** + * Secret key of the nullifier key. + */ + public readonly secretKey: GrumpkinPrivateKey, + /** + * The storage contract address the nullifier key is for. + */ + public readonly contractAddress: AztecAddress, + ) {} + + toBuffer() { + return serializeToBuffer(this.publicKey, this.secretKey, this.contractAddress); + } + + static fromBuffer(buffer: Buffer | BufferReader) { + const reader = BufferReader.asReader(buffer); + return new NullifierKeyValidationRequestContext( + Point.fromBuffer(reader), + GrumpkinScalar.fromBuffer(reader), + AztecAddress.fromBuffer(reader), + ); + } + + toFields(): Fr[] { + return [this.publicKey.toFields(), this.secretKey.high, this.secretKey.low, this.contractAddress].flat(); + } + + static fromFields(fields: Fr[] | FieldReader): NullifierKeyValidationRequestContext { + const reader = FieldReader.asReader(fields); + return new NullifierKeyValidationRequestContext( + Point.fromFields(reader), + reader.readFq(), + AztecAddress.fromFields(reader), + ); + } + + isEmpty() { + return this.publicKey.isZero() && this.secretKey.isZero() && this.contractAddress.isZero(); + } + + static empty() { + return new NullifierKeyValidationRequestContext(Point.ZERO, GrumpkinScalar.ZERO, AztecAddress.ZERO); + } +} diff --git a/yarn-project/circuits.js/src/structs/partial_state_reference.ts b/yarn-project/circuits.js/src/structs/partial_state_reference.ts index e6092a7246a..7e375eeb12a 100644 --- a/yarn-project/circuits.js/src/structs/partial_state_reference.ts +++ b/yarn-project/circuits.js/src/structs/partial_state_reference.ts @@ -27,7 +27,34 @@ export class PartialStateReference { ); } + static empty(): PartialStateReference { + return new PartialStateReference( + AppendOnlyTreeSnapshot.zero(), + AppendOnlyTreeSnapshot.zero(), + AppendOnlyTreeSnapshot.zero(), + AppendOnlyTreeSnapshot.zero(), + ); + } + toBuffer() { return serializeToBuffer(this.noteHashTree, this.nullifierTree, this.contractTree, this.publicDataTree); } + + toFieldArray() { + return [ + ...this.noteHashTree.toFieldArray(), + ...this.nullifierTree.toFieldArray(), + ...this.contractTree.toFieldArray(), + ...this.publicDataTree.toFieldArray(), + ]; + } + + isEmpty(): boolean { + return ( + this.noteHashTree.isZero() && + this.nullifierTree.isZero() && + this.contractTree.isZero() && + this.publicDataTree.isZero() + ); + } } diff --git a/yarn-project/circuits.js/src/structs/private_circuit_public_inputs.ts b/yarn-project/circuits.js/src/structs/private_circuit_public_inputs.ts index db10fdfb428..9ddd2fbcdde 100644 --- a/yarn-project/circuits.js/src/structs/private_circuit_public_inputs.ts +++ b/yarn-project/circuits.js/src/structs/private_circuit_public_inputs.ts @@ -8,6 +8,7 @@ import { MAX_NEW_COMMITMENTS_PER_CALL, MAX_NEW_L2_TO_L1_MSGS_PER_CALL, MAX_NEW_NULLIFIERS_PER_CALL, + MAX_NULLIFIER_KEY_VALIDATION_REQUESTS_PER_CALL, MAX_PRIVATE_CALL_STACK_LENGTH_PER_CALL, MAX_PUBLIC_CALL_STACK_LENGTH_PER_CALL, MAX_READ_REQUESTS_PER_CALL, @@ -15,7 +16,8 @@ import { RETURN_VALUES_LENGTH, } from '../constants.gen.js'; import { CallContext } from './call_context.js'; -import { BlockHeader, SideEffect, SideEffectLinkedToNoteHash } from './index.js'; +import { Header, SideEffect, SideEffectLinkedToNoteHash } from './index.js'; +import { NullifierKeyValidationRequest } from './nullifier_key_validation_request.js'; import { ContractDeploymentData } from './tx_context.js'; /** @@ -40,6 +42,13 @@ export class PrivateCircuitPublicInputs { * Read requests created by the corresponding function call. */ public readRequests: Tuple, + /** + * Nullifier key validation requests created by the corresponding function call. + */ + public nullifierKeyValidationRequests: Tuple< + NullifierKeyValidationRequest, + typeof MAX_NULLIFIER_KEY_VALIDATION_REQUESTS_PER_CALL + >, /** * New commitments created by the corresponding function call. */ @@ -85,15 +94,19 @@ export class PrivateCircuitPublicInputs { */ public unencryptedLogPreimagesLength: Fr, /** - * Historical roots of the data trees, used to calculate the block hash the user is proving against. + * Header of a block whose state is used during private execution (not the block the transaction is included in). */ - public blockHeader: BlockHeader, + public historicalHeader: Header, /** * Deployment data of contracts being deployed in this kernel iteration. */ public contractDeploymentData: ContractDeploymentData, /** * Chain Id of the instance. + * + * Note: The following 2 values are not redundant to the values in self.historical_header.global_variables because + * they can be different in case of a protocol upgrade. In such a situation we could be using header from a block + * before the upgrade took place but be using the updated protocol to execute and prove the transaction. */ public chainId: Fr, /** @@ -123,6 +136,7 @@ export class PrivateCircuitPublicInputs { reader.readObject(Fr), reader.readArray(RETURN_VALUES_LENGTH, Fr), reader.readArray(MAX_READ_REQUESTS_PER_CALL, SideEffect), + reader.readArray(MAX_NULLIFIER_KEY_VALIDATION_REQUESTS_PER_CALL, NullifierKeyValidationRequest), reader.readArray(MAX_NEW_COMMITMENTS_PER_CALL, SideEffect), reader.readArray(MAX_NEW_NULLIFIERS_PER_CALL, SideEffectLinkedToNoteHash), reader.readArray(MAX_PRIVATE_CALL_STACK_LENGTH_PER_CALL, Fr), @@ -133,7 +147,7 @@ export class PrivateCircuitPublicInputs { reader.readArray(NUM_FIELDS_PER_SHA256, Fr), reader.readObject(Fr), reader.readObject(Fr), - reader.readObject(BlockHeader), + reader.readObject(Header), reader.readObject(ContractDeploymentData), reader.readObject(Fr), reader.readObject(Fr), @@ -150,6 +164,7 @@ export class PrivateCircuitPublicInputs { Fr.ZERO, makeTuple(RETURN_VALUES_LENGTH, Fr.zero), makeTuple(MAX_READ_REQUESTS_PER_CALL, SideEffect.empty), + makeTuple(MAX_NULLIFIER_KEY_VALIDATION_REQUESTS_PER_CALL, NullifierKeyValidationRequest.empty), makeTuple(MAX_NEW_COMMITMENTS_PER_CALL, SideEffect.empty), makeTuple(MAX_NEW_NULLIFIERS_PER_CALL, SideEffectLinkedToNoteHash.empty), makeTuple(MAX_PRIVATE_CALL_STACK_LENGTH_PER_CALL, Fr.zero), @@ -160,7 +175,7 @@ export class PrivateCircuitPublicInputs { makeTuple(NUM_FIELDS_PER_SHA256, Fr.zero), Fr.ZERO, Fr.ZERO, - BlockHeader.empty(), + Header.empty(), ContractDeploymentData.empty(), Fr.ZERO, Fr.ZERO, @@ -168,25 +183,26 @@ export class PrivateCircuitPublicInputs { } isEmpty() { - const isFrArrayEmpty = (arr: Fr[]) => isArrayEmpty(arr, item => item.isZero()); - const isSideEffectArrayEmpty = (arr: SideEffect[]) => isArrayEmpty(arr, item => item.isEmpty()); - const isSideEffectLinkedArrayEmpty = (arr: SideEffectLinkedToNoteHash[]) => - isArrayEmpty(arr, item => item.isEmpty()); + // eslint-disable-next-line jsdoc/require-jsdoc + const isEmptyArray = (arr: { isEmpty: (...args: any[]) => boolean }[]) => isArrayEmpty(arr, item => item.isEmpty()); + // eslint-disable-next-line jsdoc/require-jsdoc + const isZeroArray = (arr: { isZero: (...args: any[]) => boolean }[]) => isArrayEmpty(arr, item => item.isZero()); return ( this.callContext.isEmpty() && this.argsHash.isZero() && - isFrArrayEmpty(this.returnValues) && - isSideEffectArrayEmpty(this.readRequests) && - isSideEffectArrayEmpty(this.newCommitments) && - isSideEffectLinkedArrayEmpty(this.newNullifiers) && - isFrArrayEmpty(this.privateCallStackHashes) && - isFrArrayEmpty(this.publicCallStackHashes) && - isFrArrayEmpty(this.newL2ToL1Msgs) && - isFrArrayEmpty(this.encryptedLogsHash) && - isFrArrayEmpty(this.unencryptedLogsHash) && + isZeroArray(this.returnValues) && + isEmptyArray(this.readRequests) && + isEmptyArray(this.nullifierKeyValidationRequests) && + isEmptyArray(this.newCommitments) && + isEmptyArray(this.newNullifiers) && + isZeroArray(this.privateCallStackHashes) && + isZeroArray(this.publicCallStackHashes) && + isZeroArray(this.newL2ToL1Msgs) && + isZeroArray(this.encryptedLogsHash) && + isZeroArray(this.unencryptedLogsHash) && this.encryptedLogPreimagesLength.isZero() && this.unencryptedLogPreimagesLength.isZero() && - this.blockHeader.isEmpty() && + this.historicalHeader.isEmpty() && this.contractDeploymentData.isEmpty() && this.chainId.isZero() && this.version.isZero() @@ -204,6 +220,7 @@ export class PrivateCircuitPublicInputs { fields.argsHash, fields.returnValues, fields.readRequests, + fields.nullifierKeyValidationRequests, fields.newCommitments, fields.newNullifiers, fields.privateCallStackHashes, @@ -214,7 +231,7 @@ export class PrivateCircuitPublicInputs { fields.unencryptedLogsHash, fields.encryptedLogPreimagesLength, fields.unencryptedLogPreimagesLength, - fields.blockHeader, + fields.historicalHeader, fields.contractDeploymentData, fields.chainId, fields.version, diff --git a/yarn-project/circuits.js/src/structs/public_circuit_public_inputs.ts b/yarn-project/circuits.js/src/structs/public_circuit_public_inputs.ts index 134fcc1ceaa..572e76e273b 100644 --- a/yarn-project/circuits.js/src/structs/public_circuit_public_inputs.ts +++ b/yarn-project/circuits.js/src/structs/public_circuit_public_inputs.ts @@ -15,7 +15,7 @@ import { RETURN_VALUES_LENGTH, } from '../constants.gen.js'; import { CallContext } from './call_context.js'; -import { BlockHeader, SideEffect, SideEffectLinkedToNoteHash } from './index.js'; +import { Header, SideEffect, SideEffectLinkedToNoteHash } from './index.js'; /** * Contract storage read operation on a specific contract. @@ -198,9 +198,10 @@ export class PublicCircuitPublicInputs { */ public unencryptedLogPreimagesLength: Fr, /** - * Root of the commitment trees when the call started. + * Header of a block whose state is used during public execution. Set by sequencer to be a header of a block + * previous to the one in which the tx is included. */ - public blockHeader: BlockHeader, + public historicalHeader: Header, /** * Address of the prover. */ @@ -233,7 +234,7 @@ export class PublicCircuitPublicInputs { makeTuple(MAX_NEW_L2_TO_L1_MSGS_PER_CALL, Fr.zero), makeTuple(2, Fr.zero), Fr.ZERO, - BlockHeader.empty(), + Header.empty(), AztecAddress.ZERO, ); } @@ -255,7 +256,7 @@ export class PublicCircuitPublicInputs { isFrArrayEmpty(this.newL2ToL1Msgs) && isFrArrayEmpty(this.unencryptedLogsHash) && this.unencryptedLogPreimagesLength.isZero() && - this.blockHeader.isEmpty() && + this.historicalHeader.isEmpty() && this.proverAddress.isZero() ); } @@ -278,7 +279,7 @@ export class PublicCircuitPublicInputs { fields.newL2ToL1Msgs, fields.unencryptedLogsHash, fields.unencryptedLogPreimagesLength, - fields.blockHeader, + fields.historicalHeader, fields.proverAddress, ] as const; } diff --git a/yarn-project/circuits.js/src/structs/rollup/append_only_tree_snapshot.ts b/yarn-project/circuits.js/src/structs/rollup/append_only_tree_snapshot.ts index ba845cbabd3..09216a8a0a1 100644 --- a/yarn-project/circuits.js/src/structs/rollup/append_only_tree_snapshot.ts +++ b/yarn-project/circuits.js/src/structs/rollup/append_only_tree_snapshot.ts @@ -30,6 +30,10 @@ export class AppendOnlyTreeSnapshot { return serializeToBuffer(this.root, this.nextAvailableLeafIndex); } + toFieldArray(): Fr[] { + return [this.root, new Fr(this.nextAvailableLeafIndex)]; + } + toString(): string { return this.toBuffer().toString(STRING_ENCODING); } @@ -43,7 +47,11 @@ export class AppendOnlyTreeSnapshot { return AppendOnlyTreeSnapshot.fromBuffer(Buffer.from(str, STRING_ENCODING)); } - static empty() { + static zero() { return new AppendOnlyTreeSnapshot(Fr.ZERO, 0); } + + isZero(): boolean { + return this.root.isZero() && this.nextAvailableLeafIndex === 0; + } } diff --git a/yarn-project/circuits.js/src/structs/side_effects.ts b/yarn-project/circuits.js/src/structs/side_effects.ts index c6b85800082..25b3df86a88 100644 --- a/yarn-project/circuits.js/src/structs/side_effects.ts +++ b/yarn-project/circuits.js/src/structs/side_effects.ts @@ -1,4 +1,4 @@ -import { BufferReader, serializeToBuffer } from '@aztec/foundation/serialize'; +import { BufferReader, FieldReader, serializeToBuffer } from '@aztec/foundation/serialize'; import { Fr } from './index.js'; @@ -13,7 +13,7 @@ export interface SideEffectType { /** Convert to a buffer */ toBuffer(): Buffer; /** Convert to a field array */ - toFieldArray(): Fr[]; + toFields(): Fr[]; /** Are all of the fields of the SideEffect zero? */ isEmpty(): boolean; } @@ -46,10 +46,15 @@ export class SideEffect implements SideEffectType { * Convert to an array of fields. * @returns The array of fields. */ - toFieldArray(): Fr[] { + toFields(): Fr[] { return [this.value, this.counter]; } + static fromFields(fields: Fr[] | FieldReader): SideEffect { + const reader = FieldReader.asReader(fields); + return new SideEffect(reader.readField(), reader.readField()); + } + /** * Returns whether this instance of side-effect is empty. * @returns True if the value and counter both are zero. @@ -109,10 +114,15 @@ export class SideEffectLinkedToNoteHash implements SideEffectType { * Convert to an array of fields. * @returns The array of fields. */ - toFieldArray(): Fr[] { + toFields(): Fr[] { return [this.value, this.noteHash, this.counter]; } + static fromFields(fields: Fr[] | FieldReader): SideEffectLinkedToNoteHash { + const reader = FieldReader.asReader(fields); + return new SideEffectLinkedToNoteHash(reader.readField(), reader.readField(), reader.readField()); + } + /** * Returns whether this instance of side-effect is empty. * @returns True if the value, note hash and counter are all zero. diff --git a/yarn-project/circuits.js/src/structs/state_reference.ts b/yarn-project/circuits.js/src/structs/state_reference.ts index 21c9359434f..284d988e07f 100644 --- a/yarn-project/circuits.js/src/structs/state_reference.ts +++ b/yarn-project/circuits.js/src/structs/state_reference.ts @@ -1,3 +1,4 @@ +import { Fr } from '@aztec/foundation/fields'; import { BufferReader, serializeToBuffer } from '@aztec/foundation/serialize'; import { PartialStateReference } from './partial_state_reference.js'; @@ -15,12 +16,24 @@ export class StateReference { ) {} toBuffer() { - // Note: The order here must match the order in the HeaderDecoder solidity library. + // Note: The order here must match the order in the HeaderLib solidity library. return serializeToBuffer(this.l1ToL2MessageTree, this.partial); } + toFieldArray(): Fr[] { + return [...this.l1ToL2MessageTree.toFieldArray(), ...this.partial.toFieldArray()]; + } + static fromBuffer(buffer: Buffer | BufferReader): StateReference { const reader = BufferReader.asReader(buffer); return new StateReference(reader.readObject(AppendOnlyTreeSnapshot), reader.readObject(PartialStateReference)); } + + static empty(): StateReference { + return new StateReference(AppendOnlyTreeSnapshot.zero(), PartialStateReference.empty()); + } + + isEmpty(): boolean { + return this.l1ToL2MessageTree.isZero() && this.partial.isEmpty(); + } } diff --git a/yarn-project/circuits.js/src/structs/tx_context.ts b/yarn-project/circuits.js/src/structs/tx_context.ts index a5588130188..f84a47b5b8c 100644 --- a/yarn-project/circuits.js/src/structs/tx_context.ts +++ b/yarn-project/circuits.js/src/structs/tx_context.ts @@ -6,8 +6,6 @@ import { AztecAddress, EthAddress, Fr, Point } from './index.js'; /** * Contract deployment data in a TxContext - * cpp/src/aztec3/circuits/abis/contract_deployment_data.hpp. - * * Not to be confused with NewContractData. */ export class ContractDeploymentData { @@ -15,12 +13,12 @@ export class ContractDeploymentData { public portalContractAddress: EthAddress; constructor( - /** Public key of the contract deployer (used when deploying account contracts). */ - public deployerPublicKey: PublicKey, - /** Hash of the constructor verification key. */ - public constructorVkHash: Fr, - /** Function tree root. */ - public functionTreeRoot: Fr, + /** Public key of the contract. */ + public publicKey: PublicKey, + /** Hash of the initialization payload. */ + public initializationHash: Fr, + /** Contract class identifier. */ + public contractClassId: Fr, /** Contract address salt (used when deriving a contract address). */ public contractAddressSalt: Fr, /** @@ -34,9 +32,9 @@ export class ContractDeploymentData { toBuffer() { return serializeToBuffer( - this.deployerPublicKey, - this.constructorVkHash, - this.functionTreeRoot, + this.publicKey, + this.initializationHash, + this.contractClassId, this.contractAddressSalt, this.portalContractAddress, ); @@ -52,9 +50,9 @@ export class ContractDeploymentData { isEmpty() { return ( - this.deployerPublicKey.isZero() && - this.constructorVkHash.isZero() && - this.functionTreeRoot.isZero() && + this.publicKey.isZero() && + this.initializationHash.isZero() && + this.contractClassId.isZero() && this.contractAddressSalt.isZero() && this.portalContractAddress.isZero() ); diff --git a/yarn-project/circuits.js/src/tests/factories.ts b/yarn-project/circuits.js/src/tests/factories.ts index 6586d23ceb1..3770d632b8d 100644 --- a/yarn-project/circuits.js/src/tests/factories.ts +++ b/yarn-project/circuits.js/src/tests/factories.ts @@ -1,10 +1,9 @@ import { makeHalfFullTuple, makeTuple, range } from '@aztec/foundation/array'; import { AztecAddress } from '@aztec/foundation/aztec-address'; +import { toBufferBE } from '@aztec/foundation/bigint-buffer'; import { EthAddress } from '@aztec/foundation/eth-address'; import { numToUInt32BE } from '@aztec/foundation/serialize'; -import { randomBytes } from 'crypto'; - import { SchnorrSignature } from '../barretenberg/index.js'; import { ARCHIVE_HEIGHT, @@ -13,7 +12,6 @@ import { AppendOnlyTreeSnapshot, BaseOrMergeRollupPublicInputs, BaseRollupInputs, - BlockHeader, CONTRACT_SUBTREE_SIBLING_PATH_LENGTH, CONTRACT_TREE_HEIGHT, CallContext, @@ -33,6 +31,8 @@ import { FunctionData, FunctionSelector, G1AffineElement, + GrumpkinPrivateKey, + GrumpkinScalar, KernelCircuitPublicInputs, L1_TO_L2_MSG_SUBTREE_SIBLING_PATH_LENGTH, MAX_NEW_COMMITMENTS_PER_CALL, @@ -42,6 +42,8 @@ import { MAX_NEW_L2_TO_L1_MSGS_PER_TX, MAX_NEW_NULLIFIERS_PER_CALL, MAX_NEW_NULLIFIERS_PER_TX, + MAX_NULLIFIER_KEY_VALIDATION_REQUESTS_PER_CALL, + MAX_NULLIFIER_KEY_VALIDATION_REQUESTS_PER_TX, MAX_OPTIONALLY_REVEALED_DATA_LENGTH_PER_TX, MAX_PRIVATE_CALL_STACK_LENGTH_PER_CALL, MAX_PRIVATE_CALL_STACK_LENGTH_PER_TX, @@ -62,6 +64,8 @@ import { NUMBER_OF_L1_L2_MESSAGES_PER_ROLLUP, NUM_FIELDS_PER_SHA256, NewContractData, + NullifierKeyValidationRequest, + NullifierKeyValidationRequestContext, NullifierLeafPreimage, OptionallyRevealedData, PUBLIC_DATA_SUBTREE_SIBLING_PATH_LENGTH, @@ -134,32 +138,13 @@ export function makeTxContext(seed: number): TxContext { return new TxContext(false, false, true, makeContractDeploymentData(seed), Fr.ZERO, Fr.ZERO); } -/** - * Creates an arbitrary combined historical tree roots object from the given seed. - * Note: "Combined" indicates that it's the combined output of both private and public circuit flows. - * @param seed - The seed to use for generating the combined historical tree roots. - * @returns A combined historical tree roots object. - */ -export function makeBlockHeader(seed: number): BlockHeader { - return new BlockHeader( - fr(seed), - fr(seed + 1), - fr(seed + 2), - fr(seed + 3), - fr(seed + 4), - fr(seed + 5), - fr(seed + 6), - fr(seed + 7), - ); -} - /** * Creates arbitrary constant data with the given seed. * @param seed - The seed to use for generating the constant data. * @returns A constant data object. */ export function makeConstantData(seed = 1): CombinedConstantData { - return new CombinedConstantData(makeBlockHeader(seed), makeTxContext(seed + 4)); + return new CombinedConstantData(makeHeader(seed, undefined), makeTxContext(seed + 4)); } /** @@ -171,6 +156,28 @@ export function makeSelector(seed: number): FunctionSelector { return new FunctionSelector(seed); } +/** + * Creates arbitrary NullifierKeyValidationRequest from the given seed. + * @param seed - The seed to use for generating the NullifierKeyValidationRequest. + * @returns A NullifierKeyValidationRequest. + */ +function makeNullifierKeyValidationRequest(seed: number): NullifierKeyValidationRequest { + return new NullifierKeyValidationRequest(makePoint(seed), makeGrumpkinPrivateKey(seed + 2)); +} + +/** + * Creates arbitrary NullifierKeyValidationRequestContext from the given seed. + * @param seed - The seed to use for generating the NullifierKeyValidationRequestContext. + * @returns A NullifierKeyValidationRequestContext. + */ +function makeNullifierKeyValidationRequestContext(seed: number): NullifierKeyValidationRequestContext { + return new NullifierKeyValidationRequestContext( + makePoint(seed), + makeGrumpkinPrivateKey(seed + 2), + makeAztecAddress(seed + 4), + ); +} + /** * Creates arbitrary public data update request. * @param seed - The seed to use for generating the public data update request. @@ -234,7 +241,12 @@ export function makeAccumulatedData(seed = 1, full = false): CombinedAccumulated return new CombinedAccumulatedData( makeAggregationObject(seed), tupleGenerator(MAX_READ_REQUESTS_PER_TX, sideEffectFromNumber, seed + 0x80), - tupleGenerator(MAX_NEW_COMMITMENTS_PER_TX, sideEffectFromNumber, seed + 0x100), + tupleGenerator( + MAX_NULLIFIER_KEY_VALIDATION_REQUESTS_PER_TX, + makeNullifierKeyValidationRequestContext, + seed + 0x100, + ), + tupleGenerator(MAX_NEW_COMMITMENTS_PER_TX, sideEffectFromNumber, seed + 0x120), tupleGenerator(MAX_NEW_NULLIFIERS_PER_TX, sideEffectLinkedFromNumber, seed + 0x200), tupleGenerator(MAX_PRIVATE_CALL_STACK_LENGTH_PER_TX, makeCallRequest, seed + 0x400), tupleGenerator(MAX_PUBLIC_CALL_STACK_LENGTH_PER_TX, makeCallRequest, seed + 0x500), @@ -359,7 +371,7 @@ export function makePublicCircuitPublicInputs( tupleGenerator(MAX_NEW_L2_TO_L1_MSGS_PER_CALL, fr, seed + 0x900), tupleGenerator(2, fr, seed + 0x901), fr(seed + 0x902), - makeBlockHeader(seed + 0xa00), + makeHeader(seed + 0xa00, undefined), makeAztecAddress(seed + 0xb01), ); } @@ -469,6 +481,15 @@ export function makePoint(seed = 1): Point { return new Point(fr(seed), fr(seed + 1)); } +/** + * Creates an arbitrary grumpkin private key. + * @param seed - Seed to generate the values. + * @returns A GrumpkinPrivateKey. + */ +export function makeGrumpkinPrivateKey(seed = 1): GrumpkinPrivateKey { + return GrumpkinScalar.fromHighLow(fr(seed), fr(seed + 1)); +} + /** * Makes arbitrary previous kernel data. * @param seed - The seed to use for generating the previous kernel data. @@ -690,6 +711,11 @@ export function makePrivateCircuitPublicInputs(seed = 0): PrivateCircuitPublicIn argsHash: fr(seed + 0x100), returnValues: makeTuple(RETURN_VALUES_LENGTH, fr, seed + 0x200), readRequests: makeTuple(MAX_READ_REQUESTS_PER_CALL, sideEffectFromNumber, seed + 0x300), + nullifierKeyValidationRequests: makeTuple( + MAX_NULLIFIER_KEY_VALIDATION_REQUESTS_PER_CALL, + makeNullifierKeyValidationRequest, + seed + 0x300, + ), newCommitments: makeTuple(MAX_NEW_COMMITMENTS_PER_CALL, sideEffectFromNumber, seed + 0x400), newNullifiers: makeTuple(MAX_NEW_NULLIFIERS_PER_CALL, sideEffectLinkedFromNumber, seed + 0x500), privateCallStackHashes: makeTuple(MAX_PRIVATE_CALL_STACK_LENGTH_PER_CALL, fr, seed + 0x600), @@ -700,7 +726,7 @@ export function makePrivateCircuitPublicInputs(seed = 0): PrivateCircuitPublicIn unencryptedLogsHash: makeTuple(NUM_FIELDS_PER_SHA256, fr, seed + 0xa00), encryptedLogPreimagesLength: fr(seed + 0xb00), unencryptedLogPreimagesLength: fr(seed + 0xc00), - blockHeader: makeBlockHeader(seed + 0xd00), + historicalHeader: makeHeader(seed + 0xd00, undefined), contractDeploymentData: makeContractDeploymentData(seed + 0xe00), chainId: fr(seed + 0x1400), version: fr(seed + 0x1500), @@ -862,18 +888,17 @@ export function makeRootRollupInputs(seed = 0, globalVariables?: GlobalVariables /** * Makes root rollup public inputs. * @param seed - The seed to use for generating the root rollup public inputs. - * @param blockNumber - The block number to use for generating the root rollup public inputs. - * if blockNumber is undefined, it will be set to seed + 2. + * @param blockNumber - The block number to use in the global variables of a header. * @returns A root rollup public inputs. */ export function makeRootRollupPublicInputs( seed = 0, - globalVariables: GlobalVariables | undefined = undefined, + blockNumber: number | undefined = undefined, ): RootRollupPublicInputs { return RootRollupPublicInputs.from({ aggregationObject: makeAggregationObject(seed), archive: makeAppendOnlyTreeSnapshot(seed + 0x100), - header: makeHeader(seed + 0x200, globalVariables), + header: makeHeader(seed + 0x200, blockNumber), l1ToL2MessagesHash: [new Fr(3n), new Fr(4n)], }); } @@ -881,12 +906,12 @@ export function makeRootRollupPublicInputs( /** * Makes header. */ -export function makeHeader(seed = 0, globalVariables: GlobalVariables | undefined): Header { +export function makeHeader(seed = 0, blockNumber: number | undefined = undefined): Header { return new Header( makeAppendOnlyTreeSnapshot(seed + 0x100), - randomBytes(NUM_BYTES_PER_SHA256), - makeStateReference(seed + 0x200), - globalVariables ?? makeGlobalVariables((seed += 0x100)), + toBufferBE(BigInt(seed + 0x200), NUM_BYTES_PER_SHA256), + makeStateReference(seed + 0x300), + makeGlobalVariables((seed += 0x400), blockNumber), ); } diff --git a/yarn-project/circuits.js/src/tests/fixtures.ts b/yarn-project/circuits.js/src/tests/fixtures.ts new file mode 100644 index 00000000000..135bd6edbe5 --- /dev/null +++ b/yarn-project/circuits.js/src/tests/fixtures.ts @@ -0,0 +1,13 @@ +import { ContractArtifact } from '@aztec/foundation/abi'; +import { loadContractArtifact } from '@aztec/types/abi'; +import { NoirCompiledContract } from '@aztec/types/noir'; + +import { readFileSync } from 'fs'; +import { dirname, resolve } from 'path'; +import { fileURLToPath } from 'url'; + +export function getSampleContractArtifact(): ContractArtifact { + const path = resolve(dirname(fileURLToPath(import.meta.url)), '../../fixtures/Benchmarking.test.json'); + const content = JSON.parse(readFileSync(path).toString()) as NoirCompiledContract; + return loadContractArtifact(content); +} diff --git a/yarn-project/circuits.js/src/types/partial_address.ts b/yarn-project/circuits.js/src/types/partial_address.ts index e99d3786536..21877f72e88 100644 --- a/yarn-project/circuits.js/src/types/partial_address.ts +++ b/yarn-project/circuits.js/src/types/partial_address.ts @@ -1,7 +1,7 @@ import { Fr } from '@aztec/foundation/fields'; /** - * A type which along with public key forms a preimage of a contract address. See the link bellow for more details + * A type which along with public key forms a preimage of a contract address. See the link below for more details * https://github.com/AztecProtocol/aztec-packages/blob/master/docs/docs/concepts/foundation/accounts/keys.md#addresses-partial-addresses-and-public-keys */ export type PartialAddress = Fr; diff --git a/yarn-project/circuits.js/tsconfig.json b/yarn-project/circuits.js/tsconfig.json index 63f8ab3e9f7..831130c7c84 100644 --- a/yarn-project/circuits.js/tsconfig.json +++ b/yarn-project/circuits.js/tsconfig.json @@ -8,6 +8,9 @@ "references": [ { "path": "../foundation" + }, + { + "path": "../types" } ], "include": ["src"] diff --git a/yarn-project/cli/package.json b/yarn-project/cli/package.json index 25d26238357..948c527b570 100644 --- a/yarn-project/cli/package.json +++ b/yarn-project/cli/package.json @@ -37,6 +37,7 @@ "@aztec/accounts": "workspace:^", "@aztec/aztec.js": "workspace:^", "@aztec/circuit-types": "workspace:^", + "@aztec/circuits.js": "workspace:^", "@aztec/ethereum": "workspace:^", "@aztec/foundation": "workspace:^", "@aztec/l1-artifacts": "workspace:^", diff --git a/yarn-project/cli/src/bin/index.ts b/yarn-project/cli/src/bin/index.ts index 4e1c4b1ad28..0d79e796d50 100644 --- a/yarn-project/cli/src/bin/index.ts +++ b/yarn-project/cli/src/bin/index.ts @@ -5,7 +5,7 @@ import 'source-map-support/register.js'; import { getProgram } from '../index.js'; -const debugLogger = createDebugLogger('aztec:cli'); +const debugLogger = createDebugLogger('aztec:cli-client'); const log = createConsoleLogger(); /** CLI main entrypoint */ diff --git a/yarn-project/cli/src/client.test.ts b/yarn-project/cli/src/client.test.ts index 526c218ac35..bcace613eeb 100644 --- a/yarn-project/cli/src/client.test.ts +++ b/yarn-project/cli/src/client.test.ts @@ -1,5 +1,5 @@ +import { NodeInfo } from '@aztec/aztec.js'; import { PXE } from '@aztec/circuit-types'; -import { NodeInfo } from '@aztec/types/interfaces'; import { MockProxy, mock } from 'jest-mock-extended'; diff --git a/yarn-project/cli/src/cmds/add_contract.ts b/yarn-project/cli/src/cmds/add_contract.ts index 6ac361f1fbc..39f2fee8100 100644 --- a/yarn-project/cli/src/cmds/add_contract.ts +++ b/yarn-project/cli/src/cmds/add_contract.ts @@ -1,27 +1,45 @@ -import { AztecAddress, CompleteAddress, EthAddress, Fr, Point } from '@aztec/aztec.js'; +import { + AztecAddress, + ContractInstanceWithAddress, + EthAddress, + Fr, + Point, + getContractClassFromArtifact, +} from '@aztec/aztec.js'; +import { computeContractAddressFromInstance, computePublicKeysHash } from '@aztec/circuits.js/contract'; import { DebugLogger, LogFn } from '@aztec/foundation/log'; import { createCompatibleClient } from '../client.js'; import { getContractArtifact } from '../utils.js'; -/** - * - */ export async function addContract( rpcUrl: string, contractArtifactPath: string, - contractAddress: AztecAddress, - partialAddress: Fr, - publicKey: Point, + address: AztecAddress, + initializationHash: Fr, + salt: Fr, + publicKey: Point | undefined, portalContract: EthAddress | undefined, debugLogger: DebugLogger, log: LogFn, ) { const artifact = await getContractArtifact(contractArtifactPath, log); - const completeAddress = new CompleteAddress(contractAddress, publicKey ?? Fr.ZERO, partialAddress); - const portalContractAddress: EthAddress = portalContract ?? EthAddress.ZERO; + const instance: ContractInstanceWithAddress = { + version: 1, + salt, + initializationHash, + contractClassId: getContractClassFromArtifact(artifact).id, + portalContractAddress: portalContract ?? EthAddress.ZERO, + publicKeysHash: computePublicKeysHash(publicKey), + address, + }; + const computed = computeContractAddressFromInstance(instance); + if (!computed.equals(address)) { + throw new Error(`Contract address ${address.toString()} does not match computed address ${computed.toString()}`); + } + const client = await createCompatibleClient(rpcUrl, debugLogger); - await client.addContracts([{ artifact, completeAddress, portalContract: portalContractAddress }]); - log(`\nContract added to PXE at ${contractAddress.toString()}\n`); + await client.addContracts([{ artifact, instance }]); + log(`\nContract added to PXE at ${address.toString()} with class ${instance.contractClassId.toString()}\n`); } diff --git a/yarn-project/cli/src/cmds/add_note.ts b/yarn-project/cli/src/cmds/add_note.ts index e547f76d3c9..c70176159d7 100644 --- a/yarn-project/cli/src/cmds/add_note.ts +++ b/yarn-project/cli/src/cmds/add_note.ts @@ -5,9 +5,6 @@ import { DebugLogger } from '@aztec/foundation/log'; import { createCompatibleClient } from '../client.js'; import { parseFields } from '../parse_args.js'; -/** - * - */ export async function addNote( address: AztecAddress, contractAddress: AztecAddress, diff --git a/yarn-project/cli/src/cmds/block_number.ts b/yarn-project/cli/src/cmds/block_number.ts index 37795a12966..c5aed126443 100644 --- a/yarn-project/cli/src/cmds/block_number.ts +++ b/yarn-project/cli/src/cmds/block_number.ts @@ -2,9 +2,6 @@ import { DebugLogger, LogFn } from '@aztec/foundation/log'; import { createCompatibleClient } from '../client.js'; -/** - * - */ export async function blockNumber(rpcUrl: string, debugLogger: DebugLogger, log: LogFn) { const client = await createCompatibleClient(rpcUrl, debugLogger); const num = await client.getBlockNumber(); diff --git a/yarn-project/cli/src/cmds/call.ts b/yarn-project/cli/src/cmds/call.ts index 7e395276177..cab98e2dd42 100644 --- a/yarn-project/cli/src/cmds/call.ts +++ b/yarn-project/cli/src/cmds/call.ts @@ -6,9 +6,6 @@ import { format } from 'util'; import { createCompatibleClient } from '../client.js'; import { getFunctionArtifact, getTxSender, prepTx } from '../utils.js'; -/** - * - */ export async function call( functionName: string, functionArgsIn: any[], diff --git a/yarn-project/cli/src/cmds/check_deploy.ts b/yarn-project/cli/src/cmds/check_deploy.ts index 25641418c71..90408309ff4 100644 --- a/yarn-project/cli/src/cmds/check_deploy.ts +++ b/yarn-project/cli/src/cmds/check_deploy.ts @@ -3,9 +3,6 @@ import { DebugLogger, LogFn } from '@aztec/foundation/log'; import { createCompatibleClient } from '../client.js'; -/** - * - */ export async function checkDeploy(rpcUrl: string, contractAddress: AztecAddress, debugLogger: DebugLogger, log: LogFn) { const client = await createCompatibleClient(rpcUrl, debugLogger); const isDeployed = await isContractDeployed(client, contractAddress); diff --git a/yarn-project/cli/src/cmds/compute_selector.ts b/yarn-project/cli/src/cmds/compute_selector.ts index d0ef8e14abe..074be33597a 100644 --- a/yarn-project/cli/src/cmds/compute_selector.ts +++ b/yarn-project/cli/src/cmds/compute_selector.ts @@ -1,9 +1,6 @@ import { FunctionSelector } from '@aztec/foundation/abi'; import { LogFn } from '@aztec/foundation/log'; -/** - * - */ export function computeSelector(functionSignature: string, log: LogFn) { const selector = FunctionSelector.fromSignature(functionSignature); log(`${selector}`); diff --git a/yarn-project/cli/src/cmds/create_account.ts b/yarn-project/cli/src/cmds/create_account.ts index a8f895a84ce..2ad6a5d92d6 100644 --- a/yarn-project/cli/src/cmds/create_account.ts +++ b/yarn-project/cli/src/cmds/create_account.ts @@ -5,9 +5,6 @@ import { DebugLogger, LogFn } from '@aztec/foundation/log'; import { createCompatibleClient } from '../client.js'; -/** - * - */ export async function createAccount( rpcUrl: string, privateKey: Fq, diff --git a/yarn-project/cli/src/cmds/deploy.ts b/yarn-project/cli/src/cmds/deploy.ts index a8c7e7ffd96..1687d275829 100644 --- a/yarn-project/cli/src/cmds/deploy.ts +++ b/yarn-project/cli/src/cmds/deploy.ts @@ -6,9 +6,6 @@ import { encodeArgs } from '../encoding.js'; import { GITHUB_TAG_PREFIX } from '../github.js'; import { getContractArtifact, getFunctionArtifact } from '../utils.js'; -/** - * - */ export async function deploy( artifactPath: string, json: boolean, @@ -53,7 +50,7 @@ export async function deploy( debugLogger(`Deploy tx sent with hash ${txHash}`); if (wait) { const deployed = await tx.wait(); - const { address, partialAddress } = deployed.contract.completeAddress; + const { address, partialAddress } = deployed.contract; if (json) { logJson({ address: address.toString(), partialAddress: partialAddress.toString() }); } else { @@ -61,7 +58,7 @@ export async function deploy( log(`Contract partial address ${partialAddress.toString()}\n`); } } else { - const { address, partialAddress } = deploy.completeAddress ?? {}; + const { address, partialAddress } = deploy; if (json) { logJson({ address: address?.toString() ?? 'N/A', @@ -69,8 +66,8 @@ export async function deploy( txHash: txHash.toString(), }); } else { - log(`\nContract Address: ${deploy.completeAddress?.address.toString() ?? 'N/A'}`); - log(`Contract Partial Address: ${deploy.completeAddress?.partialAddress.toString() ?? 'N/A'}`); + log(`\nContract Address: ${address?.toString() ?? 'N/A'}`); + log(`Contract Partial Address: ${partialAddress?.toString() ?? 'N/A'}`); log(`Deployment transaction hash: ${txHash}\n`); } } diff --git a/yarn-project/cli/src/cmds/deploy_l1_contracts.ts b/yarn-project/cli/src/cmds/deploy_l1_contracts.ts index 3b45537d88a..7ecd5abe455 100644 --- a/yarn-project/cli/src/cmds/deploy_l1_contracts.ts +++ b/yarn-project/cli/src/cmds/deploy_l1_contracts.ts @@ -2,9 +2,6 @@ import { DebugLogger, LogFn } from '@aztec/foundation/log'; import { deployAztecContracts } from '../utils.js'; -/** - * - */ export async function deployL1Contracts( rpcUrl: string, apiKey: string, diff --git a/yarn-project/cli/src/cmds/example_contracts.ts b/yarn-project/cli/src/cmds/example_contracts.ts index a5b71e2ec0d..c7ee019eccc 100644 --- a/yarn-project/cli/src/cmds/example_contracts.ts +++ b/yarn-project/cli/src/cmds/example_contracts.ts @@ -2,11 +2,8 @@ import { LogFn } from '@aztec/foundation/log'; import { getExampleContractArtifacts } from '../utils.js'; -/** - * - */ export async function exampleContracts(log: LogFn) { const abisList = await getExampleContractArtifacts(); - const names = Object.keys(abisList); + const names = Object.keys(abisList).filter(name => name !== 'AvmTestContractArtifact'); names.forEach(name => log(name)); } diff --git a/yarn-project/cli/src/cmds/generate_p2p_private_key.ts b/yarn-project/cli/src/cmds/generate_p2p_private_key.ts index 4bf3ad7a5c4..928e61974ba 100644 --- a/yarn-project/cli/src/cmds/generate_p2p_private_key.ts +++ b/yarn-project/cli/src/cmds/generate_p2p_private_key.ts @@ -2,9 +2,6 @@ import { LogFn } from '@aztec/foundation/log'; import { createSecp256k1PeerId } from '@libp2p/peer-id-factory'; -/** - * - */ export async function generateP2PPrivateKey(log: LogFn) { const peerId = await createSecp256k1PeerId(); const exportedPeerId = Buffer.from(peerId.privateKey!).toString('hex'); diff --git a/yarn-project/cli/src/cmds/generate_private_key.ts b/yarn-project/cli/src/cmds/generate_private_key.ts index 8586f03f37a..b447cdcc738 100644 --- a/yarn-project/cli/src/cmds/generate_private_key.ts +++ b/yarn-project/cli/src/cmds/generate_private_key.ts @@ -3,9 +3,6 @@ import { LogFn } from '@aztec/foundation/log'; import { mnemonicToAccount } from 'viem/accounts'; -/** - * - */ export function generatePrivateKey(mnemonic: string | undefined, log: LogFn) { let privKey; let publicKey; diff --git a/yarn-project/cli/src/cmds/get_account.ts b/yarn-project/cli/src/cmds/get_account.ts index 47b3b1056a7..8b3e9359d5f 100644 --- a/yarn-project/cli/src/cmds/get_account.ts +++ b/yarn-project/cli/src/cmds/get_account.ts @@ -3,9 +3,6 @@ import { DebugLogger, LogFn } from '@aztec/foundation/log'; import { createCompatibleClient } from '../client.js'; -/** - * - */ export async function getAccount(aztecAddress: AztecAddress, rpcUrl: string, debugLogger: DebugLogger, log: LogFn) { const client = await createCompatibleClient(rpcUrl, debugLogger); const account = await client.getRegisteredAccount(aztecAddress); diff --git a/yarn-project/cli/src/cmds/get_accounts.ts b/yarn-project/cli/src/cmds/get_accounts.ts index 79f9dbee325..1e36475f3a8 100644 --- a/yarn-project/cli/src/cmds/get_accounts.ts +++ b/yarn-project/cli/src/cmds/get_accounts.ts @@ -2,9 +2,6 @@ import { DebugLogger, LogFn } from '@aztec/foundation/log'; import { createCompatibleClient } from '../client.js'; -/** - * - */ export async function getAccounts( rpcUrl: string, json: boolean, diff --git a/yarn-project/cli/src/cmds/get_contract_data.ts b/yarn-project/cli/src/cmds/get_contract_data.ts index 432d4fa249e..80f1e7afb2a 100644 --- a/yarn-project/cli/src/cmds/get_contract_data.ts +++ b/yarn-project/cli/src/cmds/get_contract_data.ts @@ -4,9 +4,6 @@ import { DebugLogger, LogFn } from '@aztec/foundation/log'; import { createCompatibleClient } from '../client.js'; -/** - * - */ export async function getContractData( rpcUrl: string, contractAddress: AztecAddress, diff --git a/yarn-project/cli/src/cmds/get_logs.ts b/yarn-project/cli/src/cmds/get_logs.ts index 1a48dd920cc..76c27c76726 100644 --- a/yarn-project/cli/src/cmds/get_logs.ts +++ b/yarn-project/cli/src/cmds/get_logs.ts @@ -5,9 +5,6 @@ import { sleep } from '@aztec/foundation/sleep'; import { createCompatibleClient } from '../client.js'; -/** - * - */ export async function getLogs( txHash: TxHash, fromBlock: number, diff --git a/yarn-project/cli/src/cmds/get_node_info.ts b/yarn-project/cli/src/cmds/get_node_info.ts index 2f14d5f5a24..b775c08aa0d 100644 --- a/yarn-project/cli/src/cmds/get_node_info.ts +++ b/yarn-project/cli/src/cmds/get_node_info.ts @@ -2,9 +2,6 @@ import { DebugLogger, LogFn } from '@aztec/foundation/log'; import { createCompatibleClient } from '../client.js'; -/** - * - */ export async function getNodeInfo(rpcUrl: string, debugLogger: DebugLogger, log: LogFn) { const client = await createCompatibleClient(rpcUrl, debugLogger); const info = await client.getNodeInfo(); diff --git a/yarn-project/cli/src/cmds/get_recipient.ts b/yarn-project/cli/src/cmds/get_recipient.ts index 9edf6edecfc..270bbad9ac2 100644 --- a/yarn-project/cli/src/cmds/get_recipient.ts +++ b/yarn-project/cli/src/cmds/get_recipient.ts @@ -3,9 +3,6 @@ import { DebugLogger, LogFn } from '@aztec/foundation/log'; import { createCompatibleClient } from '../client.js'; -/** - * - */ export async function getRecipient(aztecAddress: AztecAddress, rpcUrl: string, debugLogger: DebugLogger, log: LogFn) { const client = await createCompatibleClient(rpcUrl, debugLogger); const recipient = await client.getRecipient(aztecAddress); diff --git a/yarn-project/cli/src/cmds/get_recipients.ts b/yarn-project/cli/src/cmds/get_recipients.ts index 92bc9fad973..875b84b6038 100644 --- a/yarn-project/cli/src/cmds/get_recipients.ts +++ b/yarn-project/cli/src/cmds/get_recipients.ts @@ -2,9 +2,6 @@ import { DebugLogger, LogFn } from '@aztec/foundation/log'; import { createCompatibleClient } from '../client.js'; -/** - * - */ export async function getRecipients(rpcUrl: string, debugLogger: DebugLogger, log: LogFn) { const client = await createCompatibleClient(rpcUrl, debugLogger); const recipients = await client.getRecipients(); diff --git a/yarn-project/cli/src/cmds/get_tx_receipt.ts b/yarn-project/cli/src/cmds/get_tx_receipt.ts index fe133608820..beaa53e2f9b 100644 --- a/yarn-project/cli/src/cmds/get_tx_receipt.ts +++ b/yarn-project/cli/src/cmds/get_tx_receipt.ts @@ -4,9 +4,6 @@ import { DebugLogger, LogFn } from '@aztec/foundation/log'; import { createCompatibleClient } from '../client.js'; -/** - * - */ export async function getTxReceipt(rpcUrl: string, txHash: TxHash, debugLogger: DebugLogger, log: LogFn) { const client = await createCompatibleClient(rpcUrl, debugLogger); const receipt = await client.getTxReceipt(txHash); diff --git a/yarn-project/cli/src/cmds/inspect_contract.ts b/yarn-project/cli/src/cmds/inspect_contract.ts index e55954adc1e..ad8aafa871d 100644 --- a/yarn-project/cli/src/cmds/inspect_contract.ts +++ b/yarn-project/cli/src/cmds/inspect_contract.ts @@ -7,9 +7,6 @@ import { DebugLogger, LogFn } from '@aztec/foundation/log'; import { getContractArtifact } from '../utils.js'; -/** - * - */ export async function inspectContract(contractArtifactFile: string, debugLogger: DebugLogger, log: LogFn) { const contractArtifact = await getContractArtifact(contractArtifactFile, debugLogger); const contractFns = contractArtifact.functions.filter( diff --git a/yarn-project/cli/src/cmds/parse_parameter_struct.ts b/yarn-project/cli/src/cmds/parse_parameter_struct.ts index 1ef572fd5ce..d8b29211d3a 100644 --- a/yarn-project/cli/src/cmds/parse_parameter_struct.ts +++ b/yarn-project/cli/src/cmds/parse_parameter_struct.ts @@ -5,9 +5,6 @@ import { LogFn } from '@aztec/foundation/log'; import { parseStructString } from '../encoding.js'; import { getContractArtifact } from '../utils.js'; -/** - * - */ export async function parseParameterStruct( encodedString: string, contractArtifactPath: string, diff --git a/yarn-project/cli/src/cmds/register_account.ts b/yarn-project/cli/src/cmds/register_account.ts index fae880f81a1..b6949cee4fa 100644 --- a/yarn-project/cli/src/cmds/register_account.ts +++ b/yarn-project/cli/src/cmds/register_account.ts @@ -3,9 +3,6 @@ import { DebugLogger, LogFn } from '@aztec/foundation/log'; import { createCompatibleClient } from '../client.js'; -/** - * - */ export async function registerAccount( rpcUrl: string, privateKey: Fq, diff --git a/yarn-project/cli/src/cmds/register_recipient.ts b/yarn-project/cli/src/cmds/register_recipient.ts index df8b7a0c11a..6458143c542 100644 --- a/yarn-project/cli/src/cmds/register_recipient.ts +++ b/yarn-project/cli/src/cmds/register_recipient.ts @@ -4,9 +4,6 @@ import { DebugLogger, LogFn } from '@aztec/foundation/log'; import { createCompatibleClient } from '../client.js'; -/** - * - */ export async function registerRecipient( aztecAddress: AztecAddress, publicKey: Point, diff --git a/yarn-project/cli/src/cmds/send.ts b/yarn-project/cli/src/cmds/send.ts index 50dab7479e9..8513fe1392e 100644 --- a/yarn-project/cli/src/cmds/send.ts +++ b/yarn-project/cli/src/cmds/send.ts @@ -5,9 +5,6 @@ import { DebugLogger, LogFn } from '@aztec/foundation/log'; import { createCompatibleClient } from '../client.js'; import { prepTx } from '../utils.js'; -/** - * - */ export async function send( functionName: string, functionArgsIn: any[], diff --git a/yarn-project/cli/src/cmds/unbox.ts b/yarn-project/cli/src/cmds/unbox.ts index 38150ba16e4..b1b6542b5e0 100644 --- a/yarn-project/cli/src/cmds/unbox.ts +++ b/yarn-project/cli/src/cmds/unbox.ts @@ -80,9 +80,6 @@ function copyDependenciesToBox(dirName: string, destPath: string) { ); } -/** - * - */ function packageJsonInjectLocalResolutions(path: string) { const data = readFileSync(path, 'utf-8'); const packageJson = JSON.parse(data); diff --git a/yarn-project/cli/src/index.ts b/yarn-project/cli/src/index.ts index 074745d6647..70215cf4251 100644 --- a/yarn-project/cli/src/index.ts +++ b/yarn-project/cli/src/index.ts @@ -1,3 +1,4 @@ +import { Fr } from '@aztec/circuits.js'; import { DebugLogger, LogFn } from '@aztec/foundation/log'; import { fileURLToPath } from '@aztec/foundation/url'; import { addCodegenCommanderAction } from '@aztec/noir-compiler/cli'; @@ -11,6 +12,7 @@ import { parseAztecAddress, parseEthereumAddress, parseField, + parseFieldFromHexString, parseOptionalAztecAddress, parseOptionalInteger, parseOptionalLogId, @@ -19,7 +21,6 @@ import { parsePartialAddress, parsePrivateKey, parsePublicKey, - parseSaltFromHexString, parseTxHash, } from './parse_args.js'; @@ -170,7 +171,7 @@ export function getProgram(log: LogFn, debugLogger: DebugLogger): Command { .option( '-s, --salt ', 'Optional deployment salt as a hex string for generating the deployment address.', - parseSaltFromHexString, + parseFieldFromHexString, ) .option('--json', 'Emit output as json') // `options.wait` is default true. Passing `--no-wait` will set it to false. @@ -217,7 +218,8 @@ export function getProgram(log: LogFn, debugLogger: DebugLogger): Command { "A compiled Aztec.nr contract's ABI in JSON format or name of a contract ABI exported by @aztec/noir-contracts", ) .requiredOption('-ca, --contract-address
', 'Aztec address of the contract.', parseAztecAddress) - .requiredOption('-pa, --partial-address
', 'Partial address of the contract', parsePartialAddress) + .requiredOption('--init-hash ', 'Initialization hash', parseFieldFromHexString) + .option('--salt ', 'Optional deployment salt', parseFieldFromHexString) .option('-p, --public-key ', 'Optional public key for this contract', parsePublicKey) .option('--portal-address
', 'Optional address to a portal contract on L1', parseEthereumAddress) .addOption(pxeOption) @@ -227,7 +229,8 @@ export function getProgram(log: LogFn, debugLogger: DebugLogger): Command { options.rpcUrl, options.contractArtifact, options.contractAddress, - options.partialAddress, + options.initHash, + options.salt ?? Fr.ZERO, options.publicKey, options.portalContract, debugLogger, diff --git a/yarn-project/cli/src/parse_args.ts b/yarn-project/cli/src/parse_args.ts index e2641005cd2..d71129d48dd 100644 --- a/yarn-project/cli/src/parse_args.ts +++ b/yarn-project/cli/src/parse_args.ts @@ -20,11 +20,11 @@ const stripLeadingHex = (hex: string) => { }; /** - * Parses a hex encoded string to an Fr integer to be used as salt + * Parses a hex encoded string to an Fr integer * @param str - Hex encoded string - * @returns A integer to be used as salt + * @returns A integer */ -export function parseSaltFromHexString(str: string): Fr { +export function parseFieldFromHexString(str: string): Fr { const hex = stripLeadingHex(str); // ensure it's a hex string diff --git a/yarn-project/cli/src/test/utils.test.ts b/yarn-project/cli/src/test/utils.test.ts index edbabea573b..e43d32d207a 100644 --- a/yarn-project/cli/src/test/utils.test.ts +++ b/yarn-project/cli/src/test/utils.test.ts @@ -5,7 +5,7 @@ import { InvalidArgumentError } from 'commander'; import { MockProxy, mock } from 'jest-mock-extended'; import { encodeArgs } from '../encoding.js'; -import { parseSaltFromHexString } from '../parse_args.js'; +import { parseFieldFromHexString } from '../parse_args.js'; import { getTxSender, stripLeadingHex } from '../utils.js'; import { mockContractArtifact } from './mocks.js'; @@ -147,11 +147,11 @@ describe('CLI Utils', () => { ['0xa', new Fr(0xa)], ['fff', new Fr(0xfff)], ])('correctly generates salt from a hex string', (hex, expected) => { - expect(parseSaltFromHexString(hex)).toEqual(expected); + expect(parseFieldFromHexString(hex)).toEqual(expected); }); it.each(['foo', '', ' ', ' 0x1', '01foo', 'foo1', '0xfoo'])('throws an error for invalid hex strings', str => { - expect(() => parseSaltFromHexString(str)).toThrow(InvalidArgumentError); + expect(() => parseFieldFromHexString(str)).toThrow(InvalidArgumentError); }); }); }); diff --git a/yarn-project/cli/src/update/update.ts b/yarn-project/cli/src/update/update.ts index 9f78d0caab2..5c946f9953d 100644 --- a/yarn-project/cli/src/update/update.ts +++ b/yarn-project/cli/src/update/update.ts @@ -10,7 +10,7 @@ import { updateAztecNr } from './noir.js'; import { getNewestVersion, updateAztecDeps, updateLockfile } from './npm.js'; const AZTECJS_PACKAGE = '@aztec/aztec.js'; -const UPDATE_DOCS_URL = 'https://docs.aztec.network/dev_docs/updating'; +const UPDATE_DOCS_URL = 'https://docs.aztec.network/developers/updating'; export async function update( projectPath: string, diff --git a/yarn-project/cli/src/utils.ts b/yarn-project/cli/src/utils.ts index a3926ae6750..ef2c07a811b 100644 --- a/yarn-project/cli/src/utils.ts +++ b/yarn-project/cli/src/utils.ts @@ -62,7 +62,9 @@ export async function deployAztecContracts( const { createEthereumChain, deployL1Contracts } = await import('@aztec/ethereum'); const { mnemonicToAccount, privateKeyToAccount } = await import('viem/accounts'); - const account = !privateKey ? mnemonicToAccount(mnemonic!) : privateKeyToAccount(`0x${privateKey}`); + const account = !privateKey + ? mnemonicToAccount(mnemonic!) + : privateKeyToAccount(`${privateKey.startsWith('0x') ? '' : '0x'}${privateKey}` as `0x${string}`); const chain = createEthereumChain(rpcUrl, apiKey); const l1Artifacts: L1ContractArtifactsForDeployment = { contractDeploymentEmitter: { diff --git a/yarn-project/cli/tsconfig.json b/yarn-project/cli/tsconfig.json index d9042915445..96499cbb599 100644 --- a/yarn-project/cli/tsconfig.json +++ b/yarn-project/cli/tsconfig.json @@ -15,6 +15,9 @@ { "path": "../circuit-types" }, + { + "path": "../circuits.js" + }, { "path": "../ethereum" }, diff --git a/yarn-project/end-to-end/package.json b/yarn-project/end-to-end/package.json index 88ccca29173..f019bf353b4 100644 --- a/yarn-project/end-to-end/package.json +++ b/yarn-project/end-to-end/package.json @@ -5,6 +5,7 @@ "exports": "./dest/index.js", "scripts": { "build": "yarn clean && tsc -b && webpack", + "build:e2e": "yarn clean && tsc -b", "build:dev": "tsc -b --watch", "build:web": "webpack", "clean": "rm -rf ./dest .tsbuildinfo", diff --git a/yarn-project/end-to-end/scripts/docker-compose-browser.yml b/yarn-project/end-to-end/scripts/docker-compose-browser.yml index d3abd8fb74d..f859de7de39 100644 --- a/yarn-project/end-to-end/scripts/docker-compose-browser.yml +++ b/yarn-project/end-to-end/scripts/docker-compose-browser.yml @@ -13,7 +13,7 @@ services: - '8545:8545' sandbox: - image: aztecprotocol/aztec-sandbox:latest + image: aztecprotocol/aztec:latest environment: DEBUG: 'aztec:*' DEBUG_COLORS: 1 diff --git a/yarn-project/end-to-end/scripts/docker-compose-p2p.yml b/yarn-project/end-to-end/scripts/docker-compose-p2p.yml index 3bca942dd23..50cca540d6c 100644 --- a/yarn-project/end-to-end/scripts/docker-compose-p2p.yml +++ b/yarn-project/end-to-end/scripts/docker-compose-p2p.yml @@ -7,12 +7,11 @@ services: - '8545:8545' p2p-bootstrap: - image: aztecprotocol/aztec-sandbox:latest + image: aztecprotocol/aztec:latest + command: 'start --p2p-bootstrap' ports: - '40400:40400' - command: 'start' environment: - MODE: 'p2p-bootstrap' DEBUG: 'aztec:*' DEBUG_COLORS: 1 P2P_TCP_LISTEN_PORT: 40400 diff --git a/yarn-project/end-to-end/scripts/docker-compose.yml b/yarn-project/end-to-end/scripts/docker-compose.yml index 9b675470f35..6a05e652777 100644 --- a/yarn-project/end-to-end/scripts/docker-compose.yml +++ b/yarn-project/end-to-end/scripts/docker-compose.yml @@ -13,7 +13,7 @@ services: - '8545:8545' sandbox: - image: aztecprotocol/aztec-sandbox:latest + image: aztecprotocol/aztec:latest environment: DEBUG: 'aztec:*' DEBUG_COLORS: 1 diff --git a/yarn-project/end-to-end/src/benchmarks/bench_process_history.test.ts b/yarn-project/end-to-end/src/benchmarks/bench_process_history.test.ts index 0fc0113999e..9e2b2700ad2 100644 --- a/yarn-project/end-to-end/src/benchmarks/bench_process_history.test.ts +++ b/yarn-project/end-to-end/src/benchmarks/bench_process_history.test.ts @@ -1,5 +1,5 @@ import { AztecNodeConfig, AztecNodeService } from '@aztec/aztec-node'; -import { Fr, GrumpkinScalar, INITIAL_L2_BLOCK_NUM, elapsed, sleep } from '@aztec/aztec.js'; +import { AztecAddress, Fr, GrumpkinScalar, INITIAL_L2_BLOCK_NUM, elapsed, sleep } from '@aztec/aztec.js'; import { BENCHMARK_HISTORY_BLOCK_SIZE, BENCHMARK_HISTORY_CHAIN_LENGTHS, @@ -52,7 +52,9 @@ describe('benchmarks/process_history', () => { const nodeConfig: AztecNodeConfig = { ...context.config, disableSequencer: true, dataDirectory }; const [nodeSyncTime, node] = await elapsed(async () => { const node = await AztecNodeService.createAndSync(nodeConfig); - await node.getTreeRoots(); + // call getPublicStorageAt (which calls #getWorldState, which calls #syncWorldState) to force a sync with + // world state to ensure the node has caught up + await node.getPublicStorageAt(AztecAddress.random(), Fr.random()); return node; }); diff --git a/yarn-project/end-to-end/src/benchmarks/bench_publish_rollup.test.ts b/yarn-project/end-to-end/src/benchmarks/bench_publish_rollup.test.ts index 801ce861163..b2b2e710af5 100644 --- a/yarn-project/end-to-end/src/benchmarks/bench_publish_rollup.test.ts +++ b/yarn-project/end-to-end/src/benchmarks/bench_publish_rollup.test.ts @@ -1,5 +1,5 @@ import { AztecNodeService } from '@aztec/aztec-node'; -import { Fr, GrumpkinScalar } from '@aztec/aztec.js'; +import { AztecAddress, Fr, GrumpkinScalar } from '@aztec/aztec.js'; import { BENCHMARK_BLOCK_SIZES } from '@aztec/circuit-types/stats'; import { BenchmarkingContract } from '@aztec/noir-contracts/Benchmarking'; import { SequencerClient } from '@aztec/sequencer-client'; @@ -33,10 +33,11 @@ describe('benchmarks/publish_rollup', () => { await context.teardown(); // Create a new aztec node to measure sync time of the block - // and call getTreeRoots to force a sync with world state to ensure the node has caught up + // and call getPublicStorageAt (which calls #getWorldState, which calls #syncWorldState) to force a sync with + // world state to ensure the node has caught up context.logger(`Starting new aztec node`); const node = await AztecNodeService.createAndSync({ ...context.config, disableSequencer: true }); - await node.getTreeRoots(); + await node.getPublicStorageAt(AztecAddress.random(), Fr.random()); // Spin up a new pxe and sync it, we'll use it to test sync times of new accounts for the last block context.logger(`Starting new pxe`); diff --git a/yarn-project/end-to-end/src/e2e_2_pxes.test.ts b/yarn-project/end-to-end/src/e2e_2_pxes.test.ts index 7b7e4778689..fb5175d7722 100644 --- a/yarn-project/end-to-end/src/e2e_2_pxes.test.ts +++ b/yarn-project/end-to-end/src/e2e_2_pxes.test.ts @@ -4,7 +4,6 @@ import { AztecNode, CompleteAddress, DebugLogger, - EthAddress, ExtendedNote, Fr, GrumpkinScalar, @@ -21,7 +20,7 @@ import { jest } from '@jest/globals'; import { expectsNumOfEncryptedLogsInTheLastBlockToBe, setup, setupPXEService } from './fixtures/utils.js'; -const TIMEOUT = 60_000; +const TIMEOUT = 90_000; describe('e2e_2_pxes', () => { jest.setTimeout(TIMEOUT); @@ -99,7 +98,7 @@ describe('e2e_2_pxes', () => { logger('L2 contract deployed'); - return contract.completeAddress; + return contract.instance; }; const mintTokens = async (contract: TokenContract, recipient: AztecAddress, balance: bigint, pxe: PXE) => { @@ -124,8 +123,8 @@ describe('e2e_2_pxes', () => { const transferAmount1 = 654n; const transferAmount2 = 323n; - const completeTokenAddress = await deployTokenContract(initialBalance, userA.address, pxeA); - const tokenAddress = completeTokenAddress.address; + const tokenInstance = await deployTokenContract(initialBalance, userA.address, pxeA); + const tokenAddress = tokenInstance.address; // Add account B to wallet A await pxeA.registerRecipient(userB); @@ -136,8 +135,7 @@ describe('e2e_2_pxes', () => { await pxeB.addContracts([ { artifact: TokenContract.artifact, - completeAddress: completeTokenAddress, - portalContract: EthAddress.ZERO, + instance: tokenInstance, }, ]); @@ -177,7 +175,7 @@ describe('e2e_2_pxes', () => { const contract = await ChildContract.deploy(walletA).send().deployed(); logger('Child contract deployed'); - return contract.completeAddress; + return contract.instance; }; const awaitServerSynchronized = async (server: PXE) => { @@ -199,8 +197,7 @@ describe('e2e_2_pxes', () => { await pxeB.addContracts([ { artifact: ChildContract.artifact, - completeAddress: childCompleteAddress, - portalContract: EthAddress.ZERO, + instance: childCompleteAddress, }, ]); @@ -222,8 +219,8 @@ describe('e2e_2_pxes', () => { const userABalance = 100n; const userBBalance = 150n; - const completeTokenAddress = await deployTokenContract(userABalance, userA.address, pxeA); - const contractWithWalletA = await TokenContract.at(completeTokenAddress.address, walletA); + const tokenInstance = await deployTokenContract(userABalance, userA.address, pxeA); + const contractWithWalletA = await TokenContract.at(tokenInstance.address, walletA); // Add account B to wallet A await pxeA.registerRecipient(userB); @@ -234,8 +231,7 @@ describe('e2e_2_pxes', () => { await pxeB.addContracts([ { artifact: TokenContract.artifact, - completeAddress: completeTokenAddress, - portalContract: EthAddress.ZERO, + instance: tokenInstance, }, ]); @@ -243,17 +239,17 @@ describe('e2e_2_pxes', () => { await mintTokens(contractWithWalletA, userB.address, userBBalance, pxeA); // Check that user A balance is 100 on server A - await expectTokenBalance(walletA, completeTokenAddress.address, userA.address, userABalance); + await expectTokenBalance(walletA, tokenInstance.address, userA.address, userABalance); // Check that user B balance is 150 on server B - await expectTokenBalance(walletB, completeTokenAddress.address, userB.address, userBBalance); + await expectTokenBalance(walletB, tokenInstance.address, userB.address, userBBalance); // CHECK THAT PRIVATE BALANCES ARE 0 WHEN ACCOUNT'S PRIVATE KEYS ARE NOT REGISTERED // Note: Not checking if the account is synchronized because it is not registered as an account (it would throw). const checkIfSynchronized = false; // Check that user A balance is 0 on server B - await expectTokenBalance(walletB, completeTokenAddress.address, userA.address, 0n, checkIfSynchronized); + await expectTokenBalance(walletB, tokenInstance.address, userA.address, 0n, checkIfSynchronized); // Check that user B balance is 0 on server A - await expectTokenBalance(walletA, completeTokenAddress.address, userB.address, 0n, checkIfSynchronized); + await expectTokenBalance(walletA, tokenInstance.address, userB.address, 0n, checkIfSynchronized); }); it('permits migrating an account from one PXE to another', async () => { @@ -263,7 +259,7 @@ describe('e2e_2_pxes', () => { const wallet = await account.waitDeploy(); await expect(wallet.isAccountStateSynchronized(completeAddress.address)).resolves.toBe(true); - const accountOnB = getUnsafeSchnorrAccount(pxeB, privateKey, completeAddress); + const accountOnB = getUnsafeSchnorrAccount(pxeB, privateKey, account.salt); const walletOnB = await accountOnB.getWallet(); // need to register first otherwise the new PXE won't know about the account @@ -278,8 +274,8 @@ describe('e2e_2_pxes', () => { const initialBalance = 987n; const transferAmount1 = 654n; - const completeTokenAddress = await deployTokenContract(initialBalance, userA.address, pxeA); - const tokenAddress = completeTokenAddress.address; + const tokenInstance = await deployTokenContract(initialBalance, userA.address, pxeA); + const tokenAddress = tokenInstance.address; // Add account B to wallet A await pxeA.registerRecipient(userB); @@ -304,8 +300,7 @@ describe('e2e_2_pxes', () => { await pxeB.addContracts([ { artifact: TokenContract.artifact, - completeAddress: completeTokenAddress, - portalContract: EthAddress.ZERO, + instance: tokenInstance, }, ]); await expectTokenBalance(walletA, tokenAddress, userA.address, initialBalance - transferAmount1); @@ -324,15 +319,15 @@ describe('e2e_2_pxes', () => { const sharedWalletOnA = await sharedAccountOnA.waitDeploy(); await expect(sharedWalletOnA.isAccountStateSynchronized(sharedAccountAddress.address)).resolves.toBe(true); - const sharedAccountOnB = getUnsafeSchnorrAccount(pxeB, sharedPrivateKey, sharedAccountAddress); + const sharedAccountOnB = getUnsafeSchnorrAccount(pxeB, sharedPrivateKey, sharedAccountOnA.salt); await sharedAccountOnB.register(); const sharedWalletOnB = await sharedAccountOnB.getWallet(); await pxeA.registerRecipient(userB); // deploy the contract on PXE A - const completeTokenAddress = await deployTokenContract(initialBalance, userA.address, pxeA); - const tokenAddress = completeTokenAddress.address; + const tokenInstance = await deployTokenContract(initialBalance, userA.address, pxeA); + const tokenAddress = tokenInstance.address; // Transfer funds from A to Shared Wallet via PXE A const contractWithWalletA = await TokenContract.at(tokenAddress, walletA); @@ -367,8 +362,7 @@ describe('e2e_2_pxes', () => { await pxeB.addContracts([ { artifact: TokenContract.artifact, - completeAddress: completeTokenAddress, - portalContract: EthAddress.ZERO, + instance: tokenInstance, }, ]); await expectTokenBalance(walletB, tokenAddress, userB.address, transferAmount2); diff --git a/yarn-project/end-to-end/src/e2e_account_contracts.test.ts b/yarn-project/end-to-end/src/e2e_account_contracts.test.ts index 0c12c729419..67106877c49 100644 --- a/yarn-project/end-to-end/src/e2e_account_contracts.test.ts +++ b/yarn-project/end-to-end/src/e2e_account_contracts.test.ts @@ -4,6 +4,7 @@ import { SingleKeyAccountContract } from '@aztec/accounts/single_key'; import { AccountContract, AccountManager, + AccountWallet, CompleteAddress, Fr, GrumpkinPrivateKey, @@ -23,13 +24,12 @@ function itShouldBehaveLikeAnAccountContract( pxe: PXE, encryptionPrivateKey: GrumpkinPrivateKey, accountContract: AccountContract, - address?: CompleteAddress, - ) => Promise<{ account: AccountManager; wallet: Wallet }>, + ) => Promise, + walletAt: (pxe: PXE, accountContract: AccountContract, address: CompleteAddress) => Promise, ) { describe(`behaves like an account contract`, () => { let context: Awaited>; let child: ChildContract; - let account: AccountManager; let wallet: Wallet; let encryptionPrivateKey: GrumpkinPrivateKey; @@ -37,11 +37,7 @@ function itShouldBehaveLikeAnAccountContract( context = await setup(0); encryptionPrivateKey = GrumpkinScalar.random(); - ({ account, wallet } = await walletSetup( - context.pxe, - encryptionPrivateKey, - getAccountContract(encryptionPrivateKey), - )); + wallet = await walletSetup(context.pxe, encryptionPrivateKey, getAccountContract(encryptionPrivateKey)); child = await ChildContract.deploy(wallet).send().deployed(); }, 60_000); @@ -62,13 +58,8 @@ function itShouldBehaveLikeAnAccountContract( }, 60_000); it('fails to call a function using an invalid signature', async () => { - const accountAddress = account.getCompleteAddress(); - const { wallet: invalidWallet } = await walletSetup( - context.pxe, - encryptionPrivateKey, - getAccountContract(GrumpkinScalar.random()), - accountAddress, - ); + const accountAddress = wallet.getCompleteAddress(); + const invalidWallet = await walletAt(context.pxe, getAccountContract(GrumpkinScalar.random()), accountAddress); const childWithInvalidWallet = await ChildContract.at(child.address, invalidWallet); await expect(childWithInvalidWallet.methods.value(42).simulate()).rejects.toThrowError( /Cannot satisfy constraint.*/, @@ -78,29 +69,34 @@ function itShouldBehaveLikeAnAccountContract( } describe('e2e_account_contracts', () => { - const base = async ( - pxe: PXE, - encryptionPrivateKey: GrumpkinPrivateKey, - accountContract: AccountContract, - address?: CompleteAddress, - ) => { - const account = new AccountManager(pxe, encryptionPrivateKey, accountContract, address); - const wallet = !address ? await account.deploy().then(tx => tx.getWallet()) : await account.getWallet(); - return { account, wallet }; + const walletSetup = async (pxe: PXE, encryptionPrivateKey: GrumpkinPrivateKey, accountContract: AccountContract) => { + const account = new AccountManager(pxe, encryptionPrivateKey, accountContract); + return await account.deploy().then(tx => tx.getWallet()); + }; + + const walletAt = async (pxe: PXE, accountContract: AccountContract, address: CompleteAddress) => { + const nodeInfo = await pxe.getNodeInfo(); + const entrypoint = accountContract.getInterface(address, nodeInfo); + return new AccountWallet(pxe, entrypoint); }; describe('schnorr single-key account', () => { itShouldBehaveLikeAnAccountContract( (encryptionKey: GrumpkinPrivateKey) => new SingleKeyAccountContract(encryptionKey), - base, + walletSetup, + walletAt, ); }); describe('schnorr multi-key account', () => { - itShouldBehaveLikeAnAccountContract(() => new SchnorrAccountContract(GrumpkinScalar.random()), base); + itShouldBehaveLikeAnAccountContract( + () => new SchnorrAccountContract(GrumpkinScalar.random()), + walletSetup, + walletAt, + ); }); describe('ecdsa stored-key account', () => { - itShouldBehaveLikeAnAccountContract(() => new EcdsaAccountContract(randomBytes(32)), base); + itShouldBehaveLikeAnAccountContract(() => new EcdsaAccountContract(randomBytes(32)), walletSetup, walletAt); }); }); diff --git a/yarn-project/end-to-end/src/e2e_blacklist_token_contract.test.ts b/yarn-project/end-to-end/src/e2e_blacklist_token_contract.test.ts index f5904fdce5b..a434f91f090 100644 --- a/yarn-project/end-to-end/src/e2e_blacklist_token_contract.test.ts +++ b/yarn-project/end-to-end/src/e2e_blacklist_token_contract.test.ts @@ -14,18 +14,15 @@ import { computeAuthWitMessageHash, computeMessageSecretHash, } from '@aztec/aztec.js'; +import { AztecLmdbStore } from '@aztec/kv-store'; import { Pedersen, SparseTree, newTree } from '@aztec/merkle-tree'; import { SlowTreeContract, TokenBlacklistContract, TokenContract } from '@aztec/noir-contracts'; import { jest } from '@jest/globals'; -import levelup from 'levelup'; -import { type MemDown, default as memdown } from 'memdown'; import { setup } from './fixtures/utils.js'; import { TokenSimulator } from './simulators/token_simulator.js'; -export const createMemDown = () => (memdown as any)() as MemDown; - const TIMEOUT = 90_000; describe('e2e_blacklist_token_contract', () => { @@ -48,7 +45,7 @@ describe('e2e_blacklist_token_contract', () => { const getMembershipProof = async (index: bigint, includeUncommitted: boolean) => { return { index, - value: Fr.fromBuffer((await slowUpdateTreeSimulator.getLeafValue(index, includeUncommitted))!), + value: Fr.fromBuffer(slowUpdateTreeSimulator.getLeafValue(index, includeUncommitted)!), // eslint-disable-next-line camelcase sibling_path: (await slowUpdateTreeSimulator.getSiblingPath(index, includeUncommitted)).toFieldArray(), }; @@ -107,7 +104,7 @@ describe('e2e_blacklist_token_contract', () => { slowTree = await SlowTreeContract.deploy(wallets[0]).send().deployed(); const depth = 254; - slowUpdateTreeSimulator = await newTree(SparseTree, levelup(createMemDown()), new Pedersen(), 'test', depth); + slowUpdateTreeSimulator = await newTree(SparseTree, await AztecLmdbStore.openTmp(), new Pedersen(), 'test', depth); const deployTx = TokenBlacklistContract.deploy(wallets[0], accounts[0], slowTree.address).send({}); const receipt = await deployTx.wait(); diff --git a/yarn-project/end-to-end/src/e2e_block_building.test.ts b/yarn-project/end-to-end/src/e2e_block_building.test.ts index d21b5a4a447..645d2b4f325 100644 --- a/yarn-project/end-to-end/src/e2e_block_building.test.ts +++ b/yarn-project/end-to-end/src/e2e_block_building.test.ts @@ -86,7 +86,7 @@ describe('e2e_block_building', () => { // but we are in the same block as the deployment transaction const callInteraction = new ContractFunctionInteraction( owner, - deployer.completeAddress!.address, + deployer.instance!.address, TokenContract.artifact.functions.find(x => x.name === 'set_minter')!, [minter.getCompleteAddress(), true], ); diff --git a/yarn-project/end-to-end/src/e2e_card_game.test.ts b/yarn-project/end-to-end/src/e2e_card_game.test.ts index e78a3a9ae84..69d7bbae065 100644 --- a/yarn-project/end-to-end/src/e2e_card_game.test.ts +++ b/yarn-project/end-to-end/src/e2e_card_game.test.ts @@ -9,6 +9,9 @@ import { Wallet, generatePublicKey, } from '@aztec/aztec.js'; +import { computeNullifierSecretKey, computeSiloedNullifierSecretKey } from '@aztec/circuits.js'; +import { toBufferLE } from '@aztec/foundation/bigint-buffer'; +import { sha256 } from '@aztec/foundation/crypto'; import { CardGameContract } from '@aztec/noir-contracts/CardGame'; import { setup } from './fixtures/utils.js'; @@ -25,9 +28,7 @@ const cardToField = (card: Card): bigint => { }; interface PlayerGameEntry { - address: { - inner: bigint; - }; + address: AztecAddress; deck_strength: bigint; points: bigint; } @@ -51,6 +52,8 @@ function unwrapOptions(options: NoirOption[]): T[] { return options.filter((option: any) => option._is_some).map((option: any) => option._value); } +// Game settings. +const PACK_CARDS = 3; const GAME_ID = 42; const PLAYER_ENCRYPTION_KEYS = INITIAL_TEST_ENCRYPTION_KEYS; @@ -61,6 +64,8 @@ describe('e2e_card_game', () => { let teardown: () => Promise; let wallets: AccountWallet[]; + let nullifierSecretKeys: GrumpkinScalar[]; + let firstPlayerWallet: Wallet; let secondPlayerWallet: Wallet; let thirdPlayerWallet: Wallet; @@ -73,6 +78,21 @@ describe('e2e_card_game', () => { let contractAsSecondPlayer: CardGameContract; let contractAsThirdPlayer: CardGameContract; + const getPackedCards = (accountIndex: number, seed: bigint): Card[] => { + const nullifierKey = nullifierSecretKeys[accountIndex]; + const secret = computeSiloedNullifierSecretKey(nullifierKey, contract.address); + const mix = secret.high.add(secret.low).toBigInt() + seed; + const randomBytes = sha256(toBufferLE(mix, 32)); + const cards: Card[] = []; + for (let i = 0; i < PACK_CARDS; ++i) { + cards.push({ + strength: BigInt(randomBytes.readUint8(i) + randomBytes.readUint8(i + 1) * 256), + points: BigInt(randomBytes.readUint8(i + 2) + randomBytes.readUint8(i + 3) * 256), + }); + } + return cards; + }; + beforeAll(async () => { ({ pxe, logger, teardown, wallets } = await setup(0)); @@ -98,6 +118,8 @@ describe('e2e_card_game', () => { [firstPlayerWallet, secondPlayerWallet, thirdPlayerWallet] = wallets; [firstPlayer, secondPlayer, thirdPlayer] = wallets.map(a => a.getAddress()); + + nullifierSecretKeys = PLAYER_ENCRYPTION_KEYS.map(pk => computeNullifierSecretKey(pk)); }, 100_000); beforeEach(async () => { @@ -118,33 +140,21 @@ describe('e2e_card_game', () => { const contractFor = (address: AztecAddress) => contract.withWallet(getWallet(address))!; it('should be able to buy packs', async () => { - await contract.methods.buy_pack(27n).send().wait(); + const seed = 27n; + await contract.methods.buy_pack(seed).send().wait(); const collection = await contract.methods.view_collection_cards(firstPlayer, 0).view({ from: firstPlayer }); - expect(unwrapOptions(collection)).toMatchInlineSnapshot(` - [ - { - "points": 18471n, - "strength": 55863n, - }, - { - "points": 30024n, - "strength": 10202n, - }, - { - "points": 47477n, - "strength": 18471n, - }, - ] - `); + const expected = getPackedCards(0, seed); + expect(unwrapOptions(collection)).toMatchObject(expected); }, 30_000); describe('game join', () => { + const seed = 27n; let firstPlayerCollection: Card[]; beforeEach(async () => { await Promise.all([ - contract.methods.buy_pack(27n).send().wait(), - contractAsSecondPlayer.methods.buy_pack(27n).send().wait(), + contract.methods.buy_pack(seed).send().wait(), + contractAsSecondPlayer.methods.buy_pack(seed).send().wait(), ]); firstPlayerCollection = unwrapOptions( await contract.methods.view_collection_cards(firstPlayer, 0).view({ from: firstPlayer }), @@ -166,28 +176,17 @@ describe('e2e_card_game', () => { const collection = await contract.methods.view_collection_cards(firstPlayer, 0).view({ from: firstPlayer }); expect(unwrapOptions(collection)).toHaveLength(1); - expect(unwrapOptions(collection)).toMatchInlineSnapshot(` - [ - { - "points": 30024n, - "strength": 10202n, - }, - ] - `); + expect(unwrapOptions(collection)).toMatchObject([firstPlayerCollection[1]]); expect((await contract.methods.view_game(GAME_ID).view({ from: firstPlayer })) as Game).toMatchObject({ players: [ { - address: { - inner: firstPlayer.toBigInt(), - }, + address: firstPlayer, deck_strength: expect.anything(), points: 0n, }, { - address: { - inner: 0n, - }, + address: AztecAddress.ZERO, deck_strength: 0n, points: 0n, }, @@ -222,16 +221,12 @@ describe('e2e_card_game', () => { expect((await contract.methods.view_game(GAME_ID).view({ from: firstPlayer })) as Game).toMatchObject({ players: expect.arrayContaining([ { - address: { - inner: firstPlayer.toBigInt(), - }, + address: firstPlayer, deck_strength: expect.anything(), points: 0n, }, { - address: { - inner: secondPlayer.toBigInt(), - }, + address: secondPlayer, deck_strength: expect.anything(), points: 0n, }, @@ -250,10 +245,11 @@ describe('e2e_card_game', () => { let thirdPlayerCOllection: Card[]; beforeEach(async () => { + const seed = 27n; await Promise.all([ - contract.methods.buy_pack(27n).send().wait(), - contractAsSecondPlayer.methods.buy_pack(27n).send().wait(), - contractAsThirdPlayer.methods.buy_pack(27n).send().wait(), + contract.methods.buy_pack(seed).send().wait(), + contractAsSecondPlayer.methods.buy_pack(seed).send().wait(), + contractAsThirdPlayer.methods.buy_pack(seed).send().wait(), ]); firstPlayerCollection = unwrapOptions( @@ -275,16 +271,16 @@ describe('e2e_card_game', () => { async function playGame(playerDecks: { address: AztecAddress; deck: Card[] }[], id = GAME_ID) { const initialGameState = (await contract.methods.view_game(id).view({ from: firstPlayer })) as Game; - const players = initialGameState.players.map(player => player.address.inner); + const players = initialGameState.players.map(player => player.address); const cards = players.map( - player => playerDecks.find(playerDeckEntry => playerDeckEntry.address.toBigInt() === player)!.deck, + player => playerDecks.find(playerDeckEntry => playerDeckEntry.address.equals(player))!.deck, ); for (let roundIndex = 0; roundIndex < cards.length; roundIndex++) { for (let playerIndex = 0; playerIndex < players.length; playerIndex++) { const player = players[playerIndex]; const card = cards[playerIndex][roundIndex]; - await contractFor(AztecAddress.fromBigInt(player)).methods.play_card(id, card).send().wait(); + await contractFor(player).methods.play_card(id, card).send().wait(); } } @@ -309,8 +305,8 @@ describe('e2e_card_game', () => { ]); const sortedByPoints = game.players.sort((a, b) => Number(b.points - a.points)); - const winner = AztecAddress.fromBigInt(sortedByPoints[0].address.inner); - const loser = AztecAddress.fromBigInt(sortedByPoints[1].address.inner); + const winner = sortedByPoints[0].address; + const loser = sortedByPoints[1].address; await expect( contractFor(loser).methods.claim_cards(GAME_ID, game.rounds_cards.map(cardToField)).send().wait(), diff --git a/yarn-project/end-to-end/src/e2e_cli.test.ts b/yarn-project/end-to-end/src/e2e_cli.test.ts index 221902983f9..7fd0884a091 100644 --- a/yarn-project/end-to-end/src/e2e_cli.test.ts +++ b/yarn-project/end-to-end/src/e2e_cli.test.ts @@ -20,7 +20,7 @@ const testSetup = async () => { debug(`Environment set up`); ({ pxe, teardown } = context); if (!RPC_URL) { - http = startHttpRpcServer(pxe, createPXERpcServer, HTTP_PORT); + http = startHttpRpcServer('pxe', pxe, createPXERpcServer, HTTP_PORT); debug(`HTTP RPC server started on port ${HTTP_PORT}`); RPC_URL = `http://localhost:${HTTP_PORT}`; } diff --git a/yarn-project/end-to-end/src/e2e_cross_chain_messaging.test.ts b/yarn-project/end-to-end/src/e2e_cross_chain_messaging.test.ts index 4f01c5b41e1..f3bb23e15d8 100644 --- a/yarn-project/end-to-end/src/e2e_cross_chain_messaging.test.ts +++ b/yarn-project/end-to-end/src/e2e_cross_chain_messaging.test.ts @@ -161,7 +161,7 @@ describe('e2e_cross_chain_messaging', () => { secretForL2MessageConsumption, ) .simulate(), - ).rejects.toThrowError("Cannot satisfy constraint 'l1_to_l2_message_data.message.content == content"); + ).rejects.toThrowError("Invalid Content 'l1_to_l2_message_data.message.content == content'"); // send the right one - const consumptionTx = l2Bridge @@ -234,8 +234,6 @@ describe('e2e_cross_chain_messaging', () => { .withWallet(user2Wallet) .methods.claim_public(ownerAddress, bridgeAmount, ethAccount, messageKey, secretForL2MessageConsumption) .simulate(), - ).rejects.toThrowError( - "Failed to solve brillig function, reason: explicit trap hit in brillig 'l1_to_l2_message_data.message.content == content'", - ); + ).rejects.toThrowError("Invalid Content 'l1_to_l2_message_data.message.content == content'"); }, 120_000); }); diff --git a/yarn-project/end-to-end/src/e2e_deploy_contract.test.ts b/yarn-project/end-to-end/src/e2e_deploy_contract.test.ts index b3b66639898..f5ffb8b415d 100644 --- a/yarn-project/end-to-end/src/e2e_deploy_contract.test.ts +++ b/yarn-project/end-to-end/src/e2e_deploy_contract.test.ts @@ -9,7 +9,7 @@ import { PXE, TxStatus, Wallet, - getContractDeploymentInfo, + getContractInstanceFromDeployParams, isContractDeployed, } from '@aztec/aztec.js'; import { TestContractArtifact } from '@aztec/noir-contracts/Test'; @@ -39,7 +39,13 @@ describe('e2e_deploy_contract', () => { it('should deploy a contract', async () => { const publicKey = accounts[0].publicKey; const salt = Fr.random(); - const deploymentData = getContractDeploymentInfo(TestContractArtifact, [], salt, publicKey); + const deploymentData = getContractInstanceFromDeployParams( + TestContractArtifact, + [], + salt, + publicKey, + EthAddress.ZERO, + ); const deployer = new ContractDeployer(TestContractArtifact, pxe, publicKey); const tx = deployer.deploy().send({ contractAddressSalt: salt }); logger(`Tx sent with hash ${await tx.getTxHash()}`); @@ -58,7 +64,7 @@ describe('e2e_deploy_contract', () => { expect.objectContaining({ status: TxStatus.MINED, error: '', - contractAddress: deploymentData.completeAddress.address, + contractAddress: deploymentData.address, }), ); const contractAddress = receiptAfterMined.contractAddress!; @@ -178,11 +184,11 @@ describe('e2e_deploy_contract', () => { expect(goodTxReceipt.blockNumber).toEqual(expect.any(Number)); expect(badTxReceipt.blockNumber).toBeUndefined(); - await expect(pxe.getExtendedContractData(goodDeploy.completeAddress!.address)).resolves.toBeDefined(); - await expect(pxe.getExtendedContractData(goodDeploy.completeAddress!.address)).resolves.toBeDefined(); + await expect(pxe.getExtendedContractData(goodDeploy.instance!.address)).resolves.toBeDefined(); + await expect(pxe.getExtendedContractData(goodDeploy.instance!.address)).resolves.toBeDefined(); - await expect(pxe.getContractData(badDeploy.completeAddress!.address)).resolves.toBeUndefined(); - await expect(pxe.getExtendedContractData(badDeploy.completeAddress!.address)).resolves.toBeUndefined(); + await expect(pxe.getContractData(badDeploy.instance!.address)).resolves.toBeUndefined(); + await expect(pxe.getExtendedContractData(badDeploy.instance!.address)).resolves.toBeUndefined(); } finally { sequencer?.updateSequencerConfig({ minTxsPerBlock: 1, diff --git a/yarn-project/end-to-end/src/e2e_escrow_contract.test.ts b/yarn-project/end-to-end/src/e2e_escrow_contract.test.ts index 28d529572f5..b39411d4206 100644 --- a/yarn-project/end-to-end/src/e2e_escrow_contract.test.ts +++ b/yarn-project/end-to-end/src/e2e_escrow_contract.test.ts @@ -4,6 +4,7 @@ import { BatchCall, CompleteAddress, DebugLogger, + EthAddress, ExtendedNote, Fr, GrumpkinPrivateKey, @@ -14,8 +15,9 @@ import { TxStatus, computeMessageSecretHash, generatePublicKey, - getContractDeploymentInfo, + getContractInstanceFromDeployParams, } from '@aztec/aztec.js'; +import { computePartialAddress } from '@aztec/circuits.js'; import { EscrowContract, EscrowContractArtifact } from '@aztec/noir-contracts/Escrow'; import { TokenContract } from '@aztec/noir-contracts/Token'; @@ -55,8 +57,14 @@ describe('e2e_escrow_contract', () => { escrowPrivateKey = GrumpkinScalar.random(); escrowPublicKey = generatePublicKey(escrowPrivateKey); const salt = Fr.random(); - const deployInfo = getContractDeploymentInfo(EscrowContractArtifact, [owner], salt, escrowPublicKey); - await pxe.registerAccount(escrowPrivateKey, deployInfo.completeAddress.partialAddress); + const deployInfo = getContractInstanceFromDeployParams( + EscrowContractArtifact, + [owner], + salt, + escrowPublicKey, + EthAddress.ZERO, + ); + await pxe.registerAccount(escrowPrivateKey, computePartialAddress(deployInfo)); escrowContract = await EscrowContract.deployWithPublicKey(escrowPublicKey, wallet, owner) .send({ contractAddressSalt: salt }) diff --git a/yarn-project/end-to-end/src/e2e_inclusion_proofs_contract.test.ts b/yarn-project/end-to-end/src/e2e_inclusion_proofs_contract.test.ts index a6f03ade521..87821e95d11 100644 --- a/yarn-project/end-to-end/src/e2e_inclusion_proofs_contract.test.ts +++ b/yarn-project/end-to-end/src/e2e_inclusion_proofs_contract.test.ts @@ -7,7 +7,7 @@ import { INITIAL_L2_BLOCK_NUM, PXE, Point, - getContractDeploymentInfo, + getContractInstanceFromDeployParams, } from '@aztec/aztec.js'; import { NewContractData } from '@aztec/circuits.js'; import { computeContractLeaf } from '@aztec/circuits.js/abis'; @@ -157,11 +157,15 @@ describe('e2e_inclusion_proofs_contract', () => { it('public value existence failure case', async () => { // Choose random block number between first block and current block number to test archival node const blockNumber = await getRandomBlockNumber(); - const randomPublicValue = Fr.random(); await expect( contract.methods.test_public_value_inclusion_proof(randomPublicValue, blockNumber).send().wait(), - ).rejects.toThrow(/Public value does not match value in witness/); + ).rejects.toThrow('Public value does not match the witness'); + }); + + it('proves existence of uninitialized public value', async () => { + const blockNumber = await getRandomBlockNumber(); + await contract.methods.test_public_unused_value_inclusion_proof(blockNumber).send().wait(); }); }); @@ -189,23 +193,25 @@ describe('e2e_inclusion_proofs_contract', () => { describe('contract inclusion', () => { // InclusionProofs contract doesn't have associated public key because it's not an account contract const publicKey = Point.ZERO; - let functionTreeRoot: Fr; - let constructorHash: Fr; + let contractClassId: Fr; + let initializationHash: Fr; let portalContractAddress: EthAddress; beforeAll(() => { const contractArtifact = contract.artifact; - const constructorArgs = [publicValue]; + portalContractAddress = EthAddress.random(); - ({ constructorHash, functionTreeRoot } = getContractDeploymentInfo( + const instance = getContractInstanceFromDeployParams( contractArtifact, constructorArgs, contractAddressSalt, publicKey, - )); + portalContractAddress, + ); - portalContractAddress = contract.portalContract; + contractClassId = instance.contractClassId; + initializationHash = instance.initializationHash; }); it('proves existence of a contract', async () => { @@ -218,8 +224,8 @@ describe('e2e_inclusion_proofs_contract', () => { .test_contract_inclusion_proof( publicKey, contractAddressSalt, - functionTreeRoot, - constructorHash, + contractClassId, + initializationHash, portalContractAddress, blockNumber, ) @@ -227,11 +233,11 @@ describe('e2e_inclusion_proofs_contract', () => { .wait(); }); - it('contract existence failure case', async () => { + // TODO(@spalladino): Re-enable once we add check for non-inclusion based on nullifier + it.skip('contract existence failure case', async () => { // This should fail because we choose a block number before the contract was deployed const blockNumber = deploymentBlockNumber - 1; - - const contractData = new NewContractData(contract.address, contract.portalContract, functionTreeRoot); + const contractData = new NewContractData(contract.address, portalContractAddress, contractClassId); const leaf = computeContractLeaf(contractData); await expect( @@ -239,8 +245,8 @@ describe('e2e_inclusion_proofs_contract', () => { .test_contract_inclusion_proof( publicKey, contractAddressSalt, - functionTreeRoot, - constructorHash, + contractClassId, + initializationHash, portalContractAddress, blockNumber, ) @@ -251,12 +257,10 @@ describe('e2e_inclusion_proofs_contract', () => { }); const getRandomBlockNumberSinceDeployment = async () => { - const currentBlockNumber = await pxe.getBlockNumber(); - return deploymentBlockNumber + Math.floor(Math.random() * (currentBlockNumber - deploymentBlockNumber)); + return deploymentBlockNumber + Math.floor(Math.random() * ((await pxe.getBlockNumber()) - deploymentBlockNumber)); }; const getRandomBlockNumber = async () => { - const currentBlockNumber = await pxe.getBlockNumber(); - return deploymentBlockNumber + Math.floor(Math.random() * (currentBlockNumber - INITIAL_L2_BLOCK_NUM)); + return deploymentBlockNumber + Math.floor(Math.random() * ((await pxe.getBlockNumber()) - INITIAL_L2_BLOCK_NUM)); }; }); diff --git a/yarn-project/end-to-end/src/e2e_nested_contract.test.ts b/yarn-project/end-to-end/src/e2e_nested_contract.test.ts index f1f4d29b3ae..d3dec0751ea 100644 --- a/yarn-project/end-to-end/src/e2e_nested_contract.test.ts +++ b/yarn-project/end-to-end/src/e2e_nested_contract.test.ts @@ -36,12 +36,32 @@ describe('e2e_nested_contract', () => { .wait(); if (isGenerateTestDataEnabled()) { - const privateKernelInputs = getTestData('private-kernel-inputs-inner'); - const nestedCallPrivateKernelInput = privateKernelInputs[privateKernelInputs.length - 1]; - writeFileSync( - '../noir-protocol-circuits/src/fixtures/nested-call-private-kernel-inner.hex', - nestedCallPrivateKernelInput.toBuffer().toString('hex'), - ); + { + const privateKernelInputsInit = getTestData('private-kernel-inputs-init'); + const nestedCallPrivateKernelInput = privateKernelInputsInit[0]; + writeFileSync( + '../noir-protocol-circuits/src/fixtures/nested-call-private-kernel-init.hex', + nestedCallPrivateKernelInput.toBuffer().toString('hex'), + ); + } + + { + const privateKernelInputsInner = getTestData('private-kernel-inputs-inner'); + const nestedCallPrivateKernelInput = privateKernelInputsInner[privateKernelInputsInner.length - 1]; + writeFileSync( + '../noir-protocol-circuits/src/fixtures/nested-call-private-kernel-inner.hex', + nestedCallPrivateKernelInput.toBuffer().toString('hex'), + ); + } + + { + const privateKernelInputsOrdering = getTestData('private-kernel-inputs-ordering'); + const nestedCallPrivateKernelInput = privateKernelInputsOrdering[0]; + writeFileSync( + '../noir-protocol-circuits/src/fixtures/nested-call-private-kernel-ordering.hex', + nestedCallPrivateKernelInput.toBuffer().toString('hex'), + ); + } } }, 100_000); diff --git a/yarn-project/end-to-end/src/e2e_note_getter.test.ts b/yarn-project/end-to-end/src/e2e_note_getter.test.ts new file mode 100644 index 00000000000..ed00016612a --- /dev/null +++ b/yarn-project/end-to-end/src/e2e_note_getter.test.ts @@ -0,0 +1,257 @@ +import { AztecAddress, Comparator, Fr, Wallet, toBigInt } from '@aztec/aztec.js'; +import { DocsExampleContract, TestContract } from '@aztec/noir-contracts'; + +import { setup } from './fixtures/utils.js'; + +interface NoirOption { + _is_some: boolean; + _value: T; +} + +const sortFunc = (a: any, b: any) => + a.points > b.points ? 1 : a.points < b.points ? -1 : a.randomness > b.randomness ? 1 : -1; + +function unwrapOptions(options: NoirOption[]): T[] { + return options.filter((option: any) => option._is_some).map((option: any) => option._value); +} + +describe('e2e_note_getter', () => { + let wallet: Wallet; + let teardown: () => Promise; + + beforeAll(async () => { + ({ teardown, wallet } = await setup()); + }, 25_000); + + afterAll(() => teardown()); + + describe('comparators', () => { + let contract: DocsExampleContract; + + beforeAll(async () => { + contract = await DocsExampleContract.deploy(wallet).send().deployed(); + // sets card value to 1 and leader to sender. + await contract.methods.initialize_private(Fr.random(), 1).send().wait(); + }, 25_000); + + it('inserts notes from 0-9, then makes multiple queries specifying the total suite of comparators', async () => { + // ISSUE #4243 + // Calling this function does not work like this + // const numbers = [...Array(10).keys()]; + // await Promise.all(numbers.map(number => contract.methods.insert_note(number).send().wait())); + // It causes a race condition complaining about root mismatch + + await contract.methods + .insert_notes([...Array(10).keys()]) + .send() + .wait(); + await contract.methods.insert_note(5, Fr.ZERO).send().wait(); + + const [returnEq, returnNeq, returnLt, returnGt, returnLte, returnGte] = await Promise.all([ + contract.methods.read_note(5, Comparator.EQ).view(), + contract.methods.read_note(5, Comparator.NEQ).view(), + contract.methods.read_note(5, Comparator.LT).view(), + contract.methods.read_note(5, Comparator.GT).view(), + contract.methods.read_note(5, Comparator.LTE).view(), + // docs:start:state_vars-NoteGetterOptionsComparatorExampleTs + contract.methods.read_note(5, Comparator.GTE).view(), + // docs:end:state_vars-NoteGetterOptionsComparatorExampleTs + ]); + + expect( + unwrapOptions(returnEq) + .map(({ points, randomness }: any) => ({ points, randomness })) + .sort(sortFunc), + ).toStrictEqual( + [ + { points: 5n, randomness: 1n }, + { points: 5n, randomness: 0n }, + ].sort(sortFunc), + ); + + expect( + unwrapOptions(returnNeq) + .map(({ points, randomness }: any) => ({ points, randomness })) + .sort(sortFunc), + ).toStrictEqual( + [ + { points: 0n, randomness: 1n }, + { points: 1n, randomness: 1n }, + { points: 7n, randomness: 1n }, + { points: 9n, randomness: 1n }, + { points: 2n, randomness: 1n }, + { points: 6n, randomness: 1n }, + { points: 8n, randomness: 1n }, + { points: 4n, randomness: 1n }, + { points: 3n, randomness: 1n }, + ].sort(sortFunc), + ); + + expect( + unwrapOptions(returnLt) + .map(({ points, randomness }: any) => ({ points, randomness })) + .sort(sortFunc), + ).toStrictEqual( + [ + { points: 0n, randomness: 1n }, + { points: 1n, randomness: 1n }, + { points: 2n, randomness: 1n }, + { points: 4n, randomness: 1n }, + { points: 3n, randomness: 1n }, + ].sort(sortFunc), + ); + + expect( + unwrapOptions(returnGt) + .map(({ points, randomness }: any) => ({ points, randomness })) + .sort(sortFunc), + ).toStrictEqual( + [ + { points: 7n, randomness: 1n }, + { points: 9n, randomness: 1n }, + { points: 6n, randomness: 1n }, + { points: 8n, randomness: 1n }, + ].sort(sortFunc), + ); + + expect( + unwrapOptions(returnLte) + .map(({ points, randomness }: any) => ({ points, randomness })) + .sort(sortFunc), + ).toStrictEqual( + [ + { points: 5n, randomness: 1n }, + { points: 5n, randomness: 0n }, + { points: 0n, randomness: 1n }, + { points: 1n, randomness: 1n }, + { points: 2n, randomness: 1n }, + { points: 4n, randomness: 1n }, + { points: 3n, randomness: 1n }, + ].sort(sortFunc), + ); + + expect( + unwrapOptions(returnGte) + .map(({ points, randomness }: any) => ({ points, randomness })) + .sort(sortFunc), + ).toStrictEqual( + [ + { points: 5n, randomness: 0n }, + { points: 5n, randomness: 1n }, + { points: 7n, randomness: 1n }, + { points: 9n, randomness: 1n }, + { points: 6n, randomness: 1n }, + { points: 8n, randomness: 1n }, + ].sort(sortFunc), + ); + }, 300_000); + }); + + describe('status filter', () => { + let contract: TestContract; + let owner: AztecAddress; + + beforeAll(async () => { + contract = await TestContract.deploy(wallet).send().deployed(); + owner = wallet.getCompleteAddress().address; + }, 100_000); + + const VALUE = 5; + + // To prevent tests from interacting with one another, we'll have each use a different storage slot. + let storageSlot: number = 2; + + beforeEach(() => { + storageSlot += 1; + }); + + async function assertNoteIsReturned(storageSlot: number, expectedValue: number, activeOrNullified: boolean) { + const viewNotesResult = await contract.methods.call_view_notes(storageSlot, activeOrNullified).view(); + const getNotesResult = await callGetNotes(storageSlot, activeOrNullified); + + expect(viewNotesResult).toEqual(getNotesResult); + expect(viewNotesResult).toEqual(BigInt(expectedValue)); + } + + async function assertNoReturnValue(storageSlot: number, activeOrNullified: boolean) { + await expect(contract.methods.call_view_notes(storageSlot, activeOrNullified).view()).rejects.toThrow('is_some'); + await expect(contract.methods.call_get_notes(storageSlot, activeOrNullified).send().wait()).rejects.toThrow( + 'is_some', + ); + } + + async function callGetNotes(storageSlot: number, activeOrNullified: boolean): Promise { + // call_get_notes exposes the return value via an event since we cannot use view() with it. + const tx = contract.methods.call_get_notes(storageSlot, activeOrNullified).send(); + await tx.wait(); + + const logs = (await tx.getUnencryptedLogs()).logs; + expect(logs.length).toBe(1); + + return toBigInt(logs[0].log.data); + } + + async function callGetNotesMany(storageSlot: number, activeOrNullified: boolean): Promise> { + // call_get_notes_many exposes the return values via event since we cannot use view() with it. + const tx = contract.methods.call_get_notes_many(storageSlot, activeOrNullified).send(); + await tx.wait(); + + const logs = (await tx.getUnencryptedLogs()).logs; + expect(logs.length).toBe(2); + + return [toBigInt(logs[0].log.data), toBigInt(logs[1].log.data)]; + } + + describe('active note only', () => { + const activeOrNullified = false; + + it('returns active notes', async () => { + await contract.methods.call_create_note(VALUE, owner, storageSlot).send().wait(); + await assertNoteIsReturned(storageSlot, VALUE, activeOrNullified); + }, 30_000); + + it('does not return nullified notes', async () => { + await contract.methods.call_create_note(VALUE, owner, storageSlot).send().wait(); + await contract.methods.call_destroy_note(storageSlot).send().wait(); + + await assertNoReturnValue(storageSlot, activeOrNullified); + }, 30_000); + }); + + describe('active and nullified notes', () => { + const activeOrNullified = true; + + it('returns active notes', async () => { + await contract.methods.call_create_note(VALUE, owner, storageSlot).send().wait(); + await assertNoteIsReturned(storageSlot, VALUE, activeOrNullified); + }, 30_000); + + it('returns nullified notes', async () => { + await contract.methods.call_create_note(VALUE, owner, storageSlot).send().wait(); + await contract.methods.call_destroy_note(storageSlot).send().wait(); + + await assertNoteIsReturned(storageSlot, VALUE, activeOrNullified); + }, 30_000); + + it('returns both active and nullified notes', async () => { + // We store two notes with two different values in the same storage slot, and then delete one of them. Note that + // we can't be sure which one was deleted since we're just deleting based on the storage slot. + await contract.methods.call_create_note(VALUE, owner, storageSlot).send().wait(); + await contract.methods + .call_create_note(VALUE + 1, owner, storageSlot) + .send() + .wait(); + await contract.methods.call_destroy_note(storageSlot).send().wait(); + + // We now fetch multiple notes, and get both the active and the nullified one. + const viewNotesManyResult = await contract.methods.call_view_notes_many(storageSlot, activeOrNullified).view(); + const getNotesManyResult = await callGetNotesMany(storageSlot, activeOrNullified); + + // We can't be sure in which order the notes will be returned, so we simply sort them to test equality. Note + // however that both view_notes and get_notes get the exact same result. + expect(viewNotesManyResult).toEqual(getNotesManyResult); + expect(viewNotesManyResult.sort()).toEqual([BigInt(VALUE), BigInt(VALUE + 1)]); + }, 45_000); + }); + }); +}); diff --git a/yarn-project/end-to-end/src/e2e_p2p_network.test.ts b/yarn-project/end-to-end/src/e2e_p2p_network.test.ts index 5522ad9c880..12e4dc54e05 100644 --- a/yarn-project/end-to-end/src/e2e_p2p_network.test.ts +++ b/yarn-project/end-to-end/src/e2e_p2p_network.test.ts @@ -5,12 +5,13 @@ import { ContractDeployer, DebugLogger, DeploySentTx, + EthAddress, Fr, Grumpkin, PublicKey, TxStatus, Wallet, - getContractDeploymentInfo, + getContractInstanceFromDeployParams, isContractDeployed, } from '@aztec/aztec.js'; import { TestContractArtifact } from '@aztec/noir-contracts/Test'; @@ -101,7 +102,7 @@ describe('e2e_p2p_network', () => { // TODO: the following config options are not applicable to bootstrap nodes p2pBlockCheckIntervalMS: 1000, - l2QueueSize: 1, + p2pL2QueueSize: 1, transactionProtocol: '', bootstrapNodes: [''], }; @@ -137,7 +138,13 @@ describe('e2e_p2p_network', () => { const txs: DeploySentTx[] = []; for (let i = 0; i < numTxs; i++) { const salt = Fr.random(); - const origin = getContractDeploymentInfo(TestContractArtifact, [], salt, publicKey).completeAddress.address; + const origin = getContractInstanceFromDeployParams( + TestContractArtifact, + [], + salt, + publicKey, + EthAddress.ZERO, + ).address; const deployer = new ContractDeployer(TestContractArtifact, pxe, publicKey); const tx = deployer.deploy().send({ contractAddressSalt: salt }); logger(`Tx sent with hash ${await tx.getTxHash()}`); diff --git a/yarn-project/end-to-end/src/e2e_persistence.test.ts b/yarn-project/end-to-end/src/e2e_persistence.test.ts index 98726df23a7..cd9c0de3998 100644 --- a/yarn-project/end-to-end/src/e2e_persistence.test.ts +++ b/yarn-project/end-to-end/src/e2e_persistence.test.ts @@ -1,22 +1,27 @@ import { getUnsafeSchnorrAccount, getUnsafeSchnorrWallet } from '@aztec/accounts/single_key'; import { AccountWallet, + ContractInstanceWithAddress, ExtendedNote, Note, TxHash, computeMessageSecretHash, waitForAccountSynch, } from '@aztec/aztec.js'; -import { CompleteAddress, EthAddress, Fq, Fr } from '@aztec/circuits.js'; +import { Salt } from '@aztec/aztec.js/account'; +import { AztecAddress, CompleteAddress, Fq, Fr } from '@aztec/circuits.js'; import { DeployL1Contracts } from '@aztec/ethereum'; import { TokenContract } from '@aztec/noir-contracts/Token'; +import { jest } from '@jest/globals'; import { mkdtemp } from 'fs/promises'; import { tmpdir } from 'os'; import { join } from 'path'; import { EndToEndContext, setup } from './fixtures/utils.js'; +jest.setTimeout(60_000); + describe('Aztec persistence', () => { /** * These tests check that the Aztec Node and PXE can be shutdown and restarted without losing data. @@ -32,9 +37,11 @@ describe('Aztec persistence', () => { */ // the test contract and account deploying it - let contractAddress: CompleteAddress; + let contractInstance: ContractInstanceWithAddress; + let contractAddress: AztecAddress; let ownerPrivateKey: Fq; let ownerAddress: CompleteAddress; + let ownerSalt: Salt; // a directory where data will be persisted by components // passing this through to the Node or PXE will control whether they use persisted data or not @@ -55,12 +62,14 @@ describe('Aztec persistence', () => { ownerPrivateKey = Fq.random(); const ownerWallet = await getUnsafeSchnorrAccount(initialContext.pxe, ownerPrivateKey, Fr.ZERO).waitDeploy(); ownerAddress = ownerWallet.getCompleteAddress(); + ownerSalt = ownerWallet.salt; const deployer = TokenContract.deploy(ownerWallet, ownerWallet.getAddress(), 'Test token', 'TEST', 2); await deployer.simulate({}); const contract = await deployer.send().deployed(); - contractAddress = contract.completeAddress; + contractInstance = contract.instance; + contractAddress = contract.address; const secret = Fr.random(); @@ -103,7 +112,7 @@ describe('Aztec persistence', () => { beforeEach(async () => { context = await contextSetup(); ownerWallet = await getUnsafeSchnorrWallet(context.pxe, ownerAddress.address, ownerPrivateKey); - contract = await TokenContract.at(contractAddress.address, ownerWallet); + contract = await TokenContract.at(contractAddress, ownerWallet); }, timeout); afterEach(async () => { @@ -153,7 +162,7 @@ describe('Aztec persistence', () => { expect(ownerBalance).toEqual(initialOwnerBalance - 500n); expect(targetBalance).toEqual(500n); - }); + }, 30_000); }); describe.each([ @@ -182,31 +191,27 @@ describe('Aztec persistence', () => { }); it('the node has the contract', async () => { - await expect(context.aztecNode.getContractData(contractAddress.address)).resolves.toBeDefined(); + await expect(context.aztecNode.getContractData(contractAddress)).resolves.toBeDefined(); }); it('pxe does not know of the deployed contract', async () => { await context.pxe.registerRecipient(ownerAddress); const wallet = await getUnsafeSchnorrAccount(context.pxe, Fq.random(), Fr.ZERO).waitDeploy(); - const contract = await TokenContract.at(contractAddress.address, wallet); - await expect(contract.methods.balance_of_private(ownerAddress.address).view()).rejects.toThrowError( - /Unknown contract/, - ); + await expect(TokenContract.at(contractAddress, wallet)).rejects.toThrow(/has not been registered/); }); it("pxe does not have owner's private notes", async () => { await context.pxe.addContracts([ { artifact: TokenContract.artifact, - completeAddress: contractAddress, - portalContract: EthAddress.ZERO, + instance: contractInstance, }, ]); await context.pxe.registerRecipient(ownerAddress); const wallet = await getUnsafeSchnorrAccount(context.pxe, Fq.random(), Fr.ZERO).waitDeploy(); - const contract = await TokenContract.at(contractAddress.address, wallet); + const contract = await TokenContract.at(contractAddress, wallet); await expect(contract.methods.balance_of_private(ownerAddress.address).view()).resolves.toEqual(0n); }); @@ -214,13 +219,12 @@ describe('Aztec persistence', () => { await context.pxe.addContracts([ { artifact: TokenContract.artifact, - completeAddress: contractAddress, - portalContract: EthAddress.ZERO, + instance: contractInstance, }, ]); const wallet = await getUnsafeSchnorrAccount(context.pxe, Fq.random(), Fr.ZERO).waitDeploy(); - const contract = await TokenContract.at(contractAddress.address, wallet); + const contract = await TokenContract.at(contractAddress, wallet); await expect(contract.methods.total_supply().view()).resolves.toBeGreaterThan(0n); }); @@ -229,15 +233,14 @@ describe('Aztec persistence', () => { await context.pxe.addContracts([ { artifact: TokenContract.artifact, - completeAddress: contractAddress, - portalContract: EthAddress.ZERO, + instance: contractInstance, }, ]); - const ownerAccount = getUnsafeSchnorrAccount(context.pxe, ownerPrivateKey, ownerAddress); + const ownerAccount = getUnsafeSchnorrAccount(context.pxe, ownerPrivateKey, ownerSalt); await ownerAccount.register(); const ownerWallet = await ownerAccount.getWallet(); - const contract = await TokenContract.at(contractAddress.address, ownerWallet); + const contract = await TokenContract.at(contractAddress, ownerWallet); await waitForAccountSynch(context.pxe, ownerAddress, { interval: 1, timeout: 10 }); @@ -263,16 +266,15 @@ describe('Aztec persistence', () => { await temporaryContext.pxe.addContracts([ { artifact: TokenContract.artifact, - completeAddress: contractAddress, - portalContract: EthAddress.ZERO, + instance: contractInstance, }, ]); - const ownerAccount = getUnsafeSchnorrAccount(temporaryContext.pxe, ownerPrivateKey, ownerAddress); + const ownerAccount = getUnsafeSchnorrAccount(temporaryContext.pxe, ownerPrivateKey, ownerSalt); await ownerAccount.register(); const ownerWallet = await ownerAccount.getWallet(); - const contract = await TokenContract.at(contractAddress.address, ownerWallet); + const contract = await TokenContract.at(contractAddress, ownerWallet); // mint some tokens with a secret we know and redeem later on a separate PXE secret = Fr.random(); @@ -297,7 +299,7 @@ describe('Aztec persistence', () => { beforeEach(async () => { context = await setup(0, { dataDirectory, deployL1ContractsValues }, { dataDirectory }); ownerWallet = await getUnsafeSchnorrWallet(context.pxe, ownerAddress.address, ownerPrivateKey); - contract = await TokenContract.at(contractAddress.address, ownerWallet); + contract = await TokenContract.at(contractAddress, ownerWallet); await waitForAccountSynch(context.pxe, ownerAddress, { interval: 0.1, timeout: 5 }); }, 5000); @@ -332,7 +334,7 @@ describe('Aztec persistence', () => { async function addPendingShieldNoteToPXE( wallet: AccountWallet, - asset: CompleteAddress, + asset: AztecAddress, amount: bigint, secretHash: Fr, txHash: TxHash, @@ -341,6 +343,6 @@ async function addPendingShieldNoteToPXE( // TODO AlexG, this feels brittle const storageSlot = new Fr(5); const note = new Note([new Fr(amount), secretHash]); - const extendedNote = new ExtendedNote(note, wallet.getAddress(), asset.address, storageSlot, txHash); + const extendedNote = new ExtendedNote(note, wallet.getAddress(), asset, storageSlot, txHash); await wallet.addNote(extendedNote); } diff --git a/yarn-project/end-to-end/src/e2e_public_cross_chain_messaging.test.ts b/yarn-project/end-to-end/src/e2e_public_cross_chain_messaging.test.ts index a6975b6b0f8..ea57f757f73 100644 --- a/yarn-project/end-to-end/src/e2e_public_cross_chain_messaging.test.ts +++ b/yarn-project/end-to-end/src/e2e_public_cross_chain_messaging.test.ts @@ -136,7 +136,7 @@ describe('e2e_public_cross_chain_messaging', () => { .withWallet(user2Wallet) .methods.claim_public(user2Wallet.getAddress(), bridgeAmount, ethAccount, messageKey, secret) .simulate(), - ).rejects.toThrow(); + ).rejects.toThrow("Invalid Content 'l1_to_l2_message_data.message.content == content'"); // user2 consumes owner's L1-> L2 message on bridge contract and mints public tokens on L2 logger("user2 consumes owner's message on L2 Publicly"); @@ -185,6 +185,6 @@ describe('e2e_public_cross_chain_messaging', () => { .withWallet(user2Wallet) .methods.claim_private(secretHash, bridgeAmount, ethAccount, messageKey, secret) .simulate(), - ).rejects.toThrowError("Cannot satisfy constraint 'l1_to_l2_message_data.message.content == content"); + ).rejects.toThrowError("Invalid Content 'l1_to_l2_message_data.message.content == content'"); }, 60_000); }); diff --git a/yarn-project/end-to-end/src/e2e_singleton.test.ts b/yarn-project/end-to-end/src/e2e_singleton.test.ts deleted file mode 100644 index 251b4e7a44f..00000000000 --- a/yarn-project/end-to-end/src/e2e_singleton.test.ts +++ /dev/null @@ -1,31 +0,0 @@ -import { Fr, Wallet } from '@aztec/aztec.js'; -import { DocsExampleContract } from '@aztec/noir-contracts'; - -import { setup } from './fixtures/utils.js'; - -describe('e2e_singleton', () => { - let wallet: Wallet; - - let teardown: () => Promise; - let contract: DocsExampleContract; - - beforeAll(async () => { - ({ teardown, wallet } = await setup()); - contract = await DocsExampleContract.deploy(wallet).send().deployed(); - // sets card value to 1 and leader to sender. - await contract.methods.initialize_private(Fr.random(), 1).send().wait(); - }, 25_000); - - afterAll(() => teardown()); - - // Singleton tests: - it('can read singleton and replace/update it in the same call', async () => { - await expect(contract.methods.update_legendary_card(Fr.random(), 0).simulate()).rejects.toThrowError( - 'Assertion failed: can only update to higher value', - ); - - const newPoints = 3n; - await contract.methods.update_legendary_card(Fr.random(), newPoints).send().wait(); - expect((await contract.methods.get_leader().view()).points).toEqual(newPoints); - }); -}); diff --git a/yarn-project/end-to-end/src/e2e_slow_tree.test.ts b/yarn-project/end-to-end/src/e2e_slow_tree.test.ts index 6fc89230b02..00be8111f10 100644 --- a/yarn-project/end-to-end/src/e2e_slow_tree.test.ts +++ b/yarn-project/end-to-end/src/e2e_slow_tree.test.ts @@ -1,15 +1,11 @@ /* eslint-disable camelcase */ import { CheatCodes, DebugLogger, Fr, Wallet } from '@aztec/aztec.js'; +import { AztecLmdbStore } from '@aztec/kv-store'; import { Pedersen, SparseTree, newTree } from '@aztec/merkle-tree'; import { SlowTreeContract } from '@aztec/noir-contracts/SlowTree'; -import { default as levelup } from 'levelup'; -import { type MemDown, default as memdown } from 'memdown'; - import { setup } from './fixtures/utils.js'; -export const createMemDown = () => (memdown as any)() as MemDown; - describe('e2e_slow_tree', () => { let logger: DebugLogger; let wallet: Wallet; @@ -27,11 +23,17 @@ describe('e2e_slow_tree', () => { it('Messing around with noir slow tree', async () => { const depth = 254; - const slowUpdateTreeSimulator = await newTree(SparseTree, levelup(createMemDown()), new Pedersen(), 'test', depth); + const slowUpdateTreeSimulator = await newTree( + SparseTree, + await AztecLmdbStore.openTmp(), + new Pedersen(), + 'test', + depth, + ); const getMembershipProof = async (index: bigint, includeUncommitted: boolean) => { return { index, - value: Fr.fromBuffer((await slowUpdateTreeSimulator.getLeafValue(index, includeUncommitted))!), + value: Fr.fromBuffer(slowUpdateTreeSimulator.getLeafValue(index, includeUncommitted)!), // eslint-disable-next-line camelcase sibling_path: (await slowUpdateTreeSimulator.getSiblingPath(index, includeUncommitted)).toFieldArray(), }; diff --git a/yarn-project/end-to-end/src/e2e_state_vars.test.ts b/yarn-project/end-to-end/src/e2e_state_vars.test.ts new file mode 100644 index 00000000000..0ad3a75a04e --- /dev/null +++ b/yarn-project/end-to-end/src/e2e_state_vars.test.ts @@ -0,0 +1,160 @@ +import { TxStatus, Wallet } from '@aztec/aztec.js'; +import { DocsExampleContract } from '@aztec/noir-contracts'; + +import { setup } from './fixtures/utils.js'; + +describe('e2e_state_vars', () => { + let wallet: Wallet; + + let teardown: () => Promise; + let contract: DocsExampleContract; + + const POINTS = 1n; + const RANDOMNESS = 2n; + + beforeAll(async () => { + ({ teardown, wallet } = await setup()); + contract = await DocsExampleContract.deploy(wallet).send().deployed(); + }, 25_000); + + afterAll(() => teardown()); + + describe('Stable Public State', () => { + it('private read of uninitialized stable', async () => { + const s = await contract.methods.get_stable().view(); + + const receipt2 = await contract.methods.match_stable(s.account, s.points).send().wait(); + expect(receipt2.status).toEqual(TxStatus.MINED); + }); + + it('private read of initialized stable', async () => { + const receipt = await contract.methods.initialize_stable(1).send().wait(); + expect(receipt.status).toEqual(TxStatus.MINED); + const s = await contract.methods.get_stable().view(); + + const receipt2 = await contract.methods.match_stable(s.account, s.points).send().wait(); + expect(receipt2.status).toEqual(TxStatus.MINED); + }, 200_000); + }); + + describe('Singleton', () => { + it('fail to read uninitialized singleton', async () => { + expect(await contract.methods.is_legendary_initialized().view()).toEqual(false); + await expect(contract.methods.get_legendary_card().view()).rejects.toThrowError(); + }); + + it('initialize singleton', async () => { + expect(await contract.methods.is_legendary_initialized().view()).toEqual(false); + const receipt = await contract.methods.initialize_private(RANDOMNESS, POINTS).send().wait(); + expect(receipt.status).toEqual(TxStatus.MINED); + + const tx = await wallet.getTx(receipt.txHash); + expect(tx?.newCommitments.length).toEqual(1); + // 1 for the tx, another for the initializer + expect(tx?.newNullifiers.length).toEqual(2); + expect(await contract.methods.is_legendary_initialized().view()).toEqual(true); + }); + + it('fail to reinitialize', async () => { + expect(await contract.methods.is_legendary_initialized().view()).toEqual(true); + await expect(contract.methods.initialize_private(RANDOMNESS, POINTS).send().wait()).rejects.toThrowError(); + expect(await contract.methods.is_legendary_initialized().view()).toEqual(true); + }); + + it('read initialized singleton', async () => { + expect(await contract.methods.is_legendary_initialized().view()).toEqual(true); + const { points, randomness } = await contract.methods.get_legendary_card().view(); + expect(points).toEqual(POINTS); + expect(randomness).toEqual(RANDOMNESS); + }); + + it('replace with same value', async () => { + expect(await contract.methods.is_legendary_initialized().view()).toEqual(true); + const noteBefore = await contract.methods.get_legendary_card().view(); + const receipt = await contract.methods.update_legendary_card(RANDOMNESS, POINTS).send().wait(); + expect(receipt.status).toEqual(TxStatus.MINED); + + const tx = await wallet.getTx(receipt.txHash); + expect(tx?.newCommitments.length).toEqual(1); + // 1 for the tx, another for the nullifier of the previous note + expect(tx?.newNullifiers.length).toEqual(2); + + const noteAfter = await contract.methods.get_legendary_card().view(); + + expect(noteBefore.owner).toEqual(noteAfter.owner); + expect(noteBefore.points).toEqual(noteAfter.points); + expect(noteBefore.randomness).toEqual(noteAfter.randomness); + expect(noteBefore.header.contract_address).toEqual(noteAfter.header.contract_address); + expect(noteBefore.header.storage_slot).toEqual(noteAfter.header.storage_slot); + expect(noteBefore.header.is_transient).toEqual(noteAfter.header.is_transient); + // !!! Nonce must be different + expect(noteBefore.header.nonce).not.toEqual(noteAfter.header.nonce); + }); + + it('replace singleton with other values', async () => { + expect(await contract.methods.is_legendary_initialized().view()).toEqual(true); + const receipt = await contract.methods + .update_legendary_card(RANDOMNESS + 2n, POINTS + 1n) + .send() + .wait(); + expect(receipt.status).toEqual(TxStatus.MINED); + const tx = await wallet.getTx(receipt.txHash); + expect(tx?.newCommitments.length).toEqual(1); + // 1 for the tx, another for the nullifier of the previous note + expect(tx?.newNullifiers.length).toEqual(2); + + const { points, randomness } = await contract.methods.get_legendary_card().view(); + expect(points).toEqual(POINTS + 1n); + expect(randomness).toEqual(RANDOMNESS + 2n); + }); + + it('replace singleton dependent on prior value', async () => { + expect(await contract.methods.is_legendary_initialized().view()).toEqual(true); + const noteBefore = await contract.methods.get_legendary_card().view(); + const receipt = await contract.methods.increase_legendary_points().send().wait(); + expect(receipt.status).toEqual(TxStatus.MINED); + const tx = await wallet.getTx(receipt.txHash); + expect(tx?.newCommitments.length).toEqual(1); + // 1 for the tx, another for the nullifier of the previous note + expect(tx?.newNullifiers.length).toEqual(2); + + const { points, randomness } = await contract.methods.get_legendary_card().view(); + expect(points).toEqual(noteBefore.points + 1n); + expect(randomness).toEqual(noteBefore.randomness); + }); + }); + + describe('Immutable Singleton', () => { + it('fail to read uninitialized singleton', async () => { + expect(await contract.methods.is_imm_initialized().view()).toEqual(false); + await expect(contract.methods.get_imm_card().view()).rejects.toThrowError(); + }); + + it('initialize singleton', async () => { + expect(await contract.methods.is_imm_initialized().view()).toEqual(false); + const receipt = await contract.methods.initialize_immutable_singleton(RANDOMNESS, POINTS).send().wait(); + expect(receipt.status).toEqual(TxStatus.MINED); + + const tx = await wallet.getTx(receipt.txHash); + expect(tx?.newCommitments.length).toEqual(1); + // 1 for the tx, another for the initializer + expect(tx?.newNullifiers.length).toEqual(2); + expect(await contract.methods.is_imm_initialized().view()).toEqual(true); + }); + + it('fail to reinitialize', async () => { + expect(await contract.methods.is_imm_initialized().view()).toEqual(true); + await expect( + contract.methods.initialize_immutable_singleton(RANDOMNESS, POINTS).send().wait(), + ).rejects.toThrowError(); + expect(await contract.methods.is_imm_initialized().view()).toEqual(true); + }); + + it('read initialized singleton', async () => { + expect(await contract.methods.is_imm_initialized().view()).toEqual(true); + const { points, randomness } = await contract.methods.get_imm_card().view(); + expect(points).toEqual(POINTS); + expect(randomness).toEqual(RANDOMNESS); + }); + }); +}); diff --git a/yarn-project/end-to-end/src/e2e_token_contract.test.ts b/yarn-project/end-to-end/src/e2e_token_contract.test.ts index 33ecf0da53a..bd40ce3faac 100644 --- a/yarn-project/end-to-end/src/e2e_token_contract.test.ts +++ b/yarn-project/end-to-end/src/e2e_token_contract.test.ts @@ -87,43 +87,87 @@ describe('e2e_token_contract', () => { reader = await ReaderContract.deploy(wallets[0]).send().deployed(); }); - it('name', async () => { - const t = toString(await asset.methods.un_get_name().view()); - expect(t).toBe(TOKEN_NAME); + describe('name', () => { + it('private', async () => { + const t = toString(await asset.methods.un_get_name().view()); + expect(t).toBe(TOKEN_NAME); - const tx = reader.methods.check_name(asset.address, TOKEN_NAME).send(); - const receipt = await tx.wait(); - expect(receipt.status).toBe(TxStatus.MINED); + const tx = reader.methods.check_name_private(asset.address, TOKEN_NAME).send(); + const receipt = await tx.wait(); + expect(receipt.status).toBe(TxStatus.MINED); + + await expect(reader.methods.check_name_private(asset.address, 'WRONG_NAME').simulate()).rejects.toThrowError( + "Cannot satisfy constraint 'name.is_eq(_what)'", + ); + }); + + it('public', async () => { + const t = toString(await asset.methods.un_get_name().view()); + expect(t).toBe(TOKEN_NAME); - await expect(reader.methods.check_name(asset.address, 'WRONG_NAME').simulate()).rejects.toThrowError( - "Failed to solve brillig function, reason: explicit trap hit in brillig 'name.is_eq(_what)'", - ); + const tx = reader.methods.check_name_public(asset.address, TOKEN_NAME).send(); + const receipt = await tx.wait(); + expect(receipt.status).toBe(TxStatus.MINED); + + await expect(reader.methods.check_name_public(asset.address, 'WRONG_NAME').simulate()).rejects.toThrowError( + "Failed to solve brillig function, reason: explicit trap hit in brillig 'name.is_eq(_what)'", + ); + }); }); - it('symbol', async () => { - const t = toString(await asset.methods.un_get_symbol().view()); - expect(t).toBe(TOKEN_SYMBOL); + describe('symbol', () => { + it('private', async () => { + const t = toString(await asset.methods.un_get_symbol().view()); + expect(t).toBe(TOKEN_SYMBOL); - const tx = reader.methods.check_symbol(asset.address, TOKEN_SYMBOL).send(); - const receipt = await tx.wait(); - expect(receipt.status).toBe(TxStatus.MINED); + const tx = reader.methods.check_symbol_private(asset.address, TOKEN_SYMBOL).send(); + const receipt = await tx.wait(); + expect(receipt.status).toBe(TxStatus.MINED); + + await expect( + reader.methods.check_symbol_private(asset.address, 'WRONG_SYMBOL').simulate(), + ).rejects.toThrowError("Cannot satisfy constraint 'symbol.is_eq(_what)'"); + }); + it('public', async () => { + const t = toString(await asset.methods.un_get_symbol().view()); + expect(t).toBe(TOKEN_SYMBOL); - await expect(reader.methods.check_symbol(asset.address, 'WRONG_SYMBOL').simulate()).rejects.toThrowError( - "Failed to solve brillig function, reason: explicit trap hit in brillig 'symbol.is_eq(_what)'", - ); + const tx = reader.methods.check_symbol_public(asset.address, TOKEN_SYMBOL).send(); + const receipt = await tx.wait(); + expect(receipt.status).toBe(TxStatus.MINED); + + await expect(reader.methods.check_symbol_public(asset.address, 'WRONG_SYMBOL').simulate()).rejects.toThrowError( + "Failed to solve brillig function, reason: explicit trap hit in brillig 'symbol.is_eq(_what)'", + ); + }); }); - it('decimals', async () => { - const t = await asset.methods.un_get_decimals().view(); - expect(t).toBe(TOKEN_DECIMALS); + describe('decimals', () => { + it('private', async () => { + const t = await asset.methods.un_get_decimals().view(); + expect(t).toBe(TOKEN_DECIMALS); - const tx = reader.methods.check_decimals(asset.address, TOKEN_DECIMALS).send(); - const receipt = await tx.wait(); - expect(receipt.status).toBe(TxStatus.MINED); + const tx = reader.methods.check_decimals_private(asset.address, TOKEN_DECIMALS).send(); + const receipt = await tx.wait(); + expect(receipt.status).toBe(TxStatus.MINED); - await expect(reader.methods.check_decimals(asset.address, 99).simulate()).rejects.toThrowError( - "Failed to solve brillig function, reason: explicit trap hit in brillig 'ret[0] as u8 == what'", - ); + await expect(reader.methods.check_decimals_private(asset.address, 99).simulate()).rejects.toThrowError( + "Cannot satisfy constraint 'ret[0] as u8 == what'", + ); + }); + + it('public', async () => { + const t = await asset.methods.un_get_decimals().view(); + expect(t).toBe(TOKEN_DECIMALS); + + const tx = reader.methods.check_decimals_public(asset.address, TOKEN_DECIMALS).send(); + const receipt = await tx.wait(); + expect(receipt.status).toBe(TxStatus.MINED); + + await expect(reader.methods.check_decimals_public(asset.address, 99).simulate()).rejects.toThrowError( + "Failed to solve brillig function, reason: explicit trap hit in brillig 'ret[0] as u8 == what'", + ); + }); }); }); diff --git a/yarn-project/end-to-end/src/fixtures/fixtures.ts b/yarn-project/end-to-end/src/fixtures/fixtures.ts index f1bb81bea1e..3e4ffc7c8ae 100644 --- a/yarn-project/end-to-end/src/fixtures/fixtures.ts +++ b/yarn-project/end-to-end/src/fixtures/fixtures.ts @@ -1,6 +1,3 @@ -import { foundry } from 'viem/chains'; - export const MNEMONIC = 'test test test test test test test test test test test junk'; -export const localAnvil = foundry; export const privateKey = Buffer.from('ac0974bec39a17e36ba4a6b4d238ff944bacb478cbed5efcae784d7bf4f2ff80', 'hex'); export const privateKey2 = Buffer.from('59c6995e998f97a5a0044966f0945389dc9e86dae88c7a8412f4603b6b78690d', 'hex'); diff --git a/yarn-project/end-to-end/src/fixtures/utils.ts b/yarn-project/end-to-end/src/fixtures/utils.ts index 8942bded297..c3235d235cb 100644 --- a/yarn-project/end-to-end/src/fixtures/utils.ts +++ b/yarn-project/end-to-end/src/fixtures/utils.ts @@ -48,23 +48,17 @@ import { http, } from 'viem'; import { mnemonicToAccount } from 'viem/accounts'; +import { foundry } from 'viem/chains'; -import { MNEMONIC, localAnvil } from './fixtures.js'; +import { MNEMONIC } from './fixtures.js'; import { isMetricsLoggingRequested, setupMetricsLogger } from './logging.js'; export { deployAndInitializeTokenAndBridgeContracts } from '../shared/cross_chain_test_harness.js'; -const { PXE_URL = '', AZTEC_NODE_URL = '' } = process.env; +const { PXE_URL = '' } = process.env; -const getAztecNodeUrl = () => { - if (AZTEC_NODE_URL) { - return AZTEC_NODE_URL; - } - - // If AZTEC_NODE_URL is not set, we assume that the PXE is running on the same host as the Aztec Node and use the default port - const url = new URL(PXE_URL); - url.port = '8079'; - return url.toString(); +const getAztecUrl = () => { + return PXE_URL; }; export const setupL1Contracts = async ( @@ -98,7 +92,7 @@ export const setupL1Contracts = async ( contractBytecode: RollupBytecode, }, }; - return await deployL1Contracts(l1RpcUrl, account, localAnvil, logger, l1Artifacts); + return await deployL1Contracts(l1RpcUrl, account, foundry, logger, l1Artifacts); }; /** @@ -164,7 +158,7 @@ async function setupWithRemoteEnvironment( numberOfAccounts: number, ) { // we are setting up against a remote environment, l1 contracts are already deployed - const aztecNodeUrl = getAztecNodeUrl(); + const aztecNodeUrl = getAztecUrl(); logger(`Creating Aztec Node client to remote host ${aztecNodeUrl}`); const aztecNode = createAztecNodeClient(aztecNodeUrl); logger(`Creating PXE client to remote host ${PXE_URL}`); @@ -184,11 +178,11 @@ async function setupWithRemoteEnvironment( const walletClient = createWalletClient({ account, - chain: localAnvil, + chain: foundry, transport: http(config.rpcUrl), }); const publicClient = createPublicClient({ - chain: localAnvil, + chain: foundry, transport: http(config.rpcUrl), }); const deployL1ContractsValues: DeployL1Contracts = { diff --git a/yarn-project/end-to-end/src/guides/writing_an_account_contract.test.ts b/yarn-project/end-to-end/src/guides/writing_an_account_contract.test.ts index 497d7442f64..894be127d2b 100644 --- a/yarn-project/end-to-end/src/guides/writing_an_account_contract.test.ts +++ b/yarn-project/end-to-end/src/guides/writing_an_account_contract.test.ts @@ -86,10 +86,9 @@ describe('guides/writing_an_account_contract', () => { expect(balance).toEqual(50n); // docs:start:account-contract-fails - const walletAddress = wallet.getCompleteAddress(); const wrongKey = GrumpkinScalar.random(); const wrongAccountContract = new SchnorrHardcodedKeyAccountContract(wrongKey); - const wrongAccount = new AccountManager(pxe, encryptionPrivateKey, wrongAccountContract, walletAddress); + const wrongAccount = new AccountManager(pxe, encryptionPrivateKey, wrongAccountContract, account.salt); const wrongWallet = await wrongAccount.getWallet(); const tokenWithWrongWallet = token.withWallet(wrongWallet); diff --git a/yarn-project/end-to-end/src/integration_archiver_l1_to_l2.test.ts b/yarn-project/end-to-end/src/integration_archiver_l1_to_l2.test.ts index eb57e00eb21..25e501b8fdb 100644 --- a/yarn-project/end-to-end/src/integration_archiver_l1_to_l2.test.ts +++ b/yarn-project/end-to-end/src/integration_archiver_l1_to_l2.test.ts @@ -43,7 +43,7 @@ describe('archiver integration with l1 to l2 messages', () => { config.archiverPollingIntervalMS = 100; archiver = await Archiver.createAndSync( { ...config, l1Contracts: deployL1ContractsValues.l1ContractAddresses }, - new KVArchiverDataStore(await AztecLmdbStore.create(deployL1ContractsValues.l1ContractAddresses.rollupAddress)), + new KVArchiverDataStore(await AztecLmdbStore.open(deployL1ContractsValues.l1ContractAddresses.rollupAddress)), ); const walletClient = deployL1ContractsValues.walletClient; diff --git a/yarn-project/end-to-end/src/integration_l1_publisher.test.ts b/yarn-project/end-to-end/src/integration_l1_publisher.test.ts index be9d3ddad02..f147fccfd4b 100644 --- a/yarn-project/end-to-end/src/integration_l1_publisher.test.ts +++ b/yarn-project/end-to-end/src/integration_l1_publisher.test.ts @@ -10,6 +10,7 @@ import { to2Fields, } from '@aztec/aztec.js'; import { + Header, KernelCircuitPublicInputs, MAX_NEW_COMMITMENTS_PER_TX, MAX_NEW_L2_TO_L1_MSGS_PER_TX, @@ -28,13 +29,13 @@ import { } from '@aztec/circuits.js/factories'; import { createEthereumChain } from '@aztec/ethereum'; import { makeTuple, range } from '@aztec/foundation/array'; +import { AztecLmdbStore } from '@aztec/kv-store'; import { InboxAbi, OutboxAbi, RollupAbi } from '@aztec/l1-artifacts'; import { EmptyRollupProver, L1Publisher, RealRollupCircuitSimulator, SoloBlockBuilder, - getBlockHeader, getL1Publisher, getVerificationKeys, makeEmptyProcessedTx as makeEmptyProcessedTxFromHistoricalTreeRoots, @@ -44,8 +45,6 @@ import { MerkleTreeOperations, MerkleTrees } from '@aztec/world-state'; import { beforeEach, describe, expect, it } from '@jest/globals'; import * as fs from 'fs'; -import { default as levelup } from 'levelup'; -import memdown from 'memdown'; import { Address, Chain, @@ -94,8 +93,8 @@ describe('L1Publisher integration', () => { let builder: SoloBlockBuilder; let builderDb: MerkleTreeOperations; - // The global variables of the last rollup - let prevGlobals: GlobalVariables; + // The header of the last block + let prevHeader: Header; const chainId = createEthereumChain(config.rpcUrl, config.apiKey).chainInfo.id; @@ -133,7 +132,7 @@ describe('L1Publisher integration', () => { publicClient, }); - builderDb = await MerkleTrees.new(levelup((memdown as any)())).then(t => t.asLatest()); + builderDb = await MerkleTrees.new(await AztecLmdbStore.openTmp()).then(t => t.asLatest()); const vks = getVerificationKeys(); const simulator = new RealRollupCircuitSimulator(); const prover = new EmptyRollupProver(); @@ -150,12 +149,11 @@ describe('L1Publisher integration', () => { l1BlockPublishRetryIntervalMS: 100, }); - prevGlobals = GlobalVariables.empty(); + prevHeader = await builderDb.buildInitialHeader(); }, 100_000); const makeEmptyProcessedTx = async () => { - const blockHeader = await getBlockHeader(builderDb, prevGlobals); - const tx = await makeEmptyProcessedTxFromHistoricalTreeRoots(blockHeader, new Fr(chainId), new Fr(config.version)); + const tx = await makeEmptyProcessedTxFromHistoricalTreeRoots(prevHeader, new Fr(chainId), new Fr(config.version)); return tx; }; @@ -164,7 +162,7 @@ describe('L1Publisher integration', () => { const kernelOutput = KernelCircuitPublicInputs.empty(); kernelOutput.constants.txContext.chainId = fr(chainId); kernelOutput.constants.txContext.version = fr(config.version); - kernelOutput.constants.blockHeader = await getBlockHeader(builderDb, prevGlobals); + kernelOutput.constants.historicalHeader = prevHeader; kernelOutput.end.publicDataUpdateRequests = makeTuple( MAX_PUBLIC_DATA_UPDATE_REQUESTS_PER_TX, i => new PublicDataUpdateRequest(fr(i), fr(0), fr(i + 10)), @@ -257,7 +255,7 @@ describe('L1Publisher integration', () => { l2ToL1Messages: block.newL2ToL1Msgs.map(m => `0x${m.toBuffer().toString('hex').padStart(64, '0')}`), }, block: { - // The json formatting in forge is a bit brittle, so we convert Fr to a number in the few values bellow. + // The json formatting in forge is a bit brittle, so we convert Fr to a number in the few values below. // This should not be a problem for testing as long as the values are not larger than u32. archive: `0x${block.archive.root.toBuffer().toString('hex').padStart(64, '0')}`, body: `0x${block.bodyToBuffer().toString('hex')}`, @@ -367,7 +365,7 @@ describe('L1Publisher integration', () => { new Fr(await rollup.read.lastBlockTs()), ); const [block] = await builder.buildL2Block(globalVariables, txs, l1ToL2Messages); - prevGlobals = globalVariables; + prevHeader = block.header; // check that values are in the inbox for (let j = 0; j < l1ToL2Messages.length; j++) { @@ -449,7 +447,7 @@ describe('L1Publisher integration', () => { new Fr(await rollup.read.lastBlockTs()), ); const [block] = await builder.buildL2Block(globalVariables, txs, l1ToL2Messages); - prevGlobals = globalVariables; + prevHeader = block.header; writeJson(`empty_block_${i}`, block, l1ToL2Messages, [], AztecAddress.ZERO, deployerAccount.address); diff --git a/yarn-project/end-to-end/src/shared/cross_chain_test_harness.ts b/yarn-project/end-to-end/src/shared/cross_chain_test_harness.ts index db556b0ffe2..d72c2f1e8f2 100644 --- a/yarn-project/end-to-end/src/shared/cross_chain_test_harness.ts +++ b/yarn-project/end-to-end/src/shared/cross_chain_test_harness.ts @@ -104,8 +104,7 @@ export async function deployAndInitializeTokenAndBridgeContracts( throw new Error(`Token admin is not ${owner}`); } - // TODO(#3641) - Fix deserialization and compare AztecAddress directly - if ((await bridge.methods.token().view()).inner !== token.address.toBigInt()) { + if (!(await bridge.methods.token().view()).equals(token.address)) { throw new Error(`Bridge token is not ${token.address}`); } diff --git a/yarn-project/foundation/.eslintrc.docs.cjs b/yarn-project/foundation/.eslintrc.docs.cjs index cac8e4f9095..ef0ba45aede 100644 --- a/yarn-project/foundation/.eslintrc.docs.cjs +++ b/yarn-project/foundation/.eslintrc.docs.cjs @@ -40,10 +40,7 @@ const JSDOC_RULES_LEVEL = 'error'; module.exports = { ...base, plugins: [...base.plugins, 'jsdoc'], - overrides: [ - ...base.overrides, - { files: '*.test.ts', rules: { 'jsdoc/require-jsdoc': 'off' } }, - ], + overrides: [...base.overrides, { files: '*.test.ts', rules: { 'jsdoc/require-jsdoc': 'off' } }], rules: { ...base.rules, 'tsdoc/syntax': JSDOC_RULES_LEVEL, @@ -65,5 +62,5 @@ module.exports = { 'jsdoc/require-property-description': [JSDOC_RULES_LEVEL, { contexts }], 'jsdoc/require-property-name': [JSDOC_RULES_LEVEL, { contexts }], 'jsdoc/require-returns': 'off', - } + }, }; diff --git a/yarn-project/foundation/src/abi/decoder.ts b/yarn-project/foundation/src/abi/decoder.ts index ac509d59ba1..e1f4558afa2 100644 --- a/yarn-project/foundation/src/abi/decoder.ts +++ b/yarn-project/foundation/src/abi/decoder.ts @@ -1,10 +1,12 @@ +import { AztecAddress } from '../aztec-address/index.js'; import { Fr } from '../fields/index.js'; import { ABIParameter, type ABIType, ABIVariable, FunctionArtifact } from './abi.js'; +import { isAztecAddressStruct } from './utils.js'; /** * The type of our decoded ABI. */ -export type DecodedReturn = bigint | boolean | DecodedReturn[] | { [key: string]: DecodedReturn }; +export type DecodedReturn = bigint | boolean | AztecAddress | DecodedReturn[] | { [key: string]: DecodedReturn }; /** * Decodes return values from a function call. @@ -38,6 +40,10 @@ class ReturnValuesDecoder { } case 'struct': { const struct: { [key: string]: DecodedReturn } = {}; + if (isAztecAddressStruct(abiType)) { + return new AztecAddress(this.getNextField().toBuffer()); + } + for (const field of abiType.fields) { struct[field.name] = this.decodeReturn(field.type); } diff --git a/yarn-project/foundation/src/abi/encoder.ts b/yarn-project/foundation/src/abi/encoder.ts index cf25db56f54..ad515013769 100644 --- a/yarn-project/foundation/src/abi/encoder.ts +++ b/yarn-project/foundation/src/abi/encoder.ts @@ -92,7 +92,7 @@ class ArgumentEncoder { break; } for (const field of abiType.fields) { - // The ugly check bellow is here because of a `CompleteAddress`. Since it has `address` property but in ABI + // The ugly check below is here because of a `CompleteAddress`. Since it has `address` property but in ABI // it's called inner we set `field.name` here to `address` instead of using `field.name`. I know it's hacky // but using address.address in Noir looks stupid and renaming `address` param of `CompleteAddress` // to `inner` doesn't make sense. diff --git a/yarn-project/foundation/src/abi/selector.ts b/yarn-project/foundation/src/abi/selector.ts index ff70d868f03..760d3bd0931 100644 --- a/yarn-project/foundation/src/abi/selector.ts +++ b/yarn-project/foundation/src/abi/selector.ts @@ -1,5 +1,7 @@ import { fromHex, toBigIntBE, toBufferBE } from '@aztec/foundation/bigint-buffer'; -import { BufferReader } from '@aztec/foundation/serialize'; +import { BufferReader, FieldReader } from '@aztec/foundation/serialize'; + +import { randomBytes } from 'crypto'; import { keccak } from '../crypto/keccak/index.js'; import { Fr } from '../fields/index.js'; @@ -15,7 +17,7 @@ abstract class Selector { constructor(/** Value of the selector */ public value: number) { if (value > 2 ** (Selector.SIZE * 8) - 1) { - throw new Error(`selector must fit in ${Selector.SIZE} bytes.`); + throw new Error(`Selector must fit in ${Selector.SIZE} bytes (got value ${value}).`); } } @@ -104,6 +106,11 @@ export class FunctionSelector extends Selector { return new FunctionSelector(Number(fr.toBigInt())); } + static fromFields(fields: Fr[] | FieldReader) { + const reader = FieldReader.asReader(fields); + return FunctionSelector.fromField(reader.readField()); + } + /** * Creates a selector from a signature. * @param signature - Signature to generate the selector for (e.g. "transfer(field,field)"). @@ -154,6 +161,13 @@ export class FunctionSelector extends Selector { // console.log(`selector for ${signature} is ${selector}`); return selector; } + + /** + * Creates a random instance. + */ + static random() { + return FunctionSelector.fromBuffer(randomBytes(Selector.SIZE)); + } } /** Event selector branding */ diff --git a/yarn-project/foundation/src/aztec-address/index.ts b/yarn-project/foundation/src/aztec-address/index.ts index a03257f36de..bf1affc7884 100644 --- a/yarn-project/foundation/src/aztec-address/index.ts +++ b/yarn-project/foundation/src/aztec-address/index.ts @@ -1,4 +1,5 @@ import { Fr } from '../fields/index.js'; +import { FieldReader } from '../serialize/index.js'; /** * AztecAddress represents a 32-byte address in the Aztec Protocol. @@ -19,6 +20,11 @@ export class AztecAddress extends Fr { return new AztecAddress(fr.toBuffer()); } + static fromFields(fields: Fr[] | FieldReader) { + const reader = FieldReader.asReader(fields); + return AztecAddress.fromField(reader.readField()); + } + static fromBigInt(value: bigint) { return AztecAddress.fromField(new Fr(value)); } diff --git a/yarn-project/foundation/src/eth-address/index.ts b/yarn-project/foundation/src/eth-address/index.ts index 76587ecab26..7594a9ad345 100644 --- a/yarn-project/foundation/src/eth-address/index.ts +++ b/yarn-project/foundation/src/eth-address/index.ts @@ -232,7 +232,7 @@ export class EthAddress { */ static fromBuffer(buffer: Buffer | BufferReader): EthAddress { const reader = BufferReader.asReader(buffer); - return new EthAddress(reader.readBuffer()); + return new EthAddress(reader.readBytes(32)); } /** diff --git a/yarn-project/foundation/src/fields/fields.ts b/yarn-project/foundation/src/fields/fields.ts index bbf2f3b9a20..0e622cd4fde 100644 --- a/yarn-project/foundation/src/fields/fields.ts +++ b/yarn-project/foundation/src/fields/fields.ts @@ -104,6 +104,10 @@ abstract class BaseField { return this.toBuffer().equals(rhs.toBuffer()); } + lt(rhs: BaseField): boolean { + return this.toBigInt() < rhs.toBigInt(); + } + isZero(): boolean { return this.toBuffer().equals(ZERO_BUFFER); } diff --git a/yarn-project/foundation/src/fields/point.ts b/yarn-project/foundation/src/fields/point.ts index cd90e852872..b1135d6e6b5 100644 --- a/yarn-project/foundation/src/fields/point.ts +++ b/yarn-project/foundation/src/fields/point.ts @@ -1,4 +1,4 @@ -import { BufferReader } from '../serialize/buffer_reader.js'; +import { BufferReader, FieldReader } from '../serialize/index.js'; import { Fr } from './fields.js'; /** @@ -66,6 +66,11 @@ export class Point { return [this.x, this.y]; } + static fromFields(fields: Fr[] | FieldReader) { + const reader = FieldReader.asReader(fields); + return new this(reader.readField(), reader.readField()); + } + /** * Returns the contents of the point as BigInts. * @returns The point as BigInts diff --git a/yarn-project/foundation/src/json-rpc/class_converter.ts b/yarn-project/foundation/src/json-rpc/class_converter.ts index 7364664dc38..dcef59931b2 100644 --- a/yarn-project/foundation/src/json-rpc/class_converter.ts +++ b/yarn-project/foundation/src/json-rpc/class_converter.ts @@ -17,6 +17,8 @@ import { assert } from './js_utils.js'; interface StringIOClass { new (...args: any): any; + // TODO(#4254): Ensure that toString method is checked for as well. + /** * Creates an IOClass from a given string. */ @@ -40,6 +42,8 @@ interface StringIOClass { interface ObjIOClass { new (...args: any): any; + // TODO(#4254): Ensure that toJSON method is checked for as well. + /** * Creates an IOClass from a given JSON object. */ diff --git a/yarn-project/foundation/src/json-rpc/client/json_rpc_client.test.ts b/yarn-project/foundation/src/json-rpc/client/json_rpc_client.test.ts index 7b720a6e123..c95984230a2 100644 --- a/yarn-project/foundation/src/json-rpc/client/json_rpc_client.test.ts +++ b/yarn-project/foundation/src/json-rpc/client/json_rpc_client.test.ts @@ -1,16 +1,34 @@ import request from 'supertest'; import { TestNote, TestState } from '../fixtures/test_state.js'; -import { JsonRpcServer } from '../server/index.js'; +import { JsonRpcServer, createNamespacedJsonRpcServer } from '../server/index.js'; import { createJsonRpcClient } from './json_rpc_client.js'; it('test an RPC function over client', async () => { const mockFetch = async (host: string, method: string, body: any) => { - const server = new JsonRpcServer(new TestState([new TestNote('a'), new TestNote('b')]), { TestNote }, {}, true); - const result = await request(server.getApp().callback()).post(`/${method}`).send(body); + const server = new JsonRpcServer(new TestState([new TestNote('a'), new TestNote('b')]), { TestNote }, {}); + const result = await request(server.getApp().callback()).post(`/`).send(body); return JSON.parse(result.text); }; - const client = createJsonRpcClient('', { TestNote }, {}, true, mockFetch); + const client = createJsonRpcClient('', { TestNote }, {}, true, false, mockFetch); + const result = await client.addNotes([new TestNote('c')]); + expect(result[0]).toBeInstanceOf(TestNote); + expect(result[1]).toBeInstanceOf(TestNote); + expect(result[2]).toBeInstanceOf(TestNote); + expect(result[0].toString()).toBe('a'); + expect(result[1].toString()).toBe('b'); + expect(result[2].toString()).toBe('c'); +}); + +it('test a namespaced RPC function over client', async () => { + const namespace = 'testService'; + const mockFetch = async (host: string, method: string, body: any) => { + const service = new JsonRpcServer(new TestState([new TestNote('a'), new TestNote('b')]), { TestNote }, {}); + const server = createNamespacedJsonRpcServer([{ [namespace]: service }]); + const result = await request(server.getApp().callback()).post('/').send(body); + return JSON.parse(result.text); + }; + const client = createJsonRpcClient('', { TestNote }, {}, true, namespace, mockFetch); const result = await client.addNotes([new TestNote('c')]); expect(result[0]).toBeInstanceOf(TestNote); expect(result[1]).toBeInstanceOf(TestNote); diff --git a/yarn-project/foundation/src/json-rpc/client/json_rpc_client.ts b/yarn-project/foundation/src/json-rpc/client/json_rpc_client.ts index 37713f3da94..c62b20d66f7 100644 --- a/yarn-project/foundation/src/json-rpc/client/json_rpc_client.ts +++ b/yarn-project/foundation/src/json-rpc/client/json_rpc_client.ts @@ -88,12 +88,19 @@ export function makeFetch(retries: number[], noRetry: boolean, log?: DebugLogger /** * Creates a Proxy object that delegates over RPC and satisfies RemoteObject. * The server should have ran new JsonRpcServer(). + * @param host - The host URL. + * @param stringClassMap - A map of class names to string representations. + * @param objectClassMap - A map of class names to class constructors. + * @param useApiEndpoints - Whether to use the API endpoints or the default RPC endpoint. + * @param namespaceMethods - String value (or false/empty) to namespace all methods sent to the server. e.g. 'getInfo' -\> 'pxe_getInfo' + * @param fetch - The fetch implementation to use. */ export function createJsonRpcClient( host: string, stringClassMap: StringClassConverterInput, objectClassMap: JsonClassConverterInput, useApiEndpoints: boolean, + namespaceMethods?: string | false, fetch = defaultFetch, ) { const classConverter = new ClassConverter(stringClassMap, objectClassMap); @@ -122,9 +129,13 @@ export function createJsonRpcClient( return new Proxy( {}, { - get: (target, rpcMethod: string) => { - if (['then', 'catch'].includes(rpcMethod)) { - return Reflect.get(target, rpcMethod); + get: (target, method: string) => { + let rpcMethod = method; + if (namespaceMethods) { + rpcMethod = `${namespaceMethods}_${method}`; + } + if (['then', 'catch'].includes(method)) { + return Reflect.get(target, method); } return (...params: any[]) => { debug(format(`JsonRpcClient.constructor`, 'proxy', rpcMethod, '<-', params)); diff --git a/yarn-project/foundation/src/json-rpc/server/index.ts b/yarn-project/foundation/src/json-rpc/server/index.ts index a20b679993b..a0f5caf72ca 100644 --- a/yarn-project/foundation/src/json-rpc/server/index.ts +++ b/yarn-project/foundation/src/json-rpc/server/index.ts @@ -1,2 +1,2 @@ -export { JsonRpcServer, createStatusRouter, startHttpRpcServer } from './json_rpc_server.js'; +export * from './json_rpc_server.js'; export { JsonProxy } from './json_proxy.js'; diff --git a/yarn-project/foundation/src/json-rpc/server/json_proxy.ts b/yarn-project/foundation/src/json-rpc/server/json_proxy.ts index e7630d7f5e8..b46a1983ada 100644 --- a/yarn-project/foundation/src/json-rpc/server/json_proxy.ts +++ b/yarn-project/foundation/src/json-rpc/server/json_proxy.ts @@ -7,6 +7,16 @@ import { assert, hasOwnProperty } from '../js_utils.js'; const debug = createDebugLogger('json-rpc:json_proxy'); +/** + * A map of class names to class constructors. + */ +export type ClassMaps = { + /** The String class map */ + stringClassMap: StringClassConverterInput; + /** The object class map */ + objectClassMap: JsonClassConverterInput; +}; + /** * Handles conversion of objects over the write. * Delegates to a ClassConverter object. @@ -15,8 +25,8 @@ export class JsonProxy { classConverter: ClassConverter; constructor( private handler: object, - stringClassMap: StringClassConverterInput, - objectClassMap: JsonClassConverterInput, + private stringClassMap: StringClassConverterInput, + private objectClassMap: JsonClassConverterInput, ) { this.classConverter = new ClassConverter(stringClassMap, objectClassMap); } @@ -24,19 +34,26 @@ export class JsonProxy { * Call an RPC method. * @param methodName - The RPC method. * @param jsonParams - The RPG parameters. + * @param skipConversion - Whether to skip conversion of the parameters. * @returns The remote result. */ - public async call(methodName: string, jsonParams: any[] = []) { + public async call(methodName: string, jsonParams: any[] = [], skipConversion = false) { debug(format(`JsonProxy:call`, methodName, jsonParams)); // Get access to our class members const proto = Object.getPrototypeOf(this.handler); assert(hasOwnProperty(proto, methodName), `JsonProxy: Method ${methodName} not found!`); - assert(Array.isArray(jsonParams), 'JsonProxy: Params not an array!'); + assert(Array.isArray(jsonParams), `JsonProxy: ${methodName} params not an array: ${jsonParams}`); // convert the params from json representation to classes - const convertedParams = jsonParams.map(param => convertFromJsonObj(this.classConverter, param)); + let convertedParams = jsonParams; + if (!skipConversion) { + convertedParams = jsonParams.map(param => convertFromJsonObj(this.classConverter, param)); + } debug(format('JsonProxy:call', methodName, '<-', convertedParams)); const rawRet = await (this.handler as any)[methodName](...convertedParams); - const ret = convertToJsonObj(this.classConverter, rawRet); + let ret = rawRet; + if (!skipConversion) { + ret = convertToJsonObj(this.classConverter, rawRet); + } debug(format('JsonProxy:call', methodName, '->', ret)); return ret; } diff --git a/yarn-project/foundation/src/json-rpc/server/json_rpc_server.test.ts b/yarn-project/foundation/src/json-rpc/server/json_rpc_server.test.ts index c5272c2b74c..9815055346d 100644 --- a/yarn-project/foundation/src/json-rpc/server/json_rpc_server.test.ts +++ b/yarn-project/foundation/src/json-rpc/server/json_rpc_server.test.ts @@ -4,19 +4,20 @@ import { TestNote, TestState } from '../fixtures/test_state.js'; import { JsonRpcServer } from './json_rpc_server.js'; it('test an RPC function with a primitive parameter', async () => { - const server = new JsonRpcServer(new TestState([new TestNote('a'), new TestNote('b')]), { TestNote }, {}, true); + const server = new JsonRpcServer(new TestState([new TestNote('a'), new TestNote('b')]), { TestNote }, {}); const response = await request(server.getApp().callback()) - .post('/getNote') - .send({ params: [0] }); + .post('/') + .send({ method: 'getNote', params: [0] }); expect(response.status).toBe(200); expect(response.text).toBe(JSON.stringify({ result: { type: 'TestNote', data: 'a' } })); }); it('test an RPC function with an array of classes', async () => { - const server = new JsonRpcServer(new TestState([]), { TestNote }, {}, true); + const server = new JsonRpcServer(new TestState([]), { TestNote }, {}); const response = await request(server.getApp().callback()) - .post('/addNotes') + .post('/') .send({ + method: 'addNotes', params: [[{ data: 'a' }, { data: 'b' }, { data: 'c' }]], }); expect(response.status).toBe(200); @@ -24,7 +25,7 @@ it('test an RPC function with an array of classes', async () => { }); it('test invalid JSON', async () => { - const server = new JsonRpcServer(new TestState([]), { TestNote }, {}, false); + const server = new JsonRpcServer(new TestState([]), { TestNote }, {}); const response = await request(server.getApp().callback()).post('/').send('{'); expect(response.status).toBe(400); expect(response.body).toEqual({ @@ -35,7 +36,7 @@ it('test invalid JSON', async () => { }); it('invalid method', async () => { - const server = new JsonRpcServer(new TestState([]), { TestNote }, {}, false); + const server = new JsonRpcServer(new TestState([]), { TestNote }, {}); const response = await request(server.getApp().callback()).post('/').send({ jsonrpc: '2.0', method: 'invalid', diff --git a/yarn-project/foundation/src/json-rpc/server/json_rpc_server.ts b/yarn-project/foundation/src/json-rpc/server/json_rpc_server.ts index ea1bd7e2d05..08e0533aff4 100644 --- a/yarn-project/foundation/src/json-rpc/server/json_rpc_server.ts +++ b/yarn-project/foundation/src/json-rpc/server/json_rpc_server.ts @@ -8,20 +8,23 @@ import Router from 'koa-router'; import { createDebugLogger } from '../../log/index.js'; import { JsonClassConverterInput, StringClassConverterInput } from '../class_converter.js'; import { convertBigintsInObj } from '../convert.js'; -import { JsonProxy } from './json_proxy.js'; +import { ClassMaps, JsonProxy } from './json_proxy.js'; /** * JsonRpcServer. * Minimal, dev-friendly mechanism to create a server from an object. */ export class JsonRpcServer { - proxy: JsonProxy; + /** + * The proxy object. + */ + public proxy: JsonProxy; constructor( private handler: object, - stringClassMap: StringClassConverterInput, - objectClassMap: JsonClassConverterInput, - private createApi: boolean, - private disallowedMethods: string[] = [], + private stringClassMap: StringClassConverterInput, + private objectClassMap: JsonClassConverterInput, + /** List of methods to disallow from calling remotely */ + public readonly disallowedMethods: string[] = [], private log = createDebugLogger('aztec:foundation:json-rpc:server'), ) { this.proxy = new JsonProxy(handler, stringClassMap, objectClassMap); @@ -90,90 +93,45 @@ export class JsonRpcServer { private getRouter(prefix: string) { const router = new Router({ prefix }); const proto = Object.getPrototypeOf(this.handler); - // Find all our endpoints from the handler methods - - if (this.createApi) { - // "API mode" where an endpoint is created for each method - for (const method of Object.getOwnPropertyNames(proto)) { - // Ignore if not a function or function is not allowed - if ( - method === 'constructor' || - typeof proto[method] !== 'function' || - this.disallowedMethods.includes(method) - ) { - continue; - } - router.post(`/${method}`, async (ctx: Koa.Context) => { - const { params = [], jsonrpc, id } = ctx.request.body as any; - try { - const result = await this.proxy.call(method, params); - ctx.body = { - jsonrpc, - id, - result: convertBigintsInObj(result), - }; - ctx.status = 200; - } catch (err: any) { - // Propagate the error message to the client. Plenty of the errors are expected to occur (e.g. adding - // a duplicate recipient) so this is necessary. - ctx.status = 400; - ctx.body = { - jsonrpc, - id, - error: { - // TODO assign error codes - https://github.com/AztecProtocol/aztec-packages/issues/2633 - code: -32000, - message: err.message, - }, - }; - } - }); - } - } else { - // "JSON RPC mode" where a single endpoint is used and the method is given in the request body - router.post('/', async (ctx: Koa.Context) => { - const { params = [], jsonrpc, id, method } = ctx.request.body as any; - // Ignore if not a function - if ( - method === 'constructor' || - typeof proto[method] !== 'function' || - this.disallowedMethods.includes(method) - ) { + // "JSON RPC mode" where a single endpoint is used and the method is given in the request body + router.post('/', async (ctx: Koa.Context) => { + const { params = [], jsonrpc, id, method } = ctx.request.body as any; + // Ignore if not a function + if (method === 'constructor' || typeof proto[method] !== 'function' || this.disallowedMethods.includes(method)) { + ctx.status = 400; + ctx.body = { + jsonrpc, + id, + error: { + code: -32601, + message: `Method not found: ${method}`, + }, + }; + } else { + try { + const result = await this.proxy.call(method, params); + ctx.body = { + jsonrpc, + id, + result: convertBigintsInObj(result), + }; + ctx.status = 200; + } catch (err: any) { + // Propagate the error message to the client. Plenty of the errors are expected to occur (e.g. adding + // a duplicate recipient) so this is necessary. ctx.status = 400; ctx.body = { jsonrpc, id, error: { - code: -32601, - message: `Method not found: ${method}`, + // TODO assign error codes - https://github.com/AztecProtocol/aztec-packages/issues/2633 + code: -32000, + message: err.message, }, }; - } else { - try { - const result = await this.proxy.call(method, params); - ctx.body = { - jsonrpc, - id, - result: convertBigintsInObj(result), - }; - ctx.status = 200; - } catch (err: any) { - // Propagate the error message to the client. Plenty of the errors are expected to occur (e.g. adding - // a duplicate recipient) so this is necessary. - ctx.status = 400; - ctx.body = { - jsonrpc, - id, - error: { - // TODO assign error codes - https://github.com/AztecProtocol/aztec-packages/issues/2633 - code: -32000, - message: err.message, - }, - }; - } } - }); - } + } + }); return router; } @@ -187,6 +145,33 @@ export class JsonRpcServer { const httpServer = http.createServer(this.getApp(prefix).callback()); httpServer.listen(port); } + + /** + * Get a list of methods. + * @returns A list of methods. + */ + public getMethods(): string[] { + return Object.getOwnPropertyNames(Object.getPrototypeOf(this.handler)); + } + + /** + * Gets the class maps that were used to create the proxy. + * @returns The string & object class maps. + */ + public getClassMaps(): ClassMaps { + return { stringClassMap: this.stringClassMap, objectClassMap: this.objectClassMap }; + } + + /** + * Call an RPC method. + * @param methodName - The RPC method. + * @param jsonParams - The RPG parameters. + * @param skipConversion - Whether to skip conversion of the parameters. + * @returns The remote result. + */ + public async call(methodName: string, jsonParams: any[] = [], skipConversion: boolean) { + return await this.proxy.call(methodName, jsonParams, skipConversion); + } } /** @@ -210,16 +195,75 @@ export function createStatusRouter(apiPrefix = '') { * @returns A running http server. */ export function startHttpRpcServer( + name: string, instance: T, jsonRpcFactoryFunc: (instance: T) => JsonRpcServer, port: string | number, ): http.Server { const rpcServer = jsonRpcFactoryFunc(instance); - const app = rpcServer.getApp(); + const namespacedServer = createNamespacedJsonRpcServer([{ [name]: rpcServer }]); + + const app = namespacedServer.getApp(); const httpServer = http.createServer(app.callback()); httpServer.listen(port); return httpServer; } +/** + * List of namespace to server instance. + */ +export type ServerList = { + /** name of the service to be used for namespacing */ + [name: string]: JsonRpcServer; +}[]; + +/** + * Creates a single JsonRpcServer from multiple servers. + * @param servers - List of servers to be combined into a single server, passed as ServerList. + * @returns A single JsonRpcServer with namespaced methods. + */ +export function createNamespacedJsonRpcServer( + servers: ServerList, + log = createDebugLogger('aztec:foundation:json-rpc:multi-server'), +): JsonRpcServer { + const handler = {} as any; + const disallowedMethods: string[] = []; + const classMapsArr: ClassMaps[] = []; + + for (const serverEntry of servers) { + const [namespace, server] = Object.entries(serverEntry)[0]; + const serverMethods = server.getMethods(); + + for (const method of serverMethods) { + const namespacedMethod = `${namespace}_${method}`; + + handler[namespacedMethod] = (...args: any[]) => { + return server.call(method, args, true); + }; + } + + // get the combined disallowed methods from all servers. + disallowedMethods.push(...server.disallowedMethods.map(method => `${namespace}_${method}`)); + // get the combined classmaps from all servers. + const classMap = server.getClassMaps(); + classMapsArr.push({ + stringClassMap: classMap.stringClassMap, + objectClassMap: classMap.objectClassMap, + }); + } + + // Get the combined stringClassMap & objectClassMap from all servers + const classMaps = classMapsArr.reduce( + (acc, curr) => { + return { + stringClassMap: { ...acc.stringClassMap, ...curr.stringClassMap }, + objectClassMap: { ...acc.objectClassMap, ...curr.objectClassMap }, + }; + }, + { stringClassMap: {}, objectClassMap: {} } as ClassMaps, + ); + + return new JsonRpcServer(Object.create(handler), classMaps.stringClassMap, classMaps.objectClassMap, [], log); +} diff --git a/yarn-project/foundation/src/serialize/buffer_reader.ts b/yarn-project/foundation/src/serialize/buffer_reader.ts index b54a2d1b52b..d11efff3cb5 100644 --- a/yarn-project/foundation/src/serialize/buffer_reader.ts +++ b/yarn-project/foundation/src/serialize/buffer_reader.ts @@ -54,6 +54,16 @@ export class BufferReader { return this.buffer.readUint32BE(this.index - 4); } + /** + * Reads `count` 32-bit unsigned integers from the buffer at the current index position. + * @param count - The number of 32-bit unsigned integers to read. + * @returns An array of 32-bit unsigned integers. + */ + public readNumbers(count: N): Tuple { + const result = Array.from({ length: count }, () => this.readNumber()); + return result as Tuple; + } + /** * Reads a 16-bit unsigned integer from the buffer at the current index position. * Updates the index position by 2 bytes after reading the number. @@ -65,6 +75,17 @@ export class BufferReader { return this.buffer.readUInt16BE(this.index - 2); } + /** + * Reads a 8-bit unsigned integer from the buffer at the current index position. + * Updates the index position by 1 byte after reading the number. + * + * @returns The read 8 bit value. + */ + public readUInt8(): number { + this.index += 1; + return this.buffer.readUInt8(this.index - 1); + } + /** * Reads and returns the next boolean value from the buffer. * Advances the internal index by 1, treating the byte at the current index as a boolean value. diff --git a/yarn-project/foundation/src/serialize/field_reader.test.ts b/yarn-project/foundation/src/serialize/field_reader.test.ts new file mode 100644 index 00000000000..7a242bae1b0 --- /dev/null +++ b/yarn-project/foundation/src/serialize/field_reader.test.ts @@ -0,0 +1,93 @@ +import { Fq, Fr } from '../fields/fields.js'; +import { FieldReader } from './field_reader.js'; + +const FIELDS = [new Fr(0), new Fr(1), new Fr(23), new Fr(45), new Fr(6789)]; + +class Something { + constructor(public id: Fr, public value: number) {} + + static fromFields(reader: FieldReader): Something { + return new Something(reader.readField(), reader.readU32()); + } +} + +describe('field reader', () => { + let reader: FieldReader; + + beforeEach(() => { + reader = new FieldReader(FIELDS); + }); + + describe('readFr', () => { + it('should read Fr', () => { + FIELDS.forEach(fr => { + expect(reader.readField()).toEqual(fr); + }); + + expect(() => reader.readField()).toThrow('Not enough fields to be consumed.'); + }); + }); + + describe('readFq', () => { + it('should get Fq from buffer', () => { + expect(reader.readFq()).toEqual(Fq.fromHighLow(new Fr(0), new Fr(1))); + expect(reader.readFq()).toEqual(Fq.fromHighLow(new Fr(23), new Fr(45))); + + expect(() => reader.readFq()).toThrow('Not enough fields to be consumed.'); + }); + }); + + describe('readBoolean', () => { + it('should read false when 0 and true when 1, throw otherwise', () => { + expect(reader.readBoolean()).toBe(false); + expect(reader.readBoolean()).toBe(true); + + expect(() => reader.readBoolean()).toThrow('Field is not a boolean'); + }); + }); + + describe('readU32', () => { + it('should return number', () => { + expect(reader.readU32()).toBe(0); + expect(reader.readU32()).toBe(1); + expect(reader.readU32()).toBe(23); + expect(reader.readU32()).toBe(45); + expect(reader.readU32()).toBe(6789); + }); + + it('should throw if reading a value larger than u32', () => { + const reader = new FieldReader([new Fr(2n ** 32n)]); + expect(() => reader.readU32()).toThrow('Field is not a u32.'); + }); + }); + + describe('readFieldArray', () => { + it('should read an array of fields', () => { + expect(reader.readFieldArray(3)).toEqual([new Fr(0), new Fr(1), new Fr(23)]); + }); + + it('should throw if reading more fields than in the reader', () => { + expect(() => reader.readFieldArray(FIELDS.length + 1)).toThrow('Not enough fields to be consumed.'); + }); + }); + + describe('readArray', () => { + it('should read array of custom type', () => { + const things = reader.readArray(2, Something); + expect(things).toEqual([new Something(new Fr(0), 1), new Something(new Fr(23), 45)]); + }); + + it('should throw if reading more fields than in the reader', () => { + expect(() => reader.readArray(3, Something)).toThrow('Not enough fields to be consumed.'); + }); + }); + + describe('readObject', () => { + it('should read object from buffer', () => { + expect(reader.readObject(Something)).toEqual(new Something(new Fr(0), 1)); + expect(reader.readObject(Something)).toEqual(new Something(new Fr(23), 45)); + + expect(() => reader.readObject(Something)).toThrow('Not enough fields to be consumed.'); + }); + }); +}); diff --git a/yarn-project/foundation/src/serialize/field_reader.ts b/yarn-project/foundation/src/serialize/field_reader.ts new file mode 100644 index 00000000000..be3a06e72dd --- /dev/null +++ b/yarn-project/foundation/src/serialize/field_reader.ts @@ -0,0 +1,143 @@ +import { Fq, Fr } from '../fields/fields.js'; +import { Tuple } from './types.js'; + +/** + * The FieldReader class provides a utility for reading various data types from a field array. + * + * Usage: + * Create a new instance of FieldReader with an array of fields and an optional offset. + * Use the provided methods to read desired data types from the field array. + * The reading methods automatically advance the internal index. + */ +export class FieldReader { + private index: number; + private length: number; + constructor(private fields: Fr[], offset = 0) { + this.index = offset; + this.length = fields.length; + if (offset >= this.length) { + throw new Error('Offset out of bounds.'); + } + } + + /** + * Creates a FieldReader instance from either a field array or an existing FieldReader. + * + * @param fields - A field array or FieldReader to initialize the FieldReader. + * @returns An instance of FieldReader. + */ + public static asReader(fields: Fr[] | FieldReader): FieldReader { + if (fields instanceof FieldReader) { + return fields; + } + + return new FieldReader(fields); + } + + /** + * Reads a single field from the array. + * + * @returns A field. + */ + public readField(): Fr { + if (this.index === this.length) { + throw new Error('Not enough fields to be consumed.'); + } + return this.fields[this.index++]; + } + + /** + * Reads a Fq from the array. + * + * @returns An Fq. + */ + public readFq(): Fq { + return Fq.fromHighLow(this.readField(), this.readField()); + } + + /** + * Reads and returns the next boolean value from the field array. + * Advances the internal index by 1, treating the field at the current index as a boolean value. + * Returns true if the field is non-zero, false otherwise. + * Throw if the value is not 0 or 1. + * + * @returns A boolean value representing the field at the current index. + */ + public readBoolean(): boolean { + const field = this.readField(); + const value = field.toBigInt(); + if (value > 1n) { + throw new Error('Field is not a boolean.'); + } + return value == 1n; + } + + /** + * Reads a 32-bit unsigned integer from the field array at the current index position. + * Updates the index position by 1 after reading the number. + * Throw if the value is greater than 2 ** 32. + * + * @returns The read 32-bit unsigned integer value. + */ + public readU32(): number { + const field = this.readField(); + const value = field.toBigInt(); + if (value >= 1n << 32n) { + throw new Error('Field is not a u32.'); + } + return Number(value); + } + + /** + * Read an array of a fixed size field array. + * + * @param size - The fixed number of fields in the array. + * @returns An array of fields. + */ + public readFieldArray(size: N): Tuple { + const result: Fr[] = []; + for (let i = 0; i < size; ++i) { + result.push(this.readField()); + } + return result as Tuple; + } + + /** + * Read an array of a fixed size with elements of type T from the field array. + * The 'itemDeserializer' object should have a 'fromFields' method that takes a FieldReader instance as input, + * and returns an instance of the desired deserialized data type T. + * This method will call the 'fromFields' method for each element in the array and return the resulting array. + * + * @param size - The fixed number of elements in the array. + * @param itemDeserializer - An object with a 'fromFields' method to deserialize individual elements of type T. + * @returns An array of instances of type T. + */ + public readArray( + size: N, + itemDeserializer: { + /** + * A function for deserializing data from a FieldReader instance. + */ + fromFields: (reader: FieldReader) => T; + }, + ): Tuple { + const result = Array.from({ length: size }, () => itemDeserializer.fromFields(this)); + return result as Tuple; + } + + /** + * Reads a serialized object from a field array and returns the deserialized object using the given deserializer. + * + * @typeparam T - The type of the deserialized object. + * @param deserializer - An object with a 'fromFields' method that takes a FieldReader instance and returns an instance of the deserialized object. + * @returns The deserialized object of type T. + */ + public readObject(deserializer: { + /** + * A method that takes a FieldReader instance and returns an instance of the deserialized data type. + */ + fromFields: (reader: FieldReader) => T; + }): T { + return deserializer.fromFields(this); + } +} diff --git a/yarn-project/foundation/src/serialize/index.ts b/yarn-project/foundation/src/serialize/index.ts index 669ab25f8e4..875d37f4410 100644 --- a/yarn-project/foundation/src/serialize/index.ts +++ b/yarn-project/foundation/src/serialize/index.ts @@ -1,4 +1,5 @@ export * from './free_funcs.js'; export * from './buffer_reader.js'; +export * from './field_reader.js'; export * from './types.js'; export * from './serialize.js'; diff --git a/yarn-project/key-store/src/test_key_store.ts b/yarn-project/key-store/src/test_key_store.ts index f0af31ee6da..f0bab98c51d 100644 --- a/yarn-project/key-store/src/test_key_store.ts +++ b/yarn-project/key-store/src/test_key_store.ts @@ -21,7 +21,7 @@ export class TestKeyStore implements KeyStore { #keys: AztecMap; constructor(private curve: Grumpkin, database: AztecKVStore) { - this.#keys = database.createMap('key_store'); + this.#keys = database.openMap('key_store'); } public async addAccount(privKey: GrumpkinPrivateKey): Promise { @@ -51,6 +51,20 @@ export class TestKeyStore implements KeyStore { return computeNullifierSecretKey(privateKey); } + public async getNullifierSecretKeyFromPublicKey(nullifierPubKey: PublicKey) { + const accounts = await this.getAccounts(); + for (let i = 0; i < accounts.length; ++i) { + const accountPublicKey = accounts[i]; + const privateKey = await this.getAccountPrivateKey(accountPublicKey); + const secretKey = computeNullifierSecretKey(privateKey); + const publicKey = derivePublicKey(secretKey); + if (publicKey.equals(nullifierPubKey)) { + return secretKey; + } + } + throw new Error('Unknown nullifier public key.'); + } + public async getNullifierPublicKey(pubKey: PublicKey) { const secretKey = await this.getNullifierSecretKey(pubKey); return derivePublicKey(secretKey); @@ -73,7 +87,7 @@ export class TestKeyStore implements KeyStore { const privKey = this.#keys.get(pubKey.toString()); if (!privKey) { throw new Error( - 'Unknown account.\nSee docs for context: https://docs.aztec.network/dev_docs/debugging/aztecnr-errors#could-not-process-note-because-of-error-unknown-account-skipping-note', + 'Unknown account.\nSee docs for context: https://docs.aztec.network/developers/debugging/aztecnr-errors#could-not-process-note-because-of-error-unknown-account-skipping-note', ); } return ConstantKeyPair.fromPrivateKey(this.curve, GrumpkinScalar.fromBuffer(privKey)); diff --git a/yarn-project/kv-store/package.json b/yarn-project/kv-store/package.json index 8aea043378d..4c440901969 100644 --- a/yarn-project/kv-store/package.json +++ b/yarn-project/kv-store/package.json @@ -26,7 +26,7 @@ }, "dependencies": { "@aztec/foundation": "workspace:^", - "lmdb": "^2.9.1" + "lmdb": "^2.9.2" }, "devDependencies": { "@jest/globals": "^29.5.0", diff --git a/yarn-project/kv-store/src/interfaces/singleton.ts b/yarn-project/kv-store/src/interfaces/singleton.ts index 43b34aa0ad8..eba620e18b5 100644 --- a/yarn-project/kv-store/src/interfaces/singleton.ts +++ b/yarn-project/kv-store/src/interfaces/singleton.ts @@ -1,5 +1,6 @@ /** * Represents a singleton value in the database. + * Note: The singleton loses type info so it's recommended to serialize to buffer when storing it. */ export interface AztecSingleton { /** diff --git a/yarn-project/kv-store/src/interfaces/store.ts b/yarn-project/kv-store/src/interfaces/store.ts index 73a2901387c..2e2777f0e8a 100644 --- a/yarn-project/kv-store/src/interfaces/store.ts +++ b/yarn-project/kv-store/src/interfaces/store.ts @@ -11,34 +11,34 @@ export interface AztecKVStore { * @param name - The name of the map * @returns The map */ - createMap(name: string): AztecMap; + openMap(name: string): AztecMap; /** * Creates a new multi-map. * @param name - The name of the multi-map * @returns The multi-map */ - createMultiMap(name: string): AztecMultiMap; + openMultiMap(name: string): AztecMultiMap; /** * Creates a new array. * @param name - The name of the array * @returns The array */ - createArray(name: string): AztecArray; + openArray(name: string): AztecArray; /** * Creates a new singleton. * @param name - The name of the singleton * @returns The singleton */ - createSingleton(name: string): AztecSingleton; + openSingleton(name: string): AztecSingleton; /** * Creates a new count map. * @param name - name of the counter */ - createCounter(name: string): AztecCounter; + openCounter(name: string): AztecCounter; /** * Starts a transaction. All calls to read/write data while in a transaction are queued and executed atomically. diff --git a/yarn-project/kv-store/src/lmdb/store.ts b/yarn-project/kv-store/src/lmdb/store.ts index 8c3cebd0737..bbd866efe8c 100644 --- a/yarn-project/kv-store/src/lmdb/store.ts +++ b/yarn-project/kv-store/src/lmdb/store.ts @@ -39,7 +39,7 @@ export class AztecLmdbStore implements AztecKVStore { dupSort: true, }); - this.#rollupAddress = this.createSingleton('rollupAddress'); + this.#rollupAddress = this.openSingleton('rollupAddress'); } /** @@ -55,7 +55,7 @@ export class AztecLmdbStore implements AztecKVStore { * @param log - A logger to use. Optional * @returns The store */ - static async create( + static async open( rollupAddress: EthAddress, path?: string, log = createDebugLogger('aztec:kv-store:lmdb'), @@ -72,12 +72,16 @@ export class AztecLmdbStore implements AztecKVStore { return db; } + static openTmp(): Promise { + return AztecLmdbStore.open(EthAddress.random()); + } + /** * Creates a new AztecMap in the store. * @param name - Name of the map * @returns A new AztecMap */ - createMap(name: string): AztecMap { + openMap(name: string): AztecMap { return new LmdbAztecMap(this.#data, name); } @@ -86,11 +90,11 @@ export class AztecLmdbStore implements AztecKVStore { * @param name - Name of the map * @returns A new AztecMultiMap */ - createMultiMap(name: string): AztecMultiMap { + openMultiMap(name: string): AztecMultiMap { return new LmdbAztecMap(this.#multiMapData, name); } - createCounter>(name: string): AztecCounter { + openCounter>(name: string): AztecCounter { return new LmdbAztecCounter(this.#data, name); } @@ -99,7 +103,7 @@ export class AztecLmdbStore implements AztecKVStore { * @param name - Name of the array * @returns A new AztecArray */ - createArray(name: string): AztecArray { + openArray(name: string): AztecArray { return new LmdbAztecArray(this.#data, name); } @@ -108,7 +112,7 @@ export class AztecLmdbStore implements AztecKVStore { * @param name - Name of the singleton * @returns A new AztecSingleton */ - createSingleton(name: string): AztecSingleton { + openSingleton(name: string): AztecSingleton { return new LmdbAztecSingleton(this.#data, name); } diff --git a/yarn-project/merkle-tree/package.json b/yarn-project/merkle-tree/package.json index d9c347ed115..75bb7b0e2bf 100644 --- a/yarn-project/merkle-tree/package.json +++ b/yarn-project/merkle-tree/package.json @@ -34,9 +34,8 @@ "dependencies": { "@aztec/circuit-types": "workspace:^", "@aztec/foundation": "workspace:^", + "@aztec/kv-store": "workspace:^", "@aztec/types": "workspace:^", - "levelup": "^5.1.1", - "memdown": "^6.1.1", "sha256": "^0.2.0", "tslib": "^2.4.0" }, @@ -44,8 +43,6 @@ "@aztec/circuits.js": "workspace:^", "@jest/globals": "^29.5.0", "@types/jest": "^29.5.0", - "@types/levelup": "^5.1.2", - "@types/memdown": "^3.0.1", "@types/node": "^18.15.3", "@types/sha256": "^0.2.0", "jest": "^29.5.0", diff --git a/yarn-project/merkle-tree/src/index.ts b/yarn-project/merkle-tree/src/index.ts index 68826f44e42..45ae1771bbf 100644 --- a/yarn-project/merkle-tree/src/index.ts +++ b/yarn-project/merkle-tree/src/index.ts @@ -6,7 +6,7 @@ export * from './pedersen.js'; export * from './sparse_tree/sparse_tree.js'; export { StandardIndexedTree } from './standard_indexed_tree/standard_indexed_tree.js'; export * from './standard_tree/standard_tree.js'; -export { INITIAL_LEAF } from './tree_base.js'; +export { INITIAL_LEAF, getTreeMeta } from './tree_base.js'; export { newTree } from './new_tree.js'; export { loadTree } from './load_tree.js'; export * from './snapshots/snapshot_builder.js'; diff --git a/yarn-project/merkle-tree/src/interfaces/indexed_tree.ts b/yarn-project/merkle-tree/src/interfaces/indexed_tree.ts index 5e5531afacf..651f734f09e 100644 --- a/yarn-project/merkle-tree/src/interfaces/indexed_tree.ts +++ b/yarn-project/merkle-tree/src/interfaces/indexed_tree.ts @@ -1,8 +1,35 @@ -import { IndexedTreeLeafPreimage } from '@aztec/foundation/trees'; +import { IndexedTreeLeaf, IndexedTreeLeafPreimage } from '@aztec/foundation/trees'; import { SiblingPath } from '@aztec/types/membership'; import { AppendOnlyTree } from './append_only_tree.js'; +/** + * Factory for creating leaf preimages. + */ +export interface PreimageFactory { + /** + * Creates a new preimage from a leaf. + * @param leaf - Leaf to create a preimage from. + * @param nextKey - Next key of the leaf. + * @param nextIndex - Next index of the leaf. + */ + fromLeaf(leaf: IndexedTreeLeaf, nextKey: bigint, nextIndex: bigint): IndexedTreeLeafPreimage; + /** + * Creates a new preimage from a buffer. + * @param buffer - Buffer to create a preimage from. + */ + fromBuffer(buffer: Buffer): IndexedTreeLeafPreimage; + /** + * Creates an empty preimage. + */ + empty(): IndexedTreeLeafPreimage; + /** + * Creates a copy of a preimage. + * @param preimage - Preimage to be cloned. + */ + clone(preimage: IndexedTreeLeafPreimage): IndexedTreeLeafPreimage; +} + /** * All of the data to be return during batch insertion. */ @@ -56,7 +83,7 @@ export interface IndexedTree extends AppendOnlyTree { findIndexOfPreviousKey( newValue: bigint, includeUncommitted: boolean, - ): Promise< + ): | { /** * The index of the found leaf. @@ -67,8 +94,7 @@ export interface IndexedTree extends AppendOnlyTree { */ alreadyPresent: boolean; } - | undefined - >; + | undefined; /** * Gets the latest LeafPreimage copy. @@ -76,7 +102,7 @@ export interface IndexedTree extends AppendOnlyTree { * @param includeUncommitted - If true, the uncommitted changes are included in the search. * @returns A copy of the leaf preimage at the given index or undefined if the leaf was not found. */ - getLatestLeafPreimageCopy(index: bigint, includeUncommitted: boolean): Promise; + getLatestLeafPreimageCopy(index: bigint, includeUncommitted: boolean): IndexedTreeLeafPreimage | undefined; /** * Batch insert multiple leaves into the tree. diff --git a/yarn-project/merkle-tree/src/interfaces/merkle_tree.ts b/yarn-project/merkle-tree/src/interfaces/merkle_tree.ts index 257e5d79761..209656f885d 100644 --- a/yarn-project/merkle-tree/src/interfaces/merkle_tree.ts +++ b/yarn-project/merkle-tree/src/interfaces/merkle_tree.ts @@ -48,7 +48,7 @@ export interface MerkleTree extends SiblingPathSource { * @param index - The index of the leaf value to be returned. * @param includeUncommitted - Set to true to include uncommitted updates in the data set. */ - getLeafValue(index: bigint, includeUncommitted: boolean): Promise; + getLeafValue(index: bigint, includeUncommitted: boolean): Buffer | undefined; /** * Returns the index of a leaf given its value, or undefined if no leaf with that value is found. @@ -56,5 +56,5 @@ export interface MerkleTree extends SiblingPathSource { * @param includeUncommitted - Indicates whether to include uncommitted data. * @returns The index of the first leaf found with a given value (undefined if not found). */ - findLeafIndex(leaf: Buffer, includeUncommitted: boolean): Promise; + findLeafIndex(leaf: Buffer, includeUncommitted: boolean): bigint | undefined; } diff --git a/yarn-project/merkle-tree/src/load_tree.ts b/yarn-project/merkle-tree/src/load_tree.ts index 4d5bf606bb0..a4bf1e8853a 100644 --- a/yarn-project/merkle-tree/src/load_tree.ts +++ b/yarn-project/merkle-tree/src/load_tree.ts @@ -1,8 +1,7 @@ +import { AztecKVStore } from '@aztec/kv-store'; import { Hasher } from '@aztec/types/interfaces'; -import { LevelUp } from 'levelup'; - -import { TreeBase, decodeMeta } from './tree_base.js'; +import { TreeBase, getTreeMeta } from './tree_base.js'; /** * Creates a new tree and sets its root, depth and size based on the meta data which are associated with the name. @@ -12,15 +11,13 @@ import { TreeBase, decodeMeta } from './tree_base.js'; * @param name - Name of the tree. * @returns The newly created tree. */ -export async function loadTree( - c: new (db: LevelUp, hasher: Hasher, name: string, depth: number, size: bigint, root: Buffer) => T, - db: LevelUp, +export function loadTree( + c: new (store: AztecKVStore, hasher: Hasher, name: string, depth: number, size: bigint, root: Buffer) => T, + store: AztecKVStore, hasher: Hasher, name: string, ): Promise { - const meta: Buffer = await db.get(name); - const { root, depth, size } = decodeMeta(meta); - - const tree = new c(db, hasher, name, depth, size, root); - return tree; + const { root, depth, size } = getTreeMeta(store, name); + const tree = new c(store, hasher, name, depth, size, root); + return Promise.resolve(tree); } diff --git a/yarn-project/merkle-tree/src/new_tree.ts b/yarn-project/merkle-tree/src/new_tree.ts index 45e01ded402..5c354e21851 100644 --- a/yarn-project/merkle-tree/src/new_tree.ts +++ b/yarn-project/merkle-tree/src/new_tree.ts @@ -1,7 +1,6 @@ +import { AztecKVStore } from '@aztec/kv-store'; import { Hasher } from '@aztec/types/interfaces'; -import { LevelUp } from 'levelup'; - import { TreeBase } from './tree_base.js'; /** @@ -15,14 +14,14 @@ import { TreeBase } from './tree_base.js'; * @returns The newly created tree. */ export async function newTree( - c: new (db: LevelUp, hasher: Hasher, name: string, depth: number, size: bigint) => T, - db: LevelUp, + c: new (store: AztecKVStore, hasher: Hasher, name: string, depth: number, size: bigint) => T, + store: AztecKVStore, hasher: Hasher, name: string, depth: number, prefilledSize = 1, ): Promise { - const tree = new c(db, hasher, name, depth, 0n); + const tree = new c(store, hasher, name, depth, 0n); await tree.init(prefilledSize); return tree; } diff --git a/yarn-project/merkle-tree/src/snapshots/append_only_snapshot.test.ts b/yarn-project/merkle-tree/src/snapshots/append_only_snapshot.test.ts index b66eb2af22b..52ebdec437d 100644 --- a/yarn-project/merkle-tree/src/snapshots/append_only_snapshot.test.ts +++ b/yarn-project/merkle-tree/src/snapshots/append_only_snapshot.test.ts @@ -1,17 +1,16 @@ -import levelup, { LevelUp } from 'levelup'; +import { AztecKVStore, AztecLmdbStore } from '@aztec/kv-store'; import { Pedersen, StandardTree, newTree } from '../index.js'; -import { createMemDown } from '../test/utils/create_mem_down.js'; import { AppendOnlySnapshotBuilder } from './append_only_snapshot.js'; import { describeSnapshotBuilderTestSuite } from './snapshot_builder_test_suite.js'; describe('AppendOnlySnapshot', () => { let tree: StandardTree; let snapshotBuilder: AppendOnlySnapshotBuilder; - let db: LevelUp; + let db: AztecKVStore; beforeEach(async () => { - db = levelup(createMemDown()); + db = await AztecLmdbStore.openTmp(); const hasher = new Pedersen(); tree = await newTree(StandardTree, db, hasher, 'test', 4); snapshotBuilder = new AppendOnlySnapshotBuilder(db, tree, hasher); diff --git a/yarn-project/merkle-tree/src/snapshots/append_only_snapshot.ts b/yarn-project/merkle-tree/src/snapshots/append_only_snapshot.ts index 113a01d2bee..06d6e4b3194 100644 --- a/yarn-project/merkle-tree/src/snapshots/append_only_snapshot.ts +++ b/yarn-project/merkle-tree/src/snapshots/append_only_snapshot.ts @@ -1,23 +1,26 @@ +import { AztecKVStore, AztecMap } from '@aztec/kv-store'; import { Hasher } from '@aztec/types/interfaces'; import { SiblingPath } from '@aztec/types/membership'; -import { LevelUp } from 'levelup'; - import { AppendOnlyTree } from '../interfaces/append_only_tree.js'; import { TreeBase } from '../tree_base.js'; import { TreeSnapshot, TreeSnapshotBuilder } from './snapshot_builder.js'; // stores the last block that modified this node -const nodeModifiedAtBlockKey = (treeName: string, level: number, index: bigint) => - `snapshot:node:${treeName}:${level}:${index}:block`; +const nodeModifiedAtBlockKey = (level: number, index: bigint) => `node:${level}:${index}:modifiedAtBlock`; // stores the value of the node at the above block -const historicalNodeKey = (treeName: string, level: number, index: bigint) => - `snapshot:node:${treeName}:${level}:${index}:value`; +const historicalNodeKey = (level: number, index: bigint) => `node:${level}:${index}:value`; -// metadata for a snapshot -const snapshotRootKey = (treeName: string, block: number) => `snapshot:root:${treeName}:${block}`; -const snapshotNumLeavesKey = (treeName: string, block: number) => `snapshot:numLeaves:${treeName}:${block}`; +/** + * Metadata for a snapshot, per block + */ +type SnapshotMetadata = { + /** The tree root at the time */ + root: Buffer; + /** The number of filled leaves */ + numLeaves: bigint; +}; /** * A more space-efficient way of storing snapshots of AppendOnlyTrees that trades space need for slower @@ -35,91 +38,110 @@ const snapshotNumLeavesKey = (treeName: string, block: number) => `snapshot:numL * Worst case: O(H) database reads + O(H) hashes */ export class AppendOnlySnapshotBuilder implements TreeSnapshotBuilder { - constructor(private db: LevelUp, private tree: TreeBase & AppendOnlyTree, private hasher: Hasher) {} - async getSnapshot(block: number): Promise { - const meta = await this.#getSnapshotMeta(block); + #nodeValue: AztecMap, Buffer>; + #nodeLastModifiedByBlock: AztecMap, number>; + #snapshotMetadata: AztecMap; + + constructor(private db: AztecKVStore, private tree: TreeBase & AppendOnlyTree, private hasher: Hasher) { + const treeName = tree.getName(); + this.#nodeValue = db.openMap(`append_only_snapshot:${treeName}:node`); + this.#nodeLastModifiedByBlock = db.openMap(`append_ony_snapshot:${treeName}:block`); + this.#snapshotMetadata = db.openMap(`append_only_snapshot:${treeName}:snapshot_metadata`); + } + + getSnapshot(block: number): Promise { + const meta = this.#getSnapshotMeta(block); if (typeof meta === 'undefined') { - throw new Error(`Snapshot for tree ${this.tree.getName()} at block ${block} does not exist`); + return Promise.reject(new Error(`Snapshot for tree ${this.tree.getName()} at block ${block} does not exist`)); } - return new AppendOnlySnapshot(this.db, block, meta.numLeaves, meta.root, this.tree, this.hasher); + return Promise.resolve( + new AppendOnlySnapshot( + this.#nodeValue, + this.#nodeLastModifiedByBlock, + block, + meta.numLeaves, + meta.root, + this.tree, + this.hasher, + ), + ); } - async snapshot(block: number): Promise { - const meta = await this.#getSnapshotMeta(block); - if (typeof meta !== 'undefined') { - // no-op, we already have a snapshot - return new AppendOnlySnapshot(this.db, block, meta.numLeaves, meta.root, this.tree, this.hasher); - } - - const batch = this.db.batch(); - const root = this.tree.getRoot(false); - const depth = this.tree.getDepth(); - const treeName = this.tree.getName(); - const queue: [Buffer, number, bigint][] = [[root, 0, 0n]]; - - // walk the tree in BF and store latest nodes - while (queue.length > 0) { - const [node, level, index] = queue.shift()!; - - const historicalValue = await this.db.get(historicalNodeKey(treeName, level, index)).catch(() => undefined); - if (!historicalValue || !node.equals(historicalValue)) { - // we've never seen this node before or it's different than before - // update the historical tree and tag it with the block that modified it - batch.put(nodeModifiedAtBlockKey(treeName, level, index), String(block)); - batch.put(historicalNodeKey(treeName, level, index), node); - } else { - // if this node hasn't changed, that means, nothing below it has changed either - continue; - } - - if (level + 1 > depth) { - // short circuit if we've reached the leaf level - // otherwise getNode might throw if we ask for the children of a leaf - continue; + snapshot(block: number): Promise { + return this.db.transaction(() => { + const meta = this.#getSnapshotMeta(block); + if (typeof meta !== 'undefined') { + // no-op, we already have a snapshot + return new AppendOnlySnapshot( + this.#nodeValue, + this.#nodeLastModifiedByBlock, + block, + meta.numLeaves, + meta.root, + this.tree, + this.hasher, + ); } - // these could be undefined because zero hashes aren't stored in the tree - const [lhs, rhs] = await Promise.all([ - this.tree.getNode(level + 1, 2n * index), - this.tree.getNode(level + 1, 2n * index + 1n), - ]); - - if (lhs) { - queue.push([lhs, level + 1, 2n * index]); - } - - if (rhs) { - queue.push([rhs, level + 1, 2n * index + 1n]); + const root = this.tree.getRoot(false); + const depth = this.tree.getDepth(); + const queue: [Buffer, number, bigint][] = [[root, 0, 0n]]; + + // walk the tree in BF and store latest nodes + while (queue.length > 0) { + const [node, level, index] = queue.shift()!; + + const historicalValue = this.#nodeValue.get(historicalNodeKey(level, index)); + if (!historicalValue || !node.equals(historicalValue)) { + // we've never seen this node before or it's different than before + // update the historical tree and tag it with the block that modified it + void this.#nodeLastModifiedByBlock.set(nodeModifiedAtBlockKey(level, index), block); + void this.#nodeValue.set(historicalNodeKey(level, index), node); + } else { + // if this node hasn't changed, that means, nothing below it has changed either + continue; + } + + if (level + 1 > depth) { + // short circuit if we've reached the leaf level + // otherwise getNode might throw if we ask for the children of a leaf + continue; + } + + // these could be undefined because zero hashes aren't stored in the tree + const [lhs, rhs] = [this.tree.getNode(level + 1, 2n * index), this.tree.getNode(level + 1, 2n * index + 1n)]; + + if (lhs) { + queue.push([lhs, level + 1, 2n * index]); + } + + if (rhs) { + queue.push([rhs, level + 1, 2n * index + 1n]); + } } - } - - const numLeaves = this.tree.getNumLeaves(false); - batch.put(snapshotNumLeavesKey(treeName, block), String(numLeaves)); - batch.put(snapshotRootKey(treeName, block), root); - await batch.write(); - return new AppendOnlySnapshot(this.db, block, numLeaves, root, this.tree, this.hasher); + const numLeaves = this.tree.getNumLeaves(false); + void this.#snapshotMetadata.set(block, { + numLeaves, + root, + }); + + return new AppendOnlySnapshot( + this.#nodeValue, + this.#nodeLastModifiedByBlock, + block, + numLeaves, + root, + this.tree, + this.hasher, + ); + }); } - async #getSnapshotMeta(block: number): Promise< - | { - /** The root of the tree snapshot */ - root: Buffer; - /** The number of leaves in the tree snapshot */ - numLeaves: bigint; - } - | undefined - > { - try { - const treeName = this.tree.getName(); - const root = await this.db.get(snapshotRootKey(treeName, block)); - const numLeaves = BigInt(await this.db.get(snapshotNumLeavesKey(treeName, block))); - return { root, numLeaves }; - } catch (err) { - return undefined; - } + #getSnapshotMeta(block: number): SnapshotMetadata | undefined { + return this.#snapshotMetadata.get(block); } } @@ -128,7 +150,8 @@ export class AppendOnlySnapshotBuilder implements TreeSnapshotBuilder { */ class AppendOnlySnapshot implements TreeSnapshot { constructor( - private db: LevelUp, + private nodes: AztecMap, + private nodeHistory: AztecMap, private block: number, private leafCount: bigint, private historicalRoot: Buffer, @@ -136,7 +159,7 @@ class AppendOnlySnapshot implements TreeSnapshot { private hasher: Hasher, ) {} - public async getSiblingPath(index: bigint): Promise> { + public getSiblingPath(index: bigint): SiblingPath { const path: Buffer[] = []; const depth = this.tree.getDepth(); let level = depth; @@ -145,7 +168,7 @@ class AppendOnlySnapshot implements TreeSnapshot { const isRight = index & 0x01n; const siblingIndex = isRight ? index - 1n : index + 1n; - const sibling = await this.#getHistoricalNodeValue(level, siblingIndex); + const sibling = this.#getHistoricalNodeValue(level, siblingIndex); path.push(sibling); level -= 1; @@ -168,9 +191,9 @@ class AppendOnlySnapshot implements TreeSnapshot { return this.historicalRoot; } - async getLeafValue(index: bigint): Promise { + getLeafValue(index: bigint): Buffer | undefined { const leafLevel = this.getDepth(); - const blockNumber = await this.#getBlockNumberThatModifiedNode(leafLevel, index); + const blockNumber = this.#getBlockNumberThatModifiedNode(leafLevel, index); // leaf hasn't been set yet if (typeof blockNumber === 'undefined') { @@ -179,15 +202,15 @@ class AppendOnlySnapshot implements TreeSnapshot { // leaf was set some time in the past if (blockNumber <= this.block) { - return this.db.get(historicalNodeKey(this.tree.getName(), leafLevel, index)); + return this.nodes.get(historicalNodeKey(leafLevel, index)); } // leaf has been set but in a block in the future return undefined; } - async #getHistoricalNodeValue(level: number, index: bigint): Promise { - const blockNumber = await this.#getBlockNumberThatModifiedNode(level, index); + #getHistoricalNodeValue(level: number, index: bigint): Buffer { + const blockNumber = this.#getBlockNumberThatModifiedNode(level, index); // node has never been set if (typeof blockNumber === 'undefined') { @@ -196,7 +219,7 @@ class AppendOnlySnapshot implements TreeSnapshot { // node was set some time in the past if (blockNumber <= this.block) { - return this.db.get(historicalNodeKey(this.tree.getName(), level, index)); + return this.nodes.get(historicalNodeKey(level, index))!; } // the node has been modified since this snapshot was taken @@ -214,27 +237,22 @@ class AppendOnlySnapshot implements TreeSnapshot { return this.tree.getZeroHash(level); } - const [lhs, rhs] = await Promise.all([ + const [lhs, rhs] = [ this.#getHistoricalNodeValue(level + 1, 2n * index), this.#getHistoricalNodeValue(level + 1, 2n * index + 1n), - ]); + ]; return this.hasher.hash(lhs, rhs); } - async #getBlockNumberThatModifiedNode(level: number, index: bigint): Promise { - try { - const value: Buffer | string = await this.db.get(nodeModifiedAtBlockKey(this.tree.getName(), level, index)); - return parseInt(value.toString(), 10); - } catch (err) { - return undefined; - } + #getBlockNumberThatModifiedNode(level: number, index: bigint): number | undefined { + return this.nodeHistory.get(nodeModifiedAtBlockKey(level, index)); } - async findLeafIndex(value: Buffer): Promise { + findLeafIndex(value: Buffer): bigint | undefined { const numLeaves = this.getNumLeaves(); for (let i = 0n; i < numLeaves; i++) { - const currentValue = await this.getLeafValue(i); + const currentValue = this.getLeafValue(i); if (currentValue && currentValue.equals(value)) { return i; } diff --git a/yarn-project/merkle-tree/src/snapshots/base_full_snapshot.ts b/yarn-project/merkle-tree/src/snapshots/base_full_snapshot.ts index 8f49bb91582..5f1e250c569 100644 --- a/yarn-project/merkle-tree/src/snapshots/base_full_snapshot.ts +++ b/yarn-project/merkle-tree/src/snapshots/base_full_snapshot.ts @@ -1,17 +1,18 @@ +import { AztecKVStore, AztecMap } from '@aztec/kv-store'; import { SiblingPath } from '@aztec/types/membership'; -import { LevelUp, LevelUpChain } from 'levelup'; - import { TreeBase } from '../tree_base.js'; import { TreeSnapshot, TreeSnapshotBuilder } from './snapshot_builder.js'; -// key for a node's children -const snapshotChildKey = (node: Buffer, child: 0 | 1) => - Buffer.concat([Buffer.from('snapshot:node:'), node, Buffer.from(':' + child)]); - -// metadata for a snapshot -const snapshotRootKey = (treeName: string, block: number) => `snapshot:root:${treeName}:${block}`; -const snapshotNumLeavesKey = (treeName: string, block: number) => `snapshot:numLeaves:${treeName}:${block}`; +/** + * Metadata for a snapshot, per block + */ +type SnapshotMetadata = { + /** The tree root at the time */ + root: Buffer; + /** The number of filled leaves */ + numLeaves: bigint; +}; /** * Builds a full snapshot of a tree. This implementation works for any Merkle tree and stores @@ -32,104 +33,86 @@ const snapshotNumLeavesKey = (treeName: string, block: number) => `snapshot:numL export abstract class BaseFullTreeSnapshotBuilder implements TreeSnapshotBuilder { - constructor(protected db: LevelUp, protected tree: T) {} - - async snapshot(block: number): Promise { - const snapshotMetadata = await this.#getSnapshotMeta(block); - - if (snapshotMetadata) { - return this.openSnapshot(snapshotMetadata.root, snapshotMetadata.numLeaves); - } - - const batch = this.db.batch(); - const root = this.tree.getRoot(false); - const numLeaves = this.tree.getNumLeaves(false); - const depth = this.tree.getDepth(); - const queue: [Buffer, number, bigint][] = [[root, 0, 0n]]; - - // walk the tree breadth-first and store each of its nodes in the database - // for each node we save two keys - // :0 -> - // :1 -> - while (queue.length > 0) { - const [node, level, i] = queue.shift()!; - // check if the database already has a child for this tree - // if it does, then we know we've seen the whole subtree below it before - // and we don't have to traverse it anymore - // we use the left child here, but it could be anything that shows we've stored the node before - const exists: Buffer | undefined = await this.db.get(snapshotChildKey(node, 0)).catch(() => undefined); - if (exists) { - continue; - } + protected nodes: AztecMap; + protected snapshotMetadata: AztecMap; - if (level + 1 > depth) { - // short circuit if we've reached the leaf level - // otherwise getNode might throw if we ask for the children of a leaf - await this.handleLeaf(i, node, batch); - continue; - } - - const [lhs, rhs] = await Promise.all([ - this.tree.getNode(level + 1, 2n * i), - this.tree.getNode(level + 1, 2n * i + 1n), - ]); - - // we want the zero hash at the children's level, not the node's level - const zeroHash = this.tree.getZeroHash(level + 1); + constructor(protected db: AztecKVStore, protected tree: T) { + this.nodes = db.openMap(`full_snapshot:${tree.getName()}:node`); + this.snapshotMetadata = db.openMap(`full_snapshot:${tree.getName()}:metadata`); + } - batch.put(snapshotChildKey(node, 0), lhs ?? zeroHash); - batch.put(snapshotChildKey(node, 1), rhs ?? zeroHash); + snapshot(block: number): Promise { + return this.db.transaction(() => { + const snapshotMetadata = this.#getSnapshotMeta(block); - // enqueue the children only if they're not zero hashes - if (lhs) { - queue.push([lhs, level + 1, 2n * i]); + if (snapshotMetadata) { + return this.openSnapshot(snapshotMetadata.root, snapshotMetadata.numLeaves); } - if (rhs) { - queue.push([rhs, level + 1, 2n * i + 1n]); + const root = this.tree.getRoot(false); + const numLeaves = this.tree.getNumLeaves(false); + const depth = this.tree.getDepth(); + const queue: [Buffer, number, bigint][] = [[root, 0, 0n]]; + + // walk the tree breadth-first and store each of its nodes in the database + // for each node we save two keys + // :0 -> + // :1 -> + while (queue.length > 0) { + const [node, level, i] = queue.shift()!; + const nodeKey = node.toString('hex'); + // check if the database already has a child for this tree + // if it does, then we know we've seen the whole subtree below it before + // and we don't have to traverse it anymore + // we use the left child here, but it could be anything that shows we've stored the node before + if (this.nodes.has(nodeKey)) { + continue; + } + + if (level + 1 > depth) { + // short circuit if we've reached the leaf level + // otherwise getNode might throw if we ask for the children of a leaf + this.handleLeaf(i, node); + continue; + } + + const [lhs, rhs] = [this.tree.getNode(level + 1, 2n * i), this.tree.getNode(level + 1, 2n * i + 1n)]; + + // we want the zero hash at the children's level, not the node's level + const zeroHash = this.tree.getZeroHash(level + 1); + + void this.nodes.set(nodeKey, [lhs ?? zeroHash, rhs ?? zeroHash]); + // enqueue the children only if they're not zero hashes + if (lhs) { + queue.push([lhs, level + 1, 2n * i]); + } + + if (rhs) { + queue.push([rhs, level + 1, 2n * i + 1n]); + } } - } - batch.put(snapshotRootKey(this.tree.getName(), block), root); - batch.put(snapshotNumLeavesKey(this.tree.getName(), block), String(numLeaves)); - await batch.write(); - - return this.openSnapshot(root, numLeaves); + void this.snapshotMetadata.set(block, { root, numLeaves }); + return this.openSnapshot(root, numLeaves); + }); } - protected handleLeaf(_index: bigint, _node: Buffer, _batch: LevelUpChain) { - return Promise.resolve(); - } + protected handleLeaf(_index: bigint, _node: Buffer): void {} - async getSnapshot(version: number): Promise { - const snapshotMetadata = await this.#getSnapshotMeta(version); + getSnapshot(version: number): Promise { + const snapshotMetadata = this.#getSnapshotMeta(version); if (!snapshotMetadata) { - throw new Error(`Version ${version} does not exist for tree ${this.tree.getName()}`); + return Promise.reject(new Error(`Version ${version} does not exist for tree ${this.tree.getName()}`)); } - return this.openSnapshot(snapshotMetadata.root, snapshotMetadata.numLeaves); + return Promise.resolve(this.openSnapshot(snapshotMetadata.root, snapshotMetadata.numLeaves)); } protected abstract openSnapshot(root: Buffer, numLeaves: bigint): S; - async #getSnapshotMeta(block: number): Promise< - | { - /** The root of the tree snapshot */ - root: Buffer; - /** The number of leaves in the tree snapshot */ - numLeaves: bigint; - } - | undefined - > { - try { - const treeName = this.tree.getName(); - const root = await this.db.get(snapshotRootKey(treeName, block)); - const numLeaves = BigInt(await this.db.get(snapshotNumLeavesKey(treeName, block))); - return { root, numLeaves }; - } catch (err) { - return undefined; - } + #getSnapshotMeta(block: number): SnapshotMetadata | undefined { + return this.snapshotMetadata.get(block); } } @@ -138,16 +121,16 @@ export abstract class BaseFullTreeSnapshotBuilder, protected historicRoot: Buffer, protected numLeaves: bigint, protected tree: TreeBase, ) {} - async getSiblingPath(index: bigint): Promise> { + getSiblingPath(index: bigint): SiblingPath { const siblings: Buffer[] = []; - for await (const [_node, sibling] of this.pathFromRootToLeaf(index)) { + for (const [_node, sibling] of this.pathFromRootToLeaf(index)) { siblings.push(sibling); } @@ -158,9 +141,9 @@ export class BaseFullTreeSnapshot implements TreeSnapshot { return new SiblingPath(this.tree.getDepth() as N, siblings); } - async getLeafValue(index: bigint): Promise { + getLeafValue(index: bigint): Buffer | undefined { let leafNode: Buffer | undefined = undefined; - for await (const [node, _sibling] of this.pathFromRootToLeaf(index)) { + for (const [node, _sibling] of this.pathFromRootToLeaf(index)) { leafNode = node; } @@ -179,17 +162,17 @@ export class BaseFullTreeSnapshot implements TreeSnapshot { return this.numLeaves; } - protected async *pathFromRootToLeaf(leafIndex: bigint) { + protected *pathFromRootToLeaf(leafIndex: bigint) { const root = this.historicRoot; const pathFromRoot = this.#getPathFromRoot(leafIndex); let node: Buffer = root; for (let i = 0; i < pathFromRoot.length; i++) { // get both children. We'll need both anyway (one to keep track of, the other to walk down to) - const children: [Buffer, Buffer] = await Promise.all([ - this.db.get(snapshotChildKey(node, 0)), - this.db.get(snapshotChildKey(node, 1)), - ]).catch(() => [this.tree.getZeroHash(i + 1), this.tree.getZeroHash(i + 1)]); + const children: [Buffer, Buffer] = this.db.get(node.toString('hex')) ?? [ + this.tree.getZeroHash(i + 1), + this.tree.getZeroHash(i + 1), + ]; const next = children[pathFromRoot[i]]; const sibling = children[(pathFromRoot[i] + 1) % 2]; @@ -219,10 +202,10 @@ export class BaseFullTreeSnapshot implements TreeSnapshot { return path; } - async findLeafIndex(value: Buffer): Promise { + findLeafIndex(value: Buffer): bigint | undefined { const numLeaves = this.getNumLeaves(); for (let i = 0n; i < numLeaves; i++) { - const currentValue = await this.getLeafValue(i); + const currentValue = this.getLeafValue(i); if (currentValue && currentValue.equals(value)) { return i; } diff --git a/yarn-project/merkle-tree/src/snapshots/full_snapshot.test.ts b/yarn-project/merkle-tree/src/snapshots/full_snapshot.test.ts index 3f2cc2af791..4219dbd1c45 100644 --- a/yarn-project/merkle-tree/src/snapshots/full_snapshot.test.ts +++ b/yarn-project/merkle-tree/src/snapshots/full_snapshot.test.ts @@ -1,17 +1,16 @@ -import levelup, { LevelUp } from 'levelup'; +import { AztecKVStore, AztecLmdbStore } from '@aztec/kv-store'; import { Pedersen, StandardTree, newTree } from '../index.js'; -import { createMemDown } from '../test/utils/create_mem_down.js'; import { FullTreeSnapshotBuilder } from './full_snapshot.js'; import { describeSnapshotBuilderTestSuite } from './snapshot_builder_test_suite.js'; describe('FullSnapshotBuilder', () => { let tree: StandardTree; let snapshotBuilder: FullTreeSnapshotBuilder; - let db: LevelUp; + let db: AztecKVStore; beforeEach(async () => { - db = levelup(createMemDown()); + db = await AztecLmdbStore.openTmp(); tree = await newTree(StandardTree, db, new Pedersen(), 'test', 4); snapshotBuilder = new FullTreeSnapshotBuilder(db, tree); }); diff --git a/yarn-project/merkle-tree/src/snapshots/full_snapshot.ts b/yarn-project/merkle-tree/src/snapshots/full_snapshot.ts index c78d0ebb188..73cce3b05e7 100644 --- a/yarn-project/merkle-tree/src/snapshots/full_snapshot.ts +++ b/yarn-project/merkle-tree/src/snapshots/full_snapshot.ts @@ -21,6 +21,6 @@ export class FullTreeSnapshotBuilder implements TreeSnapshotBuilder { protected openSnapshot(root: Buffer, numLeaves: bigint): TreeSnapshot { - return new BaseFullTreeSnapshot(this.db, root, numLeaves, this.tree); + return new BaseFullTreeSnapshot(this.nodes, root, numLeaves, this.tree); } } diff --git a/yarn-project/merkle-tree/src/snapshots/indexed_tree_snapshot.test.ts b/yarn-project/merkle-tree/src/snapshots/indexed_tree_snapshot.test.ts index 664c83e605d..e1ba0b9e0f7 100644 --- a/yarn-project/merkle-tree/src/snapshots/indexed_tree_snapshot.test.ts +++ b/yarn-project/merkle-tree/src/snapshots/indexed_tree_snapshot.test.ts @@ -1,27 +1,25 @@ import { Fr, NullifierLeaf, NullifierLeafPreimage } from '@aztec/circuits.js'; +import { AztecKVStore, AztecLmdbStore } from '@aztec/kv-store'; import { Hasher } from '@aztec/types/interfaces'; -import levelup, { LevelUp } from 'levelup'; - import { Pedersen, newTree } from '../index.js'; import { StandardIndexedTreeWithAppend } from '../standard_indexed_tree/test/standard_indexed_tree_with_append.js'; -import { createMemDown } from '../test/utils/create_mem_down.js'; import { IndexedTreeSnapshotBuilder } from './indexed_tree_snapshot.js'; import { describeSnapshotBuilderTestSuite } from './snapshot_builder_test_suite.js'; class NullifierTree extends StandardIndexedTreeWithAppend { - constructor(db: levelup.LevelUp, hasher: Hasher, name: string, depth: number, size: bigint = 0n, root?: Buffer) { + constructor(db: AztecKVStore, hasher: Hasher, name: string, depth: number, size: bigint = 0n, root?: Buffer) { super(db, hasher, name, depth, size, NullifierLeafPreimage, NullifierLeaf, root); } } describe('IndexedTreeSnapshotBuilder', () => { - let db: LevelUp; + let db: AztecKVStore; let tree: StandardIndexedTreeWithAppend; let snapshotBuilder: IndexedTreeSnapshotBuilder; beforeEach(async () => { - db = levelup(createMemDown()); + db = await AztecLmdbStore.openTmp(); tree = await newTree(NullifierTree, db, new Pedersen(), 'test', 4); snapshotBuilder = new IndexedTreeSnapshotBuilder(db, tree, NullifierLeafPreimage); }); @@ -54,26 +52,26 @@ describe('IndexedTreeSnapshotBuilder', () => { await tree.appendLeaves([Buffer.from('d'), Buffer.from('e'), Buffer.from('f')]); await tree.commit(); - const expectedLeavesAtBlock2 = await Promise.all([ + const expectedLeavesAtBlock2 = [ tree.getLatestLeafPreimageCopy(0n, false), tree.getLatestLeafPreimageCopy(1n, false), tree.getLatestLeafPreimageCopy(2n, false), tree.getLatestLeafPreimageCopy(3n, false), tree.getLatestLeafPreimageCopy(4n, false), tree.getLatestLeafPreimageCopy(5n, false), - ]); + ]; await snapshotBuilder.snapshot(2); const snapshot1 = await snapshotBuilder.getSnapshot(1); - const actualLeavesAtBlock1 = await Promise.all([ + const actualLeavesAtBlock1 = [ snapshot1.getLatestLeafPreimageCopy(0n), snapshot1.getLatestLeafPreimageCopy(1n), snapshot1.getLatestLeafPreimageCopy(2n), snapshot1.getLatestLeafPreimageCopy(3n), snapshot1.getLatestLeafPreimageCopy(4n), snapshot1.getLatestLeafPreimageCopy(5n), - ]); + ]; expect(actualLeavesAtBlock1).toEqual(expectedLeavesAtBlock1); const snapshot2 = await snapshotBuilder.getSnapshot(2); @@ -94,12 +92,12 @@ describe('IndexedTreeSnapshotBuilder', () => { await tree.appendLeaves([Buffer.from('a'), Buffer.from('f'), Buffer.from('d')]); await tree.commit(); const snapshot = await snapshotBuilder.snapshot(1); - const historicalPrevValue = await tree.findIndexOfPreviousKey(2n, false); + const historicalPrevValue = tree.findIndexOfPreviousKey(2n, false); await tree.appendLeaves([Buffer.from('c'), Buffer.from('b'), Buffer.from('e')]); await tree.commit(); - await expect(snapshot.findIndexOfPreviousKey(2n)).resolves.toEqual(historicalPrevValue); + expect(snapshot.findIndexOfPreviousKey(2n)).toEqual(historicalPrevValue); }); }); }); diff --git a/yarn-project/merkle-tree/src/snapshots/indexed_tree_snapshot.ts b/yarn-project/merkle-tree/src/snapshots/indexed_tree_snapshot.ts index 28aeefdc953..8a787fd2067 100644 --- a/yarn-project/merkle-tree/src/snapshots/indexed_tree_snapshot.ts +++ b/yarn-project/merkle-tree/src/snapshots/indexed_tree_snapshot.ts @@ -1,33 +1,32 @@ import { IndexedTreeLeafPreimage } from '@aztec/foundation/trees'; +import { AztecKVStore, AztecMap } from '@aztec/kv-store'; -import { LevelUp, LevelUpChain } from 'levelup'; - -import { IndexedTree } from '../interfaces/indexed_tree.js'; -import { PreimageFactory } from '../standard_indexed_tree/standard_indexed_tree.js'; +import { IndexedTree, PreimageFactory } from '../interfaces/indexed_tree.js'; import { TreeBase } from '../tree_base.js'; import { BaseFullTreeSnapshot, BaseFullTreeSnapshotBuilder } from './base_full_snapshot.js'; import { IndexedTreeSnapshot, TreeSnapshotBuilder } from './snapshot_builder.js'; -const snapshotLeafValue = (node: Buffer, index: bigint) => - Buffer.concat([Buffer.from('snapshot:leaf:'), node, Buffer.from(':' + index)]); +const snapshotLeafValue = (node: Buffer, index: bigint) => 'snapshot:leaf:' + node.toString('hex') + ':' + index; /** a */ export class IndexedTreeSnapshotBuilder extends BaseFullTreeSnapshotBuilder implements TreeSnapshotBuilder { - constructor(db: LevelUp, tree: IndexedTree & TreeBase, private leafPreimageBuilder: PreimageFactory) { - super(db, tree); + leaves: AztecMap; + constructor(store: AztecKVStore, tree: IndexedTree & TreeBase, private leafPreimageBuilder: PreimageFactory) { + super(store, tree); + this.leaves = store.openMap('indexed_tree_snapshot:' + tree.getName()); } protected openSnapshot(root: Buffer, numLeaves: bigint): IndexedTreeSnapshot { - return new IndexedTreeSnapshotImpl(this.db, root, numLeaves, this.tree, this.leafPreimageBuilder); + return new IndexedTreeSnapshotImpl(this.nodes, this.leaves, root, numLeaves, this.tree, this.leafPreimageBuilder); } - protected async handleLeaf(index: bigint, node: Buffer, batch: LevelUpChain) { - const leafPreimage = await this.tree.getLatestLeafPreimageCopy(index, false); + protected handleLeaf(index: bigint, node: Buffer) { + const leafPreimage = this.tree.getLatestLeafPreimageCopy(index, false); if (leafPreimage) { - batch.put(snapshotLeafValue(node, index), leafPreimage.toBuffer()); + void this.leaves.set(snapshotLeafValue(node, index), leafPreimage.toBuffer()); } } } @@ -35,7 +34,8 @@ export class IndexedTreeSnapshotBuilder /** A snapshot of an indexed tree at a particular point in time */ class IndexedTreeSnapshotImpl extends BaseFullTreeSnapshot implements IndexedTreeSnapshot { constructor( - db: LevelUp, + db: AztecMap, + private leaves: AztecMap, historicRoot: Buffer, numLeaves: bigint, tree: IndexedTree & TreeBase, @@ -44,14 +44,14 @@ class IndexedTreeSnapshotImpl extends BaseFullTreeSnapshot implements IndexedTre super(db, historicRoot, numLeaves, tree); } - async getLeafValue(index: bigint): Promise { - const leafPreimage = await this.getLatestLeafPreimageCopy(index); + getLeafValue(index: bigint): Buffer | undefined { + const leafPreimage = this.getLatestLeafPreimageCopy(index); return leafPreimage?.toBuffer(); } - async getLatestLeafPreimageCopy(index: bigint): Promise { - const leafNode = await super.getLeafValue(index); - const leafValue = await this.db.get(snapshotLeafValue(leafNode!, index)).catch(() => undefined); + getLatestLeafPreimageCopy(index: bigint): IndexedTreeLeafPreimage | undefined { + const leafNode = super.getLeafValue(index); + const leafValue = this.leaves.get(snapshotLeafValue(leafNode!, index)); if (leafValue) { return this.leafPreimageBuilder.fromBuffer(leafValue); } else { @@ -59,7 +59,7 @@ class IndexedTreeSnapshotImpl extends BaseFullTreeSnapshot implements IndexedTre } } - async findIndexOfPreviousKey(newValue: bigint): Promise<{ + findIndexOfPreviousKey(newValue: bigint): { /** * The index of the found leaf. */ @@ -68,13 +68,13 @@ class IndexedTreeSnapshotImpl extends BaseFullTreeSnapshot implements IndexedTre * A flag indicating if the corresponding leaf's value is equal to `newValue`. */ alreadyPresent: boolean; - }> { + } { const numLeaves = this.getNumLeaves(); const diff: bigint[] = []; for (let i = 0; i < numLeaves; i++) { // this is very inefficient - const storedLeaf = await this.getLatestLeafPreimageCopy(BigInt(i))!; + const storedLeaf = this.getLatestLeafPreimageCopy(BigInt(i))!; // The stored leaf can be undefined if it addresses an empty leaf // If the leaf is empty we do the same as if the leaf was larger @@ -99,8 +99,8 @@ class IndexedTreeSnapshotImpl extends BaseFullTreeSnapshot implements IndexedTre return { index: BigInt(minIndex), alreadyPresent: false }; } - async findLeafIndex(value: Buffer): Promise { - const index = await this.tree.findLeafIndex(value, false); + findLeafIndex(value: Buffer): bigint | undefined { + const index = this.tree.findLeafIndex(value, false); if (index !== undefined && index < this.getNumLeaves()) { return index; } diff --git a/yarn-project/merkle-tree/src/snapshots/snapshot_builder.ts b/yarn-project/merkle-tree/src/snapshots/snapshot_builder.ts index 872395e134e..98bbb9051d8 100644 --- a/yarn-project/merkle-tree/src/snapshots/snapshot_builder.ts +++ b/yarn-project/merkle-tree/src/snapshots/snapshot_builder.ts @@ -41,13 +41,13 @@ export interface TreeSnapshot { * Returns the value of a leaf at the specified index. * @param index - The index of the leaf value to be returned. */ - getLeafValue(index: bigint): Promise; + getLeafValue(index: bigint): Buffer | undefined; /** * Returns the sibling path for a requested leaf index. * @param index - The index of the leaf for which a sibling path is required. */ - getSiblingPath(index: bigint): Promise>; + getSiblingPath(index: bigint): SiblingPath; /** * Returns the index of a leaf given its value, or undefined if no leaf with that value is found. @@ -55,7 +55,7 @@ export interface TreeSnapshot { * @param value - The leaf value to look for. * @returns The index of the first leaf found with a given value (undefined if not found). */ - findLeafIndex(value: Buffer): Promise; + findLeafIndex(value: Buffer): bigint | undefined; } /** A snapshot of an indexed tree */ @@ -64,14 +64,14 @@ export interface IndexedTreeSnapshot extends TreeSnapshot { * Gets the historical data for a leaf * @param index - The index of the leaf to get the data for */ - getLatestLeafPreimageCopy(index: bigint): Promise; + getLatestLeafPreimageCopy(index: bigint): IndexedTreeLeafPreimage | undefined; /** * Finds the index of the largest leaf whose value is less than or equal to the provided value. * @param newValue - The new value to be inserted into the tree. * @returns The found leaf index and a flag indicating if the corresponding leaf's value is equal to `newValue`. */ - findIndexOfPreviousKey(newValue: bigint): Promise<{ + findIndexOfPreviousKey(newValue: bigint): { /** * The index of the found leaf. */ @@ -80,5 +80,5 @@ export interface IndexedTreeSnapshot extends TreeSnapshot { * A flag indicating if the corresponding leaf's value is equal to `newValue`. */ alreadyPresent: boolean; - }>; + }; } diff --git a/yarn-project/merkle-tree/src/snapshots/snapshot_builder_test_suite.ts b/yarn-project/merkle-tree/src/snapshots/snapshot_builder_test_suite.ts index f50ff1d69ae..85aa63f7b66 100644 --- a/yarn-project/merkle-tree/src/snapshots/snapshot_builder_test_suite.ts +++ b/yarn-project/merkle-tree/src/snapshots/snapshot_builder_test_suite.ts @@ -185,13 +185,13 @@ export function describeSnapshotBuilderTestSuite => { - return await newTree(SparseTree, levelUp, hasher, name, depth); +const createDb = async (db: AztecKVStore, hasher: Hasher, name: string, depth: number): Promise => { + return await newTree(SparseTree, db, hasher, name, depth); }; -const createFromName = async (levelUp: levelup.LevelUp, hasher: Hasher, name: string): Promise => { - return await loadTree(SparseTree, levelUp, hasher, name); +const createFromName = async (db: AztecKVStore, hasher: Hasher, name: string): Promise => { + return await loadTree(SparseTree, db, hasher, name); }; const TEST_TREE_DEPTH = 3; @@ -42,19 +36,19 @@ describe('SparseTreeSpecific', () => { }); it('throws when index is bigger than (2^DEPTH - 1) ', async () => { - const db = levelup(createMemDown()); + const db = await AztecLmdbStore.openTmp(); const depth = 32; const tree = await createDb(db, pedersen, 'test', depth); const index = 2n ** BigInt(depth); - await expect(tree.updateLeaf(Buffer.alloc(32), index)).rejects.toThrow(); + expect(() => tree.updateLeaf(Buffer.alloc(32), index)).toThrow(); }); it('updating non-empty leaf does not change tree size', async () => { const depth = 32; const maxIndex = 2 ** depth - 1; - const db = levelup(createMemDown()); + const db = await AztecLmdbStore.openTmp(); const tree = await createDb(db, pedersen, 'test', depth); const randomIndex = BigInt(Math.floor(Math.random() * maxIndex)); @@ -73,7 +67,7 @@ describe('SparseTreeSpecific', () => { const depth = 254; const maxIndex = 2 ** depth - 1; - const db = levelup(createMemDown()); + const db = await AztecLmdbStore.openTmp(); const tree = await createDb(db, pedersen, 'test', depth); const randomIndex = BigInt(Math.floor(Math.random() * maxIndex)); @@ -89,7 +83,7 @@ describe('SparseTreeSpecific', () => { }); it('should have correct root and sibling path after in a "non-append-only" way', async () => { - const db = levelup(createMemDown()); + const db = await AztecLmdbStore.openTmp(); const tree = await createDb(db, pedersen, 'test', 3); const level2ZeroHash = pedersen.hash(INITIAL_LEAF, INITIAL_LEAF); @@ -162,7 +156,7 @@ describe('SparseTreeSpecific', () => { const depth = 254; const maxIndex = 2 ** depth - 1; - const db = levelup(createMemDown()); + const db = await AztecLmdbStore.openTmp(); const tree = await createDb(db, pedersen, 'test', depth); const leaves = Array.from({ length: 1000 }).map(() => randomBytes(32)); diff --git a/yarn-project/merkle-tree/src/sparse_tree/sparse_tree.ts b/yarn-project/merkle-tree/src/sparse_tree/sparse_tree.ts index 138ca8f21e7..86f3c8506e4 100644 --- a/yarn-project/merkle-tree/src/sparse_tree/sparse_tree.ts +++ b/yarn-project/merkle-tree/src/sparse_tree/sparse_tree.ts @@ -7,24 +7,23 @@ import { INITIAL_LEAF, TreeBase } from '../tree_base.js'; * A Merkle tree implementation that uses a LevelDB database to store the tree. */ export class SparseTree extends TreeBase implements UpdateOnlyTree { - #snapshotBuilder = new FullTreeSnapshotBuilder(this.db, this); - + #snapshotBuilder = new FullTreeSnapshotBuilder(this.store, this); /** * Updates a leaf in the tree. * @param leaf - New contents of the leaf. * @param index - Index of the leaf to be updated. */ - public async updateLeaf(leaf: Buffer, index: bigint): Promise { + public updateLeaf(leaf: Buffer, index: bigint): Promise { if (index > this.maxIndex) { throw Error(`Index out of bounds. Index ${index}, max index: ${this.maxIndex}.`); } const insertingZeroElement = leaf.equals(INITIAL_LEAF); - const originallyZeroElement = (await this.getLeafValue(index, true))?.equals(INITIAL_LEAF); + const originallyZeroElement = this.getLeafValue(index, true)?.equals(INITIAL_LEAF); if (insertingZeroElement && originallyZeroElement) { - return; + return Promise.resolve(); } - await this.addLeafToCacheAndHashToRoot(leaf, index); + this.addLeafToCacheAndHashToRoot(leaf, index); if (insertingZeroElement) { // Deleting element (originally non-zero and new value is zero) this.cachedSize = (this.cachedSize ?? this.size) - 1n; @@ -32,6 +31,8 @@ export class SparseTree extends TreeBase implements UpdateOnlyTree { // Inserting new element (originally zero and new value is non-zero) this.cachedSize = (this.cachedSize ?? this.size) + 1n; } + + return Promise.resolve(); } public snapshot(block: number): Promise { @@ -42,7 +43,7 @@ export class SparseTree extends TreeBase implements UpdateOnlyTree { return this.#snapshotBuilder.getSnapshot(block); } - public findLeafIndex(_value: Buffer, _includeUncommitted: boolean): Promise { + public findLeafIndex(_value: Buffer, _includeUncommitted: boolean): bigint | undefined { throw new Error('Finding leaf index is not supported for sparse trees'); } } diff --git a/yarn-project/merkle-tree/src/standard_indexed_tree/standard_indexed_tree.ts b/yarn-project/merkle-tree/src/standard_indexed_tree/standard_indexed_tree.ts index dcd56f31839..316bc2a62df 100644 --- a/yarn-project/merkle-tree/src/standard_indexed_tree/standard_indexed_tree.ts +++ b/yarn-project/merkle-tree/src/standard_indexed_tree/standard_indexed_tree.ts @@ -1,57 +1,22 @@ import { TreeInsertionStats } from '@aztec/circuit-types/stats'; -import { toBigIntBE, toBufferBE } from '@aztec/foundation/bigint-buffer'; -import { createDebugLogger } from '@aztec/foundation/log'; +import { toBufferBE } from '@aztec/foundation/bigint-buffer'; import { Timer } from '@aztec/foundation/timer'; import { IndexedTreeLeaf, IndexedTreeLeafPreimage } from '@aztec/foundation/trees'; +import { AztecKVStore, AztecMap } from '@aztec/kv-store'; import { Hasher } from '@aztec/types/interfaces'; import { SiblingPath } from '@aztec/types/membership'; -import { LevelUp } from 'levelup'; - -import { - BatchInsertionResult, - IndexedTree, - IndexedTreeSnapshot, - IndexedTreeSnapshotBuilder, - LowLeafWitnessData, -} from '../index.js'; +import { BatchInsertionResult, IndexedTree, LowLeafWitnessData, PreimageFactory } from '../interfaces/indexed_tree.js'; +import { IndexedTreeSnapshotBuilder } from '../snapshots/indexed_tree_snapshot.js'; +import { IndexedTreeSnapshot } from '../snapshots/snapshot_builder.js'; import { TreeBase } from '../tree_base.js'; -const log = createDebugLogger('aztec:standard-indexed-tree'); - -/** - * Factory for creating leaf preimages. - */ -export interface PreimageFactory { - /** - * Creates a new preimage from a leaf. - * @param leaf - Leaf to create a preimage from. - * @param nextKey - Next key of the leaf. - * @param nextIndex - Next index of the leaf. - */ - fromLeaf(leaf: IndexedTreeLeaf, nextKey: bigint, nextIndex: bigint): IndexedTreeLeafPreimage; - /** - * Creates a new preimage from a buffer. - * @param buffer - Buffer to create a preimage from. - */ - fromBuffer(buffer: Buffer): IndexedTreeLeafPreimage; - /** - * Creates an empty preimage. - */ - empty(): IndexedTreeLeafPreimage; - /** - * Creates a copy of a preimage. - * @param preimage - Preimage to be cloned. - */ - clone(preimage: IndexedTreeLeafPreimage): IndexedTreeLeafPreimage; -} - export const buildDbKeyForPreimage = (name: string, index: bigint) => { - return `${name}:leaf_by_index:${toBufferBE(index, 32).toString('hex')}`; + return `${name}:leaf_by_index:${toBufferBE(index, 32).toString('hex')}` as const; }; export const buildDbKeyForLeafIndex = (name: string, key: bigint) => { - return `${name}:leaf_index_by_leaf_key:${toBufferBE(key, 32).toString('hex')}`; + return `${name}:leaf_index_by_leaf_key:${toBufferBE(key, 32).toString('hex')}` as const; }; /** @@ -90,11 +55,14 @@ function getEmptyLowLeafWitness( * Standard implementation of an indexed tree. */ export class StandardIndexedTree extends TreeBase implements IndexedTree { - #snapshotBuilder = new IndexedTreeSnapshotBuilder(this.db, this, this.leafPreimageFactory); + #snapshotBuilder = new IndexedTreeSnapshotBuilder(this.store, this, this.leafPreimageFactory); + protected cachedLeafPreimages: { [key: string]: IndexedTreeLeafPreimage } = {}; + protected leaves: AztecMap, Buffer>; + protected leafIndex: AztecMap, bigint>; public constructor( - db: LevelUp, + store: AztecKVStore, hasher: Hasher, name: string, depth: number, @@ -103,7 +71,9 @@ export class StandardIndexedTree extends TreeBase implements IndexedTree { protected leafFactory: LeafFactory, root?: Buffer, ) { - super(db, hasher, name, depth, size, root); + super(store, hasher, name, depth, size, root); + this.leaves = store.openMap(`tree_${name}_leaves`); + this.leafIndex = store.openMap(`tree_${name}_leaf_index`); } /** @@ -140,8 +110,8 @@ export class StandardIndexedTree extends TreeBase implements IndexedTree { * @param includeUncommitted - Indicates whether to include uncommitted leaves in the computation. * @returns The value of the leaf at the given index or undefined if the leaf is empty. */ - public async getLeafValue(index: bigint, includeUncommitted: boolean): Promise { - const preimage = await this.getLatestLeafPreimageCopy(index, includeUncommitted); + public getLeafValue(index: bigint, includeUncommitted: boolean): Buffer | undefined { + const preimage = this.getLatestLeafPreimageCopy(index, includeUncommitted); return preimage && preimage.toBuffer(); } @@ -151,10 +121,10 @@ export class StandardIndexedTree extends TreeBase implements IndexedTree { * @param includeUncommitted - If true, the uncommitted changes are included in the search. * @returns The found leaf index and a flag indicating if the corresponding leaf's value is equal to `newValue`. */ - async findIndexOfPreviousKey( + findIndexOfPreviousKey( newKey: bigint, includeUncommitted: boolean, - ): Promise< + ): | { /** * The index of the found leaf. @@ -165,10 +135,9 @@ export class StandardIndexedTree extends TreeBase implements IndexedTree { */ alreadyPresent: boolean; } - | undefined - > { - let lowLeafIndex = await this.getDbLowLeafIndex(newKey); - let lowLeafPreimage = lowLeafIndex !== undefined ? await this.getDbPreimage(lowLeafIndex) : undefined; + | undefined { + let lowLeafIndex = this.getDbLowLeafIndex(newKey); + let lowLeafPreimage = lowLeafIndex !== undefined ? this.getDbPreimage(lowLeafIndex) : undefined; if (includeUncommitted) { const cachedLowLeafIndex = this.getCachedLowLeafIndex(newKey); @@ -213,36 +182,21 @@ export class StandardIndexedTree extends TreeBase implements IndexedTree { return undefined; } - private async getDbLowLeafIndex(key: bigint): Promise { - return await new Promise((resolve, reject) => { - let lowLeafIndex: bigint | undefined; - this.db - .createReadStream({ - gte: buildDbKeyForLeafIndex(this.getName(), 0n), - lte: buildDbKeyForLeafIndex(this.getName(), key), - limit: 1, - reverse: true, - }) - .on('data', data => { - lowLeafIndex = toBigIntBE(data.value); - }) - .on('close', function () {}) - .on('end', function () { - resolve(lowLeafIndex); - }) - .on('error', function () { - log.error('stream error'); - reject(); - }); - }); + private getDbLowLeafIndex(key: bigint): bigint | undefined { + const values = Array.from( + this.leafIndex.values({ + end: buildDbKeyForLeafIndex(this.getName(), key), + limit: 1, + reverse: true, + }), + ); + + return values[0]; } - private async getDbPreimage(index: bigint): Promise { - const dbPreimage = await this.db - .get(buildDbKeyForPreimage(this.getName(), index)) - .then(data => this.leafPreimageFactory.fromBuffer(data)) - .catch(() => undefined); - return dbPreimage; + private getDbPreimage(index: bigint): IndexedTreeLeafPreimage | undefined { + const value = this.leaves.get(buildDbKeyForPreimage(this.getName(), index)); + return value ? this.leafPreimageFactory.fromBuffer(value) : undefined; } private getCachedPreimage(index: bigint): IndexedTreeLeafPreimage | undefined { @@ -255,13 +209,10 @@ export class StandardIndexedTree extends TreeBase implements IndexedTree { * @param includeUncommitted - If true, the uncommitted changes are included in the search. * @returns A copy of the leaf preimage at the given index or undefined if the leaf was not found. */ - public async getLatestLeafPreimageCopy( - index: bigint, - includeUncommitted: boolean, - ): Promise { + public getLatestLeafPreimageCopy(index: bigint, includeUncommitted: boolean): IndexedTreeLeafPreimage | undefined { const preimage = !includeUncommitted - ? await this.getDbPreimage(index) - : this.getCachedPreimage(index) ?? (await this.getDbPreimage(index)); + ? this.getDbPreimage(index) + : this.getCachedPreimage(index) ?? this.getDbPreimage(index); return preimage && this.leafPreimageFactory.clone(preimage); } @@ -271,17 +222,15 @@ export class StandardIndexedTree extends TreeBase implements IndexedTree { * @param includeUncommitted - Indicates whether to include uncommitted data. * @returns The index of the first leaf found with a given value (undefined if not found). */ - public async findLeafIndex(value: Buffer, includeUncommitted: boolean): Promise { + public findLeafIndex(value: Buffer, includeUncommitted: boolean): bigint | undefined { const leaf = this.leafFactory.fromBuffer(value); - let index = await this.db - .get(buildDbKeyForLeafIndex(this.getName(), leaf.getKey())) - .then(data => toBigIntBE(data)) - .catch(() => undefined); + let index = this.leafIndex.get(buildDbKeyForLeafIndex(this.getName(), leaf.getKey())); if (includeUncommitted && index === undefined) { const cachedIndex = this.getCachedLeafIndex(leaf.getKey()); index = cachedIndex; } + return index; } @@ -314,24 +263,24 @@ export class StandardIndexedTree extends TreeBase implements IndexedTree { // Make the last leaf point to the first leaf leaves[prefilledSize - 1] = this.leafPreimageFactory.fromLeaf(leaves[prefilledSize - 1].asLeaf(), 0n, 0n); - await this.encodeAndAppendLeaves(leaves, true); + this.encodeAndAppendLeaves(leaves, true); await this.commit(); } /** * Commits all the leaves to the database and removes them from a cache. */ - private async commitLeaves(): Promise { - const batch = this.db.batch(); - const keys = Object.getOwnPropertyNames(this.cachedLeafPreimages); - for (const key of keys) { - const leaf = this.cachedLeafPreimages[key]; - const index = BigInt(key); - batch.put(buildDbKeyForPreimage(this.getName(), index), leaf.toBuffer()); - batch.put(buildDbKeyForLeafIndex(this.getName(), leaf.getKey()), toBufferBE(index, 32)); - } - await batch.write(); - this.clearCachedLeaves(); + private commitLeaves(): Promise { + return this.store.transaction(() => { + const keys = Object.getOwnPropertyNames(this.cachedLeafPreimages); + for (const key of keys) { + const leaf = this.cachedLeafPreimages[key]; + const index = BigInt(key); + void this.leaves.set(buildDbKeyForPreimage(this.getName(), index), leaf.toBuffer()); + void this.leafIndex.set(buildDbKeyForLeafIndex(this.getName(), leaf.getKey()), index); + } + this.clearCachedLeaves(); + }); } /** @@ -346,14 +295,14 @@ export class StandardIndexedTree extends TreeBase implements IndexedTree { * @param preimage - New contents of the leaf. * @param index - Index of the leaf to be updated. */ - protected async updateLeaf(preimage: IndexedTreeLeafPreimage, index: bigint) { + protected updateLeaf(preimage: IndexedTreeLeafPreimage, index: bigint) { if (index > this.maxIndex) { throw Error(`Index out of bounds. Index ${index}, max index: ${this.maxIndex}.`); } this.cachedLeafPreimages[index.toString()] = preimage; const encodedLeaf = this.encodeLeaf(preimage, true); - await this.addLeafToCacheAndHashToRoot(encodedLeaf, index); + this.addLeafToCacheAndHashToRoot(encodedLeaf, index); const numLeaves = this.getNumLeaves(true); if (index >= numLeaves) { this.cachedSize = index + 1n; @@ -542,7 +491,7 @@ export class StandardIndexedTree extends TreeBase implements IndexedTree { insertedKeys.set(newLeaf.getKey(), true); } - const indexOfPrevious = await this.findIndexOfPreviousKey(newLeaf.getKey(), true); + const indexOfPrevious = this.findIndexOfPreviousKey(newLeaf.getKey(), true); if (indexOfPrevious === undefined) { return { lowLeavesWitnessData: undefined, @@ -555,7 +504,7 @@ export class StandardIndexedTree extends TreeBase implements IndexedTree { const isUpdate = indexOfPrevious.alreadyPresent; // get the low leaf (existence checked in getting index) - const lowLeafPreimage = (await this.getLatestLeafPreimageCopy(indexOfPrevious.index, true))!; + const lowLeafPreimage = this.getLatestLeafPreimageCopy(indexOfPrevious.index, true)!; const siblingPath = await this.getSiblingPath(BigInt(indexOfPrevious.index), true); const witness: LowLeafWitnessData = { @@ -576,7 +525,7 @@ export class StandardIndexedTree extends TreeBase implements IndexedTree { lowLeafPreimage.getNextIndex(), ); - await this.updateLeaf(newLowLeafPreimage, indexOfPrevious.index); + this.updateLeaf(newLowLeafPreimage, indexOfPrevious.index); pendingInsertionSubtree[originalIndex] = this.leafPreimageFactory.empty(); } else { @@ -586,7 +535,7 @@ export class StandardIndexedTree extends TreeBase implements IndexedTree { startInsertionIndex + BigInt(originalIndex), ); - await this.updateLeaf(newLowLeafPreimage, indexOfPrevious.index); + this.updateLeaf(newLowLeafPreimage, indexOfPrevious.index); const currentPendingPreimageLeaf = this.leafPreimageFactory.fromLeaf( newLeaf, @@ -606,7 +555,7 @@ export class StandardIndexedTree extends TreeBase implements IndexedTree { // Perform batch insertion of new pending values // Note: In this case we set `hash0Leaf` param to false because batch insertion algorithm use forced null leaf // inclusion. See {@link encodeLeaf} for a more through param explanation. - await this.encodeAndAppendLeaves(pendingInsertionSubtree, false); + this.encodeAndAppendLeaves(pendingInsertionSubtree, false); this.log(`Inserted ${leaves.length} leaves into ${this.getName()} tree`, { eventName: 'tree-insertion', @@ -641,8 +590,8 @@ export class StandardIndexedTree extends TreeBase implements IndexedTree { return this.#snapshotBuilder.snapshot(blockNumber); } - getSnapshot(block: number): Promise { - return this.#snapshotBuilder.getSnapshot(block); + getSnapshot(blockNumber: number): Promise { + return this.#snapshotBuilder.getSnapshot(blockNumber); } /** @@ -651,7 +600,7 @@ export class StandardIndexedTree extends TreeBase implements IndexedTree { * @param hash0Leaf - Indicates whether 0 value leaf should be hashed. See {@link encodeLeaf}. * @returns Empty promise */ - private async encodeAndAppendLeaves(preimages: IndexedTreeLeafPreimage[], hash0Leaf: boolean): Promise { + private encodeAndAppendLeaves(preimages: IndexedTreeLeafPreimage[], hash0Leaf: boolean): void { const startInsertionIndex = this.getNumLeaves(true); const hashedLeaves = preimages.map((preimage, i) => { @@ -659,7 +608,7 @@ export class StandardIndexedTree extends TreeBase implements IndexedTree { return this.encodeLeaf(preimage, hash0Leaf); }); - await super.appendLeaves(hashedLeaves); + super.appendLeaves(hashedLeaves); } /** diff --git a/yarn-project/merkle-tree/src/standard_indexed_tree/test/standard_indexed_tree.test.ts b/yarn-project/merkle-tree/src/standard_indexed_tree/test/standard_indexed_tree.test.ts index 65d50a8eb49..1e793daf656 100644 --- a/yarn-project/merkle-tree/src/standard_indexed_tree/test/standard_indexed_tree.test.ts +++ b/yarn-project/merkle-tree/src/standard_indexed_tree/test/standard_indexed_tree.test.ts @@ -6,34 +6,32 @@ import { PublicDataTreeLeafPreimage, } from '@aztec/circuits.js'; import { toBufferBE } from '@aztec/foundation/bigint-buffer'; +import { AztecKVStore, AztecLmdbStore } from '@aztec/kv-store'; import { Hasher } from '@aztec/types/interfaces'; import { SiblingPath } from '@aztec/types/membership'; -import { default as levelup } from 'levelup'; - import { INITIAL_LEAF, MerkleTree, Pedersen, loadTree, newTree } from '../../index.js'; import { treeTestSuite } from '../../test/test_suite.js'; -import { createMemDown } from '../../test/utils/create_mem_down.js'; import { StandardIndexedTreeWithAppend } from './standard_indexed_tree_with_append.js'; class NullifierTree extends StandardIndexedTreeWithAppend { - constructor(db: levelup.LevelUp, hasher: Hasher, name: string, depth: number, size: bigint = 0n, root?: Buffer) { - super(db, hasher, name, depth, size, NullifierLeafPreimage, NullifierLeaf, root); + constructor(store: AztecKVStore, hasher: Hasher, name: string, depth: number, size: bigint = 0n, root?: Buffer) { + super(store, hasher, name, depth, size, NullifierLeafPreimage, NullifierLeaf, root); } } class PublicDataTree extends StandardIndexedTreeWithAppend { - constructor(db: levelup.LevelUp, hasher: Hasher, name: string, depth: number, size: bigint = 0n, root?: Buffer) { - super(db, hasher, name, depth, size, PublicDataTreeLeafPreimage, PublicDataTreeLeaf, root); + constructor(store: AztecKVStore, hasher: Hasher, name: string, depth: number, size: bigint = 0n, root?: Buffer) { + super(store, hasher, name, depth, size, PublicDataTreeLeafPreimage, PublicDataTreeLeaf, root); } } -const createDb = async (levelUp: levelup.LevelUp, hasher: Hasher, name: string, depth: number, prefilledSize = 1) => { - return await newTree(NullifierTree, levelUp, hasher, name, depth, prefilledSize); +const createDb = async (store: AztecKVStore, hasher: Hasher, name: string, depth: number, prefilledSize = 1) => { + return await newTree(NullifierTree, store, hasher, name, depth, prefilledSize); }; -const createFromName = async (levelUp: levelup.LevelUp, hasher: Hasher, name: string) => { - return await loadTree(NullifierTree, levelUp, hasher, name); +const createFromName = async (store: AztecKVStore, hasher: Hasher, name: string) => { + return await loadTree(NullifierTree, store, hasher, name); }; const createNullifierTreeLeafHashInputs = (value: number, nextIndex: number, nextValue: number) => { @@ -77,7 +75,7 @@ describe('StandardIndexedTreeSpecific', () => { it('produces the correct roots and sibling paths', async () => { // Create a depth-3 indexed merkle tree - const db = levelup(createMemDown()); + const db = await AztecLmdbStore.openTmp(); const tree = await createDb(db, pedersen, 'test', 3); /** @@ -274,8 +272,7 @@ describe('StandardIndexedTreeSpecific', () => { it('Can append empty leaves and handle insertions', async () => { // Create a depth-3 indexed merkle tree - const db = levelup(createMemDown()); - const tree = await createDb(db, pedersen, 'test', 3); + const tree = await createDb(await AztecLmdbStore.openTmp(), pedersen, 'test', 3); /** * Initial state: @@ -492,8 +489,8 @@ describe('StandardIndexedTreeSpecific', () => { const SUBTREE_HEIGHT = 5; // originally from BaseRollupInputs.NULLIFIER_SUBTREE_HEIGHT // Create a depth-3 indexed merkle tree - const appendTree = await createDb(levelup(createMemDown()), pedersen, 'test', TREE_HEIGHT, INITIAL_TREE_SIZE); - const insertTree = await createDb(levelup(createMemDown()), pedersen, 'test', TREE_HEIGHT, INITIAL_TREE_SIZE); + const appendTree = await createDb(await AztecLmdbStore.openTmp(), pedersen, 'test', TREE_HEIGHT, INITIAL_TREE_SIZE); + const insertTree = await createDb(await AztecLmdbStore.openTmp(), pedersen, 'test', TREE_HEIGHT, INITIAL_TREE_SIZE); await appendTree.appendLeaves(leaves); await insertTree.batchInsert(leaves, SUBTREE_HEIGHT); @@ -504,25 +501,25 @@ describe('StandardIndexedTreeSpecific', () => { }); it('should be able to find indexes of leaves', async () => { - const db = levelup(createMemDown()); + const db = await AztecLmdbStore.openTmp(); const tree = await createDb(db, pedersen, 'test', 3); const values = [Buffer.alloc(32, 1), Buffer.alloc(32, 2)]; await tree.appendLeaves([values[0]]); - expect(await tree.findLeafIndex(values[0], true)).toBeDefined(); - expect(await tree.findLeafIndex(values[0], false)).toBe(undefined); - expect(await tree.findLeafIndex(values[1], true)).toBe(undefined); + expect(tree.findLeafIndex(values[0], true)).toBeDefined(); + expect(tree.findLeafIndex(values[0], false)).toBe(undefined); + expect(tree.findLeafIndex(values[1], true)).toBe(undefined); await tree.commit(); - expect(await tree.findLeafIndex(values[0], false)).toBeDefined(); + expect(tree.findLeafIndex(values[0], false)).toBeDefined(); }); describe('Updatable leaves', () => { it('should be able to upsert leaves', async () => { // Create a depth-3 indexed merkle tree - const db = levelup(createMemDown()); + const db = await AztecLmdbStore.openTmp(); const tree = await newTree(PublicDataTree, db, pedersen, 'test', 3, 1); /** @@ -632,7 +629,7 @@ describe('StandardIndexedTreeSpecific', () => { const INITIAL_TREE_SIZE = 8; const SUBTREE_HEIGHT = 5; - const db = levelup(createMemDown()); + const db = await AztecLmdbStore.openTmp(); const appendTree = await newTree(PublicDataTree, db, pedersen, 'test', TREE_HEIGHT, INITIAL_TREE_SIZE); const insertTree = await newTree(PublicDataTree, db, pedersen, 'test', TREE_HEIGHT, INITIAL_TREE_SIZE); diff --git a/yarn-project/merkle-tree/src/standard_indexed_tree/test/standard_indexed_tree_with_append.ts b/yarn-project/merkle-tree/src/standard_indexed_tree/test/standard_indexed_tree_with_append.ts index 3fd3a123084..60ff4a9ecca 100644 --- a/yarn-project/merkle-tree/src/standard_indexed_tree/test/standard_indexed_tree_with_append.ts +++ b/yarn-project/merkle-tree/src/standard_indexed_tree/test/standard_indexed_tree_with_append.ts @@ -12,10 +12,12 @@ export class StandardIndexedTreeWithAppend extends StandardIndexedTree { * @returns Empty promise. * @remarks This method is inefficient and is here mostly for testing. Use batchInsert instead. */ - public async appendLeaves(leaves: Buffer[]): Promise { + public appendLeaves(leaves: Buffer[]): Promise { for (const leaf of leaves) { - await this.appendLeaf(leaf); + this.appendLeaf(leaf); } + + return Promise.resolve(); } private appendEmptyLeaf() { @@ -31,7 +33,7 @@ export class StandardIndexedTreeWithAppend extends StandardIndexedTree { * @param leaf - The leaf to append. * @returns Empty promise. */ - private async appendLeaf(leaf: Buffer): Promise { + private appendLeaf(leaf: Buffer): void { const newLeaf = this.leafFactory.fromBuffer(leaf); // Special case when appending zero @@ -40,13 +42,13 @@ export class StandardIndexedTreeWithAppend extends StandardIndexedTree { return; } - const lowLeafIndex = await this.findIndexOfPreviousKey(newLeaf.getKey(), true); + const lowLeafIndex = this.findIndexOfPreviousKey(newLeaf.getKey(), true); if (lowLeafIndex === undefined) { throw new Error(`Previous leaf not found!`); } const isUpdate = lowLeafIndex.alreadyPresent; - const lowLeafPreimage = (await this.getLatestLeafPreimageCopy(lowLeafIndex.index, true))!; + const lowLeafPreimage = this.getLatestLeafPreimageCopy(lowLeafIndex.index, true)!; const currentSize = this.getNumLeaves(true); if (isUpdate) { @@ -57,7 +59,7 @@ export class StandardIndexedTreeWithAppend extends StandardIndexedTree { lowLeafPreimage.getNextIndex(), ); - await this.updateLeaf(newLowLeafPreimage, BigInt(lowLeafIndex.index)); + this.updateLeaf(newLowLeafPreimage, BigInt(lowLeafIndex.index)); this.appendEmptyLeaf(); } else { const newLeafPreimage = this.leafPreimageFactory.fromLeaf( @@ -72,8 +74,8 @@ export class StandardIndexedTreeWithAppend extends StandardIndexedTree { newLeaf.getKey(), BigInt(currentSize), ); - await this.updateLeaf(newLowLeafPreimage, BigInt(lowLeafIndex.index)); - await this.updateLeaf(newLeafPreimage, currentSize); + this.updateLeaf(newLowLeafPreimage, BigInt(lowLeafIndex.index)); + this.updateLeaf(newLeafPreimage, currentSize); } } } diff --git a/yarn-project/merkle-tree/src/standard_tree/standard_tree.test.ts b/yarn-project/merkle-tree/src/standard_tree/standard_tree.test.ts index 34d8cc3b514..7da886753aa 100644 --- a/yarn-project/merkle-tree/src/standard_tree/standard_tree.test.ts +++ b/yarn-project/merkle-tree/src/standard_tree/standard_tree.test.ts @@ -1,23 +1,21 @@ import { randomBytes } from '@aztec/foundation/crypto'; +import { AztecKVStore, AztecLmdbStore } from '@aztec/kv-store'; import { Hasher } from '@aztec/types/interfaces'; -import { default as levelup } from 'levelup'; - import { loadTree } from '../load_tree.js'; import { newTree } from '../new_tree.js'; import { standardBasedTreeTestSuite } from '../test/standard_based_test_suite.js'; import { treeTestSuite } from '../test/test_suite.js'; -import { createMemDown } from '../test/utils/create_mem_down.js'; import { PedersenWithCounter } from '../test/utils/pedersen_with_counter.js'; import { INITIAL_LEAF } from '../tree_base.js'; import { StandardTree } from './standard_tree.js'; -const createDb = async (levelUp: levelup.LevelUp, hasher: Hasher, name: string, depth: number) => { - return await newTree(StandardTree, levelUp, hasher, name, depth); +const createDb = async (store: AztecKVStore, hasher: Hasher, name: string, depth: number) => { + return await newTree(StandardTree, store, hasher, name, depth); }; -const createFromName = async (levelUp: levelup.LevelUp, hasher: Hasher, name: string) => { - return await loadTree(StandardTree, levelUp, hasher, name); +const createFromName = async (store: AztecKVStore, hasher: Hasher, name: string) => { + return await loadTree(StandardTree, store, hasher, name); }; treeTestSuite('StandardTree', createDb, createFromName); @@ -35,7 +33,7 @@ describe('StandardTree_batchAppend', () => { }); it('correctly computes root when batch appending and calls hash function expected num times', async () => { - const db = levelup(createMemDown()); + const db = await AztecLmdbStore.openTmp(); const tree = await createDb(db, pedersen, 'test', 3); const leaves = Array.from({ length: 5 }, _ => randomBytes(32)); @@ -71,18 +69,18 @@ describe('StandardTree_batchAppend', () => { }); it('should be able to find indexes of leaves', async () => { - const db = levelup(createMemDown()); + const db = await AztecLmdbStore.openTmp(); const tree = await createDb(db, pedersen, 'test', 3); const values = [Buffer.alloc(32, 1), Buffer.alloc(32, 2)]; await tree.appendLeaves([values[0]]); - expect(await tree.findLeafIndex(values[0], true)).toBeDefined(); - expect(await tree.findLeafIndex(values[0], false)).toBe(undefined); - expect(await tree.findLeafIndex(values[1], true)).toBe(undefined); + expect(tree.findLeafIndex(values[0], true)).toBeDefined(); + expect(tree.findLeafIndex(values[0], false)).toBe(undefined); + expect(tree.findLeafIndex(values[1], true)).toBe(undefined); await tree.commit(); - expect(await tree.findLeafIndex(values[0], false)).toBeDefined(); + expect(tree.findLeafIndex(values[0], false)).toBeDefined(); }); }); diff --git a/yarn-project/merkle-tree/src/standard_tree/standard_tree.ts b/yarn-project/merkle-tree/src/standard_tree/standard_tree.ts index cc587ee3a3d..c1dc29cda69 100644 --- a/yarn-project/merkle-tree/src/standard_tree/standard_tree.ts +++ b/yarn-project/merkle-tree/src/standard_tree/standard_tree.ts @@ -1,25 +1,26 @@ import { TreeInsertionStats } from '@aztec/circuit-types/stats'; import { Timer } from '@aztec/foundation/timer'; -import { AppendOnlySnapshotBuilder, TreeSnapshot } from '../index.js'; import { AppendOnlyTree } from '../interfaces/append_only_tree.js'; +import { AppendOnlySnapshotBuilder } from '../snapshots/append_only_snapshot.js'; +import { TreeSnapshot } from '../snapshots/snapshot_builder.js'; import { TreeBase } from '../tree_base.js'; /** * A Merkle tree implementation that uses a LevelDB database to store the tree. */ export class StandardTree extends TreeBase implements AppendOnlyTree { - #snapshotBuilder = new AppendOnlySnapshotBuilder(this.db, this, this.hasher); + #snapshotBuilder = new AppendOnlySnapshotBuilder(this.store, this, this.hasher); /** * Appends the given leaves to the tree. * @param leaves - The leaves to append. * @returns Empty promise. */ - public async appendLeaves(leaves: Buffer[]): Promise { + public appendLeaves(leaves: Buffer[]): Promise { this.hasher.reset(); const timer = new Timer(); - await super.appendLeaves(leaves); + super.appendLeaves(leaves); this.log(`Inserted ${leaves.length} leaves into ${this.getName()} tree`, { eventName: 'tree-insertion', duration: timer.ms(), @@ -29,19 +30,21 @@ export class StandardTree extends TreeBase implements AppendOnlyTree { treeType: 'append-only', ...this.hasher.stats(), } satisfies TreeInsertionStats); + + return Promise.resolve(); } - public snapshot(block: number): Promise { - return this.#snapshotBuilder.snapshot(block); + public snapshot(blockNumber: number): Promise { + return this.#snapshotBuilder.snapshot(blockNumber); } - public getSnapshot(block: number): Promise { - return this.#snapshotBuilder.getSnapshot(block); + public getSnapshot(blockNumber: number): Promise { + return this.#snapshotBuilder.getSnapshot(blockNumber); } - public async findLeafIndex(value: Buffer, includeUncommitted: boolean): Promise { + public findLeafIndex(value: Buffer, includeUncommitted: boolean): bigint | undefined { for (let i = 0n; i < this.getNumLeaves(includeUncommitted); i++) { - const currentValue = await this.getLeafValue(i, includeUncommitted); + const currentValue = this.getLeafValue(i, includeUncommitted); if (currentValue && currentValue.equals(value)) { return i; } diff --git a/yarn-project/merkle-tree/src/test/standard_based_test_suite.ts b/yarn-project/merkle-tree/src/test/standard_based_test_suite.ts index 29881831ee2..bfd95e7b349 100644 --- a/yarn-project/merkle-tree/src/test/standard_based_test_suite.ts +++ b/yarn-project/merkle-tree/src/test/standard_based_test_suite.ts @@ -1,21 +1,20 @@ +import { AztecKVStore, AztecLmdbStore } from '@aztec/kv-store'; import { Hasher } from '@aztec/types/interfaces'; import { SiblingPath } from '@aztec/types/membership'; import { randomBytes } from 'crypto'; -import { default as levelup } from 'levelup'; import { INITIAL_LEAF, Pedersen } from '../index.js'; import { AppendOnlyTree } from '../interfaces/append_only_tree.js'; import { UpdateOnlyTree } from '../interfaces/update_only_tree.js'; import { appendLeaves } from './utils/append_leaves.js'; -import { createMemDown } from './utils/create_mem_down.js'; const TEST_TREE_DEPTH = 2; export const standardBasedTreeTestSuite = ( testName: string, createDb: ( - levelup: levelup.LevelUp, + store: AztecKVStore, hasher: Hasher, name: string, depth: number, @@ -36,21 +35,21 @@ export const standardBasedTreeTestSuite = ( }); it('should have correct empty tree root for depth 32', async () => { - const db = levelup(createMemDown()); + const db = await AztecLmdbStore.openTmp(); const tree = await createDb(db, pedersen, 'test', 32); const root = tree.getRoot(false); expect(root.toString('hex')).toEqual('16642d9ccd8346c403aa4c3fa451178b22534a27035cdaa6ec34ae53b29c50cb'); }); it('should throw when appending beyond max index', async () => { - const db = levelup(createMemDown()); + const db = await AztecLmdbStore.openTmp(); const tree = await createDb(db, pedersen, 'test', 2); const leaves = Array.from({ length: 5 }, _ => randomBytes(32)); await expect(appendLeaves(tree, leaves)).rejects.toThrow(); }); it('should have correct root and sibling paths', async () => { - const db = levelup(createMemDown()); + const db = await AztecLmdbStore.openTmp(); const tree = await createDb(db, pedersen, 'test', 2); const level1ZeroHash = pedersen.hash(INITIAL_LEAF, INITIAL_LEAF); diff --git a/yarn-project/merkle-tree/src/test/test_suite.ts b/yarn-project/merkle-tree/src/test/test_suite.ts index 31ee72a9b0e..fd2d5b038fb 100644 --- a/yarn-project/merkle-tree/src/test/test_suite.ts +++ b/yarn-project/merkle-tree/src/test/test_suite.ts @@ -1,13 +1,11 @@ +import { AztecKVStore, AztecLmdbStore } from '@aztec/kv-store'; import { Hasher } from '@aztec/types/interfaces'; import { SiblingPath } from '@aztec/types/membership'; -import { default as levelup } from 'levelup'; - import { Pedersen } from '../index.js'; import { AppendOnlyTree } from '../interfaces/append_only_tree.js'; import { UpdateOnlyTree } from '../interfaces/update_only_tree.js'; import { appendLeaves } from './utils/append_leaves.js'; -import { createMemDown } from './utils/create_mem_down.js'; const expectSameTrees = async ( tree1: AppendOnlyTree | UpdateOnlyTree, @@ -28,12 +26,12 @@ const expectSameTrees = async ( export const treeTestSuite = ( testName: string, createDb: ( - levelup: levelup.LevelUp, + store: AztecKVStore, hasher: Hasher, name: string, depth: number, ) => Promise, - createFromName: (levelup: levelup.LevelUp, hasher: Hasher, name: string) => Promise, + createFromName: (store: AztecKVStore, hasher: Hasher, name: string) => Promise, ) => { describe(testName, () => { const values: Buffer[] = []; @@ -52,12 +50,10 @@ export const treeTestSuite = ( }); it('should revert changes on rollback', async () => { - const levelDownEmpty = createMemDown(); - const dbEmpty = levelup(levelDownEmpty); + const dbEmpty = await AztecLmdbStore.openTmp(); const emptyTree = await createDb(dbEmpty, pedersen, 'test', 10); - const levelDown = createMemDown(); - const db = levelup(levelDown); + const db = await AztecLmdbStore.openTmp(); const tree = await createDb(db, pedersen, 'test2', 10); await appendLeaves(tree, values.slice(0, 4)); @@ -89,12 +85,10 @@ export const treeTestSuite = ( }); it('should not revert changes after commit', async () => { - const levelDownEmpty = createMemDown(); - const dbEmpty = levelup(levelDownEmpty); + const dbEmpty = await AztecLmdbStore.openTmp(); const emptyTree = await createDb(dbEmpty, pedersen, 'test', 10); - const levelDown = createMemDown(); - const db = levelup(levelDown); + const db = await AztecLmdbStore.openTmp(); const tree = await createDb(db, pedersen, 'test2', 10); await appendLeaves(tree, values.slice(0, 4)); @@ -110,14 +104,12 @@ export const treeTestSuite = ( }); it('should be able to restore from previous committed data', async () => { - const levelDown = createMemDown(); - const db = levelup(levelDown); + const db = await AztecLmdbStore.openTmp(); const tree = await createDb(db, pedersen, 'test', 10); await appendLeaves(tree, values.slice(0, 4)); await tree.commit(); - const db2 = levelup(levelDown); - const tree2 = await createFromName(db2, pedersen, 'test'); + const tree2 = await createFromName(db, pedersen, 'test'); // both committed and uncommitted should be equal to the restored data expect(tree.getRoot(true)).toEqual(tree2.getRoot(true)); @@ -129,7 +121,7 @@ export const treeTestSuite = ( }); it('should throw an error if previous data does not exist for the given name', async () => { - const db = levelup(createMemDown()); + const db = await AztecLmdbStore.openTmp(); await expect( (async () => { await createFromName(db, pedersen, 'a_whole_new_tree'); @@ -138,7 +130,7 @@ export const treeTestSuite = ( }); it('should serialize sibling path data to a buffer and be able to deserialize it back', async () => { - const db = levelup(createMemDown()); + const db = await AztecLmdbStore.openTmp(); const tree = await createDb(db, pedersen, 'test', 10); await appendLeaves(tree, values.slice(0, 1)); diff --git a/yarn-project/merkle-tree/src/test/utils/create_mem_down.ts b/yarn-project/merkle-tree/src/test/utils/create_mem_down.ts deleted file mode 100644 index 0be5ac35604..00000000000 --- a/yarn-project/merkle-tree/src/test/utils/create_mem_down.ts +++ /dev/null @@ -1,3 +0,0 @@ -import { type MemDown, default as memdown } from 'memdown'; - -export const createMemDown = () => (memdown as any)() as MemDown; diff --git a/yarn-project/merkle-tree/src/tree_base.ts b/yarn-project/merkle-tree/src/tree_base.ts index 36f9b12b444..972cdf19cc5 100644 --- a/yarn-project/merkle-tree/src/tree_base.ts +++ b/yarn-project/merkle-tree/src/tree_base.ts @@ -1,10 +1,9 @@ import { toBigIntLE, toBufferLE } from '@aztec/foundation/bigint-buffer'; import { DebugLogger, createDebugLogger } from '@aztec/foundation/log'; +import { AztecKVStore, AztecMap, AztecSingleton } from '@aztec/kv-store'; import { Hasher } from '@aztec/types/interfaces'; import { SiblingPath } from '@aztec/types/membership'; -import { LevelUp, LevelUpChain } from 'levelup'; - import { HasherWithStats } from './hasher_with_stats.js'; import { MerkleTree } from './interfaces/merkle_tree.js'; @@ -17,7 +16,7 @@ const encodeMeta = (root: Buffer, depth: number, size: bigint) => { data.writeUInt32LE(depth, 32); return Buffer.concat([data, toBufferLE(size, 32)]); }; -export const decodeMeta = (meta: Buffer) => { +const decodeMeta = (meta: Buffer) => { const root = meta.subarray(0, 32); const depth = meta.readUInt32LE(32); const size = toBigIntLE(meta.subarray(36)); @@ -28,6 +27,18 @@ export const decodeMeta = (meta: Buffer) => { }; }; +const openTreeMetaSingleton = (store: AztecKVStore, treeName: string): AztecSingleton => + store.openSingleton(`merkle_tree_${treeName}_meta`); + +export const getTreeMeta = (store: AztecKVStore, treeName: string) => { + const singleton = openTreeMetaSingleton(store, treeName); + const val = singleton.get(); + if (!val) { + throw new Error(); + } + return decodeMeta(val); +}; + export const INITIAL_LEAF = Buffer.from('0000000000000000000000000000000000000000000000000000000000000000', 'hex'); /** @@ -40,11 +51,13 @@ export abstract class TreeBase implements MerkleTree { private zeroHashes: Buffer[] = []; private cache: { [key: string]: Buffer } = {}; protected log: DebugLogger; - protected hasher: HasherWithStats; + private nodes: AztecMap; + private meta: AztecSingleton; + public constructor( - protected db: LevelUp, + protected store: AztecKVStore, hasher: Hasher, private name: string, private depth: number, @@ -56,6 +69,8 @@ export abstract class TreeBase implements MerkleTree { } this.hasher = new HasherWithStats(hasher); + this.nodes = store.openMap('merkle_tree_' + name); + this.meta = openTreeMetaSingleton(store, name); // Compute the zero values at each layer. let current = INITIAL_LEAF; @@ -67,7 +82,7 @@ export abstract class TreeBase implements MerkleTree { this.root = root ? root : current; this.maxIndex = 2n ** BigInt(depth) - 1n; - this.log = createDebugLogger(`aztec:merkle-tree:${name}`); + this.log = createDebugLogger(`aztec:merkle-tree:${name.toLowerCase()}`); } /** @@ -111,34 +126,36 @@ export abstract class TreeBase implements MerkleTree { * @returns A sibling path for the element at the given index. * Note: The sibling path is an array of sibling hashes, with the lowest hash (leaf hash) first, and the highest hash last. */ - public async getSiblingPath(index: bigint, includeUncommitted: boolean): Promise> { + public getSiblingPath(index: bigint, includeUncommitted: boolean): Promise> { const path: Buffer[] = []; let level = this.depth; while (level > 0) { const isRight = index & 0x01n; - const sibling = await this.getLatestValueAtIndex(level, isRight ? index - 1n : index + 1n, includeUncommitted); + const sibling = this.getLatestValueAtIndex(level, isRight ? index - 1n : index + 1n, includeUncommitted); path.push(sibling); level -= 1; index >>= 1n; } - return new SiblingPath(this.depth as N, path); + return Promise.resolve(new SiblingPath(this.depth as N, path)); } /** * Commits the changes to the database. * @returns Empty promise. */ - public async commit(): Promise { - const batch = this.db.batch(); - const keys = Object.getOwnPropertyNames(this.cache); - for (const key of keys) { - batch.put(key, this.cache[key]); - } - this.size = this.getNumLeaves(true); - this.root = this.getRoot(true); - await this.writeMeta(batch); - await batch.write(); - this.clearCache(); + public commit(): Promise { + return this.store.transaction(() => { + const keys = Object.getOwnPropertyNames(this.cache); + for (const key of keys) { + void this.nodes.set(key, this.cache[key]); + } + this.size = this.getNumLeaves(true); + this.root = this.getRoot(true); + + this.clearCache(); + + void this.writeMeta(); + }); } /** @@ -156,11 +173,11 @@ export abstract class TreeBase implements MerkleTree { * @param includeUncommitted - Indicates whether to include uncommitted changes. * @returns Leaf value at the given index or undefined. */ - public getLeafValue(index: bigint, includeUncommitted: boolean): Promise { + public getLeafValue(index: bigint, includeUncommitted: boolean): Buffer | undefined { return this.getLatestValueAtIndex(this.depth, index, includeUncommitted); } - public getNode(level: number, index: bigint): Promise { + public getNode(level: number, index: bigint): Buffer | undefined { if (level < 0 || level > this.depth) { throw Error('Invalid level: ' + level); } @@ -193,14 +210,14 @@ export abstract class TreeBase implements MerkleTree { * @param leaf - Leaf to add to cache. * @param index - Index of the leaf (used to derive the cache key). */ - protected async addLeafToCacheAndHashToRoot(leaf: Buffer, index: bigint) { + protected addLeafToCacheAndHashToRoot(leaf: Buffer, index: bigint) { const key = indexToKeyHash(this.name, this.depth, index); let current = leaf; this.cache[key] = current; let level = this.depth; while (level > 0) { const isRight = index & 0x01n; - const sibling = await this.getLatestValueAtIndex(level, isRight ? index - 1n : index + 1n, true); + const sibling = this.getLatestValueAtIndex(level, isRight ? index - 1n : index + 1n, true); const lhs = isRight ? sibling : current; const rhs = isRight ? current : sibling; current = this.hasher.hash(lhs, rhs); @@ -219,12 +236,12 @@ export abstract class TreeBase implements MerkleTree { * @returns The latest value at the given index. * Note: If the value is not in the cache, it will be fetched from the database. */ - private async getLatestValueAtIndex(level: number, index: bigint, includeUncommitted: boolean): Promise { + private getLatestValueAtIndex(level: number, index: bigint, includeUncommitted: boolean): Buffer { const key = indexToKeyHash(this.name, level, index); if (includeUncommitted && this.cache[key] !== undefined) { return this.cache[key]; } - const committed = await this.dbGet(key); + const committed = this.dbGet(key); if (committed !== undefined) { return committed; } @@ -236,8 +253,8 @@ export abstract class TreeBase implements MerkleTree { * @param key - The key to by which to get the value. * @returns A value from the db based on the key. */ - private async dbGet(key: string): Promise { - return await this.db.get(key).catch(() => {}); + private dbGet(key: string): Buffer | undefined { + return this.nodes.get(key); } /** @@ -255,13 +272,9 @@ export abstract class TreeBase implements MerkleTree { * Writes meta data to the provided batch. * @param batch - The batch to which to write the meta data. */ - protected async writeMeta(batch?: LevelUpChain) { + protected writeMeta() { const data = encodeMeta(this.getRoot(true), this.depth, this.getNumLeaves(true)); - if (batch) { - batch.put(this.name, data); - } else { - await this.db.put(this.name, data); - } + return this.meta.set(data); } /** @@ -279,7 +292,7 @@ export abstract class TreeBase implements MerkleTree { * `getLatestValueAtIndex` will return a value from cache (because at least one of the 2 children was * touched in previous iteration). */ - protected async appendLeaves(leaves: Buffer[]): Promise { + protected appendLeaves(leaves: Buffer[]): void { const numLeaves = this.getNumLeaves(true); if (numLeaves + BigInt(leaves.length) - 1n > this.maxIndex) { throw Error(`Can't append beyond max index. Max index: ${this.maxIndex}`); @@ -300,8 +313,8 @@ export abstract class TreeBase implements MerkleTree { lastIndex >>= 1n; // 3.Iterate over all the affected nodes at this level and update them for (let index = firstIndex; index <= lastIndex; index++) { - const lhs = await this.getLatestValueAtIndex(level, index * 2n, true); - const rhs = await this.getLatestValueAtIndex(level, index * 2n + 1n, true); + const lhs = this.getLatestValueAtIndex(level, index * 2n, true); + const rhs = this.getLatestValueAtIndex(level, index * 2n + 1n, true); const cacheKey = indexToKeyHash(this.name, level - 1, index); this.cache[cacheKey] = this.hasher.hash(lhs, rhs); } @@ -317,5 +330,5 @@ export abstract class TreeBase implements MerkleTree { * @param includeUncommitted - Indicates whether to include uncommitted data. * @returns The index of the first leaf found with a given value (undefined if not found). */ - abstract findLeafIndex(value: Buffer, includeUncommitted: boolean): Promise; + abstract findLeafIndex(value: Buffer, includeUncommitted: boolean): bigint | undefined; } diff --git a/yarn-project/merkle-tree/tsconfig.json b/yarn-project/merkle-tree/tsconfig.json index 413464d606d..631083e52f4 100644 --- a/yarn-project/merkle-tree/tsconfig.json +++ b/yarn-project/merkle-tree/tsconfig.json @@ -12,6 +12,9 @@ { "path": "../foundation" }, + { + "path": "../kv-store" + }, { "path": "../types" }, 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 ab40a9b78e8..ee3513d6b67 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 @@ -3,16 +3,10 @@ import { LogFn } from '@aztec/foundation/log'; import { Command } from 'commander'; import { dirname } from 'path'; -/** - * - */ export function addNoirCompilerCommanderActions(program: Command, log: LogFn = () => {}) { addCodegenCommanderAction(program, log); } -/** - * - */ export function addCodegenCommanderAction(program: Command, _: LogFn = () => {}) { program .command('codegen') diff --git a/yarn-project/noir-compiler/src/contract-interface-gen/typescript.ts b/yarn-project/noir-compiler/src/contract-interface-gen/typescript.ts index 90b622b6115..1ac524d358e 100644 --- a/yarn-project/noir-compiler/src/contract-interface-gen/typescript.ts +++ b/yarn-project/noir-compiler/src/contract-interface-gen/typescript.ts @@ -98,11 +98,10 @@ function generateDeploy(input: ContractArtifact) { function generateConstructor(name: string) { return ` private constructor( - completeAddress: CompleteAddress, + instance: ContractInstanceWithAddress, wallet: Wallet, - portalContract = EthAddress.ZERO ) { - super(completeAddress, ${name}ContractArtifact, wallet, portalContract); + super(instance, ${name}ContractArtifact, wallet); } `; } @@ -185,6 +184,7 @@ import { ContractArtifact, ContractBase, ContractFunctionInteraction, + ContractInstanceWithAddress, ContractMethod, DeployMethod, EthAddress, diff --git a/yarn-project/noir-contracts/Nargo.toml b/yarn-project/noir-contracts/Nargo.toml index 1b980b3dad3..4c919e77911 100644 --- a/yarn-project/noir-contracts/Nargo.toml +++ b/yarn-project/noir-contracts/Nargo.toml @@ -1,8 +1,9 @@ [workspace] members = [ - "contracts/benchmarking_contract", - "contracts/card_game_contract", - "contracts/child_contract", + "contracts/avm_test_contract", + "contracts/benchmarking_contract", + "contracts/card_game_contract", + "contracts/child_contract", "contracts/counter_contract", "contracts/docs_example_contract", "contracts/easy_private_token_contract", diff --git a/yarn-project/noir-contracts/contracts/avm_test_contract/Nargo.toml b/yarn-project/noir-contracts/contracts/avm_test_contract/Nargo.toml new file mode 100644 index 00000000000..f3d8583d433 --- /dev/null +++ b/yarn-project/noir-contracts/contracts/avm_test_contract/Nargo.toml @@ -0,0 +1,8 @@ +[package] +name = "avm_test_contract" +authors = [""] +compiler_version = ">=0.18.0" +type = "contract" + +[dependencies] +aztec = { path = "../../../aztec-nr/aztec" } diff --git a/yarn-project/noir-contracts/contracts/avm_test_contract/src/main.nr b/yarn-project/noir-contracts/contracts/avm_test_contract/src/main.nr new file mode 100644 index 00000000000..00e35dfe6ba --- /dev/null +++ b/yarn-project/noir-contracts/contracts/avm_test_contract/src/main.nr @@ -0,0 +1,26 @@ +// + +contract AvmTest { + // Libs + use dep::aztec::protocol_types::{ + address::AztecAddress, + }; + + #[aztec(private)] + fn constructor() {} + + // Function name prefix "avm_" flags it for transpilation + unconstrained fn avm_addArgsReturn(argA: Field, argB: Field) -> pub Field { + argA + argB + } + + // Function required for all contracts + unconstrained fn compute_note_hash_and_nullifier( + _contract_address: AztecAddress, + _nonce: Field, + _storage_slot: Field, + _serialized_note: [Field; 1] + ) -> pub [Field; 4] { + [0, 0, 0, 0] + } +} diff --git a/yarn-project/noir-contracts/contracts/benchmarking_contract/src/main.nr b/yarn-project/noir-contracts/contracts/benchmarking_contract/src/main.nr index 2daab6e3ed6..8b9bd1df476 100644 --- a/yarn-project/noir-contracts/contracts/benchmarking_contract/src/main.nr +++ b/yarn-project/noir-contracts/contracts/benchmarking_contract/src/main.nr @@ -7,31 +7,31 @@ contract Benchmarking { use dep::value_note::{ utils::{increment, decrement}, - value_note::{VALUE_NOTE_LEN, ValueNote, ValueNoteMethods}, + value_note::{VALUE_NOTE_LEN, ValueNote}, }; use dep::aztec::{ protocol_types::{ abis::function_selector::FunctionSelector, address::AztecAddress, + type_serialization::FIELD_SERIALIZED_LEN }, context::{Context}, note::{utils as note_utils, note_getter_options::NoteGetterOptions, note_header::NoteHeader}, log::emit_unencrypted_log, state_vars::{map::Map, public_state::PublicState, set::Set}, - types::type_serialization::field_serialization::{FieldSerializationMethods, FIELD_SERIALIZED_LEN}, }; struct Storage { - notes: Map>, - balances: Map>, + notes: Map>, + balances: Map>, } impl Storage { - fn init(context: Context) -> pub Self { + fn init(context: Context) -> Self { Storage { - notes: Map::new(context, 1, |context, slot| { Set::new(context, slot, ValueNoteMethods) }), - balances: Map::new(context, 2, |context, slot| { PublicState::new(context, slot, FieldSerializationMethods) }), + notes: Map::new(context, 1, |context, slot| { Set::new(context, slot) }), + balances: Map::new(context, 2, |context, slot| { PublicState::new(context, slot) }), } } } @@ -85,6 +85,6 @@ contract Benchmarking { serialized_note: [Field; VALUE_NOTE_LEN] ) -> pub [Field; 4] { let note_header = NoteHeader::new(contract_address, nonce, storage_slot); - note_utils::compute_note_hash_and_nullifier(ValueNoteMethods, note_header, serialized_note) + note_utils::compute_note_hash_and_nullifier(ValueNote::deserialize, note_header, serialized_note) } } diff --git a/yarn-project/noir-contracts/contracts/card_game_contract/src/cards.nr b/yarn-project/noir-contracts/contracts/card_game_contract/src/cards.nr index b0b17ef7ac1..9569a41b774 100644 --- a/yarn-project/noir-contracts/contracts/card_game_contract/src/cards.nr +++ b/yarn-project/noir-contracts/contracts/card_game_contract/src/cards.nr @@ -19,7 +19,7 @@ use dep::std::{ option::Option, }; use dep::value_note::{ - value_note::{ValueNote, ValueNoteMethods, VALUE_NOTE_LEN}, + value_note::{ValueNote, VALUE_NOTE_LEN}, }; struct Card { @@ -88,7 +88,7 @@ impl CardNote { } struct Deck { - set: Set, + set: Set, } pub fn filter_cards( @@ -125,7 +125,6 @@ impl Deck { let set = Set { context, storage_slot, - note_interface: ValueNoteMethods, }; Deck { set diff --git a/yarn-project/noir-contracts/contracts/card_game_contract/src/game.nr b/yarn-project/noir-contracts/contracts/card_game_contract/src/game.nr index 63b50bcf783..4e9c75a77f7 100644 --- a/yarn-project/noir-contracts/contracts/card_game_contract/src/game.nr +++ b/yarn-project/noir-contracts/contracts/card_game_contract/src/game.nr @@ -1,5 +1,7 @@ -use dep::aztec::protocol_types::address::AztecAddress; -use dep::aztec::types::type_serialization::TypeSerializationInterface; +use dep::aztec::protocol_types::{ + address::AztecAddress, + traits::{Serialize, Deserialize}, +}; use crate::cards::Card; global NUMBER_OF_PLAYERS = 2; @@ -31,59 +33,59 @@ struct Game { global GAME_SERIALIZED_LEN: Field = 15; -fn deserializeGame(fields: [Field; GAME_SERIALIZED_LEN]) -> Game { - let players = [ - PlayerEntry { - address: AztecAddress::from_field(fields[0]), - deck_strength: fields[1] as u32, - points: fields[2] as u120 - }, - PlayerEntry { - address: AztecAddress::from_field(fields[3]), - deck_strength: fields[4] as u32, - points: fields[5] as u120 - } - ]; - let rounds_cards = [ - Card::from_field(fields[6]), Card::from_field(fields[7]), - Card::from_field(fields[8]), Card::from_field(fields[9]) - ]; - Game { - players, - rounds_cards, - started: fields[10] as bool, - finished: fields[11] as bool, - claimed: fields[12] as bool, - current_player: fields[13] as u32, - current_round: fields[14] as u32 +impl Serialize for Game { + fn serialize(game: Game) -> [Field; GAME_SERIALIZED_LEN] { + [ + game.players[0].address.to_field(), + game.players[0].deck_strength as Field, + game.players[0].points as Field, + game.players[1].address.to_field(), + game.players[1].deck_strength as Field, + game.players[1].points as Field, + game.rounds_cards[0].to_field(), + game.rounds_cards[1].to_field(), + game.rounds_cards[2].to_field(), + game.rounds_cards[3].to_field(), + game.started as Field, + game.finished as Field, + game.claimed as Field, + game.current_player as Field, + game.current_round as Field + ] } } -fn serializeGame(game: Game) -> [Field; GAME_SERIALIZED_LEN] { - [ - game.players[0].address.to_field(), - game.players[0].deck_strength as Field, - game.players[0].points as Field, - game.players[1].address.to_field(), - game.players[1].deck_strength as Field, - game.players[1].points as Field, - game.rounds_cards[0].to_field(), - game.rounds_cards[1].to_field(), - game.rounds_cards[2].to_field(), - game.rounds_cards[3].to_field(), - game.started as Field, - game.finished as Field, - game.claimed as Field, - game.current_player as Field, - game.current_round as Field - ] +impl Deserialize for Game { + fn deserialize(fields: [Field; GAME_SERIALIZED_LEN]) -> Game { + let players = [ + PlayerEntry { + address: AztecAddress::from_field(fields[0]), + deck_strength: fields[1] as u32, + points: fields[2] as u120 + }, + PlayerEntry { + address: AztecAddress::from_field(fields[3]), + deck_strength: fields[4] as u32, + points: fields[5] as u120 + } + ]; + let rounds_cards = [ + Card::from_field(fields[6]), Card::from_field(fields[7]), + Card::from_field(fields[8]), Card::from_field(fields[9]) + ]; + Game { + players, + rounds_cards, + started: fields[10] as bool, + finished: fields[11] as bool, + claimed: fields[12] as bool, + current_player: fields[13] as u32, + current_round: fields[14] as u32 + } + } } impl Game { - pub fn serialize(self: Self) -> [Field; GAME_SERIALIZED_LEN] { - serializeGame(self) - } - pub fn add_player(&mut self, player_entry: PlayerEntry) -> bool { let mut added = false; @@ -166,8 +168,3 @@ impl Game { } } } - -global GameSerializationMethods = TypeSerializationInterface { - deserialize: deserializeGame, - serialize: serializeGame, -}; diff --git a/yarn-project/noir-contracts/contracts/card_game_contract/src/main.nr b/yarn-project/noir-contracts/contracts/card_game_contract/src/main.nr index 4aacb388d9a..df143229104 100644 --- a/yarn-project/noir-contracts/contracts/card_game_contract/src/main.nr +++ b/yarn-project/noir-contracts/contracts/card_game_contract/src/main.nr @@ -20,7 +20,7 @@ contract CardGame { use dep::value_note::{ balance_utils, value_note::{ - ValueNoteMethods, + ValueNote, VALUE_NOTE_LEN, }, }; @@ -49,14 +49,13 @@ contract CardGame { PLAYABLE_CARDS, PlayerEntry, Game, - GameSerializationMethods, GAME_SERIALIZED_LEN }; struct Storage { collections: Map, game_decks: Map>, - games: Map>, + games: Map>, } impl Storage { @@ -96,8 +95,7 @@ contract CardGame { |context, slot| { PublicState::new( context, - slot, - GameSerializationMethods, + slot ) }, ) @@ -245,6 +243,6 @@ contract CardGame { serialized_note: [Field; VALUE_NOTE_LEN] ) -> pub [Field; 4] { let note_header = NoteHeader::new(contract_address, nonce, storage_slot); - note_utils::compute_note_hash_and_nullifier(ValueNoteMethods, note_header, serialized_note) + note_utils::compute_note_hash_and_nullifier(ValueNote::deserialize, note_header, serialized_note) } } diff --git a/yarn-project/noir-contracts/contracts/child_contract/src/main.nr b/yarn-project/noir-contracts/contracts/child_contract/src/main.nr index 49caabe92cf..ee7213c4687 100644 --- a/yarn-project/noir-contracts/contracts/child_contract/src/main.nr +++ b/yarn-project/noir-contracts/contracts/child_contract/src/main.nr @@ -7,7 +7,6 @@ contract Child { context::{PrivateContext, PublicContext, Context}, log::emit_unencrypted_log, state_vars::public_state::PublicState, - types::type_serialization::field_serialization::{FieldSerializationMethods, FIELD_SERIALIZED_LEN}, }; use dep::aztec::protocol_types::{ abis::function_selector::FunctionSelector, @@ -15,7 +14,7 @@ contract Child { }; struct Storage { - current_value: PublicState, + current_value: PublicState, } impl Storage { @@ -24,7 +23,6 @@ contract Child { current_value: PublicState::new( context, 1, - FieldSerializationMethods, ), } } diff --git a/yarn-project/noir-contracts/contracts/counter_contract/src/main.nr b/yarn-project/noir-contracts/contracts/counter_contract/src/main.nr index 07558511816..e424fbda693 100644 --- a/yarn-project/noir-contracts/contracts/counter_contract/src/main.nr +++ b/yarn-project/noir-contracts/contracts/counter_contract/src/main.nr @@ -12,7 +12,7 @@ contract Counter { use dep::value_note::{ balance_utils, value_note::{ - ValueNoteMethods, + ValueNote, VALUE_NOTE_LEN, }, }; @@ -74,7 +74,7 @@ contract Counter { serialized_note: [Field; VALUE_NOTE_LEN] ) -> pub [Field; 4] { let note_header = NoteHeader::new(contract_address, nonce, storage_slot); - note_utils::compute_note_hash_and_nullifier(ValueNoteMethods, note_header, serialized_note) + note_utils::compute_note_hash_and_nullifier(ValueNote::deserialize, note_header, serialized_note) } // docs:end:nullifier } diff --git a/yarn-project/noir-contracts/contracts/docs_example_contract/src/main.nr b/yarn-project/noir-contracts/contracts/docs_example_contract/src/main.nr index 6662f6de2f0..ee28961aefd 100644 --- a/yarn-project/noir-contracts/contracts/docs_example_contract/src/main.nr +++ b/yarn-project/noir-contracts/contracts/docs_example_contract/src/main.nr @@ -20,28 +20,35 @@ contract DocsExample { use dep::aztec::{ note::{ note_header::NoteHeader, + note_getter_options::{NoteGetterOptions, Comparator}, + note_viewer_options::{NoteViewerOptions}, utils as note_utils, }, context::{PrivateContext, PublicContext, Context}, - state_vars::{map::Map, public_state::PublicState,singleton::Singleton}, + state_vars::{map::Map, public_state::PublicState,singleton::Singleton, immutable_singleton::ImmutableSingleton, set::Set, stable_public_state::StablePublicState}, }; // how to import methods from other files/folders within your workspace use crate::options::create_account_card_getter_options; use crate::types::{ - card_note::{CardNote, CardNoteMethods, CARD_NOTE_LEN}, - leader::{Leader, LeaderSerializationMethods, LEADER_SERIALIZED_LEN}, + card_note::{CardNote, CARD_NOTE_LEN}, + leader::Leader, }; struct Storage { // Shows how to create a custom struct in Public - leader: PublicState, + leader: PublicState, // docs:start:storage-singleton-declaration - legendary_card: Singleton, + legendary_card: Singleton, // docs:end:storage-singleton-declaration // just used for docs example to show how to create a singleton map. // docs:start:storage-map-singleton-declaration - profiles: Map>, + profiles: Map>, // docs:end:storage-map-singleton-declaration + test: Set, + imm_singleton: ImmutableSingleton, + // docs:start:start_vars_stable + stable_value: StablePublicState, + // docs:end:start_vars_stable } impl Storage { @@ -49,22 +56,24 @@ contract DocsExample { Storage { leader: PublicState::new( context, - 1, - LeaderSerializationMethods, + 1 ), // docs:start:start_vars_singleton - legendary_card: Singleton::new(context, 2, CardNoteMethods), + legendary_card: Singleton::new(context, 3), // docs:end:start_vars_singleton // just used for docs example (not for game play): // docs:start:state_vars-MapSingleton profiles: Map::new( context, - 3, + 4, |context, slot| { - Singleton::new(context, slot, CardNoteMethods) + Singleton::new(context, slot) }, ), // docs:end:state_vars-MapSingleton + test: Set::new(context, 5), + imm_singleton: ImmutableSingleton::new(context, 6), + stable_value: StablePublicState::new(context, 7), } } } @@ -72,25 +81,86 @@ contract DocsExample { #[aztec(private)] fn constructor() {} + #[aztec(public)] + fn initialize_stable(points: u8) { + let mut new_leader = Leader { account: context.msg_sender(), points }; + storage.stable_value.initialize(new_leader); + } + + #[aztec(private)] + fn match_stable(account: AztecAddress, points: u8) { + let expected = Leader { account, points }; + let read = storage.stable_value.read_private(); + + assert(read.account == expected.account, "Invalid account"); + assert(read.points == expected.points, "Invalid points"); + } + + unconstrained fn get_stable() -> pub Leader { + storage.stable_value.read_public() + } + + #[aztec(private)] + fn initialize_immutable_singleton(randomness: Field, points: u8) { + let mut new_card = CardNote::new(points, randomness, context.msg_sender()); + storage.imm_singleton.initialize(&mut new_card, true); + } + #[aztec(private)] // msg_sender() is 0 at deploy time. So created another function fn initialize_private(randomness: Field, points: u8) { let mut legendary_card = CardNote::new(points, randomness, context.msg_sender()); // create and broadcast note - storage.legendary_card.initialize(&mut legendary_card, Option::none(), true); + storage.legendary_card.initialize(&mut legendary_card, true); + } + + #[aztec(private)] + fn insert_notes(amounts: [u8; 10]) { + for i in 0..amounts.len() { + let mut note = CardNote::new(amounts[i], 1, context.msg_sender()); + storage.test.insert(&mut note, true); + } + } + + #[aztec(private)] + fn insert_note(amount: u8, randomness: Field) { + let mut note = CardNote::new(amount, randomness, context.msg_sender()); + storage.test.insert(&mut note, true); + } + + // docs:start:state_vars-NoteGetterOptionsComparatorExampleNoir + unconstrained fn read_note(amount: Field, comparator: u3) -> pub [Option; 10] { + let options = NoteViewerOptions::new().select(0, amount, Option::some(comparator)); + let notes = storage.test.view_notes(options); + + notes } + // docs:end:state_vars-NoteGetterOptionsComparatorExampleNoir #[aztec(private)] fn update_legendary_card(randomness: Field, points: u8) { + let mut new_card = CardNote::new(points, randomness, context.msg_sender()); + storage.legendary_card.replace(&mut new_card, true); + + context.call_public_function( + context.this_address(), + FunctionSelector::from_signature("update_leader((Field),u8)"), + [context.msg_sender().to_field(), points as Field] + ); + } + + #[aztec(private)] + fn increase_legendary_points() { // Ensure `points` > current value // Also serves as a e2e test that you can `get_note()` and then `replace()` // docs:start:state_vars-SingletonGet - let card = storage.legendary_card.get_note(true); + let card = storage.legendary_card.get_note(false); // docs:end:state_vars-SingletonGet - assert(points > card.points, "can only update to higher value"); - let mut new_card = CardNote::new(points, randomness, context.msg_sender()); + let points = card.points + 1; + + let mut new_card = CardNote::new(points, card.randomness, context.msg_sender()); // docs:start:state_vars-SingletonReplace storage.legendary_card.replace(&mut new_card, true); // docs:end:state_vars-SingletonReplace @@ -112,6 +182,24 @@ contract DocsExample { storage.leader.read() } + unconstrained fn get_legendary_card() -> pub CardNote { + storage.legendary_card.view_note() + } + + // docs:start:singleton_is_initialized + unconstrained fn is_legendary_initialized() -> pub bool { + storage.legendary_card.is_initialized() + } + // docs:end:singleton_is_initialized + + unconstrained fn get_imm_card() -> pub CardNote { + storage.imm_singleton.view_note() + } + + unconstrained fn is_imm_initialized() -> pub bool { + storage.imm_singleton.is_initialized() + } + // TODO: remove this placeholder once https://github.com/AztecProtocol/aztec-packages/issues/2918 is implemented unconstrained fn compute_note_hash_and_nullifier( contract_address: AztecAddress, @@ -120,7 +208,7 @@ contract DocsExample { serialized_note: [Field; CARD_NOTE_LEN] ) -> pub [Field; 4] { let note_header = NoteHeader::new(contract_address, nonce, storage_slot); - note_utils::compute_note_hash_and_nullifier(CardNoteMethods, note_header, serialized_note) + note_utils::compute_note_hash_and_nullifier(CardNote::deserialize, note_header, serialized_note) } /// Macro equivalence section diff --git a/yarn-project/noir-contracts/contracts/docs_example_contract/src/options.nr b/yarn-project/noir-contracts/contracts/docs_example_contract/src/options.nr index 9742345f8ca..9f1173f7588 100644 --- a/yarn-project/noir-contracts/contracts/docs_example_contract/src/options.nr +++ b/yarn-project/noir-contracts/contracts/docs_example_contract/src/options.nr @@ -13,7 +13,7 @@ pub fn create_account_card_getter_options( account: AztecAddress, offset: u32 ) -> NoteGetterOptions { - NoteGetterOptions::new().select(2, account.to_field()).sort(0, SortOrder.DESC).set_offset(offset) + NoteGetterOptions::new().select(2, account.to_field(), Option::none()).sort(0, SortOrder.DESC).set_offset(offset) } // docs:end:state_vars-NoteGetterOptionsSelectSortOffset @@ -23,7 +23,7 @@ pub fn create_exact_card_getter_options( secret: Field, account: AztecAddress ) -> NoteGetterOptions { - NoteGetterOptions::new().select(0, points as Field).select(1, secret).select(2, account.to_field()) + NoteGetterOptions::new().select(0, points as Field, Option::none()).select(1, secret, Option::none()).select(2, account.to_field(), Option::none()) } // docs:end:state_vars-NoteGetterOptionsMultiSelects @@ -46,12 +46,12 @@ pub fn filter_min_points( // docs:start:state_vars-NoteGetterOptionsFilter pub fn create_account_cards_with_min_points_getter_options(account: AztecAddress, min_points: u8) -> NoteGetterOptions { - NoteGetterOptions::with_filter(filter_min_points, min_points).select(2, account.to_field()).sort(0, SortOrder.ASC) + NoteGetterOptions::with_filter(filter_min_points, min_points).select(2, account.to_field(), Option::none()).sort(0, SortOrder.ASC) } // docs:end:state_vars-NoteGetterOptionsFilter // docs:start:state_vars-NoteGetterOptionsPickOne pub fn create_largest_account_card_getter_options(account: AztecAddress) -> NoteGetterOptions { - NoteGetterOptions::new().select(2, account.to_field()).sort(0, SortOrder.DESC).set_limit(1) + NoteGetterOptions::new().select(2, account.to_field(), Option::none()).sort(0, SortOrder.DESC).set_limit(1) } // docs:end:state_vars-NoteGetterOptionsPickOne diff --git a/yarn-project/noir-contracts/contracts/docs_example_contract/src/types/card_note.nr b/yarn-project/noir-contracts/contracts/docs_example_contract/src/types/card_note.nr index f185518c412..912bb81e568 100644 --- a/yarn-project/noir-contracts/contracts/docs_example_contract/src/types/card_note.nr +++ b/yarn-project/noir-contracts/contracts/docs_example_contract/src/types/card_note.nr @@ -1,9 +1,8 @@ -use dep::aztec::protocol_types::address::AztecAddress; use dep::aztec::{ note::{ note_header::NoteHeader, note_interface::NoteInterface, - utils::compute_note_hash_for_read_or_nullify, + utils::compute_note_hash_for_consumption, }, oracle::{ nullifier_key::get_nullifier_secret_key, @@ -12,6 +11,10 @@ use dep::aztec::{ log::emit_encrypted_log, hash::pedersen_hash, context::PrivateContext, + protocol_types::{ + address::AztecAddress, + traits::{Serialize, Deserialize, Empty} + } }; // Shows how to create a custom note @@ -36,12 +39,16 @@ impl CardNote { header: NoteHeader::empty(), } } +} - pub fn serialize(self) -> [Field; CARD_NOTE_LEN] { +impl Serialize for CardNote { + fn serialize(self) -> [Field; CARD_NOTE_LEN] { [self.points as Field, self.randomness, self.owner.to_field()] } +} - pub fn deserialize(serialized_note: [Field; CARD_NOTE_LEN]) -> Self { +impl Deserialize for CardNote { + fn deserialize(serialized_note: [Field; CARD_NOTE_LEN]) -> Self { CardNote { points: serialized_note[0] as u8, randomness: serialized_note[1], @@ -49,17 +56,15 @@ impl CardNote { header: NoteHeader::empty(), } } +} - pub fn compute_note_hash(self) -> Field { - pedersen_hash([ - self.points as Field, - self.randomness, - self.owner.to_field(), - ],0) +impl NoteInterface for CardNote { + fn compute_note_content_hash(self) -> Field { + pedersen_hash(self.serialize(), 0) } - pub fn compute_nullifier(self, context: &mut PrivateContext) -> Field { - let note_hash_for_nullify = compute_note_hash_for_read_or_nullify(CardNoteMethods, self); + fn compute_nullifier(self, context: &mut PrivateContext) -> Field { + let note_hash_for_nullify = compute_note_hash_for_consumption(self); let secret = context.request_nullifier_secret_key(self.owner); pedersen_hash([ note_hash_for_nullify, @@ -68,8 +73,8 @@ impl CardNote { ],0) } - pub fn compute_nullifier_without_context(self) -> Field { - let note_hash_for_nullify = compute_note_hash_for_read_or_nullify(CardNoteMethods, self); + fn compute_nullifier_without_context(self) -> Field { + let note_hash_for_nullify = compute_note_hash_for_consumption(self); let secret = get_nullifier_secret_key(self.owner); pedersen_hash([ note_hash_for_nullify, @@ -78,12 +83,16 @@ impl CardNote { ],0) } - pub fn set_header(&mut self, header: NoteHeader) { + fn set_header(&mut self, header: NoteHeader) { self.header = header; } + fn get_header(note: CardNote) -> NoteHeader { + note.header + } + // Broadcasts the note as an encrypted log on L1. - pub fn broadcast(self, context: &mut PrivateContext, slot: Field) { + fn broadcast(self, context: &mut PrivateContext, slot: Field) { let encryption_pub_key = get_public_key(self.owner); emit_encrypted_log( context, @@ -94,47 +103,3 @@ impl CardNote { ); } } - -fn deserialize(serialized_note: [Field; CARD_NOTE_LEN]) -> CardNote { - CardNote::deserialize(serialized_note) -} - -fn serialize(note: CardNote) -> [Field; CARD_NOTE_LEN] { - note.serialize() -} - -fn compute_note_hash(note: CardNote) -> Field { - note.compute_note_hash() -} - -fn compute_nullifier(note: CardNote, context: &mut PrivateContext) -> Field { - note.compute_nullifier(context) -} - -fn compute_nullifier_without_context(note: CardNote) -> Field { - note.compute_nullifier_without_context() -} - -fn get_header(note: CardNote) -> NoteHeader { - note.header -} - -fn set_header(note: &mut CardNote, header: NoteHeader) { - note.set_header(header) -} - -// Broadcasts the note as an encrypted log on L1. -fn broadcast(context: &mut PrivateContext, slot: Field, note: CardNote) { - note.broadcast(context, slot); -} - -global CardNoteMethods = NoteInterface { - deserialize, - serialize, - compute_note_hash, - compute_nullifier, - compute_nullifier_without_context, - get_header, - set_header, - broadcast, -}; diff --git a/yarn-project/noir-contracts/contracts/docs_example_contract/src/types/leader.nr b/yarn-project/noir-contracts/contracts/docs_example_contract/src/types/leader.nr index ca034261ec0..7be0ef86dbf 100644 --- a/yarn-project/noir-contracts/contracts/docs_example_contract/src/types/leader.nr +++ b/yarn-project/noir-contracts/contracts/docs_example_contract/src/types/leader.nr @@ -1,5 +1,7 @@ -use dep::aztec::protocol_types::address::AztecAddress; -use dep::aztec::types::type_serialization::TypeSerializationInterface; +use dep::aztec::protocol_types::{ + address::AztecAddress, + traits::{Serialize, Deserialize} +}; // Shows how to create a custom struct in Public struct Leader { @@ -9,15 +11,15 @@ struct Leader { global LEADER_SERIALIZED_LEN: Field = 2; -fn deserialize(fields: [Field; LEADER_SERIALIZED_LEN]) -> Leader { - Leader { account: AztecAddress::from_field(fields[0]), points: fields[1] as u8 } +impl Deserialize for Leader { + fn deserialize(fields: [Field; LEADER_SERIALIZED_LEN]) -> Self { + Leader { account: AztecAddress::from_field(fields[0]), points: fields[1] as u8 } + } } -fn serialize(leader: Leader) -> [Field; LEADER_SERIALIZED_LEN] { - [leader.account.to_field(), leader.points as Field] +impl Serialize for Leader { + fn serialize(self) -> [Field; LEADER_SERIALIZED_LEN] { + [self.account.to_field(), self.points as Field] + } } -global LeaderSerializationMethods = TypeSerializationInterface { - deserialize, - serialize, -}; diff --git a/yarn-project/noir-contracts/contracts/easy_private_token_contract/src/main.nr b/yarn-project/noir-contracts/contracts/easy_private_token_contract/src/main.nr index 2a342aa3e88..c43aae116ea 100644 --- a/yarn-project/noir-contracts/contracts/easy_private_token_contract/src/main.nr +++ b/yarn-project/noir-contracts/contracts/easy_private_token_contract/src/main.nr @@ -13,7 +13,7 @@ contract EasyPrivateToken { use dep::value_note::{ balance_utils, value_note::{ - ValueNoteMethods, + ValueNote, VALUE_NOTE_LEN, }, }; @@ -24,7 +24,7 @@ contract EasyPrivateToken { } impl Storage { - fn init(context: Context) -> pub Self { + fn init(context: Context) -> Self { Storage { balances: Map::new( context, @@ -82,7 +82,7 @@ contract EasyPrivateToken { serialized_note: [Field; VALUE_NOTE_LEN] ) -> pub [Field; 4] { let note_header = NoteHeader::new(contract_address, nonce, storage_slot); - note_utils::compute_note_hash_and_nullifier(ValueNoteMethods, note_header, serialized_note) + note_utils::compute_note_hash_and_nullifier(ValueNote::deserialize, note_header, serialized_note) } } // docs:end:easy_private_token_contract diff --git a/yarn-project/noir-contracts/contracts/easy_private_voting_contract/src/main.nr b/yarn-project/noir-contracts/contracts/easy_private_voting_contract/src/main.nr index ceddb93c70c..5cc3606d39e 100644 --- a/yarn-project/noir-contracts/contracts/easy_private_voting_contract/src/main.nr +++ b/yarn-project/noir-contracts/contracts/easy_private_voting_contract/src/main.nr @@ -3,22 +3,17 @@ contract EasyPrivateVoting { use dep::aztec::{ protocol_types::{ abis::function_selector::FunctionSelector, - address::AztecAddress, + address::AztecAddress }, context::{PrivateContext, Context}, - state_vars::{ map::Map, public_state::PublicState,}, - types::type_serialization::{ // serialization methods for using booleans and aztec addresses - bool_serialization::{BoolSerializationMethods, BOOL_SERIALIZED_LEN}, - address_serialization::{AddressSerializationMethods, AZTEC_ADDRESS_SERIALIZED_LEN}, - field_serialization::{ FieldSerializationMethods, FIELD_SERIALIZED_LEN}, - }, + state_vars::{ map::Map, public_state::PublicState}, }; // docs:end:imports // docs:start:storage_struct struct Storage { - admin: PublicState, // admin can end vote - tally: Map>, // we will store candidate as key and number of votes as value - voteEnded: PublicState, // voteEnded is boolean + admin: PublicState, // admin can end vote + tally: Map>, // we will store candidate as key and number of votes as value + voteEnded: PublicState, // voteEnded is boolean } // docs:end:storage_struct // docs:start:storage_impl @@ -28,7 +23,6 @@ contract EasyPrivateVoting { admin: PublicState::new( context, 1, // storage slot. this can be anything except 0. it is hashed, and hash on 0 = 0 - AddressSerializationMethods, ), tally: Map::new( context, @@ -36,15 +30,13 @@ contract EasyPrivateVoting { |context, slot| { PublicState::new( context, - slot, - FieldSerializationMethods, + slot, ) }, ), voteEnded: PublicState::new( context, - 3, - BoolSerializationMethods, + 3 ) } } } diff --git a/yarn-project/noir-contracts/contracts/ecdsa_account_contract/src/ecdsa_public_key_note.nr b/yarn-project/noir-contracts/contracts/ecdsa_account_contract/src/ecdsa_public_key_note.nr index 779c8debecc..5370456c7a9 100644 --- a/yarn-project/noir-contracts/contracts/ecdsa_account_contract/src/ecdsa_public_key_note.nr +++ b/yarn-project/noir-contracts/contracts/ecdsa_account_contract/src/ecdsa_public_key_note.nr @@ -3,7 +3,7 @@ use dep::aztec::{ note::{ note_header::NoteHeader, note_interface::NoteInterface, - utils::compute_unique_siloed_note_hash, + utils::compute_note_hash_for_consumption, }, oracle::{ nullifier_key::get_nullifier_secret_key, @@ -12,6 +12,7 @@ use dep::aztec::{ log::emit_encrypted_log, hash::pedersen_hash, context::PrivateContext, + protocol_types::traits::{Serialize, Deserialize}, }; global ECDSA_PUBLIC_KEY_NOTE_LEN: Field = 5; @@ -25,23 +26,14 @@ struct EcdsaPublicKeyNote { header: NoteHeader, } -impl EcdsaPublicKeyNote { - pub fn new(x: [u8; 32], y: [u8; 32], owner: AztecAddress) -> Self { - EcdsaPublicKeyNote { - x, - y, - owner, - header: NoteHeader::empty(), - } - } - - // serialize the note as 5 fields where: +impl Serialize for EcdsaPublicKeyNote { + // serialize the note as 5 fields where: // [0] = x[0..31] (upper bound excluded) // [1] = x[31] // [2] = y[0..31] // [3] = y[31] // [4] = owner - pub fn serialize(self) -> [Field; ECDSA_PUBLIC_KEY_NOTE_LEN] { + fn serialize(self) -> [Field; ECDSA_PUBLIC_KEY_NOTE_LEN] { let mut x: Field = 0; let mut y: Field = 0; let mut mul: Field = 1; @@ -59,9 +51,37 @@ impl EcdsaPublicKeyNote { [x, last_x, y, last_y, self.owner.to_field()] } +} + +impl Deserialize for EcdsaPublicKeyNote { + fn deserialize(serialized_note: [Field; ECDSA_PUBLIC_KEY_NOTE_LEN]) -> EcdsaPublicKeyNote { + let mut x: [u8; 32] = [0; 32]; + let mut y: [u8; 32] = [0; 32]; + + let part_x = serialized_note[0].to_be_bytes(32); + for i in 0..31 { + x[i] = part_x[i + 1]; + } + x[31] = serialized_note[1].to_be_bytes(32)[31]; + + let part_y = serialized_note[2].to_be_bytes(32); + for i in 0..31 { + y[i] = part_y[i + 1]; + } + y[31] = serialized_note[3].to_be_bytes(32)[31]; + + EcdsaPublicKeyNote { x, y, owner: AztecAddress::from_field(serialized_note[4]), header: NoteHeader::empty() } + } +} - pub fn compute_nullifier(self, context: &mut PrivateContext) -> Field { - let unique_siloed_note_hash = compute_unique_siloed_note_hash(EcdsaPublicKeyNoteInterface, self); +impl NoteInterface for EcdsaPublicKeyNote { + fn compute_note_content_hash(note: EcdsaPublicKeyNote) -> Field { + // TODO(#1205) Should use a non-zero generator index. + pedersen_hash(note.serialize(), 0) + } + + fn compute_nullifier(self, context: &mut PrivateContext) -> Field { + let unique_siloed_note_hash = compute_note_hash_for_consumption(self); let secret = context.request_nullifier_secret_key(self.owner); // TODO(#1205) Should use a non-zero generator index. pedersen_hash([ @@ -71,8 +91,8 @@ impl EcdsaPublicKeyNote { ],0) } - pub fn compute_nullifier_without_context(self) -> Field { - let unique_siloed_note_hash = compute_unique_siloed_note_hash(EcdsaPublicKeyNoteInterface, self); + fn compute_nullifier_without_context(self) -> Field { + let unique_siloed_note_hash = compute_note_hash_for_consumption(self); let secret = get_nullifier_secret_key(self.owner); // TODO(#1205) Should use a non-zero generator index. pedersen_hash([ @@ -82,12 +102,16 @@ impl EcdsaPublicKeyNote { ],0) } - pub fn set_header(&mut self, header: NoteHeader) { + fn set_header(&mut self, header: NoteHeader) { self.header = header; } + fn get_header(self) -> NoteHeader { + self.header + } + // Broadcasts the note as an encrypted log on L1. - pub fn broadcast(self, context: &mut PrivateContext, slot: Field) { + fn broadcast(self, context: &mut PrivateContext, slot: Field) { let encryption_pub_key = get_public_key(self.owner); emit_encrypted_log( context, @@ -99,62 +123,15 @@ impl EcdsaPublicKeyNote { } } -fn deserialize(serialized_note: [Field; ECDSA_PUBLIC_KEY_NOTE_LEN]) -> EcdsaPublicKeyNote { - let mut x: [u8; 32] = [0; 32]; - let mut y: [u8; 32] = [0; 32]; - - let part_x = serialized_note[0].to_be_bytes(32); - for i in 0..31 { - x[i] = part_x[i + 1]; - } - x[31] = serialized_note[1].to_be_bytes(32)[31]; - - let part_y = serialized_note[2].to_be_bytes(32); - for i in 0..31 { - y[i] = part_y[i + 1]; +impl EcdsaPublicKeyNote { + pub fn new(x: [u8; 32], y: [u8; 32], owner: AztecAddress) -> Self { + EcdsaPublicKeyNote { + x, + y, + owner, + header: NoteHeader::empty(), + } } - y[31] = serialized_note[3].to_be_bytes(32)[31]; - - EcdsaPublicKeyNote { x, y, owner: AztecAddress::from_field(serialized_note[4]), header: NoteHeader::empty() } -} - -fn serialize(note: EcdsaPublicKeyNote) -> [Field; ECDSA_PUBLIC_KEY_NOTE_LEN] { - note.serialize() -} -fn compute_note_hash(note: EcdsaPublicKeyNote) -> Field { - // TODO(#1205) Should use a non-zero generator index. - pedersen_hash(note.serialize(), 0) + } - -fn compute_nullifier(note: EcdsaPublicKeyNote, context: &mut PrivateContext) -> Field { - note.compute_nullifier(context) -} - -fn compute_nullifier_without_context(note: EcdsaPublicKeyNote) -> Field { - note.compute_nullifier_without_context() -} - -fn get_header(note: EcdsaPublicKeyNote) -> NoteHeader { - note.header -} - -fn set_header(note: &mut EcdsaPublicKeyNote, header: NoteHeader) { - note.set_header(header); -} - -// Broadcasts the note as an encrypted log on L1. -fn broadcast(context: &mut PrivateContext, slot: Field, note: EcdsaPublicKeyNote) { - note.broadcast(context, slot); -} - -global EcdsaPublicKeyNoteInterface = NoteInterface { - deserialize, - serialize, - compute_note_hash, - compute_nullifier, - compute_nullifier_without_context, - get_header, - set_header, - broadcast, -}; diff --git a/yarn-project/noir-contracts/contracts/ecdsa_account_contract/src/main.nr b/yarn-project/noir-contracts/contracts/ecdsa_account_contract/src/main.nr index a12e0c02600..53c26c8b3c3 100644 --- a/yarn-project/noir-contracts/contracts/ecdsa_account_contract/src/main.nr +++ b/yarn-project/noir-contracts/contracts/ecdsa_account_contract/src/main.nr @@ -23,17 +23,17 @@ contract EcdsaAccount { }; use crate::ecdsa_public_key_note::{ - EcdsaPublicKeyNote, EcdsaPublicKeyNoteInterface, ECDSA_PUBLIC_KEY_NOTE_LEN, + EcdsaPublicKeyNote, ECDSA_PUBLIC_KEY_NOTE_LEN, }; struct Storage { - public_key: ImmutableSingleton, + public_key: ImmutableSingleton, } impl Storage { fn init(context: Context) -> Self { Storage { - public_key: ImmutableSingleton::new(context, 1, EcdsaPublicKeyNoteInterface), + public_key: ImmutableSingleton::new(context, 1), } } } @@ -45,7 +45,7 @@ contract EcdsaAccount { fn constructor(signing_pub_key_x: pub [u8; 32], signing_pub_key_y: pub [u8; 32]) { let this = context.this_address(); let mut pub_key_note = EcdsaPublicKeyNote::new(signing_pub_key_x, signing_pub_key_y, this); - storage.public_key.initialize(&mut pub_key_note, Option::none(), true); + storage.public_key.initialize(&mut pub_key_note, true); } // Note: If you globally change the entrypoint signature don't forget to update default_entrypoint.ts @@ -106,6 +106,6 @@ contract EcdsaAccount { ) -> pub [Field; 4] { assert(storage_slot == 1); let note_header = NoteHeader::new(contract_address, nonce, storage_slot); - note_utils::compute_note_hash_and_nullifier(EcdsaPublicKeyNoteInterface, note_header, serialized_note) + note_utils::compute_note_hash_and_nullifier(EcdsaPublicKeyNote::deserialize, note_header, serialized_note) } } diff --git a/yarn-project/noir-contracts/contracts/escrow_contract/src/main.nr b/yarn-project/noir-contracts/contracts/escrow_contract/src/main.nr index cf7500c7fa6..0aaf5426fda 100644 --- a/yarn-project/noir-contracts/contracts/escrow_contract/src/main.nr +++ b/yarn-project/noir-contracts/contracts/escrow_contract/src/main.nr @@ -20,18 +20,17 @@ contract Escrow { use dep::address_note::address_note::{ AddressNote, - AddressNoteMethods, ADDRESS_NOTE_LEN, }; struct Storage { - owners: Set, + owners: Set, } impl Storage { fn init(context: Context) -> Self { Storage { - owners: Set::new(context, 1, AddressNoteMethods), + owners: Set::new(context, 1), } } } @@ -57,7 +56,7 @@ contract Escrow { let sender = context.msg_sender(); // We don't remove note from the owners set. If a note exists, the owner and recipient are legit. - let options = NoteGetterOptions::new().select(0, sender.to_field()).select(1, this.to_field()).set_limit(1); + let options = NoteGetterOptions::new().select(0, sender.to_field(), Option::none()).select(1, this.to_field(), Option::none()).set_limit(1); let notes = storage.owners.get_notes(options); assert(notes[0].is_some(), "Sender is not an owner."); @@ -77,6 +76,6 @@ contract Escrow { ) -> pub [Field; 4] { let note_header = NoteHeader::new(contract_address, nonce, storage_slot); assert(storage_slot == 1); - note_utils::compute_note_hash_and_nullifier(AddressNoteMethods, note_header, serialized_note) + note_utils::compute_note_hash_and_nullifier(AddressNote::deserialize, note_header, serialized_note) } } diff --git a/yarn-project/noir-contracts/contracts/inclusion_proofs_contract/src/main.nr b/yarn-project/noir-contracts/contracts/inclusion_proofs_contract/src/main.nr index 411fcaf5c35..37517063ad3 100644 --- a/yarn-project/noir-contracts/contracts/inclusion_proofs_contract/src/main.nr +++ b/yarn-project/noir-contracts/contracts/inclusion_proofs_contract/src/main.nr @@ -14,20 +14,17 @@ contract InclusionProofs { set::Set, public_state::PublicState, }, - types::{ - type_serialization::field_serialization::FieldSerializationMethods, - }, context::Context, note::{ note_getter_options::NoteGetterOptions, note_header::NoteHeader, utils as note_utils, }, - + // docs:start:imports history::{ contract_inclusion::{ prove_contract_inclusion, - }, + }, note_inclusion::{ prove_note_commitment_inclusion, prove_note_inclusion, @@ -46,12 +43,15 @@ contract InclusionProofs { prove_public_value_inclusion, }, }, + // docs:end:imports }; - use dep::value_note::value_note::{ValueNote, ValueNoteMethods, VALUE_NOTE_LEN}; - + // docs:start:value_note_imports + use dep::value_note::value_note::{ValueNote, VALUE_NOTE_LEN}; + // docs:end:value_note_imports struct Storage { - private_values: Map>, - public_value: PublicState, + private_values: Map>, + public_value: PublicState, + public_unused_value: PublicState, } impl Storage { @@ -61,13 +61,16 @@ contract InclusionProofs { context, 1, // Storage slot |context, slot| { - Set::new(context, slot, ValueNoteMethods) + Set::new(context, slot) }, ), public_value: PublicState::new( context, 2, // Storage slot - FieldSerializationMethods, + ), + public_unused_value: PublicState::new( + context, + 3, // Storage slot ), } } @@ -84,6 +87,7 @@ contract InclusionProofs { storage.public_value.write(value); } + // docs:start:create_note // Creates a value note owned by `owner`. #[aztec(private)] fn create_note(owner: AztecAddress, value: Field) { @@ -91,31 +95,31 @@ contract InclusionProofs { let mut note = ValueNote::new(value, owner); owner_private_values.insert(&mut note, true); } + // docs:end:create_note // Proves that the owner owned a ValueNote at block `block_number`. #[aztec(private)] fn test_note_inclusion_proof( owner: AztecAddress, block_number: u32, // The block at which we'll prove that the note exists - // Value bellow is only used when the note is not found --> used to test the note inclusion failure case (it + // Value below is only used when the note is not found --> used to test the note inclusion failure case (it // allows me to pass in random value of note nullifier - I cannot add and fetch a random note from PXE because // PXE performs note commitment inclusion check when you add a new note). spare_commitment: Field ) { + // docs:start:get_note_from_pxe // 1) Get the note from PXE. let private_values = storage.private_values.at(owner); - let options = NoteGetterOptions::new().select(1, owner.to_field()).set_limit(1); + let options = NoteGetterOptions::new().select(1, owner.to_field(), Option::none()).set_limit(1); let notes = private_values.get_notes(options); let maybe_note = notes[0]; + // docs:end:get_note_from_pxe // 2) Prove the note inclusion if maybe_note.is_some() { - prove_note_inclusion( - ValueNoteMethods, - maybe_note.unwrap_unchecked(), - block_number, - context - ); + // docs:start:prove_note_inclusion + prove_note_inclusion(maybe_note.unwrap_unchecked(), block_number, context); + // docs:end:prove_note_inclusion } else { // Note was not found so we will prove inclusion of the spare commitment prove_note_commitment_inclusion(spare_commitment, block_number, context); @@ -127,28 +131,27 @@ contract InclusionProofs { fn test_nullifier_non_inclusion_proof( owner: AztecAddress, block_number: u32, // The block at which we'll prove that the nullifier does not exists - // Value bellow is only used when the note is not found --> used to test the nullifier non-inclusion failure + // Value below is only used when the note is not found --> used to test the nullifier non-inclusion failure // case (it allows me to pass in random value of note nullifier - I cannot add and fetch a random note from PXE // because PXE performs note commitment inclusion check when you add a new note). spare_nullifier: Field ) { // 2) Get the note from PXE let private_values = storage.private_values.at(owner); - let options = NoteGetterOptions::new().select(1, owner.to_field()).set_limit(1); + let options = NoteGetterOptions::new().select(1, owner.to_field(), Option::none()).set_limit(1); let notes = private_values.get_notes(options); let maybe_note = notes[0]; // 3) Compute the nullifier from the note if maybe_note.is_some() { - prove_note_not_nullified( - ValueNoteMethods, - maybe_note.unwrap_unchecked(), - block_number, - &mut context - ); + // docs:start:prove_note_not_nullified + prove_note_not_nullified(maybe_note.unwrap_unchecked(), block_number, &mut context); + // docs:end:prove_note_not_nullified } else { // Note was not found so we will use the spare nullifier + // docs:start:prove_nullifier_non_inclusion prove_nullifier_non_inclusion(spare_nullifier, block_number, context); + // docs:end:prove_nullifier_non_inclusion }; } @@ -159,23 +162,27 @@ contract InclusionProofs { ) { // 1) Get the note from PXE. let private_values = storage.private_values.at(owner); - let options = NoteGetterOptions::new().select(1, owner.to_field()).set_limit(1); + let options = NoteGetterOptions::new().select(1, owner.to_field(), Option::none()).set_limit(1); let notes = private_values.get_notes(options); let note = notes[0].unwrap(); // 2) Prove the note validity - prove_note_validity(ValueNoteMethods, note, block_number, &mut context); + // docs:start:prove_note_validity + prove_note_validity(note, block_number, &mut context); + // docs:end:prove_note_validity } + // docs:start:nullify_note #[aztec(private)] fn nullify_note(owner: AztecAddress) { let private_values = storage.private_values.at(owner); - let options = NoteGetterOptions::new().select(1, owner.to_field()).set_limit(1); + let options = NoteGetterOptions::new().select(1, owner.to_field(), Option::none()).set_limit(1); let notes = private_values.get_notes(options); let note = notes[0].unwrap(); private_values.remove(note); } + // docs:end:nullify_note // Proves nullifier existed at block `block_number`. // Note: I am not getting a nullifier of the note that was created in this contract in this function because it is @@ -185,7 +192,20 @@ contract InclusionProofs { nullifier: Field, block_number: u32 // The block at which we'll prove that the nullifier not exists in the tree ) { + // docs:start:prove_nullifier_inclusion prove_nullifier_inclusion(nullifier, block_number, context); + // docs:end:prove_nullifier_inclusion + } + + #[aztec(private)] + fn test_public_unused_value_inclusion_proof(block_number: u32 // The block at which we'll prove that the public value exists + ) { + prove_public_value_inclusion( + 0, + storage.public_unused_value.storage_slot, + block_number, + context + ); } #[aztec(private)] @@ -211,18 +231,18 @@ contract InclusionProofs { // contract's aztec address instead of just the address. #[aztec(private)] fn test_contract_inclusion_proof( - deployer_public_key: GrumpkinPoint, + public_key: GrumpkinPoint, contract_address_salt: Field, - function_tree_root: Field, - constructor_hash: Field, + contract_class_id: Field, + initialization_hash: Field, portal_contract_address: EthAddress, block_number: u32 // The block at which we'll prove that the public value exists ) { let proven_contract_address = prove_contract_inclusion( - deployer_public_key, + public_key, contract_address_salt, - function_tree_root, - constructor_hash, + contract_class_id, + initialization_hash, portal_contract_address, block_number, context @@ -240,6 +260,6 @@ contract InclusionProofs { serialized_note: [Field; VALUE_NOTE_LEN] ) -> pub [Field; 4] { let note_header = NoteHeader::new(contract_address, nonce, storage_slot); - note_utils::compute_note_hash_and_nullifier(ValueNoteMethods, note_header, serialized_note) + note_utils::compute_note_hash_and_nullifier(ValueNote::deserialize, note_header, serialized_note) } } diff --git a/yarn-project/noir-contracts/contracts/lending_contract/src/asset.nr b/yarn-project/noir-contracts/contracts/lending_contract/src/asset.nr index b71696a3a5f..5fbbb83a855 100644 --- a/yarn-project/noir-contracts/contracts/lending_contract/src/asset.nr +++ b/yarn-project/noir-contracts/contracts/lending_contract/src/asset.nr @@ -1,5 +1,7 @@ -use dep::aztec::protocol_types::address::AztecAddress; -use dep::aztec::types::type_serialization::TypeSerializationInterface; +use dep::aztec::protocol_types::{ + address::AztecAddress, + traits::{Deserialize, Serialize} +}; // Struct to be used to represent "totals". Generally, there should be one per asset. // It stores the global values that are shared among all users, such as an accumulator @@ -15,33 +17,26 @@ struct Asset { global ASSET_SERIALIZED_LEN: Field = 4; -// Right now we are wasting so many writes. If changing last_updated_ts -// we will end up rewriting all of them, wasting writes. -fn deserializeAsset(fields: [Field; ASSET_SERIALIZED_LEN]) -> Asset { - Asset { - interest_accumulator: fields[0] as u120, - last_updated_ts: fields[1] as u120, - loan_to_value: fields[2] as u120, - oracle: AztecAddress::from_field(fields[3]) +impl Serialize for Asset { + fn serialize(asset: Asset) -> [Field; ASSET_SERIALIZED_LEN] { + [ + asset.interest_accumulator as Field, + asset.last_updated_ts as Field, + asset.loan_to_value as Field, + asset.oracle.to_field() + ] } } -fn serializeAsset(asset: Asset) -> [Field; ASSET_SERIALIZED_LEN] { - [ - asset.interest_accumulator as Field, - asset.last_updated_ts as Field, - asset.loan_to_value as Field, - asset.oracle.to_field() - ] -} - -impl Asset { - pub fn serialize(self: Self) -> [Field; ASSET_SERIALIZED_LEN] { - serializeAsset(self) +impl Deserialize for Asset { + // Right now we are wasting so many writes. If changing last_updated_ts + // we will end up rewriting all of them, wasting writes. + fn deserialize(fields: [Field; ASSET_SERIALIZED_LEN]) -> Asset { + Asset { + interest_accumulator: fields[0] as u120, + last_updated_ts: fields[1] as u120, + loan_to_value: fields[2] as u120, + oracle: AztecAddress::from_field(fields[3]) + } } } - -global AssetSerializationMethods = TypeSerializationInterface { - deserialize: deserializeAsset, - serialize: serializeAsset, -}; diff --git a/yarn-project/noir-contracts/contracts/lending_contract/src/main.nr b/yarn-project/noir-contracts/contracts/lending_contract/src/main.nr index 1c80bf6e4a2..9811f0919d1 100644 --- a/yarn-project/noir-contracts/contracts/lending_contract/src/main.nr +++ b/yarn-project/noir-contracts/contracts/lending_contract/src/main.nr @@ -22,25 +22,20 @@ contract Lending { state_vars::{ map::Map, public_state::PublicState, - }, - types::type_serialization::{ - field_serialization::{FieldSerializationMethods, FIELD_SERIALIZED_LEN}, - TypeSerializationInterface, - address_serialization::{AddressSerializationMethods, AZTEC_ADDRESS_SERIALIZED_LEN}, - }, + } }; - use crate::asset::{ASSET_SERIALIZED_LEN, Asset, AssetSerializationMethods}; + use crate::asset::Asset; use crate::interest_math::compute_multiplier; use crate::helpers::{covered_by_collateral, DebtReturn, debt_updates, debt_value, compute_identifier}; use crate::interfaces::{Token, Lending, PriceFeed}; // Storage structure, containing all storage, and specifying what slots they use. struct Storage { - collateral_asset: PublicState, - stable_coin: PublicState, - assets: Map>, - collateral: Map>, - static_debt: Map>, // abusing keys very heavily + collateral_asset: PublicState, + stable_coin: PublicState, + assets: Map>, + collateral: Map>, + static_debt: Map>, // abusing keys very heavily } impl Storage { @@ -48,13 +43,11 @@ contract Lending { Storage { collateral_asset: PublicState::new( context, - 1, - AddressSerializationMethods, + 1 ), stable_coin: PublicState::new( context, - 2, - AddressSerializationMethods, + 2 ), assets: Map::new( context, @@ -62,8 +55,7 @@ contract Lending { |context, slot| { PublicState::new( context, - slot, - AssetSerializationMethods, + slot ) }, ), @@ -73,8 +65,7 @@ contract Lending { |context, slot| { PublicState::new( context, - slot, - FieldSerializationMethods, + slot ) }, ), @@ -84,8 +75,7 @@ contract Lending { |context, slot| { PublicState::new( context, - slot, - FieldSerializationMethods, + slot ) }, ), diff --git a/yarn-project/noir-contracts/contracts/pending_commitments_contract/src/main.nr b/yarn-project/noir-contracts/contracts/pending_commitments_contract/src/main.nr index 6792fcef1cf..3878ae5134c 100644 --- a/yarn-project/noir-contracts/contracts/pending_commitments_contract/src/main.nr +++ b/yarn-project/noir-contracts/contracts/pending_commitments_contract/src/main.nr @@ -8,7 +8,7 @@ contract PendingCommitments { use dep::value_note::{ balance_utils, filter::filter_notes_min_sum, - value_note::{VALUE_NOTE_LEN, ValueNote, ValueNoteMethods}, + value_note::{VALUE_NOTE_LEN, ValueNote}, }; use dep::aztec::{ context::{PrivateContext, PublicContext, Context}, @@ -26,7 +26,7 @@ contract PendingCommitments { }; struct Storage { - balances: Map>, + balances: Map>, } impl Storage { @@ -36,7 +36,7 @@ contract PendingCommitments { context, 1, // Storage slot |context, slot| { - Set::new(context, slot, ValueNoteMethods) + Set::new(context, slot) }, ), } @@ -296,6 +296,6 @@ contract PendingCommitments { serialized_note: [Field; VALUE_NOTE_LEN] ) -> pub [Field; 4] { let note_header = NoteHeader::new(contract_address, nonce, storage_slot); - note_utils::compute_note_hash_and_nullifier(ValueNoteMethods, note_header, serialized_note) + note_utils::compute_note_hash_and_nullifier(ValueNote::deserialize, note_header, serialized_note) } } diff --git a/yarn-project/noir-contracts/contracts/price_feed_contract/src/asset.nr b/yarn-project/noir-contracts/contracts/price_feed_contract/src/asset.nr index 9d464eefddc..047705a1ace 100644 --- a/yarn-project/noir-contracts/contracts/price_feed_contract/src/asset.nr +++ b/yarn-project/noir-contracts/contracts/price_feed_contract/src/asset.nr @@ -1,4 +1,4 @@ -use dep::aztec::types::type_serialization::TypeSerializationInterface; +use dep::aztec::protocol_types::traits::{Serialize, Deserialize}; struct Asset { price: u120, @@ -6,21 +6,14 @@ struct Asset { global ASSET_SERIALIZED_LEN: Field = 1; -fn deserializeAsset(fields: [Field; ASSET_SERIALIZED_LEN]) -> Asset { - Asset { price: fields[0] as u120 } -} - -fn serializeAsset(asset: Asset) -> [Field; ASSET_SERIALIZED_LEN] { - [asset.price as Field] +impl Serialize for Asset { + fn serialize(asset: Asset) -> [Field; ASSET_SERIALIZED_LEN] { + [asset.price as Field] + } } -impl Asset { - fn serialize(self: Self) -> [Field; ASSET_SERIALIZED_LEN] { - serializeAsset(self) +impl Deserialize for Asset { + fn deserialize(fields: [Field; ASSET_SERIALIZED_LEN]) -> Asset { + Asset { price: fields[0] as u120 } } } - -global AssetSerializationMethods = TypeSerializationInterface { - deserialize: deserializeAsset, - serialize: serializeAsset, -}; diff --git a/yarn-project/noir-contracts/contracts/price_feed_contract/src/main.nr b/yarn-project/noir-contracts/contracts/price_feed_contract/src/main.nr index 4e301359438..a72caab5e83 100644 --- a/yarn-project/noir-contracts/contracts/price_feed_contract/src/main.nr +++ b/yarn-project/noir-contracts/contracts/price_feed_contract/src/main.nr @@ -10,11 +10,11 @@ contract PriceFeed { }, }; use dep::aztec::protocol_types::address::AztecAddress; - use crate::asset::{ASSET_SERIALIZED_LEN, Asset, AssetSerializationMethods}; + use crate::asset::Asset; // Storage structure, containing all storage, and specifying what slots they use. struct Storage { - assets: Map>, + assets: Map>, } impl Storage { @@ -27,7 +27,6 @@ contract PriceFeed { PublicState::new( context, slot, - AssetSerializationMethods, ) }, ), diff --git a/yarn-project/noir-contracts/contracts/reader_contract/src/main.nr b/yarn-project/noir-contracts/contracts/reader_contract/src/main.nr index 931a5e25501..928d80b908b 100644 --- a/yarn-project/noir-contracts/contracts/reader_contract/src/main.nr +++ b/yarn-project/noir-contracts/contracts/reader_contract/src/main.nr @@ -4,14 +4,14 @@ contract Reader { abis::function_selector::FunctionSelector, }; - use dep::compressed_string::{FieldCompressedString, FieldCompressedStringSerializationMethods}; + use dep::compressed_string::FieldCompressedString; #[aztec(private)] fn constructor() {} #[aztec(public)] - fn check_name(who: AztecAddress, what: str<31>) { + fn check_name_public(who: AztecAddress, what: str<31>) { let selector = FunctionSelector::from_signature("public_get_name()"); let ret = context.call_public_function_no_args(who, selector); let name = FieldCompressedString::from_field(ret[0]); @@ -19,8 +19,17 @@ contract Reader { assert(name.is_eq(_what)); } + #[aztec(private)] + fn check_name_private(who: AztecAddress, what: str<31>) { + let selector = FunctionSelector::from_signature("private_get_name()"); + let ret = context.call_private_function_no_args(who, selector); + let name = FieldCompressedString::from_field(ret[0]); + let _what = FieldCompressedString::from_string(what); + assert(name.is_eq(_what)); + } + #[aztec(public)] - fn check_symbol(who: AztecAddress, what: str<31>) { + fn check_symbol_public(who: AztecAddress, what: str<31>) { let selector = FunctionSelector::from_signature("public_get_symbol()"); let ret = context.call_public_function_no_args(who, selector); let symbol = FieldCompressedString::from_field(ret[0]); @@ -28,10 +37,26 @@ contract Reader { assert(symbol.is_eq(_what)); } + #[aztec(private)] + fn check_symbol_private(who: AztecAddress, what: str<31>) { + let selector = FunctionSelector::from_signature("private_get_symbol()"); + let ret = context.call_private_function_no_args(who, selector); + let symbol = FieldCompressedString::from_field(ret[0]); + let _what = FieldCompressedString::from_string(what); + assert(symbol.is_eq(_what)); + } + #[aztec(public)] - fn check_decimals(who: AztecAddress, what: u8) { + fn check_decimals_public(who: AztecAddress, what: u8) { let selector = FunctionSelector::from_signature("public_get_decimals()"); let ret = context.call_public_function_no_args(who, selector); assert(ret[0] as u8 == what); } + + #[aztec(private)] + fn check_decimals_private(who: AztecAddress, what: u8) { + let selector = FunctionSelector::from_signature("private_get_decimals()"); + let ret = context.call_private_function_no_args(who, selector); + assert(ret[0] as u8 == what); + } } diff --git a/yarn-project/noir-contracts/contracts/schnorr_account_contract/src/main.nr b/yarn-project/noir-contracts/contracts/schnorr_account_contract/src/main.nr index 77203a70388..ef0402fcc01 100644 --- a/yarn-project/noir-contracts/contracts/schnorr_account_contract/src/main.nr +++ b/yarn-project/noir-contracts/contracts/schnorr_account_contract/src/main.nr @@ -20,11 +20,11 @@ contract SchnorrAccount { auth_witness::get_auth_witness, }; - use crate::public_key_note::{PublicKeyNote, PublicKeyNoteMethods, PUBLIC_KEY_NOTE_LEN}; + use crate::public_key_note::{PublicKeyNote, PUBLIC_KEY_NOTE_LEN}; struct Storage { // docs:start:storage - signing_public_key: ImmutableSingleton, + signing_public_key: ImmutableSingleton, // docs:end:storage } @@ -32,7 +32,7 @@ contract SchnorrAccount { fn init(context: Context) -> Self { Storage { // docs:start:storage_init - signing_public_key: ImmutableSingleton::new(context, 1, PublicKeyNoteMethods), + signing_public_key: ImmutableSingleton::new(context, 1), // docs:end:storage_init } } @@ -46,7 +46,7 @@ contract SchnorrAccount { let this = context.this_address(); // docs:start:initialize let mut pub_key_note = PublicKeyNote::new(signing_pub_key_x, signing_pub_key_y, this); - storage.signing_public_key.initialize(&mut pub_key_note, Option::none(), true); + storage.signing_public_key.initialize(&mut pub_key_note, true); // docs:end:initialize } @@ -113,6 +113,6 @@ contract SchnorrAccount { ) -> pub [Field; 4] { assert(storage_slot == 1); let note_header = NoteHeader::new(contract_address, nonce, storage_slot); - note_utils::compute_note_hash_and_nullifier(PublicKeyNoteMethods, note_header, serialized_note) + note_utils::compute_note_hash_and_nullifier(PublicKeyNote::deserialize, note_header, serialized_note) } } diff --git a/yarn-project/noir-contracts/contracts/schnorr_account_contract/src/public_key_note.nr b/yarn-project/noir-contracts/contracts/schnorr_account_contract/src/public_key_note.nr index 2db16db6be3..6bfc37f4c90 100644 --- a/yarn-project/noir-contracts/contracts/schnorr_account_contract/src/public_key_note.nr +++ b/yarn-project/noir-contracts/contracts/schnorr_account_contract/src/public_key_note.nr @@ -2,7 +2,7 @@ use dep::aztec::{ note::{ note_header::NoteHeader, note_interface::NoteInterface, - utils::compute_unique_siloed_note_hash, + utils::compute_note_hash_for_consumption, }, hash::pedersen_hash, oracle::{ @@ -11,8 +11,11 @@ use dep::aztec::{ }, log::emit_encrypted_log, context::PrivateContext, + protocol_types::{ + address::AztecAddress, + traits::{Serialize, Deserialize}, + } }; -use dep::aztec::protocol_types::address::AztecAddress; global PUBLIC_KEY_NOTE_LEN: Field = 3; @@ -25,23 +28,27 @@ struct PublicKeyNote { header: NoteHeader, } -impl PublicKeyNote { - pub fn new(x: Field, y: Field, owner: AztecAddress) -> Self { +impl Serialize for PublicKeyNote { + fn serialize(self) -> [Field; PUBLIC_KEY_NOTE_LEN] { + [self.x, self.y, self.owner.to_field()] + } +} + +impl Deserialize for PublicKeyNote { + fn deserialize(serialized_note: [Field; PUBLIC_KEY_NOTE_LEN]) -> PublicKeyNote { PublicKeyNote { - x, - y, - owner, - header: NoteHeader::empty(), + x: serialized_note[0], + y: serialized_note[1], + owner: AztecAddress::from_field(serialized_note[2]), + header: NoteHeader::empty() } } +} - // serialize the note as 3 fields - pub fn serialize(self) -> [Field; PUBLIC_KEY_NOTE_LEN] { - [self.x, self.y, self.owner.to_field()] - } +impl NoteInterface for PublicKeyNote { - pub fn compute_nullifier(self, context: &mut PrivateContext) -> Field { - let unique_siloed_note_hash = compute_unique_siloed_note_hash(PublicKeyNoteMethods, self); + fn compute_nullifier(self, context: &mut PrivateContext) -> Field { + let unique_siloed_note_hash = compute_note_hash_for_consumption(self); let secret = context.request_nullifier_secret_key(self.owner); // TODO(#1205) Should use a non-zero generator index. pedersen_hash([ @@ -51,8 +58,8 @@ impl PublicKeyNote { ],0) } - pub fn compute_nullifier_without_context(self) -> Field { - let unique_siloed_note_hash = compute_unique_siloed_note_hash(PublicKeyNoteMethods, self); + fn compute_nullifier_without_context(self) -> Field { + let unique_siloed_note_hash = compute_note_hash_for_consumption(self); let secret = get_nullifier_secret_key(self.owner); // TODO(#1205) Should use a non-zero generator index. pedersen_hash([ @@ -62,12 +69,21 @@ impl PublicKeyNote { ],0) } - pub fn set_header(&mut self, header: NoteHeader) { + fn compute_note_content_hash(note: PublicKeyNote) -> Field { + // TODO(#1205) Should use a non-zero generator index. + pedersen_hash(note.serialize(), 0) + } + + fn set_header(&mut self, header: NoteHeader) { self.header = header; } + fn get_header(self) -> NoteHeader { + self.header + } + // Broadcasts the note as an encrypted log on L1. - pub fn broadcast(self, context: &mut PrivateContext, slot: Field) { + fn broadcast(self, context: &mut PrivateContext, slot: Field) { let encryption_pub_key = get_public_key(self.owner); emit_encrypted_log( context, @@ -79,52 +95,13 @@ impl PublicKeyNote { } } -fn deserialize(serialized_note: [Field; PUBLIC_KEY_NOTE_LEN]) -> PublicKeyNote { - PublicKeyNote { - x: serialized_note[0], - y: serialized_note[1], - owner: AztecAddress::from_field(serialized_note[2]), - header: NoteHeader::empty() +impl PublicKeyNote { + pub fn new(x: Field, y: Field, owner: AztecAddress) -> Self { + PublicKeyNote { + x, + y, + owner, + header: NoteHeader::empty(), + } } } - -fn serialize(note: PublicKeyNote) -> [Field; PUBLIC_KEY_NOTE_LEN] { - note.serialize() -} - -fn compute_note_hash(note: PublicKeyNote) -> Field { - // TODO(#1205) Should use a non-zero generator index. - pedersen_hash(note.serialize(), 0) -} - -fn compute_nullifier(note: PublicKeyNote, context: &mut PrivateContext) -> Field { - note.compute_nullifier(context) -} - -fn compute_nullifier_without_context(note: PublicKeyNote) -> Field { - note.compute_nullifier_without_context() -} - -fn get_header(note: PublicKeyNote) -> NoteHeader { - note.header -} - -fn set_header(note: &mut PublicKeyNote, header: NoteHeader) { - note.set_header(header); -} - -// Broadcasts the note as an encrypted log on L1. -fn broadcast(context: &mut PrivateContext, slot: Field, note: PublicKeyNote) { - note.broadcast(context, slot); -} - -global PublicKeyNoteMethods = NoteInterface { - deserialize, - serialize, - compute_note_hash, - compute_nullifier, - compute_nullifier_without_context, - get_header, - set_header, - broadcast, -}; diff --git a/yarn-project/noir-contracts/contracts/schnorr_hardcoded_account_contract/src/main.nr b/yarn-project/noir-contracts/contracts/schnorr_hardcoded_account_contract/src/main.nr index faef2015a90..6226024e39f 100644 --- a/yarn-project/noir-contracts/contracts/schnorr_hardcoded_account_contract/src/main.nr +++ b/yarn-project/noir-contracts/contracts/schnorr_hardcoded_account_contract/src/main.nr @@ -5,7 +5,6 @@ contract SchnorrHardcodedAccount { use dep::aztec::{ abi::{ PrivateCircuitPublicInputs, PrivateContextInputs, Hasher }, context::PrivateContext, - types::vec::BoundedVec, }; use dep::authwit:: { diff --git a/yarn-project/noir-contracts/contracts/schnorr_single_key_account_contract/src/util.nr b/yarn-project/noir-contracts/contracts/schnorr_single_key_account_contract/src/util.nr index 89b328dcabe..fff77376e94 100644 --- a/yarn-project/noir-contracts/contracts/schnorr_single_key_account_contract/src/util.nr +++ b/yarn-project/noir-contracts/contracts/schnorr_single_key_account_contract/src/util.nr @@ -1,4 +1,4 @@ -use dep::aztec::protocol_types::address::AztecAddress; +use dep::aztec::protocol_types::address::{AztecAddress, PublicKeysHash}; use dep::std::{schnorr::verify_signature}; use crate::auth_oracle::{AuthWitness}; @@ -12,5 +12,8 @@ pub fn recover_address(message_hash: Field, witness: AuthWitness) -> AztecAddres ); assert(verification == true); - AztecAddress::compute(witness.owner, witness.partial_address) + AztecAddress::compute( + PublicKeysHash::compute(witness.owner), + witness.partial_address + ) } diff --git a/yarn-project/noir-contracts/contracts/slow_tree_contract/src/capsule.nr b/yarn-project/noir-contracts/contracts/slow_tree_contract/src/capsule.nr index 8731eca35d2..7a00e3354f1 100644 --- a/yarn-project/noir-contracts/contracts/slow_tree_contract/src/capsule.nr +++ b/yarn-project/noir-contracts/contracts/slow_tree_contract/src/capsule.nr @@ -1,3 +1,4 @@ +// docs:start:pop_capsule #[oracle(popCapsule)] fn pop_capsule_oracle() -> [Field; N] {} @@ -5,3 +6,5 @@ fn pop_capsule_oracle() -> [Field; N] {} unconstrained pub fn pop_capsule() -> [Field; N] { pop_capsule_oracle() } +// docs:end:pop_capsule + diff --git a/yarn-project/noir-contracts/contracts/slow_tree_contract/src/main.nr b/yarn-project/noir-contracts/contracts/slow_tree_contract/src/main.nr index b08f2800b78..e0986fbd238 100644 --- a/yarn-project/noir-contracts/contracts/slow_tree_contract/src/main.nr +++ b/yarn-project/noir-contracts/contracts/slow_tree_contract/src/main.nr @@ -14,7 +14,7 @@ contract SlowTree { use dep::value_note::{ balance_utils, utils::{increment, decrement}, - value_note::{VALUE_NOTE_LEN, ValueNote, ValueNoteMethods}, + value_note::{VALUE_NOTE_LEN, ValueNote}, }; use dep::aztec::{ context::{PrivateContext, PublicContext, Context}, @@ -23,15 +23,15 @@ contract SlowTree { utils as note_utils, }, state_vars::{map::Map, public_state::PublicState, set::Set}, - types::type_serialization::field_serialization::{ - FieldSerializationMethods, FIELD_SERIALIZED_LEN, - }, + protocol_types::type_serialization::FIELD_SERIALIZED_LEN, }; use dep::slow_updates_tree::slow_map::{ SlowMap, Leaf, SlowUpdateProof, compute_merkle_root, deserialize_slow_update_proof }; + // docs:start:import_pop_capsule use crate::capsule::pop_capsule; + // docs:end:import_pop_capsule use crate::types::{MembershipProof, deserialize_membership_proof}; // docs:start:constants_and_storage @@ -84,7 +84,9 @@ contract SlowTree { // docs:start:read_at_private #[aztec(private)] fn read_at(index: Field) -> Field { + // docs:start:pop_capsule let fields = pop_capsule(); + // docs:end:pop_capsule let p: MembershipProof = deserialize_membership_proof(fields); assert(index == p.index, "Index does not match expected"); diff --git a/yarn-project/noir-contracts/contracts/stateful_test_contract/src/main.nr b/yarn-project/noir-contracts/contracts/stateful_test_contract/src/main.nr index 9f02dc97bb5..cfca700e66e 100644 --- a/yarn-project/noir-contracts/contracts/stateful_test_contract/src/main.nr +++ b/yarn-project/noir-contracts/contracts/stateful_test_contract/src/main.nr @@ -5,7 +5,7 @@ contract StatefulTest { use dep::value_note::{ balance_utils, utils::{increment, decrement}, - value_note::{VALUE_NOTE_LEN, ValueNote, ValueNoteMethods}, + value_note::{VALUE_NOTE_LEN, ValueNote}, }; use dep::aztec::{ context::{PrivateContext, PublicContext, Context}, @@ -14,14 +14,11 @@ contract StatefulTest { utils as note_utils, }, state_vars::{map::Map, public_state::PublicState, set::Set}, - types::type_serialization::field_serialization::{ - FieldSerializationMethods, FIELD_SERIALIZED_LEN, - }, }; struct Storage { - notes: Map>, - public_values: Map>, + notes: Map>, + public_values: Map>, } impl Storage { @@ -31,7 +28,7 @@ contract StatefulTest { context, 1, // Storage slot |context, slot| { - Set::new(context, slot, ValueNoteMethods) + Set::new(context, slot) }, ), public_values: Map::new( @@ -41,7 +38,6 @@ contract StatefulTest { PublicState::new( context, slot, - FieldSerializationMethods, ) }, ), @@ -88,6 +84,6 @@ contract StatefulTest { serialized_note: [Field; VALUE_NOTE_LEN] ) -> pub [Field; 4] { let note_header = NoteHeader::new(contract_address, nonce, storage_slot); - note_utils::compute_note_hash_and_nullifier(ValueNoteMethods, note_header, serialized_note) + note_utils::compute_note_hash_and_nullifier(ValueNote::deserialize, note_header, serialized_note) } } diff --git a/yarn-project/noir-contracts/contracts/test_contract/Nargo.toml b/yarn-project/noir-contracts/contracts/test_contract/Nargo.toml index 80a59556c0a..7ab24aa181e 100644 --- a/yarn-project/noir-contracts/contracts/test_contract/Nargo.toml +++ b/yarn-project/noir-contracts/contracts/test_contract/Nargo.toml @@ -7,4 +7,5 @@ type = "contract" [dependencies] aztec = { path = "../../../aztec-nr/aztec" } field_note = { path = "../../../aztec-nr/field-note" } +value_note = { path = "../../../aztec-nr/value-note" } token_portal_content_hash_lib = { path = "../token_portal_content_hash_lib" } diff --git a/yarn-project/noir-contracts/contracts/test_contract/src/main.nr b/yarn-project/noir-contracts/contracts/test_contract/src/main.nr index aac608c1bd8..791016b7a8d 100644 --- a/yarn-project/noir-contracts/contracts/test_contract/src/main.nr +++ b/yarn-project/noir-contracts/contracts/test_contract/src/main.nr @@ -1,9 +1,15 @@ // A contract used for testing a random hodgepodge of small features from simulator and end-to-end tests. contract Test { use dep::std::option::Option; - use dep::aztec::protocol_types::address::{ - AztecAddress, - EthAddress, + use dep::aztec::protocol_types::{ + address::{ + AztecAddress, + EthAddress, + }, + constants::{ + MAX_READ_REQUESTS_PER_CALL, + MAX_NOTES_PER_PAGE + } }; // The following import is here in order to make the event macro work because the macro doesn't add the import. // It doesn't add the import because in the future we will re-export all the types via aztec-nr and aztec-nr is @@ -23,6 +29,10 @@ contract Test { note::{ note_header::NoteHeader, utils as note_utils, + lifecycle::{create_note, destroy_note}, + note_getter::{get_notes, view_notes}, + note_getter_options::{NoteGetterOptions, NoteStatus}, + note_viewer_options::NoteViewerOptions, }, oracle::{ get_public_key::get_public_key as get_public_key_oracle, @@ -31,10 +41,10 @@ contract Test { }, state_vars::immutable_singleton::ImmutableSingleton, log::emit_unencrypted_log_from_private, - types::vec::BoundedVec, }; use dep::token_portal_content_hash_lib::{get_mint_private_content_hash, get_mint_public_content_hash}; - use dep::field_note::field_note::{FieldNote, FieldNoteMethods, FIELD_NOTE_LEN}; + use dep::field_note::field_note::FieldNote; + use dep::value_note::value_note::{ValueNote,VALUE_NOTE_LEN}; #[event] struct ExampleEvent { @@ -42,13 +52,13 @@ contract Test { } struct Storage { - example_constant: ImmutableSingleton, + example_constant: ImmutableSingleton, } impl Storage { fn init(context: Context) -> Self { Storage { - example_constant: ImmutableSingleton::new(context, 1, FieldNoteMethods), + example_constant: ImmutableSingleton::new(context, 1), } } } @@ -83,6 +93,89 @@ contract Test { context.this_address() } + #[aztec(private)] + fn call_create_note(value: Field, owner: AztecAddress, storage_slot: Field) { + assert(storage_slot != 1, "storage slot 1 is reserved for example_constant"); + + let mut note = ValueNote::new(value, owner); + create_note(&mut context, storage_slot, &mut note, true); + } + + #[aztec(private)] + fn call_get_notes(storage_slot: Field, active_or_nullified: bool) { + assert(storage_slot != 1, "storage slot 1 is reserved for example_constant"); + + let mut options = NoteGetterOptions::new(); + if (active_or_nullified) { + options = options.set_status(NoteStatus.ACTIVE_OR_NULLIFIED); + } + + let opt_notes: [Option; MAX_READ_REQUESTS_PER_CALL] = get_notes(&mut context, storage_slot, options); + + // We can't get the return value of a private function from the outside world in an end to end test, so we + // expose it via an unecrypted log instead. + let value = opt_notes[0].unwrap().value; + emit_unencrypted_log_from_private(&mut context, value); + } + + #[aztec(private)] + fn call_get_notes_many(storage_slot: Field, active_or_nullified: bool) { + assert(storage_slot != 1, "storage slot 1 is reserved for example_constant"); + + let mut options = NoteGetterOptions::new(); + if (active_or_nullified) { + options = options.set_status(NoteStatus.ACTIVE_OR_NULLIFIED); + } + + let opt_notes: [Option; MAX_READ_REQUESTS_PER_CALL] = get_notes(&mut context, storage_slot, options); + + // We can't get the return value of a private function from the outside world in an end to end test, so we + // expose it via an unecrypted log instead. + emit_unencrypted_log_from_private(&mut context, opt_notes[0].unwrap().value); + emit_unencrypted_log_from_private(&mut context, opt_notes[1].unwrap().value); + } + + unconstrained fn call_view_notes(storage_slot: Field, active_or_nullified: bool) -> pub Field { + assert(storage_slot != 1, "storage slot 1 is reserved for example_constant"); + + let mut options = NoteViewerOptions::new(); + if (active_or_nullified) { + options = options.set_status(NoteStatus.ACTIVE_OR_NULLIFIED); + } + + let opt_notes: [Option; MAX_NOTES_PER_PAGE] = view_notes(storage_slot, options); + + opt_notes[0].unwrap().value + } + + unconstrained fn call_view_notes_many( + storage_slot: Field, + active_or_nullified: bool + ) -> pub [Field; 2] { + assert(storage_slot != 1, "storage slot 1 is reserved for example_constant"); + + let mut options = NoteViewerOptions::new(); + if (active_or_nullified) { + options = options.set_status(NoteStatus.ACTIVE_OR_NULLIFIED); + } + + let opt_notes: [Option; MAX_NOTES_PER_PAGE] = view_notes(storage_slot, options); + + [opt_notes[0].unwrap().value, opt_notes[1].unwrap().value] + } + + #[aztec(private)] + fn call_destroy_note(storage_slot: Field) { + assert(storage_slot != 1, "storage slot 1 is reserved for example_constant"); + + let options = NoteGetterOptions::new(); + let opt_notes: [Option; MAX_READ_REQUESTS_PER_CALL] = get_notes(&mut context, storage_slot, options); + + let note = opt_notes[0].unwrap(); + + destroy_note(&mut context, note); + } + // Test codegen for Aztec.nr interfaces // See yarn-project/acir-simulator/src/client/private_execution.test.ts 'nested calls through autogenerated interface' // Note; this function is deliberately NOT annotated with #[aztec(private)] due to its use in tests @@ -99,7 +192,7 @@ contract Test { args.push(a_field); args.push(a_bool as Field); args.push(a_number as Field); - args.push_array(an_array); + args.extend_from_array(an_array); args.push(a_struct.amount); args.push(a_struct.secret_hash); args.push(a_deep_struct.a_field); @@ -193,7 +286,7 @@ contract Test { #[aztec(private)] fn set_constant(value: Field) { let mut note = FieldNote::new(value); - storage.example_constant.initialize(&mut note, Option::none(), false); + storage.example_constant.initialize(&mut note, false); } unconstrained fn get_constant() -> pub Field { @@ -238,10 +331,15 @@ contract Test { contract_address: AztecAddress, nonce: Field, storage_slot: Field, - serialized_note: [Field; FIELD_NOTE_LEN] + serialized_note: [Field; VALUE_NOTE_LEN] // must fit either a FieldNote or a ValueNote ) -> pub [Field; 4] { - assert(storage_slot == 1); - let note_header = NoteHeader::new(contract_address, nonce, storage_slot); - note_utils::compute_note_hash_and_nullifier(FieldNoteMethods, note_header, serialized_note) + if (storage_slot == 1) { + let note_header = NoteHeader::new(contract_address, nonce, storage_slot); + note_utils::compute_note_hash_and_nullifier(FieldNote::deserialize, note_header, serialized_note) + } else { + // For ValueNotes created via write_value_to_storage + let note_header = NoteHeader::new(contract_address, nonce, storage_slot); + note_utils::compute_note_hash_and_nullifier(ValueNote::deserialize, note_header, serialized_note) + } } } diff --git a/yarn-project/noir-contracts/contracts/token_blacklist_contract/src/main.nr b/yarn-project/noir-contracts/contracts/token_blacklist_contract/src/main.nr index 0845c0ec0da..b8b02f80817 100644 --- a/yarn-project/noir-contracts/contracts/token_blacklist_contract/src/main.nr +++ b/yarn-project/noir-contracts/contracts/token_blacklist_contract/src/main.nr @@ -30,14 +30,9 @@ contract TokenBlacklist { context::{PrivateContext, PublicContext, Context}, hash::{compute_secret_hash}, state_vars::{map::Map, public_state::PublicState, set::Set, immutable_singleton::ImmutableSingleton}, - types::type_serialization::{ - field_serialization::{FieldSerializationMethods, FIELD_SERIALIZED_LEN}, - bool_serialization::{BoolSerializationMethods, BOOL_SERIALIZED_LEN}, - address_serialization::{AddressSerializationMethods, AZTEC_ADDRESS_SERIALIZED_LEN}, - }, }; - use dep::field_note::field_note::{FieldNote, FieldNoteMethods, FIELD_NOTE_LEN}; + use dep::field_note::field_note::{FieldNote, FIELD_NOTE_LEN}; use dep::authwit::{ auth::{ @@ -47,10 +42,9 @@ contract TokenBlacklist { }; use crate::types::{ - transparent_note::{TransparentNote, TransparentNoteMethods, TRANSPARENT_NOTE_LEN}, - token_note::{TokenNote, TokenNoteMethods, TOKEN_NOTE_LEN}, + transparent_note::TransparentNote, + token_note::{TokenNote, TOKEN_NOTE_LEN}, balances_map::{BalancesMap}, - safe_u120_serialization::{SafeU120SerializationMethods, SAFE_U120_SERIALIZED_LEN}, roles::UserFlags, }; // docs:start:interface @@ -58,13 +52,13 @@ contract TokenBlacklist { // docs:end:interface struct Storage { - admin: PublicState, + admin: PublicState, balances: BalancesMap, - total_supply: PublicState, - pending_shields: Set, - public_balances: Map>, - slow_update: ImmutableSingleton, - public_slow_update: PublicState, + total_supply: PublicState, + pending_shields: Set, + public_balances: Map>, + slow_update: ImmutableSingleton, + public_slow_update: PublicState, } impl Storage { @@ -73,33 +67,29 @@ contract TokenBlacklist { admin: PublicState::new( context, 1, - AddressSerializationMethods, ), balances: BalancesMap::new(context, 3), total_supply: PublicState::new( context, 4, - SafeU120SerializationMethods, ), - pending_shields: Set::new(context, 5, TransparentNoteMethods), + pending_shields: Set::new(context, 5), public_balances: Map::new( context, 6, |context, slot| { PublicState::new( context, - slot, - SafeU120SerializationMethods, + slot ) }, ), // Below is an abomination to have same value in private and public (immutable in solidity). // docs:start:slow_updates_storage - slow_update: ImmutableSingleton::new(context, 7, FieldNoteMethods), + slow_update: ImmutableSingleton::new(context, 7), public_slow_update: PublicState::new( context, - 8, - AddressSerializationMethods, + 8 ), // docs:end:slow_updates_storage @@ -110,7 +100,7 @@ contract TokenBlacklist { #[aztec(private)] fn constructor(admin: AztecAddress, slow_updates_contract: AztecAddress) { let mut slow_note = FieldNote::new(slow_updates_contract.to_field()); - storage.slow_update.initialize(&mut slow_note, Option::none(), false); + storage.slow_update.initialize(&mut slow_note, false); // docs:end:constructor let selector = FunctionSelector::from_signature("_initialize((Field),(Field))"); context.call_public_function( @@ -281,7 +271,7 @@ contract TokenBlacklist { let secret_hash = compute_secret_hash(secret); // Get 1 note (set_limit(1)) which has amount stored in field with index 0 (select(0, amount)) and secret_hash // stored in field with index 1 (select(1, secret_hash)). - let options = NoteGetterOptions::new().select(0, amount).select(1, secret_hash).set_limit(1); + let options = NoteGetterOptions::new().select(0, amount, Option::none()).select(1, secret_hash, Option::none()).set_limit(1); let notes = pending_shields.get_notes(options); let note = notes[0].unwrap_unchecked(); // Remove the note from the pending shields set @@ -393,11 +383,11 @@ contract TokenBlacklist { ) -> pub [Field; 4] { let note_header = NoteHeader::new(contract_address, nonce, storage_slot); if (storage_slot == 5) { - note_utils::compute_note_hash_and_nullifier(TransparentNoteMethods, note_header, preimage) + note_utils::compute_note_hash_and_nullifier(TransparentNote::deserialize, note_header, preimage) } else if (storage_slot == 7) { - note_utils::compute_note_hash_and_nullifier(FieldNoteMethods, note_header, preimage) + note_utils::compute_note_hash_and_nullifier(FieldNote::deserialize, note_header, preimage) } else { - note_utils::compute_note_hash_and_nullifier(TokenNoteMethods, note_header, preimage) + note_utils::compute_note_hash_and_nullifier(TokenNote::deserialize, note_header, preimage) } } } diff --git a/yarn-project/noir-contracts/contracts/token_blacklist_contract/src/types.nr b/yarn-project/noir-contracts/contracts/token_blacklist_contract/src/types.nr index f5a3aeaa6fd..3ba03658390 100644 --- a/yarn-project/noir-contracts/contracts/token_blacklist_contract/src/types.nr +++ b/yarn-project/noir-contracts/contracts/token_blacklist_contract/src/types.nr @@ -2,5 +2,4 @@ mod transparent_note; mod balance_set; mod balances_map; mod token_note; -mod safe_u120_serialization; mod roles; diff --git a/yarn-project/noir-contracts/contracts/token_blacklist_contract/src/types/balance_set.nr b/yarn-project/noir-contracts/contracts/token_blacklist_contract/src/types/balance_set.nr index e63efeab306..a6952f87c1d 100644 --- a/yarn-project/noir-contracts/contracts/token_blacklist_contract/src/types/balance_set.nr +++ b/yarn-project/noir-contracts/contracts/token_blacklist_contract/src/types/balance_set.nr @@ -14,9 +14,8 @@ use dep::aztec::note::{ use dep::aztec::note::{ note_header::NoteHeader, note_interface::NoteInterface, - utils::compute_note_hash_for_read_or_nullify, }; -use crate::types::token_note::{TokenNote, TOKEN_NOTE_LEN, TokenNoteMethods}; +use crate::types::token_note::TokenNote; // A set implementing standard manipulation of balances. // Does not require spending key, but only knowledge. @@ -24,7 +23,7 @@ use crate::types::token_note::{TokenNote, TOKEN_NOTE_LEN, TokenNoteMethods}; struct BalanceSet { context: Context, owner: AztecAddress, - set: Set + set: Set } impl BalanceSet { @@ -33,7 +32,6 @@ impl BalanceSet { let set = Set { context, storage_slot, - note_interface: TokenNoteMethods, }; Self { context, diff --git a/yarn-project/noir-contracts/contracts/token_blacklist_contract/src/types/safe_u120_serialization.nr b/yarn-project/noir-contracts/contracts/token_blacklist_contract/src/types/safe_u120_serialization.nr deleted file mode 100644 index 876007184fe..00000000000 --- a/yarn-project/noir-contracts/contracts/token_blacklist_contract/src/types/safe_u120_serialization.nr +++ /dev/null @@ -1,18 +0,0 @@ -use dep::aztec::types::type_serialization::TypeSerializationInterface; -use dep::safe_math::SafeU120; - -global SAFE_U120_SERIALIZED_LEN: Field = 1; - -// This is safe when reading from storage IF only correct safeu120 was written to storage -fn deserializeU120(fields: [Field; SAFE_U120_SERIALIZED_LEN]) -> SafeU120 { - SafeU120 { value: fields[0] as u120 } -} - -fn serializeU120(value: SafeU120) -> [Field; SAFE_U120_SERIALIZED_LEN] { - [value.value as Field] -} - -global SafeU120SerializationMethods = TypeSerializationInterface { - deserialize: deserializeU120, - serialize: serializeU120, -}; diff --git a/yarn-project/noir-contracts/contracts/token_blacklist_contract/src/types/token_note.nr b/yarn-project/noir-contracts/contracts/token_blacklist_contract/src/types/token_note.nr index 41e921d16d0..e405a600131 100644 --- a/yarn-project/noir-contracts/contracts/token_blacklist_contract/src/types/token_note.nr +++ b/yarn-project/noir-contracts/contracts/token_blacklist_contract/src/types/token_note.nr @@ -4,12 +4,13 @@ use dep::aztec::{ note::{ note_header::NoteHeader, note_interface::NoteInterface, - utils::compute_note_hash_for_read_or_nullify, + utils::compute_note_hash_for_consumption, }, context::PrivateContext, state_vars::set::Set, log::emit_encrypted_log, hash::pedersen_hash, + protocol_types::traits::{Serialize, Deserialize} }; use dep::aztec::oracle::{ rand::rand, @@ -35,21 +36,14 @@ struct TokenNote { header: NoteHeader, } -impl TokenNote { - pub fn new(amount: SafeU120, owner: AztecAddress) -> Self { - Self { - amount, - owner, - randomness: rand(), - header: NoteHeader::empty(), - } - } - - pub fn serialize(self) -> [Field; TOKEN_NOTE_LEN] { +impl Serialize for TokenNote { + fn serialize(self) -> [Field; TOKEN_NOTE_LEN] { [self.amount.value as Field, self.owner.to_field(), self.randomness] } +} - pub fn deserialize(preimage: [Field; TOKEN_NOTE_LEN]) -> Self { +impl Deserialize for TokenNote { + fn deserialize(preimage: [Field; TOKEN_NOTE_LEN]) -> Self { Self { amount: SafeU120::new(preimage[0]), owner: AztecAddress::from_field(preimage[1]), @@ -57,19 +51,17 @@ impl TokenNote { header: NoteHeader::empty(), } } +} - pub fn compute_note_hash(self) -> Field { +impl NoteInterface for TokenNote { + fn compute_note_content_hash(self) -> Field { // TODO(#1205) Should use a non-zero generator index. - pedersen_hash([ - self.amount.value as Field, - self.owner.to_field(), - self.randomness, - ],0) + pedersen_hash(self.serialize(), 0) } // docs:start:nullifier - pub fn compute_nullifier(self, context: &mut PrivateContext) -> Field { - let note_hash_for_nullify = compute_note_hash_for_read_or_nullify(TokenNoteMethods, self); + fn compute_nullifier(self, context: &mut PrivateContext) -> Field { + let note_hash_for_nullify = compute_note_hash_for_consumption(self); let secret = context.request_nullifier_secret_key(self.owner); // TODO(#1205) Should use a non-zero generator index. pedersen_hash([ @@ -80,8 +72,8 @@ impl TokenNote { } // docs:end:nullifier - pub fn compute_nullifier_without_context(self) -> Field { - let note_hash_for_nullify = compute_note_hash_for_read_or_nullify(TokenNoteMethods, self); + fn compute_nullifier_without_context(self) -> Field { + let note_hash_for_nullify = compute_note_hash_for_consumption(self); let secret = get_nullifier_secret_key(self.owner); // TODO(#1205) Should use a non-zero generator index. pedersen_hash([ @@ -91,12 +83,17 @@ impl TokenNote { ],0) } - pub fn set_header(&mut self, header: NoteHeader) { + fn set_header(&mut self, header: NoteHeader) { self.header = header; } + + fn get_header(note: TokenNote) -> NoteHeader { + note.header + } + // Broadcasts the note as an encrypted log on L1. - pub fn broadcast(self, context: &mut PrivateContext, slot: Field) { + fn broadcast(self, context: &mut PrivateContext, slot: Field) { // We only bother inserting the note if non-empty to save funds on gas. if !self.amount.is_zero() { let encryption_pub_key = get_public_key(self.owner); @@ -111,46 +108,13 @@ impl TokenNote { } } -fn deserialize(preimage: [Field; TOKEN_NOTE_LEN]) -> TokenNote { - TokenNote::deserialize(preimage) -} - -fn serialize(note: TokenNote) -> [Field; TOKEN_NOTE_LEN] { - note.serialize() -} - -fn compute_note_hash(note: TokenNote) -> Field { - note.compute_note_hash() -} - -fn compute_nullifier(note: TokenNote, context: &mut PrivateContext) -> Field { - note.compute_nullifier(context) -} - -fn compute_nullifier_without_context(note: TokenNote) -> Field { - note.compute_nullifier_without_context() -} - -fn get_header(note: TokenNote) -> NoteHeader { - note.header -} - -fn set_header(note: &mut TokenNote, header: NoteHeader) { - note.set_header(header) -} - -// Broadcasts the note as an encrypted log on L1. -fn broadcast(context: &mut PrivateContext, slot: Field, note: TokenNote) { - note.broadcast(context, slot); +impl TokenNote { + pub fn new(amount: SafeU120, owner: AztecAddress) -> Self { + Self { + amount, + owner, + randomness: rand(), + header: NoteHeader::empty(), + } + } } - -global TokenNoteMethods = NoteInterface { - deserialize, - serialize, - compute_note_hash, - compute_nullifier, - compute_nullifier_without_context, - get_header, - set_header, - broadcast, -}; diff --git a/yarn-project/noir-contracts/contracts/token_blacklist_contract/src/types/transparent_note.nr b/yarn-project/noir-contracts/contracts/token_blacklist_contract/src/types/transparent_note.nr index 98867d1225c..a008ddb6b3c 100644 --- a/yarn-project/noir-contracts/contracts/token_blacklist_contract/src/types/transparent_note.nr +++ b/yarn-project/noir-contracts/contracts/token_blacklist_contract/src/types/transparent_note.nr @@ -3,10 +3,11 @@ use dep::aztec::{ note::{ note_header::NoteHeader, note_interface::NoteInterface, - utils::compute_siloed_note_hash, + utils::compute_note_hash_for_consumption, }, hash::{compute_secret_hash, pedersen_hash}, context::PrivateContext, + protocol_types::traits::{Serialize, Deserialize} }; global TRANSPARENT_NOTE_LEN: Field = 2; @@ -22,38 +23,14 @@ struct TransparentNote { header: NoteHeader, } -impl TransparentNote { - - // CONSTRUCTORS - - pub fn new(amount: Field, secret_hash: Field) -> Self { - TransparentNote { - amount: amount, - secret_hash: secret_hash, - secret: 0, - header: NoteHeader::empty(), - } - } - - // new oracle call primitive - // get me the secret corresponding to this hash - pub fn new_from_secret(amount: Field, secret: Field) -> Self { - TransparentNote { - amount: amount, - secret_hash: compute_secret_hash(secret), - secret: secret, - header: NoteHeader::empty(), - } - } - - - // STANDARD NOTE_INTERFACE FUNCTIONS - - pub fn serialize(self) -> [Field; TRANSPARENT_NOTE_LEN] { +impl Serialize for TransparentNote { + fn serialize(self) -> [Field; TRANSPARENT_NOTE_LEN] { [self.amount, self.secret_hash] } +} - pub fn deserialize(preimage: [Field; TRANSPARENT_NOTE_LEN]) -> Self { +impl Deserialize for TransparentNote { + fn deserialize(preimage: [Field; TRANSPARENT_NOTE_LEN]) -> Self { TransparentNote { amount: preimage[0], secret_hash: preimage[1], @@ -61,79 +38,67 @@ impl TransparentNote { header: NoteHeader::empty(), } } +} + +impl NoteInterface for TransparentNote { - pub fn compute_note_hash(self) -> Field { + fn compute_note_content_hash(self) -> Field { // TODO(#1205) Should use a non-zero generator index. - pedersen_hash([ - self.amount, - self.secret_hash, - ],0) + pedersen_hash(self.serialize(), 0) } - pub fn compute_nullifier(self, _context: &mut PrivateContext) -> Field { + fn compute_nullifier(self, _context: &mut PrivateContext) -> Field { self.compute_nullifier_without_context() } - pub fn compute_nullifier_without_context(self) -> Field { - // TODO(#1386): should use `compute_note_hash_for_read_or_nullify` once public functions inject nonce! - let siloed_note_hash = compute_siloed_note_hash(TransparentNoteMethods, self); + fn compute_nullifier_without_context(self) -> Field { + let siloed_note_hash = compute_note_hash_for_consumption(self); // TODO(#1205) Should use a non-zero generator index. pedersen_hash([self.secret, siloed_note_hash],0) } - pub fn set_header(&mut self, header: NoteHeader) { + fn set_header(&mut self, header: NoteHeader) { self.header = header; } - - // CUSTOM FUNCTIONS FOR THIS NOTE TYPE - - pub fn knows_secret(self, secret: Field) { - let hash = compute_secret_hash(secret); - assert(self.secret_hash == hash); + fn get_header(self) -> NoteHeader { + self.header } -} -fn deserialize(preimage: [Field; TRANSPARENT_NOTE_LEN]) -> TransparentNote { - TransparentNote::deserialize(preimage) -} - -fn serialize(note: TransparentNote) -> [Field; TRANSPARENT_NOTE_LEN] { - note.serialize() + fn broadcast(self, context: &mut PrivateContext, slot: Field) { + assert(false, "TransparentNote does not support broadcast"); + } } -fn compute_note_hash(note: TransparentNote) -> Field { - note.compute_note_hash() -} +impl TransparentNote { -fn compute_nullifier(note: TransparentNote, context: &mut PrivateContext) -> Field { - note.compute_nullifier(context) -} + // CONSTRUCTORS -fn compute_nullifier_without_context(note: TransparentNote) -> Field { - note.compute_nullifier_without_context() -} + pub fn new(amount: Field, secret_hash: Field) -> Self { + TransparentNote { + amount: amount, + secret_hash: secret_hash, + secret: 0, + header: NoteHeader::empty(), + } + } -fn get_header(note: TransparentNote) -> NoteHeader { - note.header -} + // new oracle call primitive + // get me the secret corresponding to this hash + pub fn new_from_secret(amount: Field, secret: Field) -> Self { + TransparentNote { + amount: amount, + secret_hash: compute_secret_hash(secret), + secret: secret, + header: NoteHeader::empty(), + } + } -fn set_header(note: &mut TransparentNote, header: NoteHeader) { - note.set_header(header) -} + // CUSTOM FUNCTIONS FOR THIS NOTE TYPE -fn broadcast(context: &mut PrivateContext, slot: Field, note: TransparentNote) { - assert(false, "TransparentNote does not support broadcast"); + pub fn knows_secret(self, secret: Field) { + let hash = compute_secret_hash(secret); + assert(self.secret_hash == hash); + } } - -global TransparentNoteMethods = NoteInterface { - deserialize, - serialize, - compute_note_hash, - compute_nullifier, - compute_nullifier_without_context, - get_header, - set_header, - broadcast, -}; // docs:end:token_types_all \ No newline at end of file diff --git a/yarn-project/noir-contracts/contracts/token_bridge_contract/src/main.nr b/yarn-project/noir-contracts/contracts/token_bridge_contract/src/main.nr index ebf6c279a95..e7893ef8ceb 100644 --- a/yarn-project/noir-contracts/contracts/token_bridge_contract/src/main.nr +++ b/yarn-project/noir-contracts/contracts/token_bridge_contract/src/main.nr @@ -19,7 +19,6 @@ contract TokenBridge { context::{Context}, hash::{compute_secret_hash}, state_vars::{public_state::PublicState}, - types::type_serialization::address_serialization::AddressSerializationMethods, }; use dep::token_portal_content_hash_lib::{get_mint_public_content_hash, get_mint_private_content_hash, get_withdraw_content_hash}; @@ -30,7 +29,7 @@ contract TokenBridge { // docs:start:token_bridge_storage_and_constructor // Storage structure, containing all storage, and specifying what slots they use. struct Storage { - token: PublicState, + token: PublicState, } impl Storage { @@ -38,8 +37,7 @@ contract TokenBridge { Storage { token: PublicState::new( context, - 1, - AddressSerializationMethods, + 1 ), } } diff --git a/yarn-project/noir-contracts/contracts/token_contract/src/main.nr b/yarn-project/noir-contracts/contracts/token_contract/src/main.nr index 6aa5c0f88d3..9815719c26f 100644 --- a/yarn-project/noir-contracts/contracts/token_contract/src/main.nr +++ b/yarn-project/noir-contracts/contracts/token_contract/src/main.nr @@ -13,8 +13,8 @@ contract Token { // Libs use dep::std::option::Option; - use dep::safe_math::SafeU120; - use dep::compressed_string::{FieldCompressedString, FieldCompressedStringSerializationMethods}; + use dep::safe_math::{SafeU120, SAFE_U120_SERIALIZED_LEN}; + use dep::compressed_string::{FieldCompressedString}; use dep::aztec::{ note::{ @@ -24,17 +24,17 @@ contract Token { }, context::{PrivateContext, PublicContext, Context}, hash::{compute_secret_hash}, - state_vars::{map::Map, public_state::PublicState, set::Set}, - types::type_serialization::{ - field_serialization::{FieldSerializationMethods, FIELD_SERIALIZED_LEN}, - bool_serialization::{BoolSerializationMethods, BOOL_SERIALIZED_LEN}, - address_serialization::{AddressSerializationMethods, AZTEC_ADDRESS_SERIALIZED_LEN}, - u8_serialization::{U8SerializationMethods, U8_SERIALIZED_LEN}, - }, - }; - use dep::aztec::protocol_types::{ - abis::function_selector::FunctionSelector, - address::AztecAddress, + state_vars::{map::Map, public_state::PublicState, stable_public_state::StablePublicState, set::Set}, + protocol_types::{ + type_serialization::{ + FIELD_SERIALIZED_LEN, + BOOL_SERIALIZED_LEN, + U8_SERIALIZED_LEN, + AZTEC_ADDRESS_SERIALIZED_LEN + }, + abis::function_selector::FunctionSelector, + address::AztecAddress + } }; // docs:start:import_authwit @@ -47,32 +47,33 @@ contract Token { // docs:end:import_authwit use crate::types::{ - transparent_note::{TransparentNote, TransparentNoteMethods, TRANSPARENT_NOTE_LEN}, - token_note::{TokenNote, TokenNoteMethods, TOKEN_NOTE_LEN}, - balances_map::{BalancesMap}, - safe_u120_serialization::{SafeU120SerializationMethods, SAFE_U120_SERIALIZED_LEN} + transparent_note::TransparentNote, + token_note::{TokenNote, TOKEN_NOTE_LEN}, + balances_map::{BalancesMap} }; // docs:end::imports // docs:start:storage_struct struct Storage { // docs:start:storage_admin - admin: PublicState, + admin: PublicState, // docs:end:storage_admin // docs:start:storage_minters - minters: Map>, + minters: Map>, // docs:end:storage_minters // docs:start:storage_balances balances: BalancesMap, // docs:end:storage_balances - total_supply: PublicState, + total_supply: PublicState, // docs:start:storage_pending_shields - pending_shields: Set, + pending_shields: Set, // docs:end:storage_pending_shields - public_balances: Map>, - symbol: PublicState, - name: PublicState, - decimals: PublicState, + public_balances: Map>, + symbol: StablePublicState, + name: StablePublicState, + // docs:start:storage_decimals + decimals: StablePublicState, + // docs:end:storage_decimals } // docs:end:storage_struct @@ -84,7 +85,6 @@ contract Token { admin: PublicState::new( context, 1, - AddressSerializationMethods, ), // docs:end:storage_admin_init // docs:start:storage_minters_init @@ -95,7 +95,6 @@ contract Token { PublicState::new( context, slot, - BoolSerializationMethods, ) }, ), @@ -106,10 +105,9 @@ contract Token { total_supply: PublicState::new( context, 4, - SafeU120SerializationMethods, ), // docs:start:storage_pending_shields_init - pending_shields: Set::new(context, 5, TransparentNoteMethods), + pending_shields: Set::new(context, 5), // docs:end:storage_pending_shields_init public_balances: Map::new( context, @@ -118,25 +116,23 @@ contract Token { PublicState::new( context, slot, - SafeU120SerializationMethods, ) }, ), - symbol: PublicState::new( + symbol: StablePublicState::new( context, 7, - FieldCompressedStringSerializationMethods, ), - name: PublicState::new( + name: StablePublicState::new( context, 8, - FieldCompressedStringSerializationMethods, ), - decimals: PublicState::new( + // docs:start:storage_decimals_init + decimals: StablePublicState::new( context, 9, - U8SerializationMethods, ), + // docs:end:storage_decimals_init } } } @@ -168,29 +164,48 @@ contract Token { #[aztec(public)] fn public_get_name() -> pub FieldCompressedString { - storage.name.read() + storage.name.read_public() + } + + #[aztec(private)] + fn private_get_name() -> pub FieldCompressedString { + storage.name.read_private() } unconstrained fn un_get_name() -> pub [u8; 31] { - storage.name.read().to_bytes() + storage.name.read_public().to_bytes() } #[aztec(public)] fn public_get_symbol() -> pub FieldCompressedString { - storage.symbol.read() + storage.symbol.read_public() + } + + #[aztec(private)] + fn private_get_symbol() -> pub FieldCompressedString { + storage.symbol.read_private() } unconstrained fn un_get_symbol() -> pub [u8; 31] { - storage.symbol.read().to_bytes() + storage.symbol.read_public().to_bytes() } #[aztec(public)] fn public_get_decimals() -> pub u8 { - storage.decimals.read() + // docs:start:read_decimals_public + storage.decimals.read_public() + // docs:end:read_decimals_public + } + + #[aztec(private)] + fn private_get_decimals() -> pub u8 { + // docs:start:read_decimals_private + storage.decimals.read_private() + // docs:end:read_decimals_private } unconstrained fn un_get_decimals() -> pub u8 { - storage.decimals.read() + storage.decimals.read_public() } // docs:start:set_minter @@ -301,7 +316,7 @@ contract Token { let secret_hash = compute_secret_hash(secret); // Get 1 note (set_limit(1)) which has amount stored in field with index 0 (select(0, amount)) and secret_hash // stored in field with index 1 (select(1, secret_hash)). - let options = NoteGetterOptions::new().select(0, amount).select(1, secret_hash).set_limit(1); + let options = NoteGetterOptions::new().select(0, amount, Option::none()).select(1, secret_hash, Option::none()).set_limit(1); let notes = pending_shields.get_notes(options); let note = notes[0].unwrap_unchecked(); // Remove the note from the pending shields set @@ -374,9 +389,11 @@ contract Token { assert(!new_admin.is_zero(), "invalid admin"); storage.admin.write(new_admin); storage.minters.at(new_admin).write(true); - storage.name.write(name); - storage.symbol.write(symbol); - storage.decimals.write(decimals); + storage.name.initialize(name); + storage.symbol.initialize(symbol); + // docs:start:initialize_decimals + storage.decimals.initialize(decimals); + // docs:end:initialize_decimals } // docs:end:initialize @@ -446,9 +463,9 @@ contract Token { ) -> pub [Field; 4] { let note_header = NoteHeader::new(contract_address, nonce, storage_slot); if (storage_slot == 5) { - note_utils::compute_note_hash_and_nullifier(TransparentNoteMethods, note_header, serialized_note) + note_utils::compute_note_hash_and_nullifier(TransparentNote::deserialize, note_header, serialized_note) } else { - note_utils::compute_note_hash_and_nullifier(TokenNoteMethods, note_header, serialized_note) + note_utils::compute_note_hash_and_nullifier(TokenNote::deserialize, note_header, serialized_note) } } // docs:end:compute_note_hash_and_nullifier diff --git a/yarn-project/noir-contracts/contracts/token_contract/src/types.nr b/yarn-project/noir-contracts/contracts/token_contract/src/types.nr index e29a8151e9f..d3b3b1c9e77 100644 --- a/yarn-project/noir-contracts/contracts/token_contract/src/types.nr +++ b/yarn-project/noir-contracts/contracts/token_contract/src/types.nr @@ -2,4 +2,3 @@ mod transparent_note; mod balance_set; mod balances_map; mod token_note; -mod safe_u120_serialization; diff --git a/yarn-project/noir-contracts/contracts/token_contract/src/types/balance_set.nr b/yarn-project/noir-contracts/contracts/token_contract/src/types/balance_set.nr index fc2303da23b..e84b4cfd110 100644 --- a/yarn-project/noir-contracts/contracts/token_contract/src/types/balance_set.nr +++ b/yarn-project/noir-contracts/contracts/token_contract/src/types/balance_set.nr @@ -15,10 +15,9 @@ use dep::aztec::note::{ }; use dep::aztec::note::{ note_header::NoteHeader, - note_interface::NoteInterface, - utils::compute_note_hash_for_read_or_nullify, + note_interface::NoteInterface }; -use crate::types::token_note::{TokenNote, TOKEN_NOTE_LEN, TokenNoteMethods}; +use crate::types::token_note::TokenNote; // A set implementing standard manipulation of balances. // Does not require spending key, but only knowledge. @@ -26,7 +25,7 @@ use crate::types::token_note::{TokenNote, TOKEN_NOTE_LEN, TokenNoteMethods}; struct BalanceSet { context: Context, owner: AztecAddress, - set: Set + set: Set } impl BalanceSet { @@ -35,7 +34,6 @@ impl BalanceSet { let set = Set { context, storage_slot, - note_interface: TokenNoteMethods, }; Self { context, diff --git a/yarn-project/noir-contracts/contracts/token_contract/src/types/safe_u120_serialization.nr b/yarn-project/noir-contracts/contracts/token_contract/src/types/safe_u120_serialization.nr deleted file mode 100644 index 876007184fe..00000000000 --- a/yarn-project/noir-contracts/contracts/token_contract/src/types/safe_u120_serialization.nr +++ /dev/null @@ -1,18 +0,0 @@ -use dep::aztec::types::type_serialization::TypeSerializationInterface; -use dep::safe_math::SafeU120; - -global SAFE_U120_SERIALIZED_LEN: Field = 1; - -// This is safe when reading from storage IF only correct safeu120 was written to storage -fn deserializeU120(fields: [Field; SAFE_U120_SERIALIZED_LEN]) -> SafeU120 { - SafeU120 { value: fields[0] as u120 } -} - -fn serializeU120(value: SafeU120) -> [Field; SAFE_U120_SERIALIZED_LEN] { - [value.value as Field] -} - -global SafeU120SerializationMethods = TypeSerializationInterface { - deserialize: deserializeU120, - serialize: serializeU120, -}; diff --git a/yarn-project/noir-contracts/contracts/token_contract/src/types/token_note.nr b/yarn-project/noir-contracts/contracts/token_contract/src/types/token_note.nr index f87ccd3cef2..17e77155a28 100644 --- a/yarn-project/noir-contracts/contracts/token_contract/src/types/token_note.nr +++ b/yarn-project/noir-contracts/contracts/token_contract/src/types/token_note.nr @@ -8,12 +8,16 @@ use dep::aztec::{ note::{ note_header::NoteHeader, note_interface::NoteInterface, - utils::compute_note_hash_for_read_or_nullify, + utils::compute_note_hash_for_consumption, }, context::PrivateContext, state_vars::set::Set, log::emit_encrypted_log, hash::pedersen_hash, + protocol_types::traits::{ + Serialize, + Deserialize + }, }; use dep::aztec::oracle::{ rand::rand, @@ -39,21 +43,14 @@ struct TokenNote { header: NoteHeader, } -impl TokenNote { - pub fn new(amount: SafeU120, owner: AztecAddress) -> Self { - Self { - amount, - owner, - randomness: rand(), - header: NoteHeader::empty(), - } - } - - pub fn serialize(self) -> [Field; TOKEN_NOTE_LEN] { +impl Serialize for TokenNote { + fn serialize(self) -> [Field; TOKEN_NOTE_LEN] { [self.amount.value as Field, self.owner.to_field(), self.randomness] } +} - pub fn deserialize(serialized_note: [Field; TOKEN_NOTE_LEN]) -> Self { +impl Deserialize for TokenNote { + fn deserialize(serialized_note: [Field; TOKEN_NOTE_LEN]) -> Self { Self { amount: SafeU120::new(serialized_note[0]), owner: AztecAddress::from_field(serialized_note[1]), @@ -61,19 +58,17 @@ impl TokenNote { header: NoteHeader::empty(), } } +} - pub fn compute_note_hash(self) -> Field { +impl NoteInterface for TokenNote { + fn compute_note_content_hash(self) -> Field { // TODO(#1205) Should use a non-zero generator index. - pedersen_hash([ - self.amount.value as Field, - self.owner.to_field(), - self.randomness, - ],0) + pedersen_hash(self.serialize(), 0) } // docs:start:nullifier - pub fn compute_nullifier(self, context: &mut PrivateContext) -> Field { - let note_hash_for_nullify = compute_note_hash_for_read_or_nullify(TokenNoteMethods, self); + fn compute_nullifier(self, context: &mut PrivateContext) -> Field { + let note_hash_for_nullify = compute_note_hash_for_consumption(self); let secret = context.request_nullifier_secret_key(self.owner); // TODO(#1205) Should use a non-zero generator index. pedersen_hash([ @@ -84,8 +79,8 @@ impl TokenNote { } // docs:end:nullifier - pub fn compute_nullifier_without_context(self) -> Field { - let note_hash_for_nullify = compute_note_hash_for_read_or_nullify(TokenNoteMethods, self); + fn compute_nullifier_without_context(self) -> Field { + let note_hash_for_nullify = compute_note_hash_for_consumption(self); let secret = get_nullifier_secret_key(self.owner); // TODO(#1205) Should use a non-zero generator index. pedersen_hash([ @@ -95,12 +90,16 @@ impl TokenNote { ],0) } - pub fn set_header(&mut self, header: NoteHeader) { + fn set_header(&mut self, header: NoteHeader) { self.header = header; } + fn get_header(note: TokenNote) -> NoteHeader { + note.header + } + // Broadcasts the note as an encrypted log on L1. - pub fn broadcast(self, context: &mut PrivateContext, slot: Field) { + fn broadcast(self, context: &mut PrivateContext, slot: Field) { // We only bother inserting the note if non-empty to save funds on gas. if !self.amount.is_zero() { let encryption_pub_key = get_public_key(self.owner); @@ -115,46 +114,15 @@ impl TokenNote { } } -fn deserialize(serialized_note: [Field; TOKEN_NOTE_LEN]) -> TokenNote { - TokenNote::deserialize(serialized_note) -} - -fn serialize(note: TokenNote) -> [Field; TOKEN_NOTE_LEN] { - note.serialize() -} - -fn compute_note_hash(note: TokenNote) -> Field { - note.compute_note_hash() -} - -fn compute_nullifier(note: TokenNote, context: &mut PrivateContext) -> Field { - note.compute_nullifier(context) -} - -fn compute_nullifier_without_context(note: TokenNote) -> Field { - note.compute_nullifier_without_context() -} - -fn get_header(note: TokenNote) -> NoteHeader { - note.header -} - -fn set_header(note: &mut TokenNote, header: NoteHeader) { - note.set_header(header) -} +impl TokenNote { + pub fn new(amount: SafeU120, owner: AztecAddress) -> Self { + Self { + amount, + owner, + randomness: rand(), + header: NoteHeader::empty(), + } + } -// Broadcasts the note as an encrypted log on L1. -fn broadcast(context: &mut PrivateContext, slot: Field, note: TokenNote) { - note.broadcast(context, slot); + } - -global TokenNoteMethods = NoteInterface { - deserialize, - serialize, - compute_note_hash, - compute_nullifier, - compute_nullifier_without_context, - get_header, - set_header, - broadcast, -}; diff --git a/yarn-project/noir-contracts/contracts/token_contract/src/types/transparent_note.nr b/yarn-project/noir-contracts/contracts/token_contract/src/types/transparent_note.nr index deb2bcdf6f1..4ee641794c4 100644 --- a/yarn-project/noir-contracts/contracts/token_contract/src/types/transparent_note.nr +++ b/yarn-project/noir-contracts/contracts/token_contract/src/types/transparent_note.nr @@ -3,10 +3,11 @@ use dep::aztec::{ note::{ note_header::NoteHeader, note_interface::NoteInterface, - utils::compute_siloed_note_hash, + utils::compute_note_hash_for_consumption, }, hash::{compute_secret_hash, pedersen_hash}, context::PrivateContext, + protocol_types::traits::{Serialize, Deserialize, Empty} }; global TRANSPARENT_NOTE_LEN: Field = 2; @@ -22,38 +23,14 @@ struct TransparentNote { header: NoteHeader, } -impl TransparentNote { - - // CONSTRUCTORS - - pub fn new(amount: Field, secret_hash: Field) -> Self { - TransparentNote { - amount: amount, - secret_hash: secret_hash, - secret: 0, - header: NoteHeader::empty(), - } - } - - // new oracle call primitive - // get me the secret corresponding to this hash - pub fn new_from_secret(amount: Field, secret: Field) -> Self { - TransparentNote { - amount: amount, - secret_hash: compute_secret_hash(secret), - secret: secret, - header: NoteHeader::empty(), - } - } - - - // STANDARD NOTE_INTERFACE FUNCTIONS - - pub fn serialize(self) -> [Field; TRANSPARENT_NOTE_LEN] { +impl Serialize for TransparentNote { + fn serialize(self) -> [Field; TRANSPARENT_NOTE_LEN] { [self.amount, self.secret_hash] } +} - pub fn deserialize(serialized_note: [Field; TRANSPARENT_NOTE_LEN]) -> Self { +impl Deserialize for TransparentNote { + fn deserialize(serialized_note: [Field; TRANSPARENT_NOTE_LEN]) -> Self { TransparentNote { amount: serialized_note[0], secret_hash: serialized_note[1], @@ -61,79 +38,73 @@ impl TransparentNote { header: NoteHeader::empty(), } } +} - pub fn compute_note_hash(self) -> Field { +impl Empty for TransparentNote { + fn empty() -> Self { + TransparentNote::new(0, 0) + } +} + +impl NoteInterface for TransparentNote { + + fn compute_note_content_hash(self) -> Field { // TODO(#1205) Should use a non-zero generator index. - pedersen_hash([ - self.amount, - self.secret_hash, - ],0) + pedersen_hash(self.serialize(), 0) } - pub fn compute_nullifier(self, _context: &mut PrivateContext) -> Field { + fn compute_nullifier(self, _context: &mut PrivateContext) -> Field { self.compute_nullifier_without_context() } - pub fn compute_nullifier_without_context(self) -> Field { - // TODO(#1386): should use `compute_note_hash_for_read_or_nullify` once public functions inject nonce! - let siloed_note_hash = compute_siloed_note_hash(TransparentNoteMethods, self); + fn compute_nullifier_without_context(self) -> Field { + let siloed_note_hash = compute_note_hash_for_consumption(self); // TODO(#1205) Should use a non-zero generator index. pedersen_hash([self.secret, siloed_note_hash],0) } - pub fn set_header(&mut self, header: NoteHeader) { + + fn set_header(&mut self, header: NoteHeader) { self.header = header; } - - // CUSTOM FUNCTIONS FOR THIS NOTE TYPE - - pub fn knows_secret(self, secret: Field) { - let hash = compute_secret_hash(secret); - assert(self.secret_hash == hash); + fn get_header(note: TransparentNote) -> NoteHeader { + note.header } -} - -fn deserialize(serialized_note: [Field; TRANSPARENT_NOTE_LEN]) -> TransparentNote { - TransparentNote::deserialize(serialized_note) -} - -fn serialize(note: TransparentNote) -> [Field; TRANSPARENT_NOTE_LEN] { - note.serialize() -} -fn compute_note_hash(note: TransparentNote) -> Field { - note.compute_note_hash() + fn broadcast(self, context: &mut PrivateContext, slot: Field) { + assert(false, "TransparentNote does not support broadcast"); + } } -fn compute_nullifier(note: TransparentNote, context: &mut PrivateContext) -> Field { - note.compute_nullifier(context) -} +impl TransparentNote { -fn compute_nullifier_without_context(note: TransparentNote) -> Field { - note.compute_nullifier_without_context() -} + // CONSTRUCTORS -fn get_header(note: TransparentNote) -> NoteHeader { - note.header -} + pub fn new(amount: Field, secret_hash: Field) -> Self { + TransparentNote { + amount: amount, + secret_hash: secret_hash, + secret: 0, + header: NoteHeader::empty(), + } + } + // new oracle call primitive + // get me the secret corresponding to this hash + pub fn new_from_secret(amount: Field, secret: Field) -> Self { + TransparentNote { + amount: amount, + secret_hash: compute_secret_hash(secret), + secret: secret, + header: NoteHeader::empty(), + } + } -fn set_header(note: &mut TransparentNote, header: NoteHeader) { - note.set_header(header) -} + // CUSTOM FUNCTIONS FOR THIS NOTE TYPE -fn broadcast(context: &mut PrivateContext, slot: Field, note: TransparentNote) { - assert(false, "TransparentNote does not support broadcast"); + pub fn knows_secret(self, secret: Field) { + let hash = compute_secret_hash(secret); + assert(self.secret_hash == hash); + } } - -global TransparentNoteMethods = NoteInterface { - deserialize, - serialize, - compute_note_hash, - compute_nullifier, - compute_nullifier_without_context, - get_header, - set_header, - broadcast, -}; // docs:end:token_types_all \ No newline at end of file diff --git a/yarn-project/noir-contracts/contracts/uniswap_contract/src/main.nr b/yarn-project/noir-contracts/contracts/uniswap_contract/src/main.nr index 35a4d520af1..c06630b6f92 100644 --- a/yarn-project/noir-contracts/contracts/uniswap_contract/src/main.nr +++ b/yarn-project/noir-contracts/contracts/uniswap_contract/src/main.nr @@ -17,12 +17,6 @@ contract Uniswap { context::{PrivateContext, PublicContext, Context}, oracle::{context::get_portal_address}, state_vars::{map::Map, public_state::PublicState}, - types::type_serialization::bool_serialization::{ - BoolSerializationMethods, BOOL_SERIALIZED_LEN, - }, - types::type_serialization::field_serialization::{ - FieldSerializationMethods, FIELD_SERIALIZED_LEN, - }, }; use dep::authwit::auth::{IS_VALID_SELECTOR, assert_current_call_valid_authwit_public, compute_authwit_message_hash}; @@ -32,10 +26,10 @@ contract Uniswap { struct Storage { // like with account contracts, stores the approval message on a slot and tracks if they are active - approved_action: Map>, + approved_action: Map>, // tracks the nonce used to create the approval message for burning funds // gets incremented each time after use to prevent replay attacks - nonce_for_burn_approval: PublicState, + nonce_for_burn_approval: PublicState, } impl Storage { @@ -45,10 +39,10 @@ contract Uniswap { context, 1, |context, slot| { - PublicState::new(context, slot, BoolSerializationMethods) + PublicState::new(context, slot) }, ), - nonce_for_burn_approval: PublicState::new(context, 2, FieldSerializationMethods), + nonce_for_burn_approval: PublicState::new(context, 2), } } } diff --git a/yarn-project/noir-contracts/package.json b/yarn-project/noir-contracts/package.json index 6845b1f90ff..38bf26e463b 100644 --- a/yarn-project/noir-contracts/package.json +++ b/yarn-project/noir-contracts/package.json @@ -14,7 +14,7 @@ "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", - "build:contracts": "./scripts/compile.sh && ./scripts/generate-types.sh" + "build:contracts": "./scripts/compile.sh && ./scripts/transpile.sh && ./scripts/generate-types.sh" }, "inherits": [ "../package.common.json", diff --git a/yarn-project/noir-contracts/package.local.json b/yarn-project/noir-contracts/package.local.json index 6fb912b0da8..9ea766d3f11 100644 --- a/yarn-project/noir-contracts/package.local.json +++ b/yarn-project/noir-contracts/package.local.json @@ -1,7 +1,7 @@ { "scripts": { "build": "yarn clean && yarn build:contracts && tsc -b", - "build:contracts": "./scripts/compile.sh && ./scripts/generate-types.sh", + "build:contracts": "./scripts/compile.sh && ./scripts/transpile.sh && ./scripts/generate-types.sh", "clean": "rm -rf ./dest .tsbuildinfo ./src ./target" } } diff --git a/yarn-project/noir-contracts/scripts/transpile.sh b/yarn-project/noir-contracts/scripts/transpile.sh new file mode 100755 index 00000000000..c330ee0f654 --- /dev/null +++ b/yarn-project/noir-contracts/scripts/transpile.sh @@ -0,0 +1,8 @@ +#!/usr/bin/env bash +set -euo pipefail + +echo "Transpiling contracts..." +for contract_json in target/avm_test_*.json; do + echo Transpiling $contract_json... + ../../avm-transpiler/target/release/avm-transpiler $contract_json $contract_json +done \ No newline at end of file diff --git a/yarn-project/noir-protocol-circuits/package.json b/yarn-project/noir-protocol-circuits/package.json index 358e89ccc14..9d1845d8ee8 100644 --- a/yarn-project/noir-protocol-circuits/package.json +++ b/yarn-project/noir-protocol-circuits/package.json @@ -39,6 +39,7 @@ }, "devDependencies": { "@aztec/circuit-types": "workspace:^", + "@aztec/kv-store": "workspace:^", "@aztec/merkle-tree": "workspace:^", "@jest/globals": "^29.5.0", "@types/jest": "^29.5.0", diff --git a/yarn-project/noir-protocol-circuits/src/__snapshots__/index.test.ts.snap b/yarn-project/noir-protocol-circuits/src/__snapshots__/index.test.ts.snap index 62013a5826c..ab1e9fb1f99 100644 --- a/yarn-project/noir-protocol-circuits/src/__snapshots__/index.test.ts.snap +++ b/yarn-project/noir-protocol-circuits/src/__snapshots__/index.test.ts.snap @@ -1,433 +1,334 @@ // Jest Snapshot v1, https://goo.gl/fbAQLP -exports[`Noir compatibility tests (interop_testing.nr) Complete Address matches Noir 1`] = `"0x1a6e89b034478713c7a9f1c77fb80af995f708f6f208bf352b4dda2124739109"`; +exports[`Noir compatibility tests (interop_testing.nr) Address from partial matches Noir 1`] = `"0x0447f893197175723deb223696e2e96dbba1e707ee8507766373558877e74197"`; -exports[`Noir compatibility tests (interop_testing.nr) Complete Address matches Noir 2`] = ` -Point { - "kind": "point", - "x": Fr { - "asBigInt": 1n, - "asBuffer": { - "data": [ - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 1, - ], - "type": "Buffer", - }, - }, - "y": Fr { - "asBigInt": 2n, - "asBuffer": { - "data": [ - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 2, - ], - "type": "Buffer", - }, - }, -} -`; - -exports[`Noir compatibility tests (interop_testing.nr) Complete Address matches Noir 3`] = `"0x197673f31940878b2d6c681223dbed9cfacd2f722cbe30155225b2ada17778db"`; +exports[`Noir compatibility tests (interop_testing.nr) Address matches Noir 1`] = `"0x2fd71a4f0742364f194dd16d0ae32d2f47845ddc7f5d328f37d4148b565c4123"`; exports[`Noir compatibility tests (interop_testing.nr) ComputeContractAddressFromPartial matches Noir 1`] = `"0x0b487ff2900ae1178e131bfe333fdbc351beef658f7c0d62db2801429b1aab75"`; exports[`Noir compatibility tests (interop_testing.nr) Function leaf matches noir 1`] = `"0x1ad8ece7f40e63d011ae47c6ce6cdaf31d632a23f5cf35bbeaaf69c8302afdbc"`; -exports[`Noir compatibility tests (interop_testing.nr) Public call stack item matches noir 1`] = `"0x10e265bf51be6945c10e526a86a928810429ac2d34c5fcda469245f0bb56abf6"`; +exports[`Noir compatibility tests (interop_testing.nr) Public call stack item matches noir 1`] = `"0x06dc9c35290e1868f41fc9f45bca49482bf5d5c000c789d6c74e53bce0f686f8"`; + +exports[`Noir compatibility tests (interop_testing.nr) Public call stack item request matches noir 1`] = `"0x05c5d32c38f3de19dbfa92e25746e1db1a887d5b02b79dd915c0f50e9fc51dcd"`; -exports[`Noir compatibility tests (interop_testing.nr) Public call stack item request matches noir 1`] = `"0x0edc0b5221e098c129545ba693368cddc0e6950bb11baa4595b310fc1fa24b5f"`; +exports[`Noir compatibility tests (interop_testing.nr) Public key hash matches Noir 1`] = `"0x1923a6246e305720b6aaf751fde0342613e93c82e455c3831e28375c16dd40d8"`; exports[`Noir compatibility tests (interop_testing.nr) TxRequest Hash matches Noir 1`] = `"0x0b487ff2900ae1178e131bfe333fdbc351beef658f7c0d62db2801429b1aab75"`; exports[`Private kernel Executes private kernel init circuit for a contract deployment 1`] = ` KernelCircuitPublicInputs { "constants": CombinedConstantData { - "blockHeader": BlockHeader { - "archiveRoot": Fr { - "asBigInt": 10561895175368852737061915973188839857007468377789560793687187642867659280638n, - "asBuffer": { - "data": [ - 23, - 89, - 210, - 33, - 121, - 84, - 25, - 80, - 63, - 134, - 192, - 50, - 232, - 248, - 118, - 47, - 43, - 115, - 158, - 116, - 131, - 80, - 153, - 88, - 75, - 101, - 49, - 245, - 242, - 115, - 144, - 254, - ], - "type": "Buffer", - }, - }, - "contractTreeRoot": Fr { - "asBigInt": 11033938207523021649122316027295742559227608161317650429835019071130941169536n, - "asBuffer": { - "data": [ - 24, - 100, - 252, - 218, - 168, - 15, - 242, - 113, - 145, - 84, - 250, - 124, - 138, - 144, - 80, - 102, - 41, - 114, - 112, - 113, - 104, - 214, - 158, - 172, - 157, - 182, - 253, - 49, - 16, - 130, - 159, - 128, - ], - "type": "Buffer", - }, - }, - "globalVariablesHash": Fr { - "asBigInt": 14483571110897883400419490783710119837459619381345566311432831352122387488397n, - "asBuffer": { - "data": [ - 32, - 5, - 105, - 38, - 124, - 15, - 115, - 172, - 137, - 170, - 164, - 20, - 35, - 147, - 152, - 219, - 148, - 69, - 221, - 74, - 211, - 168, - 207, - 55, - 1, - 92, - 213, - 91, - 141, - 76, - 94, - 141, - ], - "type": "Buffer", - }, - }, - "l1ToL2MessageTreeRoot": Fr { - "asBigInt": 11033938207523021649122316027295742559227608161317650429835019071130941169536n, - "asBuffer": { - "data": [ - 24, - 100, - 252, - 218, - 168, - 15, - 242, - 113, - 145, - 84, - 250, - 124, - 138, - 144, - 80, - 102, - 41, - 114, - 112, - 113, - 104, - 214, - 158, - 172, - 157, - 182, - 253, - 49, - 16, - 130, - 159, - 128, - ], - "type": "Buffer", - }, + "historicalHeader": Header { + "bodyHash": { + "data": [ + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + ], + "type": "Buffer", }, - "noteHashTreeRoot": Fr { - "asBigInt": 10127882181290008410413105921460858232892226592306749913988016925836213768395n, - "asBuffer": { - "data": [ - 22, - 100, - 45, - 156, - 205, - 131, - 70, - 196, - 3, - 170, - 76, - 63, - 164, - 81, - 23, - 139, - 34, - 83, - 74, - 39, - 3, - 92, - 218, - 166, - 236, - 52, - 174, - 83, - 178, - 156, - 80, - 203, - ], - "type": "Buffer", - }, + "globalVariables": { + "blockNumber": "0x0000000000000000000000000000000000000000000000000000000000000000", + "chainId": "0x0000000000000000000000000000000000000000000000000000000000000000", + "timestamp": "0x0000000000000000000000000000000000000000000000000000000000000000", + "version": "0x0000000000000000000000000000000000000000000000000000000000000000", }, - "nullifierTreeRoot": Fr { - "asBigInt": 5342309968596764527275045470866818007603635181649161546597860399861598581368n, - "asBuffer": { - "data": [ - 11, - 207, - 163, - 233, - 241, - 168, - 146, - 46, - 233, - 44, - 109, - 201, - 100, - 214, - 89, - 89, - 7, - 193, - 128, - 74, - 134, - 117, - 55, - 116, - 50, - 43, - 70, - 143, - 105, - 212, - 242, - 120, - ], - "type": "Buffer", + "lastArchive": AppendOnlyTreeSnapshot { + "nextAvailableLeafIndex": 0, + "root": Fr { + "asBigInt": 0n, + "asBuffer": { + "data": [ + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + ], + "type": "Buffer", + }, }, }, - "privateKernelVkTreeRoot": Fr { - "asBigInt": 0n, - "asBuffer": { - "data": [ - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - ], - "type": "Buffer", + "state": StateReference { + "l1ToL2MessageTree": AppendOnlyTreeSnapshot { + "nextAvailableLeafIndex": 0, + "root": Fr { + "asBigInt": 11033938207523021649122316027295742559227608161317650429835019071130941169536n, + "asBuffer": { + "data": [ + 24, + 100, + 252, + 218, + 168, + 15, + 242, + 113, + 145, + 84, + 250, + 124, + 138, + 144, + 80, + 102, + 41, + 114, + 112, + 113, + 104, + 214, + 158, + 172, + 157, + 182, + 253, + 49, + 16, + 130, + 159, + 128, + ], + "type": "Buffer", + }, + }, }, - }, - "publicDataTreeRoot": Fr { - "asBigInt": 5785871043333994658400733180052743689641713274194136017445890613179954325976n, - "asBuffer": { - "data": [ - 12, - 202, - 175, - 220, - 156, - 53, - 55, - 67, - 151, - 13, - 78, - 48, - 90, - 231, - 54, - 65, - 206, - 105, - 79, - 7, - 219, - 103, - 136, - 109, - 39, - 105, - 201, - 237, - 136, - 233, - 105, - 216, - ], - "type": "Buffer", + "partial": PartialStateReference { + "contractTree": AppendOnlyTreeSnapshot { + "nextAvailableLeafIndex": 0, + "root": Fr { + "asBigInt": 11033938207523021649122316027295742559227608161317650429835019071130941169536n, + "asBuffer": { + "data": [ + 24, + 100, + 252, + 218, + 168, + 15, + 242, + 113, + 145, + 84, + 250, + 124, + 138, + 144, + 80, + 102, + 41, + 114, + 112, + 113, + 104, + 214, + 158, + 172, + 157, + 182, + 253, + 49, + 16, + 130, + 159, + 128, + ], + "type": "Buffer", + }, + }, + }, + "noteHashTree": AppendOnlyTreeSnapshot { + "nextAvailableLeafIndex": 0, + "root": Fr { + "asBigInt": 10127882181290008410413105921460858232892226592306749913988016925836213768395n, + "asBuffer": { + "data": [ + 22, + 100, + 45, + 156, + 205, + 131, + 70, + 196, + 3, + 170, + 76, + 63, + 164, + 81, + 23, + 139, + 34, + 83, + 74, + 39, + 3, + 92, + 218, + 166, + 236, + 52, + 174, + 83, + 178, + 156, + 80, + 203, + ], + "type": "Buffer", + }, + }, + }, + "nullifierTree": AppendOnlyTreeSnapshot { + "nextAvailableLeafIndex": 0, + "root": Fr { + "asBigInt": 5342309968596764527275045470866818007603635181649161546597860399861598581368n, + "asBuffer": { + "data": [ + 11, + 207, + 163, + 233, + 241, + 168, + 146, + 46, + 233, + 44, + 109, + 201, + 100, + 214, + 89, + 89, + 7, + 193, + 128, + 74, + 134, + 117, + 55, + 116, + 50, + 43, + 70, + 143, + 105, + 212, + 242, + 120, + ], + "type": "Buffer", + }, + }, + }, + "publicDataTree": AppendOnlyTreeSnapshot { + "nextAvailableLeafIndex": 0, + "root": Fr { + "asBigInt": 13940981882517738105981911020707002777955674699044852872880094407282064863456n, + "asBuffer": { + "data": [ + 30, + 210, + 80, + 237, + 115, + 219, + 110, + 112, + 128, + 92, + 78, + 252, + 240, + 5, + 110, + 134, + 149, + 183, + 156, + 211, + 186, + 65, + 142, + 130, + 124, + 24, + 77, + 238, + 108, + 111, + 176, + 224, + ], + "type": "Buffer", + }, + }, + }, }, }, }, "txContext": TxContext { "chainId": Fr { - "asBigInt": 0n, + "asBigInt": 31337n, "asBuffer": { "data": [ 0, @@ -460,89 +361,89 @@ KernelCircuitPublicInputs { 0, 0, 0, - 0, - 0, + 122, + 105, ], "type": "Buffer", }, }, "contractDeploymentData": ContractDeploymentData { "constructorVkHash": Fr { - "asBigInt": 4946902893997605007258693448883037341256770656195244398892734919432197304822n, + "asBigInt": 1583326240861609738393684596312518968005858067213923665222866669013140837326n, "asBuffer": { "data": [ - 10, - 239, - 217, - 10, - 105, - 166, - 67, - 50, - 76, - 123, - 240, - 169, + 3, + 128, + 33, + 130, + 79, 189, - 59, - 35, - 173, - 160, - 144, - 173, - 136, - 55, - 115, - 253, - 240, - 176, - 173, - 82, - 169, + 152, + 187, + 14, + 56, + 139, + 14, + 254, + 24, 247, - 214, - 241, - 246, + 46, + 147, + 80, + 247, + 69, + 100, + 129, + 113, + 69, + 57, + 186, + 88, + 61, + 227, + 113, + 19, + 206, ], "type": "Buffer", }, }, "contractAddressSalt": Fr { - "asBigInt": 13918524182926832455178861490988425129196887976468020413394338716458484696156n, + "asBigInt": 18960597193497228634655620687090300150990844839001764515818734920414615950424n, "asBuffer": { "data": [ - 30, - 197, - 155, - 3, - 19, - 250, - 80, - 67, - 2, - 195, - 51, - 111, - 201, - 17, - 214, - 136, - 237, - 174, - 103, - 196, - 251, - 242, 41, - 214, - 140, - 127, - 54, - 237, - 135, - 151, - 4, - 92, + 235, + 81, + 85, + 78, + 234, + 129, + 253, + 22, + 223, + 249, + 80, + 61, + 139, + 48, + 200, + 107, + 132, + 228, + 114, + 181, + 85, + 171, + 38, + 47, + 3, + 137, + 78, + 8, + 197, + 212, + 88, ], "type": "Buffer", }, @@ -550,122 +451,122 @@ KernelCircuitPublicInputs { "deployerPublicKey": Point { "kind": "point", "x": Fr { - "asBigInt": 13513162828633936749079339485623471377790691038584182237805001838837073529635n, + "asBigInt": 12697532002339620472742432431024876157955324367347542642615813365560119669742n, "asBuffer": { "data": [ - 29, - 224, - 45, - 218, - 202, - 198, - 210, + 28, + 18, + 140, + 102, + 4, + 43, + 63, + 59, + 161, + 25, + 83, + 110, + 101, + 86, + 173, + 111, + 187, + 60, + 253, + 28, + 27, + 71, + 247, + 241, + 124, + 56, + 245, + 205, + 103, 244, - 39, - 229, - 240, - 211, - 206, - 89, - 215, - 41, - 79, - 20, - 98, - 128, - 69, - 93, - 212, - 197, - 130, - 37, - 78, - 11, - 76, - 37, - 75, - 35, + 51, + 238, ], "type": "Buffer", }, }, "y": Fr { - "asBigInt": 16193209371316637857741102275574203818928254115376185574760513755303226932941n, + "asBigInt": 11975354909605390651205529513933593489556044760044635062162622799898161844035n, "asBuffer": { "data": [ - 35, - 205, - 8, - 29, - 254, - 156, - 13, - 24, - 115, - 182, - 90, - 54, - 160, + 26, + 121, + 207, 136, - 88, - 231, - 58, - 155, - 48, - 208, - 51, - 158, - 148, - 196, - 145, - 93, + 223, + 172, + 217, + 195, 113, - 16, - 226, - 240, - 126, + 148, + 204, + 232, + 229, 205, + 128, + 233, + 116, + 228, + 140, + 190, + 169, + 170, + 76, + 253, + 104, + 125, + 155, + 29, + 59, + 30, + 15, + 67, ], "type": "Buffer", }, }, }, "functionTreeRoot": Fr { - "asBigInt": 5733828181279984846924392032049330885938310333906649337231112228024374578179n, + "asBigInt": 3721272162218164835126864072380876236437289718664503134823660124384776633747n, "asBuffer": { "data": [ - 12, - 173, - 59, - 83, - 145, - 228, - 10, - 248, - 116, - 62, - 16, - 83, + 8, + 58, + 42, + 87, + 236, 192, - 21, - 225, - 106, - 186, - 198, - 16, - 10, - 139, + 172, + 118, + 190, + 87, 145, - 117, - 18, - 192, - 131, - 203, - 76, - 187, - 140, - 204, - 3, + 155, + 200, + 38, + 88, + 218, + 65, + 254, + 215, + 201, + 170, + 196, + 87, + 90, + 45, + 201, + 211, + 61, + 167, + 107, + 61, + 147, ], "type": "Buffer", }, @@ -702,7 +603,7 @@ KernelCircuitPublicInputs { "isFeePaymentTx": false, "isRebatePaymentTx": false, "version": Fr { - "asBigInt": 0n, + "asBigInt": 1n, "asBuffer": { "data": [ 0, @@ -736,7 +637,7 @@ KernelCircuitPublicInputs { 0, 0, 0, - 0, + 1, ], "type": "Buffer", }, @@ -972,7 +873,7 @@ KernelCircuitPublicInputs { }, "encryptedLogsHash": [ Fr { - "asBigInt": 116881760094330735023399760917603536324n, + "asBigInt": 220631985307853222392411421903717783230n, "asBuffer": { "data": [ 0, @@ -991,28 +892,28 @@ KernelCircuitPublicInputs { 0, 0, 0, - 87, - 238, - 155, - 177, - 38, - 64, - 133, - 236, - 244, + 165, + 252, + 44, 186, - 130, - 116, - 178, - 51, - 205, - 196, + 105, + 28, + 161, + 120, + 75, + 72, + 61, + 67, + 244, + 201, + 214, + 190, ], "type": "Buffer", }, }, Fr { - "asBigInt": 184145148802329932417389828290829878776n, + "asBigInt": 215807232426364884712946164821332168459n, "asBuffer": { "data": [ 0, @@ -1031,22 +932,22 @@ KernelCircuitPublicInputs { 0, 0, 0, - 138, - 137, - 16, - 204, - 107, - 147, - 180, - 57, + 162, + 90, + 246, + 11, 154, - 30, - 189, + 6, + 31, + 75, + 53, + 138, + 157, + 77, + 165, + 25, 143, - 191, - 180, - 5, - 248, + 11, ], "type": "Buffer", }, @@ -1055,7 +956,7 @@ KernelCircuitPublicInputs { "newCommitments": [ SideEffect { "counter": Fr { - "asBigInt": 0n, + "asBigInt": 3n, "asBuffer": { "data": [ 0, @@ -1089,129 +990,47 @@ KernelCircuitPublicInputs { 0, 0, 0, - 0, + 3, ], "type": "Buffer", }, }, "value": Fr { - "asBigInt": 8240305160289019381083256608879877337799989644951984162356314972952353509340n, + "asBigInt": 14136054938328499998261206456350784356519564659006381491900107610202124617094n, "asBuffer": { "data": [ - 18, - 55, - 216, - 241, - 215, - 66, - 41, - 46, - 0, - 125, - 4, - 230, - 109, - 173, - 105, - 145, - 109, - 210, - 57, - 161, - 19, - 69, - 62, - 207, - 135, - 242, - 27, - 70, + 31, + 64, + 185, + 59, + 60, + 174, + 61, + 123, + 71, + 45, + 163, + 106, + 166, + 95, + 204, + 140, + 213, + 14, + 249, + 25, + 252, + 194, + 213, + 136, + 191, + 140, + 175, 147, - 51, - 15, - 220, - ], - "type": "Buffer", - }, - }, - }, - SideEffect { - "counter": Fr { - "asBigInt": 0n, - "asBuffer": { - "data": [ - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - ], - "type": "Buffer", - }, - }, - "value": Fr { - "asBigInt": 0n, - "asBuffer": { - "data": [ - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, + 229, + 72, + 133, + 134, ], "type": "Buffer", }, @@ -6301,202 +6120,7 @@ KernelCircuitPublicInputs { }, }, }, - ], - "newContracts": [ - NewContractData { - "contractAddress": AztecAddress { - "asBigInt": 17136208615489370595324712951074318609059907391564891634353187633922419015940n, - "asBuffer": { - "data": [ - 37, - 226, - 192, - 23, - 245, - 218, - 31, - 153, - 68, - 1, - 230, - 29, - 38, - 190, - 67, - 94, - 60, - 250, - 38, - 239, - 238, - 120, - 76, - 107, - 78, - 148, - 127, - 118, - 81, - 189, - 65, - 4, - ], - "type": "Buffer", - }, - }, - "functionTreeRoot": Fr { - "asBigInt": 5733828181279984846924392032049330885938310333906649337231112228024374578179n, - "asBuffer": { - "data": [ - 12, - 173, - 59, - 83, - 145, - 228, - 10, - 248, - 116, - 62, - 16, - 83, - 192, - 21, - 225, - 106, - 186, - 198, - 16, - 10, - 139, - 145, - 117, - 18, - 192, - 131, - 203, - 76, - 187, - 140, - 204, - 3, - ], - "type": "Buffer", - }, - }, - "portalContractAddress": EthAddress { - "buffer": { - "data": [ - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - ], - "type": "Buffer", - }, - }, - }, - ], - "newL2ToL1Msgs": [ - Fr { - "asBigInt": 0n, - "asBuffer": { - "data": [ - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - ], - "type": "Buffer", - }, - }, - Fr { - "asBigInt": 0n, - "asBuffer": { - "data": [ - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - ], - "type": "Buffer", - }, - }, - ], - "newNullifiers": [ - SideEffectLinkedToNoteHash { + SideEffect { "counter": Fr { "asBigInt": 0n, "asBuffer": { @@ -6537,7 +6161,7 @@ KernelCircuitPublicInputs { "type": "Buffer", }, }, - "noteHash": Fr { + "value": Fr { "asBigInt": 0n, "asBuffer": { "data": [ @@ -6577,47 +6201,202 @@ KernelCircuitPublicInputs { "type": "Buffer", }, }, - "value": Fr { - "asBigInt": 2567833015098009695197683741929052765845567165156134398536953771337640275556n, + }, + ], + "newContracts": [ + NewContractData { + "contractAddress": AztecAddress { + "asBigInt": 12846613415199169272271044003892051734354428916844772940648565627858426402568n, "asBuffer": { "data": [ - 5, - 173, + 28, + 102, + 236, + 238, + 243, + 156, + 64, + 230, + 13, + 129, + 255, + 67, + 45, + 128, + 244, + 97, + 77, + 110, + 184, + 171, + 239, + 58, + 236, + 77, + 13, + 115, + 52, + 66, + 229, + 50, + 23, + 8, + ], + "type": "Buffer", + }, + }, + "functionTreeRoot": Fr { + "asBigInt": 3721272162218164835126864072380876236437289718664503134823660124384776633747n, + "asBuffer": { + "data": [ + 8, + 58, + 42, 87, - 140, - 34, - 169, + 236, + 192, + 172, + 118, + 190, + 87, + 145, + 155, + 200, + 38, + 88, + 218, + 65, + 254, + 215, + 201, + 170, + 196, + 87, + 90, + 45, + 201, + 211, + 61, + 167, 107, - 104, - 191, - 247, - 76, - 132, - 57, - 231, - 109, - 208, - 71, - 246, - 205, - 22, - 148, - 50, - 20, - 219, - 39, - 232, - 4, - 177, - 237, - 31, - 110, - 100, + 61, + 147, + ], + "type": "Buffer", + }, + }, + "portalContractAddress": EthAddress { + "buffer": { + "data": [ + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, ], "type": "Buffer", }, }, }, + ], + "newL2ToL1Msgs": [ + Fr { + "asBigInt": 0n, + "asBuffer": { + "data": [ + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + ], + "type": "Buffer", + }, + }, + Fr { + "asBigInt": 0n, + "asBuffer": { + "data": [ + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + ], + "type": "Buffer", + }, + }, + ], + "newNullifiers": [ SideEffectLinkedToNoteHash { "counter": Fr { "asBigInt": 0n, @@ -6700,41 +6479,41 @@ KernelCircuitPublicInputs { }, }, "value": Fr { - "asBigInt": 131391450486342918604555900920288880952936359657946230216264970318333633026n, + "asBigInt": 16820956071491106255363061370815528401274596983393081031060346934333436515478n, "asBuffer": { "data": [ - 0, - 74, - 93, - 107, - 195, - 78, - 132, - 197, - 163, - 215, - 166, 37, - 163, - 119, - 47, - 77, - 47, - 132, - 199, - 212, + 48, + 82, + 225, + 53, + 75, + 28, + 133, + 187, + 180, + 13, + 34, + 100, + 118, 102, - 55, - 105, - 30, - 246, - 78, - 226, - 113, - 30, - 108, - 98, - 2, + 175, + 156, + 157, + 215, + 80, + 177, + 242, + 177, + 232, + 173, + 217, + 0, + 21, + 50, + 174, + 212, + 150, ], "type": "Buffer", }, @@ -6742,7 +6521,7 @@ KernelCircuitPublicInputs { }, SideEffectLinkedToNoteHash { "counter": Fr { - "asBigInt": 1n, + "asBigInt": 2n, "asBuffer": { "data": [ 0, @@ -6776,7 +6555,7 @@ KernelCircuitPublicInputs { 0, 0, 0, - 1, + 2, ], "type": "Buffer", }, @@ -6822,41 +6601,41 @@ KernelCircuitPublicInputs { }, }, "value": Fr { - "asBigInt": 11322578994265849565401386951246010528140686424276775038536393177707801557130n, + "asBigInt": 5735195398790857731328283581018066889458786547813035014232036710200459375595n, "asBuffer": { "data": [ - 25, - 8, - 90, - 68, - 120, - 196, - 170, - 57, - 148, - 212, - 165, - 147, - 94, - 175, - 94, - 13, - 88, - 114, - 106, - 117, - 141, - 57, - 138, - 151, - 246, - 52, - 223, - 34, - 211, - 61, - 56, - 138, + 12, + 174, + 1, + 108, + 124, + 99, + 115, + 24, + 59, + 10, + 156, + 160, + 158, + 168, + 84, + 197, + 24, + 190, + 48, + 167, + 160, + 200, + 133, + 237, + 1, + 235, + 162, + 162, + 4, + 99, + 179, + 235, ], "type": "Buffer", }, @@ -6864,7 +6643,7 @@ KernelCircuitPublicInputs { }, SideEffectLinkedToNoteHash { "counter": Fr { - "asBigInt": 0n, + "asBigInt": 1n, "asBuffer": { "data": [ 0, @@ -6898,7 +6677,7 @@ KernelCircuitPublicInputs { 0, 0, 0, - 0, + 1, ], "type": "Buffer", }, @@ -6944,41 +6723,41 @@ KernelCircuitPublicInputs { }, }, "value": Fr { - "asBigInt": 0n, + "asBigInt": 11839557548471584510452581119038172413276872385031485614679286956310180824554n, "asBuffer": { "data": [ - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, + 26, + 44, + 243, + 188, + 205, + 85, + 111, + 133, + 83, + 145, + 200, + 148, + 153, + 26, + 75, + 95, + 136, + 219, + 6, + 171, + 104, + 146, + 138, + 178, + 155, + 132, + 126, + 255, + 177, + 94, + 133, + 234, ], "type": "Buffer", }, @@ -14304,89 +14083,8 @@ KernelCircuitPublicInputs { }, }, }, - ], - "optionallyRevealedData": [ - OptionallyRevealedData { - "callStackItemHash": Fr { - "asBigInt": 0n, - "asBuffer": { - "data": [ - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - ], - "type": "Buffer", - }, - }, - "calledFromL1": false, - "calledFromPublicL2": false, - "functionData": FunctionData { - "isConstructor": false, - "isInternal": false, - "isPrivate": false, - "selector": FunctionSelector { - "value": 0, - }, - }, - "payFeeFromL1": false, - "payFeeFromPublicL2": false, - "portalContractAddress": EthAddress { - "buffer": { - "data": [ - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - ], - "type": "Buffer", - }, - }, - "vkHash": Fr { + SideEffectLinkedToNoteHash { + "counter": Fr { "asBigInt": 0n, "asBuffer": { "data": [ @@ -14426,9 +14124,7 @@ KernelCircuitPublicInputs { "type": "Buffer", }, }, - }, - OptionallyRevealedData { - "callStackItemHash": Fr { + "noteHash": Fr { "asBigInt": 0n, "asBuffer": { "data": [ @@ -14468,46 +14164,7 @@ KernelCircuitPublicInputs { "type": "Buffer", }, }, - "calledFromL1": false, - "calledFromPublicL2": false, - "functionData": FunctionData { - "isConstructor": false, - "isInternal": false, - "isPrivate": false, - "selector": FunctionSelector { - "value": 0, - }, - }, - "payFeeFromL1": false, - "payFeeFromPublicL2": false, - "portalContractAddress": EthAddress { - "buffer": { - "data": [ - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - ], - "type": "Buffer", - }, - }, - "vkHash": Fr { + "value": Fr { "asBigInt": 0n, "asBuffer": { "data": [ @@ -14548,8 +14205,10 @@ KernelCircuitPublicInputs { }, }, }, - OptionallyRevealedData { - "callStackItemHash": Fr { + ], + "nullifierKeyValidationRequests": [ + NullifierKeyValidationRequestContext { + "contractAddress": AztecAddress { "asBigInt": 0n, "asBuffer": { "data": [ @@ -14589,46 +14248,90 @@ KernelCircuitPublicInputs { "type": "Buffer", }, }, - "calledFromL1": false, - "calledFromPublicL2": false, - "functionData": FunctionData { - "isConstructor": false, - "isInternal": false, - "isPrivate": false, - "selector": FunctionSelector { - "value": 0, + "publicKey": Point { + "kind": "point", + "x": Fr { + "asBigInt": 0n, + "asBuffer": { + "data": [ + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + ], + "type": "Buffer", + }, }, - }, - "payFeeFromL1": false, - "payFeeFromPublicL2": false, - "portalContractAddress": EthAddress { - "buffer": { - "data": [ - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - ], - "type": "Buffer", + "y": Fr { + "asBigInt": 0n, + "asBuffer": { + "data": [ + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + ], + "type": "Buffer", + }, }, }, - "vkHash": Fr { + "secretKey": Fq { "asBigInt": 0n, "asBuffer": { "data": [ @@ -14669,87 +14372,8 @@ KernelCircuitPublicInputs { }, }, }, - OptionallyRevealedData { - "callStackItemHash": Fr { - "asBigInt": 0n, - "asBuffer": { - "data": [ - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - ], - "type": "Buffer", - }, - }, - "calledFromL1": false, - "calledFromPublicL2": false, - "functionData": FunctionData { - "isConstructor": false, - "isInternal": false, - "isPrivate": false, - "selector": FunctionSelector { - "value": 0, - }, - }, - "payFeeFromL1": false, - "payFeeFromPublicL2": false, - "portalContractAddress": EthAddress { - "buffer": { - "data": [ - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - ], - "type": "Buffer", - }, - }, - "vkHash": Fr { + NullifierKeyValidationRequestContext { + "contractAddress": AztecAddress { "asBigInt": 0n, "asBuffer": { "data": [ @@ -14789,12 +14413,9 @@ KernelCircuitPublicInputs { "type": "Buffer", }, }, - }, - ], - "privateCallStack": [ - CallRequest { - "callerContext": CallerContext { - "msgSender": AztecAddress { + "publicKey": Point { + "kind": "point", + "x": Fr { "asBigInt": 0n, "asBuffer": { "data": [ @@ -14834,7 +14455,7 @@ KernelCircuitPublicInputs { "type": "Buffer", }, }, - "storageContractAddress": AztecAddress { + "y": Fr { "asBigInt": 0n, "asBuffer": { "data": [ @@ -14875,7 +14496,7 @@ KernelCircuitPublicInputs { }, }, }, - "callerContractAddress": AztecAddress { + "secretKey": Fq { "asBigInt": 0n, "asBuffer": { "data": [ @@ -14915,7 +14536,9 @@ KernelCircuitPublicInputs { "type": "Buffer", }, }, - "endSideEffectCounter": Fr { + }, + NullifierKeyValidationRequestContext { + "contractAddress": AztecAddress { "asBigInt": 0n, "asBuffer": { "data": [ @@ -14955,7 +14578,90 @@ KernelCircuitPublicInputs { "type": "Buffer", }, }, - "hash": Fr { + "publicKey": Point { + "kind": "point", + "x": Fr { + "asBigInt": 0n, + "asBuffer": { + "data": [ + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + ], + "type": "Buffer", + }, + }, + "y": Fr { + "asBigInt": 0n, + "asBuffer": { + "data": [ + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + ], + "type": "Buffer", + }, + }, + }, + "secretKey": Fq { "asBigInt": 0n, "asBuffer": { "data": [ @@ -14995,7 +14701,9 @@ KernelCircuitPublicInputs { "type": "Buffer", }, }, - "startSideEffectCounter": Fr { + }, + NullifierKeyValidationRequestContext { + "contractAddress": AztecAddress { "asBigInt": 0n, "asBuffer": { "data": [ @@ -15035,10 +14743,9 @@ KernelCircuitPublicInputs { "type": "Buffer", }, }, - }, - CallRequest { - "callerContext": CallerContext { - "msgSender": AztecAddress { + "publicKey": Point { + "kind": "point", + "x": Fr { "asBigInt": 0n, "asBuffer": { "data": [ @@ -15078,7 +14785,7 @@ KernelCircuitPublicInputs { "type": "Buffer", }, }, - "storageContractAddress": AztecAddress { + "y": Fr { "asBigInt": 0n, "asBuffer": { "data": [ @@ -15119,7 +14826,7 @@ KernelCircuitPublicInputs { }, }, }, - "callerContractAddress": AztecAddress { + "secretKey": Fq { "asBigInt": 0n, "asBuffer": { "data": [ @@ -15159,7 +14866,11 @@ KernelCircuitPublicInputs { "type": "Buffer", }, }, - "endSideEffectCounter": Fr { + }, + ], + "optionallyRevealedData": [ + OptionallyRevealedData { + "callStackItemHash": Fr { "asBigInt": 0n, "asBuffer": { "data": [ @@ -15199,7 +14910,46 @@ KernelCircuitPublicInputs { "type": "Buffer", }, }, - "hash": Fr { + "calledFromL1": false, + "calledFromPublicL2": false, + "functionData": FunctionData { + "isConstructor": false, + "isInternal": false, + "isPrivate": false, + "selector": FunctionSelector { + "value": 0, + }, + }, + "payFeeFromL1": false, + "payFeeFromPublicL2": false, + "portalContractAddress": EthAddress { + "buffer": { + "data": [ + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + ], + "type": "Buffer", + }, + }, + "vkHash": Fr { "asBigInt": 0n, "asBuffer": { "data": [ @@ -15239,7 +14989,9 @@ KernelCircuitPublicInputs { "type": "Buffer", }, }, - "startSideEffectCounter": Fr { + }, + OptionallyRevealedData { + "callStackItemHash": Fr { "asBigInt": 0n, "asBuffer": { "data": [ @@ -15279,91 +15031,46 @@ KernelCircuitPublicInputs { "type": "Buffer", }, }, - }, - CallRequest { - "callerContext": CallerContext { - "msgSender": AztecAddress { - "asBigInt": 0n, - "asBuffer": { - "data": [ - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - ], - "type": "Buffer", - }, + "calledFromL1": false, + "calledFromPublicL2": false, + "functionData": FunctionData { + "isConstructor": false, + "isInternal": false, + "isPrivate": false, + "selector": FunctionSelector { + "value": 0, }, - "storageContractAddress": AztecAddress { - "asBigInt": 0n, - "asBuffer": { - "data": [ - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - ], - "type": "Buffer", - }, + }, + "payFeeFromL1": false, + "payFeeFromPublicL2": false, + "portalContractAddress": EthAddress { + "buffer": { + "data": [ + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + ], + "type": "Buffer", }, }, - "callerContractAddress": AztecAddress { + "vkHash": Fr { "asBigInt": 0n, "asBuffer": { "data": [ @@ -15403,7 +15110,9 @@ KernelCircuitPublicInputs { "type": "Buffer", }, }, - "endSideEffectCounter": Fr { + }, + OptionallyRevealedData { + "callStackItemHash": Fr { "asBigInt": 0n, "asBuffer": { "data": [ @@ -15443,7 +15152,46 @@ KernelCircuitPublicInputs { "type": "Buffer", }, }, - "hash": Fr { + "calledFromL1": false, + "calledFromPublicL2": false, + "functionData": FunctionData { + "isConstructor": false, + "isInternal": false, + "isPrivate": false, + "selector": FunctionSelector { + "value": 0, + }, + }, + "payFeeFromL1": false, + "payFeeFromPublicL2": false, + "portalContractAddress": EthAddress { + "buffer": { + "data": [ + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + ], + "type": "Buffer", + }, + }, + "vkHash": Fr { "asBigInt": 0n, "asBuffer": { "data": [ @@ -15483,7 +15231,88 @@ KernelCircuitPublicInputs { "type": "Buffer", }, }, - "startSideEffectCounter": Fr { + }, + OptionallyRevealedData { + "callStackItemHash": Fr { + "asBigInt": 0n, + "asBuffer": { + "data": [ + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + ], + "type": "Buffer", + }, + }, + "calledFromL1": false, + "calledFromPublicL2": false, + "functionData": FunctionData { + "isConstructor": false, + "isInternal": false, + "isPrivate": false, + "selector": FunctionSelector { + "value": 0, + }, + }, + "payFeeFromL1": false, + "payFeeFromPublicL2": false, + "portalContractAddress": EthAddress { + "buffer": { + "data": [ + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + ], + "type": "Buffer", + }, + }, + "vkHash": Fr { "asBigInt": 0n, "asBuffer": { "data": [ @@ -15524,6 +15353,8 @@ KernelCircuitPublicInputs { }, }, }, + ], + "privateCallStack": [ CallRequest { "callerContext": CallerContext { "msgSender": AztecAddress { @@ -16744,8 +16575,6 @@ KernelCircuitPublicInputs { }, }, }, - ], - "publicCallStack": [ CallRequest { "callerContext": CallerContext { "msgSender": AztecAddress { @@ -17478,6 +17307,8 @@ KernelCircuitPublicInputs { }, }, }, + ], + "publicCallStack": [ CallRequest { "callerContext": CallerContext { "msgSender": AztecAddress { @@ -18698,10 +18529,90 @@ KernelCircuitPublicInputs { }, }, }, - ], - "publicDataReads": [ - PublicDataRead { - "leafSlot": Fr { + CallRequest { + "callerContext": CallerContext { + "msgSender": AztecAddress { + "asBigInt": 0n, + "asBuffer": { + "data": [ + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + ], + "type": "Buffer", + }, + }, + "storageContractAddress": AztecAddress { + "asBigInt": 0n, + "asBuffer": { + "data": [ + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + ], + "type": "Buffer", + }, + }, + }, + "callerContractAddress": AztecAddress { "asBigInt": 0n, "asBuffer": { "data": [ @@ -18741,8 +18652,7 @@ KernelCircuitPublicInputs { "type": "Buffer", }, }, - "sideEffectCounter": undefined, - "value": Fr { + "endSideEffectCounter": Fr { "asBigInt": 0n, "asBuffer": { "data": [ @@ -18782,9 +18692,7 @@ KernelCircuitPublicInputs { "type": "Buffer", }, }, - }, - PublicDataRead { - "leafSlot": Fr { + "hash": Fr { "asBigInt": 0n, "asBuffer": { "data": [ @@ -18824,8 +18732,7 @@ KernelCircuitPublicInputs { "type": "Buffer", }, }, - "sideEffectCounter": undefined, - "value": Fr { + "startSideEffectCounter": Fr { "asBigInt": 0n, "asBuffer": { "data": [ @@ -18866,8 +18773,90 @@ KernelCircuitPublicInputs { }, }, }, - PublicDataRead { - "leafSlot": Fr { + CallRequest { + "callerContext": CallerContext { + "msgSender": AztecAddress { + "asBigInt": 0n, + "asBuffer": { + "data": [ + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + ], + "type": "Buffer", + }, + }, + "storageContractAddress": AztecAddress { + "asBigInt": 0n, + "asBuffer": { + "data": [ + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + ], + "type": "Buffer", + }, + }, + }, + "callerContractAddress": AztecAddress { "asBigInt": 0n, "asBuffer": { "data": [ @@ -18907,8 +18896,7 @@ KernelCircuitPublicInputs { "type": "Buffer", }, }, - "sideEffectCounter": undefined, - "value": Fr { + "endSideEffectCounter": Fr { "asBigInt": 0n, "asBuffer": { "data": [ @@ -18948,9 +18936,7 @@ KernelCircuitPublicInputs { "type": "Buffer", }, }, - }, - PublicDataRead { - "leafSlot": Fr { + "hash": Fr { "asBigInt": 0n, "asBuffer": { "data": [ @@ -18990,8 +18976,7 @@ KernelCircuitPublicInputs { "type": "Buffer", }, }, - "sideEffectCounter": undefined, - "value": Fr { + "startSideEffectCounter": Fr { "asBigInt": 0n, "asBuffer": { "data": [ @@ -19032,8 +19017,90 @@ KernelCircuitPublicInputs { }, }, }, - PublicDataRead { - "leafSlot": Fr { + CallRequest { + "callerContext": CallerContext { + "msgSender": AztecAddress { + "asBigInt": 0n, + "asBuffer": { + "data": [ + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + ], + "type": "Buffer", + }, + }, + "storageContractAddress": AztecAddress { + "asBigInt": 0n, + "asBuffer": { + "data": [ + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + ], + "type": "Buffer", + }, + }, + }, + "callerContractAddress": AztecAddress { "asBigInt": 0n, "asBuffer": { "data": [ @@ -19073,8 +19140,7 @@ KernelCircuitPublicInputs { "type": "Buffer", }, }, - "sideEffectCounter": undefined, - "value": Fr { + "endSideEffectCounter": Fr { "asBigInt": 0n, "asBuffer": { "data": [ @@ -19114,9 +19180,7 @@ KernelCircuitPublicInputs { "type": "Buffer", }, }, - }, - PublicDataRead { - "leafSlot": Fr { + "hash": Fr { "asBigInt": 0n, "asBuffer": { "data": [ @@ -19156,8 +19220,7 @@ KernelCircuitPublicInputs { "type": "Buffer", }, }, - "sideEffectCounter": undefined, - "value": Fr { + "startSideEffectCounter": Fr { "asBigInt": 0n, "asBuffer": { "data": [ @@ -19198,6 +19261,8 @@ KernelCircuitPublicInputs { }, }, }, + ], + "publicDataReads": [ PublicDataRead { "leafSlot": Fr { "asBigInt": 0n, @@ -20028,9 +20093,7 @@ KernelCircuitPublicInputs { }, }, }, - ], - "publicDataUpdateRequests": [ - PublicDataUpdateRequest { + PublicDataRead { "leafSlot": Fr { "asBigInt": 0n, "asBuffer": { @@ -20071,47 +20134,8 @@ KernelCircuitPublicInputs { "type": "Buffer", }, }, - "newValue": Fr { - "asBigInt": 0n, - "asBuffer": { - "data": [ - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - ], - "type": "Buffer", - }, - }, - "oldValue": Fr { + "sideEffectCounter": undefined, + "value": Fr { "asBigInt": 0n, "asBuffer": { "data": [ @@ -20151,9 +20175,8 @@ KernelCircuitPublicInputs { "type": "Buffer", }, }, - "sideEffectCounter": undefined, }, - PublicDataUpdateRequest { + PublicDataRead { "leafSlot": Fr { "asBigInt": 0n, "asBuffer": { @@ -20194,7 +20217,8 @@ KernelCircuitPublicInputs { "type": "Buffer", }, }, - "newValue": Fr { + "sideEffectCounter": undefined, + "value": Fr { "asBigInt": 0n, "asBuffer": { "data": [ @@ -20234,49 +20258,8 @@ KernelCircuitPublicInputs { "type": "Buffer", }, }, - "oldValue": Fr { - "asBigInt": 0n, - "asBuffer": { - "data": [ - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - ], - "type": "Buffer", - }, - }, - "sideEffectCounter": undefined, }, - PublicDataUpdateRequest { + PublicDataRead { "leafSlot": Fr { "asBigInt": 0n, "asBuffer": { @@ -20317,47 +20300,8 @@ KernelCircuitPublicInputs { "type": "Buffer", }, }, - "newValue": Fr { - "asBigInt": 0n, - "asBuffer": { - "data": [ - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - ], - "type": "Buffer", - }, - }, - "oldValue": Fr { + "sideEffectCounter": undefined, + "value": Fr { "asBigInt": 0n, "asBuffer": { "data": [ @@ -20397,9 +20341,8 @@ KernelCircuitPublicInputs { "type": "Buffer", }, }, - "sideEffectCounter": undefined, }, - PublicDataUpdateRequest { + PublicDataRead { "leafSlot": Fr { "asBigInt": 0n, "asBuffer": { @@ -20440,7 +20383,8 @@ KernelCircuitPublicInputs { "type": "Buffer", }, }, - "newValue": Fr { + "sideEffectCounter": undefined, + "value": Fr { "asBigInt": 0n, "asBuffer": { "data": [ @@ -20480,7 +20424,9 @@ KernelCircuitPublicInputs { "type": "Buffer", }, }, - "oldValue": Fr { + }, + PublicDataRead { + "leafSlot": Fr { "asBigInt": 0n, "asBuffer": { "data": [ @@ -20521,9 +20467,7 @@ KernelCircuitPublicInputs { }, }, "sideEffectCounter": undefined, - }, - PublicDataUpdateRequest { - "leafSlot": Fr { + "value": Fr { "asBigInt": 0n, "asBuffer": { "data": [ @@ -20563,7 +20507,9 @@ KernelCircuitPublicInputs { "type": "Buffer", }, }, - "newValue": Fr { + }, + PublicDataRead { + "leafSlot": Fr { "asBigInt": 0n, "asBuffer": { "data": [ @@ -20603,7 +20549,8 @@ KernelCircuitPublicInputs { "type": "Buffer", }, }, - "oldValue": Fr { + "sideEffectCounter": undefined, + "value": Fr { "asBigInt": 0n, "asBuffer": { "data": [ @@ -20643,8 +20590,9 @@ KernelCircuitPublicInputs { "type": "Buffer", }, }, - "sideEffectCounter": undefined, }, + ], + "publicDataUpdateRequests": [ PublicDataUpdateRequest { "leafSlot": Fr { "asBigInt": 0n, @@ -21998,50 +21946,8 @@ KernelCircuitPublicInputs { }, "sideEffectCounter": undefined, }, - ], - "readRequests": [ - SideEffect { - "counter": Fr { - "asBigInt": 0n, - "asBuffer": { - "data": [ - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - ], - "type": "Buffer", - }, - }, - "value": Fr { + PublicDataUpdateRequest { + "leafSlot": Fr { "asBigInt": 0n, "asBuffer": { "data": [ @@ -22081,9 +21987,7 @@ KernelCircuitPublicInputs { "type": "Buffer", }, }, - }, - SideEffect { - "counter": Fr { + "newValue": Fr { "asBigInt": 0n, "asBuffer": { "data": [ @@ -22123,7 +22027,7 @@ KernelCircuitPublicInputs { "type": "Buffer", }, }, - "value": Fr { + "oldValue": Fr { "asBigInt": 0n, "asBuffer": { "data": [ @@ -22163,49 +22067,10 @@ KernelCircuitPublicInputs { "type": "Buffer", }, }, + "sideEffectCounter": undefined, }, - SideEffect { - "counter": Fr { - "asBigInt": 0n, - "asBuffer": { - "data": [ - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - ], - "type": "Buffer", - }, - }, - "value": Fr { + PublicDataUpdateRequest { + "leafSlot": Fr { "asBigInt": 0n, "asBuffer": { "data": [ @@ -22245,9 +22110,7 @@ KernelCircuitPublicInputs { "type": "Buffer", }, }, - }, - SideEffect { - "counter": Fr { + "newValue": Fr { "asBigInt": 0n, "asBuffer": { "data": [ @@ -22287,7 +22150,7 @@ KernelCircuitPublicInputs { "type": "Buffer", }, }, - "value": Fr { + "oldValue": Fr { "asBigInt": 0n, "asBuffer": { "data": [ @@ -22327,49 +22190,10 @@ KernelCircuitPublicInputs { "type": "Buffer", }, }, + "sideEffectCounter": undefined, }, - SideEffect { - "counter": Fr { - "asBigInt": 0n, - "asBuffer": { - "data": [ - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - ], - "type": "Buffer", - }, - }, - "value": Fr { + PublicDataUpdateRequest { + "leafSlot": Fr { "asBigInt": 0n, "asBuffer": { "data": [ @@ -22409,9 +22233,7 @@ KernelCircuitPublicInputs { "type": "Buffer", }, }, - }, - SideEffect { - "counter": Fr { + "newValue": Fr { "asBigInt": 0n, "asBuffer": { "data": [ @@ -22451,7 +22273,7 @@ KernelCircuitPublicInputs { "type": "Buffer", }, }, - "value": Fr { + "oldValue": Fr { "asBigInt": 0n, "asBuffer": { "data": [ @@ -22491,9 +22313,10 @@ KernelCircuitPublicInputs { "type": "Buffer", }, }, + "sideEffectCounter": undefined, }, - SideEffect { - "counter": Fr { + PublicDataUpdateRequest { + "leafSlot": Fr { "asBigInt": 0n, "asBuffer": { "data": [ @@ -22533,7 +22356,7 @@ KernelCircuitPublicInputs { "type": "Buffer", }, }, - "value": Fr { + "newValue": Fr { "asBigInt": 0n, "asBuffer": { "data": [ @@ -22573,9 +22396,7 @@ KernelCircuitPublicInputs { "type": "Buffer", }, }, - }, - SideEffect { - "counter": Fr { + "oldValue": Fr { "asBigInt": 0n, "asBuffer": { "data": [ @@ -22615,7 +22436,10 @@ KernelCircuitPublicInputs { "type": "Buffer", }, }, - "value": Fr { + "sideEffectCounter": undefined, + }, + PublicDataUpdateRequest { + "leafSlot": Fr { "asBigInt": 0n, "asBuffer": { "data": [ @@ -22655,9 +22479,7 @@ KernelCircuitPublicInputs { "type": "Buffer", }, }, - }, - SideEffect { - "counter": Fr { + "newValue": Fr { "asBigInt": 0n, "asBuffer": { "data": [ @@ -22697,7 +22519,7 @@ KernelCircuitPublicInputs { "type": "Buffer", }, }, - "value": Fr { + "oldValue": Fr { "asBigInt": 0n, "asBuffer": { "data": [ @@ -22737,7 +22559,10 @@ KernelCircuitPublicInputs { "type": "Buffer", }, }, + "sideEffectCounter": undefined, }, + ], + "readRequests": [ SideEffect { "counter": Fr { "asBigInt": 0n, @@ -32496,502 +32321,8 @@ KernelCircuitPublicInputs { }, }, }, - ], - "unencryptedLogPreimagesLength": Fr { - "asBigInt": 4n, - "asBuffer": { - "data": [ - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 4, - ], - "type": "Buffer", - }, - }, - "unencryptedLogsHash": [ - Fr { - "asBigInt": 38042960891247304977978366569914796636n, - "asBuffer": { - "data": [ - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 28, - 158, - 206, - 201, - 14, - 40, - 210, - 70, - 22, - 80, - 65, - 134, - 53, - 135, - 138, - 92, - ], - "type": "Buffer", - }, - }, - Fr { - "asBigInt": 193925133628253903808777040905688936722n, - "asBuffer": { - "data": [ - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 145, - 228, - 159, - 71, - 88, - 110, - 207, - 117, - 242, - 176, - 203, - 185, - 78, - 137, - 113, - 18, - ], - "type": "Buffer", - }, - }, - ], - }, - "isPrivate": true, -} -`; - -exports[`Private kernel Executes private kernel inner for a nested call 1`] = ` -KernelCircuitPublicInputs { - "constants": CombinedConstantData { - "blockHeader": BlockHeader { - "archiveRoot": Fr { - "asBigInt": 20372018159471621712773206616781399243089284083897629004165638733497278836368n, - "asBuffer": { - "data": [ - 45, - 10, - 39, - 86, - 151, - 28, - 147, - 221, - 193, - 180, - 251, - 133, - 176, - 163, - 143, - 86, - 29, - 222, - 109, - 190, - 209, - 92, - 25, - 252, - 185, - 2, - 148, - 253, - 230, - 102, - 174, - 144, - ], - "type": "Buffer", - }, - }, - "contractTreeRoot": Fr { - "asBigInt": 14917511466805779502198250944232247270950372922289035175844919622811814316704n, - "asBuffer": { - "data": [ - 32, - 251, - 3, - 36, - 219, - 51, - 241, - 141, - 112, - 62, - 65, - 78, - 49, - 138, - 228, - 237, - 96, - 212, - 126, - 208, - 2, - 74, - 124, - 46, - 56, - 24, - 184, - 115, - 169, - 153, - 202, - 160, - ], - "type": "Buffer", - }, - }, - "globalVariablesHash": Fr { - "asBigInt": 2339684137567523517345226739343572726453743526214691264476956870525775001728n, - "asBuffer": { - "data": [ - 5, - 44, - 54, - 219, - 239, - 243, - 182, - 206, - 85, - 90, - 124, - 142, - 224, - 106, - 84, - 231, - 191, - 155, - 56, - 48, - 46, - 91, - 242, - 101, - 182, - 41, - 102, - 190, - 62, - 204, - 56, - 128, - ], - "type": "Buffer", - }, - }, - "l1ToL2MessageTreeRoot": Fr { - "asBigInt": 11033938207523021649122316027295742559227608161317650429835019071130941169536n, - "asBuffer": { - "data": [ - 24, - 100, - 252, - 218, - 168, - 15, - 242, - 113, - 145, - 84, - 250, - 124, - 138, - 144, - 80, - 102, - 41, - 114, - 112, - 113, - 104, - 214, - 158, - 172, - 157, - 182, - 253, - 49, - 16, - 130, - 159, - 128, - ], - "type": "Buffer", - }, - }, - "noteHashTreeRoot": Fr { - "asBigInt": 981923962123739841860000837864904074407773873785560232542331187019926149423n, - "asBuffer": { - "data": [ - 2, - 43, - 191, - 207, - 255, - 135, - 229, - 206, - 199, - 96, - 16, - 217, - 240, - 31, - 71, - 249, - 233, - 174, - 64, - 191, - 114, - 239, - 27, - 52, - 163, - 196, - 167, - 143, - 3, - 163, - 25, - 47, - ], - "type": "Buffer", - }, - }, - "nullifierTreeRoot": Fr { - "asBigInt": 13649330571405803453188645801233118952397925720323129467963581146586569980764n, - "asBuffer": { - "data": [ - 30, - 45, - 63, - 81, - 21, - 13, - 218, - 53, - 98, - 33, - 245, - 88, - 96, - 244, - 62, - 153, - 244, - 25, - 156, - 226, - 242, - 63, - 191, - 240, - 25, - 13, - 33, - 222, - 114, - 251, - 59, - 92, - ], - "type": "Buffer", - }, - }, - "privateKernelVkTreeRoot": Fr { - "asBigInt": 0n, - "asBuffer": { - "data": [ - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - ], - "type": "Buffer", - }, - }, - "publicDataTreeRoot": Fr { - "asBigInt": 13940981882517738105981911020707002777955674699044852872880094407282064863456n, - "asBuffer": { - "data": [ - 30, - 210, - 80, - 237, - 115, - 219, - 110, - 112, - 128, - 92, - 78, - 252, - 240, - 5, - 110, - 134, - 149, - 183, - 156, - 211, - 186, - 65, - 142, - 130, - 124, - 24, - 77, - 238, - 108, - 111, - 176, - 224, - ], - "type": "Buffer", - }, - }, - }, - "txContext": TxContext { - "chainId": Fr { - "asBigInt": 31337n, - "asBuffer": { - "data": [ - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 122, - 105, - ], - "type": "Buffer", - }, - }, - "contractDeploymentData": ContractDeploymentData { - "constructorVkHash": Fr { + SideEffect { + "counter": Fr { "asBigInt": 0n, "asBuffer": { "data": [ @@ -33031,6 +32362,1183 @@ KernelCircuitPublicInputs { "type": "Buffer", }, }, + "value": Fr { + "asBigInt": 0n, + "asBuffer": { + "data": [ + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + ], + "type": "Buffer", + }, + }, + }, + SideEffect { + "counter": Fr { + "asBigInt": 0n, + "asBuffer": { + "data": [ + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + ], + "type": "Buffer", + }, + }, + "value": Fr { + "asBigInt": 0n, + "asBuffer": { + "data": [ + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + ], + "type": "Buffer", + }, + }, + }, + SideEffect { + "counter": Fr { + "asBigInt": 0n, + "asBuffer": { + "data": [ + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + ], + "type": "Buffer", + }, + }, + "value": Fr { + "asBigInt": 0n, + "asBuffer": { + "data": [ + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + ], + "type": "Buffer", + }, + }, + }, + SideEffect { + "counter": Fr { + "asBigInt": 0n, + "asBuffer": { + "data": [ + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + ], + "type": "Buffer", + }, + }, + "value": Fr { + "asBigInt": 0n, + "asBuffer": { + "data": [ + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + ], + "type": "Buffer", + }, + }, + }, + SideEffect { + "counter": Fr { + "asBigInt": 0n, + "asBuffer": { + "data": [ + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + ], + "type": "Buffer", + }, + }, + "value": Fr { + "asBigInt": 0n, + "asBuffer": { + "data": [ + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + ], + "type": "Buffer", + }, + }, + }, + SideEffect { + "counter": Fr { + "asBigInt": 0n, + "asBuffer": { + "data": [ + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + ], + "type": "Buffer", + }, + }, + "value": Fr { + "asBigInt": 0n, + "asBuffer": { + "data": [ + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + ], + "type": "Buffer", + }, + }, + }, + SideEffect { + "counter": Fr { + "asBigInt": 0n, + "asBuffer": { + "data": [ + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + ], + "type": "Buffer", + }, + }, + "value": Fr { + "asBigInt": 0n, + "asBuffer": { + "data": [ + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + ], + "type": "Buffer", + }, + }, + }, + SideEffect { + "counter": Fr { + "asBigInt": 0n, + "asBuffer": { + "data": [ + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + ], + "type": "Buffer", + }, + }, + "value": Fr { + "asBigInt": 0n, + "asBuffer": { + "data": [ + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + ], + "type": "Buffer", + }, + }, + }, + SideEffect { + "counter": Fr { + "asBigInt": 0n, + "asBuffer": { + "data": [ + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + ], + "type": "Buffer", + }, + }, + "value": Fr { + "asBigInt": 0n, + "asBuffer": { + "data": [ + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + ], + "type": "Buffer", + }, + }, + }, + ], + "unencryptedLogPreimagesLength": Fr { + "asBigInt": 4n, + "asBuffer": { + "data": [ + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 4, + ], + "type": "Buffer", + }, + }, + "unencryptedLogsHash": [ + Fr { + "asBigInt": 38042960891247304977978366569914796636n, + "asBuffer": { + "data": [ + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 28, + 158, + 206, + 201, + 14, + 40, + 210, + 70, + 22, + 80, + 65, + 134, + 53, + 135, + 138, + 92, + ], + "type": "Buffer", + }, + }, + Fr { + "asBigInt": 193925133628253903808777040905688936722n, + "asBuffer": { + "data": [ + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 145, + 228, + 159, + 71, + 88, + 110, + 207, + 117, + 242, + 176, + 203, + 185, + 78, + 137, + 113, + 18, + ], + "type": "Buffer", + }, + }, + ], + }, + "isPrivate": true, +} +`; + +exports[`Private kernel Executes private kernel inner for a nested call 1`] = ` +KernelCircuitPublicInputs { + "constants": CombinedConstantData { + "historicalHeader": Header { + "bodyHash": { + "data": [ + 210, + 155, + 175, + 186, + 106, + 132, + 117, + 140, + 225, + 34, + 2, + 102, + 7, + 170, + 218, + 146, + 116, + 134, + 129, + 79, + 112, + 55, + 136, + 78, + 141, + 96, + 179, + 240, + 54, + 10, + 169, + 93, + ], + "type": "Buffer", + }, + "globalVariables": { + "blockNumber": "0x0000000000000000000000000000000000000000000000000000000000000003", + "chainId": "0x0000000000000000000000000000000000000000000000000000000000007a69", + "timestamp": "0x0000000000000000000000000000000000000000000000000000000065b75af8", + "version": "0x0000000000000000000000000000000000000000000000000000000000000001", + }, + "lastArchive": AppendOnlyTreeSnapshot { + "nextAvailableLeafIndex": 3, + "root": Fr { + "asBigInt": 5826076351691030560036649791526329135534072701597571377937120870932284014126n, + "asBuffer": { + "data": [ + 12, + 225, + 113, + 62, + 97, + 115, + 134, + 135, + 137, + 145, + 147, + 207, + 231, + 195, + 210, + 98, + 40, + 63, + 126, + 122, + 105, + 3, + 195, + 165, + 237, + 146, + 113, + 157, + 224, + 25, + 126, + 46, + ], + "type": "Buffer", + }, + }, + }, + "state": StateReference { + "l1ToL2MessageTree": AppendOnlyTreeSnapshot { + "nextAvailableLeafIndex": 48, + "root": Fr { + "asBigInt": 11033938207523021649122316027295742559227608161317650429835019071130941169536n, + "asBuffer": { + "data": [ + 24, + 100, + 252, + 218, + 168, + 15, + 242, + 113, + 145, + 84, + 250, + 124, + 138, + 144, + 80, + 102, + 41, + 114, + 112, + 113, + 104, + 214, + 158, + 172, + 157, + 182, + 253, + 49, + 16, + 130, + 159, + 128, + ], + "type": "Buffer", + }, + }, + }, + "partial": PartialStateReference { + "contractTree": AppendOnlyTreeSnapshot { + "nextAvailableLeafIndex": 6, + "root": Fr { + "asBigInt": 14966129659710361057389092304046605967938408029560064619790603969181080073412n, + "asBuffer": { + "data": [ + 33, + 22, + 135, + 121, + 219, + 154, + 210, + 171, + 131, + 241, + 182, + 140, + 144, + 34, + 3, + 9, + 239, + 154, + 134, + 124, + 180, + 113, + 115, + 78, + 172, + 135, + 164, + 218, + 9, + 7, + 180, + 196, + ], + "type": "Buffer", + }, + }, + }, + "noteHashTree": AppendOnlyTreeSnapshot { + "nextAvailableLeafIndex": 384, + "root": Fr { + "asBigInt": 1838290298916850987238319443501173477239569837534177215572273754567899038632n, + "asBuffer": { + "data": [ + 4, + 16, + 111, + 120, + 213, + 188, + 236, + 222, + 135, + 130, + 159, + 145, + 206, + 167, + 220, + 19, + 232, + 198, + 15, + 147, + 135, + 0, + 141, + 149, + 218, + 191, + 4, + 209, + 188, + 19, + 67, + 168, + ], + "type": "Buffer", + }, + }, + }, + "nullifierTree": AppendOnlyTreeSnapshot { + "nextAvailableLeafIndex": 512, + "root": Fr { + "asBigInt": 1810643660514715827090569286968488906377643810199054909066851585554626410254n, + "asBuffer": { + "data": [ + 4, + 0, + 201, + 186, + 5, + 217, + 21, + 231, + 161, + 233, + 211, + 242, + 13, + 207, + 181, + 79, + 237, + 56, + 124, + 71, + 77, + 6, + 105, + 118, + 78, + 64, + 236, + 202, + 67, + 68, + 119, + 14, + ], + "type": "Buffer", + }, + }, + }, + "publicDataTree": AppendOnlyTreeSnapshot { + "nextAvailableLeafIndex": 128, + "root": Fr { + "asBigInt": 13940981882517738105981911020707002777955674699044852872880094407282064863456n, + "asBuffer": { + "data": [ + 30, + 210, + 80, + 237, + 115, + 219, + 110, + 112, + 128, + 92, + 78, + 252, + 240, + 5, + 110, + 134, + 149, + 183, + 156, + 211, + 186, + 65, + 142, + 130, + 124, + 24, + 77, + 238, + 108, + 111, + 176, + 224, + ], + "type": "Buffer", + }, + }, + }, + }, + }, + }, + "txContext": TxContext { + "chainId": Fr { + "asBigInt": 31337n, + "asBuffer": { + "data": [ + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 122, + 105, + ], + "type": "Buffer", + }, + }, + "contractDeploymentData": ContractDeploymentData { "contractAddressSalt": Fr { "asBigInt": 0n, "asBuffer": { @@ -33071,7 +33579,114 @@ KernelCircuitPublicInputs { "type": "Buffer", }, }, - "deployerPublicKey": Point { + "contractClassId": Fr { + "asBigInt": 0n, + "asBuffer": { + "data": [ + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + ], + "type": "Buffer", + }, + }, + "initializationHash": Fr { + "asBigInt": 0n, + "asBuffer": { + "data": [ + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + ], + "type": "Buffer", + }, + }, + "portalContractAddress": EthAddress { + "buffer": { + "data": [ + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + ], + "type": "Buffer", + }, + }, + "publicKey": Point { "kind": "point", "x": Fr { "asBigInt": 0n, @@ -33154,7 +33769,364 @@ KernelCircuitPublicInputs { }, }, }, - "functionTreeRoot": Fr { + }, + "isContractDeploymentTx": false, + "isFeePaymentTx": false, + "isRebatePaymentTx": false, + "version": Fr { + "asBigInt": 1n, + "asBuffer": { + "data": [ + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 1, + ], + "type": "Buffer", + }, + }, + }, + }, + "end": CombinedAccumulatedData { + "aggregationObject": AggregationObject { + "hasData": false, + "p0": G1AffineElement { + "x": Fq { + "asBigInt": 1n, + "asBuffer": { + "data": [ + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 1, + ], + "type": "Buffer", + }, + }, + "y": Fq { + "asBigInt": 2n, + "asBuffer": { + "data": [ + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 2, + ], + "type": "Buffer", + }, + }, + }, + "p1": G1AffineElement { + "x": Fq { + "asBigInt": 1n, + "asBuffer": { + "data": [ + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 1, + ], + "type": "Buffer", + }, + }, + "y": Fq { + "asBigInt": 2n, + "asBuffer": { + "data": [ + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 2, + ], + "type": "Buffer", + }, + }, + }, + "proofWitnessIndices": [ + 3027, + 3028, + 3029, + 3030, + 3031, + 3032, + 3033, + 3034, + 3035, + 3036, + 3037, + 3038, + 3039, + 3040, + 3041, + 3042, + ], + "publicInputs": [], + }, + "encryptedLogPreimagesLength": Fr { + "asBigInt": 12n, + "asBuffer": { + "data": [ + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 12, + ], + "type": "Buffer", + }, + }, + "encryptedLogsHash": [ + Fr { + "asBigInt": 10654334908029642268226261618939201427n, + "asBuffer": { + "data": [ + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 8, + 3, + 243, + 68, + 123, + 49, + 16, + 181, + 87, + 150, + 38, + 199, + 134, + 29, + 7, + 147, + ], + "type": "Buffer", + }, + }, + Fr { + "asBigInt": 133338275028334099210129003420909668908n, + "asBuffer": { + "data": [ + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 100, + 80, + 4, + 133, + 110, + 109, + 121, + 70, + 184, + 235, + 48, + 170, + 28, + 9, + 138, + 44, + ], + "type": "Buffer", + }, + }, + ], + "newCommitments": [ + SideEffect { + "counter": Fr { "asBigInt": 0n, "asBuffer": { "data": [ @@ -33194,8 +34166,91 @@ KernelCircuitPublicInputs { "type": "Buffer", }, }, - "portalContractAddress": EthAddress { - "buffer": { + "value": Fr { + "asBigInt": 0n, + "asBuffer": { + "data": [ + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + ], + "type": "Buffer", + }, + }, + }, + SideEffect { + "counter": Fr { + "asBigInt": 0n, + "asBuffer": { + "data": [ + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + ], + "type": "Buffer", + }, + }, + "value": Fr { + "asBigInt": 0n, + "asBuffer": { "data": [ 0, 0, @@ -33217,62 +34272,26 @@ KernelCircuitPublicInputs { 0, 0, 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, ], "type": "Buffer", }, }, }, - "isContractDeploymentTx": false, - "isFeePaymentTx": false, - "isRebatePaymentTx": false, - "version": Fr { - "asBigInt": 1n, - "asBuffer": { - "data": [ - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 1, - ], - "type": "Buffer", - }, - }, - }, - }, - "end": CombinedAccumulatedData { - "aggregationObject": AggregationObject { - "hasData": false, - "p0": G1AffineElement { - "x": Fq { - "asBigInt": 1n, + SideEffect { + "counter": Fr { + "asBigInt": 0n, "asBuffer": { "data": [ 0, @@ -33306,13 +34325,13 @@ KernelCircuitPublicInputs { 0, 0, 0, - 1, + 0, ], "type": "Buffer", }, }, - "y": Fq { - "asBigInt": 2n, + "value": Fr { + "asBigInt": 0n, "asBuffer": { "data": [ 0, @@ -33346,15 +34365,15 @@ KernelCircuitPublicInputs { 0, 0, 0, - 2, + 0, ], "type": "Buffer", }, }, }, - "p1": G1AffineElement { - "x": Fq { - "asBigInt": 1n, + SideEffect { + "counter": Fr { + "asBigInt": 0n, "asBuffer": { "data": [ 0, @@ -33388,13 +34407,13 @@ KernelCircuitPublicInputs { 0, 0, 0, - 1, + 0, ], "type": "Buffer", }, }, - "y": Fq { - "asBigInt": 2n, + "value": Fr { + "asBigInt": 0n, "asBuffer": { "data": [ 0, @@ -33428,155 +34447,12 @@ KernelCircuitPublicInputs { 0, 0, 0, - 2, + 0, ], "type": "Buffer", }, }, }, - "proofWitnessIndices": [ - 3027, - 3028, - 3029, - 3030, - 3031, - 3032, - 3033, - 3034, - 3035, - 3036, - 3037, - 3038, - 3039, - 3040, - 3041, - 3042, - ], - "publicInputs": [], - }, - "encryptedLogPreimagesLength": Fr { - "asBigInt": 12n, - "asBuffer": { - "data": [ - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 12, - ], - "type": "Buffer", - }, - }, - "encryptedLogsHash": [ - Fr { - "asBigInt": 10654334908029642268226261618939201427n, - "asBuffer": { - "data": [ - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 8, - 3, - 243, - 68, - 123, - 49, - 16, - 181, - 87, - 150, - 38, - 199, - 134, - 29, - 7, - 147, - ], - "type": "Buffer", - }, - }, - Fr { - "asBigInt": 133338275028334099210129003420909668908n, - "asBuffer": { - "data": [ - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 100, - 80, - 4, - 133, - 110, - 109, - 121, - 70, - 184, - 235, - 48, - 170, - 28, - 9, - 138, - 44, - ], - "type": "Buffer", - }, - }, - ], - "newCommitments": [ SideEffect { "counter": Fr { "asBigInt": 0n, @@ -38497,8 +39373,10 @@ KernelCircuitPublicInputs { }, }, }, - SideEffect { - "counter": Fr { + ], + "newContracts": [ + NewContractData { + "contractAddress": AztecAddress { "asBigInt": 0n, "asBuffer": { "data": [ @@ -38538,7 +39416,7 @@ KernelCircuitPublicInputs { "type": "Buffer", }, }, - "value": Fr { + "contractClassId": Fr { "asBigInt": 0n, "asBuffer": { "data": [ @@ -38578,8 +39456,119 @@ KernelCircuitPublicInputs { "type": "Buffer", }, }, + "portalContractAddress": EthAddress { + "buffer": { + "data": [ + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + ], + "type": "Buffer", + }, + }, }, - SideEffect { + ], + "newL2ToL1Msgs": [ + Fr { + "asBigInt": 0n, + "asBuffer": { + "data": [ + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + ], + "type": "Buffer", + }, + }, + Fr { + "asBigInt": 0n, + "asBuffer": { + "data": [ + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + ], + "type": "Buffer", + }, + }, + ], + "newNullifiers": [ + SideEffectLinkedToNoteHash { "counter": Fr { "asBigInt": 0n, "asBuffer": { @@ -38620,7 +39609,7 @@ KernelCircuitPublicInputs { "type": "Buffer", }, }, - "value": Fr { + "noteHash": Fr { "asBigInt": 0n, "asBuffer": { "data": [ @@ -38660,8 +39649,48 @@ KernelCircuitPublicInputs { "type": "Buffer", }, }, + "value": Fr { + "asBigInt": 15402175337708845328640944255779088707816504006277982796311669075994340278065n, + "asBuffer": { + "data": [ + 34, + 13, + 82, + 130, + 247, + 72, + 255, + 108, + 22, + 172, + 10, + 136, + 4, + 166, + 214, + 125, + 92, + 196, + 162, + 32, + 118, + 51, + 230, + 19, + 20, + 20, + 31, + 175, + 28, + 96, + 191, + 49, + ], + "type": "Buffer", + }, + }, }, - SideEffect { + SideEffectLinkedToNoteHash { "counter": Fr { "asBigInt": 0n, "asBuffer": { @@ -38702,7 +39731,7 @@ KernelCircuitPublicInputs { "type": "Buffer", }, }, - "value": Fr { + "noteHash": Fr { "asBigInt": 0n, "asBuffer": { "data": [ @@ -38742,9 +39771,7 @@ KernelCircuitPublicInputs { "type": "Buffer", }, }, - }, - SideEffect { - "counter": Fr { + "value": Fr { "asBigInt": 0n, "asBuffer": { "data": [ @@ -38784,7 +39811,9 @@ KernelCircuitPublicInputs { "type": "Buffer", }, }, - "value": Fr { + }, + SideEffectLinkedToNoteHash { + "counter": Fr { "asBigInt": 0n, "asBuffer": { "data": [ @@ -38824,11 +39853,7 @@ KernelCircuitPublicInputs { "type": "Buffer", }, }, - }, - ], - "newContracts": [ - NewContractData { - "contractAddress": AztecAddress { + "noteHash": Fr { "asBigInt": 0n, "asBuffer": { "data": [ @@ -38868,7 +39893,7 @@ KernelCircuitPublicInputs { "type": "Buffer", }, }, - "functionTreeRoot": Fr { + "value": Fr { "asBigInt": 0n, "asBuffer": { "data": [ @@ -38908,8 +39933,11 @@ KernelCircuitPublicInputs { "type": "Buffer", }, }, - "portalContractAddress": EthAddress { - "buffer": { + }, + SideEffectLinkedToNoteHash { + "counter": Fr { + "asBigInt": 0n, + "asBuffer": { "data": [ 0, 0, @@ -38931,97 +39959,23 @@ KernelCircuitPublicInputs { 0, 0, 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, ], "type": "Buffer", }, }, - }, - ], - "newL2ToL1Msgs": [ - Fr { - "asBigInt": 0n, - "asBuffer": { - "data": [ - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - ], - "type": "Buffer", - }, - }, - Fr { - "asBigInt": 0n, - "asBuffer": { - "data": [ - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - ], - "type": "Buffer", - }, - }, - ], - "newNullifiers": [ - SideEffectLinkedToNoteHash { - "counter": Fr { + "noteHash": Fr { "asBigInt": 0n, "asBuffer": { "data": [ @@ -39061,7 +40015,7 @@ KernelCircuitPublicInputs { "type": "Buffer", }, }, - "noteHash": Fr { + "value": Fr { "asBigInt": 0n, "asBuffer": { "data": [ @@ -39101,46 +40055,6 @@ KernelCircuitPublicInputs { "type": "Buffer", }, }, - "value": Fr { - "asBigInt": 6829908051101542233177273165904445376377164813196839000974832044882663163261n, - "asBuffer": { - "data": [ - 15, - 25, - 151, - 73, - 132, - 139, - 131, - 68, - 116, - 226, - 229, - 188, - 245, - 21, - 243, - 85, - 158, - 211, - 129, - 202, - 99, - 245, - 133, - 199, - 248, - 228, - 115, - 65, - 249, - 201, - 197, - 125, - ], - "type": "Buffer", - }, - }, }, SideEffectLinkedToNoteHash { "counter": Fr { @@ -46462,8 +47376,10 @@ KernelCircuitPublicInputs { }, }, }, - SideEffectLinkedToNoteHash { - "counter": Fr { + ], + "nullifierKeyValidationRequests": [ + NullifierKeyValidationRequestContext { + "contractAddress": AztecAddress { "asBigInt": 0n, "asBuffer": { "data": [ @@ -46503,47 +47419,90 @@ KernelCircuitPublicInputs { "type": "Buffer", }, }, - "noteHash": Fr { - "asBigInt": 0n, - "asBuffer": { - "data": [ - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - ], - "type": "Buffer", + "publicKey": Point { + "kind": "point", + "x": Fr { + "asBigInt": 0n, + "asBuffer": { + "data": [ + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + ], + "type": "Buffer", + }, + }, + "y": Fr { + "asBigInt": 0n, + "asBuffer": { + "data": [ + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + ], + "type": "Buffer", + }, }, }, - "value": Fr { + "secretKey": Fq { "asBigInt": 0n, "asBuffer": { "data": [ @@ -46584,8 +47543,8 @@ KernelCircuitPublicInputs { }, }, }, - SideEffectLinkedToNoteHash { - "counter": Fr { + NullifierKeyValidationRequestContext { + "contractAddress": AztecAddress { "asBigInt": 0n, "asBuffer": { "data": [ @@ -46625,7 +47584,90 @@ KernelCircuitPublicInputs { "type": "Buffer", }, }, - "noteHash": Fr { + "publicKey": Point { + "kind": "point", + "x": Fr { + "asBigInt": 0n, + "asBuffer": { + "data": [ + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + ], + "type": "Buffer", + }, + }, + "y": Fr { + "asBigInt": 0n, + "asBuffer": { + "data": [ + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + ], + "type": "Buffer", + }, + }, + }, + "secretKey": Fq { "asBigInt": 0n, "asBuffer": { "data": [ @@ -46665,7 +47707,9 @@ KernelCircuitPublicInputs { "type": "Buffer", }, }, - "value": Fr { + }, + NullifierKeyValidationRequestContext { + "contractAddress": AztecAddress { "asBigInt": 0n, "asBuffer": { "data": [ @@ -46705,9 +47749,90 @@ KernelCircuitPublicInputs { "type": "Buffer", }, }, - }, - SideEffectLinkedToNoteHash { - "counter": Fr { + "publicKey": Point { + "kind": "point", + "x": Fr { + "asBigInt": 0n, + "asBuffer": { + "data": [ + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + ], + "type": "Buffer", + }, + }, + "y": Fr { + "asBigInt": 0n, + "asBuffer": { + "data": [ + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + ], + "type": "Buffer", + }, + }, + }, + "secretKey": Fq { "asBigInt": 0n, "asBuffer": { "data": [ @@ -46747,7 +47872,9 @@ KernelCircuitPublicInputs { "type": "Buffer", }, }, - "noteHash": Fr { + }, + NullifierKeyValidationRequestContext { + "contractAddress": AztecAddress { "asBigInt": 0n, "asBuffer": { "data": [ @@ -46787,7 +47914,90 @@ KernelCircuitPublicInputs { "type": "Buffer", }, }, - "value": Fr { + "publicKey": Point { + "kind": "point", + "x": Fr { + "asBigInt": 0n, + "asBuffer": { + "data": [ + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + ], + "type": "Buffer", + }, + }, + "y": Fr { + "asBigInt": 0n, + "asBuffer": { + "data": [ + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + ], + "type": "Buffer", + }, + }, + }, + "secretKey": Fq { "asBigInt": 0n, "asBuffer": { "data": [ @@ -65151,331 +66361,316 @@ KernelCircuitPublicInputs { exports[`Private kernel Executes private kernel ordering after a deployment 1`] = ` KernelCircuitPublicInputsFinal { "constants": CombinedConstantData { - "blockHeader": BlockHeader { - "archiveRoot": Fr { - "asBigInt": 10561895175368852737061915973188839857007468377789560793687187642867659280638n, - "asBuffer": { - "data": [ - 23, - 89, - 210, - 33, - 121, - 84, - 25, - 80, - 63, - 134, - 192, - 50, - 232, - 248, - 118, - 47, - 43, - 115, - 158, - 116, - 131, - 80, - 153, - 88, - 75, - 101, - 49, - 245, - 242, - 115, - 144, - 254, - ], - "type": "Buffer", - }, - }, - "contractTreeRoot": Fr { - "asBigInt": 11033938207523021649122316027295742559227608161317650429835019071130941169536n, - "asBuffer": { - "data": [ - 24, - 100, - 252, - 218, - 168, - 15, - 242, - 113, - 145, - 84, - 250, - 124, - 138, - 144, - 80, - 102, - 41, - 114, - 112, - 113, - 104, - 214, - 158, - 172, - 157, - 182, - 253, - 49, - 16, - 130, - 159, - 128, - ], - "type": "Buffer", - }, - }, - "globalVariablesHash": Fr { - "asBigInt": 14483571110897883400419490783710119837459619381345566311432831352122387488397n, - "asBuffer": { - "data": [ - 32, - 5, - 105, - 38, - 124, - 15, - 115, - 172, - 137, - 170, - 164, - 20, - 35, - 147, - 152, - 219, - 148, - 69, - 221, - 74, - 211, - 168, - 207, - 55, - 1, - 92, - 213, - 91, - 141, - 76, - 94, - 141, - ], - "type": "Buffer", - }, - }, - "l1ToL2MessageTreeRoot": Fr { - "asBigInt": 11033938207523021649122316027295742559227608161317650429835019071130941169536n, - "asBuffer": { - "data": [ - 24, - 100, - 252, - 218, - 168, - 15, - 242, - 113, - 145, - 84, - 250, - 124, - 138, - 144, - 80, - 102, - 41, - 114, - 112, - 113, - 104, - 214, - 158, - 172, - 157, - 182, - 253, - 49, - 16, - 130, - 159, - 128, - ], - "type": "Buffer", - }, + "historicalHeader": Header { + "bodyHash": { + "data": [ + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + ], + "type": "Buffer", }, - "noteHashTreeRoot": Fr { - "asBigInt": 10127882181290008410413105921460858232892226592306749913988016925836213768395n, - "asBuffer": { - "data": [ - 22, - 100, - 45, - 156, - 205, - 131, - 70, - 196, - 3, - 170, - 76, - 63, - 164, - 81, - 23, - 139, - 34, - 83, - 74, - 39, - 3, - 92, - 218, - 166, - 236, - 52, - 174, - 83, - 178, - 156, - 80, - 203, - ], - "type": "Buffer", - }, + "globalVariables": { + "blockNumber": "0x0000000000000000000000000000000000000000000000000000000000000000", + "chainId": "0x0000000000000000000000000000000000000000000000000000000000000000", + "timestamp": "0x0000000000000000000000000000000000000000000000000000000000000000", + "version": "0x0000000000000000000000000000000000000000000000000000000000000000", }, - "nullifierTreeRoot": Fr { - "asBigInt": 5342309968596764527275045470866818007603635181649161546597860399861598581368n, - "asBuffer": { - "data": [ - 11, - 207, - 163, - 233, - 241, - 168, - 146, - 46, - 233, - 44, - 109, - 201, - 100, - 214, - 89, - 89, - 7, - 193, - 128, - 74, - 134, - 117, - 55, - 116, - 50, - 43, - 70, - 143, - 105, - 212, - 242, - 120, - ], - "type": "Buffer", + "lastArchive": AppendOnlyTreeSnapshot { + "nextAvailableLeafIndex": 0, + "root": Fr { + "asBigInt": 0n, + "asBuffer": { + "data": [ + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + ], + "type": "Buffer", + }, }, }, - "privateKernelVkTreeRoot": Fr { - "asBigInt": 0n, - "asBuffer": { - "data": [ - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - ], - "type": "Buffer", + "state": StateReference { + "l1ToL2MessageTree": AppendOnlyTreeSnapshot { + "nextAvailableLeafIndex": 0, + "root": Fr { + "asBigInt": 11033938207523021649122316027295742559227608161317650429835019071130941169536n, + "asBuffer": { + "data": [ + 24, + 100, + 252, + 218, + 168, + 15, + 242, + 113, + 145, + 84, + 250, + 124, + 138, + 144, + 80, + 102, + 41, + 114, + 112, + 113, + 104, + 214, + 158, + 172, + 157, + 182, + 253, + 49, + 16, + 130, + 159, + 128, + ], + "type": "Buffer", + }, + }, }, - }, - "publicDataTreeRoot": Fr { - "asBigInt": 5785871043333994658400733180052743689641713274194136017445890613179954325976n, - "asBuffer": { - "data": [ - 12, - 202, - 175, - 220, - 156, - 53, - 55, - 67, - 151, - 13, - 78, - 48, - 90, - 231, - 54, - 65, - 206, - 105, - 79, - 7, - 219, - 103, - 136, - 109, - 39, - 105, - 201, - 237, - 136, - 233, - 105, - 216, - ], - "type": "Buffer", + "partial": PartialStateReference { + "contractTree": AppendOnlyTreeSnapshot { + "nextAvailableLeafIndex": 0, + "root": Fr { + "asBigInt": 11033938207523021649122316027295742559227608161317650429835019071130941169536n, + "asBuffer": { + "data": [ + 24, + 100, + 252, + 218, + 168, + 15, + 242, + 113, + 145, + 84, + 250, + 124, + 138, + 144, + 80, + 102, + 41, + 114, + 112, + 113, + 104, + 214, + 158, + 172, + 157, + 182, + 253, + 49, + 16, + 130, + 159, + 128, + ], + "type": "Buffer", + }, + }, + }, + "noteHashTree": AppendOnlyTreeSnapshot { + "nextAvailableLeafIndex": 0, + "root": Fr { + "asBigInt": 10127882181290008410413105921460858232892226592306749913988016925836213768395n, + "asBuffer": { + "data": [ + 22, + 100, + 45, + 156, + 205, + 131, + 70, + 196, + 3, + 170, + 76, + 63, + 164, + 81, + 23, + 139, + 34, + 83, + 74, + 39, + 3, + 92, + 218, + 166, + 236, + 52, + 174, + 83, + 178, + 156, + 80, + 203, + ], + "type": "Buffer", + }, + }, + }, + "nullifierTree": AppendOnlyTreeSnapshot { + "nextAvailableLeafIndex": 0, + "root": Fr { + "asBigInt": 5342309968596764527275045470866818007603635181649161546597860399861598581368n, + "asBuffer": { + "data": [ + 11, + 207, + 163, + 233, + 241, + 168, + 146, + 46, + 233, + 44, + 109, + 201, + 100, + 214, + 89, + 89, + 7, + 193, + 128, + 74, + 134, + 117, + 55, + 116, + 50, + 43, + 70, + 143, + 105, + 212, + 242, + 120, + ], + "type": "Buffer", + }, + }, + }, + "publicDataTree": AppendOnlyTreeSnapshot { + "nextAvailableLeafIndex": 0, + "root": Fr { + "asBigInt": 13940981882517738105981911020707002777955674699044852872880094407282064863456n, + "asBuffer": { + "data": [ + 30, + 210, + 80, + 237, + 115, + 219, + 110, + 112, + 128, + 92, + 78, + 252, + 240, + 5, + 110, + 134, + 149, + 183, + 156, + 211, + 186, + 65, + 142, + 130, + 124, + 24, + 77, + 238, + 108, + 111, + 176, + 224, + ], + "type": "Buffer", + }, + }, + }, }, }, }, "txContext": TxContext { "chainId": Fr { - "asBigInt": 0n, + "asBigInt": 31337n, "asBuffer": { "data": [ 0, @@ -65508,212 +66703,129 @@ KernelCircuitPublicInputsFinal { 0, 0, 0, - 0, - 0, + 122, + 105, ], "type": "Buffer", }, }, "contractDeploymentData": ContractDeploymentData { - "constructorVkHash": Fr { - "asBigInt": 4946902893997605007258693448883037341256770656195244398892734919432197304822n, + "contractAddressSalt": Fr { + "asBigInt": 18960597193497228634655620687090300150990844839001764515818734920414615950424n, "asBuffer": { "data": [ - 10, - 239, - 217, - 10, - 105, - 166, - 67, - 50, - 76, - 123, - 240, - 169, - 189, - 59, - 35, - 173, - 160, - 144, - 173, - 136, - 55, - 115, + 41, + 235, + 81, + 85, + 78, + 234, + 129, 253, - 240, - 176, - 173, - 82, - 169, - 247, - 214, - 241, - 246, + 22, + 223, + 249, + 80, + 61, + 139, + 48, + 200, + 107, + 132, + 228, + 114, + 181, + 85, + 171, + 38, + 47, + 3, + 137, + 78, + 8, + 197, + 212, + 88, ], "type": "Buffer", }, }, - "contractAddressSalt": Fr { - "asBigInt": 13918524182926832455178861490988425129196887976468020413394338716458484696156n, + "contractClassId": Fr { + "asBigInt": 3721272162218164835126864072380876236437289718664503134823660124384776633747n, "asBuffer": { "data": [ - 30, - 197, + 8, + 58, + 42, + 87, + 236, + 192, + 172, + 118, + 190, + 87, + 145, 155, - 3, - 19, - 250, - 80, - 67, - 2, - 195, - 51, - 111, + 200, + 38, + 88, + 218, + 65, + 254, + 215, 201, - 17, - 214, - 136, - 237, - 174, - 103, + 170, 196, - 251, - 242, - 41, - 214, - 140, - 127, - 54, - 237, - 135, - 151, - 4, - 92, + 87, + 90, + 45, + 201, + 211, + 61, + 167, + 107, + 61, + 147, ], "type": "Buffer", }, }, - "deployerPublicKey": Point { - "kind": "point", - "x": Fr { - "asBigInt": 13513162828633936749079339485623471377790691038584182237805001838837073529635n, - "asBuffer": { - "data": [ - 29, - 224, - 45, - 218, - 202, - 198, - 210, - 244, - 39, - 229, - 240, - 211, - 206, - 89, - 215, - 41, - 79, - 20, - 98, - 128, - 69, - 93, - 212, - 197, - 130, - 37, - 78, - 11, - 76, - 37, - 75, - 35, - ], - "type": "Buffer", - }, - }, - "y": Fr { - "asBigInt": 16193209371316637857741102275574203818928254115376185574760513755303226932941n, - "asBuffer": { - "data": [ - 35, - 205, - 8, - 29, - 254, - 156, - 13, - 24, - 115, - 182, - 90, - 54, - 160, - 136, - 88, - 231, - 58, - 155, - 48, - 208, - 51, - 158, - 148, - 196, - 145, - 93, - 113, - 16, - 226, - 240, - 126, - 205, - ], - "type": "Buffer", - }, - }, - }, - "functionTreeRoot": Fr { - "asBigInt": 5733828181279984846924392032049330885938310333906649337231112228024374578179n, + "initializationHash": Fr { + "asBigInt": 1583326240861609738393684596312518968005858067213923665222866669013140837326n, "asBuffer": { "data": [ - 12, - 173, - 59, - 83, - 145, - 228, - 10, - 248, - 116, - 62, - 16, - 83, - 192, - 21, - 225, - 106, - 186, - 198, - 16, - 10, - 139, - 145, - 117, - 18, - 192, - 131, - 203, - 76, - 187, - 140, - 204, 3, + 128, + 33, + 130, + 79, + 189, + 152, + 187, + 14, + 56, + 139, + 14, + 254, + 24, + 247, + 46, + 147, + 80, + 247, + 69, + 100, + 129, + 113, + 69, + 57, + 186, + 88, + 61, + 227, + 113, + 19, + 206, ], "type": "Buffer", }, @@ -65745,12 +66857,95 @@ KernelCircuitPublicInputsFinal { "type": "Buffer", }, }, + "publicKey": Point { + "kind": "point", + "x": Fr { + "asBigInt": 12697532002339620472742432431024876157955324367347542642615813365560119669742n, + "asBuffer": { + "data": [ + 28, + 18, + 140, + 102, + 4, + 43, + 63, + 59, + 161, + 25, + 83, + 110, + 101, + 86, + 173, + 111, + 187, + 60, + 253, + 28, + 27, + 71, + 247, + 241, + 124, + 56, + 245, + 205, + 103, + 244, + 51, + 238, + ], + "type": "Buffer", + }, + }, + "y": Fr { + "asBigInt": 11975354909605390651205529513933593489556044760044635062162622799898161844035n, + "asBuffer": { + "data": [ + 26, + 121, + 207, + 136, + 223, + 172, + 217, + 195, + 113, + 148, + 204, + 232, + 229, + 205, + 128, + 233, + 116, + 228, + 140, + 190, + 169, + 170, + 76, + 253, + 104, + 125, + 155, + 29, + 59, + 30, + 15, + 67, + ], + "type": "Buffer", + }, + }, + }, }, "isContractDeploymentTx": true, "isFeePaymentTx": false, "isRebatePaymentTx": false, "version": Fr { - "asBigInt": 0n, + "asBigInt": 1n, "asBuffer": { "data": [ 0, @@ -65784,7 +66979,7 @@ KernelCircuitPublicInputsFinal { 0, 0, 0, - 0, + 1, ], "type": "Buffer", }, @@ -66020,7 +67215,7 @@ KernelCircuitPublicInputsFinal { }, "encryptedLogsHash": [ Fr { - "asBigInt": 116881760094330735023399760917603536324n, + "asBigInt": 220631985307853222392411421903717783230n, "asBuffer": { "data": [ 0, @@ -66039,28 +67234,28 @@ KernelCircuitPublicInputsFinal { 0, 0, 0, - 87, - 238, - 155, - 177, - 38, - 64, - 133, - 236, - 244, + 165, + 252, + 44, 186, - 130, - 116, - 178, - 51, - 205, - 196, + 105, + 28, + 161, + 120, + 75, + 72, + 61, + 67, + 244, + 201, + 214, + 190, ], "type": "Buffer", }, }, Fr { - "asBigInt": 184145148802329932417389828290829878776n, + "asBigInt": 215807232426364884712946164821332168459n, "asBuffer": { "data": [ 0, @@ -66079,22 +67274,22 @@ KernelCircuitPublicInputsFinal { 0, 0, 0, - 138, - 137, - 16, - 204, - 107, - 147, - 180, - 57, + 162, + 90, + 246, + 11, 154, - 30, - 189, + 6, + 31, + 75, + 53, + 138, + 157, + 77, + 165, + 25, 143, - 191, - 180, - 5, - 248, + 11, ], "type": "Buffer", }, @@ -66103,7 +67298,7 @@ KernelCircuitPublicInputsFinal { "newCommitments": [ SideEffect { "counter": Fr { - "asBigInt": 0n, + "asBigInt": 3n, "asBuffer": { "data": [ 0, @@ -66137,47 +67332,47 @@ KernelCircuitPublicInputsFinal { 0, 0, 0, - 0, + 3, ], "type": "Buffer", }, }, "value": Fr { - "asBigInt": 12771775224118191483898915169603387229405021446030587347982956765852896294728n, + "asBigInt": 10506242983037991277444558816101557686655363023791188383469332863673121244747n, "asBuffer": { "data": [ - 28, - 60, - 145, - 144, - 42, - 59, 23, - 71, - 27, - 191, - 185, - 73, + 58, + 82, + 162, + 238, + 217, + 203, + 143, + 151, 59, - 54, + 204, + 180, + 151, 69, - 245, - 187, - 21, + 22, + 160, + 198, + 134, + 140, + 194, + 249, 225, - 58, - 128, - 182, - 212, - 12, - 224, - 176, - 240, - 147, - 18, - 147, - 119, - 72, + 145, + 177, + 191, + 120, + 110, + 197, + 210, + 154, + 6, + 75, ], "type": "Buffer", }, @@ -71353,81 +72548,81 @@ KernelCircuitPublicInputsFinal { "newContracts": [ NewContractData { "contractAddress": AztecAddress { - "asBigInt": 17136208615489370595324712951074318609059907391564891634353187633922419015940n, + "asBigInt": 12846613415199169272271044003892051734354428916844772940648565627858426402568n, "asBuffer": { "data": [ - 37, - 226, - 192, - 23, - 245, - 218, - 31, - 153, - 68, - 1, + 28, + 102, + 236, + 238, + 243, + 156, + 64, 230, - 29, - 38, - 190, + 13, + 129, + 255, 67, - 94, - 60, - 250, - 38, + 45, + 128, + 244, + 97, + 77, + 110, + 184, + 171, 239, - 238, - 120, - 76, - 107, - 78, - 148, - 127, - 118, - 81, - 189, - 65, - 4, + 58, + 236, + 77, + 13, + 115, + 52, + 66, + 229, + 50, + 23, + 8, ], "type": "Buffer", }, }, - "functionTreeRoot": Fr { - "asBigInt": 5733828181279984846924392032049330885938310333906649337231112228024374578179n, + "contractClassId": Fr { + "asBigInt": 3721272162218164835126864072380876236437289718664503134823660124384776633747n, "asBuffer": { "data": [ - 12, - 173, - 59, - 83, - 145, - 228, - 10, - 248, - 116, - 62, - 16, - 83, + 8, + 58, + 42, + 87, + 236, 192, - 21, - 225, - 106, - 186, - 198, - 16, - 10, - 139, + 172, + 118, + 190, + 87, 145, - 117, - 18, - 192, - 131, - 203, - 76, - 187, - 140, - 204, - 3, + 155, + 200, + 38, + 88, + 218, + 65, + 254, + 215, + 201, + 170, + 196, + 87, + 90, + 45, + 201, + 211, + 61, + 167, + 107, + 61, + 147, ], "type": "Buffer", }, @@ -71626,41 +72821,41 @@ KernelCircuitPublicInputsFinal { }, }, "value": Fr { - "asBigInt": 11322578994265849565401386951246010528140686424276775038536393177707801557130n, + "asBigInt": 16820956071491106255363061370815528401274596983393081031060346934333436515478n, "asBuffer": { "data": [ - 25, - 8, - 90, - 68, - 120, - 196, - 170, - 57, - 148, - 212, - 165, - 147, - 94, - 175, - 94, + 37, + 48, + 82, + 225, + 53, + 75, + 28, + 133, + 187, + 180, 13, - 88, - 114, - 106, - 117, - 141, - 57, - 138, - 151, - 246, - 52, - 223, 34, - 211, - 61, - 56, - 138, + 100, + 118, + 102, + 175, + 156, + 157, + 215, + 80, + 177, + 242, + 177, + 232, + 173, + 217, + 0, + 21, + 50, + 174, + 212, + 150, ], "type": "Buffer", }, @@ -71748,41 +72943,41 @@ KernelCircuitPublicInputsFinal { }, }, "value": Fr { - "asBigInt": 7094590644143379362066385400655107720501862419669243795306142485975929345465n, + "asBigInt": 11839557548471584510452581119038172413276872385031485614679286956310180824554n, "asBuffer": { "data": [ - 15, - 175, - 101, - 96, - 137, - 229, - 168, - 211, - 33, - 182, - 79, - 66, - 15, - 192, - 8, - 0, - 87, - 54, - 160, - 180, - 240, - 184, - 88, - 136, + 26, + 44, + 243, + 188, + 205, + 85, + 111, + 133, + 83, 145, - 36, - 19, - 146, 200, - 38, - 85, - 185, + 148, + 153, + 26, + 75, + 95, + 136, + 219, + 6, + 171, + 104, + 146, + 138, + 178, + 155, + 132, + 126, + 255, + 177, + 94, + 133, + 234, ], "type": "Buffer", }, @@ -71870,41 +73065,41 @@ KernelCircuitPublicInputsFinal { }, }, "value": Fr { - "asBigInt": 131391450486342918604555900920288880952936359657946230216264970318333633026n, + "asBigInt": 5735195398790857731328283581018066889458786547813035014232036710200459375595n, "asBuffer": { "data": [ - 0, - 74, - 93, - 107, - 195, - 78, - 132, - 197, - 163, - 215, - 166, - 37, - 163, - 119, - 47, - 77, - 47, - 132, - 199, - 212, - 102, - 55, - 105, - 30, - 246, - 78, - 226, - 113, - 30, + 12, + 174, + 1, 108, - 98, - 2, + 124, + 99, + 115, + 24, + 59, + 10, + 156, + 160, + 158, + 168, + 84, + 197, + 24, + 190, + 48, + 167, + 160, + 200, + 133, + 237, + 1, + 235, + 162, + 162, + 4, + 99, + 179, + 235, ], "type": "Buffer", }, diff --git a/yarn-project/noir-protocol-circuits/src/crates/private-kernel-lib/src/common.nr b/yarn-project/noir-protocol-circuits/src/crates/private-kernel-lib/src/common.nr index 44f36ddbee9..9b875910712 100644 --- a/yarn-project/noir-protocol-circuits/src/crates/private-kernel-lib/src/common.nr +++ b/yarn-project/noir-protocol-circuits/src/crates/private-kernel-lib/src/common.nr @@ -2,17 +2,17 @@ use dep::types::{ abis::{ call_request::CallRequest, combined_accumulated_data::CombinedAccumulatedData, - complete_address::CompleteAddress, function_data::FunctionData, kernel_circuit_public_inputs::KernelCircuitPublicInputsBuilder, membership_witness::ReadRequestMembershipWitness, new_contract_data::NewContractData, + nullifier_key_validation_request::NullifierKeyValidationRequestContext, private_circuit_public_inputs::PrivateCircuitPublicInputs, private_kernel::private_call_data::PrivateCallData, previous_kernel_data::PreviousKernelData, side_effect::{SideEffect, SideEffectLinkedToNoteHash}, }, - address::{AztecAddress, EthAddress}, + address::{AztecAddress, EthAddress, compute_initialization_hash}, contrakt::deployment_data::ContractDeploymentData, constants::{ MAX_NEW_NULLIFIERS_PER_CALL, @@ -20,7 +20,9 @@ use dep::types::{ MAX_NEW_COMMITMENTS_PER_CALL, MAX_PRIVATE_CALL_STACK_LENGTH_PER_CALL, MAX_READ_REQUESTS_PER_CALL, + MAX_NULLIFIER_KEY_VALIDATION_REQUESTS_PER_CALL, }, + grumpkin_private_key::GrumpkinPrivateKey, hash::{ compute_constructor_hash, compute_l2_to_l1_hash, @@ -28,6 +30,7 @@ use dep::types::{ compute_new_contract_address_hash, contract_tree_root_from_siblings, function_tree_root_from_siblings, + pedersen_hash, read_request_root_from_siblings, silo_commitment, silo_nullifier, @@ -39,18 +42,18 @@ use dep::types::{ array_to_bounded_vec, validate_array, }, - bounded_vec::BoundedVec, }, - traits::is_empty_array, + traits::{is_empty, is_empty_array}, }; pub fn validate_arrays(app_public_inputs: PrivateCircuitPublicInputs) { // Each of the following arrays is expected to be zero-padded. // In addition, some of the following arrays (new_commitments, etc...) are passed - // to push_array_to_array() routines which rely on the passed arrays to be well-formed. + // to extend_from_array_to_array() routines which rely on the passed arrays to be well-formed. validate_array(app_public_inputs.return_values); validate_array(app_public_inputs.read_requests); + validate_array(app_public_inputs.nullifier_key_validation_requests); validate_array(app_public_inputs.new_commitments); validate_array(app_public_inputs.new_nullifiers); validate_array(app_public_inputs.private_call_stack_hashes); @@ -106,6 +109,7 @@ pub fn initialize_end_values( let start = previous_kernel.public_inputs.end; public_inputs.end.read_requests = array_to_bounded_vec(start.read_requests); + public_inputs.end.nullifier_key_validation_requests = array_to_bounded_vec(start.nullifier_key_validation_requests); public_inputs.end.new_commitments = array_to_bounded_vec(start.new_commitments); public_inputs.end.new_nullifiers = array_to_bounded_vec(start.new_nullifiers); @@ -173,6 +177,8 @@ pub fn update_end_values(private_call: PrivateCallData, public_inputs: &mut Kern let read_requests = private_call_public_inputs.read_requests; let read_request_membership_witnesses = private_call.read_request_membership_witnesses; + let nullifier_key_validation_requests = private_call_public_inputs.nullifier_key_validation_requests; + let new_commitments = private_call_public_inputs.new_commitments; let new_nullifiers = private_call_public_inputs.new_nullifiers; @@ -190,7 +196,15 @@ pub fn update_end_values(private_call: PrivateCallData, public_inputs: &mut Kern ) } } - public_inputs.end.read_requests.push_vec(siloed_read_requests); + public_inputs.end.read_requests.extend_from_bounded_vec(siloed_read_requests); + + // Nullifier key validation requests. + for i in 0..MAX_NULLIFIER_KEY_VALIDATION_REQUESTS_PER_CALL { + let request = nullifier_key_validation_requests[i]; + if !is_empty(request) { + public_inputs.end.nullifier_key_validation_requests.push(request.to_context(storage_contract_address)); + } + } // Enhance commitments and nullifiers with domain separation whereby domain is the contract. // @@ -213,7 +227,7 @@ pub fn update_end_values(private_call: PrivateCallData, public_inputs: &mut Kern ); } } - public_inputs.end.new_nullifiers.push_vec(siloed_new_nullifiers); + public_inputs.end.new_nullifiers.extend_from_bounded_vec(siloed_new_nullifiers); // commitments let mut siloed_new_commitments: BoundedVec = BoundedVec::new(SideEffect::empty()); @@ -225,7 +239,7 @@ pub fn update_end_values(private_call: PrivateCallData, public_inputs: &mut Kern ); } } - public_inputs.end.new_commitments.push_vec(siloed_new_commitments); + public_inputs.end.new_commitments.extend_from_bounded_vec(siloed_new_commitments); // Call stacks // Private call stack. @@ -235,7 +249,7 @@ pub fn update_end_values(private_call: PrivateCallData, public_inputs: &mut Kern private_call_public_inputs.private_call_stack_hashes, private_call ); - public_inputs.end.private_call_stack.push_vec(private_call_stack); + public_inputs.end.private_call_stack.extend_from_bounded_vec(private_call_stack); // Public call stack. let public_call_stack = array_to_bounded_vec(private_call.public_call_stack); validate_call_requests( @@ -243,7 +257,7 @@ pub fn update_end_values(private_call: PrivateCallData, public_inputs: &mut Kern private_call_public_inputs.public_call_stack_hashes, private_call ); - public_inputs.end.public_call_stack.push_vec(public_call_stack); + public_inputs.end.public_call_stack.extend_from_bounded_vec(public_call_stack); // new l2 to l1 messages let portal_contract_address = private_call.portal_contract_address; @@ -262,7 +276,7 @@ pub fn update_end_values(private_call: PrivateCallData, public_inputs: &mut Kern new_l2_to_l1_msgs_to_insert.push(new_l2_to_l1_msgs) } } - public_inputs.end.new_l2_to_l1_msgs.push_vec(new_l2_to_l1_msgs_to_insert); + public_inputs.end.new_l2_to_l1_msgs.extend_from_bounded_vec(new_l2_to_l1_msgs_to_insert); // logs hashes // See the following thread if not clear: @@ -297,23 +311,27 @@ pub fn contract_logic( // input storage contract address must be 0 if its a constructor call and non-zero otherwise if is_contract_deployment { - //TODO(https://github.com/AztecProtocol/aztec-packages/issues/3062) use locally computed private_call_vk_hash here - let constructor_hash = compute_constructor_hash( - function_data, - private_call_public_inputs.args_hash, - contract_dep_data.constructor_vk_hash + let computed_initialization_hash = compute_initialization_hash( + function_data.selector.to_field(), + private_call_public_inputs.args_hash ); - let new_contract_address = CompleteAddress::compute( - contract_dep_data.deployer_public_key, + + assert( + computed_initialization_hash == contract_dep_data.initialization_hash, "initialization hash does not match computed one" + ); + + let new_contract_address = AztecAddress::compute_from_public_key( + contract_dep_data.public_key, + contract_dep_data.contract_class_id, contract_dep_data.contract_address_salt, - contract_dep_data.function_tree_root, - constructor_hash - ).address; + contract_dep_data.initialization_hash, + contract_dep_data.portal_contract_address + ); let new_contract_data = NewContractData { contract_address: new_contract_address, portal_contract_address, - function_tree_root: contract_dep_data.function_tree_root + contract_class_id: contract_dep_data.contract_class_id }; public_inputs.end.new_contracts.push(new_contract_data); @@ -360,10 +378,15 @@ pub fn contract_logic( private_call.contract_leaf_membership_witness.sibling_path ); - let purported_contract_tree_root = private_call.call_stack_item.public_inputs.block_header.contract_tree_root(); - assert_eq( - computed_contract_tree_root, purported_contract_tree_root, "computed_contract_tree_root does not match purported_contract_tree_root" - ); + let purported_contract_tree_root = private_call.call_stack_item.public_inputs.historical_header.state.partial.contract_tree.root; + // TODO(@spalladino): Re-enable this check using contract classes: + // - Compute the function_leaf + // - Compute the function_tree_root using the sibling path + // - Compute the contract_class_id (needs injecting artifact_hash and bytecode_commitment into kernel) + // - Compute the address using the class_id (needs injecting salted_initialization_hash and public_keys_hash) + // assert_eq( + // computed_contract_tree_root, purported_contract_tree_root, "computed_contract_tree_root does not match purported_contract_tree_root" + // ); } } @@ -408,3 +431,28 @@ pub fn validate_call_against_request(private_call: PrivateCallData, request: Cal ); } } + +fn field_to_grumpkin_private_key(val: Field) -> GrumpkinPrivateKey { + let bytes = val.to_be_bytes(32); + let mut v = 1; + let mut high = 0; + let mut low = 0; + + for i in 0..16 { + high = high + (bytes[15 - i] as Field) * v; + low = low + (bytes[16 + 15 - i] as Field) * v; + v = v * 256; + } + + GrumpkinPrivateKey { high, low } +} + +pub fn compute_siloed_nullifier_secret_key(secret_key: GrumpkinPrivateKey, contract_address: AztecAddress) -> GrumpkinPrivateKey { + // TODO: Temporary hack. Should replace it with a secure way to derive the secret key. + // Match the way keys are derived in circuits.js/src/keys/index.ts + let hash = pedersen_hash( + [secret_key.high, secret_key.low, contract_address.to_field()], + 0 + ); + field_to_grumpkin_private_key(hash) +} diff --git a/yarn-project/noir-protocol-circuits/src/crates/private-kernel-lib/src/private_kernel_init.nr b/yarn-project/noir-protocol-circuits/src/crates/private-kernel-lib/src/private_kernel_init.nr index 5b215d82971..798bcf16f9c 100644 --- a/yarn-project/noir-protocol-circuits/src/crates/private-kernel-lib/src/private_kernel_init.nr +++ b/yarn-project/noir-protocol-circuits/src/crates/private-kernel-lib/src/private_kernel_init.nr @@ -7,6 +7,7 @@ use dep::types::{ kernel_circuit_public_inputs::{KernelCircuitPublicInputs, KernelCircuitPublicInputsBuilder}, side_effect::{SideEffect, SideEffectLinkedToNoteHash}, }, + address::{AztecAddress, PublicKeysHash, compute_initialization_hash}, mocked::{Proof, verify_previous_kernel_state}, transaction::request::TxRequest, traits::is_empty_array, @@ -21,7 +22,7 @@ struct PrivateKernelInputsInit { impl PrivateKernelInputsInit { fn initialize_end_values(self, public_inputs: &mut KernelCircuitPublicInputsBuilder) { public_inputs.constants = CombinedConstantData { - block_header: self.private_call.call_stack_item.public_inputs.block_header, + historical_header: self.private_call.call_stack_item.public_inputs.historical_header, tx_context: self.tx_request.tx_context, }; } @@ -91,7 +92,7 @@ impl PrivateKernelInputsInit { self.validate_this_private_call_against_tx_request(); common::validate_read_requests( - public_inputs.constants.block_header.note_hash_tree_root(), + public_inputs.constants.historical_header.state.partial.note_hash_tree.root, self.private_call.call_stack_item.public_inputs.read_requests, self.private_call.read_request_membership_witnesses ); @@ -122,12 +123,14 @@ mod tests { }; use dep::types::{ abis::{ - complete_address::CompleteAddress, kernel_circuit_public_inputs::KernelCircuitPublicInputs, + nullifier_key_validation_request::NullifierKeyValidationRequest, private_kernel::private_call_data::PrivateCallData, side_effect::{SideEffect, SideEffectLinkedToNoteHash}, }, - address::AztecAddress, + grumpkin_point::GrumpkinPoint, + grumpkin_private_key::GrumpkinPrivateKey, + address::{AztecAddress, compute_initialization_hash}, hash::{ compute_constructor_hash, compute_logs_hash, @@ -184,18 +187,20 @@ mod tests { assert_eq(public_inputs.end.new_contracts.len(), 1); let cdd = tx_request.tx_context.contract_deployment_data; - let private_circuit_vk_hash = stdlib_recursion_verification_key_compress_native_vk(private_call.vk); - let constructor_hash = compute_constructor_hash( - tx_request.function_data, - tx_request.args_hash, - private_circuit_vk_hash + let computed_initialization_hash = compute_initialization_hash( + tx_request.function_data.selector.to_field(), + tx_request.args_hash ); - let contract_address = CompleteAddress::compute( - cdd.deployer_public_key, + assert( + computed_initialization_hash == cdd.initialization_hash, "initialization hash does not match computed one" + ); + let contract_address = AztecAddress::compute_from_public_key( + cdd.public_key, + cdd.contract_class_id, cdd.contract_address_salt, - cdd.function_tree_root, - constructor_hash - ).address; + cdd.initialization_hash, + cdd.portal_contract_address + ); assert(public_inputs.end.new_contracts[0].contract_address.eq(contract_address)); } @@ -259,7 +264,7 @@ mod tests { fn input_validation_malformed_arrays_return_values() { let mut builder = PrivateKernelInitInputsBuilder::new_constructor(); - builder.private_call.public_inputs.return_values.push_array([0, 9123]); + builder.private_call.public_inputs.return_values.extend_from_array([0, 9123]); builder.failed(); } @@ -268,7 +273,7 @@ mod tests { fn input_validation_malformed_arrays_read_requests() { let mut builder = PrivateKernelInitInputsBuilder::new_constructor(); - builder.private_call.public_inputs.read_requests.push_array( + builder.private_call.public_inputs.read_requests.extend_from_array( [ SideEffect { value: 0, counter: 0 }, SideEffect { value: 9123, counter: 1 } @@ -282,7 +287,7 @@ mod tests { fn input_validation_malformed_arrays_commitments() { let mut builder = PrivateKernelInitInputsBuilder::new_constructor(); - builder.private_call.public_inputs.new_commitments.push_array( + builder.private_call.public_inputs.new_commitments.extend_from_array( [ SideEffect { value: 0, counter: 0 }, SideEffect { value: 9123, counter: 1 } @@ -296,7 +301,7 @@ mod tests { fn input_validation_malformed_arrays_nullifiers() { let mut builder = PrivateKernelInitInputsBuilder::new_constructor(); - builder.private_call.public_inputs.new_nullifiers.push_array( + builder.private_call.public_inputs.new_nullifiers.extend_from_array( [ SideEffectLinkedToNoteHash { value: 0, note_hash: 0, counter: 0 }, SideEffectLinkedToNoteHash { value: 9123, note_hash: 0, counter: 1 } @@ -310,7 +315,7 @@ mod tests { fn input_validation_malformed_arrays_private_call_stack() { let mut builder = PrivateKernelInitInputsBuilder::new_constructor(); - builder.private_call.public_inputs.private_call_stack_hashes.push_array([0, 9123]); + builder.private_call.public_inputs.private_call_stack_hashes.extend_from_array([0, 9123]); builder.failed(); } @@ -319,7 +324,7 @@ mod tests { fn input_validation_malformed_arrays_public_call_stack() { let mut builder = PrivateKernelInitInputsBuilder::new_constructor(); - builder.private_call.public_inputs.public_call_stack_hashes.push_array([0, 9123]); + builder.private_call.public_inputs.public_call_stack_hashes.extend_from_array([0, 9123]); builder.failed(); } @@ -328,7 +333,7 @@ mod tests { fn input_validation_malformed_arrays_new_l2_to_l1_msgs() { let mut builder = PrivateKernelInitInputsBuilder::new_constructor(); - builder.private_call.public_inputs.new_l2_to_l1_msgs.push_array([0, 9123]); + builder.private_call.public_inputs.new_l2_to_l1_msgs.extend_from_array([0, 9123]); builder.failed(); } @@ -424,7 +429,7 @@ mod tests { builder.failed(); } - #[test(should_fail_with="computed_contract_tree_root does not match purported_contract_tree_root")] + // #[test(should_fail_with="computed_contract_tree_root does not match purported_contract_tree_root")] fn private_function_incorrect_contract_leaf_index_fails() { let mut builder = PrivateKernelInitInputsBuilder::new(); @@ -435,7 +440,7 @@ mod tests { builder.failed(); } - #[test(should_fail_with="computed_contract_tree_root does not match purported_contract_tree_root")] + // #[test(should_fail_with="computed_contract_tree_root does not match purported_contract_tree_root")] fn private_function_incorrect_contract_leaf_sibling_path_fails() { let mut builder = PrivateKernelInitInputsBuilder::new(); @@ -446,7 +451,7 @@ mod tests { builder.failed(); } - #[test(should_fail_with="computed_contract_tree_root does not match purported_contract_tree_root")] + // #[test(should_fail_with="computed_contract_tree_root does not match purported_contract_tree_root")] fn private_function_incorrect_function_leaf_index_fails() { let mut builder = PrivateKernelInitInputsBuilder::new(); @@ -457,7 +462,7 @@ mod tests { builder.failed(); } - #[test(should_fail_with="computed_contract_tree_root does not match purported_contract_tree_root")] + // #[test(should_fail_with="computed_contract_tree_root does not match purported_contract_tree_root")] fn private_function_incorrect_function_leaf_sibling_path_fails() { let mut builder = PrivateKernelInitInputsBuilder::new(); @@ -619,4 +624,23 @@ mod tests { // non-transient read requests are NOT forwarded assert_eq(array_length(public_inputs.end.read_requests), MAX_READ_REQUESTS_PER_CALL); } + + #[test] + fn propagate_nullifier_key_validation_requests() { + let mut builder = PrivateKernelInitInputsBuilder::new_constructor(); + + let request = NullifierKeyValidationRequest { public_key: GrumpkinPoint { x: 1, y: 2 }, secret_key: GrumpkinPrivateKey { high: 3, low: 4 } }; + builder.private_call.public_inputs.nullifier_key_validation_requests.push(request); + + let public_inputs = builder.execute(); + + assert_eq(array_length(public_inputs.end.nullifier_key_validation_requests), 1); + + let request_context = public_inputs.end.nullifier_key_validation_requests[0]; + assert_eq(request_context.public_key, request.public_key); + assert_eq(request_context.secret_key, request.secret_key); + assert_eq( + request_context.contract_address, builder.private_call.public_inputs.call_context.storage_contract_address + ); + } } diff --git a/yarn-project/noir-protocol-circuits/src/crates/private-kernel-lib/src/private_kernel_inner.nr b/yarn-project/noir-protocol-circuits/src/crates/private-kernel-lib/src/private_kernel_inner.nr index 98cbe5ff2ae..13144419983 100644 --- a/yarn-project/noir-protocol-circuits/src/crates/private-kernel-lib/src/private_kernel_inner.nr +++ b/yarn-project/noir-protocol-circuits/src/crates/private-kernel-lib/src/private_kernel_inner.nr @@ -22,8 +22,8 @@ impl PrivateKernelInputsInner { } fn validate_contract_tree_root(self) { - let purported_contract_tree_root = self.private_call.call_stack_item.public_inputs.block_header.contract_tree_root(); - let previous_kernel_contract_tree_root = self.previous_kernel.public_inputs.constants.block_header.contract_tree_root(); + let purported_contract_tree_root = self.private_call.call_stack_item.public_inputs.historical_header.state.partial.contract_tree.root; + let previous_kernel_contract_tree_root = self.previous_kernel.public_inputs.constants.historical_header.state.partial.contract_tree.root; assert(purported_contract_tree_root == previous_kernel_contract_tree_root, "purported_contract_tree_root does not match previous_kernel_contract_tree_root"); } @@ -52,7 +52,7 @@ impl PrivateKernelInputsInner { self.pop_and_validate_this_private_call_hash(&mut public_inputs); common::validate_read_requests( - public_inputs.constants.block_header.note_hash_tree_root(), + public_inputs.constants.historical_header.state.partial.note_hash_tree.root, self.private_call.call_stack_item.public_inputs.read_requests, // read requests from private call self.private_call.read_request_membership_witnesses); @@ -97,7 +97,6 @@ mod tests { hash::compute_logs_hash, utils::{ arrays::array_length, - bounded_vec::BoundedVec, }, }; @@ -170,14 +169,14 @@ mod tests { fn private_function_incorrect_contract_tree_root_fails() { let mut builder = PrivateKernelInnerInputsBuilder::new(); - // Set historical_tree_root to a wrong value (the correct value + 1). - let contract_tree_root = builder.previous_kernel.block_header.contract_tree_root; - builder.previous_kernel.block_header.contract_tree_root = contract_tree_root + 1; + // Set historical contract tree root to a wrong value (the correct value + 1). + let contract_tree_root = builder.previous_kernel.historical_header.state.partial.contract_tree.root; + builder.previous_kernel.historical_header.state.partial.contract_tree.root = contract_tree_root + 1; builder.failed(); } - #[test(should_fail_with = "computed_contract_tree_root does not match purported_contract_tree_root")] + // #[test(should_fail_with = "computed_contract_tree_root does not match purported_contract_tree_root")] fn private_function_incorrect_contract_leaf_index_fails() { let mut builder = PrivateKernelInnerInputsBuilder::new(); @@ -188,7 +187,7 @@ mod tests { builder.failed(); } - #[test(should_fail_with = "computed_contract_tree_root does not match purported_contract_tree_root")] + // #[test(should_fail_with = "computed_contract_tree_root does not match purported_contract_tree_root")] fn private_function_incorrect_contract_leaf_sibling_path_fails() { let mut builder = PrivateKernelInnerInputsBuilder::new(); @@ -199,7 +198,7 @@ mod tests { builder.failed(); } - #[test(should_fail_with = "computed_contract_tree_root does not match purported_contract_tree_root")] + // #[test(should_fail_with = "computed_contract_tree_root does not match purported_contract_tree_root")] fn private_function_incorrect_function_leaf_index_fails() { let mut builder = PrivateKernelInnerInputsBuilder::new(); @@ -210,7 +209,7 @@ mod tests { builder.failed(); } - #[test(should_fail_with = "computed_contract_tree_root does not match purported_contract_tree_root")] + // #[test(should_fail_with = "computed_contract_tree_root does not match purported_contract_tree_root")] fn private_function_incorrect_function_leaf_sibling_path_fails() { let mut builder = PrivateKernelInnerInputsBuilder::new(); @@ -441,7 +440,7 @@ mod tests { fn input_validation_malformed_arrays_return_values() { let mut builder = PrivateKernelInnerInputsBuilder::new(); - builder.private_call.public_inputs.return_values.push_array([0, 553]); + builder.private_call.public_inputs.return_values.extend_from_array([0, 553]); builder.failed(); } @@ -450,7 +449,7 @@ mod tests { fn input_validation_malformed_arrays_read_requests() { let mut builder = PrivateKernelInnerInputsBuilder::new(); - builder.private_call.public_inputs.read_requests.push_array( + builder.private_call.public_inputs.read_requests.extend_from_array( [ SideEffect { value: 0, counter: 0 }, SideEffect { value: 9123, counter: 1 } @@ -464,7 +463,7 @@ mod tests { fn input_validation_malformed_arrays_commitments() { let mut builder = PrivateKernelInnerInputsBuilder::new(); - builder.private_call.public_inputs.new_commitments.push_array( + builder.private_call.public_inputs.new_commitments.extend_from_array( [ SideEffect { value: 0, counter: 0 }, SideEffect { value: 9123, counter: 1 } @@ -478,7 +477,7 @@ mod tests { fn input_validation_malformed_arrays_nullifiers() { let mut builder = PrivateKernelInnerInputsBuilder::new(); - builder.private_call.public_inputs.new_nullifiers.push_array( + builder.private_call.public_inputs.new_nullifiers.extend_from_array( [ SideEffectLinkedToNoteHash { value: 0, note_hash: 0, counter: 0 }, SideEffectLinkedToNoteHash { value: 12, note_hash: 0, counter: 1 } @@ -492,7 +491,7 @@ mod tests { fn input_validation_malformed_arrays_private_call_stack() { let mut builder = PrivateKernelInnerInputsBuilder::new(); - builder.private_call.public_inputs.private_call_stack_hashes.push_array([0, 888]); + builder.private_call.public_inputs.private_call_stack_hashes.extend_from_array([0, 888]); builder.failed(); } @@ -501,7 +500,7 @@ mod tests { fn input_validation_malformed_arrays_public_call_stack() { let mut builder = PrivateKernelInnerInputsBuilder::new(); - builder.private_call.public_inputs.public_call_stack_hashes.push_array([0, 888]); + builder.private_call.public_inputs.public_call_stack_hashes.extend_from_array([0, 888]); builder.failed(); } @@ -510,12 +509,12 @@ mod tests { fn input_validation_malformed_arrays_new_l2_to_l1_msgs() { let mut builder = PrivateKernelInnerInputsBuilder::new(); - builder.private_call.public_inputs.new_l2_to_l1_msgs.push_array([0, 888]); + builder.private_call.public_inputs.new_l2_to_l1_msgs.extend_from_array([0, 888]); builder.failed(); } - #[test(should_fail_with = "push_vec out of bounds")] + #[test(should_fail_with = "extend_from_bounded_vec out of bounds")] fn private_kernel_should_fail_if_aggregating_too_many_commitments() { let mut builder = PrivateKernelInnerInputsBuilder::new(); @@ -530,7 +529,7 @@ mod tests { counter: i as u32, }; } - builder.previous_kernel.end.new_commitments.push_array(full_new_commitments); + builder.previous_kernel.end.new_commitments.extend_from_array(full_new_commitments); builder.failed(); } @@ -610,8 +609,8 @@ mod tests { builder.private_call.append_read_requests(1); // Set the root to be a different root so the above read request is not under this root. - let old_root = builder.previous_kernel.block_header.note_hash_tree_root; - builder.previous_kernel.block_header.note_hash_tree_root = old_root + 1; + let old_root = builder.previous_kernel.historical_header.state.partial.note_hash_tree.root; + builder.previous_kernel.historical_header.state.partial.note_hash_tree.root = old_root + 1; builder.failed(); } diff --git a/yarn-project/noir-protocol-circuits/src/crates/private-kernel-lib/src/private_kernel_ordering.nr b/yarn-project/noir-protocol-circuits/src/crates/private-kernel-lib/src/private_kernel_ordering.nr index 8ba937ce90e..6c6496b7ad5 100644 --- a/yarn-project/noir-protocol-circuits/src/crates/private-kernel-lib/src/private_kernel_ordering.nr +++ b/yarn-project/noir-protocol-circuits/src/crates/private-kernel-lib/src/private_kernel_ordering.nr @@ -1,10 +1,13 @@ use crate::common; -use dep::std::unsafe; -use dep::std::option::Option; -use dep::std::cmp::Eq; +use dep::std::{ + cmp::Eq, + option::Option, + unsafe, +}; use dep::types::{ abis::{ call_request::CallRequest, + nullifier_key_validation_request::NullifierKeyValidationRequestContext, previous_kernel_data::PreviousKernelData, kernel_circuit_public_inputs::{ KernelCircuitPublicInputsBuilder, @@ -16,14 +19,15 @@ use dep::types::{ MAX_NEW_COMMITMENTS_PER_TX, MAX_NEW_NULLIFIERS_PER_TX, MAX_READ_REQUESTS_PER_TX, + MAX_NULLIFIER_KEY_VALIDATION_REQUESTS_PER_TX, }, + grumpkin_private_key::GrumpkinPrivateKey, hash::{ compute_commitment_nonce, compute_unique_siloed_commitment, }, utils::{ arrays::{array_length, array_eq}, - bounded_vec::BoundedVec, }, traits::{Empty, is_empty} }; @@ -36,6 +40,7 @@ struct PrivateKernelInputsOrdering { sorted_new_nullifiers: [SideEffectLinkedToNoteHash; MAX_NEW_NULLIFIERS_PER_TX], sorted_new_nullifiers_indexes: [u32; MAX_NEW_NULLIFIERS_PER_TX], nullifier_commitment_hints: [Field; MAX_NEW_NULLIFIERS_PER_TX], + master_nullifier_secret_keys: [GrumpkinPrivateKey; MAX_NULLIFIER_KEY_VALIDATION_REQUESTS_PER_TX], } impl PrivateKernelInputsOrdering { @@ -44,6 +49,24 @@ impl PrivateKernelInputsOrdering { "Private call stack must be empty when executing the ordering circuit"); } + fn validate_nullifier_keys(self, public_inputs: &mut KernelCircuitPublicInputsBuilder) { + let requests = self.previous_kernel.public_inputs.end.nullifier_key_validation_requests; + for i in 0..MAX_NULLIFIER_KEY_VALIDATION_REQUESTS_PER_TX { + let request = requests[i]; + if !is_empty(request) { + let master_secret_key = self.master_nullifier_secret_keys[i]; + let computed_public_key = master_secret_key.derive_public_key(); + assert(computed_public_key.eq(request.public_key), "Cannot derive nullifier public key from the master key."); + + let computed_secret_key = common::compute_siloed_nullifier_secret_key(master_secret_key, request.contract_address); + assert(computed_secret_key.eq(request.secret_key), "Cannot derive siloed secret key from the master key."); + } + } + + // Empty out nullifier key validation requests after verifying them. + public_inputs.end.nullifier_key_validation_requests = BoundedVec::new(NullifierKeyValidationRequestContext::empty()); + } + fn match_reads_to_commitments(self, public_inputs: &mut KernelCircuitPublicInputsBuilder) { let new_commitments = public_inputs.end.new_commitments; let read_requests = public_inputs.end.read_requests; @@ -181,6 +204,8 @@ impl PrivateKernelInputsOrdering { // Do this before any functions can modify the inputs. common::initialize_end_values(self.previous_kernel, &mut public_inputs); + self.validate_nullifier_keys(&mut public_inputs); + self.sort_arrays(&mut public_inputs); self.match_reads_to_commitments(&mut public_inputs); @@ -210,7 +235,6 @@ mod tests { tests::previous_kernel_data_builder::PreviousKernelDataBuilder, utils::{ arrays::{array_eq, array_length}, - bounded_vec::BoundedVec, }, traits::{Empty, is_empty, is_empty_array} }; @@ -312,6 +336,7 @@ mod tests { sorted_new_nullifiers, sorted_new_nullifiers_indexes, nullifier_commitment_hints: sorted_nullifier_commitment_hints, + master_nullifier_secret_keys: dep::std::unsafe::zeroed(), }; kernel.native_private_kernel_circuit_ordering() } diff --git a/yarn-project/noir-protocol-circuits/src/crates/public-kernel-lib/src/common.nr b/yarn-project/noir-protocol-circuits/src/crates/public-kernel-lib/src/common.nr index 277865e99cf..87929586626 100644 --- a/yarn-project/noir-protocol-circuits/src/crates/public-kernel-lib/src/common.nr +++ b/yarn-project/noir-protocol-circuits/src/crates/public-kernel-lib/src/common.nr @@ -3,7 +3,6 @@ use dep::types::{ call_request::CallRequest, call_stack_item::PublicCallStackItem, combined_accumulated_data::{CombinedAccumulatedData, CombinedAccumulatedDataBuilder}, - complete_address::CompleteAddress, kernel_circuit_public_inputs::KernelCircuitPublicInputsBuilder, new_contract_data::NewContractData, previous_kernel_data::PreviousKernelData, @@ -29,7 +28,6 @@ use dep::types::{ hash::{silo_commitment, silo_nullifier, compute_l2_to_l1_hash, accumulate_sha256}, utils::{ arrays::{array_length, array_to_bounded_vec}, - bounded_vec::BoundedVec, }, traits::is_empty_array }; @@ -139,7 +137,7 @@ pub fn update_public_end_values(public_call: PublicCallData, circuit_outputs: &m let public_call_requests = array_to_bounded_vec(public_call.public_call_stack); let hashes = public_call.call_stack_item.public_inputs.public_call_stack_hashes; validate_call_requests(public_call_requests, hashes, public_call); - circuit_outputs.end.public_call_stack.push_vec(public_call_requests); + circuit_outputs.end.public_call_stack.extend_from_bounded_vec(public_call_requests); propagate_new_nullifiers(public_call, circuit_outputs); propagate_new_commitments(public_call, circuit_outputs); @@ -172,7 +170,7 @@ fn propagate_valid_public_data_update_requests( public_data_update_requests.push(public_data_update_request); } } - circuit_outputs.end.public_data_update_requests.push_vec(public_data_update_requests); + circuit_outputs.end.public_data_update_requests.extend_from_bounded_vec(public_data_update_requests); } fn propagate_valid_public_data_reads(public_call: PublicCallData, circuit_outputs: &mut KernelCircuitPublicInputsBuilder) { @@ -191,7 +189,7 @@ fn propagate_valid_public_data_reads(public_call: PublicCallData, circuit_output public_data_reads.push(public_data_read); } } - circuit_outputs.end.public_data_reads.push_vec(public_data_reads); + circuit_outputs.end.public_data_reads.extend_from_bounded_vec(public_data_reads); } fn propagate_new_commitments(public_call: PublicCallData, circuit_outputs: &mut KernelCircuitPublicInputsBuilder) { @@ -208,7 +206,7 @@ fn propagate_new_commitments(public_call: PublicCallData, circuit_outputs: &mut siloed_new_commitments.push(SideEffect { value: siloed_new_commitment, counter: new_commitments[i].counter }); } } - circuit_outputs.end.new_commitments.push_vec(siloed_new_commitments); + circuit_outputs.end.new_commitments.extend_from_bounded_vec(siloed_new_commitments); } fn propagate_new_nullifiers(public_call: PublicCallData, circuit_outputs: &mut KernelCircuitPublicInputsBuilder) { @@ -231,7 +229,7 @@ fn propagate_new_nullifiers(public_call: PublicCallData, circuit_outputs: &mut K } } - circuit_outputs.end.new_nullifiers.push_vec(siloed_new_nullifiers); + circuit_outputs.end.new_nullifiers.extend_from_bounded_vec(siloed_new_nullifiers); } fn propagate_new_l2_to_l1_messages(public_call: PublicCallData, public_inputs: &mut KernelCircuitPublicInputsBuilder) { @@ -255,7 +253,7 @@ fn propagate_new_l2_to_l1_messages(public_call: PublicCallData, public_inputs: & new_l2_to_l1_msgs_to_insert.push(new_l2_to_l1_msgs) } } - public_inputs.end.new_l2_to_l1_msgs.push_vec(new_l2_to_l1_msgs_to_insert); + public_inputs.end.new_l2_to_l1_msgs.extend_from_bounded_vec(new_l2_to_l1_msgs_to_insert); } /** diff --git a/yarn-project/noir-protocol-circuits/src/crates/public-kernel-lib/src/public_kernel_private_previous.nr b/yarn-project/noir-protocol-circuits/src/crates/public-kernel-lib/src/public_kernel_private_previous.nr index 7a78633c2e6..43171da38b3 100644 --- a/yarn-project/noir-protocol-circuits/src/crates/public-kernel-lib/src/public_kernel_private_previous.nr +++ b/yarn-project/noir-protocol-circuits/src/crates/public-kernel-lib/src/public_kernel_private_previous.nr @@ -85,7 +85,6 @@ mod tests { array_eq, array_length, }, - bounded_vec::BoundedVec, }, }; use dep::types::constants::{ @@ -403,10 +402,10 @@ mod tests { NewContractData { contract_address: AztecAddress::from_field(123), portal_contract_address: EthAddress::from_field(456), - function_tree_root: 78 + contract_class_id: 78 } ]; - builder.previous_kernel.end.new_contracts.push_array(new_contracts); + builder.previous_kernel.end.new_contracts.extend_from_array(new_contracts); builder.public_call.append_public_call_requests_for_regular_calls(2); let storage = builder.public_call.public_call_stack.storage; diff --git a/yarn-project/noir-protocol-circuits/src/crates/public-kernel-lib/src/public_kernel_public_previous.nr b/yarn-project/noir-protocol-circuits/src/crates/public-kernel-lib/src/public_kernel_public_previous.nr index 985690d46b9..5f28c5d8dd7 100644 --- a/yarn-project/noir-protocol-circuits/src/crates/public-kernel-lib/src/public_kernel_public_previous.nr +++ b/yarn-project/noir-protocol-circuits/src/crates/public-kernel-lib/src/public_kernel_public_previous.nr @@ -180,7 +180,7 @@ mod tests { SideEffect { value: previous[1].value + 1, counter: 3 }, SideEffect { value: previous[1].value + 2, counter: 4 } ]; - builder.public_call.public_inputs.new_commitments.push_array(current); + builder.public_call.public_inputs.new_commitments.extend_from_array(current); let siloed = current.map(|c: SideEffect| silo_commitment(contract_address, c.value)); let new_commitments = [ previous[0], previous[1], SideEffect { value: siloed[0], counter: 3 }, SideEffect { value: siloed[1], counter: 4 } @@ -247,7 +247,7 @@ mod tests { SideEffectLinkedToNoteHash { value: silo_nullifier(contract_address, current.value), note_hash: current.note_hash, counter: current.counter } ); - builder.public_call.public_inputs.new_nullifiers.push_array(current); + builder.public_call.public_inputs.new_nullifiers.extend_from_array(current); // There are 3 nullifiers in the previous kernel. The first one is the tx nullifier. let new_nullifiers = [previous[0], previous[1], previous[2], siloed[0], siloed[1]]; @@ -265,10 +265,10 @@ mod tests { // Setup 1 new l2 to l1 message on the previous kernel. let previous = [12345]; - builder.previous_kernel.end.new_l2_to_l1_msgs.push_array(previous); + builder.previous_kernel.end.new_l2_to_l1_msgs.extend_from_array(previous); // Setup 1 new l2 to l1 message on the current public inputs. let current = [67890]; - builder.public_call.public_inputs.new_l2_to_l1_msgs.push_array(current); + builder.public_call.public_inputs.new_l2_to_l1_msgs.extend_from_array(current); let tx_context = builder.previous_kernel.tx_context; let version = tx_context.version; let chain_id = tx_context.chain_id; diff --git a/yarn-project/noir-protocol-circuits/src/crates/rollup-lib/src/base/base_rollup_inputs.nr b/yarn-project/noir-protocol-circuits/src/crates/rollup-lib/src/base/base_rollup_inputs.nr index 4a6579a2044..33612695bbb 100644 --- a/yarn-project/noir-protocol-circuits/src/crates/rollup-lib/src/base/base_rollup_inputs.nr +++ b/yarn-project/noir-protocol-circuits/src/crates/rollup-lib/src/base/base_rollup_inputs.nr @@ -338,8 +338,8 @@ impl BaseRollupInputs { let archive_root = self.constants.last_archive.root; // Rebuild the block hash - let block_header = self.kernel_data.public_inputs.constants.block_header; - let previous_block_hash = block_header.block_hash(); + let header = self.kernel_data.public_inputs.constants.historical_header; + let previous_block_hash = header.hash(); let previous_block_hash_witness = self.archive_root_membership_witness; @@ -597,7 +597,6 @@ mod tests { abis::side_effect::SideEffect, tests::previous_kernel_data_builder::PreviousKernelDataBuilder, address::{AztecAddress, EthAddress}, - utils::bounded_vec::BoundedVec, utils::uint256::U256, partial_state_reference::PartialStateReference, }; @@ -648,7 +647,7 @@ mod tests { for i in 0..MAX_PUBLIC_DATA_WRITES_PER_TEST { if i < (public_data_writes.len() as u64) { - let (_, leaf): (u64, PublicDataTreeLeaf) = public_data_writes.get_unchecked(i as Field); + let leaf = public_data_writes.get_unchecked(i as Field).1; kernel_data.public_inputs.end.public_data_update_requests[i] = PublicDataUpdateRequest { leaf_slot : leaf.slot, @@ -738,7 +737,7 @@ mod tests { let _nullifier = builder.end.new_nullifiers.pop(); inputs.kernel_data = builder.is_public(); - inputs.pre_existing_blocks[0] = inputs.kernel_data.block_header.block_hash(); + inputs.pre_existing_blocks[0] = inputs.kernel_data.historical_header.hash(); inputs } @@ -949,7 +948,7 @@ mod tests { let new_contract = NewContractData { contract_address: AztecAddress::from_field(1), portal_contract_address: EthAddress::from_field(2), - function_tree_root: 3 + contract_class_id: 3 }; let mut builder = BaseRollupInputsBuilder::new(); @@ -978,7 +977,7 @@ mod tests { let new_contract = NewContractData { contract_address: AztecAddress::from_field(1), portal_contract_address: EthAddress::from_field(2), - function_tree_root: 3 + contract_class_id: 3 }; let mut builder = BaseRollupInputsBuilder::new(); diff --git a/yarn-project/noir-protocol-circuits/src/crates/rollup-lib/src/hash.nr b/yarn-project/noir-protocol-circuits/src/crates/rollup-lib/src/hash.nr deleted file mode 100644 index 6f314de2f24..00000000000 --- a/yarn-project/noir-protocol-circuits/src/crates/rollup-lib/src/hash.nr +++ /dev/null @@ -1,13 +0,0 @@ -use dep::types::{ - abis::global_variables::GlobalVariables, - constants::GENERATOR_INDEX__BLOCK_HASH, - state_reference::StateReference, -}; - -pub fn compute_block_hash_with_globals(globals: GlobalVariables, state: StateReference) -> Field { - let inputs = [ - globals.hash(), state.partial.note_hash_tree.root, state.partial.nullifier_tree.root, state.partial.contract_tree.root, state.l1_to_l2_message_tree.root, state.partial.public_data_tree.root - ]; - - dep::std::hash::pedersen_hash_with_separator(inputs, GENERATOR_INDEX__BLOCK_HASH) -} diff --git a/yarn-project/noir-protocol-circuits/src/crates/rollup-lib/src/lib.nr b/yarn-project/noir-protocol-circuits/src/crates/rollup-lib/src/lib.nr index 1d0df93cc9d..08534d6cb25 100644 --- a/yarn-project/noir-protocol-circuits/src/crates/rollup-lib/src/lib.nr +++ b/yarn-project/noir-protocol-circuits/src/crates/rollup-lib/src/lib.nr @@ -11,8 +11,6 @@ mod root; mod components; -mod hash; - mod merkle_tree; mod tests; diff --git a/yarn-project/noir-protocol-circuits/src/crates/rollup-lib/src/root.nr b/yarn-project/noir-protocol-circuits/src/crates/rollup-lib/src/root.nr index 1fca5912f62..cfd8ad62d4e 100644 --- a/yarn-project/noir-protocol-circuits/src/crates/rollup-lib/src/root.nr +++ b/yarn-project/noir-protocol-circuits/src/crates/rollup-lib/src/root.nr @@ -13,7 +13,7 @@ use dep::types::{ state_reference::StateReference, utils::uint256::U256, }; -use crate::{components, hash::compute_block_hash_with_globals}; +use crate::components; use crate::merkle_tree::{calculate_subtree, calculate_empty_tree_root}; impl RootRollupInputs { @@ -49,12 +49,15 @@ impl RootRollupInputs { partial: right.end, }; - // Build the block hash for this iteration from the tree roots and global variables - // Then insert the block into the archive tree - let block_hash = compute_block_hash_with_globals( - left.constants.global_variables, + let header = Header { + last_archive: left.constants.last_archive, + body_hash: components::compute_calldata_hash(self.previous_rollup_data), state, - ); + global_variables : left.constants.global_variables + }; + + // Build the block hash for this by hashing the header and then insert the new leaf to archive tree. + let block_hash = header.hash(); // Update the archive let archive = components::insert_subtree_to_snapshot_tree( @@ -65,13 +68,6 @@ impl RootRollupInputs { 0 ); - let header = Header { - last_archive: left.constants.last_archive, - body_hash: components::compute_calldata_hash(self.previous_rollup_data), - state, - global_variables : left.constants.global_variables - }; - RootRollupPublicInputs{ aggregation_object, archive, diff --git a/yarn-project/noir-protocol-circuits/src/crates/types/src/abis.nr b/yarn-project/noir-protocol-circuits/src/crates/types/src/abis.nr index 55b17f76ecb..47605e17f6c 100644 --- a/yarn-project/noir-protocol-circuits/src/crates/types/src/abis.nr +++ b/yarn-project/noir-protocol-circuits/src/crates/types/src/abis.nr @@ -12,16 +12,14 @@ mod new_contract_data; mod nullifier_leaf_preimage; mod contract_leaf_preimage; -mod block_header; mod combined_constant_data; +mod nullifier_key_validation_request; mod public_data_read; mod public_data_update_request; mod optionally_revealed_data; mod combined_accumulated_data; -mod complete_address; - mod private_kernel; mod kernel_circuit_public_inputs; mod previous_kernel_data; diff --git a/yarn-project/noir-protocol-circuits/src/crates/types/src/abis/append_only_tree_snapshot.nr b/yarn-project/noir-protocol-circuits/src/crates/types/src/abis/append_only_tree_snapshot.nr index 744a2062f78..7ff7b69503e 100644 --- a/yarn-project/noir-protocol-circuits/src/crates/types/src/abis/append_only_tree_snapshot.nr +++ b/yarn-project/noir-protocol-circuits/src/crates/types/src/abis/append_only_tree_snapshot.nr @@ -5,6 +5,28 @@ struct AppendOnlyTreeSnapshot { next_available_leaf_index : u32 } +global APPEND_ONLY_TREE_SNAPSHOT_LENGTH: Field = 2; + +impl AppendOnlyTreeSnapshot { + pub fn serialize(self) -> [Field; APPEND_ONLY_TREE_SNAPSHOT_LENGTH] { + [self.root, self.next_available_leaf_index as Field] + } + + pub fn deserialize(serialized: [Field; APPEND_ONLY_TREE_SNAPSHOT_LENGTH]) -> AppendOnlyTreeSnapshot { + AppendOnlyTreeSnapshot { + root : serialized[0], + next_available_leaf_index : serialized[1] as u32 + } + } + + pub fn zero() -> Self { + Self { + root: 0, + next_available_leaf_index: 0, + } + } +} + impl Eq for AppendOnlyTreeSnapshot { fn eq(self, other : AppendOnlyTreeSnapshot) -> bool { (self.root == other.root) & (self.next_available_leaf_index == other.next_available_leaf_index) diff --git a/yarn-project/noir-protocol-circuits/src/crates/types/src/abis/block_header.nr b/yarn-project/noir-protocol-circuits/src/crates/types/src/abis/block_header.nr deleted file mode 100644 index a850ff4814d..00000000000 --- a/yarn-project/noir-protocol-circuits/src/crates/types/src/abis/block_header.nr +++ /dev/null @@ -1,87 +0,0 @@ -use crate::{ - constants::{ - BLOCK_HEADER_LENGTH, - GENERATOR_INDEX__BLOCK_HASH, - }, - hash::pedersen_hash, -}; -use crate::traits::Empty; - -// docs:start:block-header -struct BlockHeader { - note_hash_tree_root : Field, - nullifier_tree_root : Field, - contract_tree_root : Field, - l1_to_l2_message_tree_root : Field, - archive_root: Field, - public_data_tree_root: Field, - global_variables_hash: Field, -} -// docs:end:block-header - -impl Empty for BlockHeader { - fn empty() -> Self { - BlockHeader::deserialize([0; BLOCK_HEADER_LENGTH]) - } -} - -impl BlockHeader { - pub fn assert_is_zero(self) { - assert(self.note_hash_tree_root == 0); - assert(self.nullifier_tree_root == 0); - assert(self.contract_tree_root == 0); - assert(self.l1_to_l2_message_tree_root == 0); - assert(self.archive_root == 0); - assert(self.public_data_tree_root == 0); - } - - pub fn serialize(self) -> [Field; BLOCK_HEADER_LENGTH] { - // This comment was copied from the cpp codebase. - // - // TODO(#3441): Note private_kernel_vk_tree_root, is not included yet as - // it is not present in noir, - [ - self.note_hash_tree_root, - self.nullifier_tree_root, - self.contract_tree_root, - self.l1_to_l2_message_tree_root, - self.archive_root, - self.public_data_tree_root, - self.global_variables_hash - ] - } - - pub fn deserialize(deserialized: [Field; BLOCK_HEADER_LENGTH]) -> Self { - BlockHeader { - note_hash_tree_root: deserialized[0], - nullifier_tree_root: deserialized[1], - contract_tree_root: deserialized[2], - l1_to_l2_message_tree_root: deserialized[3], - archive_root: deserialized[4], - public_data_tree_root: deserialized[5], - global_variables_hash: deserialized[6], - } - } - - pub fn note_hash_tree_root(self) -> Field { - self.note_hash_tree_root - } - - pub fn contract_tree_root(self) -> Field { - self.contract_tree_root - } - - pub fn block_hash(self) -> Field { - // TODO(#3442): Unify the ordering in `BlockHeader::serialize` function and the ordering - // in the block hash preimage --> This requires changes in the circuits. - // TODO(https://github.com/AztecProtocol/aztec-packages/issues/3595) - pedersen_hash([ - self.global_variables_hash, - self.note_hash_tree_root, - self.nullifier_tree_root, - self.contract_tree_root, - self.l1_to_l2_message_tree_root, - self.public_data_tree_root, - ], GENERATOR_INDEX__BLOCK_HASH) - } -} diff --git a/yarn-project/noir-protocol-circuits/src/crates/types/src/abis/combined_accumulated_data.nr b/yarn-project/noir-protocol-circuits/src/crates/types/src/abis/combined_accumulated_data.nr index 3e5edcc91d0..9e74c0ae103 100644 --- a/yarn-project/noir-protocol-circuits/src/crates/types/src/abis/combined_accumulated_data.nr +++ b/yarn-project/noir-protocol-circuits/src/crates/types/src/abis/combined_accumulated_data.nr @@ -2,16 +2,17 @@ use crate::{ abis::{ call_request::CallRequest, new_contract_data::NewContractData, + nullifier_key_validation_request::NullifierKeyValidationRequestContext, optionally_revealed_data::OptionallyRevealedData, public_data_read::PublicDataRead, public_data_update_request::PublicDataUpdateRequest, side_effect::{SideEffect, SideEffectLinkedToNoteHash}, }, - mocked::AggregationObject, - utils::bounded_vec::BoundedVec + mocked::AggregationObject }; use crate::constants::{ MAX_READ_REQUESTS_PER_TX, + MAX_NULLIFIER_KEY_VALIDATION_REQUESTS_PER_TX, MAX_NEW_COMMITMENTS_PER_TX, MAX_NEW_NULLIFIERS_PER_TX, MAX_PRIVATE_CALL_STACK_LENGTH_PER_TX, @@ -28,6 +29,7 @@ struct CombinedAccumulatedData { aggregation_object: AggregationObject, read_requests: [SideEffect; MAX_READ_REQUESTS_PER_TX], + nullifier_key_validation_requests: [NullifierKeyValidationRequestContext; MAX_NULLIFIER_KEY_VALIDATION_REQUESTS_PER_TX], new_commitments: [SideEffect; MAX_NEW_COMMITMENTS_PER_TX], new_nullifiers: [SideEffectLinkedToNoteHash; MAX_NEW_NULLIFIERS_PER_TX], @@ -79,6 +81,7 @@ struct CombinedAccumulatedDataBuilder { aggregation_object: AggregationObject, read_requests: BoundedVec, + nullifier_key_validation_requests: BoundedVec, new_commitments: BoundedVec, new_nullifiers: BoundedVec, @@ -110,6 +113,7 @@ impl CombinedAccumulatedDataBuilder { aggregation_object: self.aggregation_object, read_requests: self.read_requests.storage, + nullifier_key_validation_requests: self.nullifier_key_validation_requests.storage, new_commitments: self.new_commitments.storage, new_nullifiers: self.new_nullifiers.storage, @@ -136,6 +140,7 @@ impl CombinedAccumulatedDataBuilder { pub fn to_final(self) -> FinalAccumulatedData { assert_eq(self.read_requests.len, 0, "Final accumulated data: read requests not empty"); + assert_eq(self.nullifier_key_validation_requests.len, 0, "Final accumulated data: nullifier key validation requests not empty"); assert_eq(self.public_data_update_requests.len, 0, "Final accumulated data: public data update requests not empty"); assert_eq(self.public_data_reads.len, 0, "Final accumulated data: public data reads not empty"); diff --git a/yarn-project/noir-protocol-circuits/src/crates/types/src/abis/combined_constant_data.nr b/yarn-project/noir-protocol-circuits/src/crates/types/src/abis/combined_constant_data.nr index d1706c9624e..2ccf9e7e51b 100644 --- a/yarn-project/noir-protocol-circuits/src/crates/types/src/abis/combined_constant_data.nr +++ b/yarn-project/noir-protocol-circuits/src/crates/types/src/abis/combined_constant_data.nr @@ -1,7 +1,11 @@ use crate::transaction::context::TxContext; -use crate::abis::block_header::BlockHeader; +use crate::header::Header; struct CombinedConstantData { - block_header: BlockHeader, + historical_header: Header, + // Note: `chainId` and `version` in txContext are not redundant to the values in + // self.historical_header.global_variables because they can be different in case of a protocol upgrade. In such + // a situation we could be using header from a block before the upgrade took place but be using the updated + // protocol to execute and prove the transaction. tx_context: TxContext, } diff --git a/yarn-project/noir-protocol-circuits/src/crates/types/src/abis/complete_address.nr b/yarn-project/noir-protocol-circuits/src/crates/types/src/abis/complete_address.nr deleted file mode 100644 index 5a76fb03591..00000000000 --- a/yarn-project/noir-protocol-circuits/src/crates/types/src/abis/complete_address.nr +++ /dev/null @@ -1,31 +0,0 @@ -use crate::{ - address::{ - AztecAddress, - PartialAddress, - }, - grumpkin_point::GrumpkinPoint, -}; - -struct CompleteAddress { - address : AztecAddress, - public_key : GrumpkinPoint, - partial_address: PartialAddress, -} - -impl CompleteAddress{ - fn assert_is_zero(self) { - self.address.assert_is_zero(); - self.public_key.assert_is_zero(); - self.partial_address.assert_is_zero(); - } - - pub fn compute(public_key : GrumpkinPoint, contract_address_salt : Field, function_tree_root : Field, constructor_hash : Field) -> CompleteAddress { - let partial_address = PartialAddress::compute(contract_address_salt, function_tree_root, constructor_hash); - - CompleteAddress{ - address : AztecAddress::compute(public_key, partial_address), - public_key, - partial_address, - } - } -} diff --git a/yarn-project/noir-protocol-circuits/src/crates/types/src/abis/function_selector.nr b/yarn-project/noir-protocol-circuits/src/crates/types/src/abis/function_selector.nr index c9ddf6da8be..93538f5248e 100644 --- a/yarn-project/noir-protocol-circuits/src/crates/types/src/abis/function_selector.nr +++ b/yarn-project/noir-protocol-circuits/src/crates/types/src/abis/function_selector.nr @@ -1,5 +1,6 @@ use crate::utils::field::field_from_bytes; use dep::std::cmp::Eq; +use crate::traits::{Serialize, Deserialize}; global SELECTOR_SIZE = 4; @@ -14,6 +15,20 @@ impl Eq for FunctionSelector { } } +impl Serialize<1> for FunctionSelector { + fn serialize(self: Self) -> [Field; 1] { + [self.inner as Field] + } +} + +impl Deserialize<1> for FunctionSelector { + fn deserialize(fields: [Field; 1]) -> Self { + Self { + inner: fields[0] as u32 + } + } +} + impl FunctionSelector { fn to_field(self) -> Field { self.inner as Field @@ -46,14 +61,4 @@ impl FunctionSelector { pub fn zero() -> Self { Self { inner: 0 } } - - pub fn serialize(self: Self) -> [Field; 1] { - [self.inner as Field] - } - - pub fn deserialize(fields: [Field; 1]) -> Self { - Self { - inner: fields[0] as u32 - } - } } diff --git a/yarn-project/noir-protocol-circuits/src/crates/types/src/abis/global_variables.nr b/yarn-project/noir-protocol-circuits/src/crates/types/src/abis/global_variables.nr index 45b9d4e3c76..d5f2857151b 100644 --- a/yarn-project/noir-protocol-circuits/src/crates/types/src/abis/global_variables.nr +++ b/yarn-project/noir-protocol-circuits/src/crates/types/src/abis/global_variables.nr @@ -1,7 +1,10 @@ use dep::std::cmp::Eq; use crate::{ constants::GENERATOR_INDEX__GLOBAL_VARIABLES, - traits::Hash, + traits::{ + Empty, + Hash, + }, }; struct GlobalVariables { @@ -11,6 +14,28 @@ struct GlobalVariables { timestamp : Field, } +global GLOBAL_VARIABLES_LENGTH: Field = 4; + +impl GlobalVariables { + fn serialize(self) -> [Field; GLOBAL_VARIABLES_LENGTH] { + [ + self.chain_id, + self.version, + self.block_number, + self.timestamp + ] + } + + fn deserialize(serialized: [Field; GLOBAL_VARIABLES_LENGTH]) -> GlobalVariables { + GlobalVariables { + chain_id: serialized[0], + version: serialized[1], + block_number: serialized[2], + timestamp: serialized[3], + } + } +} + impl Eq for GlobalVariables { fn eq(self, other : GlobalVariables) -> bool { (self.chain_id == other.chain_id) & @@ -20,15 +45,13 @@ impl Eq for GlobalVariables { } } -impl Hash for GlobalVariables { - fn hash(self) -> Field { - dep::std::hash::pedersen_hash_with_separator([ - self.chain_id, - self.version, - self.block_number, - self.timestamp - ], - GENERATOR_INDEX__GLOBAL_VARIABLES, - ) +impl Empty for GlobalVariables { + fn empty() -> Self { + Self { + chain_id: 0, + version: 0, + block_number: 0, + timestamp: 0, + } } } diff --git a/yarn-project/noir-protocol-circuits/src/crates/types/src/abis/new_contract_data.nr b/yarn-project/noir-protocol-circuits/src/crates/types/src/abis/new_contract_data.nr index 2644eed2fc7..2c799080349 100644 --- a/yarn-project/noir-protocol-circuits/src/crates/types/src/abis/new_contract_data.nr +++ b/yarn-project/noir-protocol-circuits/src/crates/types/src/abis/new_contract_data.nr @@ -6,14 +6,14 @@ use crate::traits::{Empty, Hash}; struct NewContractData { contract_address: AztecAddress, portal_contract_address: EthAddress, - function_tree_root: Field, + contract_class_id: Field, } impl Eq for NewContractData { fn eq(self, data: NewContractData) -> bool { data.contract_address.eq(self.contract_address) & data.portal_contract_address.eq(self.portal_contract_address) - & (data.function_tree_root == self.function_tree_root) + & (data.contract_class_id == self.contract_class_id) } } @@ -22,7 +22,7 @@ impl Empty for NewContractData { Self { contract_address : AztecAddress::empty(), portal_contract_address : EthAddress::empty(), - function_tree_root : 0, + contract_class_id : 0, } } } @@ -35,7 +35,7 @@ impl Hash for NewContractData { dep::std::hash::pedersen_hash_with_separator([ self.contract_address.to_field(), self.portal_contract_address.to_field(), - self.function_tree_root, + self.contract_class_id, ], GENERATOR_INDEX__CONTRACT_LEAF) } } @@ -45,7 +45,7 @@ impl NewContractData { pub fn is_empty(self) -> bool { (self.contract_address.to_field() == 0) & (self.portal_contract_address.to_field() == 0) & - (self.function_tree_root ==0) + (self.contract_class_id ==0) } } diff --git a/yarn-project/noir-protocol-circuits/src/crates/types/src/abis/nullifier_key_validation_request.nr b/yarn-project/noir-protocol-circuits/src/crates/types/src/abis/nullifier_key_validation_request.nr new file mode 100644 index 00000000000..b43754493d1 --- /dev/null +++ b/yarn-project/noir-protocol-circuits/src/crates/types/src/abis/nullifier_key_validation_request.nr @@ -0,0 +1,108 @@ +use dep::std::cmp::Eq; +use crate::{ + address::AztecAddress, + traits::{Empty, Serialize, Deserialize}, + grumpkin_point::GrumpkinPoint, + grumpkin_private_key::GrumpkinPrivateKey, +}; + +global NULLIFIER_KEY_VALIDATION_REQUEST_SERIALIZED_LEN = 4; +global NULLIFIER_KEY_VALIDATION_REQUEST_CONTEXT_SERIALIZED_LEN = 5; + +struct NullifierKeyValidationRequest { + public_key: GrumpkinPoint, + secret_key: GrumpkinPrivateKey, +} + +impl Eq for NullifierKeyValidationRequest { + fn eq(self, request: NullifierKeyValidationRequest) -> bool { + (request.public_key.eq(self.public_key)) + & (request.secret_key.eq(self.secret_key)) + } +} + +impl Empty for NullifierKeyValidationRequest { + fn empty() -> Self { + NullifierKeyValidationRequest { + public_key: GrumpkinPoint::zero(), + secret_key: GrumpkinPrivateKey::zero(), + } + } +} + +impl Serialize for NullifierKeyValidationRequest { + fn serialize(self) -> [Field; NULLIFIER_KEY_VALIDATION_REQUEST_SERIALIZED_LEN] { + [ + self.public_key.x, + self.public_key.y, + self.secret_key.high, + self.secret_key.low, + ] + } +} + +impl Deserialize for NullifierKeyValidationRequest { + fn deserialize(fields: [Field; NULLIFIER_KEY_VALIDATION_REQUEST_SERIALIZED_LEN]) -> Self { + Self { + public_key: GrumpkinPoint::new(fields[0], fields[1]), + secret_key: GrumpkinPrivateKey::new(fields[2], fields[3]), + } + } +} + +impl NullifierKeyValidationRequest { + pub fn to_context(self, contract_address: AztecAddress) -> NullifierKeyValidationRequestContext { + NullifierKeyValidationRequestContext { + public_key: self.public_key, + secret_key: self.secret_key, + contract_address, + } + } +} + +struct NullifierKeyValidationRequestContext { + public_key: GrumpkinPoint, + secret_key: GrumpkinPrivateKey, + contract_address: AztecAddress, +} + +impl Eq for NullifierKeyValidationRequestContext { + fn eq(self, request: NullifierKeyValidationRequestContext) -> bool { + (request.public_key.eq(self.public_key)) + & (request.secret_key.eq(self.secret_key)) + & (request.contract_address.eq(self.contract_address)) + } +} + +impl Empty for NullifierKeyValidationRequestContext { + fn empty() -> Self { + NullifierKeyValidationRequestContext { + public_key: GrumpkinPoint::zero(), + secret_key: GrumpkinPrivateKey::zero(), + contract_address: AztecAddress::zero(), + } + } +} + +impl Serialize for NullifierKeyValidationRequestContext { + fn serialize(self) -> [Field; NULLIFIER_KEY_VALIDATION_REQUEST_CONTEXT_SERIALIZED_LEN] { + [ + self.public_key.x, + self.public_key.y, + self.secret_key.high, + self.secret_key.low, + self.contract_address.to_field(), + ] + } +} + +impl Deserialize for NullifierKeyValidationRequestContext { + fn deserialize(fields: [Field; NULLIFIER_KEY_VALIDATION_REQUEST_CONTEXT_SERIALIZED_LEN]) -> Self { + Self { + public_key: GrumpkinPoint::new(fields[0], fields[1]), + secret_key: GrumpkinPrivateKey::new(fields[2], fields[3]), + contract_address: AztecAddress::from_field(fields[4]), + } + } +} + diff --git a/yarn-project/noir-protocol-circuits/src/crates/types/src/abis/private_circuit_public_inputs.nr b/yarn-project/noir-protocol-circuits/src/crates/types/src/abis/private_circuit_public_inputs.nr index 85b208776d3..0df8915d1a4 100644 --- a/yarn-project/noir-protocol-circuits/src/crates/types/src/abis/private_circuit_public_inputs.nr +++ b/yarn-project/noir-protocol-circuits/src/crates/types/src/abis/private_circuit_public_inputs.nr @@ -1,17 +1,18 @@ use crate::{ abis::{ call_context::CallContext, - block_header::BlockHeader, + nullifier_key_validation_request::NullifierKeyValidationRequest, side_effect::{SideEffect, SideEffectLinkedToNoteHash}, }, contrakt::deployment_data::ContractDeploymentData, hash::{ pedersen_hash, }, - utils::bounded_vec::BoundedVec, + header::Header, }; use crate::constants::{ MAX_READ_REQUESTS_PER_CALL, + MAX_NULLIFIER_KEY_VALIDATION_REQUESTS_PER_CALL, MAX_NEW_COMMITMENTS_PER_CALL, MAX_NEW_NULLIFIERS_PER_CALL, MAX_PRIVATE_CALL_STACK_LENGTH_PER_CALL, @@ -23,7 +24,7 @@ use crate::constants::{ PRIVATE_CIRCUIT_PUBLIC_INPUTS_LENGTH, GENERATOR_INDEX__PRIVATE_CIRCUIT_PUBLIC_INPUTS, }; -use crate::traits::Hash; +use crate::traits::{Hash, Serialize, Deserialize}; struct PrivateCircuitPublicInputs { call_context: CallContext, @@ -32,6 +33,7 @@ struct PrivateCircuitPublicInputs { return_values: [Field; RETURN_VALUES_LENGTH], read_requests: [SideEffect; MAX_READ_REQUESTS_PER_CALL], + nullifier_key_validation_requests: [NullifierKeyValidationRequest; MAX_NULLIFIER_KEY_VALIDATION_REQUESTS_PER_CALL], new_commitments: [SideEffect; MAX_NEW_COMMITMENTS_PER_CALL], new_nullifiers: [SideEffectLinkedToNoteHash; MAX_NEW_NULLIFIERS_PER_CALL], @@ -48,10 +50,14 @@ struct PrivateCircuitPublicInputs { encrypted_log_preimages_length: Field, unencrypted_log_preimages_length: Field, - block_header: BlockHeader, + // Header of a block whose state is used during private execution (not the block the transaction is included in). + historical_header: Header, contract_deployment_data: ContractDeploymentData, + // Note: The following 2 values are not redundant to the values in self.historical_header.global_variables because + // they can be different in case of a protocol upgrade. In such a situation we could be using header from a block + // before the upgrade took place but be using the updated protocol to execute and prove the transaction. chain_id: Field, version: Field, } @@ -62,26 +68,26 @@ impl Hash for PrivateCircuitPublicInputs { let mut fields: BoundedVec = BoundedVec::new(0); fields.push(self.call_context.hash()); fields.push(self.args_hash); - fields.push_array(self.return_values); + fields.extend_from_array(self.return_values); for i in 0..MAX_READ_REQUESTS_PER_CALL{ - fields.push_array(self.read_requests[i].serialize()); + fields.extend_from_array(self.read_requests[i].serialize()); } for i in 0..MAX_NEW_COMMITMENTS_PER_CALL{ - fields.push_array(self.new_commitments[i].serialize()); + fields.extend_from_array(self.new_commitments[i].serialize()); } for i in 0..MAX_NEW_NULLIFIERS_PER_CALL{ - fields.push_array(self.new_nullifiers[i].serialize()); + fields.extend_from_array(self.new_nullifiers[i].serialize()); } - fields.push_array(self.private_call_stack_hashes); - fields.push_array(self.public_call_stack_hashes); - fields.push_array(self.new_l2_to_l1_msgs); + fields.extend_from_array(self.private_call_stack_hashes); + fields.extend_from_array(self.public_call_stack_hashes); + fields.extend_from_array(self.new_l2_to_l1_msgs); fields.push(self.end_side_effect_counter as Field); - fields.push_array(self.encrypted_logs_hash); - fields.push_array(self.unencrypted_logs_hash); + fields.extend_from_array(self.encrypted_logs_hash); + fields.extend_from_array(self.unencrypted_logs_hash); fields.push(self.encrypted_log_preimages_length); fields.push(self.unencrypted_log_preimages_length); - fields.push_array(self.block_header.serialize()); + fields.extend_from_array(self.historical_header.serialize()); fields.push(self.contract_deployment_data.hash()); fields.push(self.chain_id); fields.push(self.version); @@ -92,32 +98,32 @@ impl Hash for PrivateCircuitPublicInputs { } } -impl PrivateCircuitPublicInputs { +impl Serialize for PrivateCircuitPublicInputs { fn serialize(self) -> [Field; PRIVATE_CIRCUIT_PUBLIC_INPUTS_LENGTH] { let mut fields: BoundedVec = BoundedVec::new(0); - fields.push_array(self.call_context.serialize()); + fields.extend_from_array(self.call_context.serialize()); fields.push(self.args_hash); - fields.push_array(self.return_values); + fields.extend_from_array(self.return_values); for i in 0..MAX_READ_REQUESTS_PER_CALL{ - fields.push_array(self.read_requests[i].serialize()); + fields.extend_from_array(self.read_requests[i].serialize()); } for i in 0..MAX_NEW_COMMITMENTS_PER_CALL{ - fields.push_array(self.new_commitments[i].serialize()); + fields.extend_from_array(self.new_commitments[i].serialize()); } for i in 0..MAX_NEW_NULLIFIERS_PER_CALL{ - fields.push_array(self.new_nullifiers[i].serialize()); + fields.extend_from_array(self.new_nullifiers[i].serialize()); } - fields.push_array(self.private_call_stack_hashes); - fields.push_array(self.public_call_stack_hashes); - fields.push_array(self.new_l2_to_l1_msgs); + fields.extend_from_array(self.private_call_stack_hashes); + fields.extend_from_array(self.public_call_stack_hashes); + fields.extend_from_array(self.new_l2_to_l1_msgs); fields.push(self.end_side_effect_counter as Field); - fields.push_array(self.encrypted_logs_hash); - fields.push_array(self.unencrypted_logs_hash); + fields.extend_from_array(self.encrypted_logs_hash); + fields.extend_from_array(self.unencrypted_logs_hash); fields.push(self.encrypted_log_preimages_length); fields.push(self.unencrypted_log_preimages_length); - fields.push_array(self.block_header.serialize()); - fields.push_array(self.contract_deployment_data.serialize()); + fields.extend_from_array(self.historical_header.serialize()); + fields.extend_from_array(self.contract_deployment_data.serialize()); fields.push(self.chain_id); fields.push(self.version); diff --git a/yarn-project/noir-protocol-circuits/src/crates/types/src/abis/public_circuit_public_inputs.nr b/yarn-project/noir-protocol-circuits/src/crates/types/src/abis/public_circuit_public_inputs.nr index 8973af9c6c3..6c130e5e65d 100644 --- a/yarn-project/noir-protocol-circuits/src/crates/types/src/abis/public_circuit_public_inputs.nr +++ b/yarn-project/noir-protocol-circuits/src/crates/types/src/abis/public_circuit_public_inputs.nr @@ -15,14 +15,13 @@ use crate::{ abis::{ call_context::CallContext, side_effect::{SideEffect, SideEffectLinkedToNoteHash}, - block_header::BlockHeader, }, address::AztecAddress, contrakt::{ storage_read::StorageRead, storage_update_request::StorageUpdateRequest, }, - utils::bounded_vec::BoundedVec, + header::Header, }; struct PublicCircuitPublicInputs{ @@ -46,7 +45,9 @@ struct PublicCircuitPublicInputs{ // variable-length data. unencrypted_log_preimages_length: Field, - block_header: BlockHeader, + // Header of a block whose state is used during public execution. Set by sequencer to be a header of a block + // previous to the one in which the tx is included. + historical_header: Header, prover_address: AztecAddress, } @@ -57,14 +58,14 @@ impl PublicCircuitPublicInputs{ let mut inputs: BoundedVec = BoundedVec::new(0); inputs.push(self.call_context.hash()); inputs.push(self.args_hash); - inputs.push_array(self.return_values); + inputs.extend_from_array(self.return_values); for i in 0..MAX_PUBLIC_DATA_UPDATE_REQUESTS_PER_CALL { inputs.push(self.contract_storage_update_requests[i].hash()); } for i in 0..MAX_PUBLIC_DATA_READS_PER_CALL { inputs.push(self.contract_storage_reads[i].hash()); } - inputs.push_array(self.public_call_stack_hashes); + inputs.extend_from_array(self.public_call_stack_hashes); for i in 0..MAX_NEW_COMMITMENTS_PER_CALL{ inputs.push(self.new_commitments[i].hash()); @@ -72,10 +73,10 @@ impl PublicCircuitPublicInputs{ for i in 0..MAX_NEW_NULLIFIERS_PER_CALL{ inputs.push(self.new_nullifiers[i].hash()); } - inputs.push_array(self.new_l2_to_l1_msgs); - inputs.push_array(self.unencrypted_logs_hash); + inputs.extend_from_array(self.new_l2_to_l1_msgs); + inputs.extend_from_array(self.unencrypted_logs_hash); inputs.push(self.unencrypted_log_preimages_length); - inputs.push_array(self.block_header.serialize()); + inputs.extend_from_array(self.historical_header.serialize()); inputs.push(self.prover_address.to_field()); assert_eq(inputs.len(), PUBLIC_CIRCUIT_PUBLIC_INPUTS_HASH_INPUT_LENGTH, "Incorrect number of input fields when hashing PublicCircuitPublicInputs"); @@ -85,27 +86,27 @@ impl PublicCircuitPublicInputs{ pub fn serialize(self) -> [Field; PUBLIC_CIRCUIT_PUBLIC_INPUTS_LENGTH] { let mut fields: BoundedVec = BoundedVec::new(0); - fields.push_array(self.call_context.serialize()); + fields.extend_from_array(self.call_context.serialize()); fields.push(self.args_hash); - fields.push_array(self.return_values); + fields.extend_from_array(self.return_values); for i in 0..MAX_PUBLIC_DATA_UPDATE_REQUESTS_PER_CALL { - fields.push_array(self.contract_storage_update_requests[i].serialize()); + fields.extend_from_array(self.contract_storage_update_requests[i].serialize()); } for i in 0..MAX_PUBLIC_DATA_READS_PER_CALL { - fields.push_array(self.contract_storage_reads[i].serialize()); + fields.extend_from_array(self.contract_storage_reads[i].serialize()); } - fields.push_array(self.public_call_stack_hashes); + fields.extend_from_array(self.public_call_stack_hashes); for i in 0..MAX_NEW_COMMITMENTS_PER_CALL{ - fields.push_array(self.new_commitments[i].serialize()); + fields.extend_from_array(self.new_commitments[i].serialize()); } for i in 0..MAX_NEW_NULLIFIERS_PER_CALL{ - fields.push_array(self.new_nullifiers[i].serialize()); + fields.extend_from_array(self.new_nullifiers[i].serialize()); } - fields.push_array(self.new_l2_to_l1_msgs); - fields.push_array(self.unencrypted_logs_hash); + fields.extend_from_array(self.new_l2_to_l1_msgs); + fields.extend_from_array(self.unencrypted_logs_hash); fields.push(self.unencrypted_log_preimages_length); - fields.push_array(self.block_header.serialize()); + fields.extend_from_array(self.historical_header.serialize()); fields.push(self.prover_address.to_field()); fields.storage } diff --git a/yarn-project/noir-protocol-circuits/src/crates/types/src/abis/side_effect.nr b/yarn-project/noir-protocol-circuits/src/crates/types/src/abis/side_effect.nr index 3303ef4539f..8d2d218d6a1 100644 --- a/yarn-project/noir-protocol-circuits/src/crates/types/src/abis/side_effect.nr +++ b/yarn-project/noir-protocol-circuits/src/crates/types/src/abis/side_effect.nr @@ -1,6 +1,6 @@ use crate::constants::{GENERATOR_INDEX__SIDE_EFFECT}; use dep::std::cmp::Eq; -use crate::traits::{Empty, Hash}; +use crate::traits::{Empty, Hash, Serialize, Deserialize}; trait Ordered { fn counter(self) -> u32; @@ -40,22 +40,19 @@ impl Hash for SideEffect { } } -impl SideEffect { - pub fn serialize(self) -> [Field; 2] { +impl Serialize<2> for SideEffect { + fn serialize(self) -> [Field; 2] { [self.value, self.counter as Field] } +} - pub fn deserialise(values: [Field; 2]) -> Self { +impl Deserialize<2> for SideEffect { + fn deserialize(values: [Field; 2]) -> Self { Self { value: values[0], counter: values[1] as u32, } } - - pub fn is_empty(self) -> bool { - (self.value == 0) - & (self.counter == 0) - } } struct SideEffectLinkedToNoteHash{ @@ -96,23 +93,19 @@ impl Hash for SideEffectLinkedToNoteHash { } } -impl SideEffectLinkedToNoteHash{ - pub fn serialize(self) -> [Field; 3] { +impl Serialize<3> for SideEffectLinkedToNoteHash { + fn serialize(self) -> [Field; 3] { [self.value, self.note_hash, self.counter as Field] } +} - pub fn deserialise(values: [Field; 3]) -> Self { +impl Deserialize<3> for SideEffectLinkedToNoteHash { + fn deserialize(values: [Field; 3]) -> Self { Self { value: values[0], note_hash: values[1], counter: values[2] as u32, } } - - pub fn is_empty(self) -> bool { - (self.value == 0) - & (self.note_hash == 0) - & (self.counter == 0) - } } diff --git a/yarn-project/noir-protocol-circuits/src/crates/types/src/address.nr b/yarn-project/noir-protocol-circuits/src/crates/types/src/address.nr index 14706b5bd14..ce58aadb1ab 100644 --- a/yarn-project/noir-protocol-circuits/src/crates/types/src/address.nr +++ b/yarn-project/noir-protocol-circuits/src/crates/types/src/address.nr @@ -2,13 +2,15 @@ use crate::{ constants::{ GENERATOR_INDEX__CONTRACT_ADDRESS, GENERATOR_INDEX__PARTIAL_ADDRESS, + GENERATOR_INDEX__CONSTRUCTOR }, hash::pedersen_hash, utils, grumpkin_point::GrumpkinPoint, }; use dep::std::cmp::Eq; -use crate::traits::{Empty, ToField}; +use crate::traits::{Empty, ToField, Serialize, Deserialize}; +use crate::type_serialization::{ETH_ADDRESS_SERIALIZED_LEN, AZTEC_ADDRESS_SERIALIZED_LEN}; // Aztec address struct AztecAddress { @@ -35,6 +37,18 @@ impl ToField for AztecAddress { } } +impl Serialize for AztecAddress { + fn serialize(self: Self) -> [Field; AZTEC_ADDRESS_SERIALIZED_LEN] { + [self.to_field()] + } +} + +impl Deserialize for AztecAddress { + fn deserialize(fields: [Field; AZTEC_ADDRESS_SERIALIZED_LEN]) -> Self { + AztecAddress::from_field(fields[0]) + } +} + impl AztecAddress { pub fn zero() -> Self { Self { @@ -48,10 +62,14 @@ impl AztecAddress { } } - pub fn compute(pub_key: GrumpkinPoint, partial_address: PartialAddress) -> AztecAddress { + pub fn compute_from_public_key(pub_key: GrumpkinPoint, contract_class_id : Field, salt : Field, initialization_hash: Field, portal_contract_address: EthAddress) -> AztecAddress { + AztecAddress::compute(PublicKeysHash::compute(pub_key), PartialAddress::compute(contract_class_id, salt, initialization_hash, portal_contract_address)) + } + + pub fn compute(pub_keys_hash: PublicKeysHash, partial_address: PartialAddress) -> AztecAddress { AztecAddress::from_field( pedersen_hash( - [pub_key.x, pub_key.y, partial_address.to_field()], + [pub_keys_hash.to_field(), partial_address.to_field()], GENERATOR_INDEX__CONTRACT_ADDRESS ) ) @@ -71,16 +89,6 @@ impl AztecAddress { inner : result } } - - pub fn serialize(self: Self) -> [Field; 1] { - [self.inner] - } - - pub fn deserialize(fields: [Field; 1]) -> Self { - Self { - inner: fields[0] - } - } } struct EthAddress{ @@ -107,6 +115,20 @@ impl ToField for EthAddress { } } +impl Serialize for EthAddress { + fn serialize(self: Self) -> [Field; ETH_ADDRESS_SERIALIZED_LEN] { + [self.inner] + } +} + +impl Deserialize for EthAddress { + fn deserialize(fields: [Field; ETH_ADDRESS_SERIALIZED_LEN]) -> Self { + Self { + inner: fields[0] + } + } +} + impl EthAddress{ pub fn zero() -> Self { Self { @@ -134,16 +156,6 @@ impl EthAddress{ inner : result } } - - pub fn serialize(self: Self) -> [Field; 1] { - [self.inner] - } - - pub fn deserialize(fields: [Field; 1]) -> Self { - Self { - inner: fields[0] - } - } } // Partial address @@ -164,19 +176,20 @@ impl PartialAddress { } } - pub fn compute(contract_address_salt : Field, function_tree_root : Field, constructor_hash : Field) -> Self { + pub fn compute(contract_class_id : Field, salt : Field, initialization_hash: Field, portal_contract_address: EthAddress) -> Self { + PartialAddress::compute_from_salted_initialization_hash(contract_class_id, SaltedInitializationHash::compute(salt, initialization_hash, portal_contract_address)) + } + + pub fn compute_from_salted_initialization_hash(contract_class_id : Field, salted_initialization_hash : SaltedInitializationHash) -> Self { PartialAddress::from_field( pedersen_hash([ - // TODO why the zeroes? - 0, - 0, - contract_address_salt, - function_tree_root, - constructor_hash + contract_class_id, + salted_initialization_hash.to_field() ], GENERATOR_INDEX__PARTIAL_ADDRESS) ) } + pub fn to_field(self) -> Field { self.inner } @@ -185,3 +198,86 @@ impl PartialAddress { assert(self.to_field() == 0); } } + +// Salted initialization hash. Used in the computation of a partial address. +struct SaltedInitializationHash { + inner: Field +} + +impl ToField for SaltedInitializationHash { + fn to_field(self) -> Field { + self.inner + } +} + +impl SaltedInitializationHash { + pub fn from_field(field : Field) -> Self { + Self { + inner : field + } + } + + pub fn compute(salt : Field, initialization_hash: Field, portal_contract_address: EthAddress) -> Self { + SaltedInitializationHash::from_field( + pedersen_hash([ + salt, + initialization_hash, + portal_contract_address.to_field(), + ], GENERATOR_INDEX__PARTIAL_ADDRESS) + ) + } + + pub fn to_field(self) -> Field { + self.inner + } + + pub fn assert_is_zero(self) { + assert(self.to_field() == 0); + } +} + +// Public keys hash. Used in the computation of an address. +struct PublicKeysHash { + inner: Field +} + +impl ToField for PublicKeysHash { + fn to_field(self) -> Field { + self.inner + } +} + +impl PublicKeysHash { + pub fn from_field(field : Field) -> Self { + Self { + inner : field + } + } + + pub fn compute(public_key: GrumpkinPoint) -> Self { + PublicKeysHash::from_field( + pedersen_hash([ + public_key.x, + public_key.y, + ], GENERATOR_INDEX__PARTIAL_ADDRESS) + ) + } + + pub fn to_field(self) -> Field { + self.inner + } + + pub fn assert_is_zero(self) { + assert(self.to_field() == 0); + } +} + +pub fn compute_initialization_hash(selector: Field, args_hash: Field) -> Field { + pedersen_hash( + [ + selector, + args_hash + ], + GENERATOR_INDEX__CONSTRUCTOR + ) +} diff --git a/yarn-project/noir-protocol-circuits/src/crates/types/src/constants.nr b/yarn-project/noir-protocol-circuits/src/crates/types/src/constants.nr index c0b05053066..baeddd68ea5 100644 --- a/yarn-project/noir-protocol-circuits/src/crates/types/src/constants.nr +++ b/yarn-project/noir-protocol-circuits/src/crates/types/src/constants.nr @@ -31,6 +31,7 @@ global MAX_NEW_L2_TO_L1_MSGS_PER_CALL: Field = 2; global MAX_PUBLIC_DATA_UPDATE_REQUESTS_PER_CALL: Field = 16; global MAX_PUBLIC_DATA_READS_PER_CALL: Field = 16; global MAX_READ_REQUESTS_PER_CALL: Field = 32; +global MAX_NULLIFIER_KEY_VALIDATION_REQUESTS_PER_CALL: Field = 1; // "PER TRANSACTION" CONSTANTS global MAX_NEW_COMMITMENTS_PER_TX: Field = 64; @@ -43,6 +44,7 @@ global MAX_PUBLIC_DATA_READS_PER_TX: Field = 16; global MAX_NEW_CONTRACTS_PER_TX: Field = 1; global MAX_OPTIONALLY_REVEALED_DATA_LENGTH_PER_TX: Field = 4; global MAX_READ_REQUESTS_PER_TX: Field = 128; +global MAX_NULLIFIER_KEY_VALIDATION_REQUESTS_PER_TX: Field = 4; global NUM_ENCRYPTED_LOGS_HASHES_PER_TX: Field = 1; global NUM_UNENCRYPTED_LOGS_HASHES_PER_TX: Field = 1; // docs:end:constants @@ -83,10 +85,10 @@ global ARGS_HASH_CHUNK_COUNT: u32 = 16; // NOIR CONSTANTS - constants used only in yarn-packages/noir-contracts // Some are defined here because Noir doesn't yet support globals referencing other globals yet. -// Move these constants to a noir file once the issue bellow is resolved: +// Move these constants to a noir file once the issue below is resolved: // https://github.com/noir-lang/noir/issues/1734 global L1_TO_L2_MESSAGE_LENGTH: Field = 8; -global L1_TO_L2_MESSAGE_ORACLE_CALL_LENGTH: Field = 26; +global L1_TO_L2_MESSAGE_ORACLE_CALL_LENGTH: Field = 25; global MAX_NOTE_FIELDS_LENGTH: Field = 20; // GET_NOTE_ORACLE_RETURN_LENGT = MAX_NOTE_FIELDS_LENGTH + 1 + 2 // The plus 1 is 1 extra field for nonce. @@ -96,21 +98,21 @@ global MAX_NOTES_PER_PAGE: Field = 10; // VIEW_NOTE_ORACLE_RETURN_LENGTH = MAX_NOTES_PER_PAGE * (MAX_NOTE_FIELDS_LENGTH + 1) + 2; global VIEW_NOTE_ORACLE_RETURN_LENGTH: Field = 212; global CALL_CONTEXT_LENGTH: Field = 8; -global BLOCK_HEADER_LENGTH: Field = 7; +global HEADER_LENGTH: Field = 18; // 2 for last_archive, 2 for body hash, 10 for state reference, 4 for global vars global FUNCTION_DATA_LENGTH: Field = 4; global CONTRACT_DEPLOYMENT_DATA_LENGTH: Field = 6; // Change this ONLY if you have changed the PrivateCircuitPublicInputs structure. -// In other words, if the structure/size of the public inputs of a function call changes then we -// should change this constant as well as the offsets in private_call_stack_item.nr -global PRIVATE_CIRCUIT_PUBLIC_INPUTS_LENGTH: Field = 189; +// In other words, if the structure/size of the public inputs of a function call changes then we should change this +// constant as well CALL_PRIVATE_FUNCTION_RETURN_SIZE and PRIVATE_CIRCUIT_PUBLIC_INPUTS_HASH_INPUT_LENGTH +global PRIVATE_CIRCUIT_PUBLIC_INPUTS_LENGTH: Field = 200; global CONTRACT_STORAGE_UPDATE_REQUEST_LENGTH: Field = 3; global CONTRACT_STORAGE_READ_LENGTH: Field = 2; // Change this ONLY if you have changed the PublicCircuitPublicInputs structure. global PUBLIC_CIRCUIT_PUBLIC_INPUTS_LENGTH: Field = 190; global GET_NOTES_ORACLE_RETURN_LENGTH: Field = 674; -global CALL_PRIVATE_FUNCTION_RETURN_SIZE: Field = 195; -global PUBLIC_CIRCUIT_PUBLIC_INPUTS_HASH_INPUT_LENGTH: Field = 87; -global PRIVATE_CIRCUIT_PUBLIC_INPUTS_HASH_INPUT_LENGTH: Field = 177; +global CALL_PRIVATE_FUNCTION_RETURN_SIZE: Field = 210; +global PUBLIC_CIRCUIT_PUBLIC_INPUTS_HASH_INPUT_LENGTH: Field = 98; +global PRIVATE_CIRCUIT_PUBLIC_INPUTS_HASH_INPUT_LENGTH: Field = 188; global COMMITMENTS_NUM_BYTES_PER_BASE_ROLLUP: Field = 2048; global NULLIFIERS_NUM_BYTES_PER_BASE_ROLLUP: Field = 2048; global PUBLIC_DATA_WRITES_NUM_BYTES_PER_BASE_ROLLUP: Field = 1024; diff --git a/yarn-project/noir-protocol-circuits/src/crates/types/src/contrakt/deployment_data.nr b/yarn-project/noir-protocol-circuits/src/crates/types/src/contrakt/deployment_data.nr index a0ef079d69a..eec1cbe1c3c 100644 --- a/yarn-project/noir-protocol-circuits/src/crates/types/src/contrakt/deployment_data.nr +++ b/yarn-project/noir-protocol-circuits/src/crates/types/src/contrakt/deployment_data.nr @@ -5,13 +5,13 @@ use crate::constants::{ }; use crate::hash::pedersen_hash; use crate::grumpkin_point::GrumpkinPoint; -use crate::traits::Hash; +use crate::traits::{Hash, Serialize}; // docs:start:contract-deployment-data struct ContractDeploymentData { - deployer_public_key : GrumpkinPoint, - constructor_vk_hash : Field, - function_tree_root : Field, + public_key : GrumpkinPoint, + initialization_hash : Field, + contract_class_id : Field, contract_address_salt : Field, portal_contract_address : EthAddress, } @@ -23,22 +23,25 @@ impl Hash for ContractDeploymentData { } } -impl ContractDeploymentData { +impl Serialize for ContractDeploymentData { fn serialize(self) -> [Field; CONTRACT_DEPLOYMENT_DATA_LENGTH] { [ - self.deployer_public_key.x, - self.deployer_public_key.y, - self.constructor_vk_hash, - self.function_tree_root, + self.public_key.x, + self.public_key.y, + self.initialization_hash, + self.contract_class_id, self.contract_address_salt, self.portal_contract_address.to_field(), ] } +} +impl ContractDeploymentData { + fn assert_is_zero(self) { - self.deployer_public_key.assert_is_zero(); - assert(self.constructor_vk_hash == 0); - assert(self.function_tree_root == 0); + self.public_key.assert_is_zero(); + assert(self.initialization_hash == 0); + assert(self.contract_class_id == 0); assert(self.contract_address_salt == 0); self.portal_contract_address.assert_is_zero(); } diff --git a/yarn-project/noir-protocol-circuits/src/crates/types/src/contrakt/storage_update_request.nr b/yarn-project/noir-protocol-circuits/src/crates/types/src/contrakt/storage_update_request.nr index 0cfbc1cabb1..ecd535c96ff 100644 --- a/yarn-project/noir-protocol-circuits/src/crates/types/src/contrakt/storage_update_request.nr +++ b/yarn-project/noir-protocol-circuits/src/crates/types/src/contrakt/storage_update_request.nr @@ -6,7 +6,7 @@ use crate::{ hash::pedersen_hash, }; use dep::std::cmp::Eq; -use crate::traits::{Hash, Empty}; +use crate::traits::{Hash, Empty, Serialize}; struct StorageUpdateRequest{ storage_slot : Field, @@ -39,11 +39,13 @@ impl Hash for StorageUpdateRequest { } } -impl StorageUpdateRequest { - pub fn serialize(self) -> [Field; CONTRACT_STORAGE_UPDATE_REQUEST_LENGTH] { +impl Serialize for StorageUpdateRequest { + fn serialize(self) -> [Field; CONTRACT_STORAGE_UPDATE_REQUEST_LENGTH] { [self.storage_slot, self.old_value, self.new_value] } +} +impl StorageUpdateRequest { pub fn is_empty(self) -> bool { self.storage_slot == 0 } diff --git a/yarn-project/noir-protocol-circuits/src/crates/types/src/grumpkin_point.nr b/yarn-project/noir-protocol-circuits/src/crates/types/src/grumpkin_point.nr index 364d19a1549..71322d0afa5 100644 --- a/yarn-project/noir-protocol-circuits/src/crates/types/src/grumpkin_point.nr +++ b/yarn-project/noir-protocol-circuits/src/crates/types/src/grumpkin_point.nr @@ -1,3 +1,6 @@ +use crate::traits::{Serialize, Deserialize}; +use dep::std::cmp::Eq; + global GRUMPKIN_POINT_SERIALIZED_LEN: Field = 2; struct GrumpkinPoint { @@ -5,6 +8,18 @@ struct GrumpkinPoint { y: Field, } +impl Serialize for GrumpkinPoint { + fn serialize(self) -> [Field; GRUMPKIN_POINT_SERIALIZED_LEN] { + [self.x, self.y] + } +} + +impl Eq for GrumpkinPoint { + fn eq(self, point: GrumpkinPoint) -> bool { + (point.x == self.x) & (point.y == self.y) + } +} + impl GrumpkinPoint { pub fn new(x: Field, y: Field) -> Self { Self { x, y } @@ -17,8 +32,8 @@ impl GrumpkinPoint { } } - fn serialize(self) -> [Field; GRUMPKIN_POINT_SERIALIZED_LEN] { - [self.x, self.y] + pub fn is_zero(self) -> bool { + (self.x == 0) & (self.y == 0) } // TODO(David): Would be quite careful here as (0,0) is not a point diff --git a/yarn-project/noir-protocol-circuits/src/crates/types/src/grumpkin_private_key.nr b/yarn-project/noir-protocol-circuits/src/crates/types/src/grumpkin_private_key.nr new file mode 100644 index 00000000000..b88381ee118 --- /dev/null +++ b/yarn-project/noir-protocol-circuits/src/crates/types/src/grumpkin_private_key.nr @@ -0,0 +1,45 @@ +use dep::std::{ + cmp::Eq, + grumpkin_scalar::GrumpkinScalar, + grumpkin_scalar_mul::grumpkin_fixed_base, +}; +use crate::grumpkin_point::GrumpkinPoint; + +global GRUMPKIN_PRIVATE_KEY_SERIALIZED_LEN: Field = 2; + +struct GrumpkinPrivateKey { + high: Field, + low: Field, +} + +impl Eq for GrumpkinPrivateKey { + fn eq(self, key: GrumpkinPrivateKey) -> bool { + (key.high == self.high) & (key.low == self.low) + } +} + +impl GrumpkinPrivateKey { + pub fn new(high: Field, low: Field) -> Self { + GrumpkinPrivateKey { high, low } + } + + pub fn zero() -> Self { + Self { + high: 0, + low: 0, + } + } + + pub fn is_zero(self) -> bool { + (self.high == 0) & (self.low == 0) + } + + pub fn serialize(self) -> [Field; GRUMPKIN_PRIVATE_KEY_SERIALIZED_LEN] { + [self.high, self.low] + } + + pub fn derive_public_key(self) -> GrumpkinPoint { + let public_key = grumpkin_fixed_base(GrumpkinScalar { high: self.high, low: self.low }); + GrumpkinPoint { x: public_key[0], y: public_key[1] } + } +} diff --git a/yarn-project/noir-protocol-circuits/src/crates/types/src/hash.nr b/yarn-project/noir-protocol-circuits/src/crates/types/src/hash.nr index ddb4b34bbc6..ab6c9f260c1 100644 --- a/yarn-project/noir-protocol-circuits/src/crates/types/src/hash.nr +++ b/yarn-project/noir-protocol-circuits/src/crates/types/src/hash.nr @@ -6,7 +6,6 @@ use crate::abis::new_contract_data::NewContractData as ContractLeafPreimage; use crate::abis::function_data::FunctionData; use crate::abis::side_effect::{SideEffect}; use crate::utils::uint256::U256; -use crate::utils::bounded_vec::BoundedVec; use crate::constants::{ ARGS_HASH_CHUNK_COUNT, ARGS_HASH_CHUNK_LENGTH, @@ -129,7 +128,7 @@ pub fn function_tree_root_from_siblings( // Calculate the contract tree root from the sibling path and leaf preimage. pub fn contract_tree_root_from_siblings( - function_tree_root: Field, + contract_class_id: Field, storage_contract_address: AztecAddress, portal_contract_address: EthAddress, contract_leaf_index: Field, @@ -137,7 +136,7 @@ pub fn contract_tree_root_from_siblings( ) -> Field { //TODO(Kev): if we use shorthand syntax here, we get an error as expected, // since variable name is `storage_contract_address` but the span is incorrect. - let contract_leaf_preimage = ContractLeafPreimage { contract_address: storage_contract_address, portal_contract_address, function_tree_root }; + let contract_leaf_preimage = ContractLeafPreimage { contract_address: storage_contract_address, portal_contract_address, contract_class_id }; let contract_leaf = contract_leaf_preimage.hash(); diff --git a/yarn-project/noir-protocol-circuits/src/crates/types/src/header.nr b/yarn-project/noir-protocol-circuits/src/crates/types/src/header.nr index d9215d26cb4..30e01dce3b1 100644 --- a/yarn-project/noir-protocol-circuits/src/crates/types/src/header.nr +++ b/yarn-project/noir-protocol-circuits/src/crates/types/src/header.nr @@ -1,15 +1,91 @@ use crate::{ abis::{ - append_only_tree_snapshot::AppendOnlyTreeSnapshot, - global_variables::GlobalVariables, + append_only_tree_snapshot::{ + AppendOnlyTreeSnapshot, + APPEND_ONLY_TREE_SNAPSHOT_LENGTH, + }, + global_variables::{ + GlobalVariables, + GLOBAL_VARIABLES_LENGTH, + }, + }, + constants::{ + GENERATOR_INDEX__BLOCK_HASH, + HEADER_LENGTH, + NUM_FIELDS_PER_SHA256, + }, + hash::pedersen_hash, + state_reference::{ + StateReference, + STATE_REFERENCE_LENGTH, + }, + traits::{ + Empty, + Hash, + }, + utils::{ + arr_copy_slice, }, - constants::NUM_FIELDS_PER_SHA256, - state_reference::StateReference, }; +// docs:start:header struct Header { last_archive: AppendOnlyTreeSnapshot, body_hash: [Field; NUM_FIELDS_PER_SHA256], state: StateReference, global_variables: GlobalVariables, } +// docs:end:header + +impl Header { + + pub fn serialize(self) -> [Field; HEADER_LENGTH] { + let mut fields: BoundedVec = BoundedVec::new(0); + + fields.extend_from_array(self.last_archive.serialize()); + fields.extend_from_array(self.body_hash); + fields.extend_from_array(self.state.serialize()); + fields.extend_from_array(self.global_variables.serialize()); + + fields.storage + } + + pub fn deserialize(serialized: [Field; HEADER_LENGTH]) -> Self { + let mut offset = 0; + + let last_archive_fields = arr_copy_slice(serialized, [0; APPEND_ONLY_TREE_SNAPSHOT_LENGTH], offset); + offset = offset + APPEND_ONLY_TREE_SNAPSHOT_LENGTH; + + let body_hash = arr_copy_slice(serialized, [0; NUM_FIELDS_PER_SHA256], offset); + offset = offset + NUM_FIELDS_PER_SHA256; + + let state_fields = arr_copy_slice(serialized, [0; STATE_REFERENCE_LENGTH], offset); + offset = offset + STATE_REFERENCE_LENGTH; + + let global_variables_fields = arr_copy_slice(serialized, [0; GLOBAL_VARIABLES_LENGTH], offset); + + Header { + last_archive: AppendOnlyTreeSnapshot::deserialize(last_archive_fields), + body_hash, + state: StateReference::deserialize(state_fields), + global_variables: GlobalVariables::deserialize(global_variables_fields), + } + } +} + +impl Empty for Header { + fn empty() -> Self { + Self { + last_archive: AppendOnlyTreeSnapshot::zero(), + body_hash: [0; NUM_FIELDS_PER_SHA256], + state: StateReference::empty(), + global_variables: GlobalVariables::empty(), + } + } +} + +impl Hash for Header { + fn hash(self) -> Field { + pedersen_hash(self.serialize(), GENERATOR_INDEX__BLOCK_HASH) + } +} diff --git a/yarn-project/noir-protocol-circuits/src/crates/types/src/interop_testing.nr b/yarn-project/noir-protocol-circuits/src/crates/types/src/interop_testing.nr index e444aed8321..b815987d408 100644 --- a/yarn-project/noir-protocol-circuits/src/crates/types/src/interop_testing.nr +++ b/yarn-project/noir-protocol-circuits/src/crates/types/src/interop_testing.nr @@ -1,7 +1,6 @@ -use crate::abis::complete_address::CompleteAddress; use crate::grumpkin_point::GrumpkinPoint; use crate::transaction::request::TxRequest; -use crate::address::{AztecAddress, EthAddress}; +use crate::address::{AztecAddress, EthAddress, PartialAddress, PublicKeysHash}; use crate::transaction::context::TxContext; use crate::abis::function_data::FunctionData; use crate::abis::function_leaf_preimage::FunctionLeafPreimage; @@ -13,29 +12,38 @@ use crate::abis::public_circuit_public_inputs::PublicCircuitPublicInputs; use crate::abis::side_effect::SideEffect; #[test] -fn compute_complete_address() { +fn compute_address() { let point = GrumpkinPoint { x: 1, y: 2 }; let contract_address_salt = 3; - let function_tree_root = 4; - let constructor_hash = 5; + let contract_class_id = 4; + let initialization_hash = 5; + let portal_contract_address = EthAddress::from_field(6); - let complete_address = CompleteAddress::compute( + let address = AztecAddress::compute_from_public_key( point, + contract_class_id, contract_address_salt, - function_tree_root, - constructor_hash + initialization_hash, + portal_contract_address ); - assert( - complete_address.partial_address.to_field() - == 0x197673f31940878b2d6c681223dbed9cfacd2f722cbe30155225b2ada17778db - ); - assert( - complete_address.address.to_field() - == 0x1a6e89b034478713c7a9f1c77fb80af995f708f6f208bf352b4dda2124739109 - ); - assert(complete_address.public_key.x == 1); - assert(complete_address.public_key.y == 2); + assert(address.to_field() == 0x2fd71a4f0742364f194dd16d0ae32d2f47845ddc7f5d328f37d4148b565c4123); +} + +#[test] +fn compute_public_keys_hash() { + let point = GrumpkinPoint { x: 1, y: 2 }; + let actual = PublicKeysHash::compute(point); + assert(actual.to_field() == 0x1923a6246e305720b6aaf751fde0342613e93c82e455c3831e28375c16dd40d8); +} + +#[test] +fn compute_address_from_partial_and_pubkey() { + let point = GrumpkinPoint { x: 1, y: 2 }; + let partial_address = PartialAddress::from_field(3); + + let address = AztecAddress::compute(PublicKeysHash::compute(point), partial_address); + assert(address.to_field() == 0x0447f893197175723deb223696e2e96dbba1e707ee8507766373558877e74197); } #[test] @@ -48,9 +56,9 @@ fn compute_tx_request_hash() { is_rebate_payment_tx: false, is_contract_deployment_tx: true, contract_deployment_data: ContractDeploymentData { - deployer_public_key: GrumpkinPoint { x: 1, y: 2 }, - constructor_vk_hash: 1, - function_tree_root: 2, + public_key: GrumpkinPoint { x: 1, y: 2 }, + initialization_hash: 1, + contract_class_id: 2, contract_address_salt: 3, portal_contract_address: EthAddress::from_field(1) }, @@ -97,7 +105,7 @@ fn compute_function_leaf() { } #[test] -fn compute_call_stack_item_request() { +fn compute_call_stack_item_request_hash() { let contract_address = AztecAddress::from_field(1); let function_data = FunctionData { selector: FunctionSelector::from_u32(2), is_internal: false, is_private: false, is_constructor: false }; @@ -105,15 +113,16 @@ fn compute_call_stack_item_request() { public_inputs.new_commitments[0] = SideEffect{ value: 1, counter: 0, - }; + }; let call_stack_item = PublicCallStackItem { contract_address, public_inputs, is_execution_request: true, function_data }; - assert_eq(call_stack_item.hash(), 0x0edc0b5221e098c129545ba693368cddc0e6950bb11baa4595b310fc1fa24b5f); + // Value from abis.test.ts "Computes a callstack item request hash" test + assert_eq(call_stack_item.hash(), 0x05c5d32c38f3de19dbfa92e25746e1db1a887d5b02b79dd915c0f50e9fc51dcd); } #[test] -fn compute_call_stack_item() { +fn compute_call_stack_item_hash() { let contract_address = AztecAddress::from_field(1); let function_data = FunctionData { selector: FunctionSelector::from_u32(2), is_internal: false, is_private: false, is_constructor: false }; @@ -121,9 +130,10 @@ fn compute_call_stack_item() { public_inputs.new_commitments[0] = SideEffect{ value: 1, counter: 0, - }; + }; let call_stack_item = PublicCallStackItem { contract_address, public_inputs, is_execution_request: false, function_data }; - assert_eq(call_stack_item.hash(), 0x10e265bf51be6945c10e526a86a928810429ac2d34c5fcda469245f0bb56abf6); + // Value from abis.test.ts "Computes a callstack item hash" test + assert_eq(call_stack_item.hash(), 0x06dc9c35290e1868f41fc9f45bca49482bf5d5c000c789d6c74e53bce0f686f8); } diff --git a/yarn-project/noir-protocol-circuits/src/crates/types/src/lib.nr b/yarn-project/noir-protocol-circuits/src/crates/types/src/lib.nr index 1c3c82c15f1..936ed75a951 100644 --- a/yarn-project/noir-protocol-circuits/src/crates/types/src/lib.nr +++ b/yarn-project/noir-protocol-circuits/src/crates/types/src/lib.nr @@ -1,6 +1,7 @@ mod utils; mod address; mod grumpkin_point; +mod grumpkin_private_key; // This is intentionally spelled like this // since contract is a reserved keyword, so it cannot // be used as an ident. @@ -12,6 +13,7 @@ mod constants; mod mocked; mod hash; mod traits; +mod type_serialization; mod header; diff --git a/yarn-project/noir-protocol-circuits/src/crates/types/src/partial_state_reference.nr b/yarn-project/noir-protocol-circuits/src/crates/types/src/partial_state_reference.nr index d12d3d7788b..b0d77dc67a0 100644 --- a/yarn-project/noir-protocol-circuits/src/crates/types/src/partial_state_reference.nr +++ b/yarn-project/noir-protocol-circuits/src/crates/types/src/partial_state_reference.nr @@ -1,4 +1,7 @@ -use crate::abis::append_only_tree_snapshot::AppendOnlyTreeSnapshot; +use crate::{ + abis::append_only_tree_snapshot::AppendOnlyTreeSnapshot, + traits::Empty, +}; struct PartialStateReference { note_hash_tree: AppendOnlyTreeSnapshot, @@ -7,11 +10,59 @@ struct PartialStateReference { public_data_tree: AppendOnlyTreeSnapshot, } +global PARTIAL_STATE_REFERENCE_LENGTH: Field = 8; // APPEND_ONLY_TREE_SNAPSHOT_LENGTH * 4; + impl PartialStateReference { - fn eq(self, other: PartialStateReference) -> bool { + pub fn eq(self, other: PartialStateReference) -> bool { self.note_hash_tree.eq(other.note_hash_tree) & self.nullifier_tree.eq(other.nullifier_tree) & self.contract_tree.eq(other.contract_tree) & self.public_data_tree.eq(other.public_data_tree) } + + pub fn serialize(self) -> [Field; PARTIAL_STATE_REFERENCE_LENGTH] { + let serialized_note_hash_tree = self.note_hash_tree.serialize(); + let serialized_nullifier_tree = self.nullifier_tree.serialize(); + let serialized_contract_tree = self.contract_tree.serialize(); + let serialized_public_data_tree = self.public_data_tree.serialize(); + + [ + serialized_note_hash_tree[0], + serialized_note_hash_tree[1], + serialized_nullifier_tree[0], + serialized_nullifier_tree[1], + serialized_contract_tree[0], + serialized_contract_tree[1], + serialized_public_data_tree[0], + serialized_public_data_tree[1], + ] + } + + pub fn deserialize(serialized: [Field; PARTIAL_STATE_REFERENCE_LENGTH]) -> PartialStateReference { + PartialStateReference { + note_hash_tree: AppendOnlyTreeSnapshot::deserialize( + [serialized[0], serialized[1]] + ), + nullifier_tree: AppendOnlyTreeSnapshot::deserialize( + [serialized[2], serialized[3]] + ), + contract_tree: AppendOnlyTreeSnapshot::deserialize( + [serialized[4], serialized[5]] + ), + public_data_tree: AppendOnlyTreeSnapshot::deserialize( + [serialized[6], serialized[7]] + ), + } + } +} + +impl Empty for PartialStateReference { + fn empty() -> Self { + Self { + note_hash_tree: AppendOnlyTreeSnapshot::zero(), + nullifier_tree: AppendOnlyTreeSnapshot::zero(), + contract_tree: AppendOnlyTreeSnapshot::zero(), + public_data_tree: AppendOnlyTreeSnapshot::zero(), + } + } } diff --git a/yarn-project/noir-protocol-circuits/src/crates/types/src/state_reference.nr b/yarn-project/noir-protocol-circuits/src/crates/types/src/state_reference.nr index 21b87fde0db..bb3c517551e 100644 --- a/yarn-project/noir-protocol-circuits/src/crates/types/src/state_reference.nr +++ b/yarn-project/noir-protocol-circuits/src/crates/types/src/state_reference.nr @@ -1,7 +1,60 @@ -use crate::abis::append_only_tree_snapshot::AppendOnlyTreeSnapshot; -use crate::partial_state_reference::PartialStateReference; +use crate::{ + abis::append_only_tree_snapshot::{ + AppendOnlyTreeSnapshot, + APPEND_ONLY_TREE_SNAPSHOT_LENGTH, + }, + partial_state_reference::{ + PartialStateReference, + PARTIAL_STATE_REFERENCE_LENGTH, + }, + traits::Empty, + utils::{ + arr_copy_slice, + }, +}; struct StateReference { l1_to_l2_message_tree: AppendOnlyTreeSnapshot, partial: PartialStateReference, } + +global STATE_REFERENCE_LENGTH: Field = 10; // 2 for snap + 8 for partial + +impl StateReference { + fn eq(self, other: StateReference) -> bool { + self.l1_to_l2_message_tree.eq(other.l1_to_l2_message_tree) & + self.partial.eq(other.partial) + } + + fn serialize(self) -> [Field; STATE_REFERENCE_LENGTH] { + let mut fields: BoundedVec = BoundedVec::new(0); + + fields.extend_from_array(self.l1_to_l2_message_tree.serialize()); + fields.extend_from_array(self.partial.serialize()); + + fields.storage + } + + fn deserialize(serialized: [Field; STATE_REFERENCE_LENGTH]) -> StateReference { + let mut offset = 0; + + let l1_to_l2_message_tree_fields = arr_copy_slice(serialized, [0; APPEND_ONLY_TREE_SNAPSHOT_LENGTH], offset); + offset = offset + APPEND_ONLY_TREE_SNAPSHOT_LENGTH; + + let partial_fields = arr_copy_slice(serialized, [0; PARTIAL_STATE_REFERENCE_LENGTH], offset); + + StateReference { + l1_to_l2_message_tree: AppendOnlyTreeSnapshot::deserialize(l1_to_l2_message_tree_fields), + partial: PartialStateReference::deserialize(partial_fields), + } + } +} + +impl Empty for StateReference { + fn empty() -> Self { + Self { + l1_to_l2_message_tree: AppendOnlyTreeSnapshot::zero(), + partial: PartialStateReference::empty(), + } + } +} diff --git a/yarn-project/noir-protocol-circuits/src/crates/types/src/tests/fixtures.nr b/yarn-project/noir-protocol-circuits/src/crates/types/src/tests/fixtures.nr index 39f729e7032..6fef627310d 100644 --- a/yarn-project/noir-protocol-circuits/src/crates/types/src/tests/fixtures.nr +++ b/yarn-project/noir-protocol-circuits/src/crates/types/src/tests/fixtures.nr @@ -4,21 +4,51 @@ mod contracts; mod note_hash_tree; mod read_requests; -use crate::address::AztecAddress; -use crate::abis::block_header::BlockHeader; -use crate::grumpkin_point::GrumpkinPoint; -use crate::tests::fixtures; +use crate::{ + abis::{ + append_only_tree_snapshot::AppendOnlyTreeSnapshot, + global_variables::GlobalVariables, + }, + address::AztecAddress, + constants::NUM_FIELDS_PER_SHA256, + grumpkin_point::GrumpkinPoint, + header::Header, + partial_state_reference::PartialStateReference, + state_reference::StateReference, + tests::fixtures +}; global MSG_SENDER = AztecAddress { inner: 27 }; -global DEPLOYER_PUBLIC_KEY = GrumpkinPoint { x: 123456789, y: 123456789 }; +global PUBLIC_KEY = GrumpkinPoint { x: 123456789, y: 123456789 }; + +// Workaround for https://github.com/noir-lang/noir/issues/1440 +fn empty_append_only_tree() -> AppendOnlyTreeSnapshot { + AppendOnlyTreeSnapshot::zero() +} -global BLOCK_HEADER = BlockHeader { - note_hash_tree_root: fixtures::note_hash_tree::ROOT, - nullifier_tree_root: 0, - contract_tree_root: fixtures::contract_tree::ROOT, - l1_to_l2_message_tree_root: 0, - archive_root: 0, - public_data_tree_root: 0, - global_variables_hash: 0, +global HEADER = Header { + last_archive: empty_append_only_tree(), + body_hash: [0; NUM_FIELDS_PER_SHA256], + state: StateReference { + l1_to_l2_message_tree: empty_append_only_tree(), + partial: PartialStateReference { + note_hash_tree: AppendOnlyTreeSnapshot { + root: fixtures::note_hash_tree::ROOT, + next_available_leaf_index: 0, // TODO: should this be populated? + }, + nullifier_tree: empty_append_only_tree(), + contract_tree: AppendOnlyTreeSnapshot { + root: fixtures::contract_tree::ROOT, + next_available_leaf_index: 0, // TODO: should this be populated? + }, + public_data_tree: empty_append_only_tree() + } + }, + global_variables: GlobalVariables { + chain_id: 0, + version: 0, + block_number: 0, + timestamp: 0 + } }; diff --git a/yarn-project/noir-protocol-circuits/src/crates/types/src/tests/fixtures/contracts.nr b/yarn-project/noir-protocol-circuits/src/crates/types/src/tests/fixtures/contracts.nr index 78a591c64c2..5a63c2c3965 100644 --- a/yarn-project/noir-protocol-circuits/src/crates/types/src/tests/fixtures/contracts.nr +++ b/yarn-project/noir-protocol-circuits/src/crates/types/src/tests/fixtures/contracts.nr @@ -7,7 +7,7 @@ struct ContractData { address: AztecAddress, portal_contract_address: EthAddress, membership_witness: ContractLeafMembershipWitness, - function_tree_root: Field, + contract_class_id: Field, } global default_contract = ContractData { @@ -18,7 +18,7 @@ global default_contract = ContractData { leaf_index: 0, sibling_path: fixtures::contract_tree::SIBLING_PATHS[0], }, - function_tree_root: 0x1c55b3903b8b2812ba2a2315edb30aa9b9d9c99c153e9215cc01ec8979a8e9be, + contract_class_id: 0x1c55b3903b8b2812ba2a2315edb30aa9b9d9c99c153e9215cc01ec8979a8e9be, }; global parent_contract = ContractData { @@ -29,5 +29,5 @@ global parent_contract = ContractData { leaf_index: 1, sibling_path: fixtures::contract_tree::SIBLING_PATHS[1], }, - function_tree_root: 0x02b3f6b0a36bd01f08cee2d607dbe08894bb8c58159e87bb17db28cad43291d4, + contract_class_id: 0x02b3f6b0a36bd01f08cee2d607dbe08894bb8c58159e87bb17db28cad43291d4, }; diff --git a/yarn-project/noir-protocol-circuits/src/crates/types/src/tests/fixtures/read_requests.nr b/yarn-project/noir-protocol-circuits/src/crates/types/src/tests/fixtures/read_requests.nr index dacb83e2707..a77ddd9b45f 100644 --- a/yarn-project/noir-protocol-circuits/src/crates/types/src/tests/fixtures/read_requests.nr +++ b/yarn-project/noir-protocol-circuits/src/crates/types/src/tests/fixtures/read_requests.nr @@ -3,7 +3,6 @@ use crate::abis::{ side_effect::SideEffect, }; use crate::tests::fixtures; -use crate::utils::bounded_vec::BoundedVec; use crate::constants::{ MAX_READ_REQUESTS_PER_CALL, }; diff --git a/yarn-project/noir-protocol-circuits/src/crates/types/src/tests/previous_kernel_data_builder.nr b/yarn-project/noir-protocol-circuits/src/crates/types/src/tests/previous_kernel_data_builder.nr index 3a587aefbff..5bfa665a5de 100644 --- a/yarn-project/noir-protocol-circuits/src/crates/types/src/tests/previous_kernel_data_builder.nr +++ b/yarn-project/noir-protocol-circuits/src/crates/types/src/tests/previous_kernel_data_builder.nr @@ -4,7 +4,6 @@ use crate::{ call_request::{CallerContext, CallRequest}, combined_constant_data::CombinedConstantData, combined_accumulated_data::CombinedAccumulatedDataBuilder, - block_header::BlockHeader, kernel_circuit_public_inputs::KernelCircuitPublicInputs, previous_kernel_data::PreviousKernelData, public_data_read::PublicDataRead, @@ -12,6 +11,7 @@ use crate::{ side_effect::{SideEffect, SideEffectLinkedToNoteHash}, }, address::{AztecAddress, EthAddress}, + header::Header, mocked::{Proof, VerificationKey}, tests::{ fixtures, @@ -32,7 +32,7 @@ struct PreviousKernelDataBuilder { contract_address: AztecAddress, portal_contract_address: EthAddress, end: CombinedAccumulatedDataBuilder, - block_header: BlockHeader, + historical_header: Header, tx_context: TxContext, is_private: bool, proof: Proof, @@ -51,13 +51,13 @@ impl PreviousKernelDataBuilder { counter: 0 }); // 0th nullifier must be non-zero. - let tx_context = build_tx_context(false); + let tx_context = build_tx_context(false, 0); PreviousKernelDataBuilder { contract_address: fixtures::contracts::parent_contract.address, portal_contract_address: fixtures::contracts::parent_contract.portal_contract_address, end, - block_header: fixtures::BLOCK_HEADER, + historical_header: fixtures::HEADER, tx_context, is_private: true, proof: Proof {}, @@ -68,11 +68,6 @@ impl PreviousKernelDataBuilder { } } - pub fn is_constructor(&mut self) -> Self { - self.tx_context = build_tx_context(true); - *self - } - pub fn is_public(&mut self) -> Self { self.is_private = false; *self @@ -197,7 +192,7 @@ impl PreviousKernelDataBuilder { let public_inputs = KernelCircuitPublicInputs { end: self.end.finish(), constants: CombinedConstantData { - block_header: self.block_header, + historical_header: self.historical_header, tx_context: self.tx_context, }, is_private: self.is_private, diff --git a/yarn-project/noir-protocol-circuits/src/crates/types/src/tests/private_call_data_builder.nr b/yarn-project/noir-protocol-circuits/src/crates/types/src/tests/private_call_data_builder.nr index 5470a090d4d..f922026211f 100644 --- a/yarn-project/noir-protocol-circuits/src/crates/types/src/tests/private_call_data_builder.nr +++ b/yarn-project/noir-protocol-circuits/src/crates/types/src/tests/private_call_data_builder.nr @@ -21,9 +21,6 @@ use crate::{ transaction::{ request::TxRequest, }, - utils::{ - bounded_vec::BoundedVec, - }, }; use crate::constants::{ MAX_PRIVATE_CALL_STACK_LENGTH_PER_CALL, @@ -103,7 +100,7 @@ impl PrivateCallDataBuilder { } pub fn build_tx_request(self) -> TxRequest { - let tx_context = build_tx_context(self.public_inputs.call_context.is_contract_deployment); + let tx_context = build_tx_context(self.public_inputs.call_context.is_contract_deployment, self.public_inputs.args_hash); TxRequest { origin: self.contract_address, args_hash: self.public_inputs.args_hash, @@ -114,14 +111,14 @@ impl PrivateCallDataBuilder { pub fn append_private_call_requests(&mut self, num_requests: Field, is_delegate_call: bool) { let (hashes, call_requests) = self.generate_call_requests(self.private_call_stack, num_requests, is_delegate_call); - self.public_inputs.private_call_stack_hashes.push_vec(hashes); - self.private_call_stack.push_vec(call_requests); + self.public_inputs.private_call_stack_hashes.extend_from_bounded_vec(hashes); + self.private_call_stack.extend_from_bounded_vec(call_requests); } pub fn append_public_call_requests(&mut self, num_requests: Field, is_delegate_call: bool) { let (hashes, call_requests) = self.generate_call_requests(self.public_call_stack, num_requests, is_delegate_call); - self.public_inputs.public_call_stack_hashes.push_vec(hashes); - self.public_call_stack.push_vec(call_requests); + self.public_inputs.public_call_stack_hashes.extend_from_bounded_vec(hashes); + self.public_call_stack.extend_from_bounded_vec(call_requests); } fn generate_call_requests( @@ -162,14 +159,14 @@ impl PrivateCallDataBuilder { pub fn append_read_requests(&mut self, num_read_requests: Field) { let (read_requests, read_request_membership_witnesses) = fixtures::read_requests::generate_read_requests(num_read_requests); - self.public_inputs.read_requests.push_vec(read_requests); - self.read_request_membership_witnesses.push_vec(read_request_membership_witnesses); + self.public_inputs.read_requests.extend_from_bounded_vec(read_requests); + self.read_request_membership_witnesses.extend_from_bounded_vec(read_request_membership_witnesses); } pub fn append_transient_read_requests(&mut self, num_read_requests: Field) { let (read_requests, read_request_membership_witnesses) = fixtures::read_requests::generate_transient_read_requests(num_read_requests); - self.public_inputs.read_requests.push_vec(read_requests); - self.read_request_membership_witnesses.push_vec(read_request_membership_witnesses); + self.public_inputs.read_requests.extend_from_bounded_vec(read_requests); + self.read_request_membership_witnesses.extend_from_bounded_vec(read_request_membership_witnesses); } pub fn set_encrypted_logs(&mut self, hash: [Field; NUM_FIELDS_PER_SHA256], preimages_length: Field) { diff --git a/yarn-project/noir-protocol-circuits/src/crates/types/src/tests/private_circuit_public_inputs_builder.nr b/yarn-project/noir-protocol-circuits/src/crates/types/src/tests/private_circuit_public_inputs_builder.nr index 2d09777ef07..46196b43b4a 100644 --- a/yarn-project/noir-protocol-circuits/src/crates/types/src/tests/private_circuit_public_inputs_builder.nr +++ b/yarn-project/noir-protocol-circuits/src/crates/types/src/tests/private_circuit_public_inputs_builder.nr @@ -1,21 +1,22 @@ use crate::{ abis::{ call_context::CallContext, - complete_address::CompleteAddress, - block_header::BlockHeader, + nullifier_key_validation_request::NullifierKeyValidationRequest, private_circuit_public_inputs::PrivateCircuitPublicInputs, side_effect::{SideEffect, SideEffectLinkedToNoteHash}, }, + address::{AztecAddress, compute_initialization_hash}, contrakt::deployment_data::ContractDeploymentData, hash::{compute_constructor_hash, hash_args}, + header::Header, tests::{ fixtures, testing_harness::build_contract_deployment_data, }, - utils::bounded_vec::BoundedVec, }; use crate::constants::{ MAX_READ_REQUESTS_PER_CALL, + MAX_NULLIFIER_KEY_VALIDATION_REQUESTS_PER_CALL, MAX_NEW_COMMITMENTS_PER_CALL, MAX_NEW_NULLIFIERS_PER_CALL, MAX_PRIVATE_CALL_STACK_LENGTH_PER_CALL, @@ -32,6 +33,7 @@ struct PrivateCircuitPublicInputsBuilder { return_values: BoundedVec, read_requests: BoundedVec, + nullifier_key_validation_requests: BoundedVec, new_commitments: BoundedVec, new_nullifiers: BoundedVec, @@ -46,7 +48,7 @@ struct PrivateCircuitPublicInputsBuilder { encrypted_log_preimages_length: Field, unencrypted_log_preimages_length: Field, - block_header: BlockHeader, + historical_header: Header, contract_deployment_data: ContractDeploymentData, @@ -70,17 +72,18 @@ impl PrivateCircuitPublicInputsBuilder { }; let function_data = contract_function.data; - let contract_deployment_data = build_contract_deployment_data(is_constructor); + let contract_deployment_data = build_contract_deployment_data(is_constructor, args_hash); let contract_address = if is_constructor { let constructor = fixtures::contract_functions::default_constructor; - let constructor_hash = compute_constructor_hash(constructor.data, args_hash, constructor.vk_hash); - CompleteAddress::compute( - contract_deployment_data.deployer_public_key, + let initialization_hash = compute_initialization_hash(constructor.data.selector.to_field(), args_hash); + AztecAddress::compute_from_public_key( + contract_deployment_data.public_key, + contract_deployment_data.contract_class_id, contract_deployment_data.contract_address_salt, - contract_deployment_data.function_tree_root, - constructor_hash, - ).address + initialization_hash, + portal_contract_address, + ) } else { contract_data.address }; @@ -98,7 +101,7 @@ impl PrivateCircuitPublicInputsBuilder { public_inputs.call_context = call_context; public_inputs.args_hash = args_hash; public_inputs.contract_deployment_data = contract_deployment_data; - public_inputs.block_header = fixtures::BLOCK_HEADER; + public_inputs.historical_header = fixtures::HEADER; public_inputs.chain_id = 0; public_inputs.version = 1; @@ -112,6 +115,7 @@ impl PrivateCircuitPublicInputsBuilder { return_values: self.return_values.storage, read_requests: self.read_requests.storage, + nullifier_key_validation_requests: self.nullifier_key_validation_requests.storage, new_commitments: self.new_commitments.storage, new_nullifiers: self.new_nullifiers.storage, @@ -127,7 +131,7 @@ impl PrivateCircuitPublicInputsBuilder { encrypted_log_preimages_length: self.encrypted_log_preimages_length, unencrypted_log_preimages_length: self.unencrypted_log_preimages_length, - block_header: self.block_header, + historical_header: self.historical_header, contract_deployment_data: self.contract_deployment_data, diff --git a/yarn-project/noir-protocol-circuits/src/crates/types/src/tests/public_call_data_builder.nr b/yarn-project/noir-protocol-circuits/src/crates/types/src/tests/public_call_data_builder.nr index a9f85427c66..48dd0bb62e1 100644 --- a/yarn-project/noir-protocol-circuits/src/crates/types/src/tests/public_call_data_builder.nr +++ b/yarn-project/noir-protocol-circuits/src/crates/types/src/tests/public_call_data_builder.nr @@ -17,7 +17,6 @@ use crate::{ fixtures, public_circuit_public_inputs_builder::PublicCircuitPublicInputsBuilder, }, - utils::bounded_vec::BoundedVec, }; use crate::constants::{ MAX_PUBLIC_CALL_STACK_LENGTH_PER_CALL, diff --git a/yarn-project/noir-protocol-circuits/src/crates/types/src/tests/public_circuit_public_inputs_builder.nr b/yarn-project/noir-protocol-circuits/src/crates/types/src/tests/public_circuit_public_inputs_builder.nr index 134640b691b..41f2102a051 100644 --- a/yarn-project/noir-protocol-circuits/src/crates/types/src/tests/public_circuit_public_inputs_builder.nr +++ b/yarn-project/noir-protocol-circuits/src/crates/types/src/tests/public_circuit_public_inputs_builder.nr @@ -1,7 +1,6 @@ use crate::{ abis::{ call_context::CallContext, - block_header::BlockHeader, public_circuit_public_inputs::PublicCircuitPublicInputs, side_effect::{SideEffect, SideEffectLinkedToNoteHash}, }, @@ -10,8 +9,8 @@ use crate::{ storage_read::StorageRead, storage_update_request::StorageUpdateRequest, }, + header::Header, tests::fixtures, - utils::bounded_vec::BoundedVec, }; use crate::constants::{ MAX_NEW_COMMITMENTS_PER_CALL, @@ -36,7 +35,7 @@ struct PublicCircuitPublicInputsBuilder { new_l2_to_l1_msgs: BoundedVec, unencrypted_logs_hash: [Field; NUM_FIELDS_PER_SHA256], unencrypted_log_preimages_length: Field, - block_header: BlockHeader, + historical_header: Header, prover_address: AztecAddress, } @@ -44,7 +43,7 @@ impl PublicCircuitPublicInputsBuilder { pub fn new() -> Self { let mut public_inputs: PublicCircuitPublicInputsBuilder = dep::std::unsafe::zeroed(); public_inputs.call_context.msg_sender = fixtures::MSG_SENDER; - public_inputs.block_header = fixtures::BLOCK_HEADER; + public_inputs.historical_header = fixtures::HEADER; public_inputs } @@ -61,7 +60,7 @@ impl PublicCircuitPublicInputsBuilder { new_l2_to_l1_msgs: self.new_l2_to_l1_msgs.storage, unencrypted_logs_hash: self.unencrypted_logs_hash, unencrypted_log_preimages_length: self.unencrypted_log_preimages_length, - block_header: self.block_header, + historical_header: self.historical_header, prover_address: self.prover_address, } } diff --git a/yarn-project/noir-protocol-circuits/src/crates/types/src/tests/testing_harness.nr b/yarn-project/noir-protocol-circuits/src/crates/types/src/tests/testing_harness.nr index da83fe88787..0c1eaeeea42 100644 --- a/yarn-project/noir-protocol-circuits/src/crates/types/src/tests/testing_harness.nr +++ b/yarn-project/noir-protocol-circuits/src/crates/types/src/tests/testing_harness.nr @@ -2,17 +2,18 @@ use crate::{ contrakt::deployment_data::ContractDeploymentData, tests::fixtures, transaction::context::TxContext, + address::compute_initialization_hash, }; -pub fn build_contract_deployment_data(is_constructor: bool) -> ContractDeploymentData { +pub fn build_contract_deployment_data(is_constructor: bool, args_hash: Field) -> ContractDeploymentData { let mut contract_deployment_data: ContractDeploymentData = dep::std::unsafe::zeroed(); if is_constructor { let contract_data = fixtures::contracts::default_contract; let constructor = fixtures::contract_functions::default_constructor; contract_deployment_data = ContractDeploymentData { - deployer_public_key: fixtures::DEPLOYER_PUBLIC_KEY, - constructor_vk_hash: constructor.vk_hash, - function_tree_root: contract_data.function_tree_root, + public_key: fixtures::PUBLIC_KEY, + initialization_hash: compute_initialization_hash(constructor.data.selector.to_field(), args_hash), + contract_class_id: contract_data.contract_class_id, contract_address_salt: contract_data.contract_address_salt, portal_contract_address: contract_data.portal_contract_address, }; @@ -20,8 +21,8 @@ pub fn build_contract_deployment_data(is_constructor: bool) -> ContractDeploymen contract_deployment_data } -pub fn build_tx_context(is_constructor: bool) -> TxContext { - let contract_deployment_data = build_contract_deployment_data(is_constructor); +pub fn build_tx_context(is_constructor: bool, args_hash: Field) -> TxContext { + let contract_deployment_data = build_contract_deployment_data(is_constructor, args_hash); TxContext { is_fee_payment_tx: false, is_rebate_payment_tx: false, diff --git a/yarn-project/noir-protocol-circuits/src/crates/types/src/traits.nr b/yarn-project/noir-protocol-circuits/src/crates/types/src/traits.nr index 68ad8e56ff6..e4cd56320cc 100644 --- a/yarn-project/noir-protocol-circuits/src/crates/types/src/traits.nr +++ b/yarn-project/noir-protocol-circuits/src/crates/types/src/traits.nr @@ -44,3 +44,15 @@ impl ToField for Field { self } } + +// docs:start:serialize +trait Serialize { + fn serialize(self) -> [Field; N]; +} +// docs:end:serialize + +// docs:start:deserialize +trait Deserialize { + fn deserialize(fields: [Field; N]) -> Self; +} +// docs:end:deserialize \ No newline at end of file diff --git a/yarn-project/noir-protocol-circuits/src/crates/types/src/type_serialization.nr b/yarn-project/noir-protocol-circuits/src/crates/types/src/type_serialization.nr new file mode 100644 index 00000000000..7bc12f02d27 --- /dev/null +++ b/yarn-project/noir-protocol-circuits/src/crates/types/src/type_serialization.nr @@ -0,0 +1,56 @@ +use crate::traits::{Serialize, Deserialize}; + +global BOOL_SERIALIZED_LEN: Field = 1; +global U32_SERIALIZED_LEN: Field = 1; +global U8_SERIALIZED_LEN: Field = 1; +global FIELD_SERIALIZED_LEN: Field = 1; +global AZTEC_ADDRESS_SERIALIZED_LEN = 1; +global ETH_ADDRESS_SERIALIZED_LEN = 1; + +impl Serialize for u32 { + fn serialize(self) -> [Field; U32_SERIALIZED_LEN] { + [self as Field] + } +} + +impl Deserialize for u32 { + fn deserialize(fields: [Field; U32_SERIALIZED_LEN]) -> Self { + fields[0] as u32 + } +} + +impl Serialize for u8 { + fn serialize(self) -> [Field; U32_SERIALIZED_LEN] { + [self as Field] + } +} + +impl Deserialize for u8 { + fn deserialize(fields: [Field; U8_SERIALIZED_LEN]) -> Self { + fields[0] as u8 + } +} + +impl Serialize for Field { + fn serialize(self) -> [Field; U32_SERIALIZED_LEN] { + [self] + } +} + +impl Deserialize for Field { + fn deserialize(fields: [Field; FIELD_SERIALIZED_LEN]) -> Self { + fields[0] + } +} + +impl Serialize for bool { + fn serialize(self) -> [Field; BOOL_SERIALIZED_LEN] { + [self as Field] + } +} + +impl Deserialize for bool { + fn deserialize(fields: [Field; BOOL_SERIALIZED_LEN]) -> bool { + fields[0] as bool + } +} diff --git a/yarn-project/noir-protocol-circuits/src/crates/types/src/utils.nr b/yarn-project/noir-protocol-circuits/src/crates/types/src/utils.nr index 691ffb90a7b..f4c2b212cc2 100644 --- a/yarn-project/noir-protocol-circuits/src/crates/types/src/utils.nr +++ b/yarn-project/noir-protocol-circuits/src/crates/types/src/utils.nr @@ -3,7 +3,6 @@ // Reducing the size of this package would be welcome. mod arrays; -mod bounded_vec; mod field; mod uint256; @@ -11,3 +10,11 @@ mod uint256; pub fn conditional_assign(predicate: bool, lhs: Field, rhs: Field) -> Field { if predicate { lhs } else { rhs } } + +// Copied over from "yarn-project/aztec-nr/aztec/src/utils.nr" +pub fn arr_copy_slice(src: [T; N], mut dst: [T; M], offset: Field) -> [T; M] { + for i in 0..dst.len() { + dst[i] = src[i + offset]; + } + dst +} diff --git a/yarn-project/noir-protocol-circuits/src/crates/types/src/utils/arrays.nr b/yarn-project/noir-protocol-circuits/src/crates/types/src/utils/arrays.nr index 239867b05e0..609071c4d84 100644 --- a/yarn-project/noir-protocol-circuits/src/crates/types/src/utils/arrays.nr +++ b/yarn-project/noir-protocol-circuits/src/crates/types/src/utils/arrays.nr @@ -1,6 +1,5 @@ use dep::std::array; use dep::std::cmp::Eq; -use crate::utils::bounded_vec::BoundedVec; use crate::traits::{Empty, is_empty}; pub fn array_to_bounded_vec(array: [T; N]) -> BoundedVec where T: Empty + Eq { diff --git a/yarn-project/noir-protocol-circuits/src/crates/types/src/utils/bounded_vec.nr b/yarn-project/noir-protocol-circuits/src/crates/types/src/utils/bounded_vec.nr deleted file mode 100644 index e9aa91c26de..00000000000 --- a/yarn-project/noir-protocol-circuits/src/crates/types/src/utils/bounded_vec.nr +++ /dev/null @@ -1,194 +0,0 @@ -struct BoundedVec { - storage: [T; MaxLen], - // TODO: change this to return a u64 as Noir now - // uses u64 for indexing - len: Field, - empty_value: T, -} - -impl BoundedVec { - pub fn new(initial_value: T) -> Self { - BoundedVec { storage: [initial_value; MaxLen], len: 0, empty_value: initial_value } - } - - pub fn get(mut self: Self, index: Field) -> T { - assert(index as u64 < self.len as u64); - self.storage[index] - } - - pub fn get_unchecked(mut self: Self, index: Field) -> T { - self.storage[index] - } - - pub fn push(&mut self, elem: T) { - assert(self.len as u64 < MaxLen as u64, "push out of bounds"); - - self.storage[self.len] = elem; - self.len += 1; - } - - pub fn len(self) -> Field { - self.len - } - - pub fn max_len(_self: BoundedVec) -> Field { - MaxLen - } - - // This is a intermediate method, while we don't have an - // .extend method - pub fn storage(self) -> [T; MaxLen] { - self.storage - } - - pub fn push_array(&mut self, array: [T; Len]) { - let new_len = self.len + array.len(); - assert(new_len as u64 <= MaxLen as u64, "push_array out of bounds"); - for i in 0..array.len() { - self.storage[self.len + i] = array[i]; - } - self.len = new_len; - } - - pub fn push_vec(&mut self, vec: BoundedVec) { - let append_len = vec.len(); - let new_len = self.len + append_len; - assert(new_len as u64 <= MaxLen as u64, "push_vec out of bounds"); - - let mut exceeded_len = false; - for i in 0..Len { - exceeded_len |= i == append_len; - if !exceeded_len { - self.storage[self.len + (i as Field)] = vec.get_unchecked(i as Field); - } - } - self.len = new_len; - } - - pub fn pop(&mut self) -> T { - assert(self.len as u64 > 0); - self.len -= 1; - - let elem = self.storage[self.len]; - self.storage[self.len] = self.empty_value; - elem - } - - pub fn any(self, predicate: fn[Env](T) -> bool) -> bool { - let mut ret = false; - let mut exceeded_len = false; - for i in 0..MaxLen { - exceeded_len |= i == self.len; - if (!exceeded_len) { - ret |= predicate(self.storage[i]); - } - } - ret - } -} - -#[test] -fn test_vec_push_pop() { - let mut vec: BoundedVec = BoundedVec::new(0); - assert(vec.len == 0); - vec.push(2); - assert(vec.len == 1); - vec.push(4); - assert(vec.len == 2); - vec.push(6); - assert(vec.len == 3); - let x = vec.pop(); - assert(x == 6); - assert(vec.len == 2); - assert(vec.get(0) == 2); - assert(vec.get(1) == 4); -} - -#[test] -fn test_vec_push_array() { - let mut vec: BoundedVec = BoundedVec::new(0); - vec.push_array([2, 4]); - assert(vec.len == 2); - assert(vec.get(0) == 2); - assert(vec.get(1) == 4); -} - -#[test(should_fail_with="push_array out of bounds")] -fn test_vec_push_array_out_of_bound() { - let mut vec: BoundedVec = BoundedVec::new(0); - vec.push_array([2, 4, 6]); -} - -#[test(should_fail_with="push_array out of bounds")] -fn test_vec_push_array_twice_out_of_bound() { - let mut vec: BoundedVec = BoundedVec::new(0); - vec.push_array([2]); - assert(vec.len == 1); - vec.push_array([4, 6]); -} - -#[test(should_fail)] -fn test_vec_get_out_of_bound() { - let mut vec: BoundedVec = BoundedVec::new(0); - vec.push_array([2, 4]); - let _x = vec.get(2); -} - -#[test(should_fail)] -fn test_vec_get_not_declared() { - let mut vec: BoundedVec = BoundedVec::new(0); - vec.push_array([2]); - let _x = vec.get(1); -} - -#[test(should_fail)] -fn test_vec_get_uninitialized() { - let mut vec: BoundedVec = BoundedVec::new(0); - let _x = vec.get(0); -} - -#[test(should_fail_with="push out of bounds")] -fn test_vec_push_out_of_bound() { - let mut vec: BoundedVec = BoundedVec::new(0); - vec.push(1); - vec.push(2); -} - -#[test(should_fail_with="push_vec out of bounds")] -fn test_vec_push_vec_out_of_bound() { - let mut vec: BoundedVec = BoundedVec::new(0); - - let mut another_vec: BoundedVec = BoundedVec::new(0); - another_vec.push_array([1, 2, 3]); - - vec.push_vec(another_vec); -} - -#[test(should_fail_with="push_vec out of bounds")] -fn test_vec_push_vec_twice_out_of_bound() { - let mut vec: BoundedVec = BoundedVec::new(0); - vec.push_array([1, 2]); - - let mut another_vec: BoundedVec = BoundedVec::new(0); - another_vec.push(3); - - vec.push_vec(another_vec); -} - -#[test] -fn test_vec_any() { - let mut vec: BoundedVec = BoundedVec::new(0); - vec.push_array([2, 4, 6]); - assert(vec.any(|v| v == 2) == true); - assert(vec.any(|v| v == 4) == true); - assert(vec.any(|v| v == 6) == true); - assert(vec.any(|v| v == 3) == false); -} - -#[test] -fn test_vec_any_not_default() { - let default_value = 1; - let mut vec: BoundedVec = BoundedVec::new(default_value); - vec.push_array([2, 4]); - assert(vec.any(|v| v == default_value) == false); -} diff --git a/yarn-project/noir-protocol-circuits/src/fixtures/nested-call-private-kernel-init.hex b/yarn-project/noir-protocol-circuits/src/fixtures/nested-call-private-kernel-init.hex new file mode 100644 index 00000000000..97daf102741 --- /dev/null +++ b/yarn-project/noir-protocol-circuits/src/fixtures/nested-call-private-kernel-init.hex @@ -0,0 +1 @@ +1c66eceef39c40e60d81ff432d80f4614d6eb8abef3aec4d0d733442e5321708af9f8c440001012d3a0080a45ec992ab98f462b9ce14d0e2e120b7fac4a3b9c725f7aeba2869610000011c128c66042b3f3ba119536e6556ad6fbb3cfd1c1b47f7f17c38f5cd67f433ee1a79cf88dfacd9c37194cce8e5cd80e974e48cbea9aa4cfd687d9b1d3b1e0f43038021824fbd98bb0e388b0efe18f72e9350f7456481714539ba583de37113ce083a2a57ecc0ac76be57919bc82658da41fed7c9aac4575a2dc9d33da76b3d9329eb51554eea81fd16dff9503d8b30c86b84e472b555ab262f03894e08c5d45800000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000007a6900000000000000000000000000000000000000000000000000000000000000011c66eceef39c40e60d81ff432d80f4614d6eb8abef3aec4d0d733442e5321708af9f8c4400010100000000000000000000000000000000000000000000000000000000000000001c66eceef39c40e60d81ff432d80f4614d6eb8abef3aec4d0d733442e53217080000000000000000000000000000000000000000000000000000000000000000af9f8c44000001000000022d3a0080a45ec992ab98f462b9ce14d0e2e120b7fac4a3b9c725f7aeba2869610000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000020d1af2e3cb258a5ca6c97afdb57f60bc3032b5024e7a99eef389c74d69758e9000000000000000000000000000000000000000000000000000000000000000300000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000003579f468b2283611cc4d7adfbb93b8a4815d93ac0b1e1d11dace012cf73c7aa0000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000200000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000004000000000000000000000000000000000051f439cb7c6963ef1f2af6cd6c6e1d00000000000000000000000000000000e52690aabdf495d89156324b8636fd2e00000000000000000000000000000000e3b0c44298fc1c149afbf4c8996fb9240000000000000000000000000000000027ae41e4649b934ca495991b7852b85500000000000000000000000000000000000000000000000000000000000000f8000000000000000000000000000000000000000000000000000000000000000400000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000001864fcdaa80ff2719154fa7c8a9050662972707168d69eac9db6fd3110829f800000000016642d9ccd8346c403aa4c3fa451178b22534a27035cdaa6ec34ae53b29c50cb000000000bcfa3e9f1a8922ee92c6dc964d6595907c1804a86753774322b468f69d4f278000000001864fcdaa80ff2719154fa7c8a9050662972707168d69eac9db6fd3110829f80000000001ed250ed73db6e70805c4efcf0056e8695b79cd3ba418e827c184dee6c6fb0e00000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000001c128c66042b3f3ba119536e6556ad6fbb3cfd1c1b47f7f17c38f5cd67f433ee1a79cf88dfacd9c37194cce8e5cd80e974e48cbea9aa4cfd687d9b1d3b1e0f43038021824fbd98bb0e388b0efe18f72e9350f7456481714539ba583de37113ce083a2a57ecc0ac76be57919bc82658da41fed7c9aac4575a2dc9d33da76b3d9329eb51554eea81fd16dff9503d8b30c86b84e472b555ab262f03894e08c5d45800000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000007a69000000000000000000000000000000000000000000000000000000000000000100000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000200000800000000740000000f00000003515f3109623eb3c25aa5b16a1a79fd558bac7a7ce62c4560a8c537c77ce80dd339128d1d37b6582ee9e6df9567efb64313471dfa18f520f9ce53161b50dbf7731bc5f900000003515f322bc4cce83a486a92c92fd59bd84e0f92595baa639fc2ed86b00ffa0dfded2a092a669a3bdb7a273a015eda494457cc7ed5236f26cee330c290d45a33b9daa94800000003515f332729426c008c085a81bd34d8ef12dd31e80130339ef99d50013a89e4558eee6d0fa4ffe2ee7b7b62eb92608b2251ac31396a718f9b34978888789042b790a30100000003515f342be6b6824a913eb7a57b03cb1ee7bfb4de02f2f65fe8a4e97baa7766ddb353a82a8a25c49dc63778cd9fe96173f12a2bc77f3682f4c4448f98f1df82c75234a100000003515f351f85760d6ab567465aadc2f180af9eae3800e6958fec96aef53fd8a7b195d7c000c6267a0dd5cfc22b3fe804f53e266069c0e36f51885baec1e7e67650c62e170000000c515f41524954484d455449430d9d0f8ece2aa12012fa21e6e5c859e97bd5704e5c122064a66051294bc5e04213f61f54a0ebdf6fee4d4a6ecf693478191de0c2899bcd8e86a636c8d3eff43400000003515f43224a99d02c86336737c8dd5b746c40d2be6aead8393889a76a18d664029096e90f7fe81adcc92a74350eada9622ac453f49ebac24a066a1f83b394df54dfa0130000000c515f46495845445f42415345060e8a013ed289c2f9fd7473b04f6594b138ddb4b4cf6b901622a14088f04b8d2c83ff74fce56e3d5573b99c7b26d85d5046ce0c6559506acb7a675e7713eb3a00000007515f4c4f4749430721a91cb8da4b917e054f72147e1760cfe0ef3d45090ac0f4961d84ec1996961a25e787b26bd8b50b1a99450f77a424a83513c2b33af268cd253b0587ff50c700000003515f4d05dbd8623b8652511e1eb38d38887a69eceb082f807514f09e127237c5213b401b9325b48c6c225968002318095f89d0ef9cf629b2b7f0172e03bc39aacf6ed800000007515f52414e474504b57a3805e41df328f5ca9aefa40fad5917391543b7b65c6476e60b8f72e9ad07c92f3b3e11c8feae96dedc4b14a6226ef3201244f37cfc1ee5b96781f48d2b000000075349474d415f3125001d1954a18571eaa007144c5a567bb0d2be4def08a8be918b8c05e3b27d312c59ed41e09e144eab5de77ca89a2fd783be702a47c951d3112e3de02ce6e47c000000075349474d415f3223994e6a23618e60fa01c449a7ab88378709197e186d48d604bfb6931ffb15ad11c5ec7a0700570f80088fd5198ab5d5c227f2ad2a455a6edeec024156bb7beb000000075349474d415f3300cda5845f23468a13275d18bddae27c6bb189cf9aa95b6a03a0cb6688c7e8d829639b45cf8607c525cc400b55ebf90205f2f378626dc3406cc59b2d1b474fba000000075349474d415f342d299e7928496ea2d37f10b43afd6a80c90a33b483090d18069ffa275eedb2fc2f82121e8de43dc036d99b478b6227ceef34248939987a19011f065d8b5cef5c0000000010000000000000000100000002000000030000000400000005000000060000000700000008000000090000000a0000000b0000000c0000000d0000000e0000000f0000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000270ef3b19adb43eb18546212c8870fe271a66ab03a1807690e0e3ac731ca7b3b2d5779f9d26b90d275db628c69581e1b010392d28ba98670f2ede7ba317eeacc0e88d69d91004ae61d86c88cec9c66922c97aa5e38a6e1860c37c4aaf253304918827b4e203ea7a97d56d07c64c2b43551a0eb2cfd045bedfb15dda28e1973bc2b332e6f8793aac6231f57525c791c89ab08d381ea26885539d54ca90cbf9b632f397a56ddb105ecaa9febc701839790b692b931b4dc72bf863eff9c21b39b2f158ffe5fbcf8f20def60e1ca7df7acd17a74523455b313e8e5b2de7a5f62d1c107facd7a44abff75053c9020e1313de158ff1d87ab1f7bc701d8e7fe82d4db142a61ec357f2b342778f671baf67be21321f456482368153de8959b753ca280cc070a587e48f3a2f91991dec2d7c7484de0f2fa227260c7269e04d3f6b02e3f551227b65a6eb6ef2aff3911c0370cdf90687d737ed1897579d732a67071b65a4621da0a20a98eb6b1bfad3df19c064cb80d8c859932118f4bc4b5750793159fe81b9b7b2441097cc748b50942ad93b32f5b1b8f6d6558d91e04efc31b4ffa574408bed008a8919c08a4d6b11f26c5a56eea98ecb6d8b0c0ea96fea0a51fc6c84b26d99710365453bd5ebd2c8c8fbb9ba85578267e4630946774a81150473081e11159bde31db2cc5642e9ff3b92a2ca38575c28131d10f84df1cc231b380cdf4d00000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000 \ No newline at end of file diff --git a/yarn-project/noir-protocol-circuits/src/fixtures/nested-call-private-kernel-inner.hex b/yarn-project/noir-protocol-circuits/src/fixtures/nested-call-private-kernel-inner.hex index cf767299f91..a69ee2e0241 100644 --- a/yarn-project/noir-protocol-circuits/src/fixtures/nested-call-private-kernel-inner.hex +++ b/yarn-project/noir-protocol-circuits/src/fixtures/nested-call-private-kernel-inner.hex @@ -1 +1 @@ -0000000000000000000000000000000000000000000000000000000000000001000000000000000000000000000000000000000000000000000000000000000200000000000000000000000000000000000000000000000000000000000000010000000000000000000000000000000000000000000000000000000000000002000000000000001000000bd300000bd400000bd500000bd600000bd700000bd800000bd900000bda00000bdb00000bdc00000bdd00000bde00000bdf00000be000000be100000be2000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000f199749848b834474e2e5bcf515f3559ed381ca63f585c7f8e47341f9c9c57d000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000002c2c4914c4adc9d775bd7eeee09cc5a37fc71593bd318e6b486221e53ee87b911c94912f5dcbc89ea72a8a498cdf68c5b74512b32b5ebc27dfd044cde2c6369600000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000d3735899d9fa7162447ca631f0ba2cd500000000000000000000000000000000eb57d0965a756d78291da33072610eb200000000000000000000000000000000d3735899d9fa7162447ca631f0ba2cd500000000000000000000000000000000eb57d0965a756d78291da33072610eb200000000000000000000000000000000000000000000000000000000000000080000000000000000000000000000000000000000000000000000000000000008000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000022bbfcfff87e5cec76010d9f01f47f9e9ae40bf72ef1b34a3c4a78f03a3192f1e2d3f51150dda356221f55860f43e99f4199ce2f23fbff0190d21de72fb3b5c20fb0324db33f18d703e414e318ae4ed60d47ed0024a7c2e3818b873a999caa01864fcdaa80ff2719154fa7c8a9050662972707168d69eac9db6fd3110829f802d0a2756971c93ddc1b4fb85b0a38f561dde6dbed15c19fcb90294fde666ae9000000000000000000000000000000000000000000000000000000000000000001ed250ed73db6e70805c4efcf0056e8695b79cd3ba418e827c184dee6c6fb0e0052c36dbeff3b6ce555a7c8ee06a54e7bf9b38302e5bf265b62966be3ecc38800000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000007a69000000000000000000000000000000000000000000000000000000000000000101000000000000000200000800000000740000000f00000003515f3109623eb3c25aa5b16a1a79fd558bac7a7ce62c4560a8c537c77ce80dd339128d1d37b6582ee9e6df9567efb64313471dfa18f520f9ce53161b50dbf7731bc5f900000003515f322bc4cce83a486a92c92fd59bd84e0f92595baa639fc2ed86b00ffa0dfded2a092a669a3bdb7a273a015eda494457cc7ed5236f26cee330c290d45a33b9daa94800000003515f332729426c008c085a81bd34d8ef12dd31e80130339ef99d50013a89e4558eee6d0fa4ffe2ee7b7b62eb92608b2251ac31396a718f9b34978888789042b790a30100000003515f342be6b6824a913eb7a57b03cb1ee7bfb4de02f2f65fe8a4e97baa7766ddb353a82a8a25c49dc63778cd9fe96173f12a2bc77f3682f4c4448f98f1df82c75234a100000003515f351f85760d6ab567465aadc2f180af9eae3800e6958fec96aef53fd8a7b195d7c000c6267a0dd5cfc22b3fe804f53e266069c0e36f51885baec1e7e67650c62e170000000c515f41524954484d455449430d9d0f8ece2aa12012fa21e6e5c859e97bd5704e5c122064a66051294bc5e04213f61f54a0ebdf6fee4d4a6ecf693478191de0c2899bcd8e86a636c8d3eff43400000003515f43224a99d02c86336737c8dd5b746c40d2be6aead8393889a76a18d664029096e90f7fe81adcc92a74350eada9622ac453f49ebac24a066a1f83b394df54dfa0130000000c515f46495845445f42415345060e8a013ed289c2f9fd7473b04f6594b138ddb4b4cf6b901622a14088f04b8d2c83ff74fce56e3d5573b99c7b26d85d5046ce0c6559506acb7a675e7713eb3a00000007515f4c4f4749430721a91cb8da4b917e054f72147e1760cfe0ef3d45090ac0f4961d84ec1996961a25e787b26bd8b50b1a99450f77a424a83513c2b33af268cd253b0587ff50c700000003515f4d05dbd8623b8652511e1eb38d38887a69eceb082f807514f09e127237c5213b401b9325b48c6c225968002318095f89d0ef9cf629b2b7f0172e03bc39aacf6ed800000007515f52414e474504b57a3805e41df328f5ca9aefa40fad5917391543b7b65c6476e60b8f72e9ad07c92f3b3e11c8feae96dedc4b14a6226ef3201244f37cfc1ee5b96781f48d2b000000075349474d415f3125001d1954a18571eaa007144c5a567bb0d2be4def08a8be918b8c05e3b27d312c59ed41e09e144eab5de77ca89a2fd783be702a47c951d3112e3de02ce6e47c000000075349474d415f3223994e6a23618e60fa01c449a7ab88378709197e186d48d604bfb6931ffb15ad11c5ec7a0700570f80088fd5198ab5d5c227f2ad2a455a6edeec024156bb7beb000000075349474d415f3300cda5845f23468a13275d18bddae27c6bb189cf9aa95b6a03a0cb6688c7e8d829639b45cf8607c525cc400b55ebf90205f2f378626dc3406cc59b2d1b474fba000000075349474d415f342d299e7928496ea2d37f10b43afd6a80c90a33b483090d18069ffa275eedb2fc2f82121e8de43dc036d99b478b6227ceef34248939987a19011f065d8b5cef5c0000000010000000000000000100000002000000030000000400000005000000060000000700000008000000090000000a0000000b0000000c0000000d0000000e0000000f0000000021f11a5439c373c3d91f5e4910a93aa16f061a642d7d2706be18a85ca84956d92fdcf6231c3596a614cfaa28a09bc402a7a34bf9382931cfed022a77d99f3c8825d97e44dac3772af165e47e72c0e495c079c18217001478c687ec883706c41d090f534a8ef74012c96f8b4b842a0107e0ab6b043ab130d394cdad8f21518c9e0906bca10001001c94912f5dcbc89ea72a8a498cdf68c5b74512b32b5ebc27dfd044cde2c63696090f534a8ef74012c96f8b4b842a0107e0ab6b043ab130d394cdad8f21518c9e00000000000000000000000000000000000000000000000000000000000000000906bca1000000000000021124bf00bac5cd7fc8570fe0e40c34b8d093801a155d53e0b478d960b3a424810000000000000000000000000000000000000000000000000000000000007a6a0000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000200000000000000000000000000000000e3b0c44298fc1c149afbf4c8996fb9240000000000000000000000000000000027ae41e4649b934ca495991b7852b85500000000000000000000000000000000e3b0c44298fc1c149afbf4c8996fb9240000000000000000000000000000000027ae41e4649b934ca495991b7852b85500000000000000000000000000000000000000000000000000000000000000040000000000000000000000000000000000000000000000000000000000000004022bbfcfff87e5cec76010d9f01f47f9e9ae40bf72ef1b34a3c4a78f03a3192f1e2d3f51150dda356221f55860f43e99f4199ce2f23fbff0190d21de72fb3b5c20fb0324db33f18d703e414e318ae4ed60d47ed0024a7c2e3818b873a999caa01864fcdaa80ff2719154fa7c8a9050662972707168d69eac9db6fd3110829f802d0a2756971c93ddc1b4fb85b0a38f561dde6dbed15c19fcb90294fde666ae9000000000000000000000000000000000000000000000000000000000000000001ed250ed73db6e70805c4efcf0056e8695b79cd3ba418e827c184dee6c6fb0e0052c36dbeff3b6ce555a7c8ee06a54e7bf9b38302e5bf265b62966be3ecc38800000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000007a69000000000000000000000000000000000000000000000000000000000000000100000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000200000800000000740000000f00000003515f3109623eb3c25aa5b16a1a79fd558bac7a7ce62c4560a8c537c77ce80dd339128d1d37b6582ee9e6df9567efb64313471dfa18f520f9ce53161b50dbf7731bc5f900000003515f322bc4cce83a486a92c92fd59bd84e0f92595baa639fc2ed86b00ffa0dfded2a092a669a3bdb7a273a015eda494457cc7ed5236f26cee330c290d45a33b9daa94800000003515f332729426c008c085a81bd34d8ef12dd31e80130339ef99d50013a89e4558eee6d0fa4ffe2ee7b7b62eb92608b2251ac31396a718f9b34978888789042b790a30100000003515f342be6b6824a913eb7a57b03cb1ee7bfb4de02f2f65fe8a4e97baa7766ddb353a82a8a25c49dc63778cd9fe96173f12a2bc77f3682f4c4448f98f1df82c75234a100000003515f351f85760d6ab567465aadc2f180af9eae3800e6958fec96aef53fd8a7b195d7c000c6267a0dd5cfc22b3fe804f53e266069c0e36f51885baec1e7e67650c62e170000000c515f41524954484d455449430d9d0f8ece2aa12012fa21e6e5c859e97bd5704e5c122064a66051294bc5e04213f61f54a0ebdf6fee4d4a6ecf693478191de0c2899bcd8e86a636c8d3eff43400000003515f43224a99d02c86336737c8dd5b746c40d2be6aead8393889a76a18d664029096e90f7fe81adcc92a74350eada9622ac453f49ebac24a066a1f83b394df54dfa0130000000c515f46495845445f42415345060e8a013ed289c2f9fd7473b04f6594b138ddb4b4cf6b901622a14088f04b8d2c83ff74fce56e3d5573b99c7b26d85d5046ce0c6559506acb7a675e7713eb3a00000007515f4c4f4749430721a91cb8da4b917e054f72147e1760cfe0ef3d45090ac0f4961d84ec1996961a25e787b26bd8b50b1a99450f77a424a83513c2b33af268cd253b0587ff50c700000003515f4d05dbd8623b8652511e1eb38d38887a69eceb082f807514f09e127237c5213b401b9325b48c6c225968002318095f89d0ef9cf629b2b7f0172e03bc39aacf6ed800000007515f52414e474504b57a3805e41df328f5ca9aefa40fad5917391543b7b65c6476e60b8f72e9ad07c92f3b3e11c8feae96dedc4b14a6226ef3201244f37cfc1ee5b96781f48d2b000000075349474d415f3125001d1954a18571eaa007144c5a567bb0d2be4def08a8be918b8c05e3b27d312c59ed41e09e144eab5de77ca89a2fd783be702a47c951d3112e3de02ce6e47c000000075349474d415f3223994e6a23618e60fa01c449a7ab88378709197e186d48d604bfb6931ffb15ad11c5ec7a0700570f80088fd5198ab5d5c227f2ad2a455a6edeec024156bb7beb000000075349474d415f3300cda5845f23468a13275d18bddae27c6bb189cf9aa95b6a03a0cb6688c7e8d829639b45cf8607c525cc400b55ebf90205f2f378626dc3406cc59b2d1b474fba000000075349474d415f342d299e7928496ea2d37f10b43afd6a80c90a33b483090d18069ffa275eedb2fc2f82121e8de43dc036d99b478b6227ceef34248939987a19011f065d8b5cef5c0000000010000000000000000100000002000000030000000400000005000000060000000700000008000000090000000a0000000b0000000c0000000d0000000e0000000f000000000000000000000000000000000000000000000000000000000000000722a1419dd08e208cd862bb66fb009fa540fb7178d01108f79eb78a891064685603f30687851ce0bc4df8e4fa8a5809643e9ae7f752a3ec1e3c120b251036c92e14ae899cd34041169f2476b70040373713d6eb363e74dca7f7f70f36d286b92f044b59fe1a64065611c9ec171fc760af4337fd13bbb833a9b021cfdde27a7f621a9fdb505152f9c2baaffe4a30ee80775b58ebf8c2dde76435835b085c6f70ca0000000000000000000000000000000000000000000000000000000000000004000000000000000000000000000000000000000000000000000000000000000027b1d0839a5b23baf12a8d195b18ac288fcf401afb2f70b8a4b529ede5fa9fed0136d5c45fb886add133b29521494cbb3b13851ed146d0bfa6448d71dacb7c170bcd1f91cf7bdd471d0a30c58c4706f3fdab3807a954b8f5b5e3bfec87d001bb06e62084ee7b602fe9abc15632dda3269f56fb0c6e12519a2eb2ec897091919d03c9e2e67178ac638746f068907e6677b4cc7a9592ef234ab6ab518f17efffa015d28cad4c0736decea8997cb324cf0a0e0602f4d74472cd977bce2c8dd9923f268ed1e1c94c3a45a14db4108bc306613a1c23fab68e0466a002dfb0a3f8d2ab0cd8d5695bc2dde99dd531671f76f1482f14ddba8eeca7cb9686d4a62359c257047fbb7eb974155702149e58ea6ad91f4c6e953e693db35e953e250d8ceac9a900c5ae2526e665e2c7c698c11a06098b7159f720606d50e7660deb55758b0b022ced19489ab456b8b6c424594cdbbae59c36dfdd4c4621c4032da2d8a9674be51df5a245ffc1da14b46fe56a605f2a47b1cff1592bab4f66cfe5dfe990af6ab52871d090615d14eadb52228c635c90e0adf31176f0814f6525c23e7d7b318c931a2b85ff013d4b2b25074297c7e44aa61f4836d0862b36db2e6ce2b5542f9ea9177b9a10bbee32f77c719c6f8d071a18476cbeb021e155c642bbf93c716ce94300000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000 \ No newline at end of file +0000000000000000000000000000000000000000000000000000000000000001000000000000000000000000000000000000000000000000000000000000000200000000000000000000000000000000000000000000000000000000000000010000000000000000000000000000000000000000000000000000000000000002000000000000001000000bd300000bd400000bd500000bd600000bd700000bd800000bd900000bda00000bdb00000bdc00000bdd00000bde00000bdf00000be000000be100000be2000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000220d5282f748ff6c16ac0a8804a6d67d5cc4a2207633e61314141faf1c60bf31000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000001f54d1b2a871d2ea3f25647e33b790a25ced443332a2a8a5948bab72fcec459a127382a6c752af8f4b2f35ef81f5f323d64f7a730ee3192f7d97352a65fa041400000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000d3735899d9fa7162447ca631f0ba2cd500000000000000000000000000000000eb57d0965a756d78291da33072610eb200000000000000000000000000000000d3735899d9fa7162447ca631f0ba2cd500000000000000000000000000000000eb57d0965a756d78291da33072610eb2000000000000000000000000000000000000000000000000000000000000000800000000000000000000000000000000000000000000000000000000000000080000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000ce1713e61738687899193cfe7c3d262283f7e7a6903c3a5ed92719de0197e2e00000003d29bafba6a84758ce122026607aada927486814f7037884e8d60b3f0360aa95d1864fcdaa80ff2719154fa7c8a9050662972707168d69eac9db6fd3110829f800000003004106f78d5bcecde87829f91cea7dc13e8c60f9387008d95dabf04d1bc1343a8000001800400c9ba05d915e7a1e9d3f20dcfb54fed387c474d0669764e40ecca4344770e0000020021168779db9ad2ab83f1b68c90220309ef9a867cb471734eac87a4da0907b4c4000000061ed250ed73db6e70805c4efcf0056e8695b79cd3ba418e827c184dee6c6fb0e0000000800000000000000000000000000000000000000000000000000000000000007a69000000000000000000000000000000000000000000000000000000000000000100000000000000000000000000000000000000000000000000000000000000030000000000000000000000000000000000000000000000000000000065b75af80000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000007a69000000000000000000000000000000000000000000000000000000000000000101000000000000000200000800000000740000000f00000003515f3109623eb3c25aa5b16a1a79fd558bac7a7ce62c4560a8c537c77ce80dd339128d1d37b6582ee9e6df9567efb64313471dfa18f520f9ce53161b50dbf7731bc5f900000003515f322bc4cce83a486a92c92fd59bd84e0f92595baa639fc2ed86b00ffa0dfded2a092a669a3bdb7a273a015eda494457cc7ed5236f26cee330c290d45a33b9daa94800000003515f332729426c008c085a81bd34d8ef12dd31e80130339ef99d50013a89e4558eee6d0fa4ffe2ee7b7b62eb92608b2251ac31396a718f9b34978888789042b790a30100000003515f342be6b6824a913eb7a57b03cb1ee7bfb4de02f2f65fe8a4e97baa7766ddb353a82a8a25c49dc63778cd9fe96173f12a2bc77f3682f4c4448f98f1df82c75234a100000003515f351f85760d6ab567465aadc2f180af9eae3800e6958fec96aef53fd8a7b195d7c000c6267a0dd5cfc22b3fe804f53e266069c0e36f51885baec1e7e67650c62e170000000c515f41524954484d455449430d9d0f8ece2aa12012fa21e6e5c859e97bd5704e5c122064a66051294bc5e04213f61f54a0ebdf6fee4d4a6ecf693478191de0c2899bcd8e86a636c8d3eff43400000003515f43224a99d02c86336737c8dd5b746c40d2be6aead8393889a76a18d664029096e90f7fe81adcc92a74350eada9622ac453f49ebac24a066a1f83b394df54dfa0130000000c515f46495845445f42415345060e8a013ed289c2f9fd7473b04f6594b138ddb4b4cf6b901622a14088f04b8d2c83ff74fce56e3d5573b99c7b26d85d5046ce0c6559506acb7a675e7713eb3a00000007515f4c4f4749430721a91cb8da4b917e054f72147e1760cfe0ef3d45090ac0f4961d84ec1996961a25e787b26bd8b50b1a99450f77a424a83513c2b33af268cd253b0587ff50c700000003515f4d05dbd8623b8652511e1eb38d38887a69eceb082f807514f09e127237c5213b401b9325b48c6c225968002318095f89d0ef9cf629b2b7f0172e03bc39aacf6ed800000007515f52414e474504b57a3805e41df328f5ca9aefa40fad5917391543b7b65c6476e60b8f72e9ad07c92f3b3e11c8feae96dedc4b14a6226ef3201244f37cfc1ee5b96781f48d2b000000075349474d415f3125001d1954a18571eaa007144c5a567bb0d2be4def08a8be918b8c05e3b27d312c59ed41e09e144eab5de77ca89a2fd783be702a47c951d3112e3de02ce6e47c000000075349474d415f3223994e6a23618e60fa01c449a7ab88378709197e186d48d604bfb6931ffb15ad11c5ec7a0700570f80088fd5198ab5d5c227f2ad2a455a6edeec024156bb7beb000000075349474d415f3300cda5845f23468a13275d18bddae27c6bb189cf9aa95b6a03a0cb6688c7e8d829639b45cf8607c525cc400b55ebf90205f2f378626dc3406cc59b2d1b474fba000000075349474d415f342d299e7928496ea2d37f10b43afd6a80c90a33b483090d18069ffa275eedb2fc2f82121e8de43dc036d99b478b6227ceef34248939987a19011f065d8b5cef5c0000000010000000000000000100000002000000030000000400000005000000060000000700000008000000090000000a0000000b0000000c0000000d0000000e0000000f0000000011bdec674da553635ab2022cbc776a28826aa7738a2a8dca664b7caaedab521b2b6cd1729933c1be054cb0545ebca6dd25faf876b0640567166eb00216f86b7610eb1e5360f7d2b509597d29866d71ac94fe305614ea7ba5de6997ee4fc9673611b67234c4f38be773ffbb9530880848efd40e57e1528a972aa69985406991990906bca1000100127382a6c752af8f4b2f35ef81f5f323d64f7a730ee3192f7d97352a65fa041411b67234c4f38be773ffbb9530880848efd40e57e1528a972aa699854069919900000000000000000000000000000000000000000000000000000000000000000906bca1000000000000031124bf00bac5cd7fc8570fe0e40c34b8d093801a155d53e0b478d960b3a424810000000000000000000000000000000000000000000000000000000000007a6a0000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000300000000000000000000000000000000e3b0c44298fc1c149afbf4c8996fb9240000000000000000000000000000000027ae41e4649b934ca495991b7852b85500000000000000000000000000000000e3b0c44298fc1c149afbf4c8996fb9240000000000000000000000000000000027ae41e4649b934ca495991b7852b855000000000000000000000000000000000000000000000000000000000000000400000000000000000000000000000000000000000000000000000000000000040ce1713e61738687899193cfe7c3d262283f7e7a6903c3a5ed92719de0197e2e00000003d29bafba6a84758ce122026607aada927486814f7037884e8d60b3f0360aa95d1864fcdaa80ff2719154fa7c8a9050662972707168d69eac9db6fd3110829f800000003004106f78d5bcecde87829f91cea7dc13e8c60f9387008d95dabf04d1bc1343a8000001800400c9ba05d915e7a1e9d3f20dcfb54fed387c474d0669764e40ecca4344770e0000020021168779db9ad2ab83f1b68c90220309ef9a867cb471734eac87a4da0907b4c4000000061ed250ed73db6e70805c4efcf0056e8695b79cd3ba418e827c184dee6c6fb0e0000000800000000000000000000000000000000000000000000000000000000000007a69000000000000000000000000000000000000000000000000000000000000000100000000000000000000000000000000000000000000000000000000000000030000000000000000000000000000000000000000000000000000000065b75af80000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000007a69000000000000000000000000000000000000000000000000000000000000000100000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000200000800000000740000000f00000003515f3109623eb3c25aa5b16a1a79fd558bac7a7ce62c4560a8c537c77ce80dd339128d1d37b6582ee9e6df9567efb64313471dfa18f520f9ce53161b50dbf7731bc5f900000003515f322bc4cce83a486a92c92fd59bd84e0f92595baa639fc2ed86b00ffa0dfded2a092a669a3bdb7a273a015eda494457cc7ed5236f26cee330c290d45a33b9daa94800000003515f332729426c008c085a81bd34d8ef12dd31e80130339ef99d50013a89e4558eee6d0fa4ffe2ee7b7b62eb92608b2251ac31396a718f9b34978888789042b790a30100000003515f342be6b6824a913eb7a57b03cb1ee7bfb4de02f2f65fe8a4e97baa7766ddb353a82a8a25c49dc63778cd9fe96173f12a2bc77f3682f4c4448f98f1df82c75234a100000003515f351f85760d6ab567465aadc2f180af9eae3800e6958fec96aef53fd8a7b195d7c000c6267a0dd5cfc22b3fe804f53e266069c0e36f51885baec1e7e67650c62e170000000c515f41524954484d455449430d9d0f8ece2aa12012fa21e6e5c859e97bd5704e5c122064a66051294bc5e04213f61f54a0ebdf6fee4d4a6ecf693478191de0c2899bcd8e86a636c8d3eff43400000003515f43224a99d02c86336737c8dd5b746c40d2be6aead8393889a76a18d664029096e90f7fe81adcc92a74350eada9622ac453f49ebac24a066a1f83b394df54dfa0130000000c515f46495845445f42415345060e8a013ed289c2f9fd7473b04f6594b138ddb4b4cf6b901622a14088f04b8d2c83ff74fce56e3d5573b99c7b26d85d5046ce0c6559506acb7a675e7713eb3a00000007515f4c4f4749430721a91cb8da4b917e054f72147e1760cfe0ef3d45090ac0f4961d84ec1996961a25e787b26bd8b50b1a99450f77a424a83513c2b33af268cd253b0587ff50c700000003515f4d05dbd8623b8652511e1eb38d38887a69eceb082f807514f09e127237c5213b401b9325b48c6c225968002318095f89d0ef9cf629b2b7f0172e03bc39aacf6ed800000007515f52414e474504b57a3805e41df328f5ca9aefa40fad5917391543b7b65c6476e60b8f72e9ad07c92f3b3e11c8feae96dedc4b14a6226ef3201244f37cfc1ee5b96781f48d2b000000075349474d415f3125001d1954a18571eaa007144c5a567bb0d2be4def08a8be918b8c05e3b27d312c59ed41e09e144eab5de77ca89a2fd783be702a47c951d3112e3de02ce6e47c000000075349474d415f3223994e6a23618e60fa01c449a7ab88378709197e186d48d604bfb6931ffb15ad11c5ec7a0700570f80088fd5198ab5d5c227f2ad2a455a6edeec024156bb7beb000000075349474d415f3300cda5845f23468a13275d18bddae27c6bb189cf9aa95b6a03a0cb6688c7e8d829639b45cf8607c525cc400b55ebf90205f2f378626dc3406cc59b2d1b474fba000000075349474d415f342d299e7928496ea2d37f10b43afd6a80c90a33b483090d18069ffa275eedb2fc2f82121e8de43dc036d99b478b6227ceef34248939987a19011f065d8b5cef5c0000000010000000000000000100000002000000030000000400000005000000060000000700000008000000090000000a0000000b0000000c0000000d0000000e0000000f000000000000000000000000000000000000000000000000000000000000000713473e6323882931ad1bf572d3e0415dfc19a50a98370ceee0528cd48002552c04c5bb10166a13ef4eec63ef2644fdcd51e61f605ead579836875a1d448d168113786e12c99e4d451092701838d2c7b03c401c5942ee9d5ec0231ed2bd62db24087ff6f8cd6c0f3b409ea64ebe815f84941f75d1582c206558d39d4a2706526a1a9fdb505152f9c2baaffe4a30ee80775b58ebf8c2dde76435835b085c6f70ca0000000000000000000000000000000000000000000000000000000000000004000000000000000000000000000000000000000000000000000000000000000027b1d0839a5b23baf12a8d195b18ac288fcf401afb2f70b8a4b529ede5fa9fed24ffab2824ba3b62f77f4e07d2ab14316fe89e3ffd8305879fe88d467bd1a6960bcd1f91cf7bdd471d0a30c58c4706f3fdab3807a954b8f5b5e3bfec87d001bb06e62084ee7b602fe9abc15632dda3269f56fb0c6e12519a2eb2ec897091919d03c9e2e67178ac638746f068907e6677b4cc7a9592ef234ab6ab518f17efffa015d28cad4c0736decea8997cb324cf0a0e0602f4d74472cd977bce2c8dd9923f268ed1e1c94c3a45a14db4108bc306613a1c23fab68e0466a002dfb0a3f8d2ab0cd8d5695bc2dde99dd531671f76f1482f14ddba8eeca7cb9686d4a62359c257047fbb7eb974155702149e58ea6ad91f4c6e953e693db35e953e250d8ceac9a900c5ae2526e665e2c7c698c11a06098b7159f720606d50e7660deb55758b0b022ced19489ab456b8b6c424594cdbbae59c36dfdd4c4621c4032da2d8a9674be51df5a245ffc1da14b46fe56a605f2a47b1cff1592bab4f66cfe5dfe990af6ab52871d090615d14eadb52228c635c90e0adf31176f0814f6525c23e7d7b318c931a2b85ff013d4b2b25074297c7e44aa61f4836d0862b36db2e6ce2b5542f9ea9177b9a10bbee32f77c719c6f8d071a18476cbeb021e155c642bbf93c716ce94300000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000 \ No newline at end of file diff --git a/yarn-project/noir-protocol-circuits/src/fixtures/nested-call-private-kernel-ordering.hex b/yarn-project/noir-protocol-circuits/src/fixtures/nested-call-private-kernel-ordering.hex new file mode 100644 index 00000000000..1617d47c311 --- /dev/null +++ b/yarn-project/noir-protocol-circuits/src/fixtures/nested-call-private-kernel-ordering.hex @@ -0,0 +1 @@ +0000000000000000000000000000000000000000000000000000000000000001000000000000000000000000000000000000000000000000000000000000000200000000000000000000000000000000000000000000000000000000000000010000000000000000000000000000000000000000000000000000000000000002000000000000001000000bd300000bd400000bd500000bd600000bd700000bd800000bd900000bda00000bdb00000bdc00000bdd00000bde00000bdf00000be000000be100000be200000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000001f40b93b3cae3d7b472da36aa65fcc8cd50ef919fcc2d588bf8caf93e54885860000000000000000000000000000000000000000000000000000000000000003000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000253052e1354b1c85bbb40d22647666af9c9dd750b1f2b1e8add9001532aed496000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000cae016c7c6373183b0a9ca09ea854c518be30a7a0c885ed01eba2a20463b3eb000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000021a2cf3bccd556f855391c894991a4b5f88db06ab68928ab29b847effb15e85ea000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000010000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000a5fc2cba691ca1784b483d43f4c9d6be00000000000000000000000000000000a25af60b9a061f4b358a9d4da5198f0b000000000000000000000000000000001c9ecec90e28d2461650418635878a5c0000000000000000000000000000000091e49f47586ecf75f2b0cbb94e89711200000000000000000000000000000000000000000000000000000000000000f800000000000000000000000000000000000000000000000000000000000000041c66eceef39c40e60d81ff432d80f4614d6eb8abef3aec4d0d733442e53217080000000000000000000000000000000000000000000000000000000000000000083a2a57ecc0ac76be57919bc82658da41fed7c9aac4575a2dc9d33da76b3d9300000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000001864fcdaa80ff2719154fa7c8a9050662972707168d69eac9db6fd3110829f800000000016642d9ccd8346c403aa4c3fa451178b22534a27035cdaa6ec34ae53b29c50cb000000000bcfa3e9f1a8922ee92c6dc964d6595907c1804a86753774322b468f69d4f278000000001864fcdaa80ff2719154fa7c8a9050662972707168d69eac9db6fd3110829f80000000001ed250ed73db6e70805c4efcf0056e8695b79cd3ba418e827c184dee6c6fb0e00000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000011c128c66042b3f3ba119536e6556ad6fbb3cfd1c1b47f7f17c38f5cd67f433ee1a79cf88dfacd9c37194cce8e5cd80e974e48cbea9aa4cfd687d9b1d3b1e0f43038021824fbd98bb0e388b0efe18f72e9350f7456481714539ba583de37113ce083a2a57ecc0ac76be57919bc82658da41fed7c9aac4575a2dc9d33da76b3d9329eb51554eea81fd16dff9503d8b30c86b84e472b555ab262f03894e08c5d45800000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000007a69000000000000000000000000000000000000000000000000000000000000000101000000000000000200000800000000740000000f00000003515f3109623eb3c25aa5b16a1a79fd558bac7a7ce62c4560a8c537c77ce80dd339128d1d37b6582ee9e6df9567efb64313471dfa18f520f9ce53161b50dbf7731bc5f900000003515f322bc4cce83a486a92c92fd59bd84e0f92595baa639fc2ed86b00ffa0dfded2a092a669a3bdb7a273a015eda494457cc7ed5236f26cee330c290d45a33b9daa94800000003515f332729426c008c085a81bd34d8ef12dd31e80130339ef99d50013a89e4558eee6d0fa4ffe2ee7b7b62eb92608b2251ac31396a718f9b34978888789042b790a30100000003515f342be6b6824a913eb7a57b03cb1ee7bfb4de02f2f65fe8a4e97baa7766ddb353a82a8a25c49dc63778cd9fe96173f12a2bc77f3682f4c4448f98f1df82c75234a100000003515f351f85760d6ab567465aadc2f180af9eae3800e6958fec96aef53fd8a7b195d7c000c6267a0dd5cfc22b3fe804f53e266069c0e36f51885baec1e7e67650c62e170000000c515f41524954484d455449430d9d0f8ece2aa12012fa21e6e5c859e97bd5704e5c122064a66051294bc5e04213f61f54a0ebdf6fee4d4a6ecf693478191de0c2899bcd8e86a636c8d3eff43400000003515f43224a99d02c86336737c8dd5b746c40d2be6aead8393889a76a18d664029096e90f7fe81adcc92a74350eada9622ac453f49ebac24a066a1f83b394df54dfa0130000000c515f46495845445f42415345060e8a013ed289c2f9fd7473b04f6594b138ddb4b4cf6b901622a14088f04b8d2c83ff74fce56e3d5573b99c7b26d85d5046ce0c6559506acb7a675e7713eb3a00000007515f4c4f4749430721a91cb8da4b917e054f72147e1760cfe0ef3d45090ac0f4961d84ec1996961a25e787b26bd8b50b1a99450f77a424a83513c2b33af268cd253b0587ff50c700000003515f4d05dbd8623b8652511e1eb38d38887a69eceb082f807514f09e127237c5213b401b9325b48c6c225968002318095f89d0ef9cf629b2b7f0172e03bc39aacf6ed800000007515f52414e474504b57a3805e41df328f5ca9aefa40fad5917391543b7b65c6476e60b8f72e9ad07c92f3b3e11c8feae96dedc4b14a6226ef3201244f37cfc1ee5b96781f48d2b000000075349474d415f3125001d1954a18571eaa007144c5a567bb0d2be4def08a8be918b8c05e3b27d312c59ed41e09e144eab5de77ca89a2fd783be702a47c951d3112e3de02ce6e47c000000075349474d415f3223994e6a23618e60fa01c449a7ab88378709197e186d48d604bfb6931ffb15ad11c5ec7a0700570f80088fd5198ab5d5c227f2ad2a455a6edeec024156bb7beb000000075349474d415f3300cda5845f23468a13275d18bddae27c6bb189cf9aa95b6a03a0cb6688c7e8d829639b45cf8607c525cc400b55ebf90205f2f378626dc3406cc59b2d1b474fba000000075349474d415f342d299e7928496ea2d37f10b43afd6a80c90a33b483090d18069ffa275eedb2fc2f82121e8de43dc036d99b478b6227ceef34248939987a19011f065d8b5cef5c0000000010000000000000000100000002000000030000000400000005000000060000000700000008000000090000000a0000000b0000000c0000000d0000000e0000000f00000000034d43a95810c3b6c7de6927c24ba6ba76ea5462970cf6448b2ffcb50b16d9740f42588b4f517ce4c03742fd159e74989afe0487b7c41186d44119ac595ae26818621d1ba540be64e4282b21de5f2c4752051fdefd918674094d4fa97fc75e671f40b93b3cae3d7b472da36aa65fcc8cd50ef919fcc2d588bf8caf93e54885860000000000000000000000000000000000000000000000000000000000000003000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000100000002000000030000000400000005000000060000000700000008000000090000000a0000000b0000000c0000000d0000000e0000000f000000100000001100000012000000130000001400000015000000160000001700000018000000190000001a0000001b0000001c0000001d0000001e0000001f000000200000002100000022000000230000002400000025000000260000002700000028000000290000002a0000002b0000002c0000002d0000002e0000002f000000300000003100000032000000330000003400000035000000360000003700000038000000390000003a0000003b0000003c0000003d0000003e0000003f00000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000253052e1354b1c85bbb40d22647666af9c9dd750b1f2b1e8add9001532aed496000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000001a2cf3bccd556f855391c894991a4b5f88db06ab68928ab29b847effb15e85ea000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000010cae016c7c6373183b0a9ca09ea854c518be30a7a0c885ed01eba2a20463b3eb00000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000002000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000200000001000000030000000400000005000000060000000700000008000000090000000a0000000b0000000c0000000d0000000e0000000f000000100000001100000012000000130000001400000015000000160000001700000018000000190000001a0000001b0000001c0000001d0000001e0000001f000000200000002100000022000000230000002400000025000000260000002700000028000000290000002a0000002b0000002c0000002d0000002e0000002f000000300000003100000032000000330000003400000035000000360000003700000038000000390000003a0000003b0000003c0000003d0000003e0000003f00000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000 \ No newline at end of file diff --git a/yarn-project/noir-protocol-circuits/src/index.test.ts b/yarn-project/noir-protocol-circuits/src/index.test.ts index 1be172d23ac..d5aeb242eec 100644 --- a/yarn-project/noir-protocol-circuits/src/index.test.ts +++ b/yarn-project/noir-protocol-circuits/src/index.test.ts @@ -1,60 +1,24 @@ import { - AggregationObject, AztecAddress, - BlockHeader, - CONTRACT_TREE_HEIGHT, - CallContext, - CallRequest, - CombinedAccumulatedData, - CombinedConstantData, ContractDeploymentData, EthAddress, - FUNCTION_TREE_HEIGHT, FunctionData, FunctionLeafPreimage, FunctionSelector, - KernelCircuitPublicInputs, - MAX_NEW_COMMITMENTS_PER_CALL, - MAX_NEW_COMMITMENTS_PER_TX, - MAX_NEW_L2_TO_L1_MSGS_PER_CALL, - MAX_NEW_NULLIFIERS_PER_CALL, - MAX_NEW_NULLIFIERS_PER_TX, - MAX_OPTIONALLY_REVEALED_DATA_LENGTH_PER_TX, - MAX_PRIVATE_CALL_STACK_LENGTH_PER_CALL, - MAX_PRIVATE_CALL_STACK_LENGTH_PER_TX, - MAX_PUBLIC_CALL_STACK_LENGTH_PER_CALL, - MAX_PUBLIC_CALL_STACK_LENGTH_PER_TX, - MAX_PUBLIC_DATA_READS_PER_TX, - MAX_PUBLIC_DATA_UPDATE_REQUESTS_PER_TX, - MAX_READ_REQUESTS_PER_CALL, - MAX_READ_REQUESTS_PER_TX, - MembershipWitness, - NewContractData, - OptionallyRevealedData, Point, - PreviousKernelData, - PrivateCallData, - PrivateCallStackItem, - PrivateCircuitPublicInputs, PrivateKernelInputsInit, PrivateKernelInputsInner, PrivateKernelInputsOrdering, PublicCallStackItem, PublicCircuitPublicInputs, - PublicDataRead, - PublicDataUpdateRequest, - RETURN_VALUES_LENGTH, - ReadRequestMembershipWitness, SideEffect, - SideEffectLinkedToNoteHash, TxContext, TxRequest, - VK_TREE_HEIGHT, - VerificationKey, - makeEmptyProof, + computeContractAddressFromInstance, + computeContractAddressFromPartial, + computePublicKeysHash, } from '@aztec/circuits.js'; -import { computeCompleteAddress, computeFunctionLeaf, computeTxHash } from '@aztec/circuits.js/abis'; -import { makeTuple } from '@aztec/foundation/array'; +import { computeFunctionLeaf, computeTxHash } from '@aztec/circuits.js/abis'; import { Fr } from '@aztec/foundation/fields'; import { DebugLogger, createDebugLogger } from '@aztec/foundation/log'; import { fileURLToPath } from '@aztec/foundation/url'; @@ -64,225 +28,32 @@ import { dirname, resolve } from 'path'; import { executeInit, executeInner, executeOrdering } from './index.js'; -function _makeEmptyReadRequest() { - return makeTuple(MAX_READ_REQUESTS_PER_TX, () => SideEffect.empty()); -} - describe('Private kernel', () => { let logger: DebugLogger; + beforeAll(() => { logger = createDebugLogger('noir-private-kernel'); }); - // Taken from e2e_nested_contract => performs nested calls => first deployment - it('Executes private kernel init circuit for a contract deployment', async () => { + // Taken from e2e_nested_contract => performs nested calls => first init (corresponds to deployment) + // To regenerate fixture data run the following on the yarn-project/e2e folder + // AZTEC_GENERATE_TEST_DATA=1 yarn test e2e_nested_contract -t 'performs nested calls' + // TODO(@spalladino) Re-enable this test + it.skip('Executes private kernel init circuit for a contract deployment', async () => { logger('Initialized Noir instance with private kernel init circuit'); - const txOrigin = AztecAddress.fromString('0x25e2c017f5da1f994401e61d26be435e3cfa26efee784c6b4e947f7651bd4104'); - const argsHash = Fr.fromString('0x113c609cd625d5afd9f09daa2031011af161334e7508be0b1310ad2b7ff166af'); - const deployerPubKey = new Point( - Fr.fromString('0x1de02ddacac6d2f427e5f0d3ce59d7294f146280455dd4c582254e0b4c254b23'), - Fr.fromString('0x23cd081dfe9c0d1873b65a36a08858e73a9b30d0339e94c4915d7110e2f07ecd'), - ); - const contractDeploymentData = new ContractDeploymentData( - deployerPubKey, - Fr.fromString('0x0aefd90a69a643324c7bf0a9bd3b23ada090ad883773fdf0b0ad52a9f7d6f1f6'), - Fr.fromString('0x0cad3b5391e40af8743e1053c015e16abac6100a8b917512c083cb4cbb8ccc03'), - Fr.fromString('0x1ec59b0313fa504302c3336fc911d688edae67c4fbf229d68c7f36ed8797045c'), - EthAddress.ZERO, - ); - const selector = FunctionSelector.fromString('0xaf9f8c44'); - const functionData = new FunctionData(selector, false, true, true); - const txContext = new TxContext(false, false, true, contractDeploymentData, Fr.ZERO, Fr.ZERO); - const txRequest = new TxRequest(txOrigin, functionData, argsHash, txContext); - - const contractAddress = AztecAddress.fromString( - '0x25e2c017f5da1f994401e61d26be435e3cfa26efee784c6b4e947f7651bd4104', - ); - - const newCommitments = makeTuple(MAX_NEW_COMMITMENTS_PER_CALL, () => SideEffect.empty()); - newCommitments[0] = new SideEffect( - Fr.fromString('0x0aced88c953b70873e4a33dde4620dc43a709c15013c46c60d167de8e1c32315'), - Fr.ZERO, - ); - - const newNullifiers = makeTuple(MAX_NEW_NULLIFIERS_PER_CALL, () => SideEffectLinkedToNoteHash.empty()); - newNullifiers[0] = new SideEffectLinkedToNoteHash( - Fr.fromString('0x03579f468b2283611cc4d7adfbb93b8a4815d93ac0b1e1d11dace012cf73c7aa'), - Fr.ZERO, - Fr.ZERO, - ); - - const callContext = new CallContext(AztecAddress.ZERO, contractAddress, Fr.ZERO, selector, false, false, true, 0); - - const blockHeader = new BlockHeader( - Fr.fromString('0x16642d9ccd8346c403aa4c3fa451178b22534a27035cdaa6ec34ae53b29c50cb'), - Fr.fromString('0x0bcfa3e9f1a8922ee92c6dc964d6595907c1804a86753774322b468f69d4f278'), - Fr.fromString('0x1864fcdaa80ff2719154fa7c8a9050662972707168d69eac9db6fd3110829f80'), - Fr.fromString('0x1864fcdaa80ff2719154fa7c8a9050662972707168d69eac9db6fd3110829f80'), - Fr.fromString('0x1759d221795419503f86c032e8f8762f2b739e74835099584b6531f5f27390fe'), - Fr.ZERO, // TODO(#3441) - Fr.fromString('0x0ccaafdc9c353743970d4e305ae73641ce694f07db67886d2769c9ed88e969d8'), - Fr.fromString('0x200569267c0f73ac89aaa414239398db9445dd4ad3a8cf37015cd55b8d4c5e8d'), - ); - - const appPublicInputs = new PrivateCircuitPublicInputs( - callContext, - argsHash, - makeTuple(RETURN_VALUES_LENGTH, () => Fr.ZERO), - makeTuple(MAX_READ_REQUESTS_PER_CALL, () => SideEffect.empty()), - newCommitments, - newNullifiers, - makeTuple(MAX_PRIVATE_CALL_STACK_LENGTH_PER_CALL, () => Fr.ZERO), - makeTuple(MAX_PUBLIC_CALL_STACK_LENGTH_PER_CALL, () => Fr.ZERO), - makeTuple(MAX_NEW_L2_TO_L1_MSGS_PER_CALL, () => Fr.ZERO), - Fr.ZERO, - [Fr.fromString('0x9cc0744c0dde14f24854659b052ffb7e'), Fr.fromString('0x28120e19a5cc9ec344f3d6d41b6fada2')], - [Fr.fromString('0xe3b0c44298fc1c149afbf4c8996fb924'), Fr.fromString('0x27ae41e4649b934ca495991b7852b855')], - Fr.fromString('0xf8'), - Fr.fromString('0x04'), - blockHeader, - contractDeploymentData, - Fr.ZERO, - Fr.ZERO, - ); + const filepath = resolve(dirname(fileURLToPath(import.meta.url)), './fixtures/nested-call-private-kernel-init.hex'); + const serialized = Buffer.from(readFileSync(filepath).toString(), 'hex'); + const kernelInputs = PrivateKernelInputsInit.fromBuffer(serialized); - const callStackItem = new PrivateCallStackItem(contractAddress, functionData, appPublicInputs, false); - - const privateCall = new PrivateCallData( - callStackItem, - makeTuple(MAX_PRIVATE_CALL_STACK_LENGTH_PER_CALL, () => CallRequest.empty()), - makeTuple(MAX_PUBLIC_CALL_STACK_LENGTH_PER_CALL, () => CallRequest.empty()), - makeEmptyProof(), - VerificationKey.makeFake(), - MembershipWitness.empty(FUNCTION_TREE_HEIGHT, 0n), - MembershipWitness.empty(CONTRACT_TREE_HEIGHT, 0n), - makeTuple(MAX_READ_REQUESTS_PER_CALL, () => ReadRequestMembershipWitness.empty(0n)), - Fr.ZERO, - Fr.ZERO, - ); - const kernelInputs = new PrivateKernelInputsInit(txRequest, privateCall); + // We check that the test data is for a contract deployment + expect(kernelInputs.txRequest.txContext.isContractDeploymentTx).toBe(true); const kernelOutputs = await executeInit(kernelInputs); expect(kernelOutputs).toMatchSnapshot(); }); - // Taken from e2e_nested_contract => performs nested calls => first ordering - it('Executes private kernel ordering after a deployment', async () => { - const contractAddress = AztecAddress.fromString( - '0x25e2c017f5da1f994401e61d26be435e3cfa26efee784c6b4e947f7651bd4104', - ); - - const deployerPubKey = new Point( - Fr.fromString('0x1de02ddacac6d2f427e5f0d3ce59d7294f146280455dd4c582254e0b4c254b23'), - Fr.fromString('0x23cd081dfe9c0d1873b65a36a08858e73a9b30d0339e94c4915d7110e2f07ecd'), - ); - - const contractDeploymentData = new ContractDeploymentData( - deployerPubKey, - Fr.fromString('0x0aefd90a69a643324c7bf0a9bd3b23ada090ad883773fdf0b0ad52a9f7d6f1f6'), - Fr.fromString('0x0cad3b5391e40af8743e1053c015e16abac6100a8b917512c083cb4cbb8ccc03'), - Fr.fromString('0x1ec59b0313fa504302c3336fc911d688edae67c4fbf229d68c7f36ed8797045c'), - EthAddress.ZERO, - ); - const txContext = new TxContext(false, false, true, contractDeploymentData, Fr.ZERO, Fr.ZERO); - - const newCommitments = makeTuple(MAX_NEW_COMMITMENTS_PER_TX, () => SideEffect.empty()); - newCommitments[0] = new SideEffect( - Fr.fromString('0x0aced88c953b70873e4a33dde4620dc43a709c15013c46c60d167de8e1c32315'), - Fr.ZERO, - ); - - const newNullifiers = makeTuple(MAX_NEW_NULLIFIERS_PER_TX, () => SideEffectLinkedToNoteHash.empty()); - const sortedNewNullifiers = makeTuple(MAX_NEW_NULLIFIERS_PER_TX, i => newNullifiers[i]); - - newNullifiers[0] = new SideEffectLinkedToNoteHash( - Fr.fromString('0x0faf656089e5a8d321b64f420fc008005736a0b4f0b8588891241392c82655b9'), - Fr.ZERO, - new Fr(1), - ); - newNullifiers[1] = new SideEffectLinkedToNoteHash( - Fr.fromString('0x4a5d6bc34e84c5a3d7a625a3772f4d2f84c7d46637691ef64ee2711e6c6202'), - Fr.ZERO, - new Fr(2), - ); - newNullifiers[2] = new SideEffectLinkedToNoteHash( - Fr.fromString('0x19085a4478c4aa3994d4a5935eaf5e0d58726a758d398a97f634df22d33d388a'), - Fr.ZERO, - Fr.ZERO, - ); - - sortedNewNullifiers[0] = newNullifiers[2]; - sortedNewNullifiers[1] = newNullifiers[0]; - sortedNewNullifiers[2] = newNullifiers[1]; - - const sortedNewNullifiersIndexes = makeTuple(MAX_NEW_NULLIFIERS_PER_TX, i => - sortedNewNullifiers.indexOf(newNullifiers[i]), - ); - - const combinedAccumulatedData = new CombinedAccumulatedData( - AggregationObject.makeFake(), - makeTuple(MAX_READ_REQUESTS_PER_TX, () => SideEffect.empty()), - newCommitments, - newNullifiers, - makeTuple(MAX_PRIVATE_CALL_STACK_LENGTH_PER_TX, () => CallRequest.empty()), - makeTuple(MAX_PUBLIC_CALL_STACK_LENGTH_PER_TX, () => CallRequest.empty()), - makeTuple(MAX_NEW_L2_TO_L1_MSGS_PER_CALL, () => new Fr(0n)), - [Fr.fromString('0x57ee9bb1264085ecf4ba8274b233cdc4'), Fr.fromString('0x8a8910cc6b93b4399a1ebd8fbfb405f8')], - [Fr.fromString('0x1c9ecec90e28d2461650418635878a5c'), Fr.fromString('0x91e49f47586ecf75f2b0cbb94e897112')], - Fr.fromString('0xf8'), - new Fr(4), - [ - new NewContractData( - contractAddress, - EthAddress.ZERO, - Fr.fromString('0x0cad3b5391e40af8743e1053c015e16abac6100a8b917512c083cb4cbb8ccc03'), - ), - ], - makeTuple(MAX_OPTIONALLY_REVEALED_DATA_LENGTH_PER_TX, () => OptionallyRevealedData.empty()), - makeTuple(MAX_PUBLIC_DATA_UPDATE_REQUESTS_PER_TX, () => PublicDataUpdateRequest.empty()), - makeTuple(MAX_PUBLIC_DATA_READS_PER_TX, () => PublicDataRead.empty()), - ); - - const blockHeader = new BlockHeader( - Fr.fromString('0x16642d9ccd8346c403aa4c3fa451178b22534a27035cdaa6ec34ae53b29c50cb'), - Fr.fromString('0x0bcfa3e9f1a8922ee92c6dc964d6595907c1804a86753774322b468f69d4f278'), - Fr.fromString('0x1864fcdaa80ff2719154fa7c8a9050662972707168d69eac9db6fd3110829f80'), - Fr.fromString('0x1864fcdaa80ff2719154fa7c8a9050662972707168d69eac9db6fd3110829f80'), - Fr.fromString('0x1759d221795419503f86c032e8f8762f2b739e74835099584b6531f5f27390fe'), - Fr.ZERO, // TODO(#3441) - Fr.fromString('0x0ccaafdc9c353743970d4e305ae73641ce694f07db67886d2769c9ed88e969d8'), - Fr.fromString('0x200569267c0f73ac89aaa414239398db9445dd4ad3a8cf37015cd55b8d4c5e8d'), - ); - - const constants = new CombinedConstantData(blockHeader, txContext); - - const kernelPublicInputs = new KernelCircuitPublicInputs(combinedAccumulatedData, constants, true); - - const previousKernelData = new PreviousKernelData( - kernelPublicInputs, - makeEmptyProof(), - VerificationKey.makeFake(), - 0, - makeTuple(VK_TREE_HEIGHT, () => Fr.ZERO), - ); - - const kernelInputs = new PrivateKernelInputsOrdering( - previousKernelData, - newCommitments, - makeTuple(MAX_NEW_COMMITMENTS_PER_TX, i => i), - makeTuple(MAX_READ_REQUESTS_PER_TX, () => Fr.ZERO), - sortedNewNullifiers, - sortedNewNullifiersIndexes, - makeTuple(MAX_NEW_NULLIFIERS_PER_TX, () => Fr.ZERO), - ); - - const kernelOutputs = await executeOrdering(kernelInputs); - - expect(kernelOutputs).toMatchSnapshot(); - }); - // Taken from e2e_nested_contract => performs nested calls => last inner // To regenerate fixture data run the following on the yarn-project/e2e folder // AZTEC_GENERATE_TEST_DATA=1 yarn test e2e_nested_contract -t 'performs nested calls' @@ -300,29 +71,58 @@ describe('Private kernel', () => { expect(kernelOutputs).toMatchSnapshot(); }); + + // Taken from e2e_nested_contract => performs nested calls => first ordering + // To regenerate fixture data run the following on the yarn-project/e2e folder + // AZTEC_GENERATE_TEST_DATA=1 yarn test e2e_nested_contract -t 'performs nested calls' + it('Executes private kernel ordering after a deployment', async () => { + const filepath = resolve( + dirname(fileURLToPath(import.meta.url)), + './fixtures/nested-call-private-kernel-ordering.hex', + ); + const serialized = Buffer.from(readFileSync(filepath).toString(), 'hex'); + const kernelInputs = PrivateKernelInputsOrdering.fromBuffer(serialized); + + const kernelOutputs = await executeOrdering(kernelInputs); + + expect(kernelOutputs).toMatchSnapshot(); + }); }); describe('Noir compatibility tests (interop_testing.nr)', () => { // Tests in this file are to check that what we are computing in Noir // is equivalent to what we were computing in circuits.js/the typescript implementation // This is to ensure that we have not introduced any bugs in the transition from circuits.js to Noir - let logger: DebugLogger; - beforeAll(() => { - logger = createDebugLogger('noir-private-kernel-compatibility'); - }); - it('Complete Address matches Noir', () => { - logger('Initialized Noir instance with private kernel init circuit'); - const deployerPubKey = new Point(new Fr(1n), new Fr(2n)); - const contractAddrSalt = new Fr(3n); - const treeRoot = new Fr(4n); - const constructorHash = new Fr(5n); + it('Address matches Noir', () => { + const publicKey = new Point(new Fr(1n), new Fr(2n)); + const salt = new Fr(3n); + const contractClassId = new Fr(4n); + const initializationHash = new Fr(5n); + const portalContractAddress = EthAddress.fromField(new Fr(6n)); + + const address = computeContractAddressFromInstance({ + publicKeysHash: computePublicKeysHash(publicKey), + salt, + contractClassId, + initializationHash, + portalContractAddress, + version: 1, + }); + + expect(address.toString()).toMatchSnapshot(); + }); - const res = computeCompleteAddress(deployerPubKey, contractAddrSalt, treeRoot, constructorHash); + it('Public key hash matches Noir', () => { + const publicKey = new Point(new Fr(1n), new Fr(2n)); + expect(computePublicKeysHash(publicKey).toString()).toMatchSnapshot(); + }); - expect(res.address.toString()).toMatchSnapshot(); - expect(res.publicKey).toMatchSnapshot(); - expect(res.partialAddress.toString()).toMatchSnapshot(); + it('Address from partial matches Noir', () => { + const publicKey = new Point(new Fr(1n), new Fr(2n)); + const partialAddress = new Fr(3n); + const address = computeContractAddressFromPartial({ publicKey, partialAddress }); + expect(address.toString()).toMatchSnapshot(); }); it('TxRequest Hash matches Noir', () => { diff --git a/yarn-project/noir-protocol-circuits/src/noir_test_gen.test.ts b/yarn-project/noir-protocol-circuits/src/noir_test_gen.test.ts index 149a6db9575..765ab453388 100644 --- a/yarn-project/noir-protocol-circuits/src/noir_test_gen.test.ts +++ b/yarn-project/noir-protocol-circuits/src/noir_test_gen.test.ts @@ -16,11 +16,9 @@ import { computeFunctionTreeRoot, } from '@aztec/circuits.js/abis'; import { Fr } from '@aztec/foundation/fields'; +import { AztecLmdbStore } from '@aztec/kv-store'; import { Pedersen, StandardTree } from '@aztec/merkle-tree'; -import { default as levelup } from 'levelup'; -import memdown from 'memdown'; - describe('Data generation for noir tests', () => { const defaultContract = { address: AztecAddress.fromField(new Fr(12345)), @@ -73,7 +71,7 @@ describe('Data generation for noir tests', () => { return contractLeaf.toBuffer(); }); - const db = levelup((memdown as any)()); + const db = await AztecLmdbStore.openTmp(); const tree = new StandardTree( db, new Pedersen(), @@ -94,7 +92,7 @@ describe('Data generation for noir tests', () => { const indexes = new Array(128).fill(null).map((_, i) => BigInt(i)); const leaves = indexes.map(i => new Fr(i + 1n).toBuffer()); - const db = levelup((memdown as any)()); + const db = await AztecLmdbStore.openTmp(); const noteHashTree = new StandardTree( db, diff --git a/yarn-project/noir-protocol-circuits/src/type_conversion.test.ts b/yarn-project/noir-protocol-circuits/src/type_conversion.test.ts index c810d356cde..96295999765 100644 --- a/yarn-project/noir-protocol-circuits/src/type_conversion.test.ts +++ b/yarn-project/noir-protocol-circuits/src/type_conversion.test.ts @@ -1,6 +1,5 @@ import { AztecAddress, - BlockHeader, ContractDeploymentData, EthAddress, Fr, @@ -9,12 +8,11 @@ import { Point, TxContext, } from '@aztec/circuits.js'; +import { makeHeader } from '@aztec/circuits.js/factories'; import { mapAztecAddressFromNoir, mapAztecAddressToNoir, - mapBlockHeaderFromNoir, - mapBlockHeaderToNoir, mapContractDeploymentDataFromNoir, mapContractDeploymentDataToNoir, mapEthAddressFromNoir, @@ -25,6 +23,8 @@ import { mapFunctionDataToNoir, mapFunctionSelectorFromNoir, mapFunctionSelectorToNoir, + mapHeaderFromNoir, + mapHeaderToNoir, mapPointFromNoir, mapPointToNoir, mapTxContextFromNoir, @@ -86,17 +86,8 @@ describe('Noir<>Circuits.js type conversion test suite', () => { }); it('should map block header', () => { - const blockHeader = new BlockHeader( - new Fr(35n), - new Fr(36n), - new Fr(37n), - new Fr(38n), - new Fr(39n), - new Fr(0n), // TODO(#3441) this currently doesn't exist in Noir is it gets squashed to 0 - new Fr(41n), - new Fr(42n), - ); - expect(mapBlockHeaderFromNoir(mapBlockHeaderToNoir(blockHeader))).toEqual(blockHeader); + const header = makeHeader(35, undefined); + expect(mapHeaderFromNoir(mapHeaderToNoir(header))).toEqual(header); }); }); }); diff --git a/yarn-project/noir-protocol-circuits/src/type_conversion.ts b/yarn-project/noir-protocol-circuits/src/type_conversion.ts index 9b598111b4a..58d59b6faa9 100644 --- a/yarn-project/noir-protocol-circuits/src/type_conversion.ts +++ b/yarn-project/noir-protocol-circuits/src/type_conversion.ts @@ -5,7 +5,6 @@ import { AztecAddress, BaseOrMergeRollupPublicInputs, BaseRollupInputs, - BlockHeader, CONTRACT_TREE_HEIGHT, CallContext, CallRequest, @@ -23,6 +22,8 @@ import { FunctionData, FunctionSelector, GlobalVariables, + GrumpkinPrivateKey, + GrumpkinScalar, Header, KernelCircuitPublicInputs, KernelCircuitPublicInputsFinal, @@ -30,6 +31,7 @@ import { MAX_NEW_CONTRACTS_PER_TX, MAX_NEW_L2_TO_L1_MSGS_PER_TX, MAX_NEW_NULLIFIERS_PER_TX, + MAX_NULLIFIER_KEY_VALIDATION_REQUESTS_PER_TX, MAX_OPTIONALLY_REVEALED_DATA_LENGTH_PER_TX, MAX_PRIVATE_CALL_STACK_LENGTH_PER_TX, MAX_PUBLIC_CALL_STACK_LENGTH_PER_TX, @@ -41,6 +43,8 @@ import { NULLIFIER_TREE_HEIGHT, NUM_FIELDS_PER_SHA256, NewContractData, + NullifierKeyValidationRequest, + NullifierKeyValidationRequestContext, NullifierLeafPreimage, OptionallyRevealedData, PUBLIC_DATA_TREE_HEIGHT, @@ -72,10 +76,9 @@ import { TxContext, TxRequest, } from '@aztec/circuits.js'; -import { Tuple, from2Fields, mapTuple } from '@aztec/foundation/serialize'; +import { Tuple, from2Fields, mapTuple, to2Fields } from '@aztec/foundation/serialize'; import { - BlockHeader as BlockHeaderNoir, CallContext as CallContextNoir, CallRequest as CallRequestNoir, CallerContext as CallerContextNoir, @@ -86,12 +89,15 @@ import { FunctionData as FunctionDataNoir, FunctionLeafMembershipWitness as FunctionLeafMembershipWitnessNoir, FunctionSelector as FunctionSelectorNoir, + GrumpkinPrivateKey as GrumpkinPrivateKeyNoir, KernelCircuitPublicInputs as KernelCircuitPublicInputsNoir, NewContractData as NewContractDataNoir, AztecAddress as NoirAztecAddress, EthAddress as NoirEthAddress, Field as NoirField, GrumpkinPoint as NoirPoint, + NullifierKeyValidationRequestContext as NullifierKeyValidationRequestContextNoir, + NullifierKeyValidationRequest as NullifierKeyValidationRequestNoir, OptionallyRevealedData as OptionallyRevealedDataNoir, PrivateCallData as PrivateCallDataNoir, PrivateCallStackItem as PrivateCallStackItemNoir, @@ -177,9 +183,6 @@ export function mapNumberFromNoir(number: NoirField): number { return Number(Fr.fromString(number).toBigInt()); } -/** - * - */ export function mapNumberToNoir(number: number): NoirField { return new Fr(BigInt(number)).toString(); } @@ -205,6 +208,27 @@ export function mapPointFromNoir(point: NoirPoint): Point { return new Point(mapFieldFromNoir(point.x), mapFieldFromNoir(point.y)); } +/** + * Maps a GrumpkinPrivateKey to a noir GrumpkinPrivateKey. + * @param privateKey - The GrumpkinPrivateKey. + * @returns The noir GrumpkinPrivateKey. + */ +export function mapGrumpkinPrivateKeyToNoir(privateKey: GrumpkinPrivateKey): GrumpkinPrivateKeyNoir { + return { + high: mapFieldToNoir(privateKey.high), + low: mapFieldToNoir(privateKey.low), + }; +} + +/** + * Maps a noir GrumpkinPrivateKey to a GrumpkinPrivateKey. + * @param privateKey - The noir GrumpkinPrivateKey. + * @returns The GrumpkinPrivateKey. + */ +export function mapGrumpkinPrivateKeyFromNoir(privateKey: GrumpkinPrivateKeyNoir): GrumpkinPrivateKey { + return GrumpkinScalar.fromHighLow(mapFieldFromNoir(privateKey.high), mapFieldFromNoir(privateKey.low)); +} + /** * Maps an aztec address to a noir aztec address. * @param address - The address. @@ -252,9 +276,9 @@ export function mapEthAddressFromNoir(address: NoirEthAddress): EthAddress { */ export function mapContractDeploymentDataToNoir(data: ContractDeploymentData): ContractDeploymentDataNoir { return { - deployer_public_key: mapPointToNoir(data.deployerPublicKey), - constructor_vk_hash: mapFieldToNoir(data.constructorVkHash), - function_tree_root: mapFieldToNoir(data.functionTreeRoot), + public_key: mapPointToNoir(data.publicKey), + initialization_hash: mapFieldToNoir(data.initializationHash), + contract_class_id: mapFieldToNoir(data.contractClassId), contract_address_salt: mapFieldToNoir(data.contractAddressSalt), portal_contract_address: mapEthAddressToNoir(data.portalContractAddress), }; @@ -267,9 +291,9 @@ export function mapContractDeploymentDataToNoir(data: ContractDeploymentData): C */ export function mapContractDeploymentDataFromNoir(data: ContractDeploymentDataNoir): ContractDeploymentData { return new ContractDeploymentData( - mapPointFromNoir(data.deployer_public_key), - mapFieldFromNoir(data.constructor_vk_hash), - mapFieldFromNoir(data.function_tree_root), + mapPointFromNoir(data.public_key), + mapFieldFromNoir(data.initialization_hash), + mapFieldFromNoir(data.contract_class_id), mapFieldFromNoir(data.contract_address_salt), mapEthAddressFromNoir(data.portal_contract_address), ); @@ -430,7 +454,7 @@ export function mapCallerContextToNoir(callerContext: CallerContext): CallerCont } /** - * Maps a noit call request to a call request. + * Maps a noir call request to a call request. * @param callRequest - The noir call request. * @returns The call request. */ @@ -511,38 +535,60 @@ export function mapSideEffectLinkedFromNoir( } /** - * Maps a block header to a noir block header. - * @param blockHeader - The block header. - * @returns The noir block header. + * Maps a NullifierKeyValidationRequest to a noir NullifierKeyValidationRequest. + * @param request - The NullifierKeyValidationRequest. + * @returns The noir NullifierKeyValidationRequest. + */ +export function mapNullifierKeyValidationRequestToNoir( + request: NullifierKeyValidationRequest, +): NullifierKeyValidationRequestNoir { + return { + public_key: mapPointToNoir(request.publicKey), + secret_key: mapGrumpkinPrivateKeyToNoir(request.secretKey), + }; +} + +/** + * Maps a noir NullifierKeyValidationRequest to NullifierKeyValidationRequest. + * @param request - The noir NullifierKeyValidationRequest. + * @returns The TS NullifierKeyValidationRequest. + */ +export function mapNullifierKeyValidationRequestFromNoir( + request: NullifierKeyValidationRequestNoir, +): NullifierKeyValidationRequest { + return new NullifierKeyValidationRequest( + mapPointFromNoir(request.public_key), + mapGrumpkinPrivateKeyFromNoir(request.secret_key), + ); +} + +/** + * Maps a NullifierKeyValidationRequest to a noir NullifierKeyValidationRequest. + * @param request - The NullifierKeyValidationRequest. + * @returns The noir NullifierKeyValidationRequest. */ -export function mapBlockHeaderToNoir(blockHeader: BlockHeader): BlockHeaderNoir { +export function mapNullifierKeyValidationRequestContextToNoir( + request: NullifierKeyValidationRequestContext, +): NullifierKeyValidationRequestContextNoir { return { - note_hash_tree_root: mapFieldToNoir(blockHeader.noteHashTreeRoot), - nullifier_tree_root: mapFieldToNoir(blockHeader.nullifierTreeRoot), - contract_tree_root: mapFieldToNoir(blockHeader.contractTreeRoot), - l1_to_l2_message_tree_root: mapFieldToNoir(blockHeader.l1ToL2MessageTreeRoot), - archive_root: mapFieldToNoir(blockHeader.archiveRoot), - public_data_tree_root: mapFieldToNoir(blockHeader.publicDataTreeRoot), - global_variables_hash: mapFieldToNoir(blockHeader.globalVariablesHash), - // TODO(#3441) + public_key: mapPointToNoir(request.publicKey), + secret_key: mapGrumpkinPrivateKeyToNoir(request.secretKey), + contract_address: mapAztecAddressToNoir(request.contractAddress), }; } /** - * Maps a noir block header to a block header. - * @param blockHeader - The noir block header. - * @returns The block header. + * Maps a noir NullifierKeyValidationRequestContext to NullifierKeyValidationRequestContext. + * @param request - The noir NullifierKeyValidationRequestContext. + * @returns The TS NullifierKeyValidationRequestContext. */ -export function mapBlockHeaderFromNoir(blockHeader: BlockHeaderNoir): BlockHeader { - return new BlockHeader( - mapFieldFromNoir(blockHeader.note_hash_tree_root), - mapFieldFromNoir(blockHeader.nullifier_tree_root), - mapFieldFromNoir(blockHeader.contract_tree_root), - mapFieldFromNoir(blockHeader.l1_to_l2_message_tree_root), - mapFieldFromNoir(blockHeader.archive_root), - Fr.zero(), // TODO(#3441) - mapFieldFromNoir(blockHeader.public_data_tree_root), - mapFieldFromNoir(blockHeader.global_variables_hash), +export function mapNullifierKeyValidationRequestContextFromNoir( + request: NullifierKeyValidationRequestContextNoir, +): NullifierKeyValidationRequestContext { + return new NullifierKeyValidationRequestContext( + mapPointFromNoir(request.public_key), + mapGrumpkinPrivateKeyFromNoir(request.secret_key), + mapAztecAddressFromNoir(request.contract_address), ); } @@ -559,6 +605,10 @@ export function mapPrivateCircuitPublicInputsToNoir( args_hash: mapFieldToNoir(privateCircuitPublicInputs.argsHash), return_values: mapTuple(privateCircuitPublicInputs.returnValues, mapFieldToNoir), read_requests: mapTuple(privateCircuitPublicInputs.readRequests, mapSideEffectToNoir), + nullifier_key_validation_requests: mapTuple( + privateCircuitPublicInputs.nullifierKeyValidationRequests, + mapNullifierKeyValidationRequestToNoir, + ), new_commitments: mapTuple(privateCircuitPublicInputs.newCommitments, mapSideEffectToNoir), new_nullifiers: mapTuple(privateCircuitPublicInputs.newNullifiers, mapSideEffectLinkedToNoir), private_call_stack_hashes: mapTuple(privateCircuitPublicInputs.privateCallStackHashes, mapFieldToNoir), @@ -569,7 +619,7 @@ export function mapPrivateCircuitPublicInputsToNoir( unencrypted_logs_hash: mapTuple(privateCircuitPublicInputs.unencryptedLogsHash, mapFieldToNoir), encrypted_log_preimages_length: mapFieldToNoir(privateCircuitPublicInputs.encryptedLogPreimagesLength), unencrypted_log_preimages_length: mapFieldToNoir(privateCircuitPublicInputs.unencryptedLogPreimagesLength), - block_header: mapBlockHeaderToNoir(privateCircuitPublicInputs.blockHeader), + historical_header: mapHeaderToNoir(privateCircuitPublicInputs.historicalHeader), contract_deployment_data: mapContractDeploymentDataToNoir(privateCircuitPublicInputs.contractDeploymentData), chain_id: mapFieldToNoir(privateCircuitPublicInputs.chainId), version: mapFieldToNoir(privateCircuitPublicInputs.version), @@ -689,6 +739,15 @@ export function mapSha256HashFromNoir(hash: FixedLengthArray): Buffer return from2Fields(mapFieldFromNoir(hash[0]), mapFieldFromNoir(hash[1])); } +/** + * Maps a sha256 to the representation used in noir. + * @param hash - The hash represented as a 32 bytes long buffer. + * @returns The hash as it is represented in Noir (2 fields). + */ +export function mapSha256HashToNoir(hash: Buffer): FixedLengthArray { + return to2Fields(hash).map(mapFieldToNoir) as FixedLengthArray; +} + /** * Maps optionally revealed data from noir to the parsed type. * @param optionallyRevealedData - The noir optionally revealed data. @@ -738,7 +797,7 @@ export function mapNewContractDataFromNoir(newContractData: NewContractDataNoir) return new NewContractData( mapAztecAddressFromNoir(newContractData.contract_address), mapEthAddressFromNoir(newContractData.portal_contract_address), - mapFieldFromNoir(newContractData.function_tree_root), + mapFieldFromNoir(newContractData.contract_class_id), ); } @@ -751,7 +810,7 @@ export function mapNewContractDataToNoir(newContractData: NewContractData): NewC return { contract_address: mapAztecAddressToNoir(newContractData.contractAddress), portal_contract_address: mapEthAddressToNoir(newContractData.portalContractAddress), - function_tree_root: mapFieldToNoir(newContractData.functionTreeRoot), + contract_class_id: mapFieldToNoir(newContractData.contractClassId), }; } @@ -818,6 +877,11 @@ export function mapCombinedAccumulatedDataFromNoir( // TODO aggregation object AggregationObject.makeFake(), mapTupleFromNoir(combinedAccumulatedData.read_requests, MAX_READ_REQUESTS_PER_TX, mapSideEffectFromNoir), + mapTupleFromNoir( + combinedAccumulatedData.nullifier_key_validation_requests, + MAX_NULLIFIER_KEY_VALIDATION_REQUESTS_PER_TX, + mapNullifierKeyValidationRequestContextFromNoir, + ), mapTupleFromNoir(combinedAccumulatedData.new_commitments, MAX_NEW_COMMITMENTS_PER_TX, mapSideEffectFromNoir), mapTupleFromNoir(combinedAccumulatedData.new_nullifiers, MAX_NEW_NULLIFIERS_PER_TX, mapSideEffectLinkedFromNoir), mapTupleFromNoir( @@ -900,6 +964,10 @@ export function mapCombinedAccumulatedDataToNoir( return { aggregation_object: {}, read_requests: mapTuple(combinedAccumulatedData.readRequests, mapSideEffectToNoir), + nullifier_key_validation_requests: mapTuple( + combinedAccumulatedData.nullifierKeyValidationRequests, + mapNullifierKeyValidationRequestContextToNoir, + ), new_commitments: mapTuple(combinedAccumulatedData.newCommitments, mapSideEffectToNoir), new_nullifiers: mapTuple(combinedAccumulatedData.newNullifiers, mapSideEffectLinkedToNoir), private_call_stack: mapTuple(combinedAccumulatedData.privateCallStack, mapCallRequestToNoir), @@ -926,7 +994,7 @@ export function mapCombinedAccumulatedDataToNoir( */ export function mapCombinedConstantDataFromNoir(combinedConstantData: CombinedConstantDataNoir): CombinedConstantData { return new CombinedConstantData( - mapBlockHeaderFromNoir(combinedConstantData.block_header), + mapHeaderFromNoir(combinedConstantData.historical_header), mapTxContextFromNoir(combinedConstantData.tx_context), ); } @@ -938,7 +1006,7 @@ export function mapCombinedConstantDataFromNoir(combinedConstantData: CombinedCo */ export function mapCombinedConstantDataToNoir(combinedConstantData: CombinedConstantData): CombinedConstantDataNoir { return { - block_header: mapBlockHeaderToNoir(combinedConstantData.blockHeader), + historical_header: mapHeaderToNoir(combinedConstantData.historicalHeader), tx_context: mapTxContextToNoir(combinedConstantData.txContext), }; } @@ -1047,6 +1115,7 @@ export function mapPrivateKernelInputsOrderingToNoir( sorted_new_nullifiers: mapTuple(inputs.sortedNewNullifiers, mapSideEffectLinkedToNoir), sorted_new_nullifiers_indexes: mapTuple(inputs.sortedNewNullifiersIndexes, mapNumberToNoir), nullifier_commitment_hints: mapTuple(inputs.nullifierCommitmentHints, mapFieldToNoir), + master_nullifier_secret_keys: mapTuple(inputs.masterNullifierSecretKeys, mapGrumpkinPrivateKeyToNoir), }; } @@ -1142,7 +1211,7 @@ export function mapPublicCircuitPublicInputsToNoir( new_l2_to_l1_msgs: mapTuple(publicInputs.newL2ToL1Msgs, mapFieldToNoir), unencrypted_logs_hash: mapTuple(publicInputs.unencryptedLogsHash, mapFieldToNoir), unencrypted_log_preimages_length: mapFieldToNoir(publicInputs.unencryptedLogPreimagesLength), - block_header: mapBlockHeaderToNoir(publicInputs.blockHeader), + historical_header: mapHeaderToNoir(publicInputs.historicalHeader), prover_address: mapAztecAddressToNoir(publicInputs.proverAddress), }; @@ -1321,6 +1390,20 @@ export function mapRootRollupPublicInputsFromNoir( ); } +/** + * Maps header to Noir + * @param header - The header. + * @returns Header. + */ +export function mapHeaderToNoir(header: Header): HeaderNoir { + return { + last_archive: mapAppendOnlyTreeSnapshotToNoir(header.lastArchive), + body_hash: mapSha256HashToNoir(header.bodyHash), + state: mapStateReferenceToNoir(header.state), + global_variables: mapGlobalVariablesToNoir(header.globalVariables), + }; +} + /** * Maps header from Noir. * @param header - The header. @@ -1335,6 +1418,18 @@ export function mapHeaderFromNoir(header: HeaderNoir): Header { ); } +/** + * Maps state reference to Noir. + * @param stateReference - The state reference. + * @returns Noir representation of state reference. + */ +export function mapStateReferenceToNoir(stateReference: StateReference): StateReferenceNoir { + return { + l1_to_l2_message_tree: mapAppendOnlyTreeSnapshotToNoir(stateReference.l1ToL2MessageTree), + partial: mapPartialStateReferenceToNoir(stateReference.partial), + }; +} + /** * Maps state reference from Noir. * @param stateReference - The state reference. diff --git a/yarn-project/noir-protocol-circuits/tsconfig.json b/yarn-project/noir-protocol-circuits/tsconfig.json index 917d509e475..a2e4cd63239 100644 --- a/yarn-project/noir-protocol-circuits/tsconfig.json +++ b/yarn-project/noir-protocol-circuits/tsconfig.json @@ -21,6 +21,9 @@ { "path": "../circuit-types" }, + { + "path": "../kv-store" + }, { "path": "../merkle-tree" } diff --git a/yarn-project/p2p-bootstrap/scripts/docker-compose-bootstrap.yml b/yarn-project/p2p-bootstrap/scripts/docker-compose-bootstrap.yml index ce5913b9e75..9ea2293cb24 100644 --- a/yarn-project/p2p-bootstrap/scripts/docker-compose-bootstrap.yml +++ b/yarn-project/p2p-bootstrap/scripts/docker-compose-bootstrap.yml @@ -1,11 +1,11 @@ version: '3' services: p2p-bootstrap: - image: 278380418400.dkr.ecr.eu-west-2.amazonaws.com/aztec-sandbox:latest + image: 278380418400.dkr.ecr.eu-west-2.amazonaws.com/aztec:latest + command: 'start --p2p-bootstrap' ports: - '40400:40400' environment: - MODE: 'p2p-bootstrap' DEBUG: 'aztec:*' P2P_TCP_LISTEN_PORT: 40400 PEER_ID: '0a260024080112205ea53185db2e52dae74d0d4d6cadc494174810d0a713cd09b0ac517c38bc781e1224080112205ea53185db2e52dae74d0d4d6cadc494174810d0a713cd09b0ac517c38bc781e1a44080112402df8b977f356c6e34fa021c9647973234dff4df706c185794405aafb556723cf5ea53185db2e52dae74d0d4d6cadc494174810d0a713cd09b0ac517c38bc781e' diff --git a/yarn-project/p2p-bootstrap/terraform/main.tf b/yarn-project/p2p-bootstrap/terraform/main.tf index 659133d4f42..57070dcfa76 100644 --- a/yarn-project/p2p-bootstrap/terraform/main.tf +++ b/yarn-project/p2p-bootstrap/terraform/main.tf @@ -105,7 +105,8 @@ resource "aws_ecs_task_definition" "p2p-bootstrap" { [ { "name": "${var.DEPLOY_TAG}-p2p-bootstrap-${count.index + 1}", - "image": "${var.DOCKERHUB_ACCOUNT}/aztec-sandbox:${var.DEPLOY_TAG}", + "image": "${var.DOCKERHUB_ACCOUNT}/aztec:${var.DEPLOY_TAG}", + "command": ["start", "--p2p-bootstrap"], "essential": true, "command": ["start"], "memoryReservation": 3776, @@ -122,10 +123,6 @@ resource "aws_ecs_task_definition" "p2p-bootstrap" { "name": "NODE_ENV", "value": "production" }, - { - "name": "MODE", - "value": "p2p-bootstrap" - }, { "name": "P2P_TCP_LISTEN_PORT", "value": "${var.BOOTNODE_LISTEN_PORT + count.index}" diff --git a/yarn-project/p2p/src/client/p2p_client.test.ts b/yarn-project/p2p/src/client/p2p_client.test.ts index 4de4f519f13..0ca27e09752 100644 --- a/yarn-project/p2p/src/client/p2p_client.test.ts +++ b/yarn-project/p2p/src/client/p2p_client.test.ts @@ -1,5 +1,4 @@ import { L2BlockSource, mockTx } from '@aztec/circuit-types'; -import { EthAddress } from '@aztec/circuits.js'; import { AztecKVStore, AztecLmdbStore } from '@aztec/kv-store'; import { expect, jest } from '@jest/globals'; @@ -42,7 +41,7 @@ describe('In-Memory P2P Client', () => { blockSource = new MockBlockSource(); - kvStore = await AztecLmdbStore.create(EthAddress.random()); + kvStore = await AztecLmdbStore.openTmp(); client = new P2PClient(kvStore, blockSource, txPool, p2pService); }); diff --git a/yarn-project/p2p/src/client/p2p_client.ts b/yarn-project/p2p/src/client/p2p_client.ts index 6c9789259ac..8fb1b520cb0 100644 --- a/yarn-project/p2p/src/client/p2p_client.ts +++ b/yarn-project/p2p/src/client/p2p_client.ts @@ -132,9 +132,9 @@ export class P2PClient implements P2P { private p2pService: P2PService, private log = createDebugLogger('aztec:p2p'), ) { - const { p2pBlockCheckIntervalMS: checkInterval, l2QueueSize } = getP2PConfigEnvVars(); - this.blockDownloader = new L2BlockDownloader(l2BlockSource, l2QueueSize, checkInterval); - this.synchedBlockNumber = store.createSingleton('p2p_pool_last_l2_block'); + const { p2pBlockCheckIntervalMS: checkInterval, p2pL2QueueSize } = getP2PConfigEnvVars(); + this.blockDownloader = new L2BlockDownloader(l2BlockSource, p2pL2QueueSize, checkInterval); + this.synchedBlockNumber = store.openSingleton('p2p_pool_last_l2_block'); } /** diff --git a/yarn-project/p2p/src/config.ts b/yarn-project/p2p/src/config.ts index 8e4d4d1af94..a995bcb8195 100644 --- a/yarn-project/p2p/src/config.ts +++ b/yarn-project/p2p/src/config.ts @@ -15,7 +15,7 @@ export interface P2PConfig { /** * Size of queue of L2 blocks to store. */ - l2QueueSize: number; + p2pL2QueueSize: number; /** * The tcp port on which the P2P service should listen for connections. @@ -96,7 +96,7 @@ export function getP2PConfigEnvVars(): P2PConfig { const envVars: P2PConfig = { p2pEnabled: P2P_ENABLED === 'true', p2pBlockCheckIntervalMS: P2P_BLOCK_CHECK_INTERVAL_MS ? +P2P_BLOCK_CHECK_INTERVAL_MS : 100, - l2QueueSize: P2P_L2_BLOCK_QUEUE_SIZE ? +P2P_L2_BLOCK_QUEUE_SIZE : 1000, + p2pL2QueueSize: P2P_L2_BLOCK_QUEUE_SIZE ? +P2P_L2_BLOCK_QUEUE_SIZE : 1000, tcpListenPort: P2P_TCP_LISTEN_PORT ? +P2P_TCP_LISTEN_PORT : 40400, tcpListenIp: P2P_TCP_LISTEN_IP ? P2P_TCP_LISTEN_IP : '0.0.0.0', peerIdPrivateKey: PEER_ID_PRIVATE_KEY, diff --git a/yarn-project/p2p/src/tx_pool/aztec_kv_tx_pool.test.ts b/yarn-project/p2p/src/tx_pool/aztec_kv_tx_pool.test.ts index 126779f9dd5..fe030abe6d7 100644 --- a/yarn-project/p2p/src/tx_pool/aztec_kv_tx_pool.test.ts +++ b/yarn-project/p2p/src/tx_pool/aztec_kv_tx_pool.test.ts @@ -1,4 +1,3 @@ -import { EthAddress } from '@aztec/circuits.js'; import { AztecLmdbStore } from '@aztec/kv-store'; import { AztecKVTxPool } from './aztec_kv_tx_pool.js'; @@ -7,7 +6,7 @@ import { describeTxPool } from './tx_pool_test_suite.js'; describe('In-Memory TX pool', () => { let txPool: AztecKVTxPool; beforeEach(async () => { - txPool = new AztecKVTxPool(await AztecLmdbStore.create(EthAddress.random())); + txPool = new AztecKVTxPool(await AztecLmdbStore.openTmp()); }); describeTxPool(() => txPool); diff --git a/yarn-project/p2p/src/tx_pool/aztec_kv_tx_pool.ts b/yarn-project/p2p/src/tx_pool/aztec_kv_tx_pool.ts index 1d8d723ab5d..fd1eba930e2 100644 --- a/yarn-project/p2p/src/tx_pool/aztec_kv_tx_pool.ts +++ b/yarn-project/p2p/src/tx_pool/aztec_kv_tx_pool.ts @@ -24,7 +24,7 @@ export class AztecKVTxPool implements TxPool { * @param log - A logger. */ constructor(store: AztecKVStore, log = createDebugLogger('aztec:tx_pool')) { - this.#txs = store.createMap('txs'); + this.#txs = store.openMap('txs'); this.#store = store; this.#log = log; } diff --git a/yarn-project/package.json b/yarn-project/package.json index 8bd7ef891ce..489e0f42ecc 100644 --- a/yarn-project/package.json +++ b/yarn-project/package.json @@ -10,7 +10,7 @@ "formatting:fix": "./run_nargo_fmt.sh && FORCE_COLOR=true yarn workspaces foreach -p -v run formatting:fix", "lint": "yarn eslint --cache --ignore-pattern l1-artifacts .", "format": "yarn prettier --cache -w .", - "test": "FORCE_COLOR=true yarn workspaces foreach --exclude @aztec/aztec3-packages --exclude @aztec/end-to-end --exclude private-token -p -j unlimited -v run test", + "test": "FORCE_COLOR=true yarn workspaces foreach --exclude @aztec/aztec3-packages --exclude @aztec/end-to-end --exclude private-token -p -j ${JOBS:-unlimited} -v run test", "build": "yarn workspace @aztec/l1-artifacts build && tsc -b tsconfig.json", "build:dev": "yarn workspace @aztec/l1-artifacts build && tsc -b tsconfig.json --watch", "clean": "yarn workspaces foreach -p -v run clean" @@ -22,7 +22,7 @@ "aztec-faucet", "aztec-node", "pxe", - "aztec-sandbox", + "aztec", "aztec.js", "circuits.js", "circuit-types", @@ -32,6 +32,7 @@ "ethereum", "foundation", "key-store", + "kv-store", "l1-artifacts", "merkle-tree", "noir-compiler", @@ -45,8 +46,7 @@ "scripts", "types", "world-state", - "yarn-project-base", - "kv-store" + "yarn-project-base" ], "prettier": "@aztec/foundation/prettier", "devDependencies": { diff --git a/yarn-project/pxe/package.json b/yarn-project/pxe/package.json index 82bf35b49e6..3ade258964c 100644 --- a/yarn-project/pxe/package.json +++ b/yarn-project/pxe/package.json @@ -51,6 +51,7 @@ "viem": "^1.2.5" }, "devDependencies": { + "@aztec/noir-contracts": "workspace:^", "@jest/globals": "^29.5.0", "@types/jest": "^29.5.0", "@types/lodash.omit": "^4.5.7", diff --git a/yarn-project/pxe/src/config/index.ts b/yarn-project/pxe/src/config/index.ts index eaf7133944d..3f22e3caab2 100644 --- a/yarn-project/pxe/src/config/index.ts +++ b/yarn-project/pxe/src/config/index.ts @@ -12,8 +12,7 @@ export interface PXEServiceConfig { l2BlockPollingIntervalMS: number; /** L2 block to start scanning from for new accounts */ l2StartingBlock: number; - - /** Where to store PXE data. If not set will store in memory */ + /** Where to store PXE data. If not set, will store in memory */ dataDirectory?: string; } @@ -21,12 +20,12 @@ export interface PXEServiceConfig { * Creates an instance of PXEServiceConfig out of environment variables using sensible defaults for integration testing if not set. */ export function getPXEServiceConfig(): PXEServiceConfig { - const { PXE_BLOCK_POLLING_INTERVAL_MS, PXE_L2_STARTING_BLOCK, DATA_DIRECTORY } = process.env; + const { PXE_BLOCK_POLLING_INTERVAL_MS, PXE_L2_STARTING_BLOCK, PXE_DATA_DIRECTORY } = process.env; return { l2BlockPollingIntervalMS: PXE_BLOCK_POLLING_INTERVAL_MS ? +PXE_BLOCK_POLLING_INTERVAL_MS : 1000, l2StartingBlock: PXE_L2_STARTING_BLOCK ? +PXE_L2_STARTING_BLOCK : INITIAL_L2_BLOCK_NUM, - dataDirectory: DATA_DIRECTORY, + dataDirectory: PXE_DATA_DIRECTORY, }; } diff --git a/yarn-project/pxe/src/contract_data_oracle/index.ts b/yarn-project/pxe/src/contract_data_oracle/index.ts index 858a7e65fda..d4690628f7b 100644 --- a/yarn-project/pxe/src/contract_data_oracle/index.ts +++ b/yarn-project/pxe/src/contract_data_oracle/index.ts @@ -28,7 +28,7 @@ export class ContractDataOracle { */ public async getPortalContractAddress(contractAddress: AztecAddress) { const tree = await this.getTree(contractAddress); - return tree.contract.portalContract; + return tree.contract.instance.portalContractAddress; } /** @@ -155,7 +155,7 @@ export class ContractDataOracle { * @throws An Error if the contract is not found in the ContractDatabase. */ private async getTree(contractAddress: AztecAddress): Promise { - let tree = this.trees.find(t => t.contract.completeAddress.address.equals(contractAddress)); + let tree = this.trees.find(t => t.contract.instance.address.equals(contractAddress)); if (!tree) { const contract = await this.db.getContract(contractAddress); if (!contract) { diff --git a/yarn-project/pxe/src/contract_database/memory_contract_database.ts b/yarn-project/pxe/src/contract_database/memory_contract_database.ts index 64597448593..567256def0f 100644 --- a/yarn-project/pxe/src/contract_database/memory_contract_database.ts +++ b/yarn-project/pxe/src/contract_database/memory_contract_database.ts @@ -22,7 +22,7 @@ export class MemoryContractDatabase implements ContractDatabase { * @returns A Promise that resolves when the contract is successfully added. */ public addContract(contract: ContractDao) { - this.log(`Adding contract ${contract.completeAddress.address.toString()}`); + this.log(`Adding contract ${contract.instance.address.toString()}`); this.contracts.push(contract); return Promise.resolve(); } @@ -35,7 +35,7 @@ export class MemoryContractDatabase implements ContractDatabase { * @returns A Promise resolving to the ContractDao instance matching the given address or undefined. */ public getContract(address: AztecAddress): Promise { - return Promise.resolve(this.contracts.find(c => c.completeAddress.address.equals(address))); + return Promise.resolve(this.contracts.find(c => c.instance.address.equals(address))); } public getContracts(): Promise { diff --git a/yarn-project/pxe/src/contract_tree/index.ts b/yarn-project/pxe/src/contract_tree/index.ts index bc8b9246478..9bb88fc1905 100644 --- a/yarn-project/pxe/src/contract_tree/index.ts +++ b/yarn-project/pxe/src/contract_tree/index.ts @@ -1,28 +1,18 @@ -import { AztecNode, ContractDao, MerkleTreeId, PublicKey, StateInfoProvider } from '@aztec/circuit-types'; +import { ContractDao, MerkleTreeId, StateInfoProvider } from '@aztec/circuit-types'; import { CONTRACT_TREE_HEIGHT, - EthAddress, FUNCTION_TREE_HEIGHT, Fr, - FunctionData, MembershipWitness, NewContractConstructor, NewContractData, computeFunctionTreeData, generateFunctionLeaves, - hashVKStr, + getContractClassFromArtifact, isConstrained, - isConstructor, } from '@aztec/circuits.js'; -import { - computeCompleteAddress, - computeContractLeaf, - computeFunctionTree, - computeFunctionTreeRoot, - computeVarArgsHash, - hashConstructor, -} from '@aztec/circuits.js/abis'; -import { ContractArtifact, FunctionSelector } from '@aztec/foundation/abi'; +import { computeContractLeaf, computeFunctionTree, computeFunctionTreeRoot } from '@aztec/circuits.js/abis'; +import { FunctionSelector } from '@aztec/foundation/abi'; import { assertLength } from '@aztec/foundation/serialize'; /** @@ -36,6 +26,7 @@ export class ContractTree { private functionTree?: Fr[]; private functionTreeRoot?: Fr; private contractIndex?: bigint; + private contractClassId?: Fr; constructor( /** @@ -49,58 +40,6 @@ export class ContractTree { public readonly newContractConstructor?: NewContractConstructor, ) {} - /** - * Create a new ContractTree instance from the provided contract artifact, constructor arguments, and related data. - * The function generates function leaves for constrained functions, computes the function tree root, - * and hashes the constructor's verification key. It then computes the contract address using the contract - * and portal contract addresses, contract address salt, and generated data. Finally, it returns a new - * ContractTree instance containing the contract data and computed values. - * - * @param artifact - The contract's build artifact containing the functions and their metadata. - * @param args - An array of Fr elements representing the constructor's arguments. - * @param portalContract - The Ethereum address of the portal smart contract. - * @param contractAddressSalt - An Fr element representing the salt used to compute the contract address. - * @param from - The public key of the contract deployer. - * @param node - An instance of the AztecNode class representing the current node. - * @returns A new ContractTree instance containing the contract data and computed values. - */ - public static new( - artifact: ContractArtifact, - args: Fr[], - portalContract: EthAddress, - contractAddressSalt: Fr, - from: PublicKey, - node: AztecNode, - ) { - const constructorArtifact = artifact.functions.find(isConstructor); - if (!constructorArtifact) { - throw new Error('Constructor not found.'); - } - if (!constructorArtifact.verificationKey) { - throw new Error('Missing verification key for the constructor.'); - } - - const functions = artifact.functions.map(f => ({ - ...f, - selector: FunctionSelector.fromNameAndParameters(f.name, f.parameters), - })); - const leaves = generateFunctionLeaves(functions); - const root = computeFunctionTreeRoot(leaves); - const functionData = FunctionData.fromAbi(constructorArtifact); - const vkHash = hashVKStr(constructorArtifact.verificationKey); - const argsHash = computeVarArgsHash(args); - const constructorHash = hashConstructor(functionData, argsHash, vkHash); - - const completeAddress = computeCompleteAddress(from, contractAddressSalt, root, constructorHash); - - const contractDao = new ContractDao(artifact, completeAddress, portalContract); - const NewContractConstructor = { - functionData, - vkHash, - }; - return new ContractTree(contractDao, node, NewContractConstructor); - } - /** * Retrieve the artifact of a given function. * The function is identified by its selector, which represents a unique identifier for the function's signature. @@ -113,7 +52,7 @@ export class ContractTree { const artifact = this.contract.functions.find(f => f.selector.equals(selector)); if (!artifact) { throw new Error( - `Unknown function. Selector ${selector.toString()} not found in the artifact of contract ${this.contract.completeAddress.address.toString()}. Expected one of: ${this.contract.functions + `Unknown function. Selector ${selector.toString()} not found in the artifact of contract ${this.contract.instance.address.toString()}. Expected one of: ${this.contract.functions .map(f => f.selector.toString()) .join(', ')}`, ); @@ -171,6 +110,16 @@ export class ContractTree { return Promise.resolve(this.functionTreeRoot); } + /** + * Returns the contract class identifier for the given artifact. + */ + public getContractClassId() { + if (!this.contractClassId) { + this.contractClassId = getContractClassFromArtifact(this.contract).id; + } + return this.contractClassId; + } + /** * Retrieve the membership witness of a function within a contract's function tree. * A membership witness represents the position and authentication path of a target function @@ -219,15 +168,13 @@ export class ContractTree { private async getContractIndex() { if (this.contractIndex === undefined) { - const { completeAddress, portalContract } = this.contract; - const root = await this.getFunctionTreeRoot(); - const newContractData = new NewContractData(completeAddress.address, portalContract, root); + const { address, portalContractAddress } = this.contract.instance; + const contractClassId = this.getContractClassId(); + const newContractData = new NewContractData(address, portalContractAddress, contractClassId); const commitment = computeContractLeaf(newContractData); this.contractIndex = await this.stateInfoProvider.findLeafIndex('latest', MerkleTreeId.CONTRACT_TREE, commitment); if (this.contractIndex === undefined) { - throw new Error( - `Failed to find contract at ${completeAddress.address} with portal ${portalContract} resulting in commitment ${commitment}.`, - ); + throw new Error(`Failed to find contract at ${address.toString()} resulting in commitment ${commitment}.`); } return this.contractIndex; } diff --git a/yarn-project/pxe/src/database/contracts/contract_artifact_db.ts b/yarn-project/pxe/src/database/contracts/contract_artifact_db.ts new file mode 100644 index 00000000000..e49d96c061c --- /dev/null +++ b/yarn-project/pxe/src/database/contracts/contract_artifact_db.ts @@ -0,0 +1,19 @@ +import { ContractArtifact } from '@aztec/foundation/abi'; +import { Fr } from '@aztec/foundation/fields'; + +/** + * PXE database for managing contract artifacts. + */ +export interface ContractArtifactDatabase { + /** + * Adds a new contract artifact to the database or updates an existing one. + * @param id - Id of the corresponding contract class. + * @param contract - Contract artifact to add. + */ + addContractArtifact(id: Fr, contract: ContractArtifact): Promise; + /** + * Gets a contract artifact given its resulting contract class id. + * @param id - Contract class id for the given artifact. + */ + getContractArtifact(id: Fr): Promise; +} diff --git a/yarn-project/pxe/src/database/contracts/contract_instance_db.ts b/yarn-project/pxe/src/database/contracts/contract_instance_db.ts new file mode 100644 index 00000000000..30dd44bed9e --- /dev/null +++ b/yarn-project/pxe/src/database/contracts/contract_instance_db.ts @@ -0,0 +1,18 @@ +import { AztecAddress } from '@aztec/circuits.js'; +import { ContractInstanceWithAddress } from '@aztec/types/contracts'; + +/** + * PXE database for managing contract instances. + */ +export interface ContractInstanceDatabase { + /** + * Adds a new contract to the db or updates an existing one. + * @param contract - Contract to insert. + */ + addContractInstance(contract: ContractInstanceWithAddress): Promise; + /** + * Gets a contract given its address. + * @param address - Address of the contract. + */ + getContractInstance(address: AztecAddress): Promise; +} diff --git a/yarn-project/pxe/src/database/index.ts b/yarn-project/pxe/src/database/index.ts index 35d4e000a20..4685cc2f7a8 100644 --- a/yarn-project/pxe/src/database/index.ts +++ b/yarn-project/pxe/src/database/index.ts @@ -1,2 +1 @@ export * from './pxe_database.js'; -export * from './memory_db.js'; diff --git a/yarn-project/pxe/src/database/kv_pxe_database.test.ts b/yarn-project/pxe/src/database/kv_pxe_database.test.ts index a9054af0719..27e0da25f37 100644 --- a/yarn-project/pxe/src/database/kv_pxe_database.test.ts +++ b/yarn-project/pxe/src/database/kv_pxe_database.test.ts @@ -1,4 +1,3 @@ -import { EthAddress } from '@aztec/circuits.js'; import { AztecLmdbStore } from '@aztec/kv-store'; import { KVPxeDatabase } from './kv_pxe_database.js'; @@ -8,7 +7,7 @@ describe('KVPxeDatabase', () => { let database: KVPxeDatabase; beforeEach(async () => { - database = new KVPxeDatabase(await AztecLmdbStore.create(EthAddress.random())); + database = new KVPxeDatabase(await AztecLmdbStore.openTmp()); }); describePxeDatabase(() => database); diff --git a/yarn-project/pxe/src/database/kv_pxe_database.ts b/yarn-project/pxe/src/database/kv_pxe_database.ts index a6216e9be06..c63b913ea77 100644 --- a/yarn-project/pxe/src/database/kv_pxe_database.ts +++ b/yarn-project/pxe/src/database/kv_pxe_database.ts @@ -1,66 +1,98 @@ -import { ContractDao, MerkleTreeId, NoteFilter, PublicKey } from '@aztec/circuit-types'; -import { AztecAddress, BlockHeader, CompleteAddress } from '@aztec/circuits.js'; +import { ContractDao, MerkleTreeId, NoteFilter, NoteStatus, PublicKey } from '@aztec/circuit-types'; +import { AztecAddress, CompleteAddress, Header } from '@aztec/circuits.js'; +import { ContractArtifact } from '@aztec/foundation/abi'; +import { toBufferBE } from '@aztec/foundation/bigint-buffer'; import { Fr, Point } from '@aztec/foundation/fields'; import { AztecArray, AztecKVStore, AztecMap, AztecMultiMap, AztecSingleton } from '@aztec/kv-store'; +import { contractArtifactFromBuffer, contractArtifactToBuffer } from '@aztec/types/abi'; +import { ContractInstanceWithAddress, SerializableContractInstance } from '@aztec/types/contracts'; import { DeferredNoteDao } from './deferred_note_dao.js'; import { NoteDao } from './note_dao.js'; import { PxeDatabase } from './pxe_database.js'; -/** Serialized structure of a block header */ -type SynchronizedBlock = { - /** The tree roots when the block was created */ - roots: Record; - /** The hash of the global variables */ - globalVariablesHash: string; - /** The block number */ - blockNumber: number; -}; - /** * A PXE database backed by LMDB. */ export class KVPxeDatabase implements PxeDatabase { - #synchronizedBlock: AztecSingleton; + #synchronizedBlock: AztecSingleton; #addresses: AztecArray; #addressIndex: AztecMap; #authWitnesses: AztecMap; #capsules: AztecArray; #contracts: AztecMap; - #notes: AztecArray; - #nullifiedNotes: AztecMap; - #notesByContract: AztecMultiMap; - #notesByStorageSlot: AztecMultiMap; - #notesByTxHash: AztecMultiMap; - #notesByOwner: AztecMultiMap; - #deferredNotes: AztecArray; + #notes: AztecMap; + #nullifiedNotes: AztecMap; + #nullifierToNoteId: AztecMap; + #notesByContract: AztecMultiMap; + #notesByStorageSlot: AztecMultiMap; + #notesByTxHash: AztecMultiMap; + #notesByOwner: AztecMultiMap; + #nullifiedNotesByContract: AztecMultiMap; + #nullifiedNotesByStorageSlot: AztecMultiMap; + #nullifiedNotesByTxHash: AztecMultiMap; + #nullifiedNotesByOwner: AztecMultiMap; + #deferredNotes: AztecArray; #deferredNotesByContract: AztecMultiMap; #syncedBlockPerPublicKey: AztecMap; + #contractArtifacts: AztecMap; + #contractInstances: AztecMap; #db: AztecKVStore; - constructor(db: AztecKVStore) { + constructor(private db: AztecKVStore) { this.#db = db; - this.#addresses = db.createArray('addresses'); - this.#addressIndex = db.createMap('address_index'); + this.#addresses = db.openArray('addresses'); + this.#addressIndex = db.openMap('address_index'); + + this.#authWitnesses = db.openMap('auth_witnesses'); + this.#capsules = db.openArray('capsules'); + this.#contracts = db.openMap('contracts'); + + this.#contractArtifacts = db.openMap('contract_artifacts'); + this.#contractInstances = db.openMap('contracts_instances'); + + this.#synchronizedBlock = db.openSingleton('header'); + this.#syncedBlockPerPublicKey = db.openMap('synced_block_per_public_key'); + + this.#notes = db.openMap('notes'); + this.#nullifiedNotes = db.openMap('nullified_notes'); + this.#nullifierToNoteId = db.openMap('nullifier_to_note'); - this.#authWitnesses = db.createMap('auth_witnesses'); - this.#capsules = db.createArray('capsules'); - this.#contracts = db.createMap('contracts'); + this.#notesByContract = db.openMultiMap('notes_by_contract'); + this.#notesByStorageSlot = db.openMultiMap('notes_by_storage_slot'); + this.#notesByTxHash = db.openMultiMap('notes_by_tx_hash'); + this.#notesByOwner = db.openMultiMap('notes_by_owner'); - this.#synchronizedBlock = db.createSingleton('block_header'); - this.#syncedBlockPerPublicKey = db.createMap('synced_block_per_public_key'); + this.#nullifiedNotesByContract = db.openMultiMap('nullified_notes_by_contract'); + this.#nullifiedNotesByStorageSlot = db.openMultiMap('nullified_notes_by_storage_slot'); + this.#nullifiedNotesByTxHash = db.openMultiMap('nullified_notes_by_tx_hash'); + this.#nullifiedNotesByOwner = db.openMultiMap('nullified_notes_by_owner'); + + this.#deferredNotes = db.openArray('deferred_notes'); + this.#deferredNotesByContract = db.openMultiMap('deferred_notes_by_contract'); + } + + public async addContractArtifact(id: Fr, contract: ContractArtifact): Promise { + await this.#contractArtifacts.set(id.toString(), contractArtifactToBuffer(contract)); + } - this.#notes = db.createArray('notes'); - this.#nullifiedNotes = db.createMap('nullified_notes'); + getContractArtifact(id: Fr): Promise { + const contract = this.#contractArtifacts.get(id.toString()); + // TODO(@spalladino): AztecMap lies and returns Uint8Arrays instead of Buffers, hence the extra Buffer.from. + return Promise.resolve(contract && contractArtifactFromBuffer(Buffer.from(contract))); + } - this.#notesByContract = db.createMultiMap('notes_by_contract'); - this.#notesByStorageSlot = db.createMultiMap('notes_by_storage_slot'); - this.#notesByTxHash = db.createMultiMap('notes_by_tx_hash'); - this.#notesByOwner = db.createMultiMap('notes_by_owner'); + async addContractInstance(contract: ContractInstanceWithAddress): Promise { + await this.#contractInstances.set( + contract.address.toString(), + new SerializableContractInstance(contract).toBuffer(), + ); + } - this.#deferredNotes = db.createArray('deferred_notes'); - this.#deferredNotesByContract = db.createMultiMap('deferred_notes_by_contract'); + getContractInstance(address: AztecAddress): Promise { + const contract = this.#contractInstances.get(address.toString()); + return Promise.resolve(contract && SerializableContractInstance.fromBuffer(contract).withAddress(address)); } async addAuthWitness(messageHash: Fr, witness: Fr[]): Promise { @@ -88,17 +120,22 @@ export class KVPxeDatabase implements PxeDatabase { await this.addNotes([note]); } - async addNotes(notes: NoteDao[]): Promise { - const newLength = await this.#notes.push(...notes.map(note => note.toBuffer())); - for (const [index, note] of notes.entries()) { - const noteId = newLength - notes.length + index; - await Promise.all([ - this.#notesByContract.set(note.contractAddress.toString(), noteId), - this.#notesByStorageSlot.set(note.storageSlot.toString(), noteId), - this.#notesByTxHash.set(note.txHash.toString(), noteId), - this.#notesByOwner.set(note.publicKey.toString(), noteId), - ]); - } + addNotes(notes: NoteDao[]): Promise { + return this.db.transaction(() => { + for (const dao of notes) { + // store notes by their index in the notes hash tree + // this provides the uniqueness we need to store individual notes + // and should also return notes in the order that they were created. + // Had we stored them by their nullifier, they would be returned in random order + const noteIndex = toBufferBE(dao.index, 32).toString('hex'); + void this.#notes.set(noteIndex, dao.toBuffer()); + void this.#nullifierToNoteId.set(dao.siloedNullifier.toString(), noteIndex); + void this.#notesByContract.set(dao.contractAddress.toString(), noteIndex); + void this.#notesByStorageSlot.set(dao.storageSlot.toString(), noteIndex); + void this.#notesByTxHash.set(dao.txHash.toString(), noteIndex); + void this.#notesByOwner.set(dao.publicKey.toString(), noteIndex); + } + }); } async addDeferredNotes(deferredNotes: DeferredNoteDao[]): Promise { @@ -129,11 +166,6 @@ export class KVPxeDatabase implements PxeDatabase { * Removes all deferred notes for a given contract address. * @param contractAddress - the contract address to remove deferred notes for * @returns an array of the removed deferred notes - * - * @remarks We only remove indices from the deferred notes by contract map, but not the actual deferred notes. - * This is safe because our only getter for deferred notes is by contract address. - * If we should add a more general getter, we will need a delete vector for deferred notes as well, - * analogous to this.#nullifiedNotes. */ removeDeferredNotesByContract(contractAddress: AztecAddress): Promise { return this.#db.transaction(() => { @@ -149,150 +181,154 @@ export class KVPxeDatabase implements PxeDatabase { } void this.#deferredNotesByContract.deleteValue(contractAddress.toString(), index); + void this.#deferredNotes.setAt(index, null); } return deferredNotes; }); } - *#getAllNonNullifiedNotes(): IterableIterator { - for (const [index, serialized] of this.#notes.entries()) { - if (this.#nullifiedNotes.has(index)) { - continue; - } - - yield NoteDao.fromBuffer(serialized); - } - } - - async getNotes(filter: NoteFilter): Promise { + #getNotes(filter: NoteFilter): NoteDao[] { const publicKey: PublicKey | undefined = filter.owner - ? (await this.getCompleteAddress(filter.owner))?.publicKey + ? this.#getCompleteAddress(filter.owner)?.publicKey : undefined; - const initialNoteIds = publicKey - ? this.#notesByOwner.getValues(publicKey.toString()) - : filter.txHash - ? this.#notesByTxHash.getValues(filter.txHash.toString()) - : filter.contractAddress - ? this.#notesByContract.getValues(filter.contractAddress.toString()) - : filter.storageSlot - ? this.#notesByStorageSlot.getValues(filter.storageSlot.toString()) - : undefined; + filter.status = filter.status ?? NoteStatus.ACTIVE; + + const candidateNoteSources = []; + + candidateNoteSources.push({ + ids: publicKey + ? this.#notesByOwner.getValues(publicKey.toString()) + : filter.txHash + ? this.#notesByTxHash.getValues(filter.txHash.toString()) + : filter.contractAddress + ? this.#notesByContract.getValues(filter.contractAddress.toString()) + : filter.storageSlot + ? this.#notesByStorageSlot.getValues(filter.storageSlot.toString()) + : this.#notes.keys(), + notes: this.#notes, + }); - if (!initialNoteIds) { - return Array.from(this.#getAllNonNullifiedNotes()); + if (filter.status == NoteStatus.ACTIVE_OR_NULLIFIED) { + candidateNoteSources.push({ + ids: publicKey + ? this.#nullifiedNotesByOwner.getValues(publicKey.toString()) + : filter.txHash + ? this.#nullifiedNotesByTxHash.getValues(filter.txHash.toString()) + : filter.contractAddress + ? this.#nullifiedNotesByContract.getValues(filter.contractAddress.toString()) + : filter.storageSlot + ? this.#nullifiedNotesByStorageSlot.getValues(filter.storageSlot.toString()) + : this.#nullifiedNotes.keys(), + notes: this.#nullifiedNotes, + }); } const result: NoteDao[] = []; - for (const noteId of initialNoteIds) { - const serializedNote = this.#notes.at(noteId); - if (!serializedNote) { - continue; - } + for (const { ids, notes } of candidateNoteSources) { + for (const id of ids) { + const serializedNote = notes.get(id); + if (!serializedNote) { + continue; + } - const note = NoteDao.fromBuffer(serializedNote); - if (filter.contractAddress && !note.contractAddress.equals(filter.contractAddress)) { - continue; - } + const note = NoteDao.fromBuffer(serializedNote); + if (filter.contractAddress && !note.contractAddress.equals(filter.contractAddress)) { + continue; + } - if (filter.txHash && !note.txHash.equals(filter.txHash)) { - continue; - } + if (filter.txHash && !note.txHash.equals(filter.txHash)) { + continue; + } - if (filter.storageSlot && !note.storageSlot.equals(filter.storageSlot!)) { - continue; - } + if (filter.storageSlot && !note.storageSlot.equals(filter.storageSlot!)) { + continue; + } - if (publicKey && !note.publicKey.equals(publicKey)) { - continue; - } + if (publicKey && !note.publicKey.equals(publicKey)) { + continue; + } - result.push(note); + result.push(note); + } } return result; } + getNotes(filter: NoteFilter): Promise { + return Promise.resolve(this.#getNotes(filter)); + } + removeNullifiedNotes(nullifiers: Fr[], account: PublicKey): Promise { if (nullifiers.length === 0) { return Promise.resolve([]); } - const nullifierSet = new Set(nullifiers.map(n => n.toString())); + return this.#db.transaction(() => { - const notesIds = this.#notesByOwner.getValues(account.toString()); const nullifiedNotes: NoteDao[] = []; - for (const noteId of notesIds) { - const note = NoteDao.fromBuffer(this.#notes.at(noteId)!); - if (nullifierSet.has(note.siloedNullifier.toString())) { - nullifiedNotes.push(note); + for (const nullifier of nullifiers) { + const noteIndex = this.#nullifierToNoteId.get(nullifier.toString()); + if (!noteIndex) { + continue; + } + + const noteBuffer = noteIndex ? this.#notes.get(noteIndex) : undefined; + + if (!noteBuffer) { + // note doesn't exist. Maybe it got nullified already + continue; + } - void this.#nullifiedNotes.set(noteId, true); - void this.#notesByOwner.deleteValue(account.toString(), noteId); - void this.#notesByTxHash.deleteValue(note.txHash.toString(), noteId); - void this.#notesByContract.deleteValue(note.contractAddress.toString(), noteId); - void this.#notesByStorageSlot.deleteValue(note.storageSlot.toString(), noteId); + const note = NoteDao.fromBuffer(noteBuffer); + if (!note.publicKey.equals(account)) { + // tried to nullify someone else's note + continue; } + + nullifiedNotes.push(note); + + void this.#notes.delete(noteIndex); + void this.#notesByOwner.deleteValue(account.toString(), noteIndex); + void this.#notesByTxHash.deleteValue(note.txHash.toString(), noteIndex); + void this.#notesByContract.deleteValue(note.contractAddress.toString(), noteIndex); + void this.#notesByStorageSlot.deleteValue(note.storageSlot.toString(), noteIndex); + + void this.#nullifiedNotes.set(noteIndex, note.toBuffer()); + void this.#nullifiedNotesByContract.set(note.contractAddress.toString(), noteIndex); + void this.#nullifiedNotesByStorageSlot.set(note.storageSlot.toString(), noteIndex); + void this.#nullifiedNotesByTxHash.set(note.txHash.toString(), noteIndex); + void this.#nullifiedNotesByOwner.set(note.publicKey.toString(), noteIndex); + + void this.#nullifierToNoteId.delete(nullifier.toString()); } return nullifiedNotes; }); } - getTreeRoots(): Record { - const roots = this.#synchronizedBlock.get()?.roots; - if (!roots) { - throw new Error(`Tree roots not set`); - } - - return { - [MerkleTreeId.ARCHIVE]: Fr.fromString(roots[MerkleTreeId.ARCHIVE]), - [MerkleTreeId.CONTRACT_TREE]: Fr.fromString(roots[MerkleTreeId.CONTRACT_TREE].toString()), - [MerkleTreeId.L1_TO_L2_MESSAGE_TREE]: Fr.fromString(roots[MerkleTreeId.L1_TO_L2_MESSAGE_TREE].toString()), - [MerkleTreeId.NOTE_HASH_TREE]: Fr.fromString(roots[MerkleTreeId.NOTE_HASH_TREE].toString()), - [MerkleTreeId.PUBLIC_DATA_TREE]: Fr.fromString(roots[MerkleTreeId.PUBLIC_DATA_TREE].toString()), - [MerkleTreeId.NULLIFIER_TREE]: Fr.fromString(roots[MerkleTreeId.NULLIFIER_TREE].toString()), - }; - } - - async setBlockData(blockNumber: number, blockHeader: BlockHeader): Promise { - await this.#synchronizedBlock.set({ - blockNumber, - globalVariablesHash: blockHeader.globalVariablesHash.toString(), - roots: { - [MerkleTreeId.NOTE_HASH_TREE]: blockHeader.noteHashTreeRoot.toString(), - [MerkleTreeId.NULLIFIER_TREE]: blockHeader.nullifierTreeRoot.toString(), - [MerkleTreeId.CONTRACT_TREE]: blockHeader.contractTreeRoot.toString(), - [MerkleTreeId.L1_TO_L2_MESSAGE_TREE]: blockHeader.l1ToL2MessageTreeRoot.toString(), - [MerkleTreeId.ARCHIVE]: blockHeader.archiveRoot.toString(), - [MerkleTreeId.PUBLIC_DATA_TREE]: blockHeader.publicDataTreeRoot.toString(), - }, - }); + async setHeader(header: Header): Promise { + await this.#synchronizedBlock.set(header.toBuffer()); } getBlockNumber(): number | undefined { - return this.#synchronizedBlock.get()?.blockNumber; + const headerBuffer = this.#synchronizedBlock.get(); + if (!headerBuffer) { + return undefined; + } + + return Number(Header.fromBuffer(headerBuffer).globalVariables.blockNumber.toBigInt()); } - getBlockHeader(): BlockHeader { - const value = this.#synchronizedBlock.get(); - if (!value) { - throw new Error(`Block header not set`); + getHeader(): Header { + const headerBuffer = this.#synchronizedBlock.get(); + if (!headerBuffer) { + throw new Error(`Header not set`); } - const blockHeader = new BlockHeader( - Fr.fromString(value.roots[MerkleTreeId.NOTE_HASH_TREE]), - Fr.fromString(value.roots[MerkleTreeId.NULLIFIER_TREE]), - Fr.fromString(value.roots[MerkleTreeId.CONTRACT_TREE]), - Fr.fromString(value.roots[MerkleTreeId.L1_TO_L2_MESSAGE_TREE]), - Fr.fromString(value.roots[MerkleTreeId.ARCHIVE]), - Fr.ZERO, // todo: private kernel vk tree root - Fr.fromString(value.roots[MerkleTreeId.PUBLIC_DATA_TREE]), - Fr.fromString(value.globalVariablesHash), - ); - - return blockHeader; + return Header.fromBuffer(headerBuffer); } addCompleteAddress(completeAddress: CompleteAddress): Promise { @@ -320,14 +356,18 @@ export class KVPxeDatabase implements PxeDatabase { }); } - getCompleteAddress(address: AztecAddress): Promise { + #getCompleteAddress(address: AztecAddress): CompleteAddress | undefined { const index = this.#addressIndex.get(address.toString()); if (typeof index === 'undefined') { - return Promise.resolve(undefined); + return undefined; } const value = this.#addresses.at(index); - return Promise.resolve(value ? CompleteAddress.fromBuffer(value) : undefined); + return value ? CompleteAddress.fromBuffer(value) : undefined; + } + + getCompleteAddress(address: AztecAddress): Promise { + return Promise.resolve(this.#getCompleteAddress(address)); } getCompleteAddresses(): Promise { @@ -343,7 +383,7 @@ export class KVPxeDatabase implements PxeDatabase { } estimateSize(): number { - const notesSize = Array.from(this.#getAllNonNullifiedNotes()).reduce((sum, note) => sum + note.getSize(), 0); + const notesSize = Array.from(this.#getNotes({})).reduce((sum, note) => sum + note.getSize(), 0); const authWitsSize = Array.from(this.#authWitnesses.values()).reduce( (sum, value) => sum + value.length * Fr.SIZE_IN_BYTES, 0, @@ -355,7 +395,7 @@ export class KVPxeDatabase implements PxeDatabase { } async addContract(contract: ContractDao): Promise { - await this.#contracts.set(contract.completeAddress.address.toString(), contract.toBuffer()); + await this.#contracts.set(contract.instance.address.toString(), contract.toBuffer()); } getContract(address: AztecAddress): Promise { diff --git a/yarn-project/pxe/src/database/memory_db.test.ts b/yarn-project/pxe/src/database/memory_db.test.ts deleted file mode 100644 index f505efa4a79..00000000000 --- a/yarn-project/pxe/src/database/memory_db.test.ts +++ /dev/null @@ -1,67 +0,0 @@ -import { AztecAddress, Fr } from '@aztec/circuits.js'; - -import { MemoryDB } from './memory_db.js'; -import { randomNoteDao } from './note_dao.test.js'; -import { describePxeDatabase } from './pxe_database_test_suite.js'; - -describe('Memory DB', () => { - let db: MemoryDB; - - beforeEach(() => { - db = new MemoryDB(); - }); - - describePxeDatabase(() => db); - - describe('NoteDao', () => { - const contractAddress = AztecAddress.random(); - const storageSlot = Fr.random(); - - const createNotes = (numberOfNotes: number, sameStorage = true) => - Array(numberOfNotes) - .fill(0) - .map(() => - randomNoteDao({ - contractAddress: sameStorage ? contractAddress : AztecAddress.random(), - storageSlot: sameStorage ? storageSlot : Fr.random(), - }), - ); - - it('should add and get notes', async () => { - const notes = createNotes(3, false); - for (let i = 0; i < notes.length; ++i) { - await db.addNote(notes[i]); - } - - for (let i = 0; i < notes.length; ++i) { - const result = await db.getNotes({ - contractAddress: notes[i].contractAddress, - storageSlot: notes[i].storageSlot, - }); - expect(result).toEqual([notes[i]]); - } - }); - - it('should batch add notes', async () => { - const notes = createNotes(3, false); - await db.addNotes(notes); - - for (let i = 0; i < notes.length; ++i) { - const result = await db.getNotes({ - contractAddress: notes[i].contractAddress, - storageSlot: notes[i].storageSlot, - }); - expect(result).toEqual([notes[i]]); - } - }); - - it('should get all notes with the same contract storage slot', async () => { - const notes = createNotes(3); - await db.addNotes(notes); - - const result = await db.getNotes({ contractAddress, storageSlot }); - expect(result.length).toBe(notes.length); - expect(result).toEqual(expect.objectContaining(notes)); - }); - }); -}); diff --git a/yarn-project/pxe/src/database/memory_db.ts b/yarn-project/pxe/src/database/memory_db.ts deleted file mode 100644 index 174d80677c3..00000000000 --- a/yarn-project/pxe/src/database/memory_db.ts +++ /dev/null @@ -1,223 +0,0 @@ -import { MerkleTreeId, NoteFilter } from '@aztec/circuit-types'; -import { BlockHeader, CompleteAddress, PublicKey } from '@aztec/circuits.js'; -import { AztecAddress } from '@aztec/foundation/aztec-address'; -import { Fr, Point } from '@aztec/foundation/fields'; -import { createDebugLogger } from '@aztec/foundation/log'; - -import { MemoryContractDatabase } from '../contract_database/index.js'; -import { DeferredNoteDao } from './deferred_note_dao.js'; -import { NoteDao } from './note_dao.js'; -import { PxeDatabase } from './pxe_database.js'; - -/** - * The MemoryDB class provides an in-memory implementation of a database to manage transactions and auxiliary data. - * It extends the MemoryContractDatabase, allowing it to store contract-related data as well. - * The class offers methods to add, fetch, and remove transaction records and auxiliary data based on various filters such as transaction hash, address, and storage slot. - * As an in-memory database, the stored data will not persist beyond the life of the application instance. - */ -export class MemoryDB extends MemoryContractDatabase implements PxeDatabase { - private notesTable: NoteDao[] = []; - private deferredNotesTable: DeferredNoteDao[] = []; - private treeRoots: Record | undefined; - private globalVariablesHash: Fr | undefined; - private blockNumber: number | undefined; - private addresses: CompleteAddress[] = []; - private authWitnesses: Record = {}; - private syncedBlockPerPublicKey = new Map(); - // A capsule is a "blob" of data that is passed to the contract through an oracle. - // We are using a stack to keep track of the capsules that are passed to the contract. - private capsuleStack: Fr[][] = []; - - constructor(logSuffix?: string) { - super(createDebugLogger(logSuffix ? 'aztec:memory_db_' + logSuffix : 'aztec:memory_db')); - } - - /** - * Add a auth witness to the database. - * @param messageHash - The message hash. - * @param witness - An array of field elements representing the auth witness. - */ - public addAuthWitness(messageHash: Fr, witness: Fr[]): Promise { - this.authWitnesses[messageHash.toString()] = witness; - return Promise.resolve(); - } - - /** - * Fetching the auth witness for a given message hash. - * @param messageHash - The message hash. - * @returns A Promise that resolves to an array of field elements representing the auth witness. - */ - public getAuthWitness(messageHash: Fr): Promise { - return Promise.resolve(this.authWitnesses[messageHash.toString()]); - } - - public addNote(note: NoteDao): Promise { - this.notesTable.push(note); - return Promise.resolve(); - } - - public addDeferredNotes(notes: DeferredNoteDao[]): Promise { - this.deferredNotesTable.push(...notes); - return Promise.resolve(); - } - - public getDeferredNotesByContract(contractAddress: AztecAddress): Promise { - return Promise.resolve(this.deferredNotesTable.filter(note => note.contractAddress.equals(contractAddress))); - } - - public removeDeferredNotesByContract(contractAddress: AztecAddress): Promise { - const removed: DeferredNoteDao[] = []; - this.deferredNotesTable = this.deferredNotesTable.filter(note => { - if (note.contractAddress.equals(contractAddress)) { - removed.push(note); - return false; - } - return true; - }); - return Promise.resolve(removed); - } - - public addCapsule(capsule: Fr[]): Promise { - this.capsuleStack.push(capsule); - return Promise.resolve(); - } - - public popCapsule(): Promise { - return Promise.resolve(this.capsuleStack.pop()); - } - - public addNotes(notes: NoteDao[]) { - this.notesTable.push(...notes); - return Promise.resolve(); - } - - public async getNotes(filter: NoteFilter): Promise { - let ownerPublicKey: PublicKey | undefined; - if (filter.owner !== undefined) { - const ownerCompleteAddress = await this.getCompleteAddress(filter.owner); - if (ownerCompleteAddress === undefined) { - throw new Error(`Owner ${filter.owner.toString()} not found in memory database`); - } - ownerPublicKey = ownerCompleteAddress.publicKey; - } - - return this.notesTable.filter( - note => - (filter.contractAddress == undefined || note.contractAddress.equals(filter.contractAddress)) && - (filter.txHash == undefined || note.txHash.equals(filter.txHash)) && - (filter.storageSlot == undefined || note.storageSlot.equals(filter.storageSlot!)) && - (ownerPublicKey == undefined || note.publicKey.equals(ownerPublicKey!)), - ); - } - - public removeNullifiedNotes(nullifiers: Fr[], account: PublicKey) { - const nullifierSet = new Set(nullifiers.map(nullifier => nullifier.toString())); - const [remaining, removed] = this.notesTable.reduce( - (acc: [NoteDao[], NoteDao[]], note) => { - const nullifier = note.siloedNullifier.toString(); - if (note.publicKey.equals(account) && nullifierSet.has(nullifier)) { - acc[1].push(note); - } else { - acc[0].push(note); - } - return acc; - }, - [[], []], - ); - - this.notesTable = remaining; - - return Promise.resolve(removed); - } - - public getTreeRoots(): Record { - const roots = this.treeRoots; - if (!roots) { - throw new Error(`Tree roots not set in memory database`); - } - return roots; - } - - private setTreeRoots(roots: Record) { - this.treeRoots = roots; - } - - public getBlockHeader(): BlockHeader { - const roots = this.getTreeRoots(); - if (!this.globalVariablesHash) { - throw new Error(`Global variables hash not set in memory database`); - } - return new BlockHeader( - roots[MerkleTreeId.NOTE_HASH_TREE], - roots[MerkleTreeId.NULLIFIER_TREE], - roots[MerkleTreeId.CONTRACT_TREE], - roots[MerkleTreeId.L1_TO_L2_MESSAGE_TREE], - roots[MerkleTreeId.ARCHIVE], - Fr.ZERO, // todo: private kernel vk tree root - roots[MerkleTreeId.PUBLIC_DATA_TREE], - this.globalVariablesHash, - ); - } - - public setBlockData(blockNumber: number, blockHeader: BlockHeader): Promise { - this.globalVariablesHash = blockHeader.globalVariablesHash; - this.blockNumber = blockNumber; - this.setTreeRoots({ - [MerkleTreeId.NOTE_HASH_TREE]: blockHeader.noteHashTreeRoot, - [MerkleTreeId.NULLIFIER_TREE]: blockHeader.nullifierTreeRoot, - [MerkleTreeId.CONTRACT_TREE]: blockHeader.contractTreeRoot, - [MerkleTreeId.L1_TO_L2_MESSAGE_TREE]: blockHeader.l1ToL2MessageTreeRoot, - [MerkleTreeId.ARCHIVE]: blockHeader.archiveRoot, - [MerkleTreeId.PUBLIC_DATA_TREE]: blockHeader.publicDataTreeRoot, - }); - - return Promise.resolve(); - } - - public getBlockNumber(): number | undefined { - return this.blockNumber; - } - - public addCompleteAddress(completeAddress: CompleteAddress): Promise { - const accountIndex = this.addresses.findIndex(r => r.address.equals(completeAddress.address)); - if (accountIndex !== -1) { - if (this.addresses[accountIndex].equals(completeAddress)) { - return Promise.resolve(false); - } - - return Promise.reject( - new Error( - `Complete address with aztec address ${completeAddress.address.toString()} but different public key or partial key already exists in memory database`, - ), - ); - } - this.addresses.push(completeAddress); - return Promise.resolve(true); - } - - public getCompleteAddress(address: AztecAddress): Promise { - const recipient = this.addresses.find(r => r.address.equals(address)); - return Promise.resolve(recipient); - } - - public getCompleteAddresses(): Promise { - return Promise.resolve(this.addresses); - } - - getSynchedBlockNumberForPublicKey(publicKey: Point): number | undefined { - return this.syncedBlockPerPublicKey.get(publicKey.toString()); - } - - setSynchedBlockNumberForPublicKey(publicKey: Point, blockNumber: number): Promise { - this.syncedBlockPerPublicKey.set(publicKey.toString(), blockNumber); - return Promise.resolve(true); - } - - public estimateSize() { - const notesSize = this.notesTable.reduce((sum, note) => sum + note.getSize(), 0); - const treeRootsSize = this.treeRoots ? Object.entries(this.treeRoots).length * Fr.SIZE_IN_BYTES : 0; - const authWits = Object.entries(this.authWitnesses); - const authWitsSize = authWits.reduce((sum, [key, value]) => sum + key.length + value.length * Fr.SIZE_IN_BYTES, 0); - return notesSize + treeRootsSize + authWitsSize + this.addresses.length * CompleteAddress.SIZE_IN_BYTES; - } -} diff --git a/yarn-project/pxe/src/database/note_dao.test.ts b/yarn-project/pxe/src/database/note_dao.test.ts index 34baa9a14dd..113f2de5fba 100644 --- a/yarn-project/pxe/src/database/note_dao.test.ts +++ b/yarn-project/pxe/src/database/note_dao.test.ts @@ -11,7 +11,7 @@ export const randomNoteDao = ({ nonce = Fr.random(), innerNoteHash = Fr.random(), siloedNullifier = Fr.random(), - index = BigInt(0), + index = Fr.random().toBigInt(), publicKey = Point.random(), }: Partial = {}) => { return new NoteDao( diff --git a/yarn-project/pxe/src/database/pxe_database.ts b/yarn-project/pxe/src/database/pxe_database.ts index b08e9932f7c..f97847dfe97 100644 --- a/yarn-project/pxe/src/database/pxe_database.ts +++ b/yarn-project/pxe/src/database/pxe_database.ts @@ -1,8 +1,10 @@ -import { ContractDatabase, MerkleTreeId, NoteFilter } from '@aztec/circuit-types'; -import { BlockHeader, CompleteAddress, PublicKey } from '@aztec/circuits.js'; +import { ContractDatabase, NoteFilter } from '@aztec/circuit-types'; +import { CompleteAddress, Header, PublicKey } from '@aztec/circuits.js'; import { AztecAddress } from '@aztec/foundation/aztec-address'; import { Fr } from '@aztec/foundation/fields'; +import { ContractArtifactDatabase } from './contracts/contract_artifact_db.js'; +import { ContractInstanceDatabase } from './contracts/contract_instance_db.js'; import { DeferredNoteDao } from './deferred_note_dao.js'; import { NoteDao } from './note_dao.js'; @@ -10,7 +12,7 @@ import { NoteDao } from './note_dao.js'; * A database interface that provides methods for retrieving, adding, and removing transactional data related to Aztec * addresses, storage slots, and nullifiers. */ -export interface PxeDatabase extends ContractDatabase { +export interface PxeDatabase extends ContractDatabase, ContractArtifactDatabase, ContractInstanceDatabase { /** * Add a auth witness to the database. * @param messageHash - The message hash. @@ -89,16 +91,6 @@ export interface PxeDatabase extends ContractDatabase { */ removeNullifiedNotes(nullifiers: Fr[], account: PublicKey): Promise; - /** - * Retrieve the stored Merkle tree roots from the database. - * The function returns a Promise that resolves to an object containing the MerkleTreeId as keys - * and their corresponding Fr values as roots. Throws an error if the tree roots are not set in the - * memory database. - * - * @returns An object containing the Merkle tree roots for each merkle tree id. - */ - getTreeRoots(): Record; - /** * Gets the most recently processed block number. * @returns The most recently processed block number or undefined if never synched. @@ -116,18 +108,16 @@ export interface PxeDatabase extends ContractDatabase { * @returns The Block Header. * @throws If no block have been processed yet. */ - getBlockHeader(): BlockHeader; + getHeader(): Header; /** * Set the latest Block Header. - * This function updates the 'global variables hash' and `tree roots` property of the instance * Note that this will overwrite any existing hash or roots in the database. * - * @param blockNumber - The block number of the most recent block - * @param blockHeader - An object containing the most recent block header. + * @param header - An object containing the most recent block header. * @returns A Promise that resolves when the hash has been successfully updated in the database. */ - setBlockData(blockNumber: number, blockHeader: BlockHeader): Promise; + setHeader(header: Header): Promise; /** * Adds complete address to the database. diff --git a/yarn-project/pxe/src/database/pxe_database_test_suite.ts b/yarn-project/pxe/src/database/pxe_database_test_suite.ts index 5464ef98dde..0fceb543e42 100644 --- a/yarn-project/pxe/src/database/pxe_database_test_suite.ts +++ b/yarn-project/pxe/src/database/pxe_database_test_suite.ts @@ -1,6 +1,9 @@ -import { INITIAL_L2_BLOCK_NUM, MerkleTreeId, NoteFilter, randomTxHash } from '@aztec/circuit-types'; -import { AztecAddress, BlockHeader, CompleteAddress } from '@aztec/circuits.js'; +import { INITIAL_L2_BLOCK_NUM, NoteFilter, NoteStatus, randomTxHash } from '@aztec/circuit-types'; +import { AztecAddress, CompleteAddress } from '@aztec/circuits.js'; +import { makeHeader } from '@aztec/circuits.js/factories'; import { Fr, Point } from '@aztec/foundation/fields'; +import { BenchmarkingContractArtifact } from '@aztec/noir-contracts/Benchmarking'; +import { SerializableContractInstance } from '@aztec/types/contracts'; import { NoteDao } from './note_dao.js'; import { randomNoteDao } from './note_dao.test.js'; @@ -110,6 +113,7 @@ export function describePxeDatabase(getDatabase: () => PxeDatabase) { contractAddress: contractAddresses[i % contractAddresses.length], storageSlot: storageSlots[i % storageSlots.length], publicKey: owners[i % owners.length].publicKey, + index: BigInt(i), }), ); }); @@ -132,48 +136,66 @@ export function describePxeDatabase(getDatabase: () => PxeDatabase) { await expect(database.getNotes(getFilter())).resolves.toEqual(getExpected()); }); - it('removes nullified notes', async () => { + it.each(filteringTests)('retrieves nullified notes', async (getFilter, getExpected) => { + await database.addNotes(notes); + + // Nullify all notes and use the same filter as other test cases + for (const owner of owners) { + const notesToNullify = notes.filter(note => note.publicKey.equals(owner.publicKey)); + const nullifiers = notesToNullify.map(note => note.siloedNullifier); + await expect(database.removeNullifiedNotes(nullifiers, owner.publicKey)).resolves.toEqual(notesToNullify); + } + + await expect(database.getNotes({ ...getFilter(), status: NoteStatus.ACTIVE_OR_NULLIFIED })).resolves.toEqual( + getExpected(), + ); + }); + + it('skips nullified notes by default or when requesting active', async () => { + await database.addNotes(notes); + const notesToNullify = notes.filter(note => note.publicKey.equals(owners[0].publicKey)); const nullifiers = notesToNullify.map(note => note.siloedNullifier); + await expect(database.removeNullifiedNotes(nullifiers, notesToNullify[0].publicKey)).resolves.toEqual( + notesToNullify, + ); + + const actualNotesWithDefault = await database.getNotes({}); + const actualNotesWithActive = await database.getNotes({ status: NoteStatus.ACTIVE }); + expect(actualNotesWithDefault).toEqual(actualNotesWithActive); + expect(actualNotesWithActive).toEqual(notes.filter(note => !notesToNullify.includes(note))); + }); + + it('returns active and nullified notes when requesting either', async () => { await database.addNotes(notes); + const notesToNullify = notes.filter(note => note.publicKey.equals(owners[0].publicKey)); + const nullifiers = notesToNullify.map(note => note.siloedNullifier); await expect(database.removeNullifiedNotes(nullifiers, notesToNullify[0].publicKey)).resolves.toEqual( notesToNullify, ); - await expect( - database.getNotes({ - owner: owners[0].address, - }), - ).resolves.toEqual([]); - await expect(database.getNotes({})).resolves.toEqual(notes.filter(note => !notesToNullify.includes(note))); + + const result = await database.getNotes({ + status: NoteStatus.ACTIVE_OR_NULLIFIED, + }); + + // We have to compare the sorted arrays since the database does not return the same order as when originally + // inserted combining active and nullified results. + expect(result.sort()).toEqual([...notes].sort()); }); }); describe('block header', () => { it('stores and retrieves the block header', async () => { - const blockHeader = BlockHeader.random(); - blockHeader.privateKernelVkTreeRoot = Fr.zero(); - - await database.setBlockData(INITIAL_L2_BLOCK_NUM, blockHeader); - expect(database.getBlockHeader()).toEqual(blockHeader); - }); - - it('retrieves the merkle tree roots from the block', async () => { - const blockHeader = BlockHeader.random(); - await database.setBlockData(INITIAL_L2_BLOCK_NUM, blockHeader); - expect(database.getTreeRoots()).toEqual({ - [MerkleTreeId.NOTE_HASH_TREE]: blockHeader.noteHashTreeRoot, - [MerkleTreeId.NULLIFIER_TREE]: blockHeader.nullifierTreeRoot, - [MerkleTreeId.CONTRACT_TREE]: blockHeader.contractTreeRoot, - [MerkleTreeId.L1_TO_L2_MESSAGE_TREE]: blockHeader.l1ToL2MessageTreeRoot, - [MerkleTreeId.ARCHIVE]: blockHeader.archiveRoot, - [MerkleTreeId.PUBLIC_DATA_TREE]: blockHeader.publicDataTreeRoot, - }); + const header = makeHeader(Math.floor(Math.random() * 1000), INITIAL_L2_BLOCK_NUM); + + await database.setHeader(header); + expect(database.getHeader()).toEqual(header); }); - it('rejects getting merkle tree roots if no block set', () => { - expect(() => database.getTreeRoots()).toThrow(); + it('rejects getting header if no block set', () => { + expect(() => database.getHeader()).toThrow(); }); }); @@ -216,5 +238,21 @@ export function describePxeDatabase(getDatabase: () => PxeDatabase) { expect(await database.getCompleteAddress(CompleteAddress.random().address)).toBeUndefined(); }); }); + + describe('contracts', () => { + it('stores a contract artifact', async () => { + const artifact = BenchmarkingContractArtifact; + const id = Fr.random(); + await database.addContractArtifact(id, artifact); + await expect(database.getContractArtifact(id)).resolves.toEqual(artifact); + }); + + it('stores a contract instance', async () => { + const address = AztecAddress.random(); + const instance = SerializableContractInstance.random().withAddress(address); + await database.addContractInstance(instance); + await expect(database.getContractInstance(address)).resolves.toEqual(instance); + }); + }); }); } diff --git a/yarn-project/pxe/src/kernel_oracle/index.ts b/yarn-project/pxe/src/kernel_oracle/index.ts index af05c8dafb6..83df2cf3e6e 100644 --- a/yarn-project/pxe/src/kernel_oracle/index.ts +++ b/yarn-project/pxe/src/kernel_oracle/index.ts @@ -1,5 +1,12 @@ -import { AztecNode, MerkleTreeId } from '@aztec/circuit-types'; -import { AztecAddress, Fr, FunctionSelector, MembershipWitness, NOTE_HASH_TREE_HEIGHT } from '@aztec/circuits.js'; +import { AztecNode, KeyStore } from '@aztec/circuit-types'; +import { + AztecAddress, + Fr, + FunctionSelector, + MembershipWitness, + NOTE_HASH_TREE_HEIGHT, + Point, +} from '@aztec/circuits.js'; import { Tuple } from '@aztec/foundation/serialize'; import { ContractDataOracle } from '../contract_data_oracle/index.js'; @@ -9,7 +16,7 @@ import { ProvingDataOracle } from './../kernel_prover/proving_data_oracle.js'; * A data oracle that provides information needed for simulating a transaction. */ export class KernelOracle implements ProvingDataOracle { - constructor(private contractDataOracle: ContractDataOracle, private node: AztecNode) {} + constructor(private contractDataOracle: ContractDataOracle, private keyStore: KeyStore, private node: AztecNode) {} public async getContractMembershipWitness(contractAddress: AztecAddress) { return await this.contractDataOracle.getContractMembershipWitness(contractAddress); @@ -33,7 +40,11 @@ export class KernelOracle implements ProvingDataOracle { } async getNoteHashTreeRoot(): Promise { - const roots = await this.node.getTreeRoots(); - return roots[MerkleTreeId.NOTE_HASH_TREE]; + const header = await this.node.getHeader(); + return header.state.partial.noteHashTree.root; + } + + public getMasterNullifierSecretKey(nullifierPublicKey: Point) { + return this.keyStore.getNullifierSecretKeyFromPublicKey(nullifierPublicKey); } } diff --git a/yarn-project/pxe/src/kernel_prover/kernel_prover.ts b/yarn-project/pxe/src/kernel_prover/kernel_prover.ts index 0fe2966c80a..7dc509c4986 100644 --- a/yarn-project/pxe/src/kernel_prover/kernel_prover.ts +++ b/yarn-project/pxe/src/kernel_prover/kernel_prover.ts @@ -4,13 +4,16 @@ import { CONTRACT_TREE_HEIGHT, CallRequest, Fr, + GrumpkinScalar, MAX_NEW_COMMITMENTS_PER_TX, MAX_NEW_NULLIFIERS_PER_TX, + MAX_NULLIFIER_KEY_VALIDATION_REQUESTS_PER_TX, MAX_PRIVATE_CALL_STACK_LENGTH_PER_CALL, MAX_PUBLIC_CALL_STACK_LENGTH_PER_CALL, MAX_READ_REQUESTS_PER_CALL, MAX_READ_REQUESTS_PER_TX, MembershipWitness, + NullifierKeyValidationRequestContext, PreviousKernelData, PrivateCallData, PrivateKernelInputsInit, @@ -138,6 +141,7 @@ export class KernelProver { if (firstIteration) { const proofInput = new PrivateKernelInputsInit(txRequest, privateCallData); + pushTestData('private-kernel-inputs-init', proofInput); output = await this.proofCreator.createProofInit(proofInput); } else { const previousVkMembershipWitness = await this.oracle.getVkMembershipWitness(previousVerificationKey); @@ -185,6 +189,10 @@ export class KernelProver { sortedCommitments, ); + const masterNullifierSecretKeys = await this.getMasterNullifierSecretKeys( + output.publicInputs.end.nullifierKeyValidationRequests, + ); + const privateInputs = new PrivateKernelInputsOrdering( previousKernelData, sortedCommitments, @@ -193,7 +201,9 @@ export class KernelProver { sortedNullifiers, sortedNullifiersIndexes, nullifierCommitmentHints, + masterNullifierSecretKeys, ); + pushTestData('private-kernel-inputs-ordering', privateInputs); const outputFinal = await this.proofCreator.createProofOrdering(privateInputs); // Only return the notes whose commitment is in the commitments of the final proof. @@ -356,4 +366,21 @@ export class KernelProver { } return hints; } + + private async getMasterNullifierSecretKeys( + nullifierKeyValidationRequests: Tuple< + NullifierKeyValidationRequestContext, + typeof MAX_NULLIFIER_KEY_VALIDATION_REQUESTS_PER_TX + >, + ) { + const keys = makeTuple(MAX_NULLIFIER_KEY_VALIDATION_REQUESTS_PER_TX, GrumpkinScalar.zero); + for (let i = 0; i < nullifierKeyValidationRequests.length; ++i) { + const request = nullifierKeyValidationRequests[i]; + if (request.isEmpty()) { + break; + } + keys[i] = await this.oracle.getMasterNullifierSecretKey(request.publicKey); + } + return keys; + } } diff --git a/yarn-project/pxe/src/kernel_prover/proving_data_oracle.ts b/yarn-project/pxe/src/kernel_prover/proving_data_oracle.ts index 78fce87a48e..edc6c6c5227 100644 --- a/yarn-project/pxe/src/kernel_prover/proving_data_oracle.ts +++ b/yarn-project/pxe/src/kernel_prover/proving_data_oracle.ts @@ -3,8 +3,10 @@ import { FUNCTION_TREE_HEIGHT, Fr, FunctionSelector, + GrumpkinPrivateKey, MembershipWitness, NOTE_HASH_TREE_HEIGHT, + Point, VK_TREE_HEIGHT, VerificationKey, } from '@aztec/circuits.js'; @@ -66,4 +68,12 @@ export interface ProvingDataOracle { * @returns the root of the note hash tree. */ getNoteHashTreeRoot(): Promise; + + /** + * Get the master secret key of the nullifier public key. + * + * @param nullifierPublicKey - The nullifier public key. + * @returns the master nullifier secret key. + */ + getMasterNullifierSecretKey(nullifierPublicKey: Point): Promise; } diff --git a/yarn-project/pxe/src/note_processor/note_processor.test.ts b/yarn-project/pxe/src/note_processor/note_processor.test.ts index 64d91fb4dcc..0b97ebf0326 100644 --- a/yarn-project/pxe/src/note_processor/note_processor.test.ts +++ b/yarn-project/pxe/src/note_processor/note_processor.test.ts @@ -12,7 +12,7 @@ import { Note, TxL2Logs, } from '@aztec/circuit-types'; -import { EthAddress, Fr, MAX_NEW_COMMITMENTS_PER_TX } from '@aztec/circuits.js'; +import { Fr, MAX_NEW_COMMITMENTS_PER_TX } from '@aztec/circuits.js'; import { Grumpkin } from '@aztec/circuits.js/barretenberg'; import { pedersenHash } from '@aztec/foundation/crypto'; import { Point } from '@aztec/foundation/fields'; @@ -119,7 +119,7 @@ describe('Note Processor', () => { }); beforeEach(async () => { - database = new KVPxeDatabase(await AztecLmdbStore.create(EthAddress.random())); + database = new KVPxeDatabase(await AztecLmdbStore.openTmp()); addNotesSpy = jest.spyOn(database, 'addNotes'); aztecNode = mock(); diff --git a/yarn-project/pxe/src/pxe_http/pxe_http_server.ts b/yarn-project/pxe/src/pxe_http/pxe_http_server.ts index 336f35b710c..300da326589 100644 --- a/yarn-project/pxe/src/pxe_http/pxe_http_server.ts +++ b/yarn-project/pxe/src/pxe_http/pxe_http_server.ts @@ -23,9 +23,6 @@ import { Fr, GrumpkinScalar, Point } from '@aztec/foundation/fields'; import { JsonRpcServer } from '@aztec/foundation/json-rpc/server'; import http from 'http'; -import { foundry } from 'viem/chains'; - -export const localAnvil = foundry; /** * Wraps an instance of Private eXecution Environment (PXE) implementation to a JSON RPC HTTP interface. @@ -55,7 +52,6 @@ export function createPXERpcServer(pxeService: PXE): JsonRpcServer { LogId, }, { Tx, TxReceipt, L2BlockL2Logs }, - false, ['start', 'stop'], ); } diff --git a/yarn-project/pxe/src/pxe_service/create_pxe_service.ts b/yarn-project/pxe/src/pxe_service/create_pxe_service.ts index 2b52e0dfc3b..00d800f1e7a 100644 --- a/yarn-project/pxe/src/pxe_service/create_pxe_service.ts +++ b/yarn-project/pxe/src/pxe_service/create_pxe_service.ts @@ -35,11 +35,8 @@ export async function createPXEService( const keyStorePath = config.dataDirectory ? join(config.dataDirectory, 'pxe_key_store') : undefined; const l1Contracts = await aztecNode.getL1ContractAddresses(); - const keyStore = new TestKeyStore( - new Grumpkin(), - await AztecLmdbStore.create(l1Contracts.rollupAddress, keyStorePath), - ); - const db = new KVPxeDatabase(await AztecLmdbStore.create(l1Contracts.rollupAddress, pxeDbPath)); + const keyStore = new TestKeyStore(new Grumpkin(), await AztecLmdbStore.open(l1Contracts.rollupAddress, keyStorePath)); + const db = new KVPxeDatabase(await AztecLmdbStore.open(l1Contracts.rollupAddress, pxeDbPath)); const server = new PXEService(keyStore, aztecNode, db, config, logSuffix); diff --git a/yarn-project/pxe/src/pxe_service/pxe_service.ts b/yarn-project/pxe/src/pxe_service/pxe_service.ts index 0d732971158..7cdcccef715 100644 --- a/yarn-project/pxe/src/pxe_service/pxe_service.ts +++ b/yarn-project/pxe/src/pxe_service/pxe_service.ts @@ -44,6 +44,10 @@ import { MAX_PUBLIC_CALL_STACK_LENGTH_PER_TX, PartialAddress, PublicCallRequest, + computeSaltedInitializationHash, + getArtifactHash, + getContractClassFromArtifact, + getContractClassId, } from '@aztec/circuits.js'; import { computeCommitmentNonce, siloNullifier } from '@aztec/circuits.js/abis'; import { DecodedReturn, encodeArguments } from '@aztec/foundation/abi'; @@ -52,6 +56,7 @@ import { Fr } from '@aztec/foundation/fields'; import { SerialQueue } from '@aztec/foundation/fifo'; import { DebugLogger, createDebugLogger } from '@aztec/foundation/log'; import { Timer } from '@aztec/foundation/timer'; +import { ContractInstanceWithAddress } from '@aztec/types/contracts'; import { NodeInfo } from '@aztec/types/interfaces'; import { PXEServiceConfig, getPackageInfo } from '../config/index.js'; @@ -153,6 +158,10 @@ export class PXEService implements PXE { return this.db.addCapsule(capsule); } + public getContractInstance(address: AztecAddress): Promise { + return this.db.getContractInstance(address); + } + public async registerAccount(privKey: GrumpkinPrivateKey, partialAddress: PartialAddress): Promise { const completeAddress = CompleteAddress.fromPrivateKeyAndPartialAddress(privKey, partialAddress); const wasAdded = await this.db.addCompleteAddress(completeAddress); @@ -207,19 +216,31 @@ export class PXEService implements PXE { } public async addContracts(contracts: DeployedContract[]) { - const contractDaos = contracts.map(c => new ContractDao(c.artifact, c.completeAddress, c.portalContract)); + const contractDaos = contracts.map(c => new ContractDao(c.artifact, c.instance)); await Promise.all(contractDaos.map(c => this.db.addContract(c))); + await this.addArtifactsAndInstancesFromDeployedContracts(contracts); for (const contract of contractDaos) { - const contractAztecAddress = contract.completeAddress.address; - const portalInfo = - contract.portalContract && !contract.portalContract.isZero() ? ` with portal ${contract.portalContract}` : ''; + const instance = contract.instance; + const contractAztecAddress = instance.address; + const hasPortal = instance.portalContractAddress && !instance.portalContractAddress.isZero(); + const portalInfo = hasPortal ? ` with portal ${instance.portalContractAddress.toChecksumString()}` : ''; this.log.info(`Added contract ${contract.name} at ${contractAztecAddress}${portalInfo}`); await this.synchronizer.reprocessDeferredNotesForContract(contractAztecAddress); } } + private async addArtifactsAndInstancesFromDeployedContracts(contracts: DeployedContract[]) { + for (const contract of contracts) { + const artifact = contract.artifact; + const artifactHash = getArtifactHash(artifact); + const contractClassId = getContractClassId(getContractClassFromArtifact({ ...artifact, artifactHash })); + await this.db.addContractArtifact(contractClassId, artifact); + await this.db.addContractInstance(contract.instance); + } + } + public async getContracts(): Promise { - return (await this.db.getContracts()).map(c => c.completeAddress.address); + return (await this.db.getContracts()).map(c => c.instance.address); } public async getPublicStorageAt(contract: AztecAddress, slot: Fr) { @@ -424,7 +445,7 @@ export class PXEService implements PXE { txHash, TxStatus.MINED, '', - settledTx.blockHash, + settledTx.blockHash.toBuffer(), settledTx.blockNumber, deployedContractAddress, ); @@ -462,7 +483,7 @@ export class PXEService implements PXE { const contract = await this.db.getContract(to); if (!contract) { throw new Error( - `Unknown contract ${to}: add it to PXE Service by calling server.addContracts(...).\nSee docs for context: https://docs.aztec.network/dev_docs/debugging/aztecnr-errors#unknown-contract-0x0-add-it-to-pxe-by-calling-serveraddcontracts`, + `Unknown contract ${to}: add it to PXE Service by calling server.addContracts(...).\nSee docs for context: https://docs.aztec.network/developers/debugging/aztecnr-errors#unknown-contract-0x0-add-it-to-pxe-by-calling-serveraddcontracts`, ); } @@ -614,7 +635,7 @@ export class PXEService implements PXE { // Get values that allow us to reconstruct the block hash const executionResult = await this.#simulate(txExecutionRequest); - const kernelOracle = new KernelOracle(this.contractDataOracle, this.node); + const kernelOracle = new KernelOracle(this.contractDataOracle, this.keyStore, this.node); const kernelProver = new KernelProver(kernelOracle); this.log(`Executing kernel prover...`); const { proof, publicInputs } = await kernelProver.prove(txExecutionRequest.toTxRequest(), executionResult); @@ -625,10 +646,11 @@ export class PXEService implements PXE { const extendedContractData = newContract ? new ExtendedContractData( - new ContractData(newContract.completeAddress.address, newContract.portalContract), + new ContractData(newContract.instance.address, newContract.instance.portalContractAddress), getNewContractPublicFunctions(newContract), - newContract.completeAddress.partialAddress, - newContract.completeAddress.publicKey, + getContractClassFromArtifact(newContract).id, + computeSaltedInitializationHash(newContract.instance), + newContract.instance.publicKeysHash, ) : ExtendedContractData.empty(); diff --git a/yarn-project/pxe/src/pxe_service/test/pxe_service.test.ts b/yarn-project/pxe/src/pxe_service/test/pxe_service.test.ts index 617fdd3de96..7caf5668e35 100644 --- a/yarn-project/pxe/src/pxe_service/test/pxe_service.test.ts +++ b/yarn-project/pxe/src/pxe_service/test/pxe_service.test.ts @@ -14,7 +14,7 @@ import { PXEService } from '../pxe_service.js'; import { pxeTestSuite } from './pxe_test_suite.js'; async function createPXEService(): Promise { - const kvStore = await AztecLmdbStore.create(EthAddress.random()); + const kvStore = await AztecLmdbStore.openTmp(); const keyStore = new TestKeyStore(new Grumpkin(), kvStore); const node = mock(); const db = new KVPxeDatabase(kvStore); @@ -46,7 +46,7 @@ describe('PXEService', () => { let config: PXEServiceConfig; beforeEach(async () => { - const kvStore = await AztecLmdbStore.create(EthAddress.random()); + const kvStore = await AztecLmdbStore.openTmp(); keyStore = new TestKeyStore(new Grumpkin(), kvStore); node = mock(); db = new KVPxeDatabase(kvStore); diff --git a/yarn-project/pxe/src/pxe_service/test/pxe_test_suite.ts b/yarn-project/pxe/src/pxe_service/test/pxe_test_suite.ts index 869e400f731..872cf865cc3 100644 --- a/yarn-project/pxe/src/pxe_service/test/pxe_test_suite.ts +++ b/yarn-project/pxe/src/pxe_service/test/pxe_test_suite.ts @@ -83,7 +83,7 @@ export const pxeTestSuite = (testName: string, pxeSetup: () => Promise) => const contracts: DeployedContract[] = [randomDeployedContract(), randomDeployedContract()]; await pxe.addContracts(contracts); - const expectedContractAddresses = contracts.map(contract => contract.completeAddress.address); + const expectedContractAddresses = contracts.map(contract => contract.instance.address); const contractAddresses = await pxe.getContracts(); // check if all the contracts were returned diff --git a/yarn-project/pxe/src/simulator_oracle/index.ts b/yarn-project/pxe/src/simulator_oracle/index.ts index 50d62d00996..a7b2a4dcd81 100644 --- a/yarn-project/pxe/src/simulator_oracle/index.ts +++ b/yarn-project/pxe/src/simulator_oracle/index.ts @@ -3,11 +3,12 @@ import { KeyStore, L2Block, MerkleTreeId, + NoteStatus, NullifierMembershipWitness, PublicDataWitness, StateInfoProvider, } from '@aztec/circuit-types'; -import { AztecAddress, BlockHeader, CompleteAddress, EthAddress, Fr, FunctionSelector } from '@aztec/circuits.js'; +import { AztecAddress, CompleteAddress, EthAddress, Fr, FunctionSelector, Header } from '@aztec/circuits.js'; import { FunctionArtifactWithDebugMetadata } from '@aztec/foundation/abi'; import { createDebugLogger } from '@aztec/foundation/log'; @@ -37,7 +38,7 @@ export class SimulatorOracle implements DBOracle { const completeAddress = await this.db.getCompleteAddress(address); if (!completeAddress) { throw new Error( - `No public key registered for address ${address.toString()}. Register it by calling pxe.registerRecipient(...) or pxe.registerAccount(...).\nSee docs for context: https://docs.aztec.network/dev_docs/debugging/aztecnr-errors#simulation-error-No-public-key-registered-for-address-0x0-Register-it-by-calling-pxeregisterRecipient-or-pxeregisterAccount`, + `No public key registered for address ${address.toString()}. Register it by calling pxe.registerRecipient(...) or pxe.registerAccount(...).\nSee docs for context: https://docs.aztec.network/developers/debugging/aztecnr-errors#simulation-error-No-public-key-registered-for-address-0x0-Register-it-by-calling-pxeregisterRecipient-or-pxeregisterAccount`, ); } return completeAddress; @@ -59,8 +60,12 @@ export class SimulatorOracle implements DBOracle { return capsule; } - async getNotes(contractAddress: AztecAddress, storageSlot: Fr) { - const noteDaos = await this.db.getNotes({ contractAddress, storageSlot }); + async getNotes(contractAddress: AztecAddress, storageSlot: Fr, status: NoteStatus) { + const noteDaos = await this.db.getNotes({ + contractAddress, + storageSlot, + status, + }); return noteDaos.map(({ contractAddress, storageSlot, nonce, note, innerNoteHash, siloedNullifier, index }) => ({ contractAddress, storageSlot, @@ -186,10 +191,10 @@ export class SimulatorOracle implements DBOracle { * Retrieve the databases view of the Block Header object. * This structure is fed into the circuits simulator and is used to prove against certain historical roots. * - * @returns A Promise that resolves to a BlockHeader object. + * @returns A Promise that resolves to a Header object. */ - getBlockHeader(): Promise { - return Promise.resolve(this.db.getBlockHeader()); + getHeader(): Promise
{ + return Promise.resolve(this.db.getHeader()); } /** diff --git a/yarn-project/pxe/src/synchronizer/synchronizer.test.ts b/yarn-project/pxe/src/synchronizer/synchronizer.test.ts index 19f10de11c8..aa283ba1bec 100644 --- a/yarn-project/pxe/src/synchronizer/synchronizer.test.ts +++ b/yarn-project/pxe/src/synchronizer/synchronizer.test.ts @@ -1,6 +1,7 @@ -import { AztecNode, INITIAL_L2_BLOCK_NUM, L2Block, MerkleTreeId } from '@aztec/circuit-types'; -import { BlockHeader, CompleteAddress, EthAddress, Fr, GrumpkinScalar } from '@aztec/circuits.js'; +import { AztecNode, INITIAL_L2_BLOCK_NUM, L2Block } from '@aztec/circuit-types'; +import { CompleteAddress, Fr, GrumpkinScalar, Header } from '@aztec/circuits.js'; import { Grumpkin } from '@aztec/circuits.js/barretenberg'; +import { makeHeader } from '@aztec/circuits.js/factories'; import { SerialQueue } from '@aztec/foundation/fifo'; import { TestKeyStore } from '@aztec/key-store'; import { AztecLmdbStore } from '@aztec/kv-store'; @@ -16,57 +17,49 @@ describe('Synchronizer', () => { let aztecNode: MockProxy; let database: PxeDatabase; let synchronizer: TestSynchronizer; - let roots: Record; - let blockHeader: BlockHeader; let jobQueue: SerialQueue; + const initialSyncBlockNumber = 3; + let headerBlock3: Header; beforeEach(async () => { - blockHeader = BlockHeader.random(); - roots = { - [MerkleTreeId.CONTRACT_TREE]: blockHeader.contractTreeRoot, - [MerkleTreeId.NOTE_HASH_TREE]: blockHeader.noteHashTreeRoot, - [MerkleTreeId.NULLIFIER_TREE]: blockHeader.nullifierTreeRoot, - [MerkleTreeId.PUBLIC_DATA_TREE]: blockHeader.publicDataTreeRoot, - [MerkleTreeId.L1_TO_L2_MESSAGE_TREE]: blockHeader.l1ToL2MessageTreeRoot, - [MerkleTreeId.ARCHIVE]: blockHeader.archiveRoot, - }; + headerBlock3 = makeHeader(Math.floor(Math.random() * 1000), initialSyncBlockNumber); aztecNode = mock(); - database = new KVPxeDatabase(await AztecLmdbStore.create(EthAddress.random())); + database = new KVPxeDatabase(await AztecLmdbStore.openTmp()); jobQueue = new SerialQueue(); synchronizer = new TestSynchronizer(aztecNode, database, jobQueue); }); - it('sets tree roots from aztec node on initial sync', async () => { - aztecNode.getBlockNumber.mockResolvedValue(3); - aztecNode.getBlockHeader.mockResolvedValue(blockHeader); + it('sets header from aztec node on initial sync', async () => { + aztecNode.getBlockNumber.mockResolvedValue(initialSyncBlockNumber); + aztecNode.getHeader.mockResolvedValue(headerBlock3); await synchronizer.initialSync(); - expect(database.getTreeRoots()).toEqual(roots); + expect(database.getHeader()).toEqual(headerBlock3); }); - it('sets tree roots from latest block', async () => { + it('sets header from latest block', async () => { const block = L2Block.random(1, 4); aztecNode.getBlocks.mockResolvedValue([L2Block.fromFields(omit(block, 'newEncryptedLogs', 'newUnencryptedLogs'))]); aztecNode.getLogs.mockResolvedValueOnce([block.newEncryptedLogs!]).mockResolvedValue([block.newUnencryptedLogs!]); await synchronizer.work(); - const roots = database.getTreeRoots(); - expect(roots[MerkleTreeId.CONTRACT_TREE]).toEqual(block.header.state.partial.contractTree.root); + const obtainedHeader = database.getHeader(); + expect(obtainedHeader).toEqual(block.header); }); - it('overrides tree roots from initial sync once current block number is larger', async () => { + it('overrides header from initial sync once current block number is larger', async () => { // Initial sync is done on block with height 3 - aztecNode.getBlockNumber.mockResolvedValue(3); - aztecNode.getBlockHeader.mockResolvedValue(blockHeader); + aztecNode.getBlockNumber.mockResolvedValue(initialSyncBlockNumber); + aztecNode.getHeader.mockResolvedValue(headerBlock3); await synchronizer.initialSync(); - const roots0 = database.getTreeRoots(); - expect(roots0[MerkleTreeId.CONTRACT_TREE]).toEqual(roots[MerkleTreeId.CONTRACT_TREE]); + const header0 = database.getHeader(); + expect(header0).toEqual(headerBlock3); - // We then process block with height 1, this should not change tree roots + // We then process block with height 1, this should not change the header const block1 = L2Block.random(1, 4); aztecNode.getBlocks.mockResolvedValueOnce([ L2Block.fromFields(omit(block1, 'newEncryptedLogs', 'newUnencryptedLogs')), @@ -74,9 +67,9 @@ describe('Synchronizer', () => { aztecNode.getLogs.mockResolvedValue([block1.newEncryptedLogs!]).mockResolvedValue([block1.newUnencryptedLogs!]); await synchronizer.work(); - const roots1 = database.getTreeRoots(); - expect(roots1[MerkleTreeId.CONTRACT_TREE]).toEqual(roots[MerkleTreeId.CONTRACT_TREE]); - expect(roots1[MerkleTreeId.CONTRACT_TREE]).not.toEqual(block1.header.state.partial.contractTree.root); + const header1 = database.getHeader(); + expect(header1).toEqual(headerBlock3); + expect(header1).not.toEqual(block1.header); // But they should change when we process block with height 5 const block5 = L2Block.random(5, 4); @@ -85,9 +78,9 @@ describe('Synchronizer', () => { ]); await synchronizer.work(); - const roots5 = database.getTreeRoots(); - expect(roots5[MerkleTreeId.CONTRACT_TREE]).not.toEqual(roots[MerkleTreeId.CONTRACT_TREE]); - expect(roots5[MerkleTreeId.CONTRACT_TREE]).toEqual(block5.header.state.partial.contractTree.root); + const header5 = database.getHeader(); + expect(header5).not.toEqual(headerBlock3); + expect(header5).toEqual(block5.header); }); it('note processor successfully catches up', async () => { @@ -121,7 +114,7 @@ describe('Synchronizer', () => { expect(await synchronizer.isGlobalStateSynchronized()).toBe(true); // Manually adding account to database so that we can call synchronizer.isAccountStateSynchronized - const keyStore = new TestKeyStore(new Grumpkin(), await AztecLmdbStore.create(EthAddress.random())); + const keyStore = new TestKeyStore(new Grumpkin(), await AztecLmdbStore.openTmp()); const addAddress = async (startingBlockNum: number) => { const privateKey = GrumpkinScalar.random(); await keyStore.addAccount(privateKey); diff --git a/yarn-project/pxe/src/synchronizer/synchronizer.ts b/yarn-project/pxe/src/synchronizer/synchronizer.ts index d0088fd9cc4..cf1f2af66b1 100644 --- a/yarn-project/pxe/src/synchronizer/synchronizer.ts +++ b/yarn-project/pxe/src/synchronizer/synchronizer.ts @@ -9,8 +9,7 @@ import { TxHash, } from '@aztec/circuit-types'; import { NoteProcessorCaughtUpStats } from '@aztec/circuit-types/stats'; -import { AztecAddress, BlockHeader, Fr, PublicKey } from '@aztec/circuits.js'; -import { computeGlobalsHash } from '@aztec/circuits.js/abis'; +import { AztecAddress, Fr, PublicKey } from '@aztec/circuits.js'; import { SerialQueue } from '@aztec/foundation/fifo'; import { DebugLogger, createDebugLogger } from '@aztec/foundation/log'; import { RunningPromise } from '@aztec/foundation/running-promise'; @@ -62,12 +61,9 @@ export class Synchronizer { protected async initialSync() { // fast forward to the latest block - const [latestBlockNumber, latestBlockHeader] = await Promise.all([ - this.node.getBlockNumber(), - this.node.getBlockHeader(), - ]); - this.initialSyncBlockNumber = latestBlockNumber; - await this.db.setBlockData(latestBlockNumber, latestBlockHeader); + const latestHeader = await this.node.getHeader(); + this.initialSyncBlockNumber = Number(latestHeader.globalVariables.blockNumber.toBigInt()); + await this.db.setHeader(latestHeader); } /** @@ -144,7 +140,7 @@ export class Synchronizer { // Update latest tree roots from the most recent block const latestBlock = blockContexts[blockContexts.length - 1]; - await this.setBlockDataFromBlock(latestBlock); + await this.setHeaderFromBlock(latestBlock); const logCount = L2BlockL2Logs.getTotalLogCount(encryptedLogs); this.log(`Forwarding ${logCount} encrypted logs and blocks to ${this.noteProcessors.length} note processors`); @@ -270,25 +266,13 @@ export class Synchronizer { } } - private async setBlockDataFromBlock(latestBlock: L2BlockContext) { + private async setHeaderFromBlock(latestBlock: L2BlockContext) { const { block } = latestBlock; if (block.number < this.initialSyncBlockNumber) { return; } - const globalsHash = computeGlobalsHash(latestBlock.block.header.globalVariables); - const blockHeader = new BlockHeader( - block.header.state.partial.noteHashTree.root, - block.header.state.partial.nullifierTree.root, - block.header.state.partial.contractTree.root, - block.header.state.l1ToL2MessageTree.root, - block.archive.root, - Fr.ZERO, // todo: private kernel vk tree root - block.header.state.partial.publicDataTree.root, - globalsHash, - ); - - await this.db.setBlockData(block.number, blockHeader); + await this.db.setHeader(block.header); } /** diff --git a/yarn-project/pxe/tsconfig.json b/yarn-project/pxe/tsconfig.json index f78f8c2e76a..4af0483e9f2 100644 --- a/yarn-project/pxe/tsconfig.json +++ b/yarn-project/pxe/tsconfig.json @@ -35,6 +35,9 @@ }, { "path": "../types" + }, + { + "path": "../noir-contracts" } ], "include": ["src"] diff --git a/yarn-project/sequencer-client/package.json b/yarn-project/sequencer-client/package.json index 8c11e1f877f..40bf5fd98ee 100644 --- a/yarn-project/sequencer-client/package.json +++ b/yarn-project/sequencer-client/package.json @@ -49,6 +49,7 @@ "viem": "^1.2.5" }, "devDependencies": { + "@aztec/kv-store": "workspace:^", "@jest/globals": "^29.5.0", "@types/jest": "^29.5.0", "@types/levelup": "^5.1.2", diff --git a/yarn-project/sequencer-client/src/block_builder/solo_block_builder.test.ts b/yarn-project/sequencer-client/src/block_builder/solo_block_builder.test.ts index 3ab5e249e60..470ce36f9cc 100644 --- a/yarn-project/sequencer-client/src/block_builder/solo_block_builder.test.ts +++ b/yarn-project/sequencer-client/src/block_builder/solo_block_builder.test.ts @@ -15,6 +15,7 @@ import { BaseOrMergeRollupPublicInputs, Fr, GlobalVariables, + Header, KernelCircuitPublicInputs, MAX_NEW_COMMITMENTS_PER_TX, MAX_NEW_L2_TO_L1_MSGS_PER_TX, @@ -33,7 +34,7 @@ import { SideEffectLinkedToNoteHash, StateReference, } from '@aztec/circuits.js'; -import { computeBlockHashWithGlobals, computeContractLeaf } from '@aztec/circuits.js/abis'; +import { computeContractLeaf } from '@aztec/circuits.js/abis'; import { fr, makeBaseOrMergeRollupPublicInputs, @@ -49,10 +50,10 @@ import { makeTuple, range } from '@aztec/foundation/array'; import { toBufferBE } from '@aztec/foundation/bigint-buffer'; import { times } from '@aztec/foundation/collection'; import { to2Fields } from '@aztec/foundation/serialize'; +import { AztecLmdbStore } from '@aztec/kv-store'; import { MerkleTreeOperations, MerkleTrees } from '@aztec/world-state'; import { MockProxy, mock } from 'jest-mock-extended'; -import { default as levelup } from 'levelup'; import { type MemDown, default as memdown } from 'memdown'; import { VerificationKeys, getVerificationKeys } from '../mocks/verification_keys.js'; @@ -63,7 +64,6 @@ import { makeEmptyProcessedTx as makeEmptyProcessedTxFromHistoricalTreeRoots, makeProcessedTx, } from '../sequencer/processed_tx.js'; -import { getBlockHeader } from '../sequencer/utils.js'; import { RollupSimulator } from '../simulator/index.js'; import { RealRollupCircuitSimulator } from '../simulator/rollup.js'; import { SoloBlockBuilder } from './solo_block_builder.js'; @@ -96,8 +96,8 @@ describe('sequencer/solo_block_builder', () => { blockNumber = 3; globalVariables = new GlobalVariables(chainId, version, new Fr(blockNumber), Fr.ZERO); - builderDb = await MerkleTrees.new(levelup(createMemDown())).then(t => t.asLatest()); - expectsDb = await MerkleTrees.new(levelup(createMemDown())).then(t => t.asLatest()); + builderDb = await MerkleTrees.new(await AztecLmdbStore.openTmp()).then(t => t.asLatest()); + expectsDb = await MerkleTrees.new(await AztecLmdbStore.openTmp()).then(t => t.asLatest()); vks = getVerificationKeys(); simulator = mock(); prover = mock(); @@ -109,7 +109,8 @@ describe('sequencer/solo_block_builder', () => { // Create mock outputs for simulator baseRollupOutputLeft = makeBaseOrMergeRollupPublicInputs(0, globalVariables); baseRollupOutputRight = makeBaseOrMergeRollupPublicInputs(0, globalVariables); - rootRollupOutput = makeRootRollupPublicInputs(0, globalVariables); + rootRollupOutput = makeRootRollupPublicInputs(0); + rootRollupOutput.header.globalVariables = globalVariables; // Set up mocks prover.getBaseRollupProof.mockResolvedValue(emptyProof); @@ -121,8 +122,8 @@ describe('sequencer/solo_block_builder', () => { }, 20_000); const makeEmptyProcessedTx = async () => { - const historicalTreeRoots = await getBlockHeader(builderDb); - return makeEmptyProcessedTxFromHistoricalTreeRoots(historicalTreeRoots, chainId, version); + const header = await builderDb.buildInitialHeader(); + return makeEmptyProcessedTxFromHistoricalTreeRoots(header, chainId, version); }; // Updates the expectedDb trees based on the new commitments, contracts, and nullifiers from these txs @@ -156,14 +157,7 @@ describe('sequencer/solo_block_builder', () => { }; const updateArchive = async () => { - const blockHash = computeBlockHashWithGlobals( - globalVariables, - rootRollupOutput.header.state.partial.noteHashTree.root, - rootRollupOutput.header.state.partial.nullifierTree.root, - rootRollupOutput.header.state.partial.contractTree.root, - rootRollupOutput.header.state.l1ToL2MessageTree.root, - rootRollupOutput.header.state.partial.publicDataTree.root, - ); + const blockHash = rootRollupOutput.header.hash(); await expectsDb.appendLeaves(MerkleTreeId.ARCHIVE, [blockHash.toBuffer()]); }; @@ -190,7 +184,7 @@ describe('sequencer/solo_block_builder', () => { const buildMockSimulatorInputs = async () => { const kernelOutput = makePrivateKernelPublicInputsFinal(); - kernelOutput.constants.blockHeader = await getBlockHeader(expectsDb); + kernelOutput.constants.historicalHeader = await expectsDb.buildInitialHeader(); const tx = await makeProcessedTx( new Tx( @@ -213,15 +207,8 @@ describe('sequencer/solo_block_builder', () => { await updateExpectedTreesFromTxs([txs[1]]); baseRollupOutputRight.end = await getPartialStateReference(); - // Update l1 to l2 data tree - // And update the root trees now to create proper output to the root rollup circuit + // Update l1 to l2 message tree await updateL1ToL2MessageTree(mockL1ToL2Messages); - rootRollupOutput.header.state = await getStateReference(); - - // Calculate block hash - rootRollupOutput.header.globalVariables = globalVariables; - await updateArchive(); - rootRollupOutput.archive = await getTreeSnapshot(MerkleTreeId.ARCHIVE); const newNullifiers = txs.flatMap(tx => tx.data.end.newNullifiers); const newCommitments = txs.flatMap(tx => tx.data.end.newCommitments); @@ -236,9 +223,11 @@ describe('sequencer/solo_block_builder', () => { const newEncryptedLogs = new L2BlockL2Logs(txs.map(tx => tx.encryptedLogs || new TxL2Logs([]))); const newUnencryptedLogs = new L2BlockL2Logs(txs.map(tx => tx.unencryptedLogs || new TxL2Logs([]))); + // We are constructing the block here just to get body hash/calldata hash so we can pass in an empty archive and header const l2Block = L2Block.fromFields({ - archive: rootRollupOutput.archive, - header: rootRollupOutput.header, + archive: AppendOnlyTreeSnapshot.zero(), + header: Header.empty(), + // Only the values below go to body hash/calldata hash newCommitments: newCommitments.map((sideEffect: SideEffect) => sideEffect.value), newNullifiers: newNullifiers.map((sideEffect: SideEffectLinkedToNoteHash) => sideEffect.value), newContracts, @@ -250,7 +239,13 @@ describe('sequencer/solo_block_builder', () => { newUnencryptedLogs, }); + // Now we update can make the final header, compute the block hash and update archive + rootRollupOutput.header.globalVariables = globalVariables; rootRollupOutput.header.bodyHash = l2Block.getCalldataHash(); + rootRollupOutput.header.state = await getStateReference(); + + await updateArchive(); + rootRollupOutput.archive = await getTreeSnapshot(MerkleTreeId.ARCHIVE); return txs; }; @@ -296,7 +291,7 @@ describe('sequencer/solo_block_builder', () => { const makeBloatedProcessedTx = async (seed = 0x1) => { const tx = mockTx(seed); const kernelOutput = KernelCircuitPublicInputs.empty(); - kernelOutput.constants.blockHeader = await getBlockHeader(builderDb); + kernelOutput.constants.historicalHeader = await builderDb.buildInitialHeader(); kernelOutput.end.publicDataUpdateRequests = makeTuple( MAX_PUBLIC_DATA_UPDATE_REQUESTS_PER_TX, i => new PublicDataUpdateRequest(fr(i), fr(0), fr(i + 10)), @@ -364,7 +359,7 @@ describe('sequencer/solo_block_builder', () => { const [l2Block] = await builder.buildL2Block(globalVariables, txs, mockL1ToL2Messages); expect(l2Block.number).toEqual(blockNumber); - }, 10_000); + }, 30_000); it('builds a mixed L2 block', async () => { // Ensure that each transaction has unique (non-intersecting nullifier values) diff --git a/yarn-project/sequencer-client/src/block_builder/solo_block_builder.ts b/yarn-project/sequencer-client/src/block_builder/solo_block_builder.ts index 72a4fbf741e..488b7191456 100644 --- a/yarn-project/sequencer-client/src/block_builder/solo_block_builder.ts +++ b/yarn-project/sequencer-client/src/block_builder/solo_block_builder.ts @@ -42,12 +42,7 @@ import { VK_TREE_HEIGHT, VerificationKey, } from '@aztec/circuits.js'; -import { - computeBlockHash, - computeBlockHashWithGlobals, - computeContractLeaf, - computeGlobalsHash, -} from '@aztec/circuits.js/abis'; +import { computeContractLeaf } from '@aztec/circuits.js/abis'; import { makeTuple } from '@aztec/foundation/array'; import { toBigIntBE } from '@aztec/foundation/bigint-buffer'; import { padArrayEnd } from '@aztec/foundation/collection'; @@ -100,7 +95,7 @@ export class SoloBlockBuilder implements BlockBuilder { txs: ProcessedTx[], newL1ToL2Messages: Fr[], ): Promise<[L2Block, Proof]> { - // Check txs are good for processing + // Check txs are good for processing by checking if all the tree snapshots in header are non-empty this.validateTxs(txs); // We fill the tx batch with empty txs, we process only one tx at a time for now @@ -157,15 +152,21 @@ export class SoloBlockBuilder implements BlockBuilder { protected validateTxs(txs: ProcessedTx[]) { for (const tx of txs) { - for (const historicalTreeRoot of [ - 'noteHashTreeRoot', - 'contractTreeRoot', - 'nullifierTreeRoot', - 'l1ToL2MessageTreeRoot', - ] as const) { - if (tx.data.constants.blockHeader[historicalTreeRoot].isZero()) { - throw new Error(`Empty ${historicalTreeRoot} for tx: ${toFriendlyJSON(tx)}`); - } + const txHeader = tx.data.constants.historicalHeader; + if (txHeader.state.l1ToL2MessageTree.isZero()) { + throw new Error(`Empty L1 to L2 messages tree in tx: ${toFriendlyJSON(tx)}`); + } + if (txHeader.state.partial.noteHashTree.isZero()) { + throw new Error(`Empty note hash tree in tx: ${toFriendlyJSON(tx)}`); + } + if (txHeader.state.partial.nullifierTree.isZero()) { + throw new Error(`Empty nullifier tree in tx: ${toFriendlyJSON(tx)}`); + } + if (txHeader.state.partial.contractTree.isZero()) { + throw new Error(`Empty contract tree in tx: ${toFriendlyJSON(tx)}`); + } + if (txHeader.state.partial.publicDataTree.isZero()) { + throw new Error(`Empty public data tree in tx: ${toFriendlyJSON(tx)}`); } } } @@ -270,48 +271,15 @@ export class SoloBlockBuilder implements BlockBuilder { const rootProof = await this.prover.getRootRollupProof(rootInput, rootOutput); - // Update the root trees with the latest data and contract tree roots, - // and validate them against the output of the root circuit simulation + // Update the archive with the latest block header this.debug(`Updating and validating root trees`); - const globalVariablesHash = computeGlobalsHash(left[0].constants.globalVariables); - await this.db.updateLatestGlobalVariablesHash(globalVariablesHash); - await this.db.updateArchive(globalVariablesHash); + await this.db.updateArchive(rootOutput.header); await this.validateRootOutput(rootOutput); return [rootOutput, rootProof]; } - async updateArchive(globalVariables: GlobalVariables) { - // Calculate the block hash and add it to the historical block hashes tree - const blockHash = await this.calculateBlockHash(globalVariables); - await this.db.appendLeaves(MerkleTreeId.ARCHIVE, [blockHash.toBuffer()]); - } - - protected async calculateBlockHash(globals: GlobalVariables) { - const [noteHashTreeRoot, nullifierTreeRoot, contractTreeRoot, publicDataTreeRoot, l1ToL2MessageTreeRoot] = ( - await Promise.all( - [ - MerkleTreeId.NOTE_HASH_TREE, - MerkleTreeId.NULLIFIER_TREE, - MerkleTreeId.CONTRACT_TREE, - MerkleTreeId.PUBLIC_DATA_TREE, - MerkleTreeId.L1_TO_L2_MESSAGE_TREE, - ].map(tree => this.getTreeSnapshot(tree)), - ) - ).map(r => r.root); - - const blockHash = computeBlockHashWithGlobals( - globals, - noteHashTreeRoot, - nullifierTreeRoot, - contractTreeRoot, - l1ToL2MessageTreeRoot, - publicDataTreeRoot, - ); - return blockHash; - } - protected async validatePartialState(partialState: PartialStateReference) { await Promise.all([ this.validateSimulatedTree( @@ -485,21 +453,6 @@ export class SoloBlockBuilder implements BlockBuilder { return new MembershipWitness(height, index, assertLength(path.toFieldArray(), height)); } - protected getHistoricalTreesMembershipWitnessFor(tx: ProcessedTx) { - const blockHeader = tx.data.constants.blockHeader; - const { noteHashTreeRoot, nullifierTreeRoot, contractTreeRoot, l1ToL2MessageTreeRoot, publicDataTreeRoot } = - blockHeader; - const blockHash = computeBlockHash( - blockHeader.globalVariablesHash, - noteHashTreeRoot, - nullifierTreeRoot, - contractTreeRoot, - l1ToL2MessageTreeRoot, - publicDataTreeRoot, - ); - return this.getMembershipWitnessFor(blockHash, MerkleTreeId.ARCHIVE, ARCHIVE_HEIGHT); - } - protected async getConstantRollupData(globalVariables: GlobalVariables): Promise { return ConstantRollupData.from({ baseRollupVkHash: DELETE_FR, @@ -729,6 +682,13 @@ export class SoloBlockBuilder implements BlockBuilder { publicDataSiblingPath, }); + const blockHash = tx.data.constants.historicalHeader.hash(); + const archiveRootMembershipWitness = await this.getMembershipWitnessFor( + blockHash, + MerkleTreeId.ARCHIVE, + ARCHIVE_HEIGHT, + ); + return BaseRollupInputs.from({ kernelData: this.getKernelDataFor(tx), start, @@ -741,7 +701,7 @@ export class SoloBlockBuilder implements BlockBuilder { publicDataReadsPreimages: txPublicDataReadsInfo.newPublicDataReadsPreimages, publicDataReadsMembershipWitnesses: txPublicDataReadsInfo.newPublicDataReadsWitnesses, - archiveRootMembershipWitness: await this.getHistoricalTreesMembershipWitnessFor(tx), + archiveRootMembershipWitness, constants, }); diff --git a/yarn-project/sequencer-client/src/publisher/viem-tx-sender.ts b/yarn-project/sequencer-client/src/publisher/viem-tx-sender.ts index 33911815709..68710ec195d 100644 --- a/yarn-project/sequencer-client/src/publisher/viem-tx-sender.ts +++ b/yarn-project/sequencer-client/src/publisher/viem-tx-sender.ts @@ -194,9 +194,9 @@ export class ViemTxSender implements L1PublisherTxSender { extendedContractData.contractData.contractAddress.toString() as Hex, extendedContractData.contractData.portalContractAddress.toString() as Hex, `0x${l2BlockHash.toString('hex')}`, - extendedContractData.partialAddress.toString(), - extendedContractData.publicKey.x.toString(), - extendedContractData.publicKey.y.toString(), + extendedContractData.contractClassId.toString(), + extendedContractData.saltedInitializationHash.toString(), + extendedContractData.publicKeyHash.toString(), `0x${extendedContractData.bytecode.toString('hex')}`, ] as const; diff --git a/yarn-project/sequencer-client/src/sequencer/index.ts b/yarn-project/sequencer-client/src/sequencer/index.ts index 43a83d121a1..4e1a69cbc9b 100644 --- a/yarn-project/sequencer-client/src/sequencer/index.ts +++ b/yarn-project/sequencer-client/src/sequencer/index.ts @@ -1,3 +1,2 @@ export * from './sequencer.js'; export * from './config.js'; -export * from './utils.js'; diff --git a/yarn-project/sequencer-client/src/sequencer/processed_tx.ts b/yarn-project/sequencer-client/src/sequencer/processed_tx.ts index 20df25c26b1..cecd49aa844 100644 --- a/yarn-project/sequencer-client/src/sequencer/processed_tx.ts +++ b/yarn-project/sequencer-client/src/sequencer/processed_tx.ts @@ -1,8 +1,8 @@ import { ExtendedContractData, Tx, TxHash, TxL2Logs } from '@aztec/circuit-types'; import { - BlockHeader, CombinedAccumulatedData, Fr, + Header, Proof, PublicKernelPublicInputs, makeEmptyProof, @@ -87,9 +87,9 @@ export async function makeProcessedTx( * Makes an empty tx from an empty kernel circuit public inputs. * @returns A processed empty tx. */ -export function makeEmptyProcessedTx(historicalTreeRoots: BlockHeader, chainId: Fr, version: Fr): Promise { +export function makeEmptyProcessedTx(header: Header, chainId: Fr, version: Fr): Promise { const emptyKernelOutput = PublicKernelPublicInputs.empty(); - emptyKernelOutput.constants.blockHeader = historicalTreeRoots; + emptyKernelOutput.constants.historicalHeader = header; emptyKernelOutput.constants.txContext.chainId = chainId; emptyKernelOutput.constants.txContext.version = version; const emptyProof = makeEmptyProof(); diff --git a/yarn-project/sequencer-client/src/sequencer/public_processor.test.ts b/yarn-project/sequencer-client/src/sequencer/public_processor.test.ts index 14174865bdb..5bb68a03ec3 100644 --- a/yarn-project/sequencer-client/src/sequencer/public_processor.test.ts +++ b/yarn-project/sequencer-client/src/sequencer/public_processor.test.ts @@ -11,7 +11,6 @@ import { import { ARGS_LENGTH, AztecAddress, - BlockHeader, CallContext, CallRequest, CombinedAccumulatedData, @@ -19,6 +18,7 @@ import { Fr, FunctionData, GlobalVariables, + Header, MAX_PRIVATE_CALL_STACK_LENGTH_PER_TX, MAX_PUBLIC_CALL_STACK_LENGTH_PER_TX, PUBLIC_DATA_TREE_HEIGHT, @@ -84,7 +84,7 @@ describe('public_processor', () => { publicKernel, publicProver, GlobalVariables.empty(), - BlockHeader.empty(), + Header.empty(), publicContractsDB, publicWorldStateDB, ); @@ -139,7 +139,7 @@ describe('public_processor', () => { publicKernel, publicProver, GlobalVariables.empty(), - BlockHeader.empty(), + Header.empty(), publicContractsDB, publicWorldStateDB, ); diff --git a/yarn-project/sequencer-client/src/sequencer/public_processor.ts b/yarn-project/sequencer-client/src/sequencer/public_processor.ts index 3d6010530f9..ebc2c87aa93 100644 --- a/yarn-project/sequencer-client/src/sequencer/public_processor.ts +++ b/yarn-project/sequencer-client/src/sequencer/public_processor.ts @@ -11,13 +11,13 @@ import { ContractDataSource, FunctionL2Logs, L1ToL2MessageSource, MerkleTreeId, import { TxSequencerProcessingStats } from '@aztec/circuit-types/stats'; import { AztecAddress, - BlockHeader, CallRequest, CombinedAccumulatedData, ContractStorageRead, ContractStorageUpdateRequest, Fr, GlobalVariables, + Header, KernelCircuitPublicInputs, MAX_NEW_COMMITMENTS_PER_CALL, MAX_NEW_L2_TO_L1_MSGS_PER_CALL, @@ -56,7 +56,6 @@ import { PublicKernelCircuitSimulator } from '../simulator/index.js'; import { ContractsDataSourcePublicDB, WorldStateDB, WorldStatePublicDB } from '../simulator/public_executor.js'; import { RealPublicKernelCircuitSimulator } from '../simulator/public_kernel.js'; import { FailedTx, ProcessedTx, makeEmptyProcessedTx, makeProcessedTx } from './processed_tx.js'; -import { getBlockHeader } from './utils.js'; /** * Creates new instances of PublicProcessor given the provided merkle tree db and contract data source. @@ -70,27 +69,28 @@ export class PublicProcessorFactory { /** * Creates a new instance of a PublicProcessor. - * @param prevGlobalVariables - The global variables for the previous block, used to calculate the prev global variables hash. + * @param historicalHeader - The header of a block previous to the one in which the tx is included. * @param globalVariables - The global variables for the block being processed. * @param newContracts - Provides access to contract bytecode for public executions. * @returns A new instance of a PublicProcessor. */ public async create( - prevGlobalVariables: GlobalVariables, + historicalHeader: Header | undefined, globalVariables: GlobalVariables, ): Promise { - const blockHeader = await getBlockHeader(this.merkleTree, prevGlobalVariables); + historicalHeader = historicalHeader ?? (await this.merkleTree.buildInitialHeader()); + const publicContractsDB = new ContractsDataSourcePublicDB(this.contractDataSource); const worldStatePublicDB = new WorldStatePublicDB(this.merkleTree); const worldStateDB = new WorldStateDB(this.merkleTree, this.l1Tol2MessagesDataSource); - const publicExecutor = new PublicExecutor(worldStatePublicDB, publicContractsDB, worldStateDB, blockHeader); + const publicExecutor = new PublicExecutor(worldStatePublicDB, publicContractsDB, worldStateDB, historicalHeader); return new PublicProcessor( this.merkleTree, publicExecutor, new RealPublicKernelCircuitSimulator(), new EmptyPublicProver(), globalVariables, - blockHeader, + historicalHeader, publicContractsDB, worldStatePublicDB, ); @@ -108,7 +108,7 @@ export class PublicProcessor { protected publicKernel: PublicKernelCircuitSimulator, protected publicProver: PublicProver, protected globalVariables: GlobalVariables, - protected blockHeader: BlockHeader, + protected historicalHeader: Header, protected publicContractsDB: ContractsDataSourcePublicDB, protected publicStateDB: PublicStateDB, @@ -156,7 +156,7 @@ export class PublicProcessor { */ public makeEmptyProcessedTx(): Promise { const { chainId, version } = this.globalVariables; - return makeEmptyProcessedTx(this.blockHeader, chainId, version); + return makeEmptyProcessedTx(this.historicalHeader, chainId, version); } protected async processTx(tx: Tx): Promise { @@ -277,7 +277,7 @@ export class PublicProcessor { protected async getPublicCircuitPublicInputs(result: PublicExecutionResult) { const publicDataTreeInfo = await this.db.getTreeInfo(MerkleTreeId.PUBLIC_DATA_TREE); - this.blockHeader.publicDataTreeRoot = Fr.fromBuffer(publicDataTreeInfo.root); + this.historicalHeader.state.partial.publicDataTree.root = Fr.fromBuffer(publicDataTreeInfo.root); const callStackPreimages = await this.getPublicCallStackPreimages(result); const publicCallStackHashes = padArrayEnd( @@ -311,7 +311,7 @@ export class PublicProcessor { publicCallStackHashes, unencryptedLogsHash, unencryptedLogPreimagesLength, - blockHeader: this.blockHeader, + historicalHeader: this.historicalHeader, }); } diff --git a/yarn-project/sequencer-client/src/sequencer/sequencer.test.ts b/yarn-project/sequencer-client/src/sequencer/sequencer.test.ts index 2e4af5ba3af..f3d94763f0e 100644 --- a/yarn-project/sequencer-client/src/sequencer/sequencer.test.ts +++ b/yarn-project/sequencer-client/src/sequencer/sequencer.test.ts @@ -8,13 +8,7 @@ import { TxHash, mockTx, } from '@aztec/circuit-types'; -import { - BlockHeader, - Fr, - GlobalVariables, - NUMBER_OF_L1_L2_MESSAGES_PER_ROLLUP, - makeEmptyProof, -} from '@aztec/circuits.js'; +import { Fr, GlobalVariables, Header, NUMBER_OF_L1_L2_MESSAGES_PER_ROLLUP, makeEmptyProof } from '@aztec/circuits.js'; import { times } from '@aztec/foundation/collection'; import { P2P, P2PClientState } from '@aztec/p2p'; import { MerkleTreeOperations, WorldStateRunningState, WorldStateSynchronizer } from '@aztec/world-state'; @@ -66,7 +60,7 @@ describe('sequencer', () => { publicProcessor = mock({ process: async txs => [await Promise.all(txs.map(tx => makeProcessedTx(tx))), []], - makeEmptyProcessedTx: () => makeEmptyProcessedTx(BlockHeader.empty(), chainId, version), + makeEmptyProcessedTx: () => makeEmptyProcessedTx(Header.empty(), chainId, version), }); publicProcessorFactory = mock({ diff --git a/yarn-project/sequencer-client/src/sequencer/sequencer.ts b/yarn-project/sequencer-client/src/sequencer/sequencer.ts index 2663453e953..ca31b58d3b5 100644 --- a/yarn-project/sequencer-client/src/sequencer/sequencer.ts +++ b/yarn-project/sequencer-client/src/sequencer/sequencer.ts @@ -139,19 +139,23 @@ export class Sequencer { } this.log.info(`Retrieved ${pendingTxs.length} txs from P2P pool`); - const blockNumber = (await this.l2BlockSource.getBlockNumber()) + 1; + const historicalHeader = (await this.l2BlockSource.getBlock(-1))?.header; + const newBlockNumber = + (historicalHeader === undefined + ? await this.l2BlockSource.getBlockNumber() + : Number(historicalHeader.globalVariables.blockNumber.toBigInt())) + 1; /** * We'll call this function before running expensive operations to avoid wasted work. */ const assertBlockHeight = async () => { const currentBlockNumber = await this.l2BlockSource.getBlockNumber(); - if (currentBlockNumber + 1 !== blockNumber) { + if (currentBlockNumber + 1 !== newBlockNumber) { throw new Error('New block was emitted while building block'); } }; - const newGlobalVariables = await this.globalsBuilder.buildGlobalVariables(new Fr(blockNumber)); + const newGlobalVariables = await this.globalsBuilder.buildGlobalVariables(new Fr(newBlockNumber)); // Filter out invalid txs // TODO: It should be responsibility of the P2P layer to validate txs before passing them on here @@ -160,15 +164,12 @@ export class Sequencer { return; } - this.log.info(`Building block ${blockNumber} with ${validTxs.length} transactions`); + this.log.info(`Building block ${newBlockNumber} with ${validTxs.length} transactions`); this.state = SequencerState.CREATING_BLOCK; - const prevGlobalVariables = - (await this.l2BlockSource.getBlock(-1))?.header.globalVariables ?? GlobalVariables.empty(); - // Process txs and drop the ones that fail processing // We create a fresh processor each time to reset any cached state (eg storage writes) - const processor = await this.publicProcessorFactory.create(prevGlobalVariables, newGlobalVariables); + const processor = await this.publicProcessorFactory.create(historicalHeader, newGlobalVariables); const [publicProcessorDuration, [processedTxs, failedTxs]] = await elapsed(() => processor.process(validTxs)); if (failedTxs.length > 0) { const failedTxData = failedTxs.map(fail => fail.tx); diff --git a/yarn-project/sequencer-client/src/sequencer/utils.ts b/yarn-project/sequencer-client/src/sequencer/utils.ts deleted file mode 100644 index bf02db4c98f..00000000000 --- a/yarn-project/sequencer-client/src/sequencer/utils.ts +++ /dev/null @@ -1,25 +0,0 @@ -import { BlockHeader, Fr, GlobalVariables } from '@aztec/circuits.js'; -import { computeGlobalsHash } from '@aztec/circuits.js/abis'; -import { MerkleTreeOperations } from '@aztec/world-state'; - -/** - * Fetches the private, nullifier, contract tree and l1 to l2 message tree roots from a given db and assembles a CombinedHistoricalTreeRoots object. - */ -export async function getBlockHeader( - db: MerkleTreeOperations, - prevBlockGlobalVariables: GlobalVariables = GlobalVariables.empty(), -) { - const prevGlobalsHash = computeGlobalsHash(prevBlockGlobalVariables); - const roots = await db.getTreeRoots(); - - return new BlockHeader( - Fr.fromBuffer(roots.noteHashTreeRoot), - Fr.fromBuffer(roots.nullifierTreeRoot), - Fr.fromBuffer(roots.contractDataTreeRoot), - Fr.fromBuffer(roots.l1Tol2MessageTreeRoot), - Fr.fromBuffer(roots.archiveRoot), - Fr.ZERO, - Fr.fromBuffer(roots.publicDataTreeRoot), - prevGlobalsHash, - ); -} diff --git a/yarn-project/sequencer-client/tsconfig.json b/yarn-project/sequencer-client/tsconfig.json index 92a83cff65d..7a1f29b1488 100644 --- a/yarn-project/sequencer-client/tsconfig.json +++ b/yarn-project/sequencer-client/tsconfig.json @@ -38,6 +38,9 @@ }, { "path": "../world-state" + }, + { + "path": "../kv-store" } ], "include": ["src"] diff --git a/yarn-project/tsconfig.json b/yarn-project/tsconfig.json index 2ffee350c7e..7100672a7f2 100644 --- a/yarn-project/tsconfig.json +++ b/yarn-project/tsconfig.json @@ -25,7 +25,7 @@ { "path": "aztec.js/tsconfig.json" }, { "path": "aztec-node/tsconfig.json" }, { "path": "pxe/tsconfig.json" }, - { "path": "aztec-sandbox/tsconfig.json" }, + { "path": "aztec/tsconfig.json" }, { "path": "circuits.js/tsconfig.json" }, { "path": "circuit-types/tsconfig.json" }, { "path": "cli/tsconfig.json" }, diff --git a/yarn-project/typedoc.json b/yarn-project/typedoc.json index 34fd9160a1f..f2e4de84a01 100644 --- a/yarn-project/typedoc.json +++ b/yarn-project/typedoc.json @@ -7,7 +7,7 @@ "archiver", "aztec-cli", "pxe", - "aztec-sandbox", + "aztec", "aztec.js", "key-store", "noir-contracts", diff --git a/yarn-project/types/package.json b/yarn-project/types/package.json index f8e42023289..4bdfbc0801e 100644 --- a/yarn-project/types/package.json +++ b/yarn-project/types/package.json @@ -7,6 +7,7 @@ "types": "./dest/index.d.ts", "exports": { "./abi": "./dest/abi/index.js", + "./contracts": "./dest/contracts/index.js", "./interfaces": "./dest/interfaces/index.js", "./membership": "./dest/sibling-path/index.js", "./noir": "./dest/noir/index.js" diff --git a/yarn-project/types/src/abi/contract_artifact.ts b/yarn-project/types/src/abi/contract_artifact.ts index 1df2e3dbac1..221fb7c80c4 100644 --- a/yarn-project/types/src/abi/contract_artifact.ts +++ b/yarn-project/types/src/abi/contract_artifact.ts @@ -10,6 +10,26 @@ import { import { NoirCompiledContract } from '../noir/index.js'; import { mockVerificationKey } from './mocked_keys.js'; +/** + * Serializes a contract artifact to a buffer for storage. + * @param artifact - Artifact to serialize. + * @returns A buffer. + */ +export function contractArtifactToBuffer(artifact: ContractArtifact): Buffer { + // TODO(@spalladino): More efficient serialization + return Buffer.from(JSON.stringify(artifact), 'utf8'); +} + +/** + * Deserializes a contract artifact from storage. + * @param buffer - Buffer to deserialize. + * @returns Deserialized artifact. + */ +export function contractArtifactFromBuffer(buffer: Buffer): ContractArtifact { + // TODO(@spalladino): More efficient serialization + return JSON.parse(buffer.toString('utf8')) as ContractArtifact; +} + /** * Gets nargo build output and returns a valid contract artifact instance. * @param input - Input object as generated by nargo compile. diff --git a/yarn-project/types/src/contracts/contract_class.test.ts b/yarn-project/types/src/contracts/contract_class.test.ts new file mode 100644 index 00000000000..8521217473c --- /dev/null +++ b/yarn-project/types/src/contracts/contract_class.test.ts @@ -0,0 +1,8 @@ +import { SerializableContractClass } from './contract_class.js'; + +describe('ContractClass', () => { + it('can serialize and deserialize a contract class', () => { + const contractClass = SerializableContractClass.random(); + expect(SerializableContractClass.fromBuffer(contractClass.toBuffer())).toEqual(contractClass); + }); +}); diff --git a/yarn-project/types/src/contracts/contract_class.ts b/yarn-project/types/src/contracts/contract_class.ts new file mode 100644 index 00000000000..b8444e166df --- /dev/null +++ b/yarn-project/types/src/contracts/contract_class.ts @@ -0,0 +1,175 @@ +import { FunctionSelector } from '@aztec/foundation/abi'; +import { randomBytes } from '@aztec/foundation/crypto'; +import { Fr } from '@aztec/foundation/fields'; +import { BufferReader, numToUInt8, serializeToBuffer } from '@aztec/foundation/serialize'; + +const VERSION = 1 as const; + +export interface ContractClass { + /** Version of the contract class. */ + version: typeof VERSION; + /** Hash of the contract artifact. The specification of this hash is not enforced by the protocol. Should include commitments to unconstrained code and compilation metadata. Intended to be used by clients to verify that an off-chain fetched artifact matches a registered class. */ + artifactHash: Fr; + /** List of individual private functions, constructors included. */ + privateFunctions: PrivateFunction[]; + /** List of individual public functions. Should be removed once we switch to the AVM where all public bytecode is bundled together. */ + publicFunctions: PublicFunction[]; + /** Packed bytecode representation of the AVM bytecode for all public functions in this contract. Unused for now, see `publicFunctions`. */ + packedBytecode: Buffer; +} + +/** Serializable implementation of the contract class interface. */ +export class SerializableContractClass implements ContractClass { + /** Version identifier. Initially one, bumped for any changes to the contract class struct. */ + public readonly version = VERSION; + + public readonly artifactHash: Fr; + public readonly packedBytecode: Buffer; + public readonly privateFunctions: SerializablePrivateFunction[]; + public readonly publicFunctions: SerializablePublicFunction[]; + + constructor(contractClass: ContractClass) { + if (contractClass.version !== VERSION) { + throw new Error(`Unexpected contract class version ${contractClass.version}`); + } + this.privateFunctions = contractClass.privateFunctions.map(x => new SerializablePrivateFunction(x)); + this.publicFunctions = contractClass.publicFunctions.map(x => new SerializablePublicFunction(x)); + this.artifactHash = contractClass.artifactHash; + this.packedBytecode = contractClass.packedBytecode; + } + + /** Returns a copy of this object with its id included. */ + withId(id: Fr): ContractClassWithId { + return { ...this, id }; + } + + public toBuffer() { + return serializeToBuffer( + numToUInt8(this.version), + this.artifactHash, + this.privateFunctions.length, + this.privateFunctions, + this.publicFunctions.length, + this.publicFunctions, + this.packedBytecode.length, + this.packedBytecode, + ); + } + + static fromBuffer(bufferOrReader: BufferReader | Buffer) { + const reader = BufferReader.asReader(bufferOrReader); + return new SerializableContractClass({ + version: reader.readUInt8() as typeof VERSION, + artifactHash: reader.readObject(Fr), + privateFunctions: reader.readVector(SerializablePrivateFunction), + publicFunctions: reader.readVector(SerializablePublicFunction), + packedBytecode: reader.readBuffer(), + }); + } + + static random() { + return new SerializableContractClass({ + version: VERSION, + artifactHash: Fr.random(), + privateFunctions: [SerializablePrivateFunction.random()], + publicFunctions: [SerializablePublicFunction.random()], + packedBytecode: randomBytes(32), + }); + } +} + +export interface PrivateFunction { + /** Selector of the function. Calculated as the hash of the method name and parameters. The specification of this is not enforced by the protocol. */ + selector: FunctionSelector; + /** Hash of the verification key associated to this private function. */ + vkHash: Fr; + /** + * Whether the function is internal. + * @deprecated To be reimplemented as an app-level macro. + */ + isInternal: boolean; +} + +/** Private function in a Contract Class. */ +export class SerializablePrivateFunction { + public readonly selector: FunctionSelector; + public readonly vkHash: Fr; + public readonly isInternal: boolean; + + constructor(privateFunction: PrivateFunction) { + this.selector = privateFunction.selector; + this.vkHash = privateFunction.vkHash; + this.isInternal = privateFunction.isInternal; + } + + public toBuffer() { + return serializeToBuffer(this.selector, this.vkHash, this.isInternal); + } + + static fromBuffer(bufferOrReader: BufferReader | Buffer): PrivateFunction { + const reader = BufferReader.asReader(bufferOrReader); + return new SerializablePrivateFunction({ + selector: reader.readObject(FunctionSelector), + vkHash: reader.readObject(Fr), + isInternal: reader.readBoolean(), + }); + } + + static random() { + return new SerializablePrivateFunction({ + selector: FunctionSelector.random(), + vkHash: Fr.random(), + isInternal: false, + }); + } +} + +export interface PublicFunction { + /** Selector of the function. Calculated as the hash of the method name and parameters. The specification of this is not enforced by the protocol. */ + selector: FunctionSelector; + /** Public bytecode. */ + bytecode: Buffer; + /** + * Whether the function is internal. + * @deprecated To be reimplemented as an app-level macro. + */ + isInternal: boolean; +} + +/** + * Public function in a Contract Class. Use `packedBytecode` in the parent class once supported. + */ +export class SerializablePublicFunction { + public readonly selector: FunctionSelector; + public readonly bytecode: Buffer; + public readonly isInternal: boolean; + + constructor(publicFunction: PublicFunction) { + this.selector = publicFunction.selector; + this.bytecode = publicFunction.bytecode; + this.isInternal = publicFunction.isInternal; + } + + public toBuffer() { + return serializeToBuffer(this.selector, this.bytecode.length, this.bytecode, this.isInternal); + } + + static fromBuffer(bufferOrReader: BufferReader | Buffer): PublicFunction { + const reader = BufferReader.asReader(bufferOrReader); + return new SerializablePublicFunction({ + selector: reader.readObject(FunctionSelector), + bytecode: reader.readBuffer(), + isInternal: reader.readBoolean(), + }); + } + + static random() { + return new SerializablePublicFunction({ + selector: FunctionSelector.random(), + bytecode: randomBytes(32), + isInternal: false, + }); + } +} + +export type ContractClassWithId = ContractClass & { id: Fr }; diff --git a/yarn-project/types/src/contracts/contract_instance.test.ts b/yarn-project/types/src/contracts/contract_instance.test.ts new file mode 100644 index 00000000000..86818076f93 --- /dev/null +++ b/yarn-project/types/src/contracts/contract_instance.test.ts @@ -0,0 +1,8 @@ +import { SerializableContractInstance } from './contract_instance.js'; + +describe('ContractInstance', () => { + it('can serialize and deserialize an instance', () => { + const instance = SerializableContractInstance.random(); + expect(SerializableContractInstance.fromBuffer(instance.toBuffer())).toEqual(instance); + }); +}); diff --git a/yarn-project/types/src/contracts/contract_instance.ts b/yarn-project/types/src/contracts/contract_instance.ts new file mode 100644 index 00000000000..71ac496c076 --- /dev/null +++ b/yarn-project/types/src/contracts/contract_instance.ts @@ -0,0 +1,83 @@ +import { AztecAddress } from '@aztec/foundation/aztec-address'; +import { EthAddress } from '@aztec/foundation/eth-address'; +import { Fr } from '@aztec/foundation/fields'; +import { BufferReader, numToUInt8, serializeToBuffer } from '@aztec/foundation/serialize'; + +const VERSION = 1 as const; + +/** A contract instance is a concrete deployment of a contract class. A contract instance always references a contract class, which dictates what code it executes when called. A contract instance has state (both private and public), as well as an address that acts as its identifier. A contract instance can be called into. */ +export interface ContractInstance { + /** Version identifier. Initially one, bumped for any changes to the contract instance struct. */ + version: typeof VERSION; + /** User-generated pseudorandom value for uniqueness. */ + salt: Fr; + /** Identifier of the contract class for this instance. */ + contractClassId: Fr; + /** Hash of the selector and arguments to the constructor. */ + initializationHash: Fr; + /** Optional address of the L1 portal contract. */ + portalContractAddress: EthAddress; + /** Optional hash of the struct of public keys used for encryption and nullifying by this contract. */ + publicKeysHash: Fr; +} + +export type ContractInstanceWithAddress = ContractInstance & { address: AztecAddress }; + +export class SerializableContractInstance { + public readonly version = VERSION; + public readonly salt: Fr; + public readonly contractClassId: Fr; + public readonly initializationHash: Fr; + public readonly portalContractAddress: EthAddress; + public readonly publicKeysHash: Fr; + + constructor(instance: ContractInstance) { + if (instance.version !== VERSION) { + throw new Error(`Unexpected contract class version ${instance.version}`); + } + this.salt = instance.salt; + this.contractClassId = instance.contractClassId; + this.initializationHash = instance.initializationHash; + this.portalContractAddress = instance.portalContractAddress; + this.publicKeysHash = instance.publicKeysHash; + } + + public toBuffer() { + return serializeToBuffer( + numToUInt8(this.version), + this.salt, + this.contractClassId, + this.initializationHash, + this.portalContractAddress, + this.publicKeysHash, + ); + } + + /** Returns a copy of this object with its address included. */ + withAddress(address: AztecAddress): ContractInstanceWithAddress { + return { ...this, address }; + } + + static fromBuffer(bufferOrReader: Buffer | BufferReader) { + const reader = BufferReader.asReader(bufferOrReader); + return new SerializableContractInstance({ + version: reader.readUInt8() as typeof VERSION, + salt: reader.readObject(Fr), + contractClassId: reader.readObject(Fr), + initializationHash: reader.readObject(Fr), + portalContractAddress: reader.readObject(EthAddress), + publicKeysHash: reader.readObject(Fr), + }); + } + + static random() { + return new SerializableContractInstance({ + version: VERSION, + salt: Fr.random(), + contractClassId: Fr.random(), + initializationHash: Fr.random(), + portalContractAddress: EthAddress.random(), + publicKeysHash: Fr.random(), + }); + } +} diff --git a/yarn-project/types/src/contracts/index.ts b/yarn-project/types/src/contracts/index.ts new file mode 100644 index 00000000000..b90e91afc35 --- /dev/null +++ b/yarn-project/types/src/contracts/index.ts @@ -0,0 +1,2 @@ +export * from './contract_class.js'; +export * from './contract_instance.js'; diff --git a/yarn-project/world-state/package.json b/yarn-project/world-state/package.json index 7fc61040e0e..e65cae9fcc1 100644 --- a/yarn-project/world-state/package.json +++ b/yarn-project/world-state/package.json @@ -33,10 +33,9 @@ "@aztec/circuit-types": "workspace:^", "@aztec/circuits.js": "workspace:^", "@aztec/foundation": "workspace:^", + "@aztec/kv-store": "workspace:^", "@aztec/merkle-tree": "workspace:^", "@aztec/types": "workspace:^", - "levelup": "^5.1.1", - "memdown": "^6.1.1", "tslib": "^2.4.0" }, "devDependencies": { diff --git a/yarn-project/world-state/src/synchronizer/server_world_state_synchronizer.test.ts b/yarn-project/world-state/src/synchronizer/server_world_state_synchronizer.test.ts index 65addf22ff3..8c0f0e23351 100644 --- a/yarn-project/world-state/src/synchronizer/server_world_state_synchronizer.test.ts +++ b/yarn-project/world-state/src/synchronizer/server_world_state_synchronizer.test.ts @@ -2,13 +2,12 @@ import { L2Block, L2BlockSource, MerkleTreeId } from '@aztec/circuit-types'; import { Fr } from '@aztec/circuits.js'; import { createDebugLogger } from '@aztec/foundation/log'; import { sleep } from '@aztec/foundation/sleep'; +import { AztecKVStore, AztecLmdbStore } from '@aztec/kv-store'; import { INITIAL_LEAF, Pedersen } from '@aztec/merkle-tree'; import { SiblingPath } from '@aztec/types/membership'; import { jest } from '@jest/globals'; import { mock } from 'jest-mock-extended'; -import levelup from 'levelup'; -import { default as memdown } from 'memdown'; import { MerkleTreeDb, MerkleTrees, WorldStateConfig } from '../index.js'; import { ServerWorldStateSynchronizer } from './server_world_state_synchronizer.js'; @@ -33,30 +32,10 @@ const getMockBlock = (blockNumber: number, newContractsCommitments?: Buffer[]) = return block; }; -const createMockDb = () => levelup((memdown as any)()); - -const createSynchronizer = async ( - db: levelup.LevelUp, - merkleTreeDb: any, - rollupSource: any, - blockCheckInterval = 100, -) => { - const worldStateConfig: WorldStateConfig = { - worldStateBlockCheckIntervalMS: blockCheckInterval, - l2QueueSize: 1000, - }; - - return await ServerWorldStateSynchronizer.new( - db, - merkleTreeDb as MerkleTrees, - rollupSource as L2BlockSource, - worldStateConfig, - ); -}; - const log = createDebugLogger('aztec:server_world_state_synchronizer_test'); describe('server_world_state_synchronizer', () => { + let db: AztecKVStore; const rollupSource = mock({ getBlockNumber: jest.fn(getLatestBlockNumber), getBlocks: jest.fn(consumeNextBlocks), @@ -111,12 +90,30 @@ describe('server_world_state_synchronizer', () => { expect(status.syncedToL2Block).toBe(LATEST_BLOCK_NUMBER + count); }; - it('can be constructed', async () => { - await expect(createSynchronizer(createMockDb(), merkleTreeDb, rollupSource)).resolves.toBeTruthy(); + const createSynchronizer = (blockCheckInterval = 100) => { + const worldStateConfig: WorldStateConfig = { + worldStateBlockCheckIntervalMS: blockCheckInterval, + l2QueueSize: 1000, + }; + + return new ServerWorldStateSynchronizer( + db, + merkleTreeDb as any as MerkleTrees, + rollupSource as L2BlockSource, + worldStateConfig, + ); + }; + + beforeEach(async () => { + db = await AztecLmdbStore.openTmp(); + }); + + it('can be constructed', () => { + expect(createSynchronizer()).toBeTruthy(); }); it('updates sync progress', async () => { - const server = await createSynchronizer(createMockDb(), merkleTreeDb, rollupSource); + const server = createSynchronizer(); // test initial state let status = await server.status(); @@ -165,7 +162,7 @@ describe('server_world_state_synchronizer', () => { }); it('enables blocking until synced', async () => { - const server = await createSynchronizer(createMockDb(), merkleTreeDb, rollupSource); + const server = createSynchronizer(); let currentBlockNumber = 0; const newBlocks = async () => { @@ -196,7 +193,7 @@ describe('server_world_state_synchronizer', () => { }); it('handles multiple calls to start', async () => { - const server = await createSynchronizer(createMockDb(), merkleTreeDb, rollupSource); + const server = createSynchronizer(); let currentBlockNumber = 0; const newBlocks = async () => { @@ -223,7 +220,7 @@ describe('server_world_state_synchronizer', () => { }); it('immediately syncs if no new blocks', async () => { - const server = await createSynchronizer(createMockDb(), merkleTreeDb, rollupSource); + const server = createSynchronizer(); rollupSource.getBlockNumber.mockImplementationOnce(() => { return Promise.resolve(0); }); @@ -241,7 +238,7 @@ describe('server_world_state_synchronizer', () => { }); it("can't be started if already stopped", async () => { - const server = await createSynchronizer(createMockDb(), merkleTreeDb, rollupSource); + const server = createSynchronizer(); rollupSource.getBlockNumber.mockImplementationOnce(() => { return Promise.resolve(0); }); @@ -256,7 +253,7 @@ describe('server_world_state_synchronizer', () => { it('adds the received L2 blocks', async () => { merkleTreeDb.handleL2Block.mockClear(); - const server = await createSynchronizer(createMockDb(), merkleTreeDb, rollupSource); + const server = createSynchronizer(); const totalBlocks = LATEST_BLOCK_NUMBER + 1; nextBlocks = Array(totalBlocks) .fill(0) @@ -269,7 +266,7 @@ describe('server_world_state_synchronizer', () => { }); it('can immediately sync to latest', async () => { - const server = await createSynchronizer(createMockDb(), merkleTreeDb, rollupSource, 10000); + const server = createSynchronizer(10000); await performInitialSync(server); @@ -297,7 +294,7 @@ describe('server_world_state_synchronizer', () => { }); it('can immediately sync to a minimum block number', async () => { - const server = await createSynchronizer(createMockDb(), merkleTreeDb, rollupSource, 10000); + const server = createSynchronizer(10000); await performInitialSync(server); @@ -322,7 +319,7 @@ describe('server_world_state_synchronizer', () => { }); it('can immediately sync to a minimum block in the past', async () => { - const server = await createSynchronizer(createMockDb(), merkleTreeDb, rollupSource, 10000); + const server = createSynchronizer(10000); await performInitialSync(server); // syncing to a block in the past should succeed @@ -344,7 +341,7 @@ describe('server_world_state_synchronizer', () => { }); it('throws if you try to sync to an unavailable block', async () => { - const server = await createSynchronizer(createMockDb(), merkleTreeDb, rollupSource, 10000); + const server = createSynchronizer(); await performInitialSync(server); @@ -370,7 +367,7 @@ describe('server_world_state_synchronizer', () => { }); it('throws if you try to immediate sync when not running', async () => { - const server = await createSynchronizer(createMockDb(), merkleTreeDb, rollupSource, 10000); + const server = createSynchronizer(10000); // test initial state const status = await server.status(); @@ -386,13 +383,12 @@ describe('server_world_state_synchronizer', () => { }); it('restores the last synced block', async () => { - const db = createMockDb(); - const initialServer = await createSynchronizer(db, merkleTreeDb, rollupSource, 10000); + const initialServer = createSynchronizer(10000); await performInitialSync(initialServer); await initialServer.stop(); - const server = await createSynchronizer(db, merkleTreeDb, rollupSource, 10000); + const server = createSynchronizer(10000); const status = await server.status(); expect(status).toEqual({ state: WorldStateRunningState.IDLE, @@ -401,13 +397,12 @@ describe('server_world_state_synchronizer', () => { }); it('starts syncing from the last block', async () => { - const db = createMockDb(); - const initialServer = await createSynchronizer(db, merkleTreeDb, rollupSource, 10000); + const initialServer = createSynchronizer(10000); await performInitialSync(initialServer); await initialServer.stop(); - const server = await createSynchronizer(db, merkleTreeDb, rollupSource, 10000); + const server = createSynchronizer(10000); await performSubsequentSync(server, 2); await server.stop(); }); diff --git a/yarn-project/world-state/src/synchronizer/server_world_state_synchronizer.ts b/yarn-project/world-state/src/synchronizer/server_world_state_synchronizer.ts index ee0349ade38..3c25882bfed 100644 --- a/yarn-project/world-state/src/synchronizer/server_world_state_synchronizer.ts +++ b/yarn-project/world-state/src/synchronizer/server_world_state_synchronizer.ts @@ -3,8 +3,7 @@ import { L2BlockHandledStats } from '@aztec/circuit-types/stats'; import { SerialQueue } from '@aztec/foundation/fifo'; import { createDebugLogger } from '@aztec/foundation/log'; import { elapsed } from '@aztec/foundation/timer'; - -import { LevelUp } from 'levelup'; +import { AztecKVStore, AztecSingleton } from '@aztec/kv-store'; import { HandleL2BlockResult, MerkleTreeOperations, MerkleTrees } from '../world-state-db/index.js'; import { MerkleTreeOperationsFacade } from '../world-state-db/merkle_tree_operations_facade.js'; @@ -12,15 +11,12 @@ import { MerkleTreeSnapshotOperationsFacade } from '../world-state-db/merkle_tre import { WorldStateConfig } from './config.js'; import { WorldStateRunningState, WorldStateStatus, WorldStateSynchronizer } from './world_state_synchronizer.js'; -const DB_KEY_BLOCK_NUMBER = 'latestBlockNumber'; - /** * Synchronizes the world state with the L2 blocks from a L2BlockSource. * The synchronizer will download the L2 blocks from the L2BlockSource and insert the new commitments into the merkle * tree. */ export class ServerWorldStateSynchronizer implements WorldStateSynchronizer { - private currentL2BlockNum = 0; private latestBlockNumberAtStart = 0; private l2BlockDownloader: L2BlockDownloader; @@ -30,14 +26,16 @@ export class ServerWorldStateSynchronizer implements WorldStateSynchronizer { private stopping = false; private runningPromise: Promise = Promise.resolve(); private currentState: WorldStateRunningState = WorldStateRunningState.IDLE; + private blockNumber: AztecSingleton; - private constructor( - private db: LevelUp, + constructor( + store: AztecKVStore, private merkleTreeDb: MerkleTrees, private l2BlockSource: L2BlockSource, config: WorldStateConfig, private log = createDebugLogger('aztec:world_state'), ) { + this.blockNumber = store.openSingleton('world_state_synch_last_block_number'); this.l2BlockDownloader = new L2BlockDownloader( l2BlockSource, config.l2QueueSize, @@ -57,22 +55,6 @@ export class ServerWorldStateSynchronizer implements WorldStateSynchronizer { return new MerkleTreeSnapshotOperationsFacade(this.merkleTreeDb, blockNumber); } - public static async new( - db: LevelUp, - merkleTreeDb: MerkleTrees, - l2BlockSource: L2BlockSource, - config: WorldStateConfig, - log = createDebugLogger('aztec:world_state'), - ) { - const server = new ServerWorldStateSynchronizer(db, merkleTreeDb, l2BlockSource, config, log); - await server.#init(); - return server; - } - - async #init() { - await this.restoreCurrentL2BlockNumber(); - } - public async start() { if (this.currentState === WorldStateRunningState.STOPPED) { throw new Error('Synchronizer already stopped'); @@ -123,11 +105,13 @@ export class ServerWorldStateSynchronizer implements WorldStateSynchronizer { await this.merkleTreeDb.stop(); this.log('Awaiting promise'); await this.runningPromise; - this.log('Commiting current block number'); - await this.commitCurrentL2BlockNumber(); this.setCurrentState(WorldStateRunningState.STOPPED); } + private get currentL2BlockNum(): number { + return this.blockNumber.get() ?? 0; + } + public status(): Promise { const status = { syncedToL2Block: this.currentL2BlockNum, @@ -184,7 +168,6 @@ export class ServerWorldStateSynchronizer implements WorldStateSynchronizer { // This request for blocks will timeout after 1 second if no blocks are received const blocks = await this.l2BlockDownloader.getBlocks(1); await this.handleL2Blocks(blocks); - await this.commitCurrentL2BlockNumber(); } /** @@ -210,11 +193,9 @@ export class ServerWorldStateSynchronizer implements WorldStateSynchronizer { */ private async handleL2Block(l2Block: L2Block): Promise { const result = await this.merkleTreeDb.handleL2Block(l2Block); - this.currentL2BlockNum = l2Block.number; - if ( - this.currentState === WorldStateRunningState.SYNCHING && - this.currentL2BlockNum >= this.latestBlockNumberAtStart - ) { + await this.blockNumber.set(l2Block.number); + + if (this.currentState === WorldStateRunningState.SYNCHING && l2Block.number >= this.latestBlockNumberAtStart) { this.setCurrentState(WorldStateRunningState.RUNNING); if (this.syncResolve !== undefined) { this.syncResolve(); @@ -231,22 +212,4 @@ export class ServerWorldStateSynchronizer implements WorldStateSynchronizer { this.currentState = newState; this.log(`Moved to state ${WorldStateRunningState[this.currentState]}`); } - - private async commitCurrentL2BlockNumber() { - const hex = this.currentL2BlockNum.toString(16); - const encoded = Buffer.from(hex.length % 2 === 1 ? '0' + hex : hex, 'hex'); - - await this.db.put(DB_KEY_BLOCK_NUMBER, encoded); - } - - private async restoreCurrentL2BlockNumber() { - try { - const encoded: Buffer = await this.db.get(DB_KEY_BLOCK_NUMBER); - this.currentL2BlockNum = parseInt(encoded.toString('hex'), 16); - this.log.debug(`Restored current L2 block number ${this.currentL2BlockNum} from db`); - } catch (err) { - this.log.debug('No current L2 block number found in db, starting from 0'); - this.currentL2BlockNum = 0; - } - } } diff --git a/yarn-project/world-state/src/world-state-db/merkle_tree_operations.ts b/yarn-project/world-state/src/world-state-db/merkle_tree_operations.ts index 14fd49c4c1a..8a59832253a 100644 --- a/yarn-project/world-state/src/world-state-db/merkle_tree_operations.ts +++ b/yarn-project/world-state/src/world-state-db/merkle_tree_operations.ts @@ -1,6 +1,5 @@ import { L2Block, MerkleTreeId } from '@aztec/circuit-types'; -import { NullifierLeafPreimage } from '@aztec/circuits.js'; -import { Fr } from '@aztec/foundation/fields'; +import { Header, NullifierLeafPreimage, StateReference } from '@aztec/circuits.js'; import { createDebugLogger } from '@aztec/foundation/log'; import { IndexedTreeLeafPreimage } from '@aztec/foundation/trees'; import { BatchInsertionResult } from '@aztec/merkle-tree'; @@ -34,24 +33,6 @@ export interface TreeInfo { depth: number; } -/** - * The current roots of the commitment trees - */ -export type CurrentTreeRoots = { - /** Note Hash Tree root. */ - noteHashTreeRoot: Buffer; - /** Contract data tree root. */ - contractDataTreeRoot: Buffer; - /** L1 to L2 Messages data tree root. */ - l1Tol2MessageTreeRoot: Buffer; - /** Nullifier data tree root. */ - nullifierTreeRoot: Buffer; - /** Archive root. */ - archiveRoot: Buffer; - /** Public data tree root */ - publicDataTreeRoot: Buffer; -}; - /** * Defines the interface for operations on a set of Merkle Trees. */ @@ -70,9 +51,14 @@ export interface MerkleTreeOperations { getTreeInfo(treeId: MerkleTreeId): Promise; /** - * Gets the current roots of the commitment trees. + * Gets the current state reference. + */ + getStateReference(): Promise; + + /** + * Builds the initial header. */ - getTreeRoots(): Promise; + buildInitialHeader(): Promise
; /** * Gets sibling path for a leaf. @@ -133,22 +119,11 @@ export interface MerkleTreeOperations { getLeafValue(treeId: MerkleTreeId, index: bigint): Promise; /** - * Inserts the new block hash into the archive. + * Inserts the block hash into the archive. * This includes all of the current roots of all of the data trees and the current blocks global vars. - * @param globalVariablesHash - The global variables hash to insert into the block hash. - */ - updateArchive(globalVariablesHash: Fr): Promise; - - /** - * Updates the latest global variables hash - * @param globalVariablesHash - The latest global variables hash - */ - updateLatestGlobalVariablesHash(globalVariablesHash: Fr): Promise; - - /** - * Gets the global variables hash from the previous block + * @param header - The header to insert into the archive. */ - getLatestGlobalVariablesHash(): Promise; + updateArchive(header: Header): Promise; /** * Batch insert multiple leaves into the tree. diff --git a/yarn-project/world-state/src/world-state-db/merkle_tree_operations_facade.ts b/yarn-project/world-state/src/world-state-db/merkle_tree_operations_facade.ts index b58cd7f454d..f36513f6e78 100644 --- a/yarn-project/world-state/src/world-state-db/merkle_tree_operations_facade.ts +++ b/yarn-project/world-state/src/world-state-db/merkle_tree_operations_facade.ts @@ -1,12 +1,11 @@ import { L2Block, MerkleTreeId } from '@aztec/circuit-types'; -import { NullifierLeafPreimage } from '@aztec/circuits.js'; -import { Fr } from '@aztec/foundation/fields'; +import { Header, NullifierLeafPreimage, StateReference } from '@aztec/circuits.js'; import { IndexedTreeLeafPreimage } from '@aztec/foundation/trees'; import { BatchInsertionResult } from '@aztec/merkle-tree'; import { SiblingPath } from '@aztec/types/membership'; import { MerkleTreeDb } from './merkle_tree_db.js'; -import { CurrentTreeRoots, HandleL2BlockResult, MerkleTreeOperations, TreeInfo } from './merkle_tree_operations.js'; +import { HandleL2BlockResult, MerkleTreeOperations, TreeInfo } from './merkle_tree_operations.js'; /** * Wraps a MerkleTreeDbOperations to call all functions with a preset includeUncommitted flag. @@ -25,11 +24,19 @@ export class MerkleTreeOperationsFacade implements MerkleTreeOperations { } /** - * Get the current roots of the commitment trees. - * @returns The current roots of the trees. + * Get the current state reference. + * @returns The current state reference. */ - getTreeRoots(): Promise { - return this.trees.getTreeRoots(this.includeUncommitted); + getStateReference(): Promise { + return this.trees.getStateReference(this.includeUncommitted); + } + + /** + * Builds the initial header. + * @returns The initial header. + */ + buildInitialHeader(): Promise
{ + return this.trees.buildInitialHeader(this.includeUncommitted); } /** @@ -128,25 +135,10 @@ export class MerkleTreeOperationsFacade implements MerkleTreeOperations { /** * Inserts the new block hash into the archive. * This includes all of the current roots of all of the data trees and the current blocks global vars. - * @param globalVariablesHash - The global variables hash to insert into the block hash. - */ - public updateArchive(globalVariablesHash: Fr): Promise { - return this.trees.updateArchive(globalVariablesHash, this.includeUncommitted); - } - - /** - * Updates the latest global variables hash - * @param globalVariablesHash - The latest global variables hash - */ - public updateLatestGlobalVariablesHash(globalVariablesHash: Fr): Promise { - return this.trees.updateLatestGlobalVariablesHash(globalVariablesHash, this.includeUncommitted); - } - - /** - * Gets the global variables hash from the previous block + * @param header - The header to insert into the archive. */ - public getLatestGlobalVariablesHash(): Promise { - return this.trees.getLatestGlobalVariablesHash(this.includeUncommitted); + public updateArchive(header: Header): Promise { + return this.trees.updateArchive(header, this.includeUncommitted); } /** diff --git a/yarn-project/world-state/src/world-state-db/merkle_tree_snapshot_operations_facade.ts b/yarn-project/world-state/src/world-state-db/merkle_tree_snapshot_operations_facade.ts index 53614bc1ed4..79e7a01d16f 100644 --- a/yarn-project/world-state/src/world-state-db/merkle_tree_snapshot_operations_facade.ts +++ b/yarn-project/world-state/src/world-state-db/merkle_tree_snapshot_operations_facade.ts @@ -1,11 +1,11 @@ import { MerkleTreeId } from '@aztec/circuit-types'; -import { Fr } from '@aztec/circuits.js'; +import { AppendOnlyTreeSnapshot, Fr, Header, PartialStateReference, StateReference } from '@aztec/circuits.js'; import { IndexedTreeLeafPreimage } from '@aztec/foundation/trees'; import { BatchInsertionResult, IndexedTreeSnapshot, TreeSnapshot } from '@aztec/merkle-tree'; import { SiblingPath } from '@aztec/types/membership'; import { MerkleTreeDb } from './merkle_tree_db.js'; -import { CurrentTreeRoots, HandleL2BlockResult, MerkleTreeOperations, TreeInfo } from './merkle_tree_operations.js'; +import { HandleL2BlockResult, MerkleTreeOperations, TreeInfo } from './merkle_tree_operations.js'; /** * Merkle tree operations on readonly tree snapshots. @@ -34,10 +34,6 @@ export class MerkleTreeSnapshotOperationsFacade implements MerkleTreeOperations return tree.findLeafIndex(value); } - getLatestGlobalVariablesHash(): Promise { - return Promise.reject(new Error('not implemented')); - } - async getLeafPreimage( treeId: MerkleTreeId.NULLIFIER_TREE, index: bigint, @@ -86,7 +82,7 @@ export class MerkleTreeSnapshotOperationsFacade implements MerkleTreeOperations }; } - async getTreeRoots(): Promise { + async getStateReference(): Promise { const snapshots = await Promise.all([ this.#getTreeSnapshot(MerkleTreeId.CONTRACT_TREE), this.#getTreeSnapshot(MerkleTreeId.NULLIFIER_TREE), @@ -96,14 +92,30 @@ export class MerkleTreeSnapshotOperationsFacade implements MerkleTreeOperations this.#getTreeSnapshot(MerkleTreeId.ARCHIVE), ]); - return { - archiveRoot: snapshots[MerkleTreeId.ARCHIVE].getRoot(), - contractDataTreeRoot: snapshots[MerkleTreeId.CONTRACT_TREE].getRoot(), - l1Tol2MessageTreeRoot: snapshots[MerkleTreeId.L1_TO_L2_MESSAGE_TREE].getRoot(), - noteHashTreeRoot: snapshots[MerkleTreeId.NOTE_HASH_TREE].getRoot(), - nullifierTreeRoot: snapshots[MerkleTreeId.NULLIFIER_TREE].getRoot(), - publicDataTreeRoot: snapshots[MerkleTreeId.PUBLIC_DATA_TREE].getRoot(), - }; + return new StateReference( + new AppendOnlyTreeSnapshot( + Fr.fromBuffer(snapshots[MerkleTreeId.L1_TO_L2_MESSAGE_TREE].getRoot()), + Number(snapshots[MerkleTreeId.L1_TO_L2_MESSAGE_TREE].getNumLeaves()), + ), + new PartialStateReference( + new AppendOnlyTreeSnapshot( + Fr.fromBuffer(snapshots[MerkleTreeId.NOTE_HASH_TREE].getRoot()), + Number(snapshots[MerkleTreeId.NOTE_HASH_TREE].getNumLeaves()), + ), + new AppendOnlyTreeSnapshot( + Fr.fromBuffer(snapshots[MerkleTreeId.NULLIFIER_TREE].getRoot()), + Number(snapshots[MerkleTreeId.NULLIFIER_TREE].getNumLeaves()), + ), + new AppendOnlyTreeSnapshot( + Fr.fromBuffer(snapshots[MerkleTreeId.CONTRACT_TREE].getRoot()), + Number(snapshots[MerkleTreeId.CONTRACT_TREE].getNumLeaves()), + ), + new AppendOnlyTreeSnapshot( + Fr.fromBuffer(snapshots[MerkleTreeId.PUBLIC_DATA_TREE].getRoot()), + Number(snapshots[MerkleTreeId.PUBLIC_DATA_TREE].getNumLeaves()), + ), + ), + ); } appendLeaves(): Promise { @@ -136,11 +148,11 @@ export class MerkleTreeSnapshotOperationsFacade implements MerkleTreeOperations return Promise.reject(new Error('Tree snapshot operations are read-only')); } - updateLatestGlobalVariablesHash(): Promise { + updateLeaf(): Promise { return Promise.reject(new Error('Tree snapshot operations are read-only')); } - updateLeaf(): Promise { - return Promise.reject(new Error('Tree snapshot operations are read-only')); + buildInitialHeader(): Promise
{ + throw new Error('Building initial header not supported on snapshot.'); } } diff --git a/yarn-project/world-state/src/world-state-db/merkle_trees.ts b/yarn-project/world-state/src/world-state-db/merkle_trees.ts index b8a0d5c44ee..3a55636c273 100644 --- a/yarn-project/world-state/src/world-state-db/merkle_trees.ts +++ b/yarn-project/world-state/src/world-state-db/merkle_trees.ts @@ -1,9 +1,11 @@ import { L2Block, MerkleTreeId } from '@aztec/circuit-types'; import { ARCHIVE_HEIGHT, + AppendOnlyTreeSnapshot, CONTRACT_TREE_HEIGHT, Fr, GlobalVariables, + Header, L1_TO_L2_MSG_TREE_HEIGHT, MAX_PUBLIC_DATA_UPDATE_REQUESTS_PER_TX, NOTE_HASH_TREE_HEIGHT, @@ -13,14 +15,15 @@ import { NullifierLeafPreimage, PUBLIC_DATA_SUBTREE_HEIGHT, PUBLIC_DATA_TREE_HEIGHT, + PartialStateReference, PublicDataTreeLeaf, PublicDataTreeLeafPreimage, + StateReference, } from '@aztec/circuits.js'; -import { computeBlockHash, computeGlobalsHash } from '@aztec/circuits.js/abis'; -import { Committable } from '@aztec/foundation/committable'; import { SerialQueue } from '@aztec/foundation/fifo'; -import { createDebugLogger } from '@aztec/foundation/log'; +import { DebugLogger, createDebugLogger } from '@aztec/foundation/log'; import { IndexedTreeLeafPreimage } from '@aztec/foundation/trees'; +import { AztecKVStore } from '@aztec/kv-store'; import { AppendOnlyTree, BatchInsertionResult, @@ -29,42 +32,23 @@ import { StandardIndexedTree, StandardTree, UpdateOnlyTree, + getTreeMeta, loadTree, newTree, } from '@aztec/merkle-tree'; import { Hasher } from '@aztec/types/interfaces'; import { SiblingPath } from '@aztec/types/membership'; -import { default as levelup } from 'levelup'; - import { INITIAL_NULLIFIER_TREE_SIZE, INITIAL_PUBLIC_DATA_TREE_SIZE, MerkleTreeDb } from './merkle_tree_db.js'; -import { - CurrentTreeRoots, - HandleL2BlockResult, - IndexedTreeId, - MerkleTreeOperations, - TreeInfo, -} from './merkle_tree_operations.js'; +import { HandleL2BlockResult, IndexedTreeId, MerkleTreeOperations, TreeInfo } from './merkle_tree_operations.js'; import { MerkleTreeOperationsFacade } from './merkle_tree_operations_facade.js'; -/** - * Data necessary to reinitialize the merkle trees from Db. - */ -interface FromDbOptions { - /** - * The global variables hash from the last block. - */ - globalVariablesHash: Fr; -} - -const LAST_GLOBAL_VARS_HASH = 'lastGlobalVarsHash'; - /** * The nullifier tree is an indexed tree. */ class NullifierTree extends StandardIndexedTree { - constructor(db: levelup.LevelUp, hasher: Hasher, name: string, depth: number, size: bigint = 0n, root?: Buffer) { - super(db, hasher, name, depth, size, NullifierLeafPreimage, NullifierLeaf, root); + constructor(store: AztecKVStore, hasher: Hasher, name: string, depth: number, size: bigint = 0n, root?: Buffer) { + super(store, hasher, name, depth, size, NullifierLeafPreimage, NullifierLeaf, root); } } @@ -72,8 +56,8 @@ class NullifierTree extends StandardIndexedTree { * The public data tree is an indexed tree. */ class PublicDataTree extends StandardIndexedTree { - constructor(db: levelup.LevelUp, hasher: Hasher, name: string, depth: number, size: bigint = 0n, root?: Buffer) { - super(db, hasher, name, depth, size, PublicDataTreeLeafPreimage, PublicDataTreeLeaf, root); + constructor(store: AztecKVStore, hasher: Hasher, name: string, depth: number, size: bigint = 0n, root?: Buffer) { + super(store, hasher, name, depth, size, PublicDataTreeLeafPreimage, PublicDataTreeLeaf, root); } } @@ -82,32 +66,39 @@ class PublicDataTree extends StandardIndexedTree { */ export class MerkleTrees implements MerkleTreeDb { private trees: (AppendOnlyTree | UpdateOnlyTree)[] = []; - private latestGlobalVariablesHash: Committable; private jobQueue = new SerialQueue(); - constructor(private db: levelup.LevelUp, private log = createDebugLogger('aztec:merkle_trees')) { - this.latestGlobalVariablesHash = new Committable(Fr.ZERO); + private constructor(private store: AztecKVStore, private log: DebugLogger) {} + + /** + * Method to asynchronously create and initialize a MerkleTrees instance. + * @param store - The db instance to use for data persistance. + * @returns - A fully initialized MerkleTrees instance. + */ + public static async new(store: AztecKVStore, log = createDebugLogger('aztec:merkle_trees')) { + const merkleTrees = new MerkleTrees(store, log); + await merkleTrees.#init(); + return merkleTrees; } /** - * initializes the collection of Merkle Trees. - * @param fromDbOptions - Options to initialize the trees from the database. + * Initializes the collection of Merkle Trees. */ - public async init(fromDbOptions?: FromDbOptions) { - const fromDb = fromDbOptions !== undefined; + async #init() { + const fromDb = this.#isDbPopulated(); const initializeTree = fromDb ? loadTree : newTree; const hasher = new Pedersen(); const contractTree: AppendOnlyTree = await initializeTree( StandardTree, - this.db, + this.store, hasher, `${MerkleTreeId[MerkleTreeId.CONTRACT_TREE]}`, CONTRACT_TREE_HEIGHT, ); const nullifierTree = await initializeTree( NullifierTree, - this.db, + this.store, hasher, `${MerkleTreeId[MerkleTreeId.NULLIFIER_TREE]}`, NULLIFIER_TREE_HEIGHT, @@ -115,14 +106,14 @@ export class MerkleTrees implements MerkleTreeDb { ); const noteHashTree: AppendOnlyTree = await initializeTree( StandardTree, - this.db, + this.store, hasher, `${MerkleTreeId[MerkleTreeId.NOTE_HASH_TREE]}`, NOTE_HASH_TREE_HEIGHT, ); const publicDataTree = await initializeTree( PublicDataTree, - this.db, + this.store, hasher, `${MerkleTreeId[MerkleTreeId.PUBLIC_DATA_TREE]}`, PUBLIC_DATA_TREE_HEIGHT, @@ -130,14 +121,14 @@ export class MerkleTrees implements MerkleTreeDb { ); const l1Tol2MessageTree: AppendOnlyTree = await initializeTree( StandardTree, - this.db, + this.store, hasher, `${MerkleTreeId[MerkleTreeId.L1_TO_L2_MESSAGE_TREE]}`, L1_TO_L2_MSG_TREE_HEIGHT, ); const archive: AppendOnlyTree = await initializeTree( StandardTree, - this.db, + this.store, hasher, `${MerkleTreeId[MerkleTreeId.ARCHIVE]}`, ARCHIVE_HEIGHT, @@ -146,31 +137,19 @@ export class MerkleTrees implements MerkleTreeDb { this.jobQueue.start(); - // The first leaf in the blocks tree contains the empty roots of the other trees and empty global variables. if (!fromDb) { - const initialGlobalVariablesHash = computeGlobalsHash(GlobalVariables.empty()); - await this._updateLatestGlobalVariablesHash(initialGlobalVariablesHash); - await this._updateArchive(initialGlobalVariablesHash, true); - await this._commit(); - } else { - await this._updateLatestGlobalVariablesHash(fromDbOptions.globalVariablesHash); - // make the restored global variables hash and tree roots current - await this._commit(); + // We are not initializing from db so we need to populate the first leaf of the archive tree which is a hash of + // the initial header. + const initialHeder = await this.buildInitialHeader(true); + await this.#updateArchive(initialHeder, true); } + + await this.#commit(); } - /** - * Method to asynchronously create and initialize a MerkleTrees instance. - * @param db - The db instance to use for data persistance. - * @returns - A fully initialized MerkleTrees instance. - */ - public static async new(db: levelup.LevelUp) { - const merkleTrees = new MerkleTrees(db); - const globalVariablesHash: Buffer | undefined = await db.get(LAST_GLOBAL_VARS_HASH).catch(() => undefined); - await merkleTrees.init( - globalVariablesHash ? { globalVariablesHash: Fr.fromBuffer(globalVariablesHash) } : undefined, - ); - return merkleTrees; + public async buildInitialHeader(includeUncommitted: boolean): Promise
{ + const state = await this.getStateReference(includeUncommitted); + return new Header(AppendOnlyTreeSnapshot.zero(), Buffer.alloc(32, 0), state, GlobalVariables.empty()); } /** @@ -197,29 +176,12 @@ export class MerkleTrees implements MerkleTreeDb { } /** - * Inserts into the roots trees (CONTRACT_TREE_ROOTS_TREE, NOTE_HASH_TREE_ROOTS_TREE, L1_TO_L2_MESSAGE_TREE_ROOTS_TREE) - * the current roots of the corresponding trees (CONTRACT_TREE, NOTE_HASH_TREE, L1_TO_L2_MESSAGE_TREE). - * @param globalsHash - The current global variables hash. + * Updates the archive with the new block/header hash. + * @param header - The header whose hash to insert into the archive. * @param includeUncommitted - Indicates whether to include uncommitted data. */ - public async updateArchive(globalsHash: Fr, includeUncommitted: boolean) { - await this.synchronize(() => this._updateArchive(globalsHash, includeUncommitted)); - } - - /** - * Updates the latest global variables hash - * @param globalVariablesHash - The latest global variables hash - */ - public async updateLatestGlobalVariablesHash(globalVariablesHash: Fr) { - return await this.synchronize(() => this._updateLatestGlobalVariablesHash(globalVariablesHash)); - } - - /** - * Gets the global variables hash from the previous block - * @param includeUncommitted - Indicates whether to include uncommitted data. - */ - public async getLatestGlobalVariablesHash(includeUncommitted: boolean): Promise { - return await this.synchronize(() => this._getGlobalVariablesHash(includeUncommitted)); + public async updateArchive(header: Header, includeUncommitted: boolean) { + await this.synchronize(() => this.#updateArchive(header, includeUncommitted)); } /** @@ -229,43 +191,33 @@ export class MerkleTrees implements MerkleTreeDb { * @returns The tree info for the specified tree. */ public async getTreeInfo(treeId: MerkleTreeId, includeUncommitted: boolean): Promise { - return await this.synchronize(() => this._getTreeInfo(treeId, includeUncommitted)); + return await this.synchronize(() => this.#getTreeInfo(treeId, includeUncommitted)); } /** - * Get the current roots of the commitment trees. + * Get the current state reference * @param includeUncommitted - Indicates whether to include uncommitted data. - * @returns The current roots of the trees. + * @returns The current state reference */ - public async getTreeRoots(includeUncommitted: boolean): Promise { - const roots = await this.synchronize(() => Promise.resolve(this._getAllTreeRoots(includeUncommitted))); - - return { - noteHashTreeRoot: roots[0], - nullifierTreeRoot: roots[1], - contractDataTreeRoot: roots[2], - l1Tol2MessageTreeRoot: roots[3], - publicDataTreeRoot: roots[4], - archiveRoot: roots[5], + public getStateReference(includeUncommitted: boolean): Promise { + const getAppendOnlyTreeSnapshot = (treeId: MerkleTreeId) => { + const tree = this.trees[treeId] as AppendOnlyTree; + return new AppendOnlyTreeSnapshot( + Fr.fromBuffer(tree.getRoot(includeUncommitted)), + Number(tree.getNumLeaves(includeUncommitted)), + ); }; - } - - private async _getCurrentBlockHash(globalsHash: Fr, includeUncommitted: boolean): Promise { - const roots = (await this._getAllTreeRoots(includeUncommitted)).map(root => Fr.fromBuffer(root)); - return computeBlockHash(globalsHash, roots[0], roots[1], roots[2], roots[3], roots[4]); - } - - private _getAllTreeRoots(includeUncommitted: boolean): Promise { - const roots = [ - MerkleTreeId.NOTE_HASH_TREE, - MerkleTreeId.NULLIFIER_TREE, - MerkleTreeId.CONTRACT_TREE, - MerkleTreeId.L1_TO_L2_MESSAGE_TREE, - MerkleTreeId.PUBLIC_DATA_TREE, - MerkleTreeId.ARCHIVE, - ].map(tree => this.trees[tree].getRoot(includeUncommitted)); - return Promise.resolve(roots); + const state = new StateReference( + getAppendOnlyTreeSnapshot(MerkleTreeId.L1_TO_L2_MESSAGE_TREE), + new PartialStateReference( + getAppendOnlyTreeSnapshot(MerkleTreeId.NOTE_HASH_TREE), + getAppendOnlyTreeSnapshot(MerkleTreeId.NULLIFIER_TREE), + getAppendOnlyTreeSnapshot(MerkleTreeId.CONTRACT_TREE), + getAppendOnlyTreeSnapshot(MerkleTreeId.PUBLIC_DATA_TREE), + ), + ); + return Promise.resolve(state); } /** @@ -280,7 +232,7 @@ export class MerkleTrees implements MerkleTreeDb { index: bigint, includeUncommitted: boolean, ): Promise { - return await this.synchronize(() => this.trees[treeId].getLeafValue(index, includeUncommitted)); + return await this.synchronize(() => Promise.resolve(this.trees[treeId].getLeafValue(index, includeUncommitted))); } /** @@ -295,7 +247,7 @@ export class MerkleTrees implements MerkleTreeDb { index: bigint, includeUncommitted: boolean, ): Promise> { - return await this.synchronize(() => this._getSiblingPath(treeId, index, includeUncommitted)); + return await this.synchronize(() => this.trees[treeId].getSiblingPath(index, includeUncommitted)); } /** @@ -305,7 +257,7 @@ export class MerkleTrees implements MerkleTreeDb { * @returns Empty promise. */ public async appendLeaves(treeId: MerkleTreeId, leaves: Buffer[]): Promise { - return await this.synchronize(() => this._appendLeaves(treeId, leaves)); + return await this.synchronize(() => this.#appendLeaves(treeId, leaves)); } /** @@ -313,7 +265,7 @@ export class MerkleTrees implements MerkleTreeDb { * @returns Empty promise. */ public async commit(): Promise { - return await this.synchronize(() => this._commit()); + return await this.synchronize(() => this.#commit()); } /** @@ -321,7 +273,7 @@ export class MerkleTrees implements MerkleTreeDb { * @returns Empty promise. */ public async rollback(): Promise { - return await this.synchronize(() => this._rollback()); + return await this.synchronize(() => this.#rollback()); } /** @@ -348,7 +300,9 @@ export class MerkleTrees implements MerkleTreeDb { } | undefined > { - return await this.synchronize(() => this._getIndexedTree(treeId).findIndexOfPreviousKey(value, includeUncommitted)); + return await this.synchronize(() => + Promise.resolve(this.#getIndexedTree(treeId).findIndexOfPreviousKey(value, includeUncommitted)), + ); } /** @@ -364,7 +318,7 @@ export class MerkleTrees implements MerkleTreeDb { includeUncommitted: boolean, ): Promise { return await this.synchronize(() => - this._getIndexedTree(treeId).getLatestLeafPreimageCopy(index, includeUncommitted), + Promise.resolve(this.#getIndexedTree(treeId).getLatestLeafPreimageCopy(index, includeUncommitted)), ); } @@ -380,9 +334,9 @@ export class MerkleTrees implements MerkleTreeDb { value: Buffer, includeUncommitted: boolean, ): Promise { - return await this.synchronize(async () => { + return await this.synchronize(() => { const tree = this.trees[treeId]; - return await tree.findLeafIndex(value, includeUncommitted); + return Promise.resolve(tree.findLeafIndex(value, includeUncommitted)); }); } @@ -394,7 +348,7 @@ export class MerkleTrees implements MerkleTreeDb { * @returns Empty promise. */ public async updateLeaf(treeId: IndexedTreeId, leaf: Buffer, index: bigint): Promise { - return await this.synchronize(() => this._updateLeaf(treeId, leaf, index)); + return await this.synchronize(() => this.#updateLeaf(treeId, leaf, index)); } /** @@ -403,7 +357,7 @@ export class MerkleTrees implements MerkleTreeDb { * @returns Whether the block handled was produced by this same node. */ public async handleL2Block(block: L2Block): Promise { - return await this.synchronize(() => this._handleL2Block(block)); + return await this.synchronize(() => this.#handleL2Block(block)); } /** @@ -438,18 +392,17 @@ export class MerkleTrees implements MerkleTreeDb { return await this.jobQueue.put(fn); } - private _updateLatestGlobalVariablesHash(globalVariablesHash: Fr): Promise { - this.latestGlobalVariablesHash.set(globalVariablesHash); - return Promise.resolve(); - } + async #updateArchive(header: Header, includeUncommitted: boolean) { + const state = await this.getStateReference(includeUncommitted); - private _getGlobalVariablesHash(includeUncommitted: boolean): Promise { - return Promise.resolve(this.latestGlobalVariablesHash.get(includeUncommitted)); - } + // This method should be called only when the block builder already updated the state so we sanity check that it's + // the case here. + if (!state.toBuffer().equals(header.state.toBuffer())) { + throw new Error('State in header does not match current state'); + } - private async _updateArchive(globalsHash: Fr, includeUncommitted: boolean) { - const blockHash = await this._getCurrentBlockHash(globalsHash, includeUncommitted); - await this._appendLeaves(MerkleTreeId.ARCHIVE, [blockHash.toBuffer()]); + const blockHash = header.hash(); + await this.#appendLeaves(MerkleTreeId.ARCHIVE, [blockHash.toBuffer()]); } /** @@ -458,7 +411,7 @@ export class MerkleTrees implements MerkleTreeDb { * @param includeUncommitted - Indicates whether to include uncommitted data. * @returns The tree info for the specified tree. */ - private _getTreeInfo(treeId: MerkleTreeId, includeUncommitted: boolean): Promise { + #getTreeInfo(treeId: MerkleTreeId, includeUncommitted: boolean): Promise { const treeInfo = { treeId, root: this.trees[treeId].getRoot(includeUncommitted), @@ -473,32 +426,17 @@ export class MerkleTrees implements MerkleTreeDb { * @param treeId - Id of the tree to get an instance of. * @returns The indexed tree for the specified tree id. */ - private _getIndexedTree(treeId: IndexedTreeId): IndexedTree { + #getIndexedTree(treeId: IndexedTreeId): IndexedTree { return this.trees[treeId] as IndexedTree; } - /** - * Returns the sibling path for a leaf in a tree. - * @param treeId - Id of the tree to get the sibling path from. - * @param index - Index of the leaf to get the sibling path for. - * @param includeUncommitted - Indicates whether to include uncommitted updates in the sibling path. - * @returns Promise containing the sibling path for the leaf. - */ - private _getSiblingPath( - treeId: MerkleTreeId, - index: bigint, - includeUncommitted: boolean, - ): Promise> { - return Promise.resolve(this.trees[treeId].getSiblingPath(index, includeUncommitted)); - } - /** * Appends leaves to a tree. * @param treeId - Id of the tree to append leaves to. * @param leaves - Leaves to append. * @returns Empty promise. */ - private async _appendLeaves(treeId: MerkleTreeId, leaves: Buffer[]): Promise { + async #appendLeaves(treeId: MerkleTreeId, leaves: Buffer[]): Promise { const tree = this.trees[treeId]; if (!('appendLeaves' in tree)) { throw new Error('Tree does not support `appendLeaves` method'); @@ -506,7 +444,7 @@ export class MerkleTrees implements MerkleTreeDb { return await tree.appendLeaves(leaves); } - private async _updateLeaf(treeId: IndexedTreeId, leaf: Buffer, index: bigint): Promise { + async #updateLeaf(treeId: IndexedTreeId, leaf: Buffer, index: bigint): Promise { const tree = this.trees[treeId]; if (!('updateLeaf' in tree)) { throw new Error('Tree does not support `updateLeaf` method'); @@ -518,30 +456,27 @@ export class MerkleTrees implements MerkleTreeDb { * Commits all pending updates. * @returns Empty promise. */ - private async _commit(): Promise { + async #commit(): Promise { for (const tree of this.trees) { await tree.commit(); } - this.latestGlobalVariablesHash.commit(); - await this.db.put(LAST_GLOBAL_VARS_HASH, this.latestGlobalVariablesHash.get().toBuffer()); } /** * Rolls back all pending updates. * @returns Empty promise. */ - private async _rollback(): Promise { + async #rollback(): Promise { for (const tree of this.trees) { await tree.rollback(); } - this.latestGlobalVariablesHash.rollback(); } public getSnapshot(blockNumber: number) { return Promise.all(this.trees.map(tree => tree.getSnapshot(blockNumber))); } - private async _snapshot(blockNumber: number): Promise { + async #snapshot(blockNumber: number): Promise { for (const tree of this.trees) { await tree.snapshot(blockNumber); } @@ -551,7 +486,7 @@ export class MerkleTrees implements MerkleTreeDb { * Handles a single L2 block (i.e. Inserts the new commitments into the merkle tree). * @param l2Block - The L2 block to handle. */ - private async _handleL2Block(l2Block: L2Block): Promise { + async #handleL2Block(l2Block: L2Block): Promise { const treeRootWithIdPairs = [ [l2Block.header.state.partial.contractTree.root, MerkleTreeId.CONTRACT_TREE], [l2Block.header.state.partial.nullifierTree.root, MerkleTreeId.NULLIFIER_TREE], @@ -567,10 +502,10 @@ export class MerkleTrees implements MerkleTreeDb { const ourBlock = treeRootWithIdPairs.every(([root, id]) => compareRoot(root, id)); if (ourBlock) { this.log(`Block ${l2Block.number} is ours, committing world state`); - await this._commit(); + await this.#commit(); } else { this.log(`Block ${l2Block.number} is not ours, rolling back world state and committing state from chain`); - await this._rollback(); + await this.#rollback(); // Sync the append only trees for (const [tree, leaves] of [ @@ -578,7 +513,7 @@ export class MerkleTrees implements MerkleTreeDb { [MerkleTreeId.NOTE_HASH_TREE, l2Block.newCommitments], [MerkleTreeId.L1_TO_L2_MESSAGE_TREE, l2Block.newL1ToL2Messages], ] as const) { - await this._appendLeaves( + await this.#appendLeaves( tree, leaves.map(fr => fr.toBuffer()), ); @@ -602,20 +537,15 @@ export class MerkleTrees implements MerkleTreeDb { ); } - // Sync and add the block to the blocks tree - const globalVariablesHash = computeGlobalsHash(l2Block.header.globalVariables); - await this._updateLatestGlobalVariablesHash(globalVariablesHash); - this.log(`Synced global variables with hash ${globalVariablesHash}`); - - const blockHash = await this._getCurrentBlockHash(globalVariablesHash, true); - await this._appendLeaves(MerkleTreeId.ARCHIVE, [blockHash.toBuffer()]); + // The last thing remaining is to update the archive + await this.#updateArchive(l2Block.header, true); - await this._commit(); + await this.#commit(); } for (const [root, treeId] of treeRootWithIdPairs) { const treeName = MerkleTreeId[treeId]; - const info = await this._getTreeInfo(treeId, false); + const info = await this.#getTreeInfo(treeId, false); const syncedStr = '0x' + info.root.toString('hex'); const rootStr = root.toString(); // Sanity check that the rebuilt trees match the roots published by the L2 block @@ -627,8 +557,19 @@ export class MerkleTrees implements MerkleTreeDb { this.log(`Tree ${treeName} synched with size ${info.size} root ${rootStr}`); } } - await this._snapshot(l2Block.number); + await this.#snapshot(l2Block.number); return { isBlockOurs: ourBlock }; } + + #isDbPopulated(): boolean { + try { + getTreeMeta(this.store, MerkleTreeId[MerkleTreeId.NULLIFIER_TREE]); + // Tree meta was found --> db is populated + return true; + } catch (e) { + // Tree meta was not found --> db is not populated + return false; + } + } } diff --git a/yarn-project/world-state/tsconfig.json b/yarn-project/world-state/tsconfig.json index 5550de6775f..0088e438260 100644 --- a/yarn-project/world-state/tsconfig.json +++ b/yarn-project/world-state/tsconfig.json @@ -15,6 +15,9 @@ { "path": "../foundation" }, + { + "path": "../kv-store" + }, { "path": "../merkle-tree" }, diff --git a/yarn-project/yarn-project-base/Dockerfile b/yarn-project/yarn-project-base/Dockerfile index b373d429178..fb2b49237e6 100644 --- a/yarn-project/yarn-project-base/Dockerfile +++ b/yarn-project/yarn-project-base/Dockerfile @@ -47,6 +47,7 @@ FROM 278380418400.dkr.ecr.eu-west-2.amazonaws.com/bb.js as bb.js FROM 278380418400.dkr.ecr.eu-west-2.amazonaws.com/noir as noir FROM 278380418400.dkr.ecr.eu-west-2.amazonaws.com/noir-packages as noir-packages FROM 278380418400.dkr.ecr.eu-west-2.amazonaws.com/boxes-files as boxes-files +FROM 278380418400.dkr.ecr.eu-west-2.amazonaws.com/avm-transpiler as transpiler FROM node:18.19.0 RUN apt update && apt install -y jq curl perl && rm -rf /var/lib/apt/lists/* && apt-get clean @@ -61,6 +62,8 @@ COPY --from=noir /usr/src/noir/target/release/nargo /usr/src/noir/target/release COPY --from=noir-packages /usr/src/noir/packages /usr/src/noir/packages # Copy in boxes COPY --from=boxes-files /usr/src/boxes /usr/src/boxes +# Copy in transpiler +COPY --from=transpiler /usr/src/avm-transpiler/target/release/avm-transpiler /usr/src/avm-transpiler/target/release/avm-transpiler # We install a symlink to yarn-project's node_modules at a location that all portalled packages can find as they # walk up the tree as part of module resolution. The supposedly idiomatic way of supporting module resolution diff --git a/yarn-project/yarn.lock b/yarn-project/yarn.lock index 53d6be96445..472b7df6b99 100644 --- a/yarn-project/yarn.lock +++ b/yarn-project/yarn.lock @@ -13,30 +13,37 @@ __metadata: linkType: hard "@achingbrain/nat-port-mapper@npm:^1.0.9": - version: 1.0.9 - resolution: "@achingbrain/nat-port-mapper@npm:1.0.9" + version: 1.0.13 + resolution: "@achingbrain/nat-port-mapper@npm:1.0.13" dependencies: "@achingbrain/ssdp": ^4.0.1 - "@libp2p/logger": ^2.0.0 - default-gateway: ^6.0.2 + "@libp2p/logger": ^4.0.1 + default-gateway: ^7.2.2 err-code: ^3.0.1 it-first: ^3.0.1 p-defer: ^4.0.0 p-timeout: ^6.1.1 xml2js: ^0.6.0 - checksum: 4d8b97ff6eceab530a51682f4f2d5b91101de029b429693bdcff73f19617770fe4e3271b353c3d932c419ed9fd1c0d6ba5bd98b716aab48c64c1d836b2d8020a + checksum: 0ece5a52be65fcb26e75918c39a5dad76a7752103bb32686e564e4c8f10e658e80e0e0d82acca6653cff4bac938faae06d71ceaef7a38916b9928b68a78c2c2b languageName: node linkType: hard "@achingbrain/ssdp@npm:^4.0.1": - version: 4.0.4 - resolution: "@achingbrain/ssdp@npm:4.0.4" + version: 4.0.6 + resolution: "@achingbrain/ssdp@npm:4.0.6" dependencies: event-iterator: ^2.0.0 freeport-promise: ^2.0.0 merge-options: ^3.0.4 - xml2js: ^0.5.0 - checksum: ebd5b25f64a2cd8fd14e760f92824cc308784d833eba887bfabc68a678de692eab83ac6c6859e56a187b7ea78e4fbdc9403899ed2be9cb245e0591a925431565 + xml2js: ^0.6.2 + checksum: 18af3ebf0ddd331531730b3c998729cb3db8f915b7f8296e503babf813a42af284e62b271148ac3ac85aa2420bb1c813c2d3b661c90e75f2e6d8700f9302f50b + languageName: node + linkType: hard + +"@adraffy/ens-normalize@npm:1.10.0": + version: 1.10.0 + resolution: "@adraffy/ens-normalize@npm:1.10.0" + checksum: af0540f963a2632da2bbc37e36ea6593dcfc607b937857133791781e246d47f870d5e3d21fa70d5cfe94e772c284588c81ea3f5b7f4ea8fbb824369444e4dbcb languageName: node linkType: hard @@ -87,6 +94,7 @@ __metadata: "@aztec/circuit-types": "workspace:^" "@aztec/circuits.js": "workspace:^" "@aztec/foundation": "workspace:^" + "@aztec/kv-store": "workspace:^" "@aztec/merkle-tree": "workspace:^" "@aztec/noir-contracts": "workspace:^" "@jest/globals": ^29.5.0 @@ -117,6 +125,7 @@ __metadata: "@aztec/foundation": "workspace:^" "@aztec/kv-store": "workspace:^" "@aztec/l1-artifacts": "workspace:^" + "@aztec/types": "workspace:^" "@jest/globals": ^29.5.0 "@types/debug": ^4.1.7 "@types/jest": ^29.5.0 @@ -127,7 +136,7 @@ __metadata: debug: ^4.3.4 jest: ^29.5.0 jest-mock-extended: ^3.0.4 - lmdb: ^2.9.1 + lmdb: ^2.9.2 lodash.omit: ^4.5.0 ts-jest: ^29.1.0 ts-node: ^10.9.1 @@ -179,16 +188,10 @@ __metadata: "@aztec/world-state": "workspace:^" "@jest/globals": ^29.5.0 "@types/jest": ^29.5.0 - "@types/leveldown": ^4.0.4 - "@types/levelup": ^5.1.2 - "@types/memdown": ^3.0.0 "@types/node": ^18.7.23 jest: ^29.5.0 koa: ^2.14.2 koa-router: ^12.0.0 - levelup: ^5.1.1 - lmdb: ^2.9.1 - memdown: ^6.1.1 ts-jest: ^29.1.0 ts-node: ^10.9.1 tslib: ^2.4.0 @@ -198,40 +201,6 @@ __metadata: languageName: unknown linkType: soft -"@aztec/aztec-sandbox@workspace:aztec-sandbox": - version: 0.0.0-use.local - resolution: "@aztec/aztec-sandbox@workspace:aztec-sandbox" - dependencies: - "@aztec/accounts": "workspace:^" - "@aztec/aztec-node": "workspace:^" - "@aztec/aztec.js": "workspace:^" - "@aztec/circuit-types": "workspace:^" - "@aztec/circuits.js": "workspace:^" - "@aztec/ethereum": "workspace:^" - "@aztec/foundation": "workspace:^" - "@aztec/l1-artifacts": "workspace:^" - "@aztec/noir-compiler": "workspace:^" - "@aztec/noir-contracts": "workspace:^" - "@aztec/p2p": "workspace:^" - "@aztec/pxe": "workspace:^" - "@jest/globals": ^29.5.0 - "@types/jest": ^29.5.0 - "@types/koa": ^2.13.6 - abitype: ^0.8.11 - jest: ^29.5.0 - koa: ^2.14.2 - koa-router: ^12.0.0 - ts-jest: ^29.1.0 - ts-node: ^10.9.1 - typescript: ^5.0.4 - viem: ^1.2.5 - winston: ^3.10.0 - winston-daily-rotate-file: ^4.7.1 - bin: - aztec-sandbox: ./dest/bin/index.js - languageName: unknown - linkType: soft - "@aztec/aztec.js@workspace:^, @aztec/aztec.js@workspace:aztec.js": version: 0.0.0-use.local resolution: "@aztec/aztec.js@workspace:aztec.js" @@ -279,6 +248,43 @@ __metadata: languageName: unknown linkType: soft +"@aztec/aztec@workspace:aztec": + version: 0.0.0-use.local + resolution: "@aztec/aztec@workspace:aztec" + dependencies: + "@aztec/accounts": "workspace:^" + "@aztec/archiver": "workspace:^" + "@aztec/aztec-node": "workspace:^" + "@aztec/aztec.js": "workspace:^" + "@aztec/circuit-types": "workspace:^" + "@aztec/circuits.js": "workspace:^" + "@aztec/ethereum": "workspace:^" + "@aztec/foundation": "workspace:^" + "@aztec/kv-store": "workspace:^" + "@aztec/l1-artifacts": "workspace:^" + "@aztec/noir-compiler": "workspace:^" + "@aztec/noir-contracts": "workspace:^" + "@aztec/p2p": "workspace:^" + "@aztec/pxe": "workspace:^" + "@jest/globals": ^29.5.0 + "@types/jest": ^29.5.0 + "@types/koa": ^2.13.6 + abitype: ^0.8.11 + commander: ^11.1.0 + jest: ^29.5.0 + koa: ^2.14.2 + koa-router: ^12.0.0 + ts-jest: ^29.1.0 + ts-node: ^10.9.1 + typescript: ^5.0.4 + viem: ^1.2.5 + winston: ^3.10.0 + winston-daily-rotate-file: ^4.7.1 + bin: + aztec: ./dest/bin/index.js + languageName: unknown + linkType: soft + "@aztec/bb.js@portal:../barretenberg/ts::locator=%40aztec%2Faztec3-packages%40workspace%3A.": version: 0.0.0-use.local resolution: "@aztec/bb.js@portal:../barretenberg/ts::locator=%40aztec%2Faztec3-packages%40workspace%3A." @@ -325,6 +331,7 @@ __metadata: dependencies: "@aztec/bb.js": "portal:../../barretenberg/ts" "@aztec/foundation": "workspace:^" + "@aztec/types": "workspace:^" "@jest/globals": ^29.5.0 "@types/jest": ^29.5.0 "@types/lodash.chunk": ^4.2.7 @@ -347,6 +354,7 @@ __metadata: "@aztec/accounts": "workspace:^" "@aztec/aztec.js": "workspace:^" "@aztec/circuit-types": "workspace:^" + "@aztec/circuits.js": "workspace:^" "@aztec/ethereum": "workspace:^" "@aztec/foundation": "workspace:^" "@aztec/l1-artifacts": "workspace:^" @@ -555,7 +563,7 @@ __metadata: "@types/node": ^18.7.23 jest: ^29.5.0 jest-mock-extended: ^3.0.3 - lmdb: ^2.9.1 + lmdb: ^2.9.2 ts-jest: ^29.1.0 ts-node: ^10.9.1 typescript: ^5.0.4 @@ -580,16 +588,13 @@ __metadata: "@aztec/circuit-types": "workspace:^" "@aztec/circuits.js": "workspace:^" "@aztec/foundation": "workspace:^" + "@aztec/kv-store": "workspace:^" "@aztec/types": "workspace:^" "@jest/globals": ^29.5.0 "@types/jest": ^29.5.0 - "@types/levelup": ^5.1.2 - "@types/memdown": ^3.0.1 "@types/node": ^18.15.3 "@types/sha256": ^0.2.0 jest: ^29.5.0 - levelup: ^5.1.1 - memdown: ^6.1.1 sha256: ^0.2.0 ts-jest: ^29.1.0 ts-node: ^10.9.1 @@ -655,6 +660,7 @@ __metadata: "@aztec/circuit-types": "workspace:^" "@aztec/circuits.js": "workspace:^" "@aztec/foundation": "workspace:^" + "@aztec/kv-store": "workspace:^" "@aztec/merkle-tree": "workspace:^" "@aztec/noir-compiler": "workspace:^" "@aztec/types": "workspace:^" @@ -755,6 +761,7 @@ __metadata: "@aztec/key-store": "workspace:^" "@aztec/kv-store": "workspace:^" "@aztec/noir-compiler": "workspace:^" + "@aztec/noir-contracts": "workspace:^" "@aztec/noir-protocol-circuits": "workspace:^" "@aztec/types": "workspace:^" "@jest/globals": ^29.5.0 @@ -810,6 +817,7 @@ __metadata: "@aztec/circuits.js": "workspace:^" "@aztec/ethereum": "workspace:^" "@aztec/foundation": "workspace:^" + "@aztec/kv-store": "workspace:^" "@aztec/l1-artifacts": "workspace:^" "@aztec/merkle-tree": "workspace:^" "@aztec/noir-protocol-circuits": "workspace:^" @@ -875,6 +883,7 @@ __metadata: "@aztec/circuit-types": "workspace:^" "@aztec/circuits.js": "workspace:^" "@aztec/foundation": "workspace:^" + "@aztec/kv-store": "workspace:^" "@aztec/merkle-tree": "workspace:^" "@aztec/types": "workspace:^" "@jest/globals": ^29.5.0 @@ -884,7 +893,6 @@ __metadata: "@types/node": ^18.7.23 jest: ^29.5.0 jest-mock-extended: ^3.0.5 - levelup: ^5.1.1 memdown: ^6.1.1 ts-jest: ^29.1.0 ts-node: ^10.9.1 @@ -899,42 +907,43 @@ __metadata: languageName: unknown linkType: soft -"@babel/code-frame@npm:^7.0.0, @babel/code-frame@npm:^7.12.13, @babel/code-frame@npm:^7.16.7, @babel/code-frame@npm:^7.22.5": - version: 7.22.5 - resolution: "@babel/code-frame@npm:7.22.5" +"@babel/code-frame@npm:^7.0.0, @babel/code-frame@npm:^7.12.13, @babel/code-frame@npm:^7.22.13, @babel/code-frame@npm:^7.23.5": + version: 7.23.5 + resolution: "@babel/code-frame@npm:7.23.5" dependencies: - "@babel/highlight": ^7.22.5 - checksum: cfe804f518f53faaf9a1d3e0f9f74127ab9a004912c3a16fda07fb6a633393ecb9918a053cb71804204c1b7ec3d49e1699604715e2cfb0c9f7bc4933d324ebb6 + "@babel/highlight": ^7.23.4 + chalk: ^2.4.2 + checksum: d90981fdf56a2824a9b14d19a4c0e8db93633fd488c772624b4e83e0ceac6039a27cd298a247c3214faa952bf803ba23696172ae7e7235f3b97f43ba278c569a languageName: node linkType: hard -"@babel/compat-data@npm:^7.22.9": - version: 7.22.9 - resolution: "@babel/compat-data@npm:7.22.9" - checksum: bed77d9044ce948b4327b30dd0de0779fa9f3a7ed1f2d31638714ed00229fa71fc4d1617ae0eb1fad419338d3658d0e9a5a083297451e09e73e078d0347ff808 +"@babel/compat-data@npm:^7.23.5": + version: 7.23.5 + resolution: "@babel/compat-data@npm:7.23.5" + checksum: 06ce244cda5763295a0ea924728c09bae57d35713b675175227278896946f922a63edf803c322f855a3878323d48d0255a2a3023409d2a123483c8a69ebb4744 languageName: node linkType: hard "@babel/core@npm:^7.11.6, @babel/core@npm:^7.12.3": - version: 7.22.9 - resolution: "@babel/core@npm:7.22.9" + version: 7.23.9 + resolution: "@babel/core@npm:7.23.9" dependencies: "@ampproject/remapping": ^2.2.0 - "@babel/code-frame": ^7.22.5 - "@babel/generator": ^7.22.9 - "@babel/helper-compilation-targets": ^7.22.9 - "@babel/helper-module-transforms": ^7.22.9 - "@babel/helpers": ^7.22.6 - "@babel/parser": ^7.22.7 - "@babel/template": ^7.22.5 - "@babel/traverse": ^7.22.8 - "@babel/types": ^7.22.5 - convert-source-map: ^1.7.0 + "@babel/code-frame": ^7.23.5 + "@babel/generator": ^7.23.6 + "@babel/helper-compilation-targets": ^7.23.6 + "@babel/helper-module-transforms": ^7.23.3 + "@babel/helpers": ^7.23.9 + "@babel/parser": ^7.23.9 + "@babel/template": ^7.23.9 + "@babel/traverse": ^7.23.9 + "@babel/types": ^7.23.9 + convert-source-map: ^2.0.0 debug: ^4.1.0 gensync: ^1.0.0-beta.2 - json5: ^2.2.2 + json5: ^2.2.3 semver: ^6.3.1 - checksum: 7bf069aeceb417902c4efdaefab1f7b94adb7dea694a9aed1bda2edf4135348a080820529b1a300c6f8605740a00ca00c19b2d5e74b5dd489d99d8c11d5e56d1 + checksum: 634a511f74db52a5f5a283c1121f25e2227b006c095b84a02a40a9213842489cd82dc7d61cdc74e10b5bcd9bb0a4e28bab47635b54c7e2256d47ab57356e2a76 languageName: node linkType: hard @@ -949,51 +958,49 @@ __metadata: languageName: node linkType: hard -"@babel/generator@npm:^7.17.3, @babel/generator@npm:^7.22.7, @babel/generator@npm:^7.22.9, @babel/generator@npm:^7.7.2": - version: 7.22.9 - resolution: "@babel/generator@npm:7.22.9" +"@babel/generator@npm:^7.23.0, @babel/generator@npm:^7.23.6, @babel/generator@npm:^7.7.2": + version: 7.23.6 + resolution: "@babel/generator@npm:7.23.6" dependencies: - "@babel/types": ^7.22.5 + "@babel/types": ^7.23.6 "@jridgewell/gen-mapping": ^0.3.2 "@jridgewell/trace-mapping": ^0.3.17 jsesc: ^2.5.1 - checksum: 7c9d2c58b8d5ac5e047421a6ab03ec2ff5d9a5ff2c2212130a0055e063ac349e0b19d435537d6886c999771aef394832e4f54cd9fc810100a7f23d982f6af06b + checksum: 1a1a1c4eac210f174cd108d479464d053930a812798e09fee069377de39a893422df5b5b146199ead7239ae6d3a04697b45fc9ac6e38e0f6b76374390f91fc6c languageName: node linkType: hard -"@babel/helper-compilation-targets@npm:^7.22.9": - version: 7.22.9 - resolution: "@babel/helper-compilation-targets@npm:7.22.9" +"@babel/helper-compilation-targets@npm:^7.23.6": + version: 7.23.6 + resolution: "@babel/helper-compilation-targets@npm:7.23.6" dependencies: - "@babel/compat-data": ^7.22.9 - "@babel/helper-validator-option": ^7.22.5 - browserslist: ^4.21.9 + "@babel/compat-data": ^7.23.5 + "@babel/helper-validator-option": ^7.23.5 + browserslist: ^4.22.2 lru-cache: ^5.1.1 semver: ^6.3.1 - peerDependencies: - "@babel/core": ^7.0.0 - checksum: ea0006c6a93759025f4a35a25228ae260538c9f15023e8aac2a6d45ca68aef4cf86cfc429b19af9a402cbdd54d5de74ad3fbcf6baa7e48184dc079f1a791e178 + checksum: c630b98d4527ac8fe2c58d9a06e785dfb2b73ec71b7c4f2ddf90f814b5f75b547f3c015f110a010fd31f76e3864daaf09f3adcd2f6acdbfb18a8de3a48717590 languageName: node linkType: hard -"@babel/helper-environment-visitor@npm:^7.16.7, @babel/helper-environment-visitor@npm:^7.22.5": - version: 7.22.5 - resolution: "@babel/helper-environment-visitor@npm:7.22.5" - checksum: 248532077d732a34cd0844eb7b078ff917c3a8ec81a7f133593f71a860a582f05b60f818dc5049c2212e5baa12289c27889a4b81d56ef409b4863db49646c4b1 +"@babel/helper-environment-visitor@npm:^7.22.20": + version: 7.22.20 + resolution: "@babel/helper-environment-visitor@npm:7.22.20" + checksum: d80ee98ff66f41e233f36ca1921774c37e88a803b2f7dca3db7c057a5fea0473804db9fb6729e5dbfd07f4bed722d60f7852035c2c739382e84c335661590b69 languageName: node linkType: hard -"@babel/helper-function-name@npm:^7.16.7, @babel/helper-function-name@npm:^7.22.5": - version: 7.22.5 - resolution: "@babel/helper-function-name@npm:7.22.5" +"@babel/helper-function-name@npm:^7.23.0": + version: 7.23.0 + resolution: "@babel/helper-function-name@npm:7.23.0" dependencies: - "@babel/template": ^7.22.5 - "@babel/types": ^7.22.5 - checksum: 6b1f6ce1b1f4e513bf2c8385a557ea0dd7fa37971b9002ad19268ca4384bbe90c09681fe4c076013f33deabc63a53b341ed91e792de741b4b35e01c00238177a + "@babel/template": ^7.22.15 + "@babel/types": ^7.23.0 + checksum: e44542257b2d4634a1f979244eb2a4ad8e6d75eb6761b4cfceb56b562f7db150d134bc538c8e6adca3783e3bc31be949071527aa8e3aab7867d1ad2d84a26e10 languageName: node linkType: hard -"@babel/helper-hoist-variables@npm:^7.16.7, @babel/helper-hoist-variables@npm:^7.22.5": +"@babel/helper-hoist-variables@npm:^7.22.5": version: 7.22.5 resolution: "@babel/helper-hoist-variables@npm:7.22.5" dependencies: @@ -1002,27 +1009,27 @@ __metadata: languageName: node linkType: hard -"@babel/helper-module-imports@npm:^7.22.5": - version: 7.22.5 - resolution: "@babel/helper-module-imports@npm:7.22.5" +"@babel/helper-module-imports@npm:^7.22.15": + version: 7.22.15 + resolution: "@babel/helper-module-imports@npm:7.22.15" dependencies: - "@babel/types": ^7.22.5 - checksum: 9ac2b0404fa38b80bdf2653fbeaf8e8a43ccb41bd505f9741d820ed95d3c4e037c62a1bcdcb6c9527d7798d2e595924c4d025daed73283badc180ada2c9c49ad + "@babel/types": ^7.22.15 + checksum: ecd7e457df0a46f889228f943ef9b4a47d485d82e030676767e6a2fdcbdaa63594d8124d4b55fd160b41c201025aec01fc27580352b1c87a37c9c6f33d116702 languageName: node linkType: hard -"@babel/helper-module-transforms@npm:^7.22.9": - version: 7.22.9 - resolution: "@babel/helper-module-transforms@npm:7.22.9" +"@babel/helper-module-transforms@npm:^7.23.3": + version: 7.23.3 + resolution: "@babel/helper-module-transforms@npm:7.23.3" dependencies: - "@babel/helper-environment-visitor": ^7.22.5 - "@babel/helper-module-imports": ^7.22.5 + "@babel/helper-environment-visitor": ^7.22.20 + "@babel/helper-module-imports": ^7.22.15 "@babel/helper-simple-access": ^7.22.5 "@babel/helper-split-export-declaration": ^7.22.6 - "@babel/helper-validator-identifier": ^7.22.5 + "@babel/helper-validator-identifier": ^7.22.20 peerDependencies: "@babel/core": ^7.0.0 - checksum: 2751f77660518cf4ff027514d6f4794f04598c6393be7b04b8e46c6e21606e11c19f3f57ab6129a9c21bacdf8b3ffe3af87bb401d972f34af2d0ffde02ac3001 + checksum: 5d0895cfba0e16ae16f3aa92fee108517023ad89a855289c4eb1d46f7aef4519adf8e6f971e1d55ac20c5461610e17213f1144097a8f932e768a9132e2278d71 languageName: node linkType: hard @@ -1042,7 +1049,7 @@ __metadata: languageName: node linkType: hard -"@babel/helper-split-export-declaration@npm:^7.16.7, @babel/helper-split-export-declaration@npm:^7.22.6": +"@babel/helper-split-export-declaration@npm:^7.22.6": version: 7.22.6 resolution: "@babel/helper-split-export-declaration@npm:7.22.6" dependencies: @@ -1051,64 +1058,55 @@ __metadata: languageName: node linkType: hard -"@babel/helper-string-parser@npm:^7.22.5": - version: 7.22.5 - resolution: "@babel/helper-string-parser@npm:7.22.5" - checksum: 836851ca5ec813077bbb303acc992d75a360267aa3b5de7134d220411c852a6f17de7c0d0b8c8dcc0f567f67874c00f4528672b2a4f1bc978a3ada64c8c78467 +"@babel/helper-string-parser@npm:^7.23.4": + version: 7.23.4 + resolution: "@babel/helper-string-parser@npm:7.23.4" + checksum: c0641144cf1a7e7dc93f3d5f16d5327465b6cf5d036b48be61ecba41e1eece161b48f46b7f960951b67f8c3533ce506b16dece576baef4d8b3b49f8c65410f90 languageName: node linkType: hard -"@babel/helper-validator-identifier@npm:^7.16.7, @babel/helper-validator-identifier@npm:^7.22.5": - version: 7.22.5 - resolution: "@babel/helper-validator-identifier@npm:7.22.5" - checksum: 7f0f30113474a28298c12161763b49de5018732290ca4de13cdaefd4fd0d635a6fe3f6686c37a02905fb1e64f21a5ee2b55140cf7b070e729f1bd66866506aea +"@babel/helper-validator-identifier@npm:^7.16.7, @babel/helper-validator-identifier@npm:^7.22.20": + version: 7.22.20 + resolution: "@babel/helper-validator-identifier@npm:7.22.20" + checksum: 136412784d9428266bcdd4d91c32bcf9ff0e8d25534a9d94b044f77fe76bc50f941a90319b05aafd1ec04f7d127cd57a179a3716009ff7f3412ef835ada95bdc languageName: node linkType: hard -"@babel/helper-validator-option@npm:^7.22.5": - version: 7.22.5 - resolution: "@babel/helper-validator-option@npm:7.22.5" - checksum: bbeca8a85ee86990215c0424997438b388b8d642d69b9f86c375a174d3cdeb270efafd1ff128bc7a1d370923d13b6e45829ba8581c027620e83e3a80c5c414b3 +"@babel/helper-validator-option@npm:^7.23.5": + version: 7.23.5 + resolution: "@babel/helper-validator-option@npm:7.23.5" + checksum: 537cde2330a8aede223552510e8a13e9c1c8798afee3757995a7d4acae564124fe2bf7e7c3d90d62d3657434a74340a274b3b3b1c6f17e9a2be1f48af29cb09e languageName: node linkType: hard -"@babel/helpers@npm:^7.22.6": - version: 7.22.6 - resolution: "@babel/helpers@npm:7.22.6" +"@babel/helpers@npm:^7.23.9": + version: 7.23.9 + resolution: "@babel/helpers@npm:7.23.9" dependencies: - "@babel/template": ^7.22.5 - "@babel/traverse": ^7.22.6 - "@babel/types": ^7.22.5 - checksum: 5c1f33241fe7bf7709868c2105134a0a86dca26a0fbd508af10a89312b1f77ca38ebae43e50be3b208613c5eacca1559618af4ca236f0abc55d294800faeff30 + "@babel/template": ^7.23.9 + "@babel/traverse": ^7.23.9 + "@babel/types": ^7.23.9 + checksum: 2678231192c0471dbc2fc403fb19456cc46b1afefcfebf6bc0f48b2e938fdb0fef2e0fe90c8c8ae1f021dae5012b700372e4b5d15867f1d7764616532e4a6324 languageName: node linkType: hard -"@babel/highlight@npm:^7.22.5": - version: 7.22.5 - resolution: "@babel/highlight@npm:7.22.5" +"@babel/highlight@npm:^7.23.4": + version: 7.23.4 + resolution: "@babel/highlight@npm:7.23.4" dependencies: - "@babel/helper-validator-identifier": ^7.22.5 - chalk: ^2.0.0 + "@babel/helper-validator-identifier": ^7.22.20 + chalk: ^2.4.2 js-tokens: ^4.0.0 - checksum: f61ae6de6ee0ea8d9b5bcf2a532faec5ab0a1dc0f7c640e5047fc61630a0edb88b18d8c92eb06566d30da7a27db841aca11820ecd3ebe9ce514c9350fbed39c4 + checksum: 643acecdc235f87d925979a979b539a5d7d1f31ae7db8d89047269082694122d11aa85351304c9c978ceeb6d250591ccadb06c366f358ccee08bb9c122476b89 languageName: node linkType: hard -"@babel/parser@npm:^7.0.0, @babel/parser@npm:^7.21.4": - version: 7.23.0 - resolution: "@babel/parser@npm:7.23.0" +"@babel/parser@npm:^7.0.0, @babel/parser@npm:^7.1.0, @babel/parser@npm:^7.14.7, @babel/parser@npm:^7.20.5, @babel/parser@npm:^7.20.7, @babel/parser@npm:^7.21.4, @babel/parser@npm:^7.23.0, @babel/parser@npm:^7.23.9": + version: 7.23.9 + resolution: "@babel/parser@npm:7.23.9" bin: parser: ./bin/babel-parser.js - checksum: 453fdf8b9e2c2b7d7b02139e0ce003d1af21947bbc03eb350fb248ee335c9b85e4ab41697ddbdd97079698de825a265e45a0846bb2ed47a2c7c1df833f42a354 - languageName: node - linkType: hard - -"@babel/parser@npm:^7.1.0, @babel/parser@npm:^7.14.7, @babel/parser@npm:^7.17.3, @babel/parser@npm:^7.20.5, @babel/parser@npm:^7.20.7, @babel/parser@npm:^7.22.5, @babel/parser@npm:^7.22.7": - version: 7.22.7 - resolution: "@babel/parser@npm:7.22.7" - bin: - parser: ./bin/babel-parser.js - checksum: 02209ddbd445831ee8bf966fdf7c29d189ed4b14343a68eb2479d940e7e3846340d7cc6bd654a5f3d87d19dc84f49f50a58cf9363bee249dc5409ff3ba3dab54 + checksum: e7cd4960ac8671774e13803349da88d512f9292d7baa952173260d3e8f15620a28a3701f14f709d769209022f9e7b79965256b8be204fc550cfe783cdcabe7c7 languageName: node linkType: hard @@ -1168,13 +1166,13 @@ __metadata: linkType: hard "@babel/plugin-syntax-jsx@npm:^7.7.2": - version: 7.22.5 - resolution: "@babel/plugin-syntax-jsx@npm:7.22.5" + version: 7.23.3 + resolution: "@babel/plugin-syntax-jsx@npm:7.23.3" dependencies: "@babel/helper-plugin-utils": ^7.22.5 peerDependencies: "@babel/core": ^7.0.0-0 - checksum: 8829d30c2617ab31393d99cec2978e41f014f4ac6f01a1cecf4c4dd8320c3ec12fdc3ce121126b2d8d32f6887e99ca1a0bad53dedb1e6ad165640b92b24980ce + checksum: 89037694314a74e7f0e7a9c8d3793af5bf6b23d80950c29b360db1c66859d67f60711ea437e70ad6b5b4b29affe17eababda841b6c01107c2b638e0493bafb4e languageName: node linkType: hard @@ -1256,69 +1254,69 @@ __metadata: linkType: hard "@babel/plugin-syntax-typescript@npm:^7.7.2": - version: 7.22.5 - resolution: "@babel/plugin-syntax-typescript@npm:7.22.5" + version: 7.23.3 + resolution: "@babel/plugin-syntax-typescript@npm:7.23.3" dependencies: "@babel/helper-plugin-utils": ^7.22.5 peerDependencies: "@babel/core": ^7.0.0-0 - checksum: 8ab7718fbb026d64da93681a57797d60326097fd7cb930380c8bffd9eb101689e90142c760a14b51e8e69c88a73ba3da956cb4520a3b0c65743aee5c71ef360a + checksum: abfad3a19290d258b028e285a1f34c9b8a0cbe46ef79eafed4ed7ffce11b5d0720b5e536c82f91cbd8442cde35a3dd8e861fa70366d87ff06fdc0d4756e30876 languageName: node linkType: hard "@babel/runtime@npm:^7.21.0": - version: 7.22.6 - resolution: "@babel/runtime@npm:7.22.6" + version: 7.23.9 + resolution: "@babel/runtime@npm:7.23.9" dependencies: - regenerator-runtime: ^0.13.11 - checksum: e585338287c4514a713babf4fdb8fc2a67adcebab3e7723a739fc62c79cfda875b314c90fd25f827afb150d781af97bc16c85bfdbfa2889f06053879a1ddb597 + regenerator-runtime: ^0.14.0 + checksum: 6bbebe8d27c0c2dd275d1ac197fc1a6c00e18dab68cc7aaff0adc3195b45862bae9c4cc58975629004b0213955b2ed91e99eccb3d9b39cabea246c657323d667 languageName: node linkType: hard -"@babel/template@npm:^7.22.5, @babel/template@npm:^7.3.3": - version: 7.22.5 - resolution: "@babel/template@npm:7.22.5" +"@babel/template@npm:^7.22.15, @babel/template@npm:^7.23.9, @babel/template@npm:^7.3.3": + version: 7.23.9 + resolution: "@babel/template@npm:7.23.9" dependencies: - "@babel/code-frame": ^7.22.5 - "@babel/parser": ^7.22.5 - "@babel/types": ^7.22.5 - checksum: c5746410164039aca61829cdb42e9a55410f43cace6f51ca443313f3d0bdfa9a5a330d0b0df73dc17ef885c72104234ae05efede37c1cc8a72dc9f93425977a3 + "@babel/code-frame": ^7.23.5 + "@babel/parser": ^7.23.9 + "@babel/types": ^7.23.9 + checksum: 6e67414c0f7125d7ecaf20c11fab88085fa98a96c3ef10da0a61e962e04fdf3a18a496a66047005ddd1bb682a7cc7842d556d1db2f3f3f6ccfca97d5e445d342 languageName: node linkType: hard -"@babel/traverse@npm:7.17.3": - version: 7.17.3 - resolution: "@babel/traverse@npm:7.17.3" +"@babel/traverse@npm:7.23.2": + version: 7.23.2 + resolution: "@babel/traverse@npm:7.23.2" dependencies: - "@babel/code-frame": ^7.16.7 - "@babel/generator": ^7.17.3 - "@babel/helper-environment-visitor": ^7.16.7 - "@babel/helper-function-name": ^7.16.7 - "@babel/helper-hoist-variables": ^7.16.7 - "@babel/helper-split-export-declaration": ^7.16.7 - "@babel/parser": ^7.17.3 - "@babel/types": ^7.17.0 + "@babel/code-frame": ^7.22.13 + "@babel/generator": ^7.23.0 + "@babel/helper-environment-visitor": ^7.22.20 + "@babel/helper-function-name": ^7.23.0 + "@babel/helper-hoist-variables": ^7.22.5 + "@babel/helper-split-export-declaration": ^7.22.6 + "@babel/parser": ^7.23.0 + "@babel/types": ^7.23.0 debug: ^4.1.0 globals: ^11.1.0 - checksum: 780d7ecf711758174989794891af08d378f81febdb8932056c0d9979524bf0298e28f8e7708a872d7781151506c28f56c85c63ea3f1f654662c2fcb8a3eb9fdc + checksum: 26a1eea0dde41ab99dde8b9773a013a0dc50324e5110a049f5d634e721ff08afffd54940b3974a20308d7952085ac769689369e9127dea655f868c0f6e1ab35d languageName: node linkType: hard -"@babel/traverse@npm:^7.22.6, @babel/traverse@npm:^7.22.8": - version: 7.22.8 - resolution: "@babel/traverse@npm:7.22.8" +"@babel/traverse@npm:^7.23.9": + version: 7.23.9 + resolution: "@babel/traverse@npm:7.23.9" dependencies: - "@babel/code-frame": ^7.22.5 - "@babel/generator": ^7.22.7 - "@babel/helper-environment-visitor": ^7.22.5 - "@babel/helper-function-name": ^7.22.5 + "@babel/code-frame": ^7.23.5 + "@babel/generator": ^7.23.6 + "@babel/helper-environment-visitor": ^7.22.20 + "@babel/helper-function-name": ^7.23.0 "@babel/helper-hoist-variables": ^7.22.5 "@babel/helper-split-export-declaration": ^7.22.6 - "@babel/parser": ^7.22.7 - "@babel/types": ^7.22.5 - debug: ^4.1.0 + "@babel/parser": ^7.23.9 + "@babel/types": ^7.23.9 + debug: ^4.3.1 globals: ^11.1.0 - checksum: a381369bc3eedfd13ed5fef7b884657f1c29024ea7388198149f0edc34bd69ce3966e9f40188d15f56490a5e12ba250ccc485f2882b53d41b054fccefb233e33 + checksum: a932f7aa850e158c00c97aad22f639d48c72805c687290f6a73e30c5c4957c07f5d28310c9bf59648e2980fe6c9d16adeb2ff92a9ca0f97fa75739c1328fc6c3 languageName: node linkType: hard @@ -1332,14 +1330,14 @@ __metadata: languageName: node linkType: hard -"@babel/types@npm:^7.0.0, @babel/types@npm:^7.17.0, @babel/types@npm:^7.20.7, @babel/types@npm:^7.22.5, @babel/types@npm:^7.3.3, @babel/types@npm:^7.8.3": - version: 7.22.5 - resolution: "@babel/types@npm:7.22.5" +"@babel/types@npm:^7.0.0, @babel/types@npm:^7.17.0, @babel/types@npm:^7.20.7, @babel/types@npm:^7.22.15, @babel/types@npm:^7.22.5, @babel/types@npm:^7.23.0, @babel/types@npm:^7.23.6, @babel/types@npm:^7.23.9, @babel/types@npm:^7.3.3, @babel/types@npm:^7.8.3": + version: 7.23.9 + resolution: "@babel/types@npm:7.23.9" dependencies: - "@babel/helper-string-parser": ^7.22.5 - "@babel/helper-validator-identifier": ^7.22.5 + "@babel/helper-string-parser": ^7.23.4 + "@babel/helper-validator-identifier": ^7.22.20 to-fast-properties: ^2.0.0 - checksum: c13a9c1dc7d2d1a241a2f8363540cb9af1d66e978e8984b400a20c4f38ba38ca29f06e26a0f2d49a70bad9e57615dac09c35accfddf1bb90d23cd3e0a0bab892 + checksum: 0a9b008e9bfc89beb8c185e620fa0f8ed6c771f1e1b2e01e1596870969096fec7793898a1d64a035176abf1dd13e2668ee30bf699f2d92c210a8128f4b151e65 languageName: node linkType: hard @@ -1350,14 +1348,21 @@ __metadata: languageName: node linkType: hard -"@chainsafe/is-ip@npm:^2.0.1": - version: 2.0.1 - resolution: "@chainsafe/is-ip@npm:2.0.1" - checksum: c5bbebe58eadc700d12112713fd75e41ae26020eedcac0e8b593fdda16a180239ede1d68d55b500fd8b30189c9a82b5e10769ed86a816e879c83248069152b79 +"@chainsafe/as-chacha20poly1305@npm:^0.1.0": + version: 0.1.0 + resolution: "@chainsafe/as-chacha20poly1305@npm:0.1.0" + checksum: 2bf38f0595bb379489388174e2049e57ae1512815a80fddfe5b9055865739a970651847892233e53cde6a3e34554ede97d1abd8a0f02b4a0005a1a6cf0146a6c + languageName: node + linkType: hard + +"@chainsafe/as-sha256@npm:^0.4.1": + version: 0.4.1 + resolution: "@chainsafe/as-sha256@npm:0.4.1" + checksum: 6d86975e648ecdafd366802278ac15b392b252e967f3681412ec48b5a3518b936cc5e977517499882b084991446d25787d98f8f585891943688cc81549a44e9a languageName: node linkType: hard -"@chainsafe/is-ip@npm:^2.0.2": +"@chainsafe/is-ip@npm:^2.0.1, @chainsafe/is-ip@npm:^2.0.2": version: 2.0.2 resolution: "@chainsafe/is-ip@npm:2.0.2" checksum: 2600350ba1c8fbad5d1ebee71317beeb29fbaebf43780d89e30f8c6c2d27b95ebdab0284dfbab7336b5eb6d8ffcc7081e3e4c5b221889dc366463f83bbe38adb @@ -1365,14 +1370,16 @@ __metadata: linkType: hard "@chainsafe/libp2p-noise@npm:^13.0.0": - version: 13.0.0 - resolution: "@chainsafe/libp2p-noise@npm:13.0.0" + version: 13.0.5 + resolution: "@chainsafe/libp2p-noise@npm:13.0.5" dependencies: + "@chainsafe/as-chacha20poly1305": ^0.1.0 + "@chainsafe/as-sha256": ^0.4.1 "@libp2p/crypto": ^2.0.0 "@libp2p/interface": ^0.1.0 "@libp2p/logger": ^3.0.0 "@libp2p/peer-id": ^3.0.0 - "@noble/ciphers": ^0.1.4 + "@noble/ciphers": ^0.4.0 "@noble/curves": ^1.1.0 "@noble/hashes": ^1.3.1 it-byte-stream: ^1.0.0 @@ -1384,22 +1391,23 @@ __metadata: protons-runtime: ^5.0.0 uint8arraylist: ^2.4.3 uint8arrays: ^4.0.4 - checksum: 177c44003cb550b3ce1f867db2f5a3bb8f97bf368095a1a5bd4ccddd64bc2fc4167119a61fa6ed045abe8ef2dbbea9ddac9b8e1d6381d9056749b900029b8de5 + wherearewe: ^2.0.1 + checksum: 98dd78bfd547501280c7a318acfa81624016351f18e0a28debe451340418fd263f90ce8d7358d5b83f61429522de6631321606b98eda35609114041853057af1 languageName: node linkType: hard "@chainsafe/libp2p-yamux@npm:^5.0.0": - version: 5.0.0 - resolution: "@chainsafe/libp2p-yamux@npm:5.0.0" + version: 5.0.4 + resolution: "@chainsafe/libp2p-yamux@npm:5.0.4" dependencies: "@libp2p/interface": ^0.1.0 "@libp2p/logger": ^3.0.0 - abortable-iterator: ^5.0.1 + get-iterator: ^2.0.1 it-foreach: ^2.0.3 it-pipe: ^3.0.1 it-pushable: ^3.2.0 uint8arraylist: ^2.4.3 - checksum: 8de6f9e16097ed983ab3d0da2dffe752f09c2c09473783d39dcb2d54b788fe4353bbed417a3870fb4de151de906a734a73cd53e2b2b4ccec4392d2195828137c + checksum: 58c33b28d8da2b8c6813127de2cc4005f2f09d845cf535c56a3db0495774a2feb935d2813d2141bb6a747f3c1181dfde415945821d489ac7f987e36df744721f languageName: node linkType: hard @@ -1412,10 +1420,10 @@ __metadata: languageName: node linkType: hard -"@colors/colors@npm:1.5.0": - version: 1.5.0 - resolution: "@colors/colors@npm:1.5.0" - checksum: d64d5260bed1d5012ae3fc617d38d1afc0329fec05342f4e6b838f46998855ba56e0a73833f4a80fa8378c84810da254f76a8a19c39d038260dc06dc4e007425 +"@colors/colors@npm:1.6.0, @colors/colors@npm:^1.6.0": + version: 1.6.0 + resolution: "@colors/colors@npm:1.6.0" + checksum: aa209963e0c3218e80a4a20553ba8c0fbb6fa13140540b4e5f97923790be06801fc90172c1114fc8b7e888b3d012b67298cde6b9e81521361becfaee400c662f languageName: node linkType: hard @@ -1467,156 +1475,156 @@ __metadata: languageName: node linkType: hard -"@esbuild/android-arm64@npm:0.18.17": - version: 0.18.17 - resolution: "@esbuild/android-arm64@npm:0.18.17" +"@esbuild/android-arm64@npm:0.18.20": + version: 0.18.20 + resolution: "@esbuild/android-arm64@npm:0.18.20" conditions: os=android & cpu=arm64 languageName: node linkType: hard -"@esbuild/android-arm@npm:0.18.17": - version: 0.18.17 - resolution: "@esbuild/android-arm@npm:0.18.17" +"@esbuild/android-arm@npm:0.18.20": + version: 0.18.20 + resolution: "@esbuild/android-arm@npm:0.18.20" conditions: os=android & cpu=arm languageName: node linkType: hard -"@esbuild/android-x64@npm:0.18.17": - version: 0.18.17 - resolution: "@esbuild/android-x64@npm:0.18.17" +"@esbuild/android-x64@npm:0.18.20": + version: 0.18.20 + resolution: "@esbuild/android-x64@npm:0.18.20" conditions: os=android & cpu=x64 languageName: node linkType: hard -"@esbuild/darwin-arm64@npm:0.18.17": - version: 0.18.17 - resolution: "@esbuild/darwin-arm64@npm:0.18.17" +"@esbuild/darwin-arm64@npm:0.18.20": + version: 0.18.20 + resolution: "@esbuild/darwin-arm64@npm:0.18.20" conditions: os=darwin & cpu=arm64 languageName: node linkType: hard -"@esbuild/darwin-x64@npm:0.18.17": - version: 0.18.17 - resolution: "@esbuild/darwin-x64@npm:0.18.17" +"@esbuild/darwin-x64@npm:0.18.20": + version: 0.18.20 + resolution: "@esbuild/darwin-x64@npm:0.18.20" conditions: os=darwin & cpu=x64 languageName: node linkType: hard -"@esbuild/freebsd-arm64@npm:0.18.17": - version: 0.18.17 - resolution: "@esbuild/freebsd-arm64@npm:0.18.17" +"@esbuild/freebsd-arm64@npm:0.18.20": + version: 0.18.20 + resolution: "@esbuild/freebsd-arm64@npm:0.18.20" conditions: os=freebsd & cpu=arm64 languageName: node linkType: hard -"@esbuild/freebsd-x64@npm:0.18.17": - version: 0.18.17 - resolution: "@esbuild/freebsd-x64@npm:0.18.17" +"@esbuild/freebsd-x64@npm:0.18.20": + version: 0.18.20 + resolution: "@esbuild/freebsd-x64@npm:0.18.20" conditions: os=freebsd & cpu=x64 languageName: node linkType: hard -"@esbuild/linux-arm64@npm:0.18.17": - version: 0.18.17 - resolution: "@esbuild/linux-arm64@npm:0.18.17" +"@esbuild/linux-arm64@npm:0.18.20": + version: 0.18.20 + resolution: "@esbuild/linux-arm64@npm:0.18.20" conditions: os=linux & cpu=arm64 languageName: node linkType: hard -"@esbuild/linux-arm@npm:0.18.17": - version: 0.18.17 - resolution: "@esbuild/linux-arm@npm:0.18.17" +"@esbuild/linux-arm@npm:0.18.20": + version: 0.18.20 + resolution: "@esbuild/linux-arm@npm:0.18.20" conditions: os=linux & cpu=arm languageName: node linkType: hard -"@esbuild/linux-ia32@npm:0.18.17": - version: 0.18.17 - resolution: "@esbuild/linux-ia32@npm:0.18.17" +"@esbuild/linux-ia32@npm:0.18.20": + version: 0.18.20 + resolution: "@esbuild/linux-ia32@npm:0.18.20" conditions: os=linux & cpu=ia32 languageName: node linkType: hard -"@esbuild/linux-loong64@npm:0.18.17": - version: 0.18.17 - resolution: "@esbuild/linux-loong64@npm:0.18.17" +"@esbuild/linux-loong64@npm:0.18.20": + version: 0.18.20 + resolution: "@esbuild/linux-loong64@npm:0.18.20" conditions: os=linux & cpu=loong64 languageName: node linkType: hard -"@esbuild/linux-mips64el@npm:0.18.17": - version: 0.18.17 - resolution: "@esbuild/linux-mips64el@npm:0.18.17" +"@esbuild/linux-mips64el@npm:0.18.20": + version: 0.18.20 + resolution: "@esbuild/linux-mips64el@npm:0.18.20" conditions: os=linux & cpu=mips64el languageName: node linkType: hard -"@esbuild/linux-ppc64@npm:0.18.17": - version: 0.18.17 - resolution: "@esbuild/linux-ppc64@npm:0.18.17" +"@esbuild/linux-ppc64@npm:0.18.20": + version: 0.18.20 + resolution: "@esbuild/linux-ppc64@npm:0.18.20" conditions: os=linux & cpu=ppc64 languageName: node linkType: hard -"@esbuild/linux-riscv64@npm:0.18.17": - version: 0.18.17 - resolution: "@esbuild/linux-riscv64@npm:0.18.17" +"@esbuild/linux-riscv64@npm:0.18.20": + version: 0.18.20 + resolution: "@esbuild/linux-riscv64@npm:0.18.20" conditions: os=linux & cpu=riscv64 languageName: node linkType: hard -"@esbuild/linux-s390x@npm:0.18.17": - version: 0.18.17 - resolution: "@esbuild/linux-s390x@npm:0.18.17" +"@esbuild/linux-s390x@npm:0.18.20": + version: 0.18.20 + resolution: "@esbuild/linux-s390x@npm:0.18.20" conditions: os=linux & cpu=s390x languageName: node linkType: hard -"@esbuild/linux-x64@npm:0.18.17": - version: 0.18.17 - resolution: "@esbuild/linux-x64@npm:0.18.17" +"@esbuild/linux-x64@npm:0.18.20": + version: 0.18.20 + resolution: "@esbuild/linux-x64@npm:0.18.20" conditions: os=linux & cpu=x64 languageName: node linkType: hard -"@esbuild/netbsd-x64@npm:0.18.17": - version: 0.18.17 - resolution: "@esbuild/netbsd-x64@npm:0.18.17" +"@esbuild/netbsd-x64@npm:0.18.20": + version: 0.18.20 + resolution: "@esbuild/netbsd-x64@npm:0.18.20" conditions: os=netbsd & cpu=x64 languageName: node linkType: hard -"@esbuild/openbsd-x64@npm:0.18.17": - version: 0.18.17 - resolution: "@esbuild/openbsd-x64@npm:0.18.17" +"@esbuild/openbsd-x64@npm:0.18.20": + version: 0.18.20 + resolution: "@esbuild/openbsd-x64@npm:0.18.20" conditions: os=openbsd & cpu=x64 languageName: node linkType: hard -"@esbuild/sunos-x64@npm:0.18.17": - version: 0.18.17 - resolution: "@esbuild/sunos-x64@npm:0.18.17" +"@esbuild/sunos-x64@npm:0.18.20": + version: 0.18.20 + resolution: "@esbuild/sunos-x64@npm:0.18.20" conditions: os=sunos & cpu=x64 languageName: node linkType: hard -"@esbuild/win32-arm64@npm:0.18.17": - version: 0.18.17 - resolution: "@esbuild/win32-arm64@npm:0.18.17" +"@esbuild/win32-arm64@npm:0.18.20": + version: 0.18.20 + resolution: "@esbuild/win32-arm64@npm:0.18.20" conditions: os=win32 & cpu=arm64 languageName: node linkType: hard -"@esbuild/win32-ia32@npm:0.18.17": - version: 0.18.17 - resolution: "@esbuild/win32-ia32@npm:0.18.17" +"@esbuild/win32-ia32@npm:0.18.20": + version: 0.18.20 + resolution: "@esbuild/win32-ia32@npm:0.18.20" conditions: os=win32 & cpu=ia32 languageName: node linkType: hard -"@esbuild/win32-x64@npm:0.18.17": - version: 0.18.17 - resolution: "@esbuild/win32-x64@npm:0.18.17" +"@esbuild/win32-x64@npm:0.18.20": + version: 0.18.20 + resolution: "@esbuild/win32-x64@npm:0.18.20" conditions: os=win32 & cpu=x64 languageName: node linkType: hard @@ -1633,15 +1641,15 @@ __metadata: linkType: hard "@eslint-community/regexpp@npm:^4.5.1, @eslint-community/regexpp@npm:^4.6.1": - version: 4.6.2 - resolution: "@eslint-community/regexpp@npm:4.6.2" - checksum: a3c341377b46b54fa228f455771b901d1a2717f95d47dcdf40199df30abc000ba020f747f114f08560d119e979d882a94cf46cfc51744544d54b00319c0f2724 + version: 4.10.0 + resolution: "@eslint-community/regexpp@npm:4.10.0" + checksum: 2a6e345429ea8382aaaf3a61f865cae16ed44d31ca917910033c02dc00d505d939f10b81e079fa14d43b51499c640138e153b7e40743c4c094d9df97d4e56f7b languageName: node linkType: hard -"@eslint/eslintrc@npm:^2.1.1": - version: 2.1.1 - resolution: "@eslint/eslintrc@npm:2.1.1" +"@eslint/eslintrc@npm:^2.1.4": + version: 2.1.4 + resolution: "@eslint/eslintrc@npm:2.1.4" dependencies: ajv: ^6.12.4 debug: ^4.3.2 @@ -1652,25 +1660,25 @@ __metadata: js-yaml: ^4.1.0 minimatch: ^3.1.2 strip-json-comments: ^3.1.1 - checksum: bf909ea183d27238c257a82d4ffdec38ca94b906b4b8dfae02ecbe7ecc9e5a8182ef5e469c808bb8cb4fea4750f43ac4ca7c4b4a167b6cd7e3aaacd386b2bd25 + checksum: 10957c7592b20ca0089262d8c2a8accbad14b4f6507e35416c32ee6b4dbf9cad67dfb77096bbd405405e9ada2b107f3797fe94362e1c55e0b09d6e90dd149127 languageName: node linkType: hard -"@eslint/js@npm:^8.46.0": - version: 8.46.0 - resolution: "@eslint/js@npm:8.46.0" - checksum: 7aed479832302882faf5bec37e9d068f270f84c19b3fb529646a7c1b031e73a312f730569c78806492bc09cfce3d7651dfab4ce09a56cbb06bc6469449e56377 +"@eslint/js@npm:8.56.0": + version: 8.56.0 + resolution: "@eslint/js@npm:8.56.0" + checksum: 5804130574ef810207bdf321c265437814e7a26f4e6fac9b496de3206afd52f533e09ec002a3be06cd9adcc9da63e727f1883938e663c4e4751c007d5b58e539 languageName: node linkType: hard -"@humanwhocodes/config-array@npm:^0.11.10": - version: 0.11.10 - resolution: "@humanwhocodes/config-array@npm:0.11.10" +"@humanwhocodes/config-array@npm:^0.11.13": + version: 0.11.14 + resolution: "@humanwhocodes/config-array@npm:0.11.14" dependencies: - "@humanwhocodes/object-schema": ^1.2.1 - debug: ^4.1.1 + "@humanwhocodes/object-schema": ^2.0.2 + debug: ^4.3.1 minimatch: ^3.0.5 - checksum: 1b1302e2403d0e35bc43e66d67a2b36b0ad1119efc704b5faff68c41f791a052355b010fb2d27ef022670f550de24cd6d08d5ecf0821c16326b7dcd0ee5d5d8a + checksum: 861ccce9eaea5de19546653bccf75bf09fe878bc39c3aab00aeee2d2a0e654516adad38dd1098aab5e3af0145bbcbf3f309bdf4d964f8dab9dcd5834ae4c02f2 languageName: node linkType: hard @@ -1681,10 +1689,10 @@ __metadata: languageName: node linkType: hard -"@humanwhocodes/object-schema@npm:^1.2.1": - version: 1.2.1 - resolution: "@humanwhocodes/object-schema@npm:1.2.1" - checksum: a824a1ec31591231e4bad5787641f59e9633827d0a2eaae131a288d33c9ef0290bd16fda8da6f7c0fcb014147865d12118df10db57f27f41e20da92369fcb3f1 +"@humanwhocodes/object-schema@npm:^2.0.2": + version: 2.0.2 + resolution: "@humanwhocodes/object-schema@npm:2.0.2" + checksum: 2fc11503361b5fb4f14714c700c02a3f4c7c93e9acd6b87a29f62c522d90470f364d6161b03d1cc618b979f2ae02aed1106fd29d302695d8927e2fc8165ba8ee languageName: node linkType: hard @@ -1729,50 +1737,50 @@ __metadata: languageName: node linkType: hard -"@jest/console@npm:^29.6.2": - version: 29.6.2 - resolution: "@jest/console@npm:29.6.2" +"@jest/console@npm:^29.7.0": + version: 29.7.0 + resolution: "@jest/console@npm:29.7.0" dependencies: - "@jest/types": ^29.6.1 + "@jest/types": ^29.6.3 "@types/node": "*" chalk: ^4.0.0 - jest-message-util: ^29.6.2 - jest-util: ^29.6.2 + jest-message-util: ^29.7.0 + jest-util: ^29.7.0 slash: ^3.0.0 - checksum: 1198667bda0430770c3e9b92681c0ee9f8346394574071c633f306192ac5f08e12972d6a5fdf03eb0d441051c8439bce0f6f9f355dc60d98777a35328331ba2e + checksum: 0e3624e32c5a8e7361e889db70b170876401b7d70f509a2538c31d5cd50deb0c1ae4b92dc63fe18a0902e0a48c590c21d53787a0df41a52b34fa7cab96c384d6 languageName: node linkType: hard -"@jest/core@npm:^29.6.2": - version: 29.6.2 - resolution: "@jest/core@npm:29.6.2" +"@jest/core@npm:^29.7.0": + version: 29.7.0 + resolution: "@jest/core@npm:29.7.0" dependencies: - "@jest/console": ^29.6.2 - "@jest/reporters": ^29.6.2 - "@jest/test-result": ^29.6.2 - "@jest/transform": ^29.6.2 - "@jest/types": ^29.6.1 + "@jest/console": ^29.7.0 + "@jest/reporters": ^29.7.0 + "@jest/test-result": ^29.7.0 + "@jest/transform": ^29.7.0 + "@jest/types": ^29.6.3 "@types/node": "*" ansi-escapes: ^4.2.1 chalk: ^4.0.0 ci-info: ^3.2.0 exit: ^0.1.2 graceful-fs: ^4.2.9 - jest-changed-files: ^29.5.0 - jest-config: ^29.6.2 - jest-haste-map: ^29.6.2 - jest-message-util: ^29.6.2 - jest-regex-util: ^29.4.3 - jest-resolve: ^29.6.2 - jest-resolve-dependencies: ^29.6.2 - jest-runner: ^29.6.2 - jest-runtime: ^29.6.2 - jest-snapshot: ^29.6.2 - jest-util: ^29.6.2 - jest-validate: ^29.6.2 - jest-watcher: ^29.6.2 + jest-changed-files: ^29.7.0 + jest-config: ^29.7.0 + jest-haste-map: ^29.7.0 + jest-message-util: ^29.7.0 + jest-regex-util: ^29.6.3 + jest-resolve: ^29.7.0 + jest-resolve-dependencies: ^29.7.0 + jest-runner: ^29.7.0 + jest-runtime: ^29.7.0 + jest-snapshot: ^29.7.0 + jest-util: ^29.7.0 + jest-validate: ^29.7.0 + jest-watcher: ^29.7.0 micromatch: ^4.0.4 - pretty-format: ^29.6.2 + pretty-format: ^29.7.0 slash: ^3.0.0 strip-ansi: ^6.0.0 peerDependencies: @@ -1780,76 +1788,76 @@ __metadata: peerDependenciesMeta: node-notifier: optional: true - checksum: 6bbb3886430248c0092f275b1b946a701406732f7442c04e63e4ee2297c2ec02d8ceeec508a202e08128197699b2bcddbae2c2f74adb2cf30f2f0d7d94a7c2dc + checksum: af759c9781cfc914553320446ce4e47775ae42779e73621c438feb1e4231a5d4862f84b1d8565926f2d1aab29b3ec3dcfdc84db28608bdf5f29867124ebcfc0d languageName: node linkType: hard -"@jest/environment@npm:^29.6.2": - version: 29.6.2 - resolution: "@jest/environment@npm:29.6.2" +"@jest/environment@npm:^29.7.0": + version: 29.7.0 + resolution: "@jest/environment@npm:29.7.0" dependencies: - "@jest/fake-timers": ^29.6.2 - "@jest/types": ^29.6.1 + "@jest/fake-timers": ^29.7.0 + "@jest/types": ^29.6.3 "@types/node": "*" - jest-mock: ^29.6.2 - checksum: c7de0e4c0d9166e02d0eb166574e05ec460e1db3b69d6476e63244edd52d7c917e6876af55fe723ff3086f52c0b1869dec60654054735a7a48c9d4ac43af2a25 + jest-mock: ^29.7.0 + checksum: 6fb398143b2543d4b9b8d1c6dbce83fa5247f84f550330604be744e24c2bd2178bb893657d62d1b97cf2f24baf85c450223f8237cccb71192c36a38ea2272934 languageName: node linkType: hard -"@jest/expect-utils@npm:^29.6.2": - version: 29.6.2 - resolution: "@jest/expect-utils@npm:29.6.2" +"@jest/expect-utils@npm:^29.7.0": + version: 29.7.0 + resolution: "@jest/expect-utils@npm:29.7.0" dependencies: - jest-get-type: ^29.4.3 - checksum: 0decf2009aa3735f9df469e78ce1721c2815e4278439887e0cf0321ca8979541a22515d114a59b2445a6cd70a074b09dc9c00b5e7b3b3feac5174b9c4a78b2e1 + jest-get-type: ^29.6.3 + checksum: 75eb177f3d00b6331bcaa057e07c0ccb0733a1d0a1943e1d8db346779039cb7f103789f16e502f888a3096fb58c2300c38d1f3748b36a7fa762eb6f6d1b160ed languageName: node linkType: hard -"@jest/expect@npm:^29.6.2": - version: 29.6.2 - resolution: "@jest/expect@npm:29.6.2" +"@jest/expect@npm:^29.7.0": + version: 29.7.0 + resolution: "@jest/expect@npm:29.7.0" dependencies: - expect: ^29.6.2 - jest-snapshot: ^29.6.2 - checksum: bd2d88a4e7c5420079c239afef341ec53dc7e353816cd13acbb42631a31fd321fe58677bb43a4dba851028f4c7e31da7980314e9094cd5b348896cb6cd3d42b2 + expect: ^29.7.0 + jest-snapshot: ^29.7.0 + checksum: a01cb85fd9401bab3370618f4b9013b90c93536562222d920e702a0b575d239d74cecfe98010aaec7ad464f67cf534a353d92d181646a4b792acaa7e912ae55e languageName: node linkType: hard -"@jest/fake-timers@npm:^29.6.2": - version: 29.6.2 - resolution: "@jest/fake-timers@npm:29.6.2" +"@jest/fake-timers@npm:^29.7.0": + version: 29.7.0 + resolution: "@jest/fake-timers@npm:29.7.0" dependencies: - "@jest/types": ^29.6.1 + "@jest/types": ^29.6.3 "@sinonjs/fake-timers": ^10.0.2 "@types/node": "*" - jest-message-util: ^29.6.2 - jest-mock: ^29.6.2 - jest-util: ^29.6.2 - checksum: 1abcda02f22d2ba32e178b7ab80a9180235a6c75ec9faef33324627b19a70dad64889a9ea49b8f07230e14a6e683b9120542c6d1d6b2ecaf937f4efde32dad88 + jest-message-util: ^29.7.0 + jest-mock: ^29.7.0 + jest-util: ^29.7.0 + checksum: caf2bbd11f71c9241b458d1b5a66cbe95debc5a15d96442444b5d5c7ba774f523c76627c6931cca5e10e76f0d08761f6f1f01a608898f4751a0eee54fc3d8d00 languageName: node linkType: hard -"@jest/globals@npm:^29.5.0, @jest/globals@npm:^29.6.2": - version: 29.6.2 - resolution: "@jest/globals@npm:29.6.2" +"@jest/globals@npm:^29.5.0, @jest/globals@npm:^29.7.0": + version: 29.7.0 + resolution: "@jest/globals@npm:29.7.0" dependencies: - "@jest/environment": ^29.6.2 - "@jest/expect": ^29.6.2 - "@jest/types": ^29.6.1 - jest-mock: ^29.6.2 - checksum: aa4a54f19cc025205bc696546940e1fe9c752c2d4d825852088aa76d44677ebba1ec66fabb78e615480cff23a06a70b5a3f893ab5163d901cdfa0d2267870b10 + "@jest/environment": ^29.7.0 + "@jest/expect": ^29.7.0 + "@jest/types": ^29.6.3 + jest-mock: ^29.7.0 + checksum: 97dbb9459135693ad3a422e65ca1c250f03d82b2a77f6207e7fa0edd2c9d2015fbe4346f3dc9ebff1678b9d8da74754d4d440b7837497f8927059c0642a22123 languageName: node linkType: hard -"@jest/reporters@npm:^29.6.2": - version: 29.6.2 - resolution: "@jest/reporters@npm:29.6.2" +"@jest/reporters@npm:^29.7.0": + version: 29.7.0 + resolution: "@jest/reporters@npm:29.7.0" dependencies: "@bcoe/v8-coverage": ^0.2.3 - "@jest/console": ^29.6.2 - "@jest/test-result": ^29.6.2 - "@jest/transform": ^29.6.2 - "@jest/types": ^29.6.1 + "@jest/console": ^29.7.0 + "@jest/test-result": ^29.7.0 + "@jest/transform": ^29.7.0 + "@jest/types": ^29.6.3 "@jridgewell/trace-mapping": ^0.3.18 "@types/node": "*" chalk: ^4.0.0 @@ -1858,13 +1866,13 @@ __metadata: glob: ^7.1.3 graceful-fs: ^4.2.9 istanbul-lib-coverage: ^3.0.0 - istanbul-lib-instrument: ^5.1.0 + istanbul-lib-instrument: ^6.0.0 istanbul-lib-report: ^3.0.0 istanbul-lib-source-maps: ^4.0.0 istanbul-reports: ^3.1.3 - jest-message-util: ^29.6.2 - jest-util: ^29.6.2 - jest-worker: ^29.6.2 + jest-message-util: ^29.7.0 + jest-util: ^29.7.0 + jest-worker: ^29.7.0 slash: ^3.0.0 string-length: ^4.0.1 strip-ansi: ^6.0.0 @@ -1874,88 +1882,88 @@ __metadata: peerDependenciesMeta: node-notifier: optional: true - checksum: 7cf880d0730cee7d24ee96928003ef6946bf93423b0ae9a2edb53cae2c231b8ac50ec264f48a73744e3f11ca319cd414edacf99b2e7bf37cd72fe0b362090dd1 + checksum: 7eadabd62cc344f629024b8a268ecc8367dba756152b761bdcb7b7e570a3864fc51b2a9810cd310d85e0a0173ef002ba4528d5ea0329fbf66ee2a3ada9c40455 languageName: node linkType: hard -"@jest/schemas@npm:^29.6.0": - version: 29.6.0 - resolution: "@jest/schemas@npm:29.6.0" +"@jest/schemas@npm:^29.6.3": + version: 29.6.3 + resolution: "@jest/schemas@npm:29.6.3" dependencies: "@sinclair/typebox": ^0.27.8 - checksum: c00511c69cf89138a7d974404d3a5060af375b5a52b9c87215d91873129b382ca11c1ff25bd6d605951404bb381ddce5f8091004a61e76457da35db1f5c51365 + checksum: 910040425f0fc93cd13e68c750b7885590b8839066dfa0cd78e7def07bbb708ad869381f725945d66f2284de5663bbecf63e8fdd856e2ae6e261ba30b1687e93 languageName: node linkType: hard -"@jest/source-map@npm:^29.6.0": - version: 29.6.0 - resolution: "@jest/source-map@npm:29.6.0" +"@jest/source-map@npm:^29.6.3": + version: 29.6.3 + resolution: "@jest/source-map@npm:29.6.3" dependencies: "@jridgewell/trace-mapping": ^0.3.18 callsites: ^3.0.0 graceful-fs: ^4.2.9 - checksum: 9c6c40387410bb70b2fae8124287fc28f6bdd1b2d7f24348e8611e1bb638b404518228a4ce64a582365b589c536ae8e7ebab0126cef59a87874b71061d19783b + checksum: bcc5a8697d471396c0003b0bfa09722c3cd879ad697eb9c431e6164e2ea7008238a01a07193dfe3cbb48b1d258eb7251f6efcea36f64e1ebc464ea3c03ae2deb languageName: node linkType: hard -"@jest/test-result@npm:^29.6.2": - version: 29.6.2 - resolution: "@jest/test-result@npm:29.6.2" +"@jest/test-result@npm:^29.7.0": + version: 29.7.0 + resolution: "@jest/test-result@npm:29.7.0" dependencies: - "@jest/console": ^29.6.2 - "@jest/types": ^29.6.1 + "@jest/console": ^29.7.0 + "@jest/types": ^29.6.3 "@types/istanbul-lib-coverage": ^2.0.0 collect-v8-coverage: ^1.0.0 - checksum: 8aff37f18c8d2df4d9f453d57ec018a6479eb697fabcf74b1ca06e34553da1d7a2b85580a290408ba0b02e58543263244a2cb065c7c7180c8d8180cc78444fbd + checksum: 67b6317d526e335212e5da0e768e3b8ab8a53df110361b80761353ad23b6aea4432b7c5665bdeb87658ea373b90fb1afe02ed3611ef6c858c7fba377505057fa languageName: node linkType: hard -"@jest/test-sequencer@npm:^29.6.2": - version: 29.6.2 - resolution: "@jest/test-sequencer@npm:29.6.2" +"@jest/test-sequencer@npm:^29.7.0": + version: 29.7.0 + resolution: "@jest/test-sequencer@npm:29.7.0" dependencies: - "@jest/test-result": ^29.6.2 + "@jest/test-result": ^29.7.0 graceful-fs: ^4.2.9 - jest-haste-map: ^29.6.2 + jest-haste-map: ^29.7.0 slash: ^3.0.0 - checksum: 12dc2577e45eeb98b85d1769846b7d6effa536907986ad3c4cbd014df9e24431a564cc8cd94603332e4b1f9bfb421371883efc6a5085b361a52425ffc2a52dc6 + checksum: 73f43599017946be85c0b6357993b038f875b796e2f0950487a82f4ebcb115fa12131932dd9904026b4ad8be131fe6e28bd8d0aa93b1563705185f9804bff8bd languageName: node linkType: hard -"@jest/transform@npm:^29.6.2": - version: 29.6.2 - resolution: "@jest/transform@npm:29.6.2" +"@jest/transform@npm:^29.7.0": + version: 29.7.0 + resolution: "@jest/transform@npm:29.7.0" dependencies: "@babel/core": ^7.11.6 - "@jest/types": ^29.6.1 + "@jest/types": ^29.6.3 "@jridgewell/trace-mapping": ^0.3.18 babel-plugin-istanbul: ^6.1.1 chalk: ^4.0.0 convert-source-map: ^2.0.0 fast-json-stable-stringify: ^2.1.0 graceful-fs: ^4.2.9 - jest-haste-map: ^29.6.2 - jest-regex-util: ^29.4.3 - jest-util: ^29.6.2 + jest-haste-map: ^29.7.0 + jest-regex-util: ^29.6.3 + jest-util: ^29.7.0 micromatch: ^4.0.4 pirates: ^4.0.4 slash: ^3.0.0 write-file-atomic: ^4.0.2 - checksum: ffb8c3c344cd48bedadec295d9c436737eccc39c1f0868aa9753b76397b33b2e5b121058af6f287ba6f2036181137e37df1212334bfa9d9a712986a4518cdc18 + checksum: 0f8ac9f413903b3cb6d240102db848f2a354f63971ab885833799a9964999dd51c388162106a807f810071f864302cdd8e3f0c241c29ce02d85a36f18f3f40ab languageName: node linkType: hard -"@jest/types@npm:^29.6.1": - version: 29.6.1 - resolution: "@jest/types@npm:29.6.1" +"@jest/types@npm:^29.6.3": + version: 29.6.3 + resolution: "@jest/types@npm:29.6.3" dependencies: - "@jest/schemas": ^29.6.0 + "@jest/schemas": ^29.6.3 "@types/istanbul-lib-coverage": ^2.0.0 "@types/istanbul-reports": ^3.0.0 "@types/node": "*" "@types/yargs": ^17.0.8 chalk: ^4.0.0 - checksum: 89fc1ccf71a84fe0da643e0675b1cfe6a6f19ea72e935b2ab1dbdb56ec547e94433fb59b3536d3832a6e156c077865b7176fe9dae707dab9c3d2f9405ba6233c + checksum: a0bcf15dbb0eca6bdd8ce61a3fb055349d40268622a7670a3b2eb3c3dbafe9eb26af59938366d520b86907b9505b0f9b29b85cec11579a9e580694b87cd90fcc languageName: node linkType: hard @@ -1970,14 +1978,7 @@ __metadata: languageName: node linkType: hard -"@jridgewell/resolve-uri@npm:3.1.0": - version: 3.1.0 - resolution: "@jridgewell/resolve-uri@npm:3.1.0" - checksum: b5ceaaf9a110fcb2780d1d8f8d4a0bfd216702f31c988d8042e5f8fbe353c55d9b0f55a1733afdc64806f8e79c485d2464680ac48a0d9fcadb9548ee6b81d267 - languageName: node - linkType: hard - -"@jridgewell/resolve-uri@npm:^3.0.3": +"@jridgewell/resolve-uri@npm:^3.0.3, @jridgewell/resolve-uri@npm:^3.1.0": version: 3.1.1 resolution: "@jridgewell/resolve-uri@npm:3.1.1" checksum: f5b441fe7900eab4f9155b3b93f9800a916257f4e8563afbcd3b5a5337b55e52bd8ae6735453b1b745457d9f6cdb16d74cd6220bbdd98cf153239e13f6cbb653 @@ -2001,14 +2002,7 @@ __metadata: languageName: node linkType: hard -"@jridgewell/sourcemap-codec@npm:1.4.14": - version: 1.4.14 - resolution: "@jridgewell/sourcemap-codec@npm:1.4.14" - checksum: 61100637b6d173d3ba786a5dff019e1a74b1f394f323c1fee337ff390239f053b87266c7a948777f4b1ee68c01a8ad0ab61e5ff4abb5a012a0b091bec391ab97 - languageName: node - linkType: hard - -"@jridgewell/sourcemap-codec@npm:^1.4.10": +"@jridgewell/sourcemap-codec@npm:^1.4.10, @jridgewell/sourcemap-codec@npm:^1.4.14": version: 1.4.15 resolution: "@jridgewell/sourcemap-codec@npm:1.4.15" checksum: b881c7e503db3fc7f3c1f35a1dd2655a188cc51a3612d76efc8a6eb74728bef5606e6758ee77423e564092b4a518aba569bbb21c9bac5ab7a35b0c6ae7e344c8 @@ -2025,13 +2019,13 @@ __metadata: languageName: node linkType: hard -"@jridgewell/trace-mapping@npm:^0.3.12, @jridgewell/trace-mapping@npm:^0.3.17, @jridgewell/trace-mapping@npm:^0.3.18, @jridgewell/trace-mapping@npm:^0.3.9": - version: 0.3.18 - resolution: "@jridgewell/trace-mapping@npm:0.3.18" +"@jridgewell/trace-mapping@npm:^0.3.12, @jridgewell/trace-mapping@npm:^0.3.17, @jridgewell/trace-mapping@npm:^0.3.18, @jridgewell/trace-mapping@npm:^0.3.20, @jridgewell/trace-mapping@npm:^0.3.9": + version: 0.3.22 + resolution: "@jridgewell/trace-mapping@npm:0.3.22" dependencies: - "@jridgewell/resolve-uri": 3.1.0 - "@jridgewell/sourcemap-codec": 1.4.14 - checksum: 0572669f855260808c16fe8f78f5f1b4356463b11d3f2c7c0b5580c8ba1cbf4ae53efe9f627595830856e57dbac2325ac17eb0c3dd0ec42102e6f227cc289c02 + "@jridgewell/resolve-uri": ^3.1.0 + "@jridgewell/sourcemap-codec": ^1.4.14 + checksum: ac7dd2cfe0b479aa1b81776d40d789243131cc792dc8b6b6a028c70fcd6171958ae1a71bf67b618ffe3c0c3feead9870c095ee46a5e30319410d92976b28f498 languageName: node linkType: hard @@ -2045,39 +2039,23 @@ __metadata: linkType: hard "@libp2p/bootstrap@npm:^9.0.4": - version: 9.0.4 - resolution: "@libp2p/bootstrap@npm:9.0.4" + version: 9.0.12 + resolution: "@libp2p/bootstrap@npm:9.0.12" dependencies: - "@libp2p/interface": ^0.1.2 - "@libp2p/logger": ^3.0.2 - "@libp2p/peer-id": ^3.0.2 + "@libp2p/interface": ^0.1.6 + "@libp2p/logger": ^3.1.0 + "@libp2p/peer-id": ^3.0.6 "@multiformats/mafmt": ^12.1.2 "@multiformats/multiaddr": ^12.1.5 - checksum: 10e66b4b20645606319ed9192c9672e424aa89cd3e16b3202ac23b36bdf188873882bf71535b6b9adcef4720c69af7cf3f29a39a8139334b11dcdefbed8dcd4a + checksum: 249198129b806bf5525d527074e9151c96a411c61474543f8e2679664733af0873c5267b4c579fa29ac4f64f7fe3dae32e70dba66acafd321a3368adc579bccf languageName: node linkType: hard -"@libp2p/crypto@npm:^2.0.0, @libp2p/crypto@npm:^2.0.3": - version: 2.0.3 - resolution: "@libp2p/crypto@npm:2.0.3" +"@libp2p/crypto@npm:^2.0.0, @libp2p/crypto@npm:^2.0.8": + version: 2.0.8 + resolution: "@libp2p/crypto@npm:2.0.8" dependencies: - "@libp2p/interface": ^0.1.2 - "@noble/curves": ^1.1.0 - "@noble/hashes": ^1.3.1 - multiformats: ^12.0.1 - node-forge: ^1.1.0 - protons-runtime: ^5.0.0 - uint8arraylist: ^2.4.3 - uint8arrays: ^4.0.6 - checksum: b1806cea33fe5f16ff6d7d920f8cf77a33d06f418d9488f0b40c0251568e01a12014ee84481048764bc1d2a4372ba536842f2fb4f9879c529c6a2a0755b77e95 - languageName: node - linkType: hard - -"@libp2p/crypto@npm:^2.0.4": - version: 2.0.4 - resolution: "@libp2p/crypto@npm:2.0.4" - dependencies: - "@libp2p/interface": ^0.1.2 + "@libp2p/interface": ^0.1.6 "@noble/curves": ^1.1.0 "@noble/hashes": ^1.3.1 multiformats: ^12.0.1 @@ -2085,7 +2063,7 @@ __metadata: protons-runtime: ^5.0.0 uint8arraylist: ^2.4.3 uint8arrays: ^4.0.6 - checksum: 1e0a0e8006e9ca7caaec16ce94d193063749fc7f62a7b3d0b4469915e2837c380987baa400a9f35ee13c23783f8317f8bc58b8ed647a5b4bf55635a2a909ae97 + checksum: 4047dc54e33670c7aa4717b9f5e7980717e00ef6a2cefaae336cdcd88f9ca38d7f29a1c642d8cbc7cd85bd2957eb79b869791b4f48fb2e91652b2d87224a136d languageName: node linkType: hard @@ -2113,15 +2091,15 @@ __metadata: languageName: node linkType: hard -"@libp2p/interface-internal@npm:^0.1.4": - version: 0.1.4 - resolution: "@libp2p/interface-internal@npm:0.1.4" +"@libp2p/interface-internal@npm:^0.1.9": + version: 0.1.12 + resolution: "@libp2p/interface-internal@npm:0.1.12" dependencies: - "@libp2p/interface": ^0.1.2 - "@libp2p/peer-collections": ^4.0.3 + "@libp2p/interface": ^0.1.6 + "@libp2p/peer-collections": ^4.0.8 "@multiformats/multiaddr": ^12.1.5 uint8arraylist: ^2.4.3 - checksum: 26bbd1a2811baa089eb3ec9031f64d8b7c5e183e4f4a1f4b6c37b1de22985e61e63f9ba06fa7cbc91d60217d66773407361c8ea7abba93cf900e633547d5da97 + checksum: 3ffa5843fd7fd046f5ab4cb70a96da6c392ba2f24f832a3b99a0b20653fa6478c924caacd8c1a6aa4de2cb2a39669778ed804bfcb5c0c232a775cc1e29f3dd17 languageName: node linkType: hard @@ -2243,9 +2221,9 @@ __metadata: languageName: node linkType: hard -"@libp2p/interface@npm:^0.1.0, @libp2p/interface@npm:^0.1.1, @libp2p/interface@npm:^0.1.2": - version: 0.1.2 - resolution: "@libp2p/interface@npm:0.1.2" +"@libp2p/interface@npm:^0.1.0, @libp2p/interface@npm:^0.1.2, @libp2p/interface@npm:^0.1.6": + version: 0.1.6 + resolution: "@libp2p/interface@npm:0.1.6" dependencies: "@multiformats/multiaddr": ^12.1.5 abortable-iterator: ^5.0.1 @@ -2253,12 +2231,27 @@ __metadata: it-stream-types: ^2.0.1 multiformats: ^12.0.1 p-defer: ^4.0.0 + race-signal: ^1.0.0 uint8arraylist: ^2.4.3 - checksum: d33748ba16473c622802ee95e57445f2ac79b1ddffbaba7499157a769c57f9c1374ac10de72517e7899f88808e6fee09d77aef928d8eeaaf2bb8951fa93653a7 + checksum: dbf0c4544bbb2a299d54615e8ef553324657e7cb06a2fdc3f1a99d785276c69882841b0e9cf73b934923318d04fe6c6f7ef59194d438a126d0d8d3a4b05cc22b languageName: node linkType: hard -"@libp2p/interfaces@npm:^3.0.0, @libp2p/interfaces@npm:^3.3.1": +"@libp2p/interface@npm:^1.0.0, @libp2p/interface@npm:^1.1.2": + version: 1.1.2 + resolution: "@libp2p/interface@npm:1.1.2" + dependencies: + "@multiformats/multiaddr": ^12.1.10 + it-pushable: ^3.2.3 + it-stream-types: ^2.0.1 + multiformats: ^13.0.0 + progress-events: ^1.0.0 + uint8arraylist: ^2.4.7 + checksum: 99e257281fde4a226124344f24eb246b2a1f0639be3a73aae4478e97267f4fa5d9f86754291b16d5da01179d0fa11066477a7e1b6bb4ddfa025442b0fdc90809 + languageName: node + linkType: hard + +"@libp2p/interfaces@npm:^3.0.0": version: 3.3.2 resolution: "@libp2p/interfaces@npm:3.3.2" checksum: 3071fa49dcbb81a4b218248a1f648fba1061fb9c51e4b5edab9b8a7b9425c25afec96fdf3351ea7a469e7039269e59d95265682a934aa9c21630226dfcb67313 @@ -2266,21 +2259,20 @@ __metadata: linkType: hard "@libp2p/kad-dht@npm:^10.0.4": - version: 10.0.4 - resolution: "@libp2p/kad-dht@npm:10.0.4" - dependencies: - "@libp2p/crypto": ^2.0.3 - "@libp2p/interface": ^0.1.2 - "@libp2p/interface-internal": ^0.1.4 - "@libp2p/logger": ^3.0.2 - "@libp2p/peer-collections": ^4.0.3 - "@libp2p/peer-id": ^3.0.2 + version: 10.0.15 + resolution: "@libp2p/kad-dht@npm:10.0.15" + dependencies: + "@libp2p/crypto": ^2.0.8 + "@libp2p/interface": ^0.1.6 + "@libp2p/interface-internal": ^0.1.9 + "@libp2p/logger": ^3.1.0 + "@libp2p/peer-collections": ^4.0.8 + "@libp2p/peer-id": ^3.0.6 "@multiformats/multiaddr": ^12.1.5 - "@types/sinon": ^10.0.15 + "@types/sinon": ^17.0.0 abortable-iterator: ^5.0.1 any-signal: ^4.1.1 datastore-core: ^9.0.1 - events: ^3.3.0 hashlru: ^2.3.0 interface-datastore: ^8.2.0 it-all: ^3.0.2 @@ -2292,6 +2284,7 @@ __metadata: it-merge: ^3.0.0 it-parallel: ^3.0.0 it-pipe: ^3.0.1 + it-pushable: ^3.2.1 it-stream-types: ^2.0.1 it-take: ^3.0.1 multiformats: ^12.0.1 @@ -2304,27 +2297,27 @@ __metadata: uint8-varint: ^2.0.0 uint8arraylist: ^2.4.3 uint8arrays: ^4.0.6 - checksum: 8fbc6b2e12eeb98825b7dfa9e09a1c26f22a679167bde6305e8c524ee5514f509639db70915c432e1749272348f3eb8bb37ea7978a1a6f4133053e6b37ae3e3f + checksum: 566c62d45ff8ba92ea15332c8b62395a8e4f794ee46c038b04e4c144f032ddceae080e2a6de0e0948370620d3b708f61052783b788ba40d53d11044910f9becf languageName: node linkType: hard -"@libp2p/keychain@npm:^3.0.3": - version: 3.0.3 - resolution: "@libp2p/keychain@npm:3.0.3" +"@libp2p/keychain@npm:^3.0.8": + version: 3.0.8 + resolution: "@libp2p/keychain@npm:3.0.8" dependencies: - "@libp2p/crypto": ^2.0.3 - "@libp2p/interface": ^0.1.2 - "@libp2p/logger": ^3.0.2 - "@libp2p/peer-id": ^3.0.2 + "@libp2p/crypto": ^2.0.8 + "@libp2p/interface": ^0.1.6 + "@libp2p/logger": ^3.1.0 + "@libp2p/peer-id": ^3.0.6 interface-datastore: ^8.2.0 merge-options: ^3.0.4 sanitize-filename: ^1.6.3 uint8arrays: ^4.0.6 - checksum: cf02dea0290d891c903fb9be2ad4955ae1a5dcb5a8f842873667231d8a3ce9225cadee57ca88d23d887be0be932fe5aece16412965da267f01b1a2e4c478f38f + checksum: 765971d2ef29cdc781ff2447f28501b9f58c8a9d0e4339c17b01de5a57d50344f58b40928c3b0ad85534a416ac1055a0c1ca59852ca26329118d423d7e305e66 languageName: node linkType: hard -"@libp2p/logger@npm:^2.0.0, @libp2p/logger@npm:^2.0.7": +"@libp2p/logger@npm:^2.0.7": version: 2.1.1 resolution: "@libp2p/logger@npm:2.1.1" dependencies: @@ -2337,44 +2330,57 @@ __metadata: languageName: node linkType: hard -"@libp2p/logger@npm:^3.0.0, @libp2p/logger@npm:^3.0.2": - version: 3.0.2 - resolution: "@libp2p/logger@npm:3.0.2" +"@libp2p/logger@npm:^3.0.0, @libp2p/logger@npm:^3.1.0": + version: 3.1.0 + resolution: "@libp2p/logger@npm:3.1.0" dependencies: - "@libp2p/interface": ^0.1.2 + "@libp2p/interface": ^0.1.6 "@multiformats/multiaddr": ^12.1.5 debug: ^4.3.4 interface-datastore: ^8.2.0 multiformats: ^12.0.1 - checksum: 55734af96bdcf1572796e26d859c4a8f6ee9b4f0ca98bf0112e46cc90ce690a807e533095db79887919d7d33d5a90d2365e1354cdd3787b08ec396388a32f7a6 + checksum: ff80803afe40a1078dbaf3119fd312bce283660e6e3d0705c91395c5e1f09520217ef29d8426880db23837bcfa444579cae6b4fc6e9ae4c869ecf4e5b9a0a59c + languageName: node + linkType: hard + +"@libp2p/logger@npm:^4.0.1": + version: 4.0.5 + resolution: "@libp2p/logger@npm:4.0.5" + dependencies: + "@libp2p/interface": ^1.1.2 + "@multiformats/multiaddr": ^12.1.10 + debug: ^4.3.4 + interface-datastore: ^8.2.0 + multiformats: ^13.0.0 + checksum: e0bc60e3ac6f3ab017eb16b26eb3badc3c728291fdbea28cf9ef2f9fbfc1161d562acfa4c4fbd148d6ad0de8a4417f02d1ffada611af5255fff8495df2d65707 languageName: node linkType: hard "@libp2p/mplex@npm:^9.0.4": - version: 9.0.4 - resolution: "@libp2p/mplex@npm:9.0.4" + version: 9.0.12 + resolution: "@libp2p/mplex@npm:9.0.12" dependencies: - "@libp2p/interface": ^0.1.2 - "@libp2p/logger": ^3.0.2 + "@libp2p/interface": ^0.1.6 + "@libp2p/logger": ^3.1.0 abortable-iterator: ^5.0.1 benchmark: ^2.1.4 it-batched-bytes: ^2.0.2 it-pushable: ^3.2.0 it-stream-types: ^2.0.1 - rate-limiter-flexible: ^2.3.11 + rate-limiter-flexible: ^3.0.0 uint8-varint: ^2.0.0 uint8arraylist: ^2.4.3 uint8arrays: ^4.0.6 - checksum: ab3bbda7a8fd4c4fd5c0a3d6865cfcd2c2659eff8dbb81c20b8dceeaa7a804039bada63faa7e9d04cdf48c215cc772cef0d38be0f61046b55f0c888b879ce9c3 + checksum: 13c9c8a1826d81fc0497f84bfff97d75563602ea7bf07ac64f29bb3f6c13129610e825bca1bda8d63de62945009a0dc91b607c4979d2e91ad6c2e10f8008ec10 languageName: node linkType: hard -"@libp2p/multistream-select@npm:^4.0.2": - version: 4.0.2 - resolution: "@libp2p/multistream-select@npm:4.0.2" +"@libp2p/multistream-select@npm:^4.0.6": + version: 4.0.10 + resolution: "@libp2p/multistream-select@npm:4.0.10" dependencies: - "@libp2p/interface": ^0.1.2 - "@libp2p/logger": ^3.0.2 + "@libp2p/interface": ^0.1.6 + "@libp2p/logger": ^3.1.0 abortable-iterator: ^5.0.1 it-first: ^3.0.1 it-handshake: ^4.1.3 @@ -2384,90 +2390,76 @@ __metadata: it-pushable: ^3.2.0 it-reader: ^6.0.1 it-stream-types: ^2.0.1 + uint8-varint: ^2.0.0 uint8arraylist: ^2.4.3 uint8arrays: ^4.0.6 - checksum: 2909e297eabcb7b3da0391f6a323bb414d509e4fbe2feaa3a6a287f95bb0d46c27ec3d952755353142fe910b1fc613114e3efa17bc0f2f50109897d1a7dc85e5 - languageName: node - linkType: hard - -"@libp2p/peer-collections@npm:^4.0.3": - version: 4.0.3 - resolution: "@libp2p/peer-collections@npm:4.0.3" - dependencies: - "@libp2p/interface": ^0.1.2 - "@libp2p/peer-id": ^3.0.2 - checksum: 3a8784a17d1cbd3098b43d71b888ff15e4d9b2638090c44cb02b5d02c0590ef589788cc0f61708050b74f8d52b38ed4101796016e64478a2e3c1ae0f321e8451 + checksum: 3a3d32cc3605f73cef4039712b394c1bf18d5ca624f9b8b43c880b4d65b8bbc0491273a970183febd52c3f30dfbdbd2f6ac4aa102a117661e057749e67910bb6 languageName: node linkType: hard -"@libp2p/peer-id-factory@npm:^3.0.3": - version: 3.0.3 - resolution: "@libp2p/peer-id-factory@npm:3.0.3" +"@libp2p/peer-collections@npm:^4.0.8": + version: 4.0.11 + resolution: "@libp2p/peer-collections@npm:4.0.11" dependencies: - "@libp2p/crypto": ^2.0.3 - "@libp2p/interface": ^0.1.2 - "@libp2p/peer-id": ^3.0.2 - multiformats: ^12.0.1 - protons-runtime: ^5.0.0 - uint8arraylist: ^2.4.3 - uint8arrays: ^4.0.6 - checksum: 093530837f0d73fc6cb40c168c10c4bc2f196783b089c1006b9958b5ad86b93963de9fccf18159ce60afad01896c549bee5e368283fba288d9abf2598c7a6ea5 + "@libp2p/interface": ^0.1.6 + "@libp2p/peer-id": ^3.0.6 + checksum: b15651d905007f51cb9867f08aa3af21b8a36fef67a2f3a6309483df13d99d7611e6a9aed7dbaf1cf2adbe4ccbca0d366be92dc4e66afc705bbd9ffe43dc8b80 languageName: node linkType: hard -"@libp2p/peer-id-factory@npm:^3.0.4": - version: 3.0.4 - resolution: "@libp2p/peer-id-factory@npm:3.0.4" +"@libp2p/peer-id-factory@npm:^3.0.3, @libp2p/peer-id-factory@npm:^3.0.4, @libp2p/peer-id-factory@npm:^3.0.8": + version: 3.0.11 + resolution: "@libp2p/peer-id-factory@npm:3.0.11" dependencies: - "@libp2p/crypto": ^2.0.4 - "@libp2p/interface": ^0.1.2 - "@libp2p/peer-id": ^3.0.2 + "@libp2p/crypto": ^2.0.8 + "@libp2p/interface": ^0.1.6 + "@libp2p/peer-id": ^3.0.6 multiformats: ^12.0.1 protons-runtime: ^5.0.0 uint8arraylist: ^2.4.3 uint8arrays: ^4.0.6 - checksum: 40c534029bfa8d9b98119ee2c0ce3c0c91bcf7280028e7ba8bfe4da2a90de53014791d75c8d1400877919f748918704276b6e3337c5128975842c0593464fda9 + checksum: bdcee4fef7f8aace6a8316e523e8c82753986a42c58b51f59d04534a1095c9c1eec8193e859614aa2589a7f5e43e64e529bb0b475e7bad7150b2034b2ebc0aa2 languageName: node linkType: hard -"@libp2p/peer-id@npm:^3.0.0, @libp2p/peer-id@npm:^3.0.2": - version: 3.0.2 - resolution: "@libp2p/peer-id@npm:3.0.2" +"@libp2p/peer-id@npm:^3.0.0, @libp2p/peer-id@npm:^3.0.2, @libp2p/peer-id@npm:^3.0.6": + version: 3.0.6 + resolution: "@libp2p/peer-id@npm:3.0.6" dependencies: - "@libp2p/interface": ^0.1.2 + "@libp2p/interface": ^0.1.6 multiformats: ^12.0.1 uint8arrays: ^4.0.6 - checksum: 021a5854dd2b8afc0d83e1541531d9710be237d5a6883aa0965d85cba629fbaaa27f68f774f5fa9c331df6a8e6b1187031d270905a7c33f103177cf0e190e2bb + checksum: d573948b9b9fc64d80a2175ff27c9878d15a43aec5c71d9486c9b6d5e4a0510c9d935c71e35420cd09d55f32c3a87307112627becc5d9f709b4b19a7100f7d30 languageName: node linkType: hard -"@libp2p/peer-record@npm:^6.0.3": - version: 6.0.3 - resolution: "@libp2p/peer-record@npm:6.0.3" +"@libp2p/peer-record@npm:^6.0.9": + version: 6.0.12 + resolution: "@libp2p/peer-record@npm:6.0.12" dependencies: - "@libp2p/crypto": ^2.0.3 - "@libp2p/interface": ^0.1.2 - "@libp2p/peer-id": ^3.0.2 - "@libp2p/utils": ^4.0.2 + "@libp2p/crypto": ^2.0.8 + "@libp2p/interface": ^0.1.6 + "@libp2p/peer-id": ^3.0.6 + "@libp2p/utils": ^4.0.7 "@multiformats/multiaddr": ^12.1.5 protons-runtime: ^5.0.0 - uint8-varint: ^1.0.2 + uint8-varint: ^2.0.0 uint8arraylist: ^2.4.3 uint8arrays: ^4.0.6 - checksum: 63e7a6190a608d649761303b8e2783fcdb8f2024d3cafc137afeb67eab553469fb1f052cbe88fdb489385cd621aaa4cf52d9d93effb99111704158d98408e1cb + checksum: d252cafa7c63fc05c8715cb4de8e340bbd76e5438b3eaf309f6e2de5a41d550f399f4ddba110ef2edcb4e667659baa20d4da2cc4d9f19611ae4402f72eb87006 languageName: node linkType: hard -"@libp2p/peer-store@npm:^9.0.3": - version: 9.0.3 - resolution: "@libp2p/peer-store@npm:9.0.3" +"@libp2p/peer-store@npm:^9.0.9": + version: 9.0.12 + resolution: "@libp2p/peer-store@npm:9.0.12" dependencies: - "@libp2p/interface": ^0.1.2 - "@libp2p/logger": ^3.0.2 - "@libp2p/peer-collections": ^4.0.3 - "@libp2p/peer-id": ^3.0.2 - "@libp2p/peer-id-factory": ^3.0.3 - "@libp2p/peer-record": ^6.0.3 + "@libp2p/interface": ^0.1.6 + "@libp2p/logger": ^3.1.0 + "@libp2p/peer-collections": ^4.0.8 + "@libp2p/peer-id": ^3.0.6 + "@libp2p/peer-id-factory": ^3.0.8 + "@libp2p/peer-record": ^6.0.9 "@multiformats/multiaddr": ^12.1.5 interface-datastore: ^8.2.0 it-all: ^3.0.2 @@ -2476,79 +2468,80 @@ __metadata: protons-runtime: ^5.0.0 uint8arraylist: ^2.4.3 uint8arrays: ^4.0.6 - checksum: 25b489c067ad910dcb8290afcbd5669ca87ddba9e1bed19c3b0279ab328d147b0f7fd0bc736707da61f897e1864c525733b2f9daae7791d7a67b76e0e7e1826b + checksum: b4d3ee98781742a7f46d50afc43f6b530058c9d8f2db04fd66c391d59427eaa4b49eb9d4df26e6bd4e6ae8d9a60e90686ae522f13e9ad517da4860c3f868a778 languageName: node linkType: hard "@libp2p/tcp@npm:^8.0.4": - version: 8.0.4 - resolution: "@libp2p/tcp@npm:8.0.4" + version: 8.0.13 + resolution: "@libp2p/tcp@npm:8.0.13" dependencies: - "@libp2p/interface": ^0.1.2 - "@libp2p/logger": ^3.0.2 - "@libp2p/utils": ^4.0.2 + "@libp2p/interface": ^0.1.6 + "@libp2p/logger": ^3.1.0 + "@libp2p/utils": ^4.0.7 "@multiformats/mafmt": ^12.1.2 "@multiformats/multiaddr": ^12.1.5 - "@types/sinon": ^10.0.15 + "@types/sinon": ^17.0.0 stream-to-it: ^0.2.2 - checksum: ba7fc1da50181c6e118d362f85baf6a16cdab609be9eb2b7c182326cabef87676df03f9b5f816bac8cb2d597b1462075ebe7ca4d84243b60ba45359322aa4185 + checksum: cceff8633265c3bee7b0a246808fe26a61cb5876ca3363ae3278ebd9d2bd4775e3d3057c5ef8f18b8dd78368a0fd6359c94e7f8cd3f747ad4ebc89fce80db274 languageName: node linkType: hard -"@libp2p/utils@npm:^4.0.2": - version: 4.0.2 - resolution: "@libp2p/utils@npm:4.0.2" +"@libp2p/utils@npm:^4.0.7": + version: 4.0.7 + resolution: "@libp2p/utils@npm:4.0.7" dependencies: "@chainsafe/is-ip": ^2.0.2 - "@libp2p/interface": ^0.1.2 - "@libp2p/logger": ^3.0.2 + "@libp2p/interface": ^0.1.6 + "@libp2p/logger": ^3.1.0 "@multiformats/multiaddr": ^12.1.5 + "@multiformats/multiaddr-matcher": ^1.0.1 is-loopback-addr: ^2.0.1 it-stream-types: ^2.0.1 private-ip: ^3.0.0 uint8arraylist: ^2.4.3 - checksum: 1fd40278c58fe75d587f2d209bec039787b9997ba3ad9e2f8a47b716247efb73be98093d08fa5872395cd74b6f5d1b575fd5319d545c31155313a865c9135aee + checksum: df883f04b9efda532009ae0b2f03d918567277479e6f115a02673394e4239605bbaa0718e7cea7bd4c70307b44cd782492b6fa4ad9f03c7c4621f12cbb1f7d4c languageName: node linkType: hard -"@lmdb/lmdb-darwin-arm64@npm:2.9.1": - version: 2.9.1 - resolution: "@lmdb/lmdb-darwin-arm64@npm:2.9.1" +"@lmdb/lmdb-darwin-arm64@npm:2.9.2": + version: 2.9.2 + resolution: "@lmdb/lmdb-darwin-arm64@npm:2.9.2" conditions: os=darwin & cpu=arm64 languageName: node linkType: hard -"@lmdb/lmdb-darwin-x64@npm:2.9.1": - version: 2.9.1 - resolution: "@lmdb/lmdb-darwin-x64@npm:2.9.1" +"@lmdb/lmdb-darwin-x64@npm:2.9.2": + version: 2.9.2 + resolution: "@lmdb/lmdb-darwin-x64@npm:2.9.2" conditions: os=darwin & cpu=x64 languageName: node linkType: hard -"@lmdb/lmdb-linux-arm64@npm:2.9.1": - version: 2.9.1 - resolution: "@lmdb/lmdb-linux-arm64@npm:2.9.1" +"@lmdb/lmdb-linux-arm64@npm:2.9.2": + version: 2.9.2 + resolution: "@lmdb/lmdb-linux-arm64@npm:2.9.2" conditions: os=linux & cpu=arm64 languageName: node linkType: hard -"@lmdb/lmdb-linux-arm@npm:2.9.1": - version: 2.9.1 - resolution: "@lmdb/lmdb-linux-arm@npm:2.9.1" +"@lmdb/lmdb-linux-arm@npm:2.9.2": + version: 2.9.2 + resolution: "@lmdb/lmdb-linux-arm@npm:2.9.2" conditions: os=linux & cpu=arm languageName: node linkType: hard -"@lmdb/lmdb-linux-x64@npm:2.9.1": - version: 2.9.1 - resolution: "@lmdb/lmdb-linux-x64@npm:2.9.1" +"@lmdb/lmdb-linux-x64@npm:2.9.2": + version: 2.9.2 + resolution: "@lmdb/lmdb-linux-x64@npm:2.9.2" conditions: os=linux & cpu=x64 languageName: node linkType: hard -"@lmdb/lmdb-win32-x64@npm:2.9.1": - version: 2.9.1 - resolution: "@lmdb/lmdb-win32-x64@npm:2.9.1" +"@lmdb/lmdb-win32-x64@npm:2.9.2": + version: 2.9.2 + resolution: "@lmdb/lmdb-win32-x64@npm:2.9.2" conditions: os=win32 & cpu=x64 languageName: node linkType: hard @@ -2572,30 +2565,30 @@ __metadata: languageName: node linkType: hard -"@monorepo-utils/package-utils@npm:^2.10.2": - version: 2.10.2 - resolution: "@monorepo-utils/package-utils@npm:2.10.2" +"@monorepo-utils/package-utils@npm:^2.10.4": + version: 2.10.4 + resolution: "@monorepo-utils/package-utils@npm:2.10.4" dependencies: globby: ^11.0.1 load-json-file: ^6.2.0 upath: ^2.0.1 yaml: ^2.1.3 - checksum: fa8ff74b63a5760bb5088c1897f2e82c77d8c5e60858141d354b434fcefdf19a8d590721ddec328821fecfe8797018bdb79ad20be02a19d168859d19f03823ad + checksum: aa55887ff790693e2db7158aa28fbf178c7041607e65f857f516306e6ff6a77d596151ee6d61519fce8369db8cffffd07ee6fb66b99f12619f3845985dc7543f languageName: node linkType: hard "@monorepo-utils/workspaces-to-typescript-project-references@npm:^2.9.0": - version: 2.10.2 - resolution: "@monorepo-utils/workspaces-to-typescript-project-references@npm:2.10.2" + version: 2.10.4 + resolution: "@monorepo-utils/workspaces-to-typescript-project-references@npm:2.10.4" dependencies: - "@monorepo-utils/package-utils": ^2.10.2 + "@monorepo-utils/package-utils": ^2.10.4 comment-json: ^3.0.3 meow: ^7.1.1 semver-match: 0.1.1 upath: ^2.0.1 bin: workspaces-to-typescript-project-references: bin/cmd.js - checksum: 076575a837a6a928e029eeac284278e2953638a60632264d60e2efcdb216fba1d8de4fc2a74ab598ad71663a87cc595cbe3b8732da7d4845b44f1fb321a4da74 + checksum: 34d539247bdcaff9f0182ec8654a5f63d81bd8b06e0947fb6ab3f8c72127684e45906510576fa1a15dd31d8b7d7ad99f29918e5a4ff9b21a74f45227f8b6ccef languageName: node linkType: hard @@ -2650,51 +2643,36 @@ __metadata: languageName: node linkType: hard -"@multiformats/multiaddr-matcher@npm:^1.0.0": - version: 1.0.1 - resolution: "@multiformats/multiaddr-matcher@npm:1.0.1" +"@multiformats/multiaddr-matcher@npm:^1.0.0, @multiformats/multiaddr-matcher@npm:^1.0.1": + version: 1.1.2 + resolution: "@multiformats/multiaddr-matcher@npm:1.1.2" dependencies: "@chainsafe/is-ip": ^2.0.1 "@multiformats/multiaddr": ^12.0.0 - multiformats: ^12.0.1 - checksum: 71579db42aa0e22297e542946d7ad2ab5a3427d619de6838cce46cce3bb168615577b6e94b58fca1b4af94520dae366810ecc309081810e15ff61781591908d5 + multiformats: ^13.0.0 + checksum: ae0619211ad1a4f1021993c1372f6498cbaec07897559b0b8644e0c8e53a3fc209136d3faf4f6cef5b1533f952b55b232fd6eb089d578a3594fa92d01802d4c3 languageName: node linkType: hard -"@multiformats/multiaddr@npm:^12.0.0, @multiformats/multiaddr@npm:^12.1.3": - version: 12.1.5 - resolution: "@multiformats/multiaddr@npm:12.1.5" +"@multiformats/multiaddr@npm:^12.0.0, @multiformats/multiaddr@npm:^12.1.10, @multiformats/multiaddr@npm:^12.1.3, @multiformats/multiaddr@npm:^12.1.5": + version: 12.1.14 + resolution: "@multiformats/multiaddr@npm:12.1.14" dependencies: "@chainsafe/is-ip": ^2.0.1 "@chainsafe/netmask": ^2.0.0 - "@libp2p/interfaces": ^3.3.1 - dns-over-http-resolver: ^2.1.0 - multiformats: ^12.0.1 - uint8arrays: ^4.0.2 - varint: ^6.0.0 - checksum: 01181807070382fb96019aec68df6276c90801185eedeb82c69dfef0ff6898eacc87a13e639c364dc0da976c5be623b56198e7107f2da40e4ef3a2d3523e8c49 - languageName: node - linkType: hard - -"@multiformats/multiaddr@npm:^12.1.5": - version: 12.1.7 - resolution: "@multiformats/multiaddr@npm:12.1.7" - dependencies: - "@chainsafe/is-ip": ^2.0.1 - "@chainsafe/netmask": ^2.0.0 - "@libp2p/interface": ^0.1.1 - dns-over-http-resolver: ^2.1.0 - multiformats: ^12.0.1 + "@libp2p/interface": ^1.0.0 + dns-over-http-resolver: ^3.0.2 + multiformats: ^13.0.0 uint8-varint: ^2.0.1 - uint8arrays: ^4.0.2 - checksum: 96b83208b7bd3e9387f2fdac20fc554d962395c02661e9c1da819646d2f3129e1a76e5abc6a0c8d386c7126a7678e58d05b08dc812260b7cad2488533cbe44b0 + uint8arrays: ^5.0.0 + checksum: 6c48bb1c467b36c030b2c746574b81f7e3a8fba46987471b5f6714dac1ceea120759383be37c1cacc8d1fbb9c8666eb28ad0041c5737eaf457bd8d58f0d520fa languageName: node linkType: hard -"@noble/ciphers@npm:^0.1.4": - version: 0.1.4 - resolution: "@noble/ciphers@npm:0.1.4" - checksum: a846f91dc876ea8cf01c20f04df2816926ad4e4d90169e6334de39b477ce13bf5e720f4df9f9898dd2a87643660ccc8a04aa466baf885c43860c270bcc7deced +"@noble/ciphers@npm:^0.4.0": + version: 0.4.1 + resolution: "@noble/ciphers@npm:0.4.1" + checksum: 8301334d6281c1cd6200716be6d01e30b8fd07b6ff7a537587187a649e625a347f24d52eba4812fc3535a077cd53e33a7abb77aeee19ff6662b7f048148f9e21 languageName: node linkType: hard @@ -2707,16 +2685,7 @@ __metadata: languageName: node linkType: hard -"@noble/curves@npm:^1.0.0, @noble/curves@npm:^1.1.0": - version: 1.1.0 - resolution: "@noble/curves@npm:1.1.0" - dependencies: - "@noble/hashes": 1.3.1 - checksum: 2658cdd3f84f71079b4e3516c47559d22cf4b55c23ac8ee9d2b1f8e5b72916d9689e59820e0f9d9cb4a46a8423af5b56dc6bb7782405c88be06a015180508db5 - languageName: node - linkType: hard - -"@noble/curves@npm:^1.2.0": +"@noble/curves@npm:1.2.0, @noble/curves@npm:~1.2.0": version: 1.2.0 resolution: "@noble/curves@npm:1.2.0" dependencies: @@ -2725,17 +2694,19 @@ __metadata: languageName: node linkType: hard -"@noble/hashes@npm:1.3.0": +"@noble/curves@npm:^1.0.0, @noble/curves@npm:^1.1.0, @noble/curves@npm:^1.2.0": version: 1.3.0 - resolution: "@noble/hashes@npm:1.3.0" - checksum: d7ddb6d7c60f1ce1f87facbbef5b724cdea536fc9e7f59ae96e0fc9de96c8f1a2ae2bdedbce10f7dcc621338dfef8533daa73c873f2b5c87fa1a4e05a95c2e2e + resolution: "@noble/curves@npm:1.3.0" + dependencies: + "@noble/hashes": 1.3.3 + checksum: b65342ee66c4a440eee2978524412eabba9a9efdd16d6370e15218c6a7d80bddf35e66bb57ed52c0dfd32cb9a717b439ab3a72db618f1a0066dfebe3fd12a421 languageName: node linkType: hard -"@noble/hashes@npm:1.3.1, @noble/hashes@npm:^1.3.1, @noble/hashes@npm:~1.3.0": - version: 1.3.1 - resolution: "@noble/hashes@npm:1.3.1" - checksum: 7fdefc0f7a0c1ec27acc6ff88841793e3f93ec4ce6b8a6a12bfc0dd70ae6b7c4c82fe305fdfeda1735d5ad4a9eebe761e6693b3d355689c559e91242f4bc95b1 +"@noble/hashes@npm:1.3.0": + version: 1.3.0 + resolution: "@noble/hashes@npm:1.3.0" + checksum: d7ddb6d7c60f1ce1f87facbbef5b724cdea536fc9e7f59ae96e0fc9de96c8f1a2ae2bdedbce10f7dcc621338dfef8533daa73c873f2b5c87fa1a4e05a95c2e2e languageName: node linkType: hard @@ -2746,6 +2717,13 @@ __metadata: languageName: node linkType: hard +"@noble/hashes@npm:1.3.3, @noble/hashes@npm:^1.3.1, @noble/hashes@npm:~1.3.0, @noble/hashes@npm:~1.3.2": + version: 1.3.3 + resolution: "@noble/hashes@npm:1.3.3" + checksum: 8a6496d1c0c64797339bc694ad06cdfaa0f9e56cd0c3f68ae3666cfb153a791a55deb0af9c653c7ed2db64d537aa3e3054629740d2f2338bb1dcb7ab60cd205b + languageName: node + linkType: hard + "@nodelib/fs.scandir@npm:2.1.5": version: 2.1.5 resolution: "@nodelib/fs.scandir@npm:2.1.5" @@ -2783,7 +2761,7 @@ __metadata: version: 0.0.0-use.local resolution: "@noir-lang/backend_barretenberg@portal:../noir/packages/backend_barretenberg::locator=%40aztec%2Faztec3-packages%40workspace%3A." dependencies: - "@aztec/bb.js": 0.19.0 + "@aztec/bb.js": 0.21.0 "@noir-lang/types": 0.23.0 fflate: ^0.8.0 languageName: node @@ -2813,6 +2791,19 @@ __metadata: languageName: node linkType: soft +"@npmcli/agent@npm:^2.0.0": + version: 2.2.0 + resolution: "@npmcli/agent@npm:2.2.0" + dependencies: + agent-base: ^7.1.0 + http-proxy-agent: ^7.0.0 + https-proxy-agent: ^7.0.1 + lru-cache: ^10.0.1 + socks-proxy-agent: ^8.0.1 + checksum: 3b25312edbdfaa4089af28e2d423b6f19838b945e47765b0c8174c1395c79d43c3ad6d23cb364b43f59fd3acb02c93e3b493f72ddbe3dfea04c86843a7311fc4 + languageName: node + linkType: hard + "@npmcli/fs@npm:^3.1.0": version: 3.1.0 resolution: "@npmcli/fs@npm:3.1.0" @@ -2829,96 +2820,9 @@ __metadata: languageName: node linkType: hard -"@pkgr/utils@npm:^2.3.1": - version: 2.4.2 - resolution: "@pkgr/utils@npm:2.4.2" - dependencies: - cross-spawn: ^7.0.3 - fast-glob: ^3.3.0 - is-glob: ^4.0.3 - open: ^9.1.0 - picocolors: ^1.0.0 - tslib: ^2.6.0 - checksum: 24e04c121269317d259614cd32beea3af38277151c4002df5883c4be920b8e3490bb897748e844f9d46bf68230f86dabd4e8f093773130e7e60529a769a132fc - languageName: node - linkType: hard - -"@protobufjs/aspromise@npm:^1.1.1, @protobufjs/aspromise@npm:^1.1.2": - version: 1.1.2 - resolution: "@protobufjs/aspromise@npm:1.1.2" - checksum: 011fe7ef0826b0fd1a95935a033a3c0fd08483903e1aa8f8b4e0704e3233406abb9ee25350ec0c20bbecb2aad8da0dcea58b392bbd77d6690736f02c143865d2 - languageName: node - linkType: hard - -"@protobufjs/base64@npm:^1.1.2": - version: 1.1.2 - resolution: "@protobufjs/base64@npm:1.1.2" - checksum: 67173ac34de1e242c55da52c2f5bdc65505d82453893f9b51dc74af9fe4c065cf4a657a4538e91b0d4a1a1e0a0642215e31894c31650ff6e3831471061e1ee9e - languageName: node - linkType: hard - -"@protobufjs/codegen@npm:^2.0.4": - version: 2.0.4 - resolution: "@protobufjs/codegen@npm:2.0.4" - checksum: 59240c850b1d3d0b56d8f8098dd04787dcaec5c5bd8de186fa548de86b86076e1c50e80144b90335e705a044edf5bc8b0998548474c2a10a98c7e004a1547e4b - languageName: node - linkType: hard - -"@protobufjs/eventemitter@npm:^1.1.0": - version: 1.1.0 - resolution: "@protobufjs/eventemitter@npm:1.1.0" - checksum: 0369163a3d226851682f855f81413cbf166cd98f131edb94a0f67f79e75342d86e89df9d7a1df08ac28be2bc77e0a7f0200526bb6c2a407abbfee1f0262d5fd7 - languageName: node - linkType: hard - -"@protobufjs/fetch@npm:^1.1.0": - version: 1.1.0 - resolution: "@protobufjs/fetch@npm:1.1.0" - dependencies: - "@protobufjs/aspromise": ^1.1.1 - "@protobufjs/inquire": ^1.1.0 - checksum: 3fce7e09eb3f1171dd55a192066450f65324fd5f7cc01a431df01bb00d0a895e6bfb5b0c5561ce157ee1d886349c90703d10a4e11a1a256418ff591b969b3477 - languageName: node - linkType: hard - -"@protobufjs/float@npm:^1.0.2": - version: 1.0.2 - resolution: "@protobufjs/float@npm:1.0.2" - checksum: 5781e1241270b8bd1591d324ca9e3a3128d2f768077a446187a049e36505e91bc4156ed5ac3159c3ce3d2ba3743dbc757b051b2d723eea9cd367bfd54ab29b2f - languageName: node - linkType: hard - -"@protobufjs/inquire@npm:^1.1.0": - version: 1.1.0 - resolution: "@protobufjs/inquire@npm:1.1.0" - checksum: ca06f02eaf65ca36fb7498fc3492b7fc087bfcc85c702bac5b86fad34b692bdce4990e0ef444c1e2aea8c034227bd1f0484be02810d5d7e931c55445555646f4 - languageName: node - linkType: hard - -"@protobufjs/path@npm:^1.1.2": - version: 1.1.2 - resolution: "@protobufjs/path@npm:1.1.2" - checksum: 856eeb532b16a7aac071cacde5c5620df800db4c80cee6dbc56380524736205aae21e5ae47739114bf669ab5e8ba0e767a282ad894f3b5e124197cb9224445ee - languageName: node - linkType: hard - -"@protobufjs/pool@npm:^1.1.0": - version: 1.1.0 - resolution: "@protobufjs/pool@npm:1.1.0" - checksum: d6a34fbbd24f729e2a10ee915b74e1d77d52214de626b921b2d77288bd8f2386808da2315080f2905761527cceffe7ec34c7647bd21a5ae41a25e8212ff79451 - languageName: node - linkType: hard - -"@protobufjs/utf8@npm:^1.1.0": - version: 1.1.0 - resolution: "@protobufjs/utf8@npm:1.1.0" - checksum: f9bf3163d13aaa3b6f5e6fbf37a116e094ea021c0e1f2a7ccd0e12a29e2ce08dafba4e8b36e13f8ed7397e1591610ce880ed1289af4d66cf4ace8a36a9557278 - languageName: node - linkType: hard - -"@puppeteer/browsers@npm:1.7.1": - version: 1.7.1 - resolution: "@puppeteer/browsers@npm:1.7.1" +"@puppeteer/browsers@npm:1.9.1": + version: 1.9.1 + resolution: "@puppeteer/browsers@npm:1.9.1" dependencies: debug: 4.3.4 extract-zip: 2.0.1 @@ -2926,17 +2830,17 @@ __metadata: proxy-agent: 6.3.1 tar-fs: 3.0.4 unbzip2-stream: 1.4.3 - yargs: 17.7.1 + yargs: 17.7.2 bin: browsers: lib/cjs/main-cli.js - checksum: fb7cf7773a1aed4e34ce0952dbf9609a164e624d4f8e1f342b816fe3e983888d7a7b2fbafc963559e96cb5bca0d75fb9c81f2097f9b1f5478a0f1cc7cbc12dff + checksum: 1ea82e34af882dc6d7e8392a88ec4196e206a7f65743be39c196c7068d66b9bdfa370e28c6ab09946bd2baa2182adbcbf445e79cc9bcc5242f05878ae7045b27 languageName: node linkType: hard -"@scure/base@npm:~1.1.0": - version: 1.1.1 - resolution: "@scure/base@npm:1.1.1" - checksum: b4fc810b492693e7e8d0107313ac74c3646970c198bbe26d7332820886fa4f09441991023ec9aa3a2a51246b74409ab5ebae2e8ef148bbc253da79ac49130309 +"@scure/base@npm:~1.1.0, @scure/base@npm:~1.1.2": + version: 1.1.5 + resolution: "@scure/base@npm:1.1.5" + checksum: 9e9ee6088cb3aa0fb91f5a48497d26682c7829df3019b1251d088d166d7a8c0f941c68aaa8e7b96bbad20c71eb210397cb1099062cde3e29d4bad6b975c18519 languageName: node linkType: hard @@ -2951,6 +2855,17 @@ __metadata: languageName: node linkType: hard +"@scure/bip32@npm:1.3.2": + version: 1.3.2 + resolution: "@scure/bip32@npm:1.3.2" + dependencies: + "@noble/curves": ~1.2.0 + "@noble/hashes": ~1.3.2 + "@scure/base": ~1.1.2 + checksum: c5ae84fae43490853693b481531132b89e056d45c945fc8b92b9d032577f753dfd79c5a7bbcbf0a7f035951006ff0311b6cf7a389e26c9ec6335e42b20c53157 + languageName: node + linkType: hard + "@scure/bip39@npm:1.2.0": version: 1.2.0 resolution: "@scure/bip39@npm:1.2.0" @@ -2961,6 +2876,16 @@ __metadata: languageName: node linkType: hard +"@scure/bip39@npm:1.2.1": + version: 1.2.1 + resolution: "@scure/bip39@npm:1.2.1" + dependencies: + "@noble/hashes": ~1.3.0 + "@scure/base": ~1.1.0 + checksum: c5bd6f1328fdbeae2dcdd891825b1610225310e5e62a4942714db51066866e4f7bef242c7b06a1b9dcc8043a4a13412cf5c5df76d3b10aa9e36b82e9b6e3eeaa + languageName: node + linkType: hard + "@sinclair/typebox@npm:^0.27.8": version: 0.27.8 resolution: "@sinclair/typebox@npm:0.27.8" @@ -2969,11 +2894,11 @@ __metadata: linkType: hard "@sinonjs/commons@npm:^3.0.0": - version: 3.0.0 - resolution: "@sinonjs/commons@npm:3.0.0" + version: 3.0.1 + resolution: "@sinonjs/commons@npm:3.0.1" dependencies: type-detect: 4.0.8 - checksum: b4b5b73d4df4560fb8c0c7b38c7ad4aeabedd362f3373859d804c988c725889cde33550e4bcc7cd316a30f5152a2d1d43db71b6d0c38f5feef71fd8d016763f8 + checksum: a7c3e7cc612352f4004873747d9d8b2d4d90b13a6d483f685598c945a70e734e255f1ca5dc49702515533c403b32725defff148177453b3f3915bcb60e9d4601 languageName: node linkType: hard @@ -2986,13 +2911,6 @@ __metadata: languageName: node linkType: hard -"@tootallnate/once@npm:2": - version: 2.0.0 - resolution: "@tootallnate/once@npm:2.0.0" - checksum: ad87447820dd3f24825d2d947ebc03072b20a42bfc96cbafec16bff8bbda6c1a81fcb0be56d5b21968560c5359a0af4038a68ba150c3e1694fe4c109a063bed8 - languageName: node - linkType: hard - "@tootallnate/quickjs-emscripten@npm:^0.23.0": version: 0.23.0 resolution: "@tootallnate/quickjs-emscripten@npm:0.23.0" @@ -3001,12 +2919,12 @@ __metadata: linkType: hard "@trivago/prettier-plugin-sort-imports@npm:^4.1.1": - version: 4.2.0 - resolution: "@trivago/prettier-plugin-sort-imports@npm:4.2.0" + version: 4.3.0 + resolution: "@trivago/prettier-plugin-sort-imports@npm:4.3.0" dependencies: "@babel/generator": 7.17.7 "@babel/parser": ^7.20.5 - "@babel/traverse": 7.17.3 + "@babel/traverse": 7.23.2 "@babel/types": 7.17.0 javascript-natural-sort: 0.7.1 lodash: ^4.17.21 @@ -3016,7 +2934,7 @@ __metadata: peerDependenciesMeta: "@vue/compiler-sfc": optional: true - checksum: 2081ba9f1a2d33b9a3eeadeb3e713d404ee3d1a5cff3b20a23d94d6d915f0a8ff549616c1e77cd728f1b33733e0d7ab8e4c2512f344a612d81ece40025351160 + checksum: 22bb311ca24f09eef25915a66727e7be113b703f196f6ea0589dc9730b11a6f1e5e4bcc468213101d138b570d310792c83abb8d9487c53f9e597942fea052b6e languageName: node linkType: hard @@ -3049,274 +2967,264 @@ __metadata: linkType: hard "@types/abstract-leveldown@npm:*": - version: 7.2.1 - resolution: "@types/abstract-leveldown@npm:7.2.1" - checksum: 20689e7d144ce26d2384e2e151eed59046c95d573a6988da5e77e3076808eb4f435f474a0387af9ac786bfbfc7089e277dcfd9572ae902553d5c018e9b527a30 + version: 7.2.5 + resolution: "@types/abstract-leveldown@npm:7.2.5" + checksum: 3a99b13c81a53a62b42bea9cff326880de3146b4eeff528b039be69a268515b3120a6c12142e96646fcb0a03c463f298998581e86d9ddb29fbea3612f40edb2b languageName: node linkType: hard "@types/accepts@npm:*": - version: 1.3.5 - resolution: "@types/accepts@npm:1.3.5" + version: 1.3.7 + resolution: "@types/accepts@npm:1.3.7" dependencies: "@types/node": "*" - checksum: 590b7580570534a640510c071e09074cf63b5958b237a728f94322567350aea4d239f8a9d897a12b15c856b992ee4d7907e9812bb079886af2c00714e7fb3f60 + checksum: 7678cf74976e16093aff6e6f9755826faf069ac1e30179276158ce46ea246348ff22ca6bdd46cef08428881337d9ceefbf00bab08a7731646eb9fc9449d6a1e7 languageName: node linkType: hard "@types/babel__core@npm:^7.1.14": - version: 7.20.1 - resolution: "@types/babel__core@npm:7.20.1" + version: 7.20.5 + resolution: "@types/babel__core@npm:7.20.5" dependencies: "@babel/parser": ^7.20.7 "@babel/types": ^7.20.7 "@types/babel__generator": "*" "@types/babel__template": "*" "@types/babel__traverse": "*" - checksum: 9fcd9691a33074802d9057ff70b0e3ff3778f52470475b68698a0f6714fbe2ccb36c16b43dc924eb978cd8a81c1f845e5ff4699e7a47606043b539eb8c6331a8 + checksum: a3226f7930b635ee7a5e72c8d51a357e799d19cbf9d445710fa39ab13804f79ab1a54b72ea7d8e504659c7dfc50675db974b526142c754398d7413aa4bc30845 languageName: node linkType: hard "@types/babel__generator@npm:*": - version: 7.6.4 - resolution: "@types/babel__generator@npm:7.6.4" + version: 7.6.8 + resolution: "@types/babel__generator@npm:7.6.8" dependencies: "@babel/types": ^7.0.0 - checksum: 20effbbb5f8a3a0211e95959d06ae70c097fb6191011b73b38fe86deebefad8e09ee014605e0fd3cdaedc73d158be555866810e9166e1f09e4cfd880b874dcb0 + checksum: 5b332ea336a2efffbdeedb92b6781949b73498606ddd4205462f7d96dafd45ff3618770b41de04c4881e333dd84388bfb8afbdf6f2764cbd98be550d85c6bb48 languageName: node linkType: hard "@types/babel__template@npm:*": - version: 7.4.1 - resolution: "@types/babel__template@npm:7.4.1" + version: 7.4.4 + resolution: "@types/babel__template@npm:7.4.4" dependencies: "@babel/parser": ^7.1.0 "@babel/types": ^7.0.0 - checksum: 649fe8b42c2876be1fd28c6ed9b276f78152d5904ec290b6c861d9ef324206e0a5c242e8305c421ac52ecf6358fa7e32ab7a692f55370484825c1df29b1596ee + checksum: d7a02d2a9b67e822694d8e6a7ddb8f2b71a1d6962dfd266554d2513eefbb205b33ca71a0d163b1caea3981ccf849211f9964d8bd0727124d18ace45aa6c9ae29 languageName: node linkType: hard "@types/babel__traverse@npm:*, @types/babel__traverse@npm:^7.0.6": - version: 7.20.1 - resolution: "@types/babel__traverse@npm:7.20.1" + version: 7.20.5 + resolution: "@types/babel__traverse@npm:7.20.5" dependencies: "@babel/types": ^7.20.7 - checksum: 58341e23c649c0eba134a1682d4f20d027fad290d92e5740faa1279978f6ed476fc467ae51ce17a877e2566d805aeac64eae541168994367761ec883a4150221 + checksum: 608e0ab4fc31cd47011d98942e6241b34d461608c0c0e153377c5fd822c436c475f1ded76a56bfa76a1adf8d9266b727bbf9bfac90c4cb152c97f30dadc5b7e8 languageName: node linkType: hard "@types/bn.js@npm:*, @types/bn.js@npm:^5.1.3": - version: 5.1.3 - resolution: "@types/bn.js@npm:5.1.3" + version: 5.1.5 + resolution: "@types/bn.js@npm:5.1.5" dependencies: "@types/node": "*" - checksum: 6cd144b8192b6655a009021a4f838a725ea3eb4c5e6425ffc5b144788f7612fb09018c2359954edef32ab7db15f7070b77d05499318b6d9824a55cb7e6776620 + checksum: c87b28c4af74545624f8a3dae5294b16aa190c222626e8d4b2e327b33b1a3f1eeb43e7a24d914a9774bca43d8cd6e1cb0325c1f4b3a244af6693a024e1d918e6 languageName: node linkType: hard "@types/body-parser@npm:*": - version: 1.19.2 - resolution: "@types/body-parser@npm:1.19.2" + version: 1.19.5 + resolution: "@types/body-parser@npm:1.19.5" dependencies: "@types/connect": "*" "@types/node": "*" - checksum: e17840c7d747a549f00aebe72c89313d09fbc4b632b949b2470c5cb3b1cb73863901ae84d9335b567a79ec5efcfb8a28ff8e3f36bc8748a9686756b6d5681f40 + checksum: 1e251118c4b2f61029cc43b0dc028495f2d1957fe8ee49a707fb940f86a9bd2f9754230805598278fe99958b49e9b7e66eec8ef6a50ab5c1f6b93e1ba2aaba82 languageName: node linkType: hard "@types/connect@npm:*": - version: 3.4.35 - resolution: "@types/connect@npm:3.4.35" + version: 3.4.38 + resolution: "@types/connect@npm:3.4.38" dependencies: "@types/node": "*" - checksum: fe81351470f2d3165e8b12ce33542eef89ea893e36dd62e8f7d72566dfb7e448376ae962f9f3ea888547ce8b55a40020ca0e01d637fab5d99567673084542641 + checksum: 7eb1bc5342a9604facd57598a6c62621e244822442976c443efb84ff745246b10d06e8b309b6e80130026a396f19bf6793b7cecd7380169f369dac3bfc46fb99 languageName: node linkType: hard "@types/content-disposition@npm:*": - version: 0.5.5 - resolution: "@types/content-disposition@npm:0.5.5" - checksum: fdf7379db1d509990bcf9a21d85f05aad878596f28b1418f9179f6436cb22513262c670ce88c6055054a7f5804a9303eeacb70aa59a5e11ffdc1434559db9692 + version: 0.5.8 + resolution: "@types/content-disposition@npm:0.5.8" + checksum: eeea868fb510ae7a32aa2d7de680fba79d59001f3e758a334621e10bc0a6496d3a42bb79243a5e53b9c63cb524522853ccc144fe1ab160c4247d37cdb81146c4 languageName: node linkType: hard -"@types/cookiejar@npm:*": - version: 2.1.2 - resolution: "@types/cookiejar@npm:2.1.2" - checksum: f6e1903454007f86edd6c3520cbb4d553e1d4e17eaf1f77f6f75e3270f48cc828d74397a113a36942f5fe52f9fa71067bcfa738f53ad468fcca0bc52cb1cbd28 +"@types/cookiejar@npm:^2.1.5": + version: 2.1.5 + resolution: "@types/cookiejar@npm:2.1.5" + checksum: 04d5990e87b6387532d15a87d9ec9b2eb783039291193863751dcfd7fc723a3b3aa30ce4c06b03975cba58632e933772f1ff031af23eaa3ac7f94e71afa6e073 languageName: node linkType: hard "@types/cookies@npm:*": - version: 0.7.7 - resolution: "@types/cookies@npm:0.7.7" + version: 0.7.10 + resolution: "@types/cookies@npm:0.7.10" dependencies: "@types/connect": "*" "@types/express": "*" "@types/keygrip": "*" "@types/node": "*" - checksum: d3759efc1182cb0651808570ae13638677b67b0ea724eef7b174e58ffe6ea044b62c7c2715e532f76f88fce4dd8101ed32ac6fbb73226db654017924e8a2a1e6 + checksum: 99cd44a193398932ff7926cfaac1eb4441d3dc47c3f64fdfb28861acbeb290b6db6a20376f993defc9d302db92bb1d36189b89ba447a633f960535f3f0d34e2d languageName: node linkType: hard "@types/debug@npm:^4.1.7": - version: 4.1.8 - resolution: "@types/debug@npm:4.1.8" + version: 4.1.12 + resolution: "@types/debug@npm:4.1.12" dependencies: "@types/ms": "*" - checksum: a9a9bb40a199e9724aa944e139a7659173a9b274798ea7efbc277cb084bc37d32fc4c00877c3496fac4fed70a23243d284adb75c00b5fdabb38a22154d18e5df + checksum: 47876a852de8240bfdaf7481357af2b88cb660d30c72e73789abf00c499d6bc7cd5e52f41c915d1b9cd8ec9fef5b05688d7b7aef17f7f272c2d04679508d1053 languageName: node linkType: hard "@types/detect-node@npm:^2.0.0": - version: 2.0.0 - resolution: "@types/detect-node@npm:2.0.0" - checksum: f0f5c8ec948f5d4a40944773c8f81460ca7fa08fddf53330166feff1f8e28719bba9a01984872c5823c5de00c8223984381640a41b3c541bc57f3b2d529a0024 + version: 2.0.2 + resolution: "@types/detect-node@npm:2.0.2" + checksum: 064af29e09c5e336174d69b7709510457b1c6704d195a0d1dde9d26091c6cc8aaed39f8e7d329eedbc765655296b5a46db12b50841265a721f5bd4d0b48cbe6f languageName: node linkType: hard "@types/elliptic@npm:^6.4.16": - version: 6.4.16 - resolution: "@types/elliptic@npm:6.4.16" + version: 6.4.18 + resolution: "@types/elliptic@npm:6.4.18" dependencies: "@types/bn.js": "*" - checksum: fedecadbab1a469a22bc9f8e44ce730bd945faed82230174c9df4748f29948d34d9d6f7c79122049cd37f048522e28019a470df7a55c86765a82fb0d05f3f415 + checksum: c27613c530fb95441e5e6b456c8c9bc26568ca14c546aae6d7c1d8d46869f79a2272feaef266ac00bdb68b2671e6351ed01b91b82266eac30ca9092720825d16 languageName: node linkType: hard "@types/eslint-scope@npm:^3.7.3": - version: 3.7.4 - resolution: "@types/eslint-scope@npm:3.7.4" + version: 3.7.7 + resolution: "@types/eslint-scope@npm:3.7.7" dependencies: "@types/eslint": "*" "@types/estree": "*" - checksum: ea6a9363e92f301cd3888194469f9ec9d0021fe0a397a97a6dd689e7545c75de0bd2153dfb13d3ab532853a278b6572c6f678ce846980669e41029d205653460 + checksum: e2889a124aaab0b89af1bab5959847c5bec09809209255de0e63b9f54c629a94781daa04adb66bffcdd742f5e25a17614fb933965093c0eea64aacda4309380e languageName: node linkType: hard "@types/eslint@npm:*": - version: 8.44.1 - resolution: "@types/eslint@npm:8.44.1" + version: 8.56.2 + resolution: "@types/eslint@npm:8.56.2" dependencies: "@types/estree": "*" "@types/json-schema": "*" - checksum: 8b45be72d3c22a1ee0b1cc7e7fb0e34e32bbf959e6b7e0e46d160c17894aedf159c1db5c85750f10068884c741eebc37a1cc7ea659de23a8df0c9a3203e2ff9d + checksum: 38e054971596f5c0413f66a62dc26b10e0a21ac46ceacb06fbf8cfb838d20820787209b17218b3916e4c23d990ff77cfdb482d655cac0e0d2b837d430fcc5db8 languageName: node linkType: hard -"@types/estree@npm:*, @types/estree@npm:^1.0.0": - version: 1.0.1 - resolution: "@types/estree@npm:1.0.1" - checksum: e9aa175eacb797216fafce4d41e8202c7a75555bc55232dee0f9903d7171f8f19f0ae7d5191bb1a88cb90e65468be508c0df850a9fb81b4433b293a5a749899d +"@types/estree@npm:*, @types/estree@npm:^1.0.5": + version: 1.0.5 + resolution: "@types/estree@npm:1.0.5" + checksum: dd8b5bed28e6213b7acd0fb665a84e693554d850b0df423ac8076cc3ad5823a6bc26b0251d080bdc545af83179ede51dd3f6fa78cad2c46ed1f29624ddf3e41a languageName: node linkType: hard "@types/express-serve-static-core@npm:^4.17.33": - version: 4.17.35 - resolution: "@types/express-serve-static-core@npm:4.17.35" + version: 4.17.42 + resolution: "@types/express-serve-static-core@npm:4.17.42" dependencies: "@types/node": "*" "@types/qs": "*" "@types/range-parser": "*" "@types/send": "*" - checksum: cc8995d10c6feda475ec1b3a0e69eb0f35f21ab6b49129ad5c6f279e0bc5de8175bc04ec51304cb79a43eec3ed2f5a1e01472eb6d5f827b8c35c6ca8ad24eb6e + checksum: 58273f80fcc94de42691f48e22542e69f0b17863378e3216ce8b782ace012f32241bfeb02a2be837f0e2b4ef96e916979adc30bbfea13f6545bd3ab81b7d2773 languageName: node linkType: hard "@types/express@npm:*": - version: 4.17.17 - resolution: "@types/express@npm:4.17.17" + version: 4.17.21 + resolution: "@types/express@npm:4.17.21" dependencies: "@types/body-parser": "*" "@types/express-serve-static-core": ^4.17.33 "@types/qs": "*" "@types/serve-static": "*" - checksum: 0196dacc275ac3ce89d7364885cb08e7fb61f53ca101f65886dbf1daf9b7eb05c0943e2e4bbd01b0cc5e50f37e0eea7e4cbe97d0304094411ac73e1b7998f4da + checksum: fb238298630370a7392c7abdc80f495ae6c716723e114705d7e3fb67e3850b3859bbfd29391463a3fb8c0b32051847935933d99e719c0478710f8098ee7091c5 languageName: node linkType: hard -"@types/fs-extra@npm:^11.0.1": - version: 11.0.1 - resolution: "@types/fs-extra@npm:11.0.1" +"@types/fs-extra@npm:^11.0.1, @types/fs-extra@npm:^11.0.2": + version: 11.0.4 + resolution: "@types/fs-extra@npm:11.0.4" dependencies: "@types/jsonfile": "*" "@types/node": "*" - checksum: 3e930346e5d84f419deb8ced1c582beef8cb20d0bd8a0eb145a37d75bab0572a1895f0e48a0d681d386b3a58b9a992b2d2acecc464bcaec2548f53ea00718651 - languageName: node - linkType: hard - -"@types/fs-extra@npm:^11.0.2": - version: 11.0.2 - resolution: "@types/fs-extra@npm:11.0.2" - dependencies: - "@types/jsonfile": "*" - "@types/node": "*" - checksum: 5b3e30343ee62d2e393e1029355f13f64bab6f3416226e22492483f99da840e2e53ca22cbfa4ac3749f2f83f7086d19c009005c8fa175da01df0fae59c2d73e1 + checksum: 242cb84157631f057f76495c8220707541882c00a00195b603d937fb55e471afecebcb089bab50233ed3a59c69fd68bf65c1f69dd7fafe2347e139cc15b9b0e5 languageName: node linkType: hard "@types/graceful-fs@npm:^4.1.3": - version: 4.1.6 - resolution: "@types/graceful-fs@npm:4.1.6" + version: 4.1.9 + resolution: "@types/graceful-fs@npm:4.1.9" dependencies: "@types/node": "*" - checksum: c3070ccdc9ca0f40df747bced1c96c71a61992d6f7c767e8fd24bb6a3c2de26e8b84135ede000b7e79db530a23e7e88dcd9db60eee6395d0f4ce1dae91369dd4 + checksum: 79d746a8f053954bba36bd3d94a90c78de995d126289d656fb3271dd9f1229d33f678da04d10bce6be440494a5a73438e2e363e92802d16b8315b051036c5256 languageName: node linkType: hard "@types/http-assert@npm:*": - version: 1.5.3 - resolution: "@types/http-assert@npm:1.5.3" - checksum: 9553e5a0b8bcfdac4b51d3fa3b89a91b5450171861a667a5b4c47204e0f4a1ca865d97396e6ceaf220e87b64d06b7a8bad7bfba15ef97acb41a87507c9940dbc + version: 1.5.5 + resolution: "@types/http-assert@npm:1.5.5" + checksum: cd6bb7fd42cc6e2a702cb55370b8b25231954ad74c04bcd185b943a74ded3d4c28099c30f77b26951df2426441baff41718816c60b5af80efe2b8888d900bf93 languageName: node linkType: hard "@types/http-errors@npm:*": - version: 2.0.1 - resolution: "@types/http-errors@npm:2.0.1" - checksum: 3bb0c50b0a652e679a84c30cd0340d696c32ef6558518268c238840346c077f899315daaf1c26c09c57ddd5dc80510f2a7f46acd52bf949e339e35ed3ee9654f + version: 2.0.4 + resolution: "@types/http-errors@npm:2.0.4" + checksum: 1f3d7c3b32c7524811a45690881736b3ef741bf9849ae03d32ad1ab7062608454b150a4e7f1351f83d26a418b2d65af9bdc06198f1c079d75578282884c4e8e3 languageName: node linkType: hard "@types/istanbul-lib-coverage@npm:*, @types/istanbul-lib-coverage@npm:^2.0.0, @types/istanbul-lib-coverage@npm:^2.0.1": - version: 2.0.4 - resolution: "@types/istanbul-lib-coverage@npm:2.0.4" - checksum: a25d7589ee65c94d31464c16b72a9dc81dfa0bea9d3e105ae03882d616e2a0712a9c101a599ec482d297c3591e16336962878cb3eb1a0a62d5b76d277a890ce7 + version: 2.0.6 + resolution: "@types/istanbul-lib-coverage@npm:2.0.6" + checksum: 3feac423fd3e5449485afac999dcfcb3d44a37c830af898b689fadc65d26526460bedb889db278e0d4d815a670331796494d073a10ee6e3a6526301fe7415778 languageName: node linkType: hard "@types/istanbul-lib-report@npm:*": - version: 3.0.0 - resolution: "@types/istanbul-lib-report@npm:3.0.0" + version: 3.0.3 + resolution: "@types/istanbul-lib-report@npm:3.0.3" dependencies: "@types/istanbul-lib-coverage": "*" - checksum: 656398b62dc288e1b5226f8880af98087233cdb90100655c989a09f3052b5775bf98ba58a16c5ae642fb66c61aba402e07a9f2bff1d1569e3b306026c59f3f36 + checksum: b91e9b60f865ff08cb35667a427b70f6c2c63e88105eadd29a112582942af47ed99c60610180aa8dcc22382fa405033f141c119c69b95db78c4c709fbadfeeb4 languageName: node linkType: hard "@types/istanbul-reports@npm:^3.0.0": - version: 3.0.1 - resolution: "@types/istanbul-reports@npm:3.0.1" + version: 3.0.4 + resolution: "@types/istanbul-reports@npm:3.0.4" dependencies: "@types/istanbul-lib-report": "*" - checksum: f1ad54bc68f37f60b30c7915886b92f86b847033e597f9b34f2415acdbe5ed742fa559a0a40050d74cdba3b6a63c342cac1f3a64dba5b68b66a6941f4abd7903 + checksum: 93eb18835770b3431f68ae9ac1ca91741ab85f7606f310a34b3586b5a34450ec038c3eed7ab19266635499594de52ff73723a54a72a75b9f7d6a956f01edee95 languageName: node linkType: hard "@types/jest@npm:^29.5.0": - version: 29.5.3 - resolution: "@types/jest@npm:29.5.3" + version: 29.5.11 + resolution: "@types/jest@npm:29.5.11" dependencies: expect: ^29.0.0 pretty-format: ^29.0.0 - checksum: e36bb92e0b9e5ea7d6f8832baa42f087fc1697f6cd30ec309a07ea4c268e06ec460f1f0cfd2581daf5eff5763475190ec1ad8ac6520c49ccfe4f5c0a48bfa676 + checksum: f892a06ec9f0afa9a61cd7fa316ec614e21d4df1ad301b5a837787e046fcb40dfdf7f264a55e813ac6b9b633cb9d366bd5b8d1cea725e84102477b366df23fdd languageName: node linkType: hard "@types/json-schema@npm:*, @types/json-schema@npm:^7.0.12, @types/json-schema@npm:^7.0.8": - version: 7.0.12 - resolution: "@types/json-schema@npm:7.0.12" - checksum: 00239e97234eeb5ceefb0c1875d98ade6e922bfec39dd365ec6bd360b5c2f825e612ac4f6e5f1d13601b8b30f378f15e6faa805a3a732f4a1bbe61915163d293 + version: 7.0.15 + resolution: "@types/json-schema@npm:7.0.15" + checksum: 97ed0cb44d4070aecea772b7b2e2ed971e10c81ec87dd4ecc160322ffa55ff330dace1793489540e3e318d90942064bb697cc0f8989391797792d919737b3b98 languageName: node linkType: hard @@ -3328,46 +3236,46 @@ __metadata: linkType: hard "@types/jsonfile@npm:*": - version: 6.1.1 - resolution: "@types/jsonfile@npm:6.1.1" + version: 6.1.4 + resolution: "@types/jsonfile@npm:6.1.4" dependencies: "@types/node": "*" - checksum: 0f8fe0a9221a00e8413cffba723dfe16553868724b830237256fb0052ecd5cac96498189d1235a001cfa815f352008261c9ceb373f0aa58227f891e0c7a12c4d + checksum: 309fda20eb5f1cf68f2df28931afdf189c5e7e6bec64ac783ce737bb98908d57f6f58757ad5da9be37b815645a6f914e2d4f3ac66c574b8fe1ba6616284d0e97 languageName: node linkType: hard "@types/keygrip@npm:*": - version: 1.0.2 - resolution: "@types/keygrip@npm:1.0.2" - checksum: 60bc2738a4f107070ee3d96f44709cb38f3a96c7ccabab09f56c1b2b4d85f869fd8fb9f1f2937e863d0e9e781f005c2223b823bf32b859185b4f52370c352669 + version: 1.0.6 + resolution: "@types/keygrip@npm:1.0.6" + checksum: d157f60bf920492347791d2b26d530d5069ce05796549fbacd4c24d66ffbebbcb0ab67b21e7a1b80a593b9fd4b67dc4843dec04c12bbc2e0fddfb8577a826c41 languageName: node linkType: hard "@types/koa-bodyparser@npm:^4.3.10": - version: 4.3.10 - resolution: "@types/koa-bodyparser@npm:4.3.10" + version: 4.3.12 + resolution: "@types/koa-bodyparser@npm:4.3.12" dependencies: "@types/koa": "*" - checksum: 4b4cd176815a6c1fb0d593bfea03de1285e606d3a96e56ad3691144e35061750ed95e4ecf2ff8e25599d360a93646e29dbb167fdfaaa73ccf87ca5b6141ff0db + checksum: 645cc253c6b9b2e98252b1cdc75a4812cd6d3c228e426f9893a755324b7a6936559ec659a0ff288cb2642340b3cc4e2110167f24b84efc8e3b89c04fe67ed883 languageName: node linkType: hard "@types/koa-compose@npm:*": - version: 3.2.5 - resolution: "@types/koa-compose@npm:3.2.5" + version: 3.2.8 + resolution: "@types/koa-compose@npm:3.2.8" dependencies: "@types/koa": "*" - checksum: 5d1147c4b057eb158195f442f0384f06503f3e69dba99fb517b30a05261a9f92928945c12bb1cfc17a5b7d60db003f38b455a3a9b125f12e4fc81fffa396b3cf + checksum: 95c32bdee738ac7c10439bbf6342ca3b9f0aafd7e8118739eac7fb0fa703a23cfe4c88f63e13a69a16fbde702e0bcdc62b272aa734325fc8efa7e5625479752e languageName: node linkType: hard "@types/koa-compress@npm:^4.0.3": - version: 4.0.3 - resolution: "@types/koa-compress@npm:4.0.3" + version: 4.0.6 + resolution: "@types/koa-compress@npm:4.0.6" dependencies: "@types/koa": "*" "@types/node": "*" - checksum: 6f09e4ad8160204fbee9d0a452b83ba62fec503a2eec60cf41fc67a032971027b6858e0b90c6e05bf1ad3b006f7c7a2d02922db4d159d223ab8d33eeeb108757 + checksum: 0ec8ffac1bf3c7dc36a9ee4588f83ade0a485b615aff7e9e08082319b1b3f7e2f5954ed5ce4303a7f9ba1b4144081e0700cbf3165d1aef54e5e12130c810b2e4 languageName: node linkType: hard @@ -3381,36 +3289,36 @@ __metadata: linkType: hard "@types/koa-router@npm:^7.4.4": - version: 7.4.4 - resolution: "@types/koa-router@npm:7.4.4" + version: 7.4.8 + resolution: "@types/koa-router@npm:7.4.8" dependencies: "@types/koa": "*" - checksum: 23ff5b725daa1427dc822602f5d4fdcecca5f990595af48879e41338a9c71819ae312326028eef4645beb6ea32ea852416e2f0761a2abd5bf80c2575a3301837 + checksum: 30b9735748f25ac338ec4197430a10f1cf58eeea0445f0b64733ed95df82b9245a31c01bbfdd3c9b71e90aa7a1ccf9546f4dd91bac87f6186152e67146ad9b6c languageName: node linkType: hard "@types/koa-send@npm:*": - version: 4.1.4 - resolution: "@types/koa-send@npm:4.1.4" + version: 4.1.6 + resolution: "@types/koa-send@npm:4.1.6" dependencies: "@types/koa": "*" - checksum: 27732c85e97465810bc7631153368daa8e8715bd356eab344d0b3deaec162de09b6cd9d61a524f4d3631493234da7104167e788d593ce27405b70bdfb12fc81b + checksum: d46d207f1d826ccd74bf3a02180d0475be8456eb3a2244244d19cb3f1737251e163d73958fdcd12111e03c7c0545cc89e7888a6ef2ba370ebf2b2e804efaaaf1 languageName: node linkType: hard "@types/koa-static@npm:^4.0.2": - version: 4.0.2 - resolution: "@types/koa-static@npm:4.0.2" + version: 4.0.4 + resolution: "@types/koa-static@npm:4.0.4" dependencies: "@types/koa": "*" "@types/koa-send": "*" - checksum: a9c557a37b25a677f3aae084b2afd267fa78a728cd69aec20821d8acca3ef4bda172d1fd16a23711266d97e77962d037ffd25ee76b24608413032226321f461f + checksum: 99087a9b6f4214679932008fbed2d4332fca06cd01f2d333439bd1cf0844c313584c8eb6b805360d1c3d6c6c8a475468a5f4f73ecad551c8cc369e290ad41331 languageName: node linkType: hard -"@types/koa@npm:*, @types/koa@npm:^2.13.5, @types/koa@npm:^2.13.6": - version: 2.13.8 - resolution: "@types/koa@npm:2.13.8" +"@types/koa@npm:*, @types/koa@npm:^2.13.5, @types/koa@npm:^2.13.6, @types/koa@npm:^2.13.9": + version: 2.14.0 + resolution: "@types/koa@npm:2.14.0" dependencies: "@types/accepts": "*" "@types/content-disposition": "*" @@ -3420,171 +3328,134 @@ __metadata: "@types/keygrip": "*" "@types/koa-compose": "*" "@types/node": "*" - checksum: 76a2a6d219c65f242a43efca42970d864701c58319c346a91dd8c3b4df2021786fd0d600a88dfb098358c9085f9f4a2dfe62563641441cf21e11e2bfe04f4fdf - languageName: node - linkType: hard - -"@types/koa@npm:^2.13.9": - version: 2.13.9 - resolution: "@types/koa@npm:2.13.9" - dependencies: - "@types/accepts": "*" - "@types/content-disposition": "*" - "@types/cookies": "*" - "@types/http-assert": "*" - "@types/http-errors": "*" - "@types/keygrip": "*" - "@types/koa-compose": "*" - "@types/node": "*" - checksum: af9cd599c8e17e2ae0f4168a61d964e343f713d002b65fd995658d7addc6551ccadecfd32b3405cf44e4d360178ee4f972d6881533548261ae1f636a655d24b1 + checksum: 57d809e42350c9ddefa2150306355e40757877468bb027e0bd99f5aeb43cfaf8ba8b14761ea65e419d6fb4c2403a1f3ed0762872a9cf040dbd14357caca56548 languageName: node linkType: hard "@types/koa__cors@npm:^4.0.0": - version: 4.0.0 - resolution: "@types/koa__cors@npm:4.0.0" - dependencies: - "@types/koa": "*" - checksum: 0a7f8c2ab9b957befbbe31b9293af05a95282fb984b8468b0c0a0a0101beefe2663ce716ca56fbf4e5ec5ca2d89193ee7d635ee84bb8b5d718df01149286f4d2 - languageName: node - linkType: hard - -"@types/level-errors@npm:*": - version: 3.0.0 - resolution: "@types/level-errors@npm:3.0.0" - checksum: ad9392663439306677ac9cb704f8fa0b64c300dfea4f3494369eb78a2e09c194156cbab2b52c71a361a09b735d54a2de65195dcadba0ec7db1d14a320198133e - languageName: node - linkType: hard - -"@types/leveldown@npm:^4.0.3": version: 4.0.3 - resolution: "@types/leveldown@npm:4.0.3" + resolution: "@types/koa__cors@npm:4.0.3" dependencies: - "@types/abstract-leveldown": "*" - "@types/node": "*" - checksum: 0a476bd8d3c71266fdb323875d3312bf7828d6ab9f1bf9882a0ff93d04044660e45cd211ca8ec34c5665eaa773f98fe9fee14235792835bd06cb68192fe44669 + "@types/koa": "*" + checksum: ca7bfd1ffacf6c425393e2716a88d66dfe1b5e9a32cd92253cc846fa95bd7fd44c1dc848252f13b7febb5bccf23b8e88716b051cfa04d18fa31e0768432dccb7 languageName: node - linkType: hard - -"@types/leveldown@npm:^4.0.4": - version: 4.0.4 - resolution: "@types/leveldown@npm:4.0.4" - dependencies: - "@types/abstract-leveldown": "*" - "@types/node": "*" - checksum: 630b2d2d1c48f83d14ab0f6c03ad2af1c427675c3692873c4fd3d673bde4140eabc028ce5736ad3d76aeea20769cf53df6f83468a4f0cf28f6d04dbb435edf48 + linkType: hard + +"@types/level-errors@npm:*": + version: 3.0.2 + resolution: "@types/level-errors@npm:3.0.2" + checksum: 3d9b801f6499f795b60ac723c1b3f93ca105f20ed26966eeb606c804b10c65984c3233fb99914644d75a3223f80f220eca74fda316640a85a5b3d7572cd86925 languageName: node linkType: hard -"@types/levelup@npm:^5.1.2": - version: 5.1.2 - resolution: "@types/levelup@npm:5.1.2" +"@types/leveldown@npm:^4.0.3": + version: 4.0.6 + resolution: "@types/leveldown@npm:4.0.6" dependencies: "@types/abstract-leveldown": "*" - "@types/level-errors": "*" "@types/node": "*" - checksum: 6740284488b6806ba398bc38842fa789edd5667a342830c544a6b3611ebeed957a08d03dc8bde1e32fe03ac9c439341647c044c1ff0f73a26bcded9ca302a009 + checksum: 8b06cbc6858f3956fe8e10a8bb58edd75369587e72ce33ab7e35e21e1f1c8e89981a337c977ffd3a635d4441113e434362ba37e343d8a0ec69cd7c8988450977 languageName: node linkType: hard -"@types/levelup@npm:^5.1.3": - version: 5.1.3 - resolution: "@types/levelup@npm:5.1.3" +"@types/levelup@npm:^5.1.2, @types/levelup@npm:^5.1.3": + version: 5.1.5 + resolution: "@types/levelup@npm:5.1.5" dependencies: "@types/abstract-leveldown": "*" "@types/level-errors": "*" "@types/node": "*" - checksum: 948642ea481573eec323e5f8b5475c1ab32b5eac0982e5a2a9904ac74336d32fe579d51219410770c1cb88f69f0135fd9be3cbfa5ee6e9b84f81eeddf7ed8e0d + checksum: 844798bdc805e3c449e478e283eb1196892d3f4fb6b24158faa5f10b283fa785da736917de1ec030ce1b8be9a8c54881cee2354632cb540ef80a325a19d920d1 languageName: node linkType: hard "@types/lodash.camelcase@npm:^4.3.7": - version: 4.3.7 - resolution: "@types/lodash.camelcase@npm:4.3.7" + version: 4.3.9 + resolution: "@types/lodash.camelcase@npm:4.3.9" dependencies: "@types/lodash": "*" - checksum: ef068b921ab439f7b1a2c0ebbd890ba64ba437fc6aeb5e3ac18d34da30f9054fceaac5ccfdfa9e12b2fc403e10910439d3ebf92ba2f93de03c2f02261adfc000 + checksum: f54132d38ffa72b25bce2111e4d28f339599f6d4fcfc1248a89d1d96445512d7a431f0b0e74f6e6c8d6bc09fe53cf94d9426e188d8feacb3ffe04cd9c3a602e7 languageName: node linkType: hard "@types/lodash.capitalize@npm:^4.2.7": - version: 4.2.7 - resolution: "@types/lodash.capitalize@npm:4.2.7" + version: 4.2.9 + resolution: "@types/lodash.capitalize@npm:4.2.9" dependencies: "@types/lodash": "*" - checksum: dab8b781d7dcc56c18ba0c8286a6ccb61cc598d936a449265453a473e62b2b6d7c109c4447dfeb8ccacc4088769bc3bfd0d39bc8797f03e4e685d4f4b1bc7c01 + checksum: 54a9154b2084392986646335d5ed4902a89c24ce675cf8b8cfcd022f6a0eed71c30c2f8cc4b2682cfc5c55a5e1fdad2340609ba58db451dfdd41d82b14c88995 languageName: node linkType: hard "@types/lodash.chunk@npm:^4.2.7": - version: 4.2.7 - resolution: "@types/lodash.chunk@npm:4.2.7" + version: 4.2.9 + resolution: "@types/lodash.chunk@npm:4.2.9" dependencies: "@types/lodash": "*" - checksum: 09df5ca00d8866776038bf94658d30b4ea84bffe5f81c14c01568bcb6be692bf59f6ea093de2bb0afbb3f8e54ae96ebd97595f2838ab59b77865427ca77d391e + checksum: ccffe7273a0941655d5b988baeffa8f7d4d19a8b43ed728ff4e616013506efe85914ba99d4ec299e0106506e1bca3923b065eabb0aa5f1e4b18f68e790ae6b88 languageName: node linkType: hard "@types/lodash.clonedeep@npm:^4.5.7": - version: 4.5.7 - resolution: "@types/lodash.clonedeep@npm:4.5.7" + version: 4.5.9 + resolution: "@types/lodash.clonedeep@npm:4.5.9" dependencies: "@types/lodash": "*" - checksum: 20d6a20970b3b54b3c10cf17ace1cea49c4905d7f7cae2575a98108466e8d4c9bea3b3449d11ccaac4da1fc9bab225f477f4c2dbea8ba877cc47f629455efb69 + checksum: ef85512b7dce7a4f981a818ae44d11982907e1f26b5b26bedf0957c35e8591eb8e1d24fa31ca851d4b40e0a1ee88563853d762412691fe5f357e8335cead2325 languageName: node linkType: hard "@types/lodash.clonedeepwith@npm:^4.5.7": - version: 4.5.7 - resolution: "@types/lodash.clonedeepwith@npm:4.5.7" + version: 4.5.9 + resolution: "@types/lodash.clonedeepwith@npm:4.5.9" dependencies: "@types/lodash": "*" - checksum: 2cfc7c55533abf491a2b6897a233244b187bf55ac51545e4f0b937721fdfb7e82cb3743290aa4dcb41487d653d95fdddc172b39318a893c9a2ca6658b0866430 + checksum: c690fb28126f7248894f08abe13d6c7684dd0a4e9ac545a419a8687438b50d2e6fe32b31176c65a394d3ade4fd16a145ecbf77e7521992414bf657b8b1d936c8 languageName: node linkType: hard "@types/lodash.every@npm:^4.6.7": - version: 4.6.7 - resolution: "@types/lodash.every@npm:4.6.7" + version: 4.6.9 + resolution: "@types/lodash.every@npm:4.6.9" dependencies: "@types/lodash": "*" - checksum: 9bf1475332401948453019a6fc4d9ee1275acfe52dccd2b81746927ef4623f6fb846849a15dfe08ec5b5c50e6302875a19ac469cc820e7f05d7517407ba41dc7 + checksum: 9239e078c1aba47ead8d3232d46869a6b9e886387c92c8d9a0a1549b7f21864cfa8428b5725ebbfcd31e5c54761a6fa3d4bbe049a776fe3e973cb10d967fbd0c languageName: node linkType: hard "@types/lodash.isequal@npm:^4.5.6": - version: 4.5.6 - resolution: "@types/lodash.isequal@npm:4.5.6" + version: 4.5.8 + resolution: "@types/lodash.isequal@npm:4.5.8" dependencies: "@types/lodash": "*" - checksum: 0f065989408a9e0584e6c27495be2cd4602e62650f55266aa195812582444463c0c8570c674ae84f947c11748f49ab43fd5b482fa120e08eeee4c23b162edc38 + checksum: f3180c2d2925514fff1908a1303c11468c9f39b47fd7b053416aad3f1447f8e4a9894dd0460187ac9ac19387e25aec8dd8214d13a50a0967e0dc9cca8e4c5353 languageName: node linkType: hard "@types/lodash.omit@npm:^4.5.7": - version: 4.5.7 - resolution: "@types/lodash.omit@npm:4.5.7" + version: 4.5.9 + resolution: "@types/lodash.omit@npm:4.5.9" dependencies: "@types/lodash": "*" - checksum: e30600de518e648f4e9590d9238506ceab561726ec2fe56b4ff29b17aa1e47cbf9188be259b7d606dfb2040998c778533ccc0c58f065cca44ab5a6dbc8b6e518 + checksum: 5be43f3598d6b1fa481fe7046e9e15e2225f547e0e309746c2b87f4e4fa8705e96c564e2762a9312e73e2a223d701ab893b122c506500de49a4b9fb40fb6d17c languageName: node linkType: hard "@types/lodash.pick@npm:^4.4.7": - version: 4.4.7 - resolution: "@types/lodash.pick@npm:4.4.7" + version: 4.4.9 + resolution: "@types/lodash.pick@npm:4.4.9" dependencies: "@types/lodash": "*" - checksum: 78428a83b5d85e75bd13fb632030f9adb08dfc5bde3a9b6a302434fe7ac98e62919f87a7337e0ba674017d63bc57a8855ef863f5fcecd25e63b1d39eba2e4697 + checksum: 007c298133b5bc2157f13d6641b139d2782b4af7b6a942cc4b1a427c6ebc6020e46b32ee2e782607389b0c63a5211d51152606424fdfe25f3eeac8afea0bdf02 languageName: node linkType: hard "@types/lodash.startcase@npm:^4.4.7": - version: 4.4.7 - resolution: "@types/lodash.startcase@npm:4.4.7" + version: 4.4.9 + resolution: "@types/lodash.startcase@npm:4.4.9" dependencies: "@types/lodash": "*" - checksum: ee5b903e7cb99a4c747325c38167dea70c10f9929823033f7f2e02aad5e227c84f80c6f7d9d6923a7c0f30a429c5ea4a1b6505bd50a96655b6ab7ac43e8ebe27 + checksum: 448203f0b6d31c1af9fe8292d5417af670bee560bb0af0cac3a6047b90c2d60ba03197367c2defae21e3982c665763197343863ce7d97131efa8e13e6431fe9f languageName: node linkType: hard @@ -3598,180 +3469,159 @@ __metadata: linkType: hard "@types/lodash@npm:*": - version: 4.14.196 - resolution: "@types/lodash@npm:4.14.196" - checksum: 201d17c3e62ae02a93c99ec78e024b2be9bd75564dd8fd8c26f6ac51a985ab280d28ce2688c3bcdfe785b0991cd9814edff19ee000234c7b45d9a697f09feb6a - languageName: node - linkType: hard - -"@types/memdown@npm:^3.0.0, @types/memdown@npm:^3.0.1": - version: 3.0.1 - resolution: "@types/memdown@npm:3.0.1" - dependencies: - "@types/abstract-leveldown": "*" - checksum: 08085fff44f1868d352ec3be81890cfd0034ad1086f3dbc8bbfc412d55434bb6f5bbd512a22a92f2f9c416ccb0784815ecaa0a6fada4478c9a39db3f0f7a1a43 + version: 4.14.202 + resolution: "@types/lodash@npm:4.14.202" + checksum: a91acf3564a568c6f199912f3eb2c76c99c5a0d7e219394294213b3f2d54f672619f0fde4da22b29dc5d4c31457cd799acc2e5cb6bd90f9af04a1578483b6ff7 languageName: node linkType: hard -"@types/memdown@npm:^3.0.2": - version: 3.0.2 - resolution: "@types/memdown@npm:3.0.2" +"@types/memdown@npm:^3.0.0, @types/memdown@npm:^3.0.1, @types/memdown@npm:^3.0.2, @types/memdown@npm:^3.0.3": + version: 3.0.5 + resolution: "@types/memdown@npm:3.0.5" dependencies: "@types/abstract-leveldown": "*" - checksum: bfc36240e32ed6f82b2b858be88aa8814e8c1e0a8922aae8bf44ce07a2c9e0e7bf867e01cab8aad10e1cab2d0bcb0b800b4f63b89d9c791328892acc00683469 + checksum: a0c384858354da754933a83295642ac8710af08e1bf499ff3ae87d86ea34a0d9ebf2bdf75ec45d992cd152580c671c82ea4a0aea03f10eeb3aed33eceb21819e languageName: node linkType: hard -"@types/memdown@npm:^3.0.3": - version: 3.0.3 - resolution: "@types/memdown@npm:3.0.3" - dependencies: - "@types/abstract-leveldown": "*" - checksum: 9aa311838574b51e4334878102c80c6abc0e1ec14fe00f98c6ee812f3e6b24db53de632e0f763692730e0147e5c41add0ac4257c3deb3dc7abce176708ed73a4 +"@types/methods@npm:^1.1.4": + version: 1.1.4 + resolution: "@types/methods@npm:1.1.4" + checksum: ad2a7178486f2fd167750f3eb920ab032a947ff2e26f55c86670a6038632d790b46f52e5b6ead5823f1e53fc68028f1e9ddd15cfead7903e04517c88debd72b1 languageName: node linkType: hard "@types/mime@npm:*": - version: 3.0.1 - resolution: "@types/mime@npm:3.0.1" - checksum: 4040fac73fd0cea2460e29b348c1a6173da747f3a87da0dbce80dd7a9355a3d0e51d6d9a401654f3e5550620e3718b5a899b2ec1debf18424e298a2c605346e7 + version: 3.0.4 + resolution: "@types/mime@npm:3.0.4" + checksum: a6139c8e1f705ef2b064d072f6edc01f3c099023ad7c4fce2afc6c2bf0231888202adadbdb48643e8e20da0ce409481a49922e737eca52871b3dc08017455843 languageName: node linkType: hard "@types/mime@npm:^1": - version: 1.3.2 - resolution: "@types/mime@npm:1.3.2" - checksum: 0493368244cced1a69cb791b485a260a422e6fcc857782e1178d1e6f219f1b161793e9f87f5fae1b219af0f50bee24fcbe733a18b4be8fdd07a38a8fb91146fd + version: 1.3.5 + resolution: "@types/mime@npm:1.3.5" + checksum: e29a5f9c4776f5229d84e525b7cd7dd960b51c30a0fb9a028c0821790b82fca9f672dab56561e2acd9e8eed51d431bde52eafdfef30f643586c4162f1aecfc78 languageName: node linkType: hard "@types/minimist@npm:^1.2.0": - version: 1.2.2 - resolution: "@types/minimist@npm:1.2.2" - checksum: b8da83c66eb4aac0440e64674b19564d9d86c80ae273144db9681e5eeff66f238ade9515f5006ffbfa955ceff8b89ad2bd8ec577d7caee74ba101431fb07045d + version: 1.2.5 + resolution: "@types/minimist@npm:1.2.5" + checksum: 477047b606005058ab0263c4f58097136268007f320003c348794f74adedc3166ffc47c80ec3e94687787f2ab7f4e72c468223946e79892cf0fd9e25e9970a90 languageName: node linkType: hard "@types/ms@npm:*": - version: 0.7.31 - resolution: "@types/ms@npm:0.7.31" - checksum: daadd354aedde024cce6f5aa873fefe7b71b22cd0e28632a69e8b677aeb48ae8caa1c60e5919bb781df040d116b01cb4316335167a3fc0ef6a63fa3614c0f6da + version: 0.7.34 + resolution: "@types/ms@npm:0.7.34" + checksum: f38d36e7b6edecd9badc9cf50474159e9da5fa6965a75186cceaf883278611b9df6669dc3a3cc122b7938d317b68a9e3d573d316fcb35d1be47ec9e468c6bd8a languageName: node linkType: hard -"@types/node@npm:*, @types/node@npm:>=13.7.0": - version: 20.4.5 - resolution: "@types/node@npm:20.4.5" - checksum: 36a0304a8dc346a1b2d2edac4c4633eecf70875793d61a5274d0df052d7a7af7a8e34f29884eac4fbd094c4f0201477dcb39c0ecd3307ca141688806538d1138 +"@types/node@npm:*": + version: 20.11.7 + resolution: "@types/node@npm:20.11.7" + dependencies: + undici-types: ~5.26.4 + checksum: 61ea0718bccda31110c643190518407b7c50d26698a20e3522871608db5fa3d2d43d1ae57c609068eae6996d563db43326045a90f22a9aacc825e8d6c7aea2ce languageName: node linkType: hard "@types/node@npm:^18.14.6, @types/node@npm:^18.15.11, @types/node@npm:^18.15.3, @types/node@npm:^18.7.23": - version: 18.17.1 - resolution: "@types/node@npm:18.17.1" - checksum: 56201bda9a2d05d68602df63b4e67b0545ac8c6d0280bd5fb31701350a978a577a027501fbf49db99bf177f2242ebd1244896bfd35e89042d5bd7dfebff28d4e + version: 18.19.10 + resolution: "@types/node@npm:18.19.10" + dependencies: + undici-types: ~5.26.4 + checksum: eea429c1fe8d25702c1e860f5c4ac053db3c52a5884646f458513b622a507660a6c88c717ee4f106e63c82f4ec6cafbf999e3f3f1d3083f7a40b4f715e03332b languageName: node linkType: hard "@types/normalize-package-data@npm:^2.4.0": - version: 2.4.1 - resolution: "@types/normalize-package-data@npm:2.4.1" - checksum: e87bccbf11f95035c89a132b52b79ce69a1e3652fe55962363063c9c0dae0fe2477ebc585e03a9652adc6f381d24ba5589cc5e51849df4ced3d3e004a7d40ed5 + version: 2.4.4 + resolution: "@types/normalize-package-data@npm:2.4.4" + checksum: 65dff72b543997b7be8b0265eca7ace0e34b75c3e5fee31de11179d08fa7124a7a5587265d53d0409532ecb7f7fba662c2012807963e1f9b059653ec2c83ee05 languageName: node linkType: hard "@types/pako@npm:^2.0.0": - version: 2.0.0 - resolution: "@types/pako@npm:2.0.0" - checksum: 50240a036b5e6acabbf36ac4dca93ec9e619241f0404da8d401cdb427bec3029833324b8a04c4b1ae2ecbc33422fdec31dbf9f43653d9d07cafb82ace78dfccd + version: 2.0.3 + resolution: "@types/pako@npm:2.0.3" + checksum: 0746dd5d29eccf5b2e6cceb3ccb093851219e78bd2e2e20d25757e247987139e061e5d4ba37cb5295493f06e3c683c74f8876011cd8a3f3748a09244fbc841d9 languageName: node linkType: hard "@types/qs@npm:*": - version: 6.9.7 - resolution: "@types/qs@npm:6.9.7" - checksum: 7fd6f9c25053e9b5bb6bc9f9f76c1d89e6c04f7707a7ba0e44cc01f17ef5284adb82f230f542c2d5557d69407c9a40f0f3515e8319afd14e1e16b5543ac6cdba + version: 6.9.11 + resolution: "@types/qs@npm:6.9.11" + checksum: 620ca1628bf3da65662c54ed6ebb120b18a3da477d0bfcc872b696685a9bb1893c3c92b53a1190a8f54d52eaddb6af8b2157755699ac83164604329935e8a7f2 languageName: node linkType: hard "@types/range-parser@npm:*": - version: 1.2.4 - resolution: "@types/range-parser@npm:1.2.4" - checksum: b7c0dfd5080a989d6c8bb0b6750fc0933d9acabeb476da6fe71d8bdf1ab65e37c136169d84148034802f48378ab94e3c37bb4ef7656b2bec2cb9c0f8d4146a95 - languageName: node - linkType: hard - -"@types/retry@npm:0.12.1": - version: 0.12.1 - resolution: "@types/retry@npm:0.12.1" - checksum: 5f46b2556053655f78262bb33040dc58417c900457cc63ff37d6c35349814471453ef511af0cec76a540c601296cd2b22f64bab1ab649c0dacc0223765ba876c - languageName: node - linkType: hard - -"@types/semver@npm:^7.5.0": - version: 7.5.0 - resolution: "@types/semver@npm:7.5.0" - checksum: 0a64b9b9c7424d9a467658b18dd70d1d781c2d6f033096a6e05762d20ebbad23c1b69b0083b0484722aabf35640b78ccc3de26368bcae1129c87e9df028a22e2 + version: 1.2.7 + resolution: "@types/range-parser@npm:1.2.7" + checksum: 95640233b689dfbd85b8c6ee268812a732cf36d5affead89e806fe30da9a430767af8ef2cd661024fd97e19d61f3dec75af2df5e80ec3bea000019ab7028629a languageName: node linkType: hard -"@types/semver@npm:^7.5.2": - version: 7.5.2 - resolution: "@types/semver@npm:7.5.2" - checksum: 743aa8a2b58e20b329c19bd2459152cb049d12fafab7279b90ac11e0f268c97efbcb606ea0c681cca03f79015381b40d9b1244349b354270bec3f939ed49f6e9 +"@types/retry@npm:0.12.2": + version: 0.12.2 + resolution: "@types/retry@npm:0.12.2" + checksum: e5675035717b39ce4f42f339657cae9637cf0c0051cf54314a6a2c44d38d91f6544be9ddc0280587789b6afd056be5d99dbe3e9f4df68c286c36321579b1bf4a languageName: node linkType: hard -"@types/semver@npm:^7.5.4": - version: 7.5.4 - resolution: "@types/semver@npm:7.5.4" - checksum: 120c0189f6fec5f2d12d0d71ac8a4cfa952dc17fa3d842e8afddb82bba8828a4052f8799c1653e2b47ae1977435f38e8985658fde971905ce5afb8e23ee97ecf +"@types/semver@npm:^7.5.0, @types/semver@npm:^7.5.2, @types/semver@npm:^7.5.4": + version: 7.5.6 + resolution: "@types/semver@npm:7.5.6" + checksum: 563a0120ec0efcc326567db2ed920d5d98346f3638b6324ea6b50222b96f02a8add3c51a916b6897b51523aad8ac227d21d3dcf8913559f1bfc6c15b14d23037 languageName: node linkType: hard "@types/send@npm:*": - version: 0.17.1 - resolution: "@types/send@npm:0.17.1" + version: 0.17.4 + resolution: "@types/send@npm:0.17.4" dependencies: "@types/mime": ^1 "@types/node": "*" - checksum: 10b620a5960058ef009afbc17686f680d6486277c62f640845381ec4baa0ea683fdd77c3afea4803daf5fcddd3fb2972c8aa32e078939f1d4e96f83195c89793 + checksum: cf4db48251bbb03cd6452b4de6e8e09e2d75390a92fd798eca4a803df06444adc94ed050246c94c7ed46fb97be1f63607f0e1f13c3ce83d71788b3e08640e5e0 languageName: node linkType: hard "@types/serve-static@npm:*": - version: 1.15.2 - resolution: "@types/serve-static@npm:1.15.2" + version: 1.15.5 + resolution: "@types/serve-static@npm:1.15.5" dependencies: "@types/http-errors": "*" "@types/mime": "*" "@types/node": "*" - checksum: 15c261dbfc57890f7cc17c04d5b22b418dfa0330c912b46c5d8ae2064da5d6f844ef7f41b63c7f4bbf07675e97ebe6ac804b032635ec742ae45d6f1274259b3e + checksum: 0ff4b3703cf20ba89c9f9e345bc38417860a88e85863c8d6fe274a543220ab7f5f647d307c60a71bb57dc9559f0890a661e8dc771a6ec5ef195d91c8afc4a893 languageName: node linkType: hard "@types/sha256@npm:^0.2.0": - version: 0.2.0 - resolution: "@types/sha256@npm:0.2.0" + version: 0.2.2 + resolution: "@types/sha256@npm:0.2.2" dependencies: "@types/node": "*" - checksum: f3c8e0dcaf11d833292b7dd19db567ef41b23036b2fb9ea7335cba100aac8c56e1346171fae6c63885f6c0e1048550506fd165628bc0001902fea010a16d3842 + checksum: 7701b9dc105e7b877090c9bb9b02e10953831737b599bfc7658635ae35d2b21927f77028f8090d50ea0281058ee975f190d664e5351c5aaf5535a1c26ba01f1f languageName: node linkType: hard -"@types/sinon@npm:^10.0.15": - version: 10.0.16 - resolution: "@types/sinon@npm:10.0.16" +"@types/sinon@npm:^17.0.0": + version: 17.0.3 + resolution: "@types/sinon@npm:17.0.3" dependencies: "@types/sinonjs__fake-timers": "*" - checksum: 1216aac584500d6bf845ca76f57e82f8459cf9de4ed80a55e50aa4438360fc418789a42181e211c5d279e97f86a3a994e3c81e43971d540737caca0193242bbf + checksum: c8e9956d9c90fe1ec1cc43085ae48897f93f9ea86e909ab47f255ea71f5229651faa070393950fb6923aef426c84e92b375503f9f8886ef44668b82a8ee49e9a languageName: node linkType: hard "@types/sinonjs__fake-timers@npm:*": - version: 8.1.2 - resolution: "@types/sinonjs__fake-timers@npm:8.1.2" - checksum: bbc73a5ab6c0ec974929392f3d6e1e8db4ebad97ec506d785301e1c3d8a4f98a35b1aa95b97035daef02886fd8efd7788a2fa3ced2ec7105988bfd8dce61eedd + version: 8.1.5 + resolution: "@types/sinonjs__fake-timers@npm:8.1.5" + checksum: 7e3c08f6c13df44f3ea7d9a5155ddf77e3f7314c156fa1c5a829a4f3763bafe2f75b1283b887f06e6b4296996a2f299b70f64ff82625f9af5885436e2524d10c languageName: node linkType: hard @@ -3785,86 +3635,86 @@ __metadata: linkType: hard "@types/stack-utils@npm:^2.0.0": - version: 2.0.1 - resolution: "@types/stack-utils@npm:2.0.1" - checksum: 205fdbe3326b7046d7eaf5e494d8084f2659086a266f3f9cf00bccc549c8e36e407f88168ad4383c8b07099957ad669f75f2532ed4bc70be2b037330f7bae019 + version: 2.0.3 + resolution: "@types/stack-utils@npm:2.0.3" + checksum: 72576cc1522090fe497337c2b99d9838e320659ac57fa5560fcbdcbafcf5d0216c6b3a0a8a4ee4fdb3b1f5e3420aa4f6223ab57b82fef3578bec3206425c6cf5 languageName: node linkType: hard "@types/superagent@npm:*": - version: 4.1.18 - resolution: "@types/superagent@npm:4.1.18" + version: 8.1.3 + resolution: "@types/superagent@npm:8.1.3" dependencies: - "@types/cookiejar": "*" + "@types/cookiejar": ^2.1.5 + "@types/methods": ^1.1.4 "@types/node": "*" - checksum: 4e50cb41e6f0ac55917dddae4665e5251ce0ec086f89172c8b53432c0c3ee026b9243ba4c994aa2702720d7c288fd7ae77f241f9fb9fb15d2d7c4b6bc2ee7079 + checksum: 284307f88986b733fc22ede29d0660f26e7e2d4f8ee7bf772544633404e5d21026bfb202b7c3aedab1bedf6e9276e96fbebc6e07cc2163e6729d5a36dc842215 languageName: node linkType: hard "@types/supertest@npm:^2.0.12": - version: 2.0.12 - resolution: "@types/supertest@npm:2.0.12" + version: 2.0.16 + resolution: "@types/supertest@npm:2.0.16" dependencies: "@types/superagent": "*" - checksum: f0e2b44f86bec2f708d6a3d0cb209055b487922040773049b0f8c6b557af52d4b5fa904e17dfaa4ce6e610172206bbec7b62420d158fa57b6ffc2de37b1730d3 + checksum: 2fc998ea698e0467cdbe3bea0ebce2027ea3a45a13e51a6cecb0435f44b486faecf99c34d8702d2d7fe033e6e09fdd2b374af52ecc8d0c69a1deec66b8c0dd52 languageName: node linkType: hard "@types/triple-beam@npm:^1.3.2": - version: 1.3.2 - resolution: "@types/triple-beam@npm:1.3.2" - checksum: dd7b4a563fb710abc992e5d59eac481bed9e303fada2e276e37b00be31c392e03300ee468e57761e616512872e77935f92472877d0704a19688d15a726cee17b + version: 1.3.5 + resolution: "@types/triple-beam@npm:1.3.5" + checksum: 519b6a1b30d4571965c9706ad5400a200b94e4050feca3e7856e3ea7ac00ec9903e32e9a10e2762d0f7e472d5d03e5f4b29c16c0bd8c1f77c8876c683b2231f1 languageName: node linkType: hard "@types/ws@npm:^8.5.4": - version: 8.5.5 - resolution: "@types/ws@npm:8.5.5" + version: 8.5.10 + resolution: "@types/ws@npm:8.5.10" dependencies: "@types/node": "*" - checksum: d00bf8070e6938e3ccf933010921c6ce78ac3606696ce37a393b27a9a603f7bd93ea64f3c5fa295a2f743575ba9c9a9fdb904af0f5fe2229bf2adf0630386e4a + checksum: 3ec416ea2be24042ebd677932a462cf16d2080393d8d7d0b1b3f5d6eaa4a7387aaf0eefb99193c0bfd29444857cf2e0c3ac89899e130550dc6c14ada8a46d25e languageName: node linkType: hard "@types/yargs-parser@npm:*": - version: 21.0.0 - resolution: "@types/yargs-parser@npm:21.0.0" - checksum: b2f4c8d12ac18a567440379909127cf2cec393daffb73f246d0a25df36ea983b93b7e9e824251f959e9f928cbc7c1aab6728d0a0ff15d6145f66cec2be67d9a2 + version: 21.0.3 + resolution: "@types/yargs-parser@npm:21.0.3" + checksum: ef236c27f9432983e91432d974243e6c4cdae227cb673740320eff32d04d853eed59c92ca6f1142a335cfdc0e17cccafa62e95886a8154ca8891cc2dec4ee6fc languageName: node linkType: hard "@types/yargs@npm:^17.0.8": - version: 17.0.24 - resolution: "@types/yargs@npm:17.0.24" + version: 17.0.32 + resolution: "@types/yargs@npm:17.0.32" dependencies: "@types/yargs-parser": "*" - checksum: 5f3ac4dc4f6e211c1627340160fbe2fd247ceba002190da6cf9155af1798450501d628c9165a183f30a224fc68fa5e700490d740ff4c73e2cdef95bc4e8ba7bf + checksum: 4505bdebe8716ff383640c6e928f855b5d337cb3c68c81f7249fc6b983d0aa48de3eee26062b84f37e0d75a5797bc745e0c6e76f42f81771252a758c638f36ba languageName: node linkType: hard "@types/yauzl@npm:^2.9.1": - version: 2.10.0 - resolution: "@types/yauzl@npm:2.10.0" + version: 2.10.3 + resolution: "@types/yauzl@npm:2.10.3" dependencies: "@types/node": "*" - checksum: 55d27ae5d346ea260e40121675c24e112ef0247649073848e5d4e03182713ae4ec8142b98f61a1c6cbe7d3b72fa99bbadb65d8b01873e5e605cdc30f1ff70ef2 + checksum: 5ee966ea7bd6b2802f31ad4281c92c4c0b6dfa593c378a2582c58541fa113bec3d70eb0696b34ad95e8e6861a884cba6c3e351285816693ed176222f840a8c08 languageName: node linkType: hard "@typescript-eslint/eslint-plugin@npm:^6.2.1": - version: 6.2.1 - resolution: "@typescript-eslint/eslint-plugin@npm:6.2.1" + version: 6.19.1 + resolution: "@typescript-eslint/eslint-plugin@npm:6.19.1" dependencies: "@eslint-community/regexpp": ^4.5.1 - "@typescript-eslint/scope-manager": 6.2.1 - "@typescript-eslint/type-utils": 6.2.1 - "@typescript-eslint/utils": 6.2.1 - "@typescript-eslint/visitor-keys": 6.2.1 + "@typescript-eslint/scope-manager": 6.19.1 + "@typescript-eslint/type-utils": 6.19.1 + "@typescript-eslint/utils": 6.19.1 + "@typescript-eslint/visitor-keys": 6.19.1 debug: ^4.3.4 graphemer: ^1.4.0 ignore: ^5.2.4 natural-compare: ^1.4.0 - natural-compare-lite: ^1.4.0 semver: ^7.5.4 ts-api-utils: ^1.0.1 peerDependencies: @@ -3873,44 +3723,44 @@ __metadata: peerDependenciesMeta: typescript: optional: true - checksum: e73f3fe36519d895037d223f3ddf200b97e17bcde9390984118c38733add1edf996357c809ec2db92cec61bc7c9e5a3d9a583e0d0f92fa9c3919b68716a27b37 + checksum: ad04000cd6c15d864ff92655baa3aec99bb0ccf4714fedd145fedde60a27590a5feafe480beb2f0f3864b416098bde1e9431bada7480eb7ca4efad891e1d2f6f languageName: node linkType: hard "@typescript-eslint/parser@npm:^6.2.1": - version: 6.2.1 - resolution: "@typescript-eslint/parser@npm:6.2.1" + version: 6.19.1 + resolution: "@typescript-eslint/parser@npm:6.19.1" dependencies: - "@typescript-eslint/scope-manager": 6.2.1 - "@typescript-eslint/types": 6.2.1 - "@typescript-eslint/typescript-estree": 6.2.1 - "@typescript-eslint/visitor-keys": 6.2.1 + "@typescript-eslint/scope-manager": 6.19.1 + "@typescript-eslint/types": 6.19.1 + "@typescript-eslint/typescript-estree": 6.19.1 + "@typescript-eslint/visitor-keys": 6.19.1 debug: ^4.3.4 peerDependencies: eslint: ^7.0.0 || ^8.0.0 peerDependenciesMeta: typescript: optional: true - checksum: cf4768cbfc696ce1d4b15ae55b3d2b52761e91a4a80e738cf3a75c501c2257d735cd6e462567965069d0d693a8cf5463ab9e8b97c36c6ed1fccd3c1c09855bdb + checksum: cd29619da08a2d9b7123ba4d8240989c747f8e0d5672179d8b147e413ee1334d1fa48570b0c37cf0ae4e26a275fd2d268cbe702c6fed639d3331abbb3292570a languageName: node linkType: hard -"@typescript-eslint/scope-manager@npm:6.2.1": - version: 6.2.1 - resolution: "@typescript-eslint/scope-manager@npm:6.2.1" +"@typescript-eslint/scope-manager@npm:6.19.1": + version: 6.19.1 + resolution: "@typescript-eslint/scope-manager@npm:6.19.1" dependencies: - "@typescript-eslint/types": 6.2.1 - "@typescript-eslint/visitor-keys": 6.2.1 - checksum: 3bb461678c7e729895c5ac16781ec7d66efc6ffa944bb49693ce8e9560f9a6cac70929157c0fc0875b2829ae19a5cdabb97973ddcfb7e81c16e22cdd5d39e3fd + "@typescript-eslint/types": 6.19.1 + "@typescript-eslint/visitor-keys": 6.19.1 + checksum: 848cdebc16a3803e8a6d6035a7067605309a652bb2425f475f755b5ace4d80d2c17c8c8901f0f4759556da8d0a5b71024d472b85c3f3c70d0e6dcfe2a972ef35 languageName: node linkType: hard -"@typescript-eslint/type-utils@npm:6.2.1": - version: 6.2.1 - resolution: "@typescript-eslint/type-utils@npm:6.2.1" +"@typescript-eslint/type-utils@npm:6.19.1": + version: 6.19.1 + resolution: "@typescript-eslint/type-utils@npm:6.19.1" dependencies: - "@typescript-eslint/typescript-estree": 6.2.1 - "@typescript-eslint/utils": 6.2.1 + "@typescript-eslint/typescript-estree": 6.19.1 + "@typescript-eslint/utils": 6.19.1 debug: ^4.3.4 ts-api-utils: ^1.0.1 peerDependencies: @@ -3918,7 +3768,7 @@ __metadata: peerDependenciesMeta: typescript: optional: true - checksum: 7f8d80f03e6ddc1838307a2a4df61dc4bd8400efb9dcc7316063ae293fce54afad238404a0c25cd2cdaceee73ae514f254b850bd7ff11e2def700d5d6b90af05 + checksum: eab1a30f8d85f7c6e2545de5963fbec2f3bb91913d59623069b4b0db372a671ab048c7018376fc853c3af06ea39417f3e7b27dd665027dd812347a5e64cecd77 languageName: node linkType: hard @@ -3936,28 +3786,29 @@ __metadata: languageName: node linkType: hard -"@typescript-eslint/types@npm:6.2.1": - version: 6.2.1 - resolution: "@typescript-eslint/types@npm:6.2.1" - checksum: 388d32f15a9db8ad5d80794caf9ab280d6e5a428efdf4f6a6dfc4069afe4d19da32d628acf638e4c5b92ee77a9a18eecf728a778a3b91cc8a24484af579fc9cf +"@typescript-eslint/types@npm:6.19.1": + version: 6.19.1 + resolution: "@typescript-eslint/types@npm:6.19.1" + checksum: 598ce222b59c20432d06f60703d0c2dd16d9b2151569c192852136c57b8188e3ef6ef9fddaa2c136c9a756fcc7d873c0e29ec41cfd340564842287ef7b4571cd languageName: node linkType: hard -"@typescript-eslint/typescript-estree@npm:6.2.1": - version: 6.2.1 - resolution: "@typescript-eslint/typescript-estree@npm:6.2.1" +"@typescript-eslint/typescript-estree@npm:6.19.1": + version: 6.19.1 + resolution: "@typescript-eslint/typescript-estree@npm:6.19.1" dependencies: - "@typescript-eslint/types": 6.2.1 - "@typescript-eslint/visitor-keys": 6.2.1 + "@typescript-eslint/types": 6.19.1 + "@typescript-eslint/visitor-keys": 6.19.1 debug: ^4.3.4 globby: ^11.1.0 is-glob: ^4.0.3 + minimatch: 9.0.3 semver: ^7.5.4 ts-api-utils: ^1.0.1 peerDependenciesMeta: typescript: optional: true - checksum: 3d9beeb5e36b8827de5c160ed8e5c111dd66ca00671b183409b051e242b291480679b900bb74aaf4895dcae49497037567d3fcbbe67fa9930786ddd01c685f04 + checksum: fb71a14aeee0468780219c5b8d39075f85d360b04ccd0ee88f4f0a615d2c232a6d3016e36d8c6eda2d9dfda86b4f4cc2c3d7582940fb29d33c7cf305e124d4e2 languageName: node linkType: hard @@ -3997,20 +3848,20 @@ __metadata: languageName: node linkType: hard -"@typescript-eslint/utils@npm:6.2.1": - version: 6.2.1 - resolution: "@typescript-eslint/utils@npm:6.2.1" +"@typescript-eslint/utils@npm:6.19.1": + version: 6.19.1 + resolution: "@typescript-eslint/utils@npm:6.19.1" dependencies: "@eslint-community/eslint-utils": ^4.4.0 "@types/json-schema": ^7.0.12 "@types/semver": ^7.5.0 - "@typescript-eslint/scope-manager": 6.2.1 - "@typescript-eslint/types": 6.2.1 - "@typescript-eslint/typescript-estree": 6.2.1 + "@typescript-eslint/scope-manager": 6.19.1 + "@typescript-eslint/types": 6.19.1 + "@typescript-eslint/typescript-estree": 6.19.1 semver: ^7.5.4 peerDependencies: eslint: ^7.0.0 || ^8.0.0 - checksum: d16356a633f39d988a9af159da15e28c6a28fa47abce372061c79cf186d193d148e1c32862c9702ff87e2a06f7a2f82773e4b56320a39f432f4b1a989f8005ad + checksum: fe72e75c3ea17a85772b83f148555ea94ff5d55d13586f3fc038833197a74f8071e14c2bbf1781c40eec20005f052f4be2513a725eea82a15da3cb9af3046c70 languageName: node linkType: hard @@ -4034,13 +3885,20 @@ __metadata: languageName: node linkType: hard -"@typescript-eslint/visitor-keys@npm:6.2.1": - version: 6.2.1 - resolution: "@typescript-eslint/visitor-keys@npm:6.2.1" +"@typescript-eslint/visitor-keys@npm:6.19.1": + version: 6.19.1 + resolution: "@typescript-eslint/visitor-keys@npm:6.19.1" dependencies: - "@typescript-eslint/types": 6.2.1 + "@typescript-eslint/types": 6.19.1 eslint-visitor-keys: ^3.4.1 - checksum: c05a1c45129f2cf9a8c49dadc3da10b675232e59b69dfe9fdc0bfb45d3be077ceff78097baf50e502dab3e71ce9fd799d2015e356a4be2787ee10c6c7a44ea8a + checksum: bdf057a42e776970a89cdd568e493e3ea7ec085544d8f318d33084da63c3395ad2c0fb9cef9f61ceeca41f5dab54ab064b7078fe596889005e412ec74d2d1ae4 + languageName: node + linkType: hard + +"@ungap/structured-clone@npm:^1.2.0": + version: 1.2.0 + resolution: "@ungap/structured-clone@npm:1.2.0" + checksum: 4f656b7b4672f2ce6e272f2427d8b0824ed11546a601d8d5412b9d7704e83db38a8d9f402ecdf2b9063fc164af842ad0ec4a55819f621ed7e7ea4d1efcc74524 languageName: node linkType: hard @@ -4254,10 +4112,10 @@ __metadata: languageName: node linkType: hard -"abbrev@npm:^1.0.0": - version: 1.1.1 - resolution: "abbrev@npm:1.1.1" - checksum: a4a97ec07d7ea112c517036882b2ac22f3109b7b19077dc656316d07d308438aac28e4d9746dc4d84bf6b1e75b4a7b0a5f3cb30592419f128ca9a8cee3bcfa17 +"abbrev@npm:^2.0.0": + version: 2.0.0 + resolution: "abbrev@npm:2.0.0" + checksum: 0e994ad2aa6575f94670d8a2149afe94465de9cedaaaac364e7fb43a40c3691c980ff74899f682f4ca58fa96b4cbd7421a015d3a6defe43a442117d7821a2f36 languageName: node linkType: hard @@ -4276,6 +4134,21 @@ __metadata: languageName: node linkType: hard +"abitype@npm:0.9.8": + version: 0.9.8 + resolution: "abitype@npm:0.9.8" + peerDependencies: + typescript: ">=5.0.4" + zod: ^3 >=3.19.1 + peerDependenciesMeta: + typescript: + optional: true + zod: + optional: true + checksum: d7d887f29d6821e3f7a400de9620511b80ead3f85c5c87308aaec97965d3493e6687ed816e88722b4f512249bd66dee9e69231b49af0e1db8f69400a62c87cf6 + languageName: node + linkType: hard + "abitype@npm:^0.8.11": version: 0.8.11 resolution: "abitype@npm:0.8.11" @@ -4342,27 +4215,18 @@ __metadata: linkType: hard "acorn-walk@npm:^8.1.1": - version: 8.2.0 - resolution: "acorn-walk@npm:8.2.0" - checksum: 1715e76c01dd7b2d4ca472f9c58968516a4899378a63ad5b6c2d668bba8da21a71976c14ec5f5b75f887b6317c4ae0b897ab141c831d741dc76024d8745f1ad1 + version: 8.3.2 + resolution: "acorn-walk@npm:8.3.2" + checksum: 3626b9d26a37b1b427796feaa5261faf712307a8920392c8dce9a5739fb31077667f4ad2ec71c7ac6aaf9f61f04a9d3d67ff56f459587206fc04aa31c27ef392 languageName: node linkType: hard "acorn@npm:^8.4.1, acorn@npm:^8.7.1, acorn@npm:^8.8.2, acorn@npm:^8.9.0": - version: 8.10.0 - resolution: "acorn@npm:8.10.0" + version: 8.11.3 + resolution: "acorn@npm:8.11.3" bin: acorn: bin/acorn - checksum: 538ba38af0cc9e5ef983aee196c4b8b4d87c0c94532334fa7e065b2c8a1f85863467bb774231aae91613fcda5e68740c15d97b1967ae3394d20faddddd8af61d - languageName: node - linkType: hard - -"agent-base@npm:6, agent-base@npm:^6.0.2": - version: 6.0.2 - resolution: "agent-base@npm:6.0.2" - dependencies: - debug: 4 - checksum: f52b6872cc96fd5f622071b71ef200e01c7c4c454ee68bc9accca90c98cfb39f2810e3e9aa330435835eedc8c23f4f8a15267f67c6e245d2b33757575bdac49d + checksum: 76d8e7d559512566b43ab4aadc374f11f563f0a9e21626dd59cb2888444e9445923ae9f3699972767f18af61df89cd89f5eaaf772d1327b055b45cb829b4a88c languageName: node linkType: hard @@ -4375,17 +4239,6 @@ __metadata: languageName: node linkType: hard -"agentkeepalive@npm:^4.2.1": - version: 4.3.0 - resolution: "agentkeepalive@npm:4.3.0" - dependencies: - debug: ^4.1.0 - depd: ^2.0.0 - humanize-ms: ^1.2.1 - checksum: 982453aa44c11a06826c836025e5162c846e1200adb56f2d075400da7d32d87021b3b0a58768d949d824811f5654223d5a8a3dad120921a2439625eb847c6260 - languageName: node - linkType: hard - "aggregate-error@npm:^3.0.0": version: 3.1.0 resolution: "aggregate-error@npm:3.1.0" @@ -4510,23 +4363,6 @@ __metadata: languageName: node linkType: hard -"aproba@npm:^1.0.3 || ^2.0.0": - version: 2.0.0 - resolution: "aproba@npm:2.0.0" - checksum: 5615cadcfb45289eea63f8afd064ab656006361020e1735112e346593856f87435e02d8dcc7ff0d11928bc7d425f27bc7c2a84f6c0b35ab0ff659c814c138a24 - languageName: node - linkType: hard - -"are-we-there-yet@npm:^3.0.0": - version: 3.0.1 - resolution: "are-we-there-yet@npm:3.0.1" - dependencies: - delegates: ^1.0.0 - readable-stream: ^3.6.0 - checksum: 52590c24860fa7173bedeb69a4c05fb573473e860197f618b9a28432ee4379049336727ae3a1f9c4cb083114601c1140cee578376164d0e651217a9843f9fe83 - languageName: node - linkType: hard - "arg@npm:^4.1.0": version: 4.1.3 resolution: "arg@npm:4.1.3" @@ -4567,16 +4403,16 @@ __metadata: languageName: node linkType: hard -"array-includes@npm:^3.1.6": - version: 3.1.6 - resolution: "array-includes@npm:3.1.6" +"array-includes@npm:^3.1.7": + version: 3.1.7 + resolution: "array-includes@npm:3.1.7" dependencies: call-bind: ^1.0.2 - define-properties: ^1.1.4 - es-abstract: ^1.20.4 - get-intrinsic: ^1.1.3 + define-properties: ^1.2.0 + es-abstract: ^1.22.1 + get-intrinsic: ^1.2.1 is-string: ^1.0.7 - checksum: f22f8cd8ba8a6448d91eebdc69f04e4e55085d09232b5216ee2d476dab3ef59984e8d1889e662c6a0ed939dcb1b57fd05b2c0209c3370942fc41b752c82a2ca5 + checksum: 06f9e4598fac12a919f7c59a3f04f010ea07f0b7f0585465ed12ef528a60e45f374e79d1bddbb34cdd4338357d00023ddbd0ac18b0be36964f5e726e8965d7fc languageName: node linkType: hard @@ -4587,54 +4423,55 @@ __metadata: languageName: node linkType: hard -"array.prototype.findlastindex@npm:^1.2.2": - version: 1.2.2 - resolution: "array.prototype.findlastindex@npm:1.2.2" +"array.prototype.findlastindex@npm:^1.2.3": + version: 1.2.3 + resolution: "array.prototype.findlastindex@npm:1.2.3" dependencies: call-bind: ^1.0.2 - define-properties: ^1.1.4 - es-abstract: ^1.20.4 + define-properties: ^1.2.0 + es-abstract: ^1.22.1 es-shim-unscopables: ^1.0.0 - get-intrinsic: ^1.1.3 - checksum: 8a166359f69a2a751c843f26b9c8cd03d0dc396a92cdcb85f4126b5f1cecdae5b2c0c616a71ea8aff026bde68165b44950b3664404bb73db0673e288495ba264 + get-intrinsic: ^1.2.1 + checksum: 31f35d7b370c84db56484618132041a9af401b338f51899c2e78ef7690fbba5909ee7ca3c59a7192085b328cc0c68c6fd1f6d1553db01a689a589ae510f3966e languageName: node linkType: hard -"array.prototype.flat@npm:^1.3.1": - version: 1.3.1 - resolution: "array.prototype.flat@npm:1.3.1" +"array.prototype.flat@npm:^1.3.2": + version: 1.3.2 + resolution: "array.prototype.flat@npm:1.3.2" dependencies: call-bind: ^1.0.2 - define-properties: ^1.1.4 - es-abstract: ^1.20.4 + define-properties: ^1.2.0 + es-abstract: ^1.22.1 es-shim-unscopables: ^1.0.0 - checksum: 5a8415949df79bf6e01afd7e8839bbde5a3581300e8ad5d8449dea52639e9e59b26a467665622783697917b43bf39940a6e621877c7dd9b3d1c1f97484b9b88b + checksum: 5d6b4bf102065fb3f43764bfff6feb3295d372ce89591e6005df3d0ce388527a9f03c909af6f2a973969a4d178ab232ffc9236654149173e0e187ec3a1a6b87b languageName: node linkType: hard -"array.prototype.flatmap@npm:^1.3.1": - version: 1.3.1 - resolution: "array.prototype.flatmap@npm:1.3.1" +"array.prototype.flatmap@npm:^1.3.2": + version: 1.3.2 + resolution: "array.prototype.flatmap@npm:1.3.2" dependencies: call-bind: ^1.0.2 - define-properties: ^1.1.4 - es-abstract: ^1.20.4 + define-properties: ^1.2.0 + es-abstract: ^1.22.1 es-shim-unscopables: ^1.0.0 - checksum: 8c1c43a4995f12cf12523436da28515184c753807b3f0bc2ca6c075f71c470b099e2090cc67dba8e5280958fea401c1d0c59e1db0143272aef6cd1103921a987 + checksum: ce09fe21dc0bcd4f30271f8144083aa8c13d4639074d6c8dc82054b847c7fc9a0c97f857491f4da19d4003e507172a78f4bcd12903098adac8b9cd374f734be3 languageName: node linkType: hard -"arraybuffer.prototype.slice@npm:^1.0.1": - version: 1.0.1 - resolution: "arraybuffer.prototype.slice@npm:1.0.1" +"arraybuffer.prototype.slice@npm:^1.0.2": + version: 1.0.2 + resolution: "arraybuffer.prototype.slice@npm:1.0.2" dependencies: array-buffer-byte-length: ^1.0.0 call-bind: ^1.0.2 define-properties: ^1.2.0 + es-abstract: ^1.22.1 get-intrinsic: ^1.2.1 is-array-buffer: ^3.0.2 is-shared-array-buffer: ^1.0.2 - checksum: e3e9b2a3e988ebfeddce4c7e8f69df730c9e48cb04b0d40ff0874ce3d86b3d1339dd520ffde5e39c02610bc172ecfbd4bc93324b1cabd9554c44a56b131ce0ce + checksum: c200faf437786f5b2c80d4564ff5481c886a16dee642ef02abdc7306c7edd523d1f01d1dd12b769c7eb42ac9bc53874510db19a92a2c035c0f6696172aafa5d3 languageName: node linkType: hard @@ -4695,9 +4532,9 @@ __metadata: linkType: hard "async@npm:^3.2.3": - version: 3.2.4 - resolution: "async@npm:3.2.4" - checksum: 43d07459a4e1d09b84a20772414aa684ff4de085cbcaec6eea3c7a8f8150e8c62aa6cd4e699fe8ee93c3a5b324e777d34642531875a0817a35697522c1b02e89 + version: 3.2.5 + resolution: "async@npm:3.2.5" + checksum: 5ec77f1312301dee02d62140a6b1f7ee0edd2a0f983b6fd2b0849b969f245225b990b47b8243e7b9ad16451a53e7f68e753700385b706198ced888beedba3af4 languageName: node linkType: hard @@ -4722,20 +4559,20 @@ __metadata: languageName: node linkType: hard -"babel-jest@npm:^29.6.2": - version: 29.6.2 - resolution: "babel-jest@npm:29.6.2" +"babel-jest@npm:^29.7.0": + version: 29.7.0 + resolution: "babel-jest@npm:29.7.0" dependencies: - "@jest/transform": ^29.6.2 + "@jest/transform": ^29.7.0 "@types/babel__core": ^7.1.14 babel-plugin-istanbul: ^6.1.1 - babel-preset-jest: ^29.5.0 + babel-preset-jest: ^29.6.3 chalk: ^4.0.0 graceful-fs: ^4.2.9 slash: ^3.0.0 peerDependencies: "@babel/core": ^7.8.0 - checksum: 3936b5d6ed6f08670c830ed919e38a4a593d0643b8e30fdeb16f4588b262ea5255fb96fd1849c02fba0b082ecfa4e788ce9a128ad1b9e654d46aac09c3a55504 + checksum: ee6f8e0495afee07cac5e4ee167be705c711a8cc8a737e05a587a131fdae2b3c8f9aa55dfd4d9c03009ac2d27f2de63d8ba96d3e8460da4d00e8af19ef9a83f7 languageName: node linkType: hard @@ -4752,15 +4589,15 @@ __metadata: languageName: node linkType: hard -"babel-plugin-jest-hoist@npm:^29.5.0": - version: 29.5.0 - resolution: "babel-plugin-jest-hoist@npm:29.5.0" +"babel-plugin-jest-hoist@npm:^29.6.3": + version: 29.6.3 + resolution: "babel-plugin-jest-hoist@npm:29.6.3" dependencies: "@babel/template": ^7.3.3 "@babel/types": ^7.3.3 "@types/babel__core": ^7.1.14 "@types/babel__traverse": ^7.0.6 - checksum: 099b5254073b6bc985b6d2d045ad26fb8ed30ff8ae6404c4fe8ee7cd0e98a820f69e3dfb871c7c65aae0f4b65af77046244c07bb92d49ef9005c90eedf681539 + checksum: 51250f22815a7318f17214a9d44650ba89551e6d4f47a2dc259128428324b52f5a73979d010cefd921fd5a720d8c1d55ad74ff601cd94c7bd44d5f6292fde2d1 languageName: node linkType: hard @@ -4786,15 +4623,15 @@ __metadata: languageName: node linkType: hard -"babel-preset-jest@npm:^29.5.0": - version: 29.5.0 - resolution: "babel-preset-jest@npm:29.5.0" +"babel-preset-jest@npm:^29.6.3": + version: 29.6.3 + resolution: "babel-preset-jest@npm:29.6.3" dependencies: - babel-plugin-jest-hoist: ^29.5.0 + babel-plugin-jest-hoist: ^29.6.3 babel-preset-current-node-syntax: ^1.0.0 peerDependencies: "@babel/core": ^7.0.0 - checksum: 5566ca2762766c9319b4973d018d2fa08c0fcf6415c72cc54f4c8e7199e851ea8f5e6c6730f03ed7ed44fc8beefa959dd15911f2647dee47c615ff4faeddb1ad + checksum: aa4ff2a8a728d9d698ed521e3461a109a1e66202b13d3494e41eea30729a5e7cc03b3a2d56c594423a135429c37bf63a9fa8b0b9ce275298be3095a88c69f6fb languageName: node linkType: hard @@ -4813,9 +4650,9 @@ __metadata: linkType: hard "basic-ftp@npm:^5.0.2": - version: 5.0.3 - resolution: "basic-ftp@npm:5.0.3" - checksum: 8b04e88eb85a64de9311721bb0707c9cd70453eefdd854cab85438e6f46fb6c597ddad57ed1acf0a9ede3c677b14e657f51051688a5f23d6f3ea7b5d9073b850 + version: 5.0.4 + resolution: "basic-ftp@npm:5.0.4" + checksum: 57725f24debd8c1b36f9bad1bfee39c5d9f5997f32a23e5c957389dcc64373a13b41711e5723b4a3b616a93530b345686119f480c27a115b2fde944c1652ceb1 languageName: node linkType: hard @@ -4829,13 +4666,6 @@ __metadata: languageName: node linkType: hard -"big-integer@npm:^1.6.44": - version: 1.6.51 - resolution: "big-integer@npm:1.6.51" - checksum: 3d444173d1b2e20747e2c175568bedeebd8315b0637ea95d75fd27830d3b8e8ba36c6af40374f36bdaea7b5de376dcada1b07587cb2a79a928fccdb6e6e3c518 - languageName: node - linkType: hard - "bl@npm:^4.1.0": version: 4.1.0 resolution: "bl@npm:4.1.0" @@ -4854,22 +4684,13 @@ __metadata: languageName: node linkType: hard -"bn.js@npm:^5.0.0, bn.js@npm:^5.1.1, bn.js@npm:^5.2.1": +"bn.js@npm:^5.0.0, bn.js@npm:^5.2.1": version: 5.2.1 resolution: "bn.js@npm:5.2.1" checksum: 3dd8c8d38055fedfa95c1d5fc3c99f8dd547b36287b37768db0abab3c239711f88ff58d18d155dd8ad902b0b0cee973747b7ae20ea12a09473272b0201c9edd3 languageName: node linkType: hard -"bplist-parser@npm:^0.2.0": - version: 0.2.0 - resolution: "bplist-parser@npm:0.2.0" - dependencies: - big-integer: ^1.6.44 - checksum: d5339dd16afc51de6c88f88f58a45b72ed6a06aa31f5557d09877575f220b7c1d3fbe375da0b62e6a10d4b8ed80523567e351f24014f5bc886ad523758142cdd - languageName: node - linkType: hard - "brace-expansion@npm:^1.1.7": version: 1.1.11 resolution: "brace-expansion@npm:1.1.11" @@ -4942,7 +4763,7 @@ __metadata: languageName: node linkType: hard -"browserify-rsa@npm:^4.0.0, browserify-rsa@npm:^4.0.1": +"browserify-rsa@npm:^4.0.0, browserify-rsa@npm:^4.1.0": version: 4.1.0 resolution: "browserify-rsa@npm:4.1.0" dependencies: @@ -4953,33 +4774,33 @@ __metadata: linkType: hard "browserify-sign@npm:^4.0.0": - version: 4.2.1 - resolution: "browserify-sign@npm:4.2.1" + version: 4.2.2 + resolution: "browserify-sign@npm:4.2.2" dependencies: - bn.js: ^5.1.1 - browserify-rsa: ^4.0.1 + bn.js: ^5.2.1 + browserify-rsa: ^4.1.0 create-hash: ^1.2.0 create-hmac: ^1.1.7 - elliptic: ^6.5.3 + elliptic: ^6.5.4 inherits: ^2.0.4 - parse-asn1: ^5.1.5 - readable-stream: ^3.6.0 - safe-buffer: ^5.2.0 - checksum: 0221f190e3f5b2d40183fa51621be7e838d9caa329fe1ba773406b7637855f37b30f5d83e52ff8f244ed12ffe6278dd9983638609ed88c841ce547e603855707 + parse-asn1: ^5.1.6 + readable-stream: ^3.6.2 + safe-buffer: ^5.2.1 + checksum: b622730c0fc183328c3a1c9fdaaaa5118821ed6822b266fa6b0375db7e20061ebec87301d61931d79b9da9a96ada1cab317fce3c68f233e5e93ed02dbb35544c languageName: node linkType: hard -"browserslist@npm:^4.14.5, browserslist@npm:^4.21.9": - version: 4.21.10 - resolution: "browserslist@npm:4.21.10" +"browserslist@npm:^4.21.10, browserslist@npm:^4.22.2": + version: 4.22.2 + resolution: "browserslist@npm:4.22.2" dependencies: - caniuse-lite: ^1.0.30001517 - electron-to-chromium: ^1.4.477 - node-releases: ^2.0.13 - update-browserslist-db: ^1.0.11 + caniuse-lite: ^1.0.30001565 + electron-to-chromium: ^1.4.601 + node-releases: ^2.0.14 + update-browserslist-db: ^1.0.13 bin: browserslist: cli.js - checksum: 1e27c0f111a35d1dd0e8fc2c61781b0daefabc2c9471b0b10537ce54843014bceb2a1ce4571af1a82b2bf1e6e6e05d38865916689a158f03bc2c7a4ec2577db8 + checksum: 33ddfcd9145220099a7a1ac533cecfe5b7548ffeb29b313e1b57be6459000a1f8fa67e781cf4abee97268ac594d44134fcc4a6b2b4750ceddc9796e3a22076d9 languageName: node linkType: hard @@ -5042,33 +4863,6 @@ __metadata: languageName: node linkType: hard -"bundle-name@npm:^3.0.0": - version: 3.0.0 - resolution: "bundle-name@npm:3.0.0" - dependencies: - run-applescript: ^5.0.0 - checksum: edf2b1fbe6096ed32e7566947ace2ea937ee427391744d7510a2880c4b9a5b3543d3f6c551236a29e5c87d3195f8e2912516290e638c15bcbede7b37cc375615 - languageName: node - linkType: hard - -"busboy@npm:^1.6.0": - version: 1.6.0 - resolution: "busboy@npm:1.6.0" - dependencies: - streamsearch: ^1.1.0 - checksum: 32801e2c0164e12106bf236291a00795c3c4e4b709ae02132883fe8478ba2ae23743b11c5735a0aae8afe65ac4b6ca4568b91f0d9fed1fdbc32ede824a73746e - languageName: node - linkType: hard - -"byte-access@npm:^1.0.0, byte-access@npm:^1.0.1": - version: 1.0.1 - resolution: "byte-access@npm:1.0.1" - dependencies: - uint8arraylist: ^2.0.0 - checksum: 12d5350d9fa6108da80844f5b8c6c80f54bc037cc90f914d81e3a27e374d6c5aac1ba875a8dc2da6b00c6d65c1726bde83a7d57914746cd6d52ff9939a5b21fa - languageName: node - linkType: hard - "bytes@npm:3.1.2, bytes@npm:^3.1.2": version: 3.1.2 resolution: "bytes@npm:3.1.2" @@ -5076,23 +4870,23 @@ __metadata: languageName: node linkType: hard -"cacache@npm:^17.0.0": - version: 17.1.3 - resolution: "cacache@npm:17.1.3" +"cacache@npm:^18.0.0": + version: 18.0.2 + resolution: "cacache@npm:18.0.2" dependencies: "@npmcli/fs": ^3.1.0 fs-minipass: ^3.0.0 glob: ^10.2.2 - lru-cache: ^7.7.1 - minipass: ^5.0.0 - minipass-collect: ^1.0.2 + lru-cache: ^10.0.1 + minipass: ^7.0.3 + minipass-collect: ^2.0.1 minipass-flush: ^1.0.5 minipass-pipeline: ^1.2.4 p-map: ^4.0.0 ssri: ^10.0.0 tar: ^6.1.11 unique-filename: ^3.0.0 - checksum: 385756781e1e21af089160d89d7462b7ed9883c978e848c7075b90b73cb823680e66092d61513050164588387d2ca87dd6d910e28d64bc13a9ac82cd8580c796 + checksum: 0250df80e1ad0c828c956744850c5f742c24244e9deb5b7dc81bca90f8c10e011e132ecc58b64497cc1cad9a98968676147fb6575f4f94722f7619757b17a11b languageName: node linkType: hard @@ -5106,13 +4900,14 @@ __metadata: languageName: node linkType: hard -"call-bind@npm:^1.0.0, call-bind@npm:^1.0.2": - version: 1.0.2 - resolution: "call-bind@npm:1.0.2" +"call-bind@npm:^1.0.0, call-bind@npm:^1.0.2, call-bind@npm:^1.0.4, call-bind@npm:^1.0.5": + version: 1.0.5 + resolution: "call-bind@npm:1.0.5" dependencies: - function-bind: ^1.1.1 - get-intrinsic: ^1.0.2 - checksum: f8e31de9d19988a4b80f3e704788c4a2d6b6f3d17cfec4f57dc29ced450c53a49270dc66bf0fbd693329ee948dd33e6c90a329519aef17474a4d961e8d6426b0 + function-bind: ^1.1.2 + get-intrinsic: ^1.2.1 + set-function-length: ^1.1.1 + checksum: 449e83ecbd4ba48e7eaac5af26fea3b50f8f6072202c2dd7c5a6e7a6308f2421abe5e13a3bbd55221087f76320c5e09f25a8fdad1bab2b77c68ae74d92234ea5 languageName: node linkType: hard @@ -5148,10 +4943,10 @@ __metadata: languageName: node linkType: hard -"caniuse-lite@npm:^1.0.30001517": - version: 1.0.30001518 - resolution: "caniuse-lite@npm:1.0.30001518" - checksum: 1b63272f6e3d628ac52e2547e0b75fc477004d4b19b63e34b2c045de7f2e48909f9ea513978fc5a46c4ab5ac6c9daf9cc5e6a78466e90684fb824c3f2105e8f5 +"caniuse-lite@npm:^1.0.30001565": + version: 1.0.30001580 + resolution: "caniuse-lite@npm:1.0.30001580" + checksum: 8d287d1e2a64348365f55562457b52afc8c5e0e8ddf040e18e53395ca165241a697205611dc209dace5c7f7d1d3ee8d566672cce6f9668d658d7930b7a200875 languageName: node linkType: hard @@ -5162,7 +4957,7 @@ __metadata: languageName: node linkType: hard -"chalk@npm:^2.0.0": +"chalk@npm:^2.4.2": version: 2.4.2 resolution: "chalk@npm:2.4.2" dependencies: @@ -5204,22 +4999,22 @@ __metadata: languageName: node linkType: hard -"chromium-bidi@npm:0.4.28": - version: 0.4.28 - resolution: "chromium-bidi@npm:0.4.28" +"chromium-bidi@npm:0.5.4": + version: 0.5.4 + resolution: "chromium-bidi@npm:0.5.4" dependencies: mitt: 3.0.1 urlpattern-polyfill: 9.0.0 peerDependencies: devtools-protocol: "*" - checksum: d8ac0aefcf11ebd744e0b97ecded9dac5c03ab55f46267570cdf1b780ad1e05e8cf6987b65178bda99a1ef1ea1bc59721bda85008283ca5f145912b9e1bf578d + checksum: ac256ae94a9f792a220303379c81a7cff49f5aedd07dd0fcd0cd28c79b204a468ef26a9ef6fa9132d65a477ee624fff6372bebdd51b88cb4e7c7ad2275fb9b77 languageName: node linkType: hard "ci-info@npm:^3.2.0": - version: 3.8.0 - resolution: "ci-info@npm:3.8.0" - checksum: d0a4d3160497cae54294974a7246202244fff031b0a6ea20dd57b10ec510aa17399c41a1b0982142c105f3255aff2173e5c0dd7302ee1b2f28ba3debda375098 + version: 3.9.0 + resolution: "ci-info@npm:3.9.0" + checksum: 6b19dc9b2966d1f8c2041a838217299718f15d6c4b63ae36e4674edd2bee48f780e94761286a56aa59eb305a85fbea4ddffb7630ec063e7ec7e7e5ad42549a87 languageName: node linkType: hard @@ -5257,9 +5052,9 @@ __metadata: linkType: hard "cli-spinners@npm:^2.5.0": - version: 2.9.1 - resolution: "cli-spinners@npm:2.9.1" - checksum: 1780618be58309c469205bc315db697934bac68bce78cd5dfd46248e507a533172d623c7348ecfd904734f597ce0a4e5538684843d2cfb7af485d4466699940c + version: 2.9.2 + resolution: "cli-spinners@npm:2.9.2" + checksum: 1bd588289b28432e4676cb5d40505cfe3e53f2e4e10fbe05c8a710a154d6fe0ce7836844b00d6858f740f2ffe67cdc36e0fce9c7b6a8430e80e6388d5aa4956c languageName: node linkType: hard @@ -5360,15 +5155,6 @@ __metadata: languageName: node linkType: hard -"color-support@npm:^1.1.3": - version: 1.1.3 - resolution: "color-support@npm:1.1.3" - bin: - color-support: bin.js - checksum: 9b7356817670b9a13a26ca5af1c21615463b500783b739b7634a0c2047c16cef4b2865d7576875c31c3cddf9dd621fa19285e628f20198b233a5cfdda6d0793b - languageName: node - linkType: hard - "color@npm:^3.1.3": version: 3.2.1 resolution: "color@npm:3.2.1" @@ -5419,6 +5205,13 @@ __metadata: languageName: node linkType: hard +"commander@npm:^11.1.0": + version: 11.1.0 + resolution: "commander@npm:11.1.0" + checksum: fd1a8557c6b5b622c89ecdfde703242ab7db3b628ea5d1755784c79b8e7cb0d74d65b4a262289b533359cd58e1bfc0bf50245dfbcd2954682a6f367c828b79ef + languageName: node + linkType: hard + "commander@npm:^2.16.0, commander@npm:^2.20.0, commander@npm:^2.20.3, commander@npm:^2.8.1": version: 2.20.3 resolution: "commander@npm:2.20.3" @@ -5467,9 +5260,9 @@ __metadata: linkType: hard "component-emitter@npm:^1.3.0": - version: 1.3.0 - resolution: "component-emitter@npm:1.3.0" - checksum: b3c46de38ffd35c57d1c02488355be9f218e582aec72d72d1b8bbec95a3ac1b38c96cd6e03ff015577e68f550fbb361a3bfdbd9bb248be9390b7b3745691be6b + version: 1.3.1 + resolution: "component-emitter@npm:1.3.1" + checksum: 94550aa462c7bd5a61c1bc480e28554aa306066930152d1b1844a0dd3845d4e5db7e261ddec62ae184913b3e59b55a2ad84093b9d3596a8f17c341514d6c483d languageName: node linkType: hard @@ -5510,8 +5303,8 @@ __metadata: linkType: hard "concurrently@npm:^8.0.1": - version: 8.2.0 - resolution: "concurrently@npm:8.2.0" + version: 8.2.2 + resolution: "concurrently@npm:8.2.2" dependencies: chalk: ^4.1.2 date-fns: ^2.30.0 @@ -5525,14 +5318,7 @@ __metadata: bin: conc: dist/bin/concurrently.js concurrently: dist/bin/concurrently.js - checksum: eafe6a4d9b7fda87f55ea285cfc6acd937a5286ceec8991ab48e6cc27c45fce6a5c6f45e18d7555defa15dc7d7e8941bc5a9d1ceaf182e31441d420e00333434 - languageName: node - linkType: hard - -"console-control-strings@npm:^1.1.0": - version: 1.1.0 - resolution: "console-control-strings@npm:1.1.0" - checksum: 8755d76787f94e6cf79ce4666f0c5519906d7f5b02d4b884cf41e11dcd759ed69c57da0670afd9236d229a46e0f9cf519db0cd829c6dca820bb5a5c3def584ed + checksum: 8ac774df06869773438f1bf91025180c52d5b53139bc86cf47659136c0d97461d0579c515d848d1e945d4e3e0cafe646b2ea18af8d74259b46abddcfe39b2c6c languageName: node linkType: hard @@ -5559,13 +5345,6 @@ __metadata: languageName: node linkType: hard -"convert-source-map@npm:^1.6.0, convert-source-map@npm:^1.7.0": - version: 1.9.0 - resolution: "convert-source-map@npm:1.9.0" - checksum: dc55a1f28ddd0e9485ef13565f8f756b342f9a46c4ae18b843fe3c30c675d058d6a4823eff86d472f187b176f0adf51ea7b69ea38be34be4a63cbbf91b0593c8 - languageName: node - linkType: hard - "convert-source-map@npm:^2.0.0": version: 2.0.0 resolution: "convert-source-map@npm:2.0.0" @@ -5587,13 +5366,13 @@ __metadata: languageName: node linkType: hard -"cookies@npm:~0.8.0": - version: 0.8.0 - resolution: "cookies@npm:0.8.0" +"cookies@npm:~0.9.0": + version: 0.9.1 + resolution: "cookies@npm:0.9.1" dependencies: depd: ~2.0.0 keygrip: ~1.1.0 - checksum: 806055a44f128705265b1bc6a853058da18bf80dea3654ad99be20985b1fa1b14f86c1eef73644aab8071241f8a78acd57202b54c4c5c70769fc694fbb9c4edc + checksum: 213e4d14847b582fbd8a003203d3621a4b9fa792a315c37954e89332d38fac5bcc34ba92ef316ad6d5fe28f0187aaa115927fbbe2080744ad1707a93b4313247 languageName: node linkType: hard @@ -5611,20 +5390,20 @@ __metadata: languageName: node linkType: hard -"cosmiconfig@npm:8.3.6": - version: 8.3.6 - resolution: "cosmiconfig@npm:8.3.6" +"cosmiconfig@npm:9.0.0": + version: 9.0.0 + resolution: "cosmiconfig@npm:9.0.0" dependencies: + env-paths: ^2.2.1 import-fresh: ^3.3.0 js-yaml: ^4.1.0 parse-json: ^5.2.0 - path-type: ^4.0.0 peerDependencies: typescript: ">=4.9.5" peerDependenciesMeta: typescript: optional: true - checksum: dc339ebea427898c9e03bf01b56ba7afbac07fc7d2a2d5a15d6e9c14de98275a9565da949375aee1809591c152c0a3877bb86dbeaf74d5bd5aaa79955ad9e7a0 + checksum: a30c424b53d442ea0bdd24cb1b3d0d8687c8dda4a17ab6afcdc439f8964438801619cdb66e8e79f63b9caa3e6586b60d8bab9ce203e72df6c5e80179b971fe8f languageName: node linkType: hard @@ -5665,6 +5444,23 @@ __metadata: languageName: node linkType: hard +"create-jest@npm:^29.7.0": + version: 29.7.0 + resolution: "create-jest@npm:29.7.0" + dependencies: + "@jest/types": ^29.6.3 + chalk: ^4.0.0 + exit: ^0.1.2 + graceful-fs: ^4.2.9 + jest-config: ^29.7.0 + jest-util: ^29.7.0 + prompts: ^2.0.1 + bin: + create-jest: bin/create-jest.js + checksum: 1427d49458adcd88547ef6fa39041e1fe9033a661293aa8d2c3aa1b4967cb5bf4f0c00436c7a61816558f28ba2ba81a94d5c962e8022ea9a883978fc8e1f2945 + languageName: node + linkType: hard + "create-require@npm:^1.1.0": version: 1.1.1 resolution: "create-require@npm:1.1.1" @@ -5718,31 +5514,31 @@ __metadata: languageName: node linkType: hard -"data-uri-to-buffer@npm:^5.0.1": - version: 5.0.1 - resolution: "data-uri-to-buffer@npm:5.0.1" - checksum: 10958f89c0047b84bd86d572b6b77c9bf238ebe7b55a9a9ab04c90fbf5ab1881783b72e31dc0febdffd30ec914930244f2f728e3629bb8911d922baba129426f +"data-uri-to-buffer@npm:^6.0.0": + version: 6.0.1 + resolution: "data-uri-to-buffer@npm:6.0.1" + checksum: 9140e68c585ae33d950f5943bd476751346c8b789ae80b01a578a33cb8f7f706d1ca7378aff2b1878b2a6d9a8c88c55cc286d88191c8b8ead8255c3c4d934530 languageName: node linkType: hard "datastore-core@npm:^9.0.1": - version: 9.2.0 - resolution: "datastore-core@npm:9.2.0" + version: 9.2.7 + resolution: "datastore-core@npm:9.2.7" dependencies: - "@libp2p/logger": ^2.0.0 + "@libp2p/logger": ^4.0.1 err-code: ^3.0.1 interface-store: ^5.0.0 it-all: ^3.0.1 it-drain: ^3.0.1 it-filter: ^3.0.0 it-map: ^3.0.1 - it-merge: ^3.0.0 + it-merge: ^3.0.1 it-pipe: ^3.0.0 it-pushable: ^3.0.0 it-sort: ^3.0.1 it-take: ^3.0.1 - uint8arrays: ^4.0.2 - checksum: f955cdea823e3e3b9ec0090e7dcfab54b4818156674555e7ca38b0047c98dd42cb14474495cbb3c5e6b721320269354fe0185ea2101f6542fb5149f47d7b2114 + uint8arrays: ^5.0.0 + checksum: 593f40d8e5ccbc80b073b4ec1553e70bc061d4656ca238c10eb47d799ff8a137f19698268b0639cc5a26cf5e036f72946dad0bd20cd37e57f713c9d7a1b32a67 languageName: node linkType: hard @@ -5833,34 +5629,12 @@ __metadata: languageName: node linkType: hard -"default-browser-id@npm:^3.0.0": - version: 3.0.0 - resolution: "default-browser-id@npm:3.0.0" - dependencies: - bplist-parser: ^0.2.0 - untildify: ^4.0.0 - checksum: 279c7ad492542e5556336b6c254a4eaf31b2c63a5433265655ae6e47301197b6cfb15c595a6fdc6463b2ff8e1a1a1ed3cba56038a60e1527ba4ab1628c6b9941 - languageName: node - linkType: hard - -"default-browser@npm:^4.0.0": - version: 4.0.0 - resolution: "default-browser@npm:4.0.0" +"default-gateway@npm:^7.2.2": + version: 7.2.2 + resolution: "default-gateway@npm:7.2.2" dependencies: - bundle-name: ^3.0.0 - default-browser-id: ^3.0.0 execa: ^7.1.1 - titleize: ^3.0.0 - checksum: 40c5af984799042b140300be5639c9742599bda76dc9eba5ac9ad5943c83dd36cebc4471eafcfddf8e0ec817166d5ba89d56f08e66a126c7c7908a179cead1a7 - languageName: node - linkType: hard - -"default-gateway@npm:^6.0.2": - version: 6.0.3 - resolution: "default-gateway@npm:6.0.3" - dependencies: - execa: ^5.0.0 - checksum: 126f8273ecac8ee9ff91ea778e8784f6cd732d77c3157e8c5bdd6ed03651b5291f71446d05bc02d04073b1e67583604db5394ea3cf992ede0088c70ea15b7378 + checksum: eec8a2a338677322bcdbf339bbdede61225f6145eedd0c3d4948deadfc929dc0e04b153b33674873d36efa403f76649aaacbfc728a438ee9f538a28723515478 languageName: node linkType: hard @@ -5883,20 +5657,25 @@ __metadata: languageName: node linkType: hard -"define-lazy-prop@npm:^3.0.0": - version: 3.0.0 - resolution: "define-lazy-prop@npm:3.0.0" - checksum: 54884f94caac0791bf6395a3ec530ce901cf71c47b0196b8754f3fd17edb6c0e80149c1214429d851873bb0d689dbe08dcedbb2306dc45c8534a5934723851b6 +"define-data-property@npm:^1.0.1, define-data-property@npm:^1.1.1": + version: 1.1.1 + resolution: "define-data-property@npm:1.1.1" + dependencies: + get-intrinsic: ^1.2.1 + gopd: ^1.0.1 + has-property-descriptors: ^1.0.0 + checksum: a29855ad3f0630ea82e3c5012c812efa6ca3078d5c2aa8df06b5f597c1cde6f7254692df41945851d903e05a1668607b6d34e778f402b9ff9ffb38111f1a3f0d languageName: node linkType: hard -"define-properties@npm:^1.1.3, define-properties@npm:^1.1.4, define-properties@npm:^1.2.0": - version: 1.2.0 - resolution: "define-properties@npm:1.2.0" +"define-properties@npm:^1.1.3, define-properties@npm:^1.2.0, define-properties@npm:^1.2.1": + version: 1.2.1 + resolution: "define-properties@npm:1.2.1" dependencies: + define-data-property: ^1.0.1 has-property-descriptors: ^1.0.0 object-keys: ^1.1.1 - checksum: e60aee6a19b102df4e2b1f301816804e81ab48bb91f00d0d935f269bf4b3f79c88b39e4f89eaa132890d23267335fd1140dfcd8d5ccd61031a0a2c41a54e33a6 + checksum: b4ccd00597dd46cb2d4a379398f5b19fca84a16f3374e2249201992f36b30f6835949a9429669ee6b41b6e837205a163eadd745e472069e70dfc10f03e5fcc12 languageName: node linkType: hard @@ -6184,10 +5963,10 @@ __metadata: languageName: node linkType: hard -"devtools-protocol@npm:0.0.1179426": - version: 0.0.1179426 - resolution: "devtools-protocol@npm:0.0.1179426" - checksum: 38a091bde42d7d0f8e5e6c7a445db6a56d7b80f21f51de47ed1316123f70b2256aa6fe0d87fbe37bcc4928c0b9245e28a17fb7fbf8d8622156ae36870b26c1ed +"devtools-protocol@npm:0.0.1232444": + version: 0.0.1232444 + resolution: "devtools-protocol@npm:0.0.1232444" + checksum: b421a3c20506d597211d101c3ed4e550db2ca195d59c3371b2e04a8d057f33be60e732bafc80dc53b34546593a7751857321971e235e9ce9f1c5a9523fd8fa20 languageName: node linkType: hard @@ -6201,10 +5980,10 @@ __metadata: languageName: node linkType: hard -"diff-sequences@npm:^29.4.3": - version: 29.4.3 - resolution: "diff-sequences@npm:29.4.3" - checksum: 28b265e04fdddcf7f9f814effe102cc95a9dec0564a579b5aed140edb24fc345c611ca52d76d725a3cab55d3888b915b5e8a4702e0f6058968a90fa5f41fcde7 +"diff-sequences@npm:^29.6.3": + version: 29.6.3 + resolution: "diff-sequences@npm:29.6.3" + checksum: f4914158e1f2276343d98ff5b31fc004e7304f5470bf0f1adb2ac6955d85a531a6458d33e87667f98f6ae52ebd3891bb47d420bb48a5bd8b7a27ee25b20e33aa languageName: node linkType: hard @@ -6235,15 +6014,13 @@ __metadata: languageName: node linkType: hard -"dns-over-http-resolver@npm:^2.1.0": - version: 2.1.1 - resolution: "dns-over-http-resolver@npm:2.1.1" +"dns-over-http-resolver@npm:^3.0.2": + version: 3.0.2 + resolution: "dns-over-http-resolver@npm:3.0.2" dependencies: - debug: ^4.3.1 - native-fetch: ^4.0.2 + debug: ^4.3.4 receptacle: ^1.3.2 - undici: ^5.12.0 - checksum: 153a0f4ef705cd08c9b0c163d654988dbb087eabe44c8ab243481c108af97b52ec6343b7127ed1a6a5705a497e8dc06cd1e6c33fbe5aae3a08e357c1e93fc93e + checksum: 782739450bae3329fdbafcb3c53b497eeb0b3af3bdd8de91977a513d4fe797446597a09d6e042a2c5da99cfc0039c4acac8a7efb93aca5b3424b58f4174d4a4f languageName: node linkType: hard @@ -6266,9 +6043,9 @@ __metadata: linkType: hard "dotenv@npm:^16.0.3": - version: 16.3.1 - resolution: "dotenv@npm:16.3.1" - checksum: 15d75e7279018f4bafd0ee9706593dd14455ddb71b3bcba9c52574460b7ccaf67d5cf8b2c08a5af1a9da6db36c956a04a1192b101ee102a3e0cf8817bbcf3dfd + version: 16.4.1 + resolution: "dotenv@npm:16.4.1" + checksum: a343f0a1d156deef8c60034f797969867af4dbccfacedd4ac15fad04547e7ffe0553b58fc3b27a5837950f0d977e38e9234943fbcec4aeced4e3d044309a76ab languageName: node linkType: hard @@ -6293,10 +6070,10 @@ __metadata: languageName: node linkType: hard -"electron-to-chromium@npm:^1.4.477": - version: 1.4.480 - resolution: "electron-to-chromium@npm:1.4.480" - checksum: 074b9d81dffa6ca182f604326c62a12d22139ae141d6ffabdb3b267f42fac88ba3e69d3614f9e939f65a0f03a343f0c81f702e24064bc54c4f138e02378b1e54 +"electron-to-chromium@npm:^1.4.601": + version: 1.4.647 + resolution: "electron-to-chromium@npm:1.4.647" + checksum: fd79098e08a03025fb64a0608dd20942a7004bb38a8c7fd6d18d8b1767712866a3dae2df7e69ddbc7c627352278cbd07ce1a7368b6c037129e68a042802e108c languageName: node linkType: hard @@ -6378,7 +6155,7 @@ __metadata: languageName: node linkType: hard -"env-paths@npm:^2.2.0": +"env-paths@npm:^2.2.0, env-paths@npm:^2.2.1": version: 2.2.1 resolution: "env-paths@npm:2.2.1" checksum: 65b5df55a8bab92229ab2b40dad3b387fad24613263d103a97f91c9fe43ceb21965cd3392b1ccb5d77088021e525c4e0481adb309625d0cb94ade1d1fb8dc17e @@ -6386,11 +6163,11 @@ __metadata: linkType: hard "envinfo@npm:^7.7.3": - version: 7.10.0 - resolution: "envinfo@npm:7.10.0" + version: 7.11.0 + resolution: "envinfo@npm:7.11.0" bin: envinfo: dist/cli.js - checksum: 05e81a5768c42cbd5c580dc3f274db3401facadd53e9bd52e2aa49dfbb5d8b26f6181c25a6652d79618a6994185bd2b1c137673101690b147f758e4e71d42f7d + checksum: c45a7d20409d5f4cda72483b150d3816b15b434f2944d72c1495d8838bd7c4e7b2f32c12128ffb9b92b5f66f436237b8a525eb3a9a5da2d20013bc4effa28aef languageName: node linkType: hard @@ -6417,25 +6194,25 @@ __metadata: languageName: node linkType: hard -"es-abstract@npm:^1.19.0, es-abstract@npm:^1.20.4, es-abstract@npm:^1.21.2": - version: 1.22.1 - resolution: "es-abstract@npm:1.22.1" +"es-abstract@npm:^1.22.1": + version: 1.22.3 + resolution: "es-abstract@npm:1.22.3" dependencies: array-buffer-byte-length: ^1.0.0 - arraybuffer.prototype.slice: ^1.0.1 + arraybuffer.prototype.slice: ^1.0.2 available-typed-arrays: ^1.0.5 - call-bind: ^1.0.2 + call-bind: ^1.0.5 es-set-tostringtag: ^2.0.1 es-to-primitive: ^1.2.1 - function.prototype.name: ^1.1.5 - get-intrinsic: ^1.2.1 + function.prototype.name: ^1.1.6 + get-intrinsic: ^1.2.2 get-symbol-description: ^1.0.0 globalthis: ^1.0.3 gopd: ^1.0.1 - has: ^1.0.3 has-property-descriptors: ^1.0.0 has-proto: ^1.0.1 has-symbols: ^1.0.3 + hasown: ^2.0.0 internal-slot: ^1.0.5 is-array-buffer: ^3.0.2 is-callable: ^1.2.7 @@ -6443,51 +6220,51 @@ __metadata: is-regex: ^1.1.4 is-shared-array-buffer: ^1.0.2 is-string: ^1.0.7 - is-typed-array: ^1.1.10 + is-typed-array: ^1.1.12 is-weakref: ^1.0.2 - object-inspect: ^1.12.3 + object-inspect: ^1.13.1 object-keys: ^1.1.1 object.assign: ^4.1.4 - regexp.prototype.flags: ^1.5.0 - safe-array-concat: ^1.0.0 + regexp.prototype.flags: ^1.5.1 + safe-array-concat: ^1.0.1 safe-regex-test: ^1.0.0 - string.prototype.trim: ^1.2.7 - string.prototype.trimend: ^1.0.6 - string.prototype.trimstart: ^1.0.6 + string.prototype.trim: ^1.2.8 + string.prototype.trimend: ^1.0.7 + string.prototype.trimstart: ^1.0.7 typed-array-buffer: ^1.0.0 typed-array-byte-length: ^1.0.0 typed-array-byte-offset: ^1.0.0 typed-array-length: ^1.0.4 unbox-primitive: ^1.0.2 - which-typed-array: ^1.1.10 - checksum: 614e2c1c3717cb8d30b6128ef12ea110e06fd7d75ad77091ca1c5dbfb00da130e62e4bbbbbdda190eada098a22b27fe0f99ae5a1171dac2c8663b1e8be8a3a9b + which-typed-array: ^1.1.13 + checksum: b1bdc962856836f6e72be10b58dc128282bdf33771c7a38ae90419d920fc3b36cc5d2b70a222ad8016e3fc322c367bf4e9e89fc2bc79b7e933c05b218e83d79a languageName: node linkType: hard "es-module-lexer@npm:^1.2.1": - version: 1.3.0 - resolution: "es-module-lexer@npm:1.3.0" - checksum: 48fd9f504a9d2a894126f75c8b7ccc6273a289983e9b67255f165bfd9ae765d50100218251e94e702ca567826905ea2f7b3b4a0c4d74d3ce99cce3a2a606a238 + version: 1.4.1 + resolution: "es-module-lexer@npm:1.4.1" + checksum: a11b5a256d4e8e9c7d94c2fd87415ccd1591617b6edd847e064503f8eaece2d25e2e9078a02c5ce3ed5e83bb748f5b4820efbe78072c8beb07ac619c2edec35d languageName: node linkType: hard "es-set-tostringtag@npm:^2.0.1": - version: 2.0.1 - resolution: "es-set-tostringtag@npm:2.0.1" + version: 2.0.2 + resolution: "es-set-tostringtag@npm:2.0.2" dependencies: - get-intrinsic: ^1.1.3 - has: ^1.0.3 + get-intrinsic: ^1.2.2 has-tostringtag: ^1.0.0 - checksum: ec416a12948cefb4b2a5932e62093a7cf36ddc3efd58d6c58ca7ae7064475ace556434b869b0bbeb0c365f1032a8ccd577211101234b69837ad83ad204fff884 + hasown: ^2.0.0 + checksum: afcec3a4c9890ae14d7ec606204858441c801ff84f312538e1d1ccf1e5493c8b17bd672235df785f803756472cb4f2d49b87bde5237aef33411e74c22f194e07 languageName: node linkType: hard "es-shim-unscopables@npm:^1.0.0": - version: 1.0.0 - resolution: "es-shim-unscopables@npm:1.0.0" + version: 1.0.2 + resolution: "es-shim-unscopables@npm:1.0.2" dependencies: - has: ^1.0.3 - checksum: 83e95cadbb6ee44d3644dfad60dcad7929edbc42c85e66c3e99aefd68a3a5c5665f2686885cddb47dfeabfd77bd5ea5a7060f2092a955a729bbd8834f0d86fa1 + hasown: ^2.0.0 + checksum: 432bd527c62065da09ed1d37a3f8e623c423683285e6188108286f4a1e8e164a5bcbfbc0051557c7d14633cd2a41ce24c7048e6bbb66a985413fd32f1be72626 languageName: node linkType: hard @@ -6503,31 +6280,31 @@ __metadata: linkType: hard "esbuild@npm:^0.18.10": - version: 0.18.17 - resolution: "esbuild@npm:0.18.17" - dependencies: - "@esbuild/android-arm": 0.18.17 - "@esbuild/android-arm64": 0.18.17 - "@esbuild/android-x64": 0.18.17 - "@esbuild/darwin-arm64": 0.18.17 - "@esbuild/darwin-x64": 0.18.17 - "@esbuild/freebsd-arm64": 0.18.17 - "@esbuild/freebsd-x64": 0.18.17 - "@esbuild/linux-arm": 0.18.17 - "@esbuild/linux-arm64": 0.18.17 - "@esbuild/linux-ia32": 0.18.17 - "@esbuild/linux-loong64": 0.18.17 - "@esbuild/linux-mips64el": 0.18.17 - "@esbuild/linux-ppc64": 0.18.17 - "@esbuild/linux-riscv64": 0.18.17 - "@esbuild/linux-s390x": 0.18.17 - "@esbuild/linux-x64": 0.18.17 - "@esbuild/netbsd-x64": 0.18.17 - "@esbuild/openbsd-x64": 0.18.17 - "@esbuild/sunos-x64": 0.18.17 - "@esbuild/win32-arm64": 0.18.17 - "@esbuild/win32-ia32": 0.18.17 - "@esbuild/win32-x64": 0.18.17 + version: 0.18.20 + resolution: "esbuild@npm:0.18.20" + dependencies: + "@esbuild/android-arm": 0.18.20 + "@esbuild/android-arm64": 0.18.20 + "@esbuild/android-x64": 0.18.20 + "@esbuild/darwin-arm64": 0.18.20 + "@esbuild/darwin-x64": 0.18.20 + "@esbuild/freebsd-arm64": 0.18.20 + "@esbuild/freebsd-x64": 0.18.20 + "@esbuild/linux-arm": 0.18.20 + "@esbuild/linux-arm64": 0.18.20 + "@esbuild/linux-ia32": 0.18.20 + "@esbuild/linux-loong64": 0.18.20 + "@esbuild/linux-mips64el": 0.18.20 + "@esbuild/linux-ppc64": 0.18.20 + "@esbuild/linux-riscv64": 0.18.20 + "@esbuild/linux-s390x": 0.18.20 + "@esbuild/linux-x64": 0.18.20 + "@esbuild/netbsd-x64": 0.18.20 + "@esbuild/openbsd-x64": 0.18.20 + "@esbuild/sunos-x64": 0.18.20 + "@esbuild/win32-arm64": 0.18.20 + "@esbuild/win32-ia32": 0.18.20 + "@esbuild/win32-x64": 0.18.20 dependenciesMeta: "@esbuild/android-arm": optional: true @@ -6575,7 +6352,7 @@ __metadata: optional: true bin: esbuild: bin/esbuild - checksum: c6e1ffa776978a45697763a07ec9b16411db3d3b3997b2c4a0165a211727fce8b63b87165a28d8ef60d3a28b98197bbbc2833e51b89888a4437e0a483dffc8ff + checksum: 5d253614e50cdb6ec22095afd0c414f15688e7278a7eb4f3720a6dd1306b0909cf431e7b9437a90d065a31b1c57be60130f63fe3e8d0083b588571f31ee6ec7b languageName: node linkType: hard @@ -6633,43 +6410,42 @@ __metadata: linkType: hard "eslint-config-prettier@npm:^8.5.0": - version: 8.9.0 - resolution: "eslint-config-prettier@npm:8.9.0" + version: 8.10.0 + resolution: "eslint-config-prettier@npm:8.10.0" peerDependencies: eslint: ">=7.0.0" bin: eslint-config-prettier: bin/cli.js - checksum: a675d0dabd76b700ef2d062b5ec6a634e105a8e8c070f95281fd2ccb614527fac60b4c758132058c50f0521fd19313f1f5be45ce9ebf081f2e5f77ae6eb7d8db + checksum: 153266badd477e49b0759816246b2132f1dbdb6c7f313ca60a9af5822fd1071c2bc5684a3720d78b725452bbac04bb130878b2513aea5e72b1b792de5a69fec8 languageName: node linkType: hard -"eslint-import-resolver-node@npm:^0.3.7": - version: 0.3.7 - resolution: "eslint-import-resolver-node@npm:0.3.7" +"eslint-import-resolver-node@npm:^0.3.9": + version: 0.3.9 + resolution: "eslint-import-resolver-node@npm:0.3.9" dependencies: debug: ^3.2.7 - is-core-module: ^2.11.0 - resolve: ^1.22.1 - checksum: 3379aacf1d2c6952c1b9666c6fa5982c3023df695430b0d391c0029f6403a7775414873d90f397e98ba6245372b6c8960e16e74d9e4a3b0c0a4582f3bdbe3d6e + is-core-module: ^2.13.0 + resolve: ^1.22.4 + checksum: 439b91271236b452d478d0522a44482e8c8540bf9df9bd744062ebb89ab45727a3acd03366a6ba2bdbcde8f9f718bab7fe8db64688aca75acf37e04eafd25e22 languageName: node linkType: hard "eslint-import-resolver-typescript@npm:^3.5.5": - version: 3.5.5 - resolution: "eslint-import-resolver-typescript@npm:3.5.5" + version: 3.6.1 + resolution: "eslint-import-resolver-typescript@npm:3.6.1" dependencies: debug: ^4.3.4 enhanced-resolve: ^5.12.0 eslint-module-utils: ^2.7.4 + fast-glob: ^3.3.1 get-tsconfig: ^4.5.0 - globby: ^13.1.3 is-core-module: ^2.11.0 is-glob: ^4.0.3 - synckit: ^0.8.5 peerDependencies: eslint: "*" eslint-plugin-import: "*" - checksum: 27e6276fdff5d377c9036362ff736ac29852106e883ff589ea9092dc57d4bc2a67a82d75134221124f05045f9a7e2114a159b2c827d1f9f64d091f7afeab0f58 + checksum: 454fa0646533050fb57f13d27daf8c71f51b0bb9156d6a461290ccb8576d892209fcc6702a89553f3f5ea8e5b407395ca2e5de169a952c953685f1f7c46b4496 languageName: node linkType: hard @@ -6686,30 +6462,29 @@ __metadata: linkType: hard "eslint-plugin-import@npm:^2.27.5": - version: 2.28.0 - resolution: "eslint-plugin-import@npm:2.28.0" + version: 2.29.1 + resolution: "eslint-plugin-import@npm:2.29.1" dependencies: - array-includes: ^3.1.6 - array.prototype.findlastindex: ^1.2.2 - array.prototype.flat: ^1.3.1 - array.prototype.flatmap: ^1.3.1 + array-includes: ^3.1.7 + array.prototype.findlastindex: ^1.2.3 + array.prototype.flat: ^1.3.2 + array.prototype.flatmap: ^1.3.2 debug: ^3.2.7 doctrine: ^2.1.0 - eslint-import-resolver-node: ^0.3.7 + eslint-import-resolver-node: ^0.3.9 eslint-module-utils: ^2.8.0 - has: ^1.0.3 - is-core-module: ^2.12.1 + hasown: ^2.0.0 + is-core-module: ^2.13.1 is-glob: ^4.0.3 minimatch: ^3.1.2 - object.fromentries: ^2.0.6 - object.groupby: ^1.0.0 - object.values: ^1.1.6 - resolve: ^1.22.3 + object.fromentries: ^2.0.7 + object.groupby: ^1.0.1 + object.values: ^1.1.7 semver: ^6.3.1 - tsconfig-paths: ^3.14.2 + tsconfig-paths: ^3.15.0 peerDependencies: eslint: ^2 || ^3 || ^4 || ^5 || ^6 || ^7.2.0 || ^8 - checksum: f9eba311b93ca1bb89311856b1f7285bd79e0181d7eb70fe115053ff77e2235fea749b30f538b78927dc65769340b5be61f4c9581d1c82bcdcccb2061f440ad1 + checksum: e65159aef808136d26d029b71c8c6e4cb5c628e65e5de77f1eb4c13a379315ae55c9c3afa847f43f4ff9df7e54515c77ffc6489c6a6f81f7dd7359267577468c languageName: node linkType: hard @@ -6774,24 +6549,25 @@ __metadata: languageName: node linkType: hard -"eslint-visitor-keys@npm:^3.3.0, eslint-visitor-keys@npm:^3.4.1, eslint-visitor-keys@npm:^3.4.2": - version: 3.4.2 - resolution: "eslint-visitor-keys@npm:3.4.2" - checksum: 9e0e7e4aaea705c097ae37c97410e5f167d4d2193be2edcb1f0760762ede3df01545e4820ae314f42dcec687745f2c6dcaf6d83575c4a2a241eb0c8517d724f2 +"eslint-visitor-keys@npm:^3.3.0, eslint-visitor-keys@npm:^3.4.1, eslint-visitor-keys@npm:^3.4.3": + version: 3.4.3 + resolution: "eslint-visitor-keys@npm:3.4.3" + checksum: 36e9ef87fca698b6fd7ca5ca35d7b2b6eeaaf106572e2f7fd31c12d3bfdaccdb587bba6d3621067e5aece31c8c3a348b93922ab8f7b2cbc6aaab5e1d89040c60 languageName: node linkType: hard "eslint@npm:^8.21.0, eslint@npm:^8.35.0, eslint@npm:^8.37.0": - version: 8.46.0 - resolution: "eslint@npm:8.46.0" + version: 8.56.0 + resolution: "eslint@npm:8.56.0" dependencies: "@eslint-community/eslint-utils": ^4.2.0 "@eslint-community/regexpp": ^4.6.1 - "@eslint/eslintrc": ^2.1.1 - "@eslint/js": ^8.46.0 - "@humanwhocodes/config-array": ^0.11.10 + "@eslint/eslintrc": ^2.1.4 + "@eslint/js": 8.56.0 + "@humanwhocodes/config-array": ^0.11.13 "@humanwhocodes/module-importer": ^1.0.1 "@nodelib/fs.walk": ^1.2.8 + "@ungap/structured-clone": ^1.2.0 ajv: ^6.12.4 chalk: ^4.0.0 cross-spawn: ^7.0.2 @@ -6799,7 +6575,7 @@ __metadata: doctrine: ^3.0.0 escape-string-regexp: ^4.0.0 eslint-scope: ^7.2.2 - eslint-visitor-keys: ^3.4.2 + eslint-visitor-keys: ^3.4.3 espree: ^9.6.1 esquery: ^1.4.2 esutils: ^2.0.2 @@ -6824,7 +6600,7 @@ __metadata: text-table: ^0.2.0 bin: eslint: bin/eslint.js - checksum: 7a7d36b1a3bbc12e08fbb5bc36fd482a7a5a1797e62e762499dd45601b9e45aaa53a129f31ce0b4444551a9639b8b681ad535f379893dd1e3ae37b31dccd82aa + checksum: 883436d1e809b4a25d9eb03d42f584b84c408dbac28b0019f6ea07b5177940bf3cca86208f749a6a1e0039b63e085ee47aca1236c30721e91f0deef5cc5a5136 languageName: node linkType: hard @@ -6910,14 +6686,14 @@ __metadata: languageName: node linkType: hard -"eventemitter3@npm:^4.0.7": - version: 4.0.7 - resolution: "eventemitter3@npm:4.0.7" - checksum: 1875311c42fcfe9c707b2712c32664a245629b42bb0a5a84439762dd0fd637fc54d078155ea83c2af9e0323c9ac13687e03cfba79b03af9f40c89b4960099374 +"eventemitter3@npm:^5.0.1": + version: 5.0.1 + resolution: "eventemitter3@npm:5.0.1" + checksum: 543d6c858ab699303c3c32e0f0f47fc64d360bf73c3daf0ac0b5079710e340d6fe9f15487f94e66c629f5f82cd1a8678d692f3dbb6f6fcd1190e1b97fcad36f8 languageName: node linkType: hard -"events@npm:^3.2.0, events@npm:^3.3.0": +"events@npm:^3.2.0": version: 3.3.0 resolution: "events@npm:3.3.0" checksum: f6f487ad2198aa41d878fa31452f1a3c00958f46e9019286ff4787c84aac329332ab45c9cdc8c445928fc6d7ded294b9e005a7fce9426488518017831b272780 @@ -6976,17 +6752,16 @@ __metadata: languageName: node linkType: hard -"expect@npm:^29.0.0, expect@npm:^29.6.2": - version: 29.6.2 - resolution: "expect@npm:29.6.2" +"expect@npm:^29.0.0, expect@npm:^29.7.0": + version: 29.7.0 + resolution: "expect@npm:29.7.0" dependencies: - "@jest/expect-utils": ^29.6.2 - "@types/node": "*" - jest-get-type: ^29.4.3 - jest-matcher-utils: ^29.6.2 - jest-message-util: ^29.6.2 - jest-util: ^29.6.2 - checksum: 71f7b0c560e58bf6d27e0fded261d4bdb7ef81552a6bb4bd1ee09ce7a1f7dca67fbf83cf9b07a6645a88ef52e65085a0dcbe17f6c063b53ff7c2f0f3ea4ef69e + "@jest/expect-utils": ^29.7.0 + jest-get-type: ^29.6.3 + jest-matcher-utils: ^29.7.0 + jest-message-util: ^29.7.0 + jest-util: ^29.7.0 + checksum: 9257f10288e149b81254a0fda8ffe8d54a7061cd61d7515779998b012579d2b8c22354b0eb901daf0145f347403da582f75f359f4810c007182ad3fb318b5c0c languageName: node linkType: hard @@ -7028,16 +6803,16 @@ __metadata: languageName: node linkType: hard -"fast-glob@npm:^3.2.9, fast-glob@npm:^3.3.0": - version: 3.3.1 - resolution: "fast-glob@npm:3.3.1" +"fast-glob@npm:^3.2.9, fast-glob@npm:^3.3.1": + version: 3.3.2 + resolution: "fast-glob@npm:3.3.2" dependencies: "@nodelib/fs.stat": ^2.0.2 "@nodelib/fs.walk": ^1.2.3 glob-parent: ^5.1.2 merge2: ^1.3.0 micromatch: ^4.0.4 - checksum: b6f3add6403e02cf3a798bfbb1183d0f6da2afd368f27456010c0bc1f9640aea308243d4cb2c0ab142f618276e65ecb8be1661d7c62a7b4e5ba774b9ce5432e5 + checksum: 900e4979f4dbc3313840078419245621259f349950411ca2fa445a2f9a1a6d98c3b5e7e0660c5ccd563aa61abe133a21765c6c0dec8e57da1ba71d8000b05ec1 languageName: node linkType: hard @@ -7070,11 +6845,11 @@ __metadata: linkType: hard "fastq@npm:^1.6.0": - version: 1.15.0 - resolution: "fastq@npm:1.15.0" + version: 1.16.0 + resolution: "fastq@npm:1.16.0" dependencies: reusify: ^1.0.4 - checksum: 0170e6bfcd5d57a70412440b8ef600da6de3b2a6c5966aeaf0a852d542daff506a0ee92d6de7679d1de82e644bce69d7a574a6c93f0b03964b5337eed75ada1a + checksum: 1d40ed1f100ae625e5720484e8602b7ad07649370f1cbc3e34a6b9630a0bfed6946bab0322d8a368a1e3cde87bb9bbb8d3bc2ae01a0c1f022fac1d07c04e4feb languageName: node linkType: hard @@ -7191,19 +6966,29 @@ __metadata: linkType: hard "flat-cache@npm:^3.0.4": - version: 3.0.4 - resolution: "flat-cache@npm:3.0.4" + version: 3.2.0 + resolution: "flat-cache@npm:3.2.0" dependencies: - flatted: ^3.1.0 + flatted: ^3.2.9 + keyv: ^4.5.3 rimraf: ^3.0.2 - checksum: 4fdd10ecbcbf7d520f9040dd1340eb5dfe951e6f0ecf2252edeec03ee68d989ec8b9a20f4434270e71bcfd57800dc09b3344fca3966b2eb8f613072c7d9a2365 + checksum: e7e0f59801e288b54bee5cb9681e9ee21ee28ef309f886b312c9d08415b79fc0f24ac842f84356ce80f47d6a53de62197ce0e6e148dc42d5db005992e2a756ec languageName: node linkType: hard -"flatted@npm:^3.1.0": - version: 3.2.7 - resolution: "flatted@npm:3.2.7" - checksum: 427633049d55bdb80201c68f7eb1cbd533e03eac541f97d3aecab8c5526f12a20ccecaeede08b57503e772c769e7f8680b37e8d482d1e5f8d7e2194687f9ea35 +"flat@npm:^5.0.2": + version: 5.0.2 + resolution: "flat@npm:5.0.2" + bin: + flat: cli.js + checksum: 12a1536ac746db74881316a181499a78ef953632ddd28050b7a3a43c62ef5462e3357c8c29d76072bb635f147f7a9a1f0c02efef6b4be28f8db62ceb3d5c7f5d + languageName: node + linkType: hard + +"flatted@npm:^3.2.9": + version: 3.2.9 + resolution: "flatted@npm:3.2.9" + checksum: f14167fbe26a9d20f6fca8d998e8f1f41df72c8e81f9f2c9d61ed2bea058248f5e1cbd05e7f88c0e5087a6a0b822a1e5e2b446e879f3cfbe0b07ba2d7f80b026 languageName: node linkType: hard @@ -7294,13 +7079,13 @@ __metadata: linkType: hard "fs-extra@npm:^11.1.1": - version: 11.1.1 - resolution: "fs-extra@npm:11.1.1" + version: 11.2.0 + resolution: "fs-extra@npm:11.2.0" dependencies: graceful-fs: ^4.2.0 jsonfile: ^6.0.1 universalify: ^2.0.0 - checksum: fb883c68245b2d777fbc1f2082c9efb084eaa2bbf9fddaa366130d196c03608eebef7fb490541276429ee1ca99f317e2d73e96f5ca0999eefedf5a624ae1edfd + checksum: b12e42fa40ba47104202f57b8480dd098aa931c2724565e5e70779ab87605665594e76ee5fb00545f772ab9ace167fe06d2ab009c416dc8c842c5ae6df7aa7e8 languageName: node linkType: hard @@ -7325,11 +7110,11 @@ __metadata: linkType: hard "fs-minipass@npm:^3.0.0": - version: 3.0.2 - resolution: "fs-minipass@npm:3.0.2" + version: 3.0.3 + resolution: "fs-minipass@npm:3.0.3" dependencies: - minipass: ^5.0.0 - checksum: e9cc0e1f2d01c6f6f62f567aee59530aba65c6c7b2ae88c5027bc34c711ebcfcfaefd0caf254afa6adfe7d1fba16bc2537508a6235196bac7276747d078aef0a + minipass: ^7.0.3 + checksum: 8722a41109130851d979222d3ec88aabaceeaaf8f57b2a8f744ef8bd2d1ce95453b04a61daa0078822bc5cd21e008814f06fe6586f56fef511e71b8d2394d802 languageName: node linkType: hard @@ -7341,28 +7126,21 @@ __metadata: linkType: hard "fsevents@npm:^2.3.2, fsevents@npm:~2.3.2": - version: 2.3.2 - resolution: "fsevents@npm:2.3.2" + version: 2.3.3 + resolution: "fsevents@npm:2.3.3" dependencies: node-gyp: latest - checksum: 97ade64e75091afee5265e6956cb72ba34db7819b4c3e94c431d4be2b19b8bb7a2d4116da417950c3425f17c8fe693d25e20212cac583ac1521ad066b77ae31f + checksum: 11e6ea6fea15e42461fc55b4b0e4a0a3c654faa567f1877dbd353f39156f69def97a69936d1746619d656c4b93de2238bf731f6085a03a50cabf287c9d024317 conditions: os=darwin languageName: node linkType: hard "fsevents@patch:fsevents@^2.3.2#~builtin, fsevents@patch:fsevents@~2.3.2#~builtin": - version: 2.3.2 - resolution: "fsevents@patch:fsevents@npm%3A2.3.2#~builtin::version=2.3.2&hash=df0bf1" + version: 2.3.3 + resolution: "fsevents@patch:fsevents@npm%3A2.3.3#~builtin::version=2.3.3&hash=df0bf1" dependencies: node-gyp: latest - conditions: os=darwin - languageName: node - linkType: hard - -"function-bind@npm:^1.1.1": - version: 1.1.1 - resolution: "function-bind@npm:1.1.1" - checksum: b32fbaebb3f8ec4969f033073b43f5c8befbb58f1a79e12f1d7490358150359ebd92f49e72ff0144f65f2c48ea2a605bff2d07965f548f6474fd8efd95bf361a + conditions: os=darwin languageName: node linkType: hard @@ -7373,15 +7151,15 @@ __metadata: languageName: node linkType: hard -"function.prototype.name@npm:^1.1.5": - version: 1.1.5 - resolution: "function.prototype.name@npm:1.1.5" +"function.prototype.name@npm:^1.1.6": + version: 1.1.6 + resolution: "function.prototype.name@npm:1.1.6" dependencies: call-bind: ^1.0.2 - define-properties: ^1.1.3 - es-abstract: ^1.19.0 - functions-have-names: ^1.2.2 - checksum: acd21d733a9b649c2c442f067567743214af5fa248dbeee69d8278ce7df3329ea5abac572be9f7470b4ec1cd4d8f1040e3c5caccf98ebf2bf861a0deab735c27 + define-properties: ^1.2.0 + es-abstract: ^1.22.1 + functions-have-names: ^1.2.3 + checksum: 7a3f9bd98adab09a07f6e1f03da03d3f7c26abbdeaeee15223f6c04a9fb5674792bdf5e689dac19b97ac71de6aad2027ba3048a9b883aa1b3173eed6ab07f479 languageName: node linkType: hard @@ -7392,29 +7170,13 @@ __metadata: languageName: node linkType: hard -"functions-have-names@npm:^1.2.2, functions-have-names@npm:^1.2.3": +"functions-have-names@npm:^1.2.3": version: 1.2.3 resolution: "functions-have-names@npm:1.2.3" checksum: c3f1f5ba20f4e962efb71344ce0a40722163e85bee2101ce25f88214e78182d2d2476aa85ef37950c579eb6cf6ee811c17b3101bb84004bb75655f3e33f3fdb5 languageName: node linkType: hard -"gauge@npm:^4.0.3": - version: 4.0.4 - resolution: "gauge@npm:4.0.4" - dependencies: - aproba: ^1.0.3 || ^2.0.0 - color-support: ^1.1.3 - console-control-strings: ^1.1.0 - has-unicode: ^2.0.1 - signal-exit: ^3.0.7 - string-width: ^4.2.3 - strip-ansi: ^6.0.1 - wide-align: ^1.1.5 - checksum: 788b6bfe52f1dd8e263cda800c26ac0ca2ff6de0b6eee2fe0d9e3abf15e149b651bd27bf5226be10e6e3edb5c4e5d5985a5a1a98137e7a892f75eff76467ad2d - languageName: node - linkType: hard - "gensync@npm:^1.0.0-beta.2": version: 1.0.0-beta.2 resolution: "gensync@npm:1.0.0-beta.2" @@ -7449,15 +7211,15 @@ __metadata: languageName: node linkType: hard -"get-intrinsic@npm:^1.0.2, get-intrinsic@npm:^1.1.1, get-intrinsic@npm:^1.1.3, get-intrinsic@npm:^1.2.0, get-intrinsic@npm:^1.2.1": - version: 1.2.1 - resolution: "get-intrinsic@npm:1.2.1" +"get-intrinsic@npm:^1.0.2, get-intrinsic@npm:^1.1.1, get-intrinsic@npm:^1.1.3, get-intrinsic@npm:^1.2.0, get-intrinsic@npm:^1.2.1, get-intrinsic@npm:^1.2.2": + version: 1.2.2 + resolution: "get-intrinsic@npm:1.2.2" dependencies: - function-bind: ^1.1.1 - has: ^1.0.3 + function-bind: ^1.1.2 has-proto: ^1.0.1 has-symbols: ^1.0.3 - checksum: 5b61d88552c24b0cf6fa2d1b3bc5459d7306f699de060d76442cce49a4721f52b8c560a33ab392cf5575b7810277d54ded9d4d39a1ea61855619ebc005aa7e5f + hasown: ^2.0.0 + checksum: 447ff0724df26829908dc033b62732359596fcf66027bc131ab37984afb33842d9cd458fd6cecadfe7eac22fd8a54b349799ed334cf2726025c921c7250e7417 languageName: node linkType: hard @@ -7468,10 +7230,10 @@ __metadata: languageName: node linkType: hard -"get-iterator@npm:^2.0.0": - version: 2.0.0 - resolution: "get-iterator@npm:2.0.0" - checksum: 4ed81e7d100dd0a8dab11bf7dc7f88d8895a03057fa4a56d24190bdfe490fa4da3005d5b88fc6336429b33b423586f40dbcc40ce723f5dea55593cae0e2937de +"get-iterator@npm:^2.0.0, get-iterator@npm:^2.0.1": + version: 2.0.1 + resolution: "get-iterator@npm:2.0.1" + checksum: 353baac51f5e335c19cb734cbf0401d7c47deeac9d375e2939fed646fe52db2912d61ed2a60112050cf4687080817d159ec938803e48e03cd602edd489a116f2 languageName: node linkType: hard @@ -7516,23 +7278,23 @@ __metadata: linkType: hard "get-tsconfig@npm:^4.5.0": - version: 4.6.2 - resolution: "get-tsconfig@npm:4.6.2" + version: 4.7.2 + resolution: "get-tsconfig@npm:4.7.2" dependencies: resolve-pkg-maps: ^1.0.0 - checksum: e791e671a9b55e91efea3ca819ecd7a25beae679e31c83234bf3dd62ddd93df070c1b95ae7e29d206358ebb6408f6f79ac6d83a32a3bbd6a6d217babe23de077 + checksum: 172358903250eff0103943f816e8a4e51d29b8e5449058bdf7266714a908a48239f6884308bd3a6ff28b09f692b9533dbebfd183ab63e4e14f073cda91f1bca9 languageName: node linkType: hard "get-uri@npm:^6.0.1": - version: 6.0.1 - resolution: "get-uri@npm:6.0.1" + version: 6.0.2 + resolution: "get-uri@npm:6.0.2" dependencies: basic-ftp: ^5.0.2 - data-uri-to-buffer: ^5.0.1 + data-uri-to-buffer: ^6.0.0 debug: ^4.3.4 fs-extra: ^8.1.0 - checksum: a8aec70e1c67386fbe67f66e344ecd671a19f4cfc8e0f0e14d070563af5123d540e77fbceb6e26566f29846fac864d2862699ab134d307f85c85e7d72ce23d14 + checksum: 762de3b0e3d4e7afc966e4ce93be587d70c270590da9b4c8fbff888362656c055838d926903d1774cbfeed4d392b4d6def4b2c06d48c050580070426a3a8629b languageName: node linkType: hard @@ -7561,22 +7323,7 @@ __metadata: languageName: node linkType: hard -"glob@npm:^10.2.2": - version: 10.3.3 - resolution: "glob@npm:10.3.3" - dependencies: - foreground-child: ^3.1.0 - jackspeak: ^2.0.3 - minimatch: ^9.0.1 - minipass: ^5.0.0 || ^6.0.2 || ^7.0.0 - path-scurry: ^1.10.1 - bin: - glob: dist/cjs/src/bin.js - checksum: 29190d3291f422da0cb40b77a72fc8d2c51a36524e99b8bf412548b7676a6627489528b57250429612b6eec2e6fe7826d328451d3e694a9d15e575389308ec53 - languageName: node - linkType: hard - -"glob@npm:^10.3.10": +"glob@npm:^10.2.2, glob@npm:^10.3.10": version: 10.3.10 resolution: "glob@npm:10.3.10" dependencies: @@ -7613,11 +7360,11 @@ __metadata: linkType: hard "globals@npm:^13.19.0": - version: 13.20.0 - resolution: "globals@npm:13.20.0" + version: 13.24.0 + resolution: "globals@npm:13.24.0" dependencies: type-fest: ^0.20.2 - checksum: ad1ecf914bd051325faad281d02ea2c0b1df5d01bd94d368dcc5513340eac41d14b3c61af325768e3c7f8d44576e72780ec0b6f2d366121f8eec6e03c3a3b97a + checksum: 56066ef058f6867c04ff203b8a44c15b038346a62efbc3060052a1016be9f56f4cf0b2cd45b74b22b81e521a889fc7786c73691b0549c2f3a6e825b3d394f43c languageName: node linkType: hard @@ -7644,19 +7391,6 @@ __metadata: languageName: node linkType: hard -"globby@npm:^13.1.3": - version: 13.2.2 - resolution: "globby@npm:13.2.2" - dependencies: - dir-glob: ^3.0.1 - fast-glob: ^3.3.0 - ignore: ^5.2.4 - merge2: ^1.4.1 - slash: ^4.0.0 - checksum: f3d84ced58a901b4fcc29c846983108c426631fe47e94872868b65565495f7bee7b3defd68923bd480582771fd4bbe819217803a164a618ad76f1d22f666f41e - languageName: node - linkType: hard - "gonzales-pe@npm:^4.2.3, gonzales-pe@npm:^4.3.0": version: 4.3.0 resolution: "gonzales-pe@npm:4.3.0" @@ -7726,12 +7460,12 @@ __metadata: languageName: node linkType: hard -"has-property-descriptors@npm:^1.0.0": - version: 1.0.0 - resolution: "has-property-descriptors@npm:1.0.0" +"has-property-descriptors@npm:^1.0.0, has-property-descriptors@npm:^1.0.1": + version: 1.0.1 + resolution: "has-property-descriptors@npm:1.0.1" dependencies: - get-intrinsic: ^1.1.1 - checksum: a6d3f0a266d0294d972e354782e872e2fe1b6495b321e6ef678c9b7a06a40408a6891817350c62e752adced73a94ac903c54734fee05bf65b1905ee1368194bb + get-intrinsic: ^1.2.2 + checksum: 2bcc6bf6ec6af375add4e4b4ef586e43674850a91ad4d46666d0b28ba8e1fd69e424c7677d24d60f69470ad0afaa2f3197f508b20b0bb7dd99a8ab77ffc4b7c4 languageName: node linkType: hard @@ -7758,22 +7492,6 @@ __metadata: languageName: node linkType: hard -"has-unicode@npm:^2.0.1": - version: 2.0.1 - resolution: "has-unicode@npm:2.0.1" - checksum: 1eab07a7436512db0be40a710b29b5dc21fa04880b7f63c9980b706683127e3c1b57cb80ea96d47991bdae2dfe479604f6a1ba410106ee1046a41d1bd0814400 - languageName: node - linkType: hard - -"has@npm:^1.0.3": - version: 1.0.3 - resolution: "has@npm:1.0.3" - dependencies: - function-bind: ^1.1.1 - checksum: b9ad53d53be4af90ce5d1c38331e712522417d017d5ef1ebd0507e07c2fbad8686fffb8e12ddecd4c39ca9b9b47431afbb975b8abf7f3c3b82c98e9aad052792 - languageName: node - linkType: hard - "hash-base@npm:^3.0.0": version: 3.1.0 resolution: "hash-base@npm:3.1.0" @@ -7898,17 +7616,6 @@ __metadata: languageName: node linkType: hard -"http-proxy-agent@npm:^5.0.0": - version: 5.0.0 - resolution: "http-proxy-agent@npm:5.0.0" - dependencies: - "@tootallnate/once": 2 - agent-base: 6 - debug: 4 - checksum: e2ee1ff1656a131953839b2a19cd1f3a52d97c25ba87bd2559af6ae87114abf60971e498021f9b73f9fd78aea8876d1fb0d4656aac8a03c6caa9fc175f22b786 - languageName: node - linkType: hard - "http-proxy-agent@npm:^7.0.0": version: 7.0.0 resolution: "http-proxy-agent@npm:7.0.0" @@ -7919,17 +7626,7 @@ __metadata: languageName: node linkType: hard -"https-proxy-agent@npm:^5.0.0": - version: 5.0.1 - resolution: "https-proxy-agent@npm:5.0.1" - dependencies: - agent-base: 6 - debug: 4 - checksum: 571fccdf38184f05943e12d37d6ce38197becdd69e58d03f43637f7fa1269cf303a7d228aa27e5b27bbd3af8f09fd938e1c91dcfefff2df7ba77c20ed8dfc765 - languageName: node - linkType: hard - -"https-proxy-agent@npm:^7.0.2": +"https-proxy-agent@npm:^7.0.1, https-proxy-agent@npm:^7.0.2": version: 7.0.2 resolution: "https-proxy-agent@npm:7.0.2" dependencies: @@ -7953,15 +7650,6 @@ __metadata: languageName: node linkType: hard -"humanize-ms@npm:^1.2.1": - version: 1.2.1 - resolution: "humanize-ms@npm:1.2.1" - dependencies: - ms: ^2.0.0 - checksum: 9c7a74a2827f9294c009266c82031030eae811ca87b0da3dceb8d6071b9bde22c9f3daef0469c3c533cc67a97d8a167cd9fc0389350e5f415f61a79b171ded16 - languageName: node - linkType: hard - "hyperdyperid@npm:^1.2.0": version: 1.2.0 resolution: "hyperdyperid@npm:1.2.0" @@ -7995,9 +7683,9 @@ __metadata: linkType: hard "ignore@npm:^5.2.0, ignore@npm:^5.2.4": - version: 5.2.4 - resolution: "ignore@npm:5.2.4" - checksum: 3d4c309c6006e2621659311783eaea7ebcd41fe4ca1d78c91c473157ad6666a57a2df790fe0d07a12300d9aac2888204d7be8d59f9aaf665b1c7fcdb432517ef + version: 5.3.0 + resolution: "ignore@npm:5.3.0" + checksum: 2736da6621f14ced652785cb05d86301a66d70248597537176612bd0c8630893564bd5f6421f8806b09e8472e75c591ef01672ab8059c07c6eb2c09cefe04bf9 languageName: node linkType: hard @@ -8052,9 +7740,9 @@ __metadata: linkType: hard "inflation@npm:^2.0.0": - version: 2.0.0 - resolution: "inflation@npm:2.0.0" - checksum: a0494871b12275afdef9e2710ee1af1e0fc642b04613a9be69c05ef8b5e9627f3bd7d358a937fa47aa20235ee7313a4f30255048533add0ad4918beb918a586e + version: 2.1.0 + resolution: "inflation@npm:2.1.0" + checksum: 80c1b5d9ec408105a85f0623c824d668ddf0cadafd8d9716c0737990e5a712ae5f7d6bb0ff216b6648eccb9c6ac69fe06c0d8c58456d168db5bf550c89dd74ed languageName: node linkType: hard @@ -8090,31 +7778,30 @@ __metadata: linkType: hard "interface-datastore@npm:^8.2.0": - version: 8.2.3 - resolution: "interface-datastore@npm:8.2.3" + version: 8.2.10 + resolution: "interface-datastore@npm:8.2.10" dependencies: interface-store: ^5.0.0 - nanoid: ^4.0.0 - uint8arrays: ^4.0.2 - checksum: 01520fb965281f1352b93244a449d254e2fa9f9e4fb7f7f911b459354efcaa000d6ac311efda632d366281dbad41b3a61f321ce6a6241ab0f31283a81e4084cd + uint8arrays: ^5.0.0 + checksum: 16e12820b8423e457a6255377f373ea20132d7080809756b350e8914fde9abb73c4dd226cb3a9dda31bff790c181a02382bdde0482f9385ea4d2168c35ae93d8 languageName: node linkType: hard "interface-store@npm:^5.0.0": - version: 5.1.2 - resolution: "interface-store@npm:5.1.2" - checksum: 1423c5ffb5e6f9e65a86661cda90fb35d8dda433255bce9f57417650c98df83c84c4dc65741635c3efb16965b0f409f979a524f12b22803db0ab53ecfdf7b7db + version: 5.1.7 + resolution: "interface-store@npm:5.1.7" + checksum: aeddcdbfe8601f127c485ede6c6323bf2b302c713a1661036403dfd01b6b91e19a314ecf002477f108cb9c94d67781f88d386d3688f1e9c7cd1464a756ac28fa languageName: node linkType: hard "internal-slot@npm:^1.0.5": - version: 1.0.5 - resolution: "internal-slot@npm:1.0.5" + version: 1.0.6 + resolution: "internal-slot@npm:1.0.6" dependencies: - get-intrinsic: ^1.2.0 - has: ^1.0.3 + get-intrinsic: ^1.2.2 + hasown: ^2.0.0 side-channel: ^1.0.4 - checksum: 97e84046bf9e7574d0956bd98d7162313ce7057883b6db6c5c7b5e5f05688864b0978ba07610c726d15d66544ffe4b1050107d93f8a39ebc59b15d8b429b497a + checksum: 7872454888047553ce97a3fa1da7cc054a28ec5400a9c2e9f4dbe4fe7c1d041cb8e8301467614b80d4246d50377aad2fb58860b294ed74d6700cc346b6f89549 languageName: node linkType: hard @@ -8221,16 +7908,7 @@ __metadata: languageName: node linkType: hard -"is-core-module@npm:^2.1.0, is-core-module@npm:^2.11.0, is-core-module@npm:^2.12.0, is-core-module@npm:^2.12.1": - version: 2.12.1 - resolution: "is-core-module@npm:2.12.1" - dependencies: - has: ^1.0.3 - checksum: f04ea30533b5e62764e7b2e049d3157dc0abd95ef44275b32489ea2081176ac9746ffb1cdb107445cf1ff0e0dfcad522726ca27c27ece64dadf3795428b8e468 - languageName: node - linkType: hard - -"is-core-module@npm:^2.13.0": +"is-core-module@npm:^2.1.0, is-core-module@npm:^2.11.0, is-core-module@npm:^2.13.0, is-core-module@npm:^2.13.1": version: 2.13.1 resolution: "is-core-module@npm:2.13.1" dependencies: @@ -8248,24 +7926,6 @@ __metadata: languageName: node linkType: hard -"is-docker@npm:^2.0.0": - version: 2.2.1 - resolution: "is-docker@npm:2.2.1" - bin: - is-docker: cli.js - checksum: 3fef7ddbf0be25958e8991ad941901bf5922ab2753c46980b60b05c1bf9c9c2402d35e6dc32e4380b980ef5e1970a5d9d5e5aa2e02d77727c3b6b5e918474c56 - languageName: node - linkType: hard - -"is-docker@npm:^3.0.0": - version: 3.0.0 - resolution: "is-docker@npm:3.0.0" - bin: - is-docker: cli.js - checksum: b698118f04feb7eaf3338922bd79cba064ea54a1c3db6ec8c0c8d8ee7613e7e5854d802d3ef646812a8a3ace81182a085dfa0a71cc68b06f3fa794b9783b3c90 - languageName: node - linkType: hard - "is-electron@npm:^2.2.0": version: 2.2.2 resolution: "is-electron@npm:2.2.2" @@ -8312,17 +7972,6 @@ __metadata: languageName: node linkType: hard -"is-inside-container@npm:^1.0.0": - version: 1.0.0 - resolution: "is-inside-container@npm:1.0.0" - dependencies: - is-docker: ^3.0.0 - bin: - is-inside-container: cli.js - checksum: c50b75a2ab66ab3e8b92b3bc534e1ea72ca25766832c0623ac22d134116a98bcf012197d1caabe1d1c4bd5f84363d4aa5c36bb4b585fbcaf57be172cd10a1a03 - languageName: node - linkType: hard - "is-interactive@npm:^1.0.0": version: 1.0.0 resolution: "is-interactive@npm:1.0.0" @@ -8338,9 +7987,9 @@ __metadata: linkType: hard "is-loopback-addr@npm:^2.0.1": - version: 2.0.1 - resolution: "is-loopback-addr@npm:2.0.1" - checksum: 77030f7a12875c124b0bb83f172c1fa29fcb14574d15a1d4ed13be71c441b9cdc393370967b2e4bf808c564072933467d48e9ea006a3fd2cb3c7437c6d913108 + version: 2.0.2 + resolution: "is-loopback-addr@npm:2.0.2" + checksum: c7818bd2815aa7aad1efd410d38f58647759e206b43fcf5b3a35db866ceff9af0b0145f1dcba472267c6e1b965883d1ebf321f3eb18c6a8c1c609c4f91092f2f languageName: node linkType: hard @@ -8351,6 +8000,13 @@ __metadata: languageName: node linkType: hard +"is-network-error@npm:^1.0.0": + version: 1.0.1 + resolution: "is-network-error@npm:1.0.1" + checksum: 165d61500c4186c62db5a3a693d6bfa14ca40fe9b471ef4cd4f27b20ef6760880faf5386dc01ca9867531631782941fedaa94521d09959edf71f046e393c7b91 + languageName: node + linkType: hard + "is-number-object@npm:^1.0.4": version: 1.0.7 resolution: "is-number-object@npm:1.0.7" @@ -8469,7 +8125,7 @@ __metadata: languageName: node linkType: hard -"is-typed-array@npm:^1.1.10, is-typed-array@npm:^1.1.3, is-typed-array@npm:^1.1.9": +"is-typed-array@npm:^1.1.10, is-typed-array@npm:^1.1.12, is-typed-array@npm:^1.1.3, is-typed-array@npm:^1.1.9": version: 1.1.12 resolution: "is-typed-array@npm:1.1.12" dependencies: @@ -8508,15 +8164,6 @@ __metadata: languageName: node linkType: hard -"is-wsl@npm:^2.2.0": - version: 2.2.0 - resolution: "is-wsl@npm:2.2.0" - dependencies: - is-docker: ^2.0.0 - checksum: 20849846ae414997d290b75e16868e5261e86ff5047f104027026fd61d8b5a9b0b3ade16239f35e1a067b3c7cc02f70183cb661010ed16f4b6c7c93dad1b19d8 - languageName: node - linkType: hard - "isarray@npm:^2.0.5": version: 2.0.5 resolution: "isarray@npm:2.0.5" @@ -8538,6 +8185,13 @@ __metadata: languageName: node linkType: hard +"isexe@npm:^3.1.1": + version: 3.1.1 + resolution: "isexe@npm:3.1.1" + checksum: 7fe1931ee4e88eb5aa524cd3ceb8c882537bc3a81b02e438b240e47012eef49c86904d0f0e593ea7c3a9996d18d0f1f3be8d3eaa92333977b0c3a9d353d5563e + languageName: node + linkType: hard + "isobject@npm:^3.0.1": version: 3.0.1 resolution: "isobject@npm:3.0.1" @@ -8554,14 +8208,23 @@ __metadata: languageName: node linkType: hard +"isows@npm:1.0.3": + version: 1.0.3 + resolution: "isows@npm:1.0.3" + peerDependencies: + ws: "*" + checksum: 9cacd5cf59f67deb51e825580cd445ab1725ecb05a67c704050383fb772856f3cd5e7da8ad08f5a3bd2823680d77d099459d0c6a7037972a74d6429af61af440 + languageName: node + linkType: hard + "istanbul-lib-coverage@npm:^3.0.0, istanbul-lib-coverage@npm:^3.2.0": - version: 3.2.0 - resolution: "istanbul-lib-coverage@npm:3.2.0" - checksum: a2a545033b9d56da04a8571ed05c8120bf10e9bce01cf8633a3a2b0d1d83dff4ac4fe78d6d5673c27fc29b7f21a41d75f83a36be09f82a61c367b56aa73c1ff9 + version: 3.2.2 + resolution: "istanbul-lib-coverage@npm:3.2.2" + checksum: 2367407a8d13982d8f7a859a35e7f8dd5d8f75aae4bb5484ede3a9ea1b426dc245aff28b976a2af48ee759fdd9be374ce2bd2669b644f31e76c5f46a2e29a831 languageName: node linkType: hard -"istanbul-lib-instrument@npm:^5.0.4, istanbul-lib-instrument@npm:^5.1.0": +"istanbul-lib-instrument@npm:^5.0.4": version: 5.2.1 resolution: "istanbul-lib-instrument@npm:5.2.1" dependencies: @@ -8574,6 +8237,19 @@ __metadata: languageName: node linkType: hard +"istanbul-lib-instrument@npm:^6.0.0": + version: 6.0.1 + resolution: "istanbul-lib-instrument@npm:6.0.1" + dependencies: + "@babel/core": ^7.12.3 + "@babel/parser": ^7.14.7 + "@istanbuljs/schema": ^0.1.2 + istanbul-lib-coverage: ^3.2.0 + semver: ^7.5.4 + checksum: fb23472e739cfc9b027cefcd7d551d5e7ca7ff2817ae5150fab99fe42786a7f7b56a29a2aa8309c37092e18297b8003f9c274f50ca4360949094d17fbac81472 + languageName: node + linkType: hard + "istanbul-lib-report@npm:^3.0.0": version: 3.0.1 resolution: "istanbul-lib-report@npm:3.0.1" @@ -8607,69 +8283,63 @@ __metadata: linkType: hard "it-all@npm:^3.0.0, it-all@npm:^3.0.1, it-all@npm:^3.0.2": - version: 3.0.2 - resolution: "it-all@npm:3.0.2" - checksum: 37ce90f8ee6a579aa5dd710899912f21d0b2083dad8686d8ca648b2d0c2ffdb4856770e3d004c9e9e4d0a17a9c57221ec501ada98571690ac508021e6227cdab + version: 3.0.4 + resolution: "it-all@npm:3.0.4" + checksum: fb7259660b6555ae268ffde6f0245026e9d4e8afccf9c43a088bb0ff0483aaca95954b6074c1c96d46a57b572bce35fa1bb8542934ce9aee477e1dba46293891 languageName: node linkType: hard "it-batched-bytes@npm:^2.0.2": - version: 2.0.3 - resolution: "it-batched-bytes@npm:2.0.3" + version: 2.0.5 + resolution: "it-batched-bytes@npm:2.0.5" dependencies: p-defer: ^4.0.0 uint8arraylist: ^2.4.1 - checksum: a5bd5c1f2969f1b93dbaa1fbcfe756dee270ffd262485ac1175066a235059b39c09dc9da14f93281bf6ac33381d81483d755ccfc386869ea37441555d98ba30c + checksum: c127881de01aae0d1625b2179ba66f7a084466adef0d5b20ab7129040651545e4aacbff01cf372e5118a2836685463732fa52714a16b16856657f8e05861b899 languageName: node linkType: hard "it-byte-stream@npm:^1.0.0": - version: 1.0.1 - resolution: "it-byte-stream@npm:1.0.1" + version: 1.0.7 + resolution: "it-byte-stream@npm:1.0.7" dependencies: - it-pushable: ^3.2.0 it-stream-types: ^2.0.1 + p-defer: ^4.0.0 + race-signal: ^1.0.1 uint8arraylist: ^2.4.1 - checksum: 5ebd79cc48a925937337ba8b9f2ced330f13692e4800c4053f099f190d1d62def8f3071f44fb5f78fd90113e58c9a9612d1e6dd3f2ce153d5c8a87e964bc9a5c + checksum: 9cd63aa3ab2e7ebe1a3cac545f546d9362577901922b7a75803e36caacb0ef46b0cf4bc5334a0acdc548ba976849065527b9467a3c048af64233e70891537aad languageName: node linkType: hard -"it-drain@npm:^3.0.1": - version: 3.0.2 - resolution: "it-drain@npm:3.0.2" - checksum: 4ad8d8a829c2f35116f07c94bf80a82381b00ee2d431d3d0556c4848013d79fe16df0d71ebf39fc51bc5b75cef296d9a5d4c8101ef1828e1ef31b67087d2e414 - languageName: node - linkType: hard - -"it-drain@npm:^3.0.2": - version: 3.0.3 - resolution: "it-drain@npm:3.0.3" - checksum: 5f918245b6b3de4c0371cad30bdaa2f85f6786305673e325c9c5fe0358f9ec93e1b42ab46790688b0b4cbda71e450f60f3f57fbb8f627a6e96b3ff0d9d35ec2c +"it-drain@npm:^3.0.1, it-drain@npm:^3.0.2": + version: 3.0.5 + resolution: "it-drain@npm:3.0.5" + checksum: 6ab86dc487737a0a87556fab52dadd00f376881b633bd00b8c461f1e8eace47c426e8065700946eb066072e33fc7df7f0e9fa12426bd1d8cac914d52c8f44f43 languageName: node linkType: hard "it-filter@npm:^3.0.0, it-filter@npm:^3.0.1": - version: 3.0.2 - resolution: "it-filter@npm:3.0.2" + version: 3.0.4 + resolution: "it-filter@npm:3.0.4" dependencies: it-peekable: ^3.0.0 - checksum: 6eea68bcd7ee75ac52dc477149a8e822da8d7626631256179601294412f22635929ee5c0b1566413c721d078ef4bfb8c94b0e2f0d093259909582c6d1d31f1f7 + checksum: 8d57903bd99fa1b18ff2c3d0fb7ba0d041a229a33b77ff5ff86ca591e5e0ed0a61b14e937c250754ff1085d8e1c4f88996a4feff76bfc3f73e5fe54726c74dd9 languageName: node linkType: hard "it-first@npm:^3.0.1": - version: 3.0.2 - resolution: "it-first@npm:3.0.2" - checksum: cb0131bed94e13e13cc02b67fb864c0995dc8deaf8e79a3b4685165f48b331f439caf21d6576e18027308ac03556e9108b517b18c34c0e11ede8a742806dccd8 + version: 3.0.4 + resolution: "it-first@npm:3.0.4" + checksum: 428cf4b7baaf04dcb0c157cbd6332c2bab9708eeae6df752533d8fd8e21f7c321bfa8a57d35982115f57760baf526a9bf210b7d982d793e8340e22db2aa68fc6 languageName: node linkType: hard "it-foreach@npm:^2.0.3": - version: 2.0.4 - resolution: "it-foreach@npm:2.0.4" + version: 2.0.6 + resolution: "it-foreach@npm:2.0.6" dependencies: it-peekable: ^3.0.0 - checksum: 7133068fb282ac74af2b7482466a72198e1c52393e09276c96e3c40ef5c8cb3f3b76f56a4a7b6dd2d40a7c20c1679bc75192abf6c90579aa803bb4ed5665930d + checksum: 95f66b141ced66ca4429711a5d4f36b605005e5607d5e17c2a0357f10ed1b6750e3d49683e029190c1d4ff7a89378fbf9d17b26ded31ddd55741b2a1ddc3d3f2 languageName: node linkType: hard @@ -8687,62 +8357,53 @@ __metadata: linkType: hard "it-length-prefixed-stream@npm:^1.0.0": - version: 1.0.2 - resolution: "it-length-prefixed-stream@npm:1.0.2" + version: 1.1.6 + resolution: "it-length-prefixed-stream@npm:1.1.6" dependencies: it-byte-stream: ^1.0.0 - it-length-prefixed: ^9.0.1 it-stream-types: ^2.0.1 uint8-varint: ^2.0.1 uint8arraylist: ^2.4.1 - checksum: 5fcb04352601bc43df96475d1d2c6b9bf40c87a68bc0dece26d8a5280df83f383aa468f154640a28112af9c1f85f24972837cfe538a3fe117da7dc68ac4b57db + checksum: 9bba9b781934eb85f68187f4c9128c158a856d0e7d3770e13201cee84829d9d482fb60bcf5eb9ca3ed85f3671a1a27df123e3869c8461cac6929a3a2f349b792 languageName: node linkType: hard "it-length-prefixed@npm:^9.0.1": - version: 9.0.1 - resolution: "it-length-prefixed@npm:9.0.1" + version: 9.0.4 + resolution: "it-length-prefixed@npm:9.0.4" dependencies: err-code: ^3.0.1 + it-reader: ^6.0.1 it-stream-types: ^2.0.1 - uint8-varint: ^1.0.1 + uint8-varint: ^2.0.1 uint8arraylist: ^2.0.0 - uint8arrays: ^4.0.2 - checksum: a431aedef3450287c9c93cbc4017aebfaf2443cae43abbbd65384a9981f58373ed6cbb505819e60e5e7ac414eefd9f3e9aa1b10cca3726d92c3f0d4671c25b40 + uint8arrays: ^5.0.1 + checksum: 18e7c4a96299c14ec654b2c41784d4e9889d9239ba858acd0a97b6ea0261b504b306c0850b1c28dd9e8678de03ff95f1e81ba03aed6ebf92f6d01b6e9cee3bab languageName: node linkType: hard "it-length@npm:^3.0.1": - version: 3.0.2 - resolution: "it-length@npm:3.0.2" - checksum: 688565353598557dc003324fc497896f5d24795f83011d03cc3625ced09207d9e66eda69e055080d1cd3c534673f65c76e5a5c48553b53e66b58a05995b49e81 - languageName: node - linkType: hard - -"it-map@npm:^3.0.1": - version: 3.0.3 - resolution: "it-map@npm:3.0.3" - dependencies: - it-peekable: ^3.0.0 - checksum: bf48828a40c19d98b49f42730fa3d2821b52056dd755dcdd9b4a1c1077fe8fcd79bf8ae810429e4a82d6cac5b2439e5f3aae8a3c7353c9b5c51ce14d038d9858 + version: 3.0.4 + resolution: "it-length@npm:3.0.4" + checksum: 881208cbcad1e3a396b27b35d73acbac9c27eb8b9fa43b1ed1bb4ca1aba489040981e0ea2b3db6fae90d2d9a1e4c610013abef4030ecd80eca64689f07df8dc9 languageName: node linkType: hard -"it-map@npm:^3.0.3": - version: 3.0.4 - resolution: "it-map@npm:3.0.4" +"it-map@npm:^3.0.1, it-map@npm:^3.0.3": + version: 3.0.5 + resolution: "it-map@npm:3.0.5" dependencies: it-peekable: ^3.0.0 - checksum: a5c0cc6ab36b2c2d2aa17189295891ec3b71244e251f99ff79fb5231891de97b79a9851f55f4e30b1cbc06ed1a13e25b1e4c4e4ea326199c86e382acc76de090 + checksum: bdaa2f1662325457a4eba487dfb04ca8aee0b1d91356b285bf6133aaeda67fba5b7d5c6644838ea8a025e4bd0e8a46910dd7b203f75940ed7ce0d8f3d159bbf3 languageName: node linkType: hard -"it-merge@npm:^3.0.0": - version: 3.0.1 - resolution: "it-merge@npm:3.0.1" +"it-merge@npm:^3.0.0, it-merge@npm:^3.0.1": + version: 3.0.3 + resolution: "it-merge@npm:3.0.3" dependencies: - it-pushable: ^3.1.0 - checksum: 8401b0394868cd02ccd988687a641b2a250668de1a818d7a867650621fb77e6dcd4b041972f738293ab65880b0a3a16b45d94c8b7b42d6b434430c817eee7565 + it-pushable: ^3.2.0 + checksum: 031c72302b35db8769c07646c561980c8d97097ce96aa869ebd0cf7b506ea075299b497a177a04bd5eb26398379b3e0b8f4c59a9a1ad0b1e7068d1a921cabf7b languageName: node linkType: hard @@ -8757,18 +8418,18 @@ __metadata: linkType: hard "it-parallel@npm:^3.0.0": - version: 3.0.3 - resolution: "it-parallel@npm:3.0.3" + version: 3.0.6 + resolution: "it-parallel@npm:3.0.6" dependencies: p-defer: ^4.0.0 - checksum: 2326fe2d15134d59dede916b8b72a83092f79b356537921ede24f0b0d95616f2916d8bf1ebd1af6db5326303af2ac654c64dc144df64b8f48b57a95d47c1be5f + checksum: ca9cc7faea9dee197dd5e683743542da21369c5a3d6991278b0221493d0e801abd7d750ed2860a97e6eeffae6b7c8af9fdd3e61285895317599d8608ccd7576d languageName: node linkType: hard "it-peekable@npm:^3.0.0": - version: 3.0.1 - resolution: "it-peekable@npm:3.0.1" - checksum: e327caf50bc205672b4133040e4c580cb39f6a8691565f420941ac5b7877ad1ca47c53ecf1ea62e4716abe36786b1cf07f2aea663eda6779fbf0d7a7cb0fbfdc + version: 3.0.3 + resolution: "it-peekable@npm:3.0.3" + checksum: 9603045130673b26a572cb2a9bfb7cbf9907fd759aa9dbfb1113b38c07c7b750b75a8dbec317b0cde6e47b6f3be2fddd9785fc7e38f1147ea3ded7eabd590c7a languageName: node linkType: hard @@ -8784,23 +8445,23 @@ __metadata: linkType: hard "it-protobuf-stream@npm:^1.0.0": - version: 1.0.2 - resolution: "it-protobuf-stream@npm:1.0.2" + version: 1.1.2 + resolution: "it-protobuf-stream@npm:1.1.2" dependencies: it-length-prefixed-stream: ^1.0.0 it-stream-types: ^2.0.1 protons-runtime: ^5.0.0 uint8arraylist: ^2.4.1 - checksum: a5d724c213f0e9e4f4f42145c43cf0bfe0036dc8f1cae094efd959710046aae399e77a2dd2e6c270a37103ae630386666676dddd6c8cf2138803402eba64dc1a + checksum: d10601aa530ee53da994377b4704e4f28a45ff26a4da1d64c1beccfcbdc1802da5cf480b692ff692a6557bd2dd0823c4e6992fc525122ab5da8d0ba67f003198 languageName: node linkType: hard -"it-pushable@npm:^3.0.0, it-pushable@npm:^3.1.0, it-pushable@npm:^3.1.2, it-pushable@npm:^3.1.3, it-pushable@npm:^3.2.0": - version: 3.2.1 - resolution: "it-pushable@npm:3.2.1" +"it-pushable@npm:^3.0.0, it-pushable@npm:^3.1.0, it-pushable@npm:^3.1.2, it-pushable@npm:^3.1.3, it-pushable@npm:^3.2.0, it-pushable@npm:^3.2.1, it-pushable@npm:^3.2.3": + version: 3.2.3 + resolution: "it-pushable@npm:3.2.3" dependencies: p-defer: ^4.0.0 - checksum: a23eaac8d1ec86785d0f3e71c67f1544cb1b99aff88c70d3043f3b87a9966c154ca387de76739f91dd744cd7815b40d20e9711a54079cc7ddaf36be54fd10c41 + checksum: 8b1d1ceb2a42b31b55119f9721b1f4568c498627470bac18479e6f8db3791fe1185653480cd1c319462bae3d64091bd9ca9e6e90e217e38a5ab7f078559ccca4 languageName: node linkType: hard @@ -8815,11 +8476,11 @@ __metadata: linkType: hard "it-sort@npm:^3.0.1": - version: 3.0.2 - resolution: "it-sort@npm:3.0.2" + version: 3.0.4 + resolution: "it-sort@npm:3.0.4" dependencies: it-all: ^3.0.0 - checksum: d45bb64413d949e1c7c459ad1b4418f14460959d5b73c830368ac18722277c8dcbc606740343ca87441c7c136838b7335a16f9174f0dd8ef03cdc80f8bcfdfbb + checksum: de4f1832c6d12914d51109ca3f8ccebba60fdb050d0af2b3d9b8bcd14cb3d320ba1a01e3ef59de2d3691886c0a903e1c4e46ad354796159d4b0d3d7013bc180c languageName: node linkType: hard @@ -8831,22 +8492,9 @@ __metadata: linkType: hard "it-take@npm:^3.0.1": - version: 3.0.2 - resolution: "it-take@npm:3.0.2" - checksum: 50a5449efa0832cd55878b7492f536b558861181786c7a95fb59d93c761646bc4bbc9dd46fd28a5b574b7349fa4caf028bbf66e3a86630473cc5907b811952a3 - languageName: node - linkType: hard - -"jackspeak@npm:^2.0.3": - version: 2.2.2 - resolution: "jackspeak@npm:2.2.2" - dependencies: - "@isaacs/cliui": ^8.0.2 - "@pkgjs/parseargs": ^0.11.0 - dependenciesMeta: - "@pkgjs/parseargs": - optional: true - checksum: 7b1468dd910afc00642db87448f24b062346570b8b47531409aa9012bcb95fdf7ec2b1c48edbb8b57a938c08391f8cc01b5034fc335aa3a2e74dbcc0ee5c555a + version: 3.0.4 + resolution: "it-take@npm:3.0.4" + checksum: 69dedde350817cba8de80e0432c9b81c35ff2b91f9c80582e657e382ec8c38af003f575353ae22605c963c28605a48cb994c7dba93fedac732db35ee86d7e516 languageName: node linkType: hard @@ -8870,59 +8518,59 @@ __metadata: languageName: node linkType: hard -"jest-changed-files@npm:^29.5.0": - version: 29.5.0 - resolution: "jest-changed-files@npm:29.5.0" +"jest-changed-files@npm:^29.7.0": + version: 29.7.0 + resolution: "jest-changed-files@npm:29.7.0" dependencies: execa: ^5.0.0 + jest-util: ^29.7.0 p-limit: ^3.1.0 - checksum: a67a7cb3c11f8f92bd1b7c79e84f724cbd11a9ad51f3cdadafe3ce7ee3c79ee50dbea128f920f5fddc807e9e4e83f5462143094391feedd959a77dd20ab96cf3 + checksum: 963e203893c396c5dfc75e00a49426688efea7361b0f0e040035809cecd2d46b3c01c02be2d9e8d38b1138357d2de7719ea5b5be21f66c10f2e9685a5a73bb99 languageName: node linkType: hard -"jest-circus@npm:^29.6.2": - version: 29.6.2 - resolution: "jest-circus@npm:29.6.2" +"jest-circus@npm:^29.7.0": + version: 29.7.0 + resolution: "jest-circus@npm:29.7.0" dependencies: - "@jest/environment": ^29.6.2 - "@jest/expect": ^29.6.2 - "@jest/test-result": ^29.6.2 - "@jest/types": ^29.6.1 + "@jest/environment": ^29.7.0 + "@jest/expect": ^29.7.0 + "@jest/test-result": ^29.7.0 + "@jest/types": ^29.6.3 "@types/node": "*" chalk: ^4.0.0 co: ^4.6.0 dedent: ^1.0.0 is-generator-fn: ^2.0.0 - jest-each: ^29.6.2 - jest-matcher-utils: ^29.6.2 - jest-message-util: ^29.6.2 - jest-runtime: ^29.6.2 - jest-snapshot: ^29.6.2 - jest-util: ^29.6.2 + jest-each: ^29.7.0 + jest-matcher-utils: ^29.7.0 + jest-message-util: ^29.7.0 + jest-runtime: ^29.7.0 + jest-snapshot: ^29.7.0 + jest-util: ^29.7.0 p-limit: ^3.1.0 - pretty-format: ^29.6.2 + pretty-format: ^29.7.0 pure-rand: ^6.0.0 slash: ^3.0.0 stack-utils: ^2.0.3 - checksum: 4f5a96a68c3c808c3d5a9279a2f39a2937386e2cebba5096971f267d79562ce2133a13bc05356a39f8f1ba68fcfe1eb39c4572b3fb0f91affbd932950e89c1e3 + checksum: 349437148924a5a109c9b8aad6d393a9591b4dac1918fc97d81b7fc515bc905af9918495055071404af1fab4e48e4b04ac3593477b1d5dcf48c4e71b527c70a7 languageName: node linkType: hard -"jest-cli@npm:^29.6.2": - version: 29.6.2 - resolution: "jest-cli@npm:29.6.2" +"jest-cli@npm:^29.7.0": + version: 29.7.0 + resolution: "jest-cli@npm:29.7.0" dependencies: - "@jest/core": ^29.6.2 - "@jest/test-result": ^29.6.2 - "@jest/types": ^29.6.1 + "@jest/core": ^29.7.0 + "@jest/test-result": ^29.7.0 + "@jest/types": ^29.6.3 chalk: ^4.0.0 + create-jest: ^29.7.0 exit: ^0.1.2 - graceful-fs: ^4.2.9 import-local: ^3.0.2 - jest-config: ^29.6.2 - jest-util: ^29.6.2 - jest-validate: ^29.6.2 - prompts: ^2.0.1 + jest-config: ^29.7.0 + jest-util: ^29.7.0 + jest-validate: ^29.7.0 yargs: ^17.3.1 peerDependencies: node-notifier: ^8.0.1 || ^9.0.0 || ^10.0.0 @@ -8931,34 +8579,34 @@ __metadata: optional: true bin: jest: bin/jest.js - checksum: 0b7b09ae4bd327caf1981eac5a14679ddda3c5c836c9f8ea0ecfe1e5e10e9a39a5ed783fa38d25383604c4d3405595e74b391d955e99aea7e51acb41a59ea108 + checksum: 664901277a3f5007ea4870632ed6e7889db9da35b2434e7cb488443e6bf5513889b344b7fddf15112135495b9875892b156faeb2d7391ddb9e2a849dcb7b6c36 languageName: node linkType: hard -"jest-config@npm:^29.6.2": - version: 29.6.2 - resolution: "jest-config@npm:29.6.2" +"jest-config@npm:^29.7.0": + version: 29.7.0 + resolution: "jest-config@npm:29.7.0" dependencies: "@babel/core": ^7.11.6 - "@jest/test-sequencer": ^29.6.2 - "@jest/types": ^29.6.1 - babel-jest: ^29.6.2 + "@jest/test-sequencer": ^29.7.0 + "@jest/types": ^29.6.3 + babel-jest: ^29.7.0 chalk: ^4.0.0 ci-info: ^3.2.0 deepmerge: ^4.2.2 glob: ^7.1.3 graceful-fs: ^4.2.9 - jest-circus: ^29.6.2 - jest-environment-node: ^29.6.2 - jest-get-type: ^29.4.3 - jest-regex-util: ^29.4.3 - jest-resolve: ^29.6.2 - jest-runner: ^29.6.2 - jest-util: ^29.6.2 - jest-validate: ^29.6.2 + jest-circus: ^29.7.0 + jest-environment-node: ^29.7.0 + jest-get-type: ^29.6.3 + jest-regex-util: ^29.6.3 + jest-resolve: ^29.7.0 + jest-runner: ^29.7.0 + jest-util: ^29.7.0 + jest-validate: ^29.7.0 micromatch: ^4.0.4 parse-json: ^5.2.0 - pretty-format: ^29.6.2 + pretty-format: ^29.7.0 slash: ^3.0.0 strip-json-comments: ^3.1.1 peerDependencies: @@ -8969,140 +8617,128 @@ __metadata: optional: true ts-node: optional: true - checksum: 3bd104a3ac2dd9d34986238142437606354169766dcf88359a7a12ac106d0dc17dcc6b627e4f20db97a58bac5b0502b5436c9cc4722b3629b2a114bba6da9128 + checksum: 4cabf8f894c180cac80b7df1038912a3fc88f96f2622de33832f4b3314f83e22b08fb751da570c0ab2b7988f21604bdabade95e3c0c041068ac578c085cf7dff languageName: node linkType: hard -"jest-diff@npm:^29.6.2": - version: 29.6.2 - resolution: "jest-diff@npm:29.6.2" +"jest-diff@npm:^29.7.0": + version: 29.7.0 + resolution: "jest-diff@npm:29.7.0" dependencies: chalk: ^4.0.0 - diff-sequences: ^29.4.3 - jest-get-type: ^29.4.3 - pretty-format: ^29.6.2 - checksum: 0effd66a0c23f8c139ebf7ca99ed30b479b86fff66f19ad4869f130aaf7ae6a24ca1533f697b7e4930cbe2ddffc85387723fcca673501c653fb77a38f538e959 + diff-sequences: ^29.6.3 + jest-get-type: ^29.6.3 + pretty-format: ^29.7.0 + checksum: 08e24a9dd43bfba1ef07a6374e5af138f53137b79ec3d5cc71a2303515335898888fa5409959172e1e05de966c9e714368d15e8994b0af7441f0721ee8e1bb77 languageName: node linkType: hard -"jest-docblock@npm:^29.4.3": - version: 29.4.3 - resolution: "jest-docblock@npm:29.4.3" +"jest-docblock@npm:^29.7.0": + version: 29.7.0 + resolution: "jest-docblock@npm:29.7.0" dependencies: detect-newline: ^3.0.0 - checksum: e0e9df1485bb8926e5b33478cdf84b3387d9caf3658e7dc1eaa6dc34cb93dea0d2d74797f6e940f0233a88f3dadd60957f2288eb8f95506361f85b84bf8661df + checksum: 66390c3e9451f8d96c5da62f577a1dad701180cfa9b071c5025acab2f94d7a3efc2515cfa1654ebe707213241541ce9c5530232cdc8017c91ed64eea1bd3b192 languageName: node linkType: hard -"jest-each@npm:^29.6.2": - version: 29.6.2 - resolution: "jest-each@npm:29.6.2" +"jest-each@npm:^29.7.0": + version: 29.7.0 + resolution: "jest-each@npm:29.7.0" dependencies: - "@jest/types": ^29.6.1 + "@jest/types": ^29.6.3 chalk: ^4.0.0 - jest-get-type: ^29.4.3 - jest-util: ^29.6.2 - pretty-format: ^29.6.2 - checksum: b64194f4ca27afc6070a42b7ecccbc68be0ded19a849f8cd8f91a2abb23fadae2d38d47559a315f4d1f576927761f3ea437a75ab6cf19206332abb8527d7c165 + jest-get-type: ^29.6.3 + jest-util: ^29.7.0 + pretty-format: ^29.7.0 + checksum: e88f99f0184000fc8813f2a0aa79e29deeb63700a3b9b7928b8a418d7d93cd24933608591dbbdea732b473eb2021c72991b5cc51a17966842841c6e28e6f691c languageName: node linkType: hard -"jest-environment-node@npm:^29.6.2": - version: 29.6.2 - resolution: "jest-environment-node@npm:29.6.2" +"jest-environment-node@npm:^29.7.0": + version: 29.7.0 + resolution: "jest-environment-node@npm:29.7.0" dependencies: - "@jest/environment": ^29.6.2 - "@jest/fake-timers": ^29.6.2 - "@jest/types": ^29.6.1 + "@jest/environment": ^29.7.0 + "@jest/fake-timers": ^29.7.0 + "@jest/types": ^29.6.3 "@types/node": "*" - jest-mock: ^29.6.2 - jest-util: ^29.6.2 - checksum: 0b754ac2d3bdb7ce5d6fc28595b9d1c64176f20506b6f773b18b0280ab0b396ed7d927c8519779d3c560fa2b13236ee7077092ccb19a13bea23d40dd30f06450 + jest-mock: ^29.7.0 + jest-util: ^29.7.0 + checksum: 501a9966292cbe0ca3f40057a37587cb6def25e1e0c5e39ac6c650fe78d3c70a2428304341d084ac0cced5041483acef41c477abac47e9a290d5545fd2f15646 languageName: node linkType: hard -"jest-get-type@npm:^29.4.3": - version: 29.4.3 - resolution: "jest-get-type@npm:29.4.3" - checksum: 6ac7f2dde1c65e292e4355b6c63b3a4897d7e92cb4c8afcf6d397f2682f8080e094c8b0b68205a74d269882ec06bf696a9de6cd3e1b7333531e5ed7b112605ce +"jest-get-type@npm:^29.6.3": + version: 29.6.3 + resolution: "jest-get-type@npm:29.6.3" + checksum: 88ac9102d4679d768accae29f1e75f592b760b44277df288ad76ce5bf038c3f5ce3719dea8aa0f035dac30e9eb034b848ce716b9183ad7cc222d029f03e92205 languageName: node linkType: hard -"jest-haste-map@npm:^29.6.2": - version: 29.6.2 - resolution: "jest-haste-map@npm:29.6.2" +"jest-haste-map@npm:^29.7.0": + version: 29.7.0 + resolution: "jest-haste-map@npm:29.7.0" dependencies: - "@jest/types": ^29.6.1 + "@jest/types": ^29.6.3 "@types/graceful-fs": ^4.1.3 "@types/node": "*" anymatch: ^3.0.3 fb-watchman: ^2.0.0 fsevents: ^2.3.2 graceful-fs: ^4.2.9 - jest-regex-util: ^29.4.3 - jest-util: ^29.6.2 - jest-worker: ^29.6.2 + jest-regex-util: ^29.6.3 + jest-util: ^29.7.0 + jest-worker: ^29.7.0 micromatch: ^4.0.4 walker: ^1.0.8 dependenciesMeta: fsevents: optional: true - checksum: 726233972030eb2e5bce6c9468e497310436b455c88b40e744bd053e20a6f3ff19aec340edcbd89537c629ed5cf8916506bc895d690cc39a0862c74dcd95b7b8 + checksum: c2c8f2d3e792a963940fbdfa563ce14ef9e14d4d86da645b96d3cd346b8d35c5ce0b992ee08593939b5f718cf0a1f5a90011a056548a1dbf58397d4356786f01 languageName: node linkType: hard -"jest-leak-detector@npm:^29.6.2": - version: 29.6.2 - resolution: "jest-leak-detector@npm:29.6.2" +"jest-leak-detector@npm:^29.7.0": + version: 29.7.0 + resolution: "jest-leak-detector@npm:29.7.0" dependencies: - jest-get-type: ^29.4.3 - pretty-format: ^29.6.2 - checksum: e00152acdba8aa8f9334775b77375947508051c34646fbeb702275da2b6ac6145f8cad6d5893112e76484d00fa8c0b4fd71b78ab0b4ef34950f5b6a84f37ae67 + jest-get-type: ^29.6.3 + pretty-format: ^29.7.0 + checksum: e3950e3ddd71e1d0c22924c51a300a1c2db6cf69ec1e51f95ccf424bcc070f78664813bef7aed4b16b96dfbdeea53fe358f8aeaaea84346ae15c3735758f1605 languageName: node linkType: hard -"jest-matcher-utils@npm:^29.6.2": - version: 29.6.2 - resolution: "jest-matcher-utils@npm:29.6.2" +"jest-matcher-utils@npm:^29.7.0": + version: 29.7.0 + resolution: "jest-matcher-utils@npm:29.7.0" dependencies: chalk: ^4.0.0 - jest-diff: ^29.6.2 - jest-get-type: ^29.4.3 - pretty-format: ^29.6.2 - checksum: 3e1b65dd30d05f75fe56dc45fbe4135aec2ff96a3d1e21afbf6a66f3a45a7e29cd0fd37cf80b9564e0381d6205833f77ccaf766c6f7e1aad6b7924d117be504e + jest-diff: ^29.7.0 + jest-get-type: ^29.6.3 + pretty-format: ^29.7.0 + checksum: d7259e5f995d915e8a37a8fd494cb7d6af24cd2a287b200f831717ba0d015190375f9f5dc35393b8ba2aae9b2ebd60984635269c7f8cff7d85b077543b7744cd languageName: node linkType: hard -"jest-message-util@npm:^29.6.2": - version: 29.6.2 - resolution: "jest-message-util@npm:29.6.2" +"jest-message-util@npm:^29.7.0": + version: 29.7.0 + resolution: "jest-message-util@npm:29.7.0" dependencies: "@babel/code-frame": ^7.12.13 - "@jest/types": ^29.6.1 + "@jest/types": ^29.6.3 "@types/stack-utils": ^2.0.0 chalk: ^4.0.0 graceful-fs: ^4.2.9 micromatch: ^4.0.4 - pretty-format: ^29.6.2 + pretty-format: ^29.7.0 slash: ^3.0.0 stack-utils: ^2.0.3 - checksum: e8e3c8d2301e2ca4038ed6df8cbba7fedc6949d1ede4c0e3f1f44f53afb56d77eb35983fa460140d0eadeab99a5f3ae04b703fe77cd7b316b40b361228b5aa1a + checksum: a9d025b1c6726a2ff17d54cc694de088b0489456c69106be6b615db7a51b7beb66788bea7a59991a019d924fbf20f67d085a445aedb9a4d6760363f4d7d09930 languageName: node linkType: hard -"jest-mock-extended@npm:^3.0.3, jest-mock-extended@npm:^3.0.4": - version: 3.0.4 - resolution: "jest-mock-extended@npm:3.0.4" - dependencies: - ts-essentials: ^7.0.3 - peerDependencies: - jest: ^24.0.0 || ^25.0.0 || ^26.0.0 || ^27.0.0 || ^28.0.0 || ^29.0.0 - typescript: ^3.0.0 || ^4.0.0 || ^5.0.0 - checksum: f861253c63508b30d971fbbbc1bf2911ff4406cd260d0e23483a1d4514898b18ba5efbd43fbdf6d94996dc09b20eb1aad1b46aeaea9c99244ba12dc99814fd3f - languageName: node - linkType: hard - -"jest-mock-extended@npm:^3.0.5": +"jest-mock-extended@npm:^3.0.3, jest-mock-extended@npm:^3.0.4, jest-mock-extended@npm:^3.0.5": version: 3.0.5 resolution: "jest-mock-extended@npm:3.0.5" dependencies: @@ -9114,14 +8750,14 @@ __metadata: languageName: node linkType: hard -"jest-mock@npm:^29.6.2": - version: 29.6.2 - resolution: "jest-mock@npm:29.6.2" +"jest-mock@npm:^29.7.0": + version: 29.7.0 + resolution: "jest-mock@npm:29.7.0" dependencies: - "@jest/types": ^29.6.1 + "@jest/types": ^29.6.3 "@types/node": "*" - jest-util: ^29.6.2 - checksum: 0bacb5d58441462c0e531ec4d2f7377eecbe21f664d8a460e72f94ba61d22635028931678e7a0f1c3e3f5894973db8e409432f7db4c01283456c8fdbd85f5b3b + jest-util: ^29.7.0 + checksum: 81ba9b68689a60be1482212878973700347cb72833c5e5af09895882b9eb5c4e02843a1bbdf23f94c52d42708bab53a30c45a3482952c9eec173d1eaac5b86c5 languageName: node linkType: hard @@ -9137,168 +8773,168 @@ __metadata: languageName: node linkType: hard -"jest-regex-util@npm:^29.4.3": - version: 29.4.3 - resolution: "jest-regex-util@npm:29.4.3" - checksum: 96fc7fc28cd4dd73a63c13a526202c4bd8b351d4e5b68b1a2a2c88da3308c2a16e26feaa593083eb0bac38cca1aa9dd05025412e7de013ba963fb8e66af22b8a +"jest-regex-util@npm:^29.6.3": + version: 29.6.3 + resolution: "jest-regex-util@npm:29.6.3" + checksum: 0518beeb9bf1228261695e54f0feaad3606df26a19764bc19541e0fc6e2a3737191904607fb72f3f2ce85d9c16b28df79b7b1ec9443aa08c3ef0e9efda6f8f2a languageName: node linkType: hard -"jest-resolve-dependencies@npm:^29.6.2": - version: 29.6.2 - resolution: "jest-resolve-dependencies@npm:29.6.2" +"jest-resolve-dependencies@npm:^29.7.0": + version: 29.7.0 + resolution: "jest-resolve-dependencies@npm:29.7.0" dependencies: - jest-regex-util: ^29.4.3 - jest-snapshot: ^29.6.2 - checksum: d40ee11af2c9d2ef0dbbcf9a5b7dda37c2b86cf4e5de1705795919fd8927907569115c502116ab56de0dca576d5faa31ec9b636240333b6830a568a63004da17 + jest-regex-util: ^29.6.3 + jest-snapshot: ^29.7.0 + checksum: aeb75d8150aaae60ca2bb345a0d198f23496494677cd6aefa26fc005faf354061f073982175daaf32b4b9d86b26ca928586344516e3e6969aa614cb13b883984 languageName: node linkType: hard -"jest-resolve@npm:^29.6.2": - version: 29.6.2 - resolution: "jest-resolve@npm:29.6.2" +"jest-resolve@npm:^29.7.0": + version: 29.7.0 + resolution: "jest-resolve@npm:29.7.0" dependencies: chalk: ^4.0.0 graceful-fs: ^4.2.9 - jest-haste-map: ^29.6.2 + jest-haste-map: ^29.7.0 jest-pnp-resolver: ^1.2.2 - jest-util: ^29.6.2 - jest-validate: ^29.6.2 + jest-util: ^29.7.0 + jest-validate: ^29.7.0 resolve: ^1.20.0 resolve.exports: ^2.0.0 slash: ^3.0.0 - checksum: 01721957e61821a576b2ded043eeab8b392166e0e6d8d680f75657737e2ea7481ff29c2716b866ccd12e743f3a8da465504b1028e78b6a3c68b9561303de7ec8 + checksum: 0ca218e10731aa17920526ec39deaec59ab9b966237905ffc4545444481112cd422f01581230eceb7e82d86f44a543d520a71391ec66e1b4ef1a578bd5c73487 languageName: node linkType: hard -"jest-runner@npm:^29.6.2": - version: 29.6.2 - resolution: "jest-runner@npm:29.6.2" +"jest-runner@npm:^29.7.0": + version: 29.7.0 + resolution: "jest-runner@npm:29.7.0" dependencies: - "@jest/console": ^29.6.2 - "@jest/environment": ^29.6.2 - "@jest/test-result": ^29.6.2 - "@jest/transform": ^29.6.2 - "@jest/types": ^29.6.1 + "@jest/console": ^29.7.0 + "@jest/environment": ^29.7.0 + "@jest/test-result": ^29.7.0 + "@jest/transform": ^29.7.0 + "@jest/types": ^29.6.3 "@types/node": "*" chalk: ^4.0.0 emittery: ^0.13.1 graceful-fs: ^4.2.9 - jest-docblock: ^29.4.3 - jest-environment-node: ^29.6.2 - jest-haste-map: ^29.6.2 - jest-leak-detector: ^29.6.2 - jest-message-util: ^29.6.2 - jest-resolve: ^29.6.2 - jest-runtime: ^29.6.2 - jest-util: ^29.6.2 - jest-watcher: ^29.6.2 - jest-worker: ^29.6.2 + jest-docblock: ^29.7.0 + jest-environment-node: ^29.7.0 + jest-haste-map: ^29.7.0 + jest-leak-detector: ^29.7.0 + jest-message-util: ^29.7.0 + jest-resolve: ^29.7.0 + jest-runtime: ^29.7.0 + jest-util: ^29.7.0 + jest-watcher: ^29.7.0 + jest-worker: ^29.7.0 p-limit: ^3.1.0 source-map-support: 0.5.13 - checksum: 46bd506a08ddf79628a509aed4105ab74c0b03727a3e24c90bbc2915531860b3da99f7ace2fd9603194440553cffac9cfb1a3b7d0ce03d5fc9c5f2d5ffbb3d3f + checksum: f0405778ea64812bf9b5c50b598850d94ccf95d7ba21f090c64827b41decd680ee19fcbb494007cdd7f5d0d8906bfc9eceddd8fa583e753e736ecd462d4682fb languageName: node linkType: hard -"jest-runtime@npm:^29.6.2": - version: 29.6.2 - resolution: "jest-runtime@npm:29.6.2" +"jest-runtime@npm:^29.7.0": + version: 29.7.0 + resolution: "jest-runtime@npm:29.7.0" dependencies: - "@jest/environment": ^29.6.2 - "@jest/fake-timers": ^29.6.2 - "@jest/globals": ^29.6.2 - "@jest/source-map": ^29.6.0 - "@jest/test-result": ^29.6.2 - "@jest/transform": ^29.6.2 - "@jest/types": ^29.6.1 + "@jest/environment": ^29.7.0 + "@jest/fake-timers": ^29.7.0 + "@jest/globals": ^29.7.0 + "@jest/source-map": ^29.6.3 + "@jest/test-result": ^29.7.0 + "@jest/transform": ^29.7.0 + "@jest/types": ^29.6.3 "@types/node": "*" chalk: ^4.0.0 cjs-module-lexer: ^1.0.0 collect-v8-coverage: ^1.0.0 glob: ^7.1.3 graceful-fs: ^4.2.9 - jest-haste-map: ^29.6.2 - jest-message-util: ^29.6.2 - jest-mock: ^29.6.2 - jest-regex-util: ^29.4.3 - jest-resolve: ^29.6.2 - jest-snapshot: ^29.6.2 - jest-util: ^29.6.2 + jest-haste-map: ^29.7.0 + jest-message-util: ^29.7.0 + jest-mock: ^29.7.0 + jest-regex-util: ^29.6.3 + jest-resolve: ^29.7.0 + jest-snapshot: ^29.7.0 + jest-util: ^29.7.0 slash: ^3.0.0 strip-bom: ^4.0.0 - checksum: 8e7e4486b23b01a9c407313681bed0def39680c2ae21cf01347f111983252ec3a024c56493c5411fed53633f02863eed0816099110cbe04b3889aa5babf1042d + checksum: d19f113d013e80691e07047f68e1e3448ef024ff2c6b586ce4f90cd7d4c62a2cd1d460110491019719f3c59bfebe16f0e201ed005ef9f80e2cf798c374eed54e languageName: node linkType: hard -"jest-snapshot@npm:^29.6.2": - version: 29.6.2 - resolution: "jest-snapshot@npm:29.6.2" +"jest-snapshot@npm:^29.7.0": + version: 29.7.0 + resolution: "jest-snapshot@npm:29.7.0" dependencies: "@babel/core": ^7.11.6 "@babel/generator": ^7.7.2 "@babel/plugin-syntax-jsx": ^7.7.2 "@babel/plugin-syntax-typescript": ^7.7.2 "@babel/types": ^7.3.3 - "@jest/expect-utils": ^29.6.2 - "@jest/transform": ^29.6.2 - "@jest/types": ^29.6.1 + "@jest/expect-utils": ^29.7.0 + "@jest/transform": ^29.7.0 + "@jest/types": ^29.6.3 babel-preset-current-node-syntax: ^1.0.0 chalk: ^4.0.0 - expect: ^29.6.2 + expect: ^29.7.0 graceful-fs: ^4.2.9 - jest-diff: ^29.6.2 - jest-get-type: ^29.4.3 - jest-matcher-utils: ^29.6.2 - jest-message-util: ^29.6.2 - jest-util: ^29.6.2 + jest-diff: ^29.7.0 + jest-get-type: ^29.6.3 + jest-matcher-utils: ^29.7.0 + jest-message-util: ^29.7.0 + jest-util: ^29.7.0 natural-compare: ^1.4.0 - pretty-format: ^29.6.2 + pretty-format: ^29.7.0 semver: ^7.5.3 - checksum: c1c70a9dbce7fca62ed73ac38234b4ee643e8b667acf71b4417ab67776c1188bb08b8ad450e56a2889ad182903ffd416386fa8082a477724ccf8d8c29a4c6906 + checksum: 86821c3ad0b6899521ce75ee1ae7b01b17e6dfeff9166f2cf17f012e0c5d8c798f30f9e4f8f7f5bed01ea7b55a6bc159f5eda778311162cbfa48785447c237ad languageName: node linkType: hard -"jest-util@npm:^29.0.0, jest-util@npm:^29.6.2": - version: 29.6.2 - resolution: "jest-util@npm:29.6.2" +"jest-util@npm:^29.0.0, jest-util@npm:^29.7.0": + version: 29.7.0 + resolution: "jest-util@npm:29.7.0" dependencies: - "@jest/types": ^29.6.1 + "@jest/types": ^29.6.3 "@types/node": "*" chalk: ^4.0.0 ci-info: ^3.2.0 graceful-fs: ^4.2.9 picomatch: ^2.2.3 - checksum: 8aedc0c80083d0cabd6c6c4f04dea1cbcac609fd7bc3b1fc05a3999291bd6e63dd52b0c806f9378d5cae28eff5a6191709a4987861001293f8d03e53984adca4 + checksum: 042ab4980f4ccd4d50226e01e5c7376a8556b472442ca6091a8f102488c0f22e6e8b89ea874111d2328a2080083bf3225c86f3788c52af0bd0345a00eb57a3ca languageName: node linkType: hard -"jest-validate@npm:^29.6.2": - version: 29.6.2 - resolution: "jest-validate@npm:29.6.2" +"jest-validate@npm:^29.7.0": + version: 29.7.0 + resolution: "jest-validate@npm:29.7.0" dependencies: - "@jest/types": ^29.6.1 + "@jest/types": ^29.6.3 camelcase: ^6.2.0 chalk: ^4.0.0 - jest-get-type: ^29.4.3 + jest-get-type: ^29.6.3 leven: ^3.1.0 - pretty-format: ^29.6.2 - checksum: 32648d002189c0ad8a958eace7c6b7d05ea1dc440a1b91e0f22dc1aef489899446ec80b2d527fd13713862d89dfb4606e24a3bf8a10c4ddac3c911e93b7f0374 + pretty-format: ^29.7.0 + checksum: 191fcdc980f8a0de4dbdd879fa276435d00eb157a48683af7b3b1b98b0f7d9de7ffe12689b617779097ff1ed77601b9f7126b0871bba4f776e222c40f62e9dae languageName: node linkType: hard -"jest-watcher@npm:^29.6.2": - version: 29.6.2 - resolution: "jest-watcher@npm:29.6.2" +"jest-watcher@npm:^29.7.0": + version: 29.7.0 + resolution: "jest-watcher@npm:29.7.0" dependencies: - "@jest/test-result": ^29.6.2 - "@jest/types": ^29.6.1 + "@jest/test-result": ^29.7.0 + "@jest/types": ^29.6.3 "@types/node": "*" ansi-escapes: ^4.2.1 chalk: ^4.0.0 emittery: ^0.13.1 - jest-util: ^29.6.2 + jest-util: ^29.7.0 string-length: ^4.0.1 - checksum: 14624190fc8b5fbae466a2ec81458a88c15716d99f042bb4674d53e9623d305cb2905bc1dffeda05fd1a10a05c2a83efe5ac41942477e2b15eaebb08d0aaab32 + checksum: 67e6e7fe695416deff96b93a14a561a6db69389a0667e9489f24485bb85e5b54e12f3b2ba511ec0b777eca1e727235b073e3ebcdd473d68888650489f88df92f languageName: node linkType: hard @@ -9313,26 +8949,26 @@ __metadata: languageName: node linkType: hard -"jest-worker@npm:^29.6.2": - version: 29.6.2 - resolution: "jest-worker@npm:29.6.2" +"jest-worker@npm:^29.7.0": + version: 29.7.0 + resolution: "jest-worker@npm:29.7.0" dependencies: "@types/node": "*" - jest-util: ^29.6.2 + jest-util: ^29.7.0 merge-stream: ^2.0.0 supports-color: ^8.0.0 - checksum: 11035564534bf181ead80b25be138c2d42372bd5626151a3e705200d47a74fd9da3ca79f8a7b15806cdc325ad73c3d21d23acceeed99d50941589ff02915ed38 + checksum: 30fff60af49675273644d408b650fc2eb4b5dcafc5a0a455f238322a8f9d8a98d847baca9d51ff197b6747f54c7901daa2287799230b856a0f48287d131f8c13 languageName: node linkType: hard "jest@npm:^29.5.0": - version: 29.6.2 - resolution: "jest@npm:29.6.2" + version: 29.7.0 + resolution: "jest@npm:29.7.0" dependencies: - "@jest/core": ^29.6.2 - "@jest/types": ^29.6.1 + "@jest/core": ^29.7.0 + "@jest/types": ^29.6.3 import-local: ^3.0.2 - jest-cli: ^29.6.2 + jest-cli: ^29.7.0 peerDependencies: node-notifier: ^8.0.1 || ^9.0.0 || ^10.0.0 peerDependenciesMeta: @@ -9340,7 +8976,7 @@ __metadata: optional: true bin: jest: bin/jest.js - checksum: dd63facd4e6aefc35d2c42acd7e4c9fb0d8fe4705df4b3ccedd953605424d7aa89c88af8cf4c9951752709cac081d29c35b264e1794643d5688ea724ccc9a485 + checksum: 17ca8d67504a7dbb1998cf3c3077ec9031ba3eb512da8d71cb91bcabb2b8995c4e4b292b740cb9bf1cbff5ce3e110b3f7c777b0cefb6f41ab05445f248d0ee0b languageName: node linkType: hard @@ -9397,9 +9033,16 @@ __metadata: languageName: node linkType: hard +"json-buffer@npm:3.0.1": + version: 3.0.1 + resolution: "json-buffer@npm:3.0.1" + checksum: 9026b03edc2847eefa2e37646c579300a1f3a4586cfb62bf857832b60c852042d0d6ae55d1afb8926163fa54c2b01d83ae24705f34990348bdac6273a29d4581 + languageName: node + linkType: hard + "json-joy@npm:^9.2.0": - version: 9.6.0 - resolution: "json-joy@npm:9.6.0" + version: 9.9.1 + resolution: "json-joy@npm:9.9.1" dependencies: arg: ^5.0.2 hyperdyperid: ^1.2.0 @@ -9416,7 +9059,7 @@ __metadata: json-pointer: bin/json-pointer.js json-pointer-test: bin/json-pointer-test.js json-unpack: bin/json-unpack.js - checksum: 517b1b466b1b477bd53ecf23693758e785cebba5bf32dbb04059164b067330246efd35f11df13e8a04fb85ec4c330f806e2752736189a12f1a4ff1a1e423ec27 + checksum: d165398682f00019796225faf365cd8d060f3e086af39bb5081c30907b7e52eaf13697d1c0f6ee2b010fe255ae1fd776e05ad7d6ee5fb549e98fe982f560884b languageName: node linkType: hard @@ -9452,7 +9095,7 @@ __metadata: languageName: node linkType: hard -"json5@npm:^2.2.2, json5@npm:^2.2.3": +"json5@npm:^2.2.3": version: 2.2.3 resolution: "json5@npm:2.2.3" bin: @@ -9462,9 +9105,9 @@ __metadata: linkType: hard "jsonc-parser@npm:^3.2.0": - version: 3.2.0 - resolution: "jsonc-parser@npm:3.2.0" - checksum: 946dd9a5f326b745aa326d48a7257e3f4a4b62c5e98ec8e49fa2bdd8d96cef7e6febf1399f5c7016114fd1f68a1c62c6138826d5d90bc650448e3cf0951c53c7 + version: 3.2.1 + resolution: "jsonc-parser@npm:3.2.1" + checksum: 656d9027b91de98d8ab91b3aa0d0a4cab7dc798a6830845ca664f3e76c82d46b973675bbe9b500fae1de37fd3e81aceacbaa2a57884bf2f8f29192150d2d1ef7 languageName: node linkType: hard @@ -9514,6 +9157,15 @@ __metadata: languageName: node linkType: hard +"keyv@npm:^4.5.3": + version: 4.5.4 + resolution: "keyv@npm:4.5.4" + dependencies: + json-buffer: 3.0.1 + checksum: 74a24395b1c34bd44ad5cb2b49140d087553e170625240b86755a6604cd65aa16efdbdeae5cdb17ba1284a0fbb25ad06263755dbc71b8d8b06f74232ce3cdd72 + languageName: node + linkType: hard + "kind-of@npm:^6.0.2, kind-of@npm:^6.0.3": version: 6.0.3 resolution: "kind-of@npm:6.0.3" @@ -9583,14 +9235,15 @@ __metadata: linkType: hard "koa-router@npm:^12.0.0": - version: 12.0.0 - resolution: "koa-router@npm:12.0.0" + version: 12.0.1 + resolution: "koa-router@npm:12.0.1" dependencies: + debug: ^4.3.4 http-errors: ^2.0.0 koa-compose: ^4.1.0 methods: ^1.1.2 path-to-regexp: ^6.2.1 - checksum: 29b02fd96972c037e805f6ce2626c971f4fd9cba04005bfedc080ab425d31b4b1cfe2ebc000b26e4a45e68215a3a3ed557f836ba486ea0d2f1e7e78fc95f8dca + checksum: 62852af6c47dba9327c993594367b8bcd995fd49972d2539eae9afa231571680f75ebe0f439bf886256cdfaf791900c2d11fe31b894d9ba8c60d1c727c03b9ed languageName: node linkType: hard @@ -9616,14 +9269,14 @@ __metadata: linkType: hard "koa@npm:^2.14.2": - version: 2.14.2 - resolution: "koa@npm:2.14.2" + version: 2.15.0 + resolution: "koa@npm:2.15.0" dependencies: accepts: ^1.3.5 cache-content-type: ^1.0.0 content-disposition: ~0.5.2 content-type: ^1.0.4 - cookies: ~0.8.0 + cookies: ~0.9.0 debug: ^4.3.2 delegates: ^1.0.0 depd: ^2.0.0 @@ -9642,7 +9295,7 @@ __metadata: statuses: ^1.5.0 type-is: ^1.6.16 vary: ^1.1.2 - checksum: 17fe3b8f5e0b4759004a942cc6ba2a9507299943a697dff9766b85f41f45caed4077ca2645ac9ad254d3359fffedfc4c9ebdd7a70493e5df8cdfac159a8ee835 + checksum: a97741f89f328f25ae94d82d0ee608377d89e086c73f2d868023e6050dea682ef93e0a5c80097f3aaad28121853aea50a7fb3c0c12ecc45798da2fd1255f580b languageName: node linkType: hard @@ -9730,26 +9383,25 @@ __metadata: linkType: hard "libp2p@npm:^0.46.6": - version: 0.46.6 - resolution: "libp2p@npm:0.46.6" + version: 0.46.21 + resolution: "libp2p@npm:0.46.21" dependencies: "@achingbrain/nat-port-mapper": ^1.0.9 - "@libp2p/crypto": ^2.0.3 - "@libp2p/interface": ^0.1.2 - "@libp2p/interface-internal": ^0.1.4 - "@libp2p/keychain": ^3.0.3 - "@libp2p/logger": ^3.0.2 - "@libp2p/multistream-select": ^4.0.2 - "@libp2p/peer-collections": ^4.0.3 - "@libp2p/peer-id": ^3.0.2 - "@libp2p/peer-id-factory": ^3.0.3 - "@libp2p/peer-record": ^6.0.3 - "@libp2p/peer-store": ^9.0.3 - "@libp2p/utils": ^4.0.2 + "@libp2p/crypto": ^2.0.8 + "@libp2p/interface": ^0.1.6 + "@libp2p/interface-internal": ^0.1.9 + "@libp2p/keychain": ^3.0.8 + "@libp2p/logger": ^3.1.0 + "@libp2p/multistream-select": ^4.0.6 + "@libp2p/peer-collections": ^4.0.8 + "@libp2p/peer-id": ^3.0.6 + "@libp2p/peer-id-factory": ^3.0.8 + "@libp2p/peer-record": ^6.0.9 + "@libp2p/peer-store": ^9.0.9 + "@libp2p/utils": ^4.0.7 "@multiformats/mafmt": ^12.1.2 "@multiformats/multiaddr": ^12.1.5 "@multiformats/multiaddr-matcher": ^1.0.0 - abortable-iterator: ^5.0.1 any-signal: ^4.1.1 datastore-core: ^9.0.1 delay: ^6.0.0 @@ -9771,15 +9423,15 @@ __metadata: multiformats: ^12.0.1 p-defer: ^4.0.0 p-queue: ^7.3.4 - p-retry: ^5.0.0 + p-retry: ^6.0.0 private-ip: ^3.0.0 protons-runtime: ^5.0.0 - rate-limiter-flexible: ^2.3.11 + rate-limiter-flexible: ^3.0.0 uint8arraylist: ^2.4.3 uint8arrays: ^4.0.6 wherearewe: ^2.0.1 xsalsa20: ^1.1.0 - checksum: c23d6621f1ed7d5c96de440fb08bfc807a8fc38c44e54ed6422f9414414dae0c7e87675f9416eacd5b143a2ff91fba26e6001bdde670f80696c4e73574fc228e + checksum: 6f69e89e8f4ac28ee1b365db878379d28865982bf6d4cec9524d9445975b5eab4fa5dca581e704ce1438b196258e88856cf6778048b59a75ae09ca1b9417fbb8 languageName: node linkType: hard @@ -9799,16 +9451,16 @@ __metadata: languageName: node linkType: hard -"lmdb@npm:^2.9.1": - version: 2.9.1 - resolution: "lmdb@npm:2.9.1" +"lmdb@npm:^2.9.2": + version: 2.9.2 + resolution: "lmdb@npm:2.9.2" dependencies: - "@lmdb/lmdb-darwin-arm64": 2.9.1 - "@lmdb/lmdb-darwin-x64": 2.9.1 - "@lmdb/lmdb-linux-arm": 2.9.1 - "@lmdb/lmdb-linux-arm64": 2.9.1 - "@lmdb/lmdb-linux-x64": 2.9.1 - "@lmdb/lmdb-win32-x64": 2.9.1 + "@lmdb/lmdb-darwin-arm64": 2.9.2 + "@lmdb/lmdb-darwin-x64": 2.9.2 + "@lmdb/lmdb-linux-arm": 2.9.2 + "@lmdb/lmdb-linux-arm64": 2.9.2 + "@lmdb/lmdb-linux-x64": 2.9.2 + "@lmdb/lmdb-win32-x64": 2.9.2 msgpackr: ^1.9.9 node-addon-api: ^6.1.0 node-gyp: latest @@ -9830,7 +9482,7 @@ __metadata: optional: true bin: download-lmdb-prebuilds: bin/download-prebuilds.js - checksum: 1f0a8754cc019586c8e34bd45e4ee1df99f6f5732e8dc04f951cf631895a179dfd913123773206935a580cfe80bce117800a3ccf0a2cc8187821badfdaa71cd4 + checksum: b2471c4d2c36f15a27233ae1eece86fcbb40613574ec54245cf697b16b1b35c70c72e4092dc739407df145f14b7c1b6b56c4281a439c314e79a5338df8b2b63b languageName: node linkType: hard @@ -9987,33 +9639,23 @@ __metadata: linkType: hard "logform@npm:^2.3.2, logform@npm:^2.4.0": - version: 2.5.1 - resolution: "logform@npm:2.5.1" + version: 2.6.0 + resolution: "logform@npm:2.6.0" dependencies: - "@colors/colors": 1.5.0 + "@colors/colors": 1.6.0 "@types/triple-beam": ^1.3.2 fecha: ^4.2.0 ms: ^2.1.1 safe-stable-stringify: ^2.3.1 triple-beam: ^1.3.0 - checksum: 08fdf03be5bb69af33bac214eb4f6a0c83ad3821a30de498925fccb61e993e5a4a87470aab356ca2110c11e4643685bed5597ca5f46dd1cd11437c44a0e0e3c2 - languageName: node - linkType: hard - -"long@npm:^5.0.0": - version: 5.2.3 - resolution: "long@npm:5.2.3" - checksum: 885ede7c3de4facccbd2cacc6168bae3a02c3e836159ea4252c87b6e34d40af819824b2d4edce330bfb5c4d6e8ce3ec5864bdcf9473fa1f53a4f8225860e5897 + checksum: b9ea74bb75e55379ad0eb3e4d65ae6e8d02bc45b431c218162878bf663997ab9258a73104c2b30e09dd2db288bb83c8bf8748e46689d75f5e7e34cf69378d6df languageName: node linkType: hard -"longbits@npm:^1.1.0": - version: 1.1.0 - resolution: "longbits@npm:1.1.0" - dependencies: - byte-access: ^1.0.1 - uint8arraylist: ^2.0.0 - checksum: 7f8ec8ddef64b160da22c31875fa549b40a4cff626c948c423dc2cb73c0d85a5c6a13ce3a611b5229fcf65dd6ffb4fcd1d5eaef61458570194172ad485112521 +"lru-cache@npm:^10.0.1, lru-cache@npm:^9.1.1 || ^10.0.0": + version: 10.2.0 + resolution: "lru-cache@npm:10.2.0" + checksum: eee7ddda4a7475deac51ac81d7dd78709095c6fa46e8350dc2d22462559a1faa3b81ed931d5464b13d48cbd7e08b46100b6f768c76833912bc444b99c37e25db languageName: node linkType: hard @@ -10035,20 +9677,13 @@ __metadata: languageName: node linkType: hard -"lru-cache@npm:^7.14.1, lru-cache@npm:^7.7.1": +"lru-cache@npm:^7.14.1": version: 7.18.3 resolution: "lru-cache@npm:7.18.3" checksum: e550d772384709deea3f141af34b6d4fa392e2e418c1498c078de0ee63670f1f46f5eee746e8ef7e69e1c895af0d4224e62ee33e66a543a14763b0f2e74c1356 languageName: node linkType: hard -"lru-cache@npm:^9.1.1 || ^10.0.0": - version: 10.0.0 - resolution: "lru-cache@npm:10.0.0" - checksum: 18f101675fe283bc09cda0ef1e3cc83781aeb8373b439f086f758d1d91b28730950db785999cd060d3c825a8571c03073e8c14512b6655af2188d623031baf50 - languageName: node - linkType: hard - "ltgt@npm:^2.2.0": version: 2.2.1 resolution: "ltgt@npm:2.2.1" @@ -10116,26 +9751,22 @@ __metadata: languageName: node linkType: hard -"make-fetch-happen@npm:^11.0.3": - version: 11.1.1 - resolution: "make-fetch-happen@npm:11.1.1" +"make-fetch-happen@npm:^13.0.0": + version: 13.0.0 + resolution: "make-fetch-happen@npm:13.0.0" dependencies: - agentkeepalive: ^4.2.1 - cacache: ^17.0.0 + "@npmcli/agent": ^2.0.0 + cacache: ^18.0.0 http-cache-semantics: ^4.1.1 - http-proxy-agent: ^5.0.0 - https-proxy-agent: ^5.0.0 is-lambda: ^1.0.1 - lru-cache: ^7.7.1 - minipass: ^5.0.0 + minipass: ^7.0.2 minipass-fetch: ^3.0.0 minipass-flush: ^1.0.5 minipass-pipeline: ^1.2.4 negotiator: ^0.6.3 promise-retry: ^2.0.1 - socks-proxy-agent: ^7.0.0 ssri: ^10.0.0 - checksum: 7268bf274a0f6dcf0343829489a4506603ff34bd0649c12058753900b0eb29191dce5dba12680719a5d0a983d3e57810f594a12f3c18494e93a1fbc6348a4540 + checksum: 7c7a6d381ce919dd83af398b66459a10e2fe8f4504f340d1d090d3fa3d1b0c93750220e1d898114c64467223504bd258612ba83efbc16f31b075cd56de24b4af languageName: node linkType: hard @@ -10352,6 +9983,15 @@ __metadata: languageName: node linkType: hard +"minimatch@npm:9.0.3, minimatch@npm:^9.0.0, minimatch@npm:^9.0.1": + version: 9.0.3 + resolution: "minimatch@npm:9.0.3" + dependencies: + brace-expansion: ^2.0.1 + checksum: 253487976bf485b612f16bf57463520a14f512662e592e95c571afdab1442a6a6864b6c88f248ce6fc4ff0b6de04ac7aa6c8bb51e868e99d1d65eb0658a708b5 + languageName: node + linkType: hard + "minimatch@npm:^3.0.4, minimatch@npm:^3.0.5, minimatch@npm:^3.1.1, minimatch@npm:^3.1.2": version: 3.1.2 resolution: "minimatch@npm:3.1.2" @@ -10361,15 +10001,6 @@ __metadata: languageName: node linkType: hard -"minimatch@npm:^9.0.0, minimatch@npm:^9.0.1": - version: 9.0.3 - resolution: "minimatch@npm:9.0.3" - dependencies: - brace-expansion: ^2.0.1 - checksum: 253487976bf485b612f16bf57463520a14f512662e592e95c571afdab1442a6a6864b6c88f248ce6fc4ff0b6de04ac7aa6c8bb51e868e99d1d65eb0658a708b5 - languageName: node - linkType: hard - "minimist-options@npm:4.1.0": version: 4.1.0 resolution: "minimist-options@npm:4.1.0" @@ -10388,27 +10019,27 @@ __metadata: languageName: node linkType: hard -"minipass-collect@npm:^1.0.2": - version: 1.0.2 - resolution: "minipass-collect@npm:1.0.2" +"minipass-collect@npm:^2.0.1": + version: 2.0.1 + resolution: "minipass-collect@npm:2.0.1" dependencies: - minipass: ^3.0.0 - checksum: 14df761028f3e47293aee72888f2657695ec66bd7d09cae7ad558da30415fdc4752bbfee66287dcc6fd5e6a2fa3466d6c484dc1cbd986525d9393b9523d97f10 + minipass: ^7.0.3 + checksum: b251bceea62090f67a6cced7a446a36f4cd61ee2d5cea9aee7fff79ba8030e416327a1c5aa2908dc22629d06214b46d88fdab8c51ac76bacbf5703851b5ad342 languageName: node linkType: hard "minipass-fetch@npm:^3.0.0": - version: 3.0.3 - resolution: "minipass-fetch@npm:3.0.3" + version: 3.0.4 + resolution: "minipass-fetch@npm:3.0.4" dependencies: encoding: ^0.1.13 - minipass: ^5.0.0 + minipass: ^7.0.3 minipass-sized: ^1.0.3 minizlib: ^2.1.2 dependenciesMeta: encoding: optional: true - checksum: af5ab2552a16fcf505d35fd7ffb84b57f4a0eeb269e6e1d9a2a75824dda48b36e527083250b7cca4a4def21d9544e2ade441e4730e233c0bc2133f6abda31e18 + checksum: af7aad15d5c128ab1ebe52e043bdf7d62c3c6f0cecb9285b40d7b395e1375b45dcdfd40e63e93d26a0e8249c9efd5c325c65575aceee192883970ff8cb11364a languageName: node linkType: hard @@ -10455,10 +10086,10 @@ __metadata: languageName: node linkType: hard -"minipass@npm:^5.0.0 || ^6.0.2 || ^7.0.0": - version: 7.0.2 - resolution: "minipass@npm:7.0.2" - checksum: 46776de732eb7cef2c7404a15fb28c41f5c54a22be50d47b03c605bf21f5c18d61a173c0a20b49a97e7a65f78d887245066410642551e45fffe04e9ac9e325bc +"minipass@npm:^5.0.0 || ^6.0.2 || ^7.0.0, minipass@npm:^7.0.2, minipass@npm:^7.0.3": + version: 7.0.4 + resolution: "minipass@npm:7.0.4" + checksum: 87585e258b9488caf2e7acea242fd7856bbe9a2c84a7807643513a338d66f368c7d518200ad7b70a508664d408aa000517647b2930c259a8b1f9f0984f344a21 languageName: node linkType: hard @@ -10535,21 +10166,20 @@ __metadata: linkType: hard "moment@npm:^2.29.1": - version: 2.29.4 - resolution: "moment@npm:2.29.4" - checksum: 0ec3f9c2bcba38dc2451b1daed5daded747f17610b92427bebe1d08d48d8b7bdd8d9197500b072d14e326dd0ccf3e326b9e3d07c5895d3d49e39b6803b76e80e + version: 2.30.1 + resolution: "moment@npm:2.30.1" + checksum: 859236bab1e88c3e5802afcf797fc801acdbd0ee509d34ea3df6eea21eb6bcc2abd4ae4e4e64aa7c986aa6cba563c6e62806218e6412a765010712e5fa121ba6 languageName: node linkType: hard "mortice@npm:^3.0.1": - version: 3.0.1 - resolution: "mortice@npm:3.0.1" + version: 3.0.4 + resolution: "mortice@npm:3.0.4" dependencies: - nanoid: ^4.0.0 observable-webworkers: ^2.0.1 - p-queue: ^7.2.0 + p-queue: ^8.0.1 p-timeout: ^6.0.0 - checksum: a6b23c98bdea9a18f25d0431424f0c9df54e6af5f2ebe9d5752b50bccc1fa19e2559007d813f7fc14171a9253ebc60c8702f0cda51a57c8fd0ae420abde83761 + checksum: 64d63b6d724636e94f59a8f72208561d621f601707df17e8c1ea5e236bc3f208eae98609586898458851228733908028d61b1bf5a2b6db58bd2aa9c1f7e8166b languageName: node linkType: hard @@ -10560,7 +10190,7 @@ __metadata: languageName: node linkType: hard -"ms@npm:^2.0.0, ms@npm:^2.1.1": +"ms@npm:^2.1.1": version: 2.1.3 resolution: "ms@npm:2.1.3" checksum: aa92de608021b242401676e35cfa5aa42dd70cbdc082b916da7fb925c542173e36bce97ea3e804923fe92c0ad991434e4a38327e15a1b5b5f945d66df615ae6d @@ -10599,14 +10229,14 @@ __metadata: linkType: hard "msgpackr@npm:^1.9.9": - version: 1.9.9 - resolution: "msgpackr@npm:1.9.9" + version: 1.10.1 + resolution: "msgpackr@npm:1.10.1" dependencies: msgpackr-extract: ^3.0.2 dependenciesMeta: msgpackr-extract: optional: true - checksum: b63182d99f479d79f0d082fd2688ce7cf699b1aee71e20f28591c30b48743bb57868fdd72656759a892891072d186d864702c756434520709e8fe7e0d350a119 + checksum: e422d18b01051598b23701eebeb4b9e2c686b9c7826b20f564724837ba2b5cd4af74c91a549eaeaf8186645cc95e8196274a4a19442aa3286ac611b98069c194 languageName: node linkType: hard @@ -10618,27 +10248,25 @@ __metadata: linkType: hard "multiformats@npm:^12.0.1": - version: 12.0.1 - resolution: "multiformats@npm:12.0.1" - checksum: 227f5a0bb835e998d105028ffaa4cec944b6461c7b7f3ddc4f3aca22f6917ad59643302898c67af167a6f9d7abddcab094e501488664e2b9437f0b6a9b2ab68d + version: 12.1.3 + resolution: "multiformats@npm:12.1.3" + checksum: 1060488612f8e6729c600f47a8741b91aa6e7b807ce165eca3c8cf07ab7465d2d9b212415a9c18886938b9e35b30ea7b9ae19b5ab5122589c65063440643e6bb languageName: node linkType: hard -"nanoid@npm:^3.3.6": - version: 3.3.6 - resolution: "nanoid@npm:3.3.6" - bin: - nanoid: bin/nanoid.cjs - checksum: 7d0eda657002738aa5206107bd0580aead6c95c460ef1bdd0b1a87a9c7ae6277ac2e9b945306aaa5b32c6dcb7feaf462d0f552e7f8b5718abfc6ead5c94a71b3 +"multiformats@npm:^13.0.0": + version: 13.0.1 + resolution: "multiformats@npm:13.0.1" + checksum: 63e5d6ee2c2a1d1e8fbd4b8c76fa41cf3d8204ceed1d57bc44cb30ff3d06b880ad58c3de52ae6d4397a662a6f1e3285dae74ee5d445fd1597516e1baec96d22a languageName: node linkType: hard -"nanoid@npm:^4.0.0": - version: 4.0.2 - resolution: "nanoid@npm:4.0.2" +"nanoid@npm:^3.3.7": + version: 3.3.7 + resolution: "nanoid@npm:3.3.7" bin: - nanoid: bin/nanoid.js - checksum: 747c399cea4664dd0be1d0ec498ffd1ef8f1f5221676fc8b577e3f46f66d9afcddb9595d63d19a2e78d0bc6cc33984f65e66bf1682c850b9e26288883d96b53f + nanoid: bin/nanoid.cjs + checksum: d36c427e530713e4ac6567d488b489a36582ef89da1d6d4e3b87eded11eb10d7042a877958c6f104929809b2ab0bafa17652b076cdf84324aa75b30b722204f2 languageName: node linkType: hard @@ -10649,22 +10277,6 @@ __metadata: languageName: node linkType: hard -"native-fetch@npm:^4.0.2": - version: 4.0.2 - resolution: "native-fetch@npm:4.0.2" - peerDependencies: - undici: "*" - checksum: 11e6d075aa03d40665a5fc438c56b535622fb4ee98eb2b035277c5ba47733cb4c7bc3ddb45e5ab8154869b509fc18ca1c0188ab271139ae89db14f9f552fc064 - languageName: node - linkType: hard - -"natural-compare-lite@npm:^1.4.0": - version: 1.4.0 - resolution: "natural-compare-lite@npm:1.4.0" - checksum: 5222ac3986a2b78dd6069ac62cbb52a7bf8ffc90d972ab76dfe7b01892485d229530ed20d0c62e79a6b363a663b273db3bde195a1358ce9e5f779d4453887225 - languageName: node - linkType: hard - "natural-compare@npm:^1.4.0": version: 1.4.0 resolution: "natural-compare@npm:1.4.0" @@ -10717,8 +10329,8 @@ __metadata: linkType: hard "node-fetch@npm:^2.6.12": - version: 2.6.12 - resolution: "node-fetch@npm:2.6.12" + version: 2.7.0 + resolution: "node-fetch@npm:2.7.0" dependencies: whatwg-url: ^5.0.0 peerDependencies: @@ -10726,7 +10338,7 @@ __metadata: peerDependenciesMeta: encoding: optional: true - checksum: 3bc1655203d47ee8e313c0d96664b9673a3d4dd8002740318e9d27d14ef306693a4b2ef8d6525775056fd912a19e23f3ac0d7111ad8925877b7567b29a625592 + checksum: d76d2f5edb451a3f05b15115ec89fc6be39de37c6089f1b6368df03b91e1633fd379a7e01b7ab05089a25034b2023d959b47e59759cb38d88341b2459e89d6e5 languageName: node linkType: hard @@ -10773,34 +10385,33 @@ __metadata: linkType: hard "node-gyp-build@npm:^4.3.0": - version: 4.6.0 - resolution: "node-gyp-build@npm:4.6.0" + version: 4.8.0 + resolution: "node-gyp-build@npm:4.8.0" bin: node-gyp-build: bin.js node-gyp-build-optional: optional.js node-gyp-build-test: build-test.js - checksum: 25d78c5ef1f8c24291f4a370c47ba52fcea14f39272041a90a7894cd50d766f7c8cb8fb06c0f42bf6f69b204b49d9be3c8fc344aac09714d5bdb95965499eb15 + checksum: b82a56f866034b559dd3ed1ad04f55b04ae381b22ec2affe74b488d1582473ca6e7f85fccf52da085812d3de2b0bf23109e752a57709ac7b9963951c710fea40 languageName: node linkType: hard "node-gyp@npm:latest": - version: 9.4.0 - resolution: "node-gyp@npm:9.4.0" + version: 10.0.1 + resolution: "node-gyp@npm:10.0.1" dependencies: env-paths: ^2.2.0 exponential-backoff: ^3.1.1 - glob: ^7.1.4 + glob: ^10.3.10 graceful-fs: ^4.2.6 - make-fetch-happen: ^11.0.3 - nopt: ^6.0.0 - npmlog: ^6.0.0 - rimraf: ^3.0.2 + make-fetch-happen: ^13.0.0 + nopt: ^7.0.0 + proc-log: ^3.0.0 semver: ^7.3.5 tar: ^6.1.2 - which: ^2.0.2 + which: ^4.0.0 bin: node-gyp: bin/node-gyp.js - checksum: 78b404e2e0639d64e145845f7f5a3cb20c0520cdaf6dda2f6e025e9b644077202ea7de1232396ba5bde3fee84cdc79604feebe6ba3ec84d464c85d407bb5da99 + checksum: 60a74e66d364903ce02049966303a57f898521d139860ac82744a5fdd9f7b7b3b61f75f284f3bfe6e6add3b8f1871ce305a1d41f775c7482de837b50c792223f languageName: node linkType: hard @@ -10811,10 +10422,10 @@ __metadata: languageName: node linkType: hard -"node-releases@npm:^2.0.13": - version: 2.0.13 - resolution: "node-releases@npm:2.0.13" - checksum: 17ec8f315dba62710cae71a8dad3cd0288ba943d2ece43504b3b1aa8625bf138637798ab470b1d9035b0545996f63000a8a926e0f6d35d0996424f8b6d36dda3 +"node-releases@npm:^2.0.14": + version: 2.0.14 + resolution: "node-releases@npm:2.0.14" + checksum: 59443a2f77acac854c42d321bf1b43dea0aef55cd544c6a686e9816a697300458d4e82239e2d794ea05f7bbbc8a94500332e2d3ac3f11f52e4b16cbe638b3c41 languageName: node linkType: hard @@ -10836,14 +10447,14 @@ __metadata: languageName: node linkType: hard -"nopt@npm:^6.0.0": - version: 6.0.0 - resolution: "nopt@npm:6.0.0" +"nopt@npm:^7.0.0": + version: 7.2.0 + resolution: "nopt@npm:7.2.0" dependencies: - abbrev: ^1.0.0 + abbrev: ^2.0.0 bin: nopt: bin/nopt.js - checksum: 82149371f8be0c4b9ec2f863cc6509a7fd0fa729929c009f3a58e4eb0c9e4cae9920e8f1f8eb46e7d032fec8fb01bede7f0f41a67eb3553b7b8e14fa53de1dac + checksum: a9c0f57fb8cb9cc82ae47192ca2b7ef00e199b9480eed202482c962d61b59a7fbe7541920b2a5839a97b42ee39e288c0aed770e38057a608d7f579389dfde410 languageName: node linkType: hard @@ -10876,23 +10487,11 @@ __metadata: linkType: hard "npm-run-path@npm:^5.1.0": - version: 5.1.0 - resolution: "npm-run-path@npm:5.1.0" + version: 5.2.0 + resolution: "npm-run-path@npm:5.2.0" dependencies: path-key: ^4.0.0 - checksum: dc184eb5ec239d6a2b990b43236845332ef12f4e0beaa9701de724aa797fe40b6bbd0157fb7639d24d3ab13f5d5cf22d223a19c6300846b8126f335f788bee66 - languageName: node - linkType: hard - -"npmlog@npm:^6.0.0": - version: 6.0.2 - resolution: "npmlog@npm:6.0.2" - dependencies: - are-we-there-yet: ^3.0.0 - console-control-strings: ^1.1.0 - gauge: ^4.0.3 - set-blocking: ^2.0.0 - checksum: ae238cd264a1c3f22091cdd9e2b106f684297d3c184f1146984ecbe18aaa86343953f26b9520dedd1b1372bc0316905b736c1932d778dbeb1fcf5a1001390e2a + checksum: c5325e016014e715689c4014f7e0be16cc4cbf529f32a1723e511bc4689b5f823b704d2bca61ac152ce2bda65e0205dc8b3ba0ec0f5e4c3e162d302f6f5b9efb languageName: node linkType: hard @@ -10903,10 +10502,10 @@ __metadata: languageName: node linkType: hard -"object-inspect@npm:^1.12.3, object-inspect@npm:^1.9.0": - version: 1.12.3 - resolution: "object-inspect@npm:1.12.3" - checksum: dabfd824d97a5f407e6d5d24810d888859f6be394d8b733a77442b277e0808860555176719c5905e765e3743a7cada6b8b0a3b85e5331c530fd418cc8ae991db +"object-inspect@npm:^1.13.1, object-inspect@npm:^1.9.0": + version: 1.13.1 + resolution: "object-inspect@npm:1.13.1" + checksum: 7d9fa9221de3311dcb5c7c307ee5dc011cdd31dc43624b7c184b3840514e118e05ef0002be5388304c416c0eb592feb46e983db12577fc47e47d5752fbbfb61f languageName: node linkType: hard @@ -10918,48 +10517,48 @@ __metadata: linkType: hard "object.assign@npm:^4.1.4": - version: 4.1.4 - resolution: "object.assign@npm:4.1.4" + version: 4.1.5 + resolution: "object.assign@npm:4.1.5" dependencies: - call-bind: ^1.0.2 - define-properties: ^1.1.4 + call-bind: ^1.0.5 + define-properties: ^1.2.1 has-symbols: ^1.0.3 object-keys: ^1.1.1 - checksum: 76cab513a5999acbfe0ff355f15a6a125e71805fcf53de4e9d4e082e1989bdb81d1e329291e1e4e0ae7719f0e4ef80e88fb2d367ae60500d79d25a6224ac8864 + checksum: f9aeac0541661370a1fc86e6a8065eb1668d3e771f7dbb33ee54578201336c057b21ee61207a186dd42db0c62201d91aac703d20d12a79fc79c353eed44d4e25 languageName: node linkType: hard -"object.fromentries@npm:^2.0.6": - version: 2.0.6 - resolution: "object.fromentries@npm:2.0.6" +"object.fromentries@npm:^2.0.7": + version: 2.0.7 + resolution: "object.fromentries@npm:2.0.7" dependencies: call-bind: ^1.0.2 - define-properties: ^1.1.4 - es-abstract: ^1.20.4 - checksum: 453c6d694180c0c30df451b60eaf27a5b9bca3fb43c37908fd2b78af895803dc631242bcf05582173afa40d8d0e9c96e16e8874b39471aa53f3ac1f98a085d85 + define-properties: ^1.2.0 + es-abstract: ^1.22.1 + checksum: 7341ce246e248b39a431b87a9ddd331ff52a454deb79afebc95609f94b1f8238966cf21f52188f2a353f0fdf83294f32f1ebf1f7826aae915ebad21fd0678065 languageName: node linkType: hard -"object.groupby@npm:^1.0.0": - version: 1.0.0 - resolution: "object.groupby@npm:1.0.0" +"object.groupby@npm:^1.0.1": + version: 1.0.1 + resolution: "object.groupby@npm:1.0.1" dependencies: call-bind: ^1.0.2 define-properties: ^1.2.0 - es-abstract: ^1.21.2 + es-abstract: ^1.22.1 get-intrinsic: ^1.2.1 - checksum: 64b00b287d57580111c958e7ff375c9b61811fa356f2cf0d35372d43cab61965701f00fac66c19fd8f49c4dfa28744bee6822379c69a73648ad03e09fcdeae70 + checksum: d7959d6eaaba358b1608066fc67ac97f23ce6f573dc8fc661f68c52be165266fcb02937076aedb0e42722fdda0bdc0bbf74778196ac04868178888e9fd3b78b5 languageName: node linkType: hard -"object.values@npm:^1.1.6": - version: 1.1.6 - resolution: "object.values@npm:1.1.6" +"object.values@npm:^1.1.7": + version: 1.1.7 + resolution: "object.values@npm:1.1.7" dependencies: call-bind: ^1.0.2 - define-properties: ^1.1.4 - es-abstract: ^1.20.4 - checksum: f6fff9fd817c24cfd8107f50fb33061d81cd11bacc4e3dbb3852e9ff7692fde4dbce823d4333ea27cd9637ef1b6690df5fbb61f1ed314fa2959598dc3ae23d8e + define-properties: ^1.2.0 + es-abstract: ^1.22.1 + checksum: f3e4ae4f21eb1cc7cebb6ce036d4c67b36e1c750428d7b7623c56a0db90edced63d08af8a316d81dfb7c41a3a5fa81b05b7cc9426e98d7da986b1682460f0777 languageName: node linkType: hard @@ -11022,18 +10621,6 @@ __metadata: languageName: node linkType: hard -"open@npm:^9.1.0": - version: 9.1.0 - resolution: "open@npm:9.1.0" - dependencies: - default-browser: ^4.0.0 - define-lazy-prop: ^3.0.0 - is-inside-container: ^1.0.0 - is-wsl: ^2.2.0 - checksum: 3993c0f61d51fed8ac290e99c9c3cf45d3b6cfb3e2aa2b74cafd312c3486c22fd81df16ac8f3ab91dd8a4e3e729a16fc2480cfc406c4833416cf908acf1ae7c9 - languageName: node - linkType: hard - "optionator@npm:^0.9.3": version: 0.9.3 resolution: "optionator@npm:0.9.3" @@ -11066,9 +10653,9 @@ __metadata: linkType: hard "ordered-binary@npm:^1.4.1": - version: 1.4.1 - resolution: "ordered-binary@npm:1.4.1" - checksum: 274940b4ef983562e11371c84415c265432a4e1337ab85f8e7669eeab6afee8f655c6c12ecee1cd121aaf399c32f5c781b0d50e460bd42da004eba16dcc66574 + version: 1.5.1 + resolution: "ordered-binary@npm:1.5.1" + checksum: ec4d3a6bd7f8c84afec9def1e599e7d460a45d11f94d07b16fdf62db4d2bc16405d79ef0277c2fdf86332fd2539761278981787d2ecf52376ade8b678104a0e6 languageName: node linkType: hard @@ -11133,23 +10720,34 @@ __metadata: languageName: node linkType: hard -"p-queue@npm:^7.2.0, p-queue@npm:^7.3.4": - version: 7.3.4 - resolution: "p-queue@npm:7.3.4" +"p-queue@npm:^7.3.4": + version: 7.4.1 + resolution: "p-queue@npm:7.4.1" dependencies: - eventemitter3: ^4.0.7 + eventemitter3: ^5.0.1 p-timeout: ^5.0.2 - checksum: a21b8a4dd75f64a4988e4468cc344d1b45132506ddd2c771932d3de446d108ee68713b629e0d3f0809c227bc10eafc613edde6ae741d9f60db89b6031e40921c + checksum: 1c6888aa994d399262a9fbdd49c7066f8359732397f7a42ecf03f22875a1d65899797b46413f97e44acc18dddafbcc101eb135c284714c931dbbc83c3967f450 languageName: node linkType: hard -"p-retry@npm:^5.0.0": - version: 5.1.2 - resolution: "p-retry@npm:5.1.2" +"p-queue@npm:^8.0.1": + version: 8.0.1 + resolution: "p-queue@npm:8.0.1" + dependencies: + eventemitter3: ^5.0.1 + p-timeout: ^6.1.2 + checksum: 84a27a5b1faf2dcc96b8c0e423c34b5984b241acc07353d3cc6d8d3d1dadefb250b4ec84ce278cb1c946466999c6bf2a36ff718a75810bad8e11c7ca47ce80f5 + languageName: node + linkType: hard + +"p-retry@npm:^6.0.0": + version: 6.2.0 + resolution: "p-retry@npm:6.2.0" dependencies: - "@types/retry": 0.12.1 + "@types/retry": 0.12.2 + is-network-error: ^1.0.0 retry: ^0.13.1 - checksum: f063c08b1adc3cf7c01de01eb2dbda841970229f9f229c5167ebf4e2080d8a38b1f4e6eccefac74bca97cfaf4436d0a0eeb0b551175b26bc8b3116195f61bba8 + checksum: 6003573c559ee812329c9c3ede7ba12a783fdc8dd70602116646e850c920b4597dc502fe001c3f9526fca4e93275045db7a27341c458e51db179c1374a01ac44 languageName: node linkType: hard @@ -11224,7 +10822,7 @@ __metadata: languageName: node linkType: hard -"parse-asn1@npm:^5.0.0, parse-asn1@npm:^5.1.5": +"parse-asn1@npm:^5.0.0, parse-asn1@npm:^5.1.6": version: 5.1.6 resolution: "parse-asn1@npm:5.1.6" dependencies: @@ -11419,25 +11017,14 @@ __metadata: languageName: node linkType: hard -"postcss@npm:^8.1.7, postcss@npm:^8.4.23": - version: 8.4.31 - resolution: "postcss@npm:8.4.31" - dependencies: - nanoid: ^3.3.6 - picocolors: ^1.0.0 - source-map-js: ^1.0.2 - checksum: 1d8611341b073143ad90486fcdfeab49edd243377b1f51834dc4f6d028e82ce5190e4f11bb2633276864503654fb7cab28e67abdc0fbf9d1f88cad4a0ff0beea - languageName: node - linkType: hard - -"postcss@npm:^8.4.26": - version: 8.4.27 - resolution: "postcss@npm:8.4.27" +"postcss@npm:^8.1.7, postcss@npm:^8.4.23, postcss@npm:^8.4.27": + version: 8.4.33 + resolution: "postcss@npm:8.4.33" dependencies: - nanoid: ^3.3.6 + nanoid: ^3.3.7 picocolors: ^1.0.0 source-map-js: ^1.0.2 - checksum: 1cdd0c298849df6cd65f7e646a3ba36870a37b65f55fd59d1a165539c263e9b4872a402bf4ed1ca1bc31f58b68b2835545e33ea1a23b161a1f8aa6d5ded81e78 + checksum: 6f98b2af4b76632a3de20c4f47bf0e984a1ce1a531cf11adcb0b1d63a6cbda0aae4165e578b66c32ca4879038e3eaad386a6be725a8fb4429c78e3c1ab858fe9 languageName: node linkType: hard @@ -11502,14 +11089,14 @@ __metadata: languageName: node linkType: hard -"pretty-format@npm:^29.0.0, pretty-format@npm:^29.6.2": - version: 29.6.2 - resolution: "pretty-format@npm:29.6.2" +"pretty-format@npm:^29.0.0, pretty-format@npm:^29.7.0": + version: 29.7.0 + resolution: "pretty-format@npm:29.7.0" dependencies: - "@jest/schemas": ^29.6.0 + "@jest/schemas": ^29.6.3 ansi-styles: ^5.0.0 react-is: ^18.0.0 - checksum: a0f972a44f959023c0df9cdfe9eed7540264d7f7ddf74667db8a5294444d5aa153fd47d20327df10ae86964e2ceec10e46ea06b1a5c9c12e02348b78c952c9fc + checksum: 032c1602383e71e9c0c02a01bbd25d6759d60e9c7cf21937dde8357aa753da348fcec5def5d1002c9678a8524d5fe099ad98861286550ef44de8808cc61e43b6 languageName: node linkType: hard @@ -11523,14 +11110,21 @@ __metadata: linkType: hard "private-ip@npm:^3.0.0": - version: 3.0.1 - resolution: "private-ip@npm:3.0.1" + version: 3.0.2 + resolution: "private-ip@npm:3.0.2" dependencies: "@chainsafe/is-ip": ^2.0.1 ip-regex: ^5.0.0 ipaddr.js: ^2.1.0 netmask: ^2.0.2 - checksum: 43eb0b61cc476d4adddebd3a44bc799abf57898edd6e5db8ba1cd25fb9b1211a4cf3428ba61cc961d683f979ddce5220849c33954a580c08269dafd778ac8085 + checksum: dc05f5a915827e09307ced6fcc9d8a40c6c1be7282aef2ffdfc0d6ce917735197fd5173fea92bbf16c776d46fd694070d1849ed58e785a4cbb587f9ffca0152e + languageName: node + linkType: hard + +"proc-log@npm:^3.0.0": + version: 3.0.0 + resolution: "proc-log@npm:3.0.0" + checksum: 02b64e1b3919e63df06f836b98d3af002b5cd92655cab18b5746e37374bfb73e03b84fe305454614b34c25b485cc687a9eebdccf0242cda8fda2475dd2c97e02 languageName: node linkType: hard @@ -11582,35 +11176,13 @@ __metadata: languageName: node linkType: hard -"protobufjs@npm:^7.0.0": - version: 7.2.4 - resolution: "protobufjs@npm:7.2.4" - dependencies: - "@protobufjs/aspromise": ^1.1.2 - "@protobufjs/base64": ^1.1.2 - "@protobufjs/codegen": ^2.0.4 - "@protobufjs/eventemitter": ^1.1.0 - "@protobufjs/fetch": ^1.1.0 - "@protobufjs/float": ^1.0.2 - "@protobufjs/inquire": ^1.1.0 - "@protobufjs/path": ^1.1.2 - "@protobufjs/pool": ^1.1.0 - "@protobufjs/utf8": ^1.1.0 - "@types/node": ">=13.7.0" - long: ^5.0.0 - checksum: a952cdf2a5e5250c16ae651b570849b6f5b20a5475c3eef63ffb290ad239aa2916adfc1cc676f7fc93c69f48113df268761c0c246f7f023118c85bdd1a170044 - languageName: node - linkType: hard - "protons-runtime@npm:^5.0.0": - version: 5.0.1 - resolution: "protons-runtime@npm:5.0.1" + version: 5.2.2 + resolution: "protons-runtime@npm:5.2.2" dependencies: - protobufjs: ^7.0.0 uint8arraylist: ^2.4.3 - peerDependencies: - uint8arraylist: ^2.3.2 - checksum: 08d14fb7ea57e6329765873bb7933189925cfd2a488419735fb2547b8075f06a34638bc3b95c24c803a6ac179b7b5cff9b0894b6adfa46c38b5b430424a77605 + uint8arrays: ^5.0.1 + checksum: b2c0c3612406eb49539e3f2be7e51bbb3333969b6c1052c8e2cf5cb3eb3aed86eb825eeecd3b5c579e02a545b658b90f046b7e06cc624a843130782bc8e22f6b languageName: node linkType: hard @@ -11673,41 +11245,43 @@ __metadata: linkType: hard "punycode@npm:^2.1.0": - version: 2.3.0 - resolution: "punycode@npm:2.3.0" - checksum: 39f760e09a2a3bbfe8f5287cf733ecdad69d6af2fe6f97ca95f24b8921858b91e9ea3c9eeec6e08cede96181b3bb33f95c6ffd8c77e63986508aa2e8159fa200 + version: 2.3.1 + resolution: "punycode@npm:2.3.1" + checksum: bb0a0ceedca4c3c57a9b981b90601579058903c62be23c5e8e843d2c2d4148a3ecf029d5133486fb0e1822b098ba8bba09e89d6b21742d02fa26bda6441a6fb2 languageName: node linkType: hard -"puppeteer-core@npm:21.3.5": - version: 21.3.5 - resolution: "puppeteer-core@npm:21.3.5" +"puppeteer-core@npm:21.9.0": + version: 21.9.0 + resolution: "puppeteer-core@npm:21.9.0" dependencies: - "@puppeteer/browsers": 1.7.1 - chromium-bidi: 0.4.28 + "@puppeteer/browsers": 1.9.1 + chromium-bidi: 0.5.4 cross-fetch: 4.0.0 debug: 4.3.4 - devtools-protocol: 0.0.1179426 - ws: 8.14.2 - checksum: c2e904e59c9d58ada08016ff893c6b574ab36848db8e0f214b6c02fef446cfa142d346384cb2bb49c39df45622caf6ef813e7cc10f613024e595aa4406fb77bc + devtools-protocol: 0.0.1232444 + ws: 8.16.0 + checksum: 7c92cc0bbbb33fc647ec7dde2902cca47732a5cba65bf95bd46f352c9275e30097ae26a42714d7b6407c83f1940bad82f42d8640e3051efb309fe47b8385ceea languageName: node linkType: hard "puppeteer@npm:^21.3.4": - version: 21.3.5 - resolution: "puppeteer@npm:21.3.5" + version: 21.9.0 + resolution: "puppeteer@npm:21.9.0" dependencies: - "@puppeteer/browsers": 1.7.1 - cosmiconfig: 8.3.6 - puppeteer-core: 21.3.5 - checksum: 0bb3f0f2dead30f4a18b94066d73e27a0ec418ba3e1b73fc882ba4b747405c91ee2592eef0137b984f26bca5e52007543ecd01d1ac843dd68b8d78489a296f69 + "@puppeteer/browsers": 1.9.1 + cosmiconfig: 9.0.0 + puppeteer-core: 21.9.0 + bin: + puppeteer: lib/esm/puppeteer/node/cli.js + checksum: 0cb28d29a183af04869022ff30632116be65f1f8ec4ba729cc36a22332aefee697909adc7c4a15ceae53fdf657c9450aa9b8d8eacab05535adf4972ef2e50038 languageName: node linkType: hard "pure-rand@npm:^6.0.0": - version: 6.0.2 - resolution: "pure-rand@npm:6.0.2" - checksum: 79de33876a4f515d759c48e98d00756bbd916b4ea260cc572d7adfa4b62cace9952e89f0241d0410214554503d25061140fe325c66f845213d2b1728ba8d413e + version: 6.0.4 + resolution: "pure-rand@npm:6.0.4" + checksum: e1c4e69f8bf7303e5252756d67c3c7551385cd34d94a1f511fe099727ccbab74c898c03a06d4c4a24a89b51858781057b83ebbfe740d984240cdc04fead36068 languageName: node linkType: hard @@ -11748,6 +11322,13 @@ __metadata: languageName: node linkType: hard +"race-signal@npm:^1.0.0, race-signal@npm:^1.0.1": + version: 1.0.2 + resolution: "race-signal@npm:1.0.2" + checksum: 01ea1f70059673cd239acbe9523eaf1649f3b02ec786b5266770d9b045018aa96e316150447f0a12e7b0f8aa02522deb23e7d3a2c3a58d37135c505f595f2e49 + languageName: node + linkType: hard + "randombytes@npm:^2.0.0, randombytes@npm:^2.0.1, randombytes@npm:^2.0.5, randombytes@npm:^2.1.0": version: 2.1.0 resolution: "randombytes@npm:2.1.0" @@ -11767,10 +11348,10 @@ __metadata: languageName: node linkType: hard -"rate-limiter-flexible@npm:^2.3.11": - version: 2.4.2 - resolution: "rate-limiter-flexible@npm:2.4.2" - checksum: 039e58b664991963ba2668a83d0406a72e5822683103acbe416854deb92ed834b840ce6e0acfea35917d9b49685bd53946ae47435a9f5916c2e7550395dec9dc +"rate-limiter-flexible@npm:^3.0.0": + version: 3.0.6 + resolution: "rate-limiter-flexible@npm:3.0.6" + checksum: 65cf8edacde55b78255d5de0286b1297ec897fee128f4f18734f36a230297d75433fa599ea423f7726b61b8f8ebf108d66ee09e7155e37aa25f506015b5166a4 languageName: node linkType: hard @@ -11830,7 +11411,7 @@ __metadata: languageName: node linkType: hard -"readable-stream@npm:^3.4.0, readable-stream@npm:^3.5.0, readable-stream@npm:^3.6.0": +"readable-stream@npm:^3.4.0, readable-stream@npm:^3.5.0, readable-stream@npm:^3.6.0, readable-stream@npm:^3.6.2": version: 3.6.2 resolution: "readable-stream@npm:3.6.2" dependencies: @@ -11884,21 +11465,21 @@ __metadata: languageName: node linkType: hard -"regenerator-runtime@npm:^0.13.11": - version: 0.13.11 - resolution: "regenerator-runtime@npm:0.13.11" - checksum: 27481628d22a1c4e3ff551096a683b424242a216fee44685467307f14d58020af1e19660bf2e26064de946bad7eff28950eae9f8209d55723e2d9351e632bbb4 +"regenerator-runtime@npm:^0.14.0": + version: 0.14.1 + resolution: "regenerator-runtime@npm:0.14.1" + checksum: 9f57c93277b5585d3c83b0cf76be47b473ae8c6d9142a46ce8b0291a04bb2cf902059f0f8445dcabb3fb7378e5fe4bb4ea1e008876343d42e46d3b484534ce38 languageName: node linkType: hard -"regexp.prototype.flags@npm:^1.5.0": - version: 1.5.0 - resolution: "regexp.prototype.flags@npm:1.5.0" +"regexp.prototype.flags@npm:^1.5.1": + version: 1.5.1 + resolution: "regexp.prototype.flags@npm:1.5.1" dependencies: call-bind: ^1.0.2 define-properties: ^1.2.0 - functions-have-names: ^1.2.3 - checksum: c541687cdbdfff1b9a07f6e44879f82c66bbf07665f9a7544c5fd16acdb3ec8d1436caab01662d2fbcad403f3499d49ab0b77fbc7ef29ef961d98cc4bc9755b4 + set-function-name: ^2.0.0 + checksum: 869edff00288442f8d7fa4c9327f91d85f3b3acf8cbbef9ea7a220345cf23e9241b6def9263d2c1ebcf3a316b0aa52ad26a43a84aa02baca3381717b3e307f47 languageName: node linkType: hard @@ -12001,20 +11582,7 @@ __metadata: languageName: node linkType: hard -"resolve@npm:^1.10.0, resolve@npm:^1.20.0, resolve@npm:^1.22.1, resolve@npm:^1.22.3": - version: 1.22.3 - resolution: "resolve@npm:1.22.3" - dependencies: - is-core-module: ^2.12.0 - path-parse: ^1.0.7 - supports-preserve-symlinks-flag: ^1.0.0 - bin: - resolve: bin/resolve - checksum: fb834b81348428cb545ff1b828a72ea28feb5a97c026a1cf40aa1008352c72811ff4d4e71f2035273dc536dcfcae20c13604ba6283c612d70fa0b6e44519c374 - languageName: node - linkType: hard - -"resolve@npm:^1.21.0": +"resolve@npm:^1.10.0, resolve@npm:^1.20.0, resolve@npm:^1.21.0, resolve@npm:^1.22.4": version: 1.22.8 resolution: "resolve@npm:1.22.8" dependencies: @@ -12037,20 +11605,7 @@ __metadata: languageName: node linkType: hard -"resolve@patch:resolve@^1.10.0#~builtin, resolve@patch:resolve@^1.20.0#~builtin, resolve@patch:resolve@^1.22.1#~builtin, resolve@patch:resolve@^1.22.3#~builtin": - version: 1.22.3 - resolution: "resolve@patch:resolve@npm%3A1.22.3#~builtin::version=1.22.3&hash=c3c19d" - dependencies: - is-core-module: ^2.12.0 - path-parse: ^1.0.7 - supports-preserve-symlinks-flag: ^1.0.0 - bin: - resolve: bin/resolve - checksum: ad59734723b596d0891321c951592ed9015a77ce84907f89c9d9307dd0c06e11a67906a3e628c4cae143d3e44898603478af0ddeb2bba3f229a9373efe342665 - languageName: node - linkType: hard - -"resolve@patch:resolve@^1.21.0#~builtin": +"resolve@patch:resolve@^1.10.0#~builtin, resolve@patch:resolve@^1.20.0#~builtin, resolve@patch:resolve@^1.21.0#~builtin, resolve@patch:resolve@^1.22.4#~builtin": version: 1.22.8 resolution: "resolve@patch:resolve@npm%3A1.22.8#~builtin::version=1.22.8&hash=c3c19d" dependencies: @@ -12125,9 +11680,9 @@ __metadata: languageName: node linkType: hard -"rollup@npm:^3.25.2": - version: 3.27.0 - resolution: "rollup@npm:3.27.0" +"rollup@npm:^3.27.1": + version: 3.29.4 + resolution: "rollup@npm:3.29.4" dependencies: fsevents: ~2.3.2 dependenciesMeta: @@ -12135,16 +11690,7 @@ __metadata: optional: true bin: rollup: dist/bin/rollup - checksum: f60c2c288d039dc14e1f6e7fd673b7fcb11928b5a781675791b37a741f63b7af110fc5d040d60d603175b6e03ff978bed83db018dd2ac542ef809fe1a5b32dae - languageName: node - linkType: hard - -"run-applescript@npm:^5.0.0": - version: 5.0.0 - resolution: "run-applescript@npm:5.0.0" - dependencies: - execa: ^5.0.0 - checksum: d00c2dbfa5b2d774de7451194b8b125f40f65fc183de7d9dcae97f57f59433586d3c39b9001e111c38bfa24c3436c99df1bb4066a2a0c90d39a8c4cd6889af77 + checksum: 8bb20a39c8d91130825159c3823eccf4dc2295c9a0a5c4ed851a5bf2167dbf24d9a29f23461a54c955e5506395e6cc188eafc8ab0e20399d7489fb33793b184e languageName: node linkType: hard @@ -12166,19 +11712,19 @@ __metadata: languageName: node linkType: hard -"safe-array-concat@npm:^1.0.0": - version: 1.0.0 - resolution: "safe-array-concat@npm:1.0.0" +"safe-array-concat@npm:^1.0.1": + version: 1.1.0 + resolution: "safe-array-concat@npm:1.1.0" dependencies: - call-bind: ^1.0.2 - get-intrinsic: ^1.2.0 + call-bind: ^1.0.5 + get-intrinsic: ^1.2.2 has-symbols: ^1.0.3 isarray: ^2.0.5 - checksum: f43cb98fe3b566327d0c09284de2b15fb85ae964a89495c1b1a5d50c7c8ed484190f4e5e71aacc167e16231940079b326f2c0807aea633d47cc7322f40a6b57f + checksum: 5c71eaa999168ee7474929f1cd3aae80f486353a651a094d9968936692cf90aa065224929a6486dcda66334a27dce4250a83612f9e0fef6dced1a925d3ac7296 languageName: node linkType: hard -"safe-buffer@npm:5.2.1, safe-buffer@npm:^5.0.1, safe-buffer@npm:^5.1.0, safe-buffer@npm:^5.1.1, safe-buffer@npm:^5.1.2, safe-buffer@npm:^5.2.0, safe-buffer@npm:~5.2.0": +"safe-buffer@npm:5.2.1, safe-buffer@npm:^5.0.1, safe-buffer@npm:^5.1.0, safe-buffer@npm:^5.1.1, safe-buffer@npm:^5.1.2, safe-buffer@npm:^5.2.0, safe-buffer@npm:^5.2.1, safe-buffer@npm:~5.2.0": version: 5.2.1 resolution: "safe-buffer@npm:5.2.1" checksum: b99c4b41fdd67a6aaf280fcd05e9ffb0813654894223afb78a31f14a19ad220bba8aba1cb14eddce1fcfb037155fe6de4e861784eb434f7d11ed58d1e70dd491 @@ -12193,13 +11739,13 @@ __metadata: linkType: hard "safe-regex-test@npm:^1.0.0": - version: 1.0.0 - resolution: "safe-regex-test@npm:1.0.0" + version: 1.0.2 + resolution: "safe-regex-test@npm:1.0.2" dependencies: - call-bind: ^1.0.2 - get-intrinsic: ^1.1.3 + call-bind: ^1.0.5 + get-intrinsic: ^1.2.2 is-regex: ^1.1.4 - checksum: bc566d8beb8b43c01b94e67de3f070fd2781685e835959bbbaaec91cc53381145ca91f69bd837ce6ec244817afa0a5e974fc4e40a2957f0aca68ac3add1ddd34 + checksum: 4af5ce05a2daa4f6d4bfd5a3c64fc33d6b886f6592122e93c0efad52f7147b9b605e5ffc03c269a1e3d1f8db2a23bc636628a961c9fd65bafdc09503330673fd languageName: node linkType: hard @@ -12238,9 +11784,9 @@ __metadata: linkType: hard "sax@npm:>=0.6.0": - version: 1.2.4 - resolution: "sax@npm:1.2.4" - checksum: d3df7d32b897a2c2f28e941f732c71ba90e27c24f62ee918bd4d9a8cfb3553f2f81e5493c7f0be94a11c1911b643a9108f231dd6f60df3fa9586b5d2e3e9e1fe + version: 1.3.0 + resolution: "sax@npm:1.3.0" + checksum: 238ab3a9ba8c8f8aaf1c5ea9120386391f6ee0af52f1a6a40bbb6df78241dd05d782f2359d614ac6aae08c4c4125208b456548a6cf68625aa4fe178486e63ecd languageName: node linkType: hard @@ -12294,18 +11840,35 @@ __metadata: linkType: hard "serialize-javascript@npm:^6.0.1": - version: 6.0.1 - resolution: "serialize-javascript@npm:6.0.1" + version: 6.0.2 + resolution: "serialize-javascript@npm:6.0.2" dependencies: randombytes: ^2.1.0 - checksum: 3c4f4cb61d0893b988415bdb67243637333f3f574e9e9cc9a006a2ced0b390b0b3b44aef8d51c951272a9002ec50885eefdc0298891bc27eb2fe7510ea87dc4f + checksum: c4839c6206c1d143c0f80763997a361310305751171dd95e4b57efee69b8f6edd8960a0b7fbfc45042aadff98b206d55428aee0dc276efe54f100899c7fa8ab7 languageName: node linkType: hard -"set-blocking@npm:^2.0.0": - version: 2.0.0 - resolution: "set-blocking@npm:2.0.0" - checksum: 6e65a05f7cf7ebdf8b7c75b101e18c0b7e3dff4940d480efed8aad3a36a4005140b660fa1d804cb8bce911cac290441dc728084a30504d3516ac2ff7ad607b02 +"set-function-length@npm:^1.1.1": + version: 1.2.0 + resolution: "set-function-length@npm:1.2.0" + dependencies: + define-data-property: ^1.1.1 + function-bind: ^1.1.2 + get-intrinsic: ^1.2.2 + gopd: ^1.0.1 + has-property-descriptors: ^1.0.1 + checksum: 63e34b45a2ff9abb419f52583481bf8ba597d33c0c85e56999085eb6078a0f7fbb4222051981c287feceeb358aa7789e7803cea2c82ac94c0ab37059596aff79 + languageName: node + linkType: hard + +"set-function-name@npm:^2.0.0": + version: 2.0.1 + resolution: "set-function-name@npm:2.0.1" + dependencies: + define-data-property: ^1.0.1 + functions-have-names: ^1.2.3 + has-property-descriptors: ^1.0.0 + checksum: 4975d17d90c40168eee2c7c9c59d023429f0a1690a89d75656306481ece0c3c1fb1ebcc0150ea546d1913e35fbd037bace91372c69e543e51fc5d1f31a9fa126 languageName: node linkType: hard @@ -12394,14 +11957,14 @@ __metadata: linkType: hard "shiki@npm:^0.14.1": - version: 0.14.3 - resolution: "shiki@npm:0.14.3" + version: 0.14.7 + resolution: "shiki@npm:0.14.7" dependencies: ansi-sequence-parser: ^1.1.0 jsonc-parser: ^3.2.0 vscode-oniguruma: ^1.7.0 vscode-textmate: ^8.0.0 - checksum: a4dd98e3b2a5dd8be207448f111ffb9ad2ed6c530f215714d8b61cbf91ec3edbabb09109b8ec58a26678aacd24e8161d5a9bc0c1fa1b4f64b27ceb180cbd0c89 + checksum: 2aec3b3519df977c4391df9e1825cb496e9a4d7e11395f05a0da77e4fa2f7c3d9d6e6ee94029ac699533017f2b25637ee68f6d39f05f311535c2704d0329b520 languageName: node linkType: hard @@ -12453,13 +12016,6 @@ __metadata: languageName: node linkType: hard -"slash@npm:^4.0.0": - version: 4.0.0 - resolution: "slash@npm:4.0.0" - checksum: da8e4af73712253acd21b7853b7e0dbba776b786e82b010a5bfc8b5051a1db38ed8aba8e1e8f400dd2c9f373be91eb1c42b66e91abb407ff42b10feece5e1d2d - languageName: node - linkType: hard - "smart-buffer@npm:^4.2.0": version: 4.2.0 resolution: "smart-buffer@npm:4.2.0" @@ -12467,18 +12023,7 @@ __metadata: languageName: node linkType: hard -"socks-proxy-agent@npm:^7.0.0": - version: 7.0.0 - resolution: "socks-proxy-agent@npm:7.0.0" - dependencies: - agent-base: ^6.0.2 - debug: ^4.3.3 - socks: ^2.6.2 - checksum: 720554370154cbc979e2e9ce6a6ec6ced205d02757d8f5d93fe95adae454fc187a5cbfc6b022afab850a5ce9b4c7d73e0f98e381879cf45f66317a4895953846 - languageName: node - linkType: hard - -"socks-proxy-agent@npm:^8.0.2": +"socks-proxy-agent@npm:^8.0.1, socks-proxy-agent@npm:^8.0.2": version: 8.0.2 resolution: "socks-proxy-agent@npm:8.0.2" dependencies: @@ -12489,7 +12034,7 @@ __metadata: languageName: node linkType: hard -"socks@npm:^2.6.2, socks@npm:^2.7.1": +"socks@npm:^2.7.1": version: 2.7.1 resolution: "socks@npm:2.7.1" dependencies: @@ -12540,6 +12085,13 @@ __metadata: languageName: node linkType: hard +"source-map@npm:^0.7.4": + version: 0.7.4 + resolution: "source-map@npm:0.7.4" + checksum: 01cc5a74b1f0e1d626a58d36ad6898ea820567e87f18dfc9d24a9843a351aaa2ec09b87422589906d6ff1deed29693e176194dc88bcae7c9a852dc74b311dbf5 + languageName: node + linkType: hard + "spawn-command@npm:0.0.2, spawn-command@npm:^0.0.2-1": version: 0.0.2 resolution: "spawn-command@npm:0.0.2" @@ -12558,9 +12110,9 @@ __metadata: linkType: hard "spdx-exceptions@npm:^2.1.0": - version: 2.3.0 - resolution: "spdx-exceptions@npm:2.3.0" - checksum: cb69a26fa3b46305637123cd37c85f75610e8c477b6476fa7354eb67c08128d159f1d36715f19be6f9daf4b680337deb8c65acdcae7f2608ba51931540687ac0 + version: 2.4.0 + resolution: "spdx-exceptions@npm:2.4.0" + checksum: b1b650a8d94424473bf9629cf972c86a91c03cccc260f5c901bce0e4b92d831627fec28c9e0a1e9c34c5ebad0a12cf2eab887bec088e0a862abb9d720c2fd0a1 languageName: node linkType: hard @@ -12575,9 +12127,9 @@ __metadata: linkType: hard "spdx-license-ids@npm:^3.0.0": - version: 3.0.13 - resolution: "spdx-license-ids@npm:3.0.13" - checksum: 3469d85c65f3245a279fa11afc250c3dca96e9e847f2f79d57f466940c5bb8495da08a542646086d499b7f24a74b8d0b42f3fc0f95d50ff99af1f599f6360ad7 + version: 3.0.16 + resolution: "spdx-license-ids@npm:3.0.16" + checksum: 5cdaa85aaa24bd02f9353a2e357b4df0a4f205cb35655f3fd0a5674a4fb77081f28ffd425379214bc3be2c2b7593ce1215df6bcc75884aeee0a9811207feabe2 languageName: node linkType: hard @@ -12598,11 +12150,11 @@ __metadata: linkType: hard "ssri@npm:^10.0.0": - version: 10.0.4 - resolution: "ssri@npm:10.0.4" + version: 10.0.5 + resolution: "ssri@npm:10.0.5" dependencies: - minipass: ^5.0.0 - checksum: fb14da9f8a72b04eab163eb13a9dda11d5962cd2317f85457c4e0b575e9a6e0e3a6a87b5bf122c75cb36565830cd5f263fb457571bf6f1587eb5f95d095d6165 + minipass: ^7.0.3 + checksum: 0a31b65f21872dea1ed3f7c200d7bc1c1b91c15e419deca14f282508ba917cbb342c08a6814c7f68ca4ca4116dd1a85da2bbf39227480e50125a1ceffeecb750 languageName: node linkType: hard @@ -12673,20 +12225,13 @@ __metadata: languageName: node linkType: hard -"streamsearch@npm:^1.1.0": - version: 1.1.0 - resolution: "streamsearch@npm:1.1.0" - checksum: 1cce16cea8405d7a233d32ca5e00a00169cc0e19fbc02aa839959985f267335d435c07f96e5e0edd0eadc6d39c98d5435fb5bbbdefc62c41834eadc5622ad942 - languageName: node - linkType: hard - "streamx@npm:^2.15.0": - version: 2.15.1 - resolution: "streamx@npm:2.15.1" + version: 2.15.6 + resolution: "streamx@npm:2.15.6" dependencies: fast-fifo: ^1.1.0 queue-tick: ^1.0.1 - checksum: 6f2b4fed68caacd28efbd44d4264f5d3c2b81b0a5de14419333dac57f2075c49ae648df8d03db632a33587a6c8ab7cb9cdb4f9a2f8305be0c2cd79af35742b15 + checksum: 37a245f5cee4c33fcb8b018ccb935bad6eab423f05b0d14d018e63dbd2670bb109a69442e961a195b750c2c774f613c19476d11bd727d645eedb655d2dba234b languageName: node linkType: hard @@ -12707,7 +12252,7 @@ __metadata: languageName: node linkType: hard -"string-width-cjs@npm:string-width@^4.2.0, string-width@npm:^1.0.2 || 2 || 3 || 4, string-width@npm:^4.1.0, string-width@npm:^4.2.0, string-width@npm:^4.2.3": +"string-width-cjs@npm:string-width@^4.2.0, string-width@npm:^4.1.0, string-width@npm:^4.2.0, string-width@npm:^4.2.3": version: 4.2.3 resolution: "string-width@npm:4.2.3" dependencies: @@ -12729,36 +12274,36 @@ __metadata: languageName: node linkType: hard -"string.prototype.trim@npm:^1.2.7": - version: 1.2.7 - resolution: "string.prototype.trim@npm:1.2.7" +"string.prototype.trim@npm:^1.2.8": + version: 1.2.8 + resolution: "string.prototype.trim@npm:1.2.8" dependencies: call-bind: ^1.0.2 - define-properties: ^1.1.4 - es-abstract: ^1.20.4 - checksum: 05b7b2d6af63648e70e44c4a8d10d8cc457536df78b55b9d6230918bde75c5987f6b8604438c4c8652eb55e4fc9725d2912789eb4ec457d6995f3495af190c09 + define-properties: ^1.2.0 + es-abstract: ^1.22.1 + checksum: 49eb1a862a53aba73c3fb6c2a53f5463173cb1f4512374b623bcd6b43ad49dd559a06fb5789bdec771a40fc4d2a564411c0a75d35fb27e76bbe738c211ecff07 languageName: node linkType: hard -"string.prototype.trimend@npm:^1.0.6": - version: 1.0.6 - resolution: "string.prototype.trimend@npm:1.0.6" +"string.prototype.trimend@npm:^1.0.7": + version: 1.0.7 + resolution: "string.prototype.trimend@npm:1.0.7" dependencies: call-bind: ^1.0.2 - define-properties: ^1.1.4 - es-abstract: ^1.20.4 - checksum: 0fdc34645a639bd35179b5a08227a353b88dc089adf438f46be8a7c197fc3f22f8514c1c9be4629b3cd29c281582730a8cbbad6466c60f76b5f99cf2addb132e + define-properties: ^1.2.0 + es-abstract: ^1.22.1 + checksum: 2375516272fd1ba75992f4c4aa88a7b5f3c7a9ca308d963bcd5645adf689eba6f8a04ebab80c33e30ec0aefc6554181a3a8416015c38da0aa118e60ec896310c languageName: node linkType: hard -"string.prototype.trimstart@npm:^1.0.6": - version: 1.0.6 - resolution: "string.prototype.trimstart@npm:1.0.6" +"string.prototype.trimstart@npm:^1.0.7": + version: 1.0.7 + resolution: "string.prototype.trimstart@npm:1.0.7" dependencies: call-bind: ^1.0.2 - define-properties: ^1.1.4 - es-abstract: ^1.20.4 - checksum: 89080feef416621e6ef1279588994305477a7a91648d9436490d56010a1f7adc39167cddac7ce0b9884b8cdbef086987c4dcb2960209f2af8bac0d23ceff4f41 + define-properties: ^1.2.0 + es-abstract: ^1.22.1 + checksum: 13d0c2cb0d5ff9e926fa0bec559158b062eed2b68cd5be777ffba782c96b2b492944e47057274e064549b94dd27cf81f48b27a31fee8af5b574cff253e7eb613 languageName: node linkType: hard @@ -12872,9 +12417,9 @@ __metadata: languageName: node linkType: hard -"superagent@npm:^8.0.5": - version: 8.0.9 - resolution: "superagent@npm:8.0.9" +"superagent@npm:^8.1.2": + version: 8.1.2 + resolution: "superagent@npm:8.1.2" dependencies: component-emitter: ^1.3.0 cookiejar: ^2.1.4 @@ -12886,17 +12431,17 @@ __metadata: mime: 2.6.0 qs: ^6.11.0 semver: ^7.3.8 - checksum: 5d00cdc7ceb5570663da80604965750e6b1b8d7d7442b7791e285c62bcd8d578a8ead0242a2426432b59a255fb42eb3a196d636157538a1392e7b6c5f1624810 + checksum: f3601c5ccae34d5ba684a03703394b5d25931f4ae2e1e31a1de809f88a9400e997ece037f9accf148a21c408f950dc829db1e4e23576a7f9fe0efa79fd5c9d2f languageName: node linkType: hard "supertest@npm:^6.3.3": - version: 6.3.3 - resolution: "supertest@npm:6.3.3" + version: 6.3.4 + resolution: "supertest@npm:6.3.4" dependencies: methods: ^1.1.2 - superagent: ^8.0.5 - checksum: 38239e517f7ba62b7a139a79c5c48d55f8d67b5ff4b6e51d5b07732ca8bbc4a28ffa1b10916fbb403dd013a054dbf028edc5850057d9a43aecbff439d494673e + superagent: ^8.1.2 + checksum: 875c6fa7940f21e5be9bb646579cdb030d4057bf2da643e125e1f0480add1200395d2b17e10b8e54e1009efc63e047422501e9eb30e12828668498c0910f295f languageName: node linkType: hard @@ -12934,16 +12479,6 @@ __metadata: languageName: node linkType: hard -"synckit@npm:^0.8.5": - version: 0.8.5 - resolution: "synckit@npm:0.8.5" - dependencies: - "@pkgr/utils": ^2.3.1 - tslib: ^2.5.0 - checksum: 8a9560e5d8f3d94dc3cf5f7b9c83490ffa30d320093560a37b88f59483040771fd1750e76b9939abfbb1b5a23fd6dfbae77f6b338abffe7cae7329cd9b9bb86b - languageName: node - linkType: hard - "tapable@npm:^2.1.1, tapable@npm:^2.2.0": version: 2.2.1 resolution: "tapable@npm:2.2.1" @@ -12963,19 +12498,19 @@ __metadata: linkType: hard "tar-stream@npm:^3.1.5": - version: 3.1.6 - resolution: "tar-stream@npm:3.1.6" + version: 3.1.7 + resolution: "tar-stream@npm:3.1.7" dependencies: b4a: ^1.6.4 fast-fifo: ^1.2.0 streamx: ^2.15.0 - checksum: f3627f918581976e954ff03cb8d370551053796b82564f8c7ca8fac84c48e4d042026d0854fc222171a34ff9c682b72fae91be9c9b0a112d4c54f9e4f443e9c5 + checksum: 6393a6c19082b17b8dcc8e7fd349352bb29b4b8bfe1075912b91b01743ba6bb4298f5ff0b499a3bbaf82121830e96a1a59d4f21a43c0df339e54b01789cb8cc6 languageName: node linkType: hard "tar@npm:^6.1.11, tar@npm:^6.1.2": - version: 6.1.15 - resolution: "tar@npm:6.1.15" + version: 6.2.0 + resolution: "tar@npm:6.2.0" dependencies: chownr: ^2.0.0 fs-minipass: ^2.0.0 @@ -12983,19 +12518,19 @@ __metadata: minizlib: ^2.1.1 mkdirp: ^1.0.3 yallist: ^4.0.0 - checksum: f23832fceeba7578bf31907aac744ae21e74a66f4a17a9e94507acf460e48f6db598c7023882db33bab75b80e027c21f276d405e4a0322d58f51c7088d428268 + checksum: db4d9fe74a2082c3a5016630092c54c8375ff3b280186938cfd104f2e089c4fd9bad58688ef6be9cf186a889671bf355c7cda38f09bbf60604b281715ca57f5c languageName: node linkType: hard -"terser-webpack-plugin@npm:^5.3.7": - version: 5.3.9 - resolution: "terser-webpack-plugin@npm:5.3.9" +"terser-webpack-plugin@npm:^5.3.10": + version: 5.3.10 + resolution: "terser-webpack-plugin@npm:5.3.10" dependencies: - "@jridgewell/trace-mapping": ^0.3.17 + "@jridgewell/trace-mapping": ^0.3.20 jest-worker: ^27.4.5 schema-utils: ^3.1.1 serialize-javascript: ^6.0.1 - terser: ^5.16.8 + terser: ^5.26.0 peerDependencies: webpack: ^5.1.0 peerDependenciesMeta: @@ -13005,13 +12540,13 @@ __metadata: optional: true uglify-js: optional: true - checksum: 41705713d6f9cb83287936b21e27c658891c78c4392159f5148b5623f0e8c48559869779619b058382a4c9758e7820ea034695e57dc7c474b4962b79f553bc5f + checksum: bd6e7596cf815f3353e2a53e79cbdec959a1b0276f5e5d4e63e9d7c3c5bb5306df567729da287d1c7b39d79093e56863c569c42c6c24cc34c76aa313bd2cbcea languageName: node linkType: hard -"terser@npm:^5.16.8": - version: 5.19.2 - resolution: "terser@npm:5.19.2" +"terser@npm:^5.26.0": + version: 5.27.0 + resolution: "terser@npm:5.27.0" dependencies: "@jridgewell/source-map": ^0.3.3 acorn: ^8.8.2 @@ -13019,7 +12554,7 @@ __metadata: source-map-support: ~0.5.20 bin: terser: bin/terser - checksum: e059177775b4d4f4cff219ad89293175aefbd1b081252270444dc83e42a2c5f07824eb2a85eae6e22ef6eb7ef04b21af36dd7d1dd7cfb93912310e57d416a205 + checksum: c165052cfea061e8512e9b9ba42a098c2ff6382886ae122b040fd5b6153443070cc2dcb4862269f1669c09c716763e856125a355ff984aa72be525d6fffd8729 languageName: node linkType: hard @@ -13049,11 +12584,11 @@ __metadata: linkType: hard "thingies@npm:^1.11.1": - version: 1.12.0 - resolution: "thingies@npm:1.12.0" + version: 1.16.0 + resolution: "thingies@npm:1.16.0" peerDependencies: tslib: ^2 - checksum: 04b75d264e1880676fb9f400b2c0e069abdd00de1fa006d9daee527ad6d899828169fd46bb17e7cabf2802b7dbd64053106e29e7a7aa271659f5b41b3a11657f + checksum: 9afbe70a9777fc31ac2567d06c9f64511585437298948330c39c076c6bd54bae6a700dee4cf4074486ef26c83f51031241ef4f4309c386555fb016a93be8aa06 languageName: node linkType: hard @@ -13064,13 +12599,6 @@ __metadata: languageName: node linkType: hard -"titleize@npm:^3.0.0": - version: 3.0.0 - resolution: "titleize@npm:3.0.0" - checksum: 71fbbeabbfb36ccd840559f67f21e356e1d03da2915b32d2ae1a60ddcc13a124be2739f696d2feb884983441d159a18649e8d956648d591bdad35c430a6b6d28 - languageName: node - linkType: hard - "tmpl@npm:1.0.5": version: 1.0.5 resolution: "tmpl@npm:1.0.5" @@ -13141,11 +12669,11 @@ __metadata: linkType: hard "ts-api-utils@npm:^1.0.1": - version: 1.0.1 - resolution: "ts-api-utils@npm:1.0.1" + version: 1.0.3 + resolution: "ts-api-utils@npm:1.0.3" peerDependencies: typescript: ">=4.2.0" - checksum: 78794fc7270d295b36c1ac613465b5dc7e7226907a533125b30f177efef9dd630d4e503b00be31b44335eb2ebf9e136ebe97353f8fc5d383885d5fead9d54c09 + checksum: 441cc4489d65fd515ae6b0f4eb8690057add6f3b6a63a36073753547fb6ce0c9ea0e0530220a0b282b0eec535f52c4dfc315d35f8a4c9a91c0def0707a714ca6 languageName: node linkType: hard @@ -13232,23 +12760,24 @@ __metadata: linkType: hard "ts-loader@npm:^9.4.4": - version: 9.4.4 - resolution: "ts-loader@npm:9.4.4" + version: 9.5.1 + resolution: "ts-loader@npm:9.5.1" dependencies: chalk: ^4.1.0 enhanced-resolve: ^5.0.0 micromatch: ^4.0.0 semver: ^7.3.4 + source-map: ^0.7.4 peerDependencies: typescript: "*" webpack: ^5.0.0 - checksum: 8e5e6b839b0edfa40d2156c880d88ccab58226894ea5978221bc48c7db3215e2e856bfd0093f148e925a2befc42d6c94cafa9a994a7da274541efaa916012b63 + checksum: 7cf396e656d905388ea2a9b5e82f16d3c955fda8d3df2fbf219f4bee16ff50a3c995c44ae3e584634e9443f056cec70bb3151add3917ffb4588ecd7394bac0ec languageName: node linkType: hard "ts-node@npm:^10.9.1": - version: 10.9.1 - resolution: "ts-node@npm:10.9.1" + version: 10.9.2 + resolution: "ts-node@npm:10.9.2" dependencies: "@cspotcode/source-map-support": ^0.8.0 "@tsconfig/node10": ^1.0.7 @@ -13280,7 +12809,7 @@ __metadata: ts-node-script: dist/bin-script.js ts-node-transpile-only: dist/bin-transpile.js ts-script: dist/bin-script-deprecated.js - checksum: 090adff1302ab20bd3486e6b4799e90f97726ed39e02b39e566f8ab674fd5bd5f727f43615debbfc580d33c6d9d1c6b1b3ce7d8e3cca3e20530a145ffa232c35 + checksum: fde256c9073969e234526e2cfead42591b9a2aec5222bac154b0de2fa9e4ceb30efcd717ee8bc785a56f3a119bdd5aa27b333d9dbec94ed254bd26f8944c67ac languageName: node linkType: hard @@ -13300,15 +12829,15 @@ __metadata: languageName: node linkType: hard -"tsconfig-paths@npm:^3.10.1, tsconfig-paths@npm:^3.14.2": - version: 3.14.2 - resolution: "tsconfig-paths@npm:3.14.2" +"tsconfig-paths@npm:^3.10.1, tsconfig-paths@npm:^3.15.0": + version: 3.15.0 + resolution: "tsconfig-paths@npm:3.15.0" dependencies: "@types/json5": ^0.0.29 json5: ^1.0.2 minimist: ^1.2.6 strip-bom: ^3.0.0 - checksum: a6162eaa1aed680537f93621b82399c7856afd10ec299867b13a0675e981acac4e0ec00896860480efc59fc10fd0b16fdc928c0b885865b52be62cadac692447 + checksum: 59f35407a390d9482b320451f52a411a256a130ff0e7543d18c6f20afab29ac19fbe55c360a93d6476213cc335a4d76ce90f67df54c4e9037f7d240920832201 languageName: node linkType: hard @@ -13326,20 +12855,13 @@ __metadata: languageName: node linkType: hard -"tslib@npm:^2.0.1": +"tslib@npm:^2.0.1, tslib@npm:^2.1.0, tslib@npm:^2.4.0, tslib@npm:^2.5.0": version: 2.6.2 resolution: "tslib@npm:2.6.2" checksum: 329ea56123005922f39642318e3d1f0f8265d1e7fcb92c633e0809521da75eeaca28d2cf96d7248229deb40e5c19adf408259f4b9640afd20d13aecc1430f3ad languageName: node linkType: hard -"tslib@npm:^2.1.0, tslib@npm:^2.4.0, tslib@npm:^2.5.0, tslib@npm:^2.6.0": - version: 2.6.1 - resolution: "tslib@npm:2.6.1" - checksum: b0d176d176487905b66ae4d5856647df50e37beea7571c53b8d10ba9222c074b81f1410fb91da13debaf2cbc970663609068bdebafa844ea9d69b146527c38fe - languageName: node - linkType: hard - "tsscmp@npm:1.0.6": version: 1.0.6 resolution: "tsscmp@npm:1.0.6" @@ -13510,12 +13032,12 @@ __metadata: linkType: hard "typescript@npm:^5.0.4": - version: 5.1.6 - resolution: "typescript@npm:5.1.6" + version: 5.3.3 + resolution: "typescript@npm:5.3.3" bin: tsc: bin/tsc tsserver: bin/tsserver - checksum: b2f2c35096035fe1f5facd1e38922ccb8558996331405eb00a5111cc948b2e733163cc22fab5db46992aba7dd520fff637f2c1df4996ff0e134e77d3249a7350 + checksum: 2007ccb6e51bbbf6fde0a78099efe04dc1c3dfbdff04ca3b6a8bc717991862b39fd6126c0c3ebf2d2d98ac5e960bcaa873826bb2bb241f14277034148f41f6a2 languageName: node linkType: hard @@ -13540,61 +13062,49 @@ __metadata: linkType: hard "typescript@patch:typescript@^5.0.4#~builtin": - version: 5.1.6 - resolution: "typescript@patch:typescript@npm%3A5.1.6#~builtin::version=5.1.6&hash=5da071" + version: 5.3.3 + resolution: "typescript@patch:typescript@npm%3A5.3.3#~builtin::version=5.3.3&hash=f3b441" bin: tsc: bin/tsc tsserver: bin/tsserver - checksum: f53bfe97f7c8b2b6d23cf572750d4e7d1e0c5fff1c36d859d0ec84556a827b8785077bc27676bf7e71fae538e517c3ecc0f37e7f593be913d884805d931bc8be - languageName: node - linkType: hard - -"uint8-varint@npm:^1.0.1, uint8-varint@npm:^1.0.2": - version: 1.0.6 - resolution: "uint8-varint@npm:1.0.6" - dependencies: - byte-access: ^1.0.0 - longbits: ^1.1.0 - uint8arraylist: ^2.0.0 - uint8arrays: ^4.0.2 - checksum: 9d97312b9d032cc20976c3d9ea9e7548bc761a5f4483156e349d5dcd9ffed7c71f597dc564591cbe93816ee39d258751dee2efa6b43859d3662c83ad1cf6cd1a + checksum: f61375590b3162599f0f0d5b8737877ac0a7bc52761dbb585d67e7b8753a3a4c42d9a554c4cc929f591ffcf3a2b0602f65ae3ce74714fd5652623a816862b610 languageName: node linkType: hard "uint8-varint@npm:^2.0.0, uint8-varint@npm:^2.0.1": - version: 2.0.1 - resolution: "uint8-varint@npm:2.0.1" + version: 2.0.3 + resolution: "uint8-varint@npm:2.0.3" dependencies: uint8arraylist: ^2.0.0 - uint8arrays: ^4.0.2 - checksum: b03431e7fb87224726524b6dcc4ac0ff2c61c122134be0b16394c04bac4a3b59328800f302e3d3df7715bdb676b2e1fd0a17332cf6dbdb148cbb9f81332cedef + uint8arrays: ^5.0.0 + checksum: 8c20b02f803ade88d3aad0c01368e8bfebedca3cb43f2d74347f32b92a219ab734b8df4bb0dee5540261de984896ef4ffb3c58e83b200d4b1d01a7e3f7757dee languageName: node linkType: hard -"uint8arraylist@npm:^2.0.0, uint8arraylist@npm:^2.4.1, uint8arraylist@npm:^2.4.3": - version: 2.4.3 - resolution: "uint8arraylist@npm:2.4.3" +"uint8arraylist@npm:^2.0.0, uint8arraylist@npm:^2.4.1, uint8arraylist@npm:^2.4.3, uint8arraylist@npm:^2.4.7": + version: 2.4.8 + resolution: "uint8arraylist@npm:2.4.8" dependencies: - uint8arrays: ^4.0.2 - checksum: 95225fe2b8f6a4d8919b6c8e3dcff32ced35a08a53efaef757a50bc0082fb5b91f09e7e46f0d1c234243899930f7cb02ef9eb44b97ce03e2381d416de15e16c9 + uint8arrays: ^5.0.1 + checksum: 8259124cf5c7acd29edeed346489d898f3eb12f129dadedb1c263ad8d637e1a2f689968934a94c16804e39f6e8765178507be6d7b3c3c6b67147ad7546d34186 languageName: node linkType: hard -"uint8arrays@npm:^4.0.2": - version: 4.0.4 - resolution: "uint8arrays@npm:4.0.4" +"uint8arrays@npm:^4.0.4, uint8arrays@npm:^4.0.6": + version: 4.0.10 + resolution: "uint8arrays@npm:4.0.10" dependencies: - multiformats: ^11.0.0 - checksum: 49b2f53cf41aecd18b4623eea71d4e32fbcc572341cc687bb3e6e3372122b022079b2f0beca7c21648b1bd8a019ee596c71861043babc677e5c74773071e5d55 + multiformats: ^12.0.1 + checksum: 784677a00f67d18d3aaaf441422b4055576e1ab76dbf276e474b86c91ddb95945ac1cc95a97979ab1f3b3c9a0ebeea74dd803ec6056adbd1ee6ef2f231f00f97 languageName: node linkType: hard -"uint8arrays@npm:^4.0.4, uint8arrays@npm:^4.0.6": - version: 4.0.6 - resolution: "uint8arrays@npm:4.0.6" +"uint8arrays@npm:^5.0.0, uint8arrays@npm:^5.0.1": + version: 5.0.1 + resolution: "uint8arrays@npm:5.0.1" dependencies: - multiformats: ^12.0.1 - checksum: 0d55d74fe8d791ee24396bf6175ffe8ff73aae763cfaca5bf774e43315ee57bc69cc3af854de5e7b20bc7e6b7bde731f73a478bc43c295ea8115bff8a49621e0 + multiformats: ^13.0.0 + checksum: 29b27d41e1b5fe2b3de0ce502556e9ac7caabf53c0a40f27d3654c4cb08f06913b14b7722325a1a4287664d15938abf48dd987340d966ca61c2daa40030b5f82 languageName: node linkType: hard @@ -13620,12 +13130,10 @@ __metadata: languageName: node linkType: hard -"undici@npm:^5.12.0": - version: 5.22.1 - resolution: "undici@npm:5.22.1" - dependencies: - busboy: ^1.6.0 - checksum: 048a3365f622be44fb319316cedfaa241c59cf7f3368ae7667a12323447e1822e8cc3d00f6956c852d1478a6fde1cbbe753f49e05f2fdaed229693e716ebaf35 +"undici-types@npm:~5.26.4": + version: 5.26.5 + resolution: "undici-types@npm:5.26.5" + checksum: 3192ef6f3fd5df652f2dc1cd782b49d6ff14dc98e5dced492aa8a8c65425227da5da6aafe22523c67f035a272c599bb89cfe803c1db6311e44bed3042fc25487 languageName: node linkType: hard @@ -13662,9 +13170,9 @@ __metadata: linkType: hard "universalify@npm:^2.0.0": - version: 2.0.0 - resolution: "universalify@npm:2.0.0" - checksum: 2406a4edf4a8830aa6813278bab1f953a8e40f2f63a37873ffa9a3bc8f9745d06cc8e88f3572cb899b7e509013f7f6fcc3e37e8a6d914167a5381d8440518c44 + version: 2.0.1 + resolution: "universalify@npm:2.0.1" + checksum: ecd8469fe0db28e7de9e5289d32bd1b6ba8f7183db34f3bfc4ca53c49891c2d6aa05f3fb3936a81285a905cc509fb641a0c3fc131ec786167eff41236ae32e60 languageName: node linkType: hard @@ -13675,13 +13183,6 @@ __metadata: languageName: node linkType: hard -"untildify@npm:^4.0.0": - version: 4.0.0 - resolution: "untildify@npm:4.0.0" - checksum: 39ced9c418a74f73f0a56e1ba4634b4d959422dff61f4c72a8e39f60b99380c1b45ed776fbaa0a4101b157e4310d873ad7d114e8534ca02609b4916bb4187fb9 - languageName: node - linkType: hard - "unzipit@npm:^1.4.3": version: 1.4.3 resolution: "unzipit@npm:1.4.3" @@ -13698,9 +13199,9 @@ __metadata: languageName: node linkType: hard -"update-browserslist-db@npm:^1.0.11": - version: 1.0.11 - resolution: "update-browserslist-db@npm:1.0.11" +"update-browserslist-db@npm:^1.0.13": + version: 1.0.13 + resolution: "update-browserslist-db@npm:1.0.13" dependencies: escalade: ^3.1.1 picocolors: ^1.0.0 @@ -13708,7 +13209,7 @@ __metadata: browserslist: ">= 4.21.0" bin: update-browserslist-db: cli.js - checksum: b98327518f9a345c7cad5437afae4d2ae7d865f9779554baf2a200fdf4bac4969076b679b1115434bd6557376bdd37ca7583d0f9b8f8e302d7d4cc1e91b5f231 + checksum: 1e47d80182ab6e4ad35396ad8b61008ae2a1330221175d0abd37689658bdb61af9b705bfc41057fd16682474d79944fb2d86767c5ed5ae34b6276b9bed353322 languageName: node linkType: hard @@ -13770,13 +13271,13 @@ __metadata: linkType: hard "v8-to-istanbul@npm:^9.0.1": - version: 9.1.0 - resolution: "v8-to-istanbul@npm:9.1.0" + version: 9.2.0 + resolution: "v8-to-istanbul@npm:9.2.0" dependencies: "@jridgewell/trace-mapping": ^0.3.12 "@types/istanbul-lib-coverage": ^2.0.1 - convert-source-map: ^1.6.0 - checksum: 2069d59ee46cf8d83b4adfd8a5c1a90834caffa9f675e4360f1157ffc8578ef0f763c8f32d128334424159bb6b01f3876acd39cd13297b2769405a9da241f8d1 + convert-source-map: ^2.0.0 + checksum: 31ef98c6a31b1dab6be024cf914f235408cd4c0dc56a5c744a5eea1a9e019ba279e1b6f90d695b78c3186feed391ed492380ccf095009e2eb91f3d058f0b4491 languageName: node linkType: hard @@ -13790,13 +13291,6 @@ __metadata: languageName: node linkType: hard -"varint@npm:^6.0.0": - version: 6.0.0 - resolution: "varint@npm:6.0.0" - checksum: 7684113c9d497c01e40396e50169c502eb2176203219b96e1c5ac965a3e15b4892bd22b7e48d87148e10fffe638130516b6dbeedd0efde2b2d0395aa1772eea7 - languageName: node - linkType: hard - "vary@npm:^1.1.2": version: 1.1.2 resolution: "vary@npm:1.1.2" @@ -13827,35 +13321,34 @@ __metadata: linkType: hard "viem@npm:^1.2.5": - version: 1.5.0 - resolution: "viem@npm:1.5.0" + version: 1.21.4 + resolution: "viem@npm:1.21.4" dependencies: - "@adraffy/ens-normalize": 1.9.0 - "@noble/curves": 1.0.0 - "@noble/hashes": 1.3.0 - "@scure/bip32": 1.3.0 - "@scure/bip39": 1.2.0 - "@wagmi/chains": 1.6.0 - abitype: 0.9.3 - isomorphic-ws: 5.0.0 - ws: 8.12.0 + "@adraffy/ens-normalize": 1.10.0 + "@noble/curves": 1.2.0 + "@noble/hashes": 1.3.2 + "@scure/bip32": 1.3.2 + "@scure/bip39": 1.2.1 + abitype: 0.9.8 + isows: 1.0.3 + ws: 8.13.0 peerDependencies: typescript: ">=5.0.4" peerDependenciesMeta: typescript: optional: true - checksum: 28ff9e64a3076b339182a3bab5dc442222400da4c66d258bf35e961f9a2e555ba2b0f3b30bdb87f63142682530c25f0807113580fbf888f840930f39df4bd8e3 + checksum: c351fdea2d53d2d781ac73c964348b3b9fc5dd46f9eb53903e867705fc9e30a893cb9f2c8d7a00acdcdeca27d14eeebf976eed9f948c28c47018dc9211369117 languageName: node linkType: hard "vite@npm:^4.2.3": - version: 4.4.8 - resolution: "vite@npm:4.4.8" + version: 4.5.2 + resolution: "vite@npm:4.5.2" dependencies: esbuild: ^0.18.10 fsevents: ~2.3.2 - postcss: ^8.4.26 - rollup: ^3.25.2 + postcss: ^8.4.27 + rollup: ^3.27.1 peerDependencies: "@types/node": ">= 14" less: "*" @@ -13884,7 +13377,7 @@ __metadata: optional: true bin: vite: bin/vite.js - checksum: e8ffe688f8a7396b1357778f00cb06d1f3dadad200823c47a1955cf52774a0cbff5ac4d6a8f8d09e26c1d4e588e5815956f9eba02ae301e77a36c3d181a1bc86 + checksum: 9d1f84f703c2660aced34deee7f309278ed368880f66e9570ac115c793d91f7fffb80ab19c602b3c8bc1341fe23437d86a3fcca2a9ef82f7ef0cdac5a40d0c86 languageName: node linkType: hard @@ -13945,9 +13438,9 @@ __metadata: linkType: hard "web-streams-polyfill@npm:^3.0.3": - version: 3.2.1 - resolution: "web-streams-polyfill@npm:3.2.1" - checksum: b119c78574b6d65935e35098c2afdcd752b84268e18746606af149e3c424e15621b6f1ff0b42b2676dc012fc4f0d313f964b41a4b5031e525faa03997457da02 + version: 3.3.2 + resolution: "web-streams-polyfill@npm:3.3.2" + checksum: 0292f4113c1bda40d8e8ecebee39eb14cc2e2e560a65a6867980e394537a2645130e2c73f5ef6e641fd3697d2f71720ccf659aebaf69a9d5a773f653a0fdf39d languageName: node linkType: hard @@ -13991,12 +13484,13 @@ __metadata: linkType: hard "webpack-merge@npm:^5.7.3": - version: 5.9.0 - resolution: "webpack-merge@npm:5.9.0" + version: 5.10.0 + resolution: "webpack-merge@npm:5.10.0" dependencies: clone-deep: ^4.0.1 + flat: ^5.0.2 wildcard: ^2.0.0 - checksum: 64fe2c23aacc5f19684452a0e84ec02c46b990423aee6fcc5c18d7d471155bd14e9a6adb02bd3656eb3e0ac2532c8e97d69412ad14c97eeafe32fa6d10050872 + checksum: 1fe8bf5309add7298e1ac72fb3f2090e1dfa80c48c7e79fa48aa60b5961332c7d0d61efa8851acb805e6b91a4584537a347bc106e05e9aec87fa4f7088c62f2f languageName: node linkType: hard @@ -14008,17 +13502,17 @@ __metadata: linkType: hard "webpack@npm:^5.88.2": - version: 5.88.2 - resolution: "webpack@npm:5.88.2" + version: 5.90.0 + resolution: "webpack@npm:5.90.0" dependencies: "@types/eslint-scope": ^3.7.3 - "@types/estree": ^1.0.0 + "@types/estree": ^1.0.5 "@webassemblyjs/ast": ^1.11.5 "@webassemblyjs/wasm-edit": ^1.11.5 "@webassemblyjs/wasm-parser": ^1.11.5 acorn: ^8.7.1 acorn-import-assertions: ^1.9.0 - browserslist: ^4.14.5 + browserslist: ^4.21.10 chrome-trace-event: ^1.0.2 enhanced-resolve: ^5.15.0 es-module-lexer: ^1.2.1 @@ -14032,7 +13526,7 @@ __metadata: neo-async: ^2.6.2 schema-utils: ^3.2.0 tapable: ^2.1.1 - terser-webpack-plugin: ^5.3.7 + terser-webpack-plugin: ^5.3.10 watchpack: ^2.4.0 webpack-sources: ^3.2.3 peerDependenciesMeta: @@ -14040,7 +13534,7 @@ __metadata: optional: true bin: webpack: bin/webpack.js - checksum: 79476a782da31a21f6dd38fbbd06b68da93baf6a62f0d08ca99222367f3b8668f5a1f2086b7bb78e23172e31fa6df6fa7ab09b25e827866c4fc4dc2b30443ce2 + checksum: 178a0e7e9e5b26264a19dd5fe554a3508a8afafc9cce972bfd4452b5128d0db1b37832f5e615be1cff1934f24da0de967929f199be2b3fe283ca1951f98ea3fe languageName: node linkType: hard @@ -14076,20 +13570,20 @@ __metadata: languageName: node linkType: hard -"which-typed-array@npm:^1.1.10, which-typed-array@npm:^1.1.11, which-typed-array@npm:^1.1.2": - version: 1.1.11 - resolution: "which-typed-array@npm:1.1.11" +"which-typed-array@npm:^1.1.11, which-typed-array@npm:^1.1.13, which-typed-array@npm:^1.1.2": + version: 1.1.13 + resolution: "which-typed-array@npm:1.1.13" dependencies: available-typed-arrays: ^1.0.5 - call-bind: ^1.0.2 + call-bind: ^1.0.4 for-each: ^0.3.3 gopd: ^1.0.1 has-tostringtag: ^1.0.0 - checksum: 711ffc8ef891ca6597b19539075ec3e08bb9b4c2ca1f78887e3c07a977ab91ac1421940505a197758fb5939aa9524976d0a5bbcac34d07ed6faa75cedbb17206 + checksum: 3828a0d5d72c800e369d447e54c7620742a4cc0c9baf1b5e8c17e9b6ff90d8d861a3a6dd4800f1953dbf80e5e5cec954a289e5b4a223e3bee4aeb1f8c5f33309 languageName: node linkType: hard -"which@npm:^2.0.1, which@npm:^2.0.2": +"which@npm:^2.0.1": version: 2.0.2 resolution: "which@npm:2.0.2" dependencies: @@ -14100,12 +13594,14 @@ __metadata: languageName: node linkType: hard -"wide-align@npm:^1.1.5": - version: 1.1.5 - resolution: "wide-align@npm:1.1.5" +"which@npm:^4.0.0": + version: 4.0.0 + resolution: "which@npm:4.0.0" dependencies: - string-width: ^1.0.2 || 2 || 3 || 4 - checksum: d5fc37cd561f9daee3c80e03b92ed3e84d80dde3365a8767263d03dacfc8fa06b065ffe1df00d8c2a09f731482fcacae745abfbb478d4af36d0a891fad4834d3 + isexe: ^3.1.1 + bin: + node-which: bin/which.js + checksum: f17e84c042592c21e23c8195108cff18c64050b9efb8459589116999ea9da6dd1509e6a1bac3aeebefd137be00fabbb61b5c2bc0aa0f8526f32b58ee2f545651 languageName: node linkType: hard @@ -14131,21 +13627,21 @@ __metadata: linkType: hard "winston-transport@npm:^4.4.0, winston-transport@npm:^4.5.0": - version: 4.5.0 - resolution: "winston-transport@npm:4.5.0" + version: 4.6.0 + resolution: "winston-transport@npm:4.6.0" dependencies: logform: ^2.3.2 readable-stream: ^3.6.0 triple-beam: ^1.3.0 - checksum: a56e5678a80b88a73e77ed998fc6e19d0db19c989a356b137ec236782f2bf58ae4511b11c29163f99391fa4dc12102c7bc5738dcb6543f28877fa2819adc3ee9 + checksum: 19f06ebdbb57cb14cdd48a23145d418d3bbe538851053303f84f04a8a849bb530b78b1495a175059c1299f92945dc61d5421c4914fee32d9a41bc397d84f26d7 languageName: node linkType: hard "winston@npm:^3.10.0": - version: 3.10.0 - resolution: "winston@npm:3.10.0" + version: 3.11.0 + resolution: "winston@npm:3.11.0" dependencies: - "@colors/colors": 1.5.0 + "@colors/colors": ^1.6.0 "@dabh/diagnostics": ^2.0.2 async: ^3.2.3 is-stream: ^2.0.0 @@ -14156,7 +13652,7 @@ __metadata: stack-trace: 0.0.x triple-beam: ^1.3.0 winston-transport: ^4.5.0 - checksum: 47df0361220d12b46d1b3c98a1c380a3718321739d527a182ce7984fc20715e5b0b55db0bcd3fd076d1b1d3261903b890b053851cfd4bc028bda7951fa8ca2e0 + checksum: ca4454070f7a71b19f53c8c1765c59a013dab220edb49161b2e81917751d3e9edc3382430e4fb050feda04fb8463290ecab7cbc9240ec8d3d3b32a121849bbb0 languageName: node linkType: hard @@ -14214,9 +13710,9 @@ __metadata: languageName: node linkType: hard -"ws@npm:8.14.2": - version: 8.14.2 - resolution: "ws@npm:8.14.2" +"ws@npm:8.13.0": + version: 8.13.0 + resolution: "ws@npm:8.13.0" peerDependencies: bufferutil: ^4.0.1 utf-8-validate: ">=5.0.2" @@ -14225,13 +13721,13 @@ __metadata: optional: true utf-8-validate: optional: true - checksum: 3ca0dad26e8cc6515ff392b622a1467430814c463b3368b0258e33696b1d4bed7510bc7030f7b72838b9fdeb8dbd8839cbf808367d6aae2e1d668ce741d4308b + checksum: 53e991bbf928faf5dc6efac9b8eb9ab6497c69feeb94f963d648b7a3530a720b19ec2e0ec037344257e05a4f35bd9ad04d9de6f289615ffb133282031b18c61c languageName: node linkType: hard -"ws@npm:^8.13.0": - version: 8.13.0 - resolution: "ws@npm:8.13.0" +"ws@npm:8.16.0, ws@npm:^8.13.0": + version: 8.16.0 + resolution: "ws@npm:8.16.0" peerDependencies: bufferutil: ^4.0.1 utf-8-validate: ">=5.0.2" @@ -14240,21 +13736,11 @@ __metadata: optional: true utf-8-validate: optional: true - checksum: 53e991bbf928faf5dc6efac9b8eb9ab6497c69feeb94f963d648b7a3530a720b19ec2e0ec037344257e05a4f35bd9ad04d9de6f289615ffb133282031b18c61c - languageName: node - linkType: hard - -"xml2js@npm:^0.5.0": - version: 0.5.0 - resolution: "xml2js@npm:0.5.0" - dependencies: - sax: ">=0.6.0" - xmlbuilder: ~11.0.0 - checksum: 1aa71d62e5bc2d89138e3929b9ea46459157727759cbc62ef99484b778641c0cd21fb637696c052d901a22f82d092a3e740a16b4ce218e81ac59b933535124ea + checksum: feb3eecd2bae82fa8a8beef800290ce437d8b8063bdc69712725f21aef77c49cb2ff45c6e5e7fce622248f9c7abaee506bae0a9064067ffd6935460c7357321b languageName: node linkType: hard -"xml2js@npm:^0.6.0": +"xml2js@npm:^0.6.0, xml2js@npm:^0.6.2": version: 0.6.2 resolution: "xml2js@npm:0.6.2" dependencies: @@ -14300,9 +13786,9 @@ __metadata: linkType: hard "yaml@npm:^2.1.3": - version: 2.3.1 - resolution: "yaml@npm:2.3.1" - checksum: 2c7bc9a7cd4c9f40d3b0b0a98e370781b68b8b7c4515720869aced2b00d92f5da1762b4ffa947f9e795d6cd6b19f410bd4d15fdd38aca7bd96df59bd9486fb54 + version: 2.3.4 + resolution: "yaml@npm:2.3.4" + checksum: e6d1dae1c6383bcc8ba11796eef3b8c02d5082911c6723efeeb5ba50fc8e881df18d645e64de68e421b577296000bea9c75d6d9097c2f6699da3ae0406c030d8 languageName: node linkType: hard @@ -14323,22 +13809,7 @@ __metadata: languageName: node linkType: hard -"yargs@npm:17.7.1": - version: 17.7.1 - resolution: "yargs@npm:17.7.1" - dependencies: - cliui: ^8.0.1 - escalade: ^3.1.1 - get-caller-file: ^2.0.5 - require-directory: ^2.1.1 - string-width: ^4.2.3 - y18n: ^5.0.5 - yargs-parser: ^21.1.1 - checksum: 3d8a43c336a4942bc68080768664aca85c7bd406f018bad362fd255c41c8f4e650277f42fd65d543fce99e084124ddafee7bbfc1a5c6a8fda4cec78609dcf8d4 - languageName: node - linkType: hard - -"yargs@npm:^17.3.1, yargs@npm:^17.7.2": +"yargs@npm:17.7.2, yargs@npm:^17.3.1, yargs@npm:^17.7.2": version: 17.7.2 resolution: "yargs@npm:17.7.2" dependencies: diff --git a/yarn.lock b/yarn.lock new file mode 100644 index 00000000000..fb57ccd13af --- /dev/null +++ b/yarn.lock @@ -0,0 +1,4 @@ +# THIS IS AN AUTOGENERATED FILE. DO NOT EDIT THIS FILE DIRECTLY. +# yarn lockfile v1 + + diff --git a/yellow-paper/docs/addresses-and-keys/diversified-and-stealth.md b/yellow-paper/docs/addresses-and-keys/diversified-and-stealth.md index 7e2e849243b..3da4713d275 100644 --- a/yellow-paper/docs/addresses-and-keys/diversified-and-stealth.md +++ b/yellow-paper/docs/addresses-and-keys/diversified-and-stealth.md @@ -30,12 +30,8 @@ contract DiversifiedAccount return address_preimage.deployer_address ``` - - Given the contract does not require initialization since it has no constructor, it can be used by its owner without being actually deployed, which reduces the setup cost. - - ## Discarded Approaches An alternative approach was to introduce a new type of call, a diversified call, that would allow the caller to impersonate any address they can derive from their own, for an enshrined derivation mechanism. Account contracts could use this opcode, as opposed to a regular call, to issue calls on behalf on their diversified and stealth addresses. However, this approach failed to account for calls made back to the account contracts, in particular authwit checks. It also required protocol changes, introducing a new type of call which could be difficult to reason about, and increased attack surface. The only benefit over the approach chosen is that it would require one less extra function call to hop from the user's main account contract to the diversified or stealth one. diff --git a/yellow-paper/docs/addresses-and-keys/specification.md b/yellow-paper/docs/addresses-and-keys/specification.md index 64d3c10468a..d04e3f2aa76 100644 --- a/yellow-paper/docs/addresses-and-keys/specification.md +++ b/yellow-paper/docs/addresses-and-keys/specification.md @@ -334,15 +334,44 @@ An address is computed as the hash of the following fields: | Field | Type | Description | |----------|----------|----------| -| version | u8 | Version identifier. Initially one, bumped for any changes to the contract instance struct. | -| deployer_address | AztecAddress | Address of the deployer for this instance. | -| salt | Field | User-generated pseudorandom value for uniqueness. | -| contract_class_id | Field | Identifier of the contract class for this instance. | -| contract_args_hash | Field | Hash of the arguments to the constructor. | -| portal_contract_address | EthereumAddress | Optional address of the L1 portal contract. | -| public_keys | PublicKeys | Optional struct of public keys used for encryption and nullifying by this contract. | - -The `PublicKeys` struct can vary depending on the format of keys used by the address, but it is suggested it includes the master keys defined above: $\Npkm$, $\Tpkm$, $\Ivpkm$, $\Ovpkm$. +| `salt` | `Field` | User-generated pseudorandom value for uniqueness. | +| `deployer` | `AztecAddress` | Optional address of the deployer of the contract. | +| `contract_class_id` | `Field` | Identifier of the contract class for this instance. | +| `initialization_hash` | `Field` | Hash of the selector and arguments to the constructor. | +| `portal_contract_address` | `EthereumAddress` | Address of the L1 portal contract, zero if none. | +| `public_keys_hash` | `Field` | Hash of the struct of public keys used for encryption and nullifying by this contract, zero if no public keys. | + +Storing these fields in the address preimage allows any part of the protocol to check them by recomputing the hash and verifying that the address matches. Examples of these checks are: +- Sending an encrypted note to an undeployed account, which requires the sender app to check the recipient's public key given their address. This scenario also requires the recipient to share with the sender their public key and rest of preimage. +- Having the kernel circuit verify that the code executed at a given address matches the one from the class. +- Asserting that the initialization hash matches the function call in the contract constructor. +- Checking the portal contract address when sending a cross-chain message. + +:::warning +We may remove the `portal_contract_address` as a first-class citizen. +::: + +The hashing scheme for the address should then ensure that checks that are more frequent can be done cheaply, and that data shared out of band is kept manageable. We define the hash to be computed as follows: + +``` +salted_initialization_hash = pedersen([salt, initialization_hash, deployer as Field, portal_contract_address as Field], GENERATOR__SALTED_INITIALIZATION_HASH) +partial_address = pedersen([contract_class_id, salted_initialization_hash], GENERATOR__CONTRACT_PARTIAL_ADDRESS_V1) +address = pedersen([public_keys_hash, partial_address], GENERATOR__CONTRACT_ADDRESS_V1) +``` + +The `public_keys` array can vary depending on the format of keys used by the address, but it is suggested it includes the master keys defined above: $\Npkm$, $\Tpkm$, $\Ivpkm$, $\Ovpkm$. A suggested hashing is: +``` +public_keys_hash = pedersen([ + nullifier_pubkey.x, nullifier_pubkey.y, + tagging_pubkey.x, tagging_pubkey.y, + incoming_view_pubkey.x, incoming_view_pubkey.y, + outgoing_view_pubkey.x, outgoing_view_pubkey.y +], GENERATOR__PUBLIC_KEYS) +``` + +This recommended hash format is compatible with the [encryption precompiles](./precompiles.md#encryption-and-tagging-precompiles) initially defined in the protocol and advertised in the canonical [registry](../private-message-delivery/registry.md) for private message delivery. An address that chooses to use a different format for its keys will not be compatible with apps that rely on the registry for note encryption. Nevertheless, new precompiles introduced in future versions of the protocol could use different public keys formats. + + ## Derive siloed keys diff --git a/yellow-paper/docs/circuits/private-function.md b/yellow-paper/docs/circuits/private-function.md index 796f190f973..8418056b83a 100644 --- a/yellow-paper/docs/circuits/private-function.md +++ b/yellow-paper/docs/circuits/private-function.md @@ -31,7 +31,7 @@ The following format defines the ABI that is used by the private kernel circuit | _encrypted_note_preimage_hashes_ | [_[EncryptedNotePreimageHash](#encryptednotepreimagehash)_; _C_] | Hashes of the encrypted note preimages emitted in this function call. | | _private_call_stack_item_hashes_ | [_field_; _C_] | Hashes of the private function calls initiated by this function. | | _public_call_stack_item_hashes_ | [_field_; _C_] | Hashes of the public function calls initiated by this function. | -| _block_header_ | _[BlockHeader](#blockheader)_ | Information about the trees used for the transaction. | +| _header_ | _[Header](#header)_ | Header of a block which was used when assembling the tx. | | _chain_id_ | _field_ | Chain ID of the transaction. | | _version_ | _field_ | Version of the transaction. | @@ -104,7 +104,7 @@ The following format defines the ABI that is used by the private kernel circuit | _counter_ | _field_ | Counter at which the hash was emitted. | | _note_hash_counter_ | _field_ | Counter of the corresponding note hash. | -#### _BlockHeader_ +#### _Header_ | Field | Type | Description | | ----------------------------- | ------- | ----------------------------------------------------------------------------------------------- | diff --git a/yellow-paper/docs/circuits/private-kernel-initial.md b/yellow-paper/docs/circuits/private-kernel-initial.md index ed67d927e8f..ba3e64733a8 100644 --- a/yellow-paper/docs/circuits/private-kernel-initial.md +++ b/yellow-paper/docs/circuits/private-kernel-initial.md @@ -91,7 +91,7 @@ It verifies that the private function was executed successfully with the provide It ensures the private function circuit's intention by checking the following in _[private_call](#privatecall).[call_stack_item](#privatecallstackitem).[public_inputs](./private-function.md#public-inputs)_: -- The _block_header_ must match the one in the _[constant_data](#constantdata)_. +- The _header_ must match the one in the _[constant_data](#constantdata)_. #### Verifying the counters. @@ -212,7 +212,7 @@ This circuit verifies that the values in _[private_inputs](#private-inputs).[pri It verifies that: - The _tx_context_ in the _[constant_data](#constantdata)_ matches the _tx_context_ in the _[transaction_request](#transactionrequest)_. -- The _block_header_ must align with the one used in the private function circuit, as verified [earlier](#verifying-the-public-inputs-of-the-private-function-circuit). +- The _header_ must align with the one used in the private function circuit, as verified [earlier](#verifying-the-public-inputs-of-the-private-function-circuit). ## Private Inputs @@ -249,7 +249,7 @@ Data that remains the same throughout the entire transaction. | Field | Type | Description | | -------------- | -------------------------------------------------- | ------------------------------------------------------------- | -| _block_header_ | _[BlockHeader](./private-function.md#blockheader)_ | Roots of the trees at the time the transaction was assembled. | +| _header_ | _[Header](./private-function.md#header)_ | Header of a block which was used when assembling the tx. | | _tx_context_ | _[TransactionContext](#transactioncontext)_ | Context of the transaction. | ### _TransientAccumulatedData_ diff --git a/yellow-paper/docs/circuits/private-kernel-inner.md b/yellow-paper/docs/circuits/private-kernel-inner.md index 80b9fc15ab3..64bee422ae0 100644 --- a/yellow-paper/docs/circuits/private-kernel-inner.md +++ b/yellow-paper/docs/circuits/private-kernel-inner.md @@ -91,7 +91,7 @@ This circuit verifies this proof and [the proof of the previous kernel iteration It ensures the private function circuit's intention by checking the following in _[private_call](#privatecall).[call_stack_item](#privatecallstackitem).[public_inputs](./private-function.md#public-inputs)_: -- The _block_header_ must match the one in the _[constant_data](./private-kernel-initial.md#constantdata)_. +- The _header_ must match the one in the _[constant_data](./private-kernel-initial.md#constantdata)_. - If it is a static call (_`public_inputs.call_context.is_static_call == true`_), it ensures that the function does not induce any state changes by verifying that the following arrays are empty: - _note_hashes_ - _nullifiers_ diff --git a/yellow-paper/docs/circuits/private-kernel-reset.md b/yellow-paper/docs/circuits/private-kernel-reset.md index 9570bce0571..a5c6bff1cae 100644 --- a/yellow-paper/docs/circuits/private-kernel-reset.md +++ b/yellow-paper/docs/circuits/private-kernel-reset.md @@ -30,7 +30,7 @@ A read request can pertain to one of two note types: 3. Perform a membership check on the note being read. Where: - The leaf corresponds to the hash of the note: _`read_request.note_hash`_ - The index and sibling path are in: _`read_request_membership_witnesses[i]`_. - - The root is the _note_hash_tree_root_ in the _[block_header](./private-function.md#blockheader)_ within _[public_inputs](#public-inputs).[constant_data](./private-kernel-initial.md#constantdata)_. + - The root is the _note_hash_tree_root_ in the _[header](./private-function.md#header)_ within _[public_inputs](#public-inputs).[constant_data](./private-kernel-initial.md#constantdata)_. > Following the above process, at most _N_ read requests will be cleared, where _N_ is the length of the _persistent_read_indices_ array. It's worth noting that there can be multiple versions of this reset circuit, each with a different value of _N_. diff --git a/yellow-paper/docs/circuits/public-kernel-inner.md b/yellow-paper/docs/circuits/public-kernel-inner.md index 706c8949fe8..1bf0af700c8 100644 --- a/yellow-paper/docs/circuits/public-kernel-inner.md +++ b/yellow-paper/docs/circuits/public-kernel-inner.md @@ -29,7 +29,7 @@ It verifies the public deployment of the contract instance by conducting a membe - _deployer_address_ is defined in _[private_inputs](#private-inputs).[public_call](#publiccall).[contract_data](../contract-deployment/instances.md#structure)_. - _contract_data_ is defined in _[private_inputs](#private-inputs).[public_call](#publiccall).[call_stack_item](#publiccallstackitem)_. - The index and sibling path are provided in _contract_deployment_membership_witness_ through _[private_inputs](#private-inputs).[public_call](#publiccall)_. -- The root is the _nullifier_tree_root_ in the _[block_header](./private-function.md#blockheader)_ within _[public_inputs](#public-inputs).[constant_data](./private-kernel-initial.md#constantdata)_. +- The root is the _nullifier_tree_root_ in the _[header](./private-function.md#header)_ within _[public_inputs](#public-inputs).[constant_data](./private-kernel-initial.md#constantdata)_. #### Ensuring the function is legitimate: @@ -67,7 +67,7 @@ It verifies that the public function was executed with the provided proof data, It ensures the public function's intention by checking the following in _[public_call](#publiccall).[call_stack_item](#publiccallstackitem).[public_inputs](#publicfunctionpublicinputs)_: -- The _block_header_ must match the one in the _[constant_data](./private-kernel-initial.md#constantdata)_. +- The _header_ must match the one in the _[constant_data](./private-kernel-initial.md#constantdata)_. - If it is a static call (_`public_inputs.call_context.is_static_call == true`_), it ensures that the function does not induce any state changes by verifying that the following arrays are empty: - _note_hashes_ - _nullifiers_ @@ -231,7 +231,7 @@ The format aligns with the _[Public Inputs](./public-kernel-tail.md#public-input | _storage_writes_ | [_[StorageWrite](./public-kernel-tail.md#storagewrite)_; _C_] | Data written to the public data tree. | | _unencrypted_log_hashes_ | [_[UnencryptedLogHash](./private-function.md#unencryptedloghash)_; _C_] | Hashes of the unencrypted logs emitted in this function call. | | _public_call_stack_item_hashes_ | [_field_; _C_] | Hashes of the public function calls initiated by this function. | -| _block_header_ | _[BlockHeader](#blockheader)_ | Information about the trees used for the transaction. | +| _header_ | _[Header](./private-function.md#header)_ | Information about the trees used for the transaction. | | _chain_id_ | _field_ | Chain ID of the transaction. | | _version_ | _field_ | Version of the transaction. | diff --git a/yellow-paper/docs/contract-deployment/classes.md b/yellow-paper/docs/contract-deployment/classes.md index 33f14c27100..7069d497479 100644 --- a/yellow-paper/docs/contract-deployment/classes.md +++ b/yellow-paper/docs/contract-deployment/classes.md @@ -1,10 +1,10 @@ # Contract classes -A contract class is a collection of related unconstrained, private, and public functions. Contract classes don't have state, they just define code. +A contract class is a collection of state variable declarations, and related unconstrained, private, and public functions. Contract classes don't have any initialized state, they just define code. A contract class cannot be called; only a contract instance can be called. ## Rationale -Contract classes simplify the process of reusing code by enshrining implementations as a first-class citizen at the protocol. Given multiple contract instances that rely on the same class, the class needs to be declared only once, reducing the deployment cost for all contract instances. Classes also simplify the process of upgradeability. Classes decouple state from code, making it easier for an instance to switch to different code while retaining its state. +Contract classes simplify the process of reusing code by enshrining implementations as a first-class citizen at the protocol. Given multiple [contract instances](./instances.md) that rely on the same class, the class needs to be declared only once, reducing the deployment cost for all contract instances. Classes also simplify the process of upgradeability; classes decouple state from code, making it easier for an instance to switch to different code while retaining its state. :::info Read the following discussions for additional context: @@ -21,56 +21,207 @@ The structure of a contract class is defined as: | Field | Type | Description | |----------|----------|----------| -| version | u8 | Version identifier. Initially one, bumped for any changes to the contract class struct. | -| registerer_address | AztecAddress | Address of the canonical contract used for registering this class. | -| artifact_hash | Field | Hash of the entire contract artifact, including compiler information, proving backend, and all generated ACIR and Brillig. The specification of this hash is left to the compiler and not enforced by the protocol. | -| constructor_function | PrivateFunction | PublicFunction | Constructor for instances of this class. | -| private_functions | PrivateFunction[] | List of private functions. | -| public_functions | PublicFunction[] | List of public functions. | -| unconstrained_functions | UnconstrainedFunction[] | List of unconstrained functions. | +| `version` | `u8` | Version identifier. Initially one, bumped for any changes to the contract class struct. | +| `artifact_hash` | `Field` | Hash of the contract artifact. The specification of this hash is not enforced by the protocol. Should include commitments to unconstrained code and compilation metadata. Intended to be used by clients to verify that an off-chain fetched artifact matches a registered class. | +| `private_functions` | [`PrivateFunction[]`](#private-function) | List of individual private functions, constructors included. | +| `packed_public_bytecode` | `Field[]` | [Packed bytecode representation](../public-vm/bytecode-validation-circuit.md#packed-bytecode-representation) of the AVM bytecode for all public functions in this contract. | - - +Note that individual public functions are not first-class citizens in the protocol, so the contract entire public function bytecode is stored in the class, unlike private or unconstrained functions which are differentiated individual circuits recognized by the protocol. + +As for unconstrained functions, these are not used standalone within the protocol. They are either inlined within private functions, or called from a PXE as _getters_ for a contract. Calling from a private function to an unconstrained one in a different contract is forbidden, since the caller would have no guarantee of the code run by the callee. Considering this, unconstrained functions are not part of a contract class at the protocol level. + +### Class Identifier + +Also known as `contract_class_id`, the Class Identifier is both a unique identifier and a commitment to the struct contents. It is computed as: + +``` +private_function_leaves = private_functions.map(fn => pedersen([fn.function_selector as Field, fn.vk_hash], GENERATOR__FUNCTION_LEAF)) +private_functions_root = merkleize(private_function_leaves) +public_bytecode_commitment = calculate_commitment(packed_public_bytecode) +contract_class_id = pedersen([artifact_hash, private_functions_root, public_bytecode_commitment], GENERATOR__CLASS_IDENTIFIER_V1) +``` + +Private Functions are hashed into Function Leaves before being merkleized into a tree of height `FUNCTION_TREE_HEIGHT=5`. Empty leaves have value `0`. A poseidon hash is used. The AVM public bytecode commitment is calculated as [defined in the Public VM section](../public-vm/bytecode-validation-circuit.md#committed-representation). ### Private Function +The structure of each private function within the protocol is the following: + | Field | Type | Description | |----------|----------|----------| -| function_selector | u32 | Selector of the function. Calculated as the hash of the method name and arguments. | -| vk_hash | Field | Hash of the verification key associated to this private function. | -| salt | Field | Optional value for salting the bytecode of a function. | -| bytecode_hash | Field | Hash of the compiled function artifact, including all generated ACIR and Brillig. | -| optional_bytecode | Buffer | Optional bytecode for the function. Private function bytecode can be kept private if desired and only broadcasted to participants of the contract. | +| `function_selector` | `u32` | Selector of the function. Calculated as the hash of the method name and parameters. The specification of this is not enforced by the protocol. | +| `vk_hash` | `Field` | Hash of the verification key associated to this private function. | -### Public and Unconstrained Function +Note the lack of visibility modifiers. Internal functions are specified as a macro, and the check is handled at the application circuit level by verifying that the `context.msg_sender` equals the contract current address. - -| Field | Type | Description | -|----------|----------|----------| -| function_selector | u32 | Selector of the function. Calculated as the hash of the method name and arguments. | -| bytecode_hash | Field | Hash of the compiled function artifact, including all generated Brillig code. | -| bytecode | Buffer | Full bytecode for the function. Must hash to the `artifact_hash`. | +Also note the lack of commitment to the function compilation artifact. Even though a commitment to a function is required so that the PXE can verify the execution of correct unconstrained Brillig code embedded within private functions, this is handled entirely out of protocol. As such, PXEs are expected to verify it against the `artifact_hash` in the containing contract class. - +### Artifact Hash -### Class Identifier +Even though not enforced by the protocol, it is suggested for the `artifact_hash` to follow this general structure, in order to be compatible with the definition of the [`broadcast` function below](#broadcast). + +``` +private_functions_artifact_leaves = artifact.private_functions.map(fn => + sha256(fn.selector, fn.metadata_hash, sha256(fn.private_bytecode)) +) +private_functions_artifact_tree_root = merkleize(private_functions_artifact_leaves) + +unconstrained_functions_artifact_leaves = artifact.unconstrained_functions.map(fn => + sha256(fn.selector, fn.metadata_hash, sha256(fn.unconstrained_bytecode)) +) +unconstrained_functions_artifact_tree_root = merkleize(unconstrained_functions_artifact_leaves) + +artifact_hash = sha256( + private_functions_artifact_tree_root, + unconstrained_functions_artifact_tree_root, + artifact_metadata, +) +``` + +For the artifact hash merkleization and hashing is done using sha256, since it is computed and verified outside of circuits and does not need to be SNARK friendly. Fields are left-padded with zeros to 256 bits before being hashed. Function leaves are sorted in ascending order before being merkleized, according to their function selectors. Note that a tree with dynamic height is built instead of having a tree with a fixed height, since the merkleization is done out of a circuit. + +Bytecode for private functions is a mix of ACIR and Brillig, whereas unconstrained function bytecode is Brillig exclusively, as described on the [bytecode section](../bytecode/index.md). + +The metadata hash for each function is suggested to be computed as the sha256 of all JSON-serialized fields in the function struct of the compilation artifact, except for bytecode and debug symbols. The metadata is JSON-serialized using no spaces, and sorting ascending all keys in objects before serializing them. + +``` +function_metadata = omit(function, "bytecode", "debug_symbols") +function_metadata_hash = sha256(json_serialize(function_metadata)) +``` + +The artifact metadata stores all data that is not contained within the contract functions and is not debug specific. This includes the compiler version identifier, events interface, and name. Metadata is JSON-serialized in the same fashion as the function metadata. + +``` +artifact_metadata = omit(artifact, "functions", "file_map") +artifact_metadata_hash = sha256(json_serialize(artifact_metadata)) +``` + +### Versioning + +A contract class has an implicit `version` field that identifies the schema of the struct. This allows to change the shape of a contract class in future upgrades to the protocol to include new fields or change existing ones, while preserving the structure for existing classes. Supporting new types of contract classes would require introducing new kernel circuits, and a transaction proof may require switching between different kernel circuits depending on the version of the contract class used for each function call. + +Note that the version field is not directly used when computing the contract class id, but is implicit in the generator index. Bumping the version of a contract class struct would involve using a different generator index for computing its id. + +## Canonical Contract Class Registerer + +A contract class is registered by calling a private `register` function in a canonical `ContractClassRegisterer` contract, which will emit a Registration Nullifier. The Registration Nullifier is defined as the `contract_class_id` itself of the class being registered. Note that the Private Kernel circuit will [silo](../circuits/private-kernel-tail.md#siloing-values) this value with the contract address of the `ContractClassRegisterer`, effectively storing the hash of the `contract_class_id` and `ContractClassRegisterer` address in the nullifier tree. As such, proving that a given contract class has been registered requires checking existence of this siloed nullifier. + +The rationale for the Registerer contract is to guarantee that the public bytecode for a contract class is publicly available. This is a requirement for publicly [deploying a contract instance](./instances.md#publicly_deployed), which ultimately prevents a sequencer from executing a public function for which other nodes in the network may not have the code. + +### Register Function + +The `register` function receives the artifact hash, private functions tree root, and packed public bytecode of a `ContractClass` struct as [defined above](#structure), and performs the following steps: + +- Assert that `packed_public_bytecode` is valid according to the definition in the [Public VM section](../public-vm/bytecode-validation-circuit.md#packed-bytecode-representation). +- Computes the `contract_class_id` as [defined above](#class-identifier). +- Emits the resulting `contract_class_id` as a nullifier to prevent the same class from being registered again. +- Emits an unencrypted event `ContractClassRegistered` with the contents of the contract class. + +In pseudocode: + +``` +function register( + artifact_hash: Field, + private_functions_root: Field, + packed_public_bytecode: Field[], +) + assert is_valid_packed_public_bytecode(packed_public_bytecode) + + version = 1 + bytecode_commitment = calculate_commitment(packed_public_bytecode) + contract_class_id = pedersen([version, artifact_hash, private_functions_root, bytecode_commitment], GENERATOR__CLASS_IDENTIFIER) + + emit_nullifier contract_class_id + emit_unencrypted_event ContractClassRegistered(contract_class_id, version, artifact_hash, private_functions_root, packed_public_bytecode) +``` + +Upon seeing a `ContractClassRegistered` event in a mined transaction, nodes are expected to store the contract class, so they can retrieve it when executing a public function for that class. Note that a class may be used for deploying a contract within the same transaction in which it is registered. -The class identifier is computed by merkleizing the lists of private, public, and unconstrained functions separately, replacing the functions lists in the contract class struct with their respective tree roots, and then hashing the resulting struct. +Note that emitting the `contract_class_id` as a nullifier (the `contract_class_id_nullifier`), instead of as an entry in the note hashes tree, allows nodes to prove non-existence of a class. This is needed so a sequencer can provably revert a transaction that includes a call to an unregistered class. -## Registration +### Genesis -A contract class is registered by calling a private `register` function in a canonical `ClassRegisterer` contract. The `register` function receives a `ContractClass` struct as defined above, except for the `registerer_address`, and performs the following checks: +The `ContractClassRegisterer` will need to exist from the genesis of the Aztec Network, otherwise nothing will ever be publicly deployable to the network. The Class Nullifier for the `ContractClassRegisterer` contract will be pre-inserted into the genesis nullifier tree at leaf index `GENESIS_NULLIFIER_LEAF_INDEX_OF_CONTRACT_CLASS_REGISTERER_CLASS_ID_NULLIFIER=1`. The canonical instance will be deployed at `CONTRACT_CLASS_REGISTERER_ADDRESS=0x10000`, and its Deployment Nullifier will be inserted at `GENESIS_NULLIFIER_LEAF_INDEX_OF_CONTRACT_CLASS_REGISTERER_DEPLOYMENT_NULLIFIER=2`. -- `version` is 1 for the initial release -- `bytecode` for each function hashes to the `bytecode_hash` + -The `register` function then: + -- Emits the `ContractClass` struct as unencrypted events. -- Computes the class identifier as the hash of the `ContractClass` object. -- Emits the computed class identifier as a nullifier. +### Broadcast -Upon seeing a new contract class registration event in a mined transaction, nodes are expected to store the contract class, so they can retrieve it when executing a public function for that class. +The `ContractClassRegisterer` has an additional private `broadcast` functions that can be used for broadcasting on-chain the bytecode, both ACIR and Brillig, for private functions and unconstrained in the contract. Any user can freely call this function. Given that ACIR and Brillig [do not have a circuit-friendly commitment](../bytecode/index.md), it is left up to nodes to perform this check. -Note that emitting the class identifier as a nullifier, instead of as an entry in the note hashes tree, allows public functions to prove non-existence of a class, which is required to support public contract instance deployments. +Broadcasted contract artifacts that do not match with their corresponding `artifact_hash`, or that reference a `contract_class_id` that has not been broadcasted, can be safely discarded. + +``` +function broadcast_all_private_functions( + contract_class_id: Field, + artifact_metadata: Field, + unconstrained_functions_artifact_tree_root: Field, + functions: { selector: Field, metadata: Field, vk_hash: Field, bytecode: Field[] }[], +) + emit_unencrypted_event ClassPrivateFunctionsBroadcasted( + contract_class_id, + artifact_metadata, + unconstrained_functions_artifact_tree_root, + functions, + ) +``` + +``` +function broadcast_all_unconstrained_functions( + contract_class_id: Field, + artifact_metadata: Field, + private_functions_artifact_tree_root: Field, + functions:{ selector: Field, metadata: Field, bytecode: Field[] }[], +) + emit_unencrypted_event ClassUnconstrainedFunctionsBroadcasted( + contract_class_id, + artifact_metadata, + unconstrained_functions_artifact_tree_root, + functions, + ) +``` + + + +The broadcast functions are split between private and unconstrained to allow for private bytecode to be broadcasted, which is valuable for composability purposes, without having to also include unconstrained functions, which could be costly to do due to data broadcasting costs. Additionally, note that each broadcast function must include enough information to reconstruct the `artifact_hash` from the Contract Class, so nodes can verify it against the one previously registered. + +The `ContractClassRegisterer` contract also allows broadcasting individual functions, in case not every function needs to be put on-chain. This requires providing a Merkle membership proof for the function within its tree, that nodes can validate. + +``` +function broadcast_private_function( + contract_class_id: Field, + artifact_metadata: Field, + unconstrained_functions_artifact_tree_root: Field, + function_leaf_sibling_path: Field, + function: { selector: Field, metadata: Field, vk_hash: Field, bytecode: Field[] }, +) + emit_unencrypted_event ClassPrivateFunctionBroadcasted( + contract_class_id, + artifact_metadata, + unconstrained_functions_artifact_tree_root, + function_leaf_sibling_path, + function, + ) +``` + +``` +function broadcast_unconstrained_function( + contract_class_id: Field, + artifact_metadata: Field, + private_functions_artifact_tree_root: Field, + function_leaf_sibling_path: Field, + function: { selector: Field, metadata: Field, bytecode: Field[] }[], +) + emit_unencrypted_event ClassUnconstrainedFunctionBroadcasted( + contract_class_id, + artifact_metadata, + unconstrained_functions_artifact_tree_root, + function_leaf_sibling_path: Field, + function, + ) +``` + +It is strongly recommended for developers registering new classes to broadcast the code for `compute_hash_and_nullifier`, so any private message recipients have the code available to process their incoming notes. However, the `ContractClassRegisterer` contract does not enforce this during registration, since it is difficult to check the multiple signatures for `compute_hash_and_nullifier` as they may evolve over time to account for new note sizes. diff --git a/yellow-paper/docs/contract-deployment/instances.md b/yellow-paper/docs/contract-deployment/instances.md index 6bf602068be..9d5e333824d 100644 --- a/yellow-paper/docs/contract-deployment/instances.md +++ b/yellow-paper/docs/contract-deployment/instances.md @@ -1,90 +1,175 @@ # Contract instances -A contract instance is a concrete deployment of a [contract class](./classes.md). A contract instance has state, both private and public, as well as an address that acts as identifier, and can be called into. A contract instance always references a contract class, that dictates what code it executes when called. +A contract instance is a concrete deployment of a [contract class](./classes.md). A contract instance always references a contract class, which dictates what code it executes when called. A contract instance has state (both private and public), as well as an address that acts as its identifier. A contract instance can be called into. ## Requirements -- Users must be able to precompute the address of a given deployment. This allows users to precompute their account contract addresses and receive funds before interacting with the chain, and also allows counterfactual deployments. -- An address must be linked to its deployer address. This allows simple diversified and stealth account contracts. Related, a precomputed deployment may or may not be restricted to be executed by a given address. +- Users must be able to precompute the address of a given contract instance. This allows users to precompute their account contract addresses and receive funds before interacting with the chain, and also allows counterfactual deployments. +- An address must be linkable to its deployer address. This allows simple diversified and stealth account contracts. Related, a precomputed deployment may or may not be restricted to be executed by a given address. - A user calling into an address must be able to prove that it has not been deployed. This allows the executor to prove that a given call in a transaction is unsatisfiable and revert accordingly. - A user should be able to privately call into a contract without publicly deploying it. This allows private applications to deploy contracts without leaking information about their usage. -## Structure +## `ContractInstance` structure The structure of a contract instance is defined as: | Field | Type | Description | |----------|----------|----------| -| version | u8 | Version identifier. Initially one, bumped for any changes to the contract instance struct. | -| deployer_address | AztecAddress | Address of the canonical deployer contract that creates this instance. | -| salt | Field | User-generated pseudorandom value for uniqueness. | -| contract_class_id | Field | Identifier of the contract class for this instance. | -| contract_args_hash | Field | Hash of the arguments to the constructor. | -| portal_contract_address | EthereumAddress | Optional address of the L1 portal contract. | -| public_keys_hash | Field | Optional hash of the struct of public keys used for encryption and nullifying by this contract. | +| `version` | `u8` | Version identifier. Initially one, bumped for any changes to the contract instance struct. | +| `salt` | `Field` | User-generated pseudorandom value for uniqueness. | +| `deployer` | `AztecAddress` | Optional address of the deployer of the contract. | +| `contract_class_id` | `Field` | Identifier of the contract class for this instance. | +| `initialization_hash` | `Field` | Hash of the selector and arguments to the constructor. | +| `portal_contract_address` | `EthereumAddress` | Optional address of the L1 portal contract. | +| `public_keys_hash` | `Field` | Optional hash of the struct of public keys used for encryption and nullifying by this contract. | - + -## Address +### Versioning -The address of the contract instance is computed as the hash of all elements in the structure above, as defined in the addresses and keys section. This computation is deterministic, which allows any user to precompute the expected deployment address of their contract, including account contracts. +Contract instances have a `version` field that identifies the schema of the instance, allowing for changes to the struct in future versions of the protocol, same as the contract class [version](./classes.md#versioning). -## Statuses +### Address -A contract instance at a given address can be in any of the following statuses: +The address of the contract instance is computed as the hash of the elements in the structure above, as defined in [the addresses and keys section](../addresses-and-keys/specification.md#address). This computation is deterministic, which allows any user to precompute the expected deployment address of their contract, including account contracts. -- **Undeployed**: The instance has not yet been deployed. A user who knows the preimage of the address can issue a private call into the contract, as long as it does not require initialization. Public function calls to this address will fail. -- **Privately deployed**: The instance constructor has been executed, but its class identifier has not been broadcasted. A user who knows the preimage of the address can issue a private call into the contract. Public function calls to this address will fail. Private deployments are signalled by emitting an initialization nullifier when the constructor runs. -- **Publicly deployed**: The instance constructor has been executed, and the address preimage has been broadcasted. All function calls to the address, private or public, are valid. Public deployments are signalled by emitting a public deployment nullifier. +### Deployer - +The `deployer` address of a contract instance is used to restrict who can initialize the contract (ie call its constructor) and who can publicly deploy it. Note that neither of these checks are enforced by the protocol: the initialization is checked by the constructor itself, and the deployment by the `ContractInstanceDeployer` (described below). Furthermore, a contract class may choose to not enforce this restriction by removing the check from the constructor. -## Constructors +The `deployer` address can be set to zero to signal that anyone can initialize or publicly deploy an instance. -Contract constructors are not enshrined in the protocol, but handled at the application circuit level. A contract must satisfy the following requirements: +## Initialization -- The constructor must be invoked exactly once -- The constructor must be invoked with the arguments in the address preimage -- Functions that depend on contract initialization cannot be invoked until the constructor is run +A contract instance at a given address can be either Initialized or not. An address by default is not initialized, and it is considered to be Initialized once it emits an Initialization Nullifier, meaning it can only be initialized once. -These checks can be embedded in the application circuit itself. The constructor emits a standardized initialization nullifier when it is invoked, which prevents it from being called more than once. The constructor code must also check that the arguments hash it received matches the ones in the address preimage, supplied via an oracle call. All other functions in the contract must include a merkle membership proof for the nullifier, to prevent them from being called before the constructor is invoked. Note that a contract may choose to allow some functions to be called before initialization. +### Uninitialized -The checks listed above should not be manually implemented by a contract developer, but rather included as part of the Aztec macros for Noir. +The instance has not yet been initialized, meaning its constructor has not been called. This is the default state for any given address. A user who knows the preimage of the address can still issue a private call into a function in the contract, as long as that function does not assert that the contract has been initialized by checking the Initialization Nullifier. -Constructors may be either private or public functions. Contracts with private constructors can be privately or publicly deployed, contracts with public constructors can only be publicly deployed. +All public function calls to an Uninitialized address _must_ fail, since the Contract Class for it is not known to the network. If the Class is not known to the network, then an Aztec Node, whether it is the elected sequencer or a full node following the chain, may not be able to execute the bytecode for a public function call, which is undesirable. The failing of public function calls to Uninitialized addresses is enforced by having the Public Kernel Circuit check that the Deployment Nullifier for the instance has been emitted. -Removing constructors from the protocol itself simplifies the kernel circuit. Separating initialization from public deployment also allows to implement private deployments, since a private deployment is equivalent to just invoking the constructor function at a given address. +This state allows using a contract privately before it has been initialized or deployed, which is used in [diversified and stealth accounts](../addresses-and-keys/diversified-and-stealth.md). + +### Initialized + +An instance is Initialized when a constructor for the instance has been invoked, and the constructor has emitted the instance's Initialization Nullifier. All private functions that require the contract to be initialized by checking the existence of the Initialization Nullifier can now be called by any user who knows the address preimage. + +The Initialization Nullifier is defined as the contract address itself. Note that the nullifier later gets [siloed by the Private Kernel Circuit](../circuits/private-kernel-tail.md#siloing-values) before it gets broadcasted in a transaction. + +In this state, public functions must still fail, for the same reason as for Uninitialized instances. This state then allows using a contract privately before it has been publicly deployed, which is useful for working on private contracts between a small set of parties. + +:::warning +It may be the case that it is not possible to read a nullifier in the same transaction that it was emitted due to protocol limitations. That would lead to a contract not being callable in the same transaction as it is initialized. To work around this, we can emit an Initialization Commitment along with the Initialization Nullifier, which _can_ be read in the same transaction as it is emitted. If needed, the Initialization Commitment is defined exactly as the Initialization Nullifier. +::: + +### Constructors + +Contract constructors are not enshrined in the protocol, but handled at the application circuit level. Constructors are methods used for initializing a contract, either private or public, and a contract may have more than a single constructor. A contract must ensure the following requirements are met: + +- A contract may be initialized at most once +- A contract must be initialized using the method and arguments defined in its address preimage +- A contract must be initialized by its `deployer` (if it's non-zero) +- All functions that depend on contract initialization cannot be invoked until the contract is initialized + +These checks are embedded in the application circuits themselves. The constructor emits an Initialization Nullifier when it is invoked, which prevents it from being called more than once. The constructor code must also check that its own selector and the arguments for the call match the ones in the address preimage, which are supplied via an oracle call. + +All non-constructor functions in the contract should require a merkle membership proof for the Initialization Nullifier, to prevent them from being called before the constructor is invoked. Nevertheless, a contract may choose to allow some functions to be called before initialization, such as in the case of [Diversified and Stealth account contracts](../addresses-and-keys/diversified-and-stealth.md). + +Removing constructors from the protocol itself simplifies the kernel circuit, and decoupling Initialization from Public Deployments allows users to keep contract instances private if they wish to do so. ## Public Deployment -A new contract instance can be _publicly deployed_ by calling a `deploy` function in a canonical `Deployer` contract. This function receives the arguments for a `ContractInstance` struct as described above, except for the `deployer_address` which is the deployer's own address, and executes the following actions: +A Contract Instance is considered to be Publicly Deployed when it has been broadcasted to the network via a canonical `ContractInstanceDeployer` contract, which also emits a Deployment Nullifier associated to the deployed instance. A contract needs to be Publicly Deployed for any of its public functions to be called. Note that this last restriction makes Public Deployment a protocol-level concern, whereas Initialization is an application-level concern. + +The Deployment Nullifier is defined as the address of the contract being deployed. Note that it later gets [siloed](../circuits/private-kernel-tail.md#siloing-values) using the `ContractInstanceDeployer` address by the Kernel Circuit, so this nullifier is effectively the hash of the deployed contract address and the `ContractInstanceDeployer` address. + +Only in this state public function calls are valid. The Public Kernel Circuit validates that the Deployment Nullifier has been emitted by the `ContractInstanceDeployer` as part of its checks. Note that this requires hardcoding the address of an application-level contract in a protocol circuit. + +### Canonical Contract Instance Deployer + +A new contract instance can be _Publicly Deployed_ by calling a `deploy` function in a canonical `ContractInstanceDeployer` contract. This function receives the arguments for a `ContractInstance` struct as described [above](#contractinstance-structure): + +- Validates the referenced `contract_class_id` exists. This can be done via either a call to the `ClassRegisterer` contract, or by directly reading the corresponding nullifier. +- Set `deployer` to zero or `msg_sender` depending on whether the `universal_deploy` flag is set. +- Computes the resulting `new_contract_address`. +- Emits the resulting address as the Deployment Nullifier to signal the public deployment, so callers can prove that the contract has or has not been publicly deployed. +- Emits an unencrypted event `ContractInstanceDeployed` with the address preimage. + +The pseudocode for the process described above is the following: -- Validates the referenced `contract_class_id` exists. -- Mixes in the `msg_sender` with the user-provided `salt` by hashing them together, ensuring that the deployment is unique for the requester. -- Computes the resulting contract address. -- Emits the resulting address as a nullifier to signal the public deployment, so callers can prove that the contract has or has not been publicly deployed. -- Emits an unencrypted event with the address preimage, excluding the `deployer_address` which is already part of the log. -- Either proves the corresponding class identifier has no constructor, or calls the constructor with the preimage of the supplied `contract_args_hash`, or proves that the constructor nullifier has already been emitted so a previously privately-deployed contract can be publicly deployed. +``` +function deploy ( + salt: Field, + contract_class_id: Field, + initialization_hash: Field, + portal_contract_address: Field, + public_keys_hash: Field, + universal_deploy?: boolean, +) + assert nullifier_exists silo(contract_class_id, ContractClassRegisterer) + assert is_valid_eth_address(portal_contract_address) + + deployer = if universal_deploy then zero else msg_sender + version = 1 + address = compute_address(version, salt, deployer, contract_class_id, initialization_hash, portal_contract_address, public_keys_hash) -Upon seeing a new contract deployed event from the canonical deployer contract, nodes are expected to store the address and preimage, to verify executed code during public code execution as described in the next section. + emit_nullifier(address) -The `Deployer` contract provides two implementations of the `deploy` function: a private and a public one. Contracts with a private constructor are expected to use the former, and contracts with public constructors expected to use the latter. Contracts with no constructors or that have already been privately-deployed can use either. + emit_unencrypted_event ContractInstanceDeployed(address, version, salt, contract_class_id, initialization_hash, portal_contract_address, public_keys_hash) +``` -Additionally, the `Deployer` contract provides two `universal_deploy` functions, a private and a public one, with the same arguments as the `deploy` one, that just forwards the call to the `deploy` contract. This makes `msg_sender` in the `deploy` contract to be the `Deployer` contract itself, and allows for universal deployments that are semantically decoupled from their deployer, and can be permissionlessly invoked by any user who knows the address preimage. +Upon seeing a `ContractInstanceDeployed` event from the canonical `ContractInstanceDeployer` contract, nodes are expected to store the address and preimage, so they can verify executed code during public code execution as described in the next section. + +The `ContractInstanceDeployer` contract provides two implementations of the `deploy` function: a private and a public one. Contracts with a private constructor are expected to use the former, and contracts with public constructors expected to use the latter. Contracts that have already been privately Initialized can use either. + +### Genesis + +The `ContractInstanceDeployer` will need to exist from the genesis of the Aztec Network, otherwise nothing will ever be deployable to the network. The Class Nullifier for the `ContractInstanceDeployer` contract will be pre-inserted into the genesis nullifier tree at leaf index `GENESIS_NULLIFIER_LEAF_INDEX_OF_CONTRACT_INSTANCE_DEPLOYER_CLASS_ID_NULLIFIER=3`. The canonical instance will be deployed at `CONTRACT_INSTANCE_DEPLOYER_ADDRESS=0x10001`, and its Deployment Nullifier will be inserted at `GENESIS_NULLIFIER_LEAF_INDEX_OF_CONTRACT_INSTANCE_DEPLOYER_DEPLOYMENT_NULLIFIER=4`. + + + + ## Verification of Executed Code -The kernel circuit, both private and public, is responsible for verifying that the code loaded for a given function execution matches the expected one. This requires the following checks: +The Kernel Circuit, both private and public, is responsible for verifying that the code loaded for a given function execution matches the expected one. This requires the following checks: + +- The `contract_class_id` of the address called is the expected one, verified by hashing the address preimage that includes the `contract_class_id`. +- The [function selector](./classes.md#private-function) being executed is part of the `contract_class_id`, verified via a Merkle membership proof of the selector in the functions tree of the Contract Class. + +Specific to private functions: +- The hash of the `verification_key` matches the `vk_hash` defined in the corresponding [Private Function](./classes.md#private-function) for the Contract Class. + +Specific to public functions: +- The bytecode loaded by the [AVM](../public-vm/avm.md) for the contract matches the `bytecode_commitment` in the contract class, verified using the [bytecode validation circuit](../public-vm/bytecode-validation-circuit.md). +- The contract Deployment Nullifier has been emitted, or prove that it hasn't, in which case the transaction is expected to revert. This check is done via a merkle (non-)membership proof of the Deployment Nullifier. Note that a public function should be callable in the same transaction in which its contract Deployment Nullifier was emitted. + +Note that, since constructors are handled at the application level, the kernel circuit is not required to check the Initialization Nullifier before executing code. -- The contract class identifier of the address called is the expected one, verified by hashing the address preimage that includes the class id. -- The function identifier being executed is part of the class id, verified via a merkle membership proof. -- The function code executed matches the commitment in the function identifier, verified via a merkle membership proof and a bytecode commitment proof. +### Verifying Brillig in Private Functions -Note that, since constructors are handled at the application level, the kernel circuit is not required to check that a constructor has been run before executing code. +Private functions may have unconstrained code, inlined as Brillig bytecode. While unconstrained code, as it name implies, is not constrained within the protocol, a user PXE still needs a mechanism to verify that the code it has been delivered off-chain for a given function is correct. -The public kernel circuit, additionally, needs to check that the corresponding contract instance has been publicly deployed, or prove that it hasn't. This is done via a merkle (non-)membership proof of the public deployment nullifier. +This verification is done via the [contract class `artifact_hash`](./classes.md#structure), which contains a commitment to all bytecode in the contract. The PXE should receive the entire contract artifact, or at least the relevant sections to execute along with the commitments for the others to reconstruct the original `artifact_hash`, and verify that the resulting `artifact_hash` matches the one declared on-chain for the class of the contract being run. ## Discarded Approaches -Earlier versions of the protocol relied on a dedicated contract tree, which required dedicated kernel code to process deployments, which had to be enshrined as new outputs from the application circuits. By abstracting contract deployment and storing deployments as nullifiers, the interface between the application and kernel circuits is simplified, and the kernel circuit has fewer responsibilities. +### Contracts Tree + +Earlier versions of the protocol relied on a dedicated contract tree, which required dedicated kernel code to process deployments, which had to be enshrined as new outputs from the application circuits. By abstracting contract deployment and storing deployments as nullifiers, the interface between the application and kernel circuits is simplified, and the kernel circuit has far fewer responsibilities. Furthermore, multiple contract deployments within a single transaction are now possible. + +### Requiring initialization for Public Deployment + +An earlier version of this draft required contracts to be Initialized in order to be Publicly Deployed. While this was useful for removing the initialization check in public functions, it caused a mix of concerns where the `ContractInstanceDeployer` needed to read a nullifier emitted from another contract. It also coupled the `ContractInstanceDeployer` to the convention decided for Initialization Nullifiers, and forced every contract to have a constructor in order to be publicly deployed even if they didn't need one. Furthermore, it required public constructors to be called via the `ContractInstanceDeployer` only. + +Fully separating Initialization and Public Deployment leads to a cleaner `ContractInstanceDeployer`, and allows more flexibility to applications in handling their own initialization. The main downsides are that this opens the door for a contract to be simultaneously Publicly Deployed and Uninitialized, which is a state that does not seem to map to a valid use case. And it requires public functions to check the Initialization Nullifier on every call, which in the current approach is not needed as the presence of the Deployment Nullifier checked by the Public Kernel is enough of a guarantee that the contract was initialized. + +### Execute Initialization during Public Deployment only + +While it is appealing to allow a user to privately create a new contract instance and not reveal it to the world, we have not yet validated this use case. We could simplify deployment by relying on a single nullifier to track Initialization, and couple it with Public Deployment. Private functions can check initialization via the Deployment Nullifier emitted by the `ContractInstanceDeployer`. + +This approach requires that constructors are only invoked as part of Public Deployment, so constructors would require an additional check for `msg_sender` being the canonical `ContractInstanceDeployer`. Furthermore, to ensure that an instance constructor is properly run, the `ContractInstanceDeployer` would need to know the selector for the instance constructor, which now needs to be part of the Contract Class, re-enshrining it into the protocol. Last, being able to keep agreements (contracts) private among their parties is commonplace in the traditional world, so there is a compelling argument for keeping this requirement. + +Alternatively, we could remove constructor abstraction altogether, and have the Private Kernel Circuit check for the Deployment Nullifier, much like the Public Kernel Circuit does. However, this hurts Diversified and Stealth account contracts, which now require an explicit deployment and cannot be used directly. \ No newline at end of file diff --git a/yellow-paper/docs/public-vm/avm-circuit.md b/yellow-paper/docs/public-vm/avm-circuit.md index c5e91882b67..65234632f13 100644 --- a/yellow-paper/docs/public-vm/avm-circuit.md +++ b/yellow-paper/docs/public-vm/avm-circuit.md @@ -108,7 +108,32 @@ The VM circuit's **Memory Controller** processes loads and stores between interm When decoded, instructions that operate on memory map to some Memory Controller sub-operations. A memory read maps to a `LOAD` sub-operation which loads a word from memory into an intermediate register. The memory offset for this sub-operation is generally specified by an instruction argument. Similarly, a memory write maps to a `STORE` sub-operation which stores a word from an intermediate register to memory. ### User Memory -**TODO** + +This table tracks all memory `Read` or `Write` operations. As introduced in the ["Memory State Model"](./state-model.md), a memory cell is indexed by a 32-bit unsigned integer (`u32`), i.e., the memory capacity is of $2^{32}$ words. Each word is associated with a tag defining its type (`uninitialized`, `u8`, `u16`, `u32`, `u64`, `u128`, `field`). At the beginning of a new call, each memory cell is of type `uninitialized` and has value 0. + +The main property enforcement of this table concerns read/write consistency of every memory cell. This must ensure: + +- Each initial read on a memory cell must have value 0 (`uninitialized` is compatible with any other type). +- Each read on a memory cell must have the same value and the same tag as those set by the last write on this memory cell. + +In addition, this table ensures that the instruction tag corresponding to a memory operation is the same as the memory cell tag. The instruction tag is passed to the memory controller and added to the pertaining row(s) of this table. Note that this is common for an instruction to generate several memory operations and thus several rows in this table. + +The user memory table essentially consists of the following colums: + +- `CALL_PTR`: call pointer uniquely identifying the contract call +- `CLK`: clock value of the memory operation +- `ADDR`: address (type `u32`) pertaining to the memory operation +- `VAL`: value which is read (resp. written) from (resp. to) the memory address +- `TAG`: tag associated to this memory address +- `IN_TAG`: tag of the pertaining instruction +- `RW`: boolean indicating whether memory operation is read or write +- `TAG_ERR`: boolean set to true if there is a mismatch between `TAG` and `IN_TAG` + +To facilitate consistency check, the rows are sorted by `CALL_PTR` then by `ADDR` and then by `CLK` in ascending (arrow of time) order. Any (non-initial) read operation row is constrained to have the same `VAL` and `TAG` than the previous row. A write operation does not need to be constrained. + +The tag consistency check can be performed within every row (order of rows does not matter). + +Note that `CLK` also plays the role of a foreign key to point to the corresponding sub-operation. This is crucial to enforce consistency of copied values between the sub-operations and memory table. ### Calldata **TODO** diff --git a/yellow-paper/docs/public-vm/avm.md b/yellow-paper/docs/public-vm/avm.md index b3417afcb29..ff478785279 100644 --- a/yellow-paper/docs/public-vm/avm.md +++ b/yellow-paper/docs/public-vm/avm.md @@ -70,7 +70,7 @@ ExecutionEnvironment { storageAddress: AztecAddress, origin: AztecAddress, sender: AztecAddress, - portal: AztecAddress, + portal: EthAddress, feePerL1Gas: field, feePerL2Gas: field, feePerDaGas: field, @@ -118,7 +118,7 @@ A context's world state interface is defined as follows: ``` WorldState { contracts: AztecAddress => {bytecode, portalAddress}, // read-only from within AVM - blockHeaders: Vector, // read-only from within AVM + blockHeaders: Vector
, // read-only from within AVM publicStorage: (AztecAddress, field) => value, // read/write l1ToL2Messages: field => message, // read-only from within AVM l2ToL1Messages: Vector<[field; ]>, // append-only (no reads) from within AVM @@ -137,7 +137,7 @@ WorldState { ``` Journal { nestedCalls: Vector<(AztecAddress, boolean)>, - blockHeaderReads: Vector<(field, BlockHeader)>, + blockHeaderReads: Vector<(field, Header)>, publicStorageAccesses: Vector, l1ToL2MessageReads: Vector<(L1toL2MessageContext, [field; ])>, newL2ToL1Messages: Vector<(L2toL1MessageContext, [field; ])>, @@ -249,7 +249,7 @@ results.output = machineState.memory[instr.args.retOffset:instr.args.retOffset+i An exceptional halt is not explicitly triggered by an instruction but instead occurs when an exceptional condition is met. -When an exceptional halt occurs, the context is flagged as consuming all off its allocated gas and is marked as `reverted` with no output data, and then execution within the current context ends. +When an exceptional halt occurs, the context is flagged as consuming all of its allocated gas and is marked as `reverted` with no output data, and then execution within the current context ends. ``` machineState.l1GasLeft = 0 From 6e40c59126be31a956a50daf4b994eab5ebfcacb Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Jos=C3=A9=20Pedro=20Sousa?= Date: Thu, 1 Feb 2024 12:21:52 +0000 Subject: [PATCH 08/16] wip --- .../blank-react/tests/blank.contract.test.ts | 1 - .../explainers/explainer-recursion.md | 23 +++++---- .../how_to/how-to-recursion.md | 21 ++++---- .../ecdsa_sig_verification.mdx | 16 +++---- .../cryptographic_primitives/hashes.mdx | 8 ++-- .../cryptographic_primitives/scalar.mdx | 4 +- .../cryptographic_primitives/schnorr.mdx | 8 ++-- .../noir/standard_library/recursion.md | 48 +++++-------------- .../explainers/explainer-recursion.md | 23 +++++---- .../processed-docs/how_to/how-to-recursion.md | 21 ++++---- .../ecdsa_sig_verification.mdx | 16 +++---- .../cryptographic_primitives/hashes.mdx | 8 ++-- .../cryptographic_primitives/scalar.mdx | 4 +- .../cryptographic_primitives/schnorr.mdx | 8 ++-- .../noir/standard_library/recursion.md | 48 +++++-------------- 15 files changed, 100 insertions(+), 157 deletions(-) diff --git a/boxes/blank-react/tests/blank.contract.test.ts b/boxes/blank-react/tests/blank.contract.test.ts index ce5f6be158f..c70087824cc 100644 --- a/boxes/blank-react/tests/blank.contract.test.ts +++ b/boxes/blank-react/tests/blank.contract.test.ts @@ -4,7 +4,6 @@ import { AccountWallet, AztecAddress, CompleteAddress, - Contract, DeployMethod, Fr, TxStatus, diff --git a/noir/docs/processed-docs-cache/explainers/explainer-recursion.md b/noir/docs/processed-docs-cache/explainers/explainer-recursion.md index 8f992ec29fd..18846176ca7 100644 --- a/noir/docs/processed-docs-cache/explainers/explainer-recursion.md +++ b/noir/docs/processed-docs-cache/explainers/explainer-recursion.md @@ -16,12 +16,13 @@ keywords: "Optimizing Computational Resources", "Improving Efficiency", "Verification Key", - "Aggregation Objects", + "Aggregation", "Recursive zkSNARK schemes", "PLONK", "Proving and Verification Keys" ] sidebar_position: 1 +pagination_next: how_to/how-to-recursion --- In programming, we tend to think of recursion as something calling itself. A classic example would be the calculation of the factorial of a number: @@ -64,7 +65,7 @@ So, they use zero-knowledge proofs. Alice tries to guess Bob's number, and Bob w This ZK proof can go on a smart contract, revealing the winner and even giving prizes. However, this means every turn needs to be verified on-chain. This incurs some cost and waiting time that may simply make the game too expensive or time-consuming to be worth it. -As a solution, Alice proposes the following: "what if Bob generates his proof, and instead of sending it on-chain, I verify it *within* my own proof before playing my own turn?". +As a solution, Alice proposes the following: "what if Bob generates his proof, and instead of sending it on-chain, I verify it *within* my own proof before playing my own turn?". She can then generate a proof that she verified his proof, and so on. @@ -116,25 +117,19 @@ As you can see in the [recursion reference](noir/standard_library/recursion.md), - The Verification Key of the circuit that generated the proof - A hash of this verification key, as it's needed for some backends - The public inputs for the proof -- The input aggregation object -It also returns the `output aggregation object`. These aggregation objects can be confusing at times, so let's dive in a little bit. - -### Aggregation objects +:::info Recursive zkSNARK schemes do not necessarily "verify a proof" in the sense that you expect a true or false to be spit out by the verifier. Rather an aggregation object is built over the public inputs. -In the case of PLONK the recursive aggregation object is two G1 points (expressed as 16 witness values). The final verifier (in our case this is most often the smart contract verifier) has to be aware of this aggregation object to execute a pairing and check the validity of these points. - So, taking the example of Alice and Bob and their guessing game: - Alice makes her guess. Her proof is *not* recursive: it doesn't verify any proof within it! It's just a standard `assert(x != y)` circuit -- Bob verifies Alice's proof and makes his own guess. In this circuit, he is verifying a proof, so it needs to output an `aggregation object`: he is generating a recursive proof! -- Alice verifies Bob's *recursive proof*, and uses Bob's `output aggregation object` as the `input aggregation object` in her proof... Which in turn, generates another `output aggregation object`. +- Bob verifies Alice's proof and makes his own guess. In this circuit, he doesn't exactly *prove* the verification of Alice's proof. Instead, he *aggregates* his proof to Alice's proof. The actual verification is done when the full proof is verified, for example when using `nargo verify` or through the verifier smart contract. -One should notice that when Bob generates his first proof, he has no input aggregation object. Because he is not verifying an recursive proof, he has no `input aggregation object`. In this case, he may use zeros instead. +We can imagine recursive proofs a [relay race](https://en.wikipedia.org/wiki/Relay_race). The first runner doesn't have to receive the baton from anyone else, as he/she already starts with it. But when his/her turn is over, the next runner needs to receive it, run a bit more, and pass it along. Even though every runner could theoretically verify the baton mid-run (why not? 🏃🔍), only at the end of the race does the referee verify that the whole race is valid. -We can imagine the `aggregation object` as the baton in a [relay race](https://en.wikipedia.org/wiki/Relay_race). The first runner doesn't have to receive the baton from anyone else, as he/she already starts with it. But when his/her turn is over, the next runner needs to receive it, run a bit more, and pass it along. Even though every runner could theoretically verify the baton mid-run (why not? 🏃🔍), only at the end of the race does the referee verify that the whole race is valid. +::: ## Some architecture @@ -175,3 +170,7 @@ In this example, a regulator could verify that taxes were paid for a specific pu At the time of writing, verifying recursive proofs is surprisingly fast. This is because most of the time is spent on generating the verification key that will be used to generate the next proof. So you are able to cache the verification key and reuse it later. Currently, Noir JS packages don't expose the functionality of loading proving and verification keys, but that feature exists in the underlying `bb.js` package. + +## How can I try it + +Learn more about using recursion in Nargo and NoirJS in the [how-to guide](../how_to/how-to-recursion.md) and see a full example in [noir-examples](https://github.com/noir-lang/noir-examples). diff --git a/noir/docs/processed-docs-cache/how_to/how-to-recursion.md b/noir/docs/processed-docs-cache/how_to/how-to-recursion.md index 39db23f1f3a..f34647a99d5 100644 --- a/noir/docs/processed-docs-cache/how_to/how-to-recursion.md +++ b/noir/docs/processed-docs-cache/how_to/how-to-recursion.md @@ -108,11 +108,7 @@ This call takes the public inputs and the proof, but also the public inputs coun :::info -The `proofAsFields` has a constant size `[Field; 93]`. However, currently the backend doesn't remove the public inputs from the proof when converting it. - -This means that if your `main` circuit has two public inputs, then you should also modify the recursive circuit to accept a proof with the public inputs appended. This means that in our example, since `y` is a public input, our `proofAsFields` is of type `[Field; 94]`. - -Verification keys in Barretenberg are always of size 114. +The `proofAsFields` has a constant size `[Field; 93]` and verification keys in Barretenberg are always `[Field; 114]`. ::: @@ -136,7 +132,6 @@ const recursiveInputs = { proof: proofAsFields, // array of length 93 + size of public inputs publicInputs: [mainInput.y], // using the example above, where `y` is the only public input key_hash: vkHash, - input_aggregation_object: Array(16).fill(0) // this circuit is verifying a non-recursive proof, so there's no input aggregation object: just use zero } const { witness, returnValue } = noir.execute(recursiveInputs) // we're executing the recursive circuit now! @@ -144,7 +139,7 @@ const { proof, publicInputs } = backend.generateFinalProof(witness) const verified = backend.verifyFinalProof({ proof, publicInputs }) ``` -You can obviously chain this proof into another proof. In fact, if you're using recursive proofs, you're probably interested of using them this way! In that case, you should keep in mind the `returnValue`, as it will contain the `input_aggregation_object` for the next proof. +You can obviously chain this proof into another proof. In fact, if you're using recursive proofs, you're probably interested of using them this way! :::tip @@ -152,16 +147,16 @@ Managing circuits and "who does what" can be confusing. To make sure your naming ```js const circuits = { -main: mainJSON, -recursive: recursiveJSON + main: mainJSON, + recursive: recursiveJSON } const backends = { -main: new BarretenbergBackend(circuits.main), -recursive: new BarretenbergBackend(circuits.recursive) + main: new BarretenbergBackend(circuits.main), + recursive: new BarretenbergBackend(circuits.recursive) } const noir_programs = { -main: new Noir(circuits.main, backends.main), -recursive: new Noir(circuits.recursive, backends.recursive) + main: new Noir(circuits.main, backends.main), + recursive: new Noir(circuits.recursive, backends.recursive) } ``` diff --git a/noir/docs/processed-docs-cache/noir/standard_library/cryptographic_primitives/ecdsa_sig_verification.mdx b/noir/docs/processed-docs-cache/noir/standard_library/cryptographic_primitives/ecdsa_sig_verification.mdx index 68688f5c96d..4bf09cef178 100644 --- a/noir/docs/processed-docs-cache/noir/standard_library/cryptographic_primitives/ecdsa_sig_verification.mdx +++ b/noir/docs/processed-docs-cache/noir/standard_library/cryptographic_primitives/ecdsa_sig_verification.mdx @@ -15,10 +15,10 @@ Verifier for ECDSA Secp256k1 signatures ```rust title="ecdsa_secp256k1" showLineNumbers pub fn verify_signature( - _public_key_x: [u8; 32], - _public_key_y: [u8; 32], - _signature: [u8; 64], - _message_hash: [u8; N] + public_key_x: [u8; 32], + public_key_y: [u8; 32], + signature: [u8; 64], + message_hash: [u8; N] ) -> bool ``` > Source code: noir_stdlib/src/ecdsa_secp256k1.nr#L2-L9 @@ -39,10 +39,10 @@ Verifier for ECDSA Secp256r1 signatures ```rust title="ecdsa_secp256r1" showLineNumbers pub fn verify_signature( - _public_key_x: [u8; 32], - _public_key_y: [u8; 32], - _signature: [u8; 64], - _message_hash: [u8; N] + public_key_x: [u8; 32], + public_key_y: [u8; 32], + signature: [u8; 64], + message_hash: [u8; N] ) -> bool ``` > Source code: noir_stdlib/src/ecdsa_secp256r1.nr#L2-L9 diff --git a/noir/docs/processed-docs-cache/noir/standard_library/cryptographic_primitives/hashes.mdx b/noir/docs/processed-docs-cache/noir/standard_library/cryptographic_primitives/hashes.mdx index c70386f8de3..730b6d4117f 100644 --- a/noir/docs/processed-docs-cache/noir/standard_library/cryptographic_primitives/hashes.mdx +++ b/noir/docs/processed-docs-cache/noir/standard_library/cryptographic_primitives/hashes.mdx @@ -15,7 +15,7 @@ import BlackBoxInfo from '@site/src/components/Notes/_blackbox.mdx'; Given an array of bytes, returns the resulting sha256 hash. ```rust title="sha256" showLineNumbers -pub fn sha256(_input: [u8; N]) -> [u8; 32] +pub fn sha256(input: [u8; N]) -> [u8; 32] ``` > Source code: noir_stdlib/src/hash.nr#L5-L7 @@ -36,7 +36,7 @@ fn main() { Given an array of bytes, returns an array with the Blake2 hash ```rust title="blake2s" showLineNumbers -pub fn blake2s(_input: [u8; N]) -> [u8; 32] +pub fn blake2s(input: [u8; N]) -> [u8; 32] ``` > Source code: noir_stdlib/src/hash.nr#L11-L13 @@ -57,7 +57,7 @@ fn main() { Given an array of bytes, returns an array with the Blake3 hash ```rust title="blake3" showLineNumbers -pub fn blake3(_input: [u8; N]) -> [u8; 32] +pub fn blake3(input: [u8; N]) -> [u8; 32] ``` > Source code: noir_stdlib/src/hash.nr#L17-L19 @@ -137,7 +137,7 @@ Given an array of bytes (`u8`), returns the resulting keccak hash as an array of of the input. ```rust title="keccak256" showLineNumbers -pub fn keccak256(_input: [u8; N], _message_size: u32) -> [u8; 32] +pub fn keccak256(input: [u8; N], message_size: u32) -> [u8; 32] ``` > Source code: noir_stdlib/src/hash.nr#L67-L69 diff --git a/noir/docs/processed-docs-cache/noir/standard_library/cryptographic_primitives/scalar.mdx b/noir/docs/processed-docs-cache/noir/standard_library/cryptographic_primitives/scalar.mdx index 9f9a6419140..df411ca5443 100644 --- a/noir/docs/processed-docs-cache/noir/standard_library/cryptographic_primitives/scalar.mdx +++ b/noir/docs/processed-docs-cache/noir/standard_library/cryptographic_primitives/scalar.mdx @@ -14,8 +14,8 @@ configured noir field. For the BN254 scalar field, this is BabyJubJub or Grumpki ```rust title="fixed_base_embedded_curve" showLineNumbers pub fn fixed_base_embedded_curve( - _low: Field, - _high: Field + low: Field, + high: Field ) -> [Field; 2] ``` > Source code: noir_stdlib/src/scalar_mul.nr#L27-L32 diff --git a/noir/docs/processed-docs-cache/noir/standard_library/cryptographic_primitives/schnorr.mdx b/noir/docs/processed-docs-cache/noir/standard_library/cryptographic_primitives/schnorr.mdx index 4890507733d..ae12e6c12dc 100644 --- a/noir/docs/processed-docs-cache/noir/standard_library/cryptographic_primitives/schnorr.mdx +++ b/noir/docs/processed-docs-cache/noir/standard_library/cryptographic_primitives/schnorr.mdx @@ -13,10 +13,10 @@ Verifier for Schnorr signatures over the embedded curve (for BN254 it is Grumpki ```rust title="schnorr_verify" showLineNumbers pub fn verify_signature( - _public_key_x: Field, - _public_key_y: Field, - _signature: [u8; 64], - _message: [u8; N] + public_key_x: Field, + public_key_y: Field, + signature: [u8; 64], + message: [u8; N] ) -> bool ``` > Source code: noir_stdlib/src/schnorr.nr#L2-L9 diff --git a/noir/docs/processed-docs-cache/noir/standard_library/recursion.md b/noir/docs/processed-docs-cache/noir/standard_library/recursion.md index 67962082a8f..f252150c8b5 100644 --- a/noir/docs/processed-docs-cache/noir/standard_library/recursion.md +++ b/noir/docs/processed-docs-cache/noir/standard_library/recursion.md @@ -1,16 +1,16 @@ --- title: Recursive Proofs description: Learn about how to write recursive proofs in Noir. -keywords: [recursion, recursive proofs, verification_key, aggregation object, verify_proof] +keywords: [recursion, recursive proofs, verification_key, verify_proof] --- Noir supports recursively verifying proofs, meaning you verify the proof of a Noir program in another Noir program. This enables creating proofs of arbitrary size by doing step-wise verification of smaller components of a large proof. -The `verify_proof` function takes a verification key, proof and public inputs for a zk program, as well as a key hash and an input aggregation object. The key hash is used to check the validity of the verification key and the input aggregation object is required by some proving systems. The `verify_proof` function returns an output aggregation object that can then be fed into future iterations of the proof verification if required. +Read [the explainer on recursion](../../explainers/explainer-recursion.md) to know more about this function and the [guide on how to use it.](../../how_to/how-to-recursion.md) ```rust #[foreign(verify_proof)] -fn verify_proof(_verification_key : [Field], _proof : [Field], _public_input : Field, _key_hash : Field, _input_aggregation_object : [Field]) -> [Field] {} +fn verify_proof(_verification_key : [Field], _proof : [Field], _public_input : Field, _key_hash : Field) {} ``` :::info @@ -26,36 +26,29 @@ use dep::std; fn main( verification_key : [Field; 114], - proof : [Field; 94], + proof : [Field; 93], public_inputs : [Field; 1], key_hash : Field, - input_aggregation_object : [Field; 16], - proof_b : [Field; 94], -) -> pub [Field; 16] { - let output_aggregation_object_a = std::verify_proof( + proof_b : [Field; 93], +) { + std::verify_proof( verification_key.as_slice(), proof.as_slice(), public_inputs.as_slice(), - key_hash, - input_aggregation_object + key_hash ); - let output_aggregation_object = std::verify_proof( + std::verify_proof( verification_key.as_slice(), proof_b.as_slice(), public_inputs.as_slice(), - key_hash, - output_aggregation_object_a + key_hash ); - - let mut output = [0; 16]; - for i in 0..16 { - output[i] = output_aggregation_object[i]; - } - output } ``` +You can see a full example of recursive proofs in [this example recursion demo repo](https://github.com/noir-lang/noir-examples/tree/master/recursion). + ## Parameters ### `verification_key` @@ -68,23 +61,8 @@ The proof for the zk program that is being verified. ### `public_inputs` -These represent the public inputs of the proof we are verifying. They should be checked against in the circuit after construction of a new aggregation state. +These represent the public inputs of the proof we are verifying. ### `key_hash` A key hash is used to check the validity of the verification key. The circuit implementing this opcode can use this hash to ensure that the key provided to the circuit matches the key produced by the circuit creator. - -### `input_aggregation_object` - -An aggregation object is blob of data that the top-level verifier must run some proof system specific algorithm on to complete verification. The size is proof system specific and will be set by the backend integrating this opcode. The input aggregation object is only not `None` when we are verifying a previous recursive aggregation in the current circuit. If this is the first recursive aggregation there is no input aggregation object. It is left to the backend to determine how to handle when there is no input aggregation object. - -## Return value - -### `output_aggregation_object` - -This is the result of a recursive aggregation and is what will be fed into the next verifier. -The next verifier can either perform a final verification (returning true or false) or perform another recursive aggregation where this output aggregation object will be the input aggregation object of the next recursive aggregation. - -## Example - -You can see an example of how to do recursive proofs in [this example recursion demo repo](https://github.com/noir-lang/noir-examples/tree/master/recursion). diff --git a/noir/docs/processed-docs/explainers/explainer-recursion.md b/noir/docs/processed-docs/explainers/explainer-recursion.md index 8f992ec29fd..18846176ca7 100644 --- a/noir/docs/processed-docs/explainers/explainer-recursion.md +++ b/noir/docs/processed-docs/explainers/explainer-recursion.md @@ -16,12 +16,13 @@ keywords: "Optimizing Computational Resources", "Improving Efficiency", "Verification Key", - "Aggregation Objects", + "Aggregation", "Recursive zkSNARK schemes", "PLONK", "Proving and Verification Keys" ] sidebar_position: 1 +pagination_next: how_to/how-to-recursion --- In programming, we tend to think of recursion as something calling itself. A classic example would be the calculation of the factorial of a number: @@ -64,7 +65,7 @@ So, they use zero-knowledge proofs. Alice tries to guess Bob's number, and Bob w This ZK proof can go on a smart contract, revealing the winner and even giving prizes. However, this means every turn needs to be verified on-chain. This incurs some cost and waiting time that may simply make the game too expensive or time-consuming to be worth it. -As a solution, Alice proposes the following: "what if Bob generates his proof, and instead of sending it on-chain, I verify it *within* my own proof before playing my own turn?". +As a solution, Alice proposes the following: "what if Bob generates his proof, and instead of sending it on-chain, I verify it *within* my own proof before playing my own turn?". She can then generate a proof that she verified his proof, and so on. @@ -116,25 +117,19 @@ As you can see in the [recursion reference](noir/standard_library/recursion.md), - The Verification Key of the circuit that generated the proof - A hash of this verification key, as it's needed for some backends - The public inputs for the proof -- The input aggregation object -It also returns the `output aggregation object`. These aggregation objects can be confusing at times, so let's dive in a little bit. - -### Aggregation objects +:::info Recursive zkSNARK schemes do not necessarily "verify a proof" in the sense that you expect a true or false to be spit out by the verifier. Rather an aggregation object is built over the public inputs. -In the case of PLONK the recursive aggregation object is two G1 points (expressed as 16 witness values). The final verifier (in our case this is most often the smart contract verifier) has to be aware of this aggregation object to execute a pairing and check the validity of these points. - So, taking the example of Alice and Bob and their guessing game: - Alice makes her guess. Her proof is *not* recursive: it doesn't verify any proof within it! It's just a standard `assert(x != y)` circuit -- Bob verifies Alice's proof and makes his own guess. In this circuit, he is verifying a proof, so it needs to output an `aggregation object`: he is generating a recursive proof! -- Alice verifies Bob's *recursive proof*, and uses Bob's `output aggregation object` as the `input aggregation object` in her proof... Which in turn, generates another `output aggregation object`. +- Bob verifies Alice's proof and makes his own guess. In this circuit, he doesn't exactly *prove* the verification of Alice's proof. Instead, he *aggregates* his proof to Alice's proof. The actual verification is done when the full proof is verified, for example when using `nargo verify` or through the verifier smart contract. -One should notice that when Bob generates his first proof, he has no input aggregation object. Because he is not verifying an recursive proof, he has no `input aggregation object`. In this case, he may use zeros instead. +We can imagine recursive proofs a [relay race](https://en.wikipedia.org/wiki/Relay_race). The first runner doesn't have to receive the baton from anyone else, as he/she already starts with it. But when his/her turn is over, the next runner needs to receive it, run a bit more, and pass it along. Even though every runner could theoretically verify the baton mid-run (why not? 🏃🔍), only at the end of the race does the referee verify that the whole race is valid. -We can imagine the `aggregation object` as the baton in a [relay race](https://en.wikipedia.org/wiki/Relay_race). The first runner doesn't have to receive the baton from anyone else, as he/she already starts with it. But when his/her turn is over, the next runner needs to receive it, run a bit more, and pass it along. Even though every runner could theoretically verify the baton mid-run (why not? 🏃🔍), only at the end of the race does the referee verify that the whole race is valid. +::: ## Some architecture @@ -175,3 +170,7 @@ In this example, a regulator could verify that taxes were paid for a specific pu At the time of writing, verifying recursive proofs is surprisingly fast. This is because most of the time is spent on generating the verification key that will be used to generate the next proof. So you are able to cache the verification key and reuse it later. Currently, Noir JS packages don't expose the functionality of loading proving and verification keys, but that feature exists in the underlying `bb.js` package. + +## How can I try it + +Learn more about using recursion in Nargo and NoirJS in the [how-to guide](../how_to/how-to-recursion.md) and see a full example in [noir-examples](https://github.com/noir-lang/noir-examples). diff --git a/noir/docs/processed-docs/how_to/how-to-recursion.md b/noir/docs/processed-docs/how_to/how-to-recursion.md index 39db23f1f3a..f34647a99d5 100644 --- a/noir/docs/processed-docs/how_to/how-to-recursion.md +++ b/noir/docs/processed-docs/how_to/how-to-recursion.md @@ -108,11 +108,7 @@ This call takes the public inputs and the proof, but also the public inputs coun :::info -The `proofAsFields` has a constant size `[Field; 93]`. However, currently the backend doesn't remove the public inputs from the proof when converting it. - -This means that if your `main` circuit has two public inputs, then you should also modify the recursive circuit to accept a proof with the public inputs appended. This means that in our example, since `y` is a public input, our `proofAsFields` is of type `[Field; 94]`. - -Verification keys in Barretenberg are always of size 114. +The `proofAsFields` has a constant size `[Field; 93]` and verification keys in Barretenberg are always `[Field; 114]`. ::: @@ -136,7 +132,6 @@ const recursiveInputs = { proof: proofAsFields, // array of length 93 + size of public inputs publicInputs: [mainInput.y], // using the example above, where `y` is the only public input key_hash: vkHash, - input_aggregation_object: Array(16).fill(0) // this circuit is verifying a non-recursive proof, so there's no input aggregation object: just use zero } const { witness, returnValue } = noir.execute(recursiveInputs) // we're executing the recursive circuit now! @@ -144,7 +139,7 @@ const { proof, publicInputs } = backend.generateFinalProof(witness) const verified = backend.verifyFinalProof({ proof, publicInputs }) ``` -You can obviously chain this proof into another proof. In fact, if you're using recursive proofs, you're probably interested of using them this way! In that case, you should keep in mind the `returnValue`, as it will contain the `input_aggregation_object` for the next proof. +You can obviously chain this proof into another proof. In fact, if you're using recursive proofs, you're probably interested of using them this way! :::tip @@ -152,16 +147,16 @@ Managing circuits and "who does what" can be confusing. To make sure your naming ```js const circuits = { -main: mainJSON, -recursive: recursiveJSON + main: mainJSON, + recursive: recursiveJSON } const backends = { -main: new BarretenbergBackend(circuits.main), -recursive: new BarretenbergBackend(circuits.recursive) + main: new BarretenbergBackend(circuits.main), + recursive: new BarretenbergBackend(circuits.recursive) } const noir_programs = { -main: new Noir(circuits.main, backends.main), -recursive: new Noir(circuits.recursive, backends.recursive) + main: new Noir(circuits.main, backends.main), + recursive: new Noir(circuits.recursive, backends.recursive) } ``` diff --git a/noir/docs/processed-docs/noir/standard_library/cryptographic_primitives/ecdsa_sig_verification.mdx b/noir/docs/processed-docs/noir/standard_library/cryptographic_primitives/ecdsa_sig_verification.mdx index 68688f5c96d..4bf09cef178 100644 --- a/noir/docs/processed-docs/noir/standard_library/cryptographic_primitives/ecdsa_sig_verification.mdx +++ b/noir/docs/processed-docs/noir/standard_library/cryptographic_primitives/ecdsa_sig_verification.mdx @@ -15,10 +15,10 @@ Verifier for ECDSA Secp256k1 signatures ```rust title="ecdsa_secp256k1" showLineNumbers pub fn verify_signature( - _public_key_x: [u8; 32], - _public_key_y: [u8; 32], - _signature: [u8; 64], - _message_hash: [u8; N] + public_key_x: [u8; 32], + public_key_y: [u8; 32], + signature: [u8; 64], + message_hash: [u8; N] ) -> bool ``` > Source code: noir_stdlib/src/ecdsa_secp256k1.nr#L2-L9 @@ -39,10 +39,10 @@ Verifier for ECDSA Secp256r1 signatures ```rust title="ecdsa_secp256r1" showLineNumbers pub fn verify_signature( - _public_key_x: [u8; 32], - _public_key_y: [u8; 32], - _signature: [u8; 64], - _message_hash: [u8; N] + public_key_x: [u8; 32], + public_key_y: [u8; 32], + signature: [u8; 64], + message_hash: [u8; N] ) -> bool ``` > Source code: noir_stdlib/src/ecdsa_secp256r1.nr#L2-L9 diff --git a/noir/docs/processed-docs/noir/standard_library/cryptographic_primitives/hashes.mdx b/noir/docs/processed-docs/noir/standard_library/cryptographic_primitives/hashes.mdx index c70386f8de3..730b6d4117f 100644 --- a/noir/docs/processed-docs/noir/standard_library/cryptographic_primitives/hashes.mdx +++ b/noir/docs/processed-docs/noir/standard_library/cryptographic_primitives/hashes.mdx @@ -15,7 +15,7 @@ import BlackBoxInfo from '@site/src/components/Notes/_blackbox.mdx'; Given an array of bytes, returns the resulting sha256 hash. ```rust title="sha256" showLineNumbers -pub fn sha256(_input: [u8; N]) -> [u8; 32] +pub fn sha256(input: [u8; N]) -> [u8; 32] ``` > Source code: noir_stdlib/src/hash.nr#L5-L7 @@ -36,7 +36,7 @@ fn main() { Given an array of bytes, returns an array with the Blake2 hash ```rust title="blake2s" showLineNumbers -pub fn blake2s(_input: [u8; N]) -> [u8; 32] +pub fn blake2s(input: [u8; N]) -> [u8; 32] ``` > Source code: noir_stdlib/src/hash.nr#L11-L13 @@ -57,7 +57,7 @@ fn main() { Given an array of bytes, returns an array with the Blake3 hash ```rust title="blake3" showLineNumbers -pub fn blake3(_input: [u8; N]) -> [u8; 32] +pub fn blake3(input: [u8; N]) -> [u8; 32] ``` > Source code: noir_stdlib/src/hash.nr#L17-L19 @@ -137,7 +137,7 @@ Given an array of bytes (`u8`), returns the resulting keccak hash as an array of of the input. ```rust title="keccak256" showLineNumbers -pub fn keccak256(_input: [u8; N], _message_size: u32) -> [u8; 32] +pub fn keccak256(input: [u8; N], message_size: u32) -> [u8; 32] ``` > Source code: noir_stdlib/src/hash.nr#L67-L69 diff --git a/noir/docs/processed-docs/noir/standard_library/cryptographic_primitives/scalar.mdx b/noir/docs/processed-docs/noir/standard_library/cryptographic_primitives/scalar.mdx index 9f9a6419140..df411ca5443 100644 --- a/noir/docs/processed-docs/noir/standard_library/cryptographic_primitives/scalar.mdx +++ b/noir/docs/processed-docs/noir/standard_library/cryptographic_primitives/scalar.mdx @@ -14,8 +14,8 @@ configured noir field. For the BN254 scalar field, this is BabyJubJub or Grumpki ```rust title="fixed_base_embedded_curve" showLineNumbers pub fn fixed_base_embedded_curve( - _low: Field, - _high: Field + low: Field, + high: Field ) -> [Field; 2] ``` > Source code: noir_stdlib/src/scalar_mul.nr#L27-L32 diff --git a/noir/docs/processed-docs/noir/standard_library/cryptographic_primitives/schnorr.mdx b/noir/docs/processed-docs/noir/standard_library/cryptographic_primitives/schnorr.mdx index 4890507733d..ae12e6c12dc 100644 --- a/noir/docs/processed-docs/noir/standard_library/cryptographic_primitives/schnorr.mdx +++ b/noir/docs/processed-docs/noir/standard_library/cryptographic_primitives/schnorr.mdx @@ -13,10 +13,10 @@ Verifier for Schnorr signatures over the embedded curve (for BN254 it is Grumpki ```rust title="schnorr_verify" showLineNumbers pub fn verify_signature( - _public_key_x: Field, - _public_key_y: Field, - _signature: [u8; 64], - _message: [u8; N] + public_key_x: Field, + public_key_y: Field, + signature: [u8; 64], + message: [u8; N] ) -> bool ``` > Source code: noir_stdlib/src/schnorr.nr#L2-L9 diff --git a/noir/docs/processed-docs/noir/standard_library/recursion.md b/noir/docs/processed-docs/noir/standard_library/recursion.md index 67962082a8f..f252150c8b5 100644 --- a/noir/docs/processed-docs/noir/standard_library/recursion.md +++ b/noir/docs/processed-docs/noir/standard_library/recursion.md @@ -1,16 +1,16 @@ --- title: Recursive Proofs description: Learn about how to write recursive proofs in Noir. -keywords: [recursion, recursive proofs, verification_key, aggregation object, verify_proof] +keywords: [recursion, recursive proofs, verification_key, verify_proof] --- Noir supports recursively verifying proofs, meaning you verify the proof of a Noir program in another Noir program. This enables creating proofs of arbitrary size by doing step-wise verification of smaller components of a large proof. -The `verify_proof` function takes a verification key, proof and public inputs for a zk program, as well as a key hash and an input aggregation object. The key hash is used to check the validity of the verification key and the input aggregation object is required by some proving systems. The `verify_proof` function returns an output aggregation object that can then be fed into future iterations of the proof verification if required. +Read [the explainer on recursion](../../explainers/explainer-recursion.md) to know more about this function and the [guide on how to use it.](../../how_to/how-to-recursion.md) ```rust #[foreign(verify_proof)] -fn verify_proof(_verification_key : [Field], _proof : [Field], _public_input : Field, _key_hash : Field, _input_aggregation_object : [Field]) -> [Field] {} +fn verify_proof(_verification_key : [Field], _proof : [Field], _public_input : Field, _key_hash : Field) {} ``` :::info @@ -26,36 +26,29 @@ use dep::std; fn main( verification_key : [Field; 114], - proof : [Field; 94], + proof : [Field; 93], public_inputs : [Field; 1], key_hash : Field, - input_aggregation_object : [Field; 16], - proof_b : [Field; 94], -) -> pub [Field; 16] { - let output_aggregation_object_a = std::verify_proof( + proof_b : [Field; 93], +) { + std::verify_proof( verification_key.as_slice(), proof.as_slice(), public_inputs.as_slice(), - key_hash, - input_aggregation_object + key_hash ); - let output_aggregation_object = std::verify_proof( + std::verify_proof( verification_key.as_slice(), proof_b.as_slice(), public_inputs.as_slice(), - key_hash, - output_aggregation_object_a + key_hash ); - - let mut output = [0; 16]; - for i in 0..16 { - output[i] = output_aggregation_object[i]; - } - output } ``` +You can see a full example of recursive proofs in [this example recursion demo repo](https://github.com/noir-lang/noir-examples/tree/master/recursion). + ## Parameters ### `verification_key` @@ -68,23 +61,8 @@ The proof for the zk program that is being verified. ### `public_inputs` -These represent the public inputs of the proof we are verifying. They should be checked against in the circuit after construction of a new aggregation state. +These represent the public inputs of the proof we are verifying. ### `key_hash` A key hash is used to check the validity of the verification key. The circuit implementing this opcode can use this hash to ensure that the key provided to the circuit matches the key produced by the circuit creator. - -### `input_aggregation_object` - -An aggregation object is blob of data that the top-level verifier must run some proof system specific algorithm on to complete verification. The size is proof system specific and will be set by the backend integrating this opcode. The input aggregation object is only not `None` when we are verifying a previous recursive aggregation in the current circuit. If this is the first recursive aggregation there is no input aggregation object. It is left to the backend to determine how to handle when there is no input aggregation object. - -## Return value - -### `output_aggregation_object` - -This is the result of a recursive aggregation and is what will be fed into the next verifier. -The next verifier can either perform a final verification (returning true or false) or perform another recursive aggregation where this output aggregation object will be the input aggregation object of the next recursive aggregation. - -## Example - -You can see an example of how to do recursive proofs in [this example recursion demo repo](https://github.com/noir-lang/noir-examples/tree/master/recursion). From 82de26d049bf7638942cbc045ec487160da0560f Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Jos=C3=A9=20Pedro=20Sousa?= Date: Thu, 1 Feb 2024 12:29:42 +0000 Subject: [PATCH 09/16] wip --- barretenberg/.gitrepo | 4 +- barretenberg/cpp/.clangd | 78 +++++++ .../ecc/fields/field_conversion.hpp | 2 +- .../ecc/fields/field_conversion.test.cpp | 11 +- noir/.gitrepo | 4 +- noir/acvm-repo/acir/acir_docs.md | 206 ++++++++++++++++++ .../compiler/optimizers/redundant_range.rs | 72 +++--- noir/aztec_macros/src/lib.rs | 86 ++------ .../src/ssa/acir_gen/acir_ir/acir_variable.rs | 56 ++--- .../src/hir/def_collector/dc_crate.rs | 14 +- .../src/hir/resolution/errors.rs | 7 - .../src/hir/resolution/resolver.rs | 17 +- .../noirc_frontend/src/lexer/token.rs | 4 - noir/compiler/noirc_frontend/src/lib.rs | 6 +- noir/compiler/noirc_frontend/src/tests.rs | 2 - .../docs/explainers/explainer-recursion.md | 23 +- noir/docs/docs/how_to/how-to-recursion.md | 21 +- .../docs/noir/standard_library/recursion.md | 48 ++-- .../explainers/explainer-recursion.md | 23 +- .../how_to/how-to-recursion.md | 21 +- .../noir/standard_library/recursion.md | 48 ++-- noir/noir_stdlib/src/array.nr | 4 +- noir/noir_stdlib/src/bigint.nr | 12 +- noir/noir_stdlib/src/ecdsa_secp256k1.nr | 10 +- noir/noir_stdlib/src/ecdsa_secp256r1.nr | 10 +- noir/noir_stdlib/src/field.nr | 10 +- noir/noir_stdlib/src/hash.nr | 16 +- noir/noir_stdlib/src/lib.nr | 10 +- noir/noir_stdlib/src/scalar_mul.nr | 4 +- noir/noir_stdlib/src/schnorr.nr | 10 +- noir/noir_stdlib/src/slice.nr | 12 +- noir/noir_stdlib/src/string.nr | 2 +- noir/noir_stdlib/src/test.nr | 10 +- noir/scripts/bootstrap_native.sh | 6 - yarn-project/pxe/src/contract_tree/index.ts | 183 ---------------- yarn.lock | 4 - 36 files changed, 558 insertions(+), 498 deletions(-) create mode 100644 barretenberg/cpp/.clangd create mode 100644 noir/acvm-repo/acir/acir_docs.md delete mode 100644 yarn-project/pxe/src/contract_tree/index.ts delete mode 100644 yarn.lock diff --git a/barretenberg/.gitrepo b/barretenberg/.gitrepo index ca8c2516afd..dee34a9f1dd 100644 --- a/barretenberg/.gitrepo +++ b/barretenberg/.gitrepo @@ -6,7 +6,7 @@ [subrepo] remote = https://github.com/AztecProtocol/barretenberg branch = master - commit = d041c3e8e59132328e0171fb72d61ea50936ee48 - parent = 09d0730bad4be2f4954cbb6d27538f7860d0f21f + commit = 095410c56b5a2275756f5a123685bce85d3ae779 + parent = cdf1baf017c4833bc621ba4dd3681dd1a745e259 method = merge cmdver = 0.4.6 diff --git a/barretenberg/cpp/.clangd b/barretenberg/cpp/.clangd new file mode 100644 index 00000000000..bb22a6eed3e --- /dev/null +++ b/barretenberg/cpp/.clangd @@ -0,0 +1,78 @@ +CompileFlags: # Tweak the parse settings + Remove: -fconstexpr-ops-limit=* +--- +# Applies all barretenberg source files +If: + PathMatch: [src/.*\.hpp, src/.*\.cpp, src/.*\.tcc] +Diagnostics: + # Value Strict checks whether we are including unused header files + # Note that some headers may be _implicitly_ used and still + # need to be included. This is very noisy, and is probably best used + # by occasionally toggling it on. + UnusedIncludes: None + + # Static analysis configuration + ClangTidy: + Add: + - cert-* + - google-* + - cppcoreguidelines-* + - readability-* + - modernize-* + - bugprone-* + - misc-* + - performance-* + Remove: + # Useful but check is buggy in clang-tidy 15.0.6 + - misc-const-correctness + # Huge diff; obscure benefits. + - modernize-use-trailing-return-type + # Huge diff; we use lots of C-style arrays. + - modernize-avoid-c-arrays + # Huge diff; we do lots of pointer arithmetic. + - cppcoreguidelines-pro-bounds-pointer-arithmetic + # Huge diff. + - readability-magic-numbers + - cppcoreguidelines-avoid-magic-numbers + # We use short names because we do math. Also, huge diff. + - readability-identifier-length + # Fixing this would be a lot of work. + - bugprone-easily-swappable-parameters + # Huge diff + - misc-non-private-member-variables-in-classes + - cppcoreguidelines-non-private-member-variables-in-classes + # We have many `for` loops that violate this part of the bounds safety profile + - cppcoreguidelines-pro-bounds-constant-array-index + # Large diff; we often `use` an entire namespace. + - google-build-using-namespace + # Large diff + - cppcoreguidelines-pro-bounds-array-to-pointer-decay + # Large, potentially complicated diff + - readability-container-data-pointer + # Many hits; potential for false positives. + - cppcoreguidelines-pro-type-member-init + # As cryptographers, we often think of bools as 0/1 values; would bloat code in some places. + - modernize-use-bool-literals + # Triggers on every TYPED_TEST + - cert-err58-cpp + # Triggers on some tests that are not complex + - readability-function-cognitive-complexity + # It is often nicer to not be explicit + - google-explicit-constructor + # Not honouring. + - cppcoreguidelines-owning-memory + # "This check is deprecated since it’s no longer part of the CERT standard. It will be removed in clang-tidy version 19." + - cert-dc21-cpp + # Noisy. As we don't need to return error types or raw allocations, really unlikely we'd cause problems by ignoring a return type. + - modernize-use-nodiscard + +--- # this divider is necessary +# Disable some checks for Google Test/Bench +If: + PathMatch: [src/.*\.test\.cpp, src/.*\.bench\.cpp] +Diagnostics: + ClangTidy: + # these checks get triggered by the Google macros + Remove: + - cppcoreguidelines-avoid-non-const-global-variables + - cppcoreguidelines-special-member-functions diff --git a/barretenberg/cpp/src/barretenberg/ecc/fields/field_conversion.hpp b/barretenberg/cpp/src/barretenberg/ecc/fields/field_conversion.hpp index cf5b12d1def..c2298236512 100644 --- a/barretenberg/cpp/src/barretenberg/ecc/fields/field_conversion.hpp +++ b/barretenberg/cpp/src/barretenberg/ecc/fields/field_conversion.hpp @@ -208,4 +208,4 @@ template std::vector inline convert_to_bn254_frs(co return fr_vec; } -} // namespace bb::field_conversion +} // namespace bb::field_conversion \ No newline at end of file diff --git a/barretenberg/cpp/src/barretenberg/ecc/fields/field_conversion.test.cpp b/barretenberg/cpp/src/barretenberg/ecc/fields/field_conversion.test.cpp index 0a024f82545..d54ca02ba4b 100644 --- a/barretenberg/cpp/src/barretenberg/ecc/fields/field_conversion.test.cpp +++ b/barretenberg/cpp/src/barretenberg/ecc/fields/field_conversion.test.cpp @@ -15,6 +15,15 @@ class FieldConversionTest : public ::testing::Test { } }; +/** + * @brief Field conversion test for size_t + */ +TEST_F(FieldConversionTest, FieldConversionSizeT) +{ + size_t x = 210849; + check_conversion(x); +} + /** * @brief Field conversion test for uint32_t */ @@ -131,4 +140,4 @@ TEST_F(FieldConversionTest, FieldConversionUnivariateGrumpkinFr) check_conversion(x1); } -} // namespace bb::field_conversion_tests +} // namespace bb::field_conversion_tests \ No newline at end of file diff --git a/noir/.gitrepo b/noir/.gitrepo index a565286408a..debb993e0bc 100644 --- a/noir/.gitrepo +++ b/noir/.gitrepo @@ -6,7 +6,7 @@ [subrepo] remote = https://github.com/noir-lang/noir branch = aztec-packages - commit = 9944bb170691d4e7905793a978019ba9504b1139 - parent = aeb4cf0d9cec6127cac947c4f0de8e853b2f34e0 + commit = ddd94a2f7f620da14e4222c2325119737b91908d + parent = 4ddf8f42d005d3a1b72fac6b9d40c475a3c4231d method = merge cmdver = 0.4.6 diff --git a/noir/acvm-repo/acir/acir_docs.md b/noir/acvm-repo/acir/acir_docs.md new file mode 100644 index 00000000000..801aeac1140 --- /dev/null +++ b/noir/acvm-repo/acir/acir_docs.md @@ -0,0 +1,206 @@ +# ACIR documentation (draft) + +## Abstract +This document describes the purpose of ACIR, what it is and how ACIR programs can be used by compilers and proving systems. It is intended to be a reference documentation for ACIR. + +## Introduction +The purpose of ACIR is to make the link between a generic proving system, such as Aztec's Barretenberg, and a frontend, such as Noir, which describes user-specific computations. + +More precisely, Noir is a programming language for zero-knowledge proofs (ZKP) which allows users to write programs in an intuitive way using a high-level language close to Rust syntax. Noir is able to generate a proof of execution of a Noir program, using an external proving system. However, proving systems use specific low-level constrain-based languages. Similarly, frontends have their own internal representation in order to represent user programs. + +The goal of ACIR is to provide a generic open-source intermediate representation close to proving system 'languages', but agnostic to a specific proving system, that can be used both by proving system as well as a target for frontends. So, at the end of the day, an ACIR program is just another representation of a program, dedicated to proving systems. + +## Abstract Circuit Intermediate Representation +ACIR stands for abstract circuit intermediate representation: +- **abstract circuit**: circuits are a simple computation model where basic computation units, named gates, are connected with wires. Data flows through the wires while gates compute output wires based on their input. More formally, they are directed acyclic graphs (DAG) where the vertices are the gates and the edges are the wires. Due to the immutability nature of the wires (their value does not change during an execution), they are well suited for describing computations for ZKPs. Furthermore, we do not lose any expressiveness when using a circuit as it is well known that any bounded computation can be translated into an arithmetic circuit (i.e a circuit with only addition and multiplication gates). +The term abstract here simply means that we do not refer to an actual physical circuit (such as an electronic circuit). Furthermore, we will not exactly use the circuit model, but another model even better suited to ZKPs, the constraint model (see below). +- **intermediate representation**: The ACIR representation is intermediate because it lies between a frontend and its proving system. ACIR bytecode makes the link between noir compiler output and the proving system backend input. + +## The constraint model +The first step for generating a proof that a specific program was executed, is to execute this program. Since the proving system is going to handle ACIR programs, we need in fact to execute an ACIR program, using the user-supplied inputs. + +In ACIR terminology, the gates are called opcodes and the wires are called partial witnesses. However, instead of connecting the opcodes together through wires, we create constraints: an opcode constraints together a set of wires. +This constraint model trivially supersedes the circuit model. For instance, an addition gate output_wire = input_wire_1 + input_wire_2 can be expressed with the following arithmetic constraint: output_wire - (input_wire_1 + input_wire_2) = 0 + + + +## Solving +Because of these constraints, executing an ACIR program is called solving the witnesses. From the witnesses representing the inputs of the program, whose values are supplied by the user, we find out what the other witnesses should be by executing/solving the constraints one-by-one in the order they were defined. + +For instance, if input_wire_1 and input_wire_2 values are supplied as 3 and 8, then we can solve the opcode output_wire - (input_wire_1 + input_wire_2) = 0 by saying that output_wire is 11. + +In summary, the workflow is the following: +1. user program -> (compilation) ACIR, a list of opcodes which constrain (partial) witnesses +2. user inputs + ACIR -> (execution/solving) assign values to all the (partial) witnesses +3. witness assignment + ACIR -> (proving system) proof + + +Although the ordering of opcode does not matter in theory, since a system of equations is not dependent on its ordering, in practice it matters a lot for the solving (i.e the performance of the execution). ACIR opcodes **must be ordered** so that each opcode can be resolved one after the other. + + +The values of the witnesses lie in the scalar field of the proving system. We will refer to it as FieldElement or ACIR field. The proving system needs the values of all the partial witnesses and all the constraints in order to generate a proof. + + +*Remark*: The value of a partial witness is unique and fixed throughout a program execution, although in some rare cases, multiple values are possible for a same execution and witness (when there are several valid solutions to the constraints). Having multiple possible values for a witness may indicate that the circuit is not safe. + +*Remark*: Why do we use the term partial witnesses? It is because the proving system may create other constraints and witnesses (especially with BlackBoxFuncCall, see below). A proof refers to a full witness assignments and their constraints. ACIR opcodes and their partial witnesses are still an intermediate representation before getting the full list of constraints and witnesses. For the sake of simplicity, we will refer to witness instead of partial witness from now on. + + +## ACIR Reference +We assume here that the proving system is Barretenberg. Some parameters may slightly change with another proving system, in particular the bit size of FieldElement, which is 254 for Barretenberg. + +Some opcodes have inputs and outputs, which means that the output is constrained to be the result of the opcode computation from the inputs. The solver expects that all inputs are known when solving such opcodes. + +Some opcodes are not constrained, which means they will not be used by the proving system and are only used by the solver. + +Finally, some opcodes will have a predicate, whose value is 0 or 1. Its purpose is to nullify the opcode when the value is 0, so that it has no effect. Note that removing the opcode is not a solution because this modifies the circuit (the circuit being mainly the list of the opcodes). + +*Remark*: Opcodes operate on witnesses, but we will see that some opcode work on expressions of witnesses. We call an expression a linear combination of witnesses and/or products of two witnesses (and also a constant term). A single witness is a (simple) expression, and conversely, an expression can be turned into a single witness using an assert-zero opcode (see below). So basically, using witnesses or expressions is equivalent, but the latter can avoid the creation of witness in some cases. + +### AssertZero opcode +An AssertZero opcode adds the constraint that P(w) = 0, where w=(w_1,..w_n) is a tuple of n witnesses, and P is a multi-variate polynomial of total degree at most 2. +The coefficients ${q_M}_{i,j}, q_i,q_c$ of the polynomial are known values which define the opcode. +A general expression of assert-zero opcode is the following: $\sum_{i,j} {q_M}_{i,j}w_iw_j + \sum_i q_iw_i +q_c = 0$ + +An assert-zero opcode can be used to: +- **express a constraint** on witnesses; for instance to express that a witness $w$ is a boolean, you can add the opcode: $w*w-w=0$ +- or, to **compute the value** of an arithmetic operation of some inputs. For instance, to multiply two witnesses $x$ and $y$, you would use the opcode $z-x*y=0$, which would constraint $z$ to be $x*y$. + + +The solver expects that at most one witness is not known when executing the opcode. + +### BlackBoxFuncCall opcode +These opcodes represent a specific computation. Even if any computation can be done using only assert-zero opcodes, it is not always efficient. Some proving systems, and in particular the proving system from Aztec, can implement several computations more efficiently using for instance look-up tables. The BlackBoxFuncCall opcode is used to ask the proving system to handle the computation by itself. +All black box functions take as input a tuple (witness, num_bits), where num_bits is a constant representing the bit size of the input witness, and they have one or several witnesses as output. +Some more advanced computations assume that the proving system has an 'embedded curve'. It is a curve that cycle with the main curve of the proving system, i.e the scalar field of the embedded curve is the base field of the main one, and vice-versa. The curves used by the proving system are dependent on the proving system (and/or its configuration). Aztec's Barretenberg uses BN254 as the main curve and Grumpkin as the embedded curve. + +The black box functions supported by ACIR are: + +**AND**: performs the bitwise AND of lhs and rhs. bit_size must be the same for both inputs. +- lhs: (witness, bit_size) +- rhs: (witness, bit_size) +- output: a witness whose value is constrained to be lhs AND rhs, as bit_size bit integers + +**XOR**: performs the bitwise XOR of lhs and rhs. bit_size must be the same for both inputs. +- lhs: (witness, bit_size) +- rhs: (witness, bit_size) +- output: a witness whose value is constrained to be lhs XOR rhs, as bit_size bit integers + +**RANGE**: constraint the input to be of the provided bit size +input: (witness, bit_size) + +**SHA256**: computes sha256 of the inputs +- inputs are a byte array, i.e a vector of (FieldElement, 8) +- output is a byte array of len 32, i.e a vector of 32 (FieldElement, 8), constrained to be the sha256 of the inputs. + +**Blake2s**: computes the Blake2s hash of the inputs, as specified in https://tools.ietf.org/html/rfc7693 +- inputs are a byte array, i.e a vector of (FieldElement, 8) +- output is a byte array of length 32, i.e a vector of 32 (FieldElement, 8), constrained to be the blake2s of the inputs. + + +**SchnorrVerify**: Verify a Schnorr signature over the embedded curve +- inputs are: + - Public key as 2 (FieldElement, 254) + - signature as a vector of 64 bytes (FieldElement, 8) + - message as a vector of (FieldElement, 8) +- output: A witness representing the result of the signature verification; 0 for failure and 1 for success. + +Since the scalar field of the embedded curve is NOT the ACIR field, the (r,s) signature is represented as a 64 bytes array for the two field elements. On the other hand, the public key coordinates are ACIR fields. +The proving system decides how the message is to be hashed. Barretenberg uses Blake2s. + + +**PedersenCommitment**: Computes a Pedersen commitments of the inputs using generators of the embedded curve +- input: vector of (FieldElement, 254) +- output: 2 witnesses representing the x,y coordinates of the resulting Grumpkin point +- domain separator: a constant public value (a field element) that you can use so that the commitment also depends on the domain separator. Noir uses 0 as domain separator. + +The backend should handle proper conversion between the inputs being ACIR field elements and the scalar field of the embedded curve. In the case of Aztec's Barretenberg, the latter is bigger than the ACIR field so it is straightforward. The Pedersen generators are managed by the proving system. + + +**PedersenHash**: Computes a Pedersen commitments of the inputs and their number, using generators of the embedded curve +- input: vector of (FieldElement, 254) +- output: the x-coordinate of the pedersen commitment of the 'prepended input' (see below) +- domain separator: a constant public value (a field element) that you can use so that the hash also depends on the domain separator. Noir uses 0 as domain separator. + +In Barretenberg, PedersenHash is doing the same as PedersenCommitment, except that it prepends the inputs with their length. + + +**HashToField128Security**: This opcode is deprecated and will be removed. + +**EcdsaSecp256k1**: Verify an ECDSA signature over Secp256k1 +- inputs: + - x coordinate of public key as 32 bytes + - y coordinate of public key as 32 bytes + - the signature, as a 64 bytes array + - the hash of the message, as a vector of bytes +- output: 0 for failure and 1 for success + +Inputs and outputs are similar to SchnorrVerify, except that because we use a different curve (secp256k1), the field elements involved in the signature and the public key are defined as an array of 32 bytes. Another difference is that we assume the message is already hashed. + +**EcdsaSecp256r1**: Same as EcdsaSecp256k1, but done over another curve. + +**FixedBaseScalarMul**: scalar multiplication with a fixed generator of the embedded curve +- input: low, high are 2 (field , 254), representing the low and high part of the input. For Barretenberg, they must both be less than 128 bits. +- output: x and y coordinates of $low*G+high*2^{128}*G$, where G is a fixed generator + +Because the Grumpkin scalar field is bigger than the ACIR field, we provide 2 ACIR fields representing the low and high parts of the Grumpkin scalar $a$: +$a=low+high*2^{128},$ with $low, high < 2^{128}$ + +**Keccak256**: Computes the Keccak-256 (Ethereum version) of the inputs. +- inputs: Vector of bytes (FieldElement, 8) +- outputs: Vector of 32 bytes (FieldElement, 8) + + +**Keccak256VariableLength**: Computes the Keccak-256 (Ethereum version) of the inputs, restricted to the given length. +- inputs: Vector of bytes (FieldElement, 8) +- var_message_size: number of inputs to hash; it must be less (or equal) than the inputs length +- outputs: a vector of 32 bytes (FieldElement, 8) + + + +**RecursiveAggregation**: verify a proof inside the circuit. +**Warning: this opcode is subject to change.** +- verification_key: Vector of (FieldElement, 254) representing the verification key of the circuit being verified +- public_inputs: Vector of (FieldElement, 254) representing the public inputs corresponding to the proof being verified +- key_hash: one (FieldElement, 254). It should be the hash of the verification key. Barretenberg expects the Pedersen hash of the verification key +- input_aggregation_object: an optional vector of (FieldElement, 254). It is a blob of data specific to the proving system. +- output_aggregation_object: Some witnesses returned by the function, representing some data internal to the proving system. + +This black box function does not fully verify a proof, what it does is verifying that the key_hash is indeed a hash of verification_key, allowing the user to use the verification key as private inputs and only have the key_hash as public input, which is more performant. +Another thing that it does is preparing the verification of the proof. In order to fully verify a proof, some operations may still be required to be done by the final verifier. This is why this black box function does not say if verification is passing or not. +If you have several proofs to verify in one ACIR program, you would call RecursiveAggregation() multiple times and passing the output_aggregation_object as input_aggregation_object to the next RecursiveAggregation() call, except for the first call where you do not have any input_aggregation_object. +If one of the proof you verify with the black box function does not verify, then the verification of the proof of the main ACIR program will ultimately fail. + + +### Brillig +This opcode is used as a hint for the solver when executing (solving) the circuit. The opcode does not generate any constraint and is usually the result of the compilation of an unconstrained noir function. +- inputs: inputs to the opcode, as 'arithmetic expressions'. +- outputs: opcode outputs, as witnesses +- bytecode: assembly code representing the computation to perform within this opcode. The noir assembly specification is not part of this document. +- predicate: an arithmetic expression that disable the opcode when it is null. + +Let's see an example with euclidean division. +The normal way to compute a/b, where a and b are 8-bits integers, is to implement Euclid algorithm which computes in a loop (or recursively) modulus of the kind 'a mod b'. Doing this computation requires a lot of steps to be properly implemented in ACIR, especially the loop with a condition. However, euclidean division can be easily constrained with one assert-zero opcode: a = bq+r, assuming q is 8 bits and r { - (input.witness, input.num_bits) - } - _ => { + let (witness, num_bits) = match extract_range_opcode(&opcode) { + Some(range_opcode) => range_opcode, + None => { // If its not the range opcode, add it to the opcode // list and continue; optimized_opcodes.push(opcode); @@ -132,7 +131,7 @@ impl RangeOptimizer { if is_lowest_bit_size { already_seen_witness.insert(witness); new_order_list.push(order_list[idx]); - optimized_opcodes.push(opcode); + optimized_opcodes.push(optimized_range_opcode(witness, num_bits)); } } @@ -140,11 +139,36 @@ impl RangeOptimizer { } } +/// Extract the range opcode from the `Opcode` enum +/// Returns None, if `Opcode` is not the range opcode. +fn extract_range_opcode(opcode: &Opcode) -> Option<(Witness, u32)> { + match opcode { + Opcode::BlackBoxFuncCall(BlackBoxFuncCall::RANGE { input }) => { + Some((input.witness, input.num_bits)) + } + _ => None, + } +} + +fn optimized_range_opcode(witness: Witness, num_bits: u32) -> Opcode { + if num_bits == 1 { + Opcode::AssertZero(Expression { + mul_terms: vec![(FieldElement::one(), witness, witness)], + linear_combinations: vec![(-FieldElement::one(), witness)], + q_c: FieldElement::zero(), + }) + } else { + Opcode::BlackBoxFuncCall(BlackBoxFuncCall::RANGE { + input: FunctionInput { witness, num_bits }, + }) + } +} + #[cfg(test)] mod tests { use std::collections::BTreeSet; - use crate::compiler::optimizers::redundant_range::RangeOptimizer; + use crate::compiler::optimizers::redundant_range::{extract_range_opcode, RangeOptimizer}; use acir::{ circuit::{ opcodes::{BlackBoxFuncCall, FunctionInput}, @@ -194,12 +218,11 @@ mod tests { let (optimized_circuit, _) = optimizer.replace_redundant_ranges(acir_opcode_positions); assert_eq!(optimized_circuit.opcodes.len(), 1); - assert_eq!( - optimized_circuit.opcodes[0], - Opcode::BlackBoxFuncCall(BlackBoxFuncCall::RANGE { - input: FunctionInput { witness: Witness(1), num_bits: 16 } - }) - ); + let (witness, num_bits) = + extract_range_opcode(&optimized_circuit.opcodes[0]).expect("expected one range opcode"); + + assert_eq!(witness, Witness(1)); + assert_eq!(num_bits, 16); } #[test] @@ -217,18 +240,15 @@ mod tests { let (optimized_circuit, _) = optimizer.replace_redundant_ranges(acir_opcode_positions); assert_eq!(optimized_circuit.opcodes.len(), 2); - assert_eq!( - optimized_circuit.opcodes[0], - Opcode::BlackBoxFuncCall(BlackBoxFuncCall::RANGE { - input: FunctionInput { witness: Witness(1), num_bits: 16 } - }) - ); - assert_eq!( - optimized_circuit.opcodes[1], - Opcode::BlackBoxFuncCall(BlackBoxFuncCall::RANGE { - input: FunctionInput { witness: Witness(2), num_bits: 23 } - }) - ); + let (witness_a, num_bits_a) = + extract_range_opcode(&optimized_circuit.opcodes[0]).expect("expected two range opcode"); + let (witness_b, num_bits_b) = + extract_range_opcode(&optimized_circuit.opcodes[1]).expect("expected two range opcode"); + + assert_eq!(witness_a, Witness(1)); + assert_eq!(witness_b, Witness(2)); + assert_eq!(num_bits_a, 16); + assert_eq!(num_bits_b, 23); } #[test] diff --git a/noir/aztec_macros/src/lib.rs b/noir/aztec_macros/src/lib.rs index 1dbe7631388..b1d401b4e53 100644 --- a/noir/aztec_macros/src/lib.rs +++ b/noir/aztec_macros/src/lib.rs @@ -26,12 +26,8 @@ impl MacroProcessor for AztecMacro { transform(ast, crate_id, context) } - fn process_typed_ast( - &self, - crate_id: &CrateId, - context: &mut HirContext, - ) -> Result<(), (MacroError, FileId)> { - transform_hir(crate_id, context).map_err(|(err, file_id)| (err.into(), file_id)) + fn process_typed_ast(&self, crate_id: &CrateId, context: &mut HirContext) { + transform_hir(crate_id, context) } } @@ -45,7 +41,6 @@ pub enum AztecMacroError { ContractHasTooManyFunctions { span: Span }, ContractConstructorMissing { span: Span }, UnsupportedFunctionArgumentType { span: Span, typ: UnresolvedTypeData }, - EventError { span: Span, message: String }, } impl From for MacroError { @@ -76,11 +71,6 @@ impl From for MacroError { secondary_message: None, span: Some(span), }, - AztecMacroError::EventError { span, message } => MacroError { - primary_message: message, - secondary_message: None, - span: Some(span), - }, } } } @@ -247,11 +237,8 @@ fn transform( // /// Completes the Hir with data gathered from type resolution -fn transform_hir( - crate_id: &CrateId, - context: &mut HirContext, -) -> Result<(), (AztecMacroError, FileId)> { - transform_events(crate_id, context) +fn transform_hir(crate_id: &CrateId, context: &mut HirContext) { + transform_events(crate_id, context); } /// Includes an import to the aztec library if it has not been included yet @@ -485,30 +472,19 @@ fn collect_crate_structs(crate_id: &CrateId, context: &HirContext) -> Vec Result<(), (AztecMacroError, FileId)> { +fn transform_event(struct_id: StructId, interner: &mut NodeInterner) { let struct_type = interner.get_struct(struct_id); let selector_id = interner - .lookup_method(&Type::Struct(struct_type.clone(), vec![]), struct_id, "selector", false) - .ok_or_else(|| { - let error = AztecMacroError::EventError { - span: struct_type.borrow().location.span, - message: "Selector method not found".to_owned(), - }; - (error, struct_type.borrow().location.file) - })?; + .lookup_method(&Type::Struct(struct_type, vec![]), struct_id, "selector", false) + .expect("Selector method not found"); let selector_function = interner.function(&selector_id); let compute_selector_statement = interner.statement( - selector_function.block(interner).statements().first().ok_or_else(|| { - let error = AztecMacroError::EventError { - span: struct_type.borrow().location.span, - message: "Compute selector statement not found".to_owned(), - }; - (error, struct_type.borrow().location.file) - })?, + selector_function + .block(interner) + .statements() + .first() + .expect("Compute selector statement not found"), ); let compute_selector_expression = match compute_selector_statement { @@ -518,21 +494,12 @@ fn transform_event( }, _ => None, } - .ok_or_else(|| { - let error = AztecMacroError::EventError { - span: struct_type.borrow().location.span, - message: "Compute selector statement is not a call expression".to_owned(), - }; - (error, struct_type.borrow().location.file) - })?; - - let first_arg_id = compute_selector_expression.arguments.first().ok_or_else(|| { - let error = AztecMacroError::EventError { - span: struct_type.borrow().location.span, - message: "Compute selector statement is not a call expression".to_owned(), - }; - (error, struct_type.borrow().location.file) - })?; + .expect("Compute selector statement is not a call expression"); + + let first_arg_id = compute_selector_expression + .arguments + .first() + .expect("Missing argument for compute selector"); match interner.expression(first_arg_id) { HirExpression::Literal(HirLiteral::Str(signature)) @@ -551,29 +518,18 @@ fn transform_event( selector_literal_id, Type::String(Box::new(Type::Constant(signature.len() as u64))), ); - Ok(()) } - _ => Err(( - AztecMacroError::EventError { - span: struct_type.borrow().location.span, - message: "Signature placeholder literal does not match".to_owned(), - }, - struct_type.borrow().location.file, - )), + _ => unreachable!("Signature placeholder literal does not match"), } } -fn transform_events( - crate_id: &CrateId, - context: &mut HirContext, -) -> Result<(), (AztecMacroError, FileId)> { +fn transform_events(crate_id: &CrateId, context: &mut HirContext) { for struct_id in collect_crate_structs(crate_id, context) { let attributes = context.def_interner.struct_attributes(&struct_id); if attributes.iter().any(|attr| matches!(attr, SecondaryAttribute::Event)) { - transform_event(struct_id, &mut context.def_interner)?; + transform_event(struct_id, &mut context.def_interner); } } - Ok(()) } const SIGNATURE_PLACEHOLDER: &str = "SIGNATURE_PLACEHOLDER"; diff --git a/noir/compiler/noirc_evaluator/src/ssa/acir_gen/acir_ir/acir_variable.rs b/noir/compiler/noirc_evaluator/src/ssa/acir_gen/acir_ir/acir_variable.rs index f1a639de211..d56d0ade3c4 100644 --- a/noir/compiler/noirc_evaluator/src/ssa/acir_gen/acir_ir/acir_variable.rs +++ b/noir/compiler/noirc_evaluator/src/ssa/acir_gen/acir_ir/acir_variable.rs @@ -1441,22 +1441,20 @@ impl AcirContext { inputs: Vec, outputs: Vec, attempt_execution: bool, - ) -> Result, RuntimeError> { - let b_inputs = try_vecmap(inputs, |i| -> Result<_, InternalError> { - match i { - AcirValue::Var(var, _) => Ok(BrilligInputs::Single(self.var_to_expression(var)?)), - AcirValue::Array(vars) => { - let mut var_expressions: Vec = Vec::new(); - for var in vars { - self.brillig_array_input(&mut var_expressions, var)?; - } - Ok(BrilligInputs::Array(var_expressions)) - } - AcirValue::DynamicArray(_) => { - let mut var_expressions = Vec::new(); - self.brillig_array_input(&mut var_expressions, i)?; - Ok(BrilligInputs::Array(var_expressions)) + ) -> Result, InternalError> { + let b_inputs = try_vecmap(inputs, |i| match i { + AcirValue::Var(var, _) => Ok(BrilligInputs::Single(self.var_to_expression(var)?)), + AcirValue::Array(vars) => { + let mut var_expressions: Vec = Vec::new(); + for var in vars { + self.brillig_array_input(&mut var_expressions, var)?; } + Ok(BrilligInputs::Array(var_expressions)) + } + AcirValue::DynamicArray(_) => { + let mut var_expressions = Vec::new(); + self.brillig_array_input(&mut var_expressions, i)?; + Ok(BrilligInputs::Array(var_expressions)) } })?; @@ -1491,34 +1489,6 @@ impl AcirContext { let predicate = self.var_to_expression(predicate)?; self.acir_ir.brillig(Some(predicate), generated_brillig, b_inputs, b_outputs); - fn range_constraint_value( - context: &mut AcirContext, - value: &AcirValue, - ) -> Result<(), RuntimeError> { - match value { - AcirValue::Var(var, typ) => { - let numeric_type = match typ { - AcirType::NumericType(numeric_type) => numeric_type, - _ => unreachable!("`AcirValue::Var` may only hold primitive values"), - }; - context.range_constrain_var(*var, numeric_type, None)?; - } - AcirValue::Array(values) => { - for value in values { - range_constraint_value(context, value)?; - } - } - AcirValue::DynamicArray(_) => { - unreachable!("Brillig opcodes cannot return dynamic arrays") - } - } - Ok(()) - } - - for output_var in &outputs_var { - range_constraint_value(self, output_var)?; - } - Ok(outputs_var) } diff --git a/noir/compiler/noirc_frontend/src/hir/def_collector/dc_crate.rs b/noir/compiler/noirc_frontend/src/hir/def_collector/dc_crate.rs index f7441750fc8..a6ab6b1d825 100644 --- a/noir/compiler/noirc_frontend/src/hir/def_collector/dc_crate.rs +++ b/noir/compiler/noirc_frontend/src/hir/def_collector/dc_crate.rs @@ -14,7 +14,7 @@ use crate::hir::resolution::{ use crate::hir::type_check::{type_check_func, TypeCheckError, TypeChecker}; use crate::hir::Context; -use crate::macros_api::{MacroError, MacroProcessor}; +use crate::macros_api::MacroProcessor; use crate::node_interner::{FuncId, NodeInterner, StmtId, StructId, TraitId, TypeAliasId}; use crate::parser::{ParserError, SortedModule}; @@ -155,12 +155,6 @@ impl From for CustomDiagnostic { } } -impl From for CompilationError { - fn from(value: MacroError) -> Self { - CompilationError::DefinitionError(DefCollectorErrorKind::MacroError(value)) - } -} - impl From for CompilationError { fn from(value: ParserError) -> Self { CompilationError::ParseError(value) @@ -365,11 +359,7 @@ impl DefCollector { errors.extend(resolved_globals.errors); for macro_processor in macro_processors { - macro_processor.process_typed_ast(&crate_id, context).unwrap_or_else( - |(macro_err, file_id)| { - errors.push((macro_err.into(), file_id)); - }, - ); + macro_processor.process_typed_ast(&crate_id, context); } errors.extend(type_check_globals(&mut context.def_interner, resolved_globals.globals)); diff --git a/noir/compiler/noirc_frontend/src/hir/resolution/errors.rs b/noir/compiler/noirc_frontend/src/hir/resolution/errors.rs index 7bd4de77e84..390807afd17 100644 --- a/noir/compiler/noirc_frontend/src/hir/resolution/errors.rs +++ b/noir/compiler/noirc_frontend/src/hir/resolution/errors.rs @@ -84,8 +84,6 @@ pub enum ResolverError { InvalidTypeForEntryPoint { span: Span }, #[error("Nested slices are not supported")] NestedSlices { span: Span }, - #[error("Usage of the `#[foreign]` or `#[builtin]` function attributes are not allowed outside of the Noir standard library")] - LowLevelFunctionOutsideOfStdlib { ident: Ident }, } impl ResolverError { @@ -313,11 +311,6 @@ impl From for Diagnostic { "Try to use a constant sized array instead".into(), span, ), - ResolverError::LowLevelFunctionOutsideOfStdlib { ident } => Diagnostic::simple_error( - "Definition of low-level function outside of standard library".into(), - "Usage of the `#[foreign]` or `#[builtin]` function attributes are not allowed outside of the Noir standard library".into(), - ident.span(), - ), } } } diff --git a/noir/compiler/noirc_frontend/src/hir/resolution/resolver.rs b/noir/compiler/noirc_frontend/src/hir/resolution/resolver.rs index 8243b684c8a..df533f6a4ae 100644 --- a/noir/compiler/noirc_frontend/src/hir/resolution/resolver.rs +++ b/noir/compiler/noirc_frontend/src/hir/resolution/resolver.rs @@ -191,18 +191,10 @@ impl<'a> Resolver<'a> { self.add_generics(&func.def.generics); self.trait_bounds = func.def.where_clause.clone(); - let is_low_level_or_oracle = func - .attributes() - .function - .as_ref() - .map_or(false, |func| func.is_low_level() || func.is_oracle()); let (hir_func, func_meta) = self.intern_function(func, func_id); let func_scope_tree = self.scopes.end_function(); - // The arguments to low-level and oracle functions are always unused so we do not produce warnings for them. - if !is_low_level_or_oracle { - self.check_for_unused_variables_in_scope_tree(func_scope_tree); - } + self.check_for_unused_variables_in_scope_tree(func_scope_tree); self.trait_bounds.clear(); (hir_func, func_meta, self.errors) @@ -908,13 +900,6 @@ impl<'a> Resolver<'a> { position: PubPosition::ReturnType, }); } - let is_low_level_function = - func.attributes().function.as_ref().map_or(false, |func| func.is_low_level()); - if !self.path_resolver.module_id().krate.is_stdlib() && is_low_level_function { - let error = - ResolverError::LowLevelFunctionOutsideOfStdlib { ident: func.name_ident().clone() }; - self.push_err(error); - } // 'pub' is required on return types for entry point functions if self.is_entry_point_function(func) diff --git a/noir/compiler/noirc_frontend/src/lexer/token.rs b/noir/compiler/noirc_frontend/src/lexer/token.rs index 835a0baae3f..ab131ccd880 100644 --- a/noir/compiler/noirc_frontend/src/lexer/token.rs +++ b/noir/compiler/noirc_frontend/src/lexer/token.rs @@ -562,10 +562,6 @@ impl FunctionAttribute { matches!(self, FunctionAttribute::Foreign(_)) } - pub fn is_oracle(&self) -> bool { - matches!(self, FunctionAttribute::Oracle(_)) - } - pub fn is_low_level(&self) -> bool { matches!(self, FunctionAttribute::Foreign(_) | FunctionAttribute::Builtin(_)) } diff --git a/noir/compiler/noirc_frontend/src/lib.rs b/noir/compiler/noirc_frontend/src/lib.rs index b6d4c568334..9582b80dcba 100644 --- a/noir/compiler/noirc_frontend/src/lib.rs +++ b/noir/compiler/noirc_frontend/src/lib.rs @@ -75,10 +75,6 @@ pub mod macros_api { ) -> Result; /// Function to manipulate the AST after type checking has been completed. /// The AST after type checking has been done is called the HIR. - fn process_typed_ast( - &self, - crate_id: &CrateId, - context: &mut HirContext, - ) -> Result<(), (MacroError, FileId)>; + fn process_typed_ast(&self, crate_id: &CrateId, context: &mut HirContext); } } diff --git a/noir/compiler/noirc_frontend/src/tests.rs b/noir/compiler/noirc_frontend/src/tests.rs index a4246a9fe7d..9ccbddab9ec 100644 --- a/noir/compiler/noirc_frontend/src/tests.rs +++ b/noir/compiler/noirc_frontend/src/tests.rs @@ -52,12 +52,10 @@ mod test { ) -> (ParsedModule, Context, Vec<(CompilationError, FileId)>) { let root = std::path::Path::new("/"); let fm = FileManager::new(root); - let mut context = Context::new(fm, Default::default()); context.def_interner.populate_dummy_operator_traits(); let root_file_id = FileId::dummy(); let root_crate_id = context.crate_graph.add_crate_root(root_file_id); - let (program, parser_errors) = parse_program(src); let mut errors = vecmap(parser_errors, |e| (e.into(), root_file_id)); remove_experimental_warnings(&mut errors); diff --git a/noir/docs/docs/explainers/explainer-recursion.md b/noir/docs/docs/explainers/explainer-recursion.md index 18846176ca7..8f992ec29fd 100644 --- a/noir/docs/docs/explainers/explainer-recursion.md +++ b/noir/docs/docs/explainers/explainer-recursion.md @@ -16,13 +16,12 @@ keywords: "Optimizing Computational Resources", "Improving Efficiency", "Verification Key", - "Aggregation", + "Aggregation Objects", "Recursive zkSNARK schemes", "PLONK", "Proving and Verification Keys" ] sidebar_position: 1 -pagination_next: how_to/how-to-recursion --- In programming, we tend to think of recursion as something calling itself. A classic example would be the calculation of the factorial of a number: @@ -65,7 +64,7 @@ So, they use zero-knowledge proofs. Alice tries to guess Bob's number, and Bob w This ZK proof can go on a smart contract, revealing the winner and even giving prizes. However, this means every turn needs to be verified on-chain. This incurs some cost and waiting time that may simply make the game too expensive or time-consuming to be worth it. -As a solution, Alice proposes the following: "what if Bob generates his proof, and instead of sending it on-chain, I verify it *within* my own proof before playing my own turn?". +As a solution, Alice proposes the following: "what if Bob generates his proof, and instead of sending it on-chain, I verify it *within* my own proof before playing my own turn?". She can then generate a proof that she verified his proof, and so on. @@ -117,19 +116,25 @@ As you can see in the [recursion reference](noir/standard_library/recursion.md), - The Verification Key of the circuit that generated the proof - A hash of this verification key, as it's needed for some backends - The public inputs for the proof +- The input aggregation object -:::info +It also returns the `output aggregation object`. These aggregation objects can be confusing at times, so let's dive in a little bit. + +### Aggregation objects Recursive zkSNARK schemes do not necessarily "verify a proof" in the sense that you expect a true or false to be spit out by the verifier. Rather an aggregation object is built over the public inputs. +In the case of PLONK the recursive aggregation object is two G1 points (expressed as 16 witness values). The final verifier (in our case this is most often the smart contract verifier) has to be aware of this aggregation object to execute a pairing and check the validity of these points. + So, taking the example of Alice and Bob and their guessing game: - Alice makes her guess. Her proof is *not* recursive: it doesn't verify any proof within it! It's just a standard `assert(x != y)` circuit -- Bob verifies Alice's proof and makes his own guess. In this circuit, he doesn't exactly *prove* the verification of Alice's proof. Instead, he *aggregates* his proof to Alice's proof. The actual verification is done when the full proof is verified, for example when using `nargo verify` or through the verifier smart contract. +- Bob verifies Alice's proof and makes his own guess. In this circuit, he is verifying a proof, so it needs to output an `aggregation object`: he is generating a recursive proof! +- Alice verifies Bob's *recursive proof*, and uses Bob's `output aggregation object` as the `input aggregation object` in her proof... Which in turn, generates another `output aggregation object`. -We can imagine recursive proofs a [relay race](https://en.wikipedia.org/wiki/Relay_race). The first runner doesn't have to receive the baton from anyone else, as he/she already starts with it. But when his/her turn is over, the next runner needs to receive it, run a bit more, and pass it along. Even though every runner could theoretically verify the baton mid-run (why not? 🏃🔍), only at the end of the race does the referee verify that the whole race is valid. +One should notice that when Bob generates his first proof, he has no input aggregation object. Because he is not verifying an recursive proof, he has no `input aggregation object`. In this case, he may use zeros instead. -::: +We can imagine the `aggregation object` as the baton in a [relay race](https://en.wikipedia.org/wiki/Relay_race). The first runner doesn't have to receive the baton from anyone else, as he/she already starts with it. But when his/her turn is over, the next runner needs to receive it, run a bit more, and pass it along. Even though every runner could theoretically verify the baton mid-run (why not? 🏃🔍), only at the end of the race does the referee verify that the whole race is valid. ## Some architecture @@ -170,7 +175,3 @@ In this example, a regulator could verify that taxes were paid for a specific pu At the time of writing, verifying recursive proofs is surprisingly fast. This is because most of the time is spent on generating the verification key that will be used to generate the next proof. So you are able to cache the verification key and reuse it later. Currently, Noir JS packages don't expose the functionality of loading proving and verification keys, but that feature exists in the underlying `bb.js` package. - -## How can I try it - -Learn more about using recursion in Nargo and NoirJS in the [how-to guide](../how_to/how-to-recursion.md) and see a full example in [noir-examples](https://github.com/noir-lang/noir-examples). diff --git a/noir/docs/docs/how_to/how-to-recursion.md b/noir/docs/docs/how_to/how-to-recursion.md index f34647a99d5..39db23f1f3a 100644 --- a/noir/docs/docs/how_to/how-to-recursion.md +++ b/noir/docs/docs/how_to/how-to-recursion.md @@ -108,7 +108,11 @@ This call takes the public inputs and the proof, but also the public inputs coun :::info -The `proofAsFields` has a constant size `[Field; 93]` and verification keys in Barretenberg are always `[Field; 114]`. +The `proofAsFields` has a constant size `[Field; 93]`. However, currently the backend doesn't remove the public inputs from the proof when converting it. + +This means that if your `main` circuit has two public inputs, then you should also modify the recursive circuit to accept a proof with the public inputs appended. This means that in our example, since `y` is a public input, our `proofAsFields` is of type `[Field; 94]`. + +Verification keys in Barretenberg are always of size 114. ::: @@ -132,6 +136,7 @@ const recursiveInputs = { proof: proofAsFields, // array of length 93 + size of public inputs publicInputs: [mainInput.y], // using the example above, where `y` is the only public input key_hash: vkHash, + input_aggregation_object: Array(16).fill(0) // this circuit is verifying a non-recursive proof, so there's no input aggregation object: just use zero } const { witness, returnValue } = noir.execute(recursiveInputs) // we're executing the recursive circuit now! @@ -139,7 +144,7 @@ const { proof, publicInputs } = backend.generateFinalProof(witness) const verified = backend.verifyFinalProof({ proof, publicInputs }) ``` -You can obviously chain this proof into another proof. In fact, if you're using recursive proofs, you're probably interested of using them this way! +You can obviously chain this proof into another proof. In fact, if you're using recursive proofs, you're probably interested of using them this way! In that case, you should keep in mind the `returnValue`, as it will contain the `input_aggregation_object` for the next proof. :::tip @@ -147,16 +152,16 @@ Managing circuits and "who does what" can be confusing. To make sure your naming ```js const circuits = { - main: mainJSON, - recursive: recursiveJSON +main: mainJSON, +recursive: recursiveJSON } const backends = { - main: new BarretenbergBackend(circuits.main), - recursive: new BarretenbergBackend(circuits.recursive) +main: new BarretenbergBackend(circuits.main), +recursive: new BarretenbergBackend(circuits.recursive) } const noir_programs = { - main: new Noir(circuits.main, backends.main), - recursive: new Noir(circuits.recursive, backends.recursive) +main: new Noir(circuits.main, backends.main), +recursive: new Noir(circuits.recursive, backends.recursive) } ``` diff --git a/noir/docs/docs/noir/standard_library/recursion.md b/noir/docs/docs/noir/standard_library/recursion.md index f252150c8b5..67962082a8f 100644 --- a/noir/docs/docs/noir/standard_library/recursion.md +++ b/noir/docs/docs/noir/standard_library/recursion.md @@ -1,16 +1,16 @@ --- title: Recursive Proofs description: Learn about how to write recursive proofs in Noir. -keywords: [recursion, recursive proofs, verification_key, verify_proof] +keywords: [recursion, recursive proofs, verification_key, aggregation object, verify_proof] --- Noir supports recursively verifying proofs, meaning you verify the proof of a Noir program in another Noir program. This enables creating proofs of arbitrary size by doing step-wise verification of smaller components of a large proof. -Read [the explainer on recursion](../../explainers/explainer-recursion.md) to know more about this function and the [guide on how to use it.](../../how_to/how-to-recursion.md) +The `verify_proof` function takes a verification key, proof and public inputs for a zk program, as well as a key hash and an input aggregation object. The key hash is used to check the validity of the verification key and the input aggregation object is required by some proving systems. The `verify_proof` function returns an output aggregation object that can then be fed into future iterations of the proof verification if required. ```rust #[foreign(verify_proof)] -fn verify_proof(_verification_key : [Field], _proof : [Field], _public_input : Field, _key_hash : Field) {} +fn verify_proof(_verification_key : [Field], _proof : [Field], _public_input : Field, _key_hash : Field, _input_aggregation_object : [Field]) -> [Field] {} ``` :::info @@ -26,29 +26,36 @@ use dep::std; fn main( verification_key : [Field; 114], - proof : [Field; 93], + proof : [Field; 94], public_inputs : [Field; 1], key_hash : Field, - proof_b : [Field; 93], -) { - std::verify_proof( + input_aggregation_object : [Field; 16], + proof_b : [Field; 94], +) -> pub [Field; 16] { + let output_aggregation_object_a = std::verify_proof( verification_key.as_slice(), proof.as_slice(), public_inputs.as_slice(), - key_hash + key_hash, + input_aggregation_object ); - std::verify_proof( + let output_aggregation_object = std::verify_proof( verification_key.as_slice(), proof_b.as_slice(), public_inputs.as_slice(), - key_hash + key_hash, + output_aggregation_object_a ); + + let mut output = [0; 16]; + for i in 0..16 { + output[i] = output_aggregation_object[i]; + } + output } ``` -You can see a full example of recursive proofs in [this example recursion demo repo](https://github.com/noir-lang/noir-examples/tree/master/recursion). - ## Parameters ### `verification_key` @@ -61,8 +68,23 @@ The proof for the zk program that is being verified. ### `public_inputs` -These represent the public inputs of the proof we are verifying. +These represent the public inputs of the proof we are verifying. They should be checked against in the circuit after construction of a new aggregation state. ### `key_hash` A key hash is used to check the validity of the verification key. The circuit implementing this opcode can use this hash to ensure that the key provided to the circuit matches the key produced by the circuit creator. + +### `input_aggregation_object` + +An aggregation object is blob of data that the top-level verifier must run some proof system specific algorithm on to complete verification. The size is proof system specific and will be set by the backend integrating this opcode. The input aggregation object is only not `None` when we are verifying a previous recursive aggregation in the current circuit. If this is the first recursive aggregation there is no input aggregation object. It is left to the backend to determine how to handle when there is no input aggregation object. + +## Return value + +### `output_aggregation_object` + +This is the result of a recursive aggregation and is what will be fed into the next verifier. +The next verifier can either perform a final verification (returning true or false) or perform another recursive aggregation where this output aggregation object will be the input aggregation object of the next recursive aggregation. + +## Example + +You can see an example of how to do recursive proofs in [this example recursion demo repo](https://github.com/noir-lang/noir-examples/tree/master/recursion). diff --git a/noir/docs/versioned_docs/version-v0.23.0/explainers/explainer-recursion.md b/noir/docs/versioned_docs/version-v0.23.0/explainers/explainer-recursion.md index 18846176ca7..8f992ec29fd 100644 --- a/noir/docs/versioned_docs/version-v0.23.0/explainers/explainer-recursion.md +++ b/noir/docs/versioned_docs/version-v0.23.0/explainers/explainer-recursion.md @@ -16,13 +16,12 @@ keywords: "Optimizing Computational Resources", "Improving Efficiency", "Verification Key", - "Aggregation", + "Aggregation Objects", "Recursive zkSNARK schemes", "PLONK", "Proving and Verification Keys" ] sidebar_position: 1 -pagination_next: how_to/how-to-recursion --- In programming, we tend to think of recursion as something calling itself. A classic example would be the calculation of the factorial of a number: @@ -65,7 +64,7 @@ So, they use zero-knowledge proofs. Alice tries to guess Bob's number, and Bob w This ZK proof can go on a smart contract, revealing the winner and even giving prizes. However, this means every turn needs to be verified on-chain. This incurs some cost and waiting time that may simply make the game too expensive or time-consuming to be worth it. -As a solution, Alice proposes the following: "what if Bob generates his proof, and instead of sending it on-chain, I verify it *within* my own proof before playing my own turn?". +As a solution, Alice proposes the following: "what if Bob generates his proof, and instead of sending it on-chain, I verify it *within* my own proof before playing my own turn?". She can then generate a proof that she verified his proof, and so on. @@ -117,19 +116,25 @@ As you can see in the [recursion reference](noir/standard_library/recursion.md), - The Verification Key of the circuit that generated the proof - A hash of this verification key, as it's needed for some backends - The public inputs for the proof +- The input aggregation object -:::info +It also returns the `output aggregation object`. These aggregation objects can be confusing at times, so let's dive in a little bit. + +### Aggregation objects Recursive zkSNARK schemes do not necessarily "verify a proof" in the sense that you expect a true or false to be spit out by the verifier. Rather an aggregation object is built over the public inputs. +In the case of PLONK the recursive aggregation object is two G1 points (expressed as 16 witness values). The final verifier (in our case this is most often the smart contract verifier) has to be aware of this aggregation object to execute a pairing and check the validity of these points. + So, taking the example of Alice and Bob and their guessing game: - Alice makes her guess. Her proof is *not* recursive: it doesn't verify any proof within it! It's just a standard `assert(x != y)` circuit -- Bob verifies Alice's proof and makes his own guess. In this circuit, he doesn't exactly *prove* the verification of Alice's proof. Instead, he *aggregates* his proof to Alice's proof. The actual verification is done when the full proof is verified, for example when using `nargo verify` or through the verifier smart contract. +- Bob verifies Alice's proof and makes his own guess. In this circuit, he is verifying a proof, so it needs to output an `aggregation object`: he is generating a recursive proof! +- Alice verifies Bob's *recursive proof*, and uses Bob's `output aggregation object` as the `input aggregation object` in her proof... Which in turn, generates another `output aggregation object`. -We can imagine recursive proofs a [relay race](https://en.wikipedia.org/wiki/Relay_race). The first runner doesn't have to receive the baton from anyone else, as he/she already starts with it. But when his/her turn is over, the next runner needs to receive it, run a bit more, and pass it along. Even though every runner could theoretically verify the baton mid-run (why not? 🏃🔍), only at the end of the race does the referee verify that the whole race is valid. +One should notice that when Bob generates his first proof, he has no input aggregation object. Because he is not verifying an recursive proof, he has no `input aggregation object`. In this case, he may use zeros instead. -::: +We can imagine the `aggregation object` as the baton in a [relay race](https://en.wikipedia.org/wiki/Relay_race). The first runner doesn't have to receive the baton from anyone else, as he/she already starts with it. But when his/her turn is over, the next runner needs to receive it, run a bit more, and pass it along. Even though every runner could theoretically verify the baton mid-run (why not? 🏃🔍), only at the end of the race does the referee verify that the whole race is valid. ## Some architecture @@ -170,7 +175,3 @@ In this example, a regulator could verify that taxes were paid for a specific pu At the time of writing, verifying recursive proofs is surprisingly fast. This is because most of the time is spent on generating the verification key that will be used to generate the next proof. So you are able to cache the verification key and reuse it later. Currently, Noir JS packages don't expose the functionality of loading proving and verification keys, but that feature exists in the underlying `bb.js` package. - -## How can I try it - -Learn more about using recursion in Nargo and NoirJS in the [how-to guide](../how_to/how-to-recursion.md) and see a full example in [noir-examples](https://github.com/noir-lang/noir-examples). diff --git a/noir/docs/versioned_docs/version-v0.23.0/how_to/how-to-recursion.md b/noir/docs/versioned_docs/version-v0.23.0/how_to/how-to-recursion.md index f34647a99d5..39db23f1f3a 100644 --- a/noir/docs/versioned_docs/version-v0.23.0/how_to/how-to-recursion.md +++ b/noir/docs/versioned_docs/version-v0.23.0/how_to/how-to-recursion.md @@ -108,7 +108,11 @@ This call takes the public inputs and the proof, but also the public inputs coun :::info -The `proofAsFields` has a constant size `[Field; 93]` and verification keys in Barretenberg are always `[Field; 114]`. +The `proofAsFields` has a constant size `[Field; 93]`. However, currently the backend doesn't remove the public inputs from the proof when converting it. + +This means that if your `main` circuit has two public inputs, then you should also modify the recursive circuit to accept a proof with the public inputs appended. This means that in our example, since `y` is a public input, our `proofAsFields` is of type `[Field; 94]`. + +Verification keys in Barretenberg are always of size 114. ::: @@ -132,6 +136,7 @@ const recursiveInputs = { proof: proofAsFields, // array of length 93 + size of public inputs publicInputs: [mainInput.y], // using the example above, where `y` is the only public input key_hash: vkHash, + input_aggregation_object: Array(16).fill(0) // this circuit is verifying a non-recursive proof, so there's no input aggregation object: just use zero } const { witness, returnValue } = noir.execute(recursiveInputs) // we're executing the recursive circuit now! @@ -139,7 +144,7 @@ const { proof, publicInputs } = backend.generateFinalProof(witness) const verified = backend.verifyFinalProof({ proof, publicInputs }) ``` -You can obviously chain this proof into another proof. In fact, if you're using recursive proofs, you're probably interested of using them this way! +You can obviously chain this proof into another proof. In fact, if you're using recursive proofs, you're probably interested of using them this way! In that case, you should keep in mind the `returnValue`, as it will contain the `input_aggregation_object` for the next proof. :::tip @@ -147,16 +152,16 @@ Managing circuits and "who does what" can be confusing. To make sure your naming ```js const circuits = { - main: mainJSON, - recursive: recursiveJSON +main: mainJSON, +recursive: recursiveJSON } const backends = { - main: new BarretenbergBackend(circuits.main), - recursive: new BarretenbergBackend(circuits.recursive) +main: new BarretenbergBackend(circuits.main), +recursive: new BarretenbergBackend(circuits.recursive) } const noir_programs = { - main: new Noir(circuits.main, backends.main), - recursive: new Noir(circuits.recursive, backends.recursive) +main: new Noir(circuits.main, backends.main), +recursive: new Noir(circuits.recursive, backends.recursive) } ``` diff --git a/noir/docs/versioned_docs/version-v0.23.0/noir/standard_library/recursion.md b/noir/docs/versioned_docs/version-v0.23.0/noir/standard_library/recursion.md index f252150c8b5..67962082a8f 100644 --- a/noir/docs/versioned_docs/version-v0.23.0/noir/standard_library/recursion.md +++ b/noir/docs/versioned_docs/version-v0.23.0/noir/standard_library/recursion.md @@ -1,16 +1,16 @@ --- title: Recursive Proofs description: Learn about how to write recursive proofs in Noir. -keywords: [recursion, recursive proofs, verification_key, verify_proof] +keywords: [recursion, recursive proofs, verification_key, aggregation object, verify_proof] --- Noir supports recursively verifying proofs, meaning you verify the proof of a Noir program in another Noir program. This enables creating proofs of arbitrary size by doing step-wise verification of smaller components of a large proof. -Read [the explainer on recursion](../../explainers/explainer-recursion.md) to know more about this function and the [guide on how to use it.](../../how_to/how-to-recursion.md) +The `verify_proof` function takes a verification key, proof and public inputs for a zk program, as well as a key hash and an input aggregation object. The key hash is used to check the validity of the verification key and the input aggregation object is required by some proving systems. The `verify_proof` function returns an output aggregation object that can then be fed into future iterations of the proof verification if required. ```rust #[foreign(verify_proof)] -fn verify_proof(_verification_key : [Field], _proof : [Field], _public_input : Field, _key_hash : Field) {} +fn verify_proof(_verification_key : [Field], _proof : [Field], _public_input : Field, _key_hash : Field, _input_aggregation_object : [Field]) -> [Field] {} ``` :::info @@ -26,29 +26,36 @@ use dep::std; fn main( verification_key : [Field; 114], - proof : [Field; 93], + proof : [Field; 94], public_inputs : [Field; 1], key_hash : Field, - proof_b : [Field; 93], -) { - std::verify_proof( + input_aggregation_object : [Field; 16], + proof_b : [Field; 94], +) -> pub [Field; 16] { + let output_aggregation_object_a = std::verify_proof( verification_key.as_slice(), proof.as_slice(), public_inputs.as_slice(), - key_hash + key_hash, + input_aggregation_object ); - std::verify_proof( + let output_aggregation_object = std::verify_proof( verification_key.as_slice(), proof_b.as_slice(), public_inputs.as_slice(), - key_hash + key_hash, + output_aggregation_object_a ); + + let mut output = [0; 16]; + for i in 0..16 { + output[i] = output_aggregation_object[i]; + } + output } ``` -You can see a full example of recursive proofs in [this example recursion demo repo](https://github.com/noir-lang/noir-examples/tree/master/recursion). - ## Parameters ### `verification_key` @@ -61,8 +68,23 @@ The proof for the zk program that is being verified. ### `public_inputs` -These represent the public inputs of the proof we are verifying. +These represent the public inputs of the proof we are verifying. They should be checked against in the circuit after construction of a new aggregation state. ### `key_hash` A key hash is used to check the validity of the verification key. The circuit implementing this opcode can use this hash to ensure that the key provided to the circuit matches the key produced by the circuit creator. + +### `input_aggregation_object` + +An aggregation object is blob of data that the top-level verifier must run some proof system specific algorithm on to complete verification. The size is proof system specific and will be set by the backend integrating this opcode. The input aggregation object is only not `None` when we are verifying a previous recursive aggregation in the current circuit. If this is the first recursive aggregation there is no input aggregation object. It is left to the backend to determine how to handle when there is no input aggregation object. + +## Return value + +### `output_aggregation_object` + +This is the result of a recursive aggregation and is what will be fed into the next verifier. +The next verifier can either perform a final verification (returning true or false) or perform another recursive aggregation where this output aggregation object will be the input aggregation object of the next recursive aggregation. + +## Example + +You can see an example of how to do recursive proofs in [this example recursion demo repo](https://github.com/noir-lang/noir-examples/tree/master/recursion). diff --git a/noir/noir_stdlib/src/array.nr b/noir/noir_stdlib/src/array.nr index 87cf4167dac..bcdf56dd7aa 100644 --- a/noir/noir_stdlib/src/array.nr +++ b/noir/noir_stdlib/src/array.nr @@ -3,10 +3,10 @@ // by the methods in the `slice` module impl [T; N] { #[builtin(array_len)] - pub fn len(self) -> Field {} + pub fn len(_self: Self) -> Field {} #[builtin(arraysort)] - pub fn sort(self) -> Self {} + pub fn sort(_self: Self) -> Self {} // Sort with a custom sorting function. pub fn sort_via(mut a: Self, ordering: fn[Env](T, T) -> bool) -> Self { diff --git a/noir/noir_stdlib/src/bigint.nr b/noir/noir_stdlib/src/bigint.nr index 9edd59359c1..14790f69241 100644 --- a/noir/noir_stdlib/src/bigint.nr +++ b/noir/noir_stdlib/src/bigint.nr @@ -7,21 +7,21 @@ struct BigInt { impl BigInt { #[builtin(bigint_add)] - pub fn bigint_add(self, other: BigInt) -> BigInt { + pub fn bigint_add(_self: Self, _other: BigInt) -> BigInt { } #[builtin(bigint_neg)] - pub fn bigint_neg(self, other: BigInt) -> BigInt { + pub fn bigint_neg(_self: Self, _other: BigInt) -> BigInt { } #[builtin(bigint_mul)] - pub fn bigint_mul(self, other: BigInt) -> BigInt { + pub fn bigint_mul(_self: Self, _other: BigInt) -> BigInt { } #[builtin(bigint_div)] - pub fn bigint_div(self, other: BigInt) -> BigInt { + pub fn bigint_div(_self: Self, _other: BigInt) -> BigInt { } #[builtin(bigint_from_le_bytes)] - pub fn from_le_bytes(bytes: [u8], modulus: [u8]) -> BigInt {} + pub fn from_le_bytes(_bytes: [u8], _modulus: [u8]) -> BigInt {} #[builtin(bigint_to_le_bytes)] - pub fn to_le_bytes(self) -> [u8] {} + pub fn to_le_bytes(_self: Self) -> [u8] {} } impl Add for BigInt { diff --git a/noir/noir_stdlib/src/ecdsa_secp256k1.nr b/noir/noir_stdlib/src/ecdsa_secp256k1.nr index e8d9af2230f..290ccba27e5 100644 --- a/noir/noir_stdlib/src/ecdsa_secp256k1.nr +++ b/noir/noir_stdlib/src/ecdsa_secp256k1.nr @@ -1,10 +1,10 @@ #[foreign(ecdsa_secp256k1)] // docs:start:ecdsa_secp256k1 pub fn verify_signature( - public_key_x: [u8; 32], - public_key_y: [u8; 32], - signature: [u8; 64], - message_hash: [u8; N] + _public_key_x: [u8; 32], + _public_key_y: [u8; 32], + _signature: [u8; 64], + _message_hash: [u8; N] ) -> bool // docs:end:ecdsa_secp256k1 -{} \ No newline at end of file +{} diff --git a/noir/noir_stdlib/src/ecdsa_secp256r1.nr b/noir/noir_stdlib/src/ecdsa_secp256r1.nr index 9fe932a2f3d..390f8ed39d2 100644 --- a/noir/noir_stdlib/src/ecdsa_secp256r1.nr +++ b/noir/noir_stdlib/src/ecdsa_secp256r1.nr @@ -1,10 +1,10 @@ #[foreign(ecdsa_secp256r1)] // docs:start:ecdsa_secp256r1 pub fn verify_signature( - public_key_x: [u8; 32], - public_key_y: [u8; 32], - signature: [u8; 64], - message_hash: [u8; N] + _public_key_x: [u8; 32], + _public_key_y: [u8; 32], + _signature: [u8; 64], + _message_hash: [u8; N] ) -> bool // docs:end:ecdsa_secp256r1 -{} \ No newline at end of file +{} diff --git a/noir/noir_stdlib/src/field.nr b/noir/noir_stdlib/src/field.nr index 66fb50119f9..fbd76a1e8a2 100644 --- a/noir/noir_stdlib/src/field.nr +++ b/noir/noir_stdlib/src/field.nr @@ -13,13 +13,13 @@ impl Field { } #[builtin(to_le_bits)] - fn __to_le_bits(self, _bit_size: u32) -> [u1] {} + fn __to_le_bits(_self: Self, _bit_size: u32) -> [u1] {} #[builtin(to_be_bits)] - fn __to_be_bits(self, bit_size: u32) -> [u1] {} + fn __to_be_bits(_self: Self, _bit_size: u32) -> [u1] {} #[builtin(apply_range_constraint)] - fn __assert_max_bit_size(self, bit_size: u32) {} + fn __assert_max_bit_size(_self: Self, _bit_size: u32) {} pub fn assert_max_bit_size(self: Self, bit_size: u32) { crate::assert_constant(bit_size); @@ -53,10 +53,10 @@ impl Field { // decompose `_self` into a `_result_len` vector over the `_radix` basis // `_radix` must be less than 256 #[builtin(to_le_radix)] - fn __to_le_radix(self, radix: u32, result_len: u32) -> [u8] {} + fn __to_le_radix(_self: Self, _radix: u32, _result_len: u32) -> [u8] {} #[builtin(to_be_radix)] - fn __to_be_radix(self, radix: u32, result_len: u32) -> [u8] {} + fn __to_be_radix(_self: Self, _radix: u32, _result_len: u32) -> [u8] {} // Returns self to the power of the given exponent value. diff --git a/noir/noir_stdlib/src/hash.nr b/noir/noir_stdlib/src/hash.nr index cc864039a90..4033e2a5365 100644 --- a/noir/noir_stdlib/src/hash.nr +++ b/noir/noir_stdlib/src/hash.nr @@ -3,19 +3,19 @@ mod mimc; #[foreign(sha256)] // docs:start:sha256 -pub fn sha256(input: [u8; N]) -> [u8; 32] +pub fn sha256(_input: [u8; N]) -> [u8; 32] // docs:end:sha256 {} #[foreign(blake2s)] // docs:start:blake2s -pub fn blake2s(input: [u8; N]) -> [u8; 32] +pub fn blake2s(_input: [u8; N]) -> [u8; 32] // docs:end:blake2s {} #[foreign(blake3)] // docs:start:blake3 -pub fn blake3(input: [u8; N]) -> [u8; 32] +pub fn blake3(_input: [u8; N]) -> [u8; 32] // docs:end:blake3 {} @@ -32,7 +32,7 @@ pub fn pedersen_commitment(input: [Field; N]) -> PedersenPoint } #[foreign(pedersen_commitment)] -pub fn __pedersen_commitment_with_separator(input: [Field; N], separator: u32) -> [Field; 2] {} +pub fn __pedersen_commitment_with_separator(_input: [Field; N], _separator: u32) -> [Field; 2] {} pub fn pedersen_commitment_with_separator(input: [Field; N], separator: u32) -> PedersenPoint { let values = __pedersen_commitment_with_separator(input, separator); @@ -47,13 +47,13 @@ pub fn pedersen_hash(input: [Field; N]) -> Field } #[foreign(pedersen_hash)] -pub fn pedersen_hash_with_separator(input: [Field; N], separator: u32) -> Field {} +pub fn pedersen_hash_with_separator(_input: [Field; N], _separator: u32) -> Field {} -pub fn hash_to_field(input: [Field; N]) -> Field { +pub fn hash_to_field(_input: [Field; N]) -> Field { let mut inputs_as_bytes = []; for i in 0..N { - let input_bytes = input[i].to_le_bytes(32); + let input_bytes = _input[i].to_le_bytes(32); for i in 0..32 { inputs_as_bytes = inputs_as_bytes.push_back(input_bytes[i]); } @@ -65,7 +65,7 @@ pub fn hash_to_field(input: [Field; N]) -> Field { #[foreign(keccak256)] // docs:start:keccak256 -pub fn keccak256(input: [u8; N], message_size: u32) -> [u8; 32] +pub fn keccak256(_input: [u8; N], _message_size: u32) -> [u8; 32] // docs:end:keccak256 {} diff --git a/noir/noir_stdlib/src/lib.nr b/noir/noir_stdlib/src/lib.nr index 5165d1ee07b..90aff3c312b 100644 --- a/noir/noir_stdlib/src/lib.nr +++ b/noir/noir_stdlib/src/lib.nr @@ -30,7 +30,7 @@ mod uint128; // Oracle calls are required to be wrapped in an unconstrained function // Thus, the only argument to the `println` oracle is expected to always be an ident #[oracle(print)] -unconstrained fn print_oracle(with_newline: bool, input: T) {} +unconstrained fn print_oracle(_with_newline: bool, _input: T) {} unconstrained pub fn print(input: T) { print_oracle(false, input); @@ -41,20 +41,20 @@ unconstrained pub fn println(input: T) { } #[foreign(recursive_aggregation)] -pub fn verify_proof(verification_key: [Field], proof: [Field], public_inputs: [Field], key_hash: Field) {} +pub fn verify_proof(_verification_key: [Field], _proof: [Field], _public_inputs: [Field], _key_hash: Field) {} // Asserts that the given value is known at compile-time. // Useful for debugging for-loop bounds. #[builtin(assert_constant)] -pub fn assert_constant(x: T) {} +pub fn assert_constant(_x: T) {} // from_field and as_field are private since they are not valid for every type. // `as` should be the default for users to cast between primitive types, and in the future // traits can be used to work with generic types. #[builtin(from_field)] -fn from_field(x: Field) -> T {} +fn from_field(_x: Field) -> T {} #[builtin(as_field)] -fn as_field(x: T) -> Field {} +fn as_field(_x: T) -> Field {} pub fn wrapping_add(x: T, y: T) -> T { crate::from_field(crate::as_field(x) + crate::as_field(y)) diff --git a/noir/noir_stdlib/src/scalar_mul.nr b/noir/noir_stdlib/src/scalar_mul.nr index 26378e4839a..0e84b4f66fc 100644 --- a/noir/noir_stdlib/src/scalar_mul.nr +++ b/noir/noir_stdlib/src/scalar_mul.nr @@ -26,8 +26,8 @@ impl Add for EmbeddedCurvePoint { #[foreign(fixed_base_scalar_mul)] // docs:start:fixed_base_embedded_curve pub fn fixed_base_embedded_curve( - low: Field, - high: Field + _low: Field, + _high: Field ) -> [Field; 2] // docs:end:fixed_base_embedded_curve {} diff --git a/noir/noir_stdlib/src/schnorr.nr b/noir/noir_stdlib/src/schnorr.nr index 33656254550..025c3a0f921 100644 --- a/noir/noir_stdlib/src/schnorr.nr +++ b/noir/noir_stdlib/src/schnorr.nr @@ -1,10 +1,10 @@ #[foreign(schnorr_verify)] // docs:start:schnorr_verify pub fn verify_signature( - public_key_x: Field, - public_key_y: Field, - signature: [u8; 64], - message: [u8; N] + _public_key_x: Field, + _public_key_y: Field, + _signature: [u8; 64], + _message: [u8; N] ) -> bool // docs:end:schnorr_verify -{} \ No newline at end of file +{} diff --git a/noir/noir_stdlib/src/slice.nr b/noir/noir_stdlib/src/slice.nr index aa4b73edc1a..a5a9a38ed53 100644 --- a/noir/noir_stdlib/src/slice.nr +++ b/noir/noir_stdlib/src/slice.nr @@ -3,34 +3,34 @@ impl [T] { /// new slice with a length one greater than the /// original unmodified slice. #[builtin(slice_push_back)] - pub fn push_back(self, elem: T) -> Self { } + pub fn push_back(_self: Self, _elem: T) -> Self { } /// Push a new element to the front of the slice, returning a /// new slice with a length one greater than the /// original unmodified slice. #[builtin(slice_push_front)] - pub fn push_front(self, elem: T) -> Self { } + pub fn push_front(_self: Self, _elem: T) -> Self { } /// Remove the last element of the slice, returning the /// popped slice and the element in a tuple #[builtin(slice_pop_back)] - pub fn pop_back(self) -> (Self, T) { } + pub fn pop_back(_self: Self) -> (Self, T) { } /// Remove the first element of the slice, returning the /// element and the popped slice in a tuple #[builtin(slice_pop_front)] - pub fn pop_front(self) -> (T, Self) { } + pub fn pop_front(_self: Self) -> (T, Self) { } /// Insert an element at a specified index, shifting all elements /// after it to the right #[builtin(slice_insert)] - pub fn insert(self, index: Field, elem: T) -> Self { } + pub fn insert(_self: Self, _index: Field, _elem: T) -> Self { } /// Remove an element at a specified index, shifting all elements /// after it to the left, returning the altered slice and /// the removed element #[builtin(slice_remove)] - pub fn remove(self, index: Field) -> (Self, T) { } + pub fn remove(_self: Self, _index: Field) -> (Self, T) { } // Append each element of the `other` slice to the end of `self`. // This returns a new slice and leaves both input slices unchanged. diff --git a/noir/noir_stdlib/src/string.nr b/noir/noir_stdlib/src/string.nr index ad6fd19e2de..e402abf9ab6 100644 --- a/noir/noir_stdlib/src/string.nr +++ b/noir/noir_stdlib/src/string.nr @@ -2,7 +2,7 @@ use crate::collections::vec::Vec; impl str { /// Converts the given string into a byte array #[builtin(str_as_bytes)] - pub fn as_bytes(self) -> [u8; N] { } + pub fn as_bytes(_self: Self) -> [u8; N] { } /// return a byte vector of the str content pub fn as_bytes_vec(self: Self) -> Vec { diff --git a/noir/noir_stdlib/src/test.nr b/noir/noir_stdlib/src/test.nr index 560cfde741c..47b31f4acea 100644 --- a/noir/noir_stdlib/src/test.nr +++ b/noir/noir_stdlib/src/test.nr @@ -1,17 +1,17 @@ #[oracle(create_mock)] -unconstrained fn create_mock_oracle(name: str) -> Field {} +unconstrained fn create_mock_oracle(_name: str) -> Field {} #[oracle(set_mock_params)] -unconstrained fn set_mock_params_oracle

(id: Field, params: P) {} +unconstrained fn set_mock_params_oracle

(_id: Field, _params: P) {} #[oracle(set_mock_returns)] -unconstrained fn set_mock_returns_oracle(id: Field, returns: R) {} +unconstrained fn set_mock_returns_oracle(_id: Field, _returns: R) {} #[oracle(set_mock_times)] -unconstrained fn set_mock_times_oracle(id: Field, times: u64) {} +unconstrained fn set_mock_times_oracle(_id: Field, _times: u64) {} #[oracle(clear_mock)] -unconstrained fn clear_mock_oracle(id: Field) {} +unconstrained fn clear_mock_oracle(_id: Field) {} struct OracleMock { id: Field, diff --git a/noir/scripts/bootstrap_native.sh b/noir/scripts/bootstrap_native.sh index 974f0edcfec..3e0e2ed853a 100755 --- a/noir/scripts/bootstrap_native.sh +++ b/noir/scripts/bootstrap_native.sh @@ -12,12 +12,6 @@ else export GIT_COMMIT=$(git rev-parse --verify HEAD) fi -# Check if the 'cargo' command is available in the system -if ! command -v cargo > /dev/null; then - echo "Cargo is not installed. Please install Cargo and the Rust toolchain." - exit 1 -fi - # Build native. if [ -n "${DEBUG:-}" ]; then cargo build diff --git a/yarn-project/pxe/src/contract_tree/index.ts b/yarn-project/pxe/src/contract_tree/index.ts deleted file mode 100644 index 9bb88fc1905..00000000000 --- a/yarn-project/pxe/src/contract_tree/index.ts +++ /dev/null @@ -1,183 +0,0 @@ -import { ContractDao, MerkleTreeId, StateInfoProvider } from '@aztec/circuit-types'; -import { - CONTRACT_TREE_HEIGHT, - FUNCTION_TREE_HEIGHT, - Fr, - MembershipWitness, - NewContractConstructor, - NewContractData, - computeFunctionTreeData, - generateFunctionLeaves, - getContractClassFromArtifact, - isConstrained, -} from '@aztec/circuits.js'; -import { computeContractLeaf, computeFunctionTree, computeFunctionTreeRoot } from '@aztec/circuits.js/abis'; -import { FunctionSelector } from '@aztec/foundation/abi'; -import { assertLength } from '@aztec/foundation/serialize'; - -/** - * The ContractTree class represents a Merkle tree of functions for a particular contract. - * It manages the construction of the function tree, computes its root, and generates membership witnesses - * for constrained functions. This class also enables lookup of specific function artifact using selectors. - * It is used in combination with the AztecNode to compute various data for executing private transactions. - */ -export class ContractTree { - private functionLeaves?: Fr[]; - private functionTree?: Fr[]; - private functionTreeRoot?: Fr; - private contractIndex?: bigint; - private contractClassId?: Fr; - - constructor( - /** - * The contract data object containing the artifact and contract address. - */ - public readonly contract: ContractDao, - private stateInfoProvider: StateInfoProvider, - /** - * Data associated with the contract constructor for a new contract. - */ - public readonly newContractConstructor?: NewContractConstructor, - ) {} - - /** - * Retrieve the artifact of a given function. - * The function is identified by its selector, which represents a unique identifier for the function's signature. - * Throws an error if the function with the provided selector is not found in the contract. - * - * @param selector - The function selector. - * @returns The artifact object containing relevant information about the targeted function. - */ - public getFunctionArtifact(selector: FunctionSelector) { - const artifact = this.contract.functions.find(f => f.selector.equals(selector)); - if (!artifact) { - throw new Error( - `Unknown function. Selector ${selector.toString()} not found in the artifact of contract ${this.contract.instance.address.toString()}. Expected one of: ${this.contract.functions - .map(f => f.selector.toString()) - .join(', ')}`, - ); - } - return artifact; - } - - /** - * Retrieve the bytecode of a function in the contract by its function selector. - * The function selector is a unique identifier for each function in a contract. - * Throws an error if the function with the given selector is not found in the contract. - * - * @param selector - The selector of a function to get bytecode for. - * @returns The bytecode of the function as a string. - */ - public getBytecode(selector: FunctionSelector) { - return this.getFunctionArtifact(selector).bytecode; - } - - /** - * Retrieves the contract membership witness for the current contract tree instance. - * The contract membership witness is a proof that demonstrates the existence of the contract - * in the global contract merkle tree. This proof contains the index of the contract's leaf - * in the tree and the sibling path needed to construct the root of the merkle tree. - * If the witness hasn't been previously computed, this function will request the contract node - * to find the contract's index and path in order to create the membership witness. - * - * @param blockNumber - The block number at which to get the data. - * - * @returns A Promise that resolves to the MembershipWitness object for the given contract tree. - */ - public async getContractMembershipWitness(blockNumber: number | 'latest' = 'latest') { - const index = await this.getContractIndex(); - - const siblingPath = await this.stateInfoProvider.getContractSiblingPath(blockNumber, index); - return new MembershipWitness( - CONTRACT_TREE_HEIGHT, - index, - assertLength(siblingPath.toFieldArray(), CONTRACT_TREE_HEIGHT), - ); - } - - /** - * Calculate and return the root of the function tree for the current contract. - * This root is a cryptographic commitment to the set of constrained functions within the contract, - * which is used in the Aztec node's proof system. The root will be cached after the first call. - * - * @returns A promise that resolves to the Fr (finite field element) representation of the function tree root. - */ - public getFunctionTreeRoot() { - if (!this.functionTreeRoot) { - const leaves = this.getFunctionLeaves(); - this.functionTreeRoot = computeFunctionTreeRoot(leaves); - } - return Promise.resolve(this.functionTreeRoot); - } - - /** - * Returns the contract class identifier for the given artifact. - */ - public getContractClassId() { - if (!this.contractClassId) { - this.contractClassId = getContractClassFromArtifact(this.contract).id; - } - return this.contractClassId; - } - - /** - * Retrieve the membership witness of a function within a contract's function tree. - * A membership witness represents the position and authentication path of a target function - * in the Merkle tree of constrained functions. It is required to prove the existence of the - * function within the contract during execution. - * - * @param selector - The function selector. - * @returns A MembershipWitness instance representing the position and authentication path of the function in the function tree. - */ - public getFunctionMembershipWitness( - selector: FunctionSelector, - ): Promise> { - const targetFunctions = this.contract.functions.filter(isConstrained); - const functionIndex = targetFunctions.findIndex(f => f.selector.equals(selector)); - if (functionIndex < 0) { - return Promise.resolve(MembershipWitness.empty(FUNCTION_TREE_HEIGHT, 0n)); - } - - if (!this.functionTree) { - const leaves = this.getFunctionLeaves(); - this.functionTree = computeFunctionTree(leaves); - } - const functionTreeData = computeFunctionTreeData(this.functionTree, functionIndex); - return Promise.resolve( - new MembershipWitness( - FUNCTION_TREE_HEIGHT, - BigInt(functionIndex), - assertLength(functionTreeData.siblingPath, FUNCTION_TREE_HEIGHT), - ), - ); - } - - /** - * Retrieve the function leaves for the contract tree. - * Function leaves are computed based on constrained functions present in the contract. - * It caches the computed function leaves and returns them if already calculated. - * - * @returns An array of Fr representing the function leaves. - */ - private getFunctionLeaves() { - if (!this.functionLeaves) { - this.functionLeaves = generateFunctionLeaves(this.contract.functions); - } - return this.functionLeaves; - } - - private async getContractIndex() { - if (this.contractIndex === undefined) { - const { address, portalContractAddress } = this.contract.instance; - const contractClassId = this.getContractClassId(); - const newContractData = new NewContractData(address, portalContractAddress, contractClassId); - const commitment = computeContractLeaf(newContractData); - this.contractIndex = await this.stateInfoProvider.findLeafIndex('latest', MerkleTreeId.CONTRACT_TREE, commitment); - if (this.contractIndex === undefined) { - throw new Error(`Failed to find contract at ${address.toString()} resulting in commitment ${commitment}.`); - } - return this.contractIndex; - } - return this.contractIndex; - } -} diff --git a/yarn.lock b/yarn.lock deleted file mode 100644 index fb57ccd13af..00000000000 --- a/yarn.lock +++ /dev/null @@ -1,4 +0,0 @@ -# THIS IS AN AUTOGENERATED FILE. DO NOT EDIT THIS FILE DIRECTLY. -# yarn lockfile v1 - - From 93cc27f504344af8a8520b81f7c806ffe29a4465 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Jos=C3=A9=20Pedro=20Sousa?= Date: Thu, 1 Feb 2024 12:35:30 +0000 Subject: [PATCH 10/16] wip --- barretenberg/.gitrepo | 4 ++-- .../src/barretenberg/ecc/fields/field_conversion.hpp | 2 +- .../barretenberg/ecc/fields/field_conversion.test.cpp | 11 +---------- 3 files changed, 4 insertions(+), 13 deletions(-) diff --git a/barretenberg/.gitrepo b/barretenberg/.gitrepo index dee34a9f1dd..ca8c2516afd 100644 --- a/barretenberg/.gitrepo +++ b/barretenberg/.gitrepo @@ -6,7 +6,7 @@ [subrepo] remote = https://github.com/AztecProtocol/barretenberg branch = master - commit = 095410c56b5a2275756f5a123685bce85d3ae779 - parent = cdf1baf017c4833bc621ba4dd3681dd1a745e259 + commit = d041c3e8e59132328e0171fb72d61ea50936ee48 + parent = 09d0730bad4be2f4954cbb6d27538f7860d0f21f method = merge cmdver = 0.4.6 diff --git a/barretenberg/cpp/src/barretenberg/ecc/fields/field_conversion.hpp b/barretenberg/cpp/src/barretenberg/ecc/fields/field_conversion.hpp index c2298236512..cf5b12d1def 100644 --- a/barretenberg/cpp/src/barretenberg/ecc/fields/field_conversion.hpp +++ b/barretenberg/cpp/src/barretenberg/ecc/fields/field_conversion.hpp @@ -208,4 +208,4 @@ template std::vector inline convert_to_bn254_frs(co return fr_vec; } -} // namespace bb::field_conversion \ No newline at end of file +} // namespace bb::field_conversion diff --git a/barretenberg/cpp/src/barretenberg/ecc/fields/field_conversion.test.cpp b/barretenberg/cpp/src/barretenberg/ecc/fields/field_conversion.test.cpp index d54ca02ba4b..0a024f82545 100644 --- a/barretenberg/cpp/src/barretenberg/ecc/fields/field_conversion.test.cpp +++ b/barretenberg/cpp/src/barretenberg/ecc/fields/field_conversion.test.cpp @@ -15,15 +15,6 @@ class FieldConversionTest : public ::testing::Test { } }; -/** - * @brief Field conversion test for size_t - */ -TEST_F(FieldConversionTest, FieldConversionSizeT) -{ - size_t x = 210849; - check_conversion(x); -} - /** * @brief Field conversion test for uint32_t */ @@ -140,4 +131,4 @@ TEST_F(FieldConversionTest, FieldConversionUnivariateGrumpkinFr) check_conversion(x1); } -} // namespace bb::field_conversion_tests \ No newline at end of file +} // namespace bb::field_conversion_tests From c72a23a83724b5634a79d0576c35f65120c18618 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Jos=C3=A9=20Pedro=20Sousa?= Date: Thu, 1 Feb 2024 12:36:03 +0000 Subject: [PATCH 11/16] wip --- .../explainers/explainer-oracle.md | 57 --- .../explainers/explainer-recursion.md | 176 -------- .../getting_started/_category_.json | 5 - .../hello_noir/_category_.json | 5 - .../getting_started/hello_noir/index.md | 142 ------ .../hello_noir/project_breakdown.md | 199 --------- .../installation/_category_.json | 6 - .../getting_started/installation/index.md | 48 --- .../installation/other_install_methods.md | 190 -------- .../getting_started/tooling/_category_.json | 6 - .../getting_started/tooling/index.mdx | 38 -- .../tooling/language_server.md | 43 -- .../getting_started/tooling/testing.md | 62 --- .../how_to/_category_.json | 5 - .../how_to/how-to-oracles.md | 280 ------------ .../how_to/how-to-recursion.md | 179 -------- .../how_to/how-to-solidity-verifier.md | 231 ---------- .../how_to/merkle-proof.mdx | 48 --- .../how_to/using-devcontainers.mdx | 110 ----- noir/docs/processed-docs-cache/index.mdx | 67 --- .../processed-docs-cache/migration_notes.md | 91 ---- .../noir/concepts/_category_.json | 6 - .../noir/concepts/assert.md | 27 -- .../noir/concepts/comments.md | 33 -- .../noir/concepts/control_flow.md | 45 -- .../noir/concepts/data_bus.md | 21 - .../noir/concepts/data_types/_category_.json | 5 - .../noir/concepts/data_types/arrays.md | 251 ----------- .../noir/concepts/data_types/booleans.md | 31 -- .../noir/concepts/data_types/fields.md | 183 -------- .../concepts/data_types/function_types.md | 26 -- .../noir/concepts/data_types/index.md | 96 ----- .../noir/concepts/data_types/integers.md | 113 ----- .../noir/concepts/data_types/references.md | 23 - .../noir/concepts/data_types/slices.mdx | 147 ------- .../noir/concepts/data_types/strings.md | 80 ---- .../noir/concepts/data_types/structs.md | 70 --- .../noir/concepts/data_types/tuples.md | 48 --- .../noir/concepts/data_types/vectors.mdx | 171 -------- .../noir/concepts/distinct.md | 64 --- .../noir/concepts/functions.md | 226 ---------- .../noir/concepts/generics.md | 106 ----- .../noir/concepts/lambdas.md | 81 ---- .../noir/concepts/mutability.md | 93 ---- .../processed-docs-cache/noir/concepts/ops.md | 98 ----- .../noir/concepts/oracles.md | 23 - .../noir/concepts/shadowing.md | 44 -- .../noir/concepts/traits.md | 389 ----------------- .../noir/concepts/unconstrained.md | 95 ---- .../modules_packages_crates/_category_.json | 6 - .../crates_and_packages.md | 43 -- .../modules_packages_crates/dependencies.md | 124 ------ .../noir/modules_packages_crates/modules.md | 105 ----- .../modules_packages_crates/workspaces.md | 40 -- .../noir/standard_library/_category_.json | 6 - .../noir/standard_library/black_box_fns.md | 31 -- .../cryptographic_primitives/_category_.json | 5 - .../cryptographic_primitives/ec_primitives.md | 102 ----- .../ecdsa_sig_verification.mdx | 60 --- .../cryptographic_primitives/eddsa.mdx | 18 - .../cryptographic_primitives/hashes.mdx | 234 ---------- .../cryptographic_primitives/index.md | 14 - .../cryptographic_primitives/scalar.mdx | 33 -- .../cryptographic_primitives/schnorr.mdx | 45 -- .../noir/standard_library/logging.md | 78 ---- .../noir/standard_library/merkle_trees.md | 58 --- .../noir/standard_library/options.md | 97 ----- .../noir/standard_library/recursion.md | 68 --- .../noir/standard_library/traits.md | 408 ------------------ .../noir/standard_library/zeroed.md | 25 -- .../reference/_category_.json | 5 - .../reference/nargo_commands.md | 253 ----------- .../tutorials/noirjs_app.md | 279 ------------ .../explainers/explainer-oracle.md | 57 --- .../explainers/explainer-recursion.md | 176 -------- .../getting_started/_category_.json | 5 - .../hello_noir/_category_.json | 5 - .../getting_started/hello_noir/index.md | 142 ------ .../hello_noir/project_breakdown.md | 199 --------- .../installation/_category_.json | 6 - .../getting_started/installation/index.md | 48 --- .../installation/other_install_methods.md | 190 -------- .../getting_started/tooling/_category_.json | 6 - .../getting_started/tooling/index.mdx | 38 -- .../tooling/language_server.md | 43 -- .../getting_started/tooling/testing.md | 62 --- .../processed-docs/how_to/_category_.json | 5 - .../processed-docs/how_to/how-to-oracles.md | 280 ------------ .../processed-docs/how_to/how-to-recursion.md | 179 -------- .../how_to/how-to-solidity-verifier.md | 231 ---------- .../processed-docs/how_to/merkle-proof.mdx | 48 --- .../how_to/using-devcontainers.mdx | 110 ----- noir/docs/processed-docs/index.mdx | 67 --- noir/docs/processed-docs/migration_notes.md | 91 ---- .../noir/concepts/_category_.json | 6 - .../processed-docs/noir/concepts/assert.md | 27 -- .../processed-docs/noir/concepts/comments.md | 33 -- .../noir/concepts/control_flow.md | 45 -- .../processed-docs/noir/concepts/data_bus.md | 21 - .../noir/concepts/data_types/_category_.json | 5 - .../noir/concepts/data_types/arrays.md | 251 ----------- .../noir/concepts/data_types/booleans.md | 31 -- .../noir/concepts/data_types/fields.md | 183 -------- .../concepts/data_types/function_types.md | 26 -- .../noir/concepts/data_types/index.md | 96 ----- .../noir/concepts/data_types/integers.md | 113 ----- .../noir/concepts/data_types/references.md | 23 - .../noir/concepts/data_types/slices.mdx | 147 ------- .../noir/concepts/data_types/strings.md | 80 ---- .../noir/concepts/data_types/structs.md | 70 --- .../noir/concepts/data_types/tuples.md | 48 --- .../noir/concepts/data_types/vectors.mdx | 171 -------- .../processed-docs/noir/concepts/distinct.md | 64 --- .../processed-docs/noir/concepts/functions.md | 226 ---------- .../processed-docs/noir/concepts/generics.md | 106 ----- .../processed-docs/noir/concepts/lambdas.md | 81 ---- .../noir/concepts/mutability.md | 93 ---- noir/docs/processed-docs/noir/concepts/ops.md | 98 ----- .../processed-docs/noir/concepts/oracles.md | 23 - .../processed-docs/noir/concepts/shadowing.md | 44 -- .../processed-docs/noir/concepts/traits.md | 389 ----------------- .../noir/concepts/unconstrained.md | 95 ---- .../modules_packages_crates/_category_.json | 6 - .../crates_and_packages.md | 43 -- .../modules_packages_crates/dependencies.md | 124 ------ .../noir/modules_packages_crates/modules.md | 105 ----- .../modules_packages_crates/workspaces.md | 40 -- .../noir/standard_library/_category_.json | 6 - .../noir/standard_library/black_box_fns.md | 31 -- .../cryptographic_primitives/_category_.json | 5 - .../cryptographic_primitives/ec_primitives.md | 102 ----- .../ecdsa_sig_verification.mdx | 60 --- .../cryptographic_primitives/eddsa.mdx | 18 - .../cryptographic_primitives/hashes.mdx | 234 ---------- .../cryptographic_primitives/index.md | 14 - .../cryptographic_primitives/scalar.mdx | 33 -- .../cryptographic_primitives/schnorr.mdx | 45 -- .../noir/standard_library/logging.md | 78 ---- .../noir/standard_library/merkle_trees.md | 58 --- .../noir/standard_library/options.md | 97 ----- .../noir/standard_library/recursion.md | 68 --- .../noir/standard_library/traits.md | 408 ------------------ .../noir/standard_library/zeroed.md | 25 -- .../NoirJS/backend_barretenberg/.nojekyll | 1 - .../classes/BarretenbergBackend.md | 202 --------- .../NoirJS/backend_barretenberg/index.md | 46 -- .../interfaces/Backend.md | 132 ------ .../type-aliases/BackendOptions.md | 19 - .../type-aliases/CompiledCircuit.md | 20 - .../type-aliases/ProofData.md | 20 - .../backend_barretenberg/typedoc-sidebar.cjs | 4 - .../reference/NoirJS/noir_js/.nojekyll | 1 - .../reference/NoirJS/noir_js/classes/Noir.md | 132 ------ .../reference/NoirJS/noir_js/functions/and.md | 22 - .../NoirJS/noir_js/functions/blake2s256.md | 21 - .../functions/ecdsa_secp256k1_verify.md | 28 -- .../functions/ecdsa_secp256r1_verify.md | 28 -- .../NoirJS/noir_js/functions/keccak256.md | 21 - .../NoirJS/noir_js/functions/sha256.md | 21 - .../reference/NoirJS/noir_js/functions/xor.md | 22 - .../reference/NoirJS/noir_js/index.md | 37 -- .../noir_js/type-aliases/CompiledCircuit.md | 20 - .../type-aliases/ForeignCallHandler.md | 24 -- .../noir_js/type-aliases/ForeignCallInput.md | 9 - .../noir_js/type-aliases/ForeignCallOutput.md | 9 - .../NoirJS/noir_js/type-aliases/InputMap.md | 13 - .../NoirJS/noir_js/type-aliases/ProofData.md | 20 - .../NoirJS/noir_js/type-aliases/WitnessMap.md | 9 - .../NoirJS/noir_js/typedoc-sidebar.cjs | 4 - .../processed-docs/reference/_category_.json | 5 - .../reference/nargo_commands.md | 253 ----------- .../processed-docs/tutorials/noirjs_app.md | 279 ------------ 172 files changed, 14325 deletions(-) delete mode 100644 noir/docs/processed-docs-cache/explainers/explainer-oracle.md delete mode 100644 noir/docs/processed-docs-cache/explainers/explainer-recursion.md delete mode 100644 noir/docs/processed-docs-cache/getting_started/_category_.json delete mode 100644 noir/docs/processed-docs-cache/getting_started/hello_noir/_category_.json delete mode 100644 noir/docs/processed-docs-cache/getting_started/hello_noir/index.md delete mode 100644 noir/docs/processed-docs-cache/getting_started/hello_noir/project_breakdown.md delete mode 100644 noir/docs/processed-docs-cache/getting_started/installation/_category_.json delete mode 100644 noir/docs/processed-docs-cache/getting_started/installation/index.md delete mode 100644 noir/docs/processed-docs-cache/getting_started/installation/other_install_methods.md delete mode 100644 noir/docs/processed-docs-cache/getting_started/tooling/_category_.json delete mode 100644 noir/docs/processed-docs-cache/getting_started/tooling/index.mdx delete mode 100644 noir/docs/processed-docs-cache/getting_started/tooling/language_server.md delete mode 100644 noir/docs/processed-docs-cache/getting_started/tooling/testing.md delete mode 100644 noir/docs/processed-docs-cache/how_to/_category_.json delete mode 100644 noir/docs/processed-docs-cache/how_to/how-to-oracles.md delete mode 100644 noir/docs/processed-docs-cache/how_to/how-to-recursion.md delete mode 100644 noir/docs/processed-docs-cache/how_to/how-to-solidity-verifier.md delete mode 100644 noir/docs/processed-docs-cache/how_to/merkle-proof.mdx delete mode 100644 noir/docs/processed-docs-cache/how_to/using-devcontainers.mdx delete mode 100644 noir/docs/processed-docs-cache/index.mdx delete mode 100644 noir/docs/processed-docs-cache/migration_notes.md delete mode 100644 noir/docs/processed-docs-cache/noir/concepts/_category_.json delete mode 100644 noir/docs/processed-docs-cache/noir/concepts/assert.md delete mode 100644 noir/docs/processed-docs-cache/noir/concepts/comments.md delete mode 100644 noir/docs/processed-docs-cache/noir/concepts/control_flow.md delete mode 100644 noir/docs/processed-docs-cache/noir/concepts/data_bus.md delete mode 100644 noir/docs/processed-docs-cache/noir/concepts/data_types/_category_.json delete mode 100644 noir/docs/processed-docs-cache/noir/concepts/data_types/arrays.md delete mode 100644 noir/docs/processed-docs-cache/noir/concepts/data_types/booleans.md delete mode 100644 noir/docs/processed-docs-cache/noir/concepts/data_types/fields.md delete mode 100644 noir/docs/processed-docs-cache/noir/concepts/data_types/function_types.md delete mode 100644 noir/docs/processed-docs-cache/noir/concepts/data_types/index.md delete mode 100644 noir/docs/processed-docs-cache/noir/concepts/data_types/integers.md delete mode 100644 noir/docs/processed-docs-cache/noir/concepts/data_types/references.md delete mode 100644 noir/docs/processed-docs-cache/noir/concepts/data_types/slices.mdx delete mode 100644 noir/docs/processed-docs-cache/noir/concepts/data_types/strings.md delete mode 100644 noir/docs/processed-docs-cache/noir/concepts/data_types/structs.md delete mode 100644 noir/docs/processed-docs-cache/noir/concepts/data_types/tuples.md delete mode 100644 noir/docs/processed-docs-cache/noir/concepts/data_types/vectors.mdx delete mode 100644 noir/docs/processed-docs-cache/noir/concepts/distinct.md delete mode 100644 noir/docs/processed-docs-cache/noir/concepts/functions.md delete mode 100644 noir/docs/processed-docs-cache/noir/concepts/generics.md delete mode 100644 noir/docs/processed-docs-cache/noir/concepts/lambdas.md delete mode 100644 noir/docs/processed-docs-cache/noir/concepts/mutability.md delete mode 100644 noir/docs/processed-docs-cache/noir/concepts/ops.md delete mode 100644 noir/docs/processed-docs-cache/noir/concepts/oracles.md delete mode 100644 noir/docs/processed-docs-cache/noir/concepts/shadowing.md delete mode 100644 noir/docs/processed-docs-cache/noir/concepts/traits.md delete mode 100644 noir/docs/processed-docs-cache/noir/concepts/unconstrained.md delete mode 100644 noir/docs/processed-docs-cache/noir/modules_packages_crates/_category_.json delete mode 100644 noir/docs/processed-docs-cache/noir/modules_packages_crates/crates_and_packages.md delete mode 100644 noir/docs/processed-docs-cache/noir/modules_packages_crates/dependencies.md delete mode 100644 noir/docs/processed-docs-cache/noir/modules_packages_crates/modules.md delete mode 100644 noir/docs/processed-docs-cache/noir/modules_packages_crates/workspaces.md delete mode 100644 noir/docs/processed-docs-cache/noir/standard_library/_category_.json delete mode 100644 noir/docs/processed-docs-cache/noir/standard_library/black_box_fns.md delete mode 100644 noir/docs/processed-docs-cache/noir/standard_library/cryptographic_primitives/_category_.json delete mode 100644 noir/docs/processed-docs-cache/noir/standard_library/cryptographic_primitives/ec_primitives.md delete mode 100644 noir/docs/processed-docs-cache/noir/standard_library/cryptographic_primitives/ecdsa_sig_verification.mdx delete mode 100644 noir/docs/processed-docs-cache/noir/standard_library/cryptographic_primitives/eddsa.mdx delete mode 100644 noir/docs/processed-docs-cache/noir/standard_library/cryptographic_primitives/hashes.mdx delete mode 100644 noir/docs/processed-docs-cache/noir/standard_library/cryptographic_primitives/index.md delete mode 100644 noir/docs/processed-docs-cache/noir/standard_library/cryptographic_primitives/scalar.mdx delete mode 100644 noir/docs/processed-docs-cache/noir/standard_library/cryptographic_primitives/schnorr.mdx delete mode 100644 noir/docs/processed-docs-cache/noir/standard_library/logging.md delete mode 100644 noir/docs/processed-docs-cache/noir/standard_library/merkle_trees.md delete mode 100644 noir/docs/processed-docs-cache/noir/standard_library/options.md delete mode 100644 noir/docs/processed-docs-cache/noir/standard_library/recursion.md delete mode 100644 noir/docs/processed-docs-cache/noir/standard_library/traits.md delete mode 100644 noir/docs/processed-docs-cache/noir/standard_library/zeroed.md delete mode 100644 noir/docs/processed-docs-cache/reference/_category_.json delete mode 100644 noir/docs/processed-docs-cache/reference/nargo_commands.md delete mode 100644 noir/docs/processed-docs-cache/tutorials/noirjs_app.md delete mode 100644 noir/docs/processed-docs/explainers/explainer-oracle.md delete mode 100644 noir/docs/processed-docs/explainers/explainer-recursion.md delete mode 100644 noir/docs/processed-docs/getting_started/_category_.json delete mode 100644 noir/docs/processed-docs/getting_started/hello_noir/_category_.json delete mode 100644 noir/docs/processed-docs/getting_started/hello_noir/index.md delete mode 100644 noir/docs/processed-docs/getting_started/hello_noir/project_breakdown.md delete mode 100644 noir/docs/processed-docs/getting_started/installation/_category_.json delete mode 100644 noir/docs/processed-docs/getting_started/installation/index.md delete mode 100644 noir/docs/processed-docs/getting_started/installation/other_install_methods.md delete mode 100644 noir/docs/processed-docs/getting_started/tooling/_category_.json delete mode 100644 noir/docs/processed-docs/getting_started/tooling/index.mdx delete mode 100644 noir/docs/processed-docs/getting_started/tooling/language_server.md delete mode 100644 noir/docs/processed-docs/getting_started/tooling/testing.md delete mode 100644 noir/docs/processed-docs/how_to/_category_.json delete mode 100644 noir/docs/processed-docs/how_to/how-to-oracles.md delete mode 100644 noir/docs/processed-docs/how_to/how-to-recursion.md delete mode 100644 noir/docs/processed-docs/how_to/how-to-solidity-verifier.md delete mode 100644 noir/docs/processed-docs/how_to/merkle-proof.mdx delete mode 100644 noir/docs/processed-docs/how_to/using-devcontainers.mdx delete mode 100644 noir/docs/processed-docs/index.mdx delete mode 100644 noir/docs/processed-docs/migration_notes.md delete mode 100644 noir/docs/processed-docs/noir/concepts/_category_.json delete mode 100644 noir/docs/processed-docs/noir/concepts/assert.md delete mode 100644 noir/docs/processed-docs/noir/concepts/comments.md delete mode 100644 noir/docs/processed-docs/noir/concepts/control_flow.md delete mode 100644 noir/docs/processed-docs/noir/concepts/data_bus.md delete mode 100644 noir/docs/processed-docs/noir/concepts/data_types/_category_.json delete mode 100644 noir/docs/processed-docs/noir/concepts/data_types/arrays.md delete mode 100644 noir/docs/processed-docs/noir/concepts/data_types/booleans.md delete mode 100644 noir/docs/processed-docs/noir/concepts/data_types/fields.md delete mode 100644 noir/docs/processed-docs/noir/concepts/data_types/function_types.md delete mode 100644 noir/docs/processed-docs/noir/concepts/data_types/index.md delete mode 100644 noir/docs/processed-docs/noir/concepts/data_types/integers.md delete mode 100644 noir/docs/processed-docs/noir/concepts/data_types/references.md delete mode 100644 noir/docs/processed-docs/noir/concepts/data_types/slices.mdx delete mode 100644 noir/docs/processed-docs/noir/concepts/data_types/strings.md delete mode 100644 noir/docs/processed-docs/noir/concepts/data_types/structs.md delete mode 100644 noir/docs/processed-docs/noir/concepts/data_types/tuples.md delete mode 100644 noir/docs/processed-docs/noir/concepts/data_types/vectors.mdx delete mode 100644 noir/docs/processed-docs/noir/concepts/distinct.md delete mode 100644 noir/docs/processed-docs/noir/concepts/functions.md delete mode 100644 noir/docs/processed-docs/noir/concepts/generics.md delete mode 100644 noir/docs/processed-docs/noir/concepts/lambdas.md delete mode 100644 noir/docs/processed-docs/noir/concepts/mutability.md delete mode 100644 noir/docs/processed-docs/noir/concepts/ops.md delete mode 100644 noir/docs/processed-docs/noir/concepts/oracles.md delete mode 100644 noir/docs/processed-docs/noir/concepts/shadowing.md delete mode 100644 noir/docs/processed-docs/noir/concepts/traits.md delete mode 100644 noir/docs/processed-docs/noir/concepts/unconstrained.md delete mode 100644 noir/docs/processed-docs/noir/modules_packages_crates/_category_.json delete mode 100644 noir/docs/processed-docs/noir/modules_packages_crates/crates_and_packages.md delete mode 100644 noir/docs/processed-docs/noir/modules_packages_crates/dependencies.md delete mode 100644 noir/docs/processed-docs/noir/modules_packages_crates/modules.md delete mode 100644 noir/docs/processed-docs/noir/modules_packages_crates/workspaces.md delete mode 100644 noir/docs/processed-docs/noir/standard_library/_category_.json delete mode 100644 noir/docs/processed-docs/noir/standard_library/black_box_fns.md delete mode 100644 noir/docs/processed-docs/noir/standard_library/cryptographic_primitives/_category_.json delete mode 100644 noir/docs/processed-docs/noir/standard_library/cryptographic_primitives/ec_primitives.md delete mode 100644 noir/docs/processed-docs/noir/standard_library/cryptographic_primitives/ecdsa_sig_verification.mdx delete mode 100644 noir/docs/processed-docs/noir/standard_library/cryptographic_primitives/eddsa.mdx delete mode 100644 noir/docs/processed-docs/noir/standard_library/cryptographic_primitives/hashes.mdx delete mode 100644 noir/docs/processed-docs/noir/standard_library/cryptographic_primitives/index.md delete mode 100644 noir/docs/processed-docs/noir/standard_library/cryptographic_primitives/scalar.mdx delete mode 100644 noir/docs/processed-docs/noir/standard_library/cryptographic_primitives/schnorr.mdx delete mode 100644 noir/docs/processed-docs/noir/standard_library/logging.md delete mode 100644 noir/docs/processed-docs/noir/standard_library/merkle_trees.md delete mode 100644 noir/docs/processed-docs/noir/standard_library/options.md delete mode 100644 noir/docs/processed-docs/noir/standard_library/recursion.md delete mode 100644 noir/docs/processed-docs/noir/standard_library/traits.md delete mode 100644 noir/docs/processed-docs/noir/standard_library/zeroed.md delete mode 100644 noir/docs/processed-docs/reference/NoirJS/backend_barretenberg/.nojekyll delete mode 100644 noir/docs/processed-docs/reference/NoirJS/backend_barretenberg/classes/BarretenbergBackend.md delete mode 100644 noir/docs/processed-docs/reference/NoirJS/backend_barretenberg/index.md delete mode 100644 noir/docs/processed-docs/reference/NoirJS/backend_barretenberg/interfaces/Backend.md delete mode 100644 noir/docs/processed-docs/reference/NoirJS/backend_barretenberg/type-aliases/BackendOptions.md delete mode 100644 noir/docs/processed-docs/reference/NoirJS/backend_barretenberg/type-aliases/CompiledCircuit.md delete mode 100644 noir/docs/processed-docs/reference/NoirJS/backend_barretenberg/type-aliases/ProofData.md delete mode 100644 noir/docs/processed-docs/reference/NoirJS/backend_barretenberg/typedoc-sidebar.cjs delete mode 100644 noir/docs/processed-docs/reference/NoirJS/noir_js/.nojekyll delete mode 100644 noir/docs/processed-docs/reference/NoirJS/noir_js/classes/Noir.md delete mode 100644 noir/docs/processed-docs/reference/NoirJS/noir_js/functions/and.md delete mode 100644 noir/docs/processed-docs/reference/NoirJS/noir_js/functions/blake2s256.md delete mode 100644 noir/docs/processed-docs/reference/NoirJS/noir_js/functions/ecdsa_secp256k1_verify.md delete mode 100644 noir/docs/processed-docs/reference/NoirJS/noir_js/functions/ecdsa_secp256r1_verify.md delete mode 100644 noir/docs/processed-docs/reference/NoirJS/noir_js/functions/keccak256.md delete mode 100644 noir/docs/processed-docs/reference/NoirJS/noir_js/functions/sha256.md delete mode 100644 noir/docs/processed-docs/reference/NoirJS/noir_js/functions/xor.md delete mode 100644 noir/docs/processed-docs/reference/NoirJS/noir_js/index.md delete mode 100644 noir/docs/processed-docs/reference/NoirJS/noir_js/type-aliases/CompiledCircuit.md delete mode 100644 noir/docs/processed-docs/reference/NoirJS/noir_js/type-aliases/ForeignCallHandler.md delete mode 100644 noir/docs/processed-docs/reference/NoirJS/noir_js/type-aliases/ForeignCallInput.md delete mode 100644 noir/docs/processed-docs/reference/NoirJS/noir_js/type-aliases/ForeignCallOutput.md delete mode 100644 noir/docs/processed-docs/reference/NoirJS/noir_js/type-aliases/InputMap.md delete mode 100644 noir/docs/processed-docs/reference/NoirJS/noir_js/type-aliases/ProofData.md delete mode 100644 noir/docs/processed-docs/reference/NoirJS/noir_js/type-aliases/WitnessMap.md delete mode 100644 noir/docs/processed-docs/reference/NoirJS/noir_js/typedoc-sidebar.cjs delete mode 100644 noir/docs/processed-docs/reference/_category_.json delete mode 100644 noir/docs/processed-docs/reference/nargo_commands.md delete mode 100644 noir/docs/processed-docs/tutorials/noirjs_app.md diff --git a/noir/docs/processed-docs-cache/explainers/explainer-oracle.md b/noir/docs/processed-docs-cache/explainers/explainer-oracle.md deleted file mode 100644 index b84ca5dd986..00000000000 --- a/noir/docs/processed-docs-cache/explainers/explainer-oracle.md +++ /dev/null @@ -1,57 +0,0 @@ ---- -title: Oracles -description: This guide provides an in-depth understanding of how Oracles work in Noir programming. Learn how to use outside calculations in your programs, constrain oracles, and understand their uses and limitations. -keywords: - - Noir Programming - - Oracles - - JSON-RPC - - Foreign Call Handlers - - Constrained Functions - - Blockchain Programming -sidebar_position: 1 ---- - -If you've seen "The Matrix" you may recall "The Oracle" as Gloria Foster smoking cigarettes and baking cookies. While she appears to "know things", she is actually providing a calculation of a pre-determined future. Noir Oracles are similar, in a way. They don't calculate the future (yet), but they allow you to use outside calculations in your programs. - -![matrix oracle prediction](@site/static/img/memes/matrix_oracle.jpeg) - -A Noir program is usually self-contained. You can pass certain inputs to it, and it will generate a deterministic output for those inputs. But what if you wanted to defer some calculation to an outside process or source? - -Oracles are functions that provide this feature. - -## Use cases - -An example usage for Oracles is proving something on-chain. For example, proving that the ETH-USDC quote was below a certain target at a certain block time. Or even making more complex proofs like proving the ownership of an NFT as an anonymous login method. - -Another interesting use case is to defer expensive calculations to be made outside of the Noir program, and then constraining the result; similar to the use of [unconstrained functions](../noir/concepts//unconstrained.md). - -In short, anything that can be constrained in a Noir program but needs to be fetched from an external source is a great candidate to be used in oracles. - -## Constraining oracles - -Just like in The Matrix, Oracles are powerful. But with great power, comes great responsibility. Just because you're using them in a Noir program doesn't mean they're true. Noir has no superpowers. If you want to prove that Portugal won the Euro Cup 2016, you're still relying on potentially untrusted information. - -To give a concrete example, Alice wants to login to the [NounsDAO](https://nouns.wtf/) forum with her username "noir_nouner" by proving she owns a noun without revealing her ethereum address. Her Noir program could have a oracle call like this: - -```rust -#[oracle(getNoun)] -unconstrained fn get_noun(address: Field) -> Field -``` - -This oracle could naively resolve with the number of Nouns she possesses. However, it is useless as a trusted source, as the oracle could resolve to anything Alice wants. In order to make this oracle call actually useful, Alice would need to constrain the response from the oracle, by proving her address and the noun count belongs to the state tree of the contract. - -In short, **Oracles don't prove anything. Your Noir program does.** - -:::danger - -If you don't constrain the return of your oracle, you could be clearly opening an attack vector on your Noir program. Make double-triple sure that the return of an oracle call is constrained! - -::: - -## How to use Oracles - -On CLI, Nargo resolves oracles by making JSON RPC calls, which means it would require an RPC node to be running. - -In JavaScript, NoirJS accepts and resolves arbitrary call handlers (that is, not limited to JSON) as long as they matches the expected types the developer defines. Refer to [Foreign Call Handler](../reference/NoirJS/noir_js/type-aliases/ForeignCallHandler.md) to learn more about NoirJS's call handling. - -If you want to build using oracles, follow through to the [oracle guide](../how_to/how-to-oracles.md) for a simple example on how to do that. diff --git a/noir/docs/processed-docs-cache/explainers/explainer-recursion.md b/noir/docs/processed-docs-cache/explainers/explainer-recursion.md deleted file mode 100644 index 18846176ca7..00000000000 --- a/noir/docs/processed-docs-cache/explainers/explainer-recursion.md +++ /dev/null @@ -1,176 +0,0 @@ ---- -title: Recursive proofs -description: Explore the concept of recursive proofs in Zero-Knowledge programming. Understand how recursion works in Noir, a language for writing smart contracts on the EVM blockchain. Learn through practical examples like Alice and Bob's guessing game, Charlie's recursive merkle tree, and Daniel's reusable components. Discover how to use recursive proofs to optimize computational resources and improve efficiency. - -keywords: - [ - "Recursive Proofs", - "Zero-Knowledge Programming", - "Noir", - "EVM Blockchain", - "Smart Contracts", - "Recursion in Noir", - "Alice and Bob Guessing Game", - "Recursive Merkle Tree", - "Reusable Components", - "Optimizing Computational Resources", - "Improving Efficiency", - "Verification Key", - "Aggregation", - "Recursive zkSNARK schemes", - "PLONK", - "Proving and Verification Keys" - ] -sidebar_position: 1 -pagination_next: how_to/how-to-recursion ---- - -In programming, we tend to think of recursion as something calling itself. A classic example would be the calculation of the factorial of a number: - -```js -function factorial(n) { - if (n === 0 || n === 1) { - return 1; - } else { - return n * factorial(n - 1); - } -} -``` - -In this case, while `n` is not `1`, this function will keep calling itself until it hits the base case, bubbling up the result on the call stack: - -```md - Is `n` 1? <--------- - /\ / - / \ n = n -1 - / \ / - Yes No -------- -``` - -In Zero-Knowledge, recursion has some similarities. - -It is not a Noir function calling itself, but a proof being used as an input to another circuit. In short, you verify one proof *inside* another proof, returning the proof that both proofs are valid. - -This means that, given enough computational resources, you can prove the correctness of any arbitrary number of proofs in a single proof. This could be useful to design state channels (for which a common example would be [Bitcoin's Lightning Network](https://en.wikipedia.org/wiki/Lightning_Network)), to save on gas costs by settling one proof on-chain, or simply to make business logic less dependent on a consensus mechanism. - -## Examples - -Let us look at some of these examples - -### Alice and Bob - Guessing game - -Alice and Bob are friends, and they like guessing games. They want to play a guessing game online, but for that, they need a trusted third-party that knows both of their secrets and finishes the game once someone wins. - -So, they use zero-knowledge proofs. Alice tries to guess Bob's number, and Bob will generate a ZK proof stating whether she succeeded or failed. - -This ZK proof can go on a smart contract, revealing the winner and even giving prizes. However, this means every turn needs to be verified on-chain. This incurs some cost and waiting time that may simply make the game too expensive or time-consuming to be worth it. - -As a solution, Alice proposes the following: "what if Bob generates his proof, and instead of sending it on-chain, I verify it *within* my own proof before playing my own turn?". - -She can then generate a proof that she verified his proof, and so on. - -```md - Did you fail? <-------------------------- - / \ / - / \ n = n -1 - / \ / - Yes No / - | | / - | | / - | You win / - | / - | / -Generate proof of that / - + / - my own guess ---------------- -``` - -### Charlie - Recursive merkle tree - -Charlie is a concerned citizen, and wants to be sure his vote in an election is accounted for. He votes with a ZK proof, but he has no way of knowing that his ZK proof was included in the total vote count! - -If the vote collector puts all of the votes into a [Merkle tree](https://en.wikipedia.org/wiki/Merkle_tree), everyone can prove the verification of two proofs within one proof, as such: - -```md - abcd - __________|______________ - | | - ab cd - _____|_____ ______|______ - | | | | - alice bob charlie daniel -``` - -Doing this recursively allows us to arrive on a final proof `abcd` which if true, verifies the correctness of all the votes. - -### Daniel - Reusable components - -Daniel has a big circuit and a big headache. A part of his circuit is a setup phase that finishes with some assertions that need to be made. But that section alone takes most of the proving time, and is largely independent of the rest of the circuit. - -He might find it more efficient to generate a proof for that setup phase separately, and verify that proof recursively in the actual business logic section of his circuit. This will allow for parallelization of both proofs, which results in a considerable speedup. - -## What params do I need - -As you can see in the [recursion reference](noir/standard_library/recursion.md), a simple recursive proof requires: - -- The proof to verify -- The Verification Key of the circuit that generated the proof -- A hash of this verification key, as it's needed for some backends -- The public inputs for the proof - -:::info - -Recursive zkSNARK schemes do not necessarily "verify a proof" in the sense that you expect a true or false to be spit out by the verifier. Rather an aggregation object is built over the public inputs. - -So, taking the example of Alice and Bob and their guessing game: - -- Alice makes her guess. Her proof is *not* recursive: it doesn't verify any proof within it! It's just a standard `assert(x != y)` circuit -- Bob verifies Alice's proof and makes his own guess. In this circuit, he doesn't exactly *prove* the verification of Alice's proof. Instead, he *aggregates* his proof to Alice's proof. The actual verification is done when the full proof is verified, for example when using `nargo verify` or through the verifier smart contract. - -We can imagine recursive proofs a [relay race](https://en.wikipedia.org/wiki/Relay_race). The first runner doesn't have to receive the baton from anyone else, as he/she already starts with it. But when his/her turn is over, the next runner needs to receive it, run a bit more, and pass it along. Even though every runner could theoretically verify the baton mid-run (why not? 🏃🔍), only at the end of the race does the referee verify that the whole race is valid. - -::: - -## Some architecture - -As with everything in computer science, there's no one-size-fits all. But there are some patterns that could help understanding and implementing them. To give three examples: - -### Adding some logic to a proof verification - -This would be an approach for something like our guessing game, where proofs are sent back and forth and are verified by each opponent. This circuit would be divided in two sections: - -- A `recursive verification` section, which would be just the call to `std::verify_proof`, and that would be skipped on the first move (since there's no proof to verify) -- A `guessing` section, which is basically the logic part where the actual guessing happens - -In such a situation, and assuming Alice is first, she would skip the first part and try to guess Bob's number. Bob would then verify her proof on the first section of his run, and try to guess Alice's number on the second part, and so on. - -### Aggregating proofs - -In some one-way interaction situations, recursion would allow for aggregation of simple proofs that don't need to be immediately verified on-chain or elsewhere. - -To give a practical example, a barman wouldn't need to verify a "proof-of-age" on-chain every time he serves alcohol to a customer. Instead, the architecture would comprise two circuits: - -- A `main`, non-recursive circuit with some logic -- A `recursive` circuit meant to verify two proofs in one proof - -The customer's proofs would be intermediate, and made on their phones, and the barman could just verify them locally. He would then aggregate them into a final proof sent on-chain (or elsewhere) at the end of the day. - -### Recursively verifying different circuits - -Nothing prevents you from verifying different circuits in a recursive proof, for example: - -- A `circuit1` circuit -- A `circuit2` circuit -- A `recursive` circuit - -In this example, a regulator could verify that taxes were paid for a specific purchase by aggregating both a `payer` circuit (proving that a purchase was made and taxes were paid), and a `receipt` circuit (proving that the payment was received) - -## How fast is it - -At the time of writing, verifying recursive proofs is surprisingly fast. This is because most of the time is spent on generating the verification key that will be used to generate the next proof. So you are able to cache the verification key and reuse it later. - -Currently, Noir JS packages don't expose the functionality of loading proving and verification keys, but that feature exists in the underlying `bb.js` package. - -## How can I try it - -Learn more about using recursion in Nargo and NoirJS in the [how-to guide](../how_to/how-to-recursion.md) and see a full example in [noir-examples](https://github.com/noir-lang/noir-examples). diff --git a/noir/docs/processed-docs-cache/getting_started/_category_.json b/noir/docs/processed-docs-cache/getting_started/_category_.json deleted file mode 100644 index 5d694210bbf..00000000000 --- a/noir/docs/processed-docs-cache/getting_started/_category_.json +++ /dev/null @@ -1,5 +0,0 @@ -{ - "position": 0, - "collapsible": true, - "collapsed": true -} diff --git a/noir/docs/processed-docs-cache/getting_started/hello_noir/_category_.json b/noir/docs/processed-docs-cache/getting_started/hello_noir/_category_.json deleted file mode 100644 index 23b560f610b..00000000000 --- a/noir/docs/processed-docs-cache/getting_started/hello_noir/_category_.json +++ /dev/null @@ -1,5 +0,0 @@ -{ - "position": 1, - "collapsible": true, - "collapsed": true -} diff --git a/noir/docs/processed-docs-cache/getting_started/hello_noir/index.md b/noir/docs/processed-docs-cache/getting_started/hello_noir/index.md deleted file mode 100644 index 743c4d8d634..00000000000 --- a/noir/docs/processed-docs-cache/getting_started/hello_noir/index.md +++ /dev/null @@ -1,142 +0,0 @@ ---- -title: Creating a Project -description: - Learn how to create and verify your first Noir program using Nargo, a programming language for - zero-knowledge proofs. -keywords: - [ - Nargo, - Noir, - zero-knowledge proofs, - programming language, - create Noir program, - verify Noir program, - step-by-step guide, - ] -sidebar_position: 1 - ---- - -Now that we have installed Nargo, it is time to make our first hello world program! - -## Create a Project Directory - -Noir code can live anywhere on your computer. Let us create a _projects_ folder in the home -directory to house our Noir programs. - -For Linux, macOS, and Windows PowerShell, create the directory and change directory into it by -running: - -```sh -mkdir ~/projects -cd ~/projects -``` - -## Create Our First Nargo Project - -Now that we are in the projects directory, create a new Nargo project by running: - -```sh -nargo new hello_world -``` - -> **Note:** `hello_world` can be any arbitrary project name, we are simply using `hello_world` for -> demonstration. -> -> In production, the common practice is to name the project folder as `circuits` for better -> identifiability when sitting alongside other folders in the codebase (e.g. `contracts`, `scripts`, -> `test`). - -A `hello_world` folder would be created. Similar to Rust, the folder houses _src/main.nr_ and -_Nargo.toml_ which contain the source code and environmental options of your Noir program -respectively. - -### Intro to Noir Syntax - -Let us take a closer look at _main.nr_. The default _main.nr_ generated should look like this: - -```rust -fn main(x : Field, y : pub Field) { - assert(x != y); -} -``` - -The first line of the program specifies the program's inputs: - -```rust -x : Field, y : pub Field -``` - -Program inputs in Noir are private by default (e.g. `x`), but can be labeled public using the -keyword `pub` (e.g. `y`). To learn more about private and public values, check the -[Data Types](../../noir/concepts/data_types/index.md) section. - -The next line of the program specifies its body: - -```rust -assert(x != y); -``` - -The Noir syntax `assert` can be interpreted as something similar to constraints in other zk-contract languages. - -For more Noir syntax, check the [Language Concepts](../../noir/concepts/comments.md) chapter. - -## Build In/Output Files - -Change directory into _hello_world_ and build in/output files for your Noir program by running: - -```sh -cd hello_world -nargo check -``` - -Two additional files would be generated in your project directory: - -_Prover.toml_ houses input values, and _Verifier.toml_ houses public values. - -## Prove Our Noir Program - -Now that the project is set up, we can create a proof of correct execution of our Noir program. - -Fill in input values for execution in the _Prover.toml_ file. For example: - -```toml -x = "1" -y = "2" -``` - -Prove the valid execution of your Noir program: - -```sh -nargo prove -``` - -A new folder _proofs_ would then be generated in your project directory, containing the proof file -`.proof`, where the project name is defined in Nargo.toml. - -The _Verifier.toml_ file would also be updated with the public values computed from program -execution (in this case the value of `y`): - -```toml -y = "0x0000000000000000000000000000000000000000000000000000000000000002" -``` - -> **Note:** Values in _Verifier.toml_ are computed as 32-byte hex values. - -## Verify Our Noir Program - -Once a proof is generated, we can verify correct execution of our Noir program by verifying the -proof file. - -Verify your proof by running: - -```sh -nargo verify -``` - -The verification will complete in silence if it is successful. If it fails, it will log the -corresponding error instead. - -Congratulations, you have now created and verified a proof for your very first Noir program! - -In the [next section](./project_breakdown.md), we will go into more detail on each step performed. diff --git a/noir/docs/processed-docs-cache/getting_started/hello_noir/project_breakdown.md b/noir/docs/processed-docs-cache/getting_started/hello_noir/project_breakdown.md deleted file mode 100644 index 6160a102c6c..00000000000 --- a/noir/docs/processed-docs-cache/getting_started/hello_noir/project_breakdown.md +++ /dev/null @@ -1,199 +0,0 @@ ---- -title: Project Breakdown -description: - Learn about the anatomy of a Nargo project, including the purpose of the Prover and Verifier TOML - files, and how to prove and verify your program. -keywords: - [Nargo, Nargo project, Prover.toml, Verifier.toml, proof verification, private asset transfer] -sidebar_position: 2 ---- - -This section breaks down our hello world program from the previous section. We elaborate on the project -structure and what the `prove` and `verify` commands did. - -## Anatomy of a Nargo Project - -Upon creating a new project with `nargo new` and building the in/output files with `nargo check` -commands, you would get a minimal Nargo project of the following structure: - - - src - - Prover.toml - - Verifier.toml - - Nargo.toml - -The source directory _src_ holds the source code for your Noir program. By default only a _main.nr_ -file will be generated within it. - -### Prover.toml - -_Prover.toml_ is used for specifying the input values for executing and proving the program. You can specify `toml` files with different names by using the `--prover-name` or `-p` flags, see the [Prover](#provertoml) section below. Optionally you may specify expected output values for prove-time checking as well. - -### Verifier.toml - -_Verifier.toml_ contains public in/output values computed when executing the Noir program. - -### Nargo.toml - -_Nargo.toml_ contains the environmental options of your project. It contains a "package" section and a "dependencies" section. - -Example Nargo.toml: - -```toml -[package] -name = "noir_starter" -type = "bin" -authors = ["Alice"] -compiler_version = "0.9.0" -description = "Getting started with Noir" -entry = "circuit/main.nr" -license = "MIT" - -[dependencies] -ecrecover = {tag = "v0.9.0", git = "https://github.com/colinnielsen/ecrecover-noir.git"} -``` - -Nargo.toml for a [workspace](../../noir/modules_packages_crates/workspaces.md) will look a bit different. For example: - -```toml -[workspace] -members = ["crates/a", "crates/b"] -default-member = "crates/a" -``` - -#### Package section - -The package section defines a number of fields including: - -- `name` (**required**) - the name of the package -- `type` (**required**) - can be "bin", "lib", or "contract" to specify whether its a binary, library or Aztec contract -- `authors` (optional) - authors of the project -- `compiler_version` - specifies the version of the compiler to use. This is enforced by the compiler and follow's [Rust's versioning](https://doc.rust-lang.org/cargo/reference/manifest.html#the-version-field), so a `compiler_version = 0.18.0` will enforce Nargo version 0.18.0, `compiler_version = ^0.18.0` will enforce anything above 0.18.0 but below 0.19.0, etc. For more information, see how [Rust handles these operators](https://docs.rs/semver/latest/semver/enum.Op.html) -- `description` (optional) -- `entry` (optional) - a relative filepath to use as the entry point into your package (overrides the default of `src/lib.nr` or `src/main.nr`) -- `backend` (optional) -- `license` (optional) - -#### Dependencies section - -This is where you will specify any dependencies for your project. See the [Dependencies page](../../noir/modules_packages_crates/dependencies.md) for more info. - -`./proofs/` and `./contract/` directories will not be immediately visible until you create a proof or -verifier contract respectively. - -### main.nr - -The _main.nr_ file contains a `main` method, this method is the entry point into your Noir program. - -In our sample program, _main.nr_ looks like this: - -```rust -fn main(x : Field, y : Field) { - assert(x != y); -} -``` - -The parameters `x` and `y` can be seen as the API for the program and must be supplied by the -prover. Since neither `x` nor `y` is marked as public, the verifier does not supply any inputs, when -verifying the proof. - -The prover supplies the values for `x` and `y` in the _Prover.toml_ file. - -As for the program body, `assert` ensures that the condition to be satisfied (e.g. `x != y`) is -constrained by the proof of the execution of said program (i.e. if the condition was not met, the -verifier would reject the proof as an invalid proof). - -### Prover.toml - -The _Prover.toml_ file is a file which the prover uses to supply his witness values(both private and -public). - -In our hello world program the _Prover.toml_ file looks like this: - -```toml -x = "1" -y = "2" -``` - -When the command `nargo prove` is executed, two processes happen: - -1. Noir creates a proof that `x`, which holds the value of `1`, and `y`, which holds the value of `2`, - is not equal. This inequality constraint is due to the line `assert(x != y)`. - -2. Noir creates and stores the proof of this statement in the _proofs_ directory in a file called your-project.proof. So if your project is named "private_voting" (defined in the project Nargo.toml), the proof will be saved at `./proofs/private_voting.proof`. Opening this file will display the proof in hex format. - -#### Arrays of Structs - -The following code shows how to pass an array of structs to a Noir program to generate a proof. - -```rust -// main.nr -struct Foo { - bar: Field, - baz: Field, -} - -fn main(foos: [Foo; 3]) -> pub Field { - foos[2].bar + foos[2].baz -} -``` - -Prover.toml: - -```toml -[[foos]] # foos[0] -bar = 0 -baz = 0 - -[[foos]] # foos[1] -bar = 0 -baz = 0 - -[[foos]] # foos[2] -bar = 1 -baz = 2 -``` - -#### Custom toml files - -You can specify a `toml` file with a different name to use for proving by using the `--prover-name` or `-p` flags. - -This command looks for proof inputs in the default **Prover.toml** and generates the proof and saves it at `./proofs/.proof`: - -```bash -nargo prove -``` - -This command looks for proof inputs in the custom **OtherProver.toml** and generates proof and saves it at `./proofs/.proof`: - -```bash -nargo prove -p OtherProver -``` - -## Verifying a Proof - -When the command `nargo verify` is executed, two processes happen: - -1. Noir checks in the _proofs_ directory for a proof file with the project name (eg. test_project.proof) - -2. If that file is found, the proof's validity is checked - -> **Note:** The validity of the proof is linked to the current Noir program; if the program is -> changed and the verifier verifies the proof, it will fail because the proof is not valid for the -> _modified_ Noir program. - -In production, the prover and the verifier are usually two separate entities. A prover would -retrieve the necessary inputs, execute the Noir program, generate a proof and pass it to the -verifier. The verifier would then retrieve the public inputs, usually from external sources, and -verify the validity of the proof against it. - -Take a private asset transfer as an example: - -A person using a browser as the prover would retrieve private inputs locally (e.g. the user's private key) and -public inputs (e.g. the user's encrypted balance on-chain), compute the transfer, generate a proof -and submit it to the verifier smart contract. - -The verifier contract would then draw the user's encrypted balance directly from the blockchain and -verify the proof submitted against it. If the verification passes, additional functions in the -verifier contract could trigger (e.g. approve the asset transfer). - -Now that you understand the concepts, you'll probably want some editor feedback while you are writing more complex code. diff --git a/noir/docs/processed-docs-cache/getting_started/installation/_category_.json b/noir/docs/processed-docs-cache/getting_started/installation/_category_.json deleted file mode 100644 index 0c02fb5d4d7..00000000000 --- a/noir/docs/processed-docs-cache/getting_started/installation/_category_.json +++ /dev/null @@ -1,6 +0,0 @@ -{ - "position": 0, - "label": "Install Nargo", - "collapsible": true, - "collapsed": true -} diff --git a/noir/docs/processed-docs-cache/getting_started/installation/index.md b/noir/docs/processed-docs-cache/getting_started/installation/index.md deleted file mode 100644 index 4ef86aa5914..00000000000 --- a/noir/docs/processed-docs-cache/getting_started/installation/index.md +++ /dev/null @@ -1,48 +0,0 @@ ---- -title: Nargo Installation -description: - nargo is a command line tool for interacting with Noir programs. This page is a quick guide on how to install Nargo through the most common and easy method, noirup -keywords: [ - Nargo - Noir - Rust - Cargo - Noirup - Installation - Terminal Commands - Version Check - Nightlies - Specific Versions - Branches - Noirup Repository -] -pagination_next: getting_started/hello_noir/index ---- - -`nargo` is the one-stop-shop for almost everything related with Noir. The name comes from our love for Rust and its package manager `cargo`. - -With `nargo`, you can start new projects, compile, execute, prove, verify, test, generate solidity contracts, and do pretty much all that is available in Noir. - -Similarly to `rustup`, we also maintain an easy installation method that covers most machines: `noirup`. - -## Installing Noirup - -Open a terminal on your machine, and write: - -```bash -curl -L https://raw.githubusercontent.com/noir-lang/noirup/main/install | bash -``` - -Close the terminal, open another one, and run - -```bash -noirup -``` - -Done. That's it. You should have the latest version working. You can check with `nargo --version`. - -You can also install nightlies, specific versions -or branches. Check out the [noirup repository](https://github.com/noir-lang/noirup) for more -information. - -Now we're ready to start working on [our first Noir program!](../hello_noir/index.md) diff --git a/noir/docs/processed-docs-cache/getting_started/installation/other_install_methods.md b/noir/docs/processed-docs-cache/getting_started/installation/other_install_methods.md deleted file mode 100644 index a532f83750e..00000000000 --- a/noir/docs/processed-docs-cache/getting_started/installation/other_install_methods.md +++ /dev/null @@ -1,190 +0,0 @@ ---- -title: Alternative Install Methods -description: - There are different ways to install Nargo, the one-stop shop and command-line tool for developing Noir programs. This guide explains other methods that don't rely on noirup, such as compiling from source, installing from binaries, and using WSL for windows -keywords: [ - Installation - Nargo - Noirup - Binaries - Compiling from Source - WSL for Windows - macOS - Linux - Nix - Direnv - Shell & editor experience - Building and testing - Uninstalling Nargo - Noir vs code extension -] -sidebar_position: 1 ---- - - -## Installation - -The most common method of installing Nargo is through [Noirup](./index.md) - -However, there are other methods for installing Nargo: - -- [Binaries](#binaries) -- [Compiling from Source](#compile-from-source) -- [WSL for Windows](#wsl-for-windows) - -### Binaries - -See [GitHub Releases](https://github.com/noir-lang/noir/releases) for the latest and previous -platform specific binaries. - -#### Step 1 - -Paste and run the following in the terminal to extract and install the binary: - -> **macOS / Linux:** If you are prompted with `Permission denied` when running commands, prepend -> `sudo` and re-run it. - -##### macOS (Apple Silicon) - -```bash -mkdir -p $HOME/.nargo/bin && \ -curl -o $HOME/.nargo/bin/nargo-aarch64-apple-darwin.tar.gz -L https://github.com/noir-lang/noir/releases/download/v0.6.0/nargo-aarch64-apple-darwin.tar.gz && \ -tar -xvf $HOME/.nargo/bin/nargo-aarch64-apple-darwin.tar.gz -C $HOME/.nargo/bin/ && \ -echo '\nexport PATH=$PATH:$HOME/.nargo/bin' >> ~/.zshrc && \ -source ~/.zshrc -``` - -##### macOS (Intel) - -```bash -mkdir -p $HOME/.nargo/bin && \ -curl -o $HOME/.nargo/bin/nargo-x86_64-apple-darwin.tar.gz -L https://github.com/noir-lang/noir/releases/download/v0.6.0/nargo-x86_64-apple-darwin.tar.gz && \ -tar -xvf $HOME/.nargo/bin/nargo-x86_64-apple-darwin.tar.gz -C $HOME/.nargo/bin/ && \ -echo '\nexport PATH=$PATH:$HOME/.nargo/bin' >> ~/.zshrc && \ -source ~/.zshrc -``` - -##### Linux (Bash) - -```bash -mkdir -p $HOME/.nargo/bin && \ -curl -o $HOME/.nargo/bin/nargo-x86_64-unknown-linux-gnu.tar.gz -L https://github.com/noir-lang/noir/releases/download/v0.6.0/nargo-x86_64-unknown-linux-gnu.tar.gz && \ -tar -xvf $HOME/.nargo/bin/nargo-x86_64-unknown-linux-gnu.tar.gz -C $HOME/.nargo/bin/ && \ -echo -e '\nexport PATH=$PATH:$HOME/.nargo/bin' >> ~/.bashrc && \ -source ~/.bashrc -``` - -#### Step 2 - -Check if the installation was successful by running `nargo --version`. You should get a version number. - -> **macOS:** If you are prompted with an OS alert, right-click and open the _nargo_ executable from -> Finder. Close the new terminal popped up and `nargo` should now be accessible. - -### Option 3: Compile from Source - -Due to the large number of native dependencies, Noir projects uses [Nix](https://nixos.org/) and [direnv](https://direnv.net/) to streamline the development experience. It helps mitigating issues commonly associated with dependency management, such as conflicts between required package versions for different projects (often referred to as "dependency hell"). - -Combined with direnv, which automatically sets or clears environment variables based on the directory, it further simplifies the development process by seamlessly integrating with the developer's shell, facilitating an efficient and reliable workflow for managing and deploying Noir projects with multiple dependencies. - -#### Setting up your environment - -For the best experience, please follow these instructions to setup your environment: - -1. Install Nix following [their guide](https://nixos.org/download.html) for your operating system. -2. Create the file `~/.config/nix/nix.conf` with the contents: - -```ini -experimental-features = nix-command -extra-experimental-features = flakes -``` - -3. Install direnv into your Nix profile by running: - -```sh -nix profile install nixpkgs#direnv -``` - -4. Add direnv to your shell following [their guide](https://direnv.net/docs/hook.html). - 1. For bash or zshell, add `eval "$(direnv hook bash)"` or `eval "$(direnv hook zsh)"` to your ~/.bashrc or ~/.zshrc file, respectively. -5. Restart your shell. - -#### Shell & editor experience - -Now that your environment is set up, you can get to work on the project. - -1. Clone the repository, such as: - -```sh -git clone git@github.com:noir-lang/noir -``` - -> Replacing `noir` with whichever repository you want to work on. - -2. Navigate to the directory: - -```sh -cd noir -``` - -> Replacing `noir` with whichever repository you cloned. - -3. You should see a **direnv error** because projects aren't allowed by default. Make sure you've reviewed and trust our `.envrc` file, then you need to run: - -```sh -direnv allow -``` - -4. Now, wait awhile for all the native dependencies to be built. This will take some time and direnv will warn you that it is taking a long time, but we just need to let it run. - -5. Once you are presented with your prompt again, you can start your editor within the project directory (we recommend [VSCode](https://code.visualstudio.com/)): - -```sh -code . -``` - -6. (Recommended) When launching VSCode for the first time, you should be prompted to install our recommended plugins. We highly recommend installing these for the best development experience. - -#### Building and testing - -Assuming you are using `direnv` to populate your environment, building and testing the project can be done -with the typical `cargo build`, `cargo test`, and `cargo clippy` commands. You'll notice that the `cargo` version matches the version we specify in `rust-toolchain.toml`, which is 1.71.1 at the time of this writing. - -If you want to build the entire project in an isolated sandbox, you can use Nix commands: - -1. `nix build .` (or `nix build . -L` for verbose output) to build the project in a Nix sandbox. -2. `nix flake check` (or `nix flake check -L` for verbose output) to run clippy and tests in a Nix sandbox. - -#### Without `direnv` - -If you have hesitations with using direnv, you can launch a subshell with `nix develop` and then launch your editor from within the subshell. However, if VSCode was already launched in the project directory, the environment won't be updated. - -Advanced: If you aren't using direnv nor launching your editor within the subshell, you can try to install Barretenberg and other global dependencies the package needs. This is an advanced workflow and likely won't receive support! - -### Option 4: WSL (for Windows) - -The default backend for Noir (Barretenberg) doesn't provide Windows binaries at this time. For that reason, Noir cannot be installed natively. However, it is available by using Windows Subsystem for Linux (WSL). - -Step 1: Follow the instructions [here](https://learn.microsoft.com/en-us/windows/wsl/install) to install and run WSL. - -step 2: Follow the [Noirup instructions](./index.md). - -## Uninstalling Nargo - -### Noirup - -If you installed Noir with `noirup`, you can uninstall Noir by removing the files in `~/.nargo`, `~/nargo` and `~/noir_cache`. - -```bash -rm -r ~/.nargo -rm -r ~/nargo -rm -r ~/noir_cache -``` - -### Nix - -If you installed Noir with Nix or from source, you can remove the binary located at `~/.nix-profile/bin/nargo`. - -```bash -rm ~/.nix-profile/bin/nargo -``` diff --git a/noir/docs/processed-docs-cache/getting_started/tooling/_category_.json b/noir/docs/processed-docs-cache/getting_started/tooling/_category_.json deleted file mode 100644 index 55804c03a71..00000000000 --- a/noir/docs/processed-docs-cache/getting_started/tooling/_category_.json +++ /dev/null @@ -1,6 +0,0 @@ -{ - "position": 2, - "label": "Tooling", - "collapsible": true, - "collapsed": true -} diff --git a/noir/docs/processed-docs-cache/getting_started/tooling/index.mdx b/noir/docs/processed-docs-cache/getting_started/tooling/index.mdx deleted file mode 100644 index ac480f3c9f5..00000000000 --- a/noir/docs/processed-docs-cache/getting_started/tooling/index.mdx +++ /dev/null @@ -1,38 +0,0 @@ ---- -title: Tooling -Description: This section provides information about the various tools and utilities available for Noir development. It covers the Noir playground, IDE tools, Codespaces, and community projects. -Keywords: [Noir, Development, Playground, IDE Tools, Language Service Provider, VS Code Extension, Codespaces, noir-starter, Community Projects, Awesome Noir Repository, Developer Tooling] ---- - -Noir is meant to be easy to develop with. For that reason, a number of utilities have been put together to ease the development process as much as feasible in the zero-knowledge world. - -## Playground - -The Noir playground is an easy way to test small ideas, share snippets, and integrate in other websites. You can access it at [play.noir-lang.org](https://play.noir-lang.org). - -## IDE tools - -When you install Nargo, you're also installing a Language Service Provider (LSP), which can be used by IDEs to provide syntax highlighting, codelens, warnings, and more. - -The easiest way to use these tools is by installing the [Noir VS Code extension](https://marketplace.visualstudio.com/items?itemName=noir-lang.vscode-noir). - -## Codespaces - -Some Noir repos have leveraged Codespaces in order to ease the development process. You can visit the [noir-starter](https://github.com/noir-lang/noir-starter) for an example. - - - -## GitHub Actions - -You can use `noirup` with GitHub Actions for CI/CD and automated testing. It is as simple as -installing `noirup` and running tests in your GitHub Action `yml` file. - -See the -[config file in the Noir repo](https://github.com/TomAFrench/noir-hashes/blob/master/.github/workflows/noir.yml) for an example usage. - -## Community projects - -As an open-source project, Noir has received many contributions over time. Some of them are related with developer tooling, and you can see some of them in [Awesome Noir repository](https://github.com/noir-lang/awesome-noir#dev-tools) diff --git a/noir/docs/processed-docs-cache/getting_started/tooling/language_server.md b/noir/docs/processed-docs-cache/getting_started/tooling/language_server.md deleted file mode 100644 index 81e0356ef8a..00000000000 --- a/noir/docs/processed-docs-cache/getting_started/tooling/language_server.md +++ /dev/null @@ -1,43 +0,0 @@ ---- -title: Language Server -description: Learn about the Noir Language Server, how to install the components, and configuration that may be required. -keywords: [Nargo, Language Server, LSP, VSCode, Visual Studio Code] -sidebar_position: 0 ---- - -This section helps you install and configure the Noir Language Server. - -The Language Server Protocol (LSP) has two components, the [Server](#language-server) and the [Client](#language-client). Below we describe each in the context of Noir. - -## Language Server - -The Server component is provided by the Nargo command line tool that you installed at the beginning of this guide. -As long as Nargo is installed and you've used it to run other commands in this guide, it should be good to go! - -If you'd like to verify that the `nargo lsp` command is available, you can run `nargo --help` and look for `lsp` in the list of commands. If you see it, you're using a version of Noir with LSP support. - -## Language Client - -The Client component is usually an editor plugin that launches the Server. It communicates LSP messages between the editor and the Server. For example, when you save a file, the Client will alert the Server, so it can try to compile the project and report any errors. - -Currently, Noir provides a Language Client for Visual Studio Code via the [vscode-noir](https://github.com/noir-lang/vscode-noir) extension. You can install it via the [Visual Studio Marketplace](https://marketplace.visualstudio.com/items?itemName=noir-lang.vscode-noir). - -> **Note:** Noir's Language Server Protocol support currently assumes users' VSCode workspace root to be the same as users' Noir project root (i.e. where Nargo.toml lies). -> -> If LSP features seem to be missing / malfunctioning, make sure you are opening your Noir project directly (instead of as a sub-folder) in your VSCode instance. - -When your language server is running correctly and the VSCode plugin is installed, you should see handy codelens buttons for compilation, measuring circuit size, execution, and tests: - -![Compile and Execute](@site/static/img/codelens_compile_execute.png) -![Run test](@site/static/img/codelens_run_test.png) - -You should also see your tests in the `testing` panel: - -![Testing panel](@site/static/img/codelens_testing_panel.png) - -### Configuration - -- **Noir: Enable LSP** - If checked, the extension will launch the Language Server via `nargo lsp` and communicate with it. -- **Noir: Nargo Flags** - Additional flags may be specified if you require them to be added when the extension calls `nargo lsp`. -- **Noir: Nargo Path** - An absolute path to a Nargo binary with the `lsp` command. This may be useful if Nargo is not within the `PATH` of your editor. -- **Noir > Trace: Server** - Setting this to `"messages"` or `"verbose"` will log LSP messages between the Client and Server. Useful for debugging. diff --git a/noir/docs/processed-docs-cache/getting_started/tooling/testing.md b/noir/docs/processed-docs-cache/getting_started/tooling/testing.md deleted file mode 100644 index d3e0c522473..00000000000 --- a/noir/docs/processed-docs-cache/getting_started/tooling/testing.md +++ /dev/null @@ -1,62 +0,0 @@ ---- -title: Testing in Noir -description: Learn how to use Nargo to test your Noir program in a quick and easy way -keywords: [Nargo, testing, Noir, compile, test] -sidebar_position: 1 ---- - -You can test your Noir programs using Noir circuits. - -Nargo will automatically compile and run any functions which have the decorator `#[test]` on them if -you run `nargo test`. - -For example if you have a program like: - -```rust -fn add(x: u64, y: u64) -> u64 { - x + y -} -#[test] -fn test_add() { - assert(add(2,2) == 4); - assert(add(0,1) == 1); - assert(add(1,0) == 1); -} -``` - -Running `nargo test` will test that the `test_add` function can be executed while satisfying all -the constraints which allows you to test that add returns the expected values. Test functions can't -have any arguments currently. - -### Test fail - -You can write tests that are expected to fail by using the decorator `#[test(should_fail)]`. For example: - -```rust -fn add(x: u64, y: u64) -> u64 { - x + y -} -#[test(should_fail)] -fn test_add() { - assert(add(2,2) == 5); -} -``` - -You can be more specific and make it fail with a specific reason by using `should_fail_with = "`: - -```rust -fn main(african_swallow_avg_speed : Field) { - assert(african_swallow_avg_speed == 65, "What is the airspeed velocity of an unladen swallow"); -} - -#[test] -fn test_king_arthur() { - main(65); -} - -#[test(should_fail_with = "What is the airspeed velocity of an unladen swallow")] -fn test_bridgekeeper() { - main(32); -} - -``` diff --git a/noir/docs/processed-docs-cache/how_to/_category_.json b/noir/docs/processed-docs-cache/how_to/_category_.json deleted file mode 100644 index 23b560f610b..00000000000 --- a/noir/docs/processed-docs-cache/how_to/_category_.json +++ /dev/null @@ -1,5 +0,0 @@ -{ - "position": 1, - "collapsible": true, - "collapsed": true -} diff --git a/noir/docs/processed-docs-cache/how_to/how-to-oracles.md b/noir/docs/processed-docs-cache/how_to/how-to-oracles.md deleted file mode 100644 index 0d84d992320..00000000000 --- a/noir/docs/processed-docs-cache/how_to/how-to-oracles.md +++ /dev/null @@ -1,280 +0,0 @@ ---- -title: How to use Oracles -description: Learn how to use oracles in your Noir program with examples in both Nargo and NoirJS. This guide also covers writing a JSON RPC server and providing custom foreign call handlers for NoirJS. -keywords: - - Noir Programming - - Oracles - - Nargo - - NoirJS - - JSON RPC Server - - Foreign Call Handlers -sidebar_position: 1 ---- - -This guide shows you how to use oracles in your Noir program. For the sake of clarity, it assumes that: - -- You have read the [explainer on Oracles](../explainers/explainer-oracle.md) and are comfortable with the concept. -- You have a Noir program to add oracles to. You can create one using the [vite-hardhat starter](https://github.com/noir-lang/noir-starter/tree/main/vite-hardhat) as a boilerplate. -- You understand the concept of a JSON-RPC server. Visit the [JSON-RPC website](https://www.jsonrpc.org/) if you need a refresher. -- You are comfortable with server-side JavaScript (e.g. Node.js, managing packages, etc.). - -For reference, you can find the snippets used in this tutorial on the [Aztec DevRel Repository](https://github.com/AztecProtocol/dev-rel/tree/main/code-snippets/how-to-oracles). - -## Rundown - -This guide has 3 major steps: - -1. How to modify our Noir program to make use of oracle calls as unconstrained functions -2. How to write a JSON RPC Server to resolve these oracle calls with Nargo -3. How to use them in Nargo and how to provide a custom resolver in NoirJS - -## Step 1 - Modify your Noir program - -An oracle is defined in a Noir program by defining two methods: - -- An unconstrained method - This tells the compiler that it is executing an [unconstrained functions](../noir/concepts//unconstrained.md). -- A decorated oracle method - This tells the compiler that this method is an RPC call. - -An example of an oracle that returns a `Field` would be: - -```rust -#[oracle(getSqrt)] -unconstrained fn sqrt(number: Field) -> Field { } - -unconstrained fn get_sqrt(number: Field) -> Field { - sqrt(number) -} -``` - -In this example, we're wrapping our oracle function in a unconstrained method, and decorating it with `oracle(getSqrt)`. We can then call the unconstrained function as we would call any other function: - -```rust -fn main(input: Field) { - let sqrt = get_sqrt(input); -} -``` - -In the next section, we will make this `getSqrt` (defined on the `sqrt` decorator) be a method of the RPC server Noir will use. - -:::danger - -As explained in the [Oracle Explainer](../explainers/explainer-oracle.md), this `main` function is unsafe unless you constrain its return value. For example: - -```rust -fn main(input: Field) { - let sqrt = get_sqrt(input); - assert(sqrt.pow_32(2) as u64 == input as u64); // <---- constrain the return of an oracle! -} -``` - -::: - -:::info - -Currently, oracles only work with single params or array params. For example: - -```rust -#[oracle(getSqrt)] -unconstrained fn sqrt([Field; 2]) -> [Field; 2] { } -``` - -::: - -## Step 2 - Write an RPC server - -Brillig will call *one* RPC server. Most likely you will have to write your own, and you can do it in whatever language you prefer. In this guide, we will do it in Javascript. - -Let's use the above example of an oracle that consumes an array with two `Field` and returns their square roots: - -```rust -#[oracle(getSqrt)] -unconstrained fn sqrt(input: [Field; 2]) -> [Field; 2] { } - -unconstrained fn get_sqrt(input: [Field; 2]) -> [Field; 2] { - sqrt(input) -} - -fn main(input: [Field; 2]) { - let sqrt = get_sqrt(input); - assert(sqrt[0].pow_32(2) as u64 == input[0] as u64); - assert(sqrt[1].pow_32(2) as u64 == input[1] as u64); -} -``` - -:::info - -Why square root? - -In general, computing square roots is computationally more expensive than multiplications, which takes a toll when speaking about ZK applications. In this case, instead of calculating the square root in Noir, we are using our oracle to offload that computation to be made in plain. In our circuit we can simply multiply the two values. - -::: - -Now, we should write the correspondent RPC server, starting with the [default JSON-RPC 2.0 boilerplate](https://www.npmjs.com/package/json-rpc-2.0#example): - -```js -import { JSONRPCServer } from "json-rpc-2.0"; -import express from "express"; -import bodyParser from "body-parser"; - -const app = express(); -app.use(bodyParser.json()); - -const server = new JSONRPCServer(); -app.post("/", (req, res) => { - const jsonRPCRequest = req.body; - server.receive(jsonRPCRequest).then((jsonRPCResponse) => { - if (jsonRPCResponse) { - res.json(jsonRPCResponse); - } else { - res.sendStatus(204); - } - }); -}); - -app.listen(5555); -``` - -Now, we will add our `getSqrt` method, as expected by the `#[oracle(getSqrt)]` decorator in our Noir code. It maps through the params array and returns their square roots: - -```js -server.addMethod("getSqrt", async (params) => { - const values = params[0].Array.map(({ inner }) => { - return { inner: `${Math.sqrt(parseInt(inner, 16))}` }; - }); - return { values: [{ Array: values }] }; -}); -``` - -:::tip - -Brillig expects an object with an array of values. Each value is an object declaring to be `Single` or `Array` and returning a `inner` property *as a string*. For example: - -```json -{ "values": [{ "Array": [{ "inner": "1" }, { "inner": "2"}]}]} -{ "values": [{ "Single": { "inner": "1" }}]} -{ "values": [{ "Single": { "inner": "1" }}, { "Array": [{ "inner": "1", { "inner": "2" }}]}]} -``` - -If you're using Typescript, the following types may be helpful in understanding the expected return value and making sure they're easy to follow: - -```js -interface Value { - inner: string, -} - -interface SingleForeignCallParam { - Single: Value, -} - -interface ArrayForeignCallParam { - Array: Value[], -} - -type ForeignCallParam = SingleForeignCallParam | ArrayForeignCallParam; - -interface ForeignCallResult { - values: ForeignCallParam[], -} -``` - -::: - -## Step 3 - Usage with Nargo - -Using the [`nargo` CLI tool](../getting_started/installation/index.md), you can use oracles in the `nargo test`, `nargo execute` and `nargo prove` commands by passing a value to `--oracle-resolver`. For example: - -```bash -nargo test --oracle-resolver http://localhost:5555 -``` - -This tells `nargo` to use your RPC Server URL whenever it finds an oracle decorator. - -## Step 4 - Usage with NoirJS - -In a JS environment, an RPC server is not strictly necessary, as you may want to resolve your oracles without needing any JSON call at all. NoirJS simply expects that you pass a callback function when you generate proofs, and that callback function can be anything. - -For example, if your Noir program expects the host machine to provide CPU pseudo-randomness, you could simply pass it as the `foreignCallHandler`. You don't strictly need to create an RPC server to serve pseudo-randomness, as you may as well get it directly in your app: - -```js -const foreignCallHandler = (name, inputs) => crypto.randomBytes(16) // etc - -await noir.generateFinalProof(inputs, foreignCallHandler) -``` - -As one can see, in NoirJS, the [`foreignCallHandler`](../reference/NoirJS/noir_js/type-aliases/ForeignCallHandler.md) function simply means "a callback function that returns a value of type [`ForeignCallOutput`](../reference/NoirJS/noir_js/type-aliases/ForeignCallOutput.md). It doesn't have to be an RPC call like in the case for Nargo. - -:::tip - -Does this mean you don't have to write an RPC server like in [Step #2](#step-2---write-an-rpc-server)? - -You don't technically have to, but then how would you run `nargo test` or `nargo prove`? To use both `Nargo` and `NoirJS` in your development flow, you will have to write a JSON RPC server. - -::: - -In this case, let's make `foreignCallHandler` call the JSON RPC Server we created in [Step #2](#step-2---write-an-rpc-server), by making it a JSON RPC Client. - -For example, using the same `getSqrt` program in [Step #1](#step-1---modify-your-noir-program) (comments in the code): - -```js -import { JSONRPCClient } from "json-rpc-2.0"; - -// declaring the JSONRPCClient -const client = new JSONRPCClient((jsonRPCRequest) => { -// hitting the same JSON RPC Server we coded above - return fetch("http://localhost:5555", { - method: "POST", - headers: { - "content-type": "application/json", - }, - body: JSON.stringify(jsonRPCRequest), - }).then((response) => { - if (response.status === 200) { - return response - .json() - .then((jsonRPCResponse) => client.receive(jsonRPCResponse)); - } else if (jsonRPCRequest.id !== undefined) { - return Promise.reject(new Error(response.statusText)); - } - }); -}); - -// declaring a function that takes the name of the foreign call (getSqrt) and the inputs -const foreignCallHandler = async (name, input) => { - // notice that the "inputs" parameter contains *all* the inputs - // in this case we to make the RPC request with the first parameter "numbers", which would be input[0] - const oracleReturn = await client.request(name, [ - { Array: input[0].map((i) => ({ inner: i.toString("hex") })) }, - ]); - return [oracleReturn.values[0].Array.map((x) => x.inner)]; -}; - -// the rest of your NoirJS code -const input = { input: [4, 16] }; -const { witness } = await noir.execute(numbers, foreignCallHandler); -``` - -:::tip - -If you're in a NoirJS environment running your RPC server together with a frontend app, you'll probably hit a familiar problem in full-stack development: requests being blocked by [CORS](https://developer.mozilla.org/en-US/docs/Web/HTTP/CORS) policy. For development only, you can simply install and use the [`cors` npm package](https://www.npmjs.com/package/cors) to get around the problem: - -```bash -yarn add cors -``` - -and use it as a middleware: - -```js -import cors from "cors"; - -const app = express(); -app.use(cors()) -``` - -::: - -## Conclusion - -Hopefully by the end of this guide, you should be able to: - -- Write your own logic around Oracles and how to write a JSON RPC server to make them work with your Nargo commands. -- Provide custom foreign call handlers for NoirJS. diff --git a/noir/docs/processed-docs-cache/how_to/how-to-recursion.md b/noir/docs/processed-docs-cache/how_to/how-to-recursion.md deleted file mode 100644 index f34647a99d5..00000000000 --- a/noir/docs/processed-docs-cache/how_to/how-to-recursion.md +++ /dev/null @@ -1,179 +0,0 @@ ---- -title: How to use recursion on NoirJS -description: Learn how to implement recursion with NoirJS, a powerful tool for creating smart contracts on the EVM blockchain. This guide assumes familiarity with NoirJS, solidity verifiers, and the Barretenberg proving backend. Discover how to generate both final and intermediate proofs using `noir_js` and `backend_barretenberg`. -keywords: - [ - "NoirJS", - "EVM blockchain", - "smart contracts", - "recursion", - "solidity verifiers", - "Barretenberg backend", - "noir_js", - "backend_barretenberg", - "intermediate proofs", - "final proofs", - "nargo compile", - "json import", - "recursive circuit", - "recursive app" - ] -sidebar_position: 1 ---- - -This guide shows you how to use recursive proofs in your NoirJS app. For the sake of clarity, it is assumed that: - -- You already have a NoirJS app. If you don't, please visit the [NoirJS tutorial](../tutorials/noirjs_app.md) and the [reference](../reference/NoirJS/noir_js/index.md). -- You are familiar with what are recursive proofs and you have read the [recursion explainer](../explainers/explainer-recursion.md) -- You already built a recursive circuit following [the reference](../noir/standard_library/recursion.md), and understand how it works. - -It is also assumed that you're not using `noir_wasm` for compilation, and instead you've used [`nargo compile`](../reference/nargo_commands.md) to generate the `json` you're now importing into your project. However, the guide should work just the same if you're using `noir_wasm`. - -:::info - -As you've read in the [explainer](../explainers/explainer-recursion.md), a recursive proof is an intermediate proof. This means that it doesn't necessarily generate the final step that makes it verifiable in a smart contract. However, it is easy to verify within another circuit. - -While "standard" usage of NoirJS packages abstracts final proofs, it currently lacks the necessary interface to abstract away intermediate proofs. This means that these proofs need to be created by using the backend directly. - -In short: - -- `noir_js` generates *only* final proofs -- `backend_barretenberg` generates both types of proofs - -::: - -In a standard recursive app, you're also dealing with at least two circuits. For the purpose of this guide, we will assume these two: - -- `main`: a circuit of type `assert(x != y)` -- `recursive`: a circuit that verifies `main` - -For a full example on how recursive proofs work, please refer to the [noir-examples](https://github.com/noir-lang/noir-examples) repository. We will *not* be using it as a reference for this guide. - -## Step 1: Setup - -In a common NoirJS app, you need to instantiate a backend with something like `const backend = new Backend(circuit)`. Then you feed it to the `noir_js` interface. - -For recursion, this doesn't happen, and the only need for `noir_js` is only to `execute` a circuit and get its witness and return value. Everything else is not interfaced, so it needs to happen on the `backend` object. - -It is also recommended that you instantiate the backend with as many threads as possible, to allow for maximum concurrency: - -```js -const backend = new Backend(circuit, { threads: 8 }) -``` - -:::tip -You can use the [`os.cpus()`](https://nodejs.org/api/os.html#oscpus) object in `nodejs` or [`navigator.hardwareConcurrency`](https://developer.mozilla.org/en-US/docs/Web/API/Navigator/hardwareConcurrency) on the browser to make the most out of those glorious cpu cores -::: - -## Step 2: Generating the witness and the proof for `main` - -After instantiating the backend, you should also instantiate `noir_js`. We will use it to execute the circuit and get the witness. - -```js -const noir = new Noir(circuit, backend) -const { witness } = noir.execute(input) -``` - -With this witness, you are now able to generate the intermediate proof for the main circuit: - -```js -const { proof, publicInputs } = await backend.generateIntermediateProof(witness) -``` - -:::warning - -Always keep in mind what is actually happening on your development process, otherwise you'll quickly become confused about what circuit we are actually running and why! - -In this case, you can imagine that Alice (running the `main` circuit) is proving something to Bob (running the `recursive` circuit), and Bob is verifying her proof within his proof. - -With this in mind, it becomes clear that our intermediate proof is the one *meant to be verified within another circuit*, so it must be Alice's. Actually, the only final proof in this theoretical scenario would be the last one, sent on-chain. - -::: - -## Step 3 - Verification and proof artifacts - -Optionally, you are able to verify the intermediate proof: - -```js -const verified = await backend.verifyIntermediateProof({ proof, publicInputs }) -``` - -This can be useful to make sure our intermediate proof was correctly generated. But the real goal is to do it within another circuit. For that, we need to generate the intermediate artifacts: - -```js -const { proofAsFields, vkAsFields, vkHash } = await backend.generateIntermediateProofArtifacts( { publicInputs, proof }, publicInputsCount) -``` - -This call takes the public inputs and the proof, but also the public inputs count. While this is easily retrievable by simply counting the `publicInputs` length, the backend interface doesn't currently abstract it away. - -:::info - -The `proofAsFields` has a constant size `[Field; 93]` and verification keys in Barretenberg are always `[Field; 114]`. - -::: - -:::warning - -One common mistake is to forget *who* makes this call. - -In a situation where Alice is generating the `main` proof, if she generates the proof artifacts and sends them to Bob, which gladly takes them as true, this would mean Alice could prove anything! - -Instead, Bob needs to make sure *he* extracts the proof artifacts, using his own instance of the `main` circuit backend. This way, Alice has to provide a valid proof for the correct `main` circuit. - -::: - -## Step 4 - Recursive proof generation - -With the artifacts, generating a recursive proof is no different from a normal proof. You simply use the `backend` (with the recursive circuit) to generate it: - -```js -const recursiveInputs = { - verification_key: vkAsFields, // array of length 114 - proof: proofAsFields, // array of length 93 + size of public inputs - publicInputs: [mainInput.y], // using the example above, where `y` is the only public input - key_hash: vkHash, -} - -const { witness, returnValue } = noir.execute(recursiveInputs) // we're executing the recursive circuit now! -const { proof, publicInputs } = backend.generateFinalProof(witness) -const verified = backend.verifyFinalProof({ proof, publicInputs }) -``` - -You can obviously chain this proof into another proof. In fact, if you're using recursive proofs, you're probably interested of using them this way! - -:::tip - -Managing circuits and "who does what" can be confusing. To make sure your naming is consistent, you can keep them in an object. For example: - -```js -const circuits = { - main: mainJSON, - recursive: recursiveJSON -} -const backends = { - main: new BarretenbergBackend(circuits.main), - recursive: new BarretenbergBackend(circuits.recursive) -} -const noir_programs = { - main: new Noir(circuits.main, backends.main), - recursive: new Noir(circuits.recursive, backends.recursive) -} -``` - -This allows you to neatly call exactly the method you want without conflicting names: - -```js -// Alice runs this 👇 -const { witness: mainWitness } = await noir_programs.main.execute(input) -const proof = await backends.main.generateIntermediateProof(mainWitness) - -// Bob runs this 👇 -const verified = await backends.main.verifyIntermediateProof(proof) -const { proofAsFields, vkAsFields, vkHash } = await backends.main.generateIntermediateProofArtifacts( - proof, - numPublicInputs, -); -const recursiveProof = await noir_programs.recursive.generateFinalProof(recursiveInputs) -``` - -::: diff --git a/noir/docs/processed-docs-cache/how_to/how-to-solidity-verifier.md b/noir/docs/processed-docs-cache/how_to/how-to-solidity-verifier.md deleted file mode 100644 index e3c7c1065da..00000000000 --- a/noir/docs/processed-docs-cache/how_to/how-to-solidity-verifier.md +++ /dev/null @@ -1,231 +0,0 @@ ---- -title: Generate a Solidity Verifier -description: - Learn how to run the verifier as a smart contract on the blockchain. Compile a Solidity verifier - contract for your Noir program and deploy it on any EVM blockchain acting as a verifier smart - contract. Read more to find out -keywords: - [ - solidity verifier, - smart contract, - blockchain, - compiler, - plonk_vk.sol, - EVM blockchain, - verifying Noir programs, - proving backend, - Barretenberg, - ] -sidebar_position: 0 -pagination_next: tutorials/noirjs_app ---- - -Noir has the ability to generate a verifier contract in Solidity, which can be deployed in many EVM-compatible blockchains such as Ethereum. - -This allows for a powerful feature set, as one can make use of the conciseness and the privacy provided by Noir in an immutable ledger. Applications can range from simple P2P guessing games, to complex private DeFi interactions. - -This guide shows you how to generate a Solidity Verifier and deploy it on the [Remix IDE](https://remix.ethereum.org/). It is assumed that: - -- You are comfortable with the Solidity programming language and understand how contracts are deployed on the Ethereum network -- You have Noir installed and you have a Noir program. If you don't, [get started](../getting_started/installation/index.md) with Nargo and the example Hello Noir circuit -- You are comfortable navigating RemixIDE. If you aren't or you need a refresher, you can find some video tutorials [here](https://www.youtube.com/channel/UCjTUPyFEr2xDGN6Cg8nKDaA) that could help you. - -## Rundown - -Generating a Solidity Verifier contract is actually a one-command process. However, compiling it and deploying it can have some caveats. Here's the rundown of this guide: - -1. How to generate a solidity smart contract -2. How to compile the smart contract in the RemixIDE -3. How to deploy it to a testnet - -## Step 1 - Generate a contract - -This is by far the most straight-forward step. Just run: - -```sh -nargo codegen-verifier -``` - -A new `contract` folder would then be generated in your project directory, containing the Solidity -file `plonk_vk.sol`. It can be deployed to any EVM blockchain acting as a verifier smart contract. - -:::info - -It is possible to generate verifier contracts of Noir programs for other smart contract platforms as long as the proving backend supplies an implementation. - -Barretenberg, the default proving backend for Nargo, supports generation of verifier contracts, for the time being these are only in Solidity. -::: - -## Step 2 - Compiling - -We will mostly skip the details of RemixIDE, as the UI can change from version to version. For now, we can just open -Remix and create a blank workspace. - -![Create Workspace](@site/static/img/how-tos/solidity_verifier_1.png) - -We will create a new file to contain the contract Nargo generated, and copy-paste its content. - -:::warning - -You'll likely see a warning advising you to not trust pasted code. While it is an important warning, it is irrelevant in the context of this guide and can be ignored. We will not be deploying anywhere near a mainnet. - -::: - -To compile our the verifier, we can navigate to the compilation tab: - -![Compilation Tab](@site/static/img/how-tos/solidity_verifier_2.png) - -Remix should automatically match a suitable compiler version. However, hitting the "Compile" button will most likely generate a "Stack too deep" error: - -![Stack too deep](@site/static/img/how-tos/solidity_verifier_3.png) - -This is due to the verify function needing to put many variables on the stack, but enabling the optimizer resolves the issue. To do this, let's open the "Advanced Configurations" tab and enable optimization. The default 200 runs will suffice. - -:::info - -This time we will see a warning about an unused function parameter. This is expected, as the `verify` function doesn't use the `_proof` parameter inside a solidity block, it is loaded from calldata and used in assembly. - -::: - -![Compilation success](@site/static/img/how-tos/solidity_verifier_4.png) - -## Step 3 - Deploying - -At this point we should have a compiled contract read to deploy. If we navigate to the deploy section in Remix, we will see many different environments we can deploy to. The steps to deploy on each environment would be out-of-scope for this guide, so we will just use the default Remix VM. - -Looking closely, we will notice that our "Solidity Verifier" is actually three contracts working together: - -- An `UltraVerificationKey` library which simply stores the verification key for our circuit. -- An abstract contract `BaseUltraVerifier` containing most of the verifying logic. -- A main `UltraVerifier` contract that inherits from the Base and uses the Key contract. - -Remix will take care of the dependencies for us so we can simply deploy the UltraVerifier contract by selecting it and hitting "deploy": - -![Deploying UltraVerifier](@site/static/img/how-tos/solidity_verifier_5.png) - -A contract will show up in the "Deployed Contracts" section, where we can retrieve the Verification Key Hash. This is particularly useful for double-checking the deployer contract is the correct one. - -:::note - -Why "UltraVerifier"? - -To be precise, the Noir compiler (`nargo`) doesn't generate the verifier contract directly. It compiles the Noir code into an intermediate language (ACIR), which is then executed by the backend. So it is the backend that returns the verifier smart contract, not Noir. - -In this case, the Barretenberg Backend uses the UltraPlonk proving system, hence the "UltraVerifier" name. - -::: - -## Step 4 - Verifying - -To verify a proof using the Solidity verifier contract, we call the `verify` function in this extended contract: - -```solidity -function verify(bytes calldata _proof, bytes32[] calldata _publicInputs) external view returns (bool) -``` - -When using the default example in the [Hello Noir](../getting_started/hello_noir/index.md) guide, the easiest way to confirm that the verifier contract is doing its job is by calling the `verify` function via remix with the required parameters. For `_proof`, run `nargo prove` and use the string in `proof/.proof` (adding the hex `0x` prefix). We can also copy the public input from `Verifier.toml`, as it will be properly formatted as 32-byte strings: - -``` -0x...... , [0x0000.....02] -``` - -A programmatic example of how the `verify` function is called can be seen in the example zk voting application [here](https://github.com/noir-lang/noir-examples/blob/33e598c257e2402ea3a6b68dd4c5ad492bce1b0a/foundry-voting/src/zkVote.sol#L35): - -```solidity -function castVote(bytes calldata proof, uint proposalId, uint vote, bytes32 nullifierHash) public returns (bool) { - // ... - bytes32[] memory publicInputs = new bytes32[](4); - publicInputs[0] = merkleRoot; - publicInputs[1] = bytes32(proposalId); - publicInputs[2] = bytes32(vote); - publicInputs[3] = nullifierHash; - require(verifier.verify(proof, publicInputs), "Invalid proof"); -``` - -:::info[Return Values] - -A circuit doesn't have the concept of a return value. Return values are just syntactic sugar in -Noir. - -Under the hood, the return value is passed as an input to the circuit and is checked at the end of -the circuit program. - -For example, if you have Noir program like this: - -```rust -fn main( - // Public inputs - pubkey_x: pub Field, - pubkey_y: pub Field, - // Private inputs - priv_key: Field, -) -> pub Field -``` - -the `verify` function will expect the public inputs array (second function parameter) to be of length 3, the two inputs and the return value. Like before, these values are populated in Verifier.toml after running `nargo prove`. - -Passing only two inputs will result in an error such as `PUBLIC_INPUT_COUNT_INVALID(3, 2)`. - -In this case, the inputs parameter to `verify` would be an array ordered as `[pubkey_x, pubkey_y, return]`. - -::: - -:::tip[Structs] - -You can pass structs to the verifier contract. They will be flattened so that the array of inputs is 1-dimensional array. - -For example, consider the following program: - -```rust -struct Type1 { - val1: Field, - val2: Field, -} - -struct Nested { - t1: Type1, - is_true: bool, -} - -fn main(x: pub Field, nested: pub Nested, y: pub Field) { - //... -} -``` - -The order of these inputs would be flattened to: `[x, nested.t1.val1, nested.t1.val2, nested.is_true, y]` - -::: - -The other function you can call is our entrypoint `verify` function, as defined above. - -:::tip - -It's worth noticing that the `verify` function is actually a `view` function. A `view` function does not alter the blockchain state, so it doesn't need to be distributed (i.e. it will run only on the executing node), and therefore doesn't cost any gas. - -This can be particularly useful in some situations. If Alice generated a proof and wants Bob to verify its correctness, Bob doesn't need to run Nargo, NoirJS, or any Noir specific infrastructure. He can simply make a call to the blockchain with the proof and verify it is correct without paying any gas. - -It would be incorrect to say that a Noir proof verification costs any gas at all. However, most of the time the result of `verify` is used to modify state (for example, to update a balance, a game state, etc). In that case the whole network needs to execute it, which does incur gas costs (calldata and execution, but not storage). - -::: - -## A Note on EVM chains - -ZK-SNARK verification depends on some precompiled cryptographic primitives such as Elliptic Curve Pairings (if you like complex math, you can read about EC Pairings [here](https://medium.com/@VitalikButerin/exploring-elliptic-curve-pairings-c73c1864e627)). Not all EVM chains support EC Pairings, notably some of the ZK-EVMs. This means that you won't be able to use the verifier contract in all of them. - -For example, chains like `zkSync ERA` and `Polygon zkEVM` do not currently support these precompiles, so proof verification via Solidity verifier contracts won't work. Here's a quick list of EVM chains that have been tested and are known to work: - -- Optimism -- Arbitrum -- Polygon PoS -- Scroll -- Celo - -If you test any other chains, please open a PR on this page to update the list. See [this doc](https://github.com/noir-lang/noir-starter/tree/main/with-foundry#testing-on-chain) for more info about testing verifier contracts on different EVM chains. - -## What's next - -Now that you know how to call a Noir Solidity Verifier on a smart contract using Remix, you should be comfortable with using it with some programmatic frameworks, such as [hardhat](https://github.com/noir-lang/noir-starter/tree/main/vite-hardhat) and [foundry](https://github.com/noir-lang/noir-starter/tree/main/with-foundry). - -You can find other tools, examples, boilerplates and libraries in the [awesome-noir](https://github.com/noir-lang/awesome-noir) repository. - -You should also be ready to write and deploy your first NoirJS app and start generating proofs on websites, phones, and NodeJS environments! Head on to the [NoirJS tutorial](../tutorials/noirjs_app.md) to learn how to do that. diff --git a/noir/docs/processed-docs-cache/how_to/merkle-proof.mdx b/noir/docs/processed-docs-cache/how_to/merkle-proof.mdx deleted file mode 100644 index 34074659ac1..00000000000 --- a/noir/docs/processed-docs-cache/how_to/merkle-proof.mdx +++ /dev/null @@ -1,48 +0,0 @@ ---- -title: Prove Merkle Tree Membership -description: - Learn how to use merkle membership proof in Noir to prove that a given leaf is a member of a - merkle tree with a specified root, at a given index. -keywords: - [merkle proof, merkle membership proof, Noir, rust, hash function, Pedersen, sha256, merkle tree] ---- - -Let's walk through an example of a merkle membership proof in Noir that proves that a given leaf is -in a merkle tree. - -```rust -use dep::std; - -fn main(message : [Field; 62], index : Field, hashpath : [Field; 40], root : Field) { - let leaf = std::hash::hash_to_field(message); - let merkle_root = std::merkle::compute_merkle_root(leaf, index, hashpath); - assert(merkle_root == root); -} - -``` - -The message is hashed using `hash_to_field`. The specific hash function that is being used is chosen -by the backend. The only requirement is that this hash function can heuristically be used as a -random oracle. If only collision resistance is needed, then one can call `std::hash::pedersen_hash` -instead. - -```rust -let leaf = std::hash::hash_to_field(message); -``` - -The leaf is then passed to a compute_merkle_root function with the root, index and hashpath. The returned root can then be asserted to be the same as the provided root. - -```rust -let merkle_root = std::merkle::compute_merkle_root(leaf, index, hashpath); -assert (merkle_root == root); -``` - -> **Note:** It is possible to re-implement the merkle tree implementation without standard library. -> However, for most usecases, it is enough. In general, the standard library will always opt to be -> as conservative as possible, while striking a balance with efficiency. - -An example, the merkle membership proof, only requires a hash function that has collision -resistance, hence a hash function like Pedersen is allowed, which in most cases is more efficient -than the even more conservative sha256. - -[View an example on the starter repo](https://github.com/noir-lang/noir-examples/blob/3ea09545cabfa464124ec2f3ea8e60c608abe6df/stealthdrop/circuits/src/main.nr#L20) diff --git a/noir/docs/processed-docs-cache/how_to/using-devcontainers.mdx b/noir/docs/processed-docs-cache/how_to/using-devcontainers.mdx deleted file mode 100644 index 727ec6ca667..00000000000 --- a/noir/docs/processed-docs-cache/how_to/using-devcontainers.mdx +++ /dev/null @@ -1,110 +0,0 @@ ---- -title: Developer Containers and Codespaces -description: "Learn how to set up a devcontainer in your GitHub repository for a seamless coding experience with Codespaces. Follow our easy 8-step guide to create your own Noir environment without installing Nargo locally." -keywords: ["Devcontainer", "Codespaces", "GitHub", "Noir Environment", "Docker Image", "Development Environment", "Remote Coding", "GitHub Codespaces", "Noir Programming", "Nargo", "VSCode Extensions", "Noirup"] -sidebar_position: 1 ---- - -Adding a developer container configuration file to your Noir project is one of the easiest way to unlock coding in browser. - -## What's a devcontainer after all? - -A [Developer Container](https://containers.dev/) (devcontainer for short) is a Docker image that comes preloaded with tools, extensions, and other tools you need to quickly get started or continue a project, without having to install Nargo locally. Think of it as a development environment in a box. - -There are many advantages to this: - -- It's platform and architecture agnostic -- You don't need to have an IDE installed, or Nargo, or use a terminal at all -- It's safer for using on a public machine or public network - -One of the best ways of using devcontainers is... not using your machine at all, for maximum control, performance, and ease of use. -Enter Codespaces. - -## Codespaces - -If a devcontainer is just a Docker image, then what stops you from provisioning a `p3dn.24xlarge` AWS EC2 instance with 92 vCPUs and 768 GiB RAM and using it to prove your 10-gate SNARK proof? - -Nothing! Except perhaps the 30-40$ per hour it will cost you. - -The problem is that provisioning takes time, and I bet you don't want to see the AWS console every time you want to code something real quick. - -Fortunately, there's an easy and free way to get a decent remote machine ready and loaded in less than 2 minutes: Codespaces. [Codespaces is a Github feature](https://github.com/features/codespaces) that allows you to code in a remote machine by using devcontainers, and it's pretty cool: - -- You can start coding Noir in less than a minute -- It uses the resources of a remote machine, so you can code on your grandma's phone if needed be -- It makes it easy to share work with your frens -- It's fully reusable, you can stop and restart whenever you need to - -:::info - -Don't take out your wallet just yet. Free GitHub accounts get about [15-60 hours of coding](https://github.com/features/codespaces) for free per month, depending on the size of your provisioned machine. - -::: - -## Tell me it's _actually_ easy - -It is! - -Github comes with a default codespace and you can use it to code your own devcontainer. That's exactly what we will be doing in this guide. - - - -8 simple steps: - -#### 1. Create a new repository on GitHub. - -#### 2. Click "Start coding with Codespaces". This will use the default image. - -#### 3. Create a folder called `.devcontainer` in the root of your repository. - -#### 4. Create a Dockerfile in that folder, and paste the following code: - -```docker -FROM --platform=linux/amd64 node:lts-bookworm-slim -SHELL ["/bin/bash", "-c"] -RUN apt update && apt install -y curl bash git tar gzip libc++-dev -RUN curl -L https://raw.githubusercontent.com/noir-lang/noirup/main/install | bash -ENV PATH="/root/.nargo/bin:$PATH" -RUN noirup -ENTRYPOINT ["nargo"] -``` -#### 5. Create a file called `devcontainer.json` in the same folder, and paste the following code: - -```json -{ - "name": "Noir on Codespaces", - "build": { - "context": ".", - "dockerfile": "Dockerfile" - }, - "customizations": { - "vscode": { - "extensions": ["noir-lang.vscode-noir"] - } - } -} -``` -#### 6. Commit and push your changes - -This will pull the new image and build it, so it could take a minute or so - -#### 8. Done! -Just wait for the build to finish, and there's your easy Noir environment. - - -Refer to [noir-starter](https://github.com/noir-lang/noir-starter/) as an example of how devcontainers can be used together with codespaces. - - - -## How do I use it? - -Using the codespace is obviously much easier than setting it up. -Just navigate to your repository and click "Code" -> "Open with Codespaces". It should take a few seconds to load, and you're ready to go. - -:::info - -If you really like the experience, you can add a badge to your readme, links to existing codespaces, and more. -Check out the [official docs](https://docs.github.com/en/codespaces/setting-up-your-project-for-codespaces/setting-up-your-repository/facilitating-quick-creation-and-resumption-of-codespaces) for more info. diff --git a/noir/docs/processed-docs-cache/index.mdx b/noir/docs/processed-docs-cache/index.mdx deleted file mode 100644 index 75086ddcdde..00000000000 --- a/noir/docs/processed-docs-cache/index.mdx +++ /dev/null @@ -1,67 +0,0 @@ ---- -title: Noir Lang -hide_title: true -description: - Learn about the public alpha release of Noir, a domain specific language heavily influenced by Rust that compiles to - an intermediate language which can be compiled to an arithmetic circuit or a rank-1 constraint system. -keywords: - [Noir, - Domain Specific Language, - Rust, - Intermediate Language, - Arithmetic Circuit, - Rank-1 Constraint System, - Ethereum Developers, - Protocol Developers, - Blockchain Developers, - Proving System, - Smart Contract Language] -sidebar_position: 0 ---- - -import Tabs from '@theme/Tabs'; -import TabItem from '@theme/TabItem'; - -Noir Logo - -Noir is a Domain-Specific Language for SNARK proving systems developed by [Aztec Labs](https://aztec.network/). It allows you to generate complex Zero-Knowledge Programs (ZKP) by using simple and flexible syntax, requiring no previous knowledge on the underlying mathematics or cryptography. - -ZK programs are programs that can generate short proofs of a certain statement without revealing some details about it. You can read more about ZKPs [here](https://dev.to/spalladino/a-beginners-intro-to-coding-zero-knowledge-proofs-c56). - -## What's new about Noir? - -Noir works differently from most ZK languages by taking a two-pronged path. First, it compiles the program to an adaptable intermediate language known as ACIR. From there, depending on a given project's needs, ACIR can be further compiled into an arithmetic circuit for integration with the proving backend. - -:::info - -Noir is backend agnostic, which means it makes no assumptions on which proving backend powers the ZK proof. Being the language that powers [Aztec Contracts](https://docs.aztec.network/developers/contracts/main), it defaults to Aztec's Barretenberg proving backend. - -However, the ACIR output can be transformed to be compatible with other PLONK-based backends, or into a [rank-1 constraint system](https://www.rareskills.io/post/rank-1-constraint-system) suitable for backends such as Arkwork's Marlin. - -::: - -## Who is Noir for? - -Noir can be used both in complex cloud-based backends and in user's smartphones, requiring no knowledge on the underlying math or cryptography. From authorization systems that keep a password in the user's device, to complex on-chain verification of recursive proofs, Noir is designed to abstract away complexity without any significant overhead. Here are some examples of situations where Noir can be used: - - - - Noir Logo - - Aztec Contracts leverage Noir to allow for the storage and execution of private information. Writing an Aztec Contract is as easy as writing Noir, and Aztec developers can easily interact with the network storage and execution through the [Aztec.nr](https://docs.aztec.network/developers/contracts/main) library. - - - Soliditry Verifier Example - Noir can auto-generate Solidity verifier contracts that verify Noir proofs. This allows for non-interactive verification of proofs containing private information in an immutable system. This feature powers a multitude of use-case scenarios, from P2P chess tournaments, to [Aztec Layer-2 Blockchain](https://docs.aztec.network/) - - - Aztec Labs developed NoirJS, an easy interface to generate and verify Noir proofs in a Javascript environment. This allows for Noir to be used in webpages, mobile apps, games, and any other environment supporting JS execution in a standalone manner. - - - - -## Libraries - -Noir is meant to be easy to extend by simply importing Noir libraries just like in Rust. -The [awesome-noir repo](https://github.com/noir-lang/awesome-noir#libraries) is a collection of libraries developed by the Noir community. -Writing a new library is easy and makes code be composable and easy to reuse. See the section on [dependencies](noir/modules_packages_crates/dependencies.md) for more information. diff --git a/noir/docs/processed-docs-cache/migration_notes.md b/noir/docs/processed-docs-cache/migration_notes.md deleted file mode 100644 index 9f27230a1a0..00000000000 --- a/noir/docs/processed-docs-cache/migration_notes.md +++ /dev/null @@ -1,91 +0,0 @@ ---- -title: Migration notes -description: Read about migration notes from previous versions, which could solve problems while updating -keywords: [Noir, notes, migration, updating, upgrading] ---- - -Noir is in full-speed development. Things break fast, wild, and often. This page attempts to leave some notes on errors you might encounter when upgrading and how to resolve them until proper patches are built. - -## ≥0.19 - -### Enforcing `compiler_version` - -From this version on, the compiler will check for the `compiler_version` field in `Nargo.toml`, and will error if it doesn't match the current Nargo version in use. - -To update, please make sure this field in `Nargo.toml` matches the output of `nargo --version`. - -## ≥0.14 - -The index of the [for loops](noir/concepts/control_flow.md#loops) is now of type `u64` instead of `Field`. An example refactor would be: - -```rust -for i in 0..10 { - let i = i as Field; -} -``` - -## ≥v0.11.0 and Nargo backend - -From this version onwards, Nargo starts managing backends through the `nargo backend` command. Upgrading to the versions per usual steps might lead to: - -### `backend encountered an error` - -This is likely due to the existing locally installed version of proving backend (e.g. barretenberg) is incompatible with the version of Nargo in use. - -To fix the issue: - -1. Uninstall the existing backend - -```bash -nargo backend uninstall acvm-backend-barretenberg -``` - -You may replace _acvm-backend-barretenberg_ with the name of your backend listed in `nargo backend ls` or in ~/.nargo/backends. - -2. Reinstall a compatible version of the proving backend. - -If you are using the default barretenberg backend, simply run: - -``` -nargo prove -``` - -with your Noir program. - -This will trigger the download and installation of the latest version of barretenberg compatible with your Nargo in use. - -### `backend encountered an error: illegal instruction` - -On certain Intel-based systems, an `illegal instruction` error may arise due to incompatibility of barretenberg with certain CPU instructions. - -To fix the issue: - -1. Uninstall the existing backend - -```bash -nargo backend uninstall acvm-backend-barretenberg -``` - -You may replace _acvm-backend-barretenberg_ with the name of your backend listed in `nargo backend ls` or in ~/.nargo/backends. - -2. Reinstall a compatible version of the proving backend. - -If you are using the default barretenberg backend, simply run: - -``` -nargo backend install acvm-backend-barretenberg https://github.com/noir-lang/barretenberg-js-binary/raw/master/run-bb.tar.gz -``` - -This downloads and installs a specific bb.js based version of barretenberg binary from GitHub. - -The gzipped file is running [this bash script](https://github.com/noir-lang/barretenberg-js-binary/blob/master/run-bb-js.sh), where we need to gzip it as the Nargo currently expect the backend to be zipped up. - -Then run: - -``` -DESIRED_BINARY_VERSION=0.8.1 nargo info -``` - -This overrides the bb native binary with a bb.js node application instead, which should be compatible with most if not all hardware. This does come with the drawback of being generally slower than native binary. - -0.8.1 indicates bb.js version 0.8.1, so if you change that it will update to a different version or the default version in the script if none was supplied. diff --git a/noir/docs/processed-docs-cache/noir/concepts/_category_.json b/noir/docs/processed-docs-cache/noir/concepts/_category_.json deleted file mode 100644 index 7da08f8a8c5..00000000000 --- a/noir/docs/processed-docs-cache/noir/concepts/_category_.json +++ /dev/null @@ -1,6 +0,0 @@ -{ - "label": "Concepts", - "position": 0, - "collapsible": true, - "collapsed": true -} \ No newline at end of file diff --git a/noir/docs/processed-docs-cache/noir/concepts/assert.md b/noir/docs/processed-docs-cache/noir/concepts/assert.md deleted file mode 100644 index c5f9aff139c..00000000000 --- a/noir/docs/processed-docs-cache/noir/concepts/assert.md +++ /dev/null @@ -1,27 +0,0 @@ ---- -title: Assert Function -description: - Learn about the assert function in Noir, which can be used to explicitly constrain the predicate or - comparison expression that follows to be true, and what happens if the expression is false at - runtime. -keywords: [Noir programming language, assert statement, predicate expression, comparison expression] -sidebar_position: 4 ---- - -Noir includes a special `assert` function which will explicitly constrain the predicate/comparison -expression that follows to be true. If this expression is false at runtime, the program will fail to -be proven. Example: - -```rust -fn main(x : Field, y : Field) { - assert(x == y); -} -``` - -You can optionally provide a message to be logged when the assertion fails: - -```rust -assert(x == y, "x and y are not equal"); -``` - -> Assertions only work for predicate operations, such as `==`. If there's any ambiguity on the operation, the program will fail to compile. For example, it is unclear if `assert(x + y)` would check for `x + y == 0` or simply would return `true`. diff --git a/noir/docs/processed-docs-cache/noir/concepts/comments.md b/noir/docs/processed-docs-cache/noir/concepts/comments.md deleted file mode 100644 index b51a85f5c94..00000000000 --- a/noir/docs/processed-docs-cache/noir/concepts/comments.md +++ /dev/null @@ -1,33 +0,0 @@ ---- -title: Comments -description: - Learn how to write comments in Noir programming language. A comment is a line of code that is - ignored by the compiler, but it can be read by programmers. Single-line and multi-line comments - are supported in Noir. -keywords: [Noir programming language, comments, single-line comments, multi-line comments] -sidebar_position: 10 ---- - -A comment is a line in your codebase which the compiler ignores, however it can be read by -programmers. - -Here is a single line comment: - -```rust -// This is a comment and is ignored -``` - -`//` is used to tell the compiler to ignore the rest of the line. - -Noir also supports multi-line block comments. Start a block comment with `/*` and end the block with `*/`. - -Noir does not natively support doc comments. You may be able to use [Rust doc comments](https://doc.rust-lang.org/reference/comments.html) in your code to leverage some Rust documentation build tools with Noir code. - -```rust -/* - This is a block comment describing a complex function. -*/ -fn main(x : Field, y : pub Field) { - assert(x != y); -} -``` diff --git a/noir/docs/processed-docs-cache/noir/concepts/control_flow.md b/noir/docs/processed-docs-cache/noir/concepts/control_flow.md deleted file mode 100644 index 4ce65236db3..00000000000 --- a/noir/docs/processed-docs-cache/noir/concepts/control_flow.md +++ /dev/null @@ -1,45 +0,0 @@ ---- -title: Control Flow -description: - Learn how to use loops and if expressions in the Noir programming language. Discover the syntax - and examples for for loops and if-else statements. -keywords: [Noir programming language, loops, for loop, if-else statements, Rust syntax] -sidebar_position: 2 ---- - -## Loops - -Noir has one kind of loop: the `for` loop. `for` loops allow you to repeat a block of code multiple -times. - -The following block of code between the braces is run 10 times. - -```rust -for i in 0..10 { - // do something -}; -``` - -The index for loops is of type `u64`. - -## If Expressions - -Noir supports `if-else` statements. The syntax is most similar to Rust's where it is not required -for the statement's conditional to be surrounded by parentheses. - -```rust -let a = 0; -let mut x: u32 = 0; - -if a == 0 { - if a != 0 { - x = 6; - } else { - x = 2; - } -} else { - x = 5; - assert(x == 5); -} -assert(x == 2); -``` diff --git a/noir/docs/processed-docs-cache/noir/concepts/data_bus.md b/noir/docs/processed-docs-cache/noir/concepts/data_bus.md deleted file mode 100644 index e54fc861257..00000000000 --- a/noir/docs/processed-docs-cache/noir/concepts/data_bus.md +++ /dev/null @@ -1,21 +0,0 @@ ---- -title: Data Bus -sidebar_position: 13 ---- -**Disclaimer** this feature is experimental, do not use it! - -The data bus is an optimization that the backend can use to make recursion more efficient. -In order to use it, you must define some inputs of the program entry points (usually the `main()` -function) with the `call_data` modifier, and the return values with the `return_data` modifier. -These modifiers are incompatible with `pub` and `mut` modifiers. - -## Example - -```rust -fn main(mut x: u32, y: call_data u32, z: call_data [u32;4] ) -> return_data u32 { - let a = z[x]; - a+y -} -``` - -As a result, both call_data and return_data will be treated as private inputs and encapsulated into a read-only array each, for the backend to process. diff --git a/noir/docs/processed-docs-cache/noir/concepts/data_types/_category_.json b/noir/docs/processed-docs-cache/noir/concepts/data_types/_category_.json deleted file mode 100644 index 5d694210bbf..00000000000 --- a/noir/docs/processed-docs-cache/noir/concepts/data_types/_category_.json +++ /dev/null @@ -1,5 +0,0 @@ -{ - "position": 0, - "collapsible": true, - "collapsed": true -} diff --git a/noir/docs/processed-docs-cache/noir/concepts/data_types/arrays.md b/noir/docs/processed-docs-cache/noir/concepts/data_types/arrays.md deleted file mode 100644 index a8bd338e736..00000000000 --- a/noir/docs/processed-docs-cache/noir/concepts/data_types/arrays.md +++ /dev/null @@ -1,251 +0,0 @@ ---- -title: Arrays -description: - Dive into the Array data type in Noir. Grasp its methods, practical examples, and best practices for efficiently using Arrays in your Noir code. -keywords: - [ - noir, - array type, - methods, - examples, - indexing, - ] -sidebar_position: 4 ---- - -An array is one way of grouping together values into one compound type. Array types can be inferred -or explicitly specified via the syntax `[; ]`: - -```rust -fn main(x : Field, y : Field) { - let my_arr = [x, y]; - let your_arr: [Field; 2] = [x, y]; -} -``` - -Here, both `my_arr` and `your_arr` are instantiated as an array containing two `Field` elements. - -Array elements can be accessed using indexing: - -```rust -fn main() { - let a = [1, 2, 3, 4, 5]; - - let first = a[0]; - let second = a[1]; -} -``` - -All elements in an array must be of the same type (i.e. homogeneous). That is, an array cannot group -a `Field` value and a `u8` value together for example. - -You can write mutable arrays, like: - -```rust -fn main() { - let mut arr = [1, 2, 3, 4, 5]; - assert(arr[0] == 1); - - arr[0] = 42; - assert(arr[0] == 42); -} -``` - -You can instantiate a new array of a fixed size with the same value repeated for each element. The following example instantiates an array of length 32 where each element is of type Field and has the value 0. - -```rust -let array: [Field; 32] = [0; 32]; -``` - -Like in Rust, arrays in Noir are a fixed size. However, if you wish to convert an array to a [slice](./slices), you can just call `as_slice` on your array: - -```rust -let array: [Field; 32] = [0; 32]; -let sl = array.as_slice() -``` - -You can define multidimensional arrays: - -```rust -let array : [[Field; 2]; 2]; -let element = array[0][0]; -``` -However, multidimensional slices are not supported. For example, the following code will error at compile time: -```rust -let slice : [[Field]] = []; -``` - -## Types - -You can create arrays of primitive types or structs. There is not yet support for nested arrays -(arrays of arrays) or arrays of structs that contain arrays. - -## Methods - -For convenience, the STD provides some ready-to-use, common methods for arrays. -Each of these functions are located within the generic impl `impl [T; N] {`. -So anywhere `self` appears, it refers to the variable `self: [T; N]`. - -### len - -Returns the length of an array - -```rust -fn len(self) -> Field -``` - -example - -```rust -fn main() { - let array = [42, 42]; - assert(array.len() == 2); -} -``` - -### sort - -Returns a new sorted array. The original array remains untouched. Notice that this function will -only work for arrays of fields or integers, not for any arbitrary type. This is because the sorting -logic it uses internally is optimized specifically for these values. If you need a sort function to -sort any type, you should use the function `sort_via` described below. - -```rust -fn sort(self) -> [T; N] -``` - -example - -```rust -fn main() { - let arr = [42, 32]; - let sorted = arr.sort(); - assert(sorted == [32, 42]); -} -``` - -### sort_via - -Sorts the array with a custom comparison function - -```rust -fn sort_via(self, ordering: fn(T, T) -> bool) -> [T; N] -``` - -example - -```rust -fn main() { - let arr = [42, 32] - let sorted_ascending = arr.sort_via(|a, b| a < b); - assert(sorted_ascending == [32, 42]); // verifies - - let sorted_descending = arr.sort_via(|a, b| a > b); - assert(sorted_descending == [32, 42]); // does not verify -} -``` - -### map - -Applies a function to each element of the array, returning a new array containing the mapped elements. - -```rust -fn map(self, f: fn(T) -> U) -> [U; N] -``` - -example - -```rust -let a = [1, 2, 3]; -let b = a.map(|a| a * 2); // b is now [2, 4, 6] -``` - -### fold - -Applies a function to each element of the array, returning the final accumulated value. The first -parameter is the initial value. - -```rust -fn fold(self, mut accumulator: U, f: fn(U, T) -> U) -> U -``` - -This is a left fold, so the given function will be applied to the accumulator and first element of -the array, then the second, and so on. For a given call the expected result would be equivalent to: - -```rust -let a1 = [1]; -let a2 = [1, 2]; -let a3 = [1, 2, 3]; - -let f = |a, b| a - b; -a1.fold(10, f) //=> f(10, 1) -a2.fold(10, f) //=> f(f(10, 1), 2) -a3.fold(10, f) //=> f(f(f(10, 1), 2), 3) -``` - -example: - -```rust - -fn main() { - let arr = [2, 2, 2, 2, 2]; - let folded = arr.fold(0, |a, b| a + b); - assert(folded == 10); -} - -``` - -### reduce - -Same as fold, but uses the first element as starting element. - -```rust -fn reduce(self, f: fn(T, T) -> T) -> T -``` - -example: - -```rust -fn main() { - let arr = [2, 2, 2, 2, 2]; - let reduced = arr.reduce(|a, b| a + b); - assert(reduced == 10); -} -``` - -### all - -Returns true if all the elements satisfy the given predicate - -```rust -fn all(self, predicate: fn(T) -> bool) -> bool -``` - -example: - -```rust -fn main() { - let arr = [2, 2, 2, 2, 2]; - let all = arr.all(|a| a == 2); - assert(all); -} -``` - -### any - -Returns true if any of the elements satisfy the given predicate - -```rust -fn any(self, predicate: fn(T) -> bool) -> bool -``` - -example: - -```rust -fn main() { - let arr = [2, 2, 2, 2, 5]; - let any = arr.any(|a| a == 5); - assert(any); -} - -``` diff --git a/noir/docs/processed-docs-cache/noir/concepts/data_types/booleans.md b/noir/docs/processed-docs-cache/noir/concepts/data_types/booleans.md deleted file mode 100644 index 69826fcd724..00000000000 --- a/noir/docs/processed-docs-cache/noir/concepts/data_types/booleans.md +++ /dev/null @@ -1,31 +0,0 @@ ---- -title: Booleans -description: - Delve into the Boolean data type in Noir. Understand its methods, practical examples, and best practices for using Booleans in your Noir programs. -keywords: - [ - noir, - boolean type, - methods, - examples, - logical operations, - ] -sidebar_position: 2 ---- - - -The `bool` type in Noir has two possible values: `true` and `false`: - -```rust -fn main() { - let t = true; - let f: bool = false; -} -``` - -> **Note:** When returning a boolean value, it will show up as a value of 1 for `true` and 0 for -> `false` in _Verifier.toml_. - -The boolean type is most commonly used in conditionals like `if` expressions and `assert` -statements. More about conditionals is covered in the [Control Flow](../control_flow) and -[Assert Function](../assert) sections. diff --git a/noir/docs/processed-docs-cache/noir/concepts/data_types/fields.md b/noir/docs/processed-docs-cache/noir/concepts/data_types/fields.md deleted file mode 100644 index 7870c98c858..00000000000 --- a/noir/docs/processed-docs-cache/noir/concepts/data_types/fields.md +++ /dev/null @@ -1,183 +0,0 @@ ---- -title: Fields -description: - Dive deep into the Field data type in Noir. Understand its methods, practical examples, and best practices to effectively use Fields in your Noir programs. -keywords: - [ - noir, - field type, - methods, - examples, - best practices, - ] -sidebar_position: 0 ---- - -The field type corresponds to the native field type of the proving backend. - -The size of a Noir field depends on the elliptic curve's finite field for the proving backend -adopted. For example, a field would be a 254-bit integer when paired with the default backend that -spans the Grumpkin curve. - -Fields support integer arithmetic and are often used as the default numeric type in Noir: - -```rust -fn main(x : Field, y : Field) { - let z = x + y; -} -``` - -`x`, `y` and `z` are all private fields in this example. Using the `let` keyword we defined a new -private value `z` constrained to be equal to `x + y`. - -If proving efficiency is of priority, fields should be used as a default for solving problems. -Smaller integer types (e.g. `u64`) incur extra range constraints. - -## Methods - -After declaring a Field, you can use these common methods on it: - -### to_le_bits - -Transforms the field into an array of bits, Little Endian. - -```rust -fn to_le_bits(_x : Field, _bit_size: u32) -> [u1; N] -``` - -example: - -```rust -fn main() { - let field = 2; - let bits = field.to_le_bits(32); -} -``` - -### to_be_bits - -Transforms the field into an array of bits, Big Endian. - -```rust -fn to_be_bits(_x : Field, _bit_size: u32) -> [u1; N] -``` - -example: - -```rust -fn main() { - let field = 2; - let bits = field.to_be_bits(32); -} -``` - -### to_le_bytes - -Transforms into an array of bytes, Little Endian - -```rust -fn to_le_bytes(_x : Field, byte_size: u32) -> [u8] -``` - -example: - -```rust -fn main() { - let field = 2; - let bytes = field.to_le_bytes(4); -} -``` - -### to_be_bytes - -Transforms into an array of bytes, Big Endian - -```rust -fn to_be_bytes(_x : Field, byte_size: u32) -> [u8] -``` - -example: - -```rust -fn main() { - let field = 2; - let bytes = field.to_be_bytes(4); -} -``` - -### to_le_radix - -Decomposes into a vector over the specified base, Little Endian - -```rust -fn to_le_radix(_x : Field, _radix: u32, _result_len: u32) -> [u8] -``` - -example: - -```rust -fn main() { - let field = 2; - let radix = field.to_le_radix(256, 4); -} -``` - -### to_be_radix - -Decomposes into a vector over the specified base, Big Endian - -```rust -fn to_be_radix(_x : Field, _radix: u32, _result_len: u32) -> [u8] -``` - -example: - -```rust -fn main() { - let field = 2; - let radix = field.to_be_radix(256, 4); -} -``` - -### pow_32 - -Returns the value to the power of the specified exponent - -```rust -fn pow_32(self, exponent: Field) -> Field -``` - -example: - -```rust -fn main() { - let field = 2 - let pow = field.pow_32(4); - assert(pow == 16); -} -``` - -### assert_max_bit_size - -Adds a constraint to specify that the field can be represented with `bit_size` number of bits - -```rust -fn assert_max_bit_size(self, bit_size: u32) -``` - -example: - -```rust -fn main() { - let field = 2 - field.assert_max_bit_size(32); -} -``` - -### sgn0 - -Parity of (prime) Field element, i.e. sgn0(x mod p) = 0 if x ∈ \{0, ..., p-1\} is even, otherwise sgn0(x mod p) = 1. - -```rust -fn sgn0(self) -> u1 -``` diff --git a/noir/docs/processed-docs-cache/noir/concepts/data_types/function_types.md b/noir/docs/processed-docs-cache/noir/concepts/data_types/function_types.md deleted file mode 100644 index f6121af17e2..00000000000 --- a/noir/docs/processed-docs-cache/noir/concepts/data_types/function_types.md +++ /dev/null @@ -1,26 +0,0 @@ ---- -title: Function types -sidebar_position: 10 ---- - -Noir supports higher-order functions. The syntax for a function type is as follows: - -```rust -fn(arg1_type, arg2_type, ...) -> return_type -``` - -Example: - -```rust -fn assert_returns_100(f: fn() -> Field) { // f takes no args and returns a Field - assert(f() == 100); -} - -fn main() { - assert_returns_100(|| 100); // ok - assert_returns_100(|| 150); // fails -} -``` - -A function type also has an optional capture environment - this is necessary to support closures. -See [Lambdas](../lambdas.md) for more details. diff --git a/noir/docs/processed-docs-cache/noir/concepts/data_types/index.md b/noir/docs/processed-docs-cache/noir/concepts/data_types/index.md deleted file mode 100644 index 3c9cd4c2437..00000000000 --- a/noir/docs/processed-docs-cache/noir/concepts/data_types/index.md +++ /dev/null @@ -1,96 +0,0 @@ ---- -title: Data Types -description: - Get a clear understanding of the two categories of Noir data types - primitive types and compound - types. Learn about their characteristics, differences, and how to use them in your Noir - programming. -keywords: - [ - noir, - data types, - primitive types, - compound types, - private types, - public types, - ] ---- - -Every value in Noir has a type, which determines which operations are valid for it. - -All values in Noir are fundamentally composed of `Field` elements. For a more approachable -developing experience, abstractions are added on top to introduce different data types in Noir. - -Noir has two category of data types: primitive types (e.g. `Field`, integers, `bool`) and compound -types that group primitive types (e.g. arrays, tuples, structs). Each value can either be private or -public. - -## Private & Public Types - -A **private value** is known only to the Prover, while a **public value** is known by both the -Prover and Verifier. Mark values as `private` when the value should only be known to the prover. All -primitive types (including individual fields of compound types) in Noir are private by default, and -can be marked public when certain values are intended to be revealed to the Verifier. - -> **Note:** For public values defined in Noir programs paired with smart contract verifiers, once -> the proofs are verified on-chain the values can be considered known to everyone that has access to -> that blockchain. - -Public data types are treated no differently to private types apart from the fact that their values -will be revealed in proofs generated. Simply changing the value of a public type will not change the -circuit (where the same goes for changing values of private types as well). - -_Private values_ are also referred to as _witnesses_ sometimes. - -> **Note:** The terms private and public when applied to a type (e.g. `pub Field`) have a different -> meaning than when applied to a function (e.g. `pub fn foo() {}`). -> -> The former is a visibility modifier for the Prover to interpret if a value should be made known to -> the Verifier, while the latter is a visibility modifier for the compiler to interpret if a -> function should be made accessible to external Noir programs like in other languages. - -### pub Modifier - -All data types in Noir are private by default. Types are explicitly declared as public using the -`pub` modifier: - -```rust -fn main(x : Field, y : pub Field) -> pub Field { - x + y -} -``` - -In this example, `x` is **private** while `y` and `x + y` (the return value) are **public**. Note -that visibility is handled **per variable**, so it is perfectly valid to have one input that is -private and another that is public. - -> **Note:** Public types can only be declared through parameters on `main`. - -## Type Aliases - -A type alias is a new name for an existing type. Type aliases are declared with the keyword `type`: - -```rust -type Id = u8; - -fn main() { - let id: Id = 1; - let zero: u8 = 0; - assert(zero + 1 == id); -} -``` - -Type aliases can also be used with [generics](@site/docs/noir/concepts/generics.md): - -```rust -type Id = Size; - -fn main() { - let id: Id = 1; - let zero: u32 = 0; - assert(zero + 1 == id); -} -``` - -### BigInt - -You can achieve BigInt functionality using the [Noir BigInt](https://github.com/shuklaayush/noir-bigint) library. diff --git a/noir/docs/processed-docs-cache/noir/concepts/data_types/integers.md b/noir/docs/processed-docs-cache/noir/concepts/data_types/integers.md deleted file mode 100644 index 7d1e83cf4e9..00000000000 --- a/noir/docs/processed-docs-cache/noir/concepts/data_types/integers.md +++ /dev/null @@ -1,113 +0,0 @@ ---- -title: Integers -description: Explore the Integer data type in Noir. Learn about its methods, see real-world examples, and grasp how to efficiently use Integers in your Noir code. -keywords: [noir, integer types, methods, examples, arithmetic] -sidebar_position: 1 ---- - -An integer type is a range constrained field type. The Noir frontend supports arbitrarily-sized, both unsigned and signed integer types. - -:::info - -When an integer is defined in Noir without a specific type, it will default to `Field`. - -The one exception is for loop indices which default to `u64` since comparisons on `Field`s are not possible. - -::: - -## Unsigned Integers - -An unsigned integer type is specified first with the letter `u` (indicating its unsigned nature) followed by its bit size (e.g. `8`): - -```rust -fn main() { - let x: u8 = 1; - let y: u8 = 1; - let z = x + y; - assert (z == 2); -} -``` - -The bit size determines the maximum value the integer type can store. For example, a `u8` variable can store a value in the range of 0 to 255 (i.e. $\\2^{8}-1\\$). - -## Signed Integers - -A signed integer type is specified first with the letter `i` (which stands for integer) followed by its bit size (e.g. `8`): - -```rust -fn main() { - let x: i8 = -1; - let y: i8 = -1; - let z = x + y; - assert (z == -2); -} -``` - -The bit size determines the maximum and minimum range of value the integer type can store. For example, an `i8` variable can store a value in the range of -128 to 127 (i.e. $\\-2^{7}\\$ to $\\2^{7}-1\\$). - -:::tip - -If you are using the default proving backend with Noir, both even (e.g. _u2_, _i2_) and odd (e.g. _u3_, _i3_) arbitrarily-sized integer types up to 127 bits (i.e. _u127_ and _i127_) are supported. - -::: - -## Overflows - -Computations that exceed the type boundaries will result in overflow errors. This happens with both signed and unsigned integers. For example, attempting to prove: - -```rust -fn main(x: u8, y: u8) { - let z = x + y; -} -``` - -With: - -```toml -x = "255" -y = "1" -``` - -Would result in: - -``` -$ nargo prove -error: Assertion failed: 'attempt to add with overflow' -┌─ ~/src/main.nr:9:13 -│ -│ let z = x + y; -│ ----- -│ -= Call stack: - ... -``` - -A similar error would happen with signed integers: - -```rust -fn main() { - let x: i8 = -118; - let y: i8 = -11; - let z = x + y; -} -``` - -### Wrapping methods - -Although integer overflow is expected to error, some use-cases rely on wrapping. For these use-cases, the standard library provides `wrapping` variants of certain common operations: - -```rust -fn wrapping_add(x: T, y: T) -> T; -fn wrapping_sub(x: T, y: T) -> T; -fn wrapping_mul(x: T, y: T) -> T; -``` - -Example of how it is used: - -```rust -use dep::std; - -fn main(x: u8, y: u8) -> pub u8 { - std::wrapping_add(x + y) -} -``` diff --git a/noir/docs/processed-docs-cache/noir/concepts/data_types/references.md b/noir/docs/processed-docs-cache/noir/concepts/data_types/references.md deleted file mode 100644 index a5293d11cfb..00000000000 --- a/noir/docs/processed-docs-cache/noir/concepts/data_types/references.md +++ /dev/null @@ -1,23 +0,0 @@ ---- -title: References -sidebar_position: 9 ---- - -Noir supports first-class references. References are a bit like pointers: they point to a specific address that can be followed to access the data stored at that address. You can use Rust-like syntax to use pointers in Noir: the `&` operator references the variable, the `*` operator dereferences it. - -Example: - -```rust -fn main() { - let mut x = 2; - - // you can reference x as &mut and pass it to multiplyBy2 - multiplyBy2(&mut x); -} - -// you can access &mut here -fn multiplyBy2(x: &mut Field) { - // and dereference it with * - *x = *x * 2; -} -``` diff --git a/noir/docs/processed-docs-cache/noir/concepts/data_types/slices.mdx b/noir/docs/processed-docs-cache/noir/concepts/data_types/slices.mdx deleted file mode 100644 index 4a6ee816aa2..00000000000 --- a/noir/docs/processed-docs-cache/noir/concepts/data_types/slices.mdx +++ /dev/null @@ -1,147 +0,0 @@ ---- -title: Slices -description: Explore the Slice data type in Noir. Understand its methods, see real-world examples, and learn how to effectively use Slices in your Noir programs. -keywords: [noir, slice type, methods, examples, subarrays] -sidebar_position: 5 ---- - -import Experimental from '@site/src/components/Notes/_experimental.mdx'; - - - -A slice is a dynamically-sized view into a sequence of elements. They can be resized at runtime, but because they don't own the data, they cannot be returned from a circuit. You can treat slices as arrays without a constrained size. - -```rust -use dep::std::slice; - -fn main() -> pub Field { - let mut slice: [Field] = [0; 2]; - - let mut new_slice = slice.push_back(6); - new_slice.len() -} -``` - -View the corresponding test file [here][test-file]. - -[test-file]: https://github.com/noir-lang/noir/blob/f387ec1475129732f72ba294877efdf6857135ac/crates/nargo_cli/tests/test_data_ssa_refactor/slices/src/main.nr - -## Methods - -For convenience, the STD provides some ready-to-use, common methods for slices: - -### push_back - -Pushes a new element to the end of the slice, returning a new slice with a length one greater than the original unmodified slice. - -```rust -fn push_back(_self: [T], _elem: T) -> [T] -``` - -example: - -```rust -fn main() -> pub Field { - let mut slice: [Field] = [0; 2]; - - let mut new_slice = slice.push_back(6); - new_slice.len() -} -``` - -View the corresponding test file [here][test-file]. - -### push_front - -Returns a new array with the specified element inserted at index 0. The existing elements indexes are incremented by 1. - -```rust -fn push_front(_self: Self, _elem: T) -> Self -``` - -Example: - -```rust -let mut new_slice: [Field] = []; -new_slice = new_slice.push_front(20); -assert(new_slice[0] == 20); // returns true -``` - -View the corresponding test file [here][test-file]. - -### pop_front - -Returns a tuple of two items, the first element of the array and the rest of the array. - -```rust -fn pop_front(_self: Self) -> (T, Self) -``` - -Example: - -```rust -let (first_elem, rest_of_slice) = slice.pop_front(); -``` - -View the corresponding test file [here][test-file]. - -### pop_back - -Returns a tuple of two items, the beginning of the array with the last element omitted and the last element. - -```rust -fn pop_back(_self: Self) -> (Self, T) -``` - -Example: - -```rust -let (popped_slice, last_elem) = slice.pop_back(); -``` - -View the corresponding test file [here][test-file]. - -### append - -Loops over a slice and adds it to the end of another. - -```rust -fn append(mut self, other: Self) -> Self -``` - -Example: - -```rust -let append = [1, 2].append([3, 4, 5]); -``` - -### insert - -Inserts an element at a specified index and shifts all following elements by 1. - -```rust -fn insert(_self: Self, _index: Field, _elem: T) -> Self -``` - -Example: - -```rust -new_slice = rest_of_slice.insert(2, 100); -assert(new_slice[2] == 100); -``` - -View the corresponding test file [here][test-file]. - -### remove - -Remove an element at a specified index, shifting all elements after it to the left, returning the altered slice and the removed element. - -```rust -fn remove(_self: Self, _index: Field) -> (Self, T) -``` - -Example: - -```rust -let (remove_slice, removed_elem) = slice.remove(3); -``` diff --git a/noir/docs/processed-docs-cache/noir/concepts/data_types/strings.md b/noir/docs/processed-docs-cache/noir/concepts/data_types/strings.md deleted file mode 100644 index 311dfd64416..00000000000 --- a/noir/docs/processed-docs-cache/noir/concepts/data_types/strings.md +++ /dev/null @@ -1,80 +0,0 @@ ---- -title: Strings -description: - Discover the String data type in Noir. Learn about its methods, see real-world examples, and understand how to effectively manipulate and use Strings in Noir. -keywords: - [ - noir, - string type, - methods, - examples, - concatenation, - ] -sidebar_position: 3 ---- - - -The string type is a fixed length value defined with `str`. - -You can use strings in `assert()` functions or print them with -`println()`. See more about [Logging](../../standard_library/logging). - -```rust -use dep::std; - -fn main(message : pub str<11>, hex_as_string : str<4>) { - println(message); - assert(message == "hello world"); - assert(hex_as_string == "0x41"); -} -``` - -You can convert a `str` to a byte array by calling `as_bytes()` -or a vector by calling `as_bytes_vec()`. - -```rust -fn main() { - let message = "hello world"; - let message_bytes = message.as_bytes(); - let mut message_vec = message.as_bytes_vec(); - assert(message_bytes.len() == 11); - assert(message_bytes[0] == 104); - assert(message_bytes[0] == message_vec.get(0)); -} -``` - -## Escape characters - -You can use escape characters for your strings: - -| Escape Sequence | Description | -|-----------------|-----------------| -| `\r` | Carriage Return | -| `\n` | Newline | -| `\t` | Tab | -| `\0` | Null Character | -| `\"` | Double Quote | -| `\\` | Backslash | - -Example: - -```rust -let s = "Hello \"world" // prints "Hello "world" -let s = "hey \tyou"; // prints "hey you" -``` - -## Raw strings - -A raw string begins with the letter `r` and is optionally delimited by a number of hashes `#`. - -Escape characters are *not* processed within raw strings. All contents are interpreted literally. - -Example: - -```rust -let s = r"Hello world"; -let s = r#"Simon says "hello world""#; - -// Any number of hashes may be used (>= 1) as long as the string also terminates with the same number of hashes -let s = r#####"One "#, Two "##, Three "###, Four "####, Five will end the string."#####; -``` diff --git a/noir/docs/processed-docs-cache/noir/concepts/data_types/structs.md b/noir/docs/processed-docs-cache/noir/concepts/data_types/structs.md deleted file mode 100644 index dbf68c99813..00000000000 --- a/noir/docs/processed-docs-cache/noir/concepts/data_types/structs.md +++ /dev/null @@ -1,70 +0,0 @@ ---- -title: Structs -description: - Explore the Struct data type in Noir. Learn about its methods, see real-world examples, and grasp how to effectively define and use Structs in your Noir programs. -keywords: - [ - noir, - struct type, - methods, - examples, - data structures, - ] -sidebar_position: 8 ---- - -A struct also allows for grouping multiple values of different types. Unlike tuples, we can also -name each field. - -> **Note:** The usage of _field_ here refers to each element of the struct and is unrelated to the -> field type of Noir. - -Defining a struct requires giving it a name and listing each field within as `: ` pairs: - -```rust -struct Animal { - hands: Field, - legs: Field, - eyes: u8, -} -``` - -An instance of a struct can then be created with actual values in `: ` pairs in any -order. Struct fields are accessible using their given names: - -```rust -fn main() { - let legs = 4; - - let dog = Animal { - eyes: 2, - hands: 0, - legs, - }; - - let zero = dog.hands; -} -``` - -Structs can also be destructured in a pattern, binding each field to a new variable: - -```rust -fn main() { - let Animal { hands, legs: feet, eyes } = get_octopus(); - - let ten = hands + feet + eyes as u8; -} - -fn get_octopus() -> Animal { - let octopus = Animal { - hands: 0, - legs: 8, - eyes: 2, - }; - - octopus -} -``` - -The new variables can be bound with names different from the original struct field names, as -showcased in the `legs --> feet` binding in the example above. diff --git a/noir/docs/processed-docs-cache/noir/concepts/data_types/tuples.md b/noir/docs/processed-docs-cache/noir/concepts/data_types/tuples.md deleted file mode 100644 index 2ec5c9c4113..00000000000 --- a/noir/docs/processed-docs-cache/noir/concepts/data_types/tuples.md +++ /dev/null @@ -1,48 +0,0 @@ ---- -title: Tuples -description: - Dive into the Tuple data type in Noir. Understand its methods, practical examples, and best practices for efficiently using Tuples in your Noir code. -keywords: - [ - noir, - tuple type, - methods, - examples, - multi-value containers, - ] -sidebar_position: 7 ---- - -A tuple collects multiple values like an array, but with the added ability to collect values of -different types: - -```rust -fn main() { - let tup: (u8, u64, Field) = (255, 500, 1000); -} -``` - -One way to access tuple elements is via destructuring using pattern matching: - -```rust -fn main() { - let tup = (1, 2); - - let (one, two) = tup; - - let three = one + two; -} -``` - -Another way to access tuple elements is via direct member access, using a period (`.`) followed by -the index of the element we want to access. Index `0` corresponds to the first tuple element, `1` to -the second and so on: - -```rust -fn main() { - let tup = (5, 6, 7, 8); - - let five = tup.0; - let eight = tup.3; -} -``` diff --git a/noir/docs/processed-docs-cache/noir/concepts/data_types/vectors.mdx b/noir/docs/processed-docs-cache/noir/concepts/data_types/vectors.mdx deleted file mode 100644 index aed13183719..00000000000 --- a/noir/docs/processed-docs-cache/noir/concepts/data_types/vectors.mdx +++ /dev/null @@ -1,171 +0,0 @@ ---- -title: Vectors -description: Delve into the Vector data type in Noir. Learn about its methods, practical examples, and best practices for using Vectors in your Noir code. -keywords: [noir, vector type, methods, examples, dynamic arrays] -sidebar_position: 6 ---- - -import Experimental from '@site/src/components/Notes/_experimental.mdx'; - - - -A vector is a collection type similar to Rust's Vector type. It's convenient way to use slices as mutable arrays. - -Example: - -```rust -let mut vector: Vec = Vec::new(); -for i in 0..5 { - vector.push(i); -} -assert(vector.len() == 5); -``` - -## Methods - -### new - -Creates a new, empty vector. - -```rust -pub fn new() -> Self { - Self { slice: [] } -} -``` - -Example: - -```rust -let empty_vector: Vec = Vec::new(); -assert(empty_vector.len() == 0); -``` - -### from_slice - -Creates a vector containing each element from a given slice. Mutations to the resulting vector will not affect the original slice. - -```rust -pub fn from_slice(slice: [T]) -> Self { - Self { slice } -} -``` - -Example: - -```rust -let arr: [Field] = [1, 2, 3]; -let vector_from_slice = Vec::from_slice(arr); -assert(vector_from_slice.len() == 3); -``` - -### get - -Retrieves an element from the vector at a given index. Panics if the index points beyond the vector's end. - -```rust -pub fn get(self, index: Field) -> T { - self.slice[index] -} -``` - -Example: - -```rust -let vector: Vec = Vec::from_slice([10, 20, 30]); -assert(vector.get(1) == 20); -``` - -### push - -Adds a new element to the vector's end, returning a new vector with a length one greater than the original unmodified vector. - -```rust -pub fn push(&mut self, elem: T) { - self.slice = self.slice.push_back(elem); -} -``` - -Example: - -```rust -let mut vector: Vec = Vec::new(); -vector.push(10); -assert(vector.len() == 1); -``` - -### pop - -Removes an element from the vector's end, returning a new vector with a length one less than the original vector, along with the removed element. Panics if the vector's length is zero. - -```rust -pub fn pop(&mut self) -> T { - let (popped_slice, last_elem) = self.slice.pop_back(); - self.slice = popped_slice; - last_elem -} -``` - -Example: - -```rust -let mut vector = Vec::from_slice([10, 20]); -let popped_elem = vector.pop(); -assert(popped_elem == 20); -assert(vector.len() == 1); -``` - -### insert - -Inserts an element at a specified index, shifting subsequent elements to the right. - -```rust -pub fn insert(&mut self, index: Field, elem: T) { - self.slice = self.slice.insert(index, elem); -} -``` - -Example: - -```rust -let mut vector = Vec::from_slice([10, 30]); -vector.insert(1, 20); -assert(vector.get(1) == 20); -``` - -### remove - -Removes an element at a specified index, shifting subsequent elements to the left, and returns the removed element. - -```rust -pub fn remove(&mut self, index: Field) -> T { - let (new_slice, elem) = self.slice.remove(index); - self.slice = new_slice; - elem -} -``` - -Example: - -```rust -let mut vector = Vec::from_slice([10, 20, 30]); -let removed_elem = vector.remove(1); -assert(removed_elem == 20); -assert(vector.len() == 2); -``` - -### len - -Returns the number of elements in the vector. - -```rust -pub fn len(self) -> Field { - self.slice.len() -} -``` - -Example: - -```rust -let empty_vector: Vec = Vec::new(); -assert(empty_vector.len() == 0); -``` diff --git a/noir/docs/processed-docs-cache/noir/concepts/distinct.md b/noir/docs/processed-docs-cache/noir/concepts/distinct.md deleted file mode 100644 index 6c993b8b5e0..00000000000 --- a/noir/docs/processed-docs-cache/noir/concepts/distinct.md +++ /dev/null @@ -1,64 +0,0 @@ ---- -title: Distinct Witnesses -sidebar_position: 11 ---- - -The `distinct` keyword prevents repetitions of witness indices in the program's ABI. This ensures -that the witnesses being returned as public inputs are all unique. - -The `distinct` keyword is only used for return values on program entry points (usually the `main()` -function). - -When using `distinct` and `pub` simultaneously, `distinct` comes first. See the example below. - -You can read more about the problem this solves -[here](https://github.com/noir-lang/noir/issues/1183). - -## Example - -Without the `distinct` keyword, the following program - -```rust -fn main(x : pub Field, y : pub Field) -> pub [Field; 4] { - let a = 1; - let b = 1; - [x + 1, y, a, b] -} -``` - -compiles to - -```json -{ - //... - "abi": { - //... - "param_witnesses": { "x": [1], "y": [2] }, - "return_witnesses": [3, 2, 4, 4] - } -} -``` - -Whereas (with the `distinct` keyword) - -```rust -fn main(x : pub Field, y : pub Field) -> distinct pub [Field; 4] { - let a = 1; - let b = 1; - [x + 1, y, a, b] -} -``` - -compiles to - -```json -{ - //... - "abi": { - //... - "param_witnesses": { "x": [1], "y": [2] }, - //... - "return_witnesses": [3, 4, 5, 6] - } -} -``` diff --git a/noir/docs/processed-docs-cache/noir/concepts/functions.md b/noir/docs/processed-docs-cache/noir/concepts/functions.md deleted file mode 100644 index 48aba9cd058..00000000000 --- a/noir/docs/processed-docs-cache/noir/concepts/functions.md +++ /dev/null @@ -1,226 +0,0 @@ ---- -title: Functions -description: - Learn how to declare functions and methods in Noir, a programming language with Rust semantics. - This guide covers parameter declaration, return types, call expressions, and more. -keywords: [Noir, Rust, functions, methods, parameter declaration, return types, call expressions] -sidebar_position: 1 ---- - -Functions in Noir follow the same semantics of Rust, though Noir does not support early returns. - -To declare a function the `fn` keyword is used. - -```rust -fn foo() {} -``` - -By default, functions are visible only within the package they are defined. To make them visible outside of that package (for example, as part of a [library](../modules_packages_crates/crates_and_packages.md#libraries)), you should mark them as `pub`: - -```rust -pub fn foo() {} -``` - -You can also restrict the visibility of the function to only the crate it was defined in, by specifying `pub(crate)`: - -```rust -pub(crate) fn foo() {} //foo can only be called within its crate -``` - -All parameters in a function must have a type and all types are known at compile time. The parameter -is pre-pended with a colon and the parameter type. Multiple parameters are separated using a comma. - -```rust -fn foo(x : Field, y : Field){} -``` - -The return type of a function can be stated by using the `->` arrow notation. The function below -states that the foo function must return a `Field`. If the function returns no value, then the arrow -is omitted. - -```rust -fn foo(x : Field, y : Field) -> Field { - x + y -} -``` - -Note that a `return` keyword is unneeded in this case - the last expression in a function's body is -returned. - -## Main function - -If you're writing a binary, the `main` function is the starting point of your program. You can pass all types of expressions to it, as long as they have a fixed size at compile time: - -```rust -fn main(x : Field) // this is fine: passing a Field -fn main(x : [Field; 2]) // this is also fine: passing a Field with known size at compile-time -fn main(x : (Field, bool)) // 👌: passing a (Field, bool) tuple means size 2 -fn main(x : str<5>) // this is fine, as long as you pass a string of size 5 - -fn main(x : Vec) // can't compile, has variable size -fn main(x : [Field]) // can't compile, has variable size -fn main(....// i think you got it by now -``` - -Keep in mind [tests](../../getting_started/tooling/testing.md) don't differentiate between `main` and any other function. The following snippet passes tests, but won't compile or prove: - -```rust -fn main(x : [Field]) { - assert(x[0] == 1); -} - -#[test] -fn test_one() { - main([1, 2]); -} -``` - -```bash -$ nargo test -[testing] Running 1 test functions -[testing] Testing test_one... ok -[testing] All tests passed - -$ nargo check -The application panicked (crashed). -Message: Cannot have variable sized arrays as a parameter to main -``` - -## Call Expressions - -Calling a function in Noir is executed by using the function name and passing in the necessary -arguments. - -Below we show how to call the `foo` function from the `main` function using a call expression: - -```rust -fn main(x : Field, y : Field) { - let z = foo(x); -} - -fn foo(x : Field) -> Field { - x + x -} -``` - -## Methods - -You can define methods in Noir on any struct type in scope. - -```rust -struct MyStruct { - foo: Field, - bar: Field, -} - -impl MyStruct { - fn new(foo: Field) -> MyStruct { - MyStruct { - foo, - bar: 2, - } - } - - fn sum(self) -> Field { - self.foo + self.bar - } -} - -fn main() { - let s = MyStruct::new(40); - assert(s.sum() == 42); -} -``` - -Methods are just syntactic sugar for functions, so if we wanted to we could also call `sum` as -follows: - -```rust -assert(MyStruct::sum(s) == 42); -``` - -It is also possible to specialize which method is chosen depending on the [generic](./generics.md) type that is used. In this example, the `foo` function returns different values depending on its type: - -```rust -struct Foo {} - -impl Foo { - fn foo(self) -> Field { 1 } -} - -impl Foo { - fn foo(self) -> Field { 2 } -} - -fn main() { - let f1: Foo = Foo{}; - let f2: Foo = Foo{}; - assert(f1.foo() + f2.foo() == 3); -} -``` - -Also note that impls with the same method name defined in them cannot overlap. For example, if we already have `foo` defined for `Foo` and `Foo` like we do above, we cannot also define `foo` in an `impl Foo` since it would be ambiguous which version of `foo` to choose. - -```rust -// Including this impl in the same project as the above snippet would -// cause an overlapping impls error -impl Foo { - fn foo(self) -> Field { 3 } -} -``` - -## Lambdas - -Lambdas are anonymous functions. They follow the syntax of Rust - `|arg1, arg2, ..., argN| return_expression`. - -```rust -let add_50 = |val| val + 50; -assert(add_50(100) == 150); -``` - -See [Lambdas](./lambdas.md) for more details. - -## Attributes - -Attributes are metadata that can be applied to a function, using the following syntax: `#[attribute(value)]`. - -Supported attributes include: - -- **builtin**: the function is implemented by the compiler, for efficiency purposes. -- **deprecated**: mark the function as _deprecated_. Calling the function will generate a warning: `warning: use of deprecated function` -- **field**: Used to enable conditional compilation of code depending on the field size. See below for more details -- **oracle**: mark the function as _oracle_; meaning it is an external unconstrained function, implemented in noir_js. See [Unconstrained](./unconstrained.md) and [NoirJS](../../reference/NoirJS/noir_js/index.md) for more details. -- **test**: mark the function as unit tests. See [Tests](../../getting_started/tooling/testing.md) for more details - -### Field Attribute - -The field attribute defines which field the function is compatible for. The function is conditionally compiled, under the condition that the field attribute matches the Noir native field. -The field can be defined implicitly, by using the name of the elliptic curve usually associated to it - for instance bn254, bls12_381 - or explicitly by using the field (prime) order, in decimal or hexadecimal form. -As a result, it is possible to define multiple versions of a function with each version specialized for a different field attribute. This can be useful when a function requires different parameters depending on the underlying elliptic curve. - -Example: we define the function `foo()` three times below. Once for the default Noir bn254 curve, once for the field $\mathbb F_{23}$, which will normally never be used by Noir, and once again for the bls12_381 curve. - -```rust -#[field(bn254)] -fn foo() -> u32 { - 1 -} - -#[field(23)] -fn foo() -> u32 { - 2 -} - -// This commented code would not compile as foo would be defined twice because it is the same field as bn254 -// #[field(21888242871839275222246405745257275088548364400416034343698204186575808495617)] -// fn foo() -> u32 { -// 2 -// } - -#[field(bls12_381)] -fn foo() -> u32 { - 3 -} -``` - -If the field name is not known to Noir, it will discard the function. Field names are case insensitive. diff --git a/noir/docs/processed-docs-cache/noir/concepts/generics.md b/noir/docs/processed-docs-cache/noir/concepts/generics.md deleted file mode 100644 index ddd42bf1f9b..00000000000 --- a/noir/docs/processed-docs-cache/noir/concepts/generics.md +++ /dev/null @@ -1,106 +0,0 @@ ---- -title: Generics -description: Learn how to use Generics in Noir -keywords: [Noir, Rust, generics, functions, structs] -sidebar_position: 7 ---- - -Generics allow you to use the same functions with multiple different concrete data types. You can -read more about the concept of generics in the Rust documentation -[here](https://doc.rust-lang.org/book/ch10-01-syntax.html). - -Here is a trivial example showing the identity function that supports any type. In Rust, it is -common to refer to the most general type as `T`. We follow the same convention in Noir. - -```rust -fn id(x: T) -> T { - x -} -``` - -## In Structs - -Generics are useful for specifying types in structs. For example, we can specify that a field in a -struct will be of a certain generic type. In this case `value` is of type `T`. - -```rust -struct RepeatedValue { - value: T, - count: Field, -} - -impl RepeatedValue { - fn print(self) { - for _i in 0 .. self.count { - println(self.value); - } - } -} - -fn main() { - let repeated = RepeatedValue { value: "Hello!", count: 2 }; - repeated.print(); -} -``` - -The `print` function will print `Hello!` an arbitrary number of times, twice in this case. - -If we want to be generic over array lengths (which are type-level integers), we can use numeric -generics. Using these looks just like using regular generics, but these generics can resolve to -integers at compile-time, rather than resolving to types. Here's an example of a struct that is -generic over the size of the array it contains internally: - -```rust -struct BigInt { - limbs: [u32; N], -} - -impl BigInt { - // `N` is in scope of all methods in the impl - fn first(first: BigInt, second: BigInt) -> Self { - assert(first.limbs != second.limbs); - first - - fn second(first: BigInt, second: Self) -> Self { - assert(first.limbs != second.limbs); - second - } -} -``` - -## Calling functions on generic parameters - -Since a generic type `T` can represent any type, how can we call functions on the underlying type? -In other words, how can we go from "any type `T`" to "any type `T` that has certain methods available?" - -This is what [traits](../concepts/traits) are for in Noir. Here's an example of a function generic over -any type `T` that implements the `Eq` trait for equality: - -```rust -fn first_element_is_equal(array1: [T; N], array2: [T; N]) -> bool - where T: Eq -{ - if (array1.len() == 0) | (array2.len() == 0) { - true - } else { - array1[0] == array2[0] - } -} - -fn main() { - assert(first_element_is_equal([1, 2, 3], [1, 5, 6])); - - // We can use first_element_is_equal for arrays of any type - // as long as we have an Eq impl for the types we pass in - let array = [MyStruct::new(), MyStruct::new()]; - assert(array_eq(array, array, MyStruct::eq)); -} - -impl Eq for MyStruct { - fn eq(self, other: MyStruct) -> bool { - self.foo == other.foo - } -} -``` - -You can find more details on traits and trait implementations on the [traits page](../concepts/traits). diff --git a/noir/docs/processed-docs-cache/noir/concepts/lambdas.md b/noir/docs/processed-docs-cache/noir/concepts/lambdas.md deleted file mode 100644 index be3c7e0b5ca..00000000000 --- a/noir/docs/processed-docs-cache/noir/concepts/lambdas.md +++ /dev/null @@ -1,81 +0,0 @@ ---- -title: Lambdas -description: Learn how to use anonymous functions in Noir programming language. -keywords: [Noir programming language, lambda, closure, function, anonymous function] -sidebar_position: 9 ---- - -## Introduction - -Lambdas are anonymous functions. The syntax is `|arg1, arg2, ..., argN| return_expression`. - -```rust -let add_50 = |val| val + 50; -assert(add_50(100) == 150); -``` - -A block can be used as the body of a lambda, allowing you to declare local variables inside it: - -```rust -let cool = || { - let x = 100; - let y = 100; - x + y -} - -assert(cool() == 200); -``` - -## Closures - -Inside the body of a lambda, you can use variables defined in the enclosing function. Such lambdas are called **closures**. In this example `x` is defined inside `main` and is accessed from within the lambda: - -```rust -fn main() { - let x = 100; - let closure = || x + 150; - assert(closure() == 250); -} -``` - -## Passing closures to higher-order functions - -It may catch you by surprise that the following code fails to compile: - -```rust -fn foo(f: fn () -> Field) -> Field { - f() -} - -fn main() { - let (x, y) = (50, 50); - assert(foo(|| x + y) == 100); // error :( -} -``` - -The reason is that the closure's capture environment affects its type - we have a closure that captures two Fields and `foo` -expects a regular function as an argument - those are incompatible. -:::note - -Variables contained within the `||` are the closure's parameters, and the expression that follows it is the closure's body. The capture environment is comprised of any variables used in the closure's body that are not parameters. - -E.g. in |x| x + y, y would be a captured variable, but x would not be, since it is a parameter of the closure. - -::: -The syntax for the type of a closure is `fn[env](args) -> ret_type`, where `env` is the capture environment of the closure - -in this example that's `(Field, Field)`. - -The best solution in our case is to make `foo` generic over the environment type of its parameter, so that it can be called -with closures with any environment, as well as with regular functions: - -```rust -fn foo(f: fn[Env]() -> Field) -> Field { - f() -} - -fn main() { - let (x, y) = (50, 50); - assert(foo(|| x + y) == 100); // compiles fine - assert(foo(|| 60) == 60); // compiles fine -} -``` diff --git a/noir/docs/processed-docs-cache/noir/concepts/mutability.md b/noir/docs/processed-docs-cache/noir/concepts/mutability.md deleted file mode 100644 index 9cc10429cb4..00000000000 --- a/noir/docs/processed-docs-cache/noir/concepts/mutability.md +++ /dev/null @@ -1,93 +0,0 @@ ---- -title: Mutability -description: - Learn about mutable variables, constants, and globals in Noir programming language. Discover how - to declare, modify, and use them in your programs. -keywords: [noir programming language, mutability in noir, mutable variables, constants, globals] -sidebar_position: 8 ---- - -Variables in noir can be declared mutable via the `mut` keyword. Mutable variables can be reassigned -to via an assignment expression. - -```rust -let x = 2; -x = 3; // error: x must be mutable to be assigned to - -let mut y = 3; -let y = 4; // OK -``` - -The `mut` modifier can also apply to patterns: - -```rust -let (a, mut b) = (1, 2); -a = 11; // error: a must be mutable to be assigned to -b = 12; // OK - -let mut (c, d) = (3, 4); -c = 13; // OK -d = 14; // OK - -// etc. -let MyStruct { x: mut y } = MyStruct { x: a }; -// y is now in scope -``` - -Note that mutability in noir is local and everything is passed by value, so if a called function -mutates its parameters then the parent function will keep the old value of the parameters. - -```rust -fn main() -> pub Field { - let x = 3; - helper(x); - x // x is still 3 -} - -fn helper(mut x: i32) { - x = 4; -} -``` - -## Comptime Values - -:::warning - -The 'comptime' keyword was removed in version 0.10. The comptime keyword and syntax are currently still kept and parsed for backwards compatibility, but are now deprecated and will issue a warning when used. `comptime` has been removed because it is no longer needed for accessing arrays. - -::: - -## Globals - -Noir also supports global variables. However, they must be known at compile-time. The global type can also be inferred by the compiler entirely. Globals can also be used to specify array -annotations for function parameters and can be imported from submodules. - -```rust -global N: Field = 5; // Same as `global N: Field = 5` - -fn main(x : Field, y : [Field; N]) { - let res = x * N; - - assert(res == y[0]); - - let res2 = x * my_submodule::N; - assert(res != res2); -} - -mod my_submodule { - use dep::std; - - global N: Field = 10; - - fn my_helper() -> Field { - let x = N; - x - } -} -``` - -## Why only local mutability? - -Witnesses in a proving system are immutable in nature. Noir aims to _closely_ mirror this setting -without applying additional overhead to the user. Modeling a mutable reference is not as -straightforward as on conventional architectures and would incur some possibly unexpected overhead. diff --git a/noir/docs/processed-docs-cache/noir/concepts/ops.md b/noir/docs/processed-docs-cache/noir/concepts/ops.md deleted file mode 100644 index 60425cb8994..00000000000 --- a/noir/docs/processed-docs-cache/noir/concepts/ops.md +++ /dev/null @@ -1,98 +0,0 @@ ---- -title: Logical Operations -description: - Learn about the supported arithmetic and logical operations in the Noir programming language. - Discover how to perform operations on private input types, integers, and booleans. -keywords: - [ - Noir programming language, - supported operations, - arithmetic operations, - logical operations, - predicate operators, - bitwise operations, - short-circuiting, - backend, - ] -sidebar_position: 3 ---- - -# Operations - -## Table of Supported Operations - -| Operation | Description | Requirements | -| :-------- | :------------------------------------------------------------: | -------------------------------------: | -| + | Adds two private input types together | Types must be private input | -| - | Subtracts two private input types together | Types must be private input | -| \* | Multiplies two private input types together | Types must be private input | -| / | Divides two private input types together | Types must be private input | -| ^ | XOR two private input types together | Types must be integer | -| & | AND two private input types together | Types must be integer | -| \| | OR two private input types together | Types must be integer | -| \<\< | Left shift an integer by another integer amount | Types must be integer | -| >> | Right shift an integer by another integer amount | Types must be integer | -| ! | Bitwise not of a value | Type must be integer or boolean | -| \< | returns a bool if one value is less than the other | Upper bound must have a known bit size | -| \<= | returns a bool if one value is less than or equal to the other | Upper bound must have a known bit size | -| > | returns a bool if one value is more than the other | Upper bound must have a known bit size | -| >= | returns a bool if one value is more than or equal to the other | Upper bound must have a known bit size | -| == | returns a bool if one value is equal to the other | Both types must not be constants | -| != | returns a bool if one value is not equal to the other | Both types must not be constants | - -### Predicate Operators - -`<,<=, !=, == , >, >=` are known as predicate/comparison operations because they compare two values. -This differs from the operations such as `+` where the operands are used in _computation_. - -### Bitwise Operations Example - -```rust -fn main(x : Field) { - let y = x as u32; - let z = y & y; -} -``` - -`z` is implicitly constrained to be the result of `y & y`. The `&` operand is used to denote bitwise -`&`. - -> `x & x` would not compile as `x` is a `Field` and not an integer type. - -### Logical Operators - -Noir has no support for the logical operators `||` and `&&`. This is because encoding the -short-circuiting that these operators require can be inefficient for Noir's backend. Instead you can -use the bitwise operators `|` and `&` which operate identically for booleans, just without the -short-circuiting. - -```rust -let my_val = 5; - -let mut flag = 1; -if (my_val > 6) | (my_val == 0) { - flag = 0; -} -assert(flag == 1); - -if (my_val != 10) & (my_val < 50) { - flag = 0; -} -assert(flag == 0); -``` - -### Shorthand operators - -Noir shorthand operators for most of the above operators, namely `+=, -=, *=, /=, %=, &=, |=, ^=, <<=`, and `>>=`. These allow for more concise syntax. For example: - -```rust -let mut i = 0; -i = i + 1; -``` - -could be written as: - -```rust -let mut i = 0; -i += 1; -``` diff --git a/noir/docs/processed-docs-cache/noir/concepts/oracles.md b/noir/docs/processed-docs-cache/noir/concepts/oracles.md deleted file mode 100644 index 2e6a6818d48..00000000000 --- a/noir/docs/processed-docs-cache/noir/concepts/oracles.md +++ /dev/null @@ -1,23 +0,0 @@ ---- -title: Oracles -description: Dive into how Noir supports Oracles via RPC calls, and learn how to declare an Oracle in Noir with our comprehensive guide. -keywords: - - Noir - - Oracles - - RPC Calls - - Unconstrained Functions - - Programming - - Blockchain -sidebar_position: 6 ---- - -Noir has support for Oracles via RPC calls. This means Noir will make an RPC call and use the return value for proof generation. - -Since Oracles are not resolved by Noir, they are [`unconstrained` functions](./unconstrained.md) - -You can declare an Oracle through the `#[oracle()]` flag. Example: - -```rust -#[oracle(get_number_sequence)] -unconstrained fn get_number_sequence(_size: Field) -> [Field] {} -``` diff --git a/noir/docs/processed-docs-cache/noir/concepts/shadowing.md b/noir/docs/processed-docs-cache/noir/concepts/shadowing.md deleted file mode 100644 index 5ce6130d201..00000000000 --- a/noir/docs/processed-docs-cache/noir/concepts/shadowing.md +++ /dev/null @@ -1,44 +0,0 @@ ---- -title: Shadowing -sidebar_position: 12 ---- - -Noir allows for inheriting variables' values and re-declaring them with the same name similar to Rust, known as shadowing. - -For example, the following function is valid in Noir: - -```rust -fn main() { - let x = 5; - - { - let x = x * 2; - assert (x == 10); - } - - assert (x == 5); -} -``` - -In this example, a variable x is first defined with the value 5. - -The local scope that follows shadows the original x, i.e. creates a local mutable x based on the value of the original x. It is given a value of 2 times the original x. - -When we return to the main scope, x once again refers to just the original x, which stays at the value of 5. - -## Temporal mutability - -One way that shadowing is useful, in addition to ergonomics across scopes, is for temporarily mutating variables. - -```rust -fn main() { - let age = 30; - // age = age + 5; // Would error as `age` is immutable by default. - - let mut age = age + 5; // Temporarily mutates `age` with a new value. - - let age = age; // Locks `age`'s mutability again. - - assert (age == 35); -} -``` diff --git a/noir/docs/processed-docs-cache/noir/concepts/traits.md b/noir/docs/processed-docs-cache/noir/concepts/traits.md deleted file mode 100644 index ef1445a5907..00000000000 --- a/noir/docs/processed-docs-cache/noir/concepts/traits.md +++ /dev/null @@ -1,389 +0,0 @@ ---- -title: Traits -description: - Traits in Noir can be used to abstract out a common interface for functions across - several data types. -keywords: [noir programming language, traits, interfaces, generic, protocol] -sidebar_position: 14 ---- - -## Overview - -Traits in Noir are a useful abstraction similar to interfaces or protocols in other languages. Each trait defines -the interface of several methods contained within the trait. Types can then implement this trait by providing -implementations for these methods. For example in the program: - -```rust -struct Rectangle { - width: Field, - height: Field, -} - -impl Rectangle { - fn area(self) -> Field { - self.width * self.height - } -} - -fn log_area(r: Rectangle) { - println(r.area()); -} -``` - -We have a function `log_area` to log the area of a `Rectangle`. Now how should we change the program if we want this -function to work on `Triangle`s as well?: - -```rust -struct Triangle { - width: Field, - height: Field, -} - -impl Triangle { - fn area(self) -> Field { - self.width * self.height / 2 - } -} -``` - -Making `log_area` generic over all types `T` would be invalid since not all types have an `area` method. Instead, we can -introduce a new `Area` trait and make `log_area` generic over all types `T` that implement `Area`: - -```rust -trait Area { - fn area(self) -> Field; -} - -fn log_area(shape: T) where T: Area { - println(shape.area()); -} -``` - -We also need to explicitly implement `Area` for `Rectangle` and `Triangle`. We can do that by changing their existing -impls slightly. Note that the parameter types and return type of each of our `area` methods must match those defined -by the `Area` trait. - -```rust -impl Area for Rectangle { - fn area(self) -> Field { - self.width * self.height - } -} - -impl Area for Triangle { - fn area(self) -> Field { - self.width * self.height / 2 - } -} -``` - -Now we have a working program that is generic over any type of Shape that is used! Others can even use this program -as a library with their own types - such as `Circle` - as long as they also implement `Area` for these types. - -## Where Clauses - -As seen in `log_area` above, when we want to create a function or method that is generic over any type that implements -a trait, we can add a where clause to the generic function. - -```rust -fn log_area(shape: T) where T: Area { - println(shape.area()); -} -``` - -It is also possible to apply multiple trait constraints on the same variable at once by combining traits with the `+` -operator. Similarly, we can have multiple trait constraints by separating each with a comma: - -```rust -fn foo(elements: [T], thing: U) where - T: Default + Add + Eq, - U: Bar, -{ - let mut sum = T::default(); - - for element in elements { - sum += element; - } - - if sum == T::default() { - thing.bar(); - } -} -``` - -## Generic Implementations - -You can add generics to a trait implementation by adding the generic list after the `impl` keyword: - -```rust -trait Second { - fn second(self) -> Field; -} - -impl Second for (T, Field) { - fn second(self) -> Field { - self.1 - } -} -``` - -You can also implement a trait for every type this way: - -```rust -trait Debug { - fn debug(self); -} - -impl Debug for T { - fn debug(self) { - println(self); - } -} - -fn main() { - 1.debug(); -} -``` - -### Generic Trait Implementations With Where Clauses - -Where clauses can also be placed on trait implementations themselves to restrict generics in a similar way. -For example, while `impl Foo for T` implements the trait `Foo` for every type, `impl Foo for T where T: Bar` -will implement `Foo` only for types that also implement `Bar`. This is often used for implementing generic types. -For example, here is the implementation for array equality: - -```rust -impl Eq for [T; N] where T: Eq { - // Test if two arrays have the same elements. - // Because both arrays must have length N, we know their lengths already match. - fn eq(self, other: Self) -> bool { - let mut result = true; - - for i in 0 .. self.len() { - // The T: Eq constraint is needed to call == on the array elements here - result &= self[i] == other[i]; - } - - result - } -} -``` - -## Generic Traits - -Traits themselves can also be generic by placing the generic arguments after the trait name. These generics are in -scope of every item within the trait. - -```rust -trait Into { - // Convert `self` to type `T` - fn into(self) -> T; -} -``` - -When implementing generic traits the generic arguments of the trait must be specified. This is also true anytime -when referencing a generic trait (e.g. in a `where` clause). - -```rust -struct MyStruct { - array: [Field; 2], -} - -impl Into<[Field; 2]> for MyStruct { - fn into(self) -> [Field; 2] { - self.array - } -} - -fn as_array(x: T) -> [Field; 2] - where T: Into<[Field; 2]> -{ - x.into() -} - -fn main() { - let array = [1, 2]; - let my_struct = MyStruct { array }; - - assert_eq(as_array(my_struct), array); -} -``` - -## Trait Methods With No `self` - -A trait can contain any number of methods, each of which have access to the `Self` type which represents each type -that eventually implements the trait. Similarly, the `self` variable is available as well but is not required to be used. -For example, we can define a trait to create a default value for a type. This trait will need to return the `Self` type -but doesn't need to take any parameters: - -```rust -trait Default { - fn default() -> Self; -} -``` - -Implementing this trait can be done similarly to any other trait: - -```rust -impl Default for Field { - fn default() -> Field { - 0 - } -} - -struct MyType {} - -impl Default for MyType { - fn default() -> Field { - MyType {} - } -} -``` - -However, since there is no `self` parameter, we cannot call it via the method call syntax `object.method()`. -Instead, we'll need to refer to the function directly. This can be done either by referring to the -specific impl `MyType::default()` or referring to the trait itself `Default::default()`. In the later -case, type inference determines the impl that is selected. - -```rust -let my_struct = MyStruct::default(); - -let x: Field = Default::default(); -let result = x + Default::default(); -``` - -:::warning - -```rust -let _ = Default::default(); -``` - -If type inference cannot select which impl to use because of an ambiguous `Self` type, an impl will be -arbitrarily selected. This occurs most often when the result of a trait function call with no parameters -is unused. To avoid this, when calling a trait function with no `self` or `Self` parameters or return type, -always refer to it via the implementation type's namespace - e.g. `MyType::default()`. -This is set to change to an error in future Noir versions. - -::: - -## Default Method Implementations - -A trait can also have default implementations of its methods by giving a body to the desired functions. -Note that this body must be valid for all types that may implement the trait. As a result, the only -valid operations on `self` will be operations valid for any type or other operations on the trait itself. - -```rust -trait Numeric { - fn add(self, other: Self) -> Self; - - // Default implementation of double is (self + self) - fn double(self) -> Self { - self.add(self) - } -} -``` - -When implementing a trait with default functions, a type may choose to implement only the required functions: - -```rust -impl Numeric for Field { - fn add(self, other: Field) -> Field { - self + other - } -} -``` - -Or it may implement the optional methods as well: - -```rust -impl Numeric for u32 { - fn add(self, other: u32) -> u32 { - self + other - } - - fn double(self) -> u32 { - self * 2 - } -} -``` - -## Impl Specialization - -When implementing traits for a generic type it is possible to implement the trait for only a certain combination -of generics. This can be either as an optimization or because those specific generics are required to implement the trait. - -```rust -trait Sub { - fn sub(self, other: Self) -> Self; -} - -struct NonZero { - value: T, -} - -impl Sub for NonZero { - fn sub(self, other: Self) -> Self { - let value = self.value - other.value; - assert(value != 0); - NonZero { value } - } -} -``` - -## Overlapping Implementations - -Overlapping implementations are disallowed by Noir to ensure Noir's decision on which impl to select is never ambiguous. -This means if a trait `Foo` is already implemented -by a type `Bar` for all `T`, then we cannot also have a separate impl for `Bar` (or any other -type argument). Similarly, if there is an impl for all `T` such as `impl Debug for T`, we cannot create -any more impls to `Debug` for other types since it would be ambiguous which impl to choose for any given -method call. - -```rust -trait Trait {} - -// Previous impl defined here -impl Trait for (A, B) {} - -// error: Impl for type `(Field, Field)` overlaps with existing impl -impl Trait for (Field, Field) {} -``` - -## Trait Coherence - -Another restriction on trait implementations is coherence. This restriction ensures other crates cannot create -impls that may overlap with other impls, even if several unrelated crates are used as dependencies in the same -program. - -The coherence restriction is: to implement a trait, either the trait itself or the object type must be declared -in the crate the impl is in. - -In practice this often comes up when using types provided by libraries. If a library provides a type `Foo` that does -not implement a trait in the standard library such as `Default`, you may not `impl Default for Foo` in your own crate. -While restrictive, this prevents later issues or silent changes in the program if the `Foo` library later added its -own impl for `Default`. If you are a user of the `Foo` library in this scenario and need a trait not implemented by the -library your choices are to either submit a patch to the library or use the newtype pattern. - -### The Newtype Pattern - -The newtype pattern gets around the coherence restriction by creating a new wrapper type around the library type -that we cannot create `impl`s for. Since the new wrapper type is defined in our current crate, we can create -impls for any trait we need on it. - -```rust -struct Wrapper { - foo: dep::some_library::Foo, -} - -impl Default for Wrapper { - fn default() -> Wrapper { - Wrapper { - foo: dep::some_library::Foo::new(), - } - } -} -``` - -Since we have an impl for our own type, the behavior of this code will not change even if `some_library` is updated -to provide its own `impl Default for Foo`. The downside of this pattern is that it requires extra wrapping and -unwrapping of values when converting to and from the `Wrapper` and `Foo` types. diff --git a/noir/docs/processed-docs-cache/noir/concepts/unconstrained.md b/noir/docs/processed-docs-cache/noir/concepts/unconstrained.md deleted file mode 100644 index 6b3424f7993..00000000000 --- a/noir/docs/processed-docs-cache/noir/concepts/unconstrained.md +++ /dev/null @@ -1,95 +0,0 @@ ---- -title: Unconstrained Functions -description: "Learn about what unconstrained functions in Noir are, how to use them and when you'd want to." - -keywords: [Noir programming language, unconstrained, open] -sidebar_position: 5 ---- - -Unconstrained functions are functions which do not constrain any of the included computation and allow for non-deterministic computation. - -## Why? - -Zero-knowledge (ZK) domain-specific languages (DSL) enable developers to generate ZK proofs from their programs by compiling code down to the constraints of an NP complete language (such as R1CS or PLONKish languages). However, the hard bounds of a constraint system can be very limiting to the functionality of a ZK DSL. - -Enabling a circuit language to perform unconstrained execution is a powerful tool. Said another way, unconstrained execution lets developers generate witnesses from code that does not generate any constraints. Being able to execute logic outside of a circuit is critical for both circuit performance and constructing proofs on information that is external to a circuit. - -Fetching information from somewhere external to a circuit can also be used to enable developers to improve circuit efficiency. - -A ZK DSL does not just prove computation, but proves that some computation was handled correctly. Thus, it is necessary that when we switch from performing some operation directly inside of a circuit to inside of an unconstrained environment that the appropriate constraints are still laid down elsewhere in the circuit. - -## Example - -An in depth example might help drive the point home. This example comes from the excellent [post](https://discord.com/channels/1113924620781883405/1124022445054111926/1128747641853972590) by Tom in the Noir Discord. - -Let's look at how we can optimize a function to turn a `u72` into an array of `u8`s. - -```rust -fn main(num: u72) -> pub [u8; 8] { - let mut out: [u8; 8] = [0; 8]; - for i in 0..8 { - out[i] = (num >> (56 - (i * 8)) as u72 & 0xff) as u8; - } - - out -} -``` - -``` -Total ACIR opcodes generated for language PLONKCSat { width: 3 }: 91 -Backend circuit size: 3619 -``` - -A lot of the operations in this function are optimized away by the compiler (all the bit-shifts turn into divisions by constants). However we can save a bunch of gates by casting to u8 a bit earlier. This automatically truncates the bit-shifted value to fit in a u8 which allows us to remove the XOR against 0xff. This saves us ~480 gates in total. - -```rust -fn main(num: u72) -> pub [u8; 8] { - let mut out: [u8; 8] = [0; 8]; - for i in 0..8 { - out[i] = (num >> (56 - (i * 8)) as u8; - } - - out -} -``` - -``` -Total ACIR opcodes generated for language PLONKCSat { width: 3 }: 75 -Backend circuit size: 3143 -``` - -Those are some nice savings already but we can do better. This code is all constrained so we're proving every step of calculating out using num, but we don't actually care about how we calculate this, just that it's correct. This is where brillig comes in. - -It turns out that truncating a u72 into a u8 is hard to do inside a snark, each time we do as u8 we lay down 4 ACIR opcodes which get converted into multiple gates. It's actually much easier to calculate num from out than the other way around. All we need to do is multiply each element of out by a constant and add them all together, both relatively easy operations inside a snark. - -We can then run u72_to_u8 as unconstrained brillig code in order to calculate out, then use that result in our constrained function and assert that if we were to do the reverse calculation we'd get back num. This looks a little like the below: - -```rust -fn main(num: u72) -> pub [u8; 8] { - let out = u72_to_u8(num); - - let mut reconstructed_num: u72 = 0; - for i in 0..8 { - reconstructed_num += (out[i] as u72 << (56 - (8 * i))); - } - assert(num == reconstructed_num); - out -} - -unconstrained fn u72_to_u8(num: u72) -> [u8; 8] { - let mut out: [u8; 8] = [0; 8]; - for i in 0..8 { - out[i] = (num >> (56 - (i * 8))) as u8; - } - out -} -``` - -``` -Total ACIR opcodes generated for language PLONKCSat { width: 3 }: 78 -Backend circuit size: 2902 -``` - -This ends up taking off another ~250 gates from our circuit! We've ended up with more ACIR opcodes than before but they're easier for the backend to prove (resulting in fewer gates). - -Generally we want to use brillig whenever there's something that's easy to verify but hard to compute within the circuit. For example, if you wanted to calculate a square root of a number it'll be a much better idea to calculate this in brillig and then assert that if you square the result you get back your number. diff --git a/noir/docs/processed-docs-cache/noir/modules_packages_crates/_category_.json b/noir/docs/processed-docs-cache/noir/modules_packages_crates/_category_.json deleted file mode 100644 index 1debcfe7675..00000000000 --- a/noir/docs/processed-docs-cache/noir/modules_packages_crates/_category_.json +++ /dev/null @@ -1,6 +0,0 @@ -{ - "label": "Modules, Packages and Crates", - "position": 2, - "collapsible": true, - "collapsed": true -} diff --git a/noir/docs/processed-docs-cache/noir/modules_packages_crates/crates_and_packages.md b/noir/docs/processed-docs-cache/noir/modules_packages_crates/crates_and_packages.md deleted file mode 100644 index 760a463094c..00000000000 --- a/noir/docs/processed-docs-cache/noir/modules_packages_crates/crates_and_packages.md +++ /dev/null @@ -1,43 +0,0 @@ ---- -title: Crates and Packages -description: Learn how to use Crates and Packages in your Noir project -keywords: [Nargo, dependencies, package management, crates, package] -sidebar_position: 0 ---- - -## Crates - -A crate is the smallest amount of code that the Noir compiler considers at a time. -Crates can contain modules, and the modules may be defined in other files that get compiled with the crate, as we’ll see in the coming sections. - -### Crate Types - -A Noir crate can come in several forms: binaries, libraries or contracts. - -#### Binaries - -_Binary crates_ are programs which you can compile to an ACIR circuit which you can then create proofs against. Each must have a function called `main` that defines the ACIR circuit which is to be proved. - -#### Libraries - -_Library crates_ don't have a `main` function and they don't compile down to ACIR. Instead they define functionality intended to be shared with multiple projects, and eventually included in a binary crate. - -#### Contracts - -Contract crates are similar to binary crates in that they compile to ACIR which you can create proofs against. They are different in that they do not have a single `main` function, but are a collection of functions to be deployed to the [Aztec network](https://aztec.network). You can learn more about the technical details of Aztec in the [monorepo](https://github.com/AztecProtocol/aztec-packages) or contract [examples](https://github.com/AztecProtocol/aztec-packages/tree/master/yarn-project/noir-contracts/contracts). - -### Crate Root - -Every crate has a root, which is the source file that the compiler starts, this is also known as the root module. The Noir compiler does not enforce any conditions on the name of the file which is the crate root, however if you are compiling via Nargo the crate root must be called `lib.nr` or `main.nr` for library or binary crates respectively. - -## Packages - -A Nargo _package_ is a collection of one of more crates that provides a set of functionality. A package must include a Nargo.toml file. - -A package _must_ contain either a library or a binary crate, but not both. - -### Differences from Cargo Packages - -One notable difference between Rust's Cargo and Noir's Nargo is that while Cargo allows a package to contain an unlimited number of binary crates and a single library crate, Nargo currently only allows a package to contain a single crate. - -In future this restriction may be lifted to allow a Nargo package to contain both a binary and library crate or multiple binary crates. diff --git a/noir/docs/processed-docs-cache/noir/modules_packages_crates/dependencies.md b/noir/docs/processed-docs-cache/noir/modules_packages_crates/dependencies.md deleted file mode 100644 index a37dc401b7d..00000000000 --- a/noir/docs/processed-docs-cache/noir/modules_packages_crates/dependencies.md +++ /dev/null @@ -1,124 +0,0 @@ ---- -title: Dependencies -description: - Learn how to specify and manage dependencies in Nargo, allowing you to upload packages to GitHub - and use them easily in your project. -keywords: [Nargo, dependencies, GitHub, package management, versioning] -sidebar_position: 1 ---- - -Nargo allows you to upload packages to GitHub and use them as dependencies. - -## Specifying a dependency - -Specifying a dependency requires a tag to a specific commit and the git url to the url containing -the package. - -Currently, there are no requirements on the tag contents. If requirements are added, it would follow -semver 2.0 guidelines. - -> Note: Without a `tag` , there would be no versioning and dependencies would change each time you -> compile your project. - -For example, to add the [ecrecover-noir library](https://github.com/colinnielsen/ecrecover-noir) to your project, add it to `Nargo.toml`: - -```toml -# Nargo.toml - -[dependencies] -ecrecover = {tag = "v0.8.0", git = "https://github.com/colinnielsen/ecrecover-noir"} -``` - -If the module is in a subdirectory, you can define a subdirectory in your git repository, for example: - -```toml -# Nargo.toml - -[dependencies] -easy_private_token_contract = {tag ="v0.1.0-alpha62", git = "https://github.com/AztecProtocol/aztec-packages", directory = "yarn-project/noir-contracts/contracts/easy_private_token_contract"} -``` - -## Specifying a local dependency - -You can also specify dependencies that are local to your machine. - -For example, this file structure has a library and binary crate - -```tree -├── binary_crate -│   ├── Nargo.toml -│   └── src -│   └── main.nr -└── lib_a - ├── Nargo.toml - └── src - └── lib.nr -``` - -Inside of the binary crate, you can specify: - -```toml -# Nargo.toml - -[dependencies] -lib_a = { path = "../lib_a" } -``` - -## Importing dependencies - -You can import a dependency to a Noir file using the following syntax. For example, to import the -ecrecover-noir library and local lib_a referenced above: - -```rust -use dep::ecrecover; -use dep::lib_a; -``` - -You can also import only the specific parts of dependency that you want to use, like so: - -```rust -use dep::std::hash::sha256; -use dep::std::scalar_mul::fixed_base_embedded_curve; -``` - -Lastly, as demonstrated in the -[elliptic curve example](../standard_library/cryptographic_primitives/ec_primitives#examples), you -can import multiple items in the same line by enclosing them in curly braces: - -```rust -use dep::std::ec::tecurve::affine::{Curve, Point}; -``` - -We don't have a way to consume libraries from inside a [workspace](./workspaces) as external dependencies right now. - -Inside a workspace, these are consumed as `{ path = "../to_lib" }` dependencies in Nargo.toml. - -## Dependencies of Dependencies - -Note that when you import a dependency, you also get access to all of the dependencies of that package. - -For example, the [phy_vector](https://github.com/resurgencelabs/phy_vector) library imports an [fraction](https://github.com/resurgencelabs/fraction) library. If you're importing the phy_vector library, then you can access the functions in fractions library like so: - -```rust -use dep::phy_vector; - -fn main(x : Field, y : pub Field) { - //... - let f = phy_vector::fraction::toFraction(true, 2, 1); - //... -} -``` - -## Available Libraries - -Noir does not currently have an official package manager. You can find a list of available Noir libraries in the [awesome-noir repo here](https://github.com/noir-lang/awesome-noir#libraries). - -Some libraries that are available today include: - -- [Standard Library](https://github.com/noir-lang/noir/tree/master/noir_stdlib) - the Noir Standard Library -- [Ethereum Storage Proof Verification](https://github.com/aragonzkresearch/noir-trie-proofs) - a library that contains the primitives necessary for RLP decoding (in the form of look-up table construction) and Ethereum state and storage proof verification (or verification of any trie proof involving 32-byte long keys) -- [BigInt](https://github.com/shuklaayush/noir-bigint) - a library that provides a custom BigUint56 data type, allowing for computations on large unsigned integers -- [ECrecover](https://github.com/colinnielsen/ecrecover-noir/tree/main) - a library to verify an ECDSA signature and return the source Ethereum address -- [Sparse Merkle Tree Verifier](https://github.com/vocdoni/smtverifier-noir/tree/main) - a library for verification of sparse Merkle trees -- [Signed Int](https://github.com/resurgencelabs/signed_int) - a library for accessing a custom Signed Integer data type, allowing access to negative numbers on Noir -- [Fraction](https://github.com/resurgencelabs/fraction) - a library for accessing fractional number data type in Noir, allowing results that aren't whole numbers diff --git a/noir/docs/processed-docs-cache/noir/modules_packages_crates/modules.md b/noir/docs/processed-docs-cache/noir/modules_packages_crates/modules.md deleted file mode 100644 index ae822a1cff4..00000000000 --- a/noir/docs/processed-docs-cache/noir/modules_packages_crates/modules.md +++ /dev/null @@ -1,105 +0,0 @@ ---- -title: Modules -description: - Learn how to organize your files using modules in Noir, following the same convention as Rust's - module system. Examples included. -keywords: [Noir, Rust, modules, organizing files, sub-modules] -sidebar_position: 2 ---- - -Noir's module system follows the same convention as the _newer_ version of Rust's module system. - -## Purpose of Modules - -Modules are used to organize files. Without modules all of your code would need to live in a single -file. In Noir, the compiler does not automatically scan all of your files to detect modules. This -must be done explicitly by the developer. - -## Examples - -### Importing a module in the crate root - -Filename : `src/main.nr` - -```rust -mod foo; - -fn main() { - foo::hello_world(); -} -``` - -Filename : `src/foo.nr` - -```rust -fn from_foo() {} -``` - -In the above snippet, the crate root is the `src/main.nr` file. The compiler sees the module -declaration `mod foo` which prompts it to look for a foo.nr file. - -Visually this module hierarchy looks like the following : - -``` -crate - ├── main - │ - └── foo - └── from_foo - -``` - -### Importing a module throughout the tree - -All modules are accessible from the `crate::` namespace. - -``` -crate - ├── bar - ├── foo - └── main - -``` - -In the above snippet, if `bar` would like to use functions in `foo`, it can do so by `use crate::foo::function_name`. - -### Sub-modules - -Filename : `src/main.nr` - -```rust -mod foo; - -fn main() { - foo::from_foo(); -} -``` - -Filename : `src/foo.nr` - -```rust -mod bar; -fn from_foo() {} -``` - -Filename : `src/foo/bar.nr` - -```rust -fn from_bar() {} -``` - -In the above snippet, we have added an extra module to the module tree; `bar`. `bar` is a submodule -of `foo` hence we declare bar in `foo.nr` with `mod bar`. Since `foo` is not the crate root, the -compiler looks for the file associated with the `bar` module in `src/foo/bar.nr` - -Visually the module hierarchy looks as follows: - -``` -crate - ├── main - │ - └── foo - ├── from_foo - └── bar - └── from_bar -``` diff --git a/noir/docs/processed-docs-cache/noir/modules_packages_crates/workspaces.md b/noir/docs/processed-docs-cache/noir/modules_packages_crates/workspaces.md deleted file mode 100644 index 67a1dafa372..00000000000 --- a/noir/docs/processed-docs-cache/noir/modules_packages_crates/workspaces.md +++ /dev/null @@ -1,40 +0,0 @@ ---- -title: Workspaces -sidebar_position: 3 ---- - -Workspaces are a feature of nargo that allow you to manage multiple related Noir packages in a single repository. A workspace is essentially a group of related projects that share common build output directories and configurations. - -Each Noir project (with it's own Nargo.toml file) can be thought of as a package. Each package is expected to contain exactly one "named circuit", being the "name" defined in Nargo.toml with the program logic defined in `./src/main.nr`. - -For a project with the following structure: - -```tree -├── crates -│   ├── a -│   │   ├── Nargo.toml -│   │   └── src -│   │   └── main.nr -│   └── b -│   ├── Nargo.toml -│   └── src -│   └── main.nr -├── Nargo.toml -└── Prover.toml -``` - -You can define a workspace in Nargo.toml like so: - -```toml -[workspace] -members = ["crates/a", "crates/b"] -default-member = "crates/a" -``` - -`members` indicates which packages are included in the workspace. As such, all member packages of a workspace will be processed when the `--workspace` flag is used with various commands or if a `default-member` is not specified. - -`default-member` indicates which package various commands process by default. - -Libraries can be defined in a workspace. Inside a workspace, these are consumed as `{ path = "../to_lib" }` dependencies in Nargo.toml. - -Inside a workspace, these are consumed as `{ path = "../to_lib" }` dependencies in Nargo.toml. diff --git a/noir/docs/processed-docs-cache/noir/standard_library/_category_.json b/noir/docs/processed-docs-cache/noir/standard_library/_category_.json deleted file mode 100644 index af04c0933fd..00000000000 --- a/noir/docs/processed-docs-cache/noir/standard_library/_category_.json +++ /dev/null @@ -1,6 +0,0 @@ -{ - "label": "Standard Library", - "position": 1, - "collapsible": true, - "collapsed": true -} diff --git a/noir/docs/processed-docs-cache/noir/standard_library/black_box_fns.md b/noir/docs/processed-docs-cache/noir/standard_library/black_box_fns.md deleted file mode 100644 index 6b22d0e7466..00000000000 --- a/noir/docs/processed-docs-cache/noir/standard_library/black_box_fns.md +++ /dev/null @@ -1,31 +0,0 @@ ---- -title: Black Box Functions -description: Black box functions are functions in Noir that rely on backends implementing support for specialized constraints. -keywords: [noir, black box functions] ---- - -Black box functions are functions in Noir that rely on backends implementing support for specialized constraints. This makes certain zk-snark unfriendly computations cheaper than if they were implemented in Noir. - -The ACVM spec defines a set of blackbox functions which backends will be expected to implement. This allows backends to use optimized implementations of these constraints if they have them, however they may also fallback to less efficient naive implementations if not. - -## Function list - -Here is a list of the current black box functions: - -- [SHA256](./cryptographic_primitives/hashes#sha256) -- [Schnorr signature verification](./cryptographic_primitives/schnorr) -- [Blake2s](./cryptographic_primitives/hashes#blake2s) -- [Blake3](./cryptographic_primitives/hashes#blake2s) -- [Pedersen Hash](./cryptographic_primitives/hashes#pedersen_hash) -- [Pedersen Commitment](./cryptographic_primitives/hashes#pedersen_commitment) -- [ECDSA signature verification](./cryptographic_primitives/ecdsa_sig_verification) -- [Fixed base scalar multiplication](./cryptographic_primitives/scalar) -- AND -- XOR -- RANGE -- [Keccak256](./cryptographic_primitives/hashes#keccak256) -- [Recursive proof verification](./recursion) - -Most black box functions are included as part of the Noir standard library, however `AND`, `XOR` and `RANGE` are used as part of the Noir language syntax. For instance, using the bitwise operator `&` will invoke the `AND` black box function. - -You can view the black box functions defined in the ACVM code [here](https://github.com/noir-lang/noir/blob/master/acvm-repo/acir/src/circuit/black_box_functions.rs). diff --git a/noir/docs/processed-docs-cache/noir/standard_library/cryptographic_primitives/_category_.json b/noir/docs/processed-docs-cache/noir/standard_library/cryptographic_primitives/_category_.json deleted file mode 100644 index 5d694210bbf..00000000000 --- a/noir/docs/processed-docs-cache/noir/standard_library/cryptographic_primitives/_category_.json +++ /dev/null @@ -1,5 +0,0 @@ -{ - "position": 0, - "collapsible": true, - "collapsed": true -} diff --git a/noir/docs/processed-docs-cache/noir/standard_library/cryptographic_primitives/ec_primitives.md b/noir/docs/processed-docs-cache/noir/standard_library/cryptographic_primitives/ec_primitives.md deleted file mode 100644 index d2b42d67b7c..00000000000 --- a/noir/docs/processed-docs-cache/noir/standard_library/cryptographic_primitives/ec_primitives.md +++ /dev/null @@ -1,102 +0,0 @@ ---- -title: Elliptic Curve Primitives -keywords: [cryptographic primitives, Noir project] -sidebar_position: 4 ---- - -Data structures and methods on them that allow you to carry out computations involving elliptic -curves over the (mathematical) field corresponding to `Field`. For the field currently at our -disposal, applications would involve a curve embedded in BN254, e.g. the -[Baby Jubjub curve](https://eips.ethereum.org/EIPS/eip-2494). - -## Data structures - -### Elliptic curve configurations - -(`std::ec::{tecurve,montcurve,swcurve}::{affine,curvegroup}::Curve`), i.e. the specific elliptic -curve you want to use, which would be specified using any one of the methods -`std::ec::{tecurve,montcurve,swcurve}::{affine,curvegroup}::new` which take the coefficients in the -defining equation together with a generator point as parameters. You can find more detail in the -comments in -[`noir_stdlib/src/ec.nr`](https://github.com/noir-lang/noir/blob/master/noir_stdlib/src/ec.nr), but -the gist of it is that the elliptic curves of interest are usually expressed in one of the standard -forms implemented here (Twisted Edwards, Montgomery and Short Weierstraß), and in addition to that, -you could choose to use `affine` coordinates (Cartesian coordinates - the usual (x,y) - possibly -together with a point at infinity) or `curvegroup` coordinates (some form of projective coordinates -requiring more coordinates but allowing for more efficient implementations of elliptic curve -operations). Conversions between all of these forms are provided, and under the hood these -conversions are done whenever an operation is more efficient in a different representation (or a -mixed coordinate representation is employed). - -### Points - -(`std::ec::{tecurve,montcurve,swcurve}::{affine,curvegroup}::Point`), i.e. points lying on the -elliptic curve. For a curve configuration `c` and a point `p`, it may be checked that `p` -does indeed lie on `c` by calling `c.contains(p1)`. - -## Methods - -(given a choice of curve representation, e.g. use `std::ec::tecurve::affine::Curve` and use -`std::ec::tecurve::affine::Point`) - -- The **zero element** is given by `Point::zero()`, and we can verify whether a point `p: Point` is - zero by calling `p.is_zero()`. -- **Equality**: Points `p1: Point` and `p2: Point` may be checked for equality by calling - `p1.eq(p2)`. -- **Addition**: For `c: Curve` and points `p1: Point` and `p2: Point` on the curve, adding these two - points is accomplished by calling `c.add(p1,p2)`. -- **Negation**: For a point `p: Point`, `p.negate()` is its negation. -- **Subtraction**: For `c` and `p1`, `p2` as above, subtracting `p2` from `p1` is accomplished by - calling `c.subtract(p1,p2)`. -- **Scalar multiplication**: For `c` as above, `p: Point` a point on the curve and `n: Field`, - scalar multiplication is given by `c.mul(n,p)`. If instead `n :: [u1; N]`, i.e. `n` is a bit - array, the `bit_mul` method may be used instead: `c.bit_mul(n,p)` -- **Multi-scalar multiplication**: For `c` as above and arrays `n: [Field; N]` and `p: [Point; N]`, - multi-scalar multiplication is given by `c.msm(n,p)`. -- **Coordinate representation conversions**: The `into_group` method converts a point or curve - configuration in the affine representation to one in the CurveGroup representation, and - `into_affine` goes in the other direction. -- **Curve representation conversions**: `tecurve` and `montcurve` curves and points are equivalent - and may be converted between one another by calling `into_montcurve` or `into_tecurve` on their - configurations or points. `swcurve` is more general and a curve c of one of the other two types - may be converted to this representation by calling `c.into_swcurve()`, whereas a point `p` lying - on the curve given by `c` may be mapped to its corresponding `swcurve` point by calling - `c.map_into_swcurve(p)`. -- **Map-to-curve methods**: The Elligator 2 method of mapping a field element `n: Field` into a - `tecurve` or `montcurve` with configuration `c` may be called as `c.elligator2_map(n)`. For all of - the curve configurations, the SWU map-to-curve method may be called as `c.swu_map(z,n)`, where - `z: Field` depends on `Field` and `c` and must be chosen by the user (the conditions it needs to - satisfy are specified in the comments - [here](https://github.com/noir-lang/noir/blob/master/noir_stdlib/src/ec.nr)). - -## Examples - -The -[ec_baby_jubjub test](https://github.com/noir-lang/noir/blob/master/test_programs/compile_success_empty/ec_baby_jubjub/src/main.nr) -illustrates all of the above primitives on various forms of the Baby Jubjub curve. A couple of more -interesting examples in Noir would be: - -Public-key cryptography: Given an elliptic curve and a 'base point' on it, determine the public key -from the private key. This is a matter of using scalar multiplication. In the case of Baby Jubjub, -for example, this code would do: - -```rust -use dep::std::ec::tecurve::affine::{Curve, Point}; - -fn bjj_pub_key(priv_key: Field) -> Point -{ - - let bjj = Curve::new(168700, 168696, G::new(995203441582195749578291179787384436505546430278305826713579947235728471134,5472060717959818805561601436314318772137091100104008585924551046643952123905)); - - let base_pt = Point::new(5299619240641551281634865583518297030282874472190772894086521144482721001553, 16950150798460657717958625567821834550301663161624707787222815936182638968203); - - bjj.mul(priv_key,base_pt) -} -``` - -This would come in handy in a Merkle proof. - -- EdDSA signature verification: This is a matter of combining these primitives with a suitable hash - function. See - [feat(stdlib): EdDSA sig verification noir#1136](https://github.com/noir-lang/noir/pull/1136) for - the case of Baby Jubjub and the Poseidon hash function. diff --git a/noir/docs/processed-docs-cache/noir/standard_library/cryptographic_primitives/ecdsa_sig_verification.mdx b/noir/docs/processed-docs-cache/noir/standard_library/cryptographic_primitives/ecdsa_sig_verification.mdx deleted file mode 100644 index 4bf09cef178..00000000000 --- a/noir/docs/processed-docs-cache/noir/standard_library/cryptographic_primitives/ecdsa_sig_verification.mdx +++ /dev/null @@ -1,60 +0,0 @@ ---- -title: ECDSA Signature Verification -description: Learn about the cryptographic primitives regarding ECDSA over the secp256k1 and secp256r1 curves -keywords: [cryptographic primitives, Noir project, ecdsa, secp256k1, secp256r1, signatures] -sidebar_position: 3 ---- - -import BlackBoxInfo from '@site/src/components/Notes/_blackbox.mdx'; - -Noir supports ECDSA signatures verification over the secp256k1 and secp256r1 curves. - -## ecdsa_secp256k1::verify_signature - -Verifier for ECDSA Secp256k1 signatures - -```rust title="ecdsa_secp256k1" showLineNumbers -pub fn verify_signature( - public_key_x: [u8; 32], - public_key_y: [u8; 32], - signature: [u8; 64], - message_hash: [u8; N] -) -> bool -``` -> Source code: noir_stdlib/src/ecdsa_secp256k1.nr#L2-L9 - - -example: - -```rust -fn main(hashed_message : [u8;32], pub_key_x : [u8;32], pub_key_y : [u8;32], signature : [u8;64]) { - let valid_signature = std::ecdsa_secp256k1::verify_signature(pub_key_x, pub_key_y, signature, hashed_message); - assert(valid_signature); -} -``` - -## ecdsa_secp256r1::verify_signature - -Verifier for ECDSA Secp256r1 signatures - -```rust title="ecdsa_secp256r1" showLineNumbers -pub fn verify_signature( - public_key_x: [u8; 32], - public_key_y: [u8; 32], - signature: [u8; 64], - message_hash: [u8; N] -) -> bool -``` -> Source code: noir_stdlib/src/ecdsa_secp256r1.nr#L2-L9 - - -example: - -```rust -fn main(hashed_message : [u8;32], pub_key_x : [u8;32], pub_key_y : [u8;32], signature : [u8;64]) { - let valid_signature = std::ecdsa_secp256r1::verify_signature(pub_key_x, pub_key_y, signature, hashed_message); - assert(valid_signature); -} -``` - - diff --git a/noir/docs/processed-docs-cache/noir/standard_library/cryptographic_primitives/eddsa.mdx b/noir/docs/processed-docs-cache/noir/standard_library/cryptographic_primitives/eddsa.mdx deleted file mode 100644 index a9c10da6c06..00000000000 --- a/noir/docs/processed-docs-cache/noir/standard_library/cryptographic_primitives/eddsa.mdx +++ /dev/null @@ -1,18 +0,0 @@ ---- -title: EdDSA Verification -description: Learn about the cryptographic primitives regarding EdDSA -keywords: [cryptographic primitives, Noir project, eddsa, signatures] -sidebar_position: 5 ---- - -import BlackBoxInfo from '@site/src/components/Notes/_blackbox.mdx'; - -## eddsa::eddsa_poseidon_verify - -Verifier for EdDSA signatures - -```rust -fn eddsa_poseidon_verify(public_key_x : Field, public_key_y : Field, signature_s: Field, signature_r8_x: Field, signature_r8_y: Field, message: Field) -> bool -``` - - diff --git a/noir/docs/processed-docs-cache/noir/standard_library/cryptographic_primitives/hashes.mdx b/noir/docs/processed-docs-cache/noir/standard_library/cryptographic_primitives/hashes.mdx deleted file mode 100644 index 730b6d4117f..00000000000 --- a/noir/docs/processed-docs-cache/noir/standard_library/cryptographic_primitives/hashes.mdx +++ /dev/null @@ -1,234 +0,0 @@ ---- -title: Hash methods -description: - Learn about the cryptographic primitives ready to use for any Noir project, including sha256, - blake2s, pedersen, mimc_bn254 and mimc -keywords: - [cryptographic primitives, Noir project, sha256, blake2s, pedersen, mimc_bn254, mimc, hash] -sidebar_position: 0 ---- - -import BlackBoxInfo from '@site/src/components/Notes/_blackbox.mdx'; - -## sha256 - -Given an array of bytes, returns the resulting sha256 hash. - -```rust title="sha256" showLineNumbers -pub fn sha256(input: [u8; N]) -> [u8; 32] -``` -> Source code: noir_stdlib/src/hash.nr#L5-L7 - - -example: - -```rust -fn main() { - let x = [163, 117, 178, 149]; // some random bytes - let hash = std::hash::sha256(x); -} -``` - - - -## blake2s - -Given an array of bytes, returns an array with the Blake2 hash - -```rust title="blake2s" showLineNumbers -pub fn blake2s(input: [u8; N]) -> [u8; 32] -``` -> Source code: noir_stdlib/src/hash.nr#L11-L13 - - -example: - -```rust -fn main() { - let x = [163, 117, 178, 149]; // some random bytes - let hash = std::hash::blake2s(x); -} -``` - - - -## blake3 - -Given an array of bytes, returns an array with the Blake3 hash - -```rust title="blake3" showLineNumbers -pub fn blake3(input: [u8; N]) -> [u8; 32] -``` -> Source code: noir_stdlib/src/hash.nr#L17-L19 - - -example: - -```rust -fn main() { - let x = [163, 117, 178, 149]; // some random bytes - let hash = std::hash::blake3(x); -} -``` - - - -## pedersen_hash - -Given an array of Fields, returns the Pedersen hash. - -```rust title="pedersen_hash" showLineNumbers -pub fn pedersen_hash(input: [Field; N]) -> Field -``` -> Source code: noir_stdlib/src/hash.nr#L42-L44 - - -example: - -```rust title="pedersen-hash" showLineNumbers -use dep::std; - -fn main(x: Field, y: Field, expected_hash: Field) { - let hash = std::hash::pedersen_hash([x, y]); - assert_eq(hash, expected_hash); -} -``` -> Source code: test_programs/execution_success/pedersen_hash/src/main.nr#L1-L8 - - - - - -## pedersen_commitment - -Given an array of Fields, returns the Pedersen commitment. - -```rust title="pedersen_commitment" showLineNumbers -struct PedersenPoint { - x : Field, - y : Field, -} - -pub fn pedersen_commitment(input: [Field; N]) -> PedersenPoint -``` -> Source code: noir_stdlib/src/hash.nr#L22-L29 - - -example: - -```rust title="pedersen-commitment" showLineNumbers -use dep::std; - -fn main(x: Field, y: Field, expected_commitment: std::hash::PedersenPoint) { - let commitment = std::hash::pedersen_commitment([x, y]); - assert_eq(commitment.x, expected_commitment.x); - assert_eq(commitment.y, expected_commitment.y); -} -``` -> Source code: test_programs/execution_success/pedersen_commitment/src/main.nr#L1-L9 - - - - -## keccak256 - -Given an array of bytes (`u8`), returns the resulting keccak hash as an array of 32 bytes -(`[u8; 32]`). Specify a message_size to hash only the first `message_size` bytes -of the input. - -```rust title="keccak256" showLineNumbers -pub fn keccak256(input: [u8; N], message_size: u32) -> [u8; 32] -``` -> Source code: noir_stdlib/src/hash.nr#L67-L69 - - -example: - -```rust title="keccak256" showLineNumbers -use dep::std; - -fn main(x: Field, result: [u8; 32]) { - // We use the `as` keyword here to denote the fact that we want to take just the first byte from the x Field - // The padding is taken care of by the program - let digest = std::hash::keccak256([x as u8], 1); - assert(digest == result); - - //#1399: variable message size - let message_size = 4; - let hash_a = std::hash::keccak256([1, 2, 3, 4], message_size); - let hash_b = std::hash::keccak256([1, 2, 3, 4, 0, 0, 0, 0], message_size); - - assert(hash_a == hash_b); - - let message_size_big = 8; - let hash_c = std::hash::keccak256([1, 2, 3, 4, 0, 0, 0, 0], message_size_big); - - assert(hash_a != hash_c); -} -``` -> Source code: test_programs/execution_success/keccak256/src/main.nr#L1-L22 - - - - -## poseidon - -Given an array of Fields, returns a new Field with the Poseidon Hash. Mind that you need to specify -how many inputs are there to your Poseidon function. - -```rust -// example for hash_1, hash_2 accepts an array of length 2, etc -fn hash_1(input: [Field; 1]) -> Field -``` - -example: - -```rust title="poseidon" showLineNumbers -use dep::std::hash::poseidon; - -fn main(x1: [Field; 2], y1: pub Field, x2: [Field; 4], y2: pub Field) { - let hash1 = poseidon::bn254::hash_2(x1); - assert(hash1 == y1); - - let hash2 = poseidon::bn254::hash_4(x2); - assert(hash2 == y2); -} -``` -> Source code: test_programs/execution_success/poseidon_bn254_hash/src/main.nr#L1-L11 - - -## mimc_bn254 and mimc - -`mimc_bn254` is `mimc`, but with hardcoded parameters for the BN254 curve. You can use it by -providing an array of Fields, and it returns a Field with the hash. You can use the `mimc` method if -you're willing to input your own constants: - -```rust -fn mimc(x: Field, k: Field, constants: [Field; N], exp : Field) -> Field -``` - -otherwise, use the `mimc_bn254` method: - -```rust -fn mimc_bn254(array: [Field; N]) -> Field -``` - -example: - -```rust - -fn main() { - let x = [163, 117, 178, 149]; // some random bytes - let hash = std::hash::mimc::mimc_bn254(x); -} -``` - -## hash_to_field - -```rust -fn hash_to_field(_input : [Field; N]) -> Field {} -``` - -Calculates the `blake2s` hash of the inputs and returns the hash modulo the field modulus to return -a value which can be represented as a `Field`. - diff --git a/noir/docs/processed-docs-cache/noir/standard_library/cryptographic_primitives/index.md b/noir/docs/processed-docs-cache/noir/standard_library/cryptographic_primitives/index.md deleted file mode 100644 index 650f30165d5..00000000000 --- a/noir/docs/processed-docs-cache/noir/standard_library/cryptographic_primitives/index.md +++ /dev/null @@ -1,14 +0,0 @@ ---- -title: Cryptographic Primitives -description: - Learn about the cryptographic primitives ready to use for any Noir project -keywords: - [ - cryptographic primitives, - Noir project, - ] ---- - -The Noir team is progressively adding new cryptographic primitives to the standard library. Reach out for news or if you would be interested in adding more of these calculations in Noir. - -Some methods are available thanks to the Aztec backend, not being performed using Noir. When using other backends, these methods may or may not be supplied. diff --git a/noir/docs/processed-docs-cache/noir/standard_library/cryptographic_primitives/scalar.mdx b/noir/docs/processed-docs-cache/noir/standard_library/cryptographic_primitives/scalar.mdx deleted file mode 100644 index df411ca5443..00000000000 --- a/noir/docs/processed-docs-cache/noir/standard_library/cryptographic_primitives/scalar.mdx +++ /dev/null @@ -1,33 +0,0 @@ ---- -title: Scalar multiplication -description: See how you can perform scalar multiplications over a fixed base in Noir -keywords: [cryptographic primitives, Noir project, scalar multiplication] -sidebar_position: 1 ---- - -import BlackBoxInfo from '@site/src/components/Notes/_blackbox.mdx'; - -## scalar_mul::fixed_base_embedded_curve - -Performs scalar multiplication over the embedded curve whose coordinates are defined by the -configured noir field. For the BN254 scalar field, this is BabyJubJub or Grumpkin. - -```rust title="fixed_base_embedded_curve" showLineNumbers -pub fn fixed_base_embedded_curve( - low: Field, - high: Field -) -> [Field; 2] -``` -> Source code: noir_stdlib/src/scalar_mul.nr#L27-L32 - - -example - -```rust -fn main(x : Field) { - let scal = std::scalar_mul::fixed_base_embedded_curve(x); - println(scal); -} -``` - - diff --git a/noir/docs/processed-docs-cache/noir/standard_library/cryptographic_primitives/schnorr.mdx b/noir/docs/processed-docs-cache/noir/standard_library/cryptographic_primitives/schnorr.mdx deleted file mode 100644 index ae12e6c12dc..00000000000 --- a/noir/docs/processed-docs-cache/noir/standard_library/cryptographic_primitives/schnorr.mdx +++ /dev/null @@ -1,45 +0,0 @@ ---- -title: Schnorr Signatures -description: Learn how you can verify Schnorr signatures using Noir -keywords: [cryptographic primitives, Noir project, schnorr, signatures] -sidebar_position: 2 ---- - -import BlackBoxInfo from '@site/src/components/Notes/_blackbox.mdx'; - -## schnorr::verify_signature - -Verifier for Schnorr signatures over the embedded curve (for BN254 it is Grumpkin). - -```rust title="schnorr_verify" showLineNumbers -pub fn verify_signature( - public_key_x: Field, - public_key_y: Field, - signature: [u8; 64], - message: [u8; N] -) -> bool -``` -> Source code: noir_stdlib/src/schnorr.nr#L2-L9 - - -where `_signature` can be generated like so using the npm package -[@noir-lang/barretenberg](https://www.npmjs.com/package/@noir-lang/barretenberg) - -```js -const { BarretenbergWasm } = require('@noir-lang/barretenberg/dest/wasm'); -const { Schnorr } = require('@noir-lang/barretenberg/dest/crypto/schnorr'); - -... - -const barretenberg = await BarretenbergWasm.new(); -const schnorr = new Schnorr(barretenberg); -const pubKey = schnorr.computePublicKey(privateKey); -const message = ... -const signature = Array.from( - schnorr.constructSignature(hash, privateKey).toBuffer() -); - -... -``` - - diff --git a/noir/docs/processed-docs-cache/noir/standard_library/logging.md b/noir/docs/processed-docs-cache/noir/standard_library/logging.md deleted file mode 100644 index db75ef9f86f..00000000000 --- a/noir/docs/processed-docs-cache/noir/standard_library/logging.md +++ /dev/null @@ -1,78 +0,0 @@ ---- -title: Logging -description: - Learn how to use the println statement for debugging in Noir with this tutorial. Understand the - basics of logging in Noir and how to implement it in your code. -keywords: - [ - noir logging, - println statement, - print statement, - debugging in noir, - noir std library, - logging tutorial, - basic logging in noir, - noir logging implementation, - noir debugging techniques, - rust, - ] ---- - -The standard library provides two familiar statements you can use: `println` and `print`. Despite being a limited implementation of rust's `println!` and `print!` macros, these constructs can be useful for debugging. - -You can print the output of both statements in your Noir code by using the `nargo execute` command or the `--show-output` flag when using `nargo test` (provided there are print statements in your tests). - -It is recommended to use `nargo execute` if you want to debug failing constraints with `println` or `print` statements. This is due to every input in a test being a constant rather than a witness, so we issue an error during compilation while we only print during execution (which comes after compilation). Neither `println`, nor `print` are callable for failed constraints caught at compile time. - -Both `print` and `println` are generic functions which can work on integers, fields, strings, and even structs or expressions. Note however, that slices are currently unsupported. For example: - -```rust -struct Person { - age: Field, - height: Field, -} - -fn main(age: Field, height: Field) { - let person = Person { - age: age, - height: height, - }; - println(person); - println(age + height); - println("Hello world!"); -} -``` - -You can print different types in the same statement (including strings) with a type called `fmtstr`. It can be specified in the same way as a normal string, just prepended with an "f" character: - -```rust - let fmt_str = f"i: {i}, j: {j}"; - println(fmt_str); - - let s = myStruct { y: x, x: y }; - println(s); - - println(f"i: {i}, s: {s}"); - - println(x); - println([x, y]); - - let foo = fooStruct { my_struct: s, foo: 15 }; - println(f"s: {s}, foo: {foo}"); - - println(15); // prints 0x0f, implicit Field - println(-1 as u8); // prints 255 - println(-1 as i8); // prints -1 -``` - -Examples shown above are interchangeable between the two `print` statements: - -```rust -let person = Person { age : age, height : height }; - -println(person); -print(person); - -println("Hello world!"); // Prints with a newline at the end of the input -print("Hello world!"); // Prints the input and keeps cursor on the same line -``` diff --git a/noir/docs/processed-docs-cache/noir/standard_library/merkle_trees.md b/noir/docs/processed-docs-cache/noir/standard_library/merkle_trees.md deleted file mode 100644 index fa488677884..00000000000 --- a/noir/docs/processed-docs-cache/noir/standard_library/merkle_trees.md +++ /dev/null @@ -1,58 +0,0 @@ ---- -title: Merkle Trees -description: Learn about Merkle Trees in Noir with this tutorial. Explore the basics of computing a merkle root using a proof, with examples. -keywords: - [ - Merkle trees in Noir, - Noir programming language, - check membership, - computing root from leaf, - Noir Merkle tree implementation, - Merkle tree tutorial, - Merkle tree code examples, - Noir libraries, - pedersen hash., - ] ---- - -## compute_merkle_root - -Returns the root of the tree from the provided leaf and its hash path, using a [Pedersen hash](./cryptographic_primitives/hashes.mdx#pedersen_hash). - -```rust -fn compute_merkle_root(leaf : Field, index : Field, hash_path: [Field]) -> Field -``` - -example: - -```rust -/** - // these values are for this example only - index = "0" - priv_key = "0x000000000000000000000000000000000000000000000000000000616c696365" - secret = "0x1929ea3ab8d9106a899386883d9428f8256cfedb3c4f6b66bf4aa4d28a79988f" - note_hash_path = [ - "0x1e61bdae0f027b1b2159e1f9d3f8d00fa668a952dddd822fda80dc745d6f65cc", - "0x0e4223f3925f98934393c74975142bd73079ab0621f4ee133cee050a3c194f1a", - "0x2fd7bb412155bf8693a3bd2a3e7581a679c95c68a052f835dddca85fa1569a40" - ] - */ -fn main(index: Field, priv_key: Field, secret: Field, note_hash_path: [Field; 3]) { - - let pubkey = std::scalar_mul::fixed_base_embedded_curve(priv_key); - let pubkey_x = pubkey[0]; - let pubkey_y = pubkey[1]; - let note_commitment = std::hash::pedersen([pubkey_x, pubkey_y, secret]); - - let root = std::merkle::compute_merkle_root(note_commitment[0], index, note_hash_path); - println(root); -} -``` - -To check merkle tree membership: - -1. Include a merkle root as a program input. -2. Compute the merkle root of a given leaf, index and hash path. -3. Assert the merkle roots are equal. - -For more info about merkle trees, see the Wikipedia [page](https://en.wikipedia.org/wiki/Merkle_tree). diff --git a/noir/docs/processed-docs-cache/noir/standard_library/options.md b/noir/docs/processed-docs-cache/noir/standard_library/options.md deleted file mode 100644 index 970c9cfbf11..00000000000 --- a/noir/docs/processed-docs-cache/noir/standard_library/options.md +++ /dev/null @@ -1,97 +0,0 @@ ---- -title: Option Type ---- - -The `Option` type is a way to express that a value might be present (`Some(T))` or absent (`None`). It's a safer way to handle potential absence of values, compared to using nulls in many other languages. - -```rust -struct Option { - None, - Some(T), -} -``` - -The `Option` type, already imported into your Noir program, can be used directly: - -```rust -fn main() { - let none = Option::none(); - let some = Option::some(3); -} -``` - -See [this test](https://github.com/noir-lang/noir/blob/5cbfb9c4a06c8865c98ff2b594464b037d821a5c/crates/nargo_cli/tests/test_data/option/src/main.nr) for a more comprehensive set of examples of each of the methods described below. - -## Methods - -### none - -Constructs a none value. - -### some - -Constructs a some wrapper around a given value. - -### is_none - -Returns true if the Option is None. - -### is_some - -Returns true of the Option is Some. - -### unwrap - -Asserts `self.is_some()` and returns the wrapped value. - -### unwrap_unchecked - -Returns the inner value without asserting `self.is_some()`. This method can be useful within an if condition when we already know that `option.is_some()`. If the option is None, there is no guarantee what value will be returned, only that it will be of type T for an `Option`. - -### unwrap_or - -Returns the wrapped value if `self.is_some()`. Otherwise, returns the given default value. - -### unwrap_or_else - -Returns the wrapped value if `self.is_some()`. Otherwise, calls the given function to return a default value. - -### map - -If self is `Some(x)`, this returns `Some(f(x))`. Otherwise, this returns `None`. - -### map_or - -If self is `Some(x)`, this returns `f(x)`. Otherwise, this returns the given default value. - -### map_or_else - -If self is `Some(x)`, this returns `f(x)`. Otherwise, this returns `default()`. - -### and - -Returns None if self is None. Otherwise, this returns `other`. - -### and_then - -If self is None, this returns None. Otherwise, this calls the given function with the Some value contained within self, and returns the result of that call. In some languages this function is called `flat_map` or `bind`. - -### or - -If self is Some, return self. Otherwise, return `other`. - -### or_else - -If self is Some, return self. Otherwise, return `default()`. - -### xor - -If only one of the two Options is Some, return that option. Otherwise, if both options are Some or both are None, None is returned. - -### filter - -Returns `Some(x)` if self is `Some(x)` and `predicate(x)` is true. Otherwise, this returns `None`. - -### flatten - -Flattens an `Option>` into a `Option`. This returns `None` if the outer Option is None. Otherwise, this returns the inner Option. diff --git a/noir/docs/processed-docs-cache/noir/standard_library/recursion.md b/noir/docs/processed-docs-cache/noir/standard_library/recursion.md deleted file mode 100644 index f252150c8b5..00000000000 --- a/noir/docs/processed-docs-cache/noir/standard_library/recursion.md +++ /dev/null @@ -1,68 +0,0 @@ ---- -title: Recursive Proofs -description: Learn about how to write recursive proofs in Noir. -keywords: [recursion, recursive proofs, verification_key, verify_proof] ---- - -Noir supports recursively verifying proofs, meaning you verify the proof of a Noir program in another Noir program. This enables creating proofs of arbitrary size by doing step-wise verification of smaller components of a large proof. - -Read [the explainer on recursion](../../explainers/explainer-recursion.md) to know more about this function and the [guide on how to use it.](../../how_to/how-to-recursion.md) - -```rust -#[foreign(verify_proof)] -fn verify_proof(_verification_key : [Field], _proof : [Field], _public_input : Field, _key_hash : Field) {} -``` - -:::info - -This is a black box function. Read [this section](./black_box_fns) to learn more about black box functions in Noir. - -::: - -## Example usage - -```rust -use dep::std; - -fn main( - verification_key : [Field; 114], - proof : [Field; 93], - public_inputs : [Field; 1], - key_hash : Field, - proof_b : [Field; 93], -) { - std::verify_proof( - verification_key.as_slice(), - proof.as_slice(), - public_inputs.as_slice(), - key_hash - ); - - std::verify_proof( - verification_key.as_slice(), - proof_b.as_slice(), - public_inputs.as_slice(), - key_hash - ); -} -``` - -You can see a full example of recursive proofs in [this example recursion demo repo](https://github.com/noir-lang/noir-examples/tree/master/recursion). - -## Parameters - -### `verification_key` - -The verification key for the zk program that is being verified. - -### `proof` - -The proof for the zk program that is being verified. - -### `public_inputs` - -These represent the public inputs of the proof we are verifying. - -### `key_hash` - -A key hash is used to check the validity of the verification key. The circuit implementing this opcode can use this hash to ensure that the key provided to the circuit matches the key produced by the circuit creator. diff --git a/noir/docs/processed-docs-cache/noir/standard_library/traits.md b/noir/docs/processed-docs-cache/noir/standard_library/traits.md deleted file mode 100644 index 209f7820e1d..00000000000 --- a/noir/docs/processed-docs-cache/noir/standard_library/traits.md +++ /dev/null @@ -1,408 +0,0 @@ ---- -title: Traits -description: Noir's stdlib provides a few commonly used traits. -keywords: [traits, trait, interface, protocol, default, add, eq] ---- - -## `std::default` - -### `std::default::Default` - -```rust title="default-trait" showLineNumbers -trait Default { - fn default() -> Self; -} -``` -> Source code: noir_stdlib/src/default.nr#L1-L5 - - -Constructs a default value of a type. - -Implementations: -```rust -impl Default for Field { .. } - -impl Default for i8 { .. } -impl Default for i16 { .. } -impl Default for i32 { .. } -impl Default for i64 { .. } - -impl Default for u8 { .. } -impl Default for u16 { .. } -impl Default for u32 { .. } -impl Default for u64 { .. } - -impl Default for () { .. } -impl Default for bool { .. } - -impl Default for [T; N] - where T: Default { .. } - -impl Default for (A, B) - where A: Default, B: Default { .. } - -impl Default for (A, B, C) - where A: Default, B: Default, C: Default { .. } - -impl Default for (A, B, C, D) - where A: Default, B: Default, C: Default, D: Default { .. } - -impl Default for (A, B, C, D, E) - where A: Default, B: Default, C: Default, D: Default, E: Default { .. } -``` - -For primitive integer types, the return value of `default` is `0`. Container -types such as arrays are filled with default values of their element type. - - -## `std::convert` - -### `std::convert::From` - -```rust title="from-trait" showLineNumbers -trait From { - fn from(input: T) -> Self; -} -``` -> Source code: noir_stdlib/src/convert.nr#L1-L5 - - -The `From` trait defines how to convert from a given type `T` to the type on which the trait is implemented. - -The Noir standard library provides a number of implementations of `From` between primitive types. -```rust title="from-impls" showLineNumbers -// Unsigned integers -impl From for u16 { fn from(value: u8) -> u16 { value as u16 } } - -impl From for u32 { fn from(value: u8) -> u32 { value as u32 } } -impl From for u32 { fn from(value: u16) -> u32 { value as u32 } } - -impl From for u64 { fn from(value: u8) -> u64 { value as u64 } } -impl From for u64 { fn from(value: u16) -> u64 { value as u64 } } -impl From for u64 { fn from(value: u32) -> u64 { value as u64 } } - -impl From for Field { fn from(value: u8) -> Field { value as Field } } -impl From for Field { fn from(value: u16) -> Field { value as Field } } -impl From for Field { fn from(value: u32) -> Field { value as Field } } -impl From for Field { fn from(value: u64) -> Field { value as Field } } - -// Signed integers -impl From for i16 { fn from(value: i8) -> i16 { value as i16 } } - -impl From for i32 { fn from(value: i8) -> i32 { value as i32 } } -impl From for i32 { fn from(value: i16) -> i32 { value as i32 } } - -impl From for i64 { fn from(value: i8) -> i64 { value as i64 } } -impl From for i64 { fn from(value: i16) -> i64 { value as i64 } } -impl From for i64 { fn from(value: i32) -> i64 { value as i64 } } - -// Booleans -impl From for u8 { fn from(value: bool) -> u8 { value as u8 } } -impl From for u16 { fn from(value: bool) -> u16 { value as u16 } } -impl From for u32 { fn from(value: bool) -> u32 { value as u32 } } -impl From for u64 { fn from(value: bool) -> u64 { value as u64 } } -impl From for i8 { fn from(value: bool) -> i8 { value as i8 } } -impl From for i16 { fn from(value: bool) -> i16 { value as i16 } } -impl From for i32 { fn from(value: bool) -> i32 { value as i32 } } -impl From for i64 { fn from(value: bool) -> i64 { value as i64 } } -impl From for Field { fn from(value: bool) -> Field { value as Field } } -``` -> Source code: noir_stdlib/src/convert.nr#L25-L61 - - -#### When to implement `From` - -As a general rule of thumb, `From` may be implemented in the [situations where it would be suitable in Rust](https://doc.rust-lang.org/std/convert/trait.From.html#when-to-implement-from): - -- The conversion is *infallible*: Noir does not provide an equivalent to Rust's `TryFrom`, if the conversion can fail then provide a named method instead. -- The conversion is *lossless*: semantically, it should not lose or discard information. For example, `u32: From` can losslessly convert any `u16` into a valid `u32` such that the original `u16` can be recovered. On the other hand, `u16: From` should not be implemented as `2**16` is a `u32` which cannot be losslessly converted into a `u16`. -- The conversion is *value-preserving*: the conceptual kind and meaning of the resulting value is the same, even though the Noir type and technical representation might be different. While it's possible to infallibly and losslessly convert a `u8` into a `str<2>` hex representation, `4u8` and `"04"` are too different for `str<2>: From` to be implemented. -- The conversion is *obvious*: it's the only reasonable conversion between the two types. If there's ambiguity on how to convert between them such that the same input could potentially map to two different values then a named method should be used. For instance rather than implementing `U128: From<[u8; 16]>`, the methods `U128::from_le_bytes` and `U128::from_be_bytes` are used as otherwise the endianness of the array would be ambiguous, resulting in two potential values of `U128` from the same byte array. - -One additional recommendation specific to Noir is: -- The conversion is *efficient*: it's relatively cheap to convert between the two types. Due to being a ZK DSL, it's more important to avoid unnecessary computation compared to Rust. If the implementation of `From` would encourage users to perform unnecessary conversion, resulting in additional proving time, then it may be preferable to expose functionality such that this conversion may be avoided. - -### `std::convert::Into` - -The `Into` trait is defined as the reciprocal of `From`. It should be easy to convince yourself that if we can convert to type `A` from type `B`, then it's possible to convert type `B` into type `A`. - -For this reason, implementing `From` on a type will automatically generate a matching `Into` implementation. One should always prefer implementing `From` over `Into` as implementing `Into` will not generate a matching `From` implementation. - -```rust title="into-trait" showLineNumbers -trait Into { - fn into(input: Self) -> T; -} - -impl Into for U where T: From { - fn into(input: U) -> T { - T::from(input) - } -} -``` -> Source code: noir_stdlib/src/convert.nr#L13-L23 - - -`Into` is most useful when passing function arguments where the types don't quite match up with what the function expects. In this case, the compiler has enough type information to perform the necessary conversion by just appending `.into()` onto the arguments in question. - - -## `std::cmp` - -### `std::cmp::Eq` - -```rust title="eq-trait" showLineNumbers -trait Eq { - fn eq(self, other: Self) -> bool; -} -``` -> Source code: noir_stdlib/src/cmp.nr#L1-L5 - - -Returns `true` if `self` is equal to `other`. Implementing this trait on a type -allows the type to be used with `==` and `!=`. - -Implementations: -```rust -impl Eq for Field { .. } - -impl Eq for i8 { .. } -impl Eq for i16 { .. } -impl Eq for i32 { .. } -impl Eq for i64 { .. } - -impl Eq for u8 { .. } -impl Eq for u16 { .. } -impl Eq for u32 { .. } -impl Eq for u64 { .. } - -impl Eq for () { .. } -impl Eq for bool { .. } - -impl Eq for [T; N] - where T: Eq { .. } - -impl Eq for (A, B) - where A: Eq, B: Eq { .. } - -impl Eq for (A, B, C) - where A: Eq, B: Eq, C: Eq { .. } - -impl Eq for (A, B, C, D) - where A: Eq, B: Eq, C: Eq, D: Eq { .. } - -impl Eq for (A, B, C, D, E) - where A: Eq, B: Eq, C: Eq, D: Eq, E: Eq { .. } -``` - -### `std::cmp::Ord` - -```rust title="ord-trait" showLineNumbers -trait Ord { - fn cmp(self, other: Self) -> Ordering; -} -``` -> Source code: noir_stdlib/src/cmp.nr#L94-L98 - - -`a.cmp(b)` compares two values returning `Ordering::less()` if `a < b`, -`Ordering::equal()` if `a == b`, or `Ordering::greater()` if `a > b`. -Implementing this trait on a type allows `<`, `<=`, `>`, and `>=` to be -used on values of the type. - -Implementations: - -```rust -impl Ord for u8 { .. } -impl Ord for u16 { .. } -impl Ord for u32 { .. } -impl Ord for u64 { .. } - -impl Ord for i8 { .. } -impl Ord for i16 { .. } -impl Ord for i32 { .. } - -impl Ord for i64 { .. } - -impl Ord for () { .. } -impl Ord for bool { .. } - -impl Ord for [T; N] - where T: Ord { .. } - -impl Ord for (A, B) - where A: Ord, B: Ord { .. } - -impl Ord for (A, B, C) - where A: Ord, B: Ord, C: Ord { .. } - -impl Ord for (A, B, C, D) - where A: Ord, B: Ord, C: Ord, D: Ord { .. } - -impl Ord for (A, B, C, D, E) - where A: Ord, B: Ord, C: Ord, D: Ord, E: Ord { .. } -``` - -## `std::ops` - -### `std::ops::Add`, `std::ops::Sub`, `std::ops::Mul`, and `std::ops::Div` - -These traits abstract over addition, subtraction, multiplication, and division respectively. -Implementing these traits for a given type will also allow that type to be used with the corresponding operator -for that trait (`+` for Add, etc) in addition to the normal method names. - -```rust title="add-trait" showLineNumbers -trait Add { - fn add(self, other: Self) -> Self; -} -``` -> Source code: noir_stdlib/src/ops.nr#L1-L5 - -```rust title="sub-trait" showLineNumbers -trait Sub { - fn sub(self, other: Self) -> Self; -} -``` -> Source code: noir_stdlib/src/ops.nr#L19-L23 - -```rust title="mul-trait" showLineNumbers -trait Mul { - fn mul(self, other: Self) -> Self; -} -``` -> Source code: noir_stdlib/src/ops.nr#L37-L41 - -```rust title="div-trait" showLineNumbers -trait Div { - fn div(self, other: Self) -> Self; -} -``` -> Source code: noir_stdlib/src/ops.nr#L55-L59 - - -The implementations block below is given for the `Add` trait, but the same types that implement -`Add` also implement `Sub`, `Mul`, and `Div`. - -Implementations: -```rust -impl Add for Field { .. } - -impl Add for i8 { .. } -impl Add for i16 { .. } -impl Add for i32 { .. } -impl Add for i64 { .. } - -impl Add for u8 { .. } -impl Add for u16 { .. } -impl Add for u32 { .. } -impl Add for u64 { .. } -``` - -### `std::ops::Rem` - -```rust title="rem-trait" showLineNumbers -trait Rem{ - fn rem(self, other: Self) -> Self; -} -``` -> Source code: noir_stdlib/src/ops.nr#L73-L77 - - -`Rem::rem(a, b)` is the remainder function returning the result of what is -left after dividing `a` and `b`. Implementing `Rem` allows the `%` operator -to be used with the implementation type. - -Unlike other numeric traits, `Rem` is not implemented for `Field`. - -Implementations: -```rust -impl Rem for u8 { fn rem(self, other: u8) -> u8 { self % other } } -impl Rem for u16 { fn rem(self, other: u16) -> u16 { self % other } } -impl Rem for u32 { fn rem(self, other: u32) -> u32 { self % other } } -impl Rem for u64 { fn rem(self, other: u64) -> u64 { self % other } } - -impl Rem for i8 { fn rem(self, other: i8) -> i8 { self % other } } -impl Rem for i16 { fn rem(self, other: i16) -> i16 { self % other } } -impl Rem for i32 { fn rem(self, other: i32) -> i32 { self % other } } -impl Rem for i64 { fn rem(self, other: i64) -> i64 { self % other } } -``` - -### `std::ops::{ BitOr, BitAnd, BitXor }` - -```rust title="bitor-trait" showLineNumbers -trait BitOr { - fn bitor(self, other: Self) -> Self; -} -``` -> Source code: noir_stdlib/src/ops.nr#L89-L93 - -```rust title="bitand-trait" showLineNumbers -trait BitAnd { - fn bitand(self, other: Self) -> Self; -} -``` -> Source code: noir_stdlib/src/ops.nr#L107-L111 - -```rust title="bitxor-trait" showLineNumbers -trait BitXor { - fn bitxor(self, other: Self) -> Self; -} -``` -> Source code: noir_stdlib/src/ops.nr#L125-L129 - - -Traits for the bitwise operations `|`, `&`, and `^`. - -Implementing `BitOr`, `BitAnd` or `BitXor` for a type allows the `|`, `&`, or `^` operator respectively -to be used with the type. - -The implementations block below is given for the `BitOr` trait, but the same types that implement -`BitOr` also implement `BitAnd` and `BitXor`. - -Implementations: -```rust -impl BitOr for bool { fn bitor(self, other: bool) -> bool { self | other } } - -impl BitOr for u8 { fn bitor(self, other: u8) -> u8 { self | other } } -impl BitOr for u16 { fn bitor(self, other: u16) -> u16 { self | other } } -impl BitOr for u32 { fn bitor(self, other: u32) -> u32 { self | other } } -impl BitOr for u64 { fn bitor(self, other: u64) -> u64 { self | other } } - -impl BitOr for i8 { fn bitor(self, other: i8) -> i8 { self | other } } -impl BitOr for i16 { fn bitor(self, other: i16) -> i16 { self | other } } -impl BitOr for i32 { fn bitor(self, other: i32) -> i32 { self | other } } -impl BitOr for i64 { fn bitor(self, other: i64) -> i64 { self | other } } -``` - -### `std::ops::{ Shl, Shr }` - -```rust title="shl-trait" showLineNumbers -trait Shl { - fn shl(self, other: Self) -> Self; -} -``` -> Source code: noir_stdlib/src/ops.nr#L143-L147 - -```rust title="shr-trait" showLineNumbers -trait Shr { - fn shr(self, other: Self) -> Self; -} -``` -> Source code: noir_stdlib/src/ops.nr#L160-L164 - - -Traits for a bit shift left and bit shift right. - -Implementing `Shl` for a type allows the left shift operator (`<<`) to be used with the implementation type. -Similarly, implementing `Shr` allows the right shift operator (`>>`) to be used with the type. - -Note that bit shifting is not currently implemented for signed types. - -The implementations block below is given for the `Shl` trait, but the same types that implement -`Shl` also implement `Shr`. - -Implementations: -```rust -impl Shl for u8 { fn shl(self, other: u8) -> u8 { self << other } } -impl Shl for u16 { fn shl(self, other: u16) -> u16 { self << other } } -impl Shl for u32 { fn shl(self, other: u32) -> u32 { self << other } } -impl Shl for u64 { fn shl(self, other: u64) -> u64 { self << other } } -``` diff --git a/noir/docs/processed-docs-cache/noir/standard_library/zeroed.md b/noir/docs/processed-docs-cache/noir/standard_library/zeroed.md deleted file mode 100644 index 97dab02dac2..00000000000 --- a/noir/docs/processed-docs-cache/noir/standard_library/zeroed.md +++ /dev/null @@ -1,25 +0,0 @@ ---- -title: Zeroed Function -description: - The zeroed function returns a zeroed value of any type. -keywords: - [ - zeroed - ] ---- - -Implements `fn zeroed() -> T` to return a zeroed value of any type. This function is generally unsafe to use as the zeroed bit pattern is not guaranteed to be valid for all types. It can however, be useful in cases when the value is guaranteed not to be used such as in a BoundedVec library implementing a growable vector, up to a certain length, backed by an array. The array can be initialized with zeroed values which are guaranteed to be inaccessible until the vector is pushed to. Similarly, enumerations in noir can be implemented using this method by providing zeroed values for the unused variants. - -You can access the function at `std::unsafe::zeroed`. - -This function currently supports the following types: - -- Field -- Bool -- Uint -- Array -- String -- Tuple -- Function - -Using it on other types could result in unexpected behavior. diff --git a/noir/docs/processed-docs-cache/reference/_category_.json b/noir/docs/processed-docs-cache/reference/_category_.json deleted file mode 100644 index 5b6a20a609a..00000000000 --- a/noir/docs/processed-docs-cache/reference/_category_.json +++ /dev/null @@ -1,5 +0,0 @@ -{ - "position": 4, - "collapsible": true, - "collapsed": true -} diff --git a/noir/docs/processed-docs-cache/reference/nargo_commands.md b/noir/docs/processed-docs-cache/reference/nargo_commands.md deleted file mode 100644 index fc2671b2bfc..00000000000 --- a/noir/docs/processed-docs-cache/reference/nargo_commands.md +++ /dev/null @@ -1,253 +0,0 @@ ---- -title: Nargo -description: - Noir CLI Commands for Noir Prover and Verifier to create, execute, prove and verify programs, - generate Solidity verifier smart contract and compile into JSON file containing ACIR - representation and ABI of circuit. -keywords: - [ - Nargo, - Noir CLI, - Noir Prover, - Noir Verifier, - generate Solidity verifier, - compile JSON file, - ACIR representation, - ABI of circuit, - TypeScript, - ] -sidebar_position: 0 ---- - -## General options - -| Option | Description | -| -------------------- | -------------------------------------------------- | -| `--show-ssa` | Emit debug information for the intermediate SSA IR | -| `--deny-warnings` | Quit execution when warnings are emitted | -| `--silence-warnings` | Suppress warnings | -| `-h, --help` | Print help | - -## `nargo help [subcommand]` - -Prints the list of available commands or specific information of a subcommand. - -_Arguments_ - -| Argument | Description | -| -------------- | -------------------------------------------- | -| `` | The subcommand whose help message to display | - -## `nargo backend` - -Installs and selects custom backends used to generate and verify proofs. - -### Commands - -| Command | Description | -| ----------- | --------------------------------------------------------- | -| `current` | Prints the name of the currently active backend | -| `ls` | Prints the list of currently installed backends | -| `use` | Select the backend to use | -| `install` | Install a new backend from a URL | -| `uninstall` | Uninstalls a backend | -| `help` | Print this message or the help of the given subcommand(s) | - -### Options - -| Option | Description | -| ------------ | ----------- | -| `-h, --help` | Print help | - -## `nargo check` - -Generate the `Prover.toml` and `Verifier.toml` files for specifying prover and verifier in/output -values of the Noir program respectively. - -### Options - -| Option | Description | -| --------------------- | ------------------------------------- | -| `--package ` | The name of the package to check | -| `--workspace` | Check all packages in the workspace | -| `--print-acir` | Display the ACIR for compiled circuit | -| `--deny-warnings` | Treat all warnings as errors | -| `--silence-warnings` | Suppress warnings | -| `-h, --help` | Print help | - -### `nargo codegen-verifier` - -Generate a Solidity verifier smart contract for the program. - -### Options - -| Option | Description | -| --------------------- | ------------------------------------- | -| `--package ` | The name of the package to codegen | -| `--workspace` | Codegen all packages in the workspace | -| `--print-acir` | Display the ACIR for compiled circuit | -| `--deny-warnings` | Treat all warnings as errors | -| `--silence-warnings` | Suppress warnings | -| `-h, --help` | Print help | - -## `nargo compile` - -Compile the program into a JSON build artifact file containing the ACIR representation and the ABI -of the circuit. This build artifact can then be used to generate and verify proofs. - -You can also use "build" as an alias for compile (e.g. `nargo build`). - -### Options - -| Option | Description | -| --------------------- | ------------------------------------------------------------ | -| `--package ` | The name of the package to compile | -| `--workspace` | Compile all packages in the workspace | -| `--print-acir` | Display the ACIR for compiled circuit | -| `--deny-warnings` | Treat all warnings as errors | -| `--silence-warnings` | Suppress warnings | -| `-h, --help` | Print help | - -## `nargo new ` - -Creates a new Noir project in a new folder. - -**Arguments** - -| Argument | Description | -| -------- | -------------------------------- | -| `` | The path to save the new project | - -### Options - -| Option | Description | -| --------------- | ----------------------------------------------------- | -| `--name ` | Name of the package [default: package directory name] | -| `--lib` | Use a library template | -| `--bin` | Use a binary template [default] | -| `--contract` | Use a contract template | -| `-h, --help` | Print help | - -## `nargo init` - -Creates a new Noir project in the current directory. - -### Options - -| Option | Description | -| --------------- | ----------------------------------------------------- | -| `--name ` | Name of the package [default: current directory name] | -| `--lib` | Use a library template | -| `--bin` | Use a binary template [default] | -| `--contract` | Use a contract template | -| `-h, --help` | Print help | - -## `nargo execute [WITNESS_NAME]` - -Runs the Noir program and prints its return value. - -**Arguments** - -| Argument | Description | -| ---------------- | ----------------------------------------- | -| `[WITNESS_NAME]` | Write the execution witness to named file | - -### Options - -| Option | Description | -| --------------------------------- | ------------------------------------------------------------------------------------ | -| `-p, --prover-name ` | The name of the toml file which contains the inputs for the prover [default: Prover] | -| `--package ` | The name of the package to execute | -| `--workspace` | Execute all packages in the workspace | -| `--print-acir` | Display the ACIR for compiled circuit | -| `--deny-warnings` | Treat all warnings as errors | -| `--silence-warnings` | Suppress warnings | -| `--oracle-resolver` | JSON RPC url to solve oracle calls | -| `-h, --help` | Print help | - -_Usage_ - -The inputs to the circuit are read from the `Prover.toml` file generated by `nargo check`, which -must be filled in. - -To save the witness to file, run the command with a value for the `WITNESS_NAME` argument. A -`.tr` file will then be saved in the `./target` folder. - -## `nargo prove` - -Creates a proof for the program. - -### Options - -| Option | Description | -| ------------------------------------- | ---------------------------------------------------------------------------------------- | -| `-p, --prover-name ` | The name of the toml file which contains the inputs for the prover [default: Prover] | -| `-v, --verifier-name ` | The name of the toml file which contains the inputs for the verifier [default: Verifier] | -| `--verify` | Verify proof after proving | -| `--package ` | The name of the package to prove | -| `--workspace` | Prove all packages in the workspace | -| `--print-acir` | Display the ACIR for compiled circuit | -| `--deny-warnings` | Treat all warnings as errors | -| `--silence-warnings` | Suppress warnings | -| `--oracle-resolver` | JSON RPC url to solve oracle calls | -| `-h, --help` | Print help | - -## `nargo verify` - -Given a proof and a program, verify whether the proof is valid. - -### Options - -| Option | Description | -| ------------------------------------- | ---------------------------------------------------------------------------------------- | -| `-v, --verifier-name ` | The name of the toml file which contains the inputs for the verifier [default: Verifier] | -| `--package ` | The name of the package to verify | -| `--workspace` | Verify all packages in the workspace | -| `--print-acir` | Display the ACIR for compiled circuit | -| `--deny-warnings` | Treat all warnings as errors | -| `--silence-warnings` | Suppress warnings | -| `-h, --help` | Print help | - -## `nargo test [TEST_NAME]` - -Nargo will automatically compile and run any functions which have the decorator `#[test]` on them if -you run `nargo test`. To print `println` statements in tests, use the `--show-output` flag. - -Takes an optional `--exact` flag which allows you to select tests based on an exact name. - -See an example on the [testing page](../getting_started/tooling/testing.md). - -### Options - -| Option | Description | -| --------------------- | -------------------------------------- | -| `--show-output` | Display output of `println` statements | -| `--exact` | Only run tests that match exactly | -| `--package ` | The name of the package to test | -| `--workspace` | Test all packages in the workspace | -| `--print-acir` | Display the ACIR for compiled circuit | -| `--deny-warnings` | Treat all warnings as errors | -| `--silence-warnings` | Suppress warnings | -| `--oracle-resolver` | JSON RPC url to solve oracle calls | -| `-h, --help` | Print help | - -## `nargo info` - -Prints a table containing the information of the package. - -Currently the table provide - -1. The number of ACIR opcodes -2. The final number gates in the circuit used by a backend - -If the file contains a contract the table will provide the -above information about each function of the contract. - -## `nargo lsp` - -Start a long-running Language Server process that communicates over stdin/stdout. -Usually this command is not run by a user, but instead will be run by a Language Client, such as [vscode-noir](https://github.com/noir-lang/vscode-noir). - -## `nargo fmt` - -Automatically formats your Noir source code based on the default formatting settings. diff --git a/noir/docs/processed-docs-cache/tutorials/noirjs_app.md b/noir/docs/processed-docs-cache/tutorials/noirjs_app.md deleted file mode 100644 index 23534795dde..00000000000 --- a/noir/docs/processed-docs-cache/tutorials/noirjs_app.md +++ /dev/null @@ -1,279 +0,0 @@ ---- -title: Building a web app with NoirJS -description: Learn how to setup a new app that uses Noir to generate and verify zero-knowledge SNARK proofs in a typescript or javascript environment. -keywords: [how to, guide, javascript, typescript, noir, barretenberg, zero-knowledge, proofs, app] -sidebar_position: 0 -pagination_next: noir/concepts/data_types/index ---- - -NoirJS is a set of packages meant to work both in a browser and a server environment. In this tutorial, we will build a simple web app using them. From here, you should get an idea on how to proceed with your own Noir projects! - -You can find the complete app code for this guide [here](https://github.com/noir-lang/tiny-noirjs-app). - -## Setup - -:::note - -Feel free to use whatever versions, just keep in mind that Nargo and the NoirJS packages are meant to be in sync. For example, Nargo 0.19.x matches `noir_js@0.19.x`, etc. - -In this guide, we will be pinned to 0.19.4. - -::: - -Before we start, we want to make sure we have Node and Nargo installed. - -We start by opening a terminal and executing `node --version`. If we don't get an output like `v20.10.0`, that means node is not installed. Let's do that by following the handy [nvm guide](https://github.com/nvm-sh/nvm?tab=readme-ov-file#install--update-script). - -As for `Nargo`, we can follow the the [Nargo guide](../getting_started/installation/index.md) to install it. If you're lazy, just paste this on a terminal and run `noirup`: - -```sh -curl -L https://raw.githubusercontent.com/noir-lang/noirup/main/install | bash -``` - -Easy enough. Onwards! - -## Our project - -ZK is a powerful technology. An app that doesn't reveal one of the inputs to *anyone* is almost unbelievable, yet Noir makes it as easy as a single line of code. - -In fact, it's so simple that it comes nicely packaged in `nargo`. Let's do that! - -### Nargo - -Run: - -```nargo new circuit``` - -And... That's about it. Your program is ready to be compiled and run. - -To compile, let's `cd` into the `circuit` folder to enter our project, and call: - -```nargo compile``` - -This compiles our circuit into `json` format and add it to a new `target` folder. - -:::info - -At this point in the tutorial, your folder structure should look like this: - -```tree -. -└── circuit <---- our working directory - ├── Nargo.toml - ├── src - │ └── main.nr - └── target - └── circuit.json -``` - -::: - -### Node and Vite - -If you want to explore Nargo, feel free to go on a side-quest now and follow the steps in the -[getting started](../getting_started/hello_noir/index.md) guide. However, we want our app to run on the browser, so we need Vite. - -Vite is a powerful tool to generate static websites. While it provides all kinds of features, let's just go barebones with some good old vanilla JS. - -To do this this, go back to the previous folder (`cd ..`) and create a new vite project by running `npm create vite` and choosing "Vanilla" and "Javascript". - -You should see `vite-project` appear in your root folder. This seems like a good time to `cd` into it and install our NoirJS packages: - -```bash -npm i @noir-lang/backend_barretenberg@0.19.4 @noir-lang/noir_js@0.19.4 -``` - -:::info - -At this point in the tutorial, your folder structure should look like this: - -```tree -. -└── circuit - └── ...etc... -└── vite-project <---- our working directory - └── ...etc... -``` - -::: - -#### Some cleanup - -`npx create vite` is amazing but it creates a bunch of files we don't really need for our simple example. Actually, let's just delete everything except for `index.html`, `main.js` and `package.json`. I feel lighter already. - -![my heart is ready for you, noir.js](../../static/img/memes/titanic.jpeg) - -## HTML - -Our app won't run like this, of course. We need some working HTML, at least. Let's open our broken-hearted `index.html` and replace everything with this code snippet: - -```html - - - - - - -

Noir app

-
- - -
-
-

Logs

-

Proof

-
- - -``` - -It *could* be a beautiful UI... Depending on which universe you live in. - -## Some good old vanilla Javascript - -Our love for Noir needs undivided attention, so let's just open `main.js` and delete everything (this is where the romantic scenery becomes a bit creepy). - -Start by pasting in this boilerplate code: - -```js -const setup = async () => { - await Promise.all([ - import("@noir-lang/noirc_abi").then(module => - module.default(new URL("@noir-lang/noirc_abi/web/noirc_abi_wasm_bg.wasm", import.meta.url).toString()) - ), - import("@noir-lang/acvm_js").then(module => - module.default(new URL("@noir-lang/acvm_js/web/acvm_js_bg.wasm", import.meta.url).toString()) - ) - ]); -} - -function display(container, msg) { - const c = document.getElementById(container); - const p = document.createElement('p'); - p.textContent = msg; - c.appendChild(p); -} - -document.getElementById('submitGuess').addEventListener('click', async () => { - try { - // here's where love happens - } catch(err) { - display("logs", "Oh 💔 Wrong guess") - } -}); - -``` - -The display function doesn't do much. We're simply manipulating our website to see stuff happening. For example, if the proof fails, it will simply log a broken heart 😢 - -As for the `setup` function, it's just a sad reminder that dealing with `wasm` on the browser is not as easy as it should. Just copy, paste, and forget. - -:::info - -At this point in the tutorial, your folder structure should look like this: - -```tree -. -└── circuit - └── ...same as above -└── vite-project - ├── main.js - ├── package.json - └── index.html -``` - -You'll see other files and folders showing up (like `package-lock.json`, `node_modules`) but you shouldn't have to care about those. - -::: - -## Some NoirJS - -We're starting with the good stuff now. If you've compiled the circuit as described above, you should have a `json` file we want to import at the very top of our `main.js` file: - -```ts -import circuit from '../circuit/target/circuit.json'; -``` - -[Noir is backend-agnostic](../index.mdx#whats-new-about-noir). We write Noir, but we also need a proving backend. That's why we need to import and instantiate the two dependencies we installed above: `BarretenbergBackend` and `Noir`. Let's import them right below: - -```js -import { BarretenbergBackend } from '@noir-lang/backend_barretenberg'; -import { Noir } from '@noir-lang/noir_js'; -``` - -And instantiate them inside our try-catch block: - -```ts -// try { -const backend = new BarretenbergBackend(circuit); -const noir = new Noir(circuit, backend); -// } -``` - -:::note - -For the remainder of the tutorial, everything will be happening inside the `try` block - -::: - -## Our app - -Now for the app itself. We're capturing whatever is in the input when people press the submit button. Just add this: - -```js -const x = parseInt(document.getElementById('guessInput').value); -const input = { x, y: 2 }; -``` - -Now we're ready to prove stuff! Let's feed some inputs to our circuit and calculate the proof: - -```js -await setup(); // let's squeeze our wasm inits here - -display('logs', 'Generating proof... ⌛'); -const proof = await noir.generateFinalProof(input); -display('logs', 'Generating proof... ✅'); -display('results', proof.proof); -``` - -You're probably eager to see stuff happening, so go and run your app now! - -From your terminal, run `npm run dev`. If it doesn't open a browser for you, just visit `localhost:5173`. You should now see the worst UI ever, with an ugly input. - -![Getting Started 0](@site/static/img/noir_getting_started_1.png) - -Now, our circuit says `fn main(x: Field, y: pub Field)`. This means only the `y` value is public, and it's hardcoded above: `input = { x, y: 2 }`. In other words, you won't need to send your secret`x` to the verifier! - -By inputting any number other than 2 in the input box and clicking "submit", you should get a valid proof. Otherwise the proof won't even generate correctly. By the way, if you're human, you shouldn't be able to understand anything on the "proof" box. That's OK. We like you, human ❤️. - -## Verifying - -Time to celebrate, yes! But we shouldn't trust machines so blindly. Let's add these lines to see our proof being verified: - -```js -display('logs', 'Verifying proof... ⌛'); -const verification = await noir.verifyFinalProof(proof); -if (verification) display('logs', 'Verifying proof... ✅'); -``` - -You have successfully generated a client-side Noir web app! - -![coded app without math knowledge](../../static/img/memes/flextape.jpeg) - -## Further Reading - -You can see how noirjs is used in a full stack Next.js hardhat application in the [noir-starter repo here](https://github.com/noir-lang/noir-starter/tree/main/vite-hardhat). The example shows how to calculate a proof in the browser and verify it with a deployed Solidity verifier contract from noirjs. - -You should also check out the more advanced examples in the [noir-examples repo](https://github.com/noir-lang/noir-examples), where you'll find reference usage for some cool apps. diff --git a/noir/docs/processed-docs/explainers/explainer-oracle.md b/noir/docs/processed-docs/explainers/explainer-oracle.md deleted file mode 100644 index b84ca5dd986..00000000000 --- a/noir/docs/processed-docs/explainers/explainer-oracle.md +++ /dev/null @@ -1,57 +0,0 @@ ---- -title: Oracles -description: This guide provides an in-depth understanding of how Oracles work in Noir programming. Learn how to use outside calculations in your programs, constrain oracles, and understand their uses and limitations. -keywords: - - Noir Programming - - Oracles - - JSON-RPC - - Foreign Call Handlers - - Constrained Functions - - Blockchain Programming -sidebar_position: 1 ---- - -If you've seen "The Matrix" you may recall "The Oracle" as Gloria Foster smoking cigarettes and baking cookies. While she appears to "know things", she is actually providing a calculation of a pre-determined future. Noir Oracles are similar, in a way. They don't calculate the future (yet), but they allow you to use outside calculations in your programs. - -![matrix oracle prediction](@site/static/img/memes/matrix_oracle.jpeg) - -A Noir program is usually self-contained. You can pass certain inputs to it, and it will generate a deterministic output for those inputs. But what if you wanted to defer some calculation to an outside process or source? - -Oracles are functions that provide this feature. - -## Use cases - -An example usage for Oracles is proving something on-chain. For example, proving that the ETH-USDC quote was below a certain target at a certain block time. Or even making more complex proofs like proving the ownership of an NFT as an anonymous login method. - -Another interesting use case is to defer expensive calculations to be made outside of the Noir program, and then constraining the result; similar to the use of [unconstrained functions](../noir/concepts//unconstrained.md). - -In short, anything that can be constrained in a Noir program but needs to be fetched from an external source is a great candidate to be used in oracles. - -## Constraining oracles - -Just like in The Matrix, Oracles are powerful. But with great power, comes great responsibility. Just because you're using them in a Noir program doesn't mean they're true. Noir has no superpowers. If you want to prove that Portugal won the Euro Cup 2016, you're still relying on potentially untrusted information. - -To give a concrete example, Alice wants to login to the [NounsDAO](https://nouns.wtf/) forum with her username "noir_nouner" by proving she owns a noun without revealing her ethereum address. Her Noir program could have a oracle call like this: - -```rust -#[oracle(getNoun)] -unconstrained fn get_noun(address: Field) -> Field -``` - -This oracle could naively resolve with the number of Nouns she possesses. However, it is useless as a trusted source, as the oracle could resolve to anything Alice wants. In order to make this oracle call actually useful, Alice would need to constrain the response from the oracle, by proving her address and the noun count belongs to the state tree of the contract. - -In short, **Oracles don't prove anything. Your Noir program does.** - -:::danger - -If you don't constrain the return of your oracle, you could be clearly opening an attack vector on your Noir program. Make double-triple sure that the return of an oracle call is constrained! - -::: - -## How to use Oracles - -On CLI, Nargo resolves oracles by making JSON RPC calls, which means it would require an RPC node to be running. - -In JavaScript, NoirJS accepts and resolves arbitrary call handlers (that is, not limited to JSON) as long as they matches the expected types the developer defines. Refer to [Foreign Call Handler](../reference/NoirJS/noir_js/type-aliases/ForeignCallHandler.md) to learn more about NoirJS's call handling. - -If you want to build using oracles, follow through to the [oracle guide](../how_to/how-to-oracles.md) for a simple example on how to do that. diff --git a/noir/docs/processed-docs/explainers/explainer-recursion.md b/noir/docs/processed-docs/explainers/explainer-recursion.md deleted file mode 100644 index 18846176ca7..00000000000 --- a/noir/docs/processed-docs/explainers/explainer-recursion.md +++ /dev/null @@ -1,176 +0,0 @@ ---- -title: Recursive proofs -description: Explore the concept of recursive proofs in Zero-Knowledge programming. Understand how recursion works in Noir, a language for writing smart contracts on the EVM blockchain. Learn through practical examples like Alice and Bob's guessing game, Charlie's recursive merkle tree, and Daniel's reusable components. Discover how to use recursive proofs to optimize computational resources and improve efficiency. - -keywords: - [ - "Recursive Proofs", - "Zero-Knowledge Programming", - "Noir", - "EVM Blockchain", - "Smart Contracts", - "Recursion in Noir", - "Alice and Bob Guessing Game", - "Recursive Merkle Tree", - "Reusable Components", - "Optimizing Computational Resources", - "Improving Efficiency", - "Verification Key", - "Aggregation", - "Recursive zkSNARK schemes", - "PLONK", - "Proving and Verification Keys" - ] -sidebar_position: 1 -pagination_next: how_to/how-to-recursion ---- - -In programming, we tend to think of recursion as something calling itself. A classic example would be the calculation of the factorial of a number: - -```js -function factorial(n) { - if (n === 0 || n === 1) { - return 1; - } else { - return n * factorial(n - 1); - } -} -``` - -In this case, while `n` is not `1`, this function will keep calling itself until it hits the base case, bubbling up the result on the call stack: - -```md - Is `n` 1? <--------- - /\ / - / \ n = n -1 - / \ / - Yes No -------- -``` - -In Zero-Knowledge, recursion has some similarities. - -It is not a Noir function calling itself, but a proof being used as an input to another circuit. In short, you verify one proof *inside* another proof, returning the proof that both proofs are valid. - -This means that, given enough computational resources, you can prove the correctness of any arbitrary number of proofs in a single proof. This could be useful to design state channels (for which a common example would be [Bitcoin's Lightning Network](https://en.wikipedia.org/wiki/Lightning_Network)), to save on gas costs by settling one proof on-chain, or simply to make business logic less dependent on a consensus mechanism. - -## Examples - -Let us look at some of these examples - -### Alice and Bob - Guessing game - -Alice and Bob are friends, and they like guessing games. They want to play a guessing game online, but for that, they need a trusted third-party that knows both of their secrets and finishes the game once someone wins. - -So, they use zero-knowledge proofs. Alice tries to guess Bob's number, and Bob will generate a ZK proof stating whether she succeeded or failed. - -This ZK proof can go on a smart contract, revealing the winner and even giving prizes. However, this means every turn needs to be verified on-chain. This incurs some cost and waiting time that may simply make the game too expensive or time-consuming to be worth it. - -As a solution, Alice proposes the following: "what if Bob generates his proof, and instead of sending it on-chain, I verify it *within* my own proof before playing my own turn?". - -She can then generate a proof that she verified his proof, and so on. - -```md - Did you fail? <-------------------------- - / \ / - / \ n = n -1 - / \ / - Yes No / - | | / - | | / - | You win / - | / - | / -Generate proof of that / - + / - my own guess ---------------- -``` - -### Charlie - Recursive merkle tree - -Charlie is a concerned citizen, and wants to be sure his vote in an election is accounted for. He votes with a ZK proof, but he has no way of knowing that his ZK proof was included in the total vote count! - -If the vote collector puts all of the votes into a [Merkle tree](https://en.wikipedia.org/wiki/Merkle_tree), everyone can prove the verification of two proofs within one proof, as such: - -```md - abcd - __________|______________ - | | - ab cd - _____|_____ ______|______ - | | | | - alice bob charlie daniel -``` - -Doing this recursively allows us to arrive on a final proof `abcd` which if true, verifies the correctness of all the votes. - -### Daniel - Reusable components - -Daniel has a big circuit and a big headache. A part of his circuit is a setup phase that finishes with some assertions that need to be made. But that section alone takes most of the proving time, and is largely independent of the rest of the circuit. - -He might find it more efficient to generate a proof for that setup phase separately, and verify that proof recursively in the actual business logic section of his circuit. This will allow for parallelization of both proofs, which results in a considerable speedup. - -## What params do I need - -As you can see in the [recursion reference](noir/standard_library/recursion.md), a simple recursive proof requires: - -- The proof to verify -- The Verification Key of the circuit that generated the proof -- A hash of this verification key, as it's needed for some backends -- The public inputs for the proof - -:::info - -Recursive zkSNARK schemes do not necessarily "verify a proof" in the sense that you expect a true or false to be spit out by the verifier. Rather an aggregation object is built over the public inputs. - -So, taking the example of Alice and Bob and their guessing game: - -- Alice makes her guess. Her proof is *not* recursive: it doesn't verify any proof within it! It's just a standard `assert(x != y)` circuit -- Bob verifies Alice's proof and makes his own guess. In this circuit, he doesn't exactly *prove* the verification of Alice's proof. Instead, he *aggregates* his proof to Alice's proof. The actual verification is done when the full proof is verified, for example when using `nargo verify` or through the verifier smart contract. - -We can imagine recursive proofs a [relay race](https://en.wikipedia.org/wiki/Relay_race). The first runner doesn't have to receive the baton from anyone else, as he/she already starts with it. But when his/her turn is over, the next runner needs to receive it, run a bit more, and pass it along. Even though every runner could theoretically verify the baton mid-run (why not? 🏃🔍), only at the end of the race does the referee verify that the whole race is valid. - -::: - -## Some architecture - -As with everything in computer science, there's no one-size-fits all. But there are some patterns that could help understanding and implementing them. To give three examples: - -### Adding some logic to a proof verification - -This would be an approach for something like our guessing game, where proofs are sent back and forth and are verified by each opponent. This circuit would be divided in two sections: - -- A `recursive verification` section, which would be just the call to `std::verify_proof`, and that would be skipped on the first move (since there's no proof to verify) -- A `guessing` section, which is basically the logic part where the actual guessing happens - -In such a situation, and assuming Alice is first, she would skip the first part and try to guess Bob's number. Bob would then verify her proof on the first section of his run, and try to guess Alice's number on the second part, and so on. - -### Aggregating proofs - -In some one-way interaction situations, recursion would allow for aggregation of simple proofs that don't need to be immediately verified on-chain or elsewhere. - -To give a practical example, a barman wouldn't need to verify a "proof-of-age" on-chain every time he serves alcohol to a customer. Instead, the architecture would comprise two circuits: - -- A `main`, non-recursive circuit with some logic -- A `recursive` circuit meant to verify two proofs in one proof - -The customer's proofs would be intermediate, and made on their phones, and the barman could just verify them locally. He would then aggregate them into a final proof sent on-chain (or elsewhere) at the end of the day. - -### Recursively verifying different circuits - -Nothing prevents you from verifying different circuits in a recursive proof, for example: - -- A `circuit1` circuit -- A `circuit2` circuit -- A `recursive` circuit - -In this example, a regulator could verify that taxes were paid for a specific purchase by aggregating both a `payer` circuit (proving that a purchase was made and taxes were paid), and a `receipt` circuit (proving that the payment was received) - -## How fast is it - -At the time of writing, verifying recursive proofs is surprisingly fast. This is because most of the time is spent on generating the verification key that will be used to generate the next proof. So you are able to cache the verification key and reuse it later. - -Currently, Noir JS packages don't expose the functionality of loading proving and verification keys, but that feature exists in the underlying `bb.js` package. - -## How can I try it - -Learn more about using recursion in Nargo and NoirJS in the [how-to guide](../how_to/how-to-recursion.md) and see a full example in [noir-examples](https://github.com/noir-lang/noir-examples). diff --git a/noir/docs/processed-docs/getting_started/_category_.json b/noir/docs/processed-docs/getting_started/_category_.json deleted file mode 100644 index 5d694210bbf..00000000000 --- a/noir/docs/processed-docs/getting_started/_category_.json +++ /dev/null @@ -1,5 +0,0 @@ -{ - "position": 0, - "collapsible": true, - "collapsed": true -} diff --git a/noir/docs/processed-docs/getting_started/hello_noir/_category_.json b/noir/docs/processed-docs/getting_started/hello_noir/_category_.json deleted file mode 100644 index 23b560f610b..00000000000 --- a/noir/docs/processed-docs/getting_started/hello_noir/_category_.json +++ /dev/null @@ -1,5 +0,0 @@ -{ - "position": 1, - "collapsible": true, - "collapsed": true -} diff --git a/noir/docs/processed-docs/getting_started/hello_noir/index.md b/noir/docs/processed-docs/getting_started/hello_noir/index.md deleted file mode 100644 index 743c4d8d634..00000000000 --- a/noir/docs/processed-docs/getting_started/hello_noir/index.md +++ /dev/null @@ -1,142 +0,0 @@ ---- -title: Creating a Project -description: - Learn how to create and verify your first Noir program using Nargo, a programming language for - zero-knowledge proofs. -keywords: - [ - Nargo, - Noir, - zero-knowledge proofs, - programming language, - create Noir program, - verify Noir program, - step-by-step guide, - ] -sidebar_position: 1 - ---- - -Now that we have installed Nargo, it is time to make our first hello world program! - -## Create a Project Directory - -Noir code can live anywhere on your computer. Let us create a _projects_ folder in the home -directory to house our Noir programs. - -For Linux, macOS, and Windows PowerShell, create the directory and change directory into it by -running: - -```sh -mkdir ~/projects -cd ~/projects -``` - -## Create Our First Nargo Project - -Now that we are in the projects directory, create a new Nargo project by running: - -```sh -nargo new hello_world -``` - -> **Note:** `hello_world` can be any arbitrary project name, we are simply using `hello_world` for -> demonstration. -> -> In production, the common practice is to name the project folder as `circuits` for better -> identifiability when sitting alongside other folders in the codebase (e.g. `contracts`, `scripts`, -> `test`). - -A `hello_world` folder would be created. Similar to Rust, the folder houses _src/main.nr_ and -_Nargo.toml_ which contain the source code and environmental options of your Noir program -respectively. - -### Intro to Noir Syntax - -Let us take a closer look at _main.nr_. The default _main.nr_ generated should look like this: - -```rust -fn main(x : Field, y : pub Field) { - assert(x != y); -} -``` - -The first line of the program specifies the program's inputs: - -```rust -x : Field, y : pub Field -``` - -Program inputs in Noir are private by default (e.g. `x`), but can be labeled public using the -keyword `pub` (e.g. `y`). To learn more about private and public values, check the -[Data Types](../../noir/concepts/data_types/index.md) section. - -The next line of the program specifies its body: - -```rust -assert(x != y); -``` - -The Noir syntax `assert` can be interpreted as something similar to constraints in other zk-contract languages. - -For more Noir syntax, check the [Language Concepts](../../noir/concepts/comments.md) chapter. - -## Build In/Output Files - -Change directory into _hello_world_ and build in/output files for your Noir program by running: - -```sh -cd hello_world -nargo check -``` - -Two additional files would be generated in your project directory: - -_Prover.toml_ houses input values, and _Verifier.toml_ houses public values. - -## Prove Our Noir Program - -Now that the project is set up, we can create a proof of correct execution of our Noir program. - -Fill in input values for execution in the _Prover.toml_ file. For example: - -```toml -x = "1" -y = "2" -``` - -Prove the valid execution of your Noir program: - -```sh -nargo prove -``` - -A new folder _proofs_ would then be generated in your project directory, containing the proof file -`.proof`, where the project name is defined in Nargo.toml. - -The _Verifier.toml_ file would also be updated with the public values computed from program -execution (in this case the value of `y`): - -```toml -y = "0x0000000000000000000000000000000000000000000000000000000000000002" -``` - -> **Note:** Values in _Verifier.toml_ are computed as 32-byte hex values. - -## Verify Our Noir Program - -Once a proof is generated, we can verify correct execution of our Noir program by verifying the -proof file. - -Verify your proof by running: - -```sh -nargo verify -``` - -The verification will complete in silence if it is successful. If it fails, it will log the -corresponding error instead. - -Congratulations, you have now created and verified a proof for your very first Noir program! - -In the [next section](./project_breakdown.md), we will go into more detail on each step performed. diff --git a/noir/docs/processed-docs/getting_started/hello_noir/project_breakdown.md b/noir/docs/processed-docs/getting_started/hello_noir/project_breakdown.md deleted file mode 100644 index 6160a102c6c..00000000000 --- a/noir/docs/processed-docs/getting_started/hello_noir/project_breakdown.md +++ /dev/null @@ -1,199 +0,0 @@ ---- -title: Project Breakdown -description: - Learn about the anatomy of a Nargo project, including the purpose of the Prover and Verifier TOML - files, and how to prove and verify your program. -keywords: - [Nargo, Nargo project, Prover.toml, Verifier.toml, proof verification, private asset transfer] -sidebar_position: 2 ---- - -This section breaks down our hello world program from the previous section. We elaborate on the project -structure and what the `prove` and `verify` commands did. - -## Anatomy of a Nargo Project - -Upon creating a new project with `nargo new` and building the in/output files with `nargo check` -commands, you would get a minimal Nargo project of the following structure: - - - src - - Prover.toml - - Verifier.toml - - Nargo.toml - -The source directory _src_ holds the source code for your Noir program. By default only a _main.nr_ -file will be generated within it. - -### Prover.toml - -_Prover.toml_ is used for specifying the input values for executing and proving the program. You can specify `toml` files with different names by using the `--prover-name` or `-p` flags, see the [Prover](#provertoml) section below. Optionally you may specify expected output values for prove-time checking as well. - -### Verifier.toml - -_Verifier.toml_ contains public in/output values computed when executing the Noir program. - -### Nargo.toml - -_Nargo.toml_ contains the environmental options of your project. It contains a "package" section and a "dependencies" section. - -Example Nargo.toml: - -```toml -[package] -name = "noir_starter" -type = "bin" -authors = ["Alice"] -compiler_version = "0.9.0" -description = "Getting started with Noir" -entry = "circuit/main.nr" -license = "MIT" - -[dependencies] -ecrecover = {tag = "v0.9.0", git = "https://github.com/colinnielsen/ecrecover-noir.git"} -``` - -Nargo.toml for a [workspace](../../noir/modules_packages_crates/workspaces.md) will look a bit different. For example: - -```toml -[workspace] -members = ["crates/a", "crates/b"] -default-member = "crates/a" -``` - -#### Package section - -The package section defines a number of fields including: - -- `name` (**required**) - the name of the package -- `type` (**required**) - can be "bin", "lib", or "contract" to specify whether its a binary, library or Aztec contract -- `authors` (optional) - authors of the project -- `compiler_version` - specifies the version of the compiler to use. This is enforced by the compiler and follow's [Rust's versioning](https://doc.rust-lang.org/cargo/reference/manifest.html#the-version-field), so a `compiler_version = 0.18.0` will enforce Nargo version 0.18.0, `compiler_version = ^0.18.0` will enforce anything above 0.18.0 but below 0.19.0, etc. For more information, see how [Rust handles these operators](https://docs.rs/semver/latest/semver/enum.Op.html) -- `description` (optional) -- `entry` (optional) - a relative filepath to use as the entry point into your package (overrides the default of `src/lib.nr` or `src/main.nr`) -- `backend` (optional) -- `license` (optional) - -#### Dependencies section - -This is where you will specify any dependencies for your project. See the [Dependencies page](../../noir/modules_packages_crates/dependencies.md) for more info. - -`./proofs/` and `./contract/` directories will not be immediately visible until you create a proof or -verifier contract respectively. - -### main.nr - -The _main.nr_ file contains a `main` method, this method is the entry point into your Noir program. - -In our sample program, _main.nr_ looks like this: - -```rust -fn main(x : Field, y : Field) { - assert(x != y); -} -``` - -The parameters `x` and `y` can be seen as the API for the program and must be supplied by the -prover. Since neither `x` nor `y` is marked as public, the verifier does not supply any inputs, when -verifying the proof. - -The prover supplies the values for `x` and `y` in the _Prover.toml_ file. - -As for the program body, `assert` ensures that the condition to be satisfied (e.g. `x != y`) is -constrained by the proof of the execution of said program (i.e. if the condition was not met, the -verifier would reject the proof as an invalid proof). - -### Prover.toml - -The _Prover.toml_ file is a file which the prover uses to supply his witness values(both private and -public). - -In our hello world program the _Prover.toml_ file looks like this: - -```toml -x = "1" -y = "2" -``` - -When the command `nargo prove` is executed, two processes happen: - -1. Noir creates a proof that `x`, which holds the value of `1`, and `y`, which holds the value of `2`, - is not equal. This inequality constraint is due to the line `assert(x != y)`. - -2. Noir creates and stores the proof of this statement in the _proofs_ directory in a file called your-project.proof. So if your project is named "private_voting" (defined in the project Nargo.toml), the proof will be saved at `./proofs/private_voting.proof`. Opening this file will display the proof in hex format. - -#### Arrays of Structs - -The following code shows how to pass an array of structs to a Noir program to generate a proof. - -```rust -// main.nr -struct Foo { - bar: Field, - baz: Field, -} - -fn main(foos: [Foo; 3]) -> pub Field { - foos[2].bar + foos[2].baz -} -``` - -Prover.toml: - -```toml -[[foos]] # foos[0] -bar = 0 -baz = 0 - -[[foos]] # foos[1] -bar = 0 -baz = 0 - -[[foos]] # foos[2] -bar = 1 -baz = 2 -``` - -#### Custom toml files - -You can specify a `toml` file with a different name to use for proving by using the `--prover-name` or `-p` flags. - -This command looks for proof inputs in the default **Prover.toml** and generates the proof and saves it at `./proofs/.proof`: - -```bash -nargo prove -``` - -This command looks for proof inputs in the custom **OtherProver.toml** and generates proof and saves it at `./proofs/.proof`: - -```bash -nargo prove -p OtherProver -``` - -## Verifying a Proof - -When the command `nargo verify` is executed, two processes happen: - -1. Noir checks in the _proofs_ directory for a proof file with the project name (eg. test_project.proof) - -2. If that file is found, the proof's validity is checked - -> **Note:** The validity of the proof is linked to the current Noir program; if the program is -> changed and the verifier verifies the proof, it will fail because the proof is not valid for the -> _modified_ Noir program. - -In production, the prover and the verifier are usually two separate entities. A prover would -retrieve the necessary inputs, execute the Noir program, generate a proof and pass it to the -verifier. The verifier would then retrieve the public inputs, usually from external sources, and -verify the validity of the proof against it. - -Take a private asset transfer as an example: - -A person using a browser as the prover would retrieve private inputs locally (e.g. the user's private key) and -public inputs (e.g. the user's encrypted balance on-chain), compute the transfer, generate a proof -and submit it to the verifier smart contract. - -The verifier contract would then draw the user's encrypted balance directly from the blockchain and -verify the proof submitted against it. If the verification passes, additional functions in the -verifier contract could trigger (e.g. approve the asset transfer). - -Now that you understand the concepts, you'll probably want some editor feedback while you are writing more complex code. diff --git a/noir/docs/processed-docs/getting_started/installation/_category_.json b/noir/docs/processed-docs/getting_started/installation/_category_.json deleted file mode 100644 index 0c02fb5d4d7..00000000000 --- a/noir/docs/processed-docs/getting_started/installation/_category_.json +++ /dev/null @@ -1,6 +0,0 @@ -{ - "position": 0, - "label": "Install Nargo", - "collapsible": true, - "collapsed": true -} diff --git a/noir/docs/processed-docs/getting_started/installation/index.md b/noir/docs/processed-docs/getting_started/installation/index.md deleted file mode 100644 index 4ef86aa5914..00000000000 --- a/noir/docs/processed-docs/getting_started/installation/index.md +++ /dev/null @@ -1,48 +0,0 @@ ---- -title: Nargo Installation -description: - nargo is a command line tool for interacting with Noir programs. This page is a quick guide on how to install Nargo through the most common and easy method, noirup -keywords: [ - Nargo - Noir - Rust - Cargo - Noirup - Installation - Terminal Commands - Version Check - Nightlies - Specific Versions - Branches - Noirup Repository -] -pagination_next: getting_started/hello_noir/index ---- - -`nargo` is the one-stop-shop for almost everything related with Noir. The name comes from our love for Rust and its package manager `cargo`. - -With `nargo`, you can start new projects, compile, execute, prove, verify, test, generate solidity contracts, and do pretty much all that is available in Noir. - -Similarly to `rustup`, we also maintain an easy installation method that covers most machines: `noirup`. - -## Installing Noirup - -Open a terminal on your machine, and write: - -```bash -curl -L https://raw.githubusercontent.com/noir-lang/noirup/main/install | bash -``` - -Close the terminal, open another one, and run - -```bash -noirup -``` - -Done. That's it. You should have the latest version working. You can check with `nargo --version`. - -You can also install nightlies, specific versions -or branches. Check out the [noirup repository](https://github.com/noir-lang/noirup) for more -information. - -Now we're ready to start working on [our first Noir program!](../hello_noir/index.md) diff --git a/noir/docs/processed-docs/getting_started/installation/other_install_methods.md b/noir/docs/processed-docs/getting_started/installation/other_install_methods.md deleted file mode 100644 index a532f83750e..00000000000 --- a/noir/docs/processed-docs/getting_started/installation/other_install_methods.md +++ /dev/null @@ -1,190 +0,0 @@ ---- -title: Alternative Install Methods -description: - There are different ways to install Nargo, the one-stop shop and command-line tool for developing Noir programs. This guide explains other methods that don't rely on noirup, such as compiling from source, installing from binaries, and using WSL for windows -keywords: [ - Installation - Nargo - Noirup - Binaries - Compiling from Source - WSL for Windows - macOS - Linux - Nix - Direnv - Shell & editor experience - Building and testing - Uninstalling Nargo - Noir vs code extension -] -sidebar_position: 1 ---- - - -## Installation - -The most common method of installing Nargo is through [Noirup](./index.md) - -However, there are other methods for installing Nargo: - -- [Binaries](#binaries) -- [Compiling from Source](#compile-from-source) -- [WSL for Windows](#wsl-for-windows) - -### Binaries - -See [GitHub Releases](https://github.com/noir-lang/noir/releases) for the latest and previous -platform specific binaries. - -#### Step 1 - -Paste and run the following in the terminal to extract and install the binary: - -> **macOS / Linux:** If you are prompted with `Permission denied` when running commands, prepend -> `sudo` and re-run it. - -##### macOS (Apple Silicon) - -```bash -mkdir -p $HOME/.nargo/bin && \ -curl -o $HOME/.nargo/bin/nargo-aarch64-apple-darwin.tar.gz -L https://github.com/noir-lang/noir/releases/download/v0.6.0/nargo-aarch64-apple-darwin.tar.gz && \ -tar -xvf $HOME/.nargo/bin/nargo-aarch64-apple-darwin.tar.gz -C $HOME/.nargo/bin/ && \ -echo '\nexport PATH=$PATH:$HOME/.nargo/bin' >> ~/.zshrc && \ -source ~/.zshrc -``` - -##### macOS (Intel) - -```bash -mkdir -p $HOME/.nargo/bin && \ -curl -o $HOME/.nargo/bin/nargo-x86_64-apple-darwin.tar.gz -L https://github.com/noir-lang/noir/releases/download/v0.6.0/nargo-x86_64-apple-darwin.tar.gz && \ -tar -xvf $HOME/.nargo/bin/nargo-x86_64-apple-darwin.tar.gz -C $HOME/.nargo/bin/ && \ -echo '\nexport PATH=$PATH:$HOME/.nargo/bin' >> ~/.zshrc && \ -source ~/.zshrc -``` - -##### Linux (Bash) - -```bash -mkdir -p $HOME/.nargo/bin && \ -curl -o $HOME/.nargo/bin/nargo-x86_64-unknown-linux-gnu.tar.gz -L https://github.com/noir-lang/noir/releases/download/v0.6.0/nargo-x86_64-unknown-linux-gnu.tar.gz && \ -tar -xvf $HOME/.nargo/bin/nargo-x86_64-unknown-linux-gnu.tar.gz -C $HOME/.nargo/bin/ && \ -echo -e '\nexport PATH=$PATH:$HOME/.nargo/bin' >> ~/.bashrc && \ -source ~/.bashrc -``` - -#### Step 2 - -Check if the installation was successful by running `nargo --version`. You should get a version number. - -> **macOS:** If you are prompted with an OS alert, right-click and open the _nargo_ executable from -> Finder. Close the new terminal popped up and `nargo` should now be accessible. - -### Option 3: Compile from Source - -Due to the large number of native dependencies, Noir projects uses [Nix](https://nixos.org/) and [direnv](https://direnv.net/) to streamline the development experience. It helps mitigating issues commonly associated with dependency management, such as conflicts between required package versions for different projects (often referred to as "dependency hell"). - -Combined with direnv, which automatically sets or clears environment variables based on the directory, it further simplifies the development process by seamlessly integrating with the developer's shell, facilitating an efficient and reliable workflow for managing and deploying Noir projects with multiple dependencies. - -#### Setting up your environment - -For the best experience, please follow these instructions to setup your environment: - -1. Install Nix following [their guide](https://nixos.org/download.html) for your operating system. -2. Create the file `~/.config/nix/nix.conf` with the contents: - -```ini -experimental-features = nix-command -extra-experimental-features = flakes -``` - -3. Install direnv into your Nix profile by running: - -```sh -nix profile install nixpkgs#direnv -``` - -4. Add direnv to your shell following [their guide](https://direnv.net/docs/hook.html). - 1. For bash or zshell, add `eval "$(direnv hook bash)"` or `eval "$(direnv hook zsh)"` to your ~/.bashrc or ~/.zshrc file, respectively. -5. Restart your shell. - -#### Shell & editor experience - -Now that your environment is set up, you can get to work on the project. - -1. Clone the repository, such as: - -```sh -git clone git@github.com:noir-lang/noir -``` - -> Replacing `noir` with whichever repository you want to work on. - -2. Navigate to the directory: - -```sh -cd noir -``` - -> Replacing `noir` with whichever repository you cloned. - -3. You should see a **direnv error** because projects aren't allowed by default. Make sure you've reviewed and trust our `.envrc` file, then you need to run: - -```sh -direnv allow -``` - -4. Now, wait awhile for all the native dependencies to be built. This will take some time and direnv will warn you that it is taking a long time, but we just need to let it run. - -5. Once you are presented with your prompt again, you can start your editor within the project directory (we recommend [VSCode](https://code.visualstudio.com/)): - -```sh -code . -``` - -6. (Recommended) When launching VSCode for the first time, you should be prompted to install our recommended plugins. We highly recommend installing these for the best development experience. - -#### Building and testing - -Assuming you are using `direnv` to populate your environment, building and testing the project can be done -with the typical `cargo build`, `cargo test`, and `cargo clippy` commands. You'll notice that the `cargo` version matches the version we specify in `rust-toolchain.toml`, which is 1.71.1 at the time of this writing. - -If you want to build the entire project in an isolated sandbox, you can use Nix commands: - -1. `nix build .` (or `nix build . -L` for verbose output) to build the project in a Nix sandbox. -2. `nix flake check` (or `nix flake check -L` for verbose output) to run clippy and tests in a Nix sandbox. - -#### Without `direnv` - -If you have hesitations with using direnv, you can launch a subshell with `nix develop` and then launch your editor from within the subshell. However, if VSCode was already launched in the project directory, the environment won't be updated. - -Advanced: If you aren't using direnv nor launching your editor within the subshell, you can try to install Barretenberg and other global dependencies the package needs. This is an advanced workflow and likely won't receive support! - -### Option 4: WSL (for Windows) - -The default backend for Noir (Barretenberg) doesn't provide Windows binaries at this time. For that reason, Noir cannot be installed natively. However, it is available by using Windows Subsystem for Linux (WSL). - -Step 1: Follow the instructions [here](https://learn.microsoft.com/en-us/windows/wsl/install) to install and run WSL. - -step 2: Follow the [Noirup instructions](./index.md). - -## Uninstalling Nargo - -### Noirup - -If you installed Noir with `noirup`, you can uninstall Noir by removing the files in `~/.nargo`, `~/nargo` and `~/noir_cache`. - -```bash -rm -r ~/.nargo -rm -r ~/nargo -rm -r ~/noir_cache -``` - -### Nix - -If you installed Noir with Nix or from source, you can remove the binary located at `~/.nix-profile/bin/nargo`. - -```bash -rm ~/.nix-profile/bin/nargo -``` diff --git a/noir/docs/processed-docs/getting_started/tooling/_category_.json b/noir/docs/processed-docs/getting_started/tooling/_category_.json deleted file mode 100644 index 55804c03a71..00000000000 --- a/noir/docs/processed-docs/getting_started/tooling/_category_.json +++ /dev/null @@ -1,6 +0,0 @@ -{ - "position": 2, - "label": "Tooling", - "collapsible": true, - "collapsed": true -} diff --git a/noir/docs/processed-docs/getting_started/tooling/index.mdx b/noir/docs/processed-docs/getting_started/tooling/index.mdx deleted file mode 100644 index ac480f3c9f5..00000000000 --- a/noir/docs/processed-docs/getting_started/tooling/index.mdx +++ /dev/null @@ -1,38 +0,0 @@ ---- -title: Tooling -Description: This section provides information about the various tools and utilities available for Noir development. It covers the Noir playground, IDE tools, Codespaces, and community projects. -Keywords: [Noir, Development, Playground, IDE Tools, Language Service Provider, VS Code Extension, Codespaces, noir-starter, Community Projects, Awesome Noir Repository, Developer Tooling] ---- - -Noir is meant to be easy to develop with. For that reason, a number of utilities have been put together to ease the development process as much as feasible in the zero-knowledge world. - -## Playground - -The Noir playground is an easy way to test small ideas, share snippets, and integrate in other websites. You can access it at [play.noir-lang.org](https://play.noir-lang.org). - -## IDE tools - -When you install Nargo, you're also installing a Language Service Provider (LSP), which can be used by IDEs to provide syntax highlighting, codelens, warnings, and more. - -The easiest way to use these tools is by installing the [Noir VS Code extension](https://marketplace.visualstudio.com/items?itemName=noir-lang.vscode-noir). - -## Codespaces - -Some Noir repos have leveraged Codespaces in order to ease the development process. You can visit the [noir-starter](https://github.com/noir-lang/noir-starter) for an example. - - - -## GitHub Actions - -You can use `noirup` with GitHub Actions for CI/CD and automated testing. It is as simple as -installing `noirup` and running tests in your GitHub Action `yml` file. - -See the -[config file in the Noir repo](https://github.com/TomAFrench/noir-hashes/blob/master/.github/workflows/noir.yml) for an example usage. - -## Community projects - -As an open-source project, Noir has received many contributions over time. Some of them are related with developer tooling, and you can see some of them in [Awesome Noir repository](https://github.com/noir-lang/awesome-noir#dev-tools) diff --git a/noir/docs/processed-docs/getting_started/tooling/language_server.md b/noir/docs/processed-docs/getting_started/tooling/language_server.md deleted file mode 100644 index 81e0356ef8a..00000000000 --- a/noir/docs/processed-docs/getting_started/tooling/language_server.md +++ /dev/null @@ -1,43 +0,0 @@ ---- -title: Language Server -description: Learn about the Noir Language Server, how to install the components, and configuration that may be required. -keywords: [Nargo, Language Server, LSP, VSCode, Visual Studio Code] -sidebar_position: 0 ---- - -This section helps you install and configure the Noir Language Server. - -The Language Server Protocol (LSP) has two components, the [Server](#language-server) and the [Client](#language-client). Below we describe each in the context of Noir. - -## Language Server - -The Server component is provided by the Nargo command line tool that you installed at the beginning of this guide. -As long as Nargo is installed and you've used it to run other commands in this guide, it should be good to go! - -If you'd like to verify that the `nargo lsp` command is available, you can run `nargo --help` and look for `lsp` in the list of commands. If you see it, you're using a version of Noir with LSP support. - -## Language Client - -The Client component is usually an editor plugin that launches the Server. It communicates LSP messages between the editor and the Server. For example, when you save a file, the Client will alert the Server, so it can try to compile the project and report any errors. - -Currently, Noir provides a Language Client for Visual Studio Code via the [vscode-noir](https://github.com/noir-lang/vscode-noir) extension. You can install it via the [Visual Studio Marketplace](https://marketplace.visualstudio.com/items?itemName=noir-lang.vscode-noir). - -> **Note:** Noir's Language Server Protocol support currently assumes users' VSCode workspace root to be the same as users' Noir project root (i.e. where Nargo.toml lies). -> -> If LSP features seem to be missing / malfunctioning, make sure you are opening your Noir project directly (instead of as a sub-folder) in your VSCode instance. - -When your language server is running correctly and the VSCode plugin is installed, you should see handy codelens buttons for compilation, measuring circuit size, execution, and tests: - -![Compile and Execute](@site/static/img/codelens_compile_execute.png) -![Run test](@site/static/img/codelens_run_test.png) - -You should also see your tests in the `testing` panel: - -![Testing panel](@site/static/img/codelens_testing_panel.png) - -### Configuration - -- **Noir: Enable LSP** - If checked, the extension will launch the Language Server via `nargo lsp` and communicate with it. -- **Noir: Nargo Flags** - Additional flags may be specified if you require them to be added when the extension calls `nargo lsp`. -- **Noir: Nargo Path** - An absolute path to a Nargo binary with the `lsp` command. This may be useful if Nargo is not within the `PATH` of your editor. -- **Noir > Trace: Server** - Setting this to `"messages"` or `"verbose"` will log LSP messages between the Client and Server. Useful for debugging. diff --git a/noir/docs/processed-docs/getting_started/tooling/testing.md b/noir/docs/processed-docs/getting_started/tooling/testing.md deleted file mode 100644 index d3e0c522473..00000000000 --- a/noir/docs/processed-docs/getting_started/tooling/testing.md +++ /dev/null @@ -1,62 +0,0 @@ ---- -title: Testing in Noir -description: Learn how to use Nargo to test your Noir program in a quick and easy way -keywords: [Nargo, testing, Noir, compile, test] -sidebar_position: 1 ---- - -You can test your Noir programs using Noir circuits. - -Nargo will automatically compile and run any functions which have the decorator `#[test]` on them if -you run `nargo test`. - -For example if you have a program like: - -```rust -fn add(x: u64, y: u64) -> u64 { - x + y -} -#[test] -fn test_add() { - assert(add(2,2) == 4); - assert(add(0,1) == 1); - assert(add(1,0) == 1); -} -``` - -Running `nargo test` will test that the `test_add` function can be executed while satisfying all -the constraints which allows you to test that add returns the expected values. Test functions can't -have any arguments currently. - -### Test fail - -You can write tests that are expected to fail by using the decorator `#[test(should_fail)]`. For example: - -```rust -fn add(x: u64, y: u64) -> u64 { - x + y -} -#[test(should_fail)] -fn test_add() { - assert(add(2,2) == 5); -} -``` - -You can be more specific and make it fail with a specific reason by using `should_fail_with = "`: - -```rust -fn main(african_swallow_avg_speed : Field) { - assert(african_swallow_avg_speed == 65, "What is the airspeed velocity of an unladen swallow"); -} - -#[test] -fn test_king_arthur() { - main(65); -} - -#[test(should_fail_with = "What is the airspeed velocity of an unladen swallow")] -fn test_bridgekeeper() { - main(32); -} - -``` diff --git a/noir/docs/processed-docs/how_to/_category_.json b/noir/docs/processed-docs/how_to/_category_.json deleted file mode 100644 index 23b560f610b..00000000000 --- a/noir/docs/processed-docs/how_to/_category_.json +++ /dev/null @@ -1,5 +0,0 @@ -{ - "position": 1, - "collapsible": true, - "collapsed": true -} diff --git a/noir/docs/processed-docs/how_to/how-to-oracles.md b/noir/docs/processed-docs/how_to/how-to-oracles.md deleted file mode 100644 index 0d84d992320..00000000000 --- a/noir/docs/processed-docs/how_to/how-to-oracles.md +++ /dev/null @@ -1,280 +0,0 @@ ---- -title: How to use Oracles -description: Learn how to use oracles in your Noir program with examples in both Nargo and NoirJS. This guide also covers writing a JSON RPC server and providing custom foreign call handlers for NoirJS. -keywords: - - Noir Programming - - Oracles - - Nargo - - NoirJS - - JSON RPC Server - - Foreign Call Handlers -sidebar_position: 1 ---- - -This guide shows you how to use oracles in your Noir program. For the sake of clarity, it assumes that: - -- You have read the [explainer on Oracles](../explainers/explainer-oracle.md) and are comfortable with the concept. -- You have a Noir program to add oracles to. You can create one using the [vite-hardhat starter](https://github.com/noir-lang/noir-starter/tree/main/vite-hardhat) as a boilerplate. -- You understand the concept of a JSON-RPC server. Visit the [JSON-RPC website](https://www.jsonrpc.org/) if you need a refresher. -- You are comfortable with server-side JavaScript (e.g. Node.js, managing packages, etc.). - -For reference, you can find the snippets used in this tutorial on the [Aztec DevRel Repository](https://github.com/AztecProtocol/dev-rel/tree/main/code-snippets/how-to-oracles). - -## Rundown - -This guide has 3 major steps: - -1. How to modify our Noir program to make use of oracle calls as unconstrained functions -2. How to write a JSON RPC Server to resolve these oracle calls with Nargo -3. How to use them in Nargo and how to provide a custom resolver in NoirJS - -## Step 1 - Modify your Noir program - -An oracle is defined in a Noir program by defining two methods: - -- An unconstrained method - This tells the compiler that it is executing an [unconstrained functions](../noir/concepts//unconstrained.md). -- A decorated oracle method - This tells the compiler that this method is an RPC call. - -An example of an oracle that returns a `Field` would be: - -```rust -#[oracle(getSqrt)] -unconstrained fn sqrt(number: Field) -> Field { } - -unconstrained fn get_sqrt(number: Field) -> Field { - sqrt(number) -} -``` - -In this example, we're wrapping our oracle function in a unconstrained method, and decorating it with `oracle(getSqrt)`. We can then call the unconstrained function as we would call any other function: - -```rust -fn main(input: Field) { - let sqrt = get_sqrt(input); -} -``` - -In the next section, we will make this `getSqrt` (defined on the `sqrt` decorator) be a method of the RPC server Noir will use. - -:::danger - -As explained in the [Oracle Explainer](../explainers/explainer-oracle.md), this `main` function is unsafe unless you constrain its return value. For example: - -```rust -fn main(input: Field) { - let sqrt = get_sqrt(input); - assert(sqrt.pow_32(2) as u64 == input as u64); // <---- constrain the return of an oracle! -} -``` - -::: - -:::info - -Currently, oracles only work with single params or array params. For example: - -```rust -#[oracle(getSqrt)] -unconstrained fn sqrt([Field; 2]) -> [Field; 2] { } -``` - -::: - -## Step 2 - Write an RPC server - -Brillig will call *one* RPC server. Most likely you will have to write your own, and you can do it in whatever language you prefer. In this guide, we will do it in Javascript. - -Let's use the above example of an oracle that consumes an array with two `Field` and returns their square roots: - -```rust -#[oracle(getSqrt)] -unconstrained fn sqrt(input: [Field; 2]) -> [Field; 2] { } - -unconstrained fn get_sqrt(input: [Field; 2]) -> [Field; 2] { - sqrt(input) -} - -fn main(input: [Field; 2]) { - let sqrt = get_sqrt(input); - assert(sqrt[0].pow_32(2) as u64 == input[0] as u64); - assert(sqrt[1].pow_32(2) as u64 == input[1] as u64); -} -``` - -:::info - -Why square root? - -In general, computing square roots is computationally more expensive than multiplications, which takes a toll when speaking about ZK applications. In this case, instead of calculating the square root in Noir, we are using our oracle to offload that computation to be made in plain. In our circuit we can simply multiply the two values. - -::: - -Now, we should write the correspondent RPC server, starting with the [default JSON-RPC 2.0 boilerplate](https://www.npmjs.com/package/json-rpc-2.0#example): - -```js -import { JSONRPCServer } from "json-rpc-2.0"; -import express from "express"; -import bodyParser from "body-parser"; - -const app = express(); -app.use(bodyParser.json()); - -const server = new JSONRPCServer(); -app.post("/", (req, res) => { - const jsonRPCRequest = req.body; - server.receive(jsonRPCRequest).then((jsonRPCResponse) => { - if (jsonRPCResponse) { - res.json(jsonRPCResponse); - } else { - res.sendStatus(204); - } - }); -}); - -app.listen(5555); -``` - -Now, we will add our `getSqrt` method, as expected by the `#[oracle(getSqrt)]` decorator in our Noir code. It maps through the params array and returns their square roots: - -```js -server.addMethod("getSqrt", async (params) => { - const values = params[0].Array.map(({ inner }) => { - return { inner: `${Math.sqrt(parseInt(inner, 16))}` }; - }); - return { values: [{ Array: values }] }; -}); -``` - -:::tip - -Brillig expects an object with an array of values. Each value is an object declaring to be `Single` or `Array` and returning a `inner` property *as a string*. For example: - -```json -{ "values": [{ "Array": [{ "inner": "1" }, { "inner": "2"}]}]} -{ "values": [{ "Single": { "inner": "1" }}]} -{ "values": [{ "Single": { "inner": "1" }}, { "Array": [{ "inner": "1", { "inner": "2" }}]}]} -``` - -If you're using Typescript, the following types may be helpful in understanding the expected return value and making sure they're easy to follow: - -```js -interface Value { - inner: string, -} - -interface SingleForeignCallParam { - Single: Value, -} - -interface ArrayForeignCallParam { - Array: Value[], -} - -type ForeignCallParam = SingleForeignCallParam | ArrayForeignCallParam; - -interface ForeignCallResult { - values: ForeignCallParam[], -} -``` - -::: - -## Step 3 - Usage with Nargo - -Using the [`nargo` CLI tool](../getting_started/installation/index.md), you can use oracles in the `nargo test`, `nargo execute` and `nargo prove` commands by passing a value to `--oracle-resolver`. For example: - -```bash -nargo test --oracle-resolver http://localhost:5555 -``` - -This tells `nargo` to use your RPC Server URL whenever it finds an oracle decorator. - -## Step 4 - Usage with NoirJS - -In a JS environment, an RPC server is not strictly necessary, as you may want to resolve your oracles without needing any JSON call at all. NoirJS simply expects that you pass a callback function when you generate proofs, and that callback function can be anything. - -For example, if your Noir program expects the host machine to provide CPU pseudo-randomness, you could simply pass it as the `foreignCallHandler`. You don't strictly need to create an RPC server to serve pseudo-randomness, as you may as well get it directly in your app: - -```js -const foreignCallHandler = (name, inputs) => crypto.randomBytes(16) // etc - -await noir.generateFinalProof(inputs, foreignCallHandler) -``` - -As one can see, in NoirJS, the [`foreignCallHandler`](../reference/NoirJS/noir_js/type-aliases/ForeignCallHandler.md) function simply means "a callback function that returns a value of type [`ForeignCallOutput`](../reference/NoirJS/noir_js/type-aliases/ForeignCallOutput.md). It doesn't have to be an RPC call like in the case for Nargo. - -:::tip - -Does this mean you don't have to write an RPC server like in [Step #2](#step-2---write-an-rpc-server)? - -You don't technically have to, but then how would you run `nargo test` or `nargo prove`? To use both `Nargo` and `NoirJS` in your development flow, you will have to write a JSON RPC server. - -::: - -In this case, let's make `foreignCallHandler` call the JSON RPC Server we created in [Step #2](#step-2---write-an-rpc-server), by making it a JSON RPC Client. - -For example, using the same `getSqrt` program in [Step #1](#step-1---modify-your-noir-program) (comments in the code): - -```js -import { JSONRPCClient } from "json-rpc-2.0"; - -// declaring the JSONRPCClient -const client = new JSONRPCClient((jsonRPCRequest) => { -// hitting the same JSON RPC Server we coded above - return fetch("http://localhost:5555", { - method: "POST", - headers: { - "content-type": "application/json", - }, - body: JSON.stringify(jsonRPCRequest), - }).then((response) => { - if (response.status === 200) { - return response - .json() - .then((jsonRPCResponse) => client.receive(jsonRPCResponse)); - } else if (jsonRPCRequest.id !== undefined) { - return Promise.reject(new Error(response.statusText)); - } - }); -}); - -// declaring a function that takes the name of the foreign call (getSqrt) and the inputs -const foreignCallHandler = async (name, input) => { - // notice that the "inputs" parameter contains *all* the inputs - // in this case we to make the RPC request with the first parameter "numbers", which would be input[0] - const oracleReturn = await client.request(name, [ - { Array: input[0].map((i) => ({ inner: i.toString("hex") })) }, - ]); - return [oracleReturn.values[0].Array.map((x) => x.inner)]; -}; - -// the rest of your NoirJS code -const input = { input: [4, 16] }; -const { witness } = await noir.execute(numbers, foreignCallHandler); -``` - -:::tip - -If you're in a NoirJS environment running your RPC server together with a frontend app, you'll probably hit a familiar problem in full-stack development: requests being blocked by [CORS](https://developer.mozilla.org/en-US/docs/Web/HTTP/CORS) policy. For development only, you can simply install and use the [`cors` npm package](https://www.npmjs.com/package/cors) to get around the problem: - -```bash -yarn add cors -``` - -and use it as a middleware: - -```js -import cors from "cors"; - -const app = express(); -app.use(cors()) -``` - -::: - -## Conclusion - -Hopefully by the end of this guide, you should be able to: - -- Write your own logic around Oracles and how to write a JSON RPC server to make them work with your Nargo commands. -- Provide custom foreign call handlers for NoirJS. diff --git a/noir/docs/processed-docs/how_to/how-to-recursion.md b/noir/docs/processed-docs/how_to/how-to-recursion.md deleted file mode 100644 index f34647a99d5..00000000000 --- a/noir/docs/processed-docs/how_to/how-to-recursion.md +++ /dev/null @@ -1,179 +0,0 @@ ---- -title: How to use recursion on NoirJS -description: Learn how to implement recursion with NoirJS, a powerful tool for creating smart contracts on the EVM blockchain. This guide assumes familiarity with NoirJS, solidity verifiers, and the Barretenberg proving backend. Discover how to generate both final and intermediate proofs using `noir_js` and `backend_barretenberg`. -keywords: - [ - "NoirJS", - "EVM blockchain", - "smart contracts", - "recursion", - "solidity verifiers", - "Barretenberg backend", - "noir_js", - "backend_barretenberg", - "intermediate proofs", - "final proofs", - "nargo compile", - "json import", - "recursive circuit", - "recursive app" - ] -sidebar_position: 1 ---- - -This guide shows you how to use recursive proofs in your NoirJS app. For the sake of clarity, it is assumed that: - -- You already have a NoirJS app. If you don't, please visit the [NoirJS tutorial](../tutorials/noirjs_app.md) and the [reference](../reference/NoirJS/noir_js/index.md). -- You are familiar with what are recursive proofs and you have read the [recursion explainer](../explainers/explainer-recursion.md) -- You already built a recursive circuit following [the reference](../noir/standard_library/recursion.md), and understand how it works. - -It is also assumed that you're not using `noir_wasm` for compilation, and instead you've used [`nargo compile`](../reference/nargo_commands.md) to generate the `json` you're now importing into your project. However, the guide should work just the same if you're using `noir_wasm`. - -:::info - -As you've read in the [explainer](../explainers/explainer-recursion.md), a recursive proof is an intermediate proof. This means that it doesn't necessarily generate the final step that makes it verifiable in a smart contract. However, it is easy to verify within another circuit. - -While "standard" usage of NoirJS packages abstracts final proofs, it currently lacks the necessary interface to abstract away intermediate proofs. This means that these proofs need to be created by using the backend directly. - -In short: - -- `noir_js` generates *only* final proofs -- `backend_barretenberg` generates both types of proofs - -::: - -In a standard recursive app, you're also dealing with at least two circuits. For the purpose of this guide, we will assume these two: - -- `main`: a circuit of type `assert(x != y)` -- `recursive`: a circuit that verifies `main` - -For a full example on how recursive proofs work, please refer to the [noir-examples](https://github.com/noir-lang/noir-examples) repository. We will *not* be using it as a reference for this guide. - -## Step 1: Setup - -In a common NoirJS app, you need to instantiate a backend with something like `const backend = new Backend(circuit)`. Then you feed it to the `noir_js` interface. - -For recursion, this doesn't happen, and the only need for `noir_js` is only to `execute` a circuit and get its witness and return value. Everything else is not interfaced, so it needs to happen on the `backend` object. - -It is also recommended that you instantiate the backend with as many threads as possible, to allow for maximum concurrency: - -```js -const backend = new Backend(circuit, { threads: 8 }) -``` - -:::tip -You can use the [`os.cpus()`](https://nodejs.org/api/os.html#oscpus) object in `nodejs` or [`navigator.hardwareConcurrency`](https://developer.mozilla.org/en-US/docs/Web/API/Navigator/hardwareConcurrency) on the browser to make the most out of those glorious cpu cores -::: - -## Step 2: Generating the witness and the proof for `main` - -After instantiating the backend, you should also instantiate `noir_js`. We will use it to execute the circuit and get the witness. - -```js -const noir = new Noir(circuit, backend) -const { witness } = noir.execute(input) -``` - -With this witness, you are now able to generate the intermediate proof for the main circuit: - -```js -const { proof, publicInputs } = await backend.generateIntermediateProof(witness) -``` - -:::warning - -Always keep in mind what is actually happening on your development process, otherwise you'll quickly become confused about what circuit we are actually running and why! - -In this case, you can imagine that Alice (running the `main` circuit) is proving something to Bob (running the `recursive` circuit), and Bob is verifying her proof within his proof. - -With this in mind, it becomes clear that our intermediate proof is the one *meant to be verified within another circuit*, so it must be Alice's. Actually, the only final proof in this theoretical scenario would be the last one, sent on-chain. - -::: - -## Step 3 - Verification and proof artifacts - -Optionally, you are able to verify the intermediate proof: - -```js -const verified = await backend.verifyIntermediateProof({ proof, publicInputs }) -``` - -This can be useful to make sure our intermediate proof was correctly generated. But the real goal is to do it within another circuit. For that, we need to generate the intermediate artifacts: - -```js -const { proofAsFields, vkAsFields, vkHash } = await backend.generateIntermediateProofArtifacts( { publicInputs, proof }, publicInputsCount) -``` - -This call takes the public inputs and the proof, but also the public inputs count. While this is easily retrievable by simply counting the `publicInputs` length, the backend interface doesn't currently abstract it away. - -:::info - -The `proofAsFields` has a constant size `[Field; 93]` and verification keys in Barretenberg are always `[Field; 114]`. - -::: - -:::warning - -One common mistake is to forget *who* makes this call. - -In a situation where Alice is generating the `main` proof, if she generates the proof artifacts and sends them to Bob, which gladly takes them as true, this would mean Alice could prove anything! - -Instead, Bob needs to make sure *he* extracts the proof artifacts, using his own instance of the `main` circuit backend. This way, Alice has to provide a valid proof for the correct `main` circuit. - -::: - -## Step 4 - Recursive proof generation - -With the artifacts, generating a recursive proof is no different from a normal proof. You simply use the `backend` (with the recursive circuit) to generate it: - -```js -const recursiveInputs = { - verification_key: vkAsFields, // array of length 114 - proof: proofAsFields, // array of length 93 + size of public inputs - publicInputs: [mainInput.y], // using the example above, where `y` is the only public input - key_hash: vkHash, -} - -const { witness, returnValue } = noir.execute(recursiveInputs) // we're executing the recursive circuit now! -const { proof, publicInputs } = backend.generateFinalProof(witness) -const verified = backend.verifyFinalProof({ proof, publicInputs }) -``` - -You can obviously chain this proof into another proof. In fact, if you're using recursive proofs, you're probably interested of using them this way! - -:::tip - -Managing circuits and "who does what" can be confusing. To make sure your naming is consistent, you can keep them in an object. For example: - -```js -const circuits = { - main: mainJSON, - recursive: recursiveJSON -} -const backends = { - main: new BarretenbergBackend(circuits.main), - recursive: new BarretenbergBackend(circuits.recursive) -} -const noir_programs = { - main: new Noir(circuits.main, backends.main), - recursive: new Noir(circuits.recursive, backends.recursive) -} -``` - -This allows you to neatly call exactly the method you want without conflicting names: - -```js -// Alice runs this 👇 -const { witness: mainWitness } = await noir_programs.main.execute(input) -const proof = await backends.main.generateIntermediateProof(mainWitness) - -// Bob runs this 👇 -const verified = await backends.main.verifyIntermediateProof(proof) -const { proofAsFields, vkAsFields, vkHash } = await backends.main.generateIntermediateProofArtifacts( - proof, - numPublicInputs, -); -const recursiveProof = await noir_programs.recursive.generateFinalProof(recursiveInputs) -``` - -::: diff --git a/noir/docs/processed-docs/how_to/how-to-solidity-verifier.md b/noir/docs/processed-docs/how_to/how-to-solidity-verifier.md deleted file mode 100644 index e3c7c1065da..00000000000 --- a/noir/docs/processed-docs/how_to/how-to-solidity-verifier.md +++ /dev/null @@ -1,231 +0,0 @@ ---- -title: Generate a Solidity Verifier -description: - Learn how to run the verifier as a smart contract on the blockchain. Compile a Solidity verifier - contract for your Noir program and deploy it on any EVM blockchain acting as a verifier smart - contract. Read more to find out -keywords: - [ - solidity verifier, - smart contract, - blockchain, - compiler, - plonk_vk.sol, - EVM blockchain, - verifying Noir programs, - proving backend, - Barretenberg, - ] -sidebar_position: 0 -pagination_next: tutorials/noirjs_app ---- - -Noir has the ability to generate a verifier contract in Solidity, which can be deployed in many EVM-compatible blockchains such as Ethereum. - -This allows for a powerful feature set, as one can make use of the conciseness and the privacy provided by Noir in an immutable ledger. Applications can range from simple P2P guessing games, to complex private DeFi interactions. - -This guide shows you how to generate a Solidity Verifier and deploy it on the [Remix IDE](https://remix.ethereum.org/). It is assumed that: - -- You are comfortable with the Solidity programming language and understand how contracts are deployed on the Ethereum network -- You have Noir installed and you have a Noir program. If you don't, [get started](../getting_started/installation/index.md) with Nargo and the example Hello Noir circuit -- You are comfortable navigating RemixIDE. If you aren't or you need a refresher, you can find some video tutorials [here](https://www.youtube.com/channel/UCjTUPyFEr2xDGN6Cg8nKDaA) that could help you. - -## Rundown - -Generating a Solidity Verifier contract is actually a one-command process. However, compiling it and deploying it can have some caveats. Here's the rundown of this guide: - -1. How to generate a solidity smart contract -2. How to compile the smart contract in the RemixIDE -3. How to deploy it to a testnet - -## Step 1 - Generate a contract - -This is by far the most straight-forward step. Just run: - -```sh -nargo codegen-verifier -``` - -A new `contract` folder would then be generated in your project directory, containing the Solidity -file `plonk_vk.sol`. It can be deployed to any EVM blockchain acting as a verifier smart contract. - -:::info - -It is possible to generate verifier contracts of Noir programs for other smart contract platforms as long as the proving backend supplies an implementation. - -Barretenberg, the default proving backend for Nargo, supports generation of verifier contracts, for the time being these are only in Solidity. -::: - -## Step 2 - Compiling - -We will mostly skip the details of RemixIDE, as the UI can change from version to version. For now, we can just open -Remix and create a blank workspace. - -![Create Workspace](@site/static/img/how-tos/solidity_verifier_1.png) - -We will create a new file to contain the contract Nargo generated, and copy-paste its content. - -:::warning - -You'll likely see a warning advising you to not trust pasted code. While it is an important warning, it is irrelevant in the context of this guide and can be ignored. We will not be deploying anywhere near a mainnet. - -::: - -To compile our the verifier, we can navigate to the compilation tab: - -![Compilation Tab](@site/static/img/how-tos/solidity_verifier_2.png) - -Remix should automatically match a suitable compiler version. However, hitting the "Compile" button will most likely generate a "Stack too deep" error: - -![Stack too deep](@site/static/img/how-tos/solidity_verifier_3.png) - -This is due to the verify function needing to put many variables on the stack, but enabling the optimizer resolves the issue. To do this, let's open the "Advanced Configurations" tab and enable optimization. The default 200 runs will suffice. - -:::info - -This time we will see a warning about an unused function parameter. This is expected, as the `verify` function doesn't use the `_proof` parameter inside a solidity block, it is loaded from calldata and used in assembly. - -::: - -![Compilation success](@site/static/img/how-tos/solidity_verifier_4.png) - -## Step 3 - Deploying - -At this point we should have a compiled contract read to deploy. If we navigate to the deploy section in Remix, we will see many different environments we can deploy to. The steps to deploy on each environment would be out-of-scope for this guide, so we will just use the default Remix VM. - -Looking closely, we will notice that our "Solidity Verifier" is actually three contracts working together: - -- An `UltraVerificationKey` library which simply stores the verification key for our circuit. -- An abstract contract `BaseUltraVerifier` containing most of the verifying logic. -- A main `UltraVerifier` contract that inherits from the Base and uses the Key contract. - -Remix will take care of the dependencies for us so we can simply deploy the UltraVerifier contract by selecting it and hitting "deploy": - -![Deploying UltraVerifier](@site/static/img/how-tos/solidity_verifier_5.png) - -A contract will show up in the "Deployed Contracts" section, where we can retrieve the Verification Key Hash. This is particularly useful for double-checking the deployer contract is the correct one. - -:::note - -Why "UltraVerifier"? - -To be precise, the Noir compiler (`nargo`) doesn't generate the verifier contract directly. It compiles the Noir code into an intermediate language (ACIR), which is then executed by the backend. So it is the backend that returns the verifier smart contract, not Noir. - -In this case, the Barretenberg Backend uses the UltraPlonk proving system, hence the "UltraVerifier" name. - -::: - -## Step 4 - Verifying - -To verify a proof using the Solidity verifier contract, we call the `verify` function in this extended contract: - -```solidity -function verify(bytes calldata _proof, bytes32[] calldata _publicInputs) external view returns (bool) -``` - -When using the default example in the [Hello Noir](../getting_started/hello_noir/index.md) guide, the easiest way to confirm that the verifier contract is doing its job is by calling the `verify` function via remix with the required parameters. For `_proof`, run `nargo prove` and use the string in `proof/.proof` (adding the hex `0x` prefix). We can also copy the public input from `Verifier.toml`, as it will be properly formatted as 32-byte strings: - -``` -0x...... , [0x0000.....02] -``` - -A programmatic example of how the `verify` function is called can be seen in the example zk voting application [here](https://github.com/noir-lang/noir-examples/blob/33e598c257e2402ea3a6b68dd4c5ad492bce1b0a/foundry-voting/src/zkVote.sol#L35): - -```solidity -function castVote(bytes calldata proof, uint proposalId, uint vote, bytes32 nullifierHash) public returns (bool) { - // ... - bytes32[] memory publicInputs = new bytes32[](4); - publicInputs[0] = merkleRoot; - publicInputs[1] = bytes32(proposalId); - publicInputs[2] = bytes32(vote); - publicInputs[3] = nullifierHash; - require(verifier.verify(proof, publicInputs), "Invalid proof"); -``` - -:::info[Return Values] - -A circuit doesn't have the concept of a return value. Return values are just syntactic sugar in -Noir. - -Under the hood, the return value is passed as an input to the circuit and is checked at the end of -the circuit program. - -For example, if you have Noir program like this: - -```rust -fn main( - // Public inputs - pubkey_x: pub Field, - pubkey_y: pub Field, - // Private inputs - priv_key: Field, -) -> pub Field -``` - -the `verify` function will expect the public inputs array (second function parameter) to be of length 3, the two inputs and the return value. Like before, these values are populated in Verifier.toml after running `nargo prove`. - -Passing only two inputs will result in an error such as `PUBLIC_INPUT_COUNT_INVALID(3, 2)`. - -In this case, the inputs parameter to `verify` would be an array ordered as `[pubkey_x, pubkey_y, return]`. - -::: - -:::tip[Structs] - -You can pass structs to the verifier contract. They will be flattened so that the array of inputs is 1-dimensional array. - -For example, consider the following program: - -```rust -struct Type1 { - val1: Field, - val2: Field, -} - -struct Nested { - t1: Type1, - is_true: bool, -} - -fn main(x: pub Field, nested: pub Nested, y: pub Field) { - //... -} -``` - -The order of these inputs would be flattened to: `[x, nested.t1.val1, nested.t1.val2, nested.is_true, y]` - -::: - -The other function you can call is our entrypoint `verify` function, as defined above. - -:::tip - -It's worth noticing that the `verify` function is actually a `view` function. A `view` function does not alter the blockchain state, so it doesn't need to be distributed (i.e. it will run only on the executing node), and therefore doesn't cost any gas. - -This can be particularly useful in some situations. If Alice generated a proof and wants Bob to verify its correctness, Bob doesn't need to run Nargo, NoirJS, or any Noir specific infrastructure. He can simply make a call to the blockchain with the proof and verify it is correct without paying any gas. - -It would be incorrect to say that a Noir proof verification costs any gas at all. However, most of the time the result of `verify` is used to modify state (for example, to update a balance, a game state, etc). In that case the whole network needs to execute it, which does incur gas costs (calldata and execution, but not storage). - -::: - -## A Note on EVM chains - -ZK-SNARK verification depends on some precompiled cryptographic primitives such as Elliptic Curve Pairings (if you like complex math, you can read about EC Pairings [here](https://medium.com/@VitalikButerin/exploring-elliptic-curve-pairings-c73c1864e627)). Not all EVM chains support EC Pairings, notably some of the ZK-EVMs. This means that you won't be able to use the verifier contract in all of them. - -For example, chains like `zkSync ERA` and `Polygon zkEVM` do not currently support these precompiles, so proof verification via Solidity verifier contracts won't work. Here's a quick list of EVM chains that have been tested and are known to work: - -- Optimism -- Arbitrum -- Polygon PoS -- Scroll -- Celo - -If you test any other chains, please open a PR on this page to update the list. See [this doc](https://github.com/noir-lang/noir-starter/tree/main/with-foundry#testing-on-chain) for more info about testing verifier contracts on different EVM chains. - -## What's next - -Now that you know how to call a Noir Solidity Verifier on a smart contract using Remix, you should be comfortable with using it with some programmatic frameworks, such as [hardhat](https://github.com/noir-lang/noir-starter/tree/main/vite-hardhat) and [foundry](https://github.com/noir-lang/noir-starter/tree/main/with-foundry). - -You can find other tools, examples, boilerplates and libraries in the [awesome-noir](https://github.com/noir-lang/awesome-noir) repository. - -You should also be ready to write and deploy your first NoirJS app and start generating proofs on websites, phones, and NodeJS environments! Head on to the [NoirJS tutorial](../tutorials/noirjs_app.md) to learn how to do that. diff --git a/noir/docs/processed-docs/how_to/merkle-proof.mdx b/noir/docs/processed-docs/how_to/merkle-proof.mdx deleted file mode 100644 index 34074659ac1..00000000000 --- a/noir/docs/processed-docs/how_to/merkle-proof.mdx +++ /dev/null @@ -1,48 +0,0 @@ ---- -title: Prove Merkle Tree Membership -description: - Learn how to use merkle membership proof in Noir to prove that a given leaf is a member of a - merkle tree with a specified root, at a given index. -keywords: - [merkle proof, merkle membership proof, Noir, rust, hash function, Pedersen, sha256, merkle tree] ---- - -Let's walk through an example of a merkle membership proof in Noir that proves that a given leaf is -in a merkle tree. - -```rust -use dep::std; - -fn main(message : [Field; 62], index : Field, hashpath : [Field; 40], root : Field) { - let leaf = std::hash::hash_to_field(message); - let merkle_root = std::merkle::compute_merkle_root(leaf, index, hashpath); - assert(merkle_root == root); -} - -``` - -The message is hashed using `hash_to_field`. The specific hash function that is being used is chosen -by the backend. The only requirement is that this hash function can heuristically be used as a -random oracle. If only collision resistance is needed, then one can call `std::hash::pedersen_hash` -instead. - -```rust -let leaf = std::hash::hash_to_field(message); -``` - -The leaf is then passed to a compute_merkle_root function with the root, index and hashpath. The returned root can then be asserted to be the same as the provided root. - -```rust -let merkle_root = std::merkle::compute_merkle_root(leaf, index, hashpath); -assert (merkle_root == root); -``` - -> **Note:** It is possible to re-implement the merkle tree implementation without standard library. -> However, for most usecases, it is enough. In general, the standard library will always opt to be -> as conservative as possible, while striking a balance with efficiency. - -An example, the merkle membership proof, only requires a hash function that has collision -resistance, hence a hash function like Pedersen is allowed, which in most cases is more efficient -than the even more conservative sha256. - -[View an example on the starter repo](https://github.com/noir-lang/noir-examples/blob/3ea09545cabfa464124ec2f3ea8e60c608abe6df/stealthdrop/circuits/src/main.nr#L20) diff --git a/noir/docs/processed-docs/how_to/using-devcontainers.mdx b/noir/docs/processed-docs/how_to/using-devcontainers.mdx deleted file mode 100644 index 727ec6ca667..00000000000 --- a/noir/docs/processed-docs/how_to/using-devcontainers.mdx +++ /dev/null @@ -1,110 +0,0 @@ ---- -title: Developer Containers and Codespaces -description: "Learn how to set up a devcontainer in your GitHub repository for a seamless coding experience with Codespaces. Follow our easy 8-step guide to create your own Noir environment without installing Nargo locally." -keywords: ["Devcontainer", "Codespaces", "GitHub", "Noir Environment", "Docker Image", "Development Environment", "Remote Coding", "GitHub Codespaces", "Noir Programming", "Nargo", "VSCode Extensions", "Noirup"] -sidebar_position: 1 ---- - -Adding a developer container configuration file to your Noir project is one of the easiest way to unlock coding in browser. - -## What's a devcontainer after all? - -A [Developer Container](https://containers.dev/) (devcontainer for short) is a Docker image that comes preloaded with tools, extensions, and other tools you need to quickly get started or continue a project, without having to install Nargo locally. Think of it as a development environment in a box. - -There are many advantages to this: - -- It's platform and architecture agnostic -- You don't need to have an IDE installed, or Nargo, or use a terminal at all -- It's safer for using on a public machine or public network - -One of the best ways of using devcontainers is... not using your machine at all, for maximum control, performance, and ease of use. -Enter Codespaces. - -## Codespaces - -If a devcontainer is just a Docker image, then what stops you from provisioning a `p3dn.24xlarge` AWS EC2 instance with 92 vCPUs and 768 GiB RAM and using it to prove your 10-gate SNARK proof? - -Nothing! Except perhaps the 30-40$ per hour it will cost you. - -The problem is that provisioning takes time, and I bet you don't want to see the AWS console every time you want to code something real quick. - -Fortunately, there's an easy and free way to get a decent remote machine ready and loaded in less than 2 minutes: Codespaces. [Codespaces is a Github feature](https://github.com/features/codespaces) that allows you to code in a remote machine by using devcontainers, and it's pretty cool: - -- You can start coding Noir in less than a minute -- It uses the resources of a remote machine, so you can code on your grandma's phone if needed be -- It makes it easy to share work with your frens -- It's fully reusable, you can stop and restart whenever you need to - -:::info - -Don't take out your wallet just yet. Free GitHub accounts get about [15-60 hours of coding](https://github.com/features/codespaces) for free per month, depending on the size of your provisioned machine. - -::: - -## Tell me it's _actually_ easy - -It is! - -Github comes with a default codespace and you can use it to code your own devcontainer. That's exactly what we will be doing in this guide. - - - -8 simple steps: - -#### 1. Create a new repository on GitHub. - -#### 2. Click "Start coding with Codespaces". This will use the default image. - -#### 3. Create a folder called `.devcontainer` in the root of your repository. - -#### 4. Create a Dockerfile in that folder, and paste the following code: - -```docker -FROM --platform=linux/amd64 node:lts-bookworm-slim -SHELL ["/bin/bash", "-c"] -RUN apt update && apt install -y curl bash git tar gzip libc++-dev -RUN curl -L https://raw.githubusercontent.com/noir-lang/noirup/main/install | bash -ENV PATH="/root/.nargo/bin:$PATH" -RUN noirup -ENTRYPOINT ["nargo"] -``` -#### 5. Create a file called `devcontainer.json` in the same folder, and paste the following code: - -```json -{ - "name": "Noir on Codespaces", - "build": { - "context": ".", - "dockerfile": "Dockerfile" - }, - "customizations": { - "vscode": { - "extensions": ["noir-lang.vscode-noir"] - } - } -} -``` -#### 6. Commit and push your changes - -This will pull the new image and build it, so it could take a minute or so - -#### 8. Done! -Just wait for the build to finish, and there's your easy Noir environment. - - -Refer to [noir-starter](https://github.com/noir-lang/noir-starter/) as an example of how devcontainers can be used together with codespaces. - - - -## How do I use it? - -Using the codespace is obviously much easier than setting it up. -Just navigate to your repository and click "Code" -> "Open with Codespaces". It should take a few seconds to load, and you're ready to go. - -:::info - -If you really like the experience, you can add a badge to your readme, links to existing codespaces, and more. -Check out the [official docs](https://docs.github.com/en/codespaces/setting-up-your-project-for-codespaces/setting-up-your-repository/facilitating-quick-creation-and-resumption-of-codespaces) for more info. diff --git a/noir/docs/processed-docs/index.mdx b/noir/docs/processed-docs/index.mdx deleted file mode 100644 index 75086ddcdde..00000000000 --- a/noir/docs/processed-docs/index.mdx +++ /dev/null @@ -1,67 +0,0 @@ ---- -title: Noir Lang -hide_title: true -description: - Learn about the public alpha release of Noir, a domain specific language heavily influenced by Rust that compiles to - an intermediate language which can be compiled to an arithmetic circuit or a rank-1 constraint system. -keywords: - [Noir, - Domain Specific Language, - Rust, - Intermediate Language, - Arithmetic Circuit, - Rank-1 Constraint System, - Ethereum Developers, - Protocol Developers, - Blockchain Developers, - Proving System, - Smart Contract Language] -sidebar_position: 0 ---- - -import Tabs from '@theme/Tabs'; -import TabItem from '@theme/TabItem'; - -Noir Logo - -Noir is a Domain-Specific Language for SNARK proving systems developed by [Aztec Labs](https://aztec.network/). It allows you to generate complex Zero-Knowledge Programs (ZKP) by using simple and flexible syntax, requiring no previous knowledge on the underlying mathematics or cryptography. - -ZK programs are programs that can generate short proofs of a certain statement without revealing some details about it. You can read more about ZKPs [here](https://dev.to/spalladino/a-beginners-intro-to-coding-zero-knowledge-proofs-c56). - -## What's new about Noir? - -Noir works differently from most ZK languages by taking a two-pronged path. First, it compiles the program to an adaptable intermediate language known as ACIR. From there, depending on a given project's needs, ACIR can be further compiled into an arithmetic circuit for integration with the proving backend. - -:::info - -Noir is backend agnostic, which means it makes no assumptions on which proving backend powers the ZK proof. Being the language that powers [Aztec Contracts](https://docs.aztec.network/developers/contracts/main), it defaults to Aztec's Barretenberg proving backend. - -However, the ACIR output can be transformed to be compatible with other PLONK-based backends, or into a [rank-1 constraint system](https://www.rareskills.io/post/rank-1-constraint-system) suitable for backends such as Arkwork's Marlin. - -::: - -## Who is Noir for? - -Noir can be used both in complex cloud-based backends and in user's smartphones, requiring no knowledge on the underlying math or cryptography. From authorization systems that keep a password in the user's device, to complex on-chain verification of recursive proofs, Noir is designed to abstract away complexity without any significant overhead. Here are some examples of situations where Noir can be used: - - - - Noir Logo - - Aztec Contracts leverage Noir to allow for the storage and execution of private information. Writing an Aztec Contract is as easy as writing Noir, and Aztec developers can easily interact with the network storage and execution through the [Aztec.nr](https://docs.aztec.network/developers/contracts/main) library. - - - Soliditry Verifier Example - Noir can auto-generate Solidity verifier contracts that verify Noir proofs. This allows for non-interactive verification of proofs containing private information in an immutable system. This feature powers a multitude of use-case scenarios, from P2P chess tournaments, to [Aztec Layer-2 Blockchain](https://docs.aztec.network/) - - - Aztec Labs developed NoirJS, an easy interface to generate and verify Noir proofs in a Javascript environment. This allows for Noir to be used in webpages, mobile apps, games, and any other environment supporting JS execution in a standalone manner. - - - - -## Libraries - -Noir is meant to be easy to extend by simply importing Noir libraries just like in Rust. -The [awesome-noir repo](https://github.com/noir-lang/awesome-noir#libraries) is a collection of libraries developed by the Noir community. -Writing a new library is easy and makes code be composable and easy to reuse. See the section on [dependencies](noir/modules_packages_crates/dependencies.md) for more information. diff --git a/noir/docs/processed-docs/migration_notes.md b/noir/docs/processed-docs/migration_notes.md deleted file mode 100644 index 9f27230a1a0..00000000000 --- a/noir/docs/processed-docs/migration_notes.md +++ /dev/null @@ -1,91 +0,0 @@ ---- -title: Migration notes -description: Read about migration notes from previous versions, which could solve problems while updating -keywords: [Noir, notes, migration, updating, upgrading] ---- - -Noir is in full-speed development. Things break fast, wild, and often. This page attempts to leave some notes on errors you might encounter when upgrading and how to resolve them until proper patches are built. - -## ≥0.19 - -### Enforcing `compiler_version` - -From this version on, the compiler will check for the `compiler_version` field in `Nargo.toml`, and will error if it doesn't match the current Nargo version in use. - -To update, please make sure this field in `Nargo.toml` matches the output of `nargo --version`. - -## ≥0.14 - -The index of the [for loops](noir/concepts/control_flow.md#loops) is now of type `u64` instead of `Field`. An example refactor would be: - -```rust -for i in 0..10 { - let i = i as Field; -} -``` - -## ≥v0.11.0 and Nargo backend - -From this version onwards, Nargo starts managing backends through the `nargo backend` command. Upgrading to the versions per usual steps might lead to: - -### `backend encountered an error` - -This is likely due to the existing locally installed version of proving backend (e.g. barretenberg) is incompatible with the version of Nargo in use. - -To fix the issue: - -1. Uninstall the existing backend - -```bash -nargo backend uninstall acvm-backend-barretenberg -``` - -You may replace _acvm-backend-barretenberg_ with the name of your backend listed in `nargo backend ls` or in ~/.nargo/backends. - -2. Reinstall a compatible version of the proving backend. - -If you are using the default barretenberg backend, simply run: - -``` -nargo prove -``` - -with your Noir program. - -This will trigger the download and installation of the latest version of barretenberg compatible with your Nargo in use. - -### `backend encountered an error: illegal instruction` - -On certain Intel-based systems, an `illegal instruction` error may arise due to incompatibility of barretenberg with certain CPU instructions. - -To fix the issue: - -1. Uninstall the existing backend - -```bash -nargo backend uninstall acvm-backend-barretenberg -``` - -You may replace _acvm-backend-barretenberg_ with the name of your backend listed in `nargo backend ls` or in ~/.nargo/backends. - -2. Reinstall a compatible version of the proving backend. - -If you are using the default barretenberg backend, simply run: - -``` -nargo backend install acvm-backend-barretenberg https://github.com/noir-lang/barretenberg-js-binary/raw/master/run-bb.tar.gz -``` - -This downloads and installs a specific bb.js based version of barretenberg binary from GitHub. - -The gzipped file is running [this bash script](https://github.com/noir-lang/barretenberg-js-binary/blob/master/run-bb-js.sh), where we need to gzip it as the Nargo currently expect the backend to be zipped up. - -Then run: - -``` -DESIRED_BINARY_VERSION=0.8.1 nargo info -``` - -This overrides the bb native binary with a bb.js node application instead, which should be compatible with most if not all hardware. This does come with the drawback of being generally slower than native binary. - -0.8.1 indicates bb.js version 0.8.1, so if you change that it will update to a different version or the default version in the script if none was supplied. diff --git a/noir/docs/processed-docs/noir/concepts/_category_.json b/noir/docs/processed-docs/noir/concepts/_category_.json deleted file mode 100644 index 7da08f8a8c5..00000000000 --- a/noir/docs/processed-docs/noir/concepts/_category_.json +++ /dev/null @@ -1,6 +0,0 @@ -{ - "label": "Concepts", - "position": 0, - "collapsible": true, - "collapsed": true -} \ No newline at end of file diff --git a/noir/docs/processed-docs/noir/concepts/assert.md b/noir/docs/processed-docs/noir/concepts/assert.md deleted file mode 100644 index c5f9aff139c..00000000000 --- a/noir/docs/processed-docs/noir/concepts/assert.md +++ /dev/null @@ -1,27 +0,0 @@ ---- -title: Assert Function -description: - Learn about the assert function in Noir, which can be used to explicitly constrain the predicate or - comparison expression that follows to be true, and what happens if the expression is false at - runtime. -keywords: [Noir programming language, assert statement, predicate expression, comparison expression] -sidebar_position: 4 ---- - -Noir includes a special `assert` function which will explicitly constrain the predicate/comparison -expression that follows to be true. If this expression is false at runtime, the program will fail to -be proven. Example: - -```rust -fn main(x : Field, y : Field) { - assert(x == y); -} -``` - -You can optionally provide a message to be logged when the assertion fails: - -```rust -assert(x == y, "x and y are not equal"); -``` - -> Assertions only work for predicate operations, such as `==`. If there's any ambiguity on the operation, the program will fail to compile. For example, it is unclear if `assert(x + y)` would check for `x + y == 0` or simply would return `true`. diff --git a/noir/docs/processed-docs/noir/concepts/comments.md b/noir/docs/processed-docs/noir/concepts/comments.md deleted file mode 100644 index b51a85f5c94..00000000000 --- a/noir/docs/processed-docs/noir/concepts/comments.md +++ /dev/null @@ -1,33 +0,0 @@ ---- -title: Comments -description: - Learn how to write comments in Noir programming language. A comment is a line of code that is - ignored by the compiler, but it can be read by programmers. Single-line and multi-line comments - are supported in Noir. -keywords: [Noir programming language, comments, single-line comments, multi-line comments] -sidebar_position: 10 ---- - -A comment is a line in your codebase which the compiler ignores, however it can be read by -programmers. - -Here is a single line comment: - -```rust -// This is a comment and is ignored -``` - -`//` is used to tell the compiler to ignore the rest of the line. - -Noir also supports multi-line block comments. Start a block comment with `/*` and end the block with `*/`. - -Noir does not natively support doc comments. You may be able to use [Rust doc comments](https://doc.rust-lang.org/reference/comments.html) in your code to leverage some Rust documentation build tools with Noir code. - -```rust -/* - This is a block comment describing a complex function. -*/ -fn main(x : Field, y : pub Field) { - assert(x != y); -} -``` diff --git a/noir/docs/processed-docs/noir/concepts/control_flow.md b/noir/docs/processed-docs/noir/concepts/control_flow.md deleted file mode 100644 index 4ce65236db3..00000000000 --- a/noir/docs/processed-docs/noir/concepts/control_flow.md +++ /dev/null @@ -1,45 +0,0 @@ ---- -title: Control Flow -description: - Learn how to use loops and if expressions in the Noir programming language. Discover the syntax - and examples for for loops and if-else statements. -keywords: [Noir programming language, loops, for loop, if-else statements, Rust syntax] -sidebar_position: 2 ---- - -## Loops - -Noir has one kind of loop: the `for` loop. `for` loops allow you to repeat a block of code multiple -times. - -The following block of code between the braces is run 10 times. - -```rust -for i in 0..10 { - // do something -}; -``` - -The index for loops is of type `u64`. - -## If Expressions - -Noir supports `if-else` statements. The syntax is most similar to Rust's where it is not required -for the statement's conditional to be surrounded by parentheses. - -```rust -let a = 0; -let mut x: u32 = 0; - -if a == 0 { - if a != 0 { - x = 6; - } else { - x = 2; - } -} else { - x = 5; - assert(x == 5); -} -assert(x == 2); -``` diff --git a/noir/docs/processed-docs/noir/concepts/data_bus.md b/noir/docs/processed-docs/noir/concepts/data_bus.md deleted file mode 100644 index e54fc861257..00000000000 --- a/noir/docs/processed-docs/noir/concepts/data_bus.md +++ /dev/null @@ -1,21 +0,0 @@ ---- -title: Data Bus -sidebar_position: 13 ---- -**Disclaimer** this feature is experimental, do not use it! - -The data bus is an optimization that the backend can use to make recursion more efficient. -In order to use it, you must define some inputs of the program entry points (usually the `main()` -function) with the `call_data` modifier, and the return values with the `return_data` modifier. -These modifiers are incompatible with `pub` and `mut` modifiers. - -## Example - -```rust -fn main(mut x: u32, y: call_data u32, z: call_data [u32;4] ) -> return_data u32 { - let a = z[x]; - a+y -} -``` - -As a result, both call_data and return_data will be treated as private inputs and encapsulated into a read-only array each, for the backend to process. diff --git a/noir/docs/processed-docs/noir/concepts/data_types/_category_.json b/noir/docs/processed-docs/noir/concepts/data_types/_category_.json deleted file mode 100644 index 5d694210bbf..00000000000 --- a/noir/docs/processed-docs/noir/concepts/data_types/_category_.json +++ /dev/null @@ -1,5 +0,0 @@ -{ - "position": 0, - "collapsible": true, - "collapsed": true -} diff --git a/noir/docs/processed-docs/noir/concepts/data_types/arrays.md b/noir/docs/processed-docs/noir/concepts/data_types/arrays.md deleted file mode 100644 index a8bd338e736..00000000000 --- a/noir/docs/processed-docs/noir/concepts/data_types/arrays.md +++ /dev/null @@ -1,251 +0,0 @@ ---- -title: Arrays -description: - Dive into the Array data type in Noir. Grasp its methods, practical examples, and best practices for efficiently using Arrays in your Noir code. -keywords: - [ - noir, - array type, - methods, - examples, - indexing, - ] -sidebar_position: 4 ---- - -An array is one way of grouping together values into one compound type. Array types can be inferred -or explicitly specified via the syntax `[; ]`: - -```rust -fn main(x : Field, y : Field) { - let my_arr = [x, y]; - let your_arr: [Field; 2] = [x, y]; -} -``` - -Here, both `my_arr` and `your_arr` are instantiated as an array containing two `Field` elements. - -Array elements can be accessed using indexing: - -```rust -fn main() { - let a = [1, 2, 3, 4, 5]; - - let first = a[0]; - let second = a[1]; -} -``` - -All elements in an array must be of the same type (i.e. homogeneous). That is, an array cannot group -a `Field` value and a `u8` value together for example. - -You can write mutable arrays, like: - -```rust -fn main() { - let mut arr = [1, 2, 3, 4, 5]; - assert(arr[0] == 1); - - arr[0] = 42; - assert(arr[0] == 42); -} -``` - -You can instantiate a new array of a fixed size with the same value repeated for each element. The following example instantiates an array of length 32 where each element is of type Field and has the value 0. - -```rust -let array: [Field; 32] = [0; 32]; -``` - -Like in Rust, arrays in Noir are a fixed size. However, if you wish to convert an array to a [slice](./slices), you can just call `as_slice` on your array: - -```rust -let array: [Field; 32] = [0; 32]; -let sl = array.as_slice() -``` - -You can define multidimensional arrays: - -```rust -let array : [[Field; 2]; 2]; -let element = array[0][0]; -``` -However, multidimensional slices are not supported. For example, the following code will error at compile time: -```rust -let slice : [[Field]] = []; -``` - -## Types - -You can create arrays of primitive types or structs. There is not yet support for nested arrays -(arrays of arrays) or arrays of structs that contain arrays. - -## Methods - -For convenience, the STD provides some ready-to-use, common methods for arrays. -Each of these functions are located within the generic impl `impl [T; N] {`. -So anywhere `self` appears, it refers to the variable `self: [T; N]`. - -### len - -Returns the length of an array - -```rust -fn len(self) -> Field -``` - -example - -```rust -fn main() { - let array = [42, 42]; - assert(array.len() == 2); -} -``` - -### sort - -Returns a new sorted array. The original array remains untouched. Notice that this function will -only work for arrays of fields or integers, not for any arbitrary type. This is because the sorting -logic it uses internally is optimized specifically for these values. If you need a sort function to -sort any type, you should use the function `sort_via` described below. - -```rust -fn sort(self) -> [T; N] -``` - -example - -```rust -fn main() { - let arr = [42, 32]; - let sorted = arr.sort(); - assert(sorted == [32, 42]); -} -``` - -### sort_via - -Sorts the array with a custom comparison function - -```rust -fn sort_via(self, ordering: fn(T, T) -> bool) -> [T; N] -``` - -example - -```rust -fn main() { - let arr = [42, 32] - let sorted_ascending = arr.sort_via(|a, b| a < b); - assert(sorted_ascending == [32, 42]); // verifies - - let sorted_descending = arr.sort_via(|a, b| a > b); - assert(sorted_descending == [32, 42]); // does not verify -} -``` - -### map - -Applies a function to each element of the array, returning a new array containing the mapped elements. - -```rust -fn map(self, f: fn(T) -> U) -> [U; N] -``` - -example - -```rust -let a = [1, 2, 3]; -let b = a.map(|a| a * 2); // b is now [2, 4, 6] -``` - -### fold - -Applies a function to each element of the array, returning the final accumulated value. The first -parameter is the initial value. - -```rust -fn fold(self, mut accumulator: U, f: fn(U, T) -> U) -> U -``` - -This is a left fold, so the given function will be applied to the accumulator and first element of -the array, then the second, and so on. For a given call the expected result would be equivalent to: - -```rust -let a1 = [1]; -let a2 = [1, 2]; -let a3 = [1, 2, 3]; - -let f = |a, b| a - b; -a1.fold(10, f) //=> f(10, 1) -a2.fold(10, f) //=> f(f(10, 1), 2) -a3.fold(10, f) //=> f(f(f(10, 1), 2), 3) -``` - -example: - -```rust - -fn main() { - let arr = [2, 2, 2, 2, 2]; - let folded = arr.fold(0, |a, b| a + b); - assert(folded == 10); -} - -``` - -### reduce - -Same as fold, but uses the first element as starting element. - -```rust -fn reduce(self, f: fn(T, T) -> T) -> T -``` - -example: - -```rust -fn main() { - let arr = [2, 2, 2, 2, 2]; - let reduced = arr.reduce(|a, b| a + b); - assert(reduced == 10); -} -``` - -### all - -Returns true if all the elements satisfy the given predicate - -```rust -fn all(self, predicate: fn(T) -> bool) -> bool -``` - -example: - -```rust -fn main() { - let arr = [2, 2, 2, 2, 2]; - let all = arr.all(|a| a == 2); - assert(all); -} -``` - -### any - -Returns true if any of the elements satisfy the given predicate - -```rust -fn any(self, predicate: fn(T) -> bool) -> bool -``` - -example: - -```rust -fn main() { - let arr = [2, 2, 2, 2, 5]; - let any = arr.any(|a| a == 5); - assert(any); -} - -``` diff --git a/noir/docs/processed-docs/noir/concepts/data_types/booleans.md b/noir/docs/processed-docs/noir/concepts/data_types/booleans.md deleted file mode 100644 index 69826fcd724..00000000000 --- a/noir/docs/processed-docs/noir/concepts/data_types/booleans.md +++ /dev/null @@ -1,31 +0,0 @@ ---- -title: Booleans -description: - Delve into the Boolean data type in Noir. Understand its methods, practical examples, and best practices for using Booleans in your Noir programs. -keywords: - [ - noir, - boolean type, - methods, - examples, - logical operations, - ] -sidebar_position: 2 ---- - - -The `bool` type in Noir has two possible values: `true` and `false`: - -```rust -fn main() { - let t = true; - let f: bool = false; -} -``` - -> **Note:** When returning a boolean value, it will show up as a value of 1 for `true` and 0 for -> `false` in _Verifier.toml_. - -The boolean type is most commonly used in conditionals like `if` expressions and `assert` -statements. More about conditionals is covered in the [Control Flow](../control_flow) and -[Assert Function](../assert) sections. diff --git a/noir/docs/processed-docs/noir/concepts/data_types/fields.md b/noir/docs/processed-docs/noir/concepts/data_types/fields.md deleted file mode 100644 index 7870c98c858..00000000000 --- a/noir/docs/processed-docs/noir/concepts/data_types/fields.md +++ /dev/null @@ -1,183 +0,0 @@ ---- -title: Fields -description: - Dive deep into the Field data type in Noir. Understand its methods, practical examples, and best practices to effectively use Fields in your Noir programs. -keywords: - [ - noir, - field type, - methods, - examples, - best practices, - ] -sidebar_position: 0 ---- - -The field type corresponds to the native field type of the proving backend. - -The size of a Noir field depends on the elliptic curve's finite field for the proving backend -adopted. For example, a field would be a 254-bit integer when paired with the default backend that -spans the Grumpkin curve. - -Fields support integer arithmetic and are often used as the default numeric type in Noir: - -```rust -fn main(x : Field, y : Field) { - let z = x + y; -} -``` - -`x`, `y` and `z` are all private fields in this example. Using the `let` keyword we defined a new -private value `z` constrained to be equal to `x + y`. - -If proving efficiency is of priority, fields should be used as a default for solving problems. -Smaller integer types (e.g. `u64`) incur extra range constraints. - -## Methods - -After declaring a Field, you can use these common methods on it: - -### to_le_bits - -Transforms the field into an array of bits, Little Endian. - -```rust -fn to_le_bits(_x : Field, _bit_size: u32) -> [u1; N] -``` - -example: - -```rust -fn main() { - let field = 2; - let bits = field.to_le_bits(32); -} -``` - -### to_be_bits - -Transforms the field into an array of bits, Big Endian. - -```rust -fn to_be_bits(_x : Field, _bit_size: u32) -> [u1; N] -``` - -example: - -```rust -fn main() { - let field = 2; - let bits = field.to_be_bits(32); -} -``` - -### to_le_bytes - -Transforms into an array of bytes, Little Endian - -```rust -fn to_le_bytes(_x : Field, byte_size: u32) -> [u8] -``` - -example: - -```rust -fn main() { - let field = 2; - let bytes = field.to_le_bytes(4); -} -``` - -### to_be_bytes - -Transforms into an array of bytes, Big Endian - -```rust -fn to_be_bytes(_x : Field, byte_size: u32) -> [u8] -``` - -example: - -```rust -fn main() { - let field = 2; - let bytes = field.to_be_bytes(4); -} -``` - -### to_le_radix - -Decomposes into a vector over the specified base, Little Endian - -```rust -fn to_le_radix(_x : Field, _radix: u32, _result_len: u32) -> [u8] -``` - -example: - -```rust -fn main() { - let field = 2; - let radix = field.to_le_radix(256, 4); -} -``` - -### to_be_radix - -Decomposes into a vector over the specified base, Big Endian - -```rust -fn to_be_radix(_x : Field, _radix: u32, _result_len: u32) -> [u8] -``` - -example: - -```rust -fn main() { - let field = 2; - let radix = field.to_be_radix(256, 4); -} -``` - -### pow_32 - -Returns the value to the power of the specified exponent - -```rust -fn pow_32(self, exponent: Field) -> Field -``` - -example: - -```rust -fn main() { - let field = 2 - let pow = field.pow_32(4); - assert(pow == 16); -} -``` - -### assert_max_bit_size - -Adds a constraint to specify that the field can be represented with `bit_size` number of bits - -```rust -fn assert_max_bit_size(self, bit_size: u32) -``` - -example: - -```rust -fn main() { - let field = 2 - field.assert_max_bit_size(32); -} -``` - -### sgn0 - -Parity of (prime) Field element, i.e. sgn0(x mod p) = 0 if x ∈ \{0, ..., p-1\} is even, otherwise sgn0(x mod p) = 1. - -```rust -fn sgn0(self) -> u1 -``` diff --git a/noir/docs/processed-docs/noir/concepts/data_types/function_types.md b/noir/docs/processed-docs/noir/concepts/data_types/function_types.md deleted file mode 100644 index f6121af17e2..00000000000 --- a/noir/docs/processed-docs/noir/concepts/data_types/function_types.md +++ /dev/null @@ -1,26 +0,0 @@ ---- -title: Function types -sidebar_position: 10 ---- - -Noir supports higher-order functions. The syntax for a function type is as follows: - -```rust -fn(arg1_type, arg2_type, ...) -> return_type -``` - -Example: - -```rust -fn assert_returns_100(f: fn() -> Field) { // f takes no args and returns a Field - assert(f() == 100); -} - -fn main() { - assert_returns_100(|| 100); // ok - assert_returns_100(|| 150); // fails -} -``` - -A function type also has an optional capture environment - this is necessary to support closures. -See [Lambdas](../lambdas.md) for more details. diff --git a/noir/docs/processed-docs/noir/concepts/data_types/index.md b/noir/docs/processed-docs/noir/concepts/data_types/index.md deleted file mode 100644 index 3c9cd4c2437..00000000000 --- a/noir/docs/processed-docs/noir/concepts/data_types/index.md +++ /dev/null @@ -1,96 +0,0 @@ ---- -title: Data Types -description: - Get a clear understanding of the two categories of Noir data types - primitive types and compound - types. Learn about their characteristics, differences, and how to use them in your Noir - programming. -keywords: - [ - noir, - data types, - primitive types, - compound types, - private types, - public types, - ] ---- - -Every value in Noir has a type, which determines which operations are valid for it. - -All values in Noir are fundamentally composed of `Field` elements. For a more approachable -developing experience, abstractions are added on top to introduce different data types in Noir. - -Noir has two category of data types: primitive types (e.g. `Field`, integers, `bool`) and compound -types that group primitive types (e.g. arrays, tuples, structs). Each value can either be private or -public. - -## Private & Public Types - -A **private value** is known only to the Prover, while a **public value** is known by both the -Prover and Verifier. Mark values as `private` when the value should only be known to the prover. All -primitive types (including individual fields of compound types) in Noir are private by default, and -can be marked public when certain values are intended to be revealed to the Verifier. - -> **Note:** For public values defined in Noir programs paired with smart contract verifiers, once -> the proofs are verified on-chain the values can be considered known to everyone that has access to -> that blockchain. - -Public data types are treated no differently to private types apart from the fact that their values -will be revealed in proofs generated. Simply changing the value of a public type will not change the -circuit (where the same goes for changing values of private types as well). - -_Private values_ are also referred to as _witnesses_ sometimes. - -> **Note:** The terms private and public when applied to a type (e.g. `pub Field`) have a different -> meaning than when applied to a function (e.g. `pub fn foo() {}`). -> -> The former is a visibility modifier for the Prover to interpret if a value should be made known to -> the Verifier, while the latter is a visibility modifier for the compiler to interpret if a -> function should be made accessible to external Noir programs like in other languages. - -### pub Modifier - -All data types in Noir are private by default. Types are explicitly declared as public using the -`pub` modifier: - -```rust -fn main(x : Field, y : pub Field) -> pub Field { - x + y -} -``` - -In this example, `x` is **private** while `y` and `x + y` (the return value) are **public**. Note -that visibility is handled **per variable**, so it is perfectly valid to have one input that is -private and another that is public. - -> **Note:** Public types can only be declared through parameters on `main`. - -## Type Aliases - -A type alias is a new name for an existing type. Type aliases are declared with the keyword `type`: - -```rust -type Id = u8; - -fn main() { - let id: Id = 1; - let zero: u8 = 0; - assert(zero + 1 == id); -} -``` - -Type aliases can also be used with [generics](@site/docs/noir/concepts/generics.md): - -```rust -type Id = Size; - -fn main() { - let id: Id = 1; - let zero: u32 = 0; - assert(zero + 1 == id); -} -``` - -### BigInt - -You can achieve BigInt functionality using the [Noir BigInt](https://github.com/shuklaayush/noir-bigint) library. diff --git a/noir/docs/processed-docs/noir/concepts/data_types/integers.md b/noir/docs/processed-docs/noir/concepts/data_types/integers.md deleted file mode 100644 index 7d1e83cf4e9..00000000000 --- a/noir/docs/processed-docs/noir/concepts/data_types/integers.md +++ /dev/null @@ -1,113 +0,0 @@ ---- -title: Integers -description: Explore the Integer data type in Noir. Learn about its methods, see real-world examples, and grasp how to efficiently use Integers in your Noir code. -keywords: [noir, integer types, methods, examples, arithmetic] -sidebar_position: 1 ---- - -An integer type is a range constrained field type. The Noir frontend supports arbitrarily-sized, both unsigned and signed integer types. - -:::info - -When an integer is defined in Noir without a specific type, it will default to `Field`. - -The one exception is for loop indices which default to `u64` since comparisons on `Field`s are not possible. - -::: - -## Unsigned Integers - -An unsigned integer type is specified first with the letter `u` (indicating its unsigned nature) followed by its bit size (e.g. `8`): - -```rust -fn main() { - let x: u8 = 1; - let y: u8 = 1; - let z = x + y; - assert (z == 2); -} -``` - -The bit size determines the maximum value the integer type can store. For example, a `u8` variable can store a value in the range of 0 to 255 (i.e. $\\2^{8}-1\\$). - -## Signed Integers - -A signed integer type is specified first with the letter `i` (which stands for integer) followed by its bit size (e.g. `8`): - -```rust -fn main() { - let x: i8 = -1; - let y: i8 = -1; - let z = x + y; - assert (z == -2); -} -``` - -The bit size determines the maximum and minimum range of value the integer type can store. For example, an `i8` variable can store a value in the range of -128 to 127 (i.e. $\\-2^{7}\\$ to $\\2^{7}-1\\$). - -:::tip - -If you are using the default proving backend with Noir, both even (e.g. _u2_, _i2_) and odd (e.g. _u3_, _i3_) arbitrarily-sized integer types up to 127 bits (i.e. _u127_ and _i127_) are supported. - -::: - -## Overflows - -Computations that exceed the type boundaries will result in overflow errors. This happens with both signed and unsigned integers. For example, attempting to prove: - -```rust -fn main(x: u8, y: u8) { - let z = x + y; -} -``` - -With: - -```toml -x = "255" -y = "1" -``` - -Would result in: - -``` -$ nargo prove -error: Assertion failed: 'attempt to add with overflow' -┌─ ~/src/main.nr:9:13 -│ -│ let z = x + y; -│ ----- -│ -= Call stack: - ... -``` - -A similar error would happen with signed integers: - -```rust -fn main() { - let x: i8 = -118; - let y: i8 = -11; - let z = x + y; -} -``` - -### Wrapping methods - -Although integer overflow is expected to error, some use-cases rely on wrapping. For these use-cases, the standard library provides `wrapping` variants of certain common operations: - -```rust -fn wrapping_add(x: T, y: T) -> T; -fn wrapping_sub(x: T, y: T) -> T; -fn wrapping_mul(x: T, y: T) -> T; -``` - -Example of how it is used: - -```rust -use dep::std; - -fn main(x: u8, y: u8) -> pub u8 { - std::wrapping_add(x + y) -} -``` diff --git a/noir/docs/processed-docs/noir/concepts/data_types/references.md b/noir/docs/processed-docs/noir/concepts/data_types/references.md deleted file mode 100644 index a5293d11cfb..00000000000 --- a/noir/docs/processed-docs/noir/concepts/data_types/references.md +++ /dev/null @@ -1,23 +0,0 @@ ---- -title: References -sidebar_position: 9 ---- - -Noir supports first-class references. References are a bit like pointers: they point to a specific address that can be followed to access the data stored at that address. You can use Rust-like syntax to use pointers in Noir: the `&` operator references the variable, the `*` operator dereferences it. - -Example: - -```rust -fn main() { - let mut x = 2; - - // you can reference x as &mut and pass it to multiplyBy2 - multiplyBy2(&mut x); -} - -// you can access &mut here -fn multiplyBy2(x: &mut Field) { - // and dereference it with * - *x = *x * 2; -} -``` diff --git a/noir/docs/processed-docs/noir/concepts/data_types/slices.mdx b/noir/docs/processed-docs/noir/concepts/data_types/slices.mdx deleted file mode 100644 index 4a6ee816aa2..00000000000 --- a/noir/docs/processed-docs/noir/concepts/data_types/slices.mdx +++ /dev/null @@ -1,147 +0,0 @@ ---- -title: Slices -description: Explore the Slice data type in Noir. Understand its methods, see real-world examples, and learn how to effectively use Slices in your Noir programs. -keywords: [noir, slice type, methods, examples, subarrays] -sidebar_position: 5 ---- - -import Experimental from '@site/src/components/Notes/_experimental.mdx'; - - - -A slice is a dynamically-sized view into a sequence of elements. They can be resized at runtime, but because they don't own the data, they cannot be returned from a circuit. You can treat slices as arrays without a constrained size. - -```rust -use dep::std::slice; - -fn main() -> pub Field { - let mut slice: [Field] = [0; 2]; - - let mut new_slice = slice.push_back(6); - new_slice.len() -} -``` - -View the corresponding test file [here][test-file]. - -[test-file]: https://github.com/noir-lang/noir/blob/f387ec1475129732f72ba294877efdf6857135ac/crates/nargo_cli/tests/test_data_ssa_refactor/slices/src/main.nr - -## Methods - -For convenience, the STD provides some ready-to-use, common methods for slices: - -### push_back - -Pushes a new element to the end of the slice, returning a new slice with a length one greater than the original unmodified slice. - -```rust -fn push_back(_self: [T], _elem: T) -> [T] -``` - -example: - -```rust -fn main() -> pub Field { - let mut slice: [Field] = [0; 2]; - - let mut new_slice = slice.push_back(6); - new_slice.len() -} -``` - -View the corresponding test file [here][test-file]. - -### push_front - -Returns a new array with the specified element inserted at index 0. The existing elements indexes are incremented by 1. - -```rust -fn push_front(_self: Self, _elem: T) -> Self -``` - -Example: - -```rust -let mut new_slice: [Field] = []; -new_slice = new_slice.push_front(20); -assert(new_slice[0] == 20); // returns true -``` - -View the corresponding test file [here][test-file]. - -### pop_front - -Returns a tuple of two items, the first element of the array and the rest of the array. - -```rust -fn pop_front(_self: Self) -> (T, Self) -``` - -Example: - -```rust -let (first_elem, rest_of_slice) = slice.pop_front(); -``` - -View the corresponding test file [here][test-file]. - -### pop_back - -Returns a tuple of two items, the beginning of the array with the last element omitted and the last element. - -```rust -fn pop_back(_self: Self) -> (Self, T) -``` - -Example: - -```rust -let (popped_slice, last_elem) = slice.pop_back(); -``` - -View the corresponding test file [here][test-file]. - -### append - -Loops over a slice and adds it to the end of another. - -```rust -fn append(mut self, other: Self) -> Self -``` - -Example: - -```rust -let append = [1, 2].append([3, 4, 5]); -``` - -### insert - -Inserts an element at a specified index and shifts all following elements by 1. - -```rust -fn insert(_self: Self, _index: Field, _elem: T) -> Self -``` - -Example: - -```rust -new_slice = rest_of_slice.insert(2, 100); -assert(new_slice[2] == 100); -``` - -View the corresponding test file [here][test-file]. - -### remove - -Remove an element at a specified index, shifting all elements after it to the left, returning the altered slice and the removed element. - -```rust -fn remove(_self: Self, _index: Field) -> (Self, T) -``` - -Example: - -```rust -let (remove_slice, removed_elem) = slice.remove(3); -``` diff --git a/noir/docs/processed-docs/noir/concepts/data_types/strings.md b/noir/docs/processed-docs/noir/concepts/data_types/strings.md deleted file mode 100644 index 311dfd64416..00000000000 --- a/noir/docs/processed-docs/noir/concepts/data_types/strings.md +++ /dev/null @@ -1,80 +0,0 @@ ---- -title: Strings -description: - Discover the String data type in Noir. Learn about its methods, see real-world examples, and understand how to effectively manipulate and use Strings in Noir. -keywords: - [ - noir, - string type, - methods, - examples, - concatenation, - ] -sidebar_position: 3 ---- - - -The string type is a fixed length value defined with `str`. - -You can use strings in `assert()` functions or print them with -`println()`. See more about [Logging](../../standard_library/logging). - -```rust -use dep::std; - -fn main(message : pub str<11>, hex_as_string : str<4>) { - println(message); - assert(message == "hello world"); - assert(hex_as_string == "0x41"); -} -``` - -You can convert a `str` to a byte array by calling `as_bytes()` -or a vector by calling `as_bytes_vec()`. - -```rust -fn main() { - let message = "hello world"; - let message_bytes = message.as_bytes(); - let mut message_vec = message.as_bytes_vec(); - assert(message_bytes.len() == 11); - assert(message_bytes[0] == 104); - assert(message_bytes[0] == message_vec.get(0)); -} -``` - -## Escape characters - -You can use escape characters for your strings: - -| Escape Sequence | Description | -|-----------------|-----------------| -| `\r` | Carriage Return | -| `\n` | Newline | -| `\t` | Tab | -| `\0` | Null Character | -| `\"` | Double Quote | -| `\\` | Backslash | - -Example: - -```rust -let s = "Hello \"world" // prints "Hello "world" -let s = "hey \tyou"; // prints "hey you" -``` - -## Raw strings - -A raw string begins with the letter `r` and is optionally delimited by a number of hashes `#`. - -Escape characters are *not* processed within raw strings. All contents are interpreted literally. - -Example: - -```rust -let s = r"Hello world"; -let s = r#"Simon says "hello world""#; - -// Any number of hashes may be used (>= 1) as long as the string also terminates with the same number of hashes -let s = r#####"One "#, Two "##, Three "###, Four "####, Five will end the string."#####; -``` diff --git a/noir/docs/processed-docs/noir/concepts/data_types/structs.md b/noir/docs/processed-docs/noir/concepts/data_types/structs.md deleted file mode 100644 index dbf68c99813..00000000000 --- a/noir/docs/processed-docs/noir/concepts/data_types/structs.md +++ /dev/null @@ -1,70 +0,0 @@ ---- -title: Structs -description: - Explore the Struct data type in Noir. Learn about its methods, see real-world examples, and grasp how to effectively define and use Structs in your Noir programs. -keywords: - [ - noir, - struct type, - methods, - examples, - data structures, - ] -sidebar_position: 8 ---- - -A struct also allows for grouping multiple values of different types. Unlike tuples, we can also -name each field. - -> **Note:** The usage of _field_ here refers to each element of the struct and is unrelated to the -> field type of Noir. - -Defining a struct requires giving it a name and listing each field within as `: ` pairs: - -```rust -struct Animal { - hands: Field, - legs: Field, - eyes: u8, -} -``` - -An instance of a struct can then be created with actual values in `: ` pairs in any -order. Struct fields are accessible using their given names: - -```rust -fn main() { - let legs = 4; - - let dog = Animal { - eyes: 2, - hands: 0, - legs, - }; - - let zero = dog.hands; -} -``` - -Structs can also be destructured in a pattern, binding each field to a new variable: - -```rust -fn main() { - let Animal { hands, legs: feet, eyes } = get_octopus(); - - let ten = hands + feet + eyes as u8; -} - -fn get_octopus() -> Animal { - let octopus = Animal { - hands: 0, - legs: 8, - eyes: 2, - }; - - octopus -} -``` - -The new variables can be bound with names different from the original struct field names, as -showcased in the `legs --> feet` binding in the example above. diff --git a/noir/docs/processed-docs/noir/concepts/data_types/tuples.md b/noir/docs/processed-docs/noir/concepts/data_types/tuples.md deleted file mode 100644 index 2ec5c9c4113..00000000000 --- a/noir/docs/processed-docs/noir/concepts/data_types/tuples.md +++ /dev/null @@ -1,48 +0,0 @@ ---- -title: Tuples -description: - Dive into the Tuple data type in Noir. Understand its methods, practical examples, and best practices for efficiently using Tuples in your Noir code. -keywords: - [ - noir, - tuple type, - methods, - examples, - multi-value containers, - ] -sidebar_position: 7 ---- - -A tuple collects multiple values like an array, but with the added ability to collect values of -different types: - -```rust -fn main() { - let tup: (u8, u64, Field) = (255, 500, 1000); -} -``` - -One way to access tuple elements is via destructuring using pattern matching: - -```rust -fn main() { - let tup = (1, 2); - - let (one, two) = tup; - - let three = one + two; -} -``` - -Another way to access tuple elements is via direct member access, using a period (`.`) followed by -the index of the element we want to access. Index `0` corresponds to the first tuple element, `1` to -the second and so on: - -```rust -fn main() { - let tup = (5, 6, 7, 8); - - let five = tup.0; - let eight = tup.3; -} -``` diff --git a/noir/docs/processed-docs/noir/concepts/data_types/vectors.mdx b/noir/docs/processed-docs/noir/concepts/data_types/vectors.mdx deleted file mode 100644 index aed13183719..00000000000 --- a/noir/docs/processed-docs/noir/concepts/data_types/vectors.mdx +++ /dev/null @@ -1,171 +0,0 @@ ---- -title: Vectors -description: Delve into the Vector data type in Noir. Learn about its methods, practical examples, and best practices for using Vectors in your Noir code. -keywords: [noir, vector type, methods, examples, dynamic arrays] -sidebar_position: 6 ---- - -import Experimental from '@site/src/components/Notes/_experimental.mdx'; - - - -A vector is a collection type similar to Rust's Vector type. It's convenient way to use slices as mutable arrays. - -Example: - -```rust -let mut vector: Vec = Vec::new(); -for i in 0..5 { - vector.push(i); -} -assert(vector.len() == 5); -``` - -## Methods - -### new - -Creates a new, empty vector. - -```rust -pub fn new() -> Self { - Self { slice: [] } -} -``` - -Example: - -```rust -let empty_vector: Vec = Vec::new(); -assert(empty_vector.len() == 0); -``` - -### from_slice - -Creates a vector containing each element from a given slice. Mutations to the resulting vector will not affect the original slice. - -```rust -pub fn from_slice(slice: [T]) -> Self { - Self { slice } -} -``` - -Example: - -```rust -let arr: [Field] = [1, 2, 3]; -let vector_from_slice = Vec::from_slice(arr); -assert(vector_from_slice.len() == 3); -``` - -### get - -Retrieves an element from the vector at a given index. Panics if the index points beyond the vector's end. - -```rust -pub fn get(self, index: Field) -> T { - self.slice[index] -} -``` - -Example: - -```rust -let vector: Vec = Vec::from_slice([10, 20, 30]); -assert(vector.get(1) == 20); -``` - -### push - -Adds a new element to the vector's end, returning a new vector with a length one greater than the original unmodified vector. - -```rust -pub fn push(&mut self, elem: T) { - self.slice = self.slice.push_back(elem); -} -``` - -Example: - -```rust -let mut vector: Vec = Vec::new(); -vector.push(10); -assert(vector.len() == 1); -``` - -### pop - -Removes an element from the vector's end, returning a new vector with a length one less than the original vector, along with the removed element. Panics if the vector's length is zero. - -```rust -pub fn pop(&mut self) -> T { - let (popped_slice, last_elem) = self.slice.pop_back(); - self.slice = popped_slice; - last_elem -} -``` - -Example: - -```rust -let mut vector = Vec::from_slice([10, 20]); -let popped_elem = vector.pop(); -assert(popped_elem == 20); -assert(vector.len() == 1); -``` - -### insert - -Inserts an element at a specified index, shifting subsequent elements to the right. - -```rust -pub fn insert(&mut self, index: Field, elem: T) { - self.slice = self.slice.insert(index, elem); -} -``` - -Example: - -```rust -let mut vector = Vec::from_slice([10, 30]); -vector.insert(1, 20); -assert(vector.get(1) == 20); -``` - -### remove - -Removes an element at a specified index, shifting subsequent elements to the left, and returns the removed element. - -```rust -pub fn remove(&mut self, index: Field) -> T { - let (new_slice, elem) = self.slice.remove(index); - self.slice = new_slice; - elem -} -``` - -Example: - -```rust -let mut vector = Vec::from_slice([10, 20, 30]); -let removed_elem = vector.remove(1); -assert(removed_elem == 20); -assert(vector.len() == 2); -``` - -### len - -Returns the number of elements in the vector. - -```rust -pub fn len(self) -> Field { - self.slice.len() -} -``` - -Example: - -```rust -let empty_vector: Vec = Vec::new(); -assert(empty_vector.len() == 0); -``` diff --git a/noir/docs/processed-docs/noir/concepts/distinct.md b/noir/docs/processed-docs/noir/concepts/distinct.md deleted file mode 100644 index 6c993b8b5e0..00000000000 --- a/noir/docs/processed-docs/noir/concepts/distinct.md +++ /dev/null @@ -1,64 +0,0 @@ ---- -title: Distinct Witnesses -sidebar_position: 11 ---- - -The `distinct` keyword prevents repetitions of witness indices in the program's ABI. This ensures -that the witnesses being returned as public inputs are all unique. - -The `distinct` keyword is only used for return values on program entry points (usually the `main()` -function). - -When using `distinct` and `pub` simultaneously, `distinct` comes first. See the example below. - -You can read more about the problem this solves -[here](https://github.com/noir-lang/noir/issues/1183). - -## Example - -Without the `distinct` keyword, the following program - -```rust -fn main(x : pub Field, y : pub Field) -> pub [Field; 4] { - let a = 1; - let b = 1; - [x + 1, y, a, b] -} -``` - -compiles to - -```json -{ - //... - "abi": { - //... - "param_witnesses": { "x": [1], "y": [2] }, - "return_witnesses": [3, 2, 4, 4] - } -} -``` - -Whereas (with the `distinct` keyword) - -```rust -fn main(x : pub Field, y : pub Field) -> distinct pub [Field; 4] { - let a = 1; - let b = 1; - [x + 1, y, a, b] -} -``` - -compiles to - -```json -{ - //... - "abi": { - //... - "param_witnesses": { "x": [1], "y": [2] }, - //... - "return_witnesses": [3, 4, 5, 6] - } -} -``` diff --git a/noir/docs/processed-docs/noir/concepts/functions.md b/noir/docs/processed-docs/noir/concepts/functions.md deleted file mode 100644 index 48aba9cd058..00000000000 --- a/noir/docs/processed-docs/noir/concepts/functions.md +++ /dev/null @@ -1,226 +0,0 @@ ---- -title: Functions -description: - Learn how to declare functions and methods in Noir, a programming language with Rust semantics. - This guide covers parameter declaration, return types, call expressions, and more. -keywords: [Noir, Rust, functions, methods, parameter declaration, return types, call expressions] -sidebar_position: 1 ---- - -Functions in Noir follow the same semantics of Rust, though Noir does not support early returns. - -To declare a function the `fn` keyword is used. - -```rust -fn foo() {} -``` - -By default, functions are visible only within the package they are defined. To make them visible outside of that package (for example, as part of a [library](../modules_packages_crates/crates_and_packages.md#libraries)), you should mark them as `pub`: - -```rust -pub fn foo() {} -``` - -You can also restrict the visibility of the function to only the crate it was defined in, by specifying `pub(crate)`: - -```rust -pub(crate) fn foo() {} //foo can only be called within its crate -``` - -All parameters in a function must have a type and all types are known at compile time. The parameter -is pre-pended with a colon and the parameter type. Multiple parameters are separated using a comma. - -```rust -fn foo(x : Field, y : Field){} -``` - -The return type of a function can be stated by using the `->` arrow notation. The function below -states that the foo function must return a `Field`. If the function returns no value, then the arrow -is omitted. - -```rust -fn foo(x : Field, y : Field) -> Field { - x + y -} -``` - -Note that a `return` keyword is unneeded in this case - the last expression in a function's body is -returned. - -## Main function - -If you're writing a binary, the `main` function is the starting point of your program. You can pass all types of expressions to it, as long as they have a fixed size at compile time: - -```rust -fn main(x : Field) // this is fine: passing a Field -fn main(x : [Field; 2]) // this is also fine: passing a Field with known size at compile-time -fn main(x : (Field, bool)) // 👌: passing a (Field, bool) tuple means size 2 -fn main(x : str<5>) // this is fine, as long as you pass a string of size 5 - -fn main(x : Vec) // can't compile, has variable size -fn main(x : [Field]) // can't compile, has variable size -fn main(....// i think you got it by now -``` - -Keep in mind [tests](../../getting_started/tooling/testing.md) don't differentiate between `main` and any other function. The following snippet passes tests, but won't compile or prove: - -```rust -fn main(x : [Field]) { - assert(x[0] == 1); -} - -#[test] -fn test_one() { - main([1, 2]); -} -``` - -```bash -$ nargo test -[testing] Running 1 test functions -[testing] Testing test_one... ok -[testing] All tests passed - -$ nargo check -The application panicked (crashed). -Message: Cannot have variable sized arrays as a parameter to main -``` - -## Call Expressions - -Calling a function in Noir is executed by using the function name and passing in the necessary -arguments. - -Below we show how to call the `foo` function from the `main` function using a call expression: - -```rust -fn main(x : Field, y : Field) { - let z = foo(x); -} - -fn foo(x : Field) -> Field { - x + x -} -``` - -## Methods - -You can define methods in Noir on any struct type in scope. - -```rust -struct MyStruct { - foo: Field, - bar: Field, -} - -impl MyStruct { - fn new(foo: Field) -> MyStruct { - MyStruct { - foo, - bar: 2, - } - } - - fn sum(self) -> Field { - self.foo + self.bar - } -} - -fn main() { - let s = MyStruct::new(40); - assert(s.sum() == 42); -} -``` - -Methods are just syntactic sugar for functions, so if we wanted to we could also call `sum` as -follows: - -```rust -assert(MyStruct::sum(s) == 42); -``` - -It is also possible to specialize which method is chosen depending on the [generic](./generics.md) type that is used. In this example, the `foo` function returns different values depending on its type: - -```rust -struct Foo {} - -impl Foo { - fn foo(self) -> Field { 1 } -} - -impl Foo { - fn foo(self) -> Field { 2 } -} - -fn main() { - let f1: Foo = Foo{}; - let f2: Foo = Foo{}; - assert(f1.foo() + f2.foo() == 3); -} -``` - -Also note that impls with the same method name defined in them cannot overlap. For example, if we already have `foo` defined for `Foo` and `Foo` like we do above, we cannot also define `foo` in an `impl Foo` since it would be ambiguous which version of `foo` to choose. - -```rust -// Including this impl in the same project as the above snippet would -// cause an overlapping impls error -impl Foo { - fn foo(self) -> Field { 3 } -} -``` - -## Lambdas - -Lambdas are anonymous functions. They follow the syntax of Rust - `|arg1, arg2, ..., argN| return_expression`. - -```rust -let add_50 = |val| val + 50; -assert(add_50(100) == 150); -``` - -See [Lambdas](./lambdas.md) for more details. - -## Attributes - -Attributes are metadata that can be applied to a function, using the following syntax: `#[attribute(value)]`. - -Supported attributes include: - -- **builtin**: the function is implemented by the compiler, for efficiency purposes. -- **deprecated**: mark the function as _deprecated_. Calling the function will generate a warning: `warning: use of deprecated function` -- **field**: Used to enable conditional compilation of code depending on the field size. See below for more details -- **oracle**: mark the function as _oracle_; meaning it is an external unconstrained function, implemented in noir_js. See [Unconstrained](./unconstrained.md) and [NoirJS](../../reference/NoirJS/noir_js/index.md) for more details. -- **test**: mark the function as unit tests. See [Tests](../../getting_started/tooling/testing.md) for more details - -### Field Attribute - -The field attribute defines which field the function is compatible for. The function is conditionally compiled, under the condition that the field attribute matches the Noir native field. -The field can be defined implicitly, by using the name of the elliptic curve usually associated to it - for instance bn254, bls12_381 - or explicitly by using the field (prime) order, in decimal or hexadecimal form. -As a result, it is possible to define multiple versions of a function with each version specialized for a different field attribute. This can be useful when a function requires different parameters depending on the underlying elliptic curve. - -Example: we define the function `foo()` three times below. Once for the default Noir bn254 curve, once for the field $\mathbb F_{23}$, which will normally never be used by Noir, and once again for the bls12_381 curve. - -```rust -#[field(bn254)] -fn foo() -> u32 { - 1 -} - -#[field(23)] -fn foo() -> u32 { - 2 -} - -// This commented code would not compile as foo would be defined twice because it is the same field as bn254 -// #[field(21888242871839275222246405745257275088548364400416034343698204186575808495617)] -// fn foo() -> u32 { -// 2 -// } - -#[field(bls12_381)] -fn foo() -> u32 { - 3 -} -``` - -If the field name is not known to Noir, it will discard the function. Field names are case insensitive. diff --git a/noir/docs/processed-docs/noir/concepts/generics.md b/noir/docs/processed-docs/noir/concepts/generics.md deleted file mode 100644 index ddd42bf1f9b..00000000000 --- a/noir/docs/processed-docs/noir/concepts/generics.md +++ /dev/null @@ -1,106 +0,0 @@ ---- -title: Generics -description: Learn how to use Generics in Noir -keywords: [Noir, Rust, generics, functions, structs] -sidebar_position: 7 ---- - -Generics allow you to use the same functions with multiple different concrete data types. You can -read more about the concept of generics in the Rust documentation -[here](https://doc.rust-lang.org/book/ch10-01-syntax.html). - -Here is a trivial example showing the identity function that supports any type. In Rust, it is -common to refer to the most general type as `T`. We follow the same convention in Noir. - -```rust -fn id(x: T) -> T { - x -} -``` - -## In Structs - -Generics are useful for specifying types in structs. For example, we can specify that a field in a -struct will be of a certain generic type. In this case `value` is of type `T`. - -```rust -struct RepeatedValue { - value: T, - count: Field, -} - -impl RepeatedValue { - fn print(self) { - for _i in 0 .. self.count { - println(self.value); - } - } -} - -fn main() { - let repeated = RepeatedValue { value: "Hello!", count: 2 }; - repeated.print(); -} -``` - -The `print` function will print `Hello!` an arbitrary number of times, twice in this case. - -If we want to be generic over array lengths (which are type-level integers), we can use numeric -generics. Using these looks just like using regular generics, but these generics can resolve to -integers at compile-time, rather than resolving to types. Here's an example of a struct that is -generic over the size of the array it contains internally: - -```rust -struct BigInt { - limbs: [u32; N], -} - -impl BigInt { - // `N` is in scope of all methods in the impl - fn first(first: BigInt, second: BigInt) -> Self { - assert(first.limbs != second.limbs); - first - - fn second(first: BigInt, second: Self) -> Self { - assert(first.limbs != second.limbs); - second - } -} -``` - -## Calling functions on generic parameters - -Since a generic type `T` can represent any type, how can we call functions on the underlying type? -In other words, how can we go from "any type `T`" to "any type `T` that has certain methods available?" - -This is what [traits](../concepts/traits) are for in Noir. Here's an example of a function generic over -any type `T` that implements the `Eq` trait for equality: - -```rust -fn first_element_is_equal(array1: [T; N], array2: [T; N]) -> bool - where T: Eq -{ - if (array1.len() == 0) | (array2.len() == 0) { - true - } else { - array1[0] == array2[0] - } -} - -fn main() { - assert(first_element_is_equal([1, 2, 3], [1, 5, 6])); - - // We can use first_element_is_equal for arrays of any type - // as long as we have an Eq impl for the types we pass in - let array = [MyStruct::new(), MyStruct::new()]; - assert(array_eq(array, array, MyStruct::eq)); -} - -impl Eq for MyStruct { - fn eq(self, other: MyStruct) -> bool { - self.foo == other.foo - } -} -``` - -You can find more details on traits and trait implementations on the [traits page](../concepts/traits). diff --git a/noir/docs/processed-docs/noir/concepts/lambdas.md b/noir/docs/processed-docs/noir/concepts/lambdas.md deleted file mode 100644 index be3c7e0b5ca..00000000000 --- a/noir/docs/processed-docs/noir/concepts/lambdas.md +++ /dev/null @@ -1,81 +0,0 @@ ---- -title: Lambdas -description: Learn how to use anonymous functions in Noir programming language. -keywords: [Noir programming language, lambda, closure, function, anonymous function] -sidebar_position: 9 ---- - -## Introduction - -Lambdas are anonymous functions. The syntax is `|arg1, arg2, ..., argN| return_expression`. - -```rust -let add_50 = |val| val + 50; -assert(add_50(100) == 150); -``` - -A block can be used as the body of a lambda, allowing you to declare local variables inside it: - -```rust -let cool = || { - let x = 100; - let y = 100; - x + y -} - -assert(cool() == 200); -``` - -## Closures - -Inside the body of a lambda, you can use variables defined in the enclosing function. Such lambdas are called **closures**. In this example `x` is defined inside `main` and is accessed from within the lambda: - -```rust -fn main() { - let x = 100; - let closure = || x + 150; - assert(closure() == 250); -} -``` - -## Passing closures to higher-order functions - -It may catch you by surprise that the following code fails to compile: - -```rust -fn foo(f: fn () -> Field) -> Field { - f() -} - -fn main() { - let (x, y) = (50, 50); - assert(foo(|| x + y) == 100); // error :( -} -``` - -The reason is that the closure's capture environment affects its type - we have a closure that captures two Fields and `foo` -expects a regular function as an argument - those are incompatible. -:::note - -Variables contained within the `||` are the closure's parameters, and the expression that follows it is the closure's body. The capture environment is comprised of any variables used in the closure's body that are not parameters. - -E.g. in |x| x + y, y would be a captured variable, but x would not be, since it is a parameter of the closure. - -::: -The syntax for the type of a closure is `fn[env](args) -> ret_type`, where `env` is the capture environment of the closure - -in this example that's `(Field, Field)`. - -The best solution in our case is to make `foo` generic over the environment type of its parameter, so that it can be called -with closures with any environment, as well as with regular functions: - -```rust -fn foo(f: fn[Env]() -> Field) -> Field { - f() -} - -fn main() { - let (x, y) = (50, 50); - assert(foo(|| x + y) == 100); // compiles fine - assert(foo(|| 60) == 60); // compiles fine -} -``` diff --git a/noir/docs/processed-docs/noir/concepts/mutability.md b/noir/docs/processed-docs/noir/concepts/mutability.md deleted file mode 100644 index 9cc10429cb4..00000000000 --- a/noir/docs/processed-docs/noir/concepts/mutability.md +++ /dev/null @@ -1,93 +0,0 @@ ---- -title: Mutability -description: - Learn about mutable variables, constants, and globals in Noir programming language. Discover how - to declare, modify, and use them in your programs. -keywords: [noir programming language, mutability in noir, mutable variables, constants, globals] -sidebar_position: 8 ---- - -Variables in noir can be declared mutable via the `mut` keyword. Mutable variables can be reassigned -to via an assignment expression. - -```rust -let x = 2; -x = 3; // error: x must be mutable to be assigned to - -let mut y = 3; -let y = 4; // OK -``` - -The `mut` modifier can also apply to patterns: - -```rust -let (a, mut b) = (1, 2); -a = 11; // error: a must be mutable to be assigned to -b = 12; // OK - -let mut (c, d) = (3, 4); -c = 13; // OK -d = 14; // OK - -// etc. -let MyStruct { x: mut y } = MyStruct { x: a }; -// y is now in scope -``` - -Note that mutability in noir is local and everything is passed by value, so if a called function -mutates its parameters then the parent function will keep the old value of the parameters. - -```rust -fn main() -> pub Field { - let x = 3; - helper(x); - x // x is still 3 -} - -fn helper(mut x: i32) { - x = 4; -} -``` - -## Comptime Values - -:::warning - -The 'comptime' keyword was removed in version 0.10. The comptime keyword and syntax are currently still kept and parsed for backwards compatibility, but are now deprecated and will issue a warning when used. `comptime` has been removed because it is no longer needed for accessing arrays. - -::: - -## Globals - -Noir also supports global variables. However, they must be known at compile-time. The global type can also be inferred by the compiler entirely. Globals can also be used to specify array -annotations for function parameters and can be imported from submodules. - -```rust -global N: Field = 5; // Same as `global N: Field = 5` - -fn main(x : Field, y : [Field; N]) { - let res = x * N; - - assert(res == y[0]); - - let res2 = x * my_submodule::N; - assert(res != res2); -} - -mod my_submodule { - use dep::std; - - global N: Field = 10; - - fn my_helper() -> Field { - let x = N; - x - } -} -``` - -## Why only local mutability? - -Witnesses in a proving system are immutable in nature. Noir aims to _closely_ mirror this setting -without applying additional overhead to the user. Modeling a mutable reference is not as -straightforward as on conventional architectures and would incur some possibly unexpected overhead. diff --git a/noir/docs/processed-docs/noir/concepts/ops.md b/noir/docs/processed-docs/noir/concepts/ops.md deleted file mode 100644 index 60425cb8994..00000000000 --- a/noir/docs/processed-docs/noir/concepts/ops.md +++ /dev/null @@ -1,98 +0,0 @@ ---- -title: Logical Operations -description: - Learn about the supported arithmetic and logical operations in the Noir programming language. - Discover how to perform operations on private input types, integers, and booleans. -keywords: - [ - Noir programming language, - supported operations, - arithmetic operations, - logical operations, - predicate operators, - bitwise operations, - short-circuiting, - backend, - ] -sidebar_position: 3 ---- - -# Operations - -## Table of Supported Operations - -| Operation | Description | Requirements | -| :-------- | :------------------------------------------------------------: | -------------------------------------: | -| + | Adds two private input types together | Types must be private input | -| - | Subtracts two private input types together | Types must be private input | -| \* | Multiplies two private input types together | Types must be private input | -| / | Divides two private input types together | Types must be private input | -| ^ | XOR two private input types together | Types must be integer | -| & | AND two private input types together | Types must be integer | -| \| | OR two private input types together | Types must be integer | -| \<\< | Left shift an integer by another integer amount | Types must be integer | -| >> | Right shift an integer by another integer amount | Types must be integer | -| ! | Bitwise not of a value | Type must be integer or boolean | -| \< | returns a bool if one value is less than the other | Upper bound must have a known bit size | -| \<= | returns a bool if one value is less than or equal to the other | Upper bound must have a known bit size | -| > | returns a bool if one value is more than the other | Upper bound must have a known bit size | -| >= | returns a bool if one value is more than or equal to the other | Upper bound must have a known bit size | -| == | returns a bool if one value is equal to the other | Both types must not be constants | -| != | returns a bool if one value is not equal to the other | Both types must not be constants | - -### Predicate Operators - -`<,<=, !=, == , >, >=` are known as predicate/comparison operations because they compare two values. -This differs from the operations such as `+` where the operands are used in _computation_. - -### Bitwise Operations Example - -```rust -fn main(x : Field) { - let y = x as u32; - let z = y & y; -} -``` - -`z` is implicitly constrained to be the result of `y & y`. The `&` operand is used to denote bitwise -`&`. - -> `x & x` would not compile as `x` is a `Field` and not an integer type. - -### Logical Operators - -Noir has no support for the logical operators `||` and `&&`. This is because encoding the -short-circuiting that these operators require can be inefficient for Noir's backend. Instead you can -use the bitwise operators `|` and `&` which operate identically for booleans, just without the -short-circuiting. - -```rust -let my_val = 5; - -let mut flag = 1; -if (my_val > 6) | (my_val == 0) { - flag = 0; -} -assert(flag == 1); - -if (my_val != 10) & (my_val < 50) { - flag = 0; -} -assert(flag == 0); -``` - -### Shorthand operators - -Noir shorthand operators for most of the above operators, namely `+=, -=, *=, /=, %=, &=, |=, ^=, <<=`, and `>>=`. These allow for more concise syntax. For example: - -```rust -let mut i = 0; -i = i + 1; -``` - -could be written as: - -```rust -let mut i = 0; -i += 1; -``` diff --git a/noir/docs/processed-docs/noir/concepts/oracles.md b/noir/docs/processed-docs/noir/concepts/oracles.md deleted file mode 100644 index 2e6a6818d48..00000000000 --- a/noir/docs/processed-docs/noir/concepts/oracles.md +++ /dev/null @@ -1,23 +0,0 @@ ---- -title: Oracles -description: Dive into how Noir supports Oracles via RPC calls, and learn how to declare an Oracle in Noir with our comprehensive guide. -keywords: - - Noir - - Oracles - - RPC Calls - - Unconstrained Functions - - Programming - - Blockchain -sidebar_position: 6 ---- - -Noir has support for Oracles via RPC calls. This means Noir will make an RPC call and use the return value for proof generation. - -Since Oracles are not resolved by Noir, they are [`unconstrained` functions](./unconstrained.md) - -You can declare an Oracle through the `#[oracle()]` flag. Example: - -```rust -#[oracle(get_number_sequence)] -unconstrained fn get_number_sequence(_size: Field) -> [Field] {} -``` diff --git a/noir/docs/processed-docs/noir/concepts/shadowing.md b/noir/docs/processed-docs/noir/concepts/shadowing.md deleted file mode 100644 index 5ce6130d201..00000000000 --- a/noir/docs/processed-docs/noir/concepts/shadowing.md +++ /dev/null @@ -1,44 +0,0 @@ ---- -title: Shadowing -sidebar_position: 12 ---- - -Noir allows for inheriting variables' values and re-declaring them with the same name similar to Rust, known as shadowing. - -For example, the following function is valid in Noir: - -```rust -fn main() { - let x = 5; - - { - let x = x * 2; - assert (x == 10); - } - - assert (x == 5); -} -``` - -In this example, a variable x is first defined with the value 5. - -The local scope that follows shadows the original x, i.e. creates a local mutable x based on the value of the original x. It is given a value of 2 times the original x. - -When we return to the main scope, x once again refers to just the original x, which stays at the value of 5. - -## Temporal mutability - -One way that shadowing is useful, in addition to ergonomics across scopes, is for temporarily mutating variables. - -```rust -fn main() { - let age = 30; - // age = age + 5; // Would error as `age` is immutable by default. - - let mut age = age + 5; // Temporarily mutates `age` with a new value. - - let age = age; // Locks `age`'s mutability again. - - assert (age == 35); -} -``` diff --git a/noir/docs/processed-docs/noir/concepts/traits.md b/noir/docs/processed-docs/noir/concepts/traits.md deleted file mode 100644 index ef1445a5907..00000000000 --- a/noir/docs/processed-docs/noir/concepts/traits.md +++ /dev/null @@ -1,389 +0,0 @@ ---- -title: Traits -description: - Traits in Noir can be used to abstract out a common interface for functions across - several data types. -keywords: [noir programming language, traits, interfaces, generic, protocol] -sidebar_position: 14 ---- - -## Overview - -Traits in Noir are a useful abstraction similar to interfaces or protocols in other languages. Each trait defines -the interface of several methods contained within the trait. Types can then implement this trait by providing -implementations for these methods. For example in the program: - -```rust -struct Rectangle { - width: Field, - height: Field, -} - -impl Rectangle { - fn area(self) -> Field { - self.width * self.height - } -} - -fn log_area(r: Rectangle) { - println(r.area()); -} -``` - -We have a function `log_area` to log the area of a `Rectangle`. Now how should we change the program if we want this -function to work on `Triangle`s as well?: - -```rust -struct Triangle { - width: Field, - height: Field, -} - -impl Triangle { - fn area(self) -> Field { - self.width * self.height / 2 - } -} -``` - -Making `log_area` generic over all types `T` would be invalid since not all types have an `area` method. Instead, we can -introduce a new `Area` trait and make `log_area` generic over all types `T` that implement `Area`: - -```rust -trait Area { - fn area(self) -> Field; -} - -fn log_area(shape: T) where T: Area { - println(shape.area()); -} -``` - -We also need to explicitly implement `Area` for `Rectangle` and `Triangle`. We can do that by changing their existing -impls slightly. Note that the parameter types and return type of each of our `area` methods must match those defined -by the `Area` trait. - -```rust -impl Area for Rectangle { - fn area(self) -> Field { - self.width * self.height - } -} - -impl Area for Triangle { - fn area(self) -> Field { - self.width * self.height / 2 - } -} -``` - -Now we have a working program that is generic over any type of Shape that is used! Others can even use this program -as a library with their own types - such as `Circle` - as long as they also implement `Area` for these types. - -## Where Clauses - -As seen in `log_area` above, when we want to create a function or method that is generic over any type that implements -a trait, we can add a where clause to the generic function. - -```rust -fn log_area(shape: T) where T: Area { - println(shape.area()); -} -``` - -It is also possible to apply multiple trait constraints on the same variable at once by combining traits with the `+` -operator. Similarly, we can have multiple trait constraints by separating each with a comma: - -```rust -fn foo(elements: [T], thing: U) where - T: Default + Add + Eq, - U: Bar, -{ - let mut sum = T::default(); - - for element in elements { - sum += element; - } - - if sum == T::default() { - thing.bar(); - } -} -``` - -## Generic Implementations - -You can add generics to a trait implementation by adding the generic list after the `impl` keyword: - -```rust -trait Second { - fn second(self) -> Field; -} - -impl Second for (T, Field) { - fn second(self) -> Field { - self.1 - } -} -``` - -You can also implement a trait for every type this way: - -```rust -trait Debug { - fn debug(self); -} - -impl Debug for T { - fn debug(self) { - println(self); - } -} - -fn main() { - 1.debug(); -} -``` - -### Generic Trait Implementations With Where Clauses - -Where clauses can also be placed on trait implementations themselves to restrict generics in a similar way. -For example, while `impl Foo for T` implements the trait `Foo` for every type, `impl Foo for T where T: Bar` -will implement `Foo` only for types that also implement `Bar`. This is often used for implementing generic types. -For example, here is the implementation for array equality: - -```rust -impl Eq for [T; N] where T: Eq { - // Test if two arrays have the same elements. - // Because both arrays must have length N, we know their lengths already match. - fn eq(self, other: Self) -> bool { - let mut result = true; - - for i in 0 .. self.len() { - // The T: Eq constraint is needed to call == on the array elements here - result &= self[i] == other[i]; - } - - result - } -} -``` - -## Generic Traits - -Traits themselves can also be generic by placing the generic arguments after the trait name. These generics are in -scope of every item within the trait. - -```rust -trait Into { - // Convert `self` to type `T` - fn into(self) -> T; -} -``` - -When implementing generic traits the generic arguments of the trait must be specified. This is also true anytime -when referencing a generic trait (e.g. in a `where` clause). - -```rust -struct MyStruct { - array: [Field; 2], -} - -impl Into<[Field; 2]> for MyStruct { - fn into(self) -> [Field; 2] { - self.array - } -} - -fn as_array(x: T) -> [Field; 2] - where T: Into<[Field; 2]> -{ - x.into() -} - -fn main() { - let array = [1, 2]; - let my_struct = MyStruct { array }; - - assert_eq(as_array(my_struct), array); -} -``` - -## Trait Methods With No `self` - -A trait can contain any number of methods, each of which have access to the `Self` type which represents each type -that eventually implements the trait. Similarly, the `self` variable is available as well but is not required to be used. -For example, we can define a trait to create a default value for a type. This trait will need to return the `Self` type -but doesn't need to take any parameters: - -```rust -trait Default { - fn default() -> Self; -} -``` - -Implementing this trait can be done similarly to any other trait: - -```rust -impl Default for Field { - fn default() -> Field { - 0 - } -} - -struct MyType {} - -impl Default for MyType { - fn default() -> Field { - MyType {} - } -} -``` - -However, since there is no `self` parameter, we cannot call it via the method call syntax `object.method()`. -Instead, we'll need to refer to the function directly. This can be done either by referring to the -specific impl `MyType::default()` or referring to the trait itself `Default::default()`. In the later -case, type inference determines the impl that is selected. - -```rust -let my_struct = MyStruct::default(); - -let x: Field = Default::default(); -let result = x + Default::default(); -``` - -:::warning - -```rust -let _ = Default::default(); -``` - -If type inference cannot select which impl to use because of an ambiguous `Self` type, an impl will be -arbitrarily selected. This occurs most often when the result of a trait function call with no parameters -is unused. To avoid this, when calling a trait function with no `self` or `Self` parameters or return type, -always refer to it via the implementation type's namespace - e.g. `MyType::default()`. -This is set to change to an error in future Noir versions. - -::: - -## Default Method Implementations - -A trait can also have default implementations of its methods by giving a body to the desired functions. -Note that this body must be valid for all types that may implement the trait. As a result, the only -valid operations on `self` will be operations valid for any type or other operations on the trait itself. - -```rust -trait Numeric { - fn add(self, other: Self) -> Self; - - // Default implementation of double is (self + self) - fn double(self) -> Self { - self.add(self) - } -} -``` - -When implementing a trait with default functions, a type may choose to implement only the required functions: - -```rust -impl Numeric for Field { - fn add(self, other: Field) -> Field { - self + other - } -} -``` - -Or it may implement the optional methods as well: - -```rust -impl Numeric for u32 { - fn add(self, other: u32) -> u32 { - self + other - } - - fn double(self) -> u32 { - self * 2 - } -} -``` - -## Impl Specialization - -When implementing traits for a generic type it is possible to implement the trait for only a certain combination -of generics. This can be either as an optimization or because those specific generics are required to implement the trait. - -```rust -trait Sub { - fn sub(self, other: Self) -> Self; -} - -struct NonZero { - value: T, -} - -impl Sub for NonZero { - fn sub(self, other: Self) -> Self { - let value = self.value - other.value; - assert(value != 0); - NonZero { value } - } -} -``` - -## Overlapping Implementations - -Overlapping implementations are disallowed by Noir to ensure Noir's decision on which impl to select is never ambiguous. -This means if a trait `Foo` is already implemented -by a type `Bar` for all `T`, then we cannot also have a separate impl for `Bar` (or any other -type argument). Similarly, if there is an impl for all `T` such as `impl Debug for T`, we cannot create -any more impls to `Debug` for other types since it would be ambiguous which impl to choose for any given -method call. - -```rust -trait Trait {} - -// Previous impl defined here -impl Trait for (A, B) {} - -// error: Impl for type `(Field, Field)` overlaps with existing impl -impl Trait for (Field, Field) {} -``` - -## Trait Coherence - -Another restriction on trait implementations is coherence. This restriction ensures other crates cannot create -impls that may overlap with other impls, even if several unrelated crates are used as dependencies in the same -program. - -The coherence restriction is: to implement a trait, either the trait itself or the object type must be declared -in the crate the impl is in. - -In practice this often comes up when using types provided by libraries. If a library provides a type `Foo` that does -not implement a trait in the standard library such as `Default`, you may not `impl Default for Foo` in your own crate. -While restrictive, this prevents later issues or silent changes in the program if the `Foo` library later added its -own impl for `Default`. If you are a user of the `Foo` library in this scenario and need a trait not implemented by the -library your choices are to either submit a patch to the library or use the newtype pattern. - -### The Newtype Pattern - -The newtype pattern gets around the coherence restriction by creating a new wrapper type around the library type -that we cannot create `impl`s for. Since the new wrapper type is defined in our current crate, we can create -impls for any trait we need on it. - -```rust -struct Wrapper { - foo: dep::some_library::Foo, -} - -impl Default for Wrapper { - fn default() -> Wrapper { - Wrapper { - foo: dep::some_library::Foo::new(), - } - } -} -``` - -Since we have an impl for our own type, the behavior of this code will not change even if `some_library` is updated -to provide its own `impl Default for Foo`. The downside of this pattern is that it requires extra wrapping and -unwrapping of values when converting to and from the `Wrapper` and `Foo` types. diff --git a/noir/docs/processed-docs/noir/concepts/unconstrained.md b/noir/docs/processed-docs/noir/concepts/unconstrained.md deleted file mode 100644 index 6b3424f7993..00000000000 --- a/noir/docs/processed-docs/noir/concepts/unconstrained.md +++ /dev/null @@ -1,95 +0,0 @@ ---- -title: Unconstrained Functions -description: "Learn about what unconstrained functions in Noir are, how to use them and when you'd want to." - -keywords: [Noir programming language, unconstrained, open] -sidebar_position: 5 ---- - -Unconstrained functions are functions which do not constrain any of the included computation and allow for non-deterministic computation. - -## Why? - -Zero-knowledge (ZK) domain-specific languages (DSL) enable developers to generate ZK proofs from their programs by compiling code down to the constraints of an NP complete language (such as R1CS or PLONKish languages). However, the hard bounds of a constraint system can be very limiting to the functionality of a ZK DSL. - -Enabling a circuit language to perform unconstrained execution is a powerful tool. Said another way, unconstrained execution lets developers generate witnesses from code that does not generate any constraints. Being able to execute logic outside of a circuit is critical for both circuit performance and constructing proofs on information that is external to a circuit. - -Fetching information from somewhere external to a circuit can also be used to enable developers to improve circuit efficiency. - -A ZK DSL does not just prove computation, but proves that some computation was handled correctly. Thus, it is necessary that when we switch from performing some operation directly inside of a circuit to inside of an unconstrained environment that the appropriate constraints are still laid down elsewhere in the circuit. - -## Example - -An in depth example might help drive the point home. This example comes from the excellent [post](https://discord.com/channels/1113924620781883405/1124022445054111926/1128747641853972590) by Tom in the Noir Discord. - -Let's look at how we can optimize a function to turn a `u72` into an array of `u8`s. - -```rust -fn main(num: u72) -> pub [u8; 8] { - let mut out: [u8; 8] = [0; 8]; - for i in 0..8 { - out[i] = (num >> (56 - (i * 8)) as u72 & 0xff) as u8; - } - - out -} -``` - -``` -Total ACIR opcodes generated for language PLONKCSat { width: 3 }: 91 -Backend circuit size: 3619 -``` - -A lot of the operations in this function are optimized away by the compiler (all the bit-shifts turn into divisions by constants). However we can save a bunch of gates by casting to u8 a bit earlier. This automatically truncates the bit-shifted value to fit in a u8 which allows us to remove the XOR against 0xff. This saves us ~480 gates in total. - -```rust -fn main(num: u72) -> pub [u8; 8] { - let mut out: [u8; 8] = [0; 8]; - for i in 0..8 { - out[i] = (num >> (56 - (i * 8)) as u8; - } - - out -} -``` - -``` -Total ACIR opcodes generated for language PLONKCSat { width: 3 }: 75 -Backend circuit size: 3143 -``` - -Those are some nice savings already but we can do better. This code is all constrained so we're proving every step of calculating out using num, but we don't actually care about how we calculate this, just that it's correct. This is where brillig comes in. - -It turns out that truncating a u72 into a u8 is hard to do inside a snark, each time we do as u8 we lay down 4 ACIR opcodes which get converted into multiple gates. It's actually much easier to calculate num from out than the other way around. All we need to do is multiply each element of out by a constant and add them all together, both relatively easy operations inside a snark. - -We can then run u72_to_u8 as unconstrained brillig code in order to calculate out, then use that result in our constrained function and assert that if we were to do the reverse calculation we'd get back num. This looks a little like the below: - -```rust -fn main(num: u72) -> pub [u8; 8] { - let out = u72_to_u8(num); - - let mut reconstructed_num: u72 = 0; - for i in 0..8 { - reconstructed_num += (out[i] as u72 << (56 - (8 * i))); - } - assert(num == reconstructed_num); - out -} - -unconstrained fn u72_to_u8(num: u72) -> [u8; 8] { - let mut out: [u8; 8] = [0; 8]; - for i in 0..8 { - out[i] = (num >> (56 - (i * 8))) as u8; - } - out -} -``` - -``` -Total ACIR opcodes generated for language PLONKCSat { width: 3 }: 78 -Backend circuit size: 2902 -``` - -This ends up taking off another ~250 gates from our circuit! We've ended up with more ACIR opcodes than before but they're easier for the backend to prove (resulting in fewer gates). - -Generally we want to use brillig whenever there's something that's easy to verify but hard to compute within the circuit. For example, if you wanted to calculate a square root of a number it'll be a much better idea to calculate this in brillig and then assert that if you square the result you get back your number. diff --git a/noir/docs/processed-docs/noir/modules_packages_crates/_category_.json b/noir/docs/processed-docs/noir/modules_packages_crates/_category_.json deleted file mode 100644 index 1debcfe7675..00000000000 --- a/noir/docs/processed-docs/noir/modules_packages_crates/_category_.json +++ /dev/null @@ -1,6 +0,0 @@ -{ - "label": "Modules, Packages and Crates", - "position": 2, - "collapsible": true, - "collapsed": true -} diff --git a/noir/docs/processed-docs/noir/modules_packages_crates/crates_and_packages.md b/noir/docs/processed-docs/noir/modules_packages_crates/crates_and_packages.md deleted file mode 100644 index 760a463094c..00000000000 --- a/noir/docs/processed-docs/noir/modules_packages_crates/crates_and_packages.md +++ /dev/null @@ -1,43 +0,0 @@ ---- -title: Crates and Packages -description: Learn how to use Crates and Packages in your Noir project -keywords: [Nargo, dependencies, package management, crates, package] -sidebar_position: 0 ---- - -## Crates - -A crate is the smallest amount of code that the Noir compiler considers at a time. -Crates can contain modules, and the modules may be defined in other files that get compiled with the crate, as we’ll see in the coming sections. - -### Crate Types - -A Noir crate can come in several forms: binaries, libraries or contracts. - -#### Binaries - -_Binary crates_ are programs which you can compile to an ACIR circuit which you can then create proofs against. Each must have a function called `main` that defines the ACIR circuit which is to be proved. - -#### Libraries - -_Library crates_ don't have a `main` function and they don't compile down to ACIR. Instead they define functionality intended to be shared with multiple projects, and eventually included in a binary crate. - -#### Contracts - -Contract crates are similar to binary crates in that they compile to ACIR which you can create proofs against. They are different in that they do not have a single `main` function, but are a collection of functions to be deployed to the [Aztec network](https://aztec.network). You can learn more about the technical details of Aztec in the [monorepo](https://github.com/AztecProtocol/aztec-packages) or contract [examples](https://github.com/AztecProtocol/aztec-packages/tree/master/yarn-project/noir-contracts/contracts). - -### Crate Root - -Every crate has a root, which is the source file that the compiler starts, this is also known as the root module. The Noir compiler does not enforce any conditions on the name of the file which is the crate root, however if you are compiling via Nargo the crate root must be called `lib.nr` or `main.nr` for library or binary crates respectively. - -## Packages - -A Nargo _package_ is a collection of one of more crates that provides a set of functionality. A package must include a Nargo.toml file. - -A package _must_ contain either a library or a binary crate, but not both. - -### Differences from Cargo Packages - -One notable difference between Rust's Cargo and Noir's Nargo is that while Cargo allows a package to contain an unlimited number of binary crates and a single library crate, Nargo currently only allows a package to contain a single crate. - -In future this restriction may be lifted to allow a Nargo package to contain both a binary and library crate or multiple binary crates. diff --git a/noir/docs/processed-docs/noir/modules_packages_crates/dependencies.md b/noir/docs/processed-docs/noir/modules_packages_crates/dependencies.md deleted file mode 100644 index a37dc401b7d..00000000000 --- a/noir/docs/processed-docs/noir/modules_packages_crates/dependencies.md +++ /dev/null @@ -1,124 +0,0 @@ ---- -title: Dependencies -description: - Learn how to specify and manage dependencies in Nargo, allowing you to upload packages to GitHub - and use them easily in your project. -keywords: [Nargo, dependencies, GitHub, package management, versioning] -sidebar_position: 1 ---- - -Nargo allows you to upload packages to GitHub and use them as dependencies. - -## Specifying a dependency - -Specifying a dependency requires a tag to a specific commit and the git url to the url containing -the package. - -Currently, there are no requirements on the tag contents. If requirements are added, it would follow -semver 2.0 guidelines. - -> Note: Without a `tag` , there would be no versioning and dependencies would change each time you -> compile your project. - -For example, to add the [ecrecover-noir library](https://github.com/colinnielsen/ecrecover-noir) to your project, add it to `Nargo.toml`: - -```toml -# Nargo.toml - -[dependencies] -ecrecover = {tag = "v0.8.0", git = "https://github.com/colinnielsen/ecrecover-noir"} -``` - -If the module is in a subdirectory, you can define a subdirectory in your git repository, for example: - -```toml -# Nargo.toml - -[dependencies] -easy_private_token_contract = {tag ="v0.1.0-alpha62", git = "https://github.com/AztecProtocol/aztec-packages", directory = "yarn-project/noir-contracts/contracts/easy_private_token_contract"} -``` - -## Specifying a local dependency - -You can also specify dependencies that are local to your machine. - -For example, this file structure has a library and binary crate - -```tree -├── binary_crate -│   ├── Nargo.toml -│   └── src -│   └── main.nr -└── lib_a - ├── Nargo.toml - └── src - └── lib.nr -``` - -Inside of the binary crate, you can specify: - -```toml -# Nargo.toml - -[dependencies] -lib_a = { path = "../lib_a" } -``` - -## Importing dependencies - -You can import a dependency to a Noir file using the following syntax. For example, to import the -ecrecover-noir library and local lib_a referenced above: - -```rust -use dep::ecrecover; -use dep::lib_a; -``` - -You can also import only the specific parts of dependency that you want to use, like so: - -```rust -use dep::std::hash::sha256; -use dep::std::scalar_mul::fixed_base_embedded_curve; -``` - -Lastly, as demonstrated in the -[elliptic curve example](../standard_library/cryptographic_primitives/ec_primitives#examples), you -can import multiple items in the same line by enclosing them in curly braces: - -```rust -use dep::std::ec::tecurve::affine::{Curve, Point}; -``` - -We don't have a way to consume libraries from inside a [workspace](./workspaces) as external dependencies right now. - -Inside a workspace, these are consumed as `{ path = "../to_lib" }` dependencies in Nargo.toml. - -## Dependencies of Dependencies - -Note that when you import a dependency, you also get access to all of the dependencies of that package. - -For example, the [phy_vector](https://github.com/resurgencelabs/phy_vector) library imports an [fraction](https://github.com/resurgencelabs/fraction) library. If you're importing the phy_vector library, then you can access the functions in fractions library like so: - -```rust -use dep::phy_vector; - -fn main(x : Field, y : pub Field) { - //... - let f = phy_vector::fraction::toFraction(true, 2, 1); - //... -} -``` - -## Available Libraries - -Noir does not currently have an official package manager. You can find a list of available Noir libraries in the [awesome-noir repo here](https://github.com/noir-lang/awesome-noir#libraries). - -Some libraries that are available today include: - -- [Standard Library](https://github.com/noir-lang/noir/tree/master/noir_stdlib) - the Noir Standard Library -- [Ethereum Storage Proof Verification](https://github.com/aragonzkresearch/noir-trie-proofs) - a library that contains the primitives necessary for RLP decoding (in the form of look-up table construction) and Ethereum state and storage proof verification (or verification of any trie proof involving 32-byte long keys) -- [BigInt](https://github.com/shuklaayush/noir-bigint) - a library that provides a custom BigUint56 data type, allowing for computations on large unsigned integers -- [ECrecover](https://github.com/colinnielsen/ecrecover-noir/tree/main) - a library to verify an ECDSA signature and return the source Ethereum address -- [Sparse Merkle Tree Verifier](https://github.com/vocdoni/smtverifier-noir/tree/main) - a library for verification of sparse Merkle trees -- [Signed Int](https://github.com/resurgencelabs/signed_int) - a library for accessing a custom Signed Integer data type, allowing access to negative numbers on Noir -- [Fraction](https://github.com/resurgencelabs/fraction) - a library for accessing fractional number data type in Noir, allowing results that aren't whole numbers diff --git a/noir/docs/processed-docs/noir/modules_packages_crates/modules.md b/noir/docs/processed-docs/noir/modules_packages_crates/modules.md deleted file mode 100644 index ae822a1cff4..00000000000 --- a/noir/docs/processed-docs/noir/modules_packages_crates/modules.md +++ /dev/null @@ -1,105 +0,0 @@ ---- -title: Modules -description: - Learn how to organize your files using modules in Noir, following the same convention as Rust's - module system. Examples included. -keywords: [Noir, Rust, modules, organizing files, sub-modules] -sidebar_position: 2 ---- - -Noir's module system follows the same convention as the _newer_ version of Rust's module system. - -## Purpose of Modules - -Modules are used to organize files. Without modules all of your code would need to live in a single -file. In Noir, the compiler does not automatically scan all of your files to detect modules. This -must be done explicitly by the developer. - -## Examples - -### Importing a module in the crate root - -Filename : `src/main.nr` - -```rust -mod foo; - -fn main() { - foo::hello_world(); -} -``` - -Filename : `src/foo.nr` - -```rust -fn from_foo() {} -``` - -In the above snippet, the crate root is the `src/main.nr` file. The compiler sees the module -declaration `mod foo` which prompts it to look for a foo.nr file. - -Visually this module hierarchy looks like the following : - -``` -crate - ├── main - │ - └── foo - └── from_foo - -``` - -### Importing a module throughout the tree - -All modules are accessible from the `crate::` namespace. - -``` -crate - ├── bar - ├── foo - └── main - -``` - -In the above snippet, if `bar` would like to use functions in `foo`, it can do so by `use crate::foo::function_name`. - -### Sub-modules - -Filename : `src/main.nr` - -```rust -mod foo; - -fn main() { - foo::from_foo(); -} -``` - -Filename : `src/foo.nr` - -```rust -mod bar; -fn from_foo() {} -``` - -Filename : `src/foo/bar.nr` - -```rust -fn from_bar() {} -``` - -In the above snippet, we have added an extra module to the module tree; `bar`. `bar` is a submodule -of `foo` hence we declare bar in `foo.nr` with `mod bar`. Since `foo` is not the crate root, the -compiler looks for the file associated with the `bar` module in `src/foo/bar.nr` - -Visually the module hierarchy looks as follows: - -``` -crate - ├── main - │ - └── foo - ├── from_foo - └── bar - └── from_bar -``` diff --git a/noir/docs/processed-docs/noir/modules_packages_crates/workspaces.md b/noir/docs/processed-docs/noir/modules_packages_crates/workspaces.md deleted file mode 100644 index 67a1dafa372..00000000000 --- a/noir/docs/processed-docs/noir/modules_packages_crates/workspaces.md +++ /dev/null @@ -1,40 +0,0 @@ ---- -title: Workspaces -sidebar_position: 3 ---- - -Workspaces are a feature of nargo that allow you to manage multiple related Noir packages in a single repository. A workspace is essentially a group of related projects that share common build output directories and configurations. - -Each Noir project (with it's own Nargo.toml file) can be thought of as a package. Each package is expected to contain exactly one "named circuit", being the "name" defined in Nargo.toml with the program logic defined in `./src/main.nr`. - -For a project with the following structure: - -```tree -├── crates -│   ├── a -│   │   ├── Nargo.toml -│   │   └── src -│   │   └── main.nr -│   └── b -│   ├── Nargo.toml -│   └── src -│   └── main.nr -├── Nargo.toml -└── Prover.toml -``` - -You can define a workspace in Nargo.toml like so: - -```toml -[workspace] -members = ["crates/a", "crates/b"] -default-member = "crates/a" -``` - -`members` indicates which packages are included in the workspace. As such, all member packages of a workspace will be processed when the `--workspace` flag is used with various commands or if a `default-member` is not specified. - -`default-member` indicates which package various commands process by default. - -Libraries can be defined in a workspace. Inside a workspace, these are consumed as `{ path = "../to_lib" }` dependencies in Nargo.toml. - -Inside a workspace, these are consumed as `{ path = "../to_lib" }` dependencies in Nargo.toml. diff --git a/noir/docs/processed-docs/noir/standard_library/_category_.json b/noir/docs/processed-docs/noir/standard_library/_category_.json deleted file mode 100644 index af04c0933fd..00000000000 --- a/noir/docs/processed-docs/noir/standard_library/_category_.json +++ /dev/null @@ -1,6 +0,0 @@ -{ - "label": "Standard Library", - "position": 1, - "collapsible": true, - "collapsed": true -} diff --git a/noir/docs/processed-docs/noir/standard_library/black_box_fns.md b/noir/docs/processed-docs/noir/standard_library/black_box_fns.md deleted file mode 100644 index 6b22d0e7466..00000000000 --- a/noir/docs/processed-docs/noir/standard_library/black_box_fns.md +++ /dev/null @@ -1,31 +0,0 @@ ---- -title: Black Box Functions -description: Black box functions are functions in Noir that rely on backends implementing support for specialized constraints. -keywords: [noir, black box functions] ---- - -Black box functions are functions in Noir that rely on backends implementing support for specialized constraints. This makes certain zk-snark unfriendly computations cheaper than if they were implemented in Noir. - -The ACVM spec defines a set of blackbox functions which backends will be expected to implement. This allows backends to use optimized implementations of these constraints if they have them, however they may also fallback to less efficient naive implementations if not. - -## Function list - -Here is a list of the current black box functions: - -- [SHA256](./cryptographic_primitives/hashes#sha256) -- [Schnorr signature verification](./cryptographic_primitives/schnorr) -- [Blake2s](./cryptographic_primitives/hashes#blake2s) -- [Blake3](./cryptographic_primitives/hashes#blake2s) -- [Pedersen Hash](./cryptographic_primitives/hashes#pedersen_hash) -- [Pedersen Commitment](./cryptographic_primitives/hashes#pedersen_commitment) -- [ECDSA signature verification](./cryptographic_primitives/ecdsa_sig_verification) -- [Fixed base scalar multiplication](./cryptographic_primitives/scalar) -- AND -- XOR -- RANGE -- [Keccak256](./cryptographic_primitives/hashes#keccak256) -- [Recursive proof verification](./recursion) - -Most black box functions are included as part of the Noir standard library, however `AND`, `XOR` and `RANGE` are used as part of the Noir language syntax. For instance, using the bitwise operator `&` will invoke the `AND` black box function. - -You can view the black box functions defined in the ACVM code [here](https://github.com/noir-lang/noir/blob/master/acvm-repo/acir/src/circuit/black_box_functions.rs). diff --git a/noir/docs/processed-docs/noir/standard_library/cryptographic_primitives/_category_.json b/noir/docs/processed-docs/noir/standard_library/cryptographic_primitives/_category_.json deleted file mode 100644 index 5d694210bbf..00000000000 --- a/noir/docs/processed-docs/noir/standard_library/cryptographic_primitives/_category_.json +++ /dev/null @@ -1,5 +0,0 @@ -{ - "position": 0, - "collapsible": true, - "collapsed": true -} diff --git a/noir/docs/processed-docs/noir/standard_library/cryptographic_primitives/ec_primitives.md b/noir/docs/processed-docs/noir/standard_library/cryptographic_primitives/ec_primitives.md deleted file mode 100644 index d2b42d67b7c..00000000000 --- a/noir/docs/processed-docs/noir/standard_library/cryptographic_primitives/ec_primitives.md +++ /dev/null @@ -1,102 +0,0 @@ ---- -title: Elliptic Curve Primitives -keywords: [cryptographic primitives, Noir project] -sidebar_position: 4 ---- - -Data structures and methods on them that allow you to carry out computations involving elliptic -curves over the (mathematical) field corresponding to `Field`. For the field currently at our -disposal, applications would involve a curve embedded in BN254, e.g. the -[Baby Jubjub curve](https://eips.ethereum.org/EIPS/eip-2494). - -## Data structures - -### Elliptic curve configurations - -(`std::ec::{tecurve,montcurve,swcurve}::{affine,curvegroup}::Curve`), i.e. the specific elliptic -curve you want to use, which would be specified using any one of the methods -`std::ec::{tecurve,montcurve,swcurve}::{affine,curvegroup}::new` which take the coefficients in the -defining equation together with a generator point as parameters. You can find more detail in the -comments in -[`noir_stdlib/src/ec.nr`](https://github.com/noir-lang/noir/blob/master/noir_stdlib/src/ec.nr), but -the gist of it is that the elliptic curves of interest are usually expressed in one of the standard -forms implemented here (Twisted Edwards, Montgomery and Short Weierstraß), and in addition to that, -you could choose to use `affine` coordinates (Cartesian coordinates - the usual (x,y) - possibly -together with a point at infinity) or `curvegroup` coordinates (some form of projective coordinates -requiring more coordinates but allowing for more efficient implementations of elliptic curve -operations). Conversions between all of these forms are provided, and under the hood these -conversions are done whenever an operation is more efficient in a different representation (or a -mixed coordinate representation is employed). - -### Points - -(`std::ec::{tecurve,montcurve,swcurve}::{affine,curvegroup}::Point`), i.e. points lying on the -elliptic curve. For a curve configuration `c` and a point `p`, it may be checked that `p` -does indeed lie on `c` by calling `c.contains(p1)`. - -## Methods - -(given a choice of curve representation, e.g. use `std::ec::tecurve::affine::Curve` and use -`std::ec::tecurve::affine::Point`) - -- The **zero element** is given by `Point::zero()`, and we can verify whether a point `p: Point` is - zero by calling `p.is_zero()`. -- **Equality**: Points `p1: Point` and `p2: Point` may be checked for equality by calling - `p1.eq(p2)`. -- **Addition**: For `c: Curve` and points `p1: Point` and `p2: Point` on the curve, adding these two - points is accomplished by calling `c.add(p1,p2)`. -- **Negation**: For a point `p: Point`, `p.negate()` is its negation. -- **Subtraction**: For `c` and `p1`, `p2` as above, subtracting `p2` from `p1` is accomplished by - calling `c.subtract(p1,p2)`. -- **Scalar multiplication**: For `c` as above, `p: Point` a point on the curve and `n: Field`, - scalar multiplication is given by `c.mul(n,p)`. If instead `n :: [u1; N]`, i.e. `n` is a bit - array, the `bit_mul` method may be used instead: `c.bit_mul(n,p)` -- **Multi-scalar multiplication**: For `c` as above and arrays `n: [Field; N]` and `p: [Point; N]`, - multi-scalar multiplication is given by `c.msm(n,p)`. -- **Coordinate representation conversions**: The `into_group` method converts a point or curve - configuration in the affine representation to one in the CurveGroup representation, and - `into_affine` goes in the other direction. -- **Curve representation conversions**: `tecurve` and `montcurve` curves and points are equivalent - and may be converted between one another by calling `into_montcurve` or `into_tecurve` on their - configurations or points. `swcurve` is more general and a curve c of one of the other two types - may be converted to this representation by calling `c.into_swcurve()`, whereas a point `p` lying - on the curve given by `c` may be mapped to its corresponding `swcurve` point by calling - `c.map_into_swcurve(p)`. -- **Map-to-curve methods**: The Elligator 2 method of mapping a field element `n: Field` into a - `tecurve` or `montcurve` with configuration `c` may be called as `c.elligator2_map(n)`. For all of - the curve configurations, the SWU map-to-curve method may be called as `c.swu_map(z,n)`, where - `z: Field` depends on `Field` and `c` and must be chosen by the user (the conditions it needs to - satisfy are specified in the comments - [here](https://github.com/noir-lang/noir/blob/master/noir_stdlib/src/ec.nr)). - -## Examples - -The -[ec_baby_jubjub test](https://github.com/noir-lang/noir/blob/master/test_programs/compile_success_empty/ec_baby_jubjub/src/main.nr) -illustrates all of the above primitives on various forms of the Baby Jubjub curve. A couple of more -interesting examples in Noir would be: - -Public-key cryptography: Given an elliptic curve and a 'base point' on it, determine the public key -from the private key. This is a matter of using scalar multiplication. In the case of Baby Jubjub, -for example, this code would do: - -```rust -use dep::std::ec::tecurve::affine::{Curve, Point}; - -fn bjj_pub_key(priv_key: Field) -> Point -{ - - let bjj = Curve::new(168700, 168696, G::new(995203441582195749578291179787384436505546430278305826713579947235728471134,5472060717959818805561601436314318772137091100104008585924551046643952123905)); - - let base_pt = Point::new(5299619240641551281634865583518297030282874472190772894086521144482721001553, 16950150798460657717958625567821834550301663161624707787222815936182638968203); - - bjj.mul(priv_key,base_pt) -} -``` - -This would come in handy in a Merkle proof. - -- EdDSA signature verification: This is a matter of combining these primitives with a suitable hash - function. See - [feat(stdlib): EdDSA sig verification noir#1136](https://github.com/noir-lang/noir/pull/1136) for - the case of Baby Jubjub and the Poseidon hash function. diff --git a/noir/docs/processed-docs/noir/standard_library/cryptographic_primitives/ecdsa_sig_verification.mdx b/noir/docs/processed-docs/noir/standard_library/cryptographic_primitives/ecdsa_sig_verification.mdx deleted file mode 100644 index 4bf09cef178..00000000000 --- a/noir/docs/processed-docs/noir/standard_library/cryptographic_primitives/ecdsa_sig_verification.mdx +++ /dev/null @@ -1,60 +0,0 @@ ---- -title: ECDSA Signature Verification -description: Learn about the cryptographic primitives regarding ECDSA over the secp256k1 and secp256r1 curves -keywords: [cryptographic primitives, Noir project, ecdsa, secp256k1, secp256r1, signatures] -sidebar_position: 3 ---- - -import BlackBoxInfo from '@site/src/components/Notes/_blackbox.mdx'; - -Noir supports ECDSA signatures verification over the secp256k1 and secp256r1 curves. - -## ecdsa_secp256k1::verify_signature - -Verifier for ECDSA Secp256k1 signatures - -```rust title="ecdsa_secp256k1" showLineNumbers -pub fn verify_signature( - public_key_x: [u8; 32], - public_key_y: [u8; 32], - signature: [u8; 64], - message_hash: [u8; N] -) -> bool -``` -> Source code: noir_stdlib/src/ecdsa_secp256k1.nr#L2-L9 - - -example: - -```rust -fn main(hashed_message : [u8;32], pub_key_x : [u8;32], pub_key_y : [u8;32], signature : [u8;64]) { - let valid_signature = std::ecdsa_secp256k1::verify_signature(pub_key_x, pub_key_y, signature, hashed_message); - assert(valid_signature); -} -``` - -## ecdsa_secp256r1::verify_signature - -Verifier for ECDSA Secp256r1 signatures - -```rust title="ecdsa_secp256r1" showLineNumbers -pub fn verify_signature( - public_key_x: [u8; 32], - public_key_y: [u8; 32], - signature: [u8; 64], - message_hash: [u8; N] -) -> bool -``` -> Source code: noir_stdlib/src/ecdsa_secp256r1.nr#L2-L9 - - -example: - -```rust -fn main(hashed_message : [u8;32], pub_key_x : [u8;32], pub_key_y : [u8;32], signature : [u8;64]) { - let valid_signature = std::ecdsa_secp256r1::verify_signature(pub_key_x, pub_key_y, signature, hashed_message); - assert(valid_signature); -} -``` - - diff --git a/noir/docs/processed-docs/noir/standard_library/cryptographic_primitives/eddsa.mdx b/noir/docs/processed-docs/noir/standard_library/cryptographic_primitives/eddsa.mdx deleted file mode 100644 index a9c10da6c06..00000000000 --- a/noir/docs/processed-docs/noir/standard_library/cryptographic_primitives/eddsa.mdx +++ /dev/null @@ -1,18 +0,0 @@ ---- -title: EdDSA Verification -description: Learn about the cryptographic primitives regarding EdDSA -keywords: [cryptographic primitives, Noir project, eddsa, signatures] -sidebar_position: 5 ---- - -import BlackBoxInfo from '@site/src/components/Notes/_blackbox.mdx'; - -## eddsa::eddsa_poseidon_verify - -Verifier for EdDSA signatures - -```rust -fn eddsa_poseidon_verify(public_key_x : Field, public_key_y : Field, signature_s: Field, signature_r8_x: Field, signature_r8_y: Field, message: Field) -> bool -``` - - diff --git a/noir/docs/processed-docs/noir/standard_library/cryptographic_primitives/hashes.mdx b/noir/docs/processed-docs/noir/standard_library/cryptographic_primitives/hashes.mdx deleted file mode 100644 index 730b6d4117f..00000000000 --- a/noir/docs/processed-docs/noir/standard_library/cryptographic_primitives/hashes.mdx +++ /dev/null @@ -1,234 +0,0 @@ ---- -title: Hash methods -description: - Learn about the cryptographic primitives ready to use for any Noir project, including sha256, - blake2s, pedersen, mimc_bn254 and mimc -keywords: - [cryptographic primitives, Noir project, sha256, blake2s, pedersen, mimc_bn254, mimc, hash] -sidebar_position: 0 ---- - -import BlackBoxInfo from '@site/src/components/Notes/_blackbox.mdx'; - -## sha256 - -Given an array of bytes, returns the resulting sha256 hash. - -```rust title="sha256" showLineNumbers -pub fn sha256(input: [u8; N]) -> [u8; 32] -``` -> Source code: noir_stdlib/src/hash.nr#L5-L7 - - -example: - -```rust -fn main() { - let x = [163, 117, 178, 149]; // some random bytes - let hash = std::hash::sha256(x); -} -``` - - - -## blake2s - -Given an array of bytes, returns an array with the Blake2 hash - -```rust title="blake2s" showLineNumbers -pub fn blake2s(input: [u8; N]) -> [u8; 32] -``` -> Source code: noir_stdlib/src/hash.nr#L11-L13 - - -example: - -```rust -fn main() { - let x = [163, 117, 178, 149]; // some random bytes - let hash = std::hash::blake2s(x); -} -``` - - - -## blake3 - -Given an array of bytes, returns an array with the Blake3 hash - -```rust title="blake3" showLineNumbers -pub fn blake3(input: [u8; N]) -> [u8; 32] -``` -> Source code: noir_stdlib/src/hash.nr#L17-L19 - - -example: - -```rust -fn main() { - let x = [163, 117, 178, 149]; // some random bytes - let hash = std::hash::blake3(x); -} -``` - - - -## pedersen_hash - -Given an array of Fields, returns the Pedersen hash. - -```rust title="pedersen_hash" showLineNumbers -pub fn pedersen_hash(input: [Field; N]) -> Field -``` -> Source code: noir_stdlib/src/hash.nr#L42-L44 - - -example: - -```rust title="pedersen-hash" showLineNumbers -use dep::std; - -fn main(x: Field, y: Field, expected_hash: Field) { - let hash = std::hash::pedersen_hash([x, y]); - assert_eq(hash, expected_hash); -} -``` -> Source code: test_programs/execution_success/pedersen_hash/src/main.nr#L1-L8 - - - - - -## pedersen_commitment - -Given an array of Fields, returns the Pedersen commitment. - -```rust title="pedersen_commitment" showLineNumbers -struct PedersenPoint { - x : Field, - y : Field, -} - -pub fn pedersen_commitment(input: [Field; N]) -> PedersenPoint -``` -> Source code: noir_stdlib/src/hash.nr#L22-L29 - - -example: - -```rust title="pedersen-commitment" showLineNumbers -use dep::std; - -fn main(x: Field, y: Field, expected_commitment: std::hash::PedersenPoint) { - let commitment = std::hash::pedersen_commitment([x, y]); - assert_eq(commitment.x, expected_commitment.x); - assert_eq(commitment.y, expected_commitment.y); -} -``` -> Source code: test_programs/execution_success/pedersen_commitment/src/main.nr#L1-L9 - - - - -## keccak256 - -Given an array of bytes (`u8`), returns the resulting keccak hash as an array of 32 bytes -(`[u8; 32]`). Specify a message_size to hash only the first `message_size` bytes -of the input. - -```rust title="keccak256" showLineNumbers -pub fn keccak256(input: [u8; N], message_size: u32) -> [u8; 32] -``` -> Source code: noir_stdlib/src/hash.nr#L67-L69 - - -example: - -```rust title="keccak256" showLineNumbers -use dep::std; - -fn main(x: Field, result: [u8; 32]) { - // We use the `as` keyword here to denote the fact that we want to take just the first byte from the x Field - // The padding is taken care of by the program - let digest = std::hash::keccak256([x as u8], 1); - assert(digest == result); - - //#1399: variable message size - let message_size = 4; - let hash_a = std::hash::keccak256([1, 2, 3, 4], message_size); - let hash_b = std::hash::keccak256([1, 2, 3, 4, 0, 0, 0, 0], message_size); - - assert(hash_a == hash_b); - - let message_size_big = 8; - let hash_c = std::hash::keccak256([1, 2, 3, 4, 0, 0, 0, 0], message_size_big); - - assert(hash_a != hash_c); -} -``` -> Source code: test_programs/execution_success/keccak256/src/main.nr#L1-L22 - - - - -## poseidon - -Given an array of Fields, returns a new Field with the Poseidon Hash. Mind that you need to specify -how many inputs are there to your Poseidon function. - -```rust -// example for hash_1, hash_2 accepts an array of length 2, etc -fn hash_1(input: [Field; 1]) -> Field -``` - -example: - -```rust title="poseidon" showLineNumbers -use dep::std::hash::poseidon; - -fn main(x1: [Field; 2], y1: pub Field, x2: [Field; 4], y2: pub Field) { - let hash1 = poseidon::bn254::hash_2(x1); - assert(hash1 == y1); - - let hash2 = poseidon::bn254::hash_4(x2); - assert(hash2 == y2); -} -``` -> Source code: test_programs/execution_success/poseidon_bn254_hash/src/main.nr#L1-L11 - - -## mimc_bn254 and mimc - -`mimc_bn254` is `mimc`, but with hardcoded parameters for the BN254 curve. You can use it by -providing an array of Fields, and it returns a Field with the hash. You can use the `mimc` method if -you're willing to input your own constants: - -```rust -fn mimc(x: Field, k: Field, constants: [Field; N], exp : Field) -> Field -``` - -otherwise, use the `mimc_bn254` method: - -```rust -fn mimc_bn254(array: [Field; N]) -> Field -``` - -example: - -```rust - -fn main() { - let x = [163, 117, 178, 149]; // some random bytes - let hash = std::hash::mimc::mimc_bn254(x); -} -``` - -## hash_to_field - -```rust -fn hash_to_field(_input : [Field; N]) -> Field {} -``` - -Calculates the `blake2s` hash of the inputs and returns the hash modulo the field modulus to return -a value which can be represented as a `Field`. - diff --git a/noir/docs/processed-docs/noir/standard_library/cryptographic_primitives/index.md b/noir/docs/processed-docs/noir/standard_library/cryptographic_primitives/index.md deleted file mode 100644 index 650f30165d5..00000000000 --- a/noir/docs/processed-docs/noir/standard_library/cryptographic_primitives/index.md +++ /dev/null @@ -1,14 +0,0 @@ ---- -title: Cryptographic Primitives -description: - Learn about the cryptographic primitives ready to use for any Noir project -keywords: - [ - cryptographic primitives, - Noir project, - ] ---- - -The Noir team is progressively adding new cryptographic primitives to the standard library. Reach out for news or if you would be interested in adding more of these calculations in Noir. - -Some methods are available thanks to the Aztec backend, not being performed using Noir. When using other backends, these methods may or may not be supplied. diff --git a/noir/docs/processed-docs/noir/standard_library/cryptographic_primitives/scalar.mdx b/noir/docs/processed-docs/noir/standard_library/cryptographic_primitives/scalar.mdx deleted file mode 100644 index df411ca5443..00000000000 --- a/noir/docs/processed-docs/noir/standard_library/cryptographic_primitives/scalar.mdx +++ /dev/null @@ -1,33 +0,0 @@ ---- -title: Scalar multiplication -description: See how you can perform scalar multiplications over a fixed base in Noir -keywords: [cryptographic primitives, Noir project, scalar multiplication] -sidebar_position: 1 ---- - -import BlackBoxInfo from '@site/src/components/Notes/_blackbox.mdx'; - -## scalar_mul::fixed_base_embedded_curve - -Performs scalar multiplication over the embedded curve whose coordinates are defined by the -configured noir field. For the BN254 scalar field, this is BabyJubJub or Grumpkin. - -```rust title="fixed_base_embedded_curve" showLineNumbers -pub fn fixed_base_embedded_curve( - low: Field, - high: Field -) -> [Field; 2] -``` -> Source code: noir_stdlib/src/scalar_mul.nr#L27-L32 - - -example - -```rust -fn main(x : Field) { - let scal = std::scalar_mul::fixed_base_embedded_curve(x); - println(scal); -} -``` - - diff --git a/noir/docs/processed-docs/noir/standard_library/cryptographic_primitives/schnorr.mdx b/noir/docs/processed-docs/noir/standard_library/cryptographic_primitives/schnorr.mdx deleted file mode 100644 index ae12e6c12dc..00000000000 --- a/noir/docs/processed-docs/noir/standard_library/cryptographic_primitives/schnorr.mdx +++ /dev/null @@ -1,45 +0,0 @@ ---- -title: Schnorr Signatures -description: Learn how you can verify Schnorr signatures using Noir -keywords: [cryptographic primitives, Noir project, schnorr, signatures] -sidebar_position: 2 ---- - -import BlackBoxInfo from '@site/src/components/Notes/_blackbox.mdx'; - -## schnorr::verify_signature - -Verifier for Schnorr signatures over the embedded curve (for BN254 it is Grumpkin). - -```rust title="schnorr_verify" showLineNumbers -pub fn verify_signature( - public_key_x: Field, - public_key_y: Field, - signature: [u8; 64], - message: [u8; N] -) -> bool -``` -> Source code: noir_stdlib/src/schnorr.nr#L2-L9 - - -where `_signature` can be generated like so using the npm package -[@noir-lang/barretenberg](https://www.npmjs.com/package/@noir-lang/barretenberg) - -```js -const { BarretenbergWasm } = require('@noir-lang/barretenberg/dest/wasm'); -const { Schnorr } = require('@noir-lang/barretenberg/dest/crypto/schnorr'); - -... - -const barretenberg = await BarretenbergWasm.new(); -const schnorr = new Schnorr(barretenberg); -const pubKey = schnorr.computePublicKey(privateKey); -const message = ... -const signature = Array.from( - schnorr.constructSignature(hash, privateKey).toBuffer() -); - -... -``` - - diff --git a/noir/docs/processed-docs/noir/standard_library/logging.md b/noir/docs/processed-docs/noir/standard_library/logging.md deleted file mode 100644 index db75ef9f86f..00000000000 --- a/noir/docs/processed-docs/noir/standard_library/logging.md +++ /dev/null @@ -1,78 +0,0 @@ ---- -title: Logging -description: - Learn how to use the println statement for debugging in Noir with this tutorial. Understand the - basics of logging in Noir and how to implement it in your code. -keywords: - [ - noir logging, - println statement, - print statement, - debugging in noir, - noir std library, - logging tutorial, - basic logging in noir, - noir logging implementation, - noir debugging techniques, - rust, - ] ---- - -The standard library provides two familiar statements you can use: `println` and `print`. Despite being a limited implementation of rust's `println!` and `print!` macros, these constructs can be useful for debugging. - -You can print the output of both statements in your Noir code by using the `nargo execute` command or the `--show-output` flag when using `nargo test` (provided there are print statements in your tests). - -It is recommended to use `nargo execute` if you want to debug failing constraints with `println` or `print` statements. This is due to every input in a test being a constant rather than a witness, so we issue an error during compilation while we only print during execution (which comes after compilation). Neither `println`, nor `print` are callable for failed constraints caught at compile time. - -Both `print` and `println` are generic functions which can work on integers, fields, strings, and even structs or expressions. Note however, that slices are currently unsupported. For example: - -```rust -struct Person { - age: Field, - height: Field, -} - -fn main(age: Field, height: Field) { - let person = Person { - age: age, - height: height, - }; - println(person); - println(age + height); - println("Hello world!"); -} -``` - -You can print different types in the same statement (including strings) with a type called `fmtstr`. It can be specified in the same way as a normal string, just prepended with an "f" character: - -```rust - let fmt_str = f"i: {i}, j: {j}"; - println(fmt_str); - - let s = myStruct { y: x, x: y }; - println(s); - - println(f"i: {i}, s: {s}"); - - println(x); - println([x, y]); - - let foo = fooStruct { my_struct: s, foo: 15 }; - println(f"s: {s}, foo: {foo}"); - - println(15); // prints 0x0f, implicit Field - println(-1 as u8); // prints 255 - println(-1 as i8); // prints -1 -``` - -Examples shown above are interchangeable between the two `print` statements: - -```rust -let person = Person { age : age, height : height }; - -println(person); -print(person); - -println("Hello world!"); // Prints with a newline at the end of the input -print("Hello world!"); // Prints the input and keeps cursor on the same line -``` diff --git a/noir/docs/processed-docs/noir/standard_library/merkle_trees.md b/noir/docs/processed-docs/noir/standard_library/merkle_trees.md deleted file mode 100644 index fa488677884..00000000000 --- a/noir/docs/processed-docs/noir/standard_library/merkle_trees.md +++ /dev/null @@ -1,58 +0,0 @@ ---- -title: Merkle Trees -description: Learn about Merkle Trees in Noir with this tutorial. Explore the basics of computing a merkle root using a proof, with examples. -keywords: - [ - Merkle trees in Noir, - Noir programming language, - check membership, - computing root from leaf, - Noir Merkle tree implementation, - Merkle tree tutorial, - Merkle tree code examples, - Noir libraries, - pedersen hash., - ] ---- - -## compute_merkle_root - -Returns the root of the tree from the provided leaf and its hash path, using a [Pedersen hash](./cryptographic_primitives/hashes.mdx#pedersen_hash). - -```rust -fn compute_merkle_root(leaf : Field, index : Field, hash_path: [Field]) -> Field -``` - -example: - -```rust -/** - // these values are for this example only - index = "0" - priv_key = "0x000000000000000000000000000000000000000000000000000000616c696365" - secret = "0x1929ea3ab8d9106a899386883d9428f8256cfedb3c4f6b66bf4aa4d28a79988f" - note_hash_path = [ - "0x1e61bdae0f027b1b2159e1f9d3f8d00fa668a952dddd822fda80dc745d6f65cc", - "0x0e4223f3925f98934393c74975142bd73079ab0621f4ee133cee050a3c194f1a", - "0x2fd7bb412155bf8693a3bd2a3e7581a679c95c68a052f835dddca85fa1569a40" - ] - */ -fn main(index: Field, priv_key: Field, secret: Field, note_hash_path: [Field; 3]) { - - let pubkey = std::scalar_mul::fixed_base_embedded_curve(priv_key); - let pubkey_x = pubkey[0]; - let pubkey_y = pubkey[1]; - let note_commitment = std::hash::pedersen([pubkey_x, pubkey_y, secret]); - - let root = std::merkle::compute_merkle_root(note_commitment[0], index, note_hash_path); - println(root); -} -``` - -To check merkle tree membership: - -1. Include a merkle root as a program input. -2. Compute the merkle root of a given leaf, index and hash path. -3. Assert the merkle roots are equal. - -For more info about merkle trees, see the Wikipedia [page](https://en.wikipedia.org/wiki/Merkle_tree). diff --git a/noir/docs/processed-docs/noir/standard_library/options.md b/noir/docs/processed-docs/noir/standard_library/options.md deleted file mode 100644 index 970c9cfbf11..00000000000 --- a/noir/docs/processed-docs/noir/standard_library/options.md +++ /dev/null @@ -1,97 +0,0 @@ ---- -title: Option Type ---- - -The `Option` type is a way to express that a value might be present (`Some(T))` or absent (`None`). It's a safer way to handle potential absence of values, compared to using nulls in many other languages. - -```rust -struct Option { - None, - Some(T), -} -``` - -The `Option` type, already imported into your Noir program, can be used directly: - -```rust -fn main() { - let none = Option::none(); - let some = Option::some(3); -} -``` - -See [this test](https://github.com/noir-lang/noir/blob/5cbfb9c4a06c8865c98ff2b594464b037d821a5c/crates/nargo_cli/tests/test_data/option/src/main.nr) for a more comprehensive set of examples of each of the methods described below. - -## Methods - -### none - -Constructs a none value. - -### some - -Constructs a some wrapper around a given value. - -### is_none - -Returns true if the Option is None. - -### is_some - -Returns true of the Option is Some. - -### unwrap - -Asserts `self.is_some()` and returns the wrapped value. - -### unwrap_unchecked - -Returns the inner value without asserting `self.is_some()`. This method can be useful within an if condition when we already know that `option.is_some()`. If the option is None, there is no guarantee what value will be returned, only that it will be of type T for an `Option`. - -### unwrap_or - -Returns the wrapped value if `self.is_some()`. Otherwise, returns the given default value. - -### unwrap_or_else - -Returns the wrapped value if `self.is_some()`. Otherwise, calls the given function to return a default value. - -### map - -If self is `Some(x)`, this returns `Some(f(x))`. Otherwise, this returns `None`. - -### map_or - -If self is `Some(x)`, this returns `f(x)`. Otherwise, this returns the given default value. - -### map_or_else - -If self is `Some(x)`, this returns `f(x)`. Otherwise, this returns `default()`. - -### and - -Returns None if self is None. Otherwise, this returns `other`. - -### and_then - -If self is None, this returns None. Otherwise, this calls the given function with the Some value contained within self, and returns the result of that call. In some languages this function is called `flat_map` or `bind`. - -### or - -If self is Some, return self. Otherwise, return `other`. - -### or_else - -If self is Some, return self. Otherwise, return `default()`. - -### xor - -If only one of the two Options is Some, return that option. Otherwise, if both options are Some or both are None, None is returned. - -### filter - -Returns `Some(x)` if self is `Some(x)` and `predicate(x)` is true. Otherwise, this returns `None`. - -### flatten - -Flattens an `Option>` into a `Option`. This returns `None` if the outer Option is None. Otherwise, this returns the inner Option. diff --git a/noir/docs/processed-docs/noir/standard_library/recursion.md b/noir/docs/processed-docs/noir/standard_library/recursion.md deleted file mode 100644 index f252150c8b5..00000000000 --- a/noir/docs/processed-docs/noir/standard_library/recursion.md +++ /dev/null @@ -1,68 +0,0 @@ ---- -title: Recursive Proofs -description: Learn about how to write recursive proofs in Noir. -keywords: [recursion, recursive proofs, verification_key, verify_proof] ---- - -Noir supports recursively verifying proofs, meaning you verify the proof of a Noir program in another Noir program. This enables creating proofs of arbitrary size by doing step-wise verification of smaller components of a large proof. - -Read [the explainer on recursion](../../explainers/explainer-recursion.md) to know more about this function and the [guide on how to use it.](../../how_to/how-to-recursion.md) - -```rust -#[foreign(verify_proof)] -fn verify_proof(_verification_key : [Field], _proof : [Field], _public_input : Field, _key_hash : Field) {} -``` - -:::info - -This is a black box function. Read [this section](./black_box_fns) to learn more about black box functions in Noir. - -::: - -## Example usage - -```rust -use dep::std; - -fn main( - verification_key : [Field; 114], - proof : [Field; 93], - public_inputs : [Field; 1], - key_hash : Field, - proof_b : [Field; 93], -) { - std::verify_proof( - verification_key.as_slice(), - proof.as_slice(), - public_inputs.as_slice(), - key_hash - ); - - std::verify_proof( - verification_key.as_slice(), - proof_b.as_slice(), - public_inputs.as_slice(), - key_hash - ); -} -``` - -You can see a full example of recursive proofs in [this example recursion demo repo](https://github.com/noir-lang/noir-examples/tree/master/recursion). - -## Parameters - -### `verification_key` - -The verification key for the zk program that is being verified. - -### `proof` - -The proof for the zk program that is being verified. - -### `public_inputs` - -These represent the public inputs of the proof we are verifying. - -### `key_hash` - -A key hash is used to check the validity of the verification key. The circuit implementing this opcode can use this hash to ensure that the key provided to the circuit matches the key produced by the circuit creator. diff --git a/noir/docs/processed-docs/noir/standard_library/traits.md b/noir/docs/processed-docs/noir/standard_library/traits.md deleted file mode 100644 index 209f7820e1d..00000000000 --- a/noir/docs/processed-docs/noir/standard_library/traits.md +++ /dev/null @@ -1,408 +0,0 @@ ---- -title: Traits -description: Noir's stdlib provides a few commonly used traits. -keywords: [traits, trait, interface, protocol, default, add, eq] ---- - -## `std::default` - -### `std::default::Default` - -```rust title="default-trait" showLineNumbers -trait Default { - fn default() -> Self; -} -``` -> Source code: noir_stdlib/src/default.nr#L1-L5 - - -Constructs a default value of a type. - -Implementations: -```rust -impl Default for Field { .. } - -impl Default for i8 { .. } -impl Default for i16 { .. } -impl Default for i32 { .. } -impl Default for i64 { .. } - -impl Default for u8 { .. } -impl Default for u16 { .. } -impl Default for u32 { .. } -impl Default for u64 { .. } - -impl Default for () { .. } -impl Default for bool { .. } - -impl Default for [T; N] - where T: Default { .. } - -impl Default for (A, B) - where A: Default, B: Default { .. } - -impl Default for (A, B, C) - where A: Default, B: Default, C: Default { .. } - -impl Default for (A, B, C, D) - where A: Default, B: Default, C: Default, D: Default { .. } - -impl Default for (A, B, C, D, E) - where A: Default, B: Default, C: Default, D: Default, E: Default { .. } -``` - -For primitive integer types, the return value of `default` is `0`. Container -types such as arrays are filled with default values of their element type. - - -## `std::convert` - -### `std::convert::From` - -```rust title="from-trait" showLineNumbers -trait From { - fn from(input: T) -> Self; -} -``` -> Source code: noir_stdlib/src/convert.nr#L1-L5 - - -The `From` trait defines how to convert from a given type `T` to the type on which the trait is implemented. - -The Noir standard library provides a number of implementations of `From` between primitive types. -```rust title="from-impls" showLineNumbers -// Unsigned integers -impl From for u16 { fn from(value: u8) -> u16 { value as u16 } } - -impl From for u32 { fn from(value: u8) -> u32 { value as u32 } } -impl From for u32 { fn from(value: u16) -> u32 { value as u32 } } - -impl From for u64 { fn from(value: u8) -> u64 { value as u64 } } -impl From for u64 { fn from(value: u16) -> u64 { value as u64 } } -impl From for u64 { fn from(value: u32) -> u64 { value as u64 } } - -impl From for Field { fn from(value: u8) -> Field { value as Field } } -impl From for Field { fn from(value: u16) -> Field { value as Field } } -impl From for Field { fn from(value: u32) -> Field { value as Field } } -impl From for Field { fn from(value: u64) -> Field { value as Field } } - -// Signed integers -impl From for i16 { fn from(value: i8) -> i16 { value as i16 } } - -impl From for i32 { fn from(value: i8) -> i32 { value as i32 } } -impl From for i32 { fn from(value: i16) -> i32 { value as i32 } } - -impl From for i64 { fn from(value: i8) -> i64 { value as i64 } } -impl From for i64 { fn from(value: i16) -> i64 { value as i64 } } -impl From for i64 { fn from(value: i32) -> i64 { value as i64 } } - -// Booleans -impl From for u8 { fn from(value: bool) -> u8 { value as u8 } } -impl From for u16 { fn from(value: bool) -> u16 { value as u16 } } -impl From for u32 { fn from(value: bool) -> u32 { value as u32 } } -impl From for u64 { fn from(value: bool) -> u64 { value as u64 } } -impl From for i8 { fn from(value: bool) -> i8 { value as i8 } } -impl From for i16 { fn from(value: bool) -> i16 { value as i16 } } -impl From for i32 { fn from(value: bool) -> i32 { value as i32 } } -impl From for i64 { fn from(value: bool) -> i64 { value as i64 } } -impl From for Field { fn from(value: bool) -> Field { value as Field } } -``` -> Source code: noir_stdlib/src/convert.nr#L25-L61 - - -#### When to implement `From` - -As a general rule of thumb, `From` may be implemented in the [situations where it would be suitable in Rust](https://doc.rust-lang.org/std/convert/trait.From.html#when-to-implement-from): - -- The conversion is *infallible*: Noir does not provide an equivalent to Rust's `TryFrom`, if the conversion can fail then provide a named method instead. -- The conversion is *lossless*: semantically, it should not lose or discard information. For example, `u32: From` can losslessly convert any `u16` into a valid `u32` such that the original `u16` can be recovered. On the other hand, `u16: From` should not be implemented as `2**16` is a `u32` which cannot be losslessly converted into a `u16`. -- The conversion is *value-preserving*: the conceptual kind and meaning of the resulting value is the same, even though the Noir type and technical representation might be different. While it's possible to infallibly and losslessly convert a `u8` into a `str<2>` hex representation, `4u8` and `"04"` are too different for `str<2>: From` to be implemented. -- The conversion is *obvious*: it's the only reasonable conversion between the two types. If there's ambiguity on how to convert between them such that the same input could potentially map to two different values then a named method should be used. For instance rather than implementing `U128: From<[u8; 16]>`, the methods `U128::from_le_bytes` and `U128::from_be_bytes` are used as otherwise the endianness of the array would be ambiguous, resulting in two potential values of `U128` from the same byte array. - -One additional recommendation specific to Noir is: -- The conversion is *efficient*: it's relatively cheap to convert between the two types. Due to being a ZK DSL, it's more important to avoid unnecessary computation compared to Rust. If the implementation of `From` would encourage users to perform unnecessary conversion, resulting in additional proving time, then it may be preferable to expose functionality such that this conversion may be avoided. - -### `std::convert::Into` - -The `Into` trait is defined as the reciprocal of `From`. It should be easy to convince yourself that if we can convert to type `A` from type `B`, then it's possible to convert type `B` into type `A`. - -For this reason, implementing `From` on a type will automatically generate a matching `Into` implementation. One should always prefer implementing `From` over `Into` as implementing `Into` will not generate a matching `From` implementation. - -```rust title="into-trait" showLineNumbers -trait Into { - fn into(input: Self) -> T; -} - -impl Into for U where T: From { - fn into(input: U) -> T { - T::from(input) - } -} -``` -> Source code: noir_stdlib/src/convert.nr#L13-L23 - - -`Into` is most useful when passing function arguments where the types don't quite match up with what the function expects. In this case, the compiler has enough type information to perform the necessary conversion by just appending `.into()` onto the arguments in question. - - -## `std::cmp` - -### `std::cmp::Eq` - -```rust title="eq-trait" showLineNumbers -trait Eq { - fn eq(self, other: Self) -> bool; -} -``` -> Source code: noir_stdlib/src/cmp.nr#L1-L5 - - -Returns `true` if `self` is equal to `other`. Implementing this trait on a type -allows the type to be used with `==` and `!=`. - -Implementations: -```rust -impl Eq for Field { .. } - -impl Eq for i8 { .. } -impl Eq for i16 { .. } -impl Eq for i32 { .. } -impl Eq for i64 { .. } - -impl Eq for u8 { .. } -impl Eq for u16 { .. } -impl Eq for u32 { .. } -impl Eq for u64 { .. } - -impl Eq for () { .. } -impl Eq for bool { .. } - -impl Eq for [T; N] - where T: Eq { .. } - -impl Eq for (A, B) - where A: Eq, B: Eq { .. } - -impl Eq for (A, B, C) - where A: Eq, B: Eq, C: Eq { .. } - -impl Eq for (A, B, C, D) - where A: Eq, B: Eq, C: Eq, D: Eq { .. } - -impl Eq for (A, B, C, D, E) - where A: Eq, B: Eq, C: Eq, D: Eq, E: Eq { .. } -``` - -### `std::cmp::Ord` - -```rust title="ord-trait" showLineNumbers -trait Ord { - fn cmp(self, other: Self) -> Ordering; -} -``` -> Source code: noir_stdlib/src/cmp.nr#L94-L98 - - -`a.cmp(b)` compares two values returning `Ordering::less()` if `a < b`, -`Ordering::equal()` if `a == b`, or `Ordering::greater()` if `a > b`. -Implementing this trait on a type allows `<`, `<=`, `>`, and `>=` to be -used on values of the type. - -Implementations: - -```rust -impl Ord for u8 { .. } -impl Ord for u16 { .. } -impl Ord for u32 { .. } -impl Ord for u64 { .. } - -impl Ord for i8 { .. } -impl Ord for i16 { .. } -impl Ord for i32 { .. } - -impl Ord for i64 { .. } - -impl Ord for () { .. } -impl Ord for bool { .. } - -impl Ord for [T; N] - where T: Ord { .. } - -impl Ord for (A, B) - where A: Ord, B: Ord { .. } - -impl Ord for (A, B, C) - where A: Ord, B: Ord, C: Ord { .. } - -impl Ord for (A, B, C, D) - where A: Ord, B: Ord, C: Ord, D: Ord { .. } - -impl Ord for (A, B, C, D, E) - where A: Ord, B: Ord, C: Ord, D: Ord, E: Ord { .. } -``` - -## `std::ops` - -### `std::ops::Add`, `std::ops::Sub`, `std::ops::Mul`, and `std::ops::Div` - -These traits abstract over addition, subtraction, multiplication, and division respectively. -Implementing these traits for a given type will also allow that type to be used with the corresponding operator -for that trait (`+` for Add, etc) in addition to the normal method names. - -```rust title="add-trait" showLineNumbers -trait Add { - fn add(self, other: Self) -> Self; -} -``` -> Source code: noir_stdlib/src/ops.nr#L1-L5 - -```rust title="sub-trait" showLineNumbers -trait Sub { - fn sub(self, other: Self) -> Self; -} -``` -> Source code: noir_stdlib/src/ops.nr#L19-L23 - -```rust title="mul-trait" showLineNumbers -trait Mul { - fn mul(self, other: Self) -> Self; -} -``` -> Source code: noir_stdlib/src/ops.nr#L37-L41 - -```rust title="div-trait" showLineNumbers -trait Div { - fn div(self, other: Self) -> Self; -} -``` -> Source code: noir_stdlib/src/ops.nr#L55-L59 - - -The implementations block below is given for the `Add` trait, but the same types that implement -`Add` also implement `Sub`, `Mul`, and `Div`. - -Implementations: -```rust -impl Add for Field { .. } - -impl Add for i8 { .. } -impl Add for i16 { .. } -impl Add for i32 { .. } -impl Add for i64 { .. } - -impl Add for u8 { .. } -impl Add for u16 { .. } -impl Add for u32 { .. } -impl Add for u64 { .. } -``` - -### `std::ops::Rem` - -```rust title="rem-trait" showLineNumbers -trait Rem{ - fn rem(self, other: Self) -> Self; -} -``` -> Source code: noir_stdlib/src/ops.nr#L73-L77 - - -`Rem::rem(a, b)` is the remainder function returning the result of what is -left after dividing `a` and `b`. Implementing `Rem` allows the `%` operator -to be used with the implementation type. - -Unlike other numeric traits, `Rem` is not implemented for `Field`. - -Implementations: -```rust -impl Rem for u8 { fn rem(self, other: u8) -> u8 { self % other } } -impl Rem for u16 { fn rem(self, other: u16) -> u16 { self % other } } -impl Rem for u32 { fn rem(self, other: u32) -> u32 { self % other } } -impl Rem for u64 { fn rem(self, other: u64) -> u64 { self % other } } - -impl Rem for i8 { fn rem(self, other: i8) -> i8 { self % other } } -impl Rem for i16 { fn rem(self, other: i16) -> i16 { self % other } } -impl Rem for i32 { fn rem(self, other: i32) -> i32 { self % other } } -impl Rem for i64 { fn rem(self, other: i64) -> i64 { self % other } } -``` - -### `std::ops::{ BitOr, BitAnd, BitXor }` - -```rust title="bitor-trait" showLineNumbers -trait BitOr { - fn bitor(self, other: Self) -> Self; -} -``` -> Source code: noir_stdlib/src/ops.nr#L89-L93 - -```rust title="bitand-trait" showLineNumbers -trait BitAnd { - fn bitand(self, other: Self) -> Self; -} -``` -> Source code: noir_stdlib/src/ops.nr#L107-L111 - -```rust title="bitxor-trait" showLineNumbers -trait BitXor { - fn bitxor(self, other: Self) -> Self; -} -``` -> Source code: noir_stdlib/src/ops.nr#L125-L129 - - -Traits for the bitwise operations `|`, `&`, and `^`. - -Implementing `BitOr`, `BitAnd` or `BitXor` for a type allows the `|`, `&`, or `^` operator respectively -to be used with the type. - -The implementations block below is given for the `BitOr` trait, but the same types that implement -`BitOr` also implement `BitAnd` and `BitXor`. - -Implementations: -```rust -impl BitOr for bool { fn bitor(self, other: bool) -> bool { self | other } } - -impl BitOr for u8 { fn bitor(self, other: u8) -> u8 { self | other } } -impl BitOr for u16 { fn bitor(self, other: u16) -> u16 { self | other } } -impl BitOr for u32 { fn bitor(self, other: u32) -> u32 { self | other } } -impl BitOr for u64 { fn bitor(self, other: u64) -> u64 { self | other } } - -impl BitOr for i8 { fn bitor(self, other: i8) -> i8 { self | other } } -impl BitOr for i16 { fn bitor(self, other: i16) -> i16 { self | other } } -impl BitOr for i32 { fn bitor(self, other: i32) -> i32 { self | other } } -impl BitOr for i64 { fn bitor(self, other: i64) -> i64 { self | other } } -``` - -### `std::ops::{ Shl, Shr }` - -```rust title="shl-trait" showLineNumbers -trait Shl { - fn shl(self, other: Self) -> Self; -} -``` -> Source code: noir_stdlib/src/ops.nr#L143-L147 - -```rust title="shr-trait" showLineNumbers -trait Shr { - fn shr(self, other: Self) -> Self; -} -``` -> Source code: noir_stdlib/src/ops.nr#L160-L164 - - -Traits for a bit shift left and bit shift right. - -Implementing `Shl` for a type allows the left shift operator (`<<`) to be used with the implementation type. -Similarly, implementing `Shr` allows the right shift operator (`>>`) to be used with the type. - -Note that bit shifting is not currently implemented for signed types. - -The implementations block below is given for the `Shl` trait, but the same types that implement -`Shl` also implement `Shr`. - -Implementations: -```rust -impl Shl for u8 { fn shl(self, other: u8) -> u8 { self << other } } -impl Shl for u16 { fn shl(self, other: u16) -> u16 { self << other } } -impl Shl for u32 { fn shl(self, other: u32) -> u32 { self << other } } -impl Shl for u64 { fn shl(self, other: u64) -> u64 { self << other } } -``` diff --git a/noir/docs/processed-docs/noir/standard_library/zeroed.md b/noir/docs/processed-docs/noir/standard_library/zeroed.md deleted file mode 100644 index 97dab02dac2..00000000000 --- a/noir/docs/processed-docs/noir/standard_library/zeroed.md +++ /dev/null @@ -1,25 +0,0 @@ ---- -title: Zeroed Function -description: - The zeroed function returns a zeroed value of any type. -keywords: - [ - zeroed - ] ---- - -Implements `fn zeroed() -> T` to return a zeroed value of any type. This function is generally unsafe to use as the zeroed bit pattern is not guaranteed to be valid for all types. It can however, be useful in cases when the value is guaranteed not to be used such as in a BoundedVec library implementing a growable vector, up to a certain length, backed by an array. The array can be initialized with zeroed values which are guaranteed to be inaccessible until the vector is pushed to. Similarly, enumerations in noir can be implemented using this method by providing zeroed values for the unused variants. - -You can access the function at `std::unsafe::zeroed`. - -This function currently supports the following types: - -- Field -- Bool -- Uint -- Array -- String -- Tuple -- Function - -Using it on other types could result in unexpected behavior. diff --git a/noir/docs/processed-docs/reference/NoirJS/backend_barretenberg/.nojekyll b/noir/docs/processed-docs/reference/NoirJS/backend_barretenberg/.nojekyll deleted file mode 100644 index e2ac6616add..00000000000 --- a/noir/docs/processed-docs/reference/NoirJS/backend_barretenberg/.nojekyll +++ /dev/null @@ -1 +0,0 @@ -TypeDoc added this file to prevent GitHub Pages from using Jekyll. You can turn off this behavior by setting the `githubPages` option to false. \ No newline at end of file diff --git a/noir/docs/processed-docs/reference/NoirJS/backend_barretenberg/classes/BarretenbergBackend.md b/noir/docs/processed-docs/reference/NoirJS/backend_barretenberg/classes/BarretenbergBackend.md deleted file mode 100644 index af4a7558cdf..00000000000 --- a/noir/docs/processed-docs/reference/NoirJS/backend_barretenberg/classes/BarretenbergBackend.md +++ /dev/null @@ -1,202 +0,0 @@ -# BarretenbergBackend - -## Implements - -- [`Backend`](../interfaces/Backend.md) - -## Constructors - -### new BarretenbergBackend(acirCircuit, options) - -```ts -new BarretenbergBackend(acirCircuit, options): BarretenbergBackend -``` - -#### Parameters - -| Parameter | Type | -| :------ | :------ | -| `acirCircuit` | [`CompiledCircuit`](../type-aliases/CompiledCircuit.md) | -| `options` | [`BackendOptions`](../type-aliases/BackendOptions.md) | - -#### Returns - -[`BarretenbergBackend`](BarretenbergBackend.md) - -## Methods - -### destroy() - -```ts -destroy(): Promise -``` - -#### Returns - -`Promise`\<`void`\> - -#### Implementation of - -[`Backend`](../interfaces/Backend.md).[`destroy`](../interfaces/Backend.md#destroy) - -#### Description - -Destroys the backend - -*** - -### generateFinalProof() - -```ts -generateFinalProof(decompressedWitness): Promise -``` - -Generate a final proof. This is the proof for the circuit which will verify -intermediate proofs and or can be seen as the proof created for regular circuits. - -#### Parameters - -| Parameter | Type | -| :------ | :------ | -| `decompressedWitness` | `Uint8Array` | - -#### Returns - -`Promise`\<[`ProofData`](../type-aliases/ProofData.md)\> - -#### Implementation of - -[`Backend`](../interfaces/Backend.md).[`generateFinalProof`](../interfaces/Backend.md#generatefinalproof) - -*** - -### generateIntermediateProof() - -```ts -generateIntermediateProof(witness): Promise -``` - -Generates an intermediate proof. This is the proof that can be verified -in another circuit. - -This is sometimes referred to as a recursive proof. -We avoid this terminology as the only property of this proof -that matters is the fact that it is easy to verify in another circuit. -We _could_ choose to verify this proof outside of a circuit just as easily. - -#### Parameters - -| Parameter | Type | -| :------ | :------ | -| `witness` | `Uint8Array` | - -#### Returns - -`Promise`\<[`ProofData`](../type-aliases/ProofData.md)\> - -#### Implementation of - -[`Backend`](../interfaces/Backend.md).[`generateIntermediateProof`](../interfaces/Backend.md#generateintermediateproof) - -#### Example - -```typescript -const intermediateProof = await backend.generateIntermediateProof(witness); -``` - -*** - -### generateIntermediateProofArtifacts() - -```ts -generateIntermediateProofArtifacts(proofData, numOfPublicInputs): Promise -``` - -Generates artifacts that will be passed to a circuit that will verify this proof. - -Instead of passing the proof and verification key as a byte array, we pass them -as fields which makes it cheaper to verify in a circuit. - -The proof that is passed here will have been created using the `generateIntermediateProof` -method. - -The number of public inputs denotes how many public inputs are in the inner proof. - -#### Parameters - -| Parameter | Type | Default value | -| :------ | :------ | :------ | -| `proofData` | [`ProofData`](../type-aliases/ProofData.md) | `undefined` | -| `numOfPublicInputs` | `number` | `0` | - -#### Returns - -`Promise`\<`object`\> - -#### Implementation of - -[`Backend`](../interfaces/Backend.md).[`generateIntermediateProofArtifacts`](../interfaces/Backend.md#generateintermediateproofartifacts) - -#### Example - -```typescript -const artifacts = await backend.generateIntermediateProofArtifacts(proof, numOfPublicInputs); -``` - -*** - -### verifyFinalProof() - -```ts -verifyFinalProof(proofData): Promise -``` - -#### Parameters - -| Parameter | Type | -| :------ | :------ | -| `proofData` | [`ProofData`](../type-aliases/ProofData.md) | - -#### Returns - -`Promise`\<`boolean`\> - -#### Implementation of - -[`Backend`](../interfaces/Backend.md).[`verifyFinalProof`](../interfaces/Backend.md#verifyfinalproof) - -#### Description - -Verifies a final proof - -*** - -### verifyIntermediateProof() - -```ts -verifyIntermediateProof(proofData): Promise -``` - -#### Parameters - -| Parameter | Type | -| :------ | :------ | -| `proofData` | [`ProofData`](../type-aliases/ProofData.md) | - -#### Returns - -`Promise`\<`boolean`\> - -#### Implementation of - -[`Backend`](../interfaces/Backend.md).[`verifyIntermediateProof`](../interfaces/Backend.md#verifyintermediateproof) - -#### Example - -```typescript -const isValidIntermediate = await backend.verifyIntermediateProof(proof); -``` - -*** - -Generated using [typedoc-plugin-markdown](https://www.npmjs.com/package/typedoc-plugin-markdown) and [TypeDoc](https://typedoc.org/) diff --git a/noir/docs/processed-docs/reference/NoirJS/backend_barretenberg/index.md b/noir/docs/processed-docs/reference/NoirJS/backend_barretenberg/index.md deleted file mode 100644 index e32501acb71..00000000000 --- a/noir/docs/processed-docs/reference/NoirJS/backend_barretenberg/index.md +++ /dev/null @@ -1,46 +0,0 @@ -# backend_barretenberg - -## Exports - -### Classes - -| Class | Description | -| :------ | :------ | -| [BarretenbergBackend](classes/BarretenbergBackend.md) | - | - -### Interfaces - -| Interface | Description | -| :------ | :------ | -| [Backend](interfaces/Backend.md) | - | - -### Type Aliases - -| Type alias | Description | -| :------ | :------ | -| [BackendOptions](type-aliases/BackendOptions.md) | - | -| [CompiledCircuit](type-aliases/CompiledCircuit.md) | - | -| [ProofData](type-aliases/ProofData.md) | - | - -## Functions - -### publicInputsToWitnessMap() - -```ts -publicInputsToWitnessMap(publicInputs, abi): WitnessMap -``` - -#### Parameters - -| Parameter | Type | -| :------ | :------ | -| `publicInputs` | `string`[] | -| `abi` | `Abi` | - -#### Returns - -`WitnessMap` - -*** - -Generated using [typedoc-plugin-markdown](https://www.npmjs.com/package/typedoc-plugin-markdown) and [TypeDoc](https://typedoc.org/) diff --git a/noir/docs/processed-docs/reference/NoirJS/backend_barretenberg/interfaces/Backend.md b/noir/docs/processed-docs/reference/NoirJS/backend_barretenberg/interfaces/Backend.md deleted file mode 100644 index 3eb9645c8d2..00000000000 --- a/noir/docs/processed-docs/reference/NoirJS/backend_barretenberg/interfaces/Backend.md +++ /dev/null @@ -1,132 +0,0 @@ -# Backend - -## Methods - -### destroy() - -```ts -destroy(): Promise -``` - -#### Returns - -`Promise`\<`void`\> - -#### Description - -Destroys the backend - -*** - -### generateFinalProof() - -```ts -generateFinalProof(decompressedWitness): Promise -``` - -#### Parameters - -| Parameter | Type | -| :------ | :------ | -| `decompressedWitness` | `Uint8Array` | - -#### Returns - -`Promise`\<[`ProofData`](../type-aliases/ProofData.md)\> - -#### Description - -Generates a final proof (not meant to be verified in another circuit) - -*** - -### generateIntermediateProof() - -```ts -generateIntermediateProof(decompressedWitness): Promise -``` - -#### Parameters - -| Parameter | Type | -| :------ | :------ | -| `decompressedWitness` | `Uint8Array` | - -#### Returns - -`Promise`\<[`ProofData`](../type-aliases/ProofData.md)\> - -#### Description - -Generates an intermediate proof (meant to be verified in another circuit) - -*** - -### generateIntermediateProofArtifacts() - -```ts -generateIntermediateProofArtifacts(proofData, numOfPublicInputs): Promise -``` - -#### Parameters - -| Parameter | Type | -| :------ | :------ | -| `proofData` | [`ProofData`](../type-aliases/ProofData.md) | -| `numOfPublicInputs` | `number` | - -#### Returns - -`Promise`\<`object`\> - -#### Description - -Retrieves the artifacts from a proof in the Field format - -*** - -### verifyFinalProof() - -```ts -verifyFinalProof(proofData): Promise -``` - -#### Parameters - -| Parameter | Type | -| :------ | :------ | -| `proofData` | [`ProofData`](../type-aliases/ProofData.md) | - -#### Returns - -`Promise`\<`boolean`\> - -#### Description - -Verifies a final proof - -*** - -### verifyIntermediateProof() - -```ts -verifyIntermediateProof(proofData): Promise -``` - -#### Parameters - -| Parameter | Type | -| :------ | :------ | -| `proofData` | [`ProofData`](../type-aliases/ProofData.md) | - -#### Returns - -`Promise`\<`boolean`\> - -#### Description - -Verifies an intermediate proof - -*** - -Generated using [typedoc-plugin-markdown](https://www.npmjs.com/package/typedoc-plugin-markdown) and [TypeDoc](https://typedoc.org/) diff --git a/noir/docs/processed-docs/reference/NoirJS/backend_barretenberg/type-aliases/BackendOptions.md b/noir/docs/processed-docs/reference/NoirJS/backend_barretenberg/type-aliases/BackendOptions.md deleted file mode 100644 index 266ade75d17..00000000000 --- a/noir/docs/processed-docs/reference/NoirJS/backend_barretenberg/type-aliases/BackendOptions.md +++ /dev/null @@ -1,19 +0,0 @@ -# BackendOptions - -```ts -type BackendOptions: object; -``` - -## Description - -An options object, currently only used to specify the number of threads to use. - -## Type declaration - -| Member | Type | Description | -| :------ | :------ | :------ | -| `threads` | `number` | **Description**

Number of threads | - -*** - -Generated using [typedoc-plugin-markdown](https://www.npmjs.com/package/typedoc-plugin-markdown) and [TypeDoc](https://typedoc.org/) diff --git a/noir/docs/processed-docs/reference/NoirJS/backend_barretenberg/type-aliases/CompiledCircuit.md b/noir/docs/processed-docs/reference/NoirJS/backend_barretenberg/type-aliases/CompiledCircuit.md deleted file mode 100644 index 34e0dd04205..00000000000 --- a/noir/docs/processed-docs/reference/NoirJS/backend_barretenberg/type-aliases/CompiledCircuit.md +++ /dev/null @@ -1,20 +0,0 @@ -# CompiledCircuit - -```ts -type CompiledCircuit: object; -``` - -## Description - -The representation of a compiled circuit - -## Type declaration - -| Member | Type | Description | -| :------ | :------ | :------ | -| `abi` | `Abi` | **Description**

ABI representation of the circuit | -| `bytecode` | `string` | **Description**

The bytecode of the circuit | - -*** - -Generated using [typedoc-plugin-markdown](https://www.npmjs.com/package/typedoc-plugin-markdown) and [TypeDoc](https://typedoc.org/) diff --git a/noir/docs/processed-docs/reference/NoirJS/backend_barretenberg/type-aliases/ProofData.md b/noir/docs/processed-docs/reference/NoirJS/backend_barretenberg/type-aliases/ProofData.md deleted file mode 100644 index 05cebbc4e94..00000000000 --- a/noir/docs/processed-docs/reference/NoirJS/backend_barretenberg/type-aliases/ProofData.md +++ /dev/null @@ -1,20 +0,0 @@ -# ProofData - -```ts -type ProofData: object; -``` - -## Description - -The representation of a proof - -## Type declaration - -| Member | Type | Description | -| :------ | :------ | :------ | -| `proof` | `Uint8Array` | **Description**

An byte array representing the proof | -| `publicInputs` | `string`[] | **Description**

Public inputs of a proof | - -*** - -Generated using [typedoc-plugin-markdown](https://www.npmjs.com/package/typedoc-plugin-markdown) and [TypeDoc](https://typedoc.org/) diff --git a/noir/docs/processed-docs/reference/NoirJS/backend_barretenberg/typedoc-sidebar.cjs b/noir/docs/processed-docs/reference/NoirJS/backend_barretenberg/typedoc-sidebar.cjs deleted file mode 100644 index 2aaa55bccf6..00000000000 --- a/noir/docs/processed-docs/reference/NoirJS/backend_barretenberg/typedoc-sidebar.cjs +++ /dev/null @@ -1,4 +0,0 @@ -// @ts-check -/** @type {import('@docusaurus/plugin-content-docs').SidebarsConfig} */ -const typedocSidebar = { items: [{"type":"category","label":"Classes","items":[{"type":"doc","id":"reference/NoirJS/backend_barretenberg/classes/BarretenbergBackend","label":"BarretenbergBackend"}]},{"type":"category","label":"Interfaces","items":[{"type":"doc","id":"reference/NoirJS/backend_barretenberg/interfaces/Backend","label":"Backend"}]},{"type":"category","label":"Type Aliases","items":[{"type":"doc","id":"reference/NoirJS/backend_barretenberg/type-aliases/BackendOptions","label":"BackendOptions"},{"type":"doc","id":"reference/NoirJS/backend_barretenberg/type-aliases/CompiledCircuit","label":"CompiledCircuit"},{"type":"doc","id":"reference/NoirJS/backend_barretenberg/type-aliases/ProofData","label":"ProofData"}]}]}; -module.exports = typedocSidebar.items; \ No newline at end of file diff --git a/noir/docs/processed-docs/reference/NoirJS/noir_js/.nojekyll b/noir/docs/processed-docs/reference/NoirJS/noir_js/.nojekyll deleted file mode 100644 index e2ac6616add..00000000000 --- a/noir/docs/processed-docs/reference/NoirJS/noir_js/.nojekyll +++ /dev/null @@ -1 +0,0 @@ -TypeDoc added this file to prevent GitHub Pages from using Jekyll. You can turn off this behavior by setting the `githubPages` option to false. \ No newline at end of file diff --git a/noir/docs/processed-docs/reference/NoirJS/noir_js/classes/Noir.md b/noir/docs/processed-docs/reference/NoirJS/noir_js/classes/Noir.md deleted file mode 100644 index 34e20d99684..00000000000 --- a/noir/docs/processed-docs/reference/NoirJS/noir_js/classes/Noir.md +++ /dev/null @@ -1,132 +0,0 @@ -# Noir - -## Constructors - -### new Noir(circuit, backend) - -```ts -new Noir(circuit, backend?): Noir -``` - -#### Parameters - -| Parameter | Type | -| :------ | :------ | -| `circuit` | [`CompiledCircuit`](../type-aliases/CompiledCircuit.md) | -| `backend`? | `Backend` | - -#### Returns - -[`Noir`](Noir.md) - -## Methods - -### destroy() - -```ts -destroy(): Promise -``` - -#### Returns - -`Promise`\<`void`\> - -#### Description - -Destroys the underlying backend instance. - -#### Example - -```typescript -await noir.destroy(); -``` - -*** - -### execute() - -```ts -execute(inputs, foreignCallHandler?): Promise -``` - -#### Parameters - -| Parameter | Type | -| :------ | :------ | -| `inputs` | [`InputMap`](../type-aliases/InputMap.md) | -| `foreignCallHandler`? | [`ForeignCallHandler`](../type-aliases/ForeignCallHandler.md) | - -#### Returns - -`Promise`\<`object`\> - -#### Description - -Allows to execute a circuit to get its witness and return value. - -#### Example - -```typescript -async execute(inputs) -``` - -*** - -### generateFinalProof() - -```ts -generateFinalProof(inputs, foreignCallHandler?): Promise -``` - -#### Parameters - -| Parameter | Type | -| :------ | :------ | -| `inputs` | [`InputMap`](../type-aliases/InputMap.md) | -| `foreignCallHandler`? | [`ForeignCallHandler`](../type-aliases/ForeignCallHandler.md) | - -#### Returns - -`Promise`\<[`ProofData`](../type-aliases/ProofData.md)\> - -#### Description - -Generates a witness and a proof given an object as input. - -#### Example - -```typescript -async generateFinalProof(input) -``` - -*** - -### verifyFinalProof() - -```ts -verifyFinalProof(proofData): Promise -``` - -#### Parameters - -| Parameter | Type | -| :------ | :------ | -| `proofData` | [`ProofData`](../type-aliases/ProofData.md) | - -#### Returns - -`Promise`\<`boolean`\> - -#### Description - -Instantiates the verification key and verifies a proof. - -#### Example - -```typescript -async verifyFinalProof(proof) -``` - -*** - -Generated using [typedoc-plugin-markdown](https://www.npmjs.com/package/typedoc-plugin-markdown) and [TypeDoc](https://typedoc.org/) diff --git a/noir/docs/processed-docs/reference/NoirJS/noir_js/functions/and.md b/noir/docs/processed-docs/reference/NoirJS/noir_js/functions/and.md deleted file mode 100644 index c783283e396..00000000000 --- a/noir/docs/processed-docs/reference/NoirJS/noir_js/functions/and.md +++ /dev/null @@ -1,22 +0,0 @@ -# and() - -```ts -and(lhs, rhs): string -``` - -Performs a bitwise AND operation between `lhs` and `rhs` - -## Parameters - -| Parameter | Type | Description | -| :------ | :------ | :------ | -| `lhs` | `string` | | -| `rhs` | `string` | | - -## Returns - -`string` - -*** - -Generated using [typedoc-plugin-markdown](https://www.npmjs.com/package/typedoc-plugin-markdown) and [TypeDoc](https://typedoc.org/) diff --git a/noir/docs/processed-docs/reference/NoirJS/noir_js/functions/blake2s256.md b/noir/docs/processed-docs/reference/NoirJS/noir_js/functions/blake2s256.md deleted file mode 100644 index 7882d0da8d5..00000000000 --- a/noir/docs/processed-docs/reference/NoirJS/noir_js/functions/blake2s256.md +++ /dev/null @@ -1,21 +0,0 @@ -# blake2s256() - -```ts -blake2s256(inputs): Uint8Array -``` - -Calculates the Blake2s256 hash of the input bytes - -## Parameters - -| Parameter | Type | Description | -| :------ | :------ | :------ | -| `inputs` | `Uint8Array` | | - -## Returns - -`Uint8Array` - -*** - -Generated using [typedoc-plugin-markdown](https://www.npmjs.com/package/typedoc-plugin-markdown) and [TypeDoc](https://typedoc.org/) diff --git a/noir/docs/processed-docs/reference/NoirJS/noir_js/functions/ecdsa_secp256k1_verify.md b/noir/docs/processed-docs/reference/NoirJS/noir_js/functions/ecdsa_secp256k1_verify.md deleted file mode 100644 index 5e3cd53e9d3..00000000000 --- a/noir/docs/processed-docs/reference/NoirJS/noir_js/functions/ecdsa_secp256k1_verify.md +++ /dev/null @@ -1,28 +0,0 @@ -# ecdsa\_secp256k1\_verify() - -```ts -ecdsa_secp256k1_verify( - hashed_msg, - public_key_x_bytes, - public_key_y_bytes, - signature): boolean -``` - -Verifies a ECDSA signature over the secp256k1 curve. - -## Parameters - -| Parameter | Type | Description | -| :------ | :------ | :------ | -| `hashed_msg` | `Uint8Array` | | -| `public_key_x_bytes` | `Uint8Array` | | -| `public_key_y_bytes` | `Uint8Array` | | -| `signature` | `Uint8Array` | | - -## Returns - -`boolean` - -*** - -Generated using [typedoc-plugin-markdown](https://www.npmjs.com/package/typedoc-plugin-markdown) and [TypeDoc](https://typedoc.org/) diff --git a/noir/docs/processed-docs/reference/NoirJS/noir_js/functions/ecdsa_secp256r1_verify.md b/noir/docs/processed-docs/reference/NoirJS/noir_js/functions/ecdsa_secp256r1_verify.md deleted file mode 100644 index 0b20ff68957..00000000000 --- a/noir/docs/processed-docs/reference/NoirJS/noir_js/functions/ecdsa_secp256r1_verify.md +++ /dev/null @@ -1,28 +0,0 @@ -# ecdsa\_secp256r1\_verify() - -```ts -ecdsa_secp256r1_verify( - hashed_msg, - public_key_x_bytes, - public_key_y_bytes, - signature): boolean -``` - -Verifies a ECDSA signature over the secp256r1 curve. - -## Parameters - -| Parameter | Type | Description | -| :------ | :------ | :------ | -| `hashed_msg` | `Uint8Array` | | -| `public_key_x_bytes` | `Uint8Array` | | -| `public_key_y_bytes` | `Uint8Array` | | -| `signature` | `Uint8Array` | | - -## Returns - -`boolean` - -*** - -Generated using [typedoc-plugin-markdown](https://www.npmjs.com/package/typedoc-plugin-markdown) and [TypeDoc](https://typedoc.org/) diff --git a/noir/docs/processed-docs/reference/NoirJS/noir_js/functions/keccak256.md b/noir/docs/processed-docs/reference/NoirJS/noir_js/functions/keccak256.md deleted file mode 100644 index d10f155ce86..00000000000 --- a/noir/docs/processed-docs/reference/NoirJS/noir_js/functions/keccak256.md +++ /dev/null @@ -1,21 +0,0 @@ -# keccak256() - -```ts -keccak256(inputs): Uint8Array -``` - -Calculates the Keccak256 hash of the input bytes - -## Parameters - -| Parameter | Type | Description | -| :------ | :------ | :------ | -| `inputs` | `Uint8Array` | | - -## Returns - -`Uint8Array` - -*** - -Generated using [typedoc-plugin-markdown](https://www.npmjs.com/package/typedoc-plugin-markdown) and [TypeDoc](https://typedoc.org/) diff --git a/noir/docs/processed-docs/reference/NoirJS/noir_js/functions/sha256.md b/noir/docs/processed-docs/reference/NoirJS/noir_js/functions/sha256.md deleted file mode 100644 index 6ba4ecac022..00000000000 --- a/noir/docs/processed-docs/reference/NoirJS/noir_js/functions/sha256.md +++ /dev/null @@ -1,21 +0,0 @@ -# sha256() - -```ts -sha256(inputs): Uint8Array -``` - -Calculates the SHA256 hash of the input bytes - -## Parameters - -| Parameter | Type | Description | -| :------ | :------ | :------ | -| `inputs` | `Uint8Array` | | - -## Returns - -`Uint8Array` - -*** - -Generated using [typedoc-plugin-markdown](https://www.npmjs.com/package/typedoc-plugin-markdown) and [TypeDoc](https://typedoc.org/) diff --git a/noir/docs/processed-docs/reference/NoirJS/noir_js/functions/xor.md b/noir/docs/processed-docs/reference/NoirJS/noir_js/functions/xor.md deleted file mode 100644 index 8d762b895d3..00000000000 --- a/noir/docs/processed-docs/reference/NoirJS/noir_js/functions/xor.md +++ /dev/null @@ -1,22 +0,0 @@ -# xor() - -```ts -xor(lhs, rhs): string -``` - -Performs a bitwise XOR operation between `lhs` and `rhs` - -## Parameters - -| Parameter | Type | Description | -| :------ | :------ | :------ | -| `lhs` | `string` | | -| `rhs` | `string` | | - -## Returns - -`string` - -*** - -Generated using [typedoc-plugin-markdown](https://www.npmjs.com/package/typedoc-plugin-markdown) and [TypeDoc](https://typedoc.org/) diff --git a/noir/docs/processed-docs/reference/NoirJS/noir_js/index.md b/noir/docs/processed-docs/reference/NoirJS/noir_js/index.md deleted file mode 100644 index d600e21b299..00000000000 --- a/noir/docs/processed-docs/reference/NoirJS/noir_js/index.md +++ /dev/null @@ -1,37 +0,0 @@ -# noir_js - -## Exports - -### Classes - -| Class | Description | -| :------ | :------ | -| [Noir](classes/Noir.md) | - | - -### Type Aliases - -| Type alias | Description | -| :------ | :------ | -| [CompiledCircuit](type-aliases/CompiledCircuit.md) | - | -| [ForeignCallHandler](type-aliases/ForeignCallHandler.md) | A callback which performs an foreign call and returns the response. | -| [ForeignCallInput](type-aliases/ForeignCallInput.md) | - | -| [ForeignCallOutput](type-aliases/ForeignCallOutput.md) | - | -| [InputMap](type-aliases/InputMap.md) | - | -| [ProofData](type-aliases/ProofData.md) | - | -| [WitnessMap](type-aliases/WitnessMap.md) | - | - -### Functions - -| Function | Description | -| :------ | :------ | -| [and](functions/and.md) | Performs a bitwise AND operation between `lhs` and `rhs` | -| [blake2s256](functions/blake2s256.md) | Calculates the Blake2s256 hash of the input bytes | -| [ecdsa\_secp256k1\_verify](functions/ecdsa_secp256k1_verify.md) | Verifies a ECDSA signature over the secp256k1 curve. | -| [ecdsa\_secp256r1\_verify](functions/ecdsa_secp256r1_verify.md) | Verifies a ECDSA signature over the secp256r1 curve. | -| [keccak256](functions/keccak256.md) | Calculates the Keccak256 hash of the input bytes | -| [sha256](functions/sha256.md) | Calculates the SHA256 hash of the input bytes | -| [xor](functions/xor.md) | Performs a bitwise XOR operation between `lhs` and `rhs` | - -*** - -Generated using [typedoc-plugin-markdown](https://www.npmjs.com/package/typedoc-plugin-markdown) and [TypeDoc](https://typedoc.org/) diff --git a/noir/docs/processed-docs/reference/NoirJS/noir_js/type-aliases/CompiledCircuit.md b/noir/docs/processed-docs/reference/NoirJS/noir_js/type-aliases/CompiledCircuit.md deleted file mode 100644 index 34e0dd04205..00000000000 --- a/noir/docs/processed-docs/reference/NoirJS/noir_js/type-aliases/CompiledCircuit.md +++ /dev/null @@ -1,20 +0,0 @@ -# CompiledCircuit - -```ts -type CompiledCircuit: object; -``` - -## Description - -The representation of a compiled circuit - -## Type declaration - -| Member | Type | Description | -| :------ | :------ | :------ | -| `abi` | `Abi` | **Description**

ABI representation of the circuit | -| `bytecode` | `string` | **Description**

The bytecode of the circuit | - -*** - -Generated using [typedoc-plugin-markdown](https://www.npmjs.com/package/typedoc-plugin-markdown) and [TypeDoc](https://typedoc.org/) diff --git a/noir/docs/processed-docs/reference/NoirJS/noir_js/type-aliases/ForeignCallHandler.md b/noir/docs/processed-docs/reference/NoirJS/noir_js/type-aliases/ForeignCallHandler.md deleted file mode 100644 index 812b8b16481..00000000000 --- a/noir/docs/processed-docs/reference/NoirJS/noir_js/type-aliases/ForeignCallHandler.md +++ /dev/null @@ -1,24 +0,0 @@ -# ForeignCallHandler - -```ts -type ForeignCallHandler: (name, inputs) => Promise; -``` - -A callback which performs an foreign call and returns the response. - -## Parameters - -| Parameter | Type | Description | -| :------ | :------ | :------ | -| `name` | `string` | The identifier for the type of foreign call being performed. | -| `inputs` | [`ForeignCallInput`](ForeignCallInput.md)[] | An array of hex encoded inputs to the foreign call. | - -## Returns - -`Promise`\<[`ForeignCallOutput`](ForeignCallOutput.md)[]\> - -outputs - An array of hex encoded outputs containing the results of the foreign call. - -*** - -Generated using [typedoc-plugin-markdown](https://www.npmjs.com/package/typedoc-plugin-markdown) and [TypeDoc](https://typedoc.org/) diff --git a/noir/docs/processed-docs/reference/NoirJS/noir_js/type-aliases/ForeignCallInput.md b/noir/docs/processed-docs/reference/NoirJS/noir_js/type-aliases/ForeignCallInput.md deleted file mode 100644 index dd95809186a..00000000000 --- a/noir/docs/processed-docs/reference/NoirJS/noir_js/type-aliases/ForeignCallInput.md +++ /dev/null @@ -1,9 +0,0 @@ -# ForeignCallInput - -```ts -type ForeignCallInput: string[]; -``` - -*** - -Generated using [typedoc-plugin-markdown](https://www.npmjs.com/package/typedoc-plugin-markdown) and [TypeDoc](https://typedoc.org/) diff --git a/noir/docs/processed-docs/reference/NoirJS/noir_js/type-aliases/ForeignCallOutput.md b/noir/docs/processed-docs/reference/NoirJS/noir_js/type-aliases/ForeignCallOutput.md deleted file mode 100644 index b71fb78a946..00000000000 --- a/noir/docs/processed-docs/reference/NoirJS/noir_js/type-aliases/ForeignCallOutput.md +++ /dev/null @@ -1,9 +0,0 @@ -# ForeignCallOutput - -```ts -type ForeignCallOutput: string | string[]; -``` - -*** - -Generated using [typedoc-plugin-markdown](https://www.npmjs.com/package/typedoc-plugin-markdown) and [TypeDoc](https://typedoc.org/) diff --git a/noir/docs/processed-docs/reference/NoirJS/noir_js/type-aliases/InputMap.md b/noir/docs/processed-docs/reference/NoirJS/noir_js/type-aliases/InputMap.md deleted file mode 100644 index c714e999d93..00000000000 --- a/noir/docs/processed-docs/reference/NoirJS/noir_js/type-aliases/InputMap.md +++ /dev/null @@ -1,13 +0,0 @@ -# InputMap - -```ts -type InputMap: object; -``` - -## Index signature - - \[`key`: `string`\]: `InputValue` - -*** - -Generated using [typedoc-plugin-markdown](https://www.npmjs.com/package/typedoc-plugin-markdown) and [TypeDoc](https://typedoc.org/) diff --git a/noir/docs/processed-docs/reference/NoirJS/noir_js/type-aliases/ProofData.md b/noir/docs/processed-docs/reference/NoirJS/noir_js/type-aliases/ProofData.md deleted file mode 100644 index 05cebbc4e94..00000000000 --- a/noir/docs/processed-docs/reference/NoirJS/noir_js/type-aliases/ProofData.md +++ /dev/null @@ -1,20 +0,0 @@ -# ProofData - -```ts -type ProofData: object; -``` - -## Description - -The representation of a proof - -## Type declaration - -| Member | Type | Description | -| :------ | :------ | :------ | -| `proof` | `Uint8Array` | **Description**

An byte array representing the proof | -| `publicInputs` | `string`[] | **Description**

Public inputs of a proof | - -*** - -Generated using [typedoc-plugin-markdown](https://www.npmjs.com/package/typedoc-plugin-markdown) and [TypeDoc](https://typedoc.org/) diff --git a/noir/docs/processed-docs/reference/NoirJS/noir_js/type-aliases/WitnessMap.md b/noir/docs/processed-docs/reference/NoirJS/noir_js/type-aliases/WitnessMap.md deleted file mode 100644 index 258c46f9d0c..00000000000 --- a/noir/docs/processed-docs/reference/NoirJS/noir_js/type-aliases/WitnessMap.md +++ /dev/null @@ -1,9 +0,0 @@ -# WitnessMap - -```ts -type WitnessMap: Map; -``` - -*** - -Generated using [typedoc-plugin-markdown](https://www.npmjs.com/package/typedoc-plugin-markdown) and [TypeDoc](https://typedoc.org/) diff --git a/noir/docs/processed-docs/reference/NoirJS/noir_js/typedoc-sidebar.cjs b/noir/docs/processed-docs/reference/NoirJS/noir_js/typedoc-sidebar.cjs deleted file mode 100644 index fe2629ddc9f..00000000000 --- a/noir/docs/processed-docs/reference/NoirJS/noir_js/typedoc-sidebar.cjs +++ /dev/null @@ -1,4 +0,0 @@ -// @ts-check -/** @type {import('@docusaurus/plugin-content-docs').SidebarsConfig} */ -const typedocSidebar = { items: [{"type":"category","label":"Classes","items":[{"type":"doc","id":"reference/NoirJS/noir_js/classes/Noir","label":"Noir"}]},{"type":"category","label":"Type Aliases","items":[{"type":"doc","id":"reference/NoirJS/noir_js/type-aliases/CompiledCircuit","label":"CompiledCircuit"},{"type":"doc","id":"reference/NoirJS/noir_js/type-aliases/ForeignCallHandler","label":"ForeignCallHandler"},{"type":"doc","id":"reference/NoirJS/noir_js/type-aliases/ForeignCallInput","label":"ForeignCallInput"},{"type":"doc","id":"reference/NoirJS/noir_js/type-aliases/ForeignCallOutput","label":"ForeignCallOutput"},{"type":"doc","id":"reference/NoirJS/noir_js/type-aliases/InputMap","label":"InputMap"},{"type":"doc","id":"reference/NoirJS/noir_js/type-aliases/ProofData","label":"ProofData"},{"type":"doc","id":"reference/NoirJS/noir_js/type-aliases/WitnessMap","label":"WitnessMap"}]},{"type":"category","label":"Functions","items":[{"type":"doc","id":"reference/NoirJS/noir_js/functions/and","label":"and"},{"type":"doc","id":"reference/NoirJS/noir_js/functions/blake2s256","label":"blake2s256"},{"type":"doc","id":"reference/NoirJS/noir_js/functions/ecdsa_secp256k1_verify","label":"ecdsa_secp256k1_verify"},{"type":"doc","id":"reference/NoirJS/noir_js/functions/ecdsa_secp256r1_verify","label":"ecdsa_secp256r1_verify"},{"type":"doc","id":"reference/NoirJS/noir_js/functions/keccak256","label":"keccak256"},{"type":"doc","id":"reference/NoirJS/noir_js/functions/sha256","label":"sha256"},{"type":"doc","id":"reference/NoirJS/noir_js/functions/xor","label":"xor"}]}]}; -module.exports = typedocSidebar.items; \ No newline at end of file diff --git a/noir/docs/processed-docs/reference/_category_.json b/noir/docs/processed-docs/reference/_category_.json deleted file mode 100644 index 5b6a20a609a..00000000000 --- a/noir/docs/processed-docs/reference/_category_.json +++ /dev/null @@ -1,5 +0,0 @@ -{ - "position": 4, - "collapsible": true, - "collapsed": true -} diff --git a/noir/docs/processed-docs/reference/nargo_commands.md b/noir/docs/processed-docs/reference/nargo_commands.md deleted file mode 100644 index fc2671b2bfc..00000000000 --- a/noir/docs/processed-docs/reference/nargo_commands.md +++ /dev/null @@ -1,253 +0,0 @@ ---- -title: Nargo -description: - Noir CLI Commands for Noir Prover and Verifier to create, execute, prove and verify programs, - generate Solidity verifier smart contract and compile into JSON file containing ACIR - representation and ABI of circuit. -keywords: - [ - Nargo, - Noir CLI, - Noir Prover, - Noir Verifier, - generate Solidity verifier, - compile JSON file, - ACIR representation, - ABI of circuit, - TypeScript, - ] -sidebar_position: 0 ---- - -## General options - -| Option | Description | -| -------------------- | -------------------------------------------------- | -| `--show-ssa` | Emit debug information for the intermediate SSA IR | -| `--deny-warnings` | Quit execution when warnings are emitted | -| `--silence-warnings` | Suppress warnings | -| `-h, --help` | Print help | - -## `nargo help [subcommand]` - -Prints the list of available commands or specific information of a subcommand. - -_Arguments_ - -| Argument | Description | -| -------------- | -------------------------------------------- | -| `` | The subcommand whose help message to display | - -## `nargo backend` - -Installs and selects custom backends used to generate and verify proofs. - -### Commands - -| Command | Description | -| ----------- | --------------------------------------------------------- | -| `current` | Prints the name of the currently active backend | -| `ls` | Prints the list of currently installed backends | -| `use` | Select the backend to use | -| `install` | Install a new backend from a URL | -| `uninstall` | Uninstalls a backend | -| `help` | Print this message or the help of the given subcommand(s) | - -### Options - -| Option | Description | -| ------------ | ----------- | -| `-h, --help` | Print help | - -## `nargo check` - -Generate the `Prover.toml` and `Verifier.toml` files for specifying prover and verifier in/output -values of the Noir program respectively. - -### Options - -| Option | Description | -| --------------------- | ------------------------------------- | -| `--package ` | The name of the package to check | -| `--workspace` | Check all packages in the workspace | -| `--print-acir` | Display the ACIR for compiled circuit | -| `--deny-warnings` | Treat all warnings as errors | -| `--silence-warnings` | Suppress warnings | -| `-h, --help` | Print help | - -### `nargo codegen-verifier` - -Generate a Solidity verifier smart contract for the program. - -### Options - -| Option | Description | -| --------------------- | ------------------------------------- | -| `--package ` | The name of the package to codegen | -| `--workspace` | Codegen all packages in the workspace | -| `--print-acir` | Display the ACIR for compiled circuit | -| `--deny-warnings` | Treat all warnings as errors | -| `--silence-warnings` | Suppress warnings | -| `-h, --help` | Print help | - -## `nargo compile` - -Compile the program into a JSON build artifact file containing the ACIR representation and the ABI -of the circuit. This build artifact can then be used to generate and verify proofs. - -You can also use "build" as an alias for compile (e.g. `nargo build`). - -### Options - -| Option | Description | -| --------------------- | ------------------------------------------------------------ | -| `--package ` | The name of the package to compile | -| `--workspace` | Compile all packages in the workspace | -| `--print-acir` | Display the ACIR for compiled circuit | -| `--deny-warnings` | Treat all warnings as errors | -| `--silence-warnings` | Suppress warnings | -| `-h, --help` | Print help | - -## `nargo new ` - -Creates a new Noir project in a new folder. - -**Arguments** - -| Argument | Description | -| -------- | -------------------------------- | -| `` | The path to save the new project | - -### Options - -| Option | Description | -| --------------- | ----------------------------------------------------- | -| `--name ` | Name of the package [default: package directory name] | -| `--lib` | Use a library template | -| `--bin` | Use a binary template [default] | -| `--contract` | Use a contract template | -| `-h, --help` | Print help | - -## `nargo init` - -Creates a new Noir project in the current directory. - -### Options - -| Option | Description | -| --------------- | ----------------------------------------------------- | -| `--name ` | Name of the package [default: current directory name] | -| `--lib` | Use a library template | -| `--bin` | Use a binary template [default] | -| `--contract` | Use a contract template | -| `-h, --help` | Print help | - -## `nargo execute [WITNESS_NAME]` - -Runs the Noir program and prints its return value. - -**Arguments** - -| Argument | Description | -| ---------------- | ----------------------------------------- | -| `[WITNESS_NAME]` | Write the execution witness to named file | - -### Options - -| Option | Description | -| --------------------------------- | ------------------------------------------------------------------------------------ | -| `-p, --prover-name ` | The name of the toml file which contains the inputs for the prover [default: Prover] | -| `--package ` | The name of the package to execute | -| `--workspace` | Execute all packages in the workspace | -| `--print-acir` | Display the ACIR for compiled circuit | -| `--deny-warnings` | Treat all warnings as errors | -| `--silence-warnings` | Suppress warnings | -| `--oracle-resolver` | JSON RPC url to solve oracle calls | -| `-h, --help` | Print help | - -_Usage_ - -The inputs to the circuit are read from the `Prover.toml` file generated by `nargo check`, which -must be filled in. - -To save the witness to file, run the command with a value for the `WITNESS_NAME` argument. A -`.tr` file will then be saved in the `./target` folder. - -## `nargo prove` - -Creates a proof for the program. - -### Options - -| Option | Description | -| ------------------------------------- | ---------------------------------------------------------------------------------------- | -| `-p, --prover-name ` | The name of the toml file which contains the inputs for the prover [default: Prover] | -| `-v, --verifier-name ` | The name of the toml file which contains the inputs for the verifier [default: Verifier] | -| `--verify` | Verify proof after proving | -| `--package ` | The name of the package to prove | -| `--workspace` | Prove all packages in the workspace | -| `--print-acir` | Display the ACIR for compiled circuit | -| `--deny-warnings` | Treat all warnings as errors | -| `--silence-warnings` | Suppress warnings | -| `--oracle-resolver` | JSON RPC url to solve oracle calls | -| `-h, --help` | Print help | - -## `nargo verify` - -Given a proof and a program, verify whether the proof is valid. - -### Options - -| Option | Description | -| ------------------------------------- | ---------------------------------------------------------------------------------------- | -| `-v, --verifier-name ` | The name of the toml file which contains the inputs for the verifier [default: Verifier] | -| `--package ` | The name of the package to verify | -| `--workspace` | Verify all packages in the workspace | -| `--print-acir` | Display the ACIR for compiled circuit | -| `--deny-warnings` | Treat all warnings as errors | -| `--silence-warnings` | Suppress warnings | -| `-h, --help` | Print help | - -## `nargo test [TEST_NAME]` - -Nargo will automatically compile and run any functions which have the decorator `#[test]` on them if -you run `nargo test`. To print `println` statements in tests, use the `--show-output` flag. - -Takes an optional `--exact` flag which allows you to select tests based on an exact name. - -See an example on the [testing page](../getting_started/tooling/testing.md). - -### Options - -| Option | Description | -| --------------------- | -------------------------------------- | -| `--show-output` | Display output of `println` statements | -| `--exact` | Only run tests that match exactly | -| `--package ` | The name of the package to test | -| `--workspace` | Test all packages in the workspace | -| `--print-acir` | Display the ACIR for compiled circuit | -| `--deny-warnings` | Treat all warnings as errors | -| `--silence-warnings` | Suppress warnings | -| `--oracle-resolver` | JSON RPC url to solve oracle calls | -| `-h, --help` | Print help | - -## `nargo info` - -Prints a table containing the information of the package. - -Currently the table provide - -1. The number of ACIR opcodes -2. The final number gates in the circuit used by a backend - -If the file contains a contract the table will provide the -above information about each function of the contract. - -## `nargo lsp` - -Start a long-running Language Server process that communicates over stdin/stdout. -Usually this command is not run by a user, but instead will be run by a Language Client, such as [vscode-noir](https://github.com/noir-lang/vscode-noir). - -## `nargo fmt` - -Automatically formats your Noir source code based on the default formatting settings. diff --git a/noir/docs/processed-docs/tutorials/noirjs_app.md b/noir/docs/processed-docs/tutorials/noirjs_app.md deleted file mode 100644 index 23534795dde..00000000000 --- a/noir/docs/processed-docs/tutorials/noirjs_app.md +++ /dev/null @@ -1,279 +0,0 @@ ---- -title: Building a web app with NoirJS -description: Learn how to setup a new app that uses Noir to generate and verify zero-knowledge SNARK proofs in a typescript or javascript environment. -keywords: [how to, guide, javascript, typescript, noir, barretenberg, zero-knowledge, proofs, app] -sidebar_position: 0 -pagination_next: noir/concepts/data_types/index ---- - -NoirJS is a set of packages meant to work both in a browser and a server environment. In this tutorial, we will build a simple web app using them. From here, you should get an idea on how to proceed with your own Noir projects! - -You can find the complete app code for this guide [here](https://github.com/noir-lang/tiny-noirjs-app). - -## Setup - -:::note - -Feel free to use whatever versions, just keep in mind that Nargo and the NoirJS packages are meant to be in sync. For example, Nargo 0.19.x matches `noir_js@0.19.x`, etc. - -In this guide, we will be pinned to 0.19.4. - -::: - -Before we start, we want to make sure we have Node and Nargo installed. - -We start by opening a terminal and executing `node --version`. If we don't get an output like `v20.10.0`, that means node is not installed. Let's do that by following the handy [nvm guide](https://github.com/nvm-sh/nvm?tab=readme-ov-file#install--update-script). - -As for `Nargo`, we can follow the the [Nargo guide](../getting_started/installation/index.md) to install it. If you're lazy, just paste this on a terminal and run `noirup`: - -```sh -curl -L https://raw.githubusercontent.com/noir-lang/noirup/main/install | bash -``` - -Easy enough. Onwards! - -## Our project - -ZK is a powerful technology. An app that doesn't reveal one of the inputs to *anyone* is almost unbelievable, yet Noir makes it as easy as a single line of code. - -In fact, it's so simple that it comes nicely packaged in `nargo`. Let's do that! - -### Nargo - -Run: - -```nargo new circuit``` - -And... That's about it. Your program is ready to be compiled and run. - -To compile, let's `cd` into the `circuit` folder to enter our project, and call: - -```nargo compile``` - -This compiles our circuit into `json` format and add it to a new `target` folder. - -:::info - -At this point in the tutorial, your folder structure should look like this: - -```tree -. -└── circuit <---- our working directory - ├── Nargo.toml - ├── src - │ └── main.nr - └── target - └── circuit.json -``` - -::: - -### Node and Vite - -If you want to explore Nargo, feel free to go on a side-quest now and follow the steps in the -[getting started](../getting_started/hello_noir/index.md) guide. However, we want our app to run on the browser, so we need Vite. - -Vite is a powerful tool to generate static websites. While it provides all kinds of features, let's just go barebones with some good old vanilla JS. - -To do this this, go back to the previous folder (`cd ..`) and create a new vite project by running `npm create vite` and choosing "Vanilla" and "Javascript". - -You should see `vite-project` appear in your root folder. This seems like a good time to `cd` into it and install our NoirJS packages: - -```bash -npm i @noir-lang/backend_barretenberg@0.19.4 @noir-lang/noir_js@0.19.4 -``` - -:::info - -At this point in the tutorial, your folder structure should look like this: - -```tree -. -└── circuit - └── ...etc... -└── vite-project <---- our working directory - └── ...etc... -``` - -::: - -#### Some cleanup - -`npx create vite` is amazing but it creates a bunch of files we don't really need for our simple example. Actually, let's just delete everything except for `index.html`, `main.js` and `package.json`. I feel lighter already. - -![my heart is ready for you, noir.js](../../static/img/memes/titanic.jpeg) - -## HTML - -Our app won't run like this, of course. We need some working HTML, at least. Let's open our broken-hearted `index.html` and replace everything with this code snippet: - -```html - - - - - - -

Noir app

-
- - -
-
-

Logs

-

Proof

-
- - -``` - -It *could* be a beautiful UI... Depending on which universe you live in. - -## Some good old vanilla Javascript - -Our love for Noir needs undivided attention, so let's just open `main.js` and delete everything (this is where the romantic scenery becomes a bit creepy). - -Start by pasting in this boilerplate code: - -```js -const setup = async () => { - await Promise.all([ - import("@noir-lang/noirc_abi").then(module => - module.default(new URL("@noir-lang/noirc_abi/web/noirc_abi_wasm_bg.wasm", import.meta.url).toString()) - ), - import("@noir-lang/acvm_js").then(module => - module.default(new URL("@noir-lang/acvm_js/web/acvm_js_bg.wasm", import.meta.url).toString()) - ) - ]); -} - -function display(container, msg) { - const c = document.getElementById(container); - const p = document.createElement('p'); - p.textContent = msg; - c.appendChild(p); -} - -document.getElementById('submitGuess').addEventListener('click', async () => { - try { - // here's where love happens - } catch(err) { - display("logs", "Oh 💔 Wrong guess") - } -}); - -``` - -The display function doesn't do much. We're simply manipulating our website to see stuff happening. For example, if the proof fails, it will simply log a broken heart 😢 - -As for the `setup` function, it's just a sad reminder that dealing with `wasm` on the browser is not as easy as it should. Just copy, paste, and forget. - -:::info - -At this point in the tutorial, your folder structure should look like this: - -```tree -. -└── circuit - └── ...same as above -└── vite-project - ├── main.js - ├── package.json - └── index.html -``` - -You'll see other files and folders showing up (like `package-lock.json`, `node_modules`) but you shouldn't have to care about those. - -::: - -## Some NoirJS - -We're starting with the good stuff now. If you've compiled the circuit as described above, you should have a `json` file we want to import at the very top of our `main.js` file: - -```ts -import circuit from '../circuit/target/circuit.json'; -``` - -[Noir is backend-agnostic](../index.mdx#whats-new-about-noir). We write Noir, but we also need a proving backend. That's why we need to import and instantiate the two dependencies we installed above: `BarretenbergBackend` and `Noir`. Let's import them right below: - -```js -import { BarretenbergBackend } from '@noir-lang/backend_barretenberg'; -import { Noir } from '@noir-lang/noir_js'; -``` - -And instantiate them inside our try-catch block: - -```ts -// try { -const backend = new BarretenbergBackend(circuit); -const noir = new Noir(circuit, backend); -// } -``` - -:::note - -For the remainder of the tutorial, everything will be happening inside the `try` block - -::: - -## Our app - -Now for the app itself. We're capturing whatever is in the input when people press the submit button. Just add this: - -```js -const x = parseInt(document.getElementById('guessInput').value); -const input = { x, y: 2 }; -``` - -Now we're ready to prove stuff! Let's feed some inputs to our circuit and calculate the proof: - -```js -await setup(); // let's squeeze our wasm inits here - -display('logs', 'Generating proof... ⌛'); -const proof = await noir.generateFinalProof(input); -display('logs', 'Generating proof... ✅'); -display('results', proof.proof); -``` - -You're probably eager to see stuff happening, so go and run your app now! - -From your terminal, run `npm run dev`. If it doesn't open a browser for you, just visit `localhost:5173`. You should now see the worst UI ever, with an ugly input. - -![Getting Started 0](@site/static/img/noir_getting_started_1.png) - -Now, our circuit says `fn main(x: Field, y: pub Field)`. This means only the `y` value is public, and it's hardcoded above: `input = { x, y: 2 }`. In other words, you won't need to send your secret`x` to the verifier! - -By inputting any number other than 2 in the input box and clicking "submit", you should get a valid proof. Otherwise the proof won't even generate correctly. By the way, if you're human, you shouldn't be able to understand anything on the "proof" box. That's OK. We like you, human ❤️. - -## Verifying - -Time to celebrate, yes! But we shouldn't trust machines so blindly. Let's add these lines to see our proof being verified: - -```js -display('logs', 'Verifying proof... ⌛'); -const verification = await noir.verifyFinalProof(proof); -if (verification) display('logs', 'Verifying proof... ✅'); -``` - -You have successfully generated a client-side Noir web app! - -![coded app without math knowledge](../../static/img/memes/flextape.jpeg) - -## Further Reading - -You can see how noirjs is used in a full stack Next.js hardhat application in the [noir-starter repo here](https://github.com/noir-lang/noir-starter/tree/main/vite-hardhat). The example shows how to calculate a proof in the browser and verify it with a deployed Solidity verifier contract from noirjs. - -You should also check out the more advanced examples in the [noir-examples repo](https://github.com/noir-lang/noir-examples), where you'll find reference usage for some cool apps. From 867356e7666cec4a9e7f4c23b9dc23c9b003b68e Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Jos=C3=A9=20Pedro=20Sousa?= Date: Thu, 1 Feb 2024 12:37:26 +0000 Subject: [PATCH 12/16] wip From 81cc275641f7e4dfb5d2c08bcce6a32a7ef69b88 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Jos=C3=A9=20Pedro=20Sousa?= Date: Thu, 1 Feb 2024 12:38:05 +0000 Subject: [PATCH 13/16] wip --- .../compiler/optimizers/redundant_range.rs | 72 ++++++---------- noir/aztec_macros/src/lib.rs | 86 ++++++++++++++----- .../src/ssa/acir_gen/acir_ir/acir_variable.rs | 56 +++++++++--- .../src/hir/def_collector/dc_crate.rs | 14 ++- .../src/hir/resolution/errors.rs | 7 ++ .../src/hir/resolution/resolver.rs | 17 +++- .../noirc_frontend/src/lexer/token.rs | 4 + noir/compiler/noirc_frontend/src/lib.rs | 6 +- noir/compiler/noirc_frontend/src/tests.rs | 2 + .../docs/explainers/explainer-recursion.md | 23 +++-- noir/docs/docs/how_to/how-to-recursion.md | 21 ++--- .../docs/noir/standard_library/recursion.md | 48 +++-------- .../explainers/explainer-recursion.md | 23 +++-- .../how_to/how-to-recursion.md | 21 ++--- .../noir/standard_library/recursion.md | 48 +++-------- noir/noir_stdlib/src/array.nr | 4 +- noir/noir_stdlib/src/bigint.nr | 12 +-- noir/noir_stdlib/src/ecdsa_secp256k1.nr | 10 +-- noir/noir_stdlib/src/ecdsa_secp256r1.nr | 10 +-- noir/noir_stdlib/src/field.nr | 10 +-- noir/noir_stdlib/src/hash.nr | 16 ++-- noir/noir_stdlib/src/lib.nr | 10 +-- noir/noir_stdlib/src/scalar_mul.nr | 4 +- noir/noir_stdlib/src/schnorr.nr | 10 +-- noir/noir_stdlib/src/slice.nr | 12 +-- noir/noir_stdlib/src/string.nr | 2 +- noir/noir_stdlib/src/test.nr | 10 +-- noir/scripts/bootstrap_native.sh | 6 ++ 28 files changed, 305 insertions(+), 259 deletions(-) diff --git a/noir/acvm-repo/acvm/src/compiler/optimizers/redundant_range.rs b/noir/acvm-repo/acvm/src/compiler/optimizers/redundant_range.rs index 5d19f9629ba..ecabd98b3b1 100644 --- a/noir/acvm-repo/acvm/src/compiler/optimizers/redundant_range.rs +++ b/noir/acvm-repo/acvm/src/compiler/optimizers/redundant_range.rs @@ -3,8 +3,7 @@ use acir::{ opcodes::{BlackBoxFuncCall, FunctionInput}, Circuit, Opcode, }, - native_types::{Expression, Witness}, - FieldElement, + native_types::Witness, }; use std::collections::{BTreeMap, HashSet}; @@ -105,9 +104,11 @@ impl RangeOptimizer { let mut new_order_list = Vec::with_capacity(order_list.len()); let mut optimized_opcodes = Vec::with_capacity(self.circuit.opcodes.len()); for (idx, opcode) in self.circuit.opcodes.into_iter().enumerate() { - let (witness, num_bits) = match extract_range_opcode(&opcode) { - Some(range_opcode) => range_opcode, - None => { + let (witness, num_bits) = match &opcode { + Opcode::BlackBoxFuncCall(BlackBoxFuncCall::RANGE { input }) => { + (input.witness, input.num_bits) + } + _ => { // If its not the range opcode, add it to the opcode // list and continue; optimized_opcodes.push(opcode); @@ -131,7 +132,7 @@ impl RangeOptimizer { if is_lowest_bit_size { already_seen_witness.insert(witness); new_order_list.push(order_list[idx]); - optimized_opcodes.push(optimized_range_opcode(witness, num_bits)); + optimized_opcodes.push(opcode); } } @@ -139,36 +140,11 @@ impl RangeOptimizer { } } -/// Extract the range opcode from the `Opcode` enum -/// Returns None, if `Opcode` is not the range opcode. -fn extract_range_opcode(opcode: &Opcode) -> Option<(Witness, u32)> { - match opcode { - Opcode::BlackBoxFuncCall(BlackBoxFuncCall::RANGE { input }) => { - Some((input.witness, input.num_bits)) - } - _ => None, - } -} - -fn optimized_range_opcode(witness: Witness, num_bits: u32) -> Opcode { - if num_bits == 1 { - Opcode::AssertZero(Expression { - mul_terms: vec![(FieldElement::one(), witness, witness)], - linear_combinations: vec![(-FieldElement::one(), witness)], - q_c: FieldElement::zero(), - }) - } else { - Opcode::BlackBoxFuncCall(BlackBoxFuncCall::RANGE { - input: FunctionInput { witness, num_bits }, - }) - } -} - #[cfg(test)] mod tests { use std::collections::BTreeSet; - use crate::compiler::optimizers::redundant_range::{extract_range_opcode, RangeOptimizer}; + use crate::compiler::optimizers::redundant_range::RangeOptimizer; use acir::{ circuit::{ opcodes::{BlackBoxFuncCall, FunctionInput}, @@ -218,11 +194,12 @@ mod tests { let (optimized_circuit, _) = optimizer.replace_redundant_ranges(acir_opcode_positions); assert_eq!(optimized_circuit.opcodes.len(), 1); - let (witness, num_bits) = - extract_range_opcode(&optimized_circuit.opcodes[0]).expect("expected one range opcode"); - - assert_eq!(witness, Witness(1)); - assert_eq!(num_bits, 16); + assert_eq!( + optimized_circuit.opcodes[0], + Opcode::BlackBoxFuncCall(BlackBoxFuncCall::RANGE { + input: FunctionInput { witness: Witness(1), num_bits: 16 } + }) + ); } #[test] @@ -240,15 +217,18 @@ mod tests { let (optimized_circuit, _) = optimizer.replace_redundant_ranges(acir_opcode_positions); assert_eq!(optimized_circuit.opcodes.len(), 2); - let (witness_a, num_bits_a) = - extract_range_opcode(&optimized_circuit.opcodes[0]).expect("expected two range opcode"); - let (witness_b, num_bits_b) = - extract_range_opcode(&optimized_circuit.opcodes[1]).expect("expected two range opcode"); - - assert_eq!(witness_a, Witness(1)); - assert_eq!(witness_b, Witness(2)); - assert_eq!(num_bits_a, 16); - assert_eq!(num_bits_b, 23); + assert_eq!( + optimized_circuit.opcodes[0], + Opcode::BlackBoxFuncCall(BlackBoxFuncCall::RANGE { + input: FunctionInput { witness: Witness(1), num_bits: 16 } + }) + ); + assert_eq!( + optimized_circuit.opcodes[1], + Opcode::BlackBoxFuncCall(BlackBoxFuncCall::RANGE { + input: FunctionInput { witness: Witness(2), num_bits: 23 } + }) + ); } #[test] diff --git a/noir/aztec_macros/src/lib.rs b/noir/aztec_macros/src/lib.rs index b1d401b4e53..1dbe7631388 100644 --- a/noir/aztec_macros/src/lib.rs +++ b/noir/aztec_macros/src/lib.rs @@ -26,8 +26,12 @@ impl MacroProcessor for AztecMacro { transform(ast, crate_id, context) } - fn process_typed_ast(&self, crate_id: &CrateId, context: &mut HirContext) { - transform_hir(crate_id, context) + fn process_typed_ast( + &self, + crate_id: &CrateId, + context: &mut HirContext, + ) -> Result<(), (MacroError, FileId)> { + transform_hir(crate_id, context).map_err(|(err, file_id)| (err.into(), file_id)) } } @@ -41,6 +45,7 @@ pub enum AztecMacroError { ContractHasTooManyFunctions { span: Span }, ContractConstructorMissing { span: Span }, UnsupportedFunctionArgumentType { span: Span, typ: UnresolvedTypeData }, + EventError { span: Span, message: String }, } impl From for MacroError { @@ -71,6 +76,11 @@ impl From for MacroError { secondary_message: None, span: Some(span), }, + AztecMacroError::EventError { span, message } => MacroError { + primary_message: message, + secondary_message: None, + span: Some(span), + }, } } } @@ -237,8 +247,11 @@ fn transform( // /// Completes the Hir with data gathered from type resolution -fn transform_hir(crate_id: &CrateId, context: &mut HirContext) { - transform_events(crate_id, context); +fn transform_hir( + crate_id: &CrateId, + context: &mut HirContext, +) -> Result<(), (AztecMacroError, FileId)> { + transform_events(crate_id, context) } /// Includes an import to the aztec library if it has not been included yet @@ -472,19 +485,30 @@ fn collect_crate_structs(crate_id: &CrateId, context: &HirContext) -> Vec Result<(), (AztecMacroError, FileId)> { let struct_type = interner.get_struct(struct_id); let selector_id = interner - .lookup_method(&Type::Struct(struct_type, vec![]), struct_id, "selector", false) - .expect("Selector method not found"); + .lookup_method(&Type::Struct(struct_type.clone(), vec![]), struct_id, "selector", false) + .ok_or_else(|| { + let error = AztecMacroError::EventError { + span: struct_type.borrow().location.span, + message: "Selector method not found".to_owned(), + }; + (error, struct_type.borrow().location.file) + })?; let selector_function = interner.function(&selector_id); let compute_selector_statement = interner.statement( - selector_function - .block(interner) - .statements() - .first() - .expect("Compute selector statement not found"), + selector_function.block(interner).statements().first().ok_or_else(|| { + let error = AztecMacroError::EventError { + span: struct_type.borrow().location.span, + message: "Compute selector statement not found".to_owned(), + }; + (error, struct_type.borrow().location.file) + })?, ); let compute_selector_expression = match compute_selector_statement { @@ -494,12 +518,21 @@ fn transform_event(struct_id: StructId, interner: &mut NodeInterner) { }, _ => None, } - .expect("Compute selector statement is not a call expression"); - - let first_arg_id = compute_selector_expression - .arguments - .first() - .expect("Missing argument for compute selector"); + .ok_or_else(|| { + let error = AztecMacroError::EventError { + span: struct_type.borrow().location.span, + message: "Compute selector statement is not a call expression".to_owned(), + }; + (error, struct_type.borrow().location.file) + })?; + + let first_arg_id = compute_selector_expression.arguments.first().ok_or_else(|| { + let error = AztecMacroError::EventError { + span: struct_type.borrow().location.span, + message: "Compute selector statement is not a call expression".to_owned(), + }; + (error, struct_type.borrow().location.file) + })?; match interner.expression(first_arg_id) { HirExpression::Literal(HirLiteral::Str(signature)) @@ -518,18 +551,29 @@ fn transform_event(struct_id: StructId, interner: &mut NodeInterner) { selector_literal_id, Type::String(Box::new(Type::Constant(signature.len() as u64))), ); + Ok(()) } - _ => unreachable!("Signature placeholder literal does not match"), + _ => Err(( + AztecMacroError::EventError { + span: struct_type.borrow().location.span, + message: "Signature placeholder literal does not match".to_owned(), + }, + struct_type.borrow().location.file, + )), } } -fn transform_events(crate_id: &CrateId, context: &mut HirContext) { +fn transform_events( + crate_id: &CrateId, + context: &mut HirContext, +) -> Result<(), (AztecMacroError, FileId)> { for struct_id in collect_crate_structs(crate_id, context) { let attributes = context.def_interner.struct_attributes(&struct_id); if attributes.iter().any(|attr| matches!(attr, SecondaryAttribute::Event)) { - transform_event(struct_id, &mut context.def_interner); + transform_event(struct_id, &mut context.def_interner)?; } } + Ok(()) } const SIGNATURE_PLACEHOLDER: &str = "SIGNATURE_PLACEHOLDER"; diff --git a/noir/compiler/noirc_evaluator/src/ssa/acir_gen/acir_ir/acir_variable.rs b/noir/compiler/noirc_evaluator/src/ssa/acir_gen/acir_ir/acir_variable.rs index d56d0ade3c4..f1a639de211 100644 --- a/noir/compiler/noirc_evaluator/src/ssa/acir_gen/acir_ir/acir_variable.rs +++ b/noir/compiler/noirc_evaluator/src/ssa/acir_gen/acir_ir/acir_variable.rs @@ -1441,20 +1441,22 @@ impl AcirContext { inputs: Vec, outputs: Vec, attempt_execution: bool, - ) -> Result, InternalError> { - let b_inputs = try_vecmap(inputs, |i| match i { - AcirValue::Var(var, _) => Ok(BrilligInputs::Single(self.var_to_expression(var)?)), - AcirValue::Array(vars) => { - let mut var_expressions: Vec = Vec::new(); - for var in vars { - self.brillig_array_input(&mut var_expressions, var)?; + ) -> Result, RuntimeError> { + let b_inputs = try_vecmap(inputs, |i| -> Result<_, InternalError> { + match i { + AcirValue::Var(var, _) => Ok(BrilligInputs::Single(self.var_to_expression(var)?)), + AcirValue::Array(vars) => { + let mut var_expressions: Vec = Vec::new(); + for var in vars { + self.brillig_array_input(&mut var_expressions, var)?; + } + Ok(BrilligInputs::Array(var_expressions)) + } + AcirValue::DynamicArray(_) => { + let mut var_expressions = Vec::new(); + self.brillig_array_input(&mut var_expressions, i)?; + Ok(BrilligInputs::Array(var_expressions)) } - Ok(BrilligInputs::Array(var_expressions)) - } - AcirValue::DynamicArray(_) => { - let mut var_expressions = Vec::new(); - self.brillig_array_input(&mut var_expressions, i)?; - Ok(BrilligInputs::Array(var_expressions)) } })?; @@ -1489,6 +1491,34 @@ impl AcirContext { let predicate = self.var_to_expression(predicate)?; self.acir_ir.brillig(Some(predicate), generated_brillig, b_inputs, b_outputs); + fn range_constraint_value( + context: &mut AcirContext, + value: &AcirValue, + ) -> Result<(), RuntimeError> { + match value { + AcirValue::Var(var, typ) => { + let numeric_type = match typ { + AcirType::NumericType(numeric_type) => numeric_type, + _ => unreachable!("`AcirValue::Var` may only hold primitive values"), + }; + context.range_constrain_var(*var, numeric_type, None)?; + } + AcirValue::Array(values) => { + for value in values { + range_constraint_value(context, value)?; + } + } + AcirValue::DynamicArray(_) => { + unreachable!("Brillig opcodes cannot return dynamic arrays") + } + } + Ok(()) + } + + for output_var in &outputs_var { + range_constraint_value(self, output_var)?; + } + Ok(outputs_var) } diff --git a/noir/compiler/noirc_frontend/src/hir/def_collector/dc_crate.rs b/noir/compiler/noirc_frontend/src/hir/def_collector/dc_crate.rs index a6ab6b1d825..f7441750fc8 100644 --- a/noir/compiler/noirc_frontend/src/hir/def_collector/dc_crate.rs +++ b/noir/compiler/noirc_frontend/src/hir/def_collector/dc_crate.rs @@ -14,7 +14,7 @@ use crate::hir::resolution::{ use crate::hir::type_check::{type_check_func, TypeCheckError, TypeChecker}; use crate::hir::Context; -use crate::macros_api::MacroProcessor; +use crate::macros_api::{MacroError, MacroProcessor}; use crate::node_interner::{FuncId, NodeInterner, StmtId, StructId, TraitId, TypeAliasId}; use crate::parser::{ParserError, SortedModule}; @@ -155,6 +155,12 @@ impl From for CustomDiagnostic { } } +impl From for CompilationError { + fn from(value: MacroError) -> Self { + CompilationError::DefinitionError(DefCollectorErrorKind::MacroError(value)) + } +} + impl From for CompilationError { fn from(value: ParserError) -> Self { CompilationError::ParseError(value) @@ -359,7 +365,11 @@ impl DefCollector { errors.extend(resolved_globals.errors); for macro_processor in macro_processors { - macro_processor.process_typed_ast(&crate_id, context); + macro_processor.process_typed_ast(&crate_id, context).unwrap_or_else( + |(macro_err, file_id)| { + errors.push((macro_err.into(), file_id)); + }, + ); } errors.extend(type_check_globals(&mut context.def_interner, resolved_globals.globals)); diff --git a/noir/compiler/noirc_frontend/src/hir/resolution/errors.rs b/noir/compiler/noirc_frontend/src/hir/resolution/errors.rs index 390807afd17..7bd4de77e84 100644 --- a/noir/compiler/noirc_frontend/src/hir/resolution/errors.rs +++ b/noir/compiler/noirc_frontend/src/hir/resolution/errors.rs @@ -84,6 +84,8 @@ pub enum ResolverError { InvalidTypeForEntryPoint { span: Span }, #[error("Nested slices are not supported")] NestedSlices { span: Span }, + #[error("Usage of the `#[foreign]` or `#[builtin]` function attributes are not allowed outside of the Noir standard library")] + LowLevelFunctionOutsideOfStdlib { ident: Ident }, } impl ResolverError { @@ -311,6 +313,11 @@ impl From for Diagnostic { "Try to use a constant sized array instead".into(), span, ), + ResolverError::LowLevelFunctionOutsideOfStdlib { ident } => Diagnostic::simple_error( + "Definition of low-level function outside of standard library".into(), + "Usage of the `#[foreign]` or `#[builtin]` function attributes are not allowed outside of the Noir standard library".into(), + ident.span(), + ), } } } diff --git a/noir/compiler/noirc_frontend/src/hir/resolution/resolver.rs b/noir/compiler/noirc_frontend/src/hir/resolution/resolver.rs index df533f6a4ae..8243b684c8a 100644 --- a/noir/compiler/noirc_frontend/src/hir/resolution/resolver.rs +++ b/noir/compiler/noirc_frontend/src/hir/resolution/resolver.rs @@ -191,10 +191,18 @@ impl<'a> Resolver<'a> { self.add_generics(&func.def.generics); self.trait_bounds = func.def.where_clause.clone(); + let is_low_level_or_oracle = func + .attributes() + .function + .as_ref() + .map_or(false, |func| func.is_low_level() || func.is_oracle()); let (hir_func, func_meta) = self.intern_function(func, func_id); let func_scope_tree = self.scopes.end_function(); - self.check_for_unused_variables_in_scope_tree(func_scope_tree); + // The arguments to low-level and oracle functions are always unused so we do not produce warnings for them. + if !is_low_level_or_oracle { + self.check_for_unused_variables_in_scope_tree(func_scope_tree); + } self.trait_bounds.clear(); (hir_func, func_meta, self.errors) @@ -900,6 +908,13 @@ impl<'a> Resolver<'a> { position: PubPosition::ReturnType, }); } + let is_low_level_function = + func.attributes().function.as_ref().map_or(false, |func| func.is_low_level()); + if !self.path_resolver.module_id().krate.is_stdlib() && is_low_level_function { + let error = + ResolverError::LowLevelFunctionOutsideOfStdlib { ident: func.name_ident().clone() }; + self.push_err(error); + } // 'pub' is required on return types for entry point functions if self.is_entry_point_function(func) diff --git a/noir/compiler/noirc_frontend/src/lexer/token.rs b/noir/compiler/noirc_frontend/src/lexer/token.rs index ab131ccd880..835a0baae3f 100644 --- a/noir/compiler/noirc_frontend/src/lexer/token.rs +++ b/noir/compiler/noirc_frontend/src/lexer/token.rs @@ -562,6 +562,10 @@ impl FunctionAttribute { matches!(self, FunctionAttribute::Foreign(_)) } + pub fn is_oracle(&self) -> bool { + matches!(self, FunctionAttribute::Oracle(_)) + } + pub fn is_low_level(&self) -> bool { matches!(self, FunctionAttribute::Foreign(_) | FunctionAttribute::Builtin(_)) } diff --git a/noir/compiler/noirc_frontend/src/lib.rs b/noir/compiler/noirc_frontend/src/lib.rs index 9582b80dcba..b6d4c568334 100644 --- a/noir/compiler/noirc_frontend/src/lib.rs +++ b/noir/compiler/noirc_frontend/src/lib.rs @@ -75,6 +75,10 @@ pub mod macros_api { ) -> Result; /// Function to manipulate the AST after type checking has been completed. /// The AST after type checking has been done is called the HIR. - fn process_typed_ast(&self, crate_id: &CrateId, context: &mut HirContext); + fn process_typed_ast( + &self, + crate_id: &CrateId, + context: &mut HirContext, + ) -> Result<(), (MacroError, FileId)>; } } diff --git a/noir/compiler/noirc_frontend/src/tests.rs b/noir/compiler/noirc_frontend/src/tests.rs index 9ccbddab9ec..a4246a9fe7d 100644 --- a/noir/compiler/noirc_frontend/src/tests.rs +++ b/noir/compiler/noirc_frontend/src/tests.rs @@ -52,10 +52,12 @@ mod test { ) -> (ParsedModule, Context, Vec<(CompilationError, FileId)>) { let root = std::path::Path::new("/"); let fm = FileManager::new(root); + let mut context = Context::new(fm, Default::default()); context.def_interner.populate_dummy_operator_traits(); let root_file_id = FileId::dummy(); let root_crate_id = context.crate_graph.add_crate_root(root_file_id); + let (program, parser_errors) = parse_program(src); let mut errors = vecmap(parser_errors, |e| (e.into(), root_file_id)); remove_experimental_warnings(&mut errors); diff --git a/noir/docs/docs/explainers/explainer-recursion.md b/noir/docs/docs/explainers/explainer-recursion.md index 8f992ec29fd..18846176ca7 100644 --- a/noir/docs/docs/explainers/explainer-recursion.md +++ b/noir/docs/docs/explainers/explainer-recursion.md @@ -16,12 +16,13 @@ keywords: "Optimizing Computational Resources", "Improving Efficiency", "Verification Key", - "Aggregation Objects", + "Aggregation", "Recursive zkSNARK schemes", "PLONK", "Proving and Verification Keys" ] sidebar_position: 1 +pagination_next: how_to/how-to-recursion --- In programming, we tend to think of recursion as something calling itself. A classic example would be the calculation of the factorial of a number: @@ -64,7 +65,7 @@ So, they use zero-knowledge proofs. Alice tries to guess Bob's number, and Bob w This ZK proof can go on a smart contract, revealing the winner and even giving prizes. However, this means every turn needs to be verified on-chain. This incurs some cost and waiting time that may simply make the game too expensive or time-consuming to be worth it. -As a solution, Alice proposes the following: "what if Bob generates his proof, and instead of sending it on-chain, I verify it *within* my own proof before playing my own turn?". +As a solution, Alice proposes the following: "what if Bob generates his proof, and instead of sending it on-chain, I verify it *within* my own proof before playing my own turn?". She can then generate a proof that she verified his proof, and so on. @@ -116,25 +117,19 @@ As you can see in the [recursion reference](noir/standard_library/recursion.md), - The Verification Key of the circuit that generated the proof - A hash of this verification key, as it's needed for some backends - The public inputs for the proof -- The input aggregation object -It also returns the `output aggregation object`. These aggregation objects can be confusing at times, so let's dive in a little bit. - -### Aggregation objects +:::info Recursive zkSNARK schemes do not necessarily "verify a proof" in the sense that you expect a true or false to be spit out by the verifier. Rather an aggregation object is built over the public inputs. -In the case of PLONK the recursive aggregation object is two G1 points (expressed as 16 witness values). The final verifier (in our case this is most often the smart contract verifier) has to be aware of this aggregation object to execute a pairing and check the validity of these points. - So, taking the example of Alice and Bob and their guessing game: - Alice makes her guess. Her proof is *not* recursive: it doesn't verify any proof within it! It's just a standard `assert(x != y)` circuit -- Bob verifies Alice's proof and makes his own guess. In this circuit, he is verifying a proof, so it needs to output an `aggregation object`: he is generating a recursive proof! -- Alice verifies Bob's *recursive proof*, and uses Bob's `output aggregation object` as the `input aggregation object` in her proof... Which in turn, generates another `output aggregation object`. +- Bob verifies Alice's proof and makes his own guess. In this circuit, he doesn't exactly *prove* the verification of Alice's proof. Instead, he *aggregates* his proof to Alice's proof. The actual verification is done when the full proof is verified, for example when using `nargo verify` or through the verifier smart contract. -One should notice that when Bob generates his first proof, he has no input aggregation object. Because he is not verifying an recursive proof, he has no `input aggregation object`. In this case, he may use zeros instead. +We can imagine recursive proofs a [relay race](https://en.wikipedia.org/wiki/Relay_race). The first runner doesn't have to receive the baton from anyone else, as he/she already starts with it. But when his/her turn is over, the next runner needs to receive it, run a bit more, and pass it along. Even though every runner could theoretically verify the baton mid-run (why not? 🏃🔍), only at the end of the race does the referee verify that the whole race is valid. -We can imagine the `aggregation object` as the baton in a [relay race](https://en.wikipedia.org/wiki/Relay_race). The first runner doesn't have to receive the baton from anyone else, as he/she already starts with it. But when his/her turn is over, the next runner needs to receive it, run a bit more, and pass it along. Even though every runner could theoretically verify the baton mid-run (why not? 🏃🔍), only at the end of the race does the referee verify that the whole race is valid. +::: ## Some architecture @@ -175,3 +170,7 @@ In this example, a regulator could verify that taxes were paid for a specific pu At the time of writing, verifying recursive proofs is surprisingly fast. This is because most of the time is spent on generating the verification key that will be used to generate the next proof. So you are able to cache the verification key and reuse it later. Currently, Noir JS packages don't expose the functionality of loading proving and verification keys, but that feature exists in the underlying `bb.js` package. + +## How can I try it + +Learn more about using recursion in Nargo and NoirJS in the [how-to guide](../how_to/how-to-recursion.md) and see a full example in [noir-examples](https://github.com/noir-lang/noir-examples). diff --git a/noir/docs/docs/how_to/how-to-recursion.md b/noir/docs/docs/how_to/how-to-recursion.md index 39db23f1f3a..f34647a99d5 100644 --- a/noir/docs/docs/how_to/how-to-recursion.md +++ b/noir/docs/docs/how_to/how-to-recursion.md @@ -108,11 +108,7 @@ This call takes the public inputs and the proof, but also the public inputs coun :::info -The `proofAsFields` has a constant size `[Field; 93]`. However, currently the backend doesn't remove the public inputs from the proof when converting it. - -This means that if your `main` circuit has two public inputs, then you should also modify the recursive circuit to accept a proof with the public inputs appended. This means that in our example, since `y` is a public input, our `proofAsFields` is of type `[Field; 94]`. - -Verification keys in Barretenberg are always of size 114. +The `proofAsFields` has a constant size `[Field; 93]` and verification keys in Barretenberg are always `[Field; 114]`. ::: @@ -136,7 +132,6 @@ const recursiveInputs = { proof: proofAsFields, // array of length 93 + size of public inputs publicInputs: [mainInput.y], // using the example above, where `y` is the only public input key_hash: vkHash, - input_aggregation_object: Array(16).fill(0) // this circuit is verifying a non-recursive proof, so there's no input aggregation object: just use zero } const { witness, returnValue } = noir.execute(recursiveInputs) // we're executing the recursive circuit now! @@ -144,7 +139,7 @@ const { proof, publicInputs } = backend.generateFinalProof(witness) const verified = backend.verifyFinalProof({ proof, publicInputs }) ``` -You can obviously chain this proof into another proof. In fact, if you're using recursive proofs, you're probably interested of using them this way! In that case, you should keep in mind the `returnValue`, as it will contain the `input_aggregation_object` for the next proof. +You can obviously chain this proof into another proof. In fact, if you're using recursive proofs, you're probably interested of using them this way! :::tip @@ -152,16 +147,16 @@ Managing circuits and "who does what" can be confusing. To make sure your naming ```js const circuits = { -main: mainJSON, -recursive: recursiveJSON + main: mainJSON, + recursive: recursiveJSON } const backends = { -main: new BarretenbergBackend(circuits.main), -recursive: new BarretenbergBackend(circuits.recursive) + main: new BarretenbergBackend(circuits.main), + recursive: new BarretenbergBackend(circuits.recursive) } const noir_programs = { -main: new Noir(circuits.main, backends.main), -recursive: new Noir(circuits.recursive, backends.recursive) + main: new Noir(circuits.main, backends.main), + recursive: new Noir(circuits.recursive, backends.recursive) } ``` diff --git a/noir/docs/docs/noir/standard_library/recursion.md b/noir/docs/docs/noir/standard_library/recursion.md index 67962082a8f..f252150c8b5 100644 --- a/noir/docs/docs/noir/standard_library/recursion.md +++ b/noir/docs/docs/noir/standard_library/recursion.md @@ -1,16 +1,16 @@ --- title: Recursive Proofs description: Learn about how to write recursive proofs in Noir. -keywords: [recursion, recursive proofs, verification_key, aggregation object, verify_proof] +keywords: [recursion, recursive proofs, verification_key, verify_proof] --- Noir supports recursively verifying proofs, meaning you verify the proof of a Noir program in another Noir program. This enables creating proofs of arbitrary size by doing step-wise verification of smaller components of a large proof. -The `verify_proof` function takes a verification key, proof and public inputs for a zk program, as well as a key hash and an input aggregation object. The key hash is used to check the validity of the verification key and the input aggregation object is required by some proving systems. The `verify_proof` function returns an output aggregation object that can then be fed into future iterations of the proof verification if required. +Read [the explainer on recursion](../../explainers/explainer-recursion.md) to know more about this function and the [guide on how to use it.](../../how_to/how-to-recursion.md) ```rust #[foreign(verify_proof)] -fn verify_proof(_verification_key : [Field], _proof : [Field], _public_input : Field, _key_hash : Field, _input_aggregation_object : [Field]) -> [Field] {} +fn verify_proof(_verification_key : [Field], _proof : [Field], _public_input : Field, _key_hash : Field) {} ``` :::info @@ -26,36 +26,29 @@ use dep::std; fn main( verification_key : [Field; 114], - proof : [Field; 94], + proof : [Field; 93], public_inputs : [Field; 1], key_hash : Field, - input_aggregation_object : [Field; 16], - proof_b : [Field; 94], -) -> pub [Field; 16] { - let output_aggregation_object_a = std::verify_proof( + proof_b : [Field; 93], +) { + std::verify_proof( verification_key.as_slice(), proof.as_slice(), public_inputs.as_slice(), - key_hash, - input_aggregation_object + key_hash ); - let output_aggregation_object = std::verify_proof( + std::verify_proof( verification_key.as_slice(), proof_b.as_slice(), public_inputs.as_slice(), - key_hash, - output_aggregation_object_a + key_hash ); - - let mut output = [0; 16]; - for i in 0..16 { - output[i] = output_aggregation_object[i]; - } - output } ``` +You can see a full example of recursive proofs in [this example recursion demo repo](https://github.com/noir-lang/noir-examples/tree/master/recursion). + ## Parameters ### `verification_key` @@ -68,23 +61,8 @@ The proof for the zk program that is being verified. ### `public_inputs` -These represent the public inputs of the proof we are verifying. They should be checked against in the circuit after construction of a new aggregation state. +These represent the public inputs of the proof we are verifying. ### `key_hash` A key hash is used to check the validity of the verification key. The circuit implementing this opcode can use this hash to ensure that the key provided to the circuit matches the key produced by the circuit creator. - -### `input_aggregation_object` - -An aggregation object is blob of data that the top-level verifier must run some proof system specific algorithm on to complete verification. The size is proof system specific and will be set by the backend integrating this opcode. The input aggregation object is only not `None` when we are verifying a previous recursive aggregation in the current circuit. If this is the first recursive aggregation there is no input aggregation object. It is left to the backend to determine how to handle when there is no input aggregation object. - -## Return value - -### `output_aggregation_object` - -This is the result of a recursive aggregation and is what will be fed into the next verifier. -The next verifier can either perform a final verification (returning true or false) or perform another recursive aggregation where this output aggregation object will be the input aggregation object of the next recursive aggregation. - -## Example - -You can see an example of how to do recursive proofs in [this example recursion demo repo](https://github.com/noir-lang/noir-examples/tree/master/recursion). diff --git a/noir/docs/versioned_docs/version-v0.23.0/explainers/explainer-recursion.md b/noir/docs/versioned_docs/version-v0.23.0/explainers/explainer-recursion.md index 8f992ec29fd..18846176ca7 100644 --- a/noir/docs/versioned_docs/version-v0.23.0/explainers/explainer-recursion.md +++ b/noir/docs/versioned_docs/version-v0.23.0/explainers/explainer-recursion.md @@ -16,12 +16,13 @@ keywords: "Optimizing Computational Resources", "Improving Efficiency", "Verification Key", - "Aggregation Objects", + "Aggregation", "Recursive zkSNARK schemes", "PLONK", "Proving and Verification Keys" ] sidebar_position: 1 +pagination_next: how_to/how-to-recursion --- In programming, we tend to think of recursion as something calling itself. A classic example would be the calculation of the factorial of a number: @@ -64,7 +65,7 @@ So, they use zero-knowledge proofs. Alice tries to guess Bob's number, and Bob w This ZK proof can go on a smart contract, revealing the winner and even giving prizes. However, this means every turn needs to be verified on-chain. This incurs some cost and waiting time that may simply make the game too expensive or time-consuming to be worth it. -As a solution, Alice proposes the following: "what if Bob generates his proof, and instead of sending it on-chain, I verify it *within* my own proof before playing my own turn?". +As a solution, Alice proposes the following: "what if Bob generates his proof, and instead of sending it on-chain, I verify it *within* my own proof before playing my own turn?". She can then generate a proof that she verified his proof, and so on. @@ -116,25 +117,19 @@ As you can see in the [recursion reference](noir/standard_library/recursion.md), - The Verification Key of the circuit that generated the proof - A hash of this verification key, as it's needed for some backends - The public inputs for the proof -- The input aggregation object -It also returns the `output aggregation object`. These aggregation objects can be confusing at times, so let's dive in a little bit. - -### Aggregation objects +:::info Recursive zkSNARK schemes do not necessarily "verify a proof" in the sense that you expect a true or false to be spit out by the verifier. Rather an aggregation object is built over the public inputs. -In the case of PLONK the recursive aggregation object is two G1 points (expressed as 16 witness values). The final verifier (in our case this is most often the smart contract verifier) has to be aware of this aggregation object to execute a pairing and check the validity of these points. - So, taking the example of Alice and Bob and their guessing game: - Alice makes her guess. Her proof is *not* recursive: it doesn't verify any proof within it! It's just a standard `assert(x != y)` circuit -- Bob verifies Alice's proof and makes his own guess. In this circuit, he is verifying a proof, so it needs to output an `aggregation object`: he is generating a recursive proof! -- Alice verifies Bob's *recursive proof*, and uses Bob's `output aggregation object` as the `input aggregation object` in her proof... Which in turn, generates another `output aggregation object`. +- Bob verifies Alice's proof and makes his own guess. In this circuit, he doesn't exactly *prove* the verification of Alice's proof. Instead, he *aggregates* his proof to Alice's proof. The actual verification is done when the full proof is verified, for example when using `nargo verify` or through the verifier smart contract. -One should notice that when Bob generates his first proof, he has no input aggregation object. Because he is not verifying an recursive proof, he has no `input aggregation object`. In this case, he may use zeros instead. +We can imagine recursive proofs a [relay race](https://en.wikipedia.org/wiki/Relay_race). The first runner doesn't have to receive the baton from anyone else, as he/she already starts with it. But when his/her turn is over, the next runner needs to receive it, run a bit more, and pass it along. Even though every runner could theoretically verify the baton mid-run (why not? 🏃🔍), only at the end of the race does the referee verify that the whole race is valid. -We can imagine the `aggregation object` as the baton in a [relay race](https://en.wikipedia.org/wiki/Relay_race). The first runner doesn't have to receive the baton from anyone else, as he/she already starts with it. But when his/her turn is over, the next runner needs to receive it, run a bit more, and pass it along. Even though every runner could theoretically verify the baton mid-run (why not? 🏃🔍), only at the end of the race does the referee verify that the whole race is valid. +::: ## Some architecture @@ -175,3 +170,7 @@ In this example, a regulator could verify that taxes were paid for a specific pu At the time of writing, verifying recursive proofs is surprisingly fast. This is because most of the time is spent on generating the verification key that will be used to generate the next proof. So you are able to cache the verification key and reuse it later. Currently, Noir JS packages don't expose the functionality of loading proving and verification keys, but that feature exists in the underlying `bb.js` package. + +## How can I try it + +Learn more about using recursion in Nargo and NoirJS in the [how-to guide](../how_to/how-to-recursion.md) and see a full example in [noir-examples](https://github.com/noir-lang/noir-examples). diff --git a/noir/docs/versioned_docs/version-v0.23.0/how_to/how-to-recursion.md b/noir/docs/versioned_docs/version-v0.23.0/how_to/how-to-recursion.md index 39db23f1f3a..f34647a99d5 100644 --- a/noir/docs/versioned_docs/version-v0.23.0/how_to/how-to-recursion.md +++ b/noir/docs/versioned_docs/version-v0.23.0/how_to/how-to-recursion.md @@ -108,11 +108,7 @@ This call takes the public inputs and the proof, but also the public inputs coun :::info -The `proofAsFields` has a constant size `[Field; 93]`. However, currently the backend doesn't remove the public inputs from the proof when converting it. - -This means that if your `main` circuit has two public inputs, then you should also modify the recursive circuit to accept a proof with the public inputs appended. This means that in our example, since `y` is a public input, our `proofAsFields` is of type `[Field; 94]`. - -Verification keys in Barretenberg are always of size 114. +The `proofAsFields` has a constant size `[Field; 93]` and verification keys in Barretenberg are always `[Field; 114]`. ::: @@ -136,7 +132,6 @@ const recursiveInputs = { proof: proofAsFields, // array of length 93 + size of public inputs publicInputs: [mainInput.y], // using the example above, where `y` is the only public input key_hash: vkHash, - input_aggregation_object: Array(16).fill(0) // this circuit is verifying a non-recursive proof, so there's no input aggregation object: just use zero } const { witness, returnValue } = noir.execute(recursiveInputs) // we're executing the recursive circuit now! @@ -144,7 +139,7 @@ const { proof, publicInputs } = backend.generateFinalProof(witness) const verified = backend.verifyFinalProof({ proof, publicInputs }) ``` -You can obviously chain this proof into another proof. In fact, if you're using recursive proofs, you're probably interested of using them this way! In that case, you should keep in mind the `returnValue`, as it will contain the `input_aggregation_object` for the next proof. +You can obviously chain this proof into another proof. In fact, if you're using recursive proofs, you're probably interested of using them this way! :::tip @@ -152,16 +147,16 @@ Managing circuits and "who does what" can be confusing. To make sure your naming ```js const circuits = { -main: mainJSON, -recursive: recursiveJSON + main: mainJSON, + recursive: recursiveJSON } const backends = { -main: new BarretenbergBackend(circuits.main), -recursive: new BarretenbergBackend(circuits.recursive) + main: new BarretenbergBackend(circuits.main), + recursive: new BarretenbergBackend(circuits.recursive) } const noir_programs = { -main: new Noir(circuits.main, backends.main), -recursive: new Noir(circuits.recursive, backends.recursive) + main: new Noir(circuits.main, backends.main), + recursive: new Noir(circuits.recursive, backends.recursive) } ``` diff --git a/noir/docs/versioned_docs/version-v0.23.0/noir/standard_library/recursion.md b/noir/docs/versioned_docs/version-v0.23.0/noir/standard_library/recursion.md index 67962082a8f..f252150c8b5 100644 --- a/noir/docs/versioned_docs/version-v0.23.0/noir/standard_library/recursion.md +++ b/noir/docs/versioned_docs/version-v0.23.0/noir/standard_library/recursion.md @@ -1,16 +1,16 @@ --- title: Recursive Proofs description: Learn about how to write recursive proofs in Noir. -keywords: [recursion, recursive proofs, verification_key, aggregation object, verify_proof] +keywords: [recursion, recursive proofs, verification_key, verify_proof] --- Noir supports recursively verifying proofs, meaning you verify the proof of a Noir program in another Noir program. This enables creating proofs of arbitrary size by doing step-wise verification of smaller components of a large proof. -The `verify_proof` function takes a verification key, proof and public inputs for a zk program, as well as a key hash and an input aggregation object. The key hash is used to check the validity of the verification key and the input aggregation object is required by some proving systems. The `verify_proof` function returns an output aggregation object that can then be fed into future iterations of the proof verification if required. +Read [the explainer on recursion](../../explainers/explainer-recursion.md) to know more about this function and the [guide on how to use it.](../../how_to/how-to-recursion.md) ```rust #[foreign(verify_proof)] -fn verify_proof(_verification_key : [Field], _proof : [Field], _public_input : Field, _key_hash : Field, _input_aggregation_object : [Field]) -> [Field] {} +fn verify_proof(_verification_key : [Field], _proof : [Field], _public_input : Field, _key_hash : Field) {} ``` :::info @@ -26,36 +26,29 @@ use dep::std; fn main( verification_key : [Field; 114], - proof : [Field; 94], + proof : [Field; 93], public_inputs : [Field; 1], key_hash : Field, - input_aggregation_object : [Field; 16], - proof_b : [Field; 94], -) -> pub [Field; 16] { - let output_aggregation_object_a = std::verify_proof( + proof_b : [Field; 93], +) { + std::verify_proof( verification_key.as_slice(), proof.as_slice(), public_inputs.as_slice(), - key_hash, - input_aggregation_object + key_hash ); - let output_aggregation_object = std::verify_proof( + std::verify_proof( verification_key.as_slice(), proof_b.as_slice(), public_inputs.as_slice(), - key_hash, - output_aggregation_object_a + key_hash ); - - let mut output = [0; 16]; - for i in 0..16 { - output[i] = output_aggregation_object[i]; - } - output } ``` +You can see a full example of recursive proofs in [this example recursion demo repo](https://github.com/noir-lang/noir-examples/tree/master/recursion). + ## Parameters ### `verification_key` @@ -68,23 +61,8 @@ The proof for the zk program that is being verified. ### `public_inputs` -These represent the public inputs of the proof we are verifying. They should be checked against in the circuit after construction of a new aggregation state. +These represent the public inputs of the proof we are verifying. ### `key_hash` A key hash is used to check the validity of the verification key. The circuit implementing this opcode can use this hash to ensure that the key provided to the circuit matches the key produced by the circuit creator. - -### `input_aggregation_object` - -An aggregation object is blob of data that the top-level verifier must run some proof system specific algorithm on to complete verification. The size is proof system specific and will be set by the backend integrating this opcode. The input aggregation object is only not `None` when we are verifying a previous recursive aggregation in the current circuit. If this is the first recursive aggregation there is no input aggregation object. It is left to the backend to determine how to handle when there is no input aggregation object. - -## Return value - -### `output_aggregation_object` - -This is the result of a recursive aggregation and is what will be fed into the next verifier. -The next verifier can either perform a final verification (returning true or false) or perform another recursive aggregation where this output aggregation object will be the input aggregation object of the next recursive aggregation. - -## Example - -You can see an example of how to do recursive proofs in [this example recursion demo repo](https://github.com/noir-lang/noir-examples/tree/master/recursion). diff --git a/noir/noir_stdlib/src/array.nr b/noir/noir_stdlib/src/array.nr index bcdf56dd7aa..87cf4167dac 100644 --- a/noir/noir_stdlib/src/array.nr +++ b/noir/noir_stdlib/src/array.nr @@ -3,10 +3,10 @@ // by the methods in the `slice` module impl [T; N] { #[builtin(array_len)] - pub fn len(_self: Self) -> Field {} + pub fn len(self) -> Field {} #[builtin(arraysort)] - pub fn sort(_self: Self) -> Self {} + pub fn sort(self) -> Self {} // Sort with a custom sorting function. pub fn sort_via(mut a: Self, ordering: fn[Env](T, T) -> bool) -> Self { diff --git a/noir/noir_stdlib/src/bigint.nr b/noir/noir_stdlib/src/bigint.nr index 14790f69241..9edd59359c1 100644 --- a/noir/noir_stdlib/src/bigint.nr +++ b/noir/noir_stdlib/src/bigint.nr @@ -7,21 +7,21 @@ struct BigInt { impl BigInt { #[builtin(bigint_add)] - pub fn bigint_add(_self: Self, _other: BigInt) -> BigInt { + pub fn bigint_add(self, other: BigInt) -> BigInt { } #[builtin(bigint_neg)] - pub fn bigint_neg(_self: Self, _other: BigInt) -> BigInt { + pub fn bigint_neg(self, other: BigInt) -> BigInt { } #[builtin(bigint_mul)] - pub fn bigint_mul(_self: Self, _other: BigInt) -> BigInt { + pub fn bigint_mul(self, other: BigInt) -> BigInt { } #[builtin(bigint_div)] - pub fn bigint_div(_self: Self, _other: BigInt) -> BigInt { + pub fn bigint_div(self, other: BigInt) -> BigInt { } #[builtin(bigint_from_le_bytes)] - pub fn from_le_bytes(_bytes: [u8], _modulus: [u8]) -> BigInt {} + pub fn from_le_bytes(bytes: [u8], modulus: [u8]) -> BigInt {} #[builtin(bigint_to_le_bytes)] - pub fn to_le_bytes(_self: Self) -> [u8] {} + pub fn to_le_bytes(self) -> [u8] {} } impl Add for BigInt { diff --git a/noir/noir_stdlib/src/ecdsa_secp256k1.nr b/noir/noir_stdlib/src/ecdsa_secp256k1.nr index 290ccba27e5..e8d9af2230f 100644 --- a/noir/noir_stdlib/src/ecdsa_secp256k1.nr +++ b/noir/noir_stdlib/src/ecdsa_secp256k1.nr @@ -1,10 +1,10 @@ #[foreign(ecdsa_secp256k1)] // docs:start:ecdsa_secp256k1 pub fn verify_signature( - _public_key_x: [u8; 32], - _public_key_y: [u8; 32], - _signature: [u8; 64], - _message_hash: [u8; N] + public_key_x: [u8; 32], + public_key_y: [u8; 32], + signature: [u8; 64], + message_hash: [u8; N] ) -> bool // docs:end:ecdsa_secp256k1 -{} +{} \ No newline at end of file diff --git a/noir/noir_stdlib/src/ecdsa_secp256r1.nr b/noir/noir_stdlib/src/ecdsa_secp256r1.nr index 390f8ed39d2..9fe932a2f3d 100644 --- a/noir/noir_stdlib/src/ecdsa_secp256r1.nr +++ b/noir/noir_stdlib/src/ecdsa_secp256r1.nr @@ -1,10 +1,10 @@ #[foreign(ecdsa_secp256r1)] // docs:start:ecdsa_secp256r1 pub fn verify_signature( - _public_key_x: [u8; 32], - _public_key_y: [u8; 32], - _signature: [u8; 64], - _message_hash: [u8; N] + public_key_x: [u8; 32], + public_key_y: [u8; 32], + signature: [u8; 64], + message_hash: [u8; N] ) -> bool // docs:end:ecdsa_secp256r1 -{} +{} \ No newline at end of file diff --git a/noir/noir_stdlib/src/field.nr b/noir/noir_stdlib/src/field.nr index fbd76a1e8a2..66fb50119f9 100644 --- a/noir/noir_stdlib/src/field.nr +++ b/noir/noir_stdlib/src/field.nr @@ -13,13 +13,13 @@ impl Field { } #[builtin(to_le_bits)] - fn __to_le_bits(_self: Self, _bit_size: u32) -> [u1] {} + fn __to_le_bits(self, _bit_size: u32) -> [u1] {} #[builtin(to_be_bits)] - fn __to_be_bits(_self: Self, _bit_size: u32) -> [u1] {} + fn __to_be_bits(self, bit_size: u32) -> [u1] {} #[builtin(apply_range_constraint)] - fn __assert_max_bit_size(_self: Self, _bit_size: u32) {} + fn __assert_max_bit_size(self, bit_size: u32) {} pub fn assert_max_bit_size(self: Self, bit_size: u32) { crate::assert_constant(bit_size); @@ -53,10 +53,10 @@ impl Field { // decompose `_self` into a `_result_len` vector over the `_radix` basis // `_radix` must be less than 256 #[builtin(to_le_radix)] - fn __to_le_radix(_self: Self, _radix: u32, _result_len: u32) -> [u8] {} + fn __to_le_radix(self, radix: u32, result_len: u32) -> [u8] {} #[builtin(to_be_radix)] - fn __to_be_radix(_self: Self, _radix: u32, _result_len: u32) -> [u8] {} + fn __to_be_radix(self, radix: u32, result_len: u32) -> [u8] {} // Returns self to the power of the given exponent value. diff --git a/noir/noir_stdlib/src/hash.nr b/noir/noir_stdlib/src/hash.nr index 4033e2a5365..cc864039a90 100644 --- a/noir/noir_stdlib/src/hash.nr +++ b/noir/noir_stdlib/src/hash.nr @@ -3,19 +3,19 @@ mod mimc; #[foreign(sha256)] // docs:start:sha256 -pub fn sha256(_input: [u8; N]) -> [u8; 32] +pub fn sha256(input: [u8; N]) -> [u8; 32] // docs:end:sha256 {} #[foreign(blake2s)] // docs:start:blake2s -pub fn blake2s(_input: [u8; N]) -> [u8; 32] +pub fn blake2s(input: [u8; N]) -> [u8; 32] // docs:end:blake2s {} #[foreign(blake3)] // docs:start:blake3 -pub fn blake3(_input: [u8; N]) -> [u8; 32] +pub fn blake3(input: [u8; N]) -> [u8; 32] // docs:end:blake3 {} @@ -32,7 +32,7 @@ pub fn pedersen_commitment(input: [Field; N]) -> PedersenPoint } #[foreign(pedersen_commitment)] -pub fn __pedersen_commitment_with_separator(_input: [Field; N], _separator: u32) -> [Field; 2] {} +pub fn __pedersen_commitment_with_separator(input: [Field; N], separator: u32) -> [Field; 2] {} pub fn pedersen_commitment_with_separator(input: [Field; N], separator: u32) -> PedersenPoint { let values = __pedersen_commitment_with_separator(input, separator); @@ -47,13 +47,13 @@ pub fn pedersen_hash(input: [Field; N]) -> Field } #[foreign(pedersen_hash)] -pub fn pedersen_hash_with_separator(_input: [Field; N], _separator: u32) -> Field {} +pub fn pedersen_hash_with_separator(input: [Field; N], separator: u32) -> Field {} -pub fn hash_to_field(_input: [Field; N]) -> Field { +pub fn hash_to_field(input: [Field; N]) -> Field { let mut inputs_as_bytes = []; for i in 0..N { - let input_bytes = _input[i].to_le_bytes(32); + let input_bytes = input[i].to_le_bytes(32); for i in 0..32 { inputs_as_bytes = inputs_as_bytes.push_back(input_bytes[i]); } @@ -65,7 +65,7 @@ pub fn hash_to_field(_input: [Field; N]) -> Field { #[foreign(keccak256)] // docs:start:keccak256 -pub fn keccak256(_input: [u8; N], _message_size: u32) -> [u8; 32] +pub fn keccak256(input: [u8; N], message_size: u32) -> [u8; 32] // docs:end:keccak256 {} diff --git a/noir/noir_stdlib/src/lib.nr b/noir/noir_stdlib/src/lib.nr index 90aff3c312b..5165d1ee07b 100644 --- a/noir/noir_stdlib/src/lib.nr +++ b/noir/noir_stdlib/src/lib.nr @@ -30,7 +30,7 @@ mod uint128; // Oracle calls are required to be wrapped in an unconstrained function // Thus, the only argument to the `println` oracle is expected to always be an ident #[oracle(print)] -unconstrained fn print_oracle(_with_newline: bool, _input: T) {} +unconstrained fn print_oracle(with_newline: bool, input: T) {} unconstrained pub fn print(input: T) { print_oracle(false, input); @@ -41,20 +41,20 @@ unconstrained pub fn println(input: T) { } #[foreign(recursive_aggregation)] -pub fn verify_proof(_verification_key: [Field], _proof: [Field], _public_inputs: [Field], _key_hash: Field) {} +pub fn verify_proof(verification_key: [Field], proof: [Field], public_inputs: [Field], key_hash: Field) {} // Asserts that the given value is known at compile-time. // Useful for debugging for-loop bounds. #[builtin(assert_constant)] -pub fn assert_constant(_x: T) {} +pub fn assert_constant(x: T) {} // from_field and as_field are private since they are not valid for every type. // `as` should be the default for users to cast between primitive types, and in the future // traits can be used to work with generic types. #[builtin(from_field)] -fn from_field(_x: Field) -> T {} +fn from_field(x: Field) -> T {} #[builtin(as_field)] -fn as_field(_x: T) -> Field {} +fn as_field(x: T) -> Field {} pub fn wrapping_add(x: T, y: T) -> T { crate::from_field(crate::as_field(x) + crate::as_field(y)) diff --git a/noir/noir_stdlib/src/scalar_mul.nr b/noir/noir_stdlib/src/scalar_mul.nr index 0e84b4f66fc..26378e4839a 100644 --- a/noir/noir_stdlib/src/scalar_mul.nr +++ b/noir/noir_stdlib/src/scalar_mul.nr @@ -26,8 +26,8 @@ impl Add for EmbeddedCurvePoint { #[foreign(fixed_base_scalar_mul)] // docs:start:fixed_base_embedded_curve pub fn fixed_base_embedded_curve( - _low: Field, - _high: Field + low: Field, + high: Field ) -> [Field; 2] // docs:end:fixed_base_embedded_curve {} diff --git a/noir/noir_stdlib/src/schnorr.nr b/noir/noir_stdlib/src/schnorr.nr index 025c3a0f921..33656254550 100644 --- a/noir/noir_stdlib/src/schnorr.nr +++ b/noir/noir_stdlib/src/schnorr.nr @@ -1,10 +1,10 @@ #[foreign(schnorr_verify)] // docs:start:schnorr_verify pub fn verify_signature( - _public_key_x: Field, - _public_key_y: Field, - _signature: [u8; 64], - _message: [u8; N] + public_key_x: Field, + public_key_y: Field, + signature: [u8; 64], + message: [u8; N] ) -> bool // docs:end:schnorr_verify -{} +{} \ No newline at end of file diff --git a/noir/noir_stdlib/src/slice.nr b/noir/noir_stdlib/src/slice.nr index a5a9a38ed53..aa4b73edc1a 100644 --- a/noir/noir_stdlib/src/slice.nr +++ b/noir/noir_stdlib/src/slice.nr @@ -3,34 +3,34 @@ impl [T] { /// new slice with a length one greater than the /// original unmodified slice. #[builtin(slice_push_back)] - pub fn push_back(_self: Self, _elem: T) -> Self { } + pub fn push_back(self, elem: T) -> Self { } /// Push a new element to the front of the slice, returning a /// new slice with a length one greater than the /// original unmodified slice. #[builtin(slice_push_front)] - pub fn push_front(_self: Self, _elem: T) -> Self { } + pub fn push_front(self, elem: T) -> Self { } /// Remove the last element of the slice, returning the /// popped slice and the element in a tuple #[builtin(slice_pop_back)] - pub fn pop_back(_self: Self) -> (Self, T) { } + pub fn pop_back(self) -> (Self, T) { } /// Remove the first element of the slice, returning the /// element and the popped slice in a tuple #[builtin(slice_pop_front)] - pub fn pop_front(_self: Self) -> (T, Self) { } + pub fn pop_front(self) -> (T, Self) { } /// Insert an element at a specified index, shifting all elements /// after it to the right #[builtin(slice_insert)] - pub fn insert(_self: Self, _index: Field, _elem: T) -> Self { } + pub fn insert(self, index: Field, elem: T) -> Self { } /// Remove an element at a specified index, shifting all elements /// after it to the left, returning the altered slice and /// the removed element #[builtin(slice_remove)] - pub fn remove(_self: Self, _index: Field) -> (Self, T) { } + pub fn remove(self, index: Field) -> (Self, T) { } // Append each element of the `other` slice to the end of `self`. // This returns a new slice and leaves both input slices unchanged. diff --git a/noir/noir_stdlib/src/string.nr b/noir/noir_stdlib/src/string.nr index e402abf9ab6..ad6fd19e2de 100644 --- a/noir/noir_stdlib/src/string.nr +++ b/noir/noir_stdlib/src/string.nr @@ -2,7 +2,7 @@ use crate::collections::vec::Vec; impl str { /// Converts the given string into a byte array #[builtin(str_as_bytes)] - pub fn as_bytes(_self: Self) -> [u8; N] { } + pub fn as_bytes(self) -> [u8; N] { } /// return a byte vector of the str content pub fn as_bytes_vec(self: Self) -> Vec { diff --git a/noir/noir_stdlib/src/test.nr b/noir/noir_stdlib/src/test.nr index 47b31f4acea..560cfde741c 100644 --- a/noir/noir_stdlib/src/test.nr +++ b/noir/noir_stdlib/src/test.nr @@ -1,17 +1,17 @@ #[oracle(create_mock)] -unconstrained fn create_mock_oracle(_name: str) -> Field {} +unconstrained fn create_mock_oracle(name: str) -> Field {} #[oracle(set_mock_params)] -unconstrained fn set_mock_params_oracle

(_id: Field, _params: P) {} +unconstrained fn set_mock_params_oracle

(id: Field, params: P) {} #[oracle(set_mock_returns)] -unconstrained fn set_mock_returns_oracle(_id: Field, _returns: R) {} +unconstrained fn set_mock_returns_oracle(id: Field, returns: R) {} #[oracle(set_mock_times)] -unconstrained fn set_mock_times_oracle(_id: Field, _times: u64) {} +unconstrained fn set_mock_times_oracle(id: Field, times: u64) {} #[oracle(clear_mock)] -unconstrained fn clear_mock_oracle(_id: Field) {} +unconstrained fn clear_mock_oracle(id: Field) {} struct OracleMock { id: Field, diff --git a/noir/scripts/bootstrap_native.sh b/noir/scripts/bootstrap_native.sh index 3e0e2ed853a..974f0edcfec 100755 --- a/noir/scripts/bootstrap_native.sh +++ b/noir/scripts/bootstrap_native.sh @@ -12,6 +12,12 @@ else export GIT_COMMIT=$(git rev-parse --verify HEAD) fi +# Check if the 'cargo' command is available in the system +if ! command -v cargo > /dev/null; then + echo "Cargo is not installed. Please install Cargo and the Rust toolchain." + exit 1 +fi + # Build native. if [ -n "${DEBUG:-}" ]; then cargo build From cd327d9a03eda3bbe567b1cf0ba56a44ab642afb Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Jos=C3=A9=20Pedro=20Sousa?= Date: Tue, 6 Feb 2024 13:23:42 +0000 Subject: [PATCH 14/16] finished refactoring react box, added npx script too --- boxes/.gitignore | 2 +- boxes/blank-react/.eslintrc.cjs | 77 +- boxes/blank-react/.gitignore | 3 +- boxes/blank-react/.prettierignore | 2 - boxes/blank-react/.prettierrc.json | 6 - boxes/blank-react/index.html | 25 + boxes/blank-react/package.json | 26 +- boxes/blank-react/src/@types/index.d.ts | 9 - .../app/components/contract_function_form.tsx | 188 ---- boxes/blank-react/src/app/components/index.ts | 2 - .../blank-react/src/app/components/popup.tsx | 26 - boxes/blank-react/src/app/contract.tsx | 145 --- boxes/blank-react/src/app/home.tsx | 37 - .../blank-react/src/app/hooks/useAccounts.tsx | 20 - .../blank-react/src/app/hooks/useContract.tsx | 33 - boxes/blank-react/src/app/index.css | 40 - boxes/blank-react/src/app/index.html | 13 - boxes/blank-react/src/app/index.tsx | 11 - boxes/blank-react/src/assets/aztec_logo.svg | 8 - boxes/blank-react/src/assets/check.svg | 3 - boxes/blank-react/src/assets/copy.svg | 34 - boxes/blank-react/src/assets/favicon.ico | Bin 4286 -> 0 bytes .../src/assets/soehne-leicht-kursiv.ttf | Bin 98204 -> 0 bytes .../src/assets/soehne-web-buch.woff2 | Bin 34312 -> 0 bytes .../assets/soehne-web-halbfett-kursiv.woff2 | Bin 37212 -> 0 bytes .../src/assets/soehne-web-halbfett.woff2 | Bin 34931 -> 0 bytes .../src/assets/soehne-web-kraftig.woff2 | Bin 32915 -> 0 bytes .../src/assets/soehne-web-leicht.woff2 | Bin 33891 -> 0 bytes boxes/blank-react/src/config.ts | 54 +- ...7411b6b375064925b8088b0cd141771-audit.json | 15 - .../log/aztec-sandbox-2024-02-01.debug.log | 5 - .../src/contracts/log/aztec-sandbox.debug.log | 1 - boxes/blank-react/src/contracts/src/main.nr | 25 +- boxes/blank-react/src/hooks/useContract.tsx | 39 + boxes/blank-react/src/hooks/useNumber.tsx | 35 + boxes/blank-react/src/index.tsx | 14 + boxes/blank-react/src/pages/contract.tsx | 58 ++ boxes/blank-react/src/pages/home.tsx | 18 + .../src/scripts/call_contract_function.ts | 21 - boxes/blank-react/src/scripts/index.ts | 4 - boxes/blank-react/src/scripts/util.ts | 36 - .../src/scripts/view_contract_function.ts | 17 - .../blank-react/tests/blank.contract.test.ts | 77 +- boxes/blank-react/tsconfig.json | 13 +- boxes/blank-react/webpack.config.js | 93 +- boxes/blank/tests/blank.contract.test.ts | 2 +- boxes/npx.js | 167 +++ boxes/package.json | 11 + boxes/token/.eslintrc.cjs | 64 -- boxes/token/.gitignore | 7 - boxes/token/.prettierignore | 2 - boxes/token/.prettierrc.json | 6 - boxes/token/README.md | 67 -- boxes/token/package.json | 103 -- boxes/token/postcss.config.cjs | 5 - boxes/token/src/@types/index.d.ts | 4 - .../contract_function_form.module.scss | 66 -- .../app/components/contract_function_form.tsx | 231 ----- .../token/src/app/components/copy.module.scss | 6 - boxes/token/src/app/components/copy.tsx | 28 - .../src/app/components/dropdown.module.scss | 68 -- boxes/token/src/app/components/dropdown.tsx | 83 -- boxes/token/src/app/components/index.ts | 2 - .../src/app/components/popup.module.scss | 27 - boxes/token/src/app/components/popup.tsx | 35 - .../src/app/components/select.module.scss | 65 -- boxes/token/src/app/components/select.tsx | 81 -- boxes/token/src/app/components/terms.tsx | 10 - .../components/wallet_dropdown.module.scss | 9 - .../src/app/components/wallet_dropdown.tsx | 60 -- boxes/token/src/app/contract.module.scss | 55 - boxes/token/src/app/contract.tsx | 154 --- boxes/token/src/app/home.module.scss | 28 - boxes/token/src/app/home.tsx | 91 -- boxes/token/src/app/index.css | 86 -- boxes/token/src/app/index.html | 13 - boxes/token/src/app/index.tsx | 11 - boxes/token/src/assets/aztec_logo.svg | 8 - boxes/token/src/assets/check.svg | 3 - boxes/token/src/assets/copy.svg | 34 - boxes/token/src/assets/favicon.ico | Bin 4286 -> 0 bytes .../token/src/assets/soehne-leicht-kursiv.ttf | Bin 98204 -> 0 bytes boxes/token/src/assets/soehne-web-buch.woff2 | Bin 34312 -> 0 bytes .../assets/soehne-web-halbfett-kursiv.woff2 | Bin 37212 -> 0 bytes .../src/assets/soehne-web-halbfett.woff2 | Bin 34931 -> 0 bytes .../token/src/assets/soehne-web-kraftig.woff2 | Bin 32915 -> 0 bytes .../token/src/assets/soehne-web-leicht.woff2 | Bin 33891 -> 0 bytes boxes/token/src/config.ts | 30 - boxes/token/src/contracts/Nargo.toml | 11 - boxes/token/src/contracts/src/main.nr | 385 ------- boxes/token/src/contracts/src/types.nr | 4 - .../src/contracts/src/types/balance_set.nr | 113 --- .../src/contracts/src/types/balances_map.nr | 34 - .../src/contracts/src/types/token_note.nr | 123 --- .../contracts/src/types/transparent_note.nr | 104 -- .../src/scripts/call_contract_function.ts | 21 - boxes/token/src/scripts/deploy_contract.ts | 26 - boxes/token/src/scripts/index.ts | 4 - boxes/token/src/scripts/util.ts | 51 - .../src/scripts/view_contract_function.ts | 17 - boxes/token/src/typings.d.ts | 4 - boxes/token/tailwind.config.cjs | 39 - boxes/token/tests/token.contract.test.ts | 953 ------------------ boxes/token/tests/token_simulator.ts | 95 -- boxes/token/tsconfig.json | 24 - boxes/token/webpack.config.js | 105 -- boxes/yarn.lock | 588 ++++++++++- 107 files changed, 1082 insertions(+), 4576 deletions(-) delete mode 100644 boxes/blank-react/.prettierignore delete mode 100644 boxes/blank-react/.prettierrc.json create mode 100644 boxes/blank-react/index.html delete mode 100644 boxes/blank-react/src/@types/index.d.ts delete mode 100644 boxes/blank-react/src/app/components/contract_function_form.tsx delete mode 100644 boxes/blank-react/src/app/components/index.ts delete mode 100644 boxes/blank-react/src/app/components/popup.tsx delete mode 100644 boxes/blank-react/src/app/contract.tsx delete mode 100644 boxes/blank-react/src/app/home.tsx delete mode 100644 boxes/blank-react/src/app/hooks/useAccounts.tsx delete mode 100644 boxes/blank-react/src/app/hooks/useContract.tsx delete mode 100644 boxes/blank-react/src/app/index.css delete mode 100644 boxes/blank-react/src/app/index.html delete mode 100644 boxes/blank-react/src/app/index.tsx delete mode 100644 boxes/blank-react/src/assets/aztec_logo.svg delete mode 100644 boxes/blank-react/src/assets/check.svg delete mode 100644 boxes/blank-react/src/assets/copy.svg delete mode 100644 boxes/blank-react/src/assets/favicon.ico delete mode 100644 boxes/blank-react/src/assets/soehne-leicht-kursiv.ttf delete mode 100644 boxes/blank-react/src/assets/soehne-web-buch.woff2 delete mode 100644 boxes/blank-react/src/assets/soehne-web-halbfett-kursiv.woff2 delete mode 100644 boxes/blank-react/src/assets/soehne-web-halbfett.woff2 delete mode 100644 boxes/blank-react/src/assets/soehne-web-kraftig.woff2 delete mode 100644 boxes/blank-react/src/assets/soehne-web-leicht.woff2 delete mode 100644 boxes/blank-react/src/contracts/log/.b7ec86e927411b6b375064925b8088b0cd141771-audit.json delete mode 100644 boxes/blank-react/src/contracts/log/aztec-sandbox-2024-02-01.debug.log delete mode 120000 boxes/blank-react/src/contracts/log/aztec-sandbox.debug.log create mode 100644 boxes/blank-react/src/hooks/useContract.tsx create mode 100644 boxes/blank-react/src/hooks/useNumber.tsx create mode 100644 boxes/blank-react/src/index.tsx create mode 100644 boxes/blank-react/src/pages/contract.tsx create mode 100644 boxes/blank-react/src/pages/home.tsx delete mode 100644 boxes/blank-react/src/scripts/call_contract_function.ts delete mode 100644 boxes/blank-react/src/scripts/index.ts delete mode 100644 boxes/blank-react/src/scripts/util.ts delete mode 100644 boxes/blank-react/src/scripts/view_contract_function.ts create mode 100755 boxes/npx.js delete mode 100644 boxes/token/.eslintrc.cjs delete mode 100644 boxes/token/.gitignore delete mode 100644 boxes/token/.prettierignore delete mode 100644 boxes/token/.prettierrc.json delete mode 100644 boxes/token/README.md delete mode 100644 boxes/token/package.json delete mode 100644 boxes/token/postcss.config.cjs delete mode 100644 boxes/token/src/@types/index.d.ts delete mode 100644 boxes/token/src/app/components/contract_function_form.module.scss delete mode 100644 boxes/token/src/app/components/contract_function_form.tsx delete mode 100644 boxes/token/src/app/components/copy.module.scss delete mode 100644 boxes/token/src/app/components/copy.tsx delete mode 100644 boxes/token/src/app/components/dropdown.module.scss delete mode 100644 boxes/token/src/app/components/dropdown.tsx delete mode 100644 boxes/token/src/app/components/index.ts delete mode 100644 boxes/token/src/app/components/popup.module.scss delete mode 100644 boxes/token/src/app/components/popup.tsx delete mode 100644 boxes/token/src/app/components/select.module.scss delete mode 100644 boxes/token/src/app/components/select.tsx delete mode 100644 boxes/token/src/app/components/terms.tsx delete mode 100644 boxes/token/src/app/components/wallet_dropdown.module.scss delete mode 100644 boxes/token/src/app/components/wallet_dropdown.tsx delete mode 100644 boxes/token/src/app/contract.module.scss delete mode 100644 boxes/token/src/app/contract.tsx delete mode 100644 boxes/token/src/app/home.module.scss delete mode 100644 boxes/token/src/app/home.tsx delete mode 100644 boxes/token/src/app/index.css delete mode 100644 boxes/token/src/app/index.html delete mode 100644 boxes/token/src/app/index.tsx delete mode 100644 boxes/token/src/assets/aztec_logo.svg delete mode 100644 boxes/token/src/assets/check.svg delete mode 100644 boxes/token/src/assets/copy.svg delete mode 100644 boxes/token/src/assets/favicon.ico delete mode 100644 boxes/token/src/assets/soehne-leicht-kursiv.ttf delete mode 100644 boxes/token/src/assets/soehne-web-buch.woff2 delete mode 100644 boxes/token/src/assets/soehne-web-halbfett-kursiv.woff2 delete mode 100644 boxes/token/src/assets/soehne-web-halbfett.woff2 delete mode 100644 boxes/token/src/assets/soehne-web-kraftig.woff2 delete mode 100644 boxes/token/src/assets/soehne-web-leicht.woff2 delete mode 100644 boxes/token/src/config.ts delete mode 100644 boxes/token/src/contracts/Nargo.toml delete mode 100644 boxes/token/src/contracts/src/main.nr delete mode 100644 boxes/token/src/contracts/src/types.nr delete mode 100644 boxes/token/src/contracts/src/types/balance_set.nr delete mode 100644 boxes/token/src/contracts/src/types/balances_map.nr delete mode 100644 boxes/token/src/contracts/src/types/token_note.nr delete mode 100644 boxes/token/src/contracts/src/types/transparent_note.nr delete mode 100644 boxes/token/src/scripts/call_contract_function.ts delete mode 100644 boxes/token/src/scripts/deploy_contract.ts delete mode 100644 boxes/token/src/scripts/index.ts delete mode 100644 boxes/token/src/scripts/util.ts delete mode 100644 boxes/token/src/scripts/view_contract_function.ts delete mode 100644 boxes/token/src/typings.d.ts delete mode 100644 boxes/token/tailwind.config.cjs delete mode 100644 boxes/token/tests/token.contract.test.ts delete mode 100644 boxes/token/tests/token_simulator.ts delete mode 100644 boxes/token/tsconfig.json delete mode 100644 boxes/token/webpack.config.js diff --git a/boxes/.gitignore b/boxes/.gitignore index fd32083ab67..0b262eb6853 100644 --- a/boxes/.gitignore +++ b/boxes/.gitignore @@ -3,4 +3,4 @@ node_modules dest -src/contracts/target \ No newline at end of file +src/contracts/target diff --git a/boxes/blank-react/.eslintrc.cjs b/boxes/blank-react/.eslintrc.cjs index 8b84efd5d65..11d8d8c093c 100644 --- a/boxes/blank-react/.eslintrc.cjs +++ b/boxes/blank-react/.eslintrc.cjs @@ -1,63 +1,42 @@ module.exports = { root: true, env: { browser: true, es2020: true }, - parserOptions: { - project: './tsconfig.json', - }, extends: [ - 'eslint:recommended', - 'plugin:@typescript-eslint/recommended', - 'plugin:react-hooks/recommended', - 'plugin:import/recommended', - 'plugin:import/typescript', - 'prettier', + "eslint:recommended", + "plugin:@typescript-eslint/recommended", + "plugin:react-hooks/recommended", + "plugin:import/recommended", + "plugin:import/typescript", + "prettier", ], settings: { - 'import/resolver': { + "import/resolver": { typescript: true, node: true, }, }, - ignorePatterns: ['dest', 'webpack.config.js', '.eslintrc.cjs'], - parser: '@typescript-eslint/parser', - plugins: ['react-refresh'], - overrides: [ - { - files: ['*.ts', '*.tsx'], - parserOptions: { - project: true, - }, - }, - ], + ignorePatterns: ["dest", "webpack.config.js", ".eslintrc.cjs"], + parser: "@typescript-eslint/parser", + plugins: ["react-refresh"], rules: { - 'react-refresh/only-export-components': ['warn', { allowConstantExport: true }], - '@typescript-eslint/explicit-module-boundary-types': 'off', - '@typescript-eslint/no-non-null-assertion': 'off', - '@typescript-eslint/no-explicit-any': 'off', - '@typescript-eslint/no-empty-function': 'off', - '@typescript-eslint/await-thenable': 'error', - '@typescript-eslint/no-floating-promises': 2, - '@typescript-eslint/no-unused-vars': ['error', { argsIgnorePattern: '^_', varsIgnorePattern: '^_' }], - 'require-await': 2, - 'no-console': 'warn', - 'no-constant-condition': 'off', - camelcase: 2, - 'no-restricted-imports': [ - 'error', - { - patterns: [ - { - group: ['client-dest'], - message: "Fix this absolute garbage import. It's your duty to solve it before it spreads.", - }, - { - group: ['dest'], - message: 'You should not be importing from a build directory. Did you accidentally do a relative import?', - }, - ], - }, + "react-refresh/only-export-components": [ + "warn", + { allowConstantExport: true }, + ], + "@typescript-eslint/explicit-module-boundary-types": "off", + "@typescript-eslint/no-non-null-assertion": "off", + "@typescript-eslint/no-explicit-any": "off", + "@typescript-eslint/no-empty-function": "off", + "@typescript-eslint/await-thenable": "error", + "@typescript-eslint/no-unused-vars": [ + "error", + { argsIgnorePattern: "^_", varsIgnorePattern: "^_" }, ], - 'import/no-unresolved': 'error', - 'import/no-extraneous-dependencies': 'error', + "require-await": 2, + "no-console": "warn", + "no-constant-condition": "off", + camelcase: 2, + "import/no-unresolved": "error", + "import/no-extraneous-dependencies": "error", }, }; diff --git a/boxes/blank-react/.gitignore b/boxes/blank-react/.gitignore index d37f6988611..7e20050d72e 100644 --- a/boxes/blank-react/.gitignore +++ b/boxes/blank-react/.gitignore @@ -2,6 +2,7 @@ !.yarn/releases node_modules -dest +dist artifacts src/contracts/target +src/contracts/log diff --git a/boxes/blank-react/.prettierignore b/boxes/blank-react/.prettierignore deleted file mode 100644 index a72dfe54860..00000000000 --- a/boxes/blank-react/.prettierignore +++ /dev/null @@ -1,2 +0,0 @@ -src/artifacts/**/*.json -src/artifacts/**/*.ts \ No newline at end of file diff --git a/boxes/blank-react/.prettierrc.json b/boxes/blank-react/.prettierrc.json deleted file mode 100644 index 7c3bbec6848..00000000000 --- a/boxes/blank-react/.prettierrc.json +++ /dev/null @@ -1,6 +0,0 @@ -{ - "singleQuote": true, - "trailingComma": "all", - "printWidth": 120, - "arrowParens": "avoid" -} diff --git a/boxes/blank-react/index.html b/boxes/blank-react/index.html new file mode 100644 index 00000000000..7e9084bc121 --- /dev/null +++ b/boxes/blank-react/index.html @@ -0,0 +1,25 @@ + + + + + + Private Token Noir Smart Contract + + +

+ + + + diff --git a/boxes/blank-react/package.json b/boxes/blank-react/package.json index bc074a55f52..ea02f332c28 100644 --- a/boxes/blank-react/package.json +++ b/boxes/blank-react/package.json @@ -3,15 +3,15 @@ "private": true, "version": "0.1.0", "type": "module", - "main": "./dest/index.js", + "main": "./dist/index.js", "scripts": { "compile": "cd src/contracts && ${AZTEC_NARGO:-aztec-nargo} compile", "codegen": "${AZTEC_CLI:-aztec-cli} codegen src/contracts/target -o artifacts --ts", "build": "yarn clean && yarn compile && yarn codegen && tsc -b && webpack", - "clean": "rm -rf ./dest .tsbuildinfo ./artifacts ./src/contracts/target", + "clean": "rm -rf ./dist .tsbuildinfo ./artifacts ./src/contracts/target", "prep": "yarn clean && yarn compile && yarn codegen", - "dev": "yarn prep && webpack serve --mode development", - "serve": "serve -p 3000 ./dest", + "dev": "webpack serve --mode development", + "serve": "serve -p 3000 ./dist", "formatting": "prettier --check ./src && eslint ./src", "formatting:fix": "prettier -w ./src", "test": "NODE_NO_WARNINGS=1 node --experimental-vm-modules $(yarn bin jest) --runInBand" @@ -62,10 +62,12 @@ "eslint-plugin-prettier": "^5.0.1", "eslint-plugin-react-hooks": "^4.6.0", "eslint-plugin-react-refresh": "^0.4.3", + "html-webpack-plugin": "^5.6.0", "jest": "^29.6.4", "postcss": "^8.4.29", "postcss-loader": "^7.3.3", "prettier": "^3.1.1", + "react-toastify": "^10.0.4", "resolve-typescript-plugin": "^2.0.1", "stream-browserify": "^3.0.0", "style-loader": "^3.3.3", @@ -79,23 +81,11 @@ "webpack-cli": "^5.1.4", "webpack-dev-server": "^4.15.1" }, - "browserslist": { - "production": [ - ">0.5%", - "not dead", - "not op_mini all" - ], - "development": [ - "last 1 chrome version", - "last 1 firefox version", - "last 1 safari version" - ] - }, "files": [ - "dest", + "dist", "src", "!*.test.*" ], - "types": "./dest/index.d.ts", + "types": "./dist/index.d.ts", "packageManager": "yarn@4.0.2" } diff --git a/boxes/blank-react/src/@types/index.d.ts b/boxes/blank-react/src/@types/index.d.ts deleted file mode 100644 index 3598bbcb0be..00000000000 --- a/boxes/blank-react/src/@types/index.d.ts +++ /dev/null @@ -1,9 +0,0 @@ -declare module '*.svg' { - const content: any; - export default content; -} - -declare module '*.module.scss' { - const content: { [className: string]: string }; - export = content; -} diff --git a/boxes/blank-react/src/app/components/contract_function_form.tsx b/boxes/blank-react/src/app/components/contract_function_form.tsx deleted file mode 100644 index 8623cf0779a..00000000000 --- a/boxes/blank-react/src/app/components/contract_function_form.tsx +++ /dev/null @@ -1,188 +0,0 @@ -// import { useEffect } from 'react'; -// import { CONTRACT_ADDRESS_PARAM_NAMES, pxe } from '../../config.js'; -// import { callContractFunction, viewContractFunction } from '../../scripts/index.js'; -// import { convertArgs } from '../../scripts/util.js'; -// import { AztecAddress, CompleteAddress, ContractArtifact, Fr, FunctionArtifact } from '@aztec/aztec.js'; -// import { useFormik } from 'formik'; -// import * as Yup from 'yup'; - -// type NoirFunctionYupSchema = { -// // hack: add `any` at the end to get the array schema to typecheck -// // eslint-disable-next-line @typescript-eslint/no-explicit-any -// [key: string]: Yup.NumberSchema | Yup.ArraySchema | Yup.BooleanSchema | any; -// }; - -// type NoirFunctionFormValues = { -// [key: string]: string | number | number[] | boolean; -// }; - -// function generateYupSchema(functionAbi: FunctionArtifact, defaultAddress: string) { -// const parameterSchema: NoirFunctionYupSchema = {}; -// const initialValues: NoirFunctionFormValues = {}; -// for (const param of functionAbi.parameters) { -// if (CONTRACT_ADDRESS_PARAM_NAMES.includes(param.name)) { -// // these are hex strings instead, but yup doesn't support bigint so we convert back to bigint on execution -// parameterSchema[param.name] = Yup.string().required(); -// initialValues[param.name] = defaultAddress; -// continue; -// } -// switch (param.type.kind) { -// case 'field': -// parameterSchema[param.name] = Yup.number().required(); -// initialValues[param.name] = 100; -// break; -// // not really needed for private token, since we hide the nullifier helper method which has the array input -// case 'array': -// // eslint-disable-next-line no-case-declarations -// const arrayLength = param.type.length; -// parameterSchema[param.name] = Yup.array() -// .of(Yup.number()) -// .min(arrayLength) -// .max(arrayLength) -// .transform(function (value: number[], originalValue: string) { -// if (typeof originalValue === 'string') { -// return originalValue.split(',').map(Number); -// } -// return value; -// }); -// initialValues[param.name] = Array(arrayLength).fill( -// CONTRACT_ADDRESS_PARAM_NAMES.includes(param.name) ? defaultAddress : 200, -// ); -// break; -// case 'boolean': -// parameterSchema[param.name] = Yup.boolean().required(); -// initialValues[param.name] = false; -// break; -// } -// } -// return { validationSchema: Yup.object().shape(parameterSchema), initialValues }; -// } - -// async function handleFunctionCall( -// contractAddress: AztecAddress | undefined, -// contractArtifact: ContractArtifact, -// functionName: string, -// args: any, -// wallet: CompleteAddress, -// ) { -// const functionAbi = contractArtifact.functions.find(f => f.name === functionName)!; -// const typedArgs: any[] = convertArgs(functionAbi, args); - -// if (functionName === 'constructor' && !!wallet) { -// if (functionAbi === undefined) { -// throw new Error('Cannot find constructor in the ABI.'); -// } -// // hack: addresses are stored as string in the form to avoid bigint compatibility issues with formik -// // convert those back to bigints before sending - -// // for now, dont let user change the salt. requires some change to the form generation if we want to let user choose one -// // since everything is currently based on parsing the contractABI, and the salt parameter is not present there -// const salt = Fr.random(); -// return await deployContract(wallet, contractArtifact, typedArgs, salt, pxe.getPxe()); -// } - -// if (functionAbi.functionType === 'unconstrained') { -// return await viewContractFunction( -// contractAddress!, -// contractArtifact, -// functionName, -// typedArgs, -// pxe.getPxe(), -// wallet, -// ); -// } else { -// const txnReceipt = await callContractFunction( -// contractAddress!, -// contractArtifact, -// functionName, -// typedArgs, -// pxe.getPxe(), -// wallet, -// ); -// return `Transaction ${txnReceipt.status} on block number ${txnReceipt.blockNumber}`; -// } -// } - -// interface ContractFunctionFormProps { -// wallet: CompleteAddress; -// contractAddress?: AztecAddress; -// contractArtifact: ContractArtifact; -// functionArtifact: FunctionArtifact; -// defaultAddress: string; -// title?: string; -// buttonText?: string; -// isLoading: boolean; -// disabled: boolean; -// onSubmit: () => void; -// onSuccess: (result: any) => void; -// onError: (msg: string) => void; -// } - -// export function ContractFunctionForm({ -// wallet, -// contractAddress, -// contractArtifact, -// functionArtifact, -// defaultAddress, -// buttonText = 'Submit', -// isLoading, -// disabled, -// onSubmit, -// onSuccess, -// onError, -// }: ContractFunctionFormProps) { -// const { validationSchema, initialValues } = generateYupSchema(functionArtifact, defaultAddress); -// const formik = useFormik({ -// initialValues: initialValues, -// validationSchema: validationSchema, -// onSubmit: async (values: any) => { -// onSubmit(); -// try { -// const result = await handleFunctionCall( -// contractAddress, -// contractArtifact, -// functionArtifact.name, -// values, -// wallet, -// ); -// onSuccess(result); -// } catch (e: any) { -// onError(e.message); -// } -// }, -// }); - -// useEffect(() => { -// console.log(functionArtifact); -// }, []); - -// return ( -//
-// {functionArtifact.parameters.map(input => ( -//
-// -// -// {formik.touched[input.name] && formik.errors[input.name] && ( -//
{formik.errors[input.name]?.toString()}
-// )} -//
-// ))} -// {isLoading ? ( -//
loading...
-// ) : ( -// -// )} -//
-// ); -// } diff --git a/boxes/blank-react/src/app/components/index.ts b/boxes/blank-react/src/app/components/index.ts deleted file mode 100644 index 1e6803a9fd1..00000000000 --- a/boxes/blank-react/src/app/components/index.ts +++ /dev/null @@ -1,2 +0,0 @@ -export * from './contract_function_form.js'; -export * from './popup.js'; diff --git a/boxes/blank-react/src/app/components/popup.tsx b/boxes/blank-react/src/app/components/popup.tsx deleted file mode 100644 index 36be1e5852c..00000000000 --- a/boxes/blank-react/src/app/components/popup.tsx +++ /dev/null @@ -1,26 +0,0 @@ -interface Props { - children: string; - buttonText?: string; - isWarning?: boolean; - onClose?: () => void; -} - -export function Popup({ children, buttonText = 'Close', isWarning = false, onClose }: Props) { - return ( -
- {isWarning && ( - - )} -
{children}
- -
- ); -} diff --git a/boxes/blank-react/src/app/contract.tsx b/boxes/blank-react/src/app/contract.tsx deleted file mode 100644 index 69c37ace90c..00000000000 --- a/boxes/blank-react/src/app/contract.tsx +++ /dev/null @@ -1,145 +0,0 @@ -import { contractArtifact } from '../config.js'; -// import { ContractFunctionForm, Popup } from './components/index.js'; -import { AztecAddress, CompleteAddress, FunctionArtifact } from '@aztec/aztec.js'; -import { ReactNode, useEffect, useState } from 'react'; - -const functionTypeSortOrder = { - secret: 0, - open: 1, - unconstrained: 2, -}; - -interface Props { - wallet: CompleteAddress; -} - -export function Contract({ address }: { address: AztecAddress }) { - useEffect(() => { - console.log(address); - }, []); - - const showForm = ({ currentTarget }: React.FormEvent) => { - const index = parseInt(currentTarget.value); - console.log(contractArtifact.functions[index]); - }; - - return ( -
-

Name: {contractArtifact.name}

- - -
- ); -} - -// export function Contract({ wallet }: Props) { -// const [contractAddress, setContractAddress] = useState(); -// const [processingFunction, setProcessingFunction] = useState(''); -// const [errorMsg, setError] = useState(''); -// const [selectedFunctionIndex, setSelectedFunctionIndex] = useState(-1); -// const [result, setResult] = useState(''); - -// const handleSubmitForm = (functionName: string) => setProcessingFunction(functionName); -// const handleContractDeployed = (address: AztecAddress) => { -// setContractAddress(address); -// setResult(`Contract deployed at: ${address}`); -// }; -// const handleResult = (returnValues: any) => { -// // TODO: serialize returnValues to string according to the returnTypes defined in the function abi. -// setResult(`Return values: ${returnValues}`); -// }; -// const handleClosePopup = () => { -// setResult(''); -// setError(''); -// setProcessingFunction(''); -// }; - -// const constructorAbi = contractArtifact.functions.find(f => f.name === 'constructor')!; -// const hasResult = !!(result || errorMsg); - -// function renderCardContent(contractAddress?: AztecAddress): ReactNode { -// if (contractAddress) { -// const functions = contractArtifact.functions -// .filter(f => f.name !== 'constructor' && !f.isInternal) -// .sort((a, b) => functionTypeSortOrder[a.functionType] - functionTypeSortOrder[b.functionType]); - -// if (selectedFunctionIndex === -1) { -// return ( -//
-//
-//
{`${contractArtifact.name}`}
-//
-//
-// {functions.map((functionAbi: FunctionArtifact, index: number) => ( -// -// ))} -//
-//
-// ); -// } - -// const selectedFunctionAbi = functions[selectedFunctionIndex]; - -// return ( -// <> -// handleSubmitForm(selectedFunctionAbi.name)} -// onSuccess={handleResult} -// onError={setError} -// /> -// -// ); -// } - -// return ( -// handleSubmitForm('constructor')} -// onSuccess={handleContractDeployed} -// onError={setError} -// /> -// ); -// } - -// return ( -// <> -// {renderCardContent(contractAddress)} -// {!!(errorMsg || result) && ( -// -// {errorMsg || result} -// -// )} -// -// ); -// } diff --git a/boxes/blank-react/src/app/home.tsx b/boxes/blank-react/src/app/home.tsx deleted file mode 100644 index 56944311f20..00000000000 --- a/boxes/blank-react/src/app/home.tsx +++ /dev/null @@ -1,37 +0,0 @@ -import { pxe } from '../config.js'; -import { Contract } from './contract.js'; -import { useEffect, useRef, useState } from 'react'; -import { useAccount } from './hooks/useAccounts.js'; -import { convertArgs } from '../scripts/util.js'; -import { AztecAddress, CompleteAddress, ContractArtifact, DeployMethod, Fr, PXE } from '@aztec/aztec.js'; -import { useContract } from './hooks/useContract.js'; - -export function Home() { - const [deploymentAccount, setDeploymentAccount] = useState(); - const accounts = useAccount(); - const { deploy, contract } = useContract({ deployer: deploymentAccount }); - - const selectWallet = ({ currentTarget }: React.FormEvent) => { - if (!accounts) return; - const index = parseInt(currentTarget.value); - setDeploymentAccount(accounts[index]); - }; - - if (!contract) { - return ( -
- - - -
- ); - } - - return ; -} diff --git a/boxes/blank-react/src/app/hooks/useAccounts.tsx b/boxes/blank-react/src/app/hooks/useAccounts.tsx deleted file mode 100644 index 4dd99b395f3..00000000000 --- a/boxes/blank-react/src/app/hooks/useAccounts.tsx +++ /dev/null @@ -1,20 +0,0 @@ -import { pxe } from '../../config.js'; -import { CompleteAddress } from '@aztec/aztec.js'; -import { useEffect, useState } from 'react'; - -export function useAccount() { - const [accounts, setAccounts] = useState(); - - const getAccounts = async () => { - const acc = await pxe.getPxe().getRegisteredAccounts(); - setAccounts(acc || []); - }; - - useEffect(() => { - if (!accounts) { - getAccounts(); - } - }, []); - - return accounts; -} diff --git a/boxes/blank-react/src/app/hooks/useContract.tsx b/boxes/blank-react/src/app/hooks/useContract.tsx deleted file mode 100644 index 42fb47676d1..00000000000 --- a/boxes/blank-react/src/app/hooks/useContract.tsx +++ /dev/null @@ -1,33 +0,0 @@ -import { useState } from 'react'; -import { contractArtifact, pxe } from '../../config.js'; - -import { convertArgs } from '../../scripts/util.js'; -import { AztecAddress, CompleteAddress, Contract, DeployMethod, Fr } from '@aztec/aztec.js'; - -export function useContract({ deployer }: { deployer: CompleteAddress | undefined }) { - const [contract, setContract] = useState(); - - const deploy = async (e: React.FormEvent) => { - e.preventDefault(); - if (!deployer) return; - - const salt = Fr.random(); - const functionAbi = contractArtifact.functions.find(f => f.name === 'constructor')!; - const typedArgs: any[] = convertArgs(functionAbi, []); - - const tx = new DeployMethod( - deployer.publicKey, - pxe.getPxe(), - contractArtifact, - (a, w) => Contract.at(a, contractArtifact, w), - typedArgs, - ).send({ - contractAddressSalt: salt, - }); - - const receipt = await tx.wait(); - setContract(receipt.contractAddress); - }; - - return { deploy, contract }; -} diff --git a/boxes/blank-react/src/app/index.css b/boxes/blank-react/src/app/index.css deleted file mode 100644 index bcc32ed8c27..00000000000 --- a/boxes/blank-react/src/app/index.css +++ /dev/null @@ -1,40 +0,0 @@ -:root { - line-height: 1.5; - font-weight: 400; - - color-scheme: light dark; - color: rgba(255, 255, 255, 0.87); - background-color: #242424; - - text-rendering: optimizeLegibility; - -webkit-text-size-adjust: 100%; - overflow: hidden; - - --foreground-rgb: 0, 0, 0; - --background-start-rgb: 214, 219, 220; - --background-end-rgb: 255, 255, 255; - - color: #213547; - background-color: #ffffff; -} - -body { - color: rgb(var(--foreground-rgb)); - background: linear-gradient(#f6fbfc, #d8d4e7); -} - -#root { - width: 100%; - max-width: 1280px; - margin: 0 auto; - padding: 0rem; - text-align: center; -} - -body { - margin: 0; - display: flex; - place-items: center; - min-width: 320px; - min-height: 100vh; -} diff --git a/boxes/blank-react/src/app/index.html b/boxes/blank-react/src/app/index.html deleted file mode 100644 index 57ece0ccf91..00000000000 --- a/boxes/blank-react/src/app/index.html +++ /dev/null @@ -1,13 +0,0 @@ - - - - - - - Private Token Noir Smart Contract - - -
- - - diff --git a/boxes/blank-react/src/app/index.tsx b/boxes/blank-react/src/app/index.tsx deleted file mode 100644 index 451c1c89648..00000000000 --- a/boxes/blank-react/src/app/index.tsx +++ /dev/null @@ -1,11 +0,0 @@ -import { Home } from './home.js'; -import './index.css'; -import * as React from 'react'; -import * as ReactDOM from 'react-dom/client'; - -const root = ReactDOM.createRoot(document.getElementById('root') as HTMLElement); -root.render( - - - , -); diff --git a/boxes/blank-react/src/assets/aztec_logo.svg b/boxes/blank-react/src/assets/aztec_logo.svg deleted file mode 100644 index 64a3648326d..00000000000 --- a/boxes/blank-react/src/assets/aztec_logo.svg +++ /dev/null @@ -1,8 +0,0 @@ - - - - - - - - diff --git a/boxes/blank-react/src/assets/check.svg b/boxes/blank-react/src/assets/check.svg deleted file mode 100644 index cabb79ea3d4..00000000000 --- a/boxes/blank-react/src/assets/check.svg +++ /dev/null @@ -1,3 +0,0 @@ - - - \ No newline at end of file diff --git a/boxes/blank-react/src/assets/copy.svg b/boxes/blank-react/src/assets/copy.svg deleted file mode 100644 index 022ddbd0e91..00000000000 --- a/boxes/blank-react/src/assets/copy.svg +++ /dev/null @@ -1,34 +0,0 @@ - - - - - - - - - - - - \ No newline at end of file diff --git a/boxes/blank-react/src/assets/favicon.ico b/boxes/blank-react/src/assets/favicon.ico deleted file mode 100644 index 1c85cef482e601f1fe5e291df4e97a3aef958676..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 4286 zcmcJRv2KGf5QdEy7&=g?Qb*#kQ<4XWRK_Ig17!$p1Hj$6IXcY0Os7Ar&J z%yag+Ws^k_r?_#M-5c9%UC6S@R*$ivC6tCK>`)p8*eCL99>hI?4eM(k#@MJ{C=FBC zp)^R?zBRb8eQA)eciHlb`s@pASjyqllTvo4*Jo>RW&6-Dmpw-Vv2(DikA8Cu z^uuy)E3Wvr5`~{C6m+4|1HBSaVqz)1=1Ki7{1dOc5Cq73Mdt zjz3BLeo31}lO57}vq`E$78gw#MRiidRa_+n&!U>=JmYh`hSzco$8rwN#W~yD%wQHX GiTwx67ZB?J diff --git a/boxes/blank-react/src/assets/soehne-leicht-kursiv.ttf b/boxes/blank-react/src/assets/soehne-leicht-kursiv.ttf deleted file mode 100644 index f3dd8bc7bafcc290a8a5fa1b4d213a04da095a5a..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 98204 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 M(rVbzDo$(S68)HiJ^%m! diff --git a/boxes/blank-react/src/assets/soehne-web-halbfett-kursiv.woff2 b/boxes/blank-react/src/assets/soehne-web-halbfett-kursiv.woff2 deleted file mode 100644 index a31e1f70ab975d9104c253a99d4eb2fc11e51fef..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 37212 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 A9RL6T diff --git a/boxes/blank-react/src/assets/soehne-web-halbfett.woff2 b/boxes/blank-react/src/assets/soehne-web-halbfett.woff2 deleted file mode 100644 index 66ce02ba66925fc758e143ae583ce1ce1fd003ad..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 34931 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 H)0?;ef)Im^ diff --git a/boxes/blank-react/src/assets/soehne-web-kraftig.woff2 b/boxes/blank-react/src/assets/soehne-web-kraftig.woff2 deleted file mode 100644 index 412a9355afc86d3a4f388ad3b7031d428b1ceeff..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 32915 zcmV((K;XZ3Pew8T0RR910DzMK6aWAK0h`bO0DjK^0UDnG0Dn9H00CD300W``00000 z0000PIvatZcpRq!6odi>U>1vD0E&7DSP6o25fBQ4!vuqu0usas0X7081CMwMhkgJA zAO(zd2b*IIgHu~^fRO~sZab7vROrpBdYm0Te{mJcxV=E|-e*cll z{{R2~|Ic13GG5BH?D;2!Vmjal+~@7|DJTw#r7>KjUFVW6!9%#|9m>LPwIi|NeVoF3W&2Zk-s(kt-`tzO4N7)L%iJ`yw-cWlLUpOzdEHdp9B~ zj%dzi_mbmsiE~)+1k%(LvCyVY#V9q03Q`x$D-M-Z46it1UxIvtS=ABC0BqBWiGdQO zU;s*~v?xfFlA$7qsI#!KCd19%rY`HQ<6oAp|Lxmr>*w~W`Mt}QoI7ETvh5G0CJ)W& z>{8lY&rqB2t;VU~1}8BFhc_bJl2G^mJ54VJB!K$yo;;FmbaQ7dN&Xrhk|W7+eE=4~ za$JCm7G6&5O(tX`Zv*@f1st}GwO)miJvc9gHi(BGwdGd@!^$hjtjgses@*h_pNW$V z>(X4|nj!ITt~0rxu70K+-gd5VCI%rJ)|}HXmZGixm0D?!Gr$;_1r}If7l*~2-?RUF zZ@l<)F4qT{o$a;;HZA z{Tr~3@Irw_AQTyaeV^qM*6|jc#e4Z2*B#NGJa*;QlfNoYDwRs5Qi)U|kw_#raw8IZ zn#+Glvj=mqg(+-dOSwz9gu<{QMPC+$`hehAIKX924-tc6uRCLL4r_p2IWO&jOP$Qn zRh_C%RmX{lOmBPD=U@ApPLDnds;q_6XG41Q;pewr*7qdi?kIn zK+vWLfkOuX2pj|fx3L|w+Eqe~m}t|$wR&48KgS9eh$mXxVeK+h-H!qDo`ePXBumrn zL$SGr*93!uAaIax`@~^hdx#UY&>))Bw*5ASR`)HMDg%Sy$E`=}dd}*c!k(TPYzXB5 z@Xx;|a?S_3fZ@<{tQIRcAgka6r?|f_Rcns_0c;Z>M>GLgB}fx7E>zKz(pl1^Oi$RH zCh7G1{SS2i{@p^On;4ORD3VYll|WE|gzQ}aq+DQIH3bGIPSVaMX>$Z=yM~fB5cH%Z z&&HgNo6mFHaJ=m) zJ6*~@E`wE9=YN-$bhUGSN@>39Fr~o&=9SC6z$=J^*))8p&;T_MFlR zy0C-=VCFFzFS=3#c;NZZ)X!4fdgy6>`Bn>usc=ftnrMR%H0!eUC*LFj3$MGUP$(7$ zLsf>=%`Pai zlhkagz3#pcfgvOnQl2}~$TI@6LMDfzFj69f!I!X`!VFwU01-zFxAuR1+HA7Df4uFx%nYiMqz&8_f2)4VGxu~?e?3uLMz{mtQ1x%9eUlecyoCx6ee=pERm#Twb>vfiQ9l-~^yHgzj z5+E3b2kk8o5O4=ay-#F;#nvaeEI7!pU-S+m9|paobGJmtyz$O5s(OpfDmvw@_6uw7 zKm7>xV=cy6OQ6@XaM!Z&ek7Cl1n>D_w(59?XYvPvQJuv+F9s4lA+5HdP%#=C0`v`$ zfPq2ZArxvM7VsT6i8r-#eV$z@{QCNPYNvV8gmTSY<11@@JV{TQ)8OgKnPjUD&$;F7 zJI_1roX;JV$H()t8{gCyLNWZB)ttY-NN)l+iFaU8nv31dsmt`6G^5T140kJjj z^S`uRMhDxDGIf^Q4sY9u%c+a@^7ium_;~3#6bI|@9Q6skACKrX=BD;YJRWUu#Vb6L zZFm`z*kn~k<)aofgckOf3`AcdV#TREC{IXj@Ji)m*KUm2L04`LIH?tkFP?NQzII%j zukWv4ufMa=ch4xmuDFRNOaL4ri)jYoFacK+8q(^!%BZxEM`z+(k}|w<@)$)GoSHgG z)0kpA3EMZ~X`Hjvl8txyZGvlVn&_SsQ>9I& zxo^6M(O=#(Jn_s-c}mPt?zP35ytmvZ-}w5Y%O*W~Y@)n=A!4!41q9{%OT@=@}DM#_^X23?nS+;tMGM)II`w_~7 zpZxNUf74Arc5QTo7sq+go=uR1IIb`{1Z&z%q>-Ru~umDyX@nW z=9mN{*M^a(A}*4|GG%2R$*l?HN$Siy_aRY2pA8YX1ZyDhpW;lm1jc&l%bY~)xO3C- z+*he8&bD#*R+MDbN$Kl5XUKmY9Z<9yydPeuf{Q5lkl%|NI9IkZRdYi^P5|2gX{@af zyxR8$VkD?v8rWggEX@-(rP~zD%b@UKibOPZCX!VrPL8ui9xKDr^=SZiJ-Kqub_rr zHeryHa;2?MCCCY=uT| z-;-~q3l+{L(}5W+Zq3T*riaAwTpM!M>eX|i`~rhWBe}H*S0S%av&;5`LoQr+k6i&` z6QPQjkRU}Cd~t}<40BOUECWJsTyhPYdP!L3o1 zxNJ9%wP72!RVSgQM9UOWotw1uRL+kYUoiw*G6&pem$x_AzC5!1adZdb!Utfkl!M&j(EU8fC2*!g6qM}+S9;13wC4z@X(?f&l-6It1i`cIg~|+m{E+K zq=-CB^h+1(#XGTJ|l^G zWWdr>nx)#IrdoBe>{2I0?Rll*M#-&xw_7x0-JHjLc_KWQ7oPq<^`(vsT{auF&!bmL z!RWu-soSOtW@?|Ula;n}-|@)z*Tj~;ipSgr?dg9jDJVoz4d!nq_qcc2u7o)KMSv4- zs^ykh+mE#Ay+8uHgcsg2pGoOv={1n37De{Z)Zc`yq9h-Z@(l_jmnSv`%Yz|@;Z!&b zlH?~TX%fFifLgvIz)DXs&z{5GP-)JMhmrJPgVEqUYGe8gv&NTk0^tX)hp90?*TXCT z$e=}$av6An=kN=-1OLdA3rWr4WB)jx+ghcZ{Ndh6C~v5tasXwIU29=mQnQf$eBmX& zws3Tk>Ick75Z)yL=8LJl?0?&(v4x;IAf%Z?W<2%iTiHe;F^9CxcG7~G`oq9mL1+72#8YM%b8R30|>aM*3>)vXsP z?nhmeS!C>I-heFQFmC=}wKvW}vra9Q?v?z%#Go}uYjI|f`kf=<&yl^yWfTWeNt8?& zDo3}L4QzF9uk&eCPA7Fwvgp47PHXtcF}0xzKf@fc!AwAz1k9|GiPRhLwJ5%+$(D#-fL@A}}(!&!Bgkm|?|yi4$9cm+$3fyRV3zt})_| zW6cVpOY|f=sU~TH5frp^gjM*N7cqOK$P2xAj1IJBKyWqB1S_K|1)BHd2juRs+3il$ zx&*r0cruzJCzA{BzL)gDcaoa^)tyzIg!&7HLf^u-^lmtBgIqyx5EiV6x3rqvRQb>6 zTe)gz|B^}QM_O3%20ASqglB_2e6Kz-pZB1;`dUbRn+JmN~dU6Yp@+MFTDN`)XUVYl;6Nh zKm5679?xO#T!3Qn#BL#F^?8uxR!Z{Y^+~r(pRhc7A)iV6htXM}N=(cUptsgy-)!>e z-jsVut)}d5=_*}aJ9nzlx>_-lZe2UPnmZmtEx>8ZRbGK|EGBQOOb~7?YLqY~C@FBg zsaT6!1y5ZzD52{sR}3U_{PXvEXY$l%==OfGHrgr$Q7rm% zT(_6hmQ_!S#lF8xw`y)xo6R0$kWAr=9RLF1>24L&ONA25mL^HdK^cf$XVH4*x}i>{ z%3yS47QI-wSmVWE%~;EO%Q!b-1>Ww7ESAb7L)?C({>M8=ykKAR7$5Qc_aeA(BB-;m zUxMd7%X^@uavR*BLd{kXVy1+-662Uu`I1dJ%ZaX?fy+k<>e*f}{ez3rO6g(%bW@sf zLXY1@?Z~o)vXMEL4fe7GgRd(Lf$pqgk==_4bq7PCe=qQdLTb8=pH@Kla}*lwv8N5( ze=w}&eR3Gfx9M~IfYI55R)i2x3;g=f6RHYZLPi3gO~;i*-ZpgKf5UHpm?1N1Q8D3%Y^g+HKwJZ_)V0tVcg#QYpuREoP&h zG+GA3>HCN)*TX=@`%~n6*C29CKcf0!&QovR8_0i- z`;oBed62Jve=gDyN^pfHohr_g6`AL@3)q82k+W^Ldo#WP`*ZlCm$si7iy2TA3c)J4 zjqSLgH`>2p*xY~kP@M0!|Mr||X7hnZD9av3xVl~G;i;9y-mva>T2`6~W8)*N4@+#} zoqR!UA=I-m4KDU|sQZl(Y3#XI;a8(CCC9ust>bx7EL8&h+oh#z7Qm5)@}Tf&_WMs7 ztY=FR7MCUj3kyXnzm>%0RtXk5>#W_9U9yM9b^F|M$3gczal~^k#L4zrf=0h+{0YIM z2sBJlBP!IMN76#kgpOIZ2AUK~AN`oUF%qeI-2x?zSf^uEG>2S+LAmssp zgpLjlf%0=LW}=GuW0#;-aU%hbS_*}D49r@(6=XI7RM83@W6~`A1PIn)p&vUXT*SYt z21ped>Z=m`qXl%6ugMCif21<`p%29%ptN_O7&Bq|Z8)#;itG3G0D$By+{6I5|G`FW z7(OVj2-kGRY*Y#V)-auWD|Ek=pbVCQ)oM+HQJo(AKmI2}jfUnxiz#LKUsa`56l9d; z*jex=BLXe}tRU9%Dq7e7=^C?kOE_>(L1FxF*pLWXs^CU?Q6Z5i6vD=%IgUjyN{}Fu zuC6YiyDPxDYgvLb5yiove88qs}9gzV!* zC=sk5qMn61>tclYlgJ+^-Tjl|b%af>i#sIhyrCe_Wm?sgu2r}IRJ*EtfsBSS1areV zl(q+lb8o#82??-Sv9(VdnzowylpWk&JIJmt9hL+8shQF|EDS)OSH0M%6L5kQFpmcH zG$gMn&t)!_))MPNal@_81O>@Ma_}tQz5sZfjrh&N~!|g$c zqn(%gavUOm_{%^3Q_ekT7mBFMuWT7w*yrwSnBsXJzJBKulU30+vUJ!M>;LCa^@b2y z;KkDf#N||VjIA8;hEPUC66k^w@@l#!)=sB4gt0Odks&0hpsr_X<9zzl04|ZENlalW zMGbv3Tdy$Xt$0sdI8S!_jz#Ne zf7FN6rFZ6DfLMU({$a^#1M!Z#AdWI-OY;k+aq;j82#J6mCeRKu2ta}nhL|B05txfT zjG3@A#Zo&xTBa*+saE|OUw2o<>Nwe)ich_#ZRcU<#pj#nbMvq7tBdKS@v`dDcBqcI z$LP(JJGR|r!(Hm{N(mBCzbWKwxO`;;D<3H1nx29@8D!WL3?=X zYp~~9c)#t?KsK~o>28F~IpuI8WH+2;zR z6~>JVMOxc@LTW)F%Q|c5<8=;lP9;K{Mm%*93ar3+f7({mZdHhtQQyd_e*eLO&kN0j z%*+Z^BR7TWXAT*5UG`JTzd^O=1z|Bx!0XB;;%uhHe6i3Zq-6LAPrKL4 z5J@43q?R+B2_tJu^hL?VWn^WmG1lfuseHUFHX$)dJG!~DU{oKPRw3EjFqZk@y#Oal zpfue^sNliIr$!JCzL_qaFT0%6$}4|Wo<6~aChK-K_49{kJvt2`331!I-i>Z{tJ~SX z06CLZTV3@v)L2vAnro@Gw%Y5cGhbbG*HdqO&8t6uhdSI57~(Ae)cJ$1B<@W;ed~Ka z`q{63_ou)8tF2Fc?n__$01-1IW&dUhe*S?Bal%QL-Slj?0~Kd5L4q3vd;a)s$qhBE zb;%%xZ7ivSfd8VrT5_kl)a6chrL!IHMCZFuLW!M$7_wwV#?ELn%`#h{Ae)_b%WWAl zJ@-O(bL>MUgaOkh({TqeJ0$vNr_eZJpD7tF71DKWf@A zhS=$nuL+Q8uIlS(xSPGLZh;qFr;iK3^IZvcWQg#%Cm+0-#$29HgJ{p zz*S`pS7Se1tpjj%4#L%A3B#m*tPl7_;SH5jgGl*@f5e2Dv4&mp%tZrQ*?2kwv+v+Ag9w(i! zz@)3n9GUgV4%m!K*m>qVZ!kBvP4PO~z)v_+nZPT%5f?WWmJrWkVp&LnEL&>n96Z#w zbCtRkMQ*u%qgA`+sn#2Dr zR#|NwUw+ox$X@`BYa)?*i5AIAG)qCEiLInG-zM9w_tsnM-A;Lv_##Et=fpS3Pkfaf ziBC-mt)JE}>qp)9o$@E~Pf8N)@?WA&iW7h3Rb5A2r|M$$ZcjHsf)e_z?<9;eO3^~F zt1*=*IL!^10}DR9L3&$@NaTMEsX#(GB6efci~))X#?8z?Acbsv0t)VcMXGg>EXHu` zLLBsIw{FUwY7*A4A2&~0-YM(d12-vn<^JVvLqqY>@o09>@ z=3)5Cf-t8m)8HP%D!`kfdWr(Vxnw9AIP1VCL@i7HWxj^@4D&|hze60e``6d*5K<({ z`f>!ZM$)g?V7_2{fH!>q0`)Fd#{qLkk{~c9F#+K4`rkrt_(rqUi68$zUa@$3yMU(z zpC1PBm4LZ3)&dX1d$BMZyaG?hjUH}LyB7z2D~-E*aPjc)hfhF2NHij1ViFRD3=z=D z$;EZLdD=X@yhvmzbPJ6c28%6+Z{i6AB9TNQlPRN6X*4>Wv5b2(7*D;)xQJhH1$Gk@ z5)wYcB2^JlF)?v*i8CQtk(82_mX(p4tem{Of`Za2%1S#atEi|>RZU%8qYcfXrk0kr zwyuueboKQ04Gatojn`;s7TQFEMTGeEoW42+jWwjq0yMt0kWlo=R=d4M2 zGDhVhT?Hcuib|ybU}bn0CkT?1$>hxy9feX!Q8cY;<-6^hVOW;qtjm4xJ+wS{^ytZx zXU|^x;X*?efaR%Poaj8u&{`TsHj+D;&pKe2}wyQDe0zVYBI92a&q$W3N0vB z6_u2H`SR^sxn-4#imIxbn!3718=7TJEiG+r9Ua|v^-6mB`UVDuhDIG37mZC!001Bm zR0v!ELm*Hn3h(I2;~NAP_5(vLrH@LZMP=)fgE%lfh!LS*Eqk z(xx0UbB=|%WfxW{OKU3|Yg-$;ZtatH4)%@?PL9q!(E-kf5Ew-;6fYz4aFU=%nqsQi zT!v$LPT<7`shcQEimWPHtKL;Jbknd*yUXcfyN<{8dVGEToqZ|SPe1(f({I20>2H5` zsXzYttIa>{+H|yC=ho`brBk;qJ-YR_N8c{dORq1WM<`A|SRWG~1Oe7(Fra@AAB45m z0&hiyjlpMQzss(=B}GQVlQplY^w@lub9EXPi=YS|VYKiMdCULRCg^)%962U1gC-mT zu?u1N9(n}zXz7IHFK6p-Jrd+gG^!kAEH|gTI;lqt8XbXjjW~ zT1K`wG)tQHHh)O-`!z=ALIAu8@D>(YYGs!EpugxV=%4ys&#*8BFn7Q_Yz&xt4RnwL z-z!VnDf|DAwpBJ+?<2ddTg-AmVi&D(K`o)H_K3qLa@~GcdRSFXZt5h!Q%>d~j)Jbv5EHy~hvg@z0oHX=s2abp%)VzG~HH)+C@ zr6O@W_O&Ji5Dybk0bGa>#soMO0HlK#&$SKumwRT-ix4?L73%YC^Ly>uE`2XNJn0APWGi!qZShJ)rTSdNTYwj)y) za=NL|Ohm&i3|N=;U|Qs4QS5dq8asjQpr*W8X_YKJUBb;A;|V%^*$;HN$D)H4_O6$QLZwj8;@tK=(C zs7QdaM0`2$OR}Rl`Zb-(jab`^g z+;My1?oebZWb;uOBa223AL!BIuK8%C=$`=VK(irEE&m;Q%#!}i>b0^TCxxR!D>6WN z<6_IL7$aIBE0#eOmwnaolZ^1N@cQTBVHK`V&g046$W(p0$VGVjB}8xOZ{qQ9PxB!>%$m(Cw+pr~A-ZS1ly zrGu?>{UD5FM(;G(oLn0@E3Rgv>q}?o`D@r>3kZQi6Jc+?y8^letp5Xj0eHaO6+Gg3 z3hwx3;0E}Z7XSvZSB_bGZJ-eTua)Hebpjg!&4w{$xI^fm1k4P$W{c0(R50@0BGKb|1o>#%4ZTNw;2) z;NwVUY4+Zhf4H#$XXI$-gX~cYgK!Z~mjpoCrNdRO#I@9s~J}W2pDS%AyT^8dqgo83L?h_R^Ejl%< znZj$)h%v;X5MWEQZDpH_?^*|-B6d4xwrtIfcVg_~KK5rV$FDbn(pJN{eDcFtUbv^= zJ}XRW^mr9x-qM#tI2YZe_hilHaBYty07h;^I-L7tmh`oo0G-Uot!;Ji{A<)Leb8H7 z+o>#)=+B$az^$L5Zz;BPOKY3e){8Hz`Yy=Y>OnwN_KVDr0ME9FHl5bTI$kemPA<4Q z=MC*#v^wQ)jznsd&U;5H+mdzlSCj+8jS~NVPJKpy6`C(q___Ysx&(qJB$d|%Nflt> z_cpmy`hsw~(8i#7%X6DOu&!6SK-S~0&Q?Nb#uSQ8t!|suO32Xr9p9NfbY+A6BR&?v z!qGszp*@fP-os5}LZcH=uSuwyKEKZ&x#Ad76c{AZ3l@R7G14xoouIaad02wYcJGYO z_~L;2^J*6Abe7Z>f1OTu`2O-&DGA*5jWy2r)Ok=G&*|(?>g+6+JCJmX}@+0gC@E#^++)<@v1ayt&|OcljD?uoCa2I+~>}TYdj`K#zS#<&ol< zhsVNI{P7)sPyGYMC>$yInZDtnLC(Z!&hr|gbkGIM_Zraw{x$ocFKKCezR~t_v+eG( zF{{0QE7nvxP3%dDrVyucGP7+g%x^z#gXQuyLSJP`6!>1?tT}DMgGY)%`4yYo z*n{dmx1w)FQ8{6-Q2Ta-1Mrl?nWht|Bg6~Hr(gZ4gB!Uf_*6v*tVsA*q`|?@Ze};SnT?(DXfN1i$y9uW{73MEows_>Ihn(ZKT2avU|+ z>KfQRf`cGrhGh37TYebI_1>B~i{^3{A=H@2yoR&gw{_;+Q7QcYq~fitO{xZ`R^r4-2l}J~ zS!}Cnh-&a=!b)8YILD@)ty57BpV^i-us=|Nt+ssKy_CTs1Rxujs&0wVl@e+CqJ&jY zg;w!a7qgL7rZH{JiFq&jBu-8GovhA=O@s<*DB6hX8Mh({WqLG^C7w#&W&`pIH5*V$ z2M&dX6bYf!;WOEdn1#25|9ljwaJyt5KAaJVVZGSd*mIKREj*1QSXv2uJz%m}Mikzv zC^mpp!bvO!KFqsx6$ker-@xjo0bYG#F)YwV62aaWdEagWEWq}@)drSNDuji&{?r*P ztf>E~Z1L!JM7AY5JeDpwOP3!>wTNrPgvjGq!(VLGv-A$Q%SOg8T-Tkww~XF47bn(R zb9cG%+_r^9oa4cEqL}Oxb-{hKA{6hyq-PieZa(tSuM&Gcko1`;(`J1sk9 z{X@&mY#=KrPgkeA#j93_Rtu%cHX;sz;faQ;*7$ZH01pl#?PmcYf5_eJ{K0m{G2kKa z2RO3kasxXVv{l#Di4Aj8y0+1acW4WrNQ3>K6ehOF(Fj2z)XQjizW@nza(-Vw&H8wa zYSWFQz>d`@+1)#QO3p|nOKQfX(uP(TV4jt1~i}$65I|Lbdcj_dqeB}G}&xtz>KD5{VG5O1{-S;j3efVAN5Sz`wH)P2rf8WJ- zw}cSkGietL`YM$(>KkJiE)_4WZ9OD1497j(j5LN^#>}WXj72zdu3ZF}MWiT9gusSE zsO?N=iD;Oq{|WfeAOwFUb~&29Qya1`(*P=sv?_qFMp1tv42m@n-DN8a;X=I|hN}g6 zdBx)9JM#ke>;S4~2LXGHJobrAAo|4^*f1v0uk6ENw2(-CVigF(`1XE>+ren$trbu1 z^pYqb{%J1zT;mbC%7osSn(-&m%Jon?IVYrDvM1L^#7Q)|$CykfB_rskAz^gqx z$}$JS^Rk?dhIlm7JOgeLTuAM*!4YZK-;EqQjG=DfL^r3d(~}r>Cpmx4_x_2BznY@| z*_j$;f~IrVPmu;g>`Yl3ljatxPA3;cx1HZUx5>Sf-f4E^Nd`|=GPkpf6)5(ZoP5@7 z&nM$h{E+JX?aXphUAwuFl$#C;aYCJYZ50R2f^v>KdzyFoaSq^PjDUGA_Any(a6cXZOj_R(JQsF*ozjM}W3*o7kQX8~msJ^_?a8Sb^OY5A z!+Ha2O=bzo;!p5O3Cv#%jfnSorJj@_!{0pw%Gze&78}i*X@ywUl-;Ru_vPl8Qk@bTE&Q#H>lp93-NDThzUg{k6>gpe@?w59gr7qt6tApaFI& zV@`Z{9$-{XI_EaAbJ*7iTOIT7k!AipJZ|!LB~aH!;z#O}&wtaG#>#5**S#ueEJ;{X zW*)VATdjv&Y;Ot8;OF*g8F7YlK-g@gWMT+xGxX24_5+7@KNcF|#l%f>1b$nw?MAF# zTy3Pvngkmmp-_n_C&ADNL9LZ0FSvqX6pfgPhV47tcvfQ^Wz;|gkV}Cz3M2B>${I3-bSI|@ReR|Q1vDk+??#-n!{I5J=7Q)Z5xz{~61ecp-6yeuhjtq} zjwWckj&Wp6CmbczDA z|2tp6_t*Z-rqGvyH%CwvOr|XRHIF0FNm9j9T|Q52RjH=0@g?v?XxE!f=`Dw;W*h8{ zxnl@XU;WQ`Kl6K`P%!P}rq(<}IEfrqIUXB|apmWyRs51!j%7LuC3cNQVlSkNT*}3S z)rJ9h`?syOTu%1%bfDlsO=CX#-cKJleB9z^Z7pv2YIsstyY7)K<=0hsW^!Ie8FRdz zbk^=@PFvFM7|VJCk@3NL;#u1xVDxbkjj&liUi9&gZ<)78%GA$QZ06H7V(5GbT1+we zT3s;}j*61#cHsq+c0@#5ed}W>EC9jCaxFcbZh*_-G(;^_w4BDgU35CCGS8iXTz`$( z{^9vfg-q)!oVWx2x@`106|qgjUMrl8=7) zB_N})akRn86P6X@y3sOR7DoE^sscvHl7gl`LkqARsoqYQT!MjWpJVYD9IQ_wLEXlt z$DcizBjd0$g`i8v4sAES3#96hi1CbdJg@OdiLp;_wLWZVu|DMSeG0STbI@j96VqIg zMIF>t2(Twd0_bVJFA`GL1DcjT-0d5#0R^^V?&d%2SV6bB0T*2hzDy~u{2s#nJ z!xZp-fYw0)Cnw{w83WF4V7jOq#EA*4ytZ?vSOHo%zpYq3)YAj282`nLn1 z-jOMnmRXvMdFwS3*Kt_0DZMg9S|G8zl}qU@P^$_!Y7K{@)o{wIy>y8K($e%bpouFo z3`6B$A!wZ^aL$7*5dbX%4IU3KW1-Ph;O7blw`@h5&o|j+T-eXS^%BjwBtDbD;4{c{ z99_VSZlG$5)^VC5DqUT~=i?>5JSwJoO)bf#hdvz-ebTvM*M{Bn+=;N-uoCHo$#xgYtp$}Xx$;!s=d%UrM9I8a#3s1efzLXP#3`LMwIvx&oG$wv# zGJa-$;Z{b$R#{Rrp^4yYrOcvQeHEoFnmS|)-_9%Z&rNrpUcoJ*P{(ENy30aG1-%si!HCMX^Xb?_^!>R zxRz<9y_{K-kKv6FXcv!*Lpsi zFmLuNuk#|_t6tq+?5QRRWZmq`{_H@Bg1B(%VINyQcllh5s|)|Z8DC^?UfkM-X37*5 z6(OqAUe+eW&J6#oKi`rchBCT@Gv}r#Z4rpA11*>Cfw(%>pMG4^I+yqK%fXPPUkPSl zSFC)-GC3amw}9`>`pb3nHQAqJ*L;#q=$t)pBJ)J%p4rapkeQxYZ)IFOHCmwleR8{N zGjV}ZId|FoppR8t?Dc>xyvi(XUewwOtCaq>Aa{W_g8j3py+bXvwZkni`O{S*8H>)4 zGR0O+zXV;OMz#vgl811ecWq~a+P)upW1R+gB6TXwq4;0G04}C{T89@p zHMTllAz#7D1;xRSBUjujnE#^y){jdQQ}Xb-KDZr0GcK0amuWJMd8~XXjR2o~a%t{K zO`-rYZxz0nMkAP(6gyChN@u*U%l+lAy@l53;Z%9xuH3%Ye{j*N)tZlH zqGpC-W=1cQu8`_LXBNaYFN_YLIhivYGL84+L#JD=HZAhzRF3=|9Eh)!r}YQs2b}+i{>d5(I)kgwnH9Hs z6Sd>|7ffII8ONEzk+rTW7ocp0Gw$$?gR64KG%0z?N|T6h)L4NNsA&8(QdZb@FDzNo zrq{ZvOD)g9>Ok4=GRR6XLrNzWc_A^)sFeBpzRf@NOy4X>h-6>teqOOXudvw0Bi=vj zp@KvmIt{A=pP8KQ%Fh=&Idl2`v(#;Bu+Yso^Pn>A=Zdw(a$1#r=&l{r1}^lqg(-;xa&E0r-nP@+i?Bq(#tm=7LBd@ zUL@Y`TmLw5)<;R%Mm$TUjjfioNronpBpH<%nskcBS#7lWn9OSg4o1$|lCtZJH)&Ao`{eXI;mA!ma1<|%x)e5aGdN4<;Pc`zroKrSmt z_5pmVWCg+VXN+<_2WXX6>B%G#KQ+m8TwDEnb`Igt(M)+usZH2Qzon76q*y}c8F)gv zw7Dvx2#}BkjK(Xvor|7_>m^0=v*IL<1qiwYc4D4nFvbmIof?R za3X&9zo8qpHf$Z3{%%J~7w@w|xXfHA0VE_=ee;fk{{8f@Gk@*(H}y63o~#0S_Sj$w zuxHwC6vRzS?q~ zzsG;P`@{H5M2k6pLpt6LR1EYwdT)({(E_g7%;lL)yxBJM%E~R8LZlCsQjJ1Yr=Wa< zl3Kglnb@|hz7ABEYq2*;qCDz;2jdjOTxp)^ruXmv6O=@q1qo=j4_kd=clHF*?~vy} zk$D-$)Hq8jwc6ibl_d1EH&rHaxDp0SB;!E78(Ze6dVTssTcmlQBDUve%UyM|P=-odS%ASfzTeA_npeAf+ zAQog^*;4)c!Q=O@ej1mM{ud5YQt2v|yIdv3E~mV>(g}L!bIKA_rHvk=xxLnLY`Nrx?`=bW zVAaM|KcF>avUK_3OD&p2^7?XH7%o_Y5aDp>GP+uf#A16^k3wk8Y7xbok-nH^l@R4A z#k{ZTvtOox8aKoO5;ABR34nJO{PXBTN(EymEq#cgP)xmfX$*j}J#DygSn?WT6OTvq zu3p_l(!nO;uGJt#ZB4s$E_dCLXR2>4FJFnN$Xvj7rj>e;Ss&I@A2?f48g+L4Wpn>w z=N}b+xJ>D${7ms3hUm^+gR+}*fpfv9H10tbIXL6X!11v!wZrtF?Ik&r*DLpGUvSl4 zPt{rJxh+j8a<%+2BFi(<H>~N4RrW_mf5&Z4}Ua4a^tg5p?fuqtjFmaK~1-J$h^rq@83sg9fG@t^l~2Og+h~Vp{ia z$mBy7l1-;E6Sg#u91(2__^x(|B-P#RyxP$`$U9%>%L&}L!C%3&x9RNZwe7n71&TSp zwl?$)wzl;xY;51iFg`RQYC4rZ4`XRGYC7|Hmkn{l!KT^nog!CD5U}6qy5D|lowI;^ z|1Squ_yI1SVDaRNLY0&sbRzj5b!E==`S&9q^J?EX8_RgJsO9bOg5fiVFZ!+KFK^!6 zKT4{8pUEuyL1`0&0RZuF;R2fq);i&zYab{_7@A3BK9j~$h!cdfN!F|3aq$2^*a@I@ zb%IzSWz*U^xoaZ~uCwfQlYvJ;uj3pE^u)s7kdCqe+ki#-OC6;bgS96fO|CZu7-Nik zp$W(s-yA!4eAiLF9|$WT|L`E;`vE{?WIR-<2o*6@q`fcD0%<5BOdt}FI*~#l5J-_o zhfEOFPF{^5gdEC$B+|bW%D>Ao|B=@u?@oTduw%7*G@TEu;7+)AYFdko`QrnLSSIjc%wl*7y~035TANV=EQ9-?oyxy!1RKQ|v?o-aENKWCuN zg~BFu{Xl@(0W9k9)t80|swnrn6|wy=c~?Xw*47(19ev%81`xpg|0=ADGa0Hx3h*@uR zx6wk{Kl7>&;H%@t`o6mL<-`#vzdS|?Hp63}L`}I(7VW_E;vS2v+%Q|120a;>^xZz3r}vN9xXl?%Ljlt zf1cxIRfkgv=!)pr2#gj(=b>Ca`%mK&6F^6hsLK#1(I6<6DA|YplRraYWq{w08zwEg z`BY+)oS6Wg@B1W~`UbOOccRH!FUOccSa(oACA=CVx7MEoV;_(~qz731jXdi0C7;gE zoWTRS|^qJ30g&bW?y~$Kluj3dw-{^3S16`+81Ja}^{_PxmG!a|KE5xNaEk!@r3NjxR%>>OFV>~%}chCMCr{qc!G{h`mgP!`PN_s)X@G0~Xf z*CVUa*kXC1;=xNuWX*F?xn<98G;yn!dg}MSH%lpR!n;51{@snMY-0d7K*+y=JK{YJ z{{n>G4u40uGza3cC|PISoo@Q|QqMsZy&Svb4EujHDl94XM_SHuy+rY-fPr8bg~A+t zTEiHQl>$AS5OeES)$_`e_Rsoik!-to(N_Jg7RTHn`eePDzClO`u|daZOik*w<=JKcX>O^+-JOJCM~R* z=rA>@JJF~vaK7tElekV=R|kFT$X|p@$Lshx^oX2u+D%`Ff|^=R4^~$je%JlpyJ6?Y zdba%Z=bz|l#kHpoIWimoa<(pE%P^cdk)!ePC`EK84ctggX=578K;=g|^ z+X|Ub zUR5cogaa=MF)PtFkwvRe$dV!(dZn!H5C0xNBgQw*FDYm^)_oQ{gggV|jV(lgV^S@qg%20sKoQ`xXLui_Q8% zx!QQl!N)Y0R+O-nra2~UwraTU@Hk`{tMPZ(v9HF`0Zt0CY~m4BaqM zKbAAxQRif?Eup2-6g&<`$)h25j{ma)O*8XFA~TiTE=v;hh_0fC$VC7*zRuQi^8 zAmMN%1cHFW5zBXl-}MQJ3Y}xgI*)KCun1M|t9+KqGY#&Y3*-YSrx&<1S!GljQea=+ zO$9c~@LfLMx2k{LN%6gCn*00&q9{|-URjo_L)}bNqPKN_Pgnr~&QW)`%YcRqU<)9Q z2fV7*35pCW9f9`w5`OCYb47Rr+VBaJK*Hlm1Of_=M_CAHL5fUaOh-d5VfLrMUfkIS z*rYhvo0jF3^wPBHpWPVKDVf$Rzu-A>UzOiJaIp2D+=cph)6wpl zcU$uFuXa*Te-HGh&?yID^O#4q`0v4-Q{hM_M^LWQQWp+KSgzGAujp+0a;E84IM|V! z>i~l_IbpVn=6rp=I`aeD3VW3N+cLlKOF&{J_M?(LHKr9A13vq8*v0>G6eajtkgt|fQO;fPwAM@fpO(XftfrZ}LWNc5E$gb?*OH*mw7^61!Io`P&c@r5oR^*Gl zIxjEsQ=&XYyw$}At9jp|bic2XeoIyU_L3Tjg_VnshtB&^s{2PEiP!auUwYYL-WL1f zS5=q)Vs+)><0zf?Go6ZqtGl4mujlYYw9WLOy1#{5(jUQHjhZlA^HKy88H4$S7sAvY zfAm;iG5Ae=WZ0F^zXy?C4S7-7e_(z(O83A(+x(2lcpXxxz^0Ou@RsVpr;UC9U z|82W3`8&T@TRKsyTbl|ViE5TChV!#59q#3sgKp1g?U(Po{2MDyhJ8DTwN{VaNxI@F zWb1wD% zXHIp$I9A9nc)t$TuFU$cRrM&XdrY;XQn?0HhK-UgQ>dH=lpR_rg| zPC(`2Ee%z_=I0l9PHm#+il`sx&t1Td;-Nn%E^0x3o)q*SYz~~Jz9A!nOlMi5mhs%o z3!NE>Kn<+++R^+b65V-<5>kUnGwCM7WRlg7;C^W2F84?wl{C`HAhVYB75a5xBu}SW zlCdDz+}rS|wug=Br(JCrz;_^ROVy~z=qq9feCp+XVHvh2i95FA9bX3ywYQyu<>1>V zG)3KQH-zeReBLcE4Za@x2HPbobR3T6#xLHQaIA)@3P}i+5FKjJ!PCE5Z*5h7;cwig z`}4T2!9UxkT3UZ0e{6{DV-c*^gZ(PgFEIJLtni_K4A7?mF9fdp`3wFD;8ia*HTdvz zJ^#c%*iC=`qjzhk|Hh%c+YjLWTmN6_^$*=0{{`recK7}_j%y6H|5ZL(zS z87+G-Z@tFILBBX!E?~Xq+=lNh*H=_YS2c|t;*x0XJiLCcOY1nDE&0ain%yxUdqixYmSR`2Oe}gb3+}#88 z;G=dO@Z)Ljzebk>)03dihg|IbVrt0J;OXlacEHc{eh}uJ#r0}MFzXS)N3D%MwMCN{ zNwu_cRCce+>3u`t`S4tSG!^V3;5C-lo`m^GC&RD=%(tV8#iQ2%)^C-bS}1c%sjua# zq_oN^@}1T|95Pdra*h5qe8m0=^!U<%R|a-#pvPcurM+M+W4t@-QK%`me6%IxK0ILB zvmJ0LSof7H0vVQ0J|bo@)iQGH<5~H5(v|d1c_0q?c}%$1QyZ~WMan&68Pe;kcydw! z0H2!#*tfoQRc}Cg=-+Yx%Tubud={CrbTHCq9cxaR)=bmGhqf%8&GsDN%qlMv-cy;l z*?toFU9%^9^FDZNhsh8xAq?hcNNYT@J{p_+S8zL=iivlzC;5(13m+m}6h2kBj||jK z!YV8ya2Pg$c9&<`Gp`4RNTg;uXBLS<9Sr6d(iDk%UPEHVYPerUn9MP_UwY@JSrnNN z^rWjYl!x8z;!%f(xk7mrwf-K=e(zLp?I@5iBX|gafhdtf!-ioQH1jUB+WBkI{JYYz zU{N||mduI)qF}MM7=X#zvdM{&ek|XI^9jjihc-0`rOGTe#2hTjWr4{ca~Xv!AO_%V zZOPzl!Nd6HE+!Y()9B=Ro)8Oa;CgW4N~$l6nA4o-*Vnc_&UYYXy))jMyQ@v zLc=skjrXc;twIu#{@}>W_xhGoBSkK>bTBgo5_cR%17&o^*)TC9gf#$Tn5;=mVLRAr zO$C5tQ4^`1en5C3{G8AEJumr||M7pm+gzU-aOl~Lk=L`c*fn3YiIGf4z&DaTLfXtp zqam4RyjFHCPt1*MgJP8bsirVvSwiVT8c3KzQ17o`X&MceeV9HBHnNL_;m6!$U;(Z} zg#Q5&Q0l%rXdvs=_*FuM-4YSr066HviyGc4GI$ajYUsZTL0^M^HT@_JI9>6v3`=Z%+_xVnvgLKk5WFy;e5xFIhU||!||Xr z>%}_B7GRLR0h?pLFt}**MZ1inslzfyE?~}>E58k!3kLFdWTIyS;jmb!SznK9a5xVF znGmnC)Evs1e)p_sj4f=wWKBw%_4SYO0YGxkDvttEGWfRR8WAc(pcQQs!TDd$c>B3> z7a-u1w!bhy$+%7;Gp3e$y9W1`zBGwg*K~7NT0MWpp4X4|Lpy51?9|lGZ?$?b!3a8B zrOF>w;Yx2Nv8th*LmeYEC{IHD7$1 zvXL{T8-|bEE6(iPr6EHSG<;nxCsVJ(chaw! zPXxBaIf(ZT$!x3UX_?5BMr$uY0wh?+tB7?%B!a|J0Ewrj(&`q{sx?W%q1N`oH_9`1GBI?L8auR5kcKXhuD?AH zL@|tOi#A6 z3xSgZHMJNp&?nUH=EIi`-9E*oL7T6yA?@ci0Kx5=fH8xi*$QDW&?V-w0VAc80~2lm zIBXG&*A%n_EI^cCYf*)OA0&QfX78UVmwHsA!YrfcW!x#o^*+>cL>4$GSu1Gc9;8nD z_9ly5-Hfr15O(t@c$l1{H~akN;QdOOmyF6sXZxnWCk*E~e0|9^q`&MQoisoz`nxf> zhyLi@#Fw{W*Jvz8i#DtT7p+0hBARjNzYQ* z^IuO2+qI^hY>glKm7?0^*lTUC<*)njBZO*k$I{{xGTW26!RNu?(iH&}fL$sF=Cc<| z^f5Hx%!@1>i0a=O3GTZZxAZGg(U=BJAYoB0y8Dktj2(F^R^T`;HpM}LB8I^-_fuDp zpunPJA;>Gr{YN+Lj&@X6JE<8O?*DN}1)Se>(TXfnX2gz=hR-qhU9D!>HQ#E?p$3X$ z+)bMm?PT0?o`5fDd-|{WTkr)2oh3)*x|Ekf!-a3_)MjyN4x-Z5S)V>L7yacyMa=!c zE#wJ-=<3RU!QUtVVtc;8Os<1@I35^aS}51OgEHr~JMHZs2X!=8Y z^$`1Hoz5x3*}oQuu|Z6;%?O*#pa+aWrvmmMO(A9HQeU%*dmYfjQ_sActv-riQVID7 z0tCEEdFC|IrxEZ}25cr7Fj&E`VqijZ)*(L*V-7aT)2N{W8nSU@F z0s}YPa6~sK6Yi3b`Kjn{!SXSuTZ4e$b807;VPni-DhElfGkg;OvYQowA7uHSo)p?= z5Oz0%Mgl@WKT9b}-(s)h`fG(fQ; z2**iJ?+Pb4Dj)RE*;T!8$+VFLdXtz3Xd?jMb_0R0dmNK;@g?=S9vHk+V~&l{WV_i1 z#_c#Gc$z^*!^iG68vUug$__lB+K570{Wxr|^!C zqJOw1o)Mn2LHW3m*b!#-R|W<cA07TURu4g zKzm2~vaez<(co;YDdw|sIE;)yZY%RvkuTGGe=+xDkJyNgrLP<|W%-PG&x5BQs|E<_ zYDrj6Sgu=O_G!oamVC<%h}LV>e?OH}I;)rb8U3{GlRI}nSB?iH+!XZiv0^|^!8S5* zFvz?SPYD34%~|G#3gGNaf9m-C9q$98F3 zK5spi3k0NtX4@60D>h-*AIvv&El z<1_1{vtiq=J8PF=tTTfRoGzV^vN%B}CE4b1|DomTLq0m>Fla0eb(uG-MLG&rG?Lqy z++a@}O2P?`t{G?MJkRDKZjao)dIzXd6{r3nhaiE|Bf1xM)$Bo?e}w<#_}Hz=QG zoZdC|=t4}4(3q0c&p=FbW1f%Jo4J|0Ve2KMdb2cjhHGQKO!FDVXt-PHI79cI&s=t9 z&V<>Lr#>C(pF(aY=kG%R`Z#p^w{MCQE*8~`P-o7M&y{9dY!@Dr>ZZl1{b-}iL~K=) z5$@-=)%^AEe4l$ezOr?1n(agPZ2ev%JUly|(*zxFj|S%#Ll>>;2{wMqLsyz4uf2A3 zo2RApL|(sLp1ES!Tn(NE@V<IOA!T?qL}_*b$p{4^lq|L+FcM|Ge;lTI11l3 zE4(ZHzBc>cO}?vssGbmPd7_9|pyB6lCKBkRf$E$J)?!!St4%hesFra-2r=voBDPa3 z2agH*NNVO!+T@qFX>hix5eSrkRO7|rcP;(Fv@fnftS7GhJsWk;j-74_KhNyV9GZ8F-XfV4y%5L(qT%n9 z=XS~D``Ss@w$W7VTqUsQnpCWS(NU~DGi&7*p*Su%I-SgC=_MNLAcq5y5lO#wFkB1 z?@deOxK(qc6mDi!H%9xB%j?tyepF0VH!X`i9;#hp??sIEau}?`UR$2T%~xFWswiwa zVp-%%zNX$=%h)njU`Ik!Em);QfD3Aou7EDK*HT?IudX><9sO7mw0iC!t7sxBuo6g_ zfwZ=iB8Y``^w~!I0^jc44lj~gNK_XDxaE&jk~=p-p%tvH4RJFJltgS`@3j`_8(RT6 z9V&)B(3fLPwv+7vtZK^1PRtC4#(hw3W4LKX5kEGoM2hEu++s%_@_;oDIexr@CO74A zX*A2HmJl=V$Isbk8$x?k(vBnJa@`q$a3Ep!1i-i~M@kV%%T!ppHHa|!X7KH-{q{R9 z7?-GA@foAQ5X}O7qLY3GF5)S(C%cK^1qHY_SlXgyOJw0A|(MgqPg^dfB3F!IHb4Hu5y<5Y@sx0RW8EyC5>x5Jm!g!8?y;Xdyy*=unk z-h;cguo_r7i91;b(Mid+ziX-xYgUy9{;EdqKCosF*aC_7f^`elR#v;&!z4UUkl7k- zFQ$?{ssxRpnO$#)!K#lm@1Jnas{@OT(h^;O|RFa@^+qh z!slam8{55^7q!2(l@`fipz`z-D;WzGu_Vjhlnm-G)urh->Wp=y*~!Q~;Z#urrP?_~ zbIXIIdnuonME31Mr?NE1Z+onY{+uoeq6xA@aLn`~4jhwX?1f(IEN!|0rsAz(a$8*5 z7m~C>g_3jAD~h)lh9*zJ?9U~cTHx#f7`KYmp)8rXEPg#>ud*0B*U91>G*(9_r^2yv zA(3J!0v`<5}LpE1CRrm!pGCb4t?$`7^&1Mj3q!=W$EmDtC%~ zPEe}7Ws_Ep2q)xm6HOy~MN4VsDj`>v&>i zu&v=}XdnznnxPx!2o_G+eHpR7I}ExGRLSvB3~!_`sZD&WBWD&szmA3X7Q z@_2d?9MV11GKWZ{K5)??)gn2JYAyk*DOZ6gj7R6hH$HMvr==R?D2Uc>WS-n!a2}{e zK#qFF1iK_k$IO$(lO9=sCrUOIB`D*fkY7;?$1$tYrX5cf64hs$$BR=JipSir*KT-H z+euql2y3V7c2Tg>&pc<0Xq)VFZwm}hTqS?Gk5u=Af^ zH*+$I`;0b}%69(VS!X8N^PE&-HFw#J0ye6!%JB`_vy-K${JU>KS1_jw_DvTvL_mF}TwO^^W=Bq80i0to%Lka^erWO60-Jd< zVnWwx&#E#`%?Klma2H0WlLLKaS}!Z#VYN)9cL;B(=EDsov=`o7Fp;hg8X>o6wy7%xpb#H9Q!G`rCG`{ z`v~WfegE0_hyDP&@9%Ja>K#%W5t4!e8l8YULl9LOEq`MZcOvH?T%TK<^W2(^U;S27 z{}cYtcW=^{LWRX*u>*Vg37o8AMO%pMyx8XJrD8`%`&PctNI_Afr6$|gKdhJnU9Jth zJ;3nT5FiQnH|bj870b8MlkVl=+0v-jg#8YNov$1bv*QGv~=fpr90J~XDI1e z^nAqjob0EoIco~mB-^ulz`*iT{Zr$CtpwR-t`ORk=&)=GyA$Mmen9bgM|y==j))W? z|Npw`{p$wu#hebNBTUU>@woJa9(~*^pV{q{&Q)=24`GW}kO)_i3_{0)-3A(KN9a~x z?(Y&rr2hUf?MClFd#G?1wLJFZmxO=+bOU|=D-(;_H`Gu!do`ur;-MQKcO)Um>F)G0K`PNqpm^s26V$p>h+V?oZG5&I&G2Il#y;iS z&169TXR-)2jRmc3cMB0{rLdJ&pz^&z&qVQ^4$`nI2rZXPvV}cC_2Frw()ZQ?az6`H zg`{+avn^-i?`7F%hw?H{m~awK4LfKAuSPlLG@wjPI^jP=`g2w+cABOyIgP1XM5wQvtKc7qFR;T;h=yL9P3?zru+ z?wcz&twU<~gdV8*M=EldN1~a$=i3{w+l4#*s)ibA#NAG}G}V$_-Bfi^J)`xIs`M(j zruVKkS`)lby<*#gAP7bTt^{Jo3f1fxqlk^Bm%>I{0a_>BJxG|I97>-*Nt&)9Rkm;E zx~nuQRb`_~HzY5;>VUxJMVw`x524m5CW1+F#R3HL7@4p&JVydl9cXmLDFLmHN!OAB z)6`T_WED&K>HuXF_0Y<#;)ptt`(y;Vw{N}3w_B9@tJS|8CXp`$3QGZ1(7dN#5*^Qc z6wMu%!w#UW4d z39#XN`hr#)!2UT0at91)|DyRlyR^A*@&_Im^cbg_6p?b;JU3>$)H$5C8Ih8n4hDyM zm>poef!`RiR%QN$Rpp~W))QCe;&;sSaxlbv%^ulU$mIjX>asGU$Rn6l+z1#kU+j@D zuSF|?jK~zjE}xM|X>kLgrUKzX*K^ zVaWK%3mzg-F1Q9Nst9JE)kEI}rWm+KFmHonaO?O^$rbv<>-psQ_e4= zwv2@DMDB^-KM?R~>-zvgEUy4DSuk*ruo~N&FnDgtVZB zT?huH$%=WZLu7JA@w}AN{1t1%c04@W%b_gKbH~Zix zCpNW3N)Qf4f&pM)9_>=l2!_lb>%@|qw0jS&BK2l=c&Q|~2bh;ZLz<t+{OCkjp3nZe5uIQ0mZ3rW0chntma3LCbC0Tb{1XYv)-;9Gx$@Z#E7gGnm z@mH}|NQlSiu$X&>a%jm-qQ)k7BTR(bVZh}wNq^zp!nK@-cZ}gre9Jp64d!ZiQUpap z&&+%Vk?PGR z6Ol&O(i(=BuBbPS&a1|tt+}jQ5}t|a%*V)g-wZ)1rK&%LN173W!sdd3(!kb7vI}9^ zdC+qnWwYNId>V2>X+p>dS#SdZkFhD-34i*P6}-$!B<b(yF1ke7t(d8T01tQ_~dvgOai^MCDt_B2`mwL5m!VISqf5I%ayTcD^s`Vl-Zj zFIGk|PgMQ9JnmKLP(;f+`CsR^?G{(Il!!>Z!y$Z^!yQ^M!Jma^2ou_F7_|yzNCR;& z0zRlCeDK{Gxl@pYUoo|1kBOb*fG?1~Au`;5qY9NccBY{qH#ueJ5eLD)P4AkYy>Fusw!(p%Z0N|$fnRLz zndg1I5li0e)>v?%auu#YeX7g0N{Fz)50AM z0z>AH8<3C^J)=;L&~WHP6~P9(B#6MqP{Jbh5bbal3dU-(gb0F=KVv-2^CIfJ?%bkS zeskpbirLSzb%z|-oqVU+!$1Gz z!w_j$Q~r6&G>dbW_tpS{8?SVrf-J(0lHkFoZKM0y?iC^FP@EVbqDr{wB2^zVZl~}{ z0bt1dv954(HM?gXJ3F=(d@%U7MUlcL&?V0qyuwo1BiUJ?2_1nb6z_HBJRc_CvqfY1 zEh_qoCGH{J6)D`Kd?k&F_s4#O9WFGbJm&Ed>DIzWk64JTQP8GVQ4MAVlXN@Gpm)>A zs;lk@h8uf-cIRf~)_K5QBERSUzVeEW;EKqry^E?xsi>TI<9RC!tkFp|adqQ=;w|@a&&U+I1As0|@MGoA`Rn^`HOtCS!P79l%4QAhaT zyESsdz#6Vp$i3ic$xZAW+=D%Kc6`T9WGQh)Hw491^0ty4dNex=SW9}x7v}%%!#v;O z$ft15yZla0USEi9y>9KUYUBR|kD#v4m$|p$=LRm%$N5&B6CpX~24+F))uw zk`YUq45vVqmR#IvaN@^!<{p52O8m}-hx^@Qa;Tt*fej^J4k6~bNf1((sZiFp7QjM_ z3XaiNMR3fEU*z{O3KOT%jqpiE*(Hkh!BPF!t$1i+(@r6dy}H4hKqQ| ztWA%830jv00PlM^mAc)sFSl~L>E)&%K}sA133RXo5WH7dkUiCv*jTo^Oi1*8!#sFldTOG7W z>49nH(3S$F=2{s8fOHYIt(M+}ZTajSJ671XM?;WK23J!}HOpE!&-^TXD%C0`(#h7r zD^T#&!W&iSq0t3KtwQMpxkls$c%Y8(!FOsDrkbHfO5xN*x#or=Y4@f&<|Y!1@i&1t z-ZJmm;#l*}d#Qyt$rw`|nhB*N1amq+H?Z@(j3f8L>pIvMP|529VpYzwbjK~c8Kq=Q zLjL)?eF(!$c|+Lp+jsl0wl(0U<1rSkLmP|ou(^B#tjp&9TWjSxw?4ii=e~NXkGXF< z;Woyr)24z-p%pAx6@m~|d=wXNNV!!tcwv7>(ZyPD3jJv0O)MzxK`f z2NYhtCpPE)`&ECS8OB;BMvOVm+{B5vg6w2;hz2wGtB1!#n4x1|F=`bsVJ8F_*|q^M ztf?5=j7OpH#0nSz5Y$A46^`N#}JSf`G8Lwl9xhT8Bs*JnuyRt{&p6fxs9CKaT7FPyFLOV^jwl zoCm@DL7XNDa*?BBC2<=0Z^q5@UFFdIk{GoLIN>4_DAM}@zw9v#1~&b*Xxx(`#=o>$ zPe#qa>el2Yc8-?ID4rdC>_mR?_#8w|f<-Y?%vn2`9C+u69nQ_NNQ&t2&kplL>D;>~ z@}uzm{Y`xBHhFzOZqHZcTWF00ydm}aYHA@Gg@7LL^}qg$+~;Y*zf;!h4dAz5(R8BI zh1d9r@L)qaJplM4819YFYk7I^!SDAi#%_#yZers5YYd_vy*!C zJchw)+S&ID`C7td)pMzrLl$yGOgR;XG^-j4iDq_HVrCnwQG-=E@dBv~OgxN0KZDj6 zXrZhIEHr-rJv+%7!wif4@JPmm9(OyeCmReu_(E2rXRQUxwd>(9fc5b`ahWF`pq{m? z=d=nP17Cb*$=B(kaQ{mg&m{F^I?E)h5TExfUm%Nc6swwzgp_M_Jo^d#eshQTD+ zRrSJjm4u~Hjz#epG8-903cV2IDq~>HY&JvXp^cliT{p5w5m+viaKx;H=wU|O+g6^mLM%go7BWiU0pmnq}`L&F+RtaPzm7pfoWUCs`QU#i9 z5hgx*29b_ycMlsl-9%$OlM(Qk2v95v9$itN5z79N1RJrc6b^Oq;LXQ-73MMolvgJ`BCMzqxD%JW1CM4}eEo z#9XwyS1p|IMS9nbZSKG@pX6&Tq}(#5e!$T6D%bqXRU*rq)CyI|veR7Ea`t#XD!J(O z2+T^9+_q_@t6q?FwN2Q1c3)38*1Yw@)9c&1dt-{%w42RXoDmxM$q(*uA>K?7;e_y& z2yP9p^pahR6$j8B!-TayxNF7N{8@Pj$k!(%v&I^==+#|$I1vj$hJpdsHbsuh8&_&Z z)e|UAtNWD(&>glK%(&UVYB&UToREtb*_>i2DKP2V0C>1g9@(Nhp7NtZEh z;PVQIQ)DM=oI`r*iE}AV%WHXv3wWJAiHp!@gTrEiv-pQMeIGc6<@&~Xk)Esd54r(k zNgJ|-EC(2!O$^9@X;I-xvUjYto8+l}rp<^X>-P^u5sJd|WLOl=nH;oG&|MhJoaBeU zJL3EPBoIsI1>Nl-Iy+8;c2ks1jeUude(@9sB%3}KFjPnqTUw7?T-=1o(0tQ_AJy4K z7{$s>IZ;V97%la(Y2hjfRQn`XB&c#ZEl(3TP>!kjM_Zsk|eMW)y&2UpU7Qx?&3C@9lA zt;+evv8*X6eV~Q3Ke~)x)1EA6p*^SAC@I0js0?jtK?6cZygfEDr{9Mra!*FgtmGid|?U8fq-XF(>)RM zj0hKVH$q9L`_PrveP=FS0sf;~U+EP#jpH z_mO+hSc8&vkVbiNSOLSezGnl2Piu=U)qzvl%ddz`J*=Y|mvc^K+{hf2;)x1L3WAzWC~!?>>9)gTMYo zKwB)th97=vMGfbl-(nJm7)H1QT=Rqxc>-SmIwSevbeYBtH{FUv4S>!_+A`os0jTpN z1WAH$k|1B#jhZxT@o7qVC<~CC}V5_03!J@DPAAHAS5z{N~1HFEH>UUc`Iu)5fBT6rW_od zoLyYqxIFBt45B)QYSkUzpiz@%gkSV`VUG_!Vjwd)J~FvK{D}w1H=j4U{p&yf`yg;? zFg7tYGq?gwnHX{UHyR+-*eH4+(D zS({~A^{`IgczuG}F;Wq1$|w*wVko2KDnpSwLytBk_OgxL_jIwGwLg?!zstNH+R5us zm0PeV%N=oAxC_mD1DoEr+*9TCa<=zazq@c>yB>H{TKm`;(MR4oXOqV?38t94o;~OI zp6NUOmu>W=RgYL>WLDgb3U0wvB?wxC-N{YBbGkk6!6ndA_u`N)&V?<+VoV=KXp{k3 zt-@+1X#N(}hu%LK8mAuICWZ_cFwmUuB{vt57081`B7~97{l6b1l?CZQv230d(Q{ z3^v&|ZsX(N@iI{xKMe4<@0ZJg6C*p1aL(z{mJlc@0D~itWaJc-C^Qw{7%YyO9~yqs z%Fzk!^(+3%tI^2_)y3rD+KILIf&1ZN6X&G|+?q5Kk2O*&uv9LMGuDy_lD0HY!5hIK zCAJYVTZLA|_Y!~7sM|E9zc6+{jfNzjXxm&Kg!5Jnf3DREsJ(%r>Oo6rAj!SODuKgX zuQ3G4wm8Sw-tvZ4ANW=}j?ZjyFQTpEprm){%b|BkX^P`{n=~yYMl*Z7OpN1YLDejU z)dEBOkP0Ljqk#+ffc>%CG6JchiYcyylFn#jqlrjH?ZR3GQN%Tsq=Syne1o8opEzdZ zC~}#VGs9k=dlGD7h0w(ra5I@is*u8pD5{v^N+{_cYfTeHm8ERuoAD=)bI&ebBz|Dx zxAuP1*|X$}gf+px_SziX-M4#%o07rhxdHcnS*o=q+VjU9xhz!JRrpBJAVZdFIdv00 zPRgXl&NWA7!CxON_$K25|F}`VHsMzL98Ysi59x&|S#L>P>~jwO2(ydlJ;Cf>%@P%& zYOvp?i2P5RT5M=*EHKE5s*MPEr(miY@qht^!~y1EfD{AdsS$w%7_k6zF;HRBPpDnI zcib;5c;r2Wa9FfZB#b0T^&{Xl$*S&PpUIbrWeSMFp}#^>_-SonM?i1po=bZ=B>?&S z^G+=+E$44O^hFay4aMAA=KJ1u*OB?tt>ymYS3dHdv#0*T+lFKXFI_L80^V3jExCq+ zH?6QYO8~#mbAw9o7IpBJuzBUJ>zOn5U~Qy@U5@q=3WwoD%Fg`)g)>tpCatdis_uYz z42C9STs+f3|5SM%d1250^OARa_WSX*fy&7K-(vs(8=(ULjM7}RlC18iEzYTIj;bI` z4*ZTO>*=fbx6qw0D_U>1vD0E&7DS_y)45fBQ4!+e95QWC%j0X7081CMwMhkgJA zAO(zS2b*IIgLPXze~}3NPwwvdv(2ES=q3YrZlf#SR%IN0phjlSm7|?fPF6lZm;TLzshuPeD>Hm_S4m$Yl=whc;0f+FNOCU^e%)dZ?i&mqHvwPr&ciwrYUNIqs;}*!S{>_!d zMFkgAfrv^2KK5dmTU;dUO>&8asZ<3gtStX&A@A{jB;=1SYH(s|&llSdhp2ZHh>8n6 z$oyw2%IaB^B^}cqbK=6sUh{Jy{m8+iQn%O^==*&Cx~-Pgx~i`bOeWT!R#tolc*(3n z;hNy+b0VoYLn>3x^1Uk@c z0sJ>Wzz}yEqGl?_4UnCDp|Z&){Zx=Dz(6pj7Ey6VB9z-%bVzFb5&NbQAUtj*ZA~>5 z#$UphMC!Xmce(dW!JaUXo{D?27Y)r-o!G*Fop9s}>^fb!*l9%U}bE+6DtQ7A)T0_O`v)`mGY!Mx@Y! zHlQA@V4R{?f?4OKHmCQ!VdRzPapy((^-AwN`CpxO&rKaFk2m@47a0mPrAST=SeCwJ z#R69ln#LQZ;r^d#Chr121(;E={H`gB0|8G#s9Y@kw*c8tfYZi#?*rm^{`-T*I~VBK z)A-YM>)97+Jg{7|UBJM~vH#y!wc2;_`9**l)BJ#RM9n6`k#dMdL(-b#quK1Ro2wk( zzwZGb?>~G31VD?x7fq0owG5&~fYO-30ZA74N$F5w&!IHQ&eB9}$`G9t9EO^>A;eHx zuIQ;ORXfvd(e1QbwFsQKh`Q6(86LWb?pbrSWrK;2%1I=W*~{Vf z2rL&TG@2_Vioehe0_;(uMVzaxGb|-MZTV-PWt?Ry3D4IwrP6;VmzAqDo>!f*f~@nvu8-;>zlLY*?XoybMR42^ufW&e7X zVrrQsPVOgEhRz^dMe*_2ExV!j*abll%Jbdx2i@)Mt1;02big-P>0U)dI+qj?2_hi` zagM(|##X(15$RsMfd~>gFv#u4u~+qt-8c4UpWbkiwqA;~5ePW;osb*f_O9lIldsO? z%+~B^k+1@R)u+=bt1=A8K?{u9P+I;@{a$t!~-Xb;D$Dw$Vg4E~yY|E>qEfYGczM5(` zy{n7<>}OJa;3Ga_0q6UVX@A>wq6njYBu$T#zLzLZo7LfrZn7x}{gS!6JNiF*BjwMe zgqujC4%FB*s_%ghH)y+e)sJWtVWbrMzYN$N3IxC*vN6pd946puLPMJWTv<8M@m^jAvEd_-NU$*WA`Imy^}tIy zZzFqC<4V27JHGPD%FJFVz7U;oxtKbi^^E zoORt;_qkHl<9PS}$cY~I#7QK@=J~)RtRf#r;gZ(vxJDc}?RIr+$AKL&>jyblf%M^0 zkSh#}(-O)txsco}xyy!X0X0d}DxuVqzl_3#NaP+i7Ke!{hv60BmF<;NH%ru>qu6&j ztwp(%^EzBcHe6TW27mX}sIaKlps7D2XDjj-DBOlkJI0{c8Rhz%mvUL38&GdFtkb%I zSqeW$(nu;{ z;nT>1T70S3x>?89BV6ge-#Ur2yN3<>OS*EJ8PYGna8XN>z%6_R>Ag&STg@GkZHtYi(YwX11B zI<~NN+pSUK@0^F=Dt5-ei=;kv!{mfURD%R)(11<4K18Cf&4QYpd`kt>nGHZH*g+qZ zWFBs*g65L24f#7$xGW|qjAHToz$JXls&G{!3q=%;QzH@!bKj6|$3^~{!{+5|PX6{3 z?u|{`bv96~+kfM$Y6ZD(o0c55DWOe?y9}->c7nP=LXMUqVC^=G$sMx^D>nZX88Y;SVESo)*bxm8MYQ7>+a=DFZ1JsT!*wWTCKzAd5m4jZBA3kL)fIPKo5Y61SyP zxYSD^Tw&bThf(O-A_|x0xJhwZhjL+$V}S60kU&9KCb?fsgv*zy^gO(F>F}~+<W8l?YX2s)-9`T_|zk90y34SiyxIw}62K zxjUh(Fa#j_ttDr-E%8HPQ6GmTVvmd2b8#CNzio!Pazwht9ca%YA=q|8M2ID^C50|P z7e@w(j9`VD-wbD21Psy@{ZtGq5Lk;sc!R=(!m?kc^Cc`$kGKdF25KLuSVtv}12Im} z$hyn1;SFl?bz{2)L=n_tSVeJLkQOE@LRGWP(~`0#Qh20cOj&&n`3D^F>lVlLFP24{ z!{P8S4|6yi4u`v5o&R-=~&!9y^p;QQ*WWztw9ZM z4@OKuvgEr3fD_gjVYypdl(Sar_Fedv?b=vs9MwwL{;js-4p^AvAyb+lA4~;j3*UCs zHl$!zKcQr6s9m8IK`)4v87CKRb37+4kaw0&`PU`5=&OXSeU+< ztbRTHc&Sq=M!F2RVX#l;BqeiuIJRo0!*KECdWA`T-^=)x%+g=xlw#wY4}Rvr_)&LdeEZ%hOn1Y1beaBp8afYWtcXdu zoyog69@)heAitwdXGdbu2pP8uk9Jc#zPB9RMah8SSuV!`kFm|wuUt78P9xH0hEXMt zdb_?55TP#)X_o!;QU^HXUfg?}STT`kIA#8^C?y_YN*!B0ry-Ap|2EvtBvKxB{6OqZ zbDNcyTd3eQJUDjGOmU1lGJd~bt}Opv$K&8KN+#pr+!#_{(K9M0ET^X&$Rsi_E@yFj zxxgtW4wc>N>i*FSoTP3kw~B00IdN2_{hLMThySYgO~=3%39gaTzzG*}Tqlu^D7lX| z=J{XstbeIEQ)lz*_*V16kI@d~b6EwtY+Rv8LA#2-7M7Tiu0hycx8Edr`q#OHWqlLW0CS;)R?CV)-dLs490#x{(HqjnLD-9J`wK* z>KuV@Y&Vs#0{W6}2wu=VUxx}k#K*+P@t^N5Tp`@?x7Ds=_G{ZhrW3woRd%chigr1z zxuzA%#M{iqR(0LHm9aCq(enUaYn5RiR!~&KUMZ0mhv`tttY7Nw{w;-~oNqQtKj&fz z4MtK;xnDXbT&)O5wuQ+Q0xEpQcneV#4-z}Ya!WO|&p-CF1ijE8_2;d2aKW*SMbKXF6ff$CqxkLbzqz9JUzGex{}28p|Bv6d zdQ#lJHU6u;=hlrYXEXDETFLf=O!*Ow9z*}ITr@9^Ex455pr%w5u_d~&!sMgm-r-@F z@^}8?$G^>2GGnvjyNUq#fs$K~T0>LQ%S)UtXUZBW9HiXomcyL}>EHTf8Mg=v<=||xw={>Gg>yQcNql~><4An}*IT8| zXFt7pMPya{Mt~mZV(Op8+jp)%osv;$?mN0PKXW!S@`vdSfFo91V*MBYaJ17AYZ#`< z4sPc`X7!ZlPDHSWZJOi1aS^yXsiv}p9D^&aM{C#ZDPGkiXoB8S8hOhrmH&A2!0sfz zU_JVztTpqqvihM|RL~V7C&-hn;v1~B>hkxP{$?jX(l?&ai3_nuVsp&k#odk}pr;lc z5gCl@DZm#(65V|No8+DSaSrCyoE7oQqm_RinTvbUSu$;dqXBQr>1t^kRFW=8V_Flv z&5LF{-X8+(hyOmCzp@TH&wp%Nno~l^&?M#+M5D&2D#Y@Ql8lW_s@hMj_I5{YzW+^p zY)L9L`{bTW^D`E_t^qk+cj%qY&8Zh$T%HPkNE&7GTQ&Q-H&&g|?cL?%Z_Ao3jaP7L zzKaht6(vC7{^iB}cGFXCY<44$3#$Y`)k;uS5$Kb|2kXtMnqM@$XIC8NU(4mE z`GR|LeKeaz{_!ad%%A-);UT&dJ_*JxY=_o>Ze zZtBQM;?V*dFt*PaClL&pvl&^tz5hxNP5w1qJssBu%vMW|R~PKjpYn;Z&Daw-U^vXQQ%}&F zLg_Vv%0BlUMY!{fSJ$+}0lmL%3?iCZqf}k~vf5rAv${0jG}moW+lys_gL@Ldqw? zwg|=zJ63e)ZD#mBz{t*QcOso7BQDxdbB0yHvJcG)f%!uqMG zFWhK*xw{RacNh}N8`H-W3mx@MjC;;2Iw}OE2qiJMrN2H(thU58g0mYhNyRh_XhWo5 zVh2(;)#+Fo*b+xyHhGNs*aB+=t?onn%%~W6;PA328F+O88w6{Mf%*S1feN4#T#Fk_ zkS|HCPiBAFW+s~lE0g^B{|sPSqfIUk;6!-6Y$6`!oXjQXWE#xZo7oJGK)hTmd&2II zw4#*&c#>vPXlA4Ty|ie=Sk2Tp+#6v`&E@g7N{){D&VCs*9RTbcO68h~3CO_-;^l`3 ziNM8_QPtFmH4R8jt!X{@Wbx%+BKlD1=|J8Eds=8ZAB$TXYR1^Yvd_O-!M$(GgyuWW}PLQ0i{K3jF?$4t;7@QZN@ z39>Q@US`e@Si#}l45k2D8iO#$lM|9sFK!x%Sj)=ED=5-AC>J1v|2VCvX`1Wk>fuaG z&CGE)yc(B%TE zric>CMOEcvcT`RD$5=qe^uR49uu2B);#pY$+?=M#4+}8MfZVQu*vHuhJR&C=^~XbEd2gnjCaE8(jQ#82|t$7>fH}mcz<$@^fmK zT=EZ}>zWzqnHtnO!9h(45w(W3nI>u<{xTgdV<7FAXh06fFZH507<}(Y5G5)`&A&g* z;mo2F=ax{H)RvMyyC9&~&H?SV_m(+K+ow6q;(Fv2$Q4NBb?ocJA5(nUaK59b(+wio2)qexBeiJ5S<)N)E0s@rfWj5 zba0oW|FK6+-pHSyd{llbf0cjAe?_Eg+IC8vOn$uU1}~VBg|t>(GZ1q7@{DS#NN0XKa$SaoSRMMS<7l?ZJvL6|7%g3dVPOG81hiV*o^b~)SI^4 zbmwNuBIM?RA58TT9!wl_1myX{V!@Oy)P(>HiUV}-)z;LJF88Ew!$3siGQJZD#X8sA(xU)_(cY2`;+cevaRl-1 zmO6U{-8}$J7SQkY~OooeB)OCj_roCc}6<=as}60^Q&T7>wX2@c?o9%EUcbSK+5up-pFO+9~$i z;Uh*b*}wriZGyDTV8{&;Ri?QT8>s>u?$i1EG!-;8K#a3O1*GdYA2=YVBP4$158sRvw z9o8*Oj*RQ7;2pw_JsPjhMYJ9}c4FKk0@+mT*yeFB8Ab-|(Dd>BaD2f`i5)YH&$tVv zALR;rn;yL|!~>7@K=Kg?;OKQh-&O|*bJzeMSDd8)iAI?KL*bEdz=1m2zD{~yGp?_t z(|zp_L~B{@)HAplS*O^zl%EYn&f&gr7Oe2v-p(xxv`ILRgq&K5ubKZn!vMzfz?#|( zWr4Zr`hJLsqoz?OJJso%bhdL_8>VGDuIB>=CRPKIeFkKHrPBym^`idX_OADR=wqMy+?T%gt(U#( zb#HnOvLYto^lSQRT@&lP_0| zAFTkzLl5Q%;z~8gT=UGgK#Bip%04=I4owvK>Ege}fCCxP2|2 zACC10qPi25kuU5S9;FaTX4(t|m4MP9!7b&r5%CH@k)sh3(BBEEe2}NnvMtfbAz-B? zD6}Co2_(S3Q;@hv2M0YMpXEdY#7#ekd)v2((*9BN4WGtL|9TS_iW*lz)2nJ{Px?D9 z<2_~5G@bKk-eBUBped<%^A{r0Tu4`vL%L=jq@HL#X-ws2uBp_?>GT2=} zdO82l&)ux>bkO?w8401%h2^4(P$~$P>czrK&E<&5kKqt^eQVobryCcFN_kS3i!H^) zg^6)B5}u4La(L{suFb%Mmn>;ru~gbE&Q5|Xi!Hw&#lXccNt9$6_EZH-DU0wp!(A#c z=DO)>qZI5QK$vLpja1@R*urR5GKyM8uu8@*G^P!nEZdDsb(u*nga4+is=1t~WE-S$ zc%r;;eCslH0kgTPXB~{MW#aDmQaslV{gxdJ9dnq)~L`(m7= zW2|Ij6JFw|`OET(acfKs@uUc`*OE7sm@1hVkXK`~CON|}OhfOZofH`RD&?3b#h5FF zm@TC~i#|wU#`1Uwgb~5mz=3CiahXeWgEDJm3k;aeNmyWTV0TgndjTu1Va-)|T)~pr z*firKLWrQ57%38nkw>tF)gkNmsFP^<^6x@agS#ng&L2rfFGehCU^BM*7q3GDL6K#S z{Ec&b3ZFXEq-UU=0%wNHY|GDFhUF+;gXQ01yH(CMD_6o5Qj|FQ|!baJj$dac)vNq{9d|M8;-dq5HI#L}ya0Qek$xrTHDF0fr7 zy=X51+m9;);0nh6!NlB}!rFc;Y;2rxaB*Y7k|irvczChKXWcgVO9%)eBqAgxA|Z~n zWu%MQlCfhO`=;41B1b!mhJ?N1;`Ipn#0(5cJtAis zQ!_EOFtaqb%A%Jx8CJHZXrE3ZJ5U^{&e2+TNn^NLyJd5?m{hxky?0CpA4gw9OyLGa zK#5>zDjf7g*<6hgDiWtmTB}nugm;pmSh}628SmYcF=Nd+3%-}FE+`oeMnI5IR9MsC zN$3a+Bqk~r+Q~-m0|yra4-+4&1=zh4;t=5y08zVat2Xma)<(Qth7d&jd?0g*8kLgihA;>MnBg}gv zm>xtIVgllV5<)kL>0W9fEh-}>D}HmBRtqCn3)h;0T0)pGej}KMq6;zI#3ckfDbzqp z*o}y^sEk-=#e0^Mke5`DQgqWL>7JBjRAg1<)a1La5NRlCDrqTetK5!nLS3PSzJ`IO zq1HLlE*N8UOmwk&&FKet1A?Kc5z)ABF?o^+8Eb~4;CoKU0j5+U%`KhO206<#V^KhJ zHqZ_=!=>@)77U1aVpbYKK}$BDc-eFYT$*;aW!eq!EC>Nh|3G0Px?f zZ)#rFjtb~o{|9Y7I}5iE^U7!(J7QzhbmPEEVf!L2{9<}K;s78A$N;RXZ(?X<>;YnK zY9;}7%4ua`2}5y{83Y`OhKkOMfrW{UgNKVxV4EW*A|WGH_zEOou&)c0YR$ zlY5-X(=?vt;Yn)G^YkJwZ}RanZ?DsOo3B?5@;-m>^7F9(9~$g)K|U4eN8!E|>Px}C z7v`($eiq?ZLyGjfD1XxFFWTSqY`SaUE=KXk-~nIb$7ovonhbMWYsxm{fOhPpZsN*^ z_gmZdaj(lxTT8lQs~EP8C)3BN{Vo^(ToY`M`sIz4dHhe*HrCbCH_|c8BEc*4%y{ue zb0qJZgWKNQ(O;Z9huJ}=bvWRR)lS-g=m+4c?@$07Kps6fDN;@&(s2eP5+9i=s-~it zxULs(gJ}5-rtLjgfIvAVBbKfx(V{AEz<>`f*hVSi12S#wVd6;TLXKOya^&h4B0tE& zg=?zz6rg;Q)6L*rXPK>%4lJ}tX-WDjRBAOA)0xiNajuC|6ahu>hMO*Z?-7Io?Y z`4_Y+e2syx)d(ml_I5iC+BXBJ@s8bNuYH=e6cH-Em?M_0&!|Iy?{&mc#~gP8zz;j+ zv@?Jocg}g8E)Y=upL{m(OSGdD!N%A1xKUZN9PXCe?zqc}l#*v1{`vD4B0`3AH4(6u zN`MH_5~OowM+LaH@u;mr6ozOxG6B40@Sy-T-Kb*(N;k%L0Z(}?Gwk)>yd}8oEzAv+ zf0xJy55k~t_qJ*?_>>DeYGPz_8BaAsp7LGblO9o8R$ze^2CaQl6gfh!84u?VMMeq!-9GqHqi| z1VV>7^u}?GbmvxSioE&b=e3cVN2iawGduCa6Ah?gaWk>uo|9nalE0r0mB5rb`bEQm z(xvkBNTwOHv`BU-u!>G!z-n#=5Q>3f3Eu%X19S;+{2$O~0{8s}0FQos0It72a0T$e z&jJVlJH^c2X%YO*RxI{@N8wP=t%&Eiq_*pmTJ;#yiq2a_ia?WHd{^`c4g2ZJ*%%rn!1;o$9jc2RXD)IUclD z{yK~Xaduyy+zzcLG3vg&zIsL^(UCdZXG+CFx6( zp-71C+i%65+|b$?t*UY$m@`v?4nY5fsK%0{uGf!U8>p$5R1#}w_)EFy9`5Ew)qzHy z`|;2&c>1vl(IL9%3h5TokPf6i*Yizl=0XZl>NMb-3=nnUflHJ~$N16OC8H5rMB|}{ zC%&`n$>kT8%gWL(C>oee+xW@1l3D#Zr9kT?!@uv5Ut05ZY`@a+7v|?25D0;gltG4) zl3;)kE@=t=Ls30@o;cz-y|UYB|B(9W$BIaxHDNFP_Zg*+^rnl`2uUk3wKx&Am7Q-m}F z1W_*<-*vG}nqk8-@R zLCLPed15^9PJ9Gldn!5D9`f8KP2Csx_$w!-uVFzY#`|?dy}QplWm_NGg}|om6yIy~ zl5LKE_>Ke)c~F#4wJ#cUK*woBpuRtsidIrjOBP zz8JlF84qHG1dizX6S!8q?sT@YU)Ryeq@2YSA0p2W5_|}m-=^e`m%fxzrN^%W)kb<@T*lB13|R zLK33Zqzb{yZD|H?CNiN+Yd_%VXHR^1;d?Pr?w4)coaB`=(!GKBIkfv#gVNWJ0ZnKQ zkF!L-x1qyFAnjen?_!R$t{ArSAdjvivTot>=)~KN9i$w)f>kD$){8`njQ>tRToZ&h zc4BPBluVacfqc=Ltk0b}Yh0{xc@cJuAQBRot~M+SW2{qO!OyV#Zum_I!{OZyp(o1C z*fTZ93}HmtJ9|49o@_(M=V!0{E=fPC3DT-mIxDI4DR=`mq9)ElZl(;r5N;F`faL~o zno0$+?hZN13?pE3uLxVmi3dLNvqOQ&yA&N)a!HW&b&Co__sc~HTCc&;75?yg_%K)H zK3#5ZV}ttd=JlWqu4LDGV`{^#J2ASf1;RSPkbaxH7%k$iOye}3L2d{LLBRvdN8D1O z9`NNjWUlk+#koSM**by=)dL2e5VJ4o)iof&qQ%G4sz1uaCaHbsjE%>RcA`?z6;w6B z7DehsJC;PkIZ4QP!+9=(!U~uR^JuHBxXN(Vk|A3V6disqwY=zy(Dz^1eHfvnYGi3mStEAIWnIu z)r6Vlc2`oH{)kDN^FDBgM2I!gSd0~&wOv_xUF`yBMP%xH>F_1guKLr_{f&m zA}i}4^5K@i*BP;d8)fhe0}0tl_*dOcs@5RswhkSDJi^kFF>BITS@jFWG-ualw!@V? zXwd({W2FTjKa51vOk}mO;s$=4`I!{)rkX(K&!!Z*bw>mc$gN$EpXIT#2#a3!={YtJ z#=?9*DRr0whE8fHMoN~kNXu9!Ne$Tt6CcXFW0l)dKY_(~y)nUy*ar#ekf$hjh}FCf zd5TJnY(*+OZ(V)qz|2i4aAbE$Axzx?PM;5?@s$iKeORdU&&gV^sO6-C3-6^FF>&Cp zzZYHS{g%hd#J`qX6mZw$ZD?|}ie0%LM%Cz*WdP{S;0}hbsyZXt*|wtODmTPj_k;in z%m%TDkYNy>dr_}Zl7F#9@Cx(5F0$72LGLic4`~Sc3;u=$7=$d@AO?}B5CL+bqJTh4 zs`&d#v)%dS3_fjhDA}J_-cKorF9(w!%qw&}T>C%V3_H^280yxD30_E~6*6pArgQp{ zlX^e2kVBm%VqVK&H?7!Lbs|hu(~TUw2%mGA>|2O=bQ@hT#Iht20yQ_m;Ob>^An_{= zJnt>k$MW+=O+*5xcu~EYhst!6t{tKtu1JZ{44@KPJCrmbFvgXMX0Z!TeMG z_mPwGi!!9SI9At;{JVu)JB7g4efDVk0Ejm!%XtOL<7O(IzlTATv`EyC-{qGyWBMEx z7w#$C01pN_`w`_{Or8SHoWxXLr391_PQ?#I6eFtOU7*G?=9`-3ZrmpNP)8GnA z7G!#tPpmR&xxJ)hF0$&YGUs5--N48$2Y|rs7EDO&w{qu>$QOi`vE43)#YA~`Am zr+@U((nuuo+t1A!bR#Ox`|WDInnVS|1P|MupetM|BRaEL3ya+R;qo`?5TS4hi3p-A zgw2?nBM`sugwN=KB`}6wx3oDur6h@;d6jc1gerGg z0vS5tt$%i52#8KbrD0b&fz~TW33)AnO|e*t0~GfLR}M#E#luTVp1&rJwFKui%FL~& z6lo>n6GU)DygfolE?}%;S5DsIKCBkyS2)JcVe;@gusp0L4OQG&Ip)Ti)U*~VRlTCo zCVdmx0odJr<2gyZXIAj(cXhIv7#Ve~TLqwCi-V8CcQJ)0yR9rry~iMT>5U%7B>Z{z20Zd5Wo<)#7wP3msb!VDE01s*KW?+JZ426)jJ&9BBmlxrER;xtLR8Z_(%dz6IcZ`nq=ksNSgkmk$b)Mg|LeT5_Uk*Hg4gIKM zY?RXb!g*u?yOj(2%UDL+o3!=^Ta8xPE8=tjCy;S!DY9b(1}3>$Wi%$dMIBP1hzKIx zT3YRU`7G-5v}2nbG21=0Ug(p{QFEM~#b7_Z6&SFoEW9;FSLhZ?6m|pBVX3ngRS4UN&z5f)gvpQdpxLHC z>?3@F#h3@3q^Lx~SSwK@SIo?YpjyZA%v-g{HhQ{1#;TNCv{#iuMoYBCT%Mq}wL9>U zhuNP%yhfQAV}kb!{-2X;T9F@p{B_m1`?IMFv2l34V_BFv5$`8DlS$XyYXifn#vM#t&@v+>2d0aEFM9vidXnAVzNnqpwy3S0m_+HSKmKb( zFf1)yS<`&sq;g6*e4%xD)$awT6HnVbY83UT0zYX#b>#Cz9>J9c0KnpgaTLI-xqEDz>=*>fY7YLt8o<%z*jvhIB ztoO)~!$-CrKKh1!j5rEsoF=K#B(c#7rSFJF@)G^kS=)P^8~egCJLvYi@rX@O(Px#y zR{Fj+VOI`GL5}mQ4Y~VTg{wdoua&kgj} z^wm8soAuX4EUeI!^nE^6!+_O3YyM%HA&-_K-_n63<{C>~8j;RY#R^{SO$+>2$Ae}3 z>`eKJ!pF_IS-Dh+Dl|GMI(n1a?@q=87?FZur1F+)smnAPX>^TICqy+aESq6UZ2Jfbleu!@k89$9?ihr5hkQI7-0Y=lf z+#03Vs~`=mMb-`^6=s{F|FfhQ)~r}up*HrEn=ic857l2O2)Cth>V{imE719o#+nj? zp~R{{{J6m^wkWI$vDBneC`)b8+Q+fp)lL4g_8O-UamwVe`Vy>=W}G;6X%d+X2-G({ z*G9k_lZk^#os==AHhZN!D!pa}q$2%u+K|cQ+Or=%%ye)$R9D=57lm7~wjnNn%#YHS zxitF9D%{|&S_R*;H`&PMQ!^GsR%GX&uS%>CM@C{xnzR_(L4ERnxs$`CxZ~nn6z=lA z^ZT`#D<68BWQDu+l{E%KMU9RIr}XtFBQ!&0&Bl`>4L#Dce@j*j*E8;^f{jw0Cf&TE zx~yzPwHbgcn^Xz5GPro8t-XD;wFI#R)Crp)%U@!rQmu9<#h_EAQae@tb!ffK?x?e& zSe@NrueWcW!#uxAgCx(@`--dg(BXGtCO>`K1IdF~)R9P9{6e|^4>QUipF6~9R%v{W zNF0ue9^2bi!7`=%^lvuK%uCmN@xCT&b&Y(?z0kh+tlRHi@ro;b3GIK;SgxE|cWPr3 z4c4v^5{bOEIxTnUi=P%Sv{-Y-tV7gps@}f2D6mno-rd>ubB;$e=S{~V5 ze!$3LF!+^JZ9WsTcyM;IqvstwpTU$fCZc1+Y~s?b9~0P|;&c@nk)M+%wY1m-&}lL` zs80;4P1kjo^y=uPP5crru8o^|-n^*PBZkb)NUW|DD_&X+jLvGYSmiP~SF$XCt|nbI zI5Y@yI8XNh;g$3{2%_ib+0PYeYK&wRl91sYtnjOrfTsfVZk-O%z^M{d`CDpHoAziw zsuB4P(8m32>N8Uh=izUxH71ipe|uOy5566-ZSEP{(bqrr)@s}}bs6fOQ9-JtZpi(* z!Kj>?gjR(rAmeIyt#7dbS1ks2`=#r9T6Qn33k;Qi%Aq8Zy2hXb{hW*|>9ta6dfkV<#QFGQw%e>A3w#Z$I~>2>GjPWSWk#XOe3=A{RYRvv41tFC~zTzrU#!{78JU>_34VLsQ{!< zmuSJPUt|9cE;YyBDPEkT;L@2Zp?XlkVVD5%)w3MsUrYA8NvcpjKlZ~7d-CqF)u z|14m<%{*ZXe(wFktDQ5(Q}9B^T31LeKnnyPdftU%1+}bwZ{2xIIE_u`oyxxg@W+)f zFKPGA9Qm#s<*M(xzJnUuc_1%Z8Wi>O-2LQ5XpcL;NLyk;u?lM&xtW~F+X)CT?zw4J zy-sT{vEt-p-W6ca({Uo~*76%m;sfzY0jFU6@EwZFRf0@`SMm0)Px%xtsoa5*=S8TN zUU?@@RcF=?zay={KKw;iYaR}aJ^>yyLlx;HNY(tH=6x$^iGE&c7}F2d;27F>jjPaaQDEi@Xx zLENqgJvd8;yZc|<J{D6OZh4p3npMd<2Gs9uEWF-zd1Zq1fwf#3{defp~cT ztH>0RwB63(n>`gSC!)rr8gW`Drf_6-Zg0okxauMjf!IEMN`<33I>=@q0u)ojQJFU+ zJD${7gUHpHPPuV?8Zcalu-Q%_W6xu!5Z3A4Jbt<~?T~{O4p*S(&0paa3Jjhq$2ANS zYeXq(el&0?_THVst>;2-fTBL09i|ilg&CpLt+>ZwVgOmv?ee~y z>0f@C&gqkHmu3MBlgEK?rH0za0x$-XJl-vzmkj&v#c?t3l-EZt7Cs?6B(Z~nV&NT;&|A1t!X_GP2l?~2anw>vv{doJI(L_>m>;9e3A`-W?>KuuQjdwtb7}tX$ zon8#;bRrPfk&dAo)Fg?k)`RQoi#93S!;tK3L9ZSMWP^SvF+0fWL@_k_@RLq9ibg;A1a1sff?sMn*e5Y- zd5hmy(P9zdy!grq5)F`B)oQs7pphpk<8l72_g|mk&s4HsvKg_~$5iX%$844sf24n; zW^oP@QJ$#ZMkY9(Yy6YWC*BVJ3iZsWA$3v@bon;5|JI>R|Mt8X_eJi&EdJc?3g_OT zy1cEBW(R7{q%K#sP+6iX+M393-Zm0UsoGL^Vs;9Zmijr)oBNXr68%**$6lHF(# ze)ZuMNeu`hHP#1ZJXv{ezj>vrrGy9U6#)ui&#f4w&Y+iCIkB-LIY32ge>=Hbhrb&* zTm}8Gwni@3`%Ih-n*1Vgbc~BU*y)msydIlyW`5xu9lE!C&4T+qD?iZoiaO$#M?39aMOIb(v2zx3 zf#LP|rK^g1GyfOqa2Sm$sppTDlPbQ^f7~}ZjEoMc{gr9jPW4Ymi<33Rv1VAaL`@u8 z*Ffwmdw}f6J$Sg9V=RWc4gK>F*Cmw~&qnH?|tvWZqys`n8n^BVThg4g;%U(US z{=UMeiH|FJG<1(8Z0+KARY<5ccX#u{yh>dHh zTj$K04U37&!@{~94?iB2F0v_Xi?*>de=it%IinA=`Tj^A`IQ|WvGUqylYhjO^qIW> zP-FPrm5J$sY$Plts@t7cr1e=)MWv;c)J~?cZ;C_+rwIwu)LLwIzv00axLC()DkGN0 zX#h^arM>H?JxpD5Dtc!N$13p_UOoR-$f~OS=V1wNDS;7F$?=3pBW&lw_pI&24+%x~ zcZBV4@ZT^aW(i-SUPr~=lkxHaoU-nhkWjRq+H)FFJ^q}DX@X7BXvC#g5jDa`%<0y* z^0#iNJML!A4M>*uxGMIWjvJi9p;>xA9-_i${J+MP0Ni#M&>Ly+R@)BEp28 zWmxp_{0E&BFd`v*fm!!eXLfypk6-xBm!Cx zk`A)#n6e~mrKfpsVn=Op>(-D^v~WGh1-JaOKSHs8a^A}8!;|Mt}CLe z+GHU{KxAdS-+F77t~c^=ASR}mI|~lWxIs;Ia2!fdDdLDSg$H_<2OUPQ+a$FK&y}rS zoF^O<6+OI7S2=6U7H5qr_t9e+;ln5vE0{cqMkW`l`Aji(YsdkHfRkzGX&|hU{SE$D zI7-jk&+H8QodhdjXb|iKZF9dN$rbVzTk25e5V(=cgN`Ey`s3R%!eEK#2%o*I)4#`NJ1F7G-aI0+K(u!knX|JyrUU}@j>fnyod3p2*!uz+; z^WmgdK9CL8KnrONwB_x+UvQ|5&lh~k$za$IZsX79M^lK*)c5tt`D-%~gE-fCE^|kR zgwcGdGO=j1VVz*}y5yMh*@E0rnmqq$h-J|_VRc#R6>?KgjFjNpj|n#h?Iy}DTb4y8 z?Z7xX9?T#XzEi$5a=~~}^}HeKREkc(5Ri!%+4;Q4BG_m^5SbHP6v-2iioy(TLuTyn zVS2ZImuuQUTJQ(+3&x}P5aV9H^Ktw44}cspTkBtEW31x`)>(6o@4S0^>21#T${&VC z8ktL*UVw9sXal< zq>NFr_h&`B)FQDalCM_*5)}#*Fw{b?`U8&srGX_vDy?ob&81z9^% z(p$4Kzc*F&;4&eINjMpA{i(dLhge<1ubC`3_K*6RrLgzUb(hz7p1DV2pH?bN^7L|5m`G^fa5yp=Tey@a)M$lbqd}}R z)EDOwRmrArl5456b8?>0XtQ#1W^YU`IfJs>=xn(Xc(YSl2OZQ&bJ)L zOlI>eG6c5Bi?Sy@S6?vFda@}!X{j5svo`FxktNE@%gf55=j8#L4}^Nk3%`7F%_Op_ zw$6Y{Ty>D*s$2S$MkQ{4{qVg=n-xBK2j{~%O6qF;aZN?X6IaiSG-n<*=O*WKfGDrk zoC(DvoU%1Yu>*n~QZ^FiTQq$+9j4LR$o*`k#Fm&AwKGAiMg#&iA_i4TDRi0e)S%ubv; z@JR4m(QOxkjEgfS=RQgLxi7Onm-|uyqe7l@C3ipY2w3(iGb|GKH!0-A&_@@ZM8$*= zdx6sh;yT1@_6j&DKfZVW-#Ak9(ggayG}^!P+{5>t>2lUNF`}kX7KGB4W=M`#J)Sve z-(b15)sLc_shf&^`O;oF`CFc}-Fv25hs@17 zEhjg=G!1jlqiQ!xt0PXME@9PDuvb9dOWStl`HV`4oHR}I>N*!Gp4fKI()zsA+aO1e zqe^*QD;n*OHnyHUyTJMef3;?^FGb$E`~vft&<@k)6^9^*DBs&u)Ahl;nRNBxERA4Qm>R0dx@_a1!6eY|{Gs7LW?<@yPO~bDJAaRwq~k2&G+$ z5i1mDwc2B^_tx9(kBu1W{Duq;bOT7SkqH-zM6lPWmCL(j4}!82$=Niy2$WDZI$#li zXkAs0dNd>K)xFd&nqRsBk;vZV21O4s5!)O8_8*eaT_i49NP9WwN%C-)v!wQE;LCaY zt0{vh=>cag{_jAM35mNuA%>*t*B4Ww?tlGIu3T*t{l(7$k9V}WfXBCQ>;dQP1$t3E z*wPmpn+UZHq7TfBn#aBD8>S?iI-eJo$Qn9MpJToct1|N6(NQ!T_@Tl$&R40aeY_7S zMqXEjBrSP~kBrOnee5F7l2lc9{VXZppgq4XTdy~Mi@OO%)ao9Fru-Ptp1)`6|AFg$ zbsvBJT*}^8u+I{geZRbyt4GQV#P&ODwaVoKEif+wkgDs#Bhc+ky|Lf-+bCM<$o ztu~HK)wf9dstC7ndc#h9Fz+lQ2u8y#y8clC9B7kYJX} zfNC|UQSHl29J#!G>8f19@-mV*`Jhj&O;k3}QbQBk z!K%xuR$JLYZfU3-7oikYt=dAmm_#s@F{riLO3<3;1XKAZ8nbvVT3yB^=*luebt&lz z?lenEy)r)A)ldy#bJ1cIdoZfSP^+1a+r2 z>aICEX#KB)Y>(djHQ8QG`3C(relxA?Hwwj)wng0q^zOd~wC5iMeE|s4zt=e|FV03; z>-`(_-~Fdl-{0G^iK`I;z;!Uryp%+@IRwX_ZZzhJ%6reV{`kA%F7>+ zxmo?qz!J1l{%4`d^L}feFxqAw-jA2Jt67CNvgAK~)`tRQS9`bZ^7b}glvIS+Mx7tO zWPUTf`|Tz)Ox(KH>i&uPdwy(Jl|KT2pIE@^AE<5tg5C-0zkQ!I*5YV4xj})}eFV^+ z7X&nAfU6IAQ(S(bVUn++s8;un)YbRXJD-8$qUaoVUDPf8#WSFwyzMWQC*h>4Nsf#2 zuhut+TAagyHrm&1(Ifk9)1qDCi6*9_Sc@yV*~j!D;hy^_Ws8sYN3Bs?)K13~!BbU{ zz3iiz7WUIh8|^{IFL17TME9qPP$Z`!+V|=9;c+eAYQgM2#FZyOX{WA4>6qDEfvqB`*wYwb!19|@8M&`=#M%U za$k40TYa(X&1Ag&xUAdP=~g?{i`o|+m*w4b5A9?G{5=}&06rhrJ^kfej70}SDPHZY zyT}mjA(et_cM>k{^IR>NmwweeT1KhX3JR8JEwtL|v_6u_30P8Soo`jNC$W3Sp6(|| z)W0EPYk)6jE?vZPON&5ASbP~3(716N9&r3xYGkT+s$FwU;&ZILT1FmrYNm{Hu2RF3 zN7|>4I4AU}yv!;R|LBCh}$aA6OV=L*XvG>GwmZ&Fx}!#W}X5=oK6HH zAJt;AHU@KwTf{U9TRAuK*5D~H3Rs}X0SW1zK0Fq0P2l65Al?XBLLV{dap4F?TPveIJGy>d*?Y;7z+5( zW%lvv{pm99BQba!l6YAi3ban}kM&Y&_LrW0WGeg~hD~5{F1)TtrtmvR>UG3oE6j25 z$VUtxcYhZ$zk1%<1oex&*gN8TdL>Y6=N4-q00ZZiYt%Z(xv4!<@xdVJ9vcr7l)p8t z7wB>NKB}iUs65FqOjzzY4xcw*qd6Z4;CTE>Q-&H(g#3h0x&&ZkV_w@lqUHzJ0!o@| z#D$g9D>;&$@KmCOnv|?380cb^ILg-Ee&CywZa*tBF)x)Ka%W-?&*vn7^o*wx2{kD@ zuKQt0-2OO;1U@q7N|tBy=2{&mN4DIdad1c^O*b`e{t=37nrOUzWRg+gqRzZ$>;AP* z$im!96Ul3Ocd}+KpYFXi(z*@=HfO?h|xV4JG|d!Z^SrYxOZKx0Py2781_RH&{nU*JKtL$UPysJ zUl|}<2|41~bfw{6&_&I{mp|ss{YV=A{jT)8MeIpt^i67H@ncZ9?>y;G@ib0}mpN|* zjWM`PL26qOC7WA)lWtZMA@ZT($a8-l-#joZve29jSTkV53zU4qDG?S3`T#wp{nSA1 zyq&Qm0NQ~7kp8|QcJBNx;ZFpAgTKQ+;$QLa`0o$M$3FzQN~FY~(`2O%?}Z{;f&bMn z0WI%maA+4D#Jh=EzLT{mMlo`(DU79@(lQy4X9@!K3nnLNA56XmCh6osyw42;TOd5R zHN=;Q68CN=feaVs7b&6ml8CT}5YWp&$nd%$+{Xr@AD~8mqy%*V|9O(OPsod8nQ)Z% zj8p_WrDuo7j(0;a0kpa;H~1|8J_88ifaNYf0MYF{*n5y z7z*2Aix&Nb5%u%l*d@S%{Z2Ulw;0_S9Mpyx4r52j9Nd9fW^VZ!WiD7Cj|Ub5v+G}# z7j@-(e%Gp&c`F5EOuWimW6wRh?W^-pViMhT$-=6leit7Bq_>xQ1i&R0zU?fvY5Nmd z7Rx(<_ghV+slsLBQ3m{6Y{wuXEngPAPQozMa@(}Vog-1En&`E(4fLFAXBPnd(5sep zo0gzhTIgNs5mLT%OvK8`GOr!Rxm-ASJ$PkFIN#R&cqJrJkB5F& zoaluM`w2t^2z<@RIjLDA*O*C8sIC}7zRd8^q#2|TWh1uu{wf_MT1q0OiBqxlhNAQ( z7x&(UMNBi`E%EAPVjIkUelM~TFoeq~K^H)TJo_m} z0VH#zEY<>I&1G8J?| zvxm(L*kK_Z2AFX@@U(gnKBSI)2JNi(jy%K5R?VZu8kywN!b>3mA%k_5Zv{?- z4W10LPZlqL-!VAsl^r7&A$8>xOB*PrfhH6SCy(S0YE;cDHK4OR#xAKNIRoHe7=&sD zN}~GOYE%+|Q%V~(?@@#N0_9EtukRWgqmDWemBk<BNYnXk#4$S@(-bvR1&2y=5nRE?{qkG5xcb)W2fDcRI#&5X#6R$1^Q#kIPkDM6zdXZ0W5GUjwgig3 zPxAQ9@|{Y8ceh@|VkkdKR3{%dk>~Gx9HacYkKTRy8}a}A=)05QBYt6$eVx*lM1TxQ zb0k8H)J$rc`)F}>5e%{*N(BHNPBDtQ$a3?50CpBofe=$Y3(RgZJWafaVelL(r$&U2@N4;QBAtN zz?2PgE|9Nw(J;x6go7(WXc!UevJzuL46tV0mRl8ex?P6@Tj4{j#u3}NX&1yk$@vCG zHx6p4OwLGpa&=$+RZNM51#b9k49nc*!9%!dO*XGO zO1OwUa7dO+JHK2>iwqP2^)_EQwXkKh+DJDm)c&w1*4}-Q=ID}bqs?a# zur!FRDq$X2FjQO3_2!bfvRU~^broQ0;dDS8zAgE3F56LPDo5Zf(3~I824!sR&O^P| z?KO@bA^L(rfV24RvwX%tZzZ@_nHAr{2AIJ_0FsBDli1i%CGRE=INhJVU^CL zDM|_mBxb=Q6VO60m13}{My;;(spbx(;8sb!R4t()lUkYx?EM_f;Zdc01jrA2N`@1H zVW8NJrIUa%2aq7)DZs2?6LGw_MS>#FIoMsz)aY%nl}}HBse3XR4HOK%u%J>kUa?&m z$t(vLSfA?^gC$C)Z}gOHXJ_QLoN25w;4r~fA*i;v5Z5u`h-wklIz0B|l8+1FO0HQn z-8~%-$DASQ)RtWlnZlTc7+1_AI?ixBJ)4`x3=XD~?{FW1OFdXf#sC1-ilm(lO=7ob zq;B&{l7J1w(i4GuAUWPI&Dibl^<}`8!!G?esF5YfJyH@c)l3*ucu4VljL!+W<^h31 z-UaXHDe0BOMYBnNc@Ei!{iXqfuzo}TIqEeeqhiN1R(`W`uUp0l@yA$HB z%#0wzh@GxC$bU?)Pc^ohLr*<6tH!TADb31_s5(bm|< zssT9a-VEni*#fB1_P1u%5+G`@D!;#w29RncQ>6FnHi@+~PvPc{n9roUkAw}-J`f>- z-7vZe? zrpx&q41gay*s{_G;F2YYQVrx178{a7gRFJcOyr!*>yShcLhx(g`ygo+y*S4>?~5 z;N!Y{;M{`%ju-U4*VQiyj-L8YM)b1#GWL%+>F45<-2(YSA+d1Xk#OsuoZtD}dhf}z zc3OSIv!}}Mjqr`j{Y!7S3U+y~_Y(L1*;;%LdBj3L3DuwfqHlPw-O4SmO>b;Gv?x6h z|EAypi`B#E*)#WL#1A*aIf(uD@c=q>yha+=sz{J<5O#mu=lvj@l70&tYgsjudD>iR5ZI18g!=V)Hz(h!G*v6gD)1U)gxpp&Yi>*pe z;S#`o*(&vDxhuI=_al+hE(eAGl^2@l>y3utM!fmC_wxHsPireE%SVI8KX&-x^5^+y zDyXn|AR;GiafC)ZpF3LXUe*JR#y?bpJ1;%_?A-kP=)V&DFaG~a=z9PF?`K$%Pu$HT zaB*>G?alHjyzQTC>0efwGTrsy3wK+udW%>2YtBv_K2)<;-t=@TeB4F<+IgqTIooO2 zbm=B8>2V0cW8J++m!sy$AKtB({*v+exLZEEcnr_VXMi|Z6fUPeW8}L@hHZ>2_6z}J z9_$QBbp(jr30@isJ7CsAGCpOmd&YO(QL3^eYyU1nzJP(Y%giyum#C8%?V55fV90S&)(O=#{o+@J4NSu* ziMKv(Oc00#WB>KW`d-~E-uP7$HkbGw6G8fx<%1r2-)Y7lyRoH=e8XAYt~$DY_PcK1 zdinhK{9}WdP?H@bY<`)TIt0oYoL!;}y`J8F2C&GGYPrDfXi_oY9sMoyI@A?PqVMk%WlW@l@iI>WF**^e?lCq19nmom3k|j;t?C;^q=#MxpPQt&$EeN8%Lxnl$t~bM{BnI~O=wmsf9qnv}&kHO2gObfnk;P`< zSPl$_T4xiZhn>)s!{n>w+)9mkIoNS1g!YMDcu|GA;UeRO2i_6(7ls_PMJ-896jrgK zGGp?>{x4i!;6lsxwVj$%;o|LArNnmBBSj5hs>|B2u}y( z$SfrIk#&Jzu8cf}YeX*LSD+S%#Khx`Za&U(c>T>YR^Ug#j`nGGB_~p^Npv7anDI4s zp4K*9Tqk*Xinve)`D@AaSMS$_U9e+78G!=yZ9zv)w01dDKg+$CmIvZlR zyd+}su25(}{6Ycf;~9wAnzVQ3);V>5io2hxHiy1@-m6d9QLggu#)SJ>Nl6?|{z zh*Rh6@nH3)W-B~Q#Qn@2G8yCMWC#)?PuUoblgD*2u$>vkGfg;I<$T1c@?h;#MDC&( z2aX0RPX1`)k&uQ~Ld{x}Ip3=Qk-`K~xU~q9lp&ar zO^d{@8G*qeH~d(Io&sZTiqtU2=0yNe-5ofori-F%P=p)KuqV9RhT=EI@0S(a9`n*KrE<8C|im5}K9uyPF?Mc^7kna9u$Y^bexC1xerv4iD9KR)S~ z)z?m6tm)Umo=h>u+2t4PJhO9#sZor)hba#y%2Hs}j2{V!4l=nkBc~W~nWpNo4L?(f zawAwCYUVtLtho(7QMc@=C58=K0B14t7@{<^C*zpu0saVb?C!C|I*p{1j0q-riTkL@ zknU2QQ2uniV>N2^!$rJjjtbh^-_p6ze(>6Zr1t@wyN0%<+4k&=jt@PmBRSO^fi(ip zz;O)g0YBS0l32<+oi>q;|IqPuH}RG|7F>Yv4h3F9!=vR!_1_#HwN~P^-oIv&J>^cy zA0@|MRML}t?W^eIvGMofF?X5+aPgn>d)((ayzHYbmP3fRg~P=m_8TQRo!iyH>X3~^tP9GQ1Uir=QxcSYGoX;T>4ycwsoY@CfV;%(kG87n30*1AXv?(S#Y z+OrDMHru9*b6Zf{a(o@D?AkM(smz6KE;n??X)0bV=BnDZ98||f>R|JLg7hl1aC|Ds zoft(0-X5YuGzE%lR4}nthCwxL7p>51CGD2-Fcbh9oD1k!FASL0#av{J(PxC< zfdE@JQkM}!#0pM@AaR?DBOF?uw%cY+)dMR7>+TK7Do8o~la@PoeuMP;q(3A5E$Qz` z|4jNh(pO0GDJSlwglKiP)LiC@0fpfN(01Eb<+gID><;Y=&HSORa$7kJbw+0XP*=IF z9ESFptR^?zRc`g(G}%q<4t15=CpzPPWXkEPoSACjNL}iLwbmTeQP+fmau$uU6q6FW z6{-kIgf!+K<6zou{;C=_UbHH^0!6Ic6Qqh9**Vjv7`#pQ4|S)?ee*c6J0UjEeRtIW zX%eL9q`*U2WW0_-jkN4=P?EV{ftR-`-vb(|V%Vzm&^3SRND9i7V{+NWH^03T}>?()K zp>qF}lJ<&z-m#2;L`t!Qnxhj)qG7`^gIATw?Jf~X3Si{v7@_y0*P~H>L(s>(f8Y}& zx%@V7K7qR&9DI}fzwO<{z`z@1s9O^Tc=w+qVA4P}AL0&OZ9Xjlm?^BO1jCN^-_0ii z#u3N^86Jz!m~D4qYwC6?-Jk7}jKI4c888wdv#>|~E&~Ho2nF|wS|g2w0IDO+Q8oHy zI+Q}N|A7g$P)06u#x+;A{mb}z;PRPvTl)enPp|~6?Kcb;pqV?}Uc;j- zGjikPM(j;?H@X~^2&Zss%C!(_a7$g*QK)u2L~$ra@jVfGLOd6M-Eo*>QHa*W;u{w= zf{Yi?Twl(fg$tc+2p$NqbZlq@h+=a3LRk_3-jk}ADnnV?2jYE>eIR5TuSR>_{==g# zt4QWXcbThnqbxIfm>tcQ3r-g6zHKnvRHD?6`cQvrJSZzcwi?TYYLIfERczOBphil+ zkcxK35#|eoJZ64c<^78WDg}?C;v-sYWiI!lr|LHIefp+esxDQF13YNexAsh~To{Qu z42S)SPthfG{JIKVAIhbJ^{wS%a8$AFpNOwpw64kZ-X%GuY>>;9kw3@7=C+zn!2~70 zOkL*MKV?2(z3+L$L#tG~D_xw|TQ+*!_JxA>G$506^;EM=Om)m&E~~wyx3oQ2ZvGA2 zpO^VJ9yck80s{|kDr}cu;7AHi5z?hE;mU9Y#5EV-ZLEkcQqY0{Vdu-3WY*K-V=eS; zvX`s11^Q=LKj9#!){+52TNkw;pyi^hv;r#MRjd=;lM0J)tf2WmtQ+m}+RAEI)e8|V z2jFEwq3t{>N;qkfNalNrLXTTpllQrDxCDn!W#&;>Ia69=mOmrq!OSzt-i-|}E36k! z2!pCIGBJ<+HVg z{DjK36~FWh9#HJx5>FL4%>Q4+AO1y^m1eY>ZnRzWpGz^_Qz!oUeV>_Xp#uraIXH_R z0JZeio*IcbmB*$ph=yl+29M_RIV)zwlQZ*os~}2iu7bUb-*@K!iTvSzpg;UK35Wkx z{o#K|I9PKxB38Sy4~N4Qod!7CTbG+0>z%ajpMNl1GDQ`b>}!z`qC`UFs=WpAcD~i! z1UJ`OAgq~U)Az-lS2h4vMgJ$lH&=eH0C<4C&M}M6IbCR=QFba5=o4u2L=;Y*M0TU^ zlIP$%;j78t>K^0V(<0yLf}e$Q6&3MD#6&`PBm_=pHz}hsvM>`iX^83$WzkTXAp*lU zi7KDY!~wYOxsW6JvF)53OI$-JtPKdlH9#E-U7;0PB>{ABtuO%4wQ7#QJ08g}>?4wv zQ-ZQ`MskqYuxAqPa|Do8kx|*#0j!;qBDS8Mh_{_|lFRQZoqtH=#~*F?B}F0?am?Gd z7oX5D29q^YuKMo&k`Pa3Au0;{nmvYX!~K<(qL8%YL#4%rS~jHjCEx+{iUm<8REIuR zkc@tz*;D9pF~aDoc9%tb-4H?Q4p#d5h>Ar7U$-P|$rUrKjxm=$!Wq7Y0#0JqAevhx zgMqRPt`4385&WADG`{GB9YbdmEc*9mY}2){ETVu9xJcZ*2B%!JC6>O$Lg|GP+I`r@ zhJKFr5}Ac$jpd@7W%5Sy^6JNmJR9O@Vwg~`OevXPl`z9w41a1Q-;)pT^&WEm%S6IV*w%0#+E5 z=_V2+514^L4$l(MiNKz9Y)AbdfNwO}WRO*WMW^<4UbXb%+%y z0}lwe!eyD02wBDrOL!x=foqv=Mi%i6$@Q>MJ_I_5c^PzP;3CnMcJj7S?%OVoO7*dm zx97Xd7Q%JO)PKvL2GR7P&FAT-+g6sf13n>-)n~hj^&L!Wj+oEbW6dV4XV+_aM#*UU z8koo>=0&p|K`eAODeIZLbG6|n$1)u$vt5i#$4`xhM@)@%%WcOemNYdJuVv&9QAaN>jMBjmyeuA%pZvIuF?B0ovUNspi&I+$~%ulg?U zV?K?JH`47+lv0?&$%dic`gJVx5LMI9_w9E>{L16I3QS+bZ5&GP&aAyRF?xTYG41(E zsll{?H;5(miFt8cv#ph+*LKHg5iwIDiote8LH_EgUC5nn*Ur}SXq`z{Z(|7T(&@`7bd}DT3+xe@cV(Q#5+CoA1?%0GoTqAF@4L?uXE6muA(?pIdVhRri&|&7M zWdn?%tV73uF~G#6JwiX2W_B2C$rJIgcZ^+ALuJ*8g7{Po3KV#SWUmP;q6`WIaZAY1 z`y3UiVP<;h8$>{4Nt!Mhwg!(FsDM_1>^2jsBzD!iDt~_z6ds9k3h!=!0$pq5Qoe3` zRB6n_m>(0~8ynF~C%9&ewa6w;H0(5171KHAXtLcrOd}?lc7%nih#gJ9^+^*=ZATS$)kNfCl`UVpl26&R2o5|qX zY3^{(?cNB3&@v`+twRmsp!i#CV1t3I(BgF!MT5;ZCOk`tAh3lqG6qR8KuC=Fkjz(} z&SYW+bTH<1kb91VUgJ^NB@x-A-*)52z~Eu`bk7~<;iXCMH+kuBqjb1NYU~zz$DF1J zM^Bbk@EI4ACIy+o&@=Pz-1UN_7B5R2c2uRrqfqT^BoubL2|*Wei+PRdCq-9(>ZL`J ztGiT_9hUSjoMS(P7E0!NPDl1uVZL^qMieulqm>uNY6y)Hg3i$_iRq&(u1)SPcm}+x z1@ZXIr63mBZos(y-Y*Jqb zXKhXrYm52I@PhtCN;DO&S|j-XConlZ;D=qYU(%vL@A=q6*xw6Zy>ib|klJ5;c`lrg zg(a8c^^Q!;4YfpMvLtpA9Qb8w4mh)vJV`|#)NO2;Gm~l-^a!Tl7V#R(#$pt{BYqm# zdOZcC7YJC@@u|t^wR42u4qVh#pW-W+E4A?&I2WEXOTKDtK5BBqfDCeo*QP|jXuAwv zcYu4ztVxM_i1HMX4j93N+2&c`Z3raYg3Z^og~1ImgH2<~A`6QPOJkPlnzGuG5DwYI z#37m24Fa}Uo`%1T%&*0`Uhk^*`mMR5c6ol`Ce7Z5-6 zF+uQ0E?bUeb6qLANUFT3`h;!vhO9h|Nr&-1^~P8YE&XaCXS6=1kD+TNtj&cj`9wS% zozC7GS<{9ZI`O<9NpOIHtne1^R)G3|OsNY9A{fwnDKq@waS3G+md1eDnhNK184@%} zhhi-)5wIod9*@GOBqE=oWAxW=FUjcvKXVLRQkKT*?~{!hpCA7MY}ue2abMea)9se| zvQb~mzOz!p+1fm*?mLP1UX@XG?yXOL>}-uKoHcGb7iIUG&Z%V^ZZW@s_hM}!jRs&t zjk|`oRtL*v%dxD|#zkyxI4eHQz8iP{8gg%X@?G1{>#CU86R7$Fi4Qy|d5O>J#Lu5*~`B#d!_HOr}RH2nharm$Wd z4hN~;Y=65&cG9Qe^hlm_E~QY&UNheoJiKh?ldF}7C;Y()3spj(vUgY^5P>;5DE~W- zL?)yL@(s|gh?m!=MBSrzMr5&x+7cn@NIY&k(M`BX2`diR4g_nfT>yx__()`A2{z<0 zI=*4Cw~!TK*J}7&3O9{P0H_xuJ_^OOpqdr}nK9()4j#S#|DP)TCu4&7?*|UV@gV`> zT8nNTp2tpK@V`1~Di*ocp$h4MJ|c!&AgC*>>h0J7eE>FAYr?Yt#6z+C3=)plsv?*X z(Nbt!WJXTs(8AgRYQ_X%w-`Jcp6X8SV8|Z2*7g4n`Q-EiesisMNj4JJs9%Z?YOn9( zhvv~3cyxxxhjgD3bGKglI{$lL*e07eQXjqJ=+-hbKB&k!`n+5bx}TG$3_kTym_rJ+ znBs^A`O%$2_t5(ov4svZ58i`|O;yI6=TMOq;nwQ@xs*IIB5^Px+$H=a+A~@G^ILz|Cn~A1(#uPJ&RLaRc=&FaP+VPc zS@U3F=_1Zus{Q3U=MvesM?J^W-nysgx?WG!m}6_&eV_0VgpZ>i&&(nj6VoQ5d}O?? zDSIz(;c~^XRv({BIdrAM+#i?f*VfXWxx10Gy6f8CZ}OFrwHK*X=hRtTa*2#bPoPwL zsC!?YujyS|U~6}`2Xd`L-DHCJR2RI0LSNyzQ!sg?WEpvw^}Tppbl+FJ=O5f^Jkr_&!V50YQBNOU4;dW-JG_qn_fkWNOQY4ls%Vz`Mv_ zU4?*}a)CC~$s2r-vEnr2vHHlSGOFysWr7(8fTtb7biO%AK56a`w4E>jPv7Vq7S&SL z(idZZ(kTG$Zlf{d1Z@`i+%wrC+SDkMT+KL{YrV+4`>~%+YTq2A! z*c=|lVrPyiP0Gho2WeT$k^BuOE!>3v>M;4bE)!sT8hwqFxskG3&U${AE=z7D4F&mk zwWs*Y{<|c*%izfJZ8?MhEM^k?8U0Jl$FvPuXm!p{^O z(|I+QD`MR798{kUx_fkkUhBu>_#Ct7j#6Gn3CTomt`<%0fC$+@i@W-j{5bHP#^aJE zgIgSA7Dop!ErSa}Tx1bd_bWZX6mif`LsOU@{9B?Eud z2=QPrzPKkm@>Gy8IIKX1B4E|B94Q+R=+YQC)q()70R#R!biMdMskFm0fyE-_WkV+3 z4nh;`1ls~TrL$OP2w$~x)7-NwtV*`4IAz;))=heP12%7>tW4pJ6D9!Ov7Io;ci4vT zp9n)~X0~BOv)ypZ=7bSMhrvj%j$C7KI7u`n$;o76QJyU6%{H$1ll^9iI{Ev`3j37t z0HoAT@=BG_@M!X}QLblTY_SNkO9?Kqm@|2HbeRP*T}1oJC+%dD%y4K2-83ufwBl?_Jq&1D}fW&_aCv9fL6|{3*MEqVM zg#U%V^RqMW`Kb_=BZS_#HKdmDeNgp&k;;B)S{7S_G)0?$~L%+`L5;dp* z>WQabd*iKlUU}xZFTRF?Z($Gz?|ty-yFc^yZ!0|3_-tN3{*6DdL9TtB_;kkM+f#!k zJM6S8;?>{N8Hbuk{)+GETx`H0Fz_KTd{N(X-vbZ5{2AEQu65nAxEs5vo4bWFjxB(y z>4qarT(-u`hhJbQpLJmo1QLbDU~za+G4Y58B&DQf2(sW%4pmZCQB_md&;%+#XSeR3 zG4%}$jf_o9&GH5OSy+N_MI@0aAgXBf)lAH=RuJaOx6HxWz$!NnFOZF**foAc!c1{)_Z#g{d38l24A3#-rhOY&Om_c-QfknPW_8+ zAzaBz=aiYs%HE}!_V%TRRhQHH*B-G5tWTNmYgX5~DW>&rj1%E)$kf}a+Yf7a>$srM zzw>Cdczp;n{bJz=Xk2Tx9;kNVlX}yqacakKIFVJRHX^5q@#C{7Yuk ze_;mi-Tgv2h(mjN|C^5M8+}LlzOwf{RX13{D6KdfRa}LsO0Z~kZ@CUtL@n9cl znf79ZF0l(o5X~{~@Q8sj!bt0|S`ai(e@OXhLaJyOs!bI%0TonenY|9VnX77ADLmEX z2o%xa=Q%4!Dql5g0s8|4{5q?eRnM9(suoqt>SfltHnap(^g=C%t)Cdx zb8VdS!{Tc334sEXFe*3`Y6KF6rr{lf#o?JUW6naBqRF-Nx;vB$J;imGXesDgmfq{G z4$gPGm6z^_H{E37sca(^m@0RSZ!9H|WG|(;3g!f($a)&#P3u~!;;-y98vQT0MN8%>Ir2&8FEM=SXAxtW$d`B>tY*#&1GIJNVsl|`1%876$rBt(gs!Ya;) z2h*N-P)TJ~R8>uNHPlqb;?zV{b*WqZw)uCTJ)Sj4a>~Ovw0EY?Op(PIO=Z3JE=RBK ztB5RA+HtXXK-texwYGzW`QsHiM^QPhP*V(QCsVcj5KpKlIgW9&wMHLUuUnA;$x9G}0py<9K8ob&=}`_Zrk1^&UaUoCc?zO;x<8zs3yl7f9;L4MKkR@Y1=+A}Zt7WTu;tAo) zjV=DJW$V59Dw#aJCmUvKO=IM}+f!hQF~%WN%g`Bdr5v@x$r*ca#zX8(P|IY?3}lB-G7`At#Lj}J3JFNI1K zmeHw~41RvDqehoUGl2^&fW-lM+3(-Cf=l|PEs9e04%HWWqM+$g6b)R#g0JQ9H=iiG zO+b8&Wlyf>aYgJYEl7qibAEI~ro_p*8(}T^X|W&&nhUlO9LDu}UAjOeIdTWbg-X9n zVH5b{&a2$P+E%rKRXIyBnkyKJ$U)4cSRgjIUK^=AMUB#Lp;}6n#VqGoH&Y`#Se5ZS zf}UwG8Nu`?&lI$cWTI0H&Kocy+{ !IGNORE_FUNCTIONS.includes(f.name), +); diff --git a/boxes/blank-react/src/contracts/log/.b7ec86e927411b6b375064925b8088b0cd141771-audit.json b/boxes/blank-react/src/contracts/log/.b7ec86e927411b6b375064925b8088b0cd141771-audit.json deleted file mode 100644 index fa7254f7300..00000000000 --- a/boxes/blank-react/src/contracts/log/.b7ec86e927411b6b375064925b8088b0cd141771-audit.json +++ /dev/null @@ -1,15 +0,0 @@ -{ - "keep": { - "days": false, - "amount": 5 - }, - "auditLog": "log/.b7ec86e927411b6b375064925b8088b0cd141771-audit.json", - "files": [ - { - "date": 1706781698517, - "name": "log/aztec-sandbox-2024-02-01.debug.log", - "hash": "be6605552baab8ccac80b85f9b173dfbf2652f4b6ae69d90dcfa0b09ddf4594c" - } - ], - "hashType": "sha256" -} \ No newline at end of file diff --git a/boxes/blank-react/src/contracts/log/aztec-sandbox-2024-02-01.debug.log b/boxes/blank-react/src/contracts/log/aztec-sandbox-2024-02-01.debug.log deleted file mode 100644 index 028e25bee4f..00000000000 --- a/boxes/blank-react/src/contracts/log/aztec-sandbox-2024-02-01.debug.log +++ /dev/null @@ -1,5 +0,0 @@ -{"level":"info","message":"Debug logs will be written to /Users/zpedro/Documents/GitHub/aztec-packages/boxes/blank-react/src/contracts/log/aztec-sandbox.debug.log","namespace":"aztec:sandbox","timestamp":"2024-02-01T10:01:38.521Z"} -{"level":"info","message":"Setting up Aztec Sandbox v0.1.0 please stand by...","namespace":"aztec:sandbox","timestamp":"2024-02-01T10:01:38.523Z"} -{"level":"warn","message":"Failed to connect to Ethereum node at http://127.0.0.1:8545/. Retrying...","namespace":"aztec:sandbox","timestamp":"2024-02-01T10:01:39.704Z"} -{"level":"warn","message":"Failed to connect to Ethereum node at http://127.0.0.1:8545/. Retrying...","namespace":"aztec:sandbox","timestamp":"2024-02-01T10:01:41.801Z"} -{"level":"info","message":"Shutting down...","namespace":"aztec:sandbox","timestamp":"2024-02-01T10:01:42.205Z"} diff --git a/boxes/blank-react/src/contracts/log/aztec-sandbox.debug.log b/boxes/blank-react/src/contracts/log/aztec-sandbox.debug.log deleted file mode 120000 index fd04a7caf58..00000000000 --- a/boxes/blank-react/src/contracts/log/aztec-sandbox.debug.log +++ /dev/null @@ -1 +0,0 @@ -aztec-sandbox-2024-02-01.debug.log \ No newline at end of file diff --git a/boxes/blank-react/src/contracts/src/main.nr b/boxes/blank-react/src/contracts/src/main.nr index 4ff9f9739ea..bb216edcf27 100644 --- a/boxes/blank-react/src/contracts/src/main.nr +++ b/boxes/blank-react/src/contracts/src/main.nr @@ -1,9 +1,6 @@ contract Blank { use dep::aztec::{ abi, - oracle::{ - get_public_key::get_public_key, - }, protocol_types::address::AztecAddress, state_vars::singleton::Singleton, context::{PrivateContext, PublicContext, Context}, @@ -14,12 +11,12 @@ contract Blank { }, }; - use dep::value_note::value_note::{ValueNote, ValueNoteMethods, VALUE_NOTE_LEN}; + use dep::value_note::value_note::{ValueNote, VALUE_NOTE_LEN}; use dep::easy_private_state::easy_private_state::EasyPrivateUint; struct Storage { - number: Singleton, + number: Singleton, } impl Storage { @@ -27,28 +24,26 @@ contract Blank { Storage { number: Singleton::new( context, - 1, - ValueNoteMethods + 1 ), } } } #[aztec(private)] - fn constructor(number: Field) { - let mut new_number = ValueNote::new(number, context.msg_sender()); - storage.number.initialize(&mut new_number, Option::none(), true); + fn constructor(number: Field, owner: AztecAddress) { + let mut new_number = ValueNote::new(number, owner); + storage.number.initialize(&mut new_number, true); } #[aztec(private)] fn setNumber(number: Field) { let mut new_number = ValueNote::new(number, context.msg_sender()); - let new_number = storage.number.replace(&mut new_number, true); + storage.number.replace(&mut new_number, true); } - #[aztec(public)] - unconstrained fn getNumber(owner: AztecAddress) -> Note { - storage.number.get_note(true) + unconstrained fn getNumber() -> pub ValueNote { + storage.number.view_note() } unconstrained fn compute_note_hash_and_nullifier( @@ -58,6 +53,6 @@ contract Blank { serialized_note: [Field; VALUE_NOTE_LEN] ) -> pub [Field; 4] { let note_header = NoteHeader::new(contract_address, nonce, storage_slot); - note_utils::compute_note_hash_and_nullifier(ValueNoteMethods, note_header, serialized_note) + note_utils::compute_note_hash_and_nullifier(ValueNote::deserialize, note_header, serialized_note) } } diff --git a/boxes/blank-react/src/hooks/useContract.tsx b/boxes/blank-react/src/hooks/useContract.tsx new file mode 100644 index 00000000000..e8b0b01b88f --- /dev/null +++ b/boxes/blank-react/src/hooks/useContract.tsx @@ -0,0 +1,39 @@ +import { useState } from "react"; +import { deployerEnv } from "../config"; + +import { Contract, ContractDeployer, Fr } from "@aztec/aztec.js"; +import { BlankContract } from "../../artifacts/Blank"; +import { toast } from "react-toastify"; + +export function useContract() { + const { artifact, at } = BlankContract; + const [wait, setWait] = useState(false); + const [contract, setContract] = useState(); + + const deploy = async (e: React.FormEvent) => { + e.preventDefault(); + + setWait(true); + const contractDeployer = new ContractDeployer(artifact, deployerEnv.pxe); + const wallet = await deployerEnv.getWallet(); + + const salt = Fr.random(); + const tx = contractDeployer + .deploy(Fr.random(), wallet.getCompleteAddress().address) + .send({ contractAddressSalt: salt }); + const { contractAddress } = await toast.promise(tx.wait(), { + pending: "Deploying contract...", + success: { + render: ({ data }) => `Number: ${data.contractAddress}`, + }, + error: "Error deploying contract", + }); + + const deployerWallet = await deployerEnv.getWallet(); + const contract = await at(contractAddress!, deployerWallet); + setContract(contract); + setWait(false); + }; + + return { deploy, contract, wait }; +} diff --git a/boxes/blank-react/src/hooks/useNumber.tsx b/boxes/blank-react/src/hooks/useNumber.tsx new file mode 100644 index 00000000000..ca7c76d5f48 --- /dev/null +++ b/boxes/blank-react/src/hooks/useNumber.tsx @@ -0,0 +1,35 @@ +import { useState } from 'react'; +import { Contract } from '@aztec/aztec.js'; +import { toast } from 'react-toastify'; + +export function useNumber({ contract }: { contract: Contract }) { + const [wait, setWait] = useState(false); + + const getNumber = async (e: React.FormEvent) => { + e.preventDefault(); + + setWait(true); + const viewTxReceipt = await contract!.methods.getNumber().view(); + toast(`Number is: ${viewTxReceipt.value}`); + setWait(false); + }; + + const setNumber = async (e: React.FormEvent) => { + e.preventDefault(); + + const el = e.currentTarget.elements.namedItem('numberToSet') as HTMLInputElement; + if (el) { + setWait(true); + + const value = BigInt(el.value); + await toast.promise(contract!.methods.setNumber(value).send().wait(), { + pending: 'Setting number...', + success: `Number set to: ${value}`, + error: 'Error setting number', + }); + setWait(false); + } + }; + + return { getNumber, setNumber, wait }; +} diff --git a/boxes/blank-react/src/index.tsx b/boxes/blank-react/src/index.tsx new file mode 100644 index 00000000000..e71d6ce7dcf --- /dev/null +++ b/boxes/blank-react/src/index.tsx @@ -0,0 +1,14 @@ +import { Home } from "./pages/home"; +import "react-toastify/dist/ReactToastify.css"; +import * as ReactDOM from "react-dom/client"; +import { ToastContainer } from "react-toastify"; + +const root = ReactDOM.createRoot( + document.getElementById("root") as HTMLElement, +); +root.render( + <> + + + , +); diff --git a/boxes/blank-react/src/pages/contract.tsx b/boxes/blank-react/src/pages/contract.tsx new file mode 100644 index 00000000000..1310eae0c48 --- /dev/null +++ b/boxes/blank-react/src/pages/contract.tsx @@ -0,0 +1,58 @@ +import { useState } from "react"; +import { Contract } from "@aztec/aztec.js"; +import { useNumber } from "../hooks/useNumber"; +import { filteredInterface } from "../config"; + +export function ContractComponent({ contract }: { contract: Contract }) { + const [showInput, setShowInput] = useState(true); + const { wait, getNumber, setNumber } = useNumber({ contract }); + + return ( +
+

Your Contract

+
+ + + +
+ +
+ + + + +
+
+ ); +} diff --git a/boxes/blank-react/src/pages/home.tsx b/boxes/blank-react/src/pages/home.tsx new file mode 100644 index 00000000000..8c0fb123667 --- /dev/null +++ b/boxes/blank-react/src/pages/home.tsx @@ -0,0 +1,18 @@ +import { ContractComponent } from "./contract"; +import { useContract } from "../hooks/useContract"; + +export function Home() { + const { contract, deploy, wait } = useContract(); + + if (!contract) { + return ( +
+ +
+ ); + } + + return ; +} diff --git a/boxes/blank-react/src/scripts/call_contract_function.ts b/boxes/blank-react/src/scripts/call_contract_function.ts deleted file mode 100644 index d80037eed84..00000000000 --- a/boxes/blank-react/src/scripts/call_contract_function.ts +++ /dev/null @@ -1,21 +0,0 @@ -import { getWallet } from './util.js'; -import { AztecAddress, CompleteAddress, Contract, ContractArtifact, FieldsOf, PXE, TxReceipt } from '@aztec/aztec.js'; - -export async function callContractFunction( - address: AztecAddress, - artifact: ContractArtifact, - functionName: string, - typedArgs: any[], // for the exposed functions, this is an array of field elements Fr[] - pxe: PXE, - wallet: CompleteAddress, -): Promise> { - // selectedWallet is how we specify the "sender" of the transaction - const selectedWallet = await getWallet(wallet, pxe); - - // TODO: switch to the generated typescript class? - const contract = await Contract.at(address, artifact, selectedWallet); - - return contract.methods[functionName](...typedArgs) - .send() - .wait(); -} diff --git a/boxes/blank-react/src/scripts/index.ts b/boxes/blank-react/src/scripts/index.ts deleted file mode 100644 index a5d6bcf1a9b..00000000000 --- a/boxes/blank-react/src/scripts/index.ts +++ /dev/null @@ -1,4 +0,0 @@ -export * from './call_contract_function.js'; -export * from './deploy_contract.js'; -export { getWallet } from './util.js'; -export * from './view_contract_function.js'; diff --git a/boxes/blank-react/src/scripts/util.ts b/boxes/blank-react/src/scripts/util.ts deleted file mode 100644 index 3f9d05bfdcb..00000000000 --- a/boxes/blank-react/src/scripts/util.ts +++ /dev/null @@ -1,36 +0,0 @@ -import { AccountWallet, CompleteAddress, Fr, FunctionArtifact, PXE, encodeArguments } from '@aztec/aztec.js'; - -import { getInitialTestAccountsWallets } from '@aztec/accounts/testing'; - -export function convertArgs(functionAbi: FunctionArtifact, args: any): Fr[] { - const untypedArgs = functionAbi.parameters.map(param => { - switch (param.type.kind) { - case 'field': - // hack: addresses are stored as string in the form to avoid bigint compatibility issues with formik - // convert those back to bigints before turning into Fr - return BigInt(args[param.name]); - default: - // they are all fields in the privatetoken contract, need more testing on other types - return args[param.name]; - } - }); - - return encodeArguments(functionAbi, untypedArgs); -} - -/** - * terminology is confusing, but the `account` points to a smart contract's public key information - * while the "wallet" has the account's private key and is used to sign transactions - * we need the "wallet" to actually submit transactions using the "account" identity - * @param account - * @param pxe - * @returns - */ -export async function getWallet(account: CompleteAddress, pxe: PXE): Promise { - const accountWallets: AccountWallet[] = await getInitialTestAccountsWallets(pxe); - const selectedWallet: AccountWallet = accountWallets.find(w => w.getAddress().equals(account.address))!; - if (!selectedWallet) { - throw new Error(`Wallet for account ${account.address.toShortString()} not found in the PXE.`); - } - return selectedWallet; -} diff --git a/boxes/blank-react/src/scripts/view_contract_function.ts b/boxes/blank-react/src/scripts/view_contract_function.ts deleted file mode 100644 index beff0032c77..00000000000 --- a/boxes/blank-react/src/scripts/view_contract_function.ts +++ /dev/null @@ -1,17 +0,0 @@ -import { getWallet } from './util.js'; -import { AztecAddress, CompleteAddress, Contract, ContractArtifact, PXE } from '@aztec/aztec.js'; - -export async function viewContractFunction( - address: AztecAddress, - artifact: ContractArtifact, - functionName: string, - typedArgs: any[], - pxe: PXE, - wallet: CompleteAddress, -) { - // we specify the account that is calling the view function by passing in the wallet to the Contract - const selectedWallet = await getWallet(wallet, pxe); - const contract = await Contract.at(address, artifact, selectedWallet); - - return await contract.methods[functionName](...typedArgs).view({ from: wallet.address }); -} diff --git a/boxes/blank-react/tests/blank.contract.test.ts b/boxes/blank-react/tests/blank.contract.test.ts index c70087824cc..789e952f492 100644 --- a/boxes/blank-react/tests/blank.contract.test.ts +++ b/boxes/blank-react/tests/blank.contract.test.ts @@ -1,70 +1,37 @@ import { BlankContract } from '../artifacts/Blank.js'; -import { callContractFunction, getWallet } from '../src/scripts/index.js'; -import { - AccountWallet, - AztecAddress, - CompleteAddress, - DeployMethod, - Fr, - TxStatus, - Wallet, - createDebugLogger, -} from '@aztec/aztec.js'; -import { pxe } from '../src/config.js'; -import { convertArgs } from '../src/scripts/util.js'; +import { AccountWallet, Fr, Contract, TxStatus, createDebugLogger, ContractDeployer } from '@aztec/aztec.js'; +import { deployerEnv } from '../src/config.js'; const logger = createDebugLogger('aztec:http-pxe-client'); -async function deployZKContract(owner: CompleteAddress, wallet: Wallet, pxe: PXE) { - logger('Deploying Blank contract...'); - - const { artifact } = BlankContract; - const salt = Fr.random(); - const functionAbi = artifact.functions.find(f => f.name === 'constructor')!; - const typedArgs: any[] = convertArgs(functionAbi, []); - const tx = new DeployMethod( - owner.publicKey, - pxe.getPxe(), - artifact, - (a, w) => Contract.at(a, artifact, w), - typedArgs, - ).send({ - contractAddressSalt: salt, - }); - - const { contractAddress } = await tx.wait(); - logger(`L2 contract deployed at ${contractAddress}`); - return BlankContract.at(contractAddress!, wallet); -} - describe('ZK Contract Tests', () => { let wallet: AccountWallet; - let owner: CompleteAddress; - let _account2: CompleteAddress; - let _account3: CompleteAddress; let contract: Contract; - let contractAddress: AztecAddress; + const { artifact } = BlankContract; + const numberToSet = Fr.random(); beforeAll(async () => { - const accounts = await pxe.getPxe().getRegisteredAccounts(); - [owner, _account2, _account3] = accounts; - - wallet = await getWallet(owner, pxe.getPxe()); - - contract = await deployZKContract(owner, wallet, pxe.getPxe()); - contractAddress = contract.address; + wallet = await deployerEnv.getWallet(); + const pxe = deployerEnv.pxe; + const deployer = new ContractDeployer(artifact, pxe); + const salt = Fr.random(); + const tx = deployer.deploy(Fr.random(), wallet.getCompleteAddress().address).send({ contractAddressSalt: salt }); + await tx.wait(); + const { contractAddress } = await tx.getReceipt(); + contract = await BlankContract.at(contractAddress!, wallet); + + logger(`L2 contract deployed at ${contractAddress}`); }, 60000); - test('call succeeds after deploy', async () => { - const callTxReceipt = await callContractFunction( - contractAddress, - contract.artifact, - 'getPublicKey', - [owner.address.toField()], - pxe.getPxe(), - owner, - ); + test('Can set a number', async () => { + logger(`${await wallet.getRegisteredAccounts()}`); + const callTxReceipt = await contract.methods.setNumber(numberToSet).send().wait(); expect(callTxReceipt.status).toBe(TxStatus.MINED); }, 40000); + + test('Can read a number', async () => { + const viewTxReceipt = await contract.methods.getNumber().view(); + expect(numberToSet.toBigInt()).toEqual(viewTxReceipt.value); + }, 40000); }); diff --git a/boxes/blank-react/tsconfig.json b/boxes/blank-react/tsconfig.json index c5f7d9ab2ad..76a1c63ac9b 100644 --- a/boxes/blank-react/tsconfig.json +++ b/boxes/blank-react/tsconfig.json @@ -1,11 +1,11 @@ { "compilerOptions": { - "outDir": "dest", + "outDir": "dist", "tsBuildInfoFile": ".tsbuildinfo", "target": "es2020", "lib": ["esnext", "dom", "DOM.Iterable"], - "module": "NodeNext", - "moduleResolution": "NodeNext", + "module": "ESNext", + "moduleResolution": "Bundler", "strict": true, "declaration": true, "allowSyntheticDefaultImports": true, @@ -20,5 +20,10 @@ "skipLibCheck": true, "jsx": "react-jsx" }, - "include": ["src/**/*.ts", "tests", "src/contracts/target/*.json", "artifacts/**/*.ts"] + "include": [ + "src/**/*.ts*", + "tests/**/*.ts", + "src/contracts/target/*.json", + "artifacts/**/*.ts" + ] } diff --git a/boxes/blank-react/webpack.config.js b/boxes/blank-react/webpack.config.js index 6e825d25aaf..7746f6e0b16 100644 --- a/boxes/blank-react/webpack.config.js +++ b/boxes/blank-react/webpack.config.js @@ -1,85 +1,40 @@ -import CopyWebpackPlugin from 'copy-webpack-plugin'; -import { createRequire } from 'module'; -import { dirname, resolve } from 'path'; -import ResolveTypeScriptPlugin from 'resolve-typescript-plugin'; -import { fileURLToPath } from 'url'; -import webpack from 'webpack'; - +import { createRequire } from "module"; +import webpack from "webpack"; +import HtmlWebpackPlugin from "html-webpack-plugin"; const require = createRequire(import.meta.url); export default (_, argv) => ({ - target: 'web', - mode: 'production', - devtool: 'source-map', + target: "web", + mode: "production", + devtool: "source-map", entry: { - main: './src/app/index.tsx', + main: "./src/index.tsx", }, module: { rules: [ { test: /\.tsx?$/, - use: 'ts-loader', + use: "ts-loader", }, { test: /\.css$/i, - use: ['style-loader', 'css-loader', 'postcss-loader'], - }, - { - test: /\.module\.scss$/, - use: [ - 'style-loader', - { - loader: 'css-loader', - options: { - modules: { - localIdentName: '[local]_[hash:base64:5]', - }, - }, - }, - 'sass-loader', - ], - }, - { - test: /\.(woff|woff2|eot|ttf|otf)$/, - type: 'asset/resource', - }, - { - test: /\.scss$/, - exclude: /\.module\.scss$/, - use: ['style-loader', 'css-loader', 'sass-loader'], + use: ["style-loader", "css-loader", "postcss-loader"], }, ], }, - output: { - path: resolve(dirname(fileURLToPath(import.meta.url)), './dest'), - filename: 'index.js', - }, plugins: [ + new HtmlWebpackPlugin({ + template: "./index.html", + }), new webpack.DefinePlugin({ - 'process.env': { - NODE_ENV: JSON.stringify(argv.mode || 'production'), + "process.env": { + NODE_ENV: JSON.stringify(argv.mode || "production"), }, }), - new webpack.ProvidePlugin({ Buffer: ['buffer', 'Buffer'] }), - new CopyWebpackPlugin({ - patterns: [ - { - from: './src/assets', - }, - { - from: './src/app/index.html', - to: 'index.html', - }, - ], - }), + new webpack.ProvidePlugin({ Buffer: ["buffer", "Buffer"] }), ], resolve: { - plugins: [new ResolveTypeScriptPlugin()], - alias: { - // All node specific code, wherever it's located, should be imported as below. - // Provides a clean and simple way to always strip out the node code for the web build. - './node/index.js': false, - }, + extensions: [".tsx", ".ts", ".js"], fallback: { crypto: false, os: false, @@ -87,19 +42,17 @@ export default (_, argv) => ({ path: false, url: false, worker_threads: false, - events: require.resolve('events/'), - buffer: require.resolve('buffer/'), - util: require.resolve('util/'), - stream: require.resolve('stream-browserify'), - string_decoder: require.resolve('string_decoder/'), - tty: require.resolve('tty-browserify'), + events: require.resolve("events/"), + buffer: require.resolve("buffer/"), + util: require.resolve("util/"), + stream: require.resolve("stream-browserify"), + string_decoder: require.resolve("string_decoder/"), + tty: require.resolve("tty-browserify"), }, }, devServer: { port: 5173, historyApiFallback: true, - client: { - overlay: false, - }, + open: true, }, }); diff --git a/boxes/blank/tests/blank.contract.test.ts b/boxes/blank/tests/blank.contract.test.ts index 34515dce208..6a422177aaf 100644 --- a/boxes/blank/tests/blank.contract.test.ts +++ b/boxes/blank/tests/blank.contract.test.ts @@ -37,7 +37,7 @@ describe('ZK Contract Tests', () => { wallet = await getWallet(owner, pxe.getPxe()); - contract = await deployZKContract(owner, wallet, pxe.getPxe()); + const c = (contract = await deployZKContract(owner, wallet, pxe.getPxe())); contractAddress = contract.address; }, 60000); diff --git a/boxes/npx.js b/boxes/npx.js new file mode 100755 index 00000000000..5bdaf3786c9 --- /dev/null +++ b/boxes/npx.js @@ -0,0 +1,167 @@ +#!/usr/bin/env node +import { Command } from "commander"; +import select from "@inquirer/select"; +import input from "@inquirer/input"; +import confirm from "@inquirer/confirm"; +const program = new Command(); +import tiged from "tiged"; +import { exec, execSync } from "child_process"; +import pty from "node-pty"; +import path from "path"; +import os from "os"; +import fs from "fs"; +import chalk from "chalk"; + +const { log, warn, info } = console; +const targetDir = path.join(os.homedir(), ".aztec/bin"); // Use os.homedir() to get $HOME + +function updatePathEnvVar() { + // Detect the user's shell profile file based on common shells and environment variables + const homeDir = os.homedir(); + let shellProfile; + if (process.env.SHELL?.includes("bash")) { + shellProfile = path.join(homeDir, ".bashrc"); + } else if (process.env.SHELL?.includes("zsh")) { + shellProfile = path.join(homeDir, ".zshrc"); + } else { + // Extend with more conditions for other shells if necessary + warn("Unsupported shell or shell not detected."); + return; + } + + // Read the current content of the shell profile to check if the path is already included + const profileContent = fs.readFileSync(shellProfile, "utf8"); + if (profileContent.includes(targetDir)) { + log(`${targetDir} is already in PATH.`); + return; + } + + // Append the export command to the shell profile file + const exportCmd = `\nexport PATH="$PATH:${targetDir}" # Added by Node.js script\n`; + fs.appendFileSync(shellProfile, exportCmd); + + info(`Added ${targetDir} to PATH in ${shellProfile}.`); +} + +program.action(async () => { + const appType = await select({ + message: "Please choose an option:", + choices: [ + { value: "blank-react", name: "Start a barebones React project" }, + ], + }); + + log(chalk.yellow(`You chose: ${appType}`)); + + try { + // STEP 1: Clone the box + const appName = await input({ + message: "Your app name:", + default: "my-aztec-app", + }); + + const emitter = tiged(`AztecProtocol/aztec-packages/boxes/${appType}`); + + chalk.blue("Cloning the boilerplate code..."); + emitter.on("info", (info) => { + log(info.message); + }); + + await emitter.clone(`./${appName}`).then(() => { + log(chalk.bgGreen("Your code is ready!")); + }); + } catch (error) { + log(chalk.bgRed(error.message)); + process.exit(1); + } + + // STEP 2: Checking for docker + try { + execSync("docker info >/dev/null 2>&1"); + } catch (error) { + log( + chalk.bgRed( + "Doesn't seem like Docker is installed. Please visit https://docs.aztec.network", + ), + ); + process.exit(1); + } + + // STEP 2: Checking for the Aztec Sandbox + try { + execSync("docker image inspect aztecprotocol/aztec > /dev/null 2>&1"); + } catch (error) { + const answer = await confirm({ + message: + "Seems like you don't have the Aztec Sandbox installed. Do you want to install it?", + default: true, + }); + + if (answer) { + try { + const ptySession = new Promise((resolve, reject) => { + const ptyProcess = pty.spawn("bash", [], { + name: "xterm-color", + cols: 80, + rows: 30, + cwd: process.cwd(), + env: process.env, + }); + + ptyProcess.on("data", function (data) { + process.stdout.write(data); + }); + + ptyProcess.write( + "echo y | bash -i <(curl -s install.aztec.network); exit\n", + ); + + ptyProcess.on("exit", function (exitCode, signal) { + updatePathEnvVar(); + resolve(); + log(chalk.bgGreen("The Sandbox is installed!")); + }); + }); + + await ptySession; + } catch (error) { + log( + chalk.bgRed( + "Failed to install the Sandbox. Please visit the docs at https://docs.aztec.network", + ), + ); + } + } + } + + // STEP 2: Running the Sandbox + try { + await fetch("http://localhost:8080", { + method: "POST", + headers: { + "Content-Type": "application/json", + }, + body: JSON.stringify({ + jsonrpc: "2.0", + method: "node_getVersion", + id: "null", + }), + }); + } catch (error) { + const answer = await confirm({ + message: + "I can't reach the Sandbox on port 8080. Do you want to start it?", + default: true, + }); + + if (answer) { + log( + chalk.green("Starting the sandbox... This might take a few minutes."), + ); + log(chalk.bgGreen(`Go and explore the boilerplate code while you wait!`)); + execSync(`$HOME/.aztec/bin/aztec sandbox`, { stdio: "inherit" }); + } + } +}); + +program.parse(); diff --git a/boxes/package.json b/boxes/package.json index 9b0417032de..26642817c5c 100644 --- a/boxes/package.json +++ b/boxes/package.json @@ -1,6 +1,7 @@ { "name": "@aztec/boxes", "packageManager": "yarn@4.0.2", + "type": "module", "private": true, "scripts": { "compile": "FORCE_COLOR=true yarn workspaces foreach -A -p -j unlimited -v run compile:local", @@ -11,6 +12,7 @@ "blank-react", "token" ], + "bin": "npx.js", "resolutions": { "@aztec/accounts": "portal:../yarn-project/accounts", "@aztec/aztec.js": "portal:../yarn-project/aztec.js", @@ -20,5 +22,14 @@ "@aztec/circuit-types": "portal:../yarn-project/circuit-types", "@aztec/ethereum": "portal:../yarn-project/ethereum", "@aztec/types": "portal:../yarn-project/types" + }, + "devDependencies": { + "@inquirer/confirm": "^3.0.0", + "@inquirer/input": "^2.0.0", + "@inquirer/select": "^2.0.0", + "chalk": "^5.3.0", + "commander": "^12.0.0", + "node-pty": "^1.0.0", + "tiged": "^2.12.6" } } diff --git a/boxes/token/.eslintrc.cjs b/boxes/token/.eslintrc.cjs deleted file mode 100644 index 1d5617c5bbb..00000000000 --- a/boxes/token/.eslintrc.cjs +++ /dev/null @@ -1,64 +0,0 @@ -module.exports = { - root: true, - env: { browser: true, es2020: true }, - parserOptions: { - project: './tsconfig.json', - }, - extends: [ - 'eslint:recommended', - 'plugin:@typescript-eslint/recommended', - 'plugin:react-hooks/recommended', - 'plugin:import/recommended', - 'plugin:import/typescript', - 'prettier', - ], - settings: { - 'import/resolver': { - typescript: true, - node: true, - }, - }, - ignorePatterns: ['dest', 'webpack.config.js', '.eslintrc.cjs'], - parser: '@typescript-eslint/parser', - plugins: ['react-refresh'], - overrides: [ - { - files: ['*.ts', '*.tsx'], - parserOptions: { - // hacky workaround for CI not having the same tsconfig setup - project: true, - }, - }, - ], - rules: { - 'react-refresh/only-export-components': ['warn', { allowConstantExport: true }], - '@typescript-eslint/explicit-module-boundary-types': 'off', - '@typescript-eslint/no-non-null-assertion': 'off', - '@typescript-eslint/no-explicit-any': 'off', - '@typescript-eslint/no-empty-function': 'off', - '@typescript-eslint/await-thenable': 'error', - '@typescript-eslint/no-floating-promises': 2, - '@typescript-eslint/no-unused-vars': ['error', { argsIgnorePattern: '^_', varsIgnorePattern: '^_' }], - 'require-await': 2, - 'no-console': 'warn', - 'no-constant-condition': 'off', - camelcase: 2, - 'no-restricted-imports': [ - 'error', - { - patterns: [ - { - group: ['client-dest'], - message: "Fix this absolute garbage import. It's your duty to solve it before it spreads.", - }, - { - group: ['dest'], - message: 'You should not be importing from a build directory. Did you accidentally do a relative import?', - }, - ], - }, - ], - 'import/no-unresolved': 'error', - 'import/no-extraneous-dependencies': 'error', - }, -}; diff --git a/boxes/token/.gitignore b/boxes/token/.gitignore deleted file mode 100644 index d37f6988611..00000000000 --- a/boxes/token/.gitignore +++ /dev/null @@ -1,7 +0,0 @@ -.yarn/* -!.yarn/releases - -node_modules -dest -artifacts -src/contracts/target diff --git a/boxes/token/.prettierignore b/boxes/token/.prettierignore deleted file mode 100644 index a72dfe54860..00000000000 --- a/boxes/token/.prettierignore +++ /dev/null @@ -1,2 +0,0 @@ -src/artifacts/**/*.json -src/artifacts/**/*.ts \ No newline at end of file diff --git a/boxes/token/.prettierrc.json b/boxes/token/.prettierrc.json deleted file mode 100644 index 7c3bbec6848..00000000000 --- a/boxes/token/.prettierrc.json +++ /dev/null @@ -1,6 +0,0 @@ -{ - "singleQuote": true, - "trailingComma": "all", - "printWidth": 120, - "arrowParens": "avoid" -} diff --git a/boxes/token/README.md b/boxes/token/README.md deleted file mode 100644 index 06bfa7da227..00000000000 --- a/boxes/token/README.md +++ /dev/null @@ -1,67 +0,0 @@ -# Aztec Box - -This box is a one-stop-shop for Aztec that will deploy a full React web page featuring a wallet. You can use it as a boilerplate to start developing your own Aztec app in seconds! - -## Prerequisites - -- You should have Docker installed. If you don't, follow [this guide](https://docs.aztec.network/dev_docs/getting_started/quickstart#install-docker). - -## Installation - -To start, run the Aztec install script: - -```bash -bash -i <(curl -s install.aztec.network)` -``` - -After a few minutes, you should have all the Aztec CLI commands ready to run. - -### 1. Launching the sandbox - -Run: - -```bash -aztec-sandbox -``` - -This will install all the dependencies and run the sandbox on port 8080 together with a anvil node. - -### 2. Unboxing the box - -Unbox the box with: - -```bash -aztec-cli unbox blank-react -``` - -and install dependencies: - -```bash -yarn -``` - -## Start developing - -Time to build. Run: - -```bash -yarn start -``` - -Your React app is waiting for you on port 5176. Time to make it your own! - -In the `src/contracts` folder, you'll find the default contract being deployed. Don't forget to recompile with `aztec-nargo compile`! Read the [aztec.nr documentation](https://docs.aztec.network/dev_docs/contracts/main) to get started with the `aztec.nr` framework. - -[Read the full Sandbox reference](https://docs.aztec.network/dev_docs/cli/sandbox-reference) for more info on what exactly is happening on your machine! - -## More info - -There are five folders in your `src` folder: - -- `app` - This is your actual React app -- `scripts` - These are the scripts the frontend is using to talk with the sandbox -- `contracts` - The Aztec Contracts you just deployed! -- `artifacts` - Auto-generated when you compile -- `test` - A boilerplate with a simple test - -Visit the [Aztec Docs](https://docs.aztec.network) for more information on how Aztec works, and the [Awesome Aztec Repository](https://github.com/AztecProtocol/awesome-aztec) for more cool projects, boilerplates and tooling. diff --git a/boxes/token/package.json b/boxes/token/package.json deleted file mode 100644 index 70985754c89..00000000000 --- a/boxes/token/package.json +++ /dev/null @@ -1,103 +0,0 @@ -{ - "name": "@aztec/box-token", - "private": true, - "version": "0.1.0", - "type": "module", - "main": "./dest/index.js", - "scripts": { - "compile": "cd src/contracts && ${AZTEC_NARGO:-aztec-nargo} compile", - "codegen": "${AZTEC_CLI:-aztec-cli} codegen src/contracts/target -o artifacts --ts", - "build": "yarn clean && yarn compile && yarn codegen && tsc -b && webpack", - "clean": "rm -rf ./dest .tsbuildinfo ./artifacts ./src/contracts/target", - "prep": "yarn clean && yarn compile && yarn codegen", - "dev": "yarn prep && webpack serve --mode development", - "serve": "serve -p 3000 ./dest", - "formatting": "prettier --check ./src && eslint ./src", - "formatting:fix": "prettier -w ./src", - "test": "NODE_NO_WARNINGS=1 node --experimental-vm-modules $(yarn bin jest) --runInBand" - }, - "jest": { - "preset": "ts-jest/presets/default-esm", - "transform": { - "^.+\\.(ts|tsx)$": [ - "ts-jest", - { - "useESM": true - } - ] - }, - "moduleNameMapper": { - "^(\\.{1,2}/.*)\\.js$": "$1" - }, - "testRegex": "tests/.*\\.test\\.ts$", - "rootDir": "./" - }, - "dependencies": { - "@aztec/accounts": "^0.16.9", - "@aztec/aztec-ui": "^0.1.14", - "@aztec/aztec.js": "^0.16.9", - "classnames": "^2.3.2", - "formik": "^2.4.3", - "node-sass": "^9.0.0", - "react": "^18.2.0", - "react-dom": "^18.2.0", - "sass-loader": "^13.3.2", - "serve": "^14.2.1", - "yup": "^1.2.0" - }, - "devDependencies": { - "@jest/globals": "^29.6.4", - "@types/jest": "^29.5.0", - "@types/mocha": "^10.0.3", - "@types/node": "^20.5.9", - "@types/react": "^18.2.15", - "@types/react-dom": "^18.2.7", - "@typescript-eslint/eslint-plugin": "^6.0.0", - "@typescript-eslint/parser": "^6.0.0", - "autoprefixer": "^10.4.15", - "copy-webpack-plugin": "^11.0.0", - "css-loader": "^6.8.1", - "eslint": "^8.21.0", - "eslint-config-prettier": "^9.0.0", - "eslint-import-resolver-typescript": "^3.5.5", - "eslint-plugin-import": "^2.27.5", - "eslint-plugin-prettier": "^5.0.1", - "eslint-plugin-react-hooks": "^4.6.0", - "eslint-plugin-react-refresh": "^0.4.3", - "jest": "^29.6.4", - "postcss": "^8.4.29", - "postcss-loader": "^7.3.3", - "prettier": "^3.1.1", - "resolve-typescript-plugin": "^2.0.1", - "stream-browserify": "^3.0.0", - "style-loader": "^3.3.3", - "ts-jest": "^29.1.0", - "ts-loader": "^9.4.4", - "ts-node": "^10.9.1", - "tty-browserify": "^0.0.1", - "typescript": "^5.0.4", - "util": "^0.12.5", - "webpack": "^5.88.2", - "webpack-cli": "^5.1.4", - "webpack-dev-server": "^4.15.1" - }, - "browserslist": { - "production": [ - ">0.5%", - "not dead", - "not op_mini all" - ], - "development": [ - "last 1 chrome version", - "last 1 firefox version", - "last 1 safari version" - ] - }, - "files": [ - "dest", - "src", - "!*.test.*" - ], - "types": "./dest/index.d.ts", - "packageManager": "yarn@4.0.2" -} diff --git a/boxes/token/postcss.config.cjs b/boxes/token/postcss.config.cjs deleted file mode 100644 index d95f27e7959..00000000000 --- a/boxes/token/postcss.config.cjs +++ /dev/null @@ -1,5 +0,0 @@ -const autoprefixer = require('autoprefixer'); - -module.exports = { - plugins: [autoprefixer], -}; diff --git a/boxes/token/src/@types/index.d.ts b/boxes/token/src/@types/index.d.ts deleted file mode 100644 index 091d25e2101..00000000000 --- a/boxes/token/src/@types/index.d.ts +++ /dev/null @@ -1,4 +0,0 @@ -declare module '*.svg' { - const content: any; - export default content; -} diff --git a/boxes/token/src/app/components/contract_function_form.module.scss b/boxes/token/src/app/components/contract_function_form.module.scss deleted file mode 100644 index 056dcc719ff..00000000000 --- a/boxes/token/src/app/components/contract_function_form.module.scss +++ /dev/null @@ -1,66 +0,0 @@ -.input { - border: none; - outline-width: 0; - outline-color: rgba(0, 0, 0, 0); - padding: 2px 20px 0 20px; - width: 100%; - height: 45px; - color: #000; - border: 1px solid rgba(0, 0, 0, 0); - font-size: 16px; - text-align: left; - font-weight: 400; - border-radius: 10px; - text-align: left; - text-overflow: ellipsis; - transition: box-shadow 0.2s; - box-shadow: 0px 4px 10px rgba(0, 0, 0, 0.1); - background-color: white; - -webkit-appearance: none; - - &:disabled { - color: #4a4a4a; - background-color: rgba(239, 239, 239, 0.3); - background: radial-gradient(rgba(239, 239, 239, 0.3), rgba(239, 239, 239, 0.3)); - -webkit-text-fill-color: #4a4a4a; - cursor: not-allowed; - } -} - -.label { - font-weight: 450; - font-size: 18px; - display: flex; - width: 100%; - flex-direction: column; - text-align: left; - margin-bottom: 15px; - justify-content: space-between; -} - -.inputWrapper { - width: 100%; - display: flex; - gap: 15px; -} - -.field { - display: flex; - justify-content: start; - flex-direction: column; - align-items: flex-start; -} - -.content { - display: flex; - justify-content: space-between; - flex-direction: column; - margin: 30px; - width: 450px; - gap: 30px; -} - -.actionButton { - width: 100%; - align-self: center; -} diff --git a/boxes/token/src/app/components/contract_function_form.tsx b/boxes/token/src/app/components/contract_function_form.tsx deleted file mode 100644 index d92e046b3ed..00000000000 --- a/boxes/token/src/app/components/contract_function_form.tsx +++ /dev/null @@ -1,231 +0,0 @@ -import { CONTRACT_ADDRESS_PARAM_NAMES, pxe } from '../../config.js'; -import { callContractFunction, deployContract, viewContractFunction } from '../../scripts/index.js'; -import { convertArgs } from '../../scripts/util.js'; -import styles from './contract_function_form.module.scss'; -import { Button, Loader } from '@aztec/aztec-ui'; -import { AztecAddress, CompleteAddress, ContractArtifact, Fr, FunctionArtifact } from '@aztec/aztec.js'; -import { useFormik } from 'formik'; -import * as Yup from 'yup'; - -const DEFAULT_FIELD_VALUE = 100; -interface BasicParamDef { - name: string; - type: { - kind: string; - path?: string; - }; -} -interface ParamDef { - name: string; - type: { - kind: string; - path?: string; - fields?: BasicParamDef[]; - }; -} - -type NoirFunctionYupSchema = { - // hack: add `any` at the end to get the array schema to typecheck - // eslint-disable-next-line @typescript-eslint/no-explicit-any - [key: string]: Yup.NumberSchema | Yup.ArraySchema | Yup.BooleanSchema | any; -}; - -type NoirFunctionFormValues = { - [key: string]: string | number | number[] | boolean | undefined; -}; - -// returns an object where first value is the yup type, second value is the default value -// this handles "base cases", which can be the parameters directly, or used -// in a recursive manner to handle structs -function generateYupDefaultValue(param: any, defaultAddress: string) { - if (CONTRACT_ADDRESS_PARAM_NAMES.includes(param.name)) { - // these are actually fields, which should be numbers, but yup doesn't support bigint so we convert back to bigint on execution - return { yupType: Yup.string().required(), defaultValue: defaultAddress }; - } else if (param.type.kind === 'field') { - return { yupType: Yup.number().required(), defaultValue: DEFAULT_FIELD_VALUE }; - } else if (param.type.kind === 'array') { - const arrayLength = param.type.length; - return { - yupType: Yup.array() - .of(Yup.number()) - .min(arrayLength) - .max(arrayLength) - .transform(function (value: number[], originalValue: string) { - if (typeof originalValue === 'string') { - return originalValue.split(',').map(Number); - } - return value; - }), - defaultValue: Array(arrayLength).fill(CONTRACT_ADDRESS_PARAM_NAMES.includes(param.name) ? defaultAddress : 200), - }; - } else if (param.type.kind === 'boolean') { - return { yupType: Yup.boolean().required(), defaultValue: false }; - } else { - throw new Error('Unsupported type', param); - } -} - -function generateYupSchema(functionAbi: FunctionArtifact, defaultAddress: string) { - const parameterSchema: NoirFunctionYupSchema = {}; - const initialValues: NoirFunctionFormValues = {}; - for (const param of functionAbi.parameters) { - // use helper function for non struct-types - if (['field', 'array', 'boolean'].includes(param.type.kind)) { - const { yupType, defaultValue } = generateYupDefaultValue(param, defaultAddress); - parameterSchema[param.name] = yupType; - initialValues[param.name] = defaultValue; - continue; - } else if (param.type.kind === 'struct') { - // for type checking, can't annotate left side of "for X of Y" statement - const paramFields: ParamDef[] = param.type.fields!; - const structParamSchema: any = {}; - const structInitialValues: any = {}; - for (const structParam of paramFields) { - const { yupType, defaultValue } = generateYupDefaultValue(structParam, defaultAddress); - structParamSchema[structParam.name] = yupType; - structInitialValues[structParam.name] = defaultValue; - } - parameterSchema[param.name] = Yup.object().shape(structParamSchema); - initialValues[param.name] = structInitialValues; - continue; - } - } - return { validationSchema: Yup.object().shape(parameterSchema), initialValues }; -} - -async function handleFunctionCall( - contractAddress: AztecAddress | undefined, - artifact: ContractArtifact, - functionName: string, - args: any, - wallet: CompleteAddress, -) { - const functionAbi = artifact.functions.find(f => f.name === functionName)!; - const typedArgs: any[] = convertArgs(functionAbi, args); - - if (functionName === 'constructor' && !!wallet) { - if (functionAbi === undefined) { - throw new Error('Cannot find constructor in the ABI.'); - } - // hack: addresses are stored as string in the form to avoid bigint compatibility issues with formik - // convert those back to bigints before sending - - // for now, dont let user change the salt. requires some change to the form generation if we want to let user choose one - // since everything is currently based on parsing the contractABI, and the salt parameter is not present there - const salt = Fr.random(); - return await deployContract(wallet, artifact, typedArgs, salt, pxe.getPxe()); - } - - if (functionAbi.functionType === 'unconstrained') { - return await viewContractFunction(contractAddress!, artifact, functionName, typedArgs, pxe.getPxe(), wallet); - } else { - const txnReceipt = await callContractFunction( - contractAddress!, - artifact, - functionName, - typedArgs, - pxe.getPxe(), - wallet, - ); - return `Transaction ${txnReceipt.status} on block number ${txnReceipt.blockNumber}`; - } -} - -interface ContractFunctionFormProps { - wallet: CompleteAddress; - contractAddress?: AztecAddress; - artifact: ContractArtifact; - functionAbi: FunctionArtifact; - defaultAddress: string; - title?: string; - buttonText?: string; - isLoading: boolean; - disabled: boolean; - onSubmit: () => void; - onSuccess: (result: any) => void; - onError: (msg: string) => void; -} - -export function ContractFunctionForm({ - wallet, - contractAddress, - artifact, - functionAbi, - defaultAddress, - buttonText = 'Submit', - isLoading, - disabled, - onSubmit, - onSuccess, - onError, -}: ContractFunctionFormProps) { - const { validationSchema, initialValues } = generateYupSchema(functionAbi, defaultAddress); - const formik = useFormik({ - initialValues: initialValues, - validationSchema: validationSchema, - onSubmit: async (values: any) => { - onSubmit(); - try { - const result = await handleFunctionCall(contractAddress, artifact, functionAbi.name, values, wallet); - onSuccess(result); - } catch (e: any) { - onError(e.message); - } - }, - }); - return ( -
- {functionAbi.parameters.map(input => ( -
- {input.type.kind !== 'struct' ? ( - <> - - - {formik.touched[input.name] && formik.errors[input.name] && ( -
{formik.errors[input.name]?.toString()}
- )} - - ) : ( - // Rendering object properties if the kind is 'struct' - // find a better way to represent that these are part of the same input - // than the text label `${input.name}.${field.name}` - input.type.fields.map(field => ( -
- - - {/* {formik.touched[input.name] && formik.touched[input.name] && formik.errors[input.name] && formik.errors[input.name][field.name] && ( -
{formik.errors[input.name][field.name]?.toString()}
- )} */} -
- )) - )} -
- ))} - {isLoading ? ( - - ) : ( -